feat(playlist): per-entry/bulk trim, preview, and trim editor on import
플레이리스트 가져오기 화면에 다음을 추가: - 항목별 제목 입력을 절반으로 줄이고 오른쪽에 시작/종료 시간 입력 추가 (기본 시작 0, 종료는 영상 길이) - 정렬 옵션 옆에 일괄 시작/종료 입력과 적용/초기화 버튼 추가 (초기화 = 시작 0, 종료 = 영상 길이) - 항목 썸네일 클릭 시 YouTube 임베드로 미리보기 재생 - 항목 우클릭 → 자르기 메뉴 → /video/editor 식 타임라인 모달(YouTube IFrame) 에서 구간을 정하면 항목의 시작/종료 입력에 반영 - 백엔드: playlist/start 가 항목별 startSec/endSec 를 받아 다운로드 잡에 실어 보내고, 60fps 후처리 후 ffmpeg 로 해당 구간을 잘라 편집본 생성 Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
This commit is contained in:
@@ -385,7 +385,13 @@ opRouter.post(
|
||||
throw new Error('등록할 항목이 없습니다.')
|
||||
}
|
||||
|
||||
type Cleaned = { url: string; title: string; videoId: string }
|
||||
type Cleaned = {
|
||||
url: string
|
||||
title: string
|
||||
videoId: string
|
||||
startSec: number
|
||||
endSec: number | null
|
||||
}
|
||||
const cleaned: Cleaned[] = []
|
||||
const seen = new Set<string>()
|
||||
for (let i = 0; i < rawEntries.length; i += 1) {
|
||||
@@ -405,8 +411,14 @@ opRouter.post(
|
||||
if (videoIdExists(videoId)) {
|
||||
throw new Error(`항목 ${i + 1}: 영상 ID "${videoId}" 가 이미 사용 중입니다.`)
|
||||
}
|
||||
// 자르기 구간(선택). 잘못된 값은 무시하고 전체로 처리.
|
||||
const startRaw = Number(e?.startSec)
|
||||
const startSec = Number.isFinite(startRaw) && startRaw > 0 ? startRaw : 0
|
||||
const endRaw = Number(e?.endSec)
|
||||
const endSec =
|
||||
Number.isFinite(endRaw) && endRaw > startSec ? endRaw : null
|
||||
seen.add(videoId)
|
||||
cleaned.push({ url, title, videoId })
|
||||
cleaned.push({ url, title, videoId, startSec, endSec })
|
||||
}
|
||||
|
||||
// 검증 통과 → 순차적으로 잡 시작. createVideo 의 unique 제약이 마지막 안전망.
|
||||
@@ -416,7 +428,9 @@ opRouter.post(
|
||||
folderId: folder.id,
|
||||
url: entry.url,
|
||||
title: entry.title,
|
||||
videoId: entry.videoId
|
||||
videoId: entry.videoId,
|
||||
startSec: entry.startSec,
|
||||
endSec: entry.endSec
|
||||
})
|
||||
jobs.push({ jobId: job.id, videoId: job.videoId })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user