diff --git a/src/routes/public.ts b/src/routes/public.ts index 3bb16a2..4126c60 100644 --- a/src/routes/public.ts +++ b/src/routes/public.ts @@ -100,8 +100,14 @@ function sendVideoFile(video: Video, req: Parameters[0], res: Pa /** * 영상 파일 스트리밍 (짧은 ID-only URL — 외부 공유 호환용 / 레거시). * - GET /file/video/:videoId - * - GET /api/video/:videoId/file + * ID 가 전역에서 정확히 1건일 때만 제공한다. 같은 ID 가 여러 폴더에 있으면 + * getVideoByIdGlobal 이 null 을 돌려주므로 404 (어느 폴더인지 모호). + * 폴더가 확실한 경우엔 정규 경로 URL `/api/video/폴더/[폴더/]ID` 를 쓸 것. * ?edited=0 이면 원본을 강제, 기본은 편집본 있으면 편집본. + * + * 주의: 예전 `/api/video/:videoId/file` 레거시 라우트는 제거했다. 정규 경로 + * 라우트 `/api/video/:topName/:videoId` 와 2세그먼트에서 충돌해서, ID 가 + * 리터럴 "file" 인 영상의 정규 URL `/api/video/폴더/file` 을 가로챘기 때문. */ const streamVideoHandler: RequestHandler = (req, res, next) => { try { @@ -116,7 +122,6 @@ const streamVideoHandler: RequestHandler = (req, res, next) => { } } publicRouter.get('/file/video/:videoId', streamVideoHandler) -publicRouter.get('/api/video/:videoId/file', streamVideoHandler) /** * 영상 파일 스트리밍 (폴더 경로 + ID — 정규 공유 URL). @@ -125,9 +130,8 @@ publicRouter.get('/api/video/:videoId/file', streamVideoHandler) * * 재생 페이지(/video/...)와 동일한 경로 규칙. 마지막 세그먼트가 영상 ID 이고 * 앞쪽 세그먼트는 그 영상이 실제 속한 폴더 경로와 일치해야 한다. - * - * 라우트 등록 순서 주의: 위의 `/api/video/:videoId/file` (2세그·끝이 리터럴 "file") - * 보다 뒤에 둬서, 레거시 URL 이 이 경로 라우트에 가로채이지 않게 한다. + * (예전 `/api/video/:videoId/file` 레거시 라우트와의 2세그먼트 충돌은 그 라우트를 + * 제거해서 해소했다 — 이제 ID 가 "file" 인 영상도 정규 URL 로 정확히 찾는다.) */ const streamVideoByPathHandler: RequestHandler = (req, res, next) => { try { diff --git a/src/storeDb.ts b/src/storeDb.ts index bacb0b2..7606672 100644 --- a/src/storeDb.ts +++ b/src/storeDb.ts @@ -309,15 +309,18 @@ export function getVideoInFolder(folderId: number, id: string): Video | null { } /** - * ID 만으로 전역에서 첫 번째 영상을 찾는다 (폴더 경로 없는 레거시/외부공유 fallback). - * 같은 ID 가 여러 폴더에 있으면 folder_id 가 작은(=먼저 만든) 쪽을 돌려준다. + * ID 만으로 전역에서 영상을 찾는다 (폴더 경로 없는 레거시/외부공유 fallback). + * ID 는 이제 폴더별로만 유일하므로, 같은 ID 가 여러 폴더에 있으면 어느 쪽인지 + * 모호하다. 그런 경우엔 아무 것도 돌려주지 않아 호출 측이 404 로 처리하게 한다. + * 전역에서 정확히 1건일 때만 그 영상을 돌려준다. */ export function getVideoByIdGlobal(id: string): Video | null { if (!isSafeVideoId(id)) return null - const r = getDb() - .prepare(`SELECT * FROM videos WHERE id = ? ORDER BY folder_id LIMIT 1`) - .get(id) as VideoRow | undefined - return r ? rowToVideo(r) : null + const rows = getDb() + .prepare(`SELECT * FROM videos WHERE id = ? ORDER BY folder_id LIMIT 2`) + .all(id) as VideoRow[] + if (rows.length !== 1) return null // 없음 또는 중복(모호) → 404 + return rowToVideo(rows[0]) } export function listVideosInFolder(folderId: number): Video[] {