feat(routes): add short /file/video/:id alias for video stream

외부 공유용으로 더 짧은 영상 파일 URL 을 제공:
  /file/video/:videoId   ← 신규 (영상 주소 복사 결과)
  /api/video/:videoId/file ← 호환용 alias 로 유지 (기존 링크 안 깨지게)
  /api/video/:videoId     ← 메타 JSON (변경 없음)

핸들러는 한 함수를 두 라우트에 공유. 우클릭 "영상 주소 복사" 가
이제 새 짧은 경로를 클립보드에 넣음.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-15 21:58:37 +09:00
parent 25a809546e
commit 59f96a12a6
3 changed files with 13 additions and 8 deletions

View File

@@ -60,7 +60,7 @@
}) })
function copyVideoUrl(id) { function copyVideoUrl(id) {
var url = location.origin + '/api/video/' + encodeURIComponent(id) + '/file' var url = location.origin + '/file/video/' + encodeURIComponent(id)
if (navigator.clipboard && navigator.clipboard.writeText) { if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(url).then(function () { navigator.clipboard.writeText(url).then(function () {
flashToast('주소가 복사되었습니다.') flashToast('주소가 복사되었습니다.')

View File

@@ -34,7 +34,7 @@
}) })
function copyVideoUrl(id) { function copyVideoUrl(id) {
var url = location.origin + '/api/video/' + encodeURIComponent(id) + '/file' var url = location.origin + '/file/video/' + encodeURIComponent(id)
if (navigator.clipboard && navigator.clipboard.writeText) { if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(url).then(function () { navigator.clipboard.writeText(url).then(function () {
flashToast('주소가 복사되었습니다.') flashToast('주소가 복사되었습니다.')

View File

@@ -1,4 +1,4 @@
import { Router } from 'express' import { Router, type RequestHandler } from 'express'
import path from 'node:path' import path from 'node:path'
import { promises as fs } from 'node:fs' import { promises as fs } from 'node:fs'
import { import {
@@ -57,16 +57,19 @@ publicRouter.get('/player/:videoId', async (req, res, next) => {
} }
}) })
/** 영상 파일 스트리밍. ?edited=1 이면 편집본을, 아니면 원본을 보낸다. */ /**
publicRouter.get('/api/video/:videoId/file', async (req, res, next) => { * 영상 파일 스트리밍.
* - 짧은 외부 공유용 경로: /file/video/:videoId
* - 기존 경로: /api/video/:videoId/file (호환용으로 유지)
* - ?edited=0 이면 원본을 강제하고, 기본은 편집본 있으면 편집본.
*/
const streamVideoHandler: RequestHandler = async (req, res, next) => {
try { try {
const found = await findVideoAnywhere(req.params.videoId) const found = await findVideoAnywhere(req.params.videoId)
if (!found) { if (!found) {
res.status(404).end() res.status(404).end()
return return
} }
// 기본 동작: 편집본(edited)이 있으면 그것을 재생한다. 원본을 강제로 보고 싶으면 ?edited=0.
// 명시적으로 ?edited=1 을 줘도 편집본이 있을 때만 적용된다.
const editedParam = typeof req.query.edited === 'string' ? req.query.edited : '' const editedParam = typeof req.query.edited === 'string' ? req.query.edited : ''
const wantOriginal = editedParam === '0' || editedParam === 'false' const wantOriginal = editedParam === '0' || editedParam === 'false'
const fileName = const fileName =
@@ -80,7 +83,9 @@ publicRouter.get('/api/video/:videoId/file', async (req, res, next) => {
} catch (err) { } catch (err) {
next(err) next(err)
} }
}) }
publicRouter.get('/file/video/:videoId', streamVideoHandler)
publicRouter.get('/api/video/:videoId/file', streamVideoHandler)
/** 비디오 메타 조회 (플레이어/관리자 양쪽에서 사용) */ /** 비디오 메타 조회 (플레이어/관리자 양쪽에서 사용) */
publicRouter.get('/api/video/:videoId', async (req, res, next) => { publicRouter.get('/api/video/:videoId', async (req, res, next) => {