Separate Studio 2 source and canvas and apply size and seed choices
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { validateRequest } from '~/shared/studio2/contracts.mjs';
|
||||
import { resolveRequestSize } from '../../utils/studio2/size';
|
||||
import { parsePostedLoraStack, assertImageV2LoraStack } from '../../utils/loras';
|
||||
import { saveRecord, readRecord } from '../../utils/studio2/store';
|
||||
import { addStudioJob, kickStudioQueue, type StudioJobPayload } from '../../utils/studioQueue';
|
||||
@@ -22,10 +23,11 @@ export default defineEventHandler(async (event) => {
|
||||
assertFolderAccess(event, getStill(owner, id).folderId);
|
||||
if (request.startClipId)
|
||||
assertFolderAccess(event, getClip(owner, request.startClipId).folderId);
|
||||
await resolveRequestSize(owner,request);
|
||||
const id = crypto.randomUUID(), record = { id, owner, request, state: 'waiting', queuedAt: Date.now(), familyId: crypto.randomUUID(), outputs: [], purgeResult: 'Not yet saved' };
|
||||
saveRecord(record);
|
||||
const row = await addStudioJob({ ownerKey: owner, kind: ['video', 'extend'].includes(request.mode) ? 'video' : 'edit', familyId: record.familyId, payload: { ...request.settings, studio2Id: id, prompt: request.compiledPrompt, folderId: request.folderId, extensions: [], referenceStillIds: [], useIdentityRefs: false, queueAutoRun: true } as StudioJobPayload });
|
||||
saveRecord({ ...readRecord(owner, id), queueId: row.id });
|
||||
kickStudioQueue();
|
||||
return { id };
|
||||
return { id, seed:request.settings.seed, width:request.settings.width, height:request.settings.height };
|
||||
});
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { watchProgress } from './progress';
|
||||
import { fitStill } from './media';
|
||||
import { resolveSize } from '~/shared/studio2/input.mjs';
|
||||
import { resolveRequestSize } from './size';
|
||||
import { queueSeeds } from '~/shared/studio2/seed.mjs';
|
||||
import { readFileSync, mkdirSync, existsSync, unlinkSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import template from '../../assets/studio2_minimax_native.json';
|
||||
import { nativeVideoGraph, attachHeroReference } from '~/shared/studio2/graphs.mjs';
|
||||
import { nativeVideoGraph, attachHeroReference, applyResolvedImageSize } from '~/shared/studio2/graphs.mjs';
|
||||
import { compilePrompt, scopedFile } from '~/shared/studio2/contracts.mjs';
|
||||
import { createJob, restoreJob, getJob, emitJob, type Job } from '../jobs';
|
||||
import { markStudioLive, onLiveVideoSettled, type StudioJob } from '../studioQueue';
|
||||
@@ -65,8 +66,7 @@ async function purge(r: any) {
|
||||
async function prepareGraph(r: any) {
|
||||
const q = r.request, s = q.settings, prefix = String(useRuntimeConfig().comfyFilenamePrefix).replace(/\/$/, '') + `/studio2/${r.id}/${r.index}`;
|
||||
const load = async (id: string, label: string) => id ? upload(r, label, readFileSync(stillPath(r.owner, getStill(r.owner, id).id))) : '';
|
||||
const source = q.startClipId ? getClip(r.owner,q.startClipId) : (q.imageAId || q.identityStillId) ? getStill(r.owner,q.imageAId || q.identityStillId) : null;
|
||||
Object.assign(s,resolveSize(s.aspect,source,s));
|
||||
await resolveRequestSize(r.owner,q);
|
||||
saveRecord(r);
|
||||
const hero = q.lockFace === false && q.lockOutfit === false ? '' : await load(q.identityStillId, 'hero'), a = await load(q.imageAId, 'source'), b = await load(q.imageBId, 'compose'), mask = await load(q.maskId, 'mask');
|
||||
let graph: any;
|
||||
@@ -96,6 +96,7 @@ async function prepareGraph(r: any) {
|
||||
const found = q.engine === 'krea' ? await resolveKreaGenerateAssets() : null;
|
||||
const assets = found ? { kreaUnetName: found.unet, kreaClipName: found.clip, kreaVaeName: found.vae, kreaConceptLora: found.conceptLora } : {};
|
||||
graph = buildImageV2Workflow({ ...s, ...assets, engine: q.engine, mode, task: 'scene', prompt: q.compiledPrompt, imageAName: a, imageBName: mode === 'compose' ? b : undefined, maskName: mode === 'refine' ? mask : undefined, filenamePrefix: prefix + '/image' }).graph;
|
||||
applyResolvedImageSize(graph,s);
|
||||
if (q.engine === 'flux')
|
||||
attachHeroReference(graph, hero);
|
||||
else if (hero && ['edit', 'compose', 'iterate'].includes(q.mode))
|
||||
@@ -112,6 +113,7 @@ async function run(r: any, job: Job) {
|
||||
try {
|
||||
const prompts = r.prompts || [r.request.promptSections, ...r.request.batch];
|
||||
r.prompts = prompts;
|
||||
r.request.shotSeeds ||= queueSeeds(r.request.settings,prompts.length);
|
||||
saveRecord(r);
|
||||
for (r.index = r.index || 0; r.index < prompts.length; r.index++) {
|
||||
if (job.status === 'cancelled')
|
||||
@@ -122,6 +124,7 @@ async function run(r: any, job: Job) {
|
||||
throw new Error('Cancelled');
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
}
|
||||
r.request.settings.seed = r.request.shotSeeds[r.index];
|
||||
r.files = [];
|
||||
r.progress = null;
|
||||
r.request.promptSections = prompts[r.index];
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { execFile } from 'node:child_process'
|
||||
import { promisify } from 'node:util'
|
||||
import { getClip, getStill, clipVideoPath, stillPath } from '../library'
|
||||
import { imageDimensions } from '../resolution'
|
||||
import { resolveSize } from '~/shared/studio2/input.mjs'
|
||||
const exec = promisify(execFile)
|
||||
export async function resolveRequestSize(owner: string, request: any) {
|
||||
const video=['video','extend'].includes(request.mode)
|
||||
let dimensions:any
|
||||
if (video && request.startClipId) {
|
||||
getClip(owner,request.startClipId)
|
||||
const result=await exec('ffprobe',['-v','error','-select_streams','v:0','-show_entries','stream=width,height','-of','json',clipVideoPath(owner,request.startClipId)],{windowsHide:true,timeout:15000})
|
||||
dimensions=JSON.parse(result.stdout).streams?.[0]
|
||||
} else {
|
||||
const id=request.imageAId || request.identityStillId
|
||||
if (id) {
|
||||
getStill(owner,id)
|
||||
dimensions=imageDimensions(readFileSync(stillPath(owner,id)))
|
||||
if (!dimensions) throw new Error('Cannot measure the source still.')
|
||||
}
|
||||
}
|
||||
if (video && request.startClipId && !dimensions?.width) throw new Error('Cannot measure the previous clip.')
|
||||
Object.assign(request.settings,resolveSize(request.settings.aspect,dimensions))
|
||||
}
|
||||
Reference in New Issue
Block a user