feat: implement video site per README spec
- Express + EJS + express-session stack (auth/navbar ported from minecraft_launcher)
- Public: main folder list, folder video grid, internal popup player (/player/:videoId)
- Admin (/op): login, folder CRUD with right-click context menu + add-folder modal
- Admin folder: video grid with right-click edit/rename/delete, "영상 추가" -> editor
- Video editor: drag-drop upload, file picker, YouTube URL probe (ETA + 5분 경고),
background yt-dlp download with progress polling, navbar title edit, trim controls,
save runs ffmpeg trim (original preserved)
- Filesystem storage under data/folders/<name>/<videoId>/{meta.json, original.<ext>, edited.<ext>}
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
51
public/player.js
Normal file
51
public/player.js
Normal file
@@ -0,0 +1,51 @@
|
||||
(function () {
|
||||
var overlay = document.getElementById('playerOverlay')
|
||||
var closeBtn = document.getElementById('playerClose')
|
||||
var video = document.getElementById('playerVideo')
|
||||
var titleEl = document.getElementById('playerTitle')
|
||||
|
||||
function openPlayer(videoId, title) {
|
||||
// 스펙: /player/:videoId 로 이동한 것처럼 동작하면서 내부 팝업으로 띄운다.
|
||||
// pushState 로 URL 만 바꿔, 새로고침/직접접근 시 player.ejs 가 응답한다.
|
||||
history.pushState({ player: true, videoId: videoId }, '', '/player/' + encodeURIComponent(videoId))
|
||||
titleEl.textContent = title || ''
|
||||
video.src = '/api/video/' + encodeURIComponent(videoId) + '/file'
|
||||
overlay.hidden = false
|
||||
video.play().catch(function () { /* 자동재생 막힘 무시 */ })
|
||||
}
|
||||
|
||||
function closePlayer() {
|
||||
overlay.hidden = true
|
||||
video.pause()
|
||||
video.removeAttribute('src')
|
||||
video.load()
|
||||
// 폴더 페이지로 되돌리기
|
||||
if (history.state && history.state.player) {
|
||||
history.back()
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelectorAll('.videoCard').forEach(function (card) {
|
||||
card.addEventListener('click', function () {
|
||||
var id = card.getAttribute('data-video-id')
|
||||
var title = card.querySelector('.videoTitle')
|
||||
openPlayer(id, title ? title.textContent : '')
|
||||
})
|
||||
})
|
||||
|
||||
if (closeBtn) closeBtn.addEventListener('click', closePlayer)
|
||||
if (overlay) overlay.addEventListener('click', function (e) {
|
||||
if (e.target === overlay) closePlayer()
|
||||
})
|
||||
window.addEventListener('popstate', function () {
|
||||
if (!overlay.hidden) {
|
||||
overlay.hidden = true
|
||||
video.pause()
|
||||
video.removeAttribute('src')
|
||||
video.load()
|
||||
}
|
||||
})
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Escape' && !overlay.hidden) closePlayer()
|
||||
})
|
||||
})()
|
||||
Reference in New Issue
Block a user