diff --git a/src/comfyworkflows/generate_video.json b/src/comfyworkflows/generate_video.json index 1b9f71c..2b10c0e 100644 --- a/src/comfyworkflows/generate_video.json +++ b/src/comfyworkflows/generate_video.json @@ -97,7 +97,7 @@ }, "52": { "inputs": { - "image": "ComfyUI_00036_.png" + "image": "AG9782s.webp" }, "class_type": "LoadImage", "_meta": { @@ -133,7 +133,7 @@ "57": { "inputs": { "add_noise": "enable", - "noise_seed": 375574453154296, + "noise_seed": 989367225141070, "steps": 6, "cfg": 1, "sampler_name": "euler", @@ -216,9 +216,9 @@ }, "63": { "inputs": { - "frame_rate": 25, + "frame_rate": 32, "loop_count": 0, - "filename_prefix": "RADOMVIDEOMAKERVIDEO", + "filename_prefix": "wan22_", "format": "video/h264-mp4", "pix_fmt": "yuv420p", "crf": 19, @@ -227,7 +227,7 @@ "pingpong": false, "save_output": true, "images": [ - "71", + "73", 0 ] }, @@ -336,39 +336,6 @@ "title": "LoraLoaderModelOnly" } }, - "71": { - "inputs": { - "ckpt_name": "rife49.pth", - "clear_cache_after_n_frames": 10, - "multiplier": 2, - "fast_mode": true, - "ensemble": true, - "scale_factor": 1, - "frames": [ - "73", - 0 - ] - }, - "class_type": "RIFE VFI", - "_meta": { - "title": "RIFE VFI (recommend rife47 and rife49)" - } - }, - "72": { - "inputs": { - "upscale_model": "4x-UltraSharp.pth", - "mode": "rescale", - "rescale_factor": 2.0000000000000004, - "resize_width": 832, - "resampling_method": "lanczos", - "supersample": "true", - "rounding_modulus": 8 - }, - "class_type": "CR Upscale Image", - "_meta": { - "title": "🔍 CR Upscale Image" - } - }, "73": { "inputs": { "resize_to": "4k", diff --git a/src/generate_pinterest_keywords.ts b/src/generate_pinterest_keywords.ts index 654d7db..edab493 100644 --- a/src/generate_pinterest_keywords.ts +++ b/src/generate_pinterest_keywords.ts @@ -556,7 +556,7 @@ const searchList: { genre: string; subGenre: string }[] = [ { "genre": "girl", "subGenre": "Samurai Girl Costume" }, { "genre": "girl", "subGenre": "Succubus Outfit" }, { "genre": "girl", "subGenre": "Video Game Heroine Cosplay" } -*/ + { "genre": "epic", "subGenre": "woman" }, { "genre": "epic", "subGenre": "girl" }, @@ -580,6 +580,15 @@ const searchList: { genre: string; subGenre: string }[] = [ { "genre": "epic", "subGenre": "future" }, { "genre": "epic", "subGenre": "landscape" }, { "genre": "epic", "subGenre": "stars" }, + */ + + { "genre": "dance", "subGenre": "hiphop horizontal" }, + { "genre": "dance", "subGenre": "hiphop group horizontal" }, + { "genre": "dance", "subGenre": "tiktok horizontal" }, + { "genre": "dance", "subGenre": "female street horizontal" }, + { "genre": "dance", "subGenre": "male street horizontal" }, + { "genre": "dance", "subGenre": "idol horizontal" }, + ]; function extractPinIdFromHref(href: string): string | null { diff --git a/src/pinterest_keywords.json b/src/pinterest_keywords.json index 40d983f..664079f 100644 --- a/src/pinterest_keywords.json +++ b/src/pinterest_keywords.json @@ -13705,5 +13705,317 @@ "12033123999166506", "2603712282017526" ] + }, + { + "genre": "dance", + "subGenre": "hiphop", + "pinIds": [ + "211174977824866", + "4151824652242858", + "11751649022253094", + "4151824652573137", + "1196337403811579", + "3870349674349808", + "5911043254404438", + "68187381855608833", + "3659243440996462", + "6333255721651797", + "70437487452096", + "409898003607513319", + "200058408444623324", + "31736372371052234", + "11329436557634702", + "73465037666692422", + "52565520644561872", + "26951297765277007", + "28077197670682813", + "3870349674611148" + ] + }, + { + "genre": "dance", + "subGenre": "hiphop group", + "pinIds": [ + "51017408275366795", + "18155204742683066", + "319474167337785285", + "446067538114071536", + "80853755805409174", + "22588435625158407", + "61854194877053486", + "622341242295540493", + "1266706140953250", + "286752701269505326", + "15270086237181640", + "4785143351454319", + "161285230400768171", + "33706697205949595", + "52565520644561872", + "175358979236709500", + "246009198391391957", + "30047522508244392", + "24488391719962433", + "228417012344850965" + ] + }, + { + "genre": "dance", + "subGenre": "tiktok", + "pinIds": [ + "140806234252193", + "492649953185623", + "4714774603765704", + "5136987070105443", + "879820477185085651", + "41447259075490684", + "55802482886369204", + "140806234033617", + "110127153387524671", + "17662623533356278", + "140806233881932", + "6122149487832007", + "6544361953318652", + "140806234252185", + "2181499815665398", + "140806233598748", + "1266706141368960", + "140806234252147", + "10625749117973695", + "1125968709926251" + ] + }, + { + "genre": "dance", + "subGenre": "female street", + "pinIds": [ + "29836416276937743", + "2674081024811692", + "478014947963945399", + "35184440833696149", + "211174977267005", + "30047522508576059", + "11822017759243879", + "69102175527156245", + "211174977267065", + "2462974792056825", + "26458716559295118", + "991425305450233979", + "75224256272777400", + "4574037116554085", + "2392606045480815", + "2674081024811683", + "211174977267030", + "12384967722862015", + "5770305768458460", + "350928995983350460" + ] + }, + { + "genre": "dance", + "subGenre": "male street", + "pinIds": [ + "121808364916249150", + "281543725363980", + "114771490503714892", + "4222193394406108", + "384987468167147799", + "293578469443960279", + "15129348744112745", + "1407443605843667", + "522487994288188660", + "17029304836280675", + "75294624994420694", + "53691420544479148", + "13440498882101766", + "460000549462566787", + "15551561209729869", + "2322237301691943", + "710231803777552291", + "2955556002835825", + "414683078210574425", + "49961877110595847" + ] + }, + { + "genre": "dance", + "subGenre": "idol", + "pinIds": [ + "479070479128993304", + "9710955441859177", + "12666442696319204", + "73535406411990426", + "2251868558199764", + "60517188737112937", + "10625749119266659", + "78320481016042577", + "35043703346687880", + "303500462409467861", + "17944098509782080", + "9922061674449928", + "872150284107867457", + "872713234061332018", + "11610911541352906", + "29062360090228192", + "28640147624756440", + "15762667443251492", + "21532904464086875", + "1196337404177142" + ] + }, + { + "genre": "dance", + "subGenre": "hiphop horizontal", + "pinIds": [ + "22588435625158407", + "111253053268402821", + "579697783324182197", + "42080577764442272", + "46021227437082457", + "53198839342539022", + "26388347810401564", + "22518066879964986", + "61854194877053486", + "15903404929100314", + "3096293468380704", + "74027987620325960", + "164029611421877250", + "136937644915997992", + "73183562689288232", + "40673202878375483", + "362117626307328064", + "52143308179831724", + "22588435626452959", + "43699058880991705" + ] + }, + { + "genre": "dance", + "subGenre": "hiphop group horizontal", + "pinIds": [ + "22588435625158407", + "53198839342539022", + "42080577764442272", + "61080138757501026", + "22799541855295233", + "424816177367063497", + "26388347810401564", + "43699058880991705", + "22588435626452959", + "27232772741180313", + "52143308179831724", + "5488830791814440", + "359162139052573524", + "7951736836765738", + "3096293468380704", + "3729612228764589", + "29203097580024326", + "1970393582251716", + "15903404929100314", + "61854194877053486" + ] + }, + { + "genre": "dance", + "subGenre": "tiktok horizontal", + "pinIds": [ + "73465037666211331", + "43699058880991705", + "22799541855295233", + "392235448816403495", + "672514156895739901", + "53198839342539022", + "43980533856526242", + "744782857168620985", + "1970393579106717", + "1010002653945282381", + "17662623533356278", + "960885270479179006", + "216735800826589240", + "4011087181038067", + "2603712281876303", + "140806234329989", + "588071663875453228", + "71002131617949973", + "995928905078221437", + "924715735964273693" + ] + }, + { + "genre": "dance", + "subGenre": "female street horizontal", + "pinIds": [ + "6192518231726746", + "292522938316891256", + "18436679719411895", + "21955116928732000", + "3448137208807749", + "62768988546993074", + "4362930883292424", + "376824693841084142", + "77335318596618908", + "295408056839351760", + "10766486605294157", + "27162403996724334", + "16677461115817962", + "337558934569327291", + "507217976803695634", + "19562579625841321", + "67976275700800180", + "759912137109453867", + "4785143351454311", + "11540542792451857" + ] + }, + { + "genre": "dance", + "subGenre": "male street horizontal", + "pinIds": [ + "114771490503714892", + "293578469443960279", + "46021227437082457", + "545709679863479586", + "21110691999438756", + "37365871902405484", + "1900024839288376", + "522487994288188660", + "441352832253442625", + "30117891245977002", + "2814818511674258", + "39476934229200399", + "313844667795737371", + "9992430417137567", + "2955556002835825", + "12033123999019109", + "15551561209715503", + "91620173666114006", + "395190936060863771", + "91338698687797380" + ] + }, + { + "genre": "dance", + "subGenre": "idol horizontal", + "pinIds": [ + "56646907808073030", + "22799541855295233", + "433893745365981433", + "43699058880991705", + "1759287344686459", + "568931365446363211", + "598486238021077046", + "911416043332185169", + "9077636744649113", + "671177150754503568", + "404901822771298882", + "53480314319393838", + "582231058114702827", + "74379831361145666", + "73465037666211331", + "10977592836583413", + "341007003057403086", + "776308054556029222", + "208995238951563833", + "11399805457139263" + ] } ] \ No newline at end of file diff --git a/src/piterest_styletransfer_video.ts b/src/piterest_styletransfer_video.ts index f85b207..fb7c4f2 100644 --- a/src/piterest_styletransfer_video.ts +++ b/src/piterest_styletransfer_video.ts @@ -358,29 +358,16 @@ async function getPinUrlFromPinterest(keyword: string): Promise { } /* + allKeywords = allKeywords.filter(a => { + return (a.genre == "dance" && a.subGenre == "women tiktok") || + (a.genre == "dance" && a.subGenre == "street real") || + (a.genre == "dance" && a.subGenre == "man street") || + (a.genre == "dance" && a.subGenre == "group street") || + (a.genre == "dance" && a.subGenre == "group street") || + }); + */ allKeywords = allKeywords.filter(a => { - return (a.genre == "city" && a.subGenre == "Bridges") || - (a.genre == "city" && a.subGenre == "Castles") || - (a.genre == "city" && a.subGenre == "Cathedrals") || - (a.genre == "city" && a.subGenre == "Factories") || - (a.genre == "city" && a.subGenre == "Futuristic Cities") || - (a.genre == "city" && a.subGenre == "Historic Towns") || - (a.genre == "city" && a.subGenre == "Libraries") || - (a.genre == "city" && a.subGenre == "Markets") || - (a.genre == "city" && a.subGenre == "Modern Plazas") || - (a.genre == "city" && a.subGenre == "Museums") || - (a.genre == "city" && a.subGenre == "Palaces") || - (a.genre == "city" && a.subGenre == "Residential Blocks") || - (a.genre == "city" && a.subGenre == "Skylines") || - (a.genre == "city" && a.subGenre == "Stadiums") || - (a.genre == "city" && a.subGenre == "Street Cafes") || - (a.genre == "city" && a.subGenre == "Urban Parks") || - (a.genre == "city" && a.subGenre == "Skyscrapers") || - (a.genre == "city" && a.subGenre == "Slums") - }); - */ - allKeywords = allKeywords.filter(a => { - return (a.genre == "epic") + return (a.genre == "dance") || (a.genre == "epic") }); function shuffle(arr: T[]): T[] { @@ -392,7 +379,7 @@ async function getPinUrlFromPinterest(keyword: string): Promise { } //const selectedEntries = shuffle(allKeywords.slice()).slice(0, Math.min(20, allKeywords.length)); - const selectedEntries = allKeywords; + const selectedEntries = shuffle(allKeywords); // Download up to `count` images from a pin URL by opening the pin page and scro lling up to 5 times to trigger lazy loading // Returns an array of saved image paths (may be empty) @@ -559,83 +546,119 @@ async function getPinUrlFromPinterest(keyword: string): Promise { // --- Image Generation Phase --- logger.info(`--- Starting image generation for ${generationTasks.length} tasks ---`); - for (const task of generationTasks) { - const server = servers[Math.floor(Math.random() * servers.length)]; - const imagePath = await generateImageForTask(task, server); - if (imagePath) { - task.generatedImagePath = imagePath; - } + if (generationTasks.length > 0) { + // Distribute tasks evenly across servers (round-robin) and run each server's tasks concurrently. + const tasksByServer: GenerationTask[][] = servers.map(() => []); + generationTasks.forEach((t, idx) => { + const serverIndex = idx % servers.length; + tasksByServer[serverIndex].push(t); + }); + + await Promise.all(servers.map(async (server, serverIndex) => { + const tasks = tasksByServer[serverIndex]; + if (!tasks || tasks.length === 0) return; + logger.info(`Server ${server.baseUrl} starting ${tasks.length} image task(s)`); + const results = await Promise.all(tasks.map(t => generateImageForTask(t, server))); + for (let i = 0; i < tasks.length; i++) { + const res = results[i]; + if (res) tasks[i].generatedImagePath = res; + } + logger.info(`Server ${server.baseUrl} finished image task(s)`); + })); } logger.info("--- Finished image generation ---"); // --- Video Generation Phase --- logger.info(`--- Starting video generation for ${generationTasks.length} tasks ---`); - for (const task of generationTasks) { - if (!task.generatedImagePath) { - logger.warn(`Skipping video generation for task ${task.imageFileName} as it has no generated image.`); - continue; - } - const server = servers[Math.floor(Math.random() * servers.length)]; - const inputDir = server.outputDir.replace("output", "input"); - const generatedImageName = path.basename(task.generatedImagePath); - const serverImagePath = path.join(inputDir, generatedImageName); + // Prepare tasks that have generatedImagePath + const tasksWithImages = generationTasks.filter(t => t.generatedImagePath); + if (tasksWithImages.length > 0) { + // Distribute video tasks evenly across servers and run them concurrently per server. + const videoTasksByServer: GenerationTask[][] = servers.map(() => []); + tasksWithImages.forEach((t, idx) => { + const serverIndex = idx % servers.length; + videoTasksByServer[serverIndex].push(t); + }); - try { - await fs.copyFile(task.generatedImagePath, serverImagePath); - logger.info(`Copied ${task.generatedImagePath} to ${serverImagePath}`); + await Promise.all(servers.map(async (server, serverIndex) => { + const tasks = videoTasksByServer[serverIndex]; + if (!tasks || tasks.length === 0) return; + logger.info(`Server ${server.baseUrl} starting ${tasks.length} video task(s)`); - const videoFileName = task.imageFileName.replace('.png', '.mp4'); - const videoPath = await generateVideo( - task.videoPrompt, - generatedImageName, - videoFileName, - server.baseUrl, - server.outputDir, - { width: 1280, height: 720 } - ); + // Run all tasks for this server concurrently + await Promise.allSettled(tasks.map(async (task) => { + const inputDir = server.outputDir.replace("output", "input"); + if (!task.generatedImagePath) { + logger.warn(`Skipping task ${task.imageFileName} on server ${server.baseUrl} - missing generatedImagePath`); + return; + } + const generatedImageName = path.basename(task.generatedImagePath); + const serverImagePath = path.join(inputDir, generatedImageName); + try { + // copy image to server input dir + await fs.copyFile(task.generatedImagePath, serverImagePath); + logger.info(`Copied ${task.generatedImagePath} to ${serverImagePath}`); - if (videoPath) { - const videoData = { - genre: task.genre, - sub_genre: task.subGenre, - scene: task.scene, - action: task.action, - camera: task.camera, - image_prompt: task.imagePrompt, - video_prompt: task.videoPrompt, - image_path: task.generatedImagePath, - video_path: videoPath, - }; - const videoId = await VideoModel.create(videoData); - logger.info(`Successfully saved video record to database with ID: ${videoId}`); + const videoFileName = task.imageFileName.replace('.png', '.mp4'); + const videoPath = await generateVideo( + task.videoPrompt, + generatedImageName, + videoFileName, + server.baseUrl, + server.outputDir, + { width: 1280, height: 720 } + ); - const newImageName = `${videoId}_${task.genre}_${task.subGenre}${path.extname(task.generatedImagePath)}`; - const newVideoName = `${videoId}_${task.genre}_${task.subGenre}${path.extname(videoPath)}`; + if (videoPath) { + const videoData = { + genre: task.genre, + sub_genre: task.subGenre, + scene: task.scene, + action: task.action, + camera: task.camera, + image_prompt: task.imagePrompt, + video_prompt: task.videoPrompt, + image_path: task.generatedImagePath!, + video_path: videoPath, + }; + const videoId = await VideoModel.create(videoData); + logger.info(`Successfully saved video record to database with ID: ${videoId}`); - const newImagePath = path.join(path.dirname(task.generatedImagePath), newImageName); - const newVideoPath = path.join(path.dirname(videoPath), newVideoName); + const newImageName = `${videoId}_${task.genre}_${task.subGenre}${path.extname(task.generatedImagePath)}`; + const newVideoName = `${videoId}_${task.genre}_${task.subGenre}${path.extname(videoPath)}`; - await fs.rename(task.generatedImagePath, newImagePath); - await fs.rename(videoPath, newVideoPath); + const newImagePath = path.join(path.dirname(task.generatedImagePath), newImageName); + const newVideoPath = path.join(path.dirname(videoPath), newVideoName); - await VideoModel.update(videoId, { - image_path: newImagePath, - video_path: newVideoPath, - }); - logger.info(`Renamed files and updated database record for video ID: ${videoId}`); - } - } catch (error) { - logger.error('An error occurred during video generation or database operations:', error); - } finally { - try { - await fs.unlink(serverImagePath); - logger.debug(`Deleted server image: ${serverImagePath}`); - } catch (error) { - logger.error(`Failed to delete server image ${serverImagePath}:`, error); - } - } + await fs.rename(task.generatedImagePath, newImagePath); + await fs.rename(videoPath, newVideoPath); + + await VideoModel.update(videoId, { + image_path: newImagePath, + video_path: newVideoPath, + }); + logger.info(`Renamed files and updated database record for video ID: ${videoId}`); + } else { + logger.warn(`Video generation returned no path for task ${task.imageFileName} on server ${server.baseUrl}`); + } + } catch (error) { + logger.error('An error occurred during video generation or database operations:', error); + } finally { + try { + await fs.unlink(serverImagePath); + logger.debug(`Deleted server image: ${serverImagePath}`); + } catch (error) { + logger.error(`Failed to delete server image ${serverImagePath}:`, error); + } + } + })); + logger.info(`Server ${server.baseUrl} finished video task(s)`); + })); + } else { + logger.info('No generated images available for video generation.'); } + logger.info("--- Finished video generation ---"); if (RUN_ONCE) {