feat(folders): nested folder tree (max depth 2) + id-based mutations
- new storeDb.ts: DB+FS layer (folders w/ parent_id, videos w/ global id)
- depth cap: sub-folder cannot have children (enforced in createFolder)
- video id: editable anytime (changeVideoId), uniqueness via PK
- routes:
- public /folder/:topName[/:subName], /video/:topName[/:subName]/:videoId
- admin /op/folder/:topName[/:subName], id-based /op/folders/:id/*
and /op/videos/:id/* (rename/changeId/delete/save + idAvailable check)
- views: breadcrumb + subFolder grid; 플레이리스트 추가 button shows at 2단계
- client JS: id-based mutation endpoints, share URL data attribute,
in-site player pushes share URL to address bar
- store.ts slimmed to Account + readAccounts (FS layer moved to storeDb)
- pickIntId accepts numeric JSON bodies
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { spawn, spawnSync } from 'node:child_process'
|
||||
import { promises as fs } from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { videoDir, loadVideoMeta, saveVideoMeta, type VideoTrim } from './store.js'
|
||||
import { getVideo, updateVideo, videoDiskDir, type VideoTrim } from './storeDb.js'
|
||||
|
||||
export class FfmpegUnavailableError extends Error {
|
||||
constructor() {
|
||||
@@ -184,14 +184,13 @@ export async function upscaleOriginalTo60Fps(
|
||||
* stream copy 를 우선 시도해 빠르게 자르고, 실패하면 재인코딩.
|
||||
*/
|
||||
export async function applyTrimToVideo(
|
||||
folder: string,
|
||||
videoId: string,
|
||||
trim: VideoTrim
|
||||
): Promise<string> {
|
||||
const bin = getFfmpegPath()
|
||||
const meta = await loadVideoMeta(folder, videoId)
|
||||
const meta = getVideo(videoId)
|
||||
if (!meta) throw new Error('비디오를 찾을 수 없습니다.')
|
||||
const dir = videoDir(folder, videoId)
|
||||
const dir = videoDiskDir(videoId)
|
||||
const inputPath = path.join(dir, meta.originalFile)
|
||||
await fs.access(inputPath)
|
||||
|
||||
@@ -236,9 +235,10 @@ export async function applyTrimToVideo(
|
||||
}
|
||||
await fs.rename(tmpPath, outPath)
|
||||
|
||||
meta.editedFile = outName
|
||||
meta.trim = { startSec, endSec }
|
||||
await saveVideoMeta(folder, meta)
|
||||
updateVideo(videoId, {
|
||||
editedFile: outName,
|
||||
trim: { startSec, endSec }
|
||||
})
|
||||
return outName
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user