Per-entry and bulk trim inputs now take a play length in seconds (start + length) instead of an end time. Default 15s, so start=30 produces a 30–45s clip. Converted to endSec at register time; the trim editor maps its end handle to a length on apply. Backend unchanged (still works in start/end terms). Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
123 lines
5.8 KiB
Plaintext
123 lines
5.8 KiB
Plaintext
<!doctype html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>플레이리스트 추가 · <%= breadcrumb.join(' / ') %></title>
|
|
<link rel="stylesheet" href="/static/styles.css" />
|
|
</head>
|
|
<body class="siteBody">
|
|
<%
|
|
var folderPathEnc = breadcrumb.map(function (s) { return encodeURIComponent(s) }).join('/')
|
|
var folderHref = '/op/folder/' + folderPathEnc
|
|
var folderLabel = breadcrumb.join(' / ')
|
|
%>
|
|
<header class="editorNav">
|
|
<div class="editorNavLeft">
|
|
<a class="muted" href="<%= folderHref %>">← <%= folderLabel %></a>
|
|
<span class="titleInput" style="cursor:default;">플레이리스트 추가</span>
|
|
</div>
|
|
</header>
|
|
|
|
<main class="editorMain">
|
|
<section class="playlistProbeSection">
|
|
<p class="muted">유튜브 플레이리스트 주소를 입력하면 항목 목록을 불러옵니다.</p>
|
|
<div class="playlistProbeRow">
|
|
<input type="text" id="playlistUrl" placeholder="https://www.youtube.com/playlist?list=..." />
|
|
<button type="button" class="primaryButton" id="probeBtn">목록 불러오기</button>
|
|
</div>
|
|
<p id="probeStatus" class="muted"></p>
|
|
</section>
|
|
|
|
<section class="playlistEntriesSection" id="entriesSection" hidden>
|
|
<div class="playlistControlsRow">
|
|
<div class="idModeBox">
|
|
<span class="idModeLabel">영상 ID 생성 방식</span>
|
|
<label><input type="radio" name="idMode" value="random" checked /> 랜덤</label>
|
|
<label><input type="radio" name="idMode" value="sequential" /> 정렬된 번호</label>
|
|
<label class="zeroPadWrap" id="zeroPadWrap" hidden>
|
|
<input type="checkbox" id="zeroPadToggle" /> 자릿수 채우기 (예: 001)
|
|
</label>
|
|
</div>
|
|
<div class="bulkTrimBox">
|
|
<span class="idModeLabel">일괄 구간</span>
|
|
<label>시작 <input type="text" id="bulkStart" placeholder="0:00" /></label>
|
|
<label>길이(초) <input type="text" id="bulkLength" placeholder="15" /></label>
|
|
<button type="button" class="secondaryButton" id="bulkApplyBtn">적용</button>
|
|
<button type="button" class="secondaryButton" id="bulkResetBtn">초기화</button>
|
|
</div>
|
|
<div class="playlistSummary">
|
|
<span id="entriesCount" class="muted">0 항목</span>
|
|
</div>
|
|
</div>
|
|
|
|
<ol class="playlistEntryList" id="entryList"></ol>
|
|
|
|
<div class="playlistFooter">
|
|
<p id="registerStatus" class="muted"></p>
|
|
<button type="button" class="primaryButton" id="registerBtn">등록 & 다운로드 시작</button>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<!-- 항목 우클릭 메뉴 -->
|
|
<div class="ctxMenu" id="entryCtxMenu" hidden>
|
|
<button type="button" data-action="trim">자르기</button>
|
|
</div>
|
|
|
|
<!-- 미리보기 재생 모달 -->
|
|
<div class="playerOverlay" id="previewOverlay" hidden>
|
|
<div class="playerModal" role="dialog" aria-modal="true">
|
|
<button type="button" class="playerClose" id="previewClose" aria-label="닫기">✕</button>
|
|
<div class="playerTitle" id="previewTitle"></div>
|
|
<div class="ytEmbedWrap"><div id="previewPlayer"></div></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 자르기(트림) 모달 — /video/editor 타임라인을 YouTube 임베드로 재현 -->
|
|
<div class="playerOverlay" id="trimOverlay" hidden>
|
|
<div class="playerModal trimModal" role="dialog" aria-modal="true">
|
|
<button type="button" class="playerClose" id="trimClose" aria-label="닫기">✕</button>
|
|
<div class="playerTitle" id="trimTitle"></div>
|
|
<div class="ytEmbedWrap"><div id="trimPlayer"></div></div>
|
|
<div class="trimTimeline">
|
|
<div class="trimTimelineHeader">
|
|
<span class="timeReadout" id="plTimeReadout">00:00.0 / 00:00.0</span>
|
|
<div class="trimTimelineActions">
|
|
<button type="button" data-pmark="start" title="현재 시점을 시작점으로">[ 시작점</button>
|
|
<button type="button" data-pmark="end" title="현재 시점을 끝점으로">끝점 ]</button>
|
|
<button type="button" data-paction="playSelection" title="선택 구간만 재생">▶ 선택 재생</button>
|
|
<button type="button" data-paction="reset" title="자르기 초기화">초기화</button>
|
|
</div>
|
|
</div>
|
|
<div class="trimBar" id="plTrimBar">
|
|
<div class="trimRangeFill" id="plTrimRangeFill"></div>
|
|
<div class="trimHandle trimHandleStart" id="plTrimHandleStart" data-phandle="start" title="시작 핸들"></div>
|
|
<div class="trimHandle trimHandleEnd" id="plTrimHandleEnd" data-phandle="end" title="끝 핸들"></div>
|
|
<div class="trimPlayhead" id="plTrimPlayhead"></div>
|
|
</div>
|
|
<div class="trimNumericRow">
|
|
<label>시작(초) <input type="number" id="plStartSec" step="0.1" min="0" value="0" /></label>
|
|
<span class="trimDuration" id="plTrimDuration">선택: 0.0초</span>
|
|
<label>끝(초, 비우면 끝까지) <input type="number" id="plEndSec" step="0.1" min="0" value="" /></label>
|
|
</div>
|
|
<p class="muted">재생바의 파란 핸들을 끌어 구간을 정합니다. 적용하면 항목의 시작/길이가 갱신되고, 다운로드 완료 후 이 구간으로 잘립니다.</p>
|
|
</div>
|
|
<div class="modalActions">
|
|
<button type="button" class="secondaryButton" id="trimCancel">취소</button>
|
|
<button type="button" class="primaryButton" id="trimApply">적용</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
window.__PLAYLIST__ = {
|
|
folderPath: <%- jsonForScript(breadcrumb) %>,
|
|
folderId: <%- jsonForScript(folder.id) %>
|
|
}
|
|
</script>
|
|
<script src="https://www.youtube.com/iframe_api"></script>
|
|
<script src="/static/playlist.js"></script>
|
|
</body>
|
|
</html>
|