fix(youtube): upscale downloaded original to 60fps after yt-dlp finishes

Review P1: yt-dlp 가 fps 1순위로 정렬해 가져와도, 영상 자체에 60fps
포맷이 없는 경우 original.* 이 30fps 그대로 저장되어 "원본도 60fps 로
받아줘" 요청과 어긋났습니다.

다운로드 완료 직후 후처리 단계를 추가:

- editor.ts 에 `upscaleOriginalTo60Fps(dir, inputName)` 노출
  · ffprobe 로 source fps 측정
  · ≥60fps 면 그대로 두고 inputName 반환
  · <60fps 면 minterpolate(mci, aobmc, bidir) 로 60fps 까지 끌어올려
    `original.mp4` 로 저장하고 기존 파일 제거
  · ffmpeg/ffprobe 없거나 보간 실패하면 원본 그대로 유지 (다운로드 살림)

- youtube.ts `runJob` 마지막에 이 함수를 호출하고, 새 파일명이 돌아오면
  meta.originalFile 도 업데이트. 후처리 중 진행률을 99% 로 표시하고
  실패해도 다운로드 자체는 성공으로 마감.

이로써 편집기에서 다시 보간할 일도 없어집니다 (이미 60fps 원본).
편집 코드 쪽 보호 분기는 직접 업로드 경로용 안전망으로 그대로 둡니다.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-05-16 02:38:08 +09:00
parent 105c5bf09d
commit cdd23b8195
2 changed files with 84 additions and 2 deletions

View File

@@ -2,6 +2,7 @@ import { spawn, spawnSync } from 'node:child_process'
import { promises as fs } from 'node:fs'
import path from 'node:path'
import { jobsDir, projectRoot } from './paths.js'
import { upscaleOriginalTo60Fps } from './editor.js'
import {
loadVideoMeta,
newVideoId,
@@ -231,6 +232,24 @@ async function runJob(job: DownloadJob, bin: string): Promise<void> {
const found = entries.find((n) => n.startsWith('original.'))
if (found) finalName = found
}
// 사용자가 "원본도 60fps 로" 요청 — yt-dlp 가 fps 1순위로 잡아도 60fps 자체가
// 없는 영상이 있으니, 다운로드 후 원본을 ffprobe 로 확인해 <60fps 면
// minterpolate 로 60fps 까지 끌어올린다. 실패해도 원본은 그대로 둠.
try {
job.progress = 99
job.message = '60fps 변환 확인 중'
await persistJob(job)
const bumped = await upscaleOriginalTo60Fps(dir, finalName)
if (bumped !== finalName) {
job.message = '60fps 변환 완료'
finalName = bumped
}
} catch (err) {
// 후처리 실패는 다운로드 자체를 실패시키지 않는다. 원본 보존이 우선.
console.error('[youtube] 60fps 후처리 실패:', err)
}
const meta = await loadVideoMeta(job.folder, job.videoId)
if (meta) {
meta.originalFile = finalName