save changes

This commit is contained in:
2025-09-17 20:00:13 +02:00
parent 0521b28626
commit d68c44de99
3 changed files with 4006 additions and 550 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@ import puppeteer from 'puppeteer';
import { VideoModel } from './lib/db/video'; import { VideoModel } from './lib/db/video';
dotenv.config(); dotenv.config();
const RUN_ONCE = (process.env.RUN_ONCE || 'false').toLowerCase() === 'true';
const USE_REFERENCE_IMAGE = (process.env.USE_REFERENCE_IMAGE || 'true').toLowerCase() === 'true'; const USE_REFERENCE_IMAGE = (process.env.USE_REFERENCE_IMAGE || 'true').toLowerCase() === 'true';
@ -69,16 +70,15 @@ async function callOpenAIWithFileAndExtract(imagePath: string, prompt: string, m
return null; return null;
} }
const servers = [ const servers = [
{ {
baseUrl: process.env.SERVER1_COMFY_BASE_URL, baseUrl: process.env.SERVER1_COMFY_BASE_URL,
outputDir: process.env.SERVER1_COMFY_OUTPUT_DIR, outputDir: process.env.SERVER1_COMFY_OUTPUT_DIR,
}, },
/*
{ {
baseUrl: process.env.SERVER2_COMFY_BASE_URL, baseUrl: process.env.SERVER2_COMFY_BASE_URL,
outputDir: process.env.SERVER2_COMFY_OUTPUT_DIR, outputDir: process.env.SERVER2_COMFY_OUTPUT_DIR,
}, },
*/
].filter((s): s is { baseUrl: string; outputDir: string } => !!s.baseUrl && !!s.outputDir); ].filter((s): s is { baseUrl: string; outputDir: string } => !!s.baseUrl && !!s.outputDir);
interface GenerationTask { interface GenerationTask {
@ -135,6 +135,7 @@ Decide which single action type best fits this scene from the list:
- micro animation (animate object but small movement) - micro animation (animate object but small movement)
- big movement - big movement
- impossible movement - impossible movement
- Dance ( if its woman portrait )
Return exactly one JSON object and nothing else: { "actiontype": "..." }. Return exactly one JSON object and nothing else: { "actiontype": "..." }.
Do not add commentary. Choose the single best option from the list above. Do not add commentary. Choose the single best option from the list above.
@ -272,7 +273,7 @@ async function generateImageForTask(task: GenerationTask, server: { baseUrl: str
imageFileName, imageFileName,
baseUrl, baseUrl,
outputDir, outputDir,
{ width: 720, height: 1280 } { width: 1280, height: 720 }
); );
return generatedImagePath; return generatedImagePath;
} else { } else {
@ -283,7 +284,7 @@ async function generateImageForTask(task: GenerationTask, server: { baseUrl: str
baseUrl, baseUrl,
outputDir, outputDir,
'qwen', 'qwen',
{ width: 720, height: 1280 } { width: 1280, height: 720 }
); );
return generatedImagePath; return generatedImagePath;
} }
@ -379,7 +380,7 @@ async function getPinUrlFromPinterest(keyword: string): Promise<string | null> {
}); });
*/ */
allKeywords = allKeywords.filter(a => { allKeywords = allKeywords.filter(a => {
return (a.genre == "city") return (a.genre == "epic")
}); });
function shuffle<T>(arr: T[]): T[] { function shuffle<T>(arr: T[]): T[] {
@ -471,7 +472,7 @@ async function getPinUrlFromPinterest(keyword: string): Promise<string | null> {
} }
} }
const numberOfPinIds = 20; const numberOfPinIds = Number(process.env.NUMBER_OF_PINIDS) || 20;
// Build keywords list with single chosen pinId per selected subGenre // Build keywords list with single chosen pinId per selected subGenre
const keywords: { const keywords: {
genre: string; subGenre: string; pinIds: string[], videoInstructions?: string[] genre: string; subGenre: string; pinIds: string[], videoInstructions?: string[]
@ -493,45 +494,64 @@ async function getPinUrlFromPinterest(keyword: string): Promise<string | null> {
return; return;
} }
type pinIdsType = {
pinId: string,
genreSubGenre: { genre: string, subGenre: string, pinIds: string[], videoInstructions: string[] }
};
while (true) { while (true) {
const generationTasks: GenerationTask[] = []; const generationTasks: GenerationTask[] = [];
const allPinIds: pinIdsType[] = keywords.reduce<pinIdsType[]>((acc, curr) => {
const videoInstructions = curr.videoInstructions ?? [];
for (const id of curr.pinIds ?? []) {
acc.push({
pinId: id,
genreSubGenre: {
genre: curr.genre,
subGenre: curr.subGenre,
pinIds: curr.pinIds,
videoInstructions,
},
});
}
return acc;
}, []);
const pickedUpPinIds: pinIdsType[] = shuffle(allPinIds).slice(0, 30);
for (const genreSubGenre of keywords) { for (const row of pickedUpPinIds) {
const { genre, subGenre } = genreSubGenre; const { genreSubGenre, pinId } = row;
const genre = genreSubGenre.genre;
const subGenre = genreSubGenre.subGenre;
const pickedUpPinIds = shuffle<string>(genreSubGenre.pinIds).slice(0, 2); const pin = `https://www.pinterest.com/pin/${pinId}/`;
logger.info(`--- Starting processing for pin: ${pin} ---`);
for (const pinId of pickedUpPinIds) { // download images from the pin page (pass desired count as second arg)
const downloadedImagePaths = await downloadOneImageFromPin(pin, 20);
if (!downloadedImagePaths || downloadedImagePaths.length === 0) {
logger.warn(`No images were downloaded for pin ${pin}. Skipping.`);
continue;
}
const pin = `https://www.pinterest.com/pin/${pinId}/`; const selectedImages = downloadedImagePaths.sort(() => 0.5 - Math.random()).slice(0, 2);
logger.info(`--- Starting processing for pin: ${pin} ---`); logger.info(`--- Downloaded ${selectedImages.length} image(s) for processing ---`);
// download images from the pin page (pass desired count as second arg) // proceed if we have at least one image
const downloadedImagePaths = await downloadOneImageFromPin(pin, 20); if (selectedImages.length >= 1) {
if (!downloadedImagePaths || downloadedImagePaths.length === 0) { const task = await getPromptsForImage(selectedImages, pin, genre, subGenre, genreSubGenre.videoInstructions);
logger.warn(`No images were downloaded for pin ${pin}. Skipping.`); if (task) {
continue; task.videoInstructions = genreSubGenre.videoInstructions;
generationTasks.push(task);
} }
} else {
const selectedImages = downloadedImagePaths.sort(() => 0.5 - Math.random()).slice(0, 2); logger.warn(`Skipping pin ${pin} as it did not yield images.`);
logger.info(`--- Downloaded ${selectedImages.length} image(s) for processing ---`); for (const imagePath of selectedImages) {
try {
// proceed if we have at least one image await fs.unlink(imagePath);
if (selectedImages.length >= 1) { } catch (error) {
const task = await getPromptsForImage(selectedImages, pin, genre, subGenre, genreSubGenre.videoInstructions); logger.error(`Failed to delete image ${imagePath}:`, error);
if (task) {
task.videoInstructions = genreSubGenre.videoInstructions;
generationTasks.push(task);
}
} else {
logger.warn(`Skipping pin ${pin} as it did not yield images.`);
for (const imagePath of selectedImages) {
try {
await fs.unlink(imagePath);
} catch (error) {
logger.error(`Failed to delete image ${imagePath}:`, error);
}
} }
} }
} }
@ -572,7 +592,7 @@ async function getPinUrlFromPinterest(keyword: string): Promise<string | null> {
videoFileName, videoFileName,
server.baseUrl, server.baseUrl,
server.outputDir, server.outputDir,
{ width: 720, height: 1280 } { width: 1280, height: 720 }
); );
if (videoPath) { if (videoPath) {
@ -618,5 +638,9 @@ async function getPinUrlFromPinterest(keyword: string): Promise<string | null> {
} }
logger.info("--- Finished video generation ---"); logger.info("--- Finished video generation ---");
if (RUN_ONCE) {
logger.info('RUN_ONCE=true - exiting after a single iteration of generation.');
return;
}
} }
})(); })();