71 lines
2.0 KiB
TypeScript
71 lines
2.0 KiB
TypeScript
import OpenAI from "openai";
|
|
import * as fs from "fs";
|
|
import * as path from "path";
|
|
import { StoryConfig } from "./config";
|
|
|
|
let openaiClient: OpenAI | null = null;
|
|
function getOpenAI(): OpenAI {
|
|
if (!openaiClient) {
|
|
openaiClient = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
|
}
|
|
return openaiClient;
|
|
}
|
|
|
|
const allowedSizesValues = [
|
|
"256x256",
|
|
"512x512",
|
|
"1024x1024",
|
|
"1536x1024",
|
|
"1024x1536",
|
|
"1792x1024",
|
|
"1024x1792",
|
|
] as const;
|
|
type AllowedSize = (typeof allowedSizesValues)[number];
|
|
|
|
function pickImageSize(resolution?: string): AllowedSize {
|
|
if (!resolution) return "1024x1024";
|
|
const match = resolution.match(/^(\d+)x(\d+)$/);
|
|
if (!match) return "1024x1024";
|
|
const width = parseInt(match[1], 10);
|
|
const height = parseInt(match[2], 10);
|
|
if (!Number.isFinite(width) || !Number.isFinite(height)) return "1024x1024";
|
|
if (width === height) return "1024x1024";
|
|
const landscapeCandidates: AllowedSize[] = ["1536x1024", "1792x1024"];
|
|
const portraitCandidates: AllowedSize[] = ["1024x1536", "1024x1792"];
|
|
return width > height ? landscapeCandidates[0] : portraitCandidates[0];
|
|
}
|
|
|
|
export async function generateImage(
|
|
storyName: string,
|
|
storyConfig: StoryConfig,
|
|
chunk: string,
|
|
chunkIndex: number,
|
|
imageIndex: number
|
|
): Promise<string> {
|
|
const imagePath = path.join("stories", storyName, "images", `chunk_${chunkIndex}_img${imageIndex}.png`);
|
|
const prompt = `${(storyConfig.config.image_style_prompts || "").trim()}
|
|
|
|
Illustration for the following passage:
|
|
"${chunk.slice(0, 500)}"`;
|
|
|
|
const size = pickImageSize(storyConfig.config.export_settings?.resolution);
|
|
|
|
const response = await getOpenAI().images.generate({
|
|
model: "dall-e-3",
|
|
prompt,
|
|
n: 1,
|
|
size,
|
|
response_format: "b64_json",
|
|
});
|
|
|
|
if (!response.data?.[0].b64_json) {
|
|
throw new Error("Image data not found in response");
|
|
}
|
|
|
|
const imageBase64 = response.data[0].b64_json;
|
|
const imageBuffer = Buffer.from(imageBase64, "base64");
|
|
fs.writeFileSync(imagePath, imageBuffer);
|
|
|
|
return imagePath;
|
|
}
|