(function () { var ctx = window.__EDITOR__ || { folder: '', video: null } var folder = ctx.folder var video = ctx.video var dropZone = document.getElementById('dropZone') var fileInput = document.getElementById('fileInput') var ytUrl = document.getElementById('ytUrl') var ytProbeBtn = document.getElementById('ytProbeBtn') var ytStartBtn = document.getElementById('ytStartBtn') var probeInfo = document.getElementById('probeInfo') var dlProgress = document.getElementById('downloadProgress') var uploadStatus = document.getElementById('uploadStatus') var videoPanel = document.getElementById('videoPanel') var editVideo = document.getElementById('editVideo') var titleInput = document.getElementById('titleInput') var startSec = document.getElementById('startSec') var endSec = document.getElementById('endSec') var saveBtn = document.getElementById('saveBtn') // 드래그&드롭 ;['dragenter', 'dragover'].forEach(function (evt) { dropZone.addEventListener(evt, function (e) { e.preventDefault(); e.stopPropagation() dropZone.classList.add('dragOver') }) }) ;['dragleave', 'drop'].forEach(function (evt) { dropZone.addEventListener(evt, function (e) { e.preventDefault(); e.stopPropagation() dropZone.classList.remove('dragOver') }) }) dropZone.addEventListener('drop', function (e) { var files = e.dataTransfer && e.dataTransfer.files if (files && files.length > 0) uploadFile(files[0]) }) fileInput.addEventListener('change', function () { if (fileInput.files && fileInput.files[0]) uploadFile(fileInput.files[0]) }) function uploadFile(file) { var form = new FormData() form.append('file', file) form.append('title', titleInput.value || file.name) uploadStatus.textContent = '업로드 중...' var xhr = new XMLHttpRequest() xhr.open('POST', '/op/folder/' + encodeURIComponent(folder) + '/video/upload') xhr.upload.addEventListener('progress', function (e) { if (e.lengthComputable) { var pct = Math.round((e.loaded / e.total) * 100) uploadStatus.textContent = '업로드 ' + pct + '%' } }) xhr.onload = function () { try { var res = JSON.parse(xhr.responseText) if (res.ok) { location.href = '/op/folder/' + encodeURIComponent(folder) + '/video/editor?id=' + encodeURIComponent(res.videoId) } else { uploadStatus.textContent = '업로드 실패: ' + (res.message || '') } } catch (err) { uploadStatus.textContent = '업로드 실패' } } xhr.onerror = function () { uploadStatus.textContent = '업로드 실패 (네트워크)' } xhr.send(form) } // YouTube probe ytProbeBtn.addEventListener('click', function () { var url = ytUrl.value.trim() if (!url) return probeInfo.textContent = '확인 중...' ytStartBtn.disabled = true fetch('/op/folder/' + encodeURIComponent(folder) + '/video/youtube/probe', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ url: url }) }).then(function (r) { return r.json() }).then(function (j) { if (!j.ok) { probeInfo.textContent = j.message || '확인 실패' return } var p = j.probe var parts = [ '제목: ' + p.title, '길이: ' + formatDuration(p.durationSec) ] if (p.filesizeApprox) parts.push('대략 ' + formatSize(p.filesizeApprox)) if (p.etaSec) parts.push('예상 다운로드: ' + formatDuration(p.etaSec)) probeInfo.textContent = parts.join(' · ') if (p.warnOver5min) { if (!window.confirm('가져오는 데 5분 이상 걸릴 수 있습니다. 진행할까요?\n(다른 페이지에서 작업해도 백그라운드로 계속 진행됩니다.)')) { return } } if (!titleInput.value) titleInput.value = p.title ytStartBtn.disabled = false }).catch(function (e) { probeInfo.textContent = '확인 실패: ' + e.message }) }) ytStartBtn.addEventListener('click', function () { var url = ytUrl.value.trim() if (!url) return fetch('/op/folder/' + encodeURIComponent(folder) + '/video/youtube/start', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ url: url, title: titleInput.value }) }).then(function (r) { return r.json() }).then(function (j) { if (!j.ok) { probeInfo.textContent = j.message || '시작 실패' return } dlProgress.hidden = false probeInfo.textContent = '백그라운드 다운로드 시작...' pollJob(j.jobId, j.videoId) }) }) function pollJob(jobId, videoId) { fetch('/op/job/' + encodeURIComponent(jobId)).then(function (r) { return r.json() }).then(function (j) { if (!j.ok) { probeInfo.textContent = j.message || '작업을 찾을 수 없음' return } var job = j.job dlProgress.value = job.progress probeInfo.textContent = job.message if (job.status === 'done') { location.href = '/op/folder/' + encodeURIComponent(folder) + '/video/editor?id=' + encodeURIComponent(videoId) } else if (job.status === 'error') { probeInfo.textContent = '실패: ' + (job.error || '') } else { setTimeout(function () { pollJob(jobId, videoId) }, 1500) } }) } // 트림 컨트롤 - "현재 시점" 버튼 document.querySelectorAll('[data-set-current]').forEach(function (btn) { btn.addEventListener('click', function () { if (!editVideo) return var which = btn.getAttribute('data-set-current') var t = editVideo.currentTime.toFixed(2) if (which === 'start') startSec.value = t else endSec.value = t }) }) // 저장 saveBtn.addEventListener('click', function () { if (!video) { alert('먼저 영상을 추가하세요.'); return } var payload = { id: video.id, title: titleInput.value, startSec: Number(startSec.value || 0), endSec: endSec.value === '' ? null : Number(endSec.value) } saveBtn.disabled = true saveBtn.textContent = '저장 중...' fetch('/op/folder/' + encodeURIComponent(folder) + '/video/save', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload) }).then(function (r) { return r.json() }).then(function (j) { saveBtn.disabled = false saveBtn.textContent = '저장' if (j.ok) { alert(j.note || '저장 완료') location.href = '/op/folder/' + encodeURIComponent(folder) } else { alert(j.message || '저장 실패') } }) }) function formatDuration(sec) { if (!sec || sec <= 0) return '0초' var s = Math.round(sec) var h = Math.floor(s / 3600); s = s % 3600 var m = Math.floor(s / 60); s = s % 60 if (h > 0) return h + '시간 ' + m + '분 ' + s + '초' if (m > 0) return m + '분 ' + s + '초' return s + '초' } function formatSize(bytes) { if (bytes < 1024) return bytes + ' B' if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB' if (bytes < 1024 * 1024 * 1024) return (bytes / 1024 / 1024).toFixed(1) + ' MB' return (bytes / 1024 / 1024 / 1024).toFixed(2) + ' GB' } })()