This commit is contained in:
2025-10-02 07:35:43 +02:00
17 changed files with 3801 additions and 4372 deletions

View File

@ -0,0 +1,229 @@
{
"1": {
"inputs": {
"clip_name1": "clip_l.safetensors",
"clip_name2": "t5xxl_fp16.safetensors",
"type": "flux",
"device": "default"
},
"class_type": "DualCLIPLoader",
"_meta": {
"title": "DualCLIPLoader"
}
},
"2": {
"inputs": {
"unet_name": "flux1-krea-dev_fp8_scaled.safetensors",
"weight_dtype": "default"
},
"class_type": "UNETLoader",
"_meta": {
"title": "Load Diffusion Model"
}
},
"3": {
"inputs": {
"width": 720,
"height": 1280,
"batch_size": 1
},
"class_type": "EmptySD3LatentImage",
"_meta": {
"title": "EmptySD3LatentImage"
}
},
"4": {
"inputs": {
"seed": 445107772143446,
"steps": 20,
"cfg": 1,
"sampler_name": "euler",
"scheduler": "simple",
"denoise": 1,
"model": [
"2",
0
],
"positive": [
"11",
0
],
"negative": [
"5",
0
],
"latent_image": [
"3",
0
]
},
"class_type": "KSampler",
"_meta": {
"title": "KSampler"
}
},
"5": {
"inputs": {
"conditioning": [
"14",
0
]
},
"class_type": "ConditioningZeroOut",
"_meta": {
"title": "ConditioningZeroOut"
}
},
"6": {
"inputs": {
"samples": [
"4",
0
],
"vae": [
"12",
0
]
},
"class_type": "VAEDecode",
"_meta": {
"title": "VAE Decode"
}
},
"8": {
"inputs": {
"style_model_name": "flux1-redux-dev.safetensors"
},
"class_type": "StyleModelLoader",
"_meta": {
"title": "Load Style Model"
}
},
"9": {
"inputs": {
"crop": "center",
"clip_vision": [
"10",
0
],
"image": [
"13",
0
]
},
"class_type": "CLIPVisionEncode",
"_meta": {
"title": "CLIP Vision Encode"
}
},
"10": {
"inputs": {
"clip_name": "sigclip_vision_patch14_384.safetensors"
},
"class_type": "CLIPVisionLoader",
"_meta": {
"title": "Load CLIP Vision"
}
},
"11": {
"inputs": {
"strength": 0.30000000000000004,
"conditioning": [
"14",
0
],
"style_model": [
"8",
0
],
"clip_vision_output": [
"9",
0
]
},
"class_type": "ApplyStyleModelAdjust",
"_meta": {
"title": "Apply Style Model (Adjusted)"
}
},
"12": {
"inputs": {
"vae_name": "ae.safetensors"
},
"class_type": "VAELoader",
"_meta": {
"title": "Load VAE"
}
},
"13": {
"inputs": {
"image": "281543725739981_1759177922955_0.png"
},
"class_type": "LoadImage",
"_meta": {
"title": "Load Image 1"
}
},
"14": {
"inputs": {
"text": "realistic photo of woman, wavy long blong hair, fullbody shot,, A dynamic dance scene begins with a distorted glitch effect mirroring the images grayscale aesthetic, quickly transitioning into a vibrant, fast-paced choreography featuring dancers in similar pale makeup and unsettling expressions. The music is electronic with heavy bass and industrial elements. The camera work should be kinetic and disorienting, utilizing quick cuts and unconventional angles, emphasizing the feeling of being trapped or haunted. The dance evolves from frantic movements to controlled yet eerie poses that echo the images gesture of covering the face. The setting changes between a stark white room similar to the image's background and abstract digital landscapes.",
"clip": [
"1",
0
]
},
"class_type": "CLIPTextEncode",
"_meta": {
"title": "CLIP Text Encode (Prompt)"
}
},
"15": {
"inputs": {
"filename_prefix": "STYLEDVIDEOMAKER",
"images": [
"20",
0
]
},
"class_type": "SaveImage",
"_meta": {
"title": "Save Image"
}
},
"19": {
"inputs": {
"image": "face.png"
},
"class_type": "LoadImage",
"_meta": {
"title": "Load Image"
}
},
"20": {
"inputs": {
"enabled": true,
"swap_model": "inswapper_128.onnx",
"facedetection": "retinaface_resnet50",
"face_restore_model": "GPEN-BFR-2048.onnx",
"face_restore_visibility": 1,
"codeformer_weight": 1,
"detect_gender_input": "no",
"detect_gender_source": "no",
"input_faces_index": "0",
"source_faces_index": "0",
"console_log_level": 1,
"input_image": [
"6",
0
],
"source_image": [
"19",
0
]
},
"class_type": "ReActorFaceSwap",
"_meta": {
"title": "ReActor 🌌 Fast Face Swap"
}
}
}

View File

@ -30,7 +30,7 @@ async function generateImage(
workflow['13']['inputs']['image'] = imageName1;
// Set image name
//workflow['16']['inputs']['image'] = imageName2;
workflow['16']['inputs']['image'] = imageName2;
workflow['3']['inputs']['width'] = size.width;
workflow['3']['inputs']['height'] = size.height;

View File

@ -0,0 +1,78 @@
import * as fs from 'fs/promises';
import * as path from 'path';
import axios from 'axios';
import dotenv from 'dotenv';
dotenv.config();
interface ImageSize {
width: number;
height: number;
}
async function generateImage(
prompt: string,
faceImage: string,
styleImage: string,
newFileName: string,
comfyBaseUrl: string,
comfyOutputDir: string,
size: ImageSize = { width: 720, height: 1280 }
): Promise<string> {
const COMFY_BASE_URL = comfyBaseUrl.replace(/\/$/, '');
const COMFY_OUTPUT_DIR = comfyOutputDir;
let workflow;
workflow = JSON.parse(await fs.readFile('src/comfyworkflows/generate_image_style_faceswap.json', 'utf-8'));
workflow['14']['inputs']['text'] = prompt;
// Set image name
workflow['13']['inputs']['image'] = styleImage;
// Set image name
workflow['19']['inputs']['image'] = faceImage;
workflow['3']['inputs']['width'] = size.width;
workflow['3']['inputs']['height'] = size.height;
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('STYLEDVIDEOMAKER'));
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 });
const sourcePath = path.join(COMFY_OUTPUT_DIR!, latestFile);
try {
await fs.unlink(newFilePath);
} catch (err) {
// ignore if not exists
}
await fs.copyFile(sourcePath, newFilePath);
return newFilePath;
}
export { generateImage };

View File

@ -108,6 +108,7 @@ async function callLMStudioAPIWithFile(imagePath: string, prompt: string): Promi
return JSON.parse(arrayMatch[0]);
}
}
return content;
} else {
logger.error('Unexpected API response:', data);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@ -1,19 +1,28 @@
import fs from 'fs';
import path from 'path';
import crypto from 'crypto';
import { generateImage } from '../../lib/image-generator';
import { generateImage } from '../../lib/image-generator-style-faceswap';
import { logger } from '../../lib/logger';
import dotenv from 'dotenv';
dotenv.config();
const scenesFilePath = path.resolve(process.cwd(), 'src/musicspot_generator/v2/scenes.json');
const faceFilePath = path.resolve(process.cwd(), 'src/musicspot_generator/v2/face.png');
const GENERATED_DIR = path.resolve('generated');
const DEFAULT_SIZE = { width: 1280, height: 720 };
interface Scene {
scene: string;
imagePrompt: string;
imagePrompt: {
description: string,
style: string,
lighting: string,
outfit: string,
location: string,
poses: string,
angle: string,
};
videoPromp: string;
baseImagePath: string;
}
@ -43,11 +52,12 @@ async function generatePhotos() {
try {
await generateImage(
scene.imagePrompt,
`realistic photo of woman, wavy long brown hair, fullbody shot, ${scene.imagePrompt.location},${scene.imagePrompt.angle},${scene.imagePrompt.lighting},${scene.imagePrompt.outfit}`,
faceFilePath,
scene.baseImagePath,
imgFileName,
COMFY_BASE_URL,
COMFY_OUTPUT_DIR,
'flux',
DEFAULT_SIZE
);
logger.info(`Successfully generated photo: ${imgFileName}`);

View File

@ -4,9 +4,9 @@ import { callLMStudioAPIWithFile } from '../../lib/lmstudio';
import { logger } from '../../lib/logger';
const promptInstructions = `
Video prompt: No slowmotion, Be creative and generate dynamic dance scene.
Video prompt: No slowmotion, Be creative and generate gengle action scene.
`;
6
const inputDir = path.resolve(process.cwd(), 'input');
const outputFilePath = path.resolve(process.cwd(), 'src/musicspot_generator/v2/scenes.json');

View File

@ -21,33 +21,39 @@ async function processScenes() {
const scenes: Scene[] = JSON.parse(scenesData).scenes;
for (const scene of scenes) {
const hash = crypto.createHash('sha256').update(scene.baseImagePath).digest('hex');
const imageFileName = `${hash}.png`;
const imagePath = path.join(generatedFolderPath, imageFileName);
if (fs.existsSync(imagePath)) {
const outputVideoFileName = `${hash}.mp4`;
const outputVideoPath = path.join(generatedFolderPath, outputVideoFileName);
try {
const hash = crypto.createHash('sha256').update(scene.baseImagePath).digest('hex');
const imageFileName = `${hash}.png`;
const imagePath = path.join(generatedFolderPath, imageFileName);
if (fs.existsSync(outputVideoPath)) {
console.log(`Video already exists for scene ${scene.scene}, skipping.`);
continue;
if (fs.existsSync(imagePath)) {
const outputVideoFileName = `${hash}.mp4`;
const outputVideoPath = path.join(generatedFolderPath, outputVideoFileName);
if (fs.existsSync(outputVideoPath)) {
console.log(`Video already exists for scene ${scene.scene}, skipping.`);
continue;
}
console.log(`Generating video for scene ${scene.scene}...`);
await generateVideo(
scene.videoPrompt,
imagePath,
outputVideoPath,
process.env.COMFY_BASE_URL!,
process.env.COMFY_OUTPUT_DIR!,
{ width: 1280, height: 720 }
);
console.log(`Video for scene ${scene.scene} saved to ${outputVideoPath}`);
} else {
console.warn(`Image not found for scene ${scene.scene}: ${imagePath}`);
}
console.log(`Generating video for scene ${scene.scene}...`);
await generateVideo(
scene.videoPrompt,
imagePath,
outputVideoPath,
process.env.COMFY_BASE_URL!,
process.env.COMFY_OUTPUT_DIR!,
{ width: 1280, height: 720 }
);
console.log(`Video for scene ${scene.scene} saved to ${outputVideoPath}`);
} else {
console.warn(`Image not found for scene ${scene.scene}: ${imagePath}`);
} catch (e) {
continue;
}
}
} catch (error) {
console.error('Error processing scenes:', error);

View File

@ -0,0 +1,68 @@
import * as fs from 'fs';
import * as path from 'path';
import * as crypto from 'crypto';
import { generateVideo } from '../../lib/video-generator';
import { callLMStudioAPIWithFile } from '../../lib/lmstudio';
import dotenv from 'dotenv';
dotenv.config();
const inputFolderPath = path.join(__dirname, '..', '..', '..', 'input');
const generatedFolderPath = path.join(__dirname, '..', '..', '..', 'generated');
async function processImages() {
try {
const imageFiles = fs.readdirSync(inputFolderPath);
for (const imageFile of imageFiles) {
const imagePath = path.join(inputFolderPath, imageFile);
try {
const hash = crypto.createHash('sha256').update(imageFile).digest('hex');
const outputVideoFileName = `${hash}.mp4`;
const outputVideoPath = path.join(generatedFolderPath, outputVideoFileName);
if (fs.existsSync(outputVideoPath)) {
console.log(`Video already exists for image ${imageFile}, skipping.`);
continue;
}
console.log(`Generating video prompt for image ${imageFile}...`);
const promptResult = await callLMStudioAPIWithFile(
imagePath,
`
Generate a short, dancing video prompt for an image located at ${imagePath}.
Return the result in this format: {"result":""}
Instruction:
- Find best dancing expression based on the photo
`);
const videoPrompt = promptResult.result;
console.log(`Video prompt ${videoPrompt}`);
if (!videoPrompt) {
console.error(`Could not generate video prompt for image ${imageFile}`);
continue;
}
console.log(`Generating video for image ${imageFile}...`);
await generateVideo(
videoPrompt,
imagePath,
outputVideoPath,
process.env.COMFY_BASE_URL!,
process.env.COMFY_OUTPUT_DIR!,
{ width: 1280, height: 720 }
);
console.log(`Video for image ${imageFile} saved to ${outputVideoPath}`);
} catch (e) {
console.error(`Error processing image ${imageFile}:`, e);
continue;
}
}
} catch (error) {
console.error('Error processing images:', error);
}
}
processImages();

View File

@ -13,8 +13,8 @@ const PINS_TO_COLLECT = 5;
// Hard-coded user prompt
const HARDCODED_USER_PROMPT = process.env.HARDCODED_USER_PROMPT || `
Generate 20 keywords for photos of group of people dancing together focus on street and urban style. All keywords shoudld contain \"group horizontal\" and what you create.
Example output : ["group horizontal hiphop dance","group horizontal modern dance","",... and 20 items in array]
Generate 20 keywords for photos of a girl in scary scene for music video for haloween. All keywords should containe girl in a scene.
Example output : ["a girl in grave yard,"a girl in scary forest","",... and 20 items in array]
`;
async function getPinUrlsFromPinterest(keyword: string, scrollCount = SCROLL_SEARCH, limit = PINS_TO_COLLECT): Promise<string[]> {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,82 @@
import * as fs from 'fs/promises';
import * as path from 'path';
import dotenv from 'dotenv';
import { callLMStudioAPIWithFile, callLmstudio } from '../lib/lmstudio';
import { convertImage } from '../lib/image-converter';
import { logger } from '../lib/logger';
dotenv.config();
const COMFY_BASE_URL = process.env.SERVER2_COMFY_BASE_URL;
const COMFY_OUTPUT_DIR = process.env.SERVER2_COMFY_OUTPUT_DIR;
const INPUT_DIR = './input';
const GENERATED_DIR = './generated';
async function batchConvert() {
if (!COMFY_BASE_URL || !COMFY_OUTPUT_DIR) {
throw new Error('ComfyUI server details are not defined in the .env file');
}
try {
await fs.mkdir(GENERATED_DIR, { recursive: true });
const files = await fs.readdir(INPUT_DIR);
for (const file of files) {
try {
const inputFile = path.join(INPUT_DIR, file);
logger.info(`Processing ${inputFile}`);
const firstPrompt = `
Read the file in the photo and detect the main subject. Extract the following information:
- what is the main subject in one word
- describe character in 5 words
- describe background in 5 words
- 3 ideas to make this character's look, 2 or 3 words per idea
Return the result in this format:
{ "character":"", "characterDescription": "", "background":"", "idea":"idea1, idea2, idea3"}
`;
const imageInfo = await callLMStudioAPIWithFile(inputFile, firstPrompt);
const { character, idea, background } = imageInfo;
const ideas = idea.split(',').map((i: string) => i.trim());
const secondPrompt = `
Generate a prompt to convert the photo to a group dancing photo.
I need only the prompt in this format, main subject is ${character}
{"result":"
Change the camera angle to a full-body shot, place many ${character} in the scene messily,
Change lighting to silhouette lighting,
Change the style for each body using these ideas: ${ideas.join(', ')},
Change color of each body,
Change the background in horror movie style of ${background},
Overall photo should be realistic and spooky,
"} `;
const promptResult = await callLmstudio(secondPrompt);
const finalPrompt = promptResult.result;
const inputFolderFullPath = COMFY_OUTPUT_DIR.replace("output", "input");
const serverInputFile = path.join(inputFolderFullPath, file);
await fs.copyFile(inputFile, serverInputFile);
logger.info(`Generating image for ${file} with prompt: ${finalPrompt}`);
const generatedFile = await convertImage(finalPrompt, file, COMFY_BASE_URL, COMFY_OUTPUT_DIR);
const finalOutputPath = path.join(GENERATED_DIR, path.basename(generatedFile));
await fs.copyFile(generatedFile, finalOutputPath);
logger.info(`Saved converted image to ${finalOutputPath}`);
} catch (e) {
logger.error('An error occurred during batch conversion:', e);
continue;
}
}
} catch (error) {
logger.error('An error occurred during batch conversion:', error);
}
}
batchConvert();

View File

@ -0,0 +1,74 @@
import * as fs from 'fs/promises';
import * as path from 'path';
import { generateVideo } from '../lib/video-generator';
import dotenv from 'dotenv';
dotenv.config();
const inputFolder = './input';
const prompt = "a girl making a cute pose, static camera";
b
async function main() {
try {
const files = await fs.readdir(inputFolder);
const pngFiles = files.filter(file => path.extname(file).toLowerCase() === '.png');
if (pngFiles.length === 0) {
console.log('No PNG files found in the input folder.');
return;
}
const comfyBaseUrl = process.env.SERVER2_COMFY_BASE_URL;
const comfyOutputDir = process.env.SERVER2_COMFY_OUTPUT_DIR;
if (!comfyBaseUrl || !comfyOutputDir) {
console.error('Please define SERVER1_COMFY_BASE_URL and SERVER1_COMFY_OUTPUT_DIR in your .env file.');
return;
}
const comfyInputDir = comfyOutputDir.replace('output', 'input');
for (const file of pngFiles) {
const inputImagePath = path.join(inputFolder, file);
const comfyInputImagePath = path.join(comfyInputDir, file);
console.log(`Processing ${file}...`);
try {
await fs.access(inputImagePath);
// Copy file to comfy input directory
await fs.copyFile(inputImagePath, comfyInputImagePath);
console.log(`Copied ${file} to ComfyUI input directory.`);
} catch (e) {
console.error(`Error copying file ${file}:`, e);
continue;
}
const newFileName = `${path.parse(file).name}.mp4`;
try {
const generatedVideoPath = await generateVideo(
prompt,
file, // Pass only the filename as per instructions
newFileName,
comfyBaseUrl,
comfyOutputDir
);
console.log(`Successfully generated video for ${file} at: ${generatedVideoPath}`);
} catch (e: any) {
if (e.code === 'ECONNREFUSED' || e.code === 'ETIMEDOUT') {
console.error(`\nError: Connection to ComfyUI server at ${comfyBaseUrl} failed.`);
console.error('Please ensure the ComfyUI server is running and accessible.');
break;
} else {
console.error(`An error occurred while generating video for ${file}:`, e);
}
}
}
} catch (error) {
console.error('An unexpected error occurred:', error);
}
}
main();

View File

@ -3,20 +3,57 @@ import * as fs from 'fs/promises';
import * as path from 'path';
import { logger } from '../lib/logger';
import { getPinUrlFromPinterest, downloadImageFromPin } from '../lib/pinterest';
import { convertImage } from '../lib/image-converter';
import { generateImage } from '../lib/image-generator-face';
import { callLMStudioAPIWithFile } from '../lib/lmstudio';
dotenv.config();
const MODE: "keywords" | "pinIds" = "keywords";
const KEYWORDS = [
'fullbody pose cute ',
'fullbody pose model ',
'fullbody pose woman ',
'fullbody pose idol ',
'fullbody pose kawaii',
'fullbody pose japanese',
'fullbody pose kawaii sit',
'fullbody pose model sit',
'fullbody pose cute sit',];
'a girl in scary forest',
'a girl in graveyard',
''];
const pinIDs = [
"22377329393970367",
"18999629674210230",
"3166662232983784",
"291537775902572408",
"2744449769232655",
"9429480465939847",
"34058540926328062",
"1071153092617107265",
"6825836928646465",
"1407443629997072",
"333407178685095962",
"15833036184288417",
"6825836928284784",
"2181499815469509",
"199706564723106062",
"1759287348280571",
"56083957854040032",
"3025924743999802",
"2955556001576084",
"1407443627212889",
"836965911982723974",
"97460779431981493",
"282600945363725869",
"/59532026387104913",
"70437490453979",
"152489137384620437",
"50947039528553588",
"73042825197955754",
"624593042089280419",
"351912466315529",
"624030092104188250",
"21673641951379251",
"27021666506512503",
"3377768467678091",
"985231163409578",
"17240411068654164"
]
const TARGET_COUNT = Number(process.env.IMAGE_COUNT || 20);
const PROMPT =
`
@ -24,6 +61,12 @@ const PROMPT =
change background to light gray with faing gradient,
change clothes to shite sports bra and shite cotton short pants
`;
const LmStudioPrompt = `
describe the image in 50 words including, scene, lighting, describe character(s), add some beautiful accent like light, fog, starts, lamps, whatever suits with scene.
then return in this format
{"prompt":""}
`;
type ServerCfg = { baseUrl: string; outputDir: string; inputDir: string };
@ -60,37 +103,65 @@ async function collectImages(keyword: string, total: number): Promise<string[]>
// ensure local download dir exists (pinterest.ts also ensures it, but harmless here)
await ensureDir(path.join(process.cwd(), 'download'));
while (results.length < total) {
try {
const pinUrl = await getPinUrlFromPinterest(keyword);
if (!pinUrl) {
logger.warn('No pin URL found, retrying...');
await sleep(1500);
return [];
if (MODE == "keywords") {
while (results.length < total) {
try {
const pinUrl = await getPinUrlFromPinterest(keyword);
if (!pinUrl) {
logger.warn('No pin URL found, retrying...');
await sleep(1500);
return [];
}
const remaining = total - results.length;
// attempt to grab up to 5 per pin to reduce churn
const batchTarget = Math.min(5, remaining);
const imgs = await downloadImageFromPin(pinUrl, batchTarget);
if (imgs && imgs.length > 0) {
results.push(...imgs);
logger.info(`Downloaded ${imgs.length} image(s) from ${pinUrl}.Progress: ${results.length}/${total}`);
} else {
logger.warn(`Pin yielded no downloadable images: ${pinUrl}`);
}
await sleep(1000 + Math.random() * 1000);
} catch (err) {
logger.error('Error while collecting images:', err);
await sleep(2000);
}
const remaining = total - results.length;
// attempt to grab up to 5 per pin to reduce churn
const batchTarget = Math.min(5, remaining);
const imgs = await downloadImageFromPin(pinUrl, batchTarget);
if (imgs && imgs.length > 0) {
results.push(...imgs);
logger.info(`Downloaded ${imgs.length} image(s) from ${pinUrl}.Progress: ${results.length}/${total}`);
} else {
logger.warn(`Pin yielded no downloadable images: ${pinUrl}`);
}
} else if (MODE == "pinIds") {
while (results.length < total) {
const shuffledPinIds = pinIDs.slice().sort(() => 0.5 - Math.random());
const pinUrl = `https://www.pinterest.com/pin/${shuffledPinIds[0]}`;
try {
const remaining = total - results.length;
// attempt to grab up to 5 per pin to reduce churn
const batchTarget = Math.min(5, remaining);
const imgs = await downloadImageFromPin(pinUrl, batchTarget);
if (imgs && imgs.length > 0) {
results.push(...imgs);
logger.info(`Downloaded ${imgs.length} image(s) from ${pinUrl}.Progress: ${results.length}/${total}`);
} else {
logger.warn(`Pin yielded no downloadable images: ${pinUrl}`);
}
await sleep(1000 + Math.random() * 1000);
} catch (err) {
logger.error('Error while collecting images:', err);
await sleep(2000);
}
await sleep(1000 + Math.random() * 1000);
} catch (err) {
logger.error('Error while collecting images:', err);
await sleep(2000);
}
}
return results.slice(0, total);
}
let imageIndex = 0;
async function processImages(imagePaths: string[], server: ServerCfg) {
await ensureDir(server.inputDir);
for (const localImagePath of imagePaths) {
const response = await callLMStudioAPIWithFile(localImagePath, LmStudioPrompt);
const prompt = response.prompt;
const baseName = path.basename(localImagePath);
const serverInputPath = path.join(server.inputDir, baseName);
@ -100,14 +171,16 @@ async function processImages(imagePaths: string[], server: ServerCfg) {
logger.info(`Copied ${localImagePath} -> ${serverInputPath}`);
// Run conversion (sequential to avoid output race conditions)
const generatedPath = await convertImage(
PROMPT,
const generatedPath = await generateImage(
`ultra realistic photo, ${prompt}`,
baseName,
`monster_${imageIndex}.png`,
server.baseUrl,
server.outputDir,
{ width: 720, height: 1280 } // portrait
{ width: 1280, height: 720 } // portrait
);
logger.info(`Generated image: ${generatedPath}`);
imageIndex++;
} catch (err) {
logger.error(`Failed to convert ${localImagePath}:`, err);
} finally {
@ -125,6 +198,16 @@ async function processImages(imagePaths: string[], server: ServerCfg) {
async function main() {
const server = getServerConfig();
const files = await fs.readdir(path.join(process.cwd(), 'generated'));
const monsterFiles = files.filter((f) => f.startsWith('monster_'));
if (monsterFiles.length > 0) {
const latestFile = monsterFiles.sort().pop();
if (latestFile) {
const latestIndex = parseInt(latestFile.replace('monster_', '').replace('.png', ''));
imageIndex = latestIndex + 1;
}
}
// Infinite loop as requested
while (true) {

View File

@ -1,17 +1,23 @@
import * as fs from 'fs';
import * as path from 'path';
import { convertImageVton } from '../lib/image-converter';
import { convertImageVton, convertImage } from '../lib/image-converter';
import * as dotenv from 'dotenv';
dotenv.config();
<<<<<<< HEAD
const modelsBodyDir = 'D:\\CatsEye\\long videos\\vton-demo\\VTON\\models_body';
const clothesDir = 'D:\\CatsEye\\long videos\\vton-demo\\VTON\\clothes';
const posesDir = 'D:\\CatsEye\\long videos\\vton-demo\\VTON\\poses';
=======
const clothesDir = 'C:\\Users\\fm201\\Documents\\VTON\\\clothes';
const modelPath = 'C:\\Users\\fm201\\Documents\\VTON\\models\\Jessica_body.png';
const posesDir = 'C:\\Users\\fm201\\Documents\\VTON\\\poses';
>>>>>>> bdca42e82102a00f771ecf58b4ff0673dbd218af
const outputDir = 'generated';
const comfyBaseUrl = process.env.SERVER1_COMFY_BASE_URL;
const comfyOutputDir = process.env.SERVER1_COMFY_OUTPUT_DIR;
const comfyBaseUrl = process.env.SERVER2_COMFY_BASE_URL;
const comfyOutputDir = process.env.SERVER2_COMFY_OUTPUT_DIR;
function getNextIndex(directory: string): number {
if (!fs.existsSync(directory)) {