Keep Studio 2 family tiles tied to existing library files
This commit is contained in:
@@ -0,0 +1,15 @@
|
|||||||
|
import { records, saveRecord } from '../../utils/studio2/store';
|
||||||
|
import { listStudioJobs } from '../../utils/studioQueue';
|
||||||
|
export default defineEventHandler(event => {
|
||||||
|
const { owner } = assertLibraryOwner(event), queue=listStudioJobs(owner);
|
||||||
|
let cleared=0;
|
||||||
|
for (const r of records(owner)) {
|
||||||
|
try { assertFolderAccess(event,r.request.folderId); } catch { continue; }
|
||||||
|
const status=queue.find(q=>q.id===r.queueId)?.status;
|
||||||
|
if (['complete','failed','cancelled'].includes(r.state) || ['cancelled','error'].includes(status || '')) {
|
||||||
|
r.dismissed=true; saveRecord(r); cleared++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Retain internal manifests for cleanup retries; remove these records from the visible jobs list.
|
||||||
|
return {cleared};
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { records } from '../../utils/studio2/store';
|
import { records } from '../../utils/studio2/store';
|
||||||
import { listStudioJobs } from '../../utils/studioQueue';
|
import { listStudioJobs } from '../../utils/studioQueue';
|
||||||
export default defineEventHandler(event => { const { owner } = assertLibraryOwner(event); const rows = listStudioJobs(owner); return records(owner).filter(r => { try {
|
export default defineEventHandler(event => { const { owner } = assertLibraryOwner(event); const rows = listStudioJobs(owner); return records(owner).filter(r => !r.dismissed).filter(r => { try {
|
||||||
assertFolderAccess(event, r.request.folderId);
|
assertFolderAccess(event, r.request.folderId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { existsSync } from 'node:fs';
|
||||||
|
import { publicLibrary, stillPath, clipVideoPath } from '../../utils/library';
|
||||||
|
export default defineEventHandler(event => {
|
||||||
|
const { owner } = assertLibraryOwner(event);
|
||||||
|
const library = publicLibrary(event);
|
||||||
|
return {...library, stills:library.stills.filter(s=>existsSync(stillPath(owner,s.id))), clips:library.clips.filter(c=>existsSync(clipVideoPath(owner,c.id)))};
|
||||||
|
});
|
||||||
@@ -1,14 +1,12 @@
|
|||||||
export function folderAssets(library, jobs, folderId) {
|
export function folderAssets(library, jobs, folderId) {
|
||||||
const assets=new Map()
|
const metadata=new Map(jobs.flatMap(j=>j.outputs || []).map(a=>[a.id,a.studio2]))
|
||||||
for (const kind of ['still','clip']) for (const asset of library[kind==='still'?'stills':'clips'] || []) {
|
return ['still','clip'].flatMap(kind=>(library[kind==='still'?'stills':'clips'] || []).filter(a=>a.id && a.folderId===folderId && a.hasVideo!==false && a.available!==false).map(a=>({...a,kind,studio2:a.studio2 || metadata.get(a.id)}))).sort((a,b)=>(a.createdAt || a.studio2?.savedAt || 0)-(b.createdAt || b.studio2?.savedAt || 0))
|
||||||
if (asset.folderId===folderId) assets.set(asset.id,{...asset,kind})
|
|
||||||
}
|
|
||||||
for (const job of jobs) if (job.request.folderId===folderId) for (const asset of job.outputs || []) assets.set(asset.id,{...assets.get(asset.id),...asset})
|
|
||||||
return [...assets.values()].sort((a,b)=>(a.createdAt || a.studio2?.savedAt || 0)-(b.createdAt || b.studio2?.savedAt || 0))
|
|
||||||
}
|
}
|
||||||
export function newestOutput(jobs, seen, folderId) {
|
export function newestOutput(jobs, seen, folderId) {
|
||||||
const fresh=jobs.filter(j=>j.request.folderId===folderId).flatMap(j=>j.outputs || []).filter(a=>!seen.has(a.id))
|
return jobs.filter(j=>j.request.folderId===folderId).flatMap(j=>j.outputs || []).filter(a=>!seen.has(a.id)).sort((a,b)=>(a.studio2?.savedAt || 0)-(b.studio2?.savedAt || 0)).at(-1) || null
|
||||||
return fresh.sort((a,b)=>(a.studio2?.savedAt || 0)-(b.studio2?.savedAt || 0)).at(-1) || null
|
|
||||||
}
|
}
|
||||||
|
export function reconcileSelection(selected, assets) { return assets.find(a=>a.id===selected?.id) || assets.at(-1) || null }
|
||||||
|
export function activeJobs(jobs) { return jobs.filter(j=>['waking','submitting','rendering','saving'].includes(j.state)) }
|
||||||
|
export function previewHidden(asset, xaigen, hideInput, hideOutput) { return !!xaigen && (asset?.role==='input' ? !!hideInput : !!hideOutput) }
|
||||||
export function selectResult(state, asset) { return {...state,selected:asset} }
|
export function selectResult(state, asset) { return {...state,selected:asset} }
|
||||||
export function assetLabel(asset) { return asset?.name || asset?.originalName || (asset ? `Still ${asset.id.slice(0,8)}` : 'None') }
|
export function assetLabel(asset) { return asset?.name || asset?.originalName || asset?.filename || (asset ? (asset.kind==='clip'?'Clip ':'Still ')+asset.id.slice(0,8) : 'None') }
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {applyResolvedImageSize} from '../shared/studio2/graphs.mjs'
|
|||||||
const base={mode:'generate',engine:'flux',folderId:'f',prompt:'Walk'}
|
const base={mode:'generate',engine:'flux',folderId:'f',prompt:'Walk'}
|
||||||
test('completed outputs join all folder assets and select only the output canvas',()=>{
|
test('completed outputs join all folder assets and select only the output canvas',()=>{
|
||||||
const source={id:'source',folderId:'f'},output={id:'result',kind:'still',studio2:{savedAt:2}}
|
const source={id:'source',folderId:'f'},output={id:'result',kind:'still',studio2:{savedAt:2}}
|
||||||
const jobs=[{request:{folderId:'f'},outputs:[output]}],library={stills:[source,{id:'other',folderId:'f'}],clips:[{id:'video',folderId:'f'}]}
|
const jobs=[{request:{folderId:'f'},outputs:[output]}],library={stills:[source,{id:'other',folderId:'f'},{id:'result',folderId:'f'}],clips:[{id:'video',folderId:'f'}]}
|
||||||
const strip=folderAssets(library,jobs,'f');assert.equal(strip.length,4)
|
const strip=folderAssets(library,jobs,'f');assert.equal(strip.length,4)
|
||||||
const state=selectResult({imageAId:'source',startClipId:'existing'},newestOutput(jobs,new Set(),'f'))
|
const state=selectResult({imageAId:'source',startClipId:'existing'},newestOutput(jobs,new Set(),'f'))
|
||||||
assert.equal(state.selected.id,'result');assert.equal(state.imageAId,'source');assert.equal(state.startClipId,'existing')
|
assert.equal(state.selected.id,'result');assert.equal(state.imageAId,'source');assert.equal(state.startClipId,'existing')
|
||||||
|
|||||||
Reference in New Issue
Block a user