Group extension chains in the library and add shot-script queues.

Keep every saved segment, show the longest clip with an expand list, branch on extend, and parse shot 2/3 blocks into a shared-duration queue with live part status.
This commit is contained in:
2026-08-26 08:03:41 -05:00
parent d78c3fee7d
commit db8c9aabba
9 changed files with 375 additions and 89 deletions
+23 -2
View File
@@ -34,6 +34,10 @@ export interface LibraryClip {
fps?: number
samplerName?: string
scheduler?: string
duration?: number
familyId?: string
parentClipId?: string
chainIndex?: number
}
export interface LibraryStill {
@@ -800,6 +804,10 @@ export async function saveClip(params: {
fps?: number
samplerName?: string
scheduler?: string
duration?: number
familyId?: string
parentClipId?: string
chainIndex?: number
}) {
const catalog = readCatalog(params.ownerKey)
const folder = catalog.folders.find(item => item.id === params.folderId) || catalog.folders[0]
@@ -821,10 +829,23 @@ export async function saveClip(params: {
cfg: params.cfg,
fps: params.fps,
samplerName: params.samplerName,
scheduler: params.scheduler
scheduler: params.scheduler,
familyId: params.familyId,
parentClipId: params.parentClipId,
chainIndex: params.chainIndex
}
mkdirSync(clipDir(params.ownerKey, clip.id), { recursive: true })
writeFileSync(clipVideoPath(params.ownerKey, clip.id), params.video)
const videoPath = clipVideoPath(params.ownerKey, clip.id)
writeFileSync(videoPath, params.video)
if (typeof params.duration === 'number' && params.duration > 0) {
clip.duration = params.duration
} else {
try {
clip.duration = await probeDuration(videoPath)
} catch {
/* duration stays unset */
}
}
if (params.thumb?.length) {
writeFileSync(join(clipDir(params.ownerKey, clip.id), 'thumb'), params.thumb)
} else {