feat: 영상 ID 를 폴더 안에서만 유일하게 (UNIQUE(folder_id, id))
폴더가 다르면 같은 영상 ID(예: 1,2,3,4)를 재사용할 수 있도록 변경. ID 가 더 이상 전역 PK 가 아니므로 모든 영상 조회를 폴더 스코프로 전환: - db: videos PK→UNIQUE(folder_id,id) + 기존 DB 자동 마이그레이션 - storeDb: getVideo→getVideoInFolder(folderId,id) + getVideoByIdGlobal 폴백 - public/op/editor/youtube: 재생·썸네일·편집·삭제·rename 모두 폴더 스코프 - 썸네일을 경로기반 URL(/file/video/<폴더>/<id>/thumb)로 전환 - 클라이언트 mutation 요청에 folderId 동봉 Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
This commit is contained in:
@@ -56,7 +56,7 @@
|
||||
changeIdBtn.disabled = true
|
||||
clearTimeout(idDebounce)
|
||||
idDebounce = setTimeout(function () {
|
||||
fetch('/op/videos/idAvailable?id=' + encodeURIComponent(v), { cache: 'no-store' })
|
||||
fetch('/op/videos/idAvailable?id=' + encodeURIComponent(v) + '&folderId=' + encodeURIComponent(ctx.folderId), { cache: 'no-store' })
|
||||
.then(function (r) { return r.json() })
|
||||
.then(function (j) {
|
||||
// stale 응답 (사용자가 그 사이에 입력을 더 바꾼 경우) 무시
|
||||
@@ -87,7 +87,7 @@
|
||||
fetch('/op/videos/' + encodeURIComponent(video.id) + '/changeId', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({ newId: newId })
|
||||
body: JSON.stringify({ newId: newId, folderId: ctx.folderId })
|
||||
}).then(function (r) { return r.json() }).then(function (j) {
|
||||
if (!j.ok) {
|
||||
setIdStatus(j.message || 'ID 변경 실패', 'idStatusBad')
|
||||
@@ -464,7 +464,8 @@
|
||||
var payload = {
|
||||
title: titleInput.value,
|
||||
startSec: trimStart,
|
||||
endSec: trimEnd
|
||||
endSec: trimEnd,
|
||||
folderId: ctx.folderId
|
||||
}
|
||||
saveBtn.disabled = true
|
||||
saveBtn.textContent = '저장 중...'
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
fetch('/op/videos/' + encodeURIComponent(videoTargetId) + '/rename', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({ title: t })
|
||||
body: JSON.stringify({ title: t, folderId: op.folderId })
|
||||
}).then(function (r) { return r.json() }).then(function (j) {
|
||||
if (j.ok) location.reload()
|
||||
else alert(j.message || '이름 변경 실패')
|
||||
@@ -51,7 +51,7 @@
|
||||
fetch('/op/videos/' + encodeURIComponent(videoTargetId) + '/changeId', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({ newId: newId })
|
||||
body: JSON.stringify({ newId: newId, folderId: op.folderId })
|
||||
}).then(function (r) { return r.json() }).then(function (j) {
|
||||
if (j.ok) location.reload()
|
||||
else alert(j.message || 'ID 변경 실패')
|
||||
@@ -60,7 +60,9 @@
|
||||
} else if (action === 'delete') {
|
||||
if (window.confirm('"' + videoTargetTitle + '" 영상을 정말 삭제할까요?')) {
|
||||
fetch('/op/videos/' + encodeURIComponent(videoTargetId) + '/delete', {
|
||||
method: 'POST'
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({ folderId: op.folderId })
|
||||
}).then(function (r) { return r.json() }).then(function (j) {
|
||||
if (j.ok) location.reload()
|
||||
else alert(j.message || '삭제 실패')
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
entry.idDebounce = setTimeout(function () {
|
||||
var current = state.entries[idx]
|
||||
if (!current || current.videoId !== v) return
|
||||
fetch('/op/videos/idAvailable?id=' + encodeURIComponent(v), { cache: 'no-store' })
|
||||
fetch('/op/videos/idAvailable?id=' + encodeURIComponent(v) + '&folderId=' + encodeURIComponent(ctx.folderId), { cache: 'no-store' })
|
||||
.then(function (r) { return r.json() })
|
||||
.then(function (j) {
|
||||
var c = state.entries[idx]
|
||||
|
||||
Reference in New Issue
Block a user