import fs from 'fs'; import path from 'path'; import { error, type RequestHandler } from '@sveltejs/kit'; import pool from '$lib/server/database'; import { lookup as getMimeType } from 'mime-types'; // ✅ ESM 호환 안전한 방식 export const GET: RequestHandler = async ({ params, request }) => { console.log(`\n--- [video-stream] 요청 수신: ${new Date().toISOString()} ---`); const Index = params.Index; console.log(`[1. 파라미터] 요청된 동영상 Index: ${Index}`); if (!Index || isNaN(Number(Index))) { throw error(400, '유효한 동영상 Index가 필요합니다.'); } const client = await pool.connect(); let filePath: string; try { console.log('[2. DB] 파일 경로 조회 시작...'); const query = ` SELECT "LocationFile" FROM "TumerMovie" WHERE "Index" = $1; `; const result = await client.query(query, [Index]); if (result.rows.length === 0) { throw error(404, `해당 Index(${Index})의 동영상을 찾을 수 없습니다.`); } filePath = result.rows[0].LocationFile; console.log(`[2. DB] 조회된 파일 경로: ${filePath}`); } catch (dbError) { console.error('[DB 오류]', dbError); throw error(500, '데이터베이스 조회 중 오류 발생'); } finally { client.release(); } try { const stat = fs.statSync(filePath); const fileSize = stat.size; const contentType = getMimeType(filePath) || 'application/octet-stream'; // ✅ 안전한 MIME 판별 console.log(`[3. 파일 시스템] 파일 크기: ${fileSize}, Content-Type: ${contentType}`); const range = request.headers.get('range'); if (!range) { console.log('[4. Range 없음 → 전체 전송]'); const fullStream = fs.createReadStream(filePath); return new Response(fullStream as any, { status: 200, headers: { 'Content-Type': contentType, 'Content-Length': fileSize.toString(), 'Accept-Ranges': 'bytes' } }); } console.log(`[4. Range 요청 수신]: ${range}`); const parts = range.replace(/bytes=/, '').split('-'); const start = parseInt(parts[0], 10); const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1; if (isNaN(start) || isNaN(end) || start >= fileSize || end >= fileSize) { console.error('[Range 오류] 잘못된 범위 요청:', range); throw error(416, '잘못된 Range 요청'); } const chunkSize = end - start + 1; console.log(`[5. 스트리밍 구간] ${start} - ${end} (${chunkSize} bytes)`); const fileStream = fs.createReadStream(filePath, { start, end }); return new Response(fileStream as any, { status: 206, headers: { 'Content-Range': `bytes ${start}-${end}/${fileSize}`, 'Accept-Ranges': 'bytes', 'Content-Length': chunkSize.toString(), 'Content-Type': contentType } }); } catch (fileError: any) { if (fileError.code === 'ENOENT') { console.error('[파일 오류] 파일 없음:', filePath); throw error(404, '파일을 찾을 수 없습니다.'); } console.error('[파일 시스템 오류]', fileError); throw error(500, '파일 스트리밍 중 오류 발생'); } };