Compare commits

...

2 Commits

Author SHA1 Message Date
c7279b4e8b Merge branch 'master' of https://git.yasue.org/ken/RandomVideoMaker 2025-09-17 20:00:19 +02:00
d68c44de99 save changes 2025-09-17 20:00:13 +02:00
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';
dotenv.config();
const RUN_ONCE = (process.env.RUN_ONCE || 'false').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;
}
const servers = [
{
baseUrl: process.env.SERVER1_COMFY_BASE_URL,
outputDir: process.env.SERVER1_COMFY_OUTPUT_DIR,
},
/*
{
baseUrl: process.env.SERVER2_COMFY_BASE_URL,
outputDir: process.env.SERVER2_COMFY_OUTPUT_DIR,
},
*/
].filter((s): s is { baseUrl: string; outputDir: string } => !!s.baseUrl && !!s.outputDir);
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)
- big movement
- impossible movement
- Dance ( if its woman portrait )
Return exactly one JSON object and nothing else: { "actiontype": "..." }.
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,
baseUrl,
outputDir,
{ width: 720, height: 1280 }
{ width: 1280, height: 720 }
);
return generatedImagePath;
} else {
@ -283,7 +284,7 @@ async function generateImageForTask(task: GenerationTask, server: { baseUrl: str
baseUrl,
outputDir,
'qwen',
{ width: 720, height: 1280 }
{ width: 1280, height: 720 }
);
return generatedImagePath;
}
@ -379,7 +380,7 @@ async function getPinUrlFromPinterest(keyword: string): Promise<string | null> {
});
*/
allKeywords = allKeywords.filter(a => {
return (a.genre == "city")
return (a.genre == "epic")
});
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
const keywords: {
genre: string; subGenre: string; pinIds: string[], videoInstructions?: string[]
@ -493,45 +494,64 @@ async function getPinUrlFromPinterest(keyword: string): Promise<string | null> {
return;
}
type pinIdsType = {
pinId: string,
genreSubGenre: { genre: string, subGenre: string, pinIds: string[], videoInstructions: string[] }
};
while (true) {
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}/`;
logger.info(`--- Starting processing for pin: ${pin} ---`);
const selectedImages = downloadedImagePaths.sort(() => 0.5 - Math.random()).slice(0, 2);
logger.info(`--- Downloaded ${selectedImages.length} image(s) for processing ---`);
// 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;
// proceed if we have at least one image
if (selectedImages.length >= 1) {
const task = await getPromptsForImage(selectedImages, pin, genre, subGenre, genreSubGenre.videoInstructions);
if (task) {
task.videoInstructions = genreSubGenre.videoInstructions;
generationTasks.push(task);
}
const selectedImages = downloadedImagePaths.sort(() => 0.5 - Math.random()).slice(0, 2);
logger.info(`--- Downloaded ${selectedImages.length} image(s) for processing ---`);
// proceed if we have at least one image
if (selectedImages.length >= 1) {
const task = await getPromptsForImage(selectedImages, pin, genre, subGenre, genreSubGenre.videoInstructions);
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);
}
} 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,
server.baseUrl,
server.outputDir,
{ width: 720, height: 1280 }
{ width: 1280, height: 720 }
);
if (videoPath) {
@ -618,5 +638,9 @@ async function getPinUrlFromPinterest(keyword: string): Promise<string | null> {
}
logger.info("--- Finished video generation ---");
if (RUN_ONCE) {
logger.info('RUN_ONCE=true - exiting after a single iteration of generation.');
return;
}
}
})();