ai-chatbot/lib/video-canvas/video-upscale.ts

63 lines
2.0 KiB
TypeScript

import { ProviderTaskUncertainError } from "./video-task-lifecycle";
import "server-only";
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { uploadFile } from "@/lib/storage/service";
import { videoUpscaleSchema } from "./video-upscale-contract";
import { remoteUpscale, type UpscaleCheckpoint } from "./video-upscale-remote";
export async function executeVideoUpscale(
params: Record<string, unknown>,
userId: string,
taskId: string,
lifecycle: {
resume?: UpscaleCheckpoint;
active: () => Promise<void>;
save: (value: Record<string, unknown>) => Promise<void>;
}
) {
const input = videoUpscaleSchema.parse(params),
dir = await mkdtemp(path.join(tmpdir(), "upscale-"));
try {
const output = await remoteUpscale(input, userId, taskId, dir, {
resume: lifecycle.resume,
active: lifecycle.active,
save: (remote, stage) => lifecycle.save({ upscale: { remote, stage } }),
});
await lifecycle.active();
const file = await readFile(output.file);
let stored;
try {
stored = await uploadFile(
`video-upscale-${taskId}.mp4`,
(file.byteOffset === 0 && file.byteLength === file.buffer.byteLength
? file.buffer
: file.buffer.slice(
file.byteOffset,
file.byteOffset + file.byteLength
)) as ArrayBuffer
);
} catch {
throw new ProviderTaskUncertainError(
"高清结果已生成,归档暂时失败,将继续归档同一结果"
);
}
return {
url: stored.url,
metadata: {
width: output.width,
height: output.height,
durationSeconds: output.duration,
fps: output.fps,
frames: output.frames,
hasAudio: output.audio,
engine: "Real-ESRGAN",
model: "realesr-general-x4v3",
resolution: input.resolution,
},
};
} finally {
await rm(dir, { recursive: true, force: true });
}
}