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:
@@ -363,7 +363,7 @@ body.siteBody.centerLayout { display: flex; align-items: center; justify-content
|
||||
}
|
||||
.playlistEntryRow {
|
||||
display: grid;
|
||||
grid-template-columns: 26px 36px 96px 1fr 70px 28px;
|
||||
grid-template-columns: 24px 28px 90px minmax(0, 1fr) 210px 60px 26px;
|
||||
align-items: center; gap: 10px;
|
||||
background: var(--bg-card); border: 1px solid var(--border);
|
||||
border-radius: 10px; padding: 8px 10px;
|
||||
@@ -378,10 +378,18 @@ body.siteBody.centerLayout { display: flex; align-items: center; justify-content
|
||||
color: var(--text-muted); font-size: 13px; text-align: right;
|
||||
}
|
||||
.entryThumb {
|
||||
width: 96px; aspect-ratio: 16 / 9;
|
||||
border-radius: 6px; overflow: hidden;
|
||||
width: 90px; aspect-ratio: 16 / 9;
|
||||
border-radius: 6px; overflow: hidden; position: relative;
|
||||
background: var(--bg); border: 1px solid var(--border);
|
||||
cursor: pointer;
|
||||
}
|
||||
.entryThumb::after {
|
||||
content: '▶'; position: absolute; inset: 0;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: #fff; font-size: 18px; text-shadow: 0 1px 4px rgba(0,0,0,0.7);
|
||||
opacity: 0; transition: opacity 120ms ease; pointer-events: none;
|
||||
}
|
||||
.entryThumb:hover::after { opacity: 1; }
|
||||
.entryThumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||
.entryThumbEmpty { opacity: 0.6; }
|
||||
.entryFields {
|
||||
@@ -412,6 +420,49 @@ body.siteBody.centerLayout { display: flex; align-items: center; justify-content
|
||||
}
|
||||
.entryRemove:hover { color: #e57373; border-color: #e57373; }
|
||||
|
||||
/* 항목별 시작/종료 시간 입력 (제목 오른쪽 절반 공간) */
|
||||
.entryTrim {
|
||||
display: flex; flex-direction: column; gap: 4px;
|
||||
}
|
||||
.entryTrim label {
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
font-size: 12px; color: var(--text-muted);
|
||||
}
|
||||
.entryTrim input {
|
||||
flex: 1; min-width: 0;
|
||||
background: var(--bg); border: 1px solid var(--border); border-radius: 6px;
|
||||
color: var(--text); padding: 3px 6px; font-size: 12px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
|
||||
/* 일괄 시작/종료 + 적용/초기화 */
|
||||
.bulkTrimBox {
|
||||
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
|
||||
background: var(--bg-card); border: 1px solid var(--border);
|
||||
border-radius: 10px; padding: 10px 14px;
|
||||
}
|
||||
.bulkTrimBox label {
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
color: var(--text); font-size: 13px;
|
||||
}
|
||||
.bulkTrimBox input {
|
||||
width: 70px;
|
||||
background: var(--bg); border: 1px solid var(--border); border-radius: 6px;
|
||||
color: var(--text); padding: 4px 8px; font-size: 13px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
|
||||
/* YouTube 임베드 (미리보기 / 자르기 모달) */
|
||||
.ytEmbedWrap {
|
||||
position: relative; width: 100%; aspect-ratio: 16 / 9;
|
||||
background: #000; border-radius: 8px; overflow: hidden;
|
||||
}
|
||||
.ytEmbedWrap iframe, .ytEmbedWrap > div {
|
||||
position: absolute; inset: 0; width: 100%; height: 100%; border: 0;
|
||||
}
|
||||
.trimModal { width: min(900px, 94vw); gap: 12px; }
|
||||
.trimModal .trimTimeline { background: var(--bg-alt); }
|
||||
|
||||
.playlistFooter {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
gap: 12px; flex-wrap: wrap; margin-top: 8px;
|
||||
|
||||
Reference in New Issue
Block a user