save changes

This commit is contained in:
2025-08-29 11:21:46 +02:00
parent 153e50a356
commit 62e22e4965
3 changed files with 11058 additions and 54 deletions

View File

@ -43,7 +43,7 @@ async function getPromptsForImage(imagePaths: string[], pinUrl: string, genre: s
const timestamp = new Date().getTime();
const imageFileName = `${pinId}_${timestamp}.png`;
const renamedImagePaths = [];
const renamedImagePaths: string[] = [];
for (let i = 0; i < imagePaths.length; i++) {
const renamedPath = path.join(path.dirname(imagePaths[i]), `${pinId}_${timestamp}_${i}.png`);
await fs.rename(imagePaths[i], renamedPath);
@ -106,10 +106,14 @@ async function generateImageForTask(task: GenerationTask, server: { baseUrl: str
logger.info(`Copied ${sourcePath} to ${destPath}`);
}
// generateImage expects two source files; if we only have one, pass the same one twice
const srcA = sourceFileNames[0];
const srcB = sourceFileNames[1] || sourceFileNames[0];
const generatedImagePath = await generateImage(
imagePrompt,
sourceFileNames[0],
sourceFileNames[1],
srcA,
srcB,
imageFileName,
baseUrl,
outputDir,
@ -173,16 +177,107 @@ async function getPinUrlFromPinterest(keyword: string): Promise<string | null> {
}
(async () => {
const keywords = [
{ genre: "fantasy", subGenre: "ethereal", pinId: "311311393008071543" },
{ genre: "fantasy", subGenre: "ethereal", pinId: "1688918605889326" },
{ genre: "fantasy", subGenre: "ethereal", pinId: "4925880837133615" },
{ genre: "fantasy", subGenre: "ethereal", pinId: "985231163711528" },
{ genre: "fantasy", subGenre: "dark ethereal", pinId: "768497123954670531" },
{ genre: "fantasy", subGenre: "dark ethereal", pinId: "5840674510388768" },
{ genre: "fantasy", subGenre: "dark ethereal", pinId: "281123201734741654" },
{ genre: "fantasy", subGenre: "dark ethereal", pinId: "41306521578186951" },
];
// Load pinterest keywords JSON, pick up to 20 subGenres and choose 1 pinId per subGenre
const keywordsFilePath = path.resolve(process.cwd(), 'src', 'pinterest_keywords.json');
let allKeywords: { genre: string; subGenre: string; pinIds?: string[]; pinId?: string[] }[] = [];
try {
const raw = await fs.readFile(keywordsFilePath, 'utf-8');
allKeywords = JSON.parse(raw);
} catch (err) {
logger.error('Failed to read pinterest keywords JSON:', err);
return;
}
allKeywords = allKeywords.filter(a => {
return (a.genre == "food" && a.subGenre == "imagination")
});
function shuffle<T>(arr: T[]): T[] {
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
const selectedEntries = shuffle(allKeywords.slice()).slice(0, Math.min(20, allKeywords.length));
// Download up to `count` images from a pin URL by opening the pin page and scrolling up to 5 times to trigger lazy loading
// Returns an array of saved image paths (may be empty)
async function downloadOneImageFromPin(pinUrl: string, count: number = 1): Promise<string[]> {
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36');
await page.setViewport({ width: 1920, height: 1080 });
try {
await page.goto(pinUrl, { waitUntil: 'networkidle2', timeout: 30000 });
for (let i = 0; i < 5; i++) {
await page.evaluate('window.scrollTo(0, document.body.scrollHeight)');
await new Promise((r) => setTimeout(r, 700 + Math.random() * 800));
}
const imgs: string[] = await page.$$eval('img', imgs =>
imgs.map(img => (img as HTMLImageElement).src)
.filter(src => !!src && (src.includes('pinimg') || /\.(jpe?g|png|webp)$/i.test(src)))
);
if (!imgs || imgs.length === 0) {
logger.warn(`No image src found on pin page ${pinUrl}`);
return [];
}
// shuffle and pick up to `count` unique images
const shuffled = imgs.slice().sort(() => 0.5 - Math.random());
const chosen = shuffled.slice(0, Math.min(count, shuffled.length));
const outDir = path.join(process.cwd(), 'download');
await fs.mkdir(outDir, { recursive: true });
const results: string[] = [];
for (let i = 0; i < chosen.length; i++) {
const src = chosen[i];
try {
const imgPage = await browser.newPage();
const resp = await imgPage.goto(src, { timeout: 30000, waitUntil: 'networkidle2' });
if (!resp) {
logger.warn(`Failed to fetch image ${src} from ${pinUrl}`);
await imgPage.close();
continue;
}
const buffer = await resp.buffer();
const pinId = pinUrl.split('/').filter(Boolean).pop() || `pin_${Date.now()}`;
const timestamp = Date.now();
const outPath = path.join(outDir, `${pinId}_${timestamp}_${i}.png`);
await fs.writeFile(outPath, buffer);
results.push(outPath);
await imgPage.close();
} catch (err) {
logger.error(`Failed to download image ${src} from ${pinUrl}:`, err);
}
}
return results;
} catch (err) {
logger.error(`Failed to download images from ${pinUrl}:`, err);
return [];
} finally {
await browser.close();
}
}
// Build keywords list with single chosen pinId per selected subGenre
const keywords: { genre: string; subGenre: string; pinId: string[] }[] = [];
for (const entry of selectedEntries) {
const pinIds = (entry.pinIds || entry.pinId) as string[] | undefined;
if (!Array.isArray(pinIds) || pinIds.length === 0) continue;
const chosenPinId = pinIds[Math.floor(Math.random() * pinIds.length)];
keywords.push({ genre: entry.genre, subGenre: entry.subGenre, pinId: [chosenPinId] });
}
if (keywords.length === 0) {
logger.error("No keywords/pinIds available from pinterest_keywords.json. Exiting.");
return;
}
if (servers.length === 0) {
logger.error("No servers configured. Please check your .env file.");
@ -196,45 +291,51 @@ async function getPinUrlFromPinterest(keyword: string): Promise<string | null> {
const { genre, subGenre } = genreSubGenre;
const pinId = (genreSubGenre as any).pinId;
let pin: string | null = null;
if (pinId) {
pin = `https://www.pinterest.com/pin/${pinId}/`;
logger.info(`Using direct pin URL for pinId: ${pinId}`);
} /* else {
// Uncomment the block below to enable searching by term instead of using pinId.
// const searchTerm = (genreSubGenre as any).search || `${genre} ${subGenre}`;
// logger.info(`Searching for a pin with search term: ${searchTerm}`);
// pin = await getPinUrlFromPinterest(searchTerm);
} */
if (!pin) {
logger.warn(`No pinId provided and search is disabled for genre: ${genre}, subGenre: ${subGenre}. Skipping.`);
continue;
}
logger.info(`--- Starting processing for pin: ${pin} ---`);
const downloadedImagePaths = await downloadImagesFromPinterestPin(pin);
if (downloadedImagePaths.length === 0) {
logger.warn(`No images were downloaded for pin ${pin}. Skipping.`);
continue;
}
const selectedImages = downloadedImagePaths.sort(() => 0.5 - Math.random()).slice(0, 2);
logger.info(`--- Randomly selected ${selectedImages.length} images for processing ---`);
if (selectedImages.length === 2) {
const pinId = path.basename(selectedImages[0], path.extname(selectedImages[0])).split('_related_')[0];
const pinUrl = `https://www.pinterest.com/pin/${pinId}/`;
const task = await getPromptsForImage(selectedImages, pinUrl, genre, subGenre);
if (task) {
generationTasks.push(task);
for (let i = 0; i < 10; i++) {
// pinId is now an array with a single chosen id. Pick the first element.
const pinIdField = (genreSubGenre as any).pinId;
let selectedPinId: string | undefined;
if (Array.isArray(pinIdField) && pinIdField.length > 0) {
selectedPinId = pinIdField[0];
logger.info(`Selected chosen pinId ${selectedPinId} for ${genre} / ${subGenre}`);
} else if (typeof pinIdField === 'string' && pinIdField) {
selectedPinId = pinIdField;
logger.info(`Using single pinId ${selectedPinId} for ${genre} / ${subGenre}`);
}
} else {
logger.warn(`Skipping pin ${pin} as it did not have at least 2 images.`);
for (const imagePath of selectedImages) {
await fs.unlink(imagePath);
if (!selectedPinId) {
logger.warn(`No pinId available for ${genre}/${subGenre}. Skipping.`);
continue;
}
const pin = `https://www.pinterest.com/pin/${selectedPinId}/`;
logger.info(`--- Starting processing for pin: ${pin} ---`);
// 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 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);
if (task) {
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);
}
}
}
}
}
@ -306,9 +407,6 @@ async function getPinUrlFromPinterest(keyword: string): Promise<string | null> {
video_path: newVideoPath,
});
logger.info(`Renamed files and updated database record for video ID: ${videoId}`);
await fs.unlink(newImagePath);
logger.info(`Deleted paired image: ${newImagePath}`);
}
} catch (error) {
logger.error('An error occurred during video generation or database operations:', error);