fix(video): 대표 프레임을 변환보다 먼저 뽑고, 붙은 걸 목록이 알게 한다 - #98
Merged
Conversation
동영상 썸네일이 파일에 따라 되기도 하고 안 되기도 하던 원인이 양쪽에 하나씩 있었다. 서버: 포스터를 720p 변환이 끝난 뒤에 뽑고 있었다. 변환이 타임아웃(8×실시간, 상한 15분)이나 OOM으로 죽으면 processNext의 catch가 failed로만 표시하고 포스터는 시도조차 안 했다. 작은 영상은 shouldSkipVideoTranscode로 일찍 빠져 포스터가 붙으니, 큰 영상만 썸네일이 없는 패턴이 된다. 프레임 한 장은 수백 ms고 변환은 분 단위다 — 추출을 앞으로 옮기고 별도 updateMany로 즉시 붙인다 (R120). 한 번 실패한 포스터는 되돌아올 경로가 없어 영구히 비어 있었다. 기동 시 한 번, 최대 50건 메우는 backfillMissingPosters()를 둔다. 주기 실행은 하지 않는다. 클라이언트: 업로드 응답에는 posterPath가 null이고, 목록은 포스터가 뒤늦게 붙은 걸 알 방법이 없었다. 새로고침 전까지 자리표시자나 맨 <video>로 남는데, 안드로이드 크롬은 preload="metadata" <video>를 대개 검게 칠한다. useVideoPosterRefresh가 해당 첨부의 상태만 되묻어 제자리에 덮는다 — 목록 전체 재조회는 업로드 중 낙관적 첨부를 지우므로 쓰지 않는다 (R121). 2s→30s 백오프, 8회로 끝, 가려지면 쉰다. 새 라우트 GET /api/attachments/posters — 인증·가구 스코프·읽기 전용, id 상한 60. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
eigger
force-pushed
the
fix/video-poster-reliability
branch
from
September 6, 2026 06:05
d9226d6 to
c11cd9a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
동영상 썸네일이 파일에 따라 되기도 하고 안 되기도 하던 원인이 서버·클라이언트에 하나씩 있었다.
서버 — 포스터가 변환 뒤에 있었다 (R122)
transcodeClaimedAttachment가 720p 변환을 끝낸 다음에야 프레임을 뽑았다. 변환이 타임아웃(8×실시간, 상한 15분)이나 OOM으로 죽으면processNext의 catch가failed로만 표시하고 포스터는 시도조차 하지 않았다. 작은 영상은shouldSkipVideoTranscode로 일찍 빠져 포스터가 붙으므로 큰 영상만 썸네일이 없는 패턴이 된다.프레임 한 장 추출은 수백 ms, 재인코딩은 분 단위다. 추출을 변환 앞으로 옮기고 별도
updateMany({ where: { posterPath: null } })로 즉시 붙인다 — 변환이 어떻게 끝나든 포스터는 남고, 큰 영상은 몇 분 일찍 썸네일이 생긴다.서버 — 한 번 실패한 포스터는 영영 다시 안 뽑았다
상태가 종단(ready/skipped/failed)으로 가면 되돌아올 경로가 없어 그 영상은 영구히 썸네일이 없었다.
backfillMissingPosters()를 기동 시 한 번, 최대 50건 돈다. 주기 실행은 하지 않는다 — 진짜로 못 읽는 파일이면 매 tick마다 ffmpeg를 20초씩 태우게 된다. 이미 그렇게 남은 기존 영상들이 이걸로 메워진다.클라이언트 — 포스터가 붙은 걸 화면이 몰랐다 (R123)
업로드 응답에는
posterPath: null이 담기고(포스터는 변환 잡이 뒤에서 만든다) 목록은 그걸 그대로 들고 있다. 폴링도 재조회도 없어 새로고침 전까지 자리표시자(pending/processing) 또는 맨<video>(종단 상태 + 포스터 없음)로 남았다. 안드로이드 크롬은preload="metadata"<video>를 대개 검게 칠하므로 "썸네일이 안 나온다"로 보인다.useVideoPosterRefresh가 대기 중인 첨부의 상태만 되묻어 제자리에 덮는다. 목록 전체 재조회(loadEvents)는setEvents(page)로 배열을 갈아끼워 업로드 중 낙관적 첨부를 지우므로 쓰지 않는다. 2s→30s 백오프, 8회로 끝, 화면이 가려지면 쉰다.새 라우트
GET /api/attachments/posters?ids=— 인증 필수, 가구 스코프(K-1), 읽기 전용(K-7), id 상한 60.posterPath·transcodeStatus만 돌려준다.검증
npm test— shared 65, api 233, web 245 통과 (포스터 순서·중복 방지·백필 3건, 클라 헬퍼 6건 추가)tsc --noEmit통과 /npm run lint기존 경고 2건 외 없음🤖 Generated with Claude Code