您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Allows queuing multiple prompts and images for HailuoAI Video generator and adds bulk download functionality with enhanced queue control features. Visit https://github.com/BillarySquintin/Billy_Scripts/
// ==UserScript== // @name HailuoAI Minimax Video Prompt Queue and Bulk Download // @namespace http://tampermonkey.net/ // @version 3.15 // @description Allows queuing multiple prompts and images for HailuoAI Video generator and adds bulk download functionality with enhanced queue control features. Visit https://github.com/BillarySquintin/Billy_Scripts/ // @author Billary // @match https://hailuoai.video/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; if (!window.location.href.startsWith('https://hailuoai.video/')) { return; } // Create a container for the buttons const buttonContainer = document.createElement('div'); buttonContainer.style.position = 'fixed'; buttonContainer.style.top = '10px'; buttonContainer.style.left = '0'; buttonContainer.style.right = '0'; buttonContainer.style.zIndex = '1000'; buttonContainer.style.textAlign = 'center'; buttonContainer.style.display = 'flex'; buttonContainer.style.justifyContent = 'center'; buttonContainer.style.alignItems = 'center'; document.body.appendChild(buttonContainer); // Create the Selected Videos Counter const selectedCounter = document.createElement('div'); selectedCounter.textContent = ''; selectedCounter.style.backgroundColor = '#007BFF'; selectedCounter.style.color = 'white'; selectedCounter.style.padding = '5px 10px'; selectedCounter.style.borderRadius = '5px'; selectedCounter.style.marginRight = '10px'; selectedCounter.style.display = 'none'; // Initially hidden buttonContainer.appendChild(selectedCounter); // Create the Prompt Queue button const queueButton = document.createElement("button"); queueButton.innerHTML = "Prompt Queue"; queueButton.style.backgroundColor = "#4CAF50"; queueButton.style.color = "white"; queueButton.style.border = "none"; queueButton.style.padding = "10px"; queueButton.style.borderRadius = "5px"; queueButton.style.cursor = "pointer"; queueButton.style.margin = '0 5px'; buttonContainer.appendChild(queueButton); // Create the Bulk Download button const bulkDownloadButton = document.createElement("button"); bulkDownloadButton.innerHTML = "Bulk Download"; bulkDownloadButton.style.backgroundColor = "#4CAF50"; bulkDownloadButton.style.color = "white"; bulkDownloadButton.style.border = "none"; bulkDownloadButton.style.padding = "10px"; bulkDownloadButton.style.borderRadius = "5px"; bulkDownloadButton.style.cursor = "pointer"; bulkDownloadButton.style.margin = '0 5px'; buttonContainer.appendChild(bulkDownloadButton); // Create the Toggle Autoplay button const toggleAutoplayButton = document.createElement("button"); toggleAutoplayButton.innerHTML = "Disable Autoplay"; toggleAutoplayButton.style.backgroundColor = "#4CAF50"; toggleAutoplayButton.style.color = "white"; toggleAutoplayButton.style.border = "none"; toggleAutoplayButton.style.padding = "10px"; toggleAutoplayButton.style.borderRadius = "5px"; toggleAutoplayButton.style.cursor = "pointer"; toggleAutoplayButton.style.margin = '0 5px'; buttonContainer.appendChild(toggleAutoplayButton); // Create the Pause All Videos button const pauseAllButton = document.createElement("button"); pauseAllButton.innerHTML = "Pause All Videos"; pauseAllButton.style.backgroundColor = "#4CAF50"; pauseAllButton.style.color = "white"; pauseAllButton.style.border = "none"; pauseAllButton.style.padding = "10px"; pauseAllButton.style.borderRadius = "5px"; pauseAllButton.style.cursor = "pointer"; pauseAllButton.style.margin = '0 5px'; buttonContainer.appendChild(pauseAllButton); // Function to generate a random delay between min and max milliseconds function getRandomDelay(minMs, maxMs) { return Math.floor(Math.random() * (maxMs - minMs + 1)) + minMs; } // Variable to track autoplay state let isAutoplayEnabled = true; // Function to update video autoplay settings function updateVideoAutoplay() { let videoElements = document.querySelectorAll('div.video-cards video'); videoElements.forEach(function(video) { if (isAutoplayEnabled) { video.setAttribute('autoplay', ''); video.muted = true; } else { video.removeAttribute('autoplay'); video.pause(); } }); } // Function to handle video hover events function handleVideoHover(event) { if (!isAutoplayEnabled) { event.preventDefault(); event.stopPropagation(); } } // Function to add hover event listeners to video cards function addHoverListeners() { let videoCards = document.querySelectorAll('div.video-cards'); videoCards.forEach(function(card) { if (!card.getAttribute('data-hover-listener-added')) { card.addEventListener('mouseenter', handleVideoHover, true); card.addEventListener('mouseover', handleVideoHover, true); card.setAttribute('data-hover-listener-added', 'true'); } }); } // Observer to watch for new video elements being added to the page const observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { updateVideoAutoplay(); addHoverListeners(); } }); }); // Start observing the body for changes in the child elements observer.observe(document.body, { childList: true, subtree: true }); // Initial setup updateVideoAutoplay(); addHoverListeners(); // Event listener for Toggle Autoplay button toggleAutoplayButton.addEventListener('click', function() { isAutoplayEnabled = !isAutoplayEnabled; toggleAutoplayButton.innerHTML = isAutoplayEnabled ? "Disable Autoplay" : "Enable Autoplay"; updateVideoAutoplay(); }); // Event listener for Pause All Videos button pauseAllButton.addEventListener('click', function() { let videoElements = document.querySelectorAll('div.video-cards video'); videoElements.forEach(function(video) { video.pause(); }); }); // Create a container div for the prompt queue var container = document.createElement('div'); container.style.position = 'fixed'; container.style.top = '50px'; container.style.left = '50%'; container.style.transform = 'translateX(-50%)'; container.style.zIndex = '10000'; container.style.backgroundColor = 'white'; container.style.border = '1px solid black'; container.style.padding = '10px'; container.style.maxWidth = '400px'; container.style.overflowY = 'auto'; container.style.boxShadow = '0px 0px 10px rgba(0,0,0,0.5)'; container.style.borderRadius = '8px'; container.style.display = 'none'; // Create a close button for the container var closeButton = document.createElement('button'); closeButton.textContent = 'X'; closeButton.style.position = 'absolute'; closeButton.style.top = '5px'; closeButton.style.right = '5px'; closeButton.style.background = 'transparent'; closeButton.style.border = 'none'; closeButton.style.fontSize = '16px'; closeButton.style.cursor = 'pointer'; closeButton.addEventListener('click', function() { container.style.display = 'none'; }); container.appendChild(closeButton); // Create a label for the textarea var label = document.createElement('label'); label.textContent = 'Enter your prompts here (JSON format for images), separated by "---":'; label.style.display = 'block'; label.style.marginBottom = '5px'; container.appendChild(label); // Create a textarea for prompts var textarea = document.createElement('textarea'); textarea.rows = 10; textarea.cols = 40; textarea.placeholder = 'Enter your prompts here, separated by "---"'; textarea.style.width = '100%'; textarea.style.boxSizing = 'border-box'; container.appendChild(textarea); // Create a label and input for Repeat Count var repeatLabel = document.createElement('label'); repeatLabel.textContent = 'Repeat Count (default 1, "~" for infinity):'; repeatLabel.style.display = 'block'; repeatLabel.style.marginTop = '10px'; container.appendChild(repeatLabel); var repeatInput = document.createElement('input'); repeatInput.type = 'text'; repeatInput.value = '1'; repeatInput.style.width = '100%'; container.appendChild(repeatInput); // Create a label and input for Starting Prompt Number var startPromptLabel = document.createElement('label'); startPromptLabel.textContent = 'Starting Prompt Number (default 1):'; startPromptLabel.style.display = 'block'; startPromptLabel.style.marginTop = '10px'; container.appendChild(startPromptLabel); var startPromptInput = document.createElement('input'); startPromptInput.type = 'number'; startPromptInput.value = '1'; startPromptInput.min = '1'; startPromptInput.style.width = '100%'; container.appendChild(startPromptInput); // Create a label to display the current batch number var batchLabel = document.createElement('label'); batchLabel.textContent = 'Current Batch: 0'; batchLabel.style.display = 'block'; batchLabel.style.marginTop = '10px'; container.appendChild(batchLabel); // Create a label to display the current prompt number var promptCounterLabel = document.createElement('label'); promptCounterLabel.textContent = 'Current Prompt: 0 / 0'; promptCounterLabel.style.display = 'block'; promptCounterLabel.style.marginTop = '10px'; container.appendChild(promptCounterLabel); // Create control buttons (Pause, Back, Hold) var controlButtonsContainer = document.createElement('div'); controlButtonsContainer.style.marginTop = '10px'; container.appendChild(controlButtonsContainer); var pauseButton = document.createElement('button'); pauseButton.textContent = 'Pause Queue'; pauseButton.style.marginRight = '10px'; pauseButton.style.padding = '5px 10px'; pauseButton.style.cursor = 'pointer'; controlButtonsContainer.appendChild(pauseButton); var backButton = document.createElement('button'); backButton.textContent = 'Go Back One'; backButton.style.marginRight = '10px'; backButton.style.padding = '5px 10px'; backButton.style.cursor = 'pointer'; controlButtonsContainer.appendChild(backButton); var holdButton = document.createElement('button'); holdButton.textContent = 'Hold Current Prompt'; holdButton.style.padding = '5px 10px'; holdButton.style.cursor = 'pointer'; controlButtonsContainer.appendChild(holdButton); // Create an "Add Images" button var addImagesButton = document.createElement('button'); addImagesButton.textContent = 'Add Images'; addImagesButton.style.marginTop = '10px'; addImagesButton.style.marginRight = '10px'; addImagesButton.style.padding = '5px 10px'; addImagesButton.style.cursor = 'pointer'; container.appendChild(addImagesButton); // Create a start button var startButton = document.createElement('button'); startButton.textContent = 'Start Queue'; startButton.style.marginTop = '10px'; startButton.style.padding = '5px 10px'; startButton.style.cursor = 'pointer'; container.appendChild(startButton); // Append the container to the body document.body.appendChild(container); // Add an event listener to the queueButton to toggle the prompt queue container queueButton.addEventListener('click', function() { container.style.display = (container.style.display === 'none') ? 'block' : 'none'; }); // Store images added via "Add Images" button var imagesMap = new Map(); // Handle adding images through file selection addImagesButton.addEventListener('click', function() { var fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = '.jpg,.jpeg,.png'; fileInput.multiple = true; fileInput.style.display = 'none'; fileInput.addEventListener('change', function(event) { var files = event.target.files; if (files.length === 0) return; Array.from(files).forEach(function(file) { imagesMap.set(file.name, file); }); alert('Images have been added. Please ensure the filenames in your JSON input match the selected images.'); }); fileInput.click(); }); // Initialize variables var promptQueue = []; var originalPromptQueue = []; var totalRepeatCount = 1; var currentRepeat = 0; var startingPromptIndex = 1; var currentPromptNumber = 0; var totalPromptCount = 0; var totalPrompts = 0; var isPaused = false; var isHolding = false; // Now, when the start button is clicked, we start processing the prompts startButton.addEventListener('click', function() { // Clear previous queue promptQueue = []; var content = textarea.value.trim(); if (!content) { alert('Please enter prompts or image-prompt pairs in the textarea.'); return; } var repeatCountInput = repeatInput.value.trim(); var repeatCount = 1; if (repeatCountInput === '~') { repeatCount = Infinity; } else { repeatCount = parseInt(repeatCountInput, 10); if (isNaN(repeatCount) || repeatCount < 1) { repeatCount = 1; } } var startPromptInputValue = parseInt(startPromptInput.value.trim(), 10); startingPromptIndex = isNaN(startPromptInputValue) || startPromptInputValue < 1 ? 1 : startPromptInputValue; var items = content.split('---').map(item => item.trim()).filter(item => item.length > 0); totalPrompts = items.length; processTextareaItems(items, function() { if(promptQueue.length === 0) { alert('No valid prompts or images to process.'); return; } // Duplicate the promptQueue based on repeat count totalRepeatCount = repeatCount; currentRepeat = 0; originalPromptQueue = promptQueue.slice(); // Make a copy of the original prompts // Initialize prompt counters totalPromptCount = originalPromptQueue.length; // Adjust the prompt queue based on the starting prompt index startingPromptIndex = Math.min(startingPromptIndex, promptQueue.length); startingPromptIndex = Math.max(1, startingPromptIndex); promptQueue = promptQueue.slice(startingPromptIndex - 1); // Update the batch label batchLabel.textContent = 'Current Batch: ' + (currentRepeat + 1); // Set the current prompt number currentPromptNumber = startingPromptIndex; updatePromptCounterLabel(); // Disable the start button to prevent multiple clicks startButton.disabled = true; // Start processing the prompts processPrompts(); }); }); // Function to update the prompt counter label function updatePromptCounterLabel() { promptCounterLabel.textContent = 'Current Prompt: ' + currentPromptNumber + ' / ' + totalPromptCount; } // Function to process items from textarea function processTextareaItems(items, callback) { items.forEach(function(item) { try { // Try to parse item as JSON var jsonItem = JSON.parse(item); if (Array.isArray(jsonItem)) { jsonItem.forEach(function(entry) { processJsonEntry(entry); }); } else if (typeof jsonItem === 'object' && jsonItem !== null) { // Single JSON object processJsonEntry(jsonItem); } else { alert('Invalid JSON format. Expected an object or an array of objects.'); } } catch (e) { // Not JSON, treat as text prompt promptQueue.push({ type: 'text', content: item }); } }); callback(); } // Function to check for the specific error message //function checkForErrorMessage(callback) { // // Check for the error message element // var errorMessageElement = document.querySelector('div.adm-auto-center-content'); // if (errorMessageElement && errorMessageElement.textContent.includes('An error occurred while generating the content, please try again')) { // callback(true); // } else { // callback(false); // } //} function processJsonEntry(entry) { if (entry.filename && entry.prompt) { // Check if the image has been added via "Add Images" if (imagesMap.has(entry.filename)) { promptQueue.push({ type: 'image', file: imagesMap.get(entry.filename), prompt: entry.prompt }); } else { alert(`Image "${entry.filename}" has not been added via "Add Images". Please add it.`); } } else { alert('Invalid JSON entry. Each entry must have "filename" and "prompt".'); } } // Function to set the value of a textarea in a way that React recognizes function setNativeValue(element, value) { const lastValue = element.value; element.value = value; const event = new Event('input', { bubbles: true }); // Hack to make React notice the change const tracker = element._valueTracker; if (tracker) { tracker.setValue(lastValue); } element.dispatchEvent(event); } // Updated processPrompts function with random delay function processPrompts() { // If paused or holding, wait and retry if (isPaused || isHolding) { setTimeout(processPrompts, 1000); return; } if (promptQueue.length === 0) { currentRepeat++; if (currentRepeat < totalRepeatCount || totalRepeatCount === Infinity) { // Reset the promptQueue to original and start over promptQueue = originalPromptQueue.slice(); // Update the batch label batchLabel.textContent = 'Current Batch: ' + (currentRepeat + 1); // Reset the current prompt number currentPromptNumber = 1; updatePromptCounterLabel(); processPrompts(); } else { alert('All prompts have been processed.'); startButton.disabled = false; // Reset the batch label and prompt counter batchLabel.textContent = 'Current Batch: 0'; promptCounterLabel.textContent = 'Current Prompt: 0 / 0'; } return; } var item = promptQueue.shift(); // Update current prompt number updatePromptCounterLabel(); // Wait for queue space before proceeding waitForQueueSpace(function() { // Remove any uploaded image before processing the next prompt removeUploadedImage(); if (item.type === 'text') { processTextPrompt(item.content, function() { currentPromptNumber++; // Wait between 10 and 45 seconds before proceeding to the next prompt var delay = getRandomDelay(10000, 45000); console.log('Waiting for ' + (delay / 1000) + ' seconds before processing the next prompt.'); setTimeout(processPrompts, delay); }); } else if (item.type === 'image') { revealUploadPane(function() { processImagePrompt(item, function() { currentPromptNumber++; // Wait between 10 and 45 seconds before proceeding to the next prompt var delay = getRandomDelay(10000, 45000); console.log('Waiting for ' + (delay / 1000) + ' seconds before processing the next prompt.'); setTimeout(processPrompts, delay); }); }); } }); } // Updated waitForQueueSpace function function waitForQueueSpace(callback) { var checkQueue = setInterval(function() { // If paused or holding, wait and retry if (isPaused || isHolding) { return; // Do nothing, stay in the interval } var submitButtonLoading = document.querySelector('img[src="/assets/img/dark-loading.png"]'); var submitButtonReady = false; var queueCounterFound = false; var queueTextCurrentSize = 0; var queueTextMaxSize = 5; // Default to 5 if not found // Check if submit button is not loading if (!submitButtonLoading) { submitButtonReady = true; } else { console.log('Submit button is loading. Waiting...'); } // Try to get queue size from the queue counter var queueCounterElements = document.querySelectorAll('div.bg-clip-text'); queueCounterElements.forEach(function(element) { var textContent = element.textContent.replace(/\s+/g, ''); var numbers = textContent.match(/\d+/g); if (numbers && numbers.length === 2) { queueTextCurrentSize = parseInt(numbers[0], 10); queueTextMaxSize = parseInt(numbers[1], 10); queueCounterFound = true; } }); // If we didn't find the counter with numbers, try to get queue size from the message text if (!queueCounterFound) { var messageElement = document.querySelector('div.font-medium'); if (messageElement) { var messageText = messageElement.textContent.trim(); var match = messageText.match(/(\d+)\s+jobs\s+in\s+queue/); if (match) { queueTextCurrentSize = parseInt(match[1], 10); queueTextMaxSize = 5; // Assuming max queue size is 5 queueCounterFound = true; } else if (messageText.includes('you can queue')) { // Assuming queue is empty when the message says 'you can queue 5 jobs at once' queueTextCurrentSize = 0; queueTextMaxSize = 5; queueCounterFound = true; } } else { console.log('Queue message element not found.'); } } // Count number of queued videos (waiting to be processed) by class 'video-gen-loading' var queuedVideoElements = document.querySelectorAll('div.video-gen-loading'); var queuedVideoCount = queuedVideoElements.length; // Count number of processing videos (currently being processed) by elements with 'role="progressbar"' var processingVideoElements = document.querySelectorAll('[role="progressbar"]'); var processingVideoCount = processingVideoElements.length; // Output the counts for debugging console.log('Submit button ready:', submitButtonReady); console.log('Queue counter found:', queueCounterFound); console.log('Queue counter size:', queueTextCurrentSize + '/' + queueTextMaxSize); console.log('Queued video count:', queuedVideoCount); console.log('Processing video count:', processingVideoCount); // Decide on the current queue size var totalQueueSize = queuedVideoCount + processingVideoCount; var maxQueueSize = Math.max(queueTextMaxSize, 5); if (totalQueueSize < maxQueueSize && submitButtonReady) { clearInterval(checkQueue); // Wait for a random delay before proceeding var delay = getRandomDelay(10000, 45000); console.log('Waiting for ' + (delay / 1000) + ' seconds before submitting the next prompt.'); setTimeout(callback, delay); } else { console.log('Queue is full (' + totalQueueSize + '/' + maxQueueSize + ') or submit button not ready. Waiting...'); } }, 2000); } // Function to check for error messages function checkForErrorMessage(callback) { var errorElement = document.querySelector('.web-toast.error'); if (errorElement) { callback(true); // Optionally remove the error element to prevent duplicate detections errorElement.remove(); } else { callback(false); } } // Updated processTextPrompt function with retry logic function processTextPrompt(prompt, callback, retryCount = 0) { var maxRetries = 3; var promptTextarea = document.querySelector('.description_wrap textarea'); if(!promptTextarea) { alert('Could not find prompt textarea on the page.'); startButton.disabled = false; return; } setNativeValue(promptTextarea, prompt); waitForSubmitButton(function(submitButton) { submitButton.click(); // Wait for potential error message setTimeout(function() { checkForErrorMessage(function(hasError) { if (hasError) { if (retryCount < maxRetries) { console.log('Error occurred. Retrying prompt in 3 seconds. Retry count: ' + (retryCount + 1)); setTimeout(function() { processTextPrompt(prompt, callback, retryCount + 1); }, 3000); } else { console.log('Max retries reached for this prompt. Moving to next prompt.'); callback(); } } else { // Proceed to the next prompt callback(); } }); }, 3000); }); } // Updated processImagePrompt function with submission error handling function processImagePrompt(item, callback, retryCount = 0) { var maxRetries = 3; var promptTextarea = document.querySelector('.description_wrap textarea'); if (!promptTextarea) { alert('Could not find prompt textarea on the page.'); startButton.disabled = false; return; } setNativeValue(promptTextarea, item.prompt); uploadImage(item.file, function(success) { if (!success) { alert('Failed to upload image.'); callback(); return; } waitForSubmitButton(function(submitButton) { submitButton.click(); // Wait for potential error message setTimeout(function() { checkForErrorMessage(function(hasError) { if (hasError) { if (retryCount < maxRetries) { console.log('Error occurred. Retrying prompt in 3 seconds. Retry count: ' + (retryCount + 1)); setTimeout(function() { // Remove the image and attempt to upload again removeUploadedImage(); // Open upload pane again if needed revealUploadPane(function() { processImagePrompt(item, callback, retryCount + 1); }); }, 3000); } else { console.log('Max retries reached for this prompt. Removing image and moving to next prompt.'); removeUploadedImage(); callback(); } } else { // Proceed to the next prompt callback(); } }); }, 3000); }); }); } // Function to wait until the image has finished uploading function waitForImageUpload(callback) { var maxRetries = 30; // Adjust retries as necessary var retries = 0; var checkUpload = setInterval(function() { var uploadedImage = document.querySelector('img[alt="uploaded image"]'); if (uploadedImage) { // Get the closest parent div with class 'relative' var parentDiv = uploadedImage.closest('div.relative'); if (parentDiv) { // Check if there is a child div.absolute (the overlay with the spinner or error) var overlayDiv = parentDiv.querySelector('div.absolute'); if (overlayDiv) { // Check if the retry button is present var retryButton = overlayDiv.querySelector('svg.fill-\\[\\#F55762\\]'); if (retryButton) { console.log('Image upload failed. Retrying...'); // Click the retry button var retryButtonParent = retryButton.closest('div.cursor-pointer'); if (retryButtonParent) { retryButtonParent.click(); } else { console.log('Retry button parent not found.'); } } else { console.log('Image is still uploading...'); } } else { clearInterval(checkUpload); console.log('Image upload successful.'); callback(true); } } else { console.log('Cannot find parent div of the uploaded image.'); retries++; } } else { console.log('Uploaded image element not found.'); retries++; } if (retries >= maxRetries) { clearInterval(checkUpload); console.log('Image upload timed out.'); callback(false); } }, 2000); } // Function to remove any uploaded image function removeUploadedImage() { var removeButton = document.querySelector('span.hover\\:cursor-pointer'); if (removeButton) { removeButton.click(); console.log('Uploaded image removed.'); } else { console.log('No uploaded image to remove.'); } } // Updated revealUploadPane function function revealUploadPane(callback) { // Updated selector to match the new element var uploadPaneOpener = document.querySelector('span.inline-block.group-hover\\:hidden'); if (uploadPaneOpener) { uploadPaneOpener.click(); // Wait for the upload pane to appear setTimeout(function() { callback(); }, 1000); // Adjust the delay if necessary } else { alert('Could not find the button to open the upload pane.'); startButton.disabled = false; } } // Function to upload an image function uploadImage(file, callback, uploadRetryCount = 0) { var maxUploadRetries = 3; var uploadInput = document.querySelector('.ant-upload input[type="file"]'); if (!uploadInput) { console.log('Could not find image upload input on the page.'); if (uploadRetryCount < maxUploadRetries) { console.log('Retrying to find the upload input. Retry count: ' + (uploadRetryCount + 1)); setTimeout(function() { uploadImage(file, callback, uploadRetryCount + 1); }, 1000); } else { alert('Failed to find image upload input after multiple attempts.'); callback(false); } return; } var dataTransfer = new DataTransfer(); dataTransfer.items.add(file); uploadInput.files = dataTransfer.files; uploadInput.dispatchEvent(new Event('change', { bubbles: true })); // Wait for image to finish uploading waitForImageUpload(function(success) { if (success) { callback(true); } else { if (uploadRetryCount < maxUploadRetries) { console.log('Image upload failed. Removing image and retrying upload. Retry count: ' + (uploadRetryCount + 1)); removeUploadedImage(); uploadImage(file, callback, uploadRetryCount + 1); } else { alert('Image upload failed after multiple attempts.'); callback(false); } } }); } // Function to check if the submit button is available function isSubmitButtonAvailable() { var submitButton = getSubmitButton(); return submitButton !== null; } // Function to get the submit button function getSubmitButton() { var buttons = document.querySelectorAll('div.cursor-pointer'); for (var i = 0; i < buttons.length; i++) { var img = buttons[i].querySelector('img[alt="hilo ai video create icon"]'); if (img) { return buttons[i]; } } return null; } // Function to wait for the submit button to be available function waitForSubmitButton(callback) { var checkButton = setInterval(function() { if (isSubmitButtonAvailable()) { clearInterval(checkButton); var submitButton = getSubmitButton(); callback(submitButton); } }, 1000); } // Event listeners for control buttons pauseButton.addEventListener('click', function() { isPaused = !isPaused; pauseButton.textContent = isPaused ? 'Resume Queue' : 'Pause Queue'; }); backButton.addEventListener('click', function() { if (currentPromptNumber > 1) { // Move back one prompt currentPromptNumber -= 2; // Subtract 2 because processPrompts will increment it by 1 promptQueue.unshift(originalPromptQueue[currentPromptNumber]); // Re-insert the previous prompt at the beginning updatePromptCounterLabel(); } else { alert('Already at the first prompt.'); } }); holdButton.addEventListener('click', function() { isHolding = !isHolding; holdButton.textContent = isHolding ? 'Release Hold' : 'Hold Current Prompt'; }); // BULK DOWNLOAD FUNCTIONALITY STARTS HERE // Create a container div for the bulk download var bulkDownloadContainer = document.createElement('div'); bulkDownloadContainer.style.position = 'fixed'; bulkDownloadContainer.style.top = '50px'; bulkDownloadContainer.style.left = '50%'; bulkDownloadContainer.style.transform = 'translateX(-50%)'; bulkDownloadContainer.style.zIndex = '10000'; bulkDownloadContainer.style.backgroundColor = 'white'; bulkDownloadContainer.style.border = '1px solid black'; bulkDownloadContainer.style.padding = '10px'; bulkDownloadContainer.style.maxWidth = '400px'; bulkDownloadContainer.style.overflowY = 'auto'; bulkDownloadContainer.style.boxShadow = '0px 0px 10px rgba(0,0,0,0.5)'; bulkDownloadContainer.style.borderRadius = '8px'; bulkDownloadContainer.style.display = 'none'; // Create a close button for the bulk download container var bulkCloseButton = document.createElement('button'); bulkCloseButton.textContent = 'X'; bulkCloseButton.style.position = 'absolute'; bulkCloseButton.style.top = '5px'; bulkCloseButton.style.right = '5px'; bulkCloseButton.style.background = 'transparent'; bulkCloseButton.style.border = 'none'; bulkCloseButton.style.fontSize = '16px'; bulkCloseButton.style.cursor = 'pointer'; bulkCloseButton.addEventListener('click', function() { bulkDownloadContainer.style.display = 'none'; }); bulkDownloadContainer.appendChild(bulkCloseButton); // Create "Download All Visible Videos" button var downloadAllButton = document.createElement('button'); downloadAllButton.textContent = 'Download All Visible Videos'; downloadAllButton.style.marginTop = '10px'; downloadAllButton.style.padding = '5px 10px'; downloadAllButton.style.cursor = 'pointer'; bulkDownloadContainer.appendChild(downloadAllButton); // Create "Select All Visible" button var selectAllButton = document.createElement('button'); selectAllButton.textContent = 'Select All Visible'; selectAllButton.style.marginTop = '10px'; selectAllButton.style.marginRight = '10px'; selectAllButton.style.padding = '5px 10px'; selectAllButton.style.cursor = 'pointer'; bulkDownloadContainer.appendChild(selectAllButton); // Create "Deselect All" button var deselectAllButton = document.createElement('button'); deselectAllButton.textContent = 'Deselect All'; deselectAllButton.style.marginTop = '10px'; deselectAllButton.style.marginRight = '10px'; deselectAllButton.style.padding = '5px 10px'; deselectAllButton.style.cursor = 'pointer'; bulkDownloadContainer.appendChild(deselectAllButton); // Create "Select Videos Between" button var selectBetweenButton = document.createElement('button'); selectBetweenButton.textContent = 'Select Videos Between'; selectBetweenButton.style.marginTop = '10px'; selectBetweenButton.style.marginRight = '10px'; selectBetweenButton.style.padding = '5px 10px'; selectBetweenButton.style.cursor = 'pointer'; bulkDownloadContainer.appendChild(selectBetweenButton); // Create "Download Selected" button var downloadSelectedButton = document.createElement('button'); downloadSelectedButton.textContent = 'Download Selected'; downloadSelectedButton.style.marginTop = '10px'; downloadSelectedButton.style.padding = '5px 10px'; downloadSelectedButton.style.cursor = 'pointer'; bulkDownloadContainer.appendChild(downloadSelectedButton); // Append the bulk download container to the body document.body.appendChild(bulkDownloadContainer); // Add event listener to the bulkDownloadButton to toggle the bulk download container bulkDownloadButton.addEventListener('click', function() { bulkDownloadContainer.style.display = (bulkDownloadContainer.style.display === 'none') ? 'block' : 'none'; // Add checkboxes to videos addCheckboxesToVideos(); // Update the selected counter updateSelectedCounter(); }); // Function to add checkboxes to videos function addCheckboxesToVideos() { let videoElements = document.querySelectorAll('div.video-cards'); videoElements.forEach(function(videoElement, index) { // Check if the checkbox is already added if (!videoElement.querySelector('.video-checkbox')) { // Create a checkbox let checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'video-checkbox'; checkbox.style.position = 'absolute'; checkbox.style.top = '10px'; checkbox.style.left = '10px'; checkbox.style.zIndex = '100'; checkbox.style.transform = 'scale(2)'; // Make the checkbox twice as big // Add event listener to update the counter when checkbox state changes checkbox.addEventListener('change', updateSelectedCounter); // Append the checkbox to the video element videoElement.style.position = 'relative'; // Ensure the video element is positioned relative videoElement.appendChild(checkbox); } }); } // Function to update the selected counter function updateSelectedCounter() { let checkboxes = document.querySelectorAll('.video-checkbox'); let selectedCount = 0; checkboxes.forEach(function(checkbox) { if (checkbox.checked) { selectedCount++; } }); if (selectedCount > 0) { selectedCounter.textContent = 'Selected Videos: ' + selectedCount; selectedCounter.style.display = 'block'; } else { selectedCounter.style.display = 'none'; } } // "Select All Visible" button event listener selectAllButton.addEventListener('click', function() { let checkboxes = document.querySelectorAll('.video-checkbox'); checkboxes.forEach(function(checkbox) { checkbox.checked = true; }); updateSelectedCounter(); }); // "Deselect All" button event listener deselectAllButton.addEventListener('click', function() { let checkboxes = document.querySelectorAll('.video-checkbox'); checkboxes.forEach(function(checkbox) { checkbox.checked = false; }); updateSelectedCounter(); }); // "Select Videos Between" button event listener selectBetweenButton.addEventListener('click', function() { let checkboxes = Array.from(document.querySelectorAll('.video-checkbox')); let selectedIndexes = checkboxes.map((checkbox, index) => checkbox.checked ? index : -1).filter(index => index !== -1); if (selectedIndexes.length < 2) { alert('Please select at least two videos to use this feature.'); return; } let start = Math.min(...selectedIndexes); let end = Math.max(...selectedIndexes); for (let i = start; i <= end; i++) { checkboxes[i].checked = true; } updateSelectedCounter(); }); // "Download Selected" button event listener downloadSelectedButton.addEventListener('click', function() { // Disable the button to prevent multiple clicks downloadSelectedButton.disabled = true; downloadSelectedButton.innerHTML = "Processing..."; // Start processing the selected videos processSelectedVideos(function() { // Re-enable the button after processing downloadSelectedButton.disabled = false; downloadSelectedButton.innerHTML = "Download Selected"; }); }); // "Download All Visible Videos" button event listener downloadAllButton.addEventListener('click', function() { // Disable the button to prevent multiple clicks downloadAllButton.disabled = true; downloadAllButton.innerHTML = "Processing..."; // Start processing the videos processVideos(function() { // Re-enable the button after processing downloadAllButton.disabled = false; downloadAllButton.innerHTML = "Download All Visible Videos"; }); }); // Updated processVideos function with random delay function processVideos(callback) { // Array to hold the video data let videoDataArray = []; // Get all video elements let videoElements = Array.from(document.querySelectorAll('div.video-cards')); if (videoElements.length === 0) { alert('No videos found on the page.'); callback(); return; } // Helper function to process each video element with random delay function processVideoElement(index) { if (index >= videoElements.length) { // All videos processed, save the data and call the callback saveVideoData(videoDataArray); callback(); return; } const videoElement = videoElements[index]; // Extract the prompt let promptElement = videoElement.querySelector('.line-clamp-2'); let prompt = promptElement ? promptElement.textContent.trim() : ''; // Get the download button let downloadButton = videoElement.querySelector('div.flex.items-center.mb-2\\.5.cursor-pointer'); if (downloadButton) { // Simulate a click on the download button to trigger the download logic downloadButton.click(); // Wait for a brief moment to allow any JavaScript to execute setTimeout(function() { // Attempt to retrieve the raw video URL let rawVideoUrl = null; // Get the video tag and replace watermark URL let videoTag = videoElement.querySelector('video'); if (videoTag) { let watermarkedUrl = videoTag.getAttribute('src'); if (watermarkedUrl) { rawVideoUrl = watermarkedUrl.replace('video_watermark', 'video_raw'); } } if (rawVideoUrl) { // Add the data to the array videoDataArray.push({ prompt: prompt, raw_video_url: rawVideoUrl }); } // Move to the next video after a random delay between 500ms and 2500ms var delay = getRandomDelay(500, 2500); console.log('Waiting for ' + delay + ' milliseconds before processing the next video.'); setTimeout(function() { processVideoElement(index + 1); }, delay); }, 500); // Initial 500ms delay after clicking download button } else { console.log('Download button not found for a video.'); // Move to the next video immediately if no download button processVideoElement(index + 1); } } // Start processing the first video element processVideoElement(0); } // Updated processSelectedVideos function with random delay function processSelectedVideos(callback) { // Array to hold the video data let videoDataArray = []; // Get all video elements with selected checkboxes let videoElements = Array.from(document.querySelectorAll('div.video-cards')); let selectedVideoElements = videoElements.filter(function(videoElement) { let checkbox = videoElement.querySelector('.video-checkbox'); return checkbox && checkbox.checked; }); if (selectedVideoElements.length === 0) { alert('No videos selected for download.'); callback(); return; } // Helper function to process each video element with random delay function processVideoElement(index) { if (index >= selectedVideoElements.length) { // All videos processed, save the data and call the callback saveVideoData(videoDataArray); callback(); return; } const videoElement = selectedVideoElements[index]; // Extract the prompt let promptElement = videoElement.querySelector('.line-clamp-2'); let prompt = promptElement ? promptElement.textContent.trim() : ''; // Get the download button let downloadButton = videoElement.querySelector('div.flex.items-center.mb-2\\.5.cursor-pointer'); if (downloadButton) { // Simulate a click on the download button to trigger the download logic downloadButton.click(); // Wait for a brief moment to allow any JavaScript to execute setTimeout(function() { // Attempt to retrieve the raw video URL let rawVideoUrl = null; // Get the video tag and replace watermark URL let videoTag = videoElement.querySelector('video'); if (videoTag) { let watermarkedUrl = videoTag.getAttribute('src'); if (watermarkedUrl) { rawVideoUrl = watermarkedUrl.replace('video_watermark', 'video_raw'); } } if (rawVideoUrl) { // Add the data to the array videoDataArray.push({ prompt: prompt, raw_video_url: rawVideoUrl }); } // Move to the next video after a random delay between 500ms and 2500ms var delay = getRandomDelay(500, 2500); console.log('Waiting for ' + delay + ' milliseconds before processing the next video.'); setTimeout(function() { processVideoElement(index + 1); }, delay); }, 500); // Initial 500ms delay after clicking download button } else { console.log('Download button not found for a video.'); // Move to the next video immediately if no download button processVideoElement(index + 1); } } // Start processing the first selected video element processVideoElement(0); } // Function to save the video data as a JSON file function saveVideoData(data) { if (data.length === 0) { alert('No video data to save.'); return; } // Convert the data to JSON format let jsonData = JSON.stringify(data, null, 2); // Create a blob from the JSON data let blob = new Blob([jsonData], { type: 'application/json' }); // Create a download link let url = URL.createObjectURL(blob); let a = document.createElement('a'); a.href = url; a.download = 'video_data.json'; // Append the link to the body document.body.appendChild(a); // Programmatically click the link to trigger the download a.click(); // Remove the link from the document document.body.removeChild(a); // Revoke the object URL URL.revokeObjectURL(url); alert('Video data has been saved as video_data.json'); } })();