save changes
This commit is contained in:
141
src/comfyworkflows/generate_image.json
Normal file
141
src/comfyworkflows/generate_image.json
Normal file
@ -0,0 +1,141 @@
|
||||
{
|
||||
"3": {
|
||||
"inputs": {
|
||||
"seed": 143057099452116,
|
||||
"steps": 10,
|
||||
"cfg": 1,
|
||||
"sampler_name": "res_multistep",
|
||||
"scheduler": "simple",
|
||||
"denoise": 1,
|
||||
"model": [
|
||||
"66",
|
||||
0
|
||||
],
|
||||
"positive": [
|
||||
"6",
|
||||
0
|
||||
],
|
||||
"negative": [
|
||||
"7",
|
||||
0
|
||||
],
|
||||
"latent_image": [
|
||||
"72",
|
||||
0
|
||||
]
|
||||
},
|
||||
"class_type": "KSampler",
|
||||
"_meta": {
|
||||
"title": "KSampler"
|
||||
}
|
||||
},
|
||||
"6": {
|
||||
"inputs": {
|
||||
"text": "Create an image of a young man sitting on a stone ledge overlooking a nighttime cityscape. He should be wearing headphones and gazing at a starry sky filled with shooting stars. Include a large statue of a winged creature beside him. The scene should have a peaceful, contemplative mood. Use a color palette dominated by deep blues, purples, and pinks for the sky. The city below should be illuminated creating a warm glow.",
|
||||
"clip": [
|
||||
"38",
|
||||
0
|
||||
]
|
||||
},
|
||||
"class_type": "CLIPTextEncode",
|
||||
"_meta": {
|
||||
"title": "CLIP Text Encode (Positive Prompt)"
|
||||
}
|
||||
},
|
||||
"7": {
|
||||
"inputs": {
|
||||
"text": "",
|
||||
"clip": [
|
||||
"38",
|
||||
0
|
||||
]
|
||||
},
|
||||
"class_type": "CLIPTextEncode",
|
||||
"_meta": {
|
||||
"title": "CLIP Text Encode (Negative Prompt)"
|
||||
}
|
||||
},
|
||||
"8": {
|
||||
"inputs": {
|
||||
"samples": [
|
||||
"3",
|
||||
0
|
||||
],
|
||||
"vae": [
|
||||
"39",
|
||||
0
|
||||
]
|
||||
},
|
||||
"class_type": "VAEDecode",
|
||||
"_meta": {
|
||||
"title": "VAE Decode"
|
||||
}
|
||||
},
|
||||
"37": {
|
||||
"inputs": {
|
||||
"unet_name": "qwen_image_distill_full_fp8_e4m3fn.safetensors",
|
||||
"weight_dtype": "default"
|
||||
},
|
||||
"class_type": "UNETLoader",
|
||||
"_meta": {
|
||||
"title": "Load Diffusion Model"
|
||||
}
|
||||
},
|
||||
"38": {
|
||||
"inputs": {
|
||||
"clip_name": "qwen_2.5_vl_7b_fp8_scaled.safetensors",
|
||||
"type": "qwen_image",
|
||||
"device": "default"
|
||||
},
|
||||
"class_type": "CLIPLoader",
|
||||
"_meta": {
|
||||
"title": "Load CLIP"
|
||||
}
|
||||
},
|
||||
"39": {
|
||||
"inputs": {
|
||||
"vae_name": "qwen_image_vae.safetensors"
|
||||
},
|
||||
"class_type": "VAELoader",
|
||||
"_meta": {
|
||||
"title": "Load VAE"
|
||||
}
|
||||
},
|
||||
"60": {
|
||||
"inputs": {
|
||||
"filename_prefix": "RADOMVIDEOMAKERIMG",
|
||||
"images": [
|
||||
"8",
|
||||
0
|
||||
]
|
||||
},
|
||||
"class_type": "SaveImage",
|
||||
"_meta": {
|
||||
"title": "Save Image"
|
||||
}
|
||||
},
|
||||
"66": {
|
||||
"inputs": {
|
||||
"shift": 5.000000000000001,
|
||||
"model": [
|
||||
"37",
|
||||
0
|
||||
]
|
||||
},
|
||||
"class_type": "ModelSamplingAuraFlow",
|
||||
"_meta": {
|
||||
"title": "ModelSamplingAuraFlow"
|
||||
}
|
||||
},
|
||||
"72": {
|
||||
"inputs": {
|
||||
"width": 720,
|
||||
"height": 1280,
|
||||
"batch_size": 1
|
||||
},
|
||||
"class_type": "EmptySD3LatentImage",
|
||||
"_meta": {
|
||||
"title": "EmptySD3LatentImage"
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/index.ts
10
src/index.ts
@ -1,6 +1,7 @@
|
||||
import { downloadPinterestImages } from './lib/downloader';
|
||||
import { describeImage } from './lib/image-describer';
|
||||
import { logger, setLogLevel, LogLevel } from './lib/logger';
|
||||
import { generateImage } from './lib/image-generator';
|
||||
import { logger } from './lib/logger';
|
||||
|
||||
(async () => {
|
||||
const keyword = 'cyberpunk city';
|
||||
@ -22,8 +23,13 @@ import { logger, setLogLevel, LogLevel } from './lib/logger';
|
||||
`);
|
||||
const prompt = llmResponseJSON.prompt;
|
||||
logger.info(`Description for ${imagePath}:`, prompt);
|
||||
|
||||
const timestamp = new Date().getTime();
|
||||
const newFileName = `${keyword.replace(/\s/g, '_')}_${timestamp}.png`;
|
||||
const generatedImagePath = await generateImage(prompt, newFileName);
|
||||
logger.info(`Generated new image from prompt, saved to: ${generatedImagePath}`);
|
||||
} catch (error) {
|
||||
logger.error(`Failed to describe ${imagePath}:`, error);
|
||||
logger.error(`Failed to process ${imagePath}:`, error);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
@ -18,14 +18,22 @@ async function downloadImage(url: string, filepath: string) {
|
||||
export async function downloadPinterestImages(keyword: string, numberOfPages: number): 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/91.0.4472.124 Safari/537.36');
|
||||
await page.setViewport({ width: 1280, height: 800 });
|
||||
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 });
|
||||
|
||||
const encodedKeyword = encodeURIComponent(keyword);
|
||||
await page.goto(`https://www.pinterest.com/search/pins/?q=${encodedKeyword}`, { waitUntil: 'networkidle2' });
|
||||
|
||||
logger.debug('Searching for:', keyword);
|
||||
|
||||
try {
|
||||
await page.waitForSelector('img[src*="i.pinimg.com"]', { timeout: 10000 });
|
||||
} catch (error) {
|
||||
logger.error('Could not find images on the page. Pinterest might have changed its layout or is blocking the scraper.');
|
||||
await browser.close();
|
||||
return [];
|
||||
}
|
||||
|
||||
let imageCount = 0;
|
||||
const downloadedUrls = new Set<string>();
|
||||
const downloadedImagePaths: string[] = [];
|
||||
@ -35,18 +43,15 @@ export async function downloadPinterestImages(keyword: string, numberOfPages: nu
|
||||
try {
|
||||
const imageUrls = await page.evaluate(() => {
|
||||
const images = Array.from(document.querySelectorAll('img[src*="i.pinimg.com"]'));
|
||||
const urls = images.map(img => {
|
||||
const srcset = (img as HTMLImageElement).srcset;
|
||||
return images.map(img => {
|
||||
const image = img as HTMLImageElement;
|
||||
const srcset = image.srcset;
|
||||
if (srcset) {
|
||||
const sources = srcset.split(',').map(s => s.trim());
|
||||
const source4x = sources.find(s => s.endsWith(' 4x'));
|
||||
if (source4x) {
|
||||
return source4x.split(' ')[0];
|
||||
}
|
||||
const sources = srcset.split(',').map(s => s.trim().split(' ')[0]);
|
||||
return sources[sources.length - 1];
|
||||
}
|
||||
return null;
|
||||
});
|
||||
return urls.filter((url): url is string => url !== null);
|
||||
return image.src;
|
||||
}).filter(Boolean);
|
||||
});
|
||||
|
||||
for (const url of imageUrls) {
|
||||
|
||||
46
src/lib/image-generator.ts
Normal file
46
src/lib/image-generator.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
import axios from 'axios';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const COMFY_BASE_URL = process.env.COMFY_BASE_URL?.replace(/\/$/, '');
|
||||
const COMFY_OUTPUT_DIR = process.env.COMFY_OUTPUT_DIR;
|
||||
|
||||
async function generateImage(prompt: string, newFileName: string): Promise<string> {
|
||||
const workflow = JSON.parse(await fs.readFile('src/comfyworkflows/generate_image.json', 'utf-8'));
|
||||
workflow['6']['inputs']['text'] = prompt;
|
||||
|
||||
const response = await axios.post(`${COMFY_BASE_URL}/prompt`, { prompt: workflow });
|
||||
const promptId = response.data.prompt_id;
|
||||
|
||||
let history;
|
||||
do {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
const historyResponse = await axios.get(`${COMFY_BASE_URL}/history/${promptId}`);
|
||||
history = historyResponse.data[promptId];
|
||||
} while (!history || Object.keys(history.outputs).length === 0);
|
||||
|
||||
const files = await fs.readdir(COMFY_OUTPUT_DIR!);
|
||||
const generatedFiles = files.filter(file => file.startsWith('RADOMVIDEOMAKERIMG'));
|
||||
|
||||
const fileStats = await Promise.all(
|
||||
generatedFiles.map(async (file) => {
|
||||
const stat = await fs.stat(path.join(COMFY_OUTPUT_DIR!, file));
|
||||
return { file, mtime: stat.mtime };
|
||||
})
|
||||
);
|
||||
|
||||
fileStats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
||||
|
||||
const latestFile = fileStats[0].file;
|
||||
const newFilePath = path.resolve('./generated', newFileName);
|
||||
|
||||
await fs.mkdir('./generated', { recursive: true });
|
||||
await fs.rename(path.join(COMFY_OUTPUT_DIR!, latestFile), newFilePath);
|
||||
|
||||
return newFilePath;
|
||||
}
|
||||
|
||||
export { generateImage };
|
||||
Reference in New Issue
Block a user