150 lines
5.6 KiB
TypeScript
150 lines
5.6 KiB
TypeScript
import { downloadPinterestImages } from './lib/downloader';
|
|
import { describeImage } from './lib/image-describer';
|
|
import { generateImage } from './lib/image-generator';
|
|
import { generateVideo } from './lib/video-generator';
|
|
import { logger } from './lib/logger';
|
|
import * as fs from 'fs/promises';
|
|
import dotenv from 'dotenv';
|
|
|
|
dotenv.config();
|
|
|
|
const COMFY_BASE_URL = process.env.COMFY_BASE_URL1 || "";
|
|
const COMFY_OUTPUT_DIR = process.env.COMFY_OUTPUT_DIR1 || "";
|
|
|
|
async function prepareImageForKeyword(keyword: string): Promise<{ keyword: string; generatedImagePath: string } | null> {
|
|
const numberOfPages = 1;
|
|
const imagePaths = await downloadPinterestImages(keyword, numberOfPages);
|
|
logger.debug(`Downloaded ${imagePaths.length} images for keyword: ${keyword}`);
|
|
|
|
if (imagePaths.length === 0) {
|
|
logger.warn(`No images downloaded for keyword "${keyword}", cannot proceed.`);
|
|
return null;
|
|
}
|
|
|
|
const randomImagePath = imagePaths[Math.floor(Math.random() * imagePaths.length)];
|
|
logger.debug(`Randomly selected image for "${keyword}": ${randomImagePath}`);
|
|
|
|
// Delete all other images
|
|
for (const imagePath of imagePaths) {
|
|
if (imagePath !== randomImagePath) {
|
|
try {
|
|
await fs.unlink(imagePath);
|
|
logger.debug(`Deleted image: ${imagePath}`);
|
|
} catch (error) {
|
|
logger.error(`Failed to delete image ${imagePath}:`, error);
|
|
}
|
|
}
|
|
}
|
|
|
|
try {
|
|
const imagePromptResponse = await describeImage(randomImagePath,
|
|
`Describe this image as a prompt for an image generation model.
|
|
Prompt should be in 200 words.
|
|
Output should be in this format
|
|
---
|
|
{
|
|
"prompt":"Ultra detialed realistic photo, {result comes here}"
|
|
}
|
|
---
|
|
`);
|
|
const imagePrompt = imagePromptResponse.prompt;
|
|
logger.info(`Description for ${randomImagePath}:`, imagePrompt);
|
|
|
|
const timestamp = new Date().getTime();
|
|
const imageFileName = `${keyword.replace(/\s/g, '_')}_${timestamp}.png`;
|
|
const generatedImagePath = await generateImage(
|
|
imagePrompt,
|
|
imageFileName,
|
|
COMFY_BASE_URL,
|
|
COMFY_OUTPUT_DIR,
|
|
'flux',
|
|
{ width: 720, height: 1280 }
|
|
);
|
|
logger.debug(`Generated new image from prompt, saved to: ${generatedImagePath}`);
|
|
return { keyword, generatedImagePath };
|
|
} catch (error) {
|
|
logger.error(`Failed to process ${randomImagePath} for keyword "${keyword}":`, error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function generateVideoFromImagePath(keyword: string, generatedImagePath: string) {
|
|
try {
|
|
const videoPromptResponse = await describeImage(generatedImagePath,
|
|
`Generate a prompt for an 8-second video based on the provided image.
|
|
The prompt should describe a dynamic scene that evolves from the static image.
|
|
Output should be in this format
|
|
---
|
|
{
|
|
"prompt":""
|
|
}
|
|
---
|
|
`);
|
|
const videoPrompt = videoPromptResponse.prompt;
|
|
logger.info(`Generated video prompt for ${generatedImagePath}: ${videoPrompt}`);
|
|
|
|
const timestamp = new Date().getTime();
|
|
const videoFileName = `${keyword.replace(/\s/g, '_')}_${timestamp}.mp4`;
|
|
const generatedVideoPath = await generateVideo(
|
|
videoPrompt,
|
|
generatedImagePath,
|
|
videoFileName,
|
|
COMFY_BASE_URL,
|
|
COMFY_OUTPUT_DIR,
|
|
{ width: 720, height: 1280 }
|
|
);
|
|
logger.debug(`Generated video from prompt, saved to: ${generatedVideoPath}`);
|
|
} catch (error) {
|
|
logger.error(`Failed to generate video for ${generatedImagePath}:`, error);
|
|
}
|
|
}
|
|
|
|
|
|
(async () => {
|
|
const keywords = [
|
|
"beautiful fantasy scene",
|
|
"AI art",
|
|
"beautiful scifi scene",
|
|
"illumunate scene",
|
|
"night aesthetic",
|
|
"stars aesthetic",
|
|
"letro woman",
|
|
"magic aesthetic",
|
|
"fire aesthetic",
|
|
"high fashion outfits",
|
|
"led fashion",
|
|
"halloween",
|
|
"horror aethstetic",
|
|
"1890s fashion women"
|
|
];
|
|
|
|
while (1) {
|
|
const preparedImages = [];
|
|
logger.info('--- Starting image preparation phase ---');
|
|
for (const keyword of keywords) {
|
|
const startTime = Date.now();
|
|
logger.info(`--- Preparing image for keyword: ${keyword} ---`);
|
|
const result = await prepareImageForKeyword(keyword);
|
|
if (result) {
|
|
preparedImages.push(result);
|
|
}
|
|
const endTime = Date.now();
|
|
const durationInSeconds = (endTime - startTime) / 1000;
|
|
logger.info(`--- Finished preparing image for keyword: ${keyword} in ${durationInSeconds.toFixed(2)} seconds ---`);
|
|
}
|
|
logger.info('--- Finished image preparation phase ---');
|
|
|
|
logger.info('--- Starting video generation phase ---');
|
|
for (const { keyword, generatedImagePath } of preparedImages) {
|
|
const startTime = Date.now();
|
|
logger.info(`--- Generating video for keyword: ${keyword} ---`);
|
|
await generateVideoFromImagePath(keyword, generatedImagePath);
|
|
const endTime = Date.now();
|
|
const durationInSeconds = (endTime - startTime) / 1000;
|
|
logger.info(`--- Finished generating video for keyword: ${keyword} in ${durationInSeconds.toFixed(2)} seconds ---`);
|
|
}
|
|
logger.info('--- Finished video generation phase ---');
|
|
}
|
|
|
|
})();
|