mirror of
https://github.com/vibegif/vibegif.lol.git
synced 2026-04-07 02:12:12 +00:00
Delete: Merged into app.js IIFE
This commit is contained in:
@@ -1,137 +0,0 @@
|
|||||||
import { NEXT_FRAME_PROMPT_TEMPLATE } from "./config.js";
|
|
||||||
|
|
||||||
const OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions";
|
|
||||||
|
|
||||||
const SYSTEM_PROMPT = [
|
|
||||||
"You generate a single image per request for an animation pipeline.",
|
|
||||||
"Keep style consistency across frames.",
|
|
||||||
"No text overlays, no labels, no typography.",
|
|
||||||
"Output only the image.",
|
|
||||||
].join(" ");
|
|
||||||
|
|
||||||
function buildFramePrompt({
|
|
||||||
masterPrompt,
|
|
||||||
userPrompt,
|
|
||||||
frameCount,
|
|
||||||
frameIndex,
|
|
||||||
}) {
|
|
||||||
const lines = [
|
|
||||||
`Create frame ${frameIndex} of ${frameCount} for an animated GIF.`,
|
|
||||||
`Concept: ${userPrompt}.`,
|
|
||||||
`Master style lock: ${masterPrompt}.`,
|
|
||||||
"Use a white background and clean black line doodle style.",
|
|
||||||
"Keep the subject identity and scene composition coherent between frames.",
|
|
||||||
];
|
|
||||||
|
|
||||||
if (frameIndex === 1) {
|
|
||||||
lines.push("This is the first frame. Establish a clear starting pose.");
|
|
||||||
} else {
|
|
||||||
lines.push(NEXT_FRAME_PROMPT_TEMPLATE(frameCount));
|
|
||||||
lines.push(
|
|
||||||
`This is frame ${frameIndex}. Advance the motion naturally from the prior frame references.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
lines.push("Return one meaningful image only.");
|
|
||||||
return lines.join("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildUserContent(promptText, previousFrames = []) {
|
|
||||||
const parts = [{ type: "text", text: promptText }];
|
|
||||||
|
|
||||||
for (const frame of previousFrames.slice(-2)) {
|
|
||||||
parts.push({
|
|
||||||
type: "image_url",
|
|
||||||
image_url: { url: frame },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
function extractImageUrl(json) {
|
|
||||||
const message = json?.choices?.[0]?.message;
|
|
||||||
const image = message?.images?.[0];
|
|
||||||
return image?.image_url?.url || image?.imageUrl?.url || "";
|
|
||||||
}
|
|
||||||
|
|
||||||
function extractErrorMessage(text, status, fallbackStatusText) {
|
|
||||||
try {
|
|
||||||
const parsed = JSON.parse(text);
|
|
||||||
return (
|
|
||||||
parsed?.error?.message ||
|
|
||||||
parsed?.message ||
|
|
||||||
`Request failed (${status} ${fallbackStatusText})`
|
|
||||||
);
|
|
||||||
} catch {
|
|
||||||
return text || `Request failed (${status} ${fallbackStatusText})`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function requestFrameImage({
|
|
||||||
apiKey,
|
|
||||||
model,
|
|
||||||
userPrompt,
|
|
||||||
masterPrompt,
|
|
||||||
frameCount,
|
|
||||||
frameIndex,
|
|
||||||
previousFrames,
|
|
||||||
imageSize,
|
|
||||||
aspectRatio,
|
|
||||||
}) {
|
|
||||||
const promptText = buildFramePrompt({
|
|
||||||
masterPrompt,
|
|
||||||
userPrompt,
|
|
||||||
frameCount,
|
|
||||||
frameIndex,
|
|
||||||
});
|
|
||||||
|
|
||||||
const body = {
|
|
||||||
model,
|
|
||||||
stream: false,
|
|
||||||
modalities: ["image"],
|
|
||||||
messages: [
|
|
||||||
{ role: "system", content: SYSTEM_PROMPT },
|
|
||||||
{
|
|
||||||
role: "user",
|
|
||||||
content: buildUserContent(promptText, previousFrames),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
image_config: {
|
|
||||||
image_size: imageSize,
|
|
||||||
aspect_ratio: aspectRatio,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const headers = {
|
|
||||||
Authorization: `Bearer ${apiKey}`,
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
};
|
|
||||||
|
|
||||||
if (typeof window !== "undefined" && window.location?.origin?.startsWith("http")) {
|
|
||||||
headers["HTTP-Referer"] = window.location.origin;
|
|
||||||
headers["X-OpenRouter-Title"] = "vibegif.lol";
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await fetch(OPENROUTER_URL, {
|
|
||||||
method: "POST",
|
|
||||||
headers,
|
|
||||||
body: JSON.stringify(body),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const raw = await response.text();
|
|
||||||
throw new Error(
|
|
||||||
extractErrorMessage(raw, response.status, response.statusText)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const json = await response.json();
|
|
||||||
const imageUrl = extractImageUrl(json);
|
|
||||||
|
|
||||||
if (!imageUrl) {
|
|
||||||
throw new Error("No image returned by model. Try again with fewer frames or simpler prompt.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return imageUrl;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user