import { openSync, readSync, closeSync } 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) function readImageHeader(path: string, maxBytes = 65536) { const fd = openSync(path, 'r') try { const buf = Buffer.alloc(maxBytes) const n = readSync(fd, buf, 0, maxBytes, 0) return buf.subarray(0, n) } finally { closeSync(fd) } } export async function resolveRequestSize(owner: string, request: any) { const video = ['video', 'extend'].includes(request.mode) let dimensions: any if (video && request.startClipId) { const clip = getClip(owner, request.startClipId) if (clip.width && clip.height) { dimensions = { width: clip.width, height: clip.height } } else { 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) { const still = getStill(owner, id) if (still.width && still.height) { dimensions = { width: still.width, height: still.height } } else { // Header only — never read the full archive PNG into memory on Generate click dimensions = imageDimensions(readImageHeader(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, undefined, request.engine)) }