最近使用Fooocus,比使用WebUI A1111來的頻繁,主要是設定夠簡單,能省很多步驟,而且生圖速度真的快很多很多,一張1152x896的圖幾秒鐘就好了(Extreme Speed)的模式(顯卡才3060),A1111這尺寸就得跑30秒才能出一張圖。
Fooocus真的很不錯,但是最近想要可以批次生圖,就是可以輸入很多組prompt,自動運行完能自動換下一組prompt自動運行,我找了一圈,都沒找到這功能
去官方github討論區也沒找到解決辦法,原本想用它的API來實現,但找不到文檔,給的gradio api真不知道在寫啥東東....雖然有個API分支(但不太想另外安裝)
最後決定自己寫個外掛腳本,來實現這個批次執行的功能。
首先得先安裝竄改猴(油猴) tampermonkey,這個Chrome擴充工具可以讓我們在指定頁面注入js,這樣可以增加自訂功能。
使用擴充工具的好處是不用改Fooocus源碼,之後Fooocus有新版版也不會因為動了源碼影響升級
壞處是假如Fooocus更新有變動UI的話,這腳本可能就運行不了。但基本上這個概率比較小。
竄改猴
之後在竄改猴新增腳本,將下面的腳本貼入,然後保存起來
// ==UserScript== // @name Fooocus批次提示詞執行任務 // @namespace https://blog.aidec.tw/ // @version 2024-06-25 // @description Fooocus 批次提示詞執行任務功能,可以在批次執行的欄位輸入多筆提示詞,每行一組。 // @author AidecLi // @match http://127.0.0.1:7865/ // @icon https://www.google.com/s2/favicons?sz=64&domain=0.1 // @grant none // ==/UserScript== (function() { 'use strict'; let isProcessing = false; let stopRequested=false; let origPrompts = ''; let promptInputTextarea, processedTextarea, startButton, stopButton, progressBar, statusDiv; // 添加UI元素 function addUI() { const targetElement = document.querySelector('#component-3'); if (!targetElement) { console.error('Target element #component-3 not found'); return; } const container = document.createElement('div'); container.style.marginTop = '20px'; container.style.display = 'flex'; container.style.flexDirection = 'column'; container.style.gap = '10px'; promptInputTextarea = document.createElement('textarea'); promptInputTextarea.id = 'promptList'; promptInputTextarea.rows = 5; promptInputTextarea.style.width = '100%'; promptInputTextarea.placeholder = '待處理的提示詞,每行一組'; processedTextarea = document.createElement('textarea'); processedTextarea.id = 'processedList'; processedTextarea.rows = 5; processedTextarea.style.width = '100%'; processedTextarea.placeholder = '已處理的提示詞'; processedTextarea.readOnly = true; startButton = document.createElement('button'); startButton.textContent = '開始批次處理'; startButton.onclick = processPrompts; startButton.style.padding = '5px 10px'; startButton.style.cursor = 'pointer'; startButton.className = 'lg secondary type_row svelte-cmf5ev'; stopButton = document.createElement('button'); stopButton.textContent = '停止批次處理'; stopButton.onclick = stopProcessing; stopButton.style.padding = '5px 10px'; stopButton.style.cursor = 'pointer'; stopButton.className = 'lg secondary type_row svelte-cmf5ev'; progressBar = document.createElement('progress'); progressBar.id = 'progressBar'; progressBar.value = 0; progressBar.max = 100; progressBar.style.width = '100%'; statusDiv = document.createElement('div'); statusDiv.id = 'statusDiv'; statusDiv.textContent = '狀態:尚未開始'; statusDiv.style.fontWeight = 'bold'; container.appendChild(statusDiv); container.appendChild(promptInputTextarea); container.appendChild(processedTextarea); container.appendChild(startButton); container.appendChild(stopButton); container.appendChild(progressBar); targetElement.appendChild(container); } //處理批次提示詞 async function processPrompts() { if (isProcessing) return; isProcessing = true; stopRequested = false; disableUI(); const promptList = promptInputTextarea.value.split('\n').filter(p => p.trim() !== ''); console.log(promptList); const totalPrompts = promptList.length; const positivePromptTextarea = document.querySelector('#positive_prompt textarea'); const generateButton = document.querySelector('#generate_button'); origPrompts = promptInputTextarea.value; let i = 0; for (const prompt of promptList) { if (stopRequested) break; statusDiv.textContent = `正在處理第 ${i + 1} 組,共 ${totalPrompts} 組 Prompt: ${prompt}`; console.log(prompt); positivePromptTextarea.value = prompt; positivePromptTextarea.dispatchEvent(new Event('input', { bubbles: true })); console.log('開始執行'+prompt) await new Promise(resolve => setTimeout(resolve, 500)); generateButton.click(); await new Promise(resolve => setTimeout(resolve, 1000)); await waitForGeneration(generateButton); if (stopRequested) break; console.log('結束執行'+prompt) // 將已處理的移到已處理區 processedTextarea.value += prompt + '\n'; //將已處理的從待處理區移除 document.getElementById('promptList').value = waitPromptList(promptList,i+1); //更新進度條 progressBar.value = ((i + 1) / totalPrompts) * 100; // 等待3秒再繼續下一個提示詞 statusDiv.textContent = `第 ${i + 1} 組,已處理完畢,等待2秒後進入下一組。`; await new Promise(resolve => setTimeout(resolve, 2000)); i++; } if (stopRequested) { statusDiv.textContent = '處理已被中止'; } else { statusDiv.textContent = '本次所有提示詞已處理完畢'; } isProcessing = false; enableUI(); //還原回原本的Prompt promptInputTextarea.value = origPrompts; } // 停止處理 function stopProcessing() { stopRequested = true; isProcessing = false; enableUI(); //也停止本輪正在運作的生圖 const stopButton = document.querySelector('#stop_button'); stopButton.click(); promptInputTextarea.value = origPrompts; processedTextarea.value = ''; } // 將尚未處理的提示詞合併成字串 function waitPromptList(promptList, currentIndex) { return promptList.slice(currentIndex).join('\n'); } // 禁用UI元素 function disableUI() { promptInputTextarea.disabled = true; startButton.disabled = true; startButton.textContent = '處理中...'; } //啟用UI元素 function enableUI() { promptInputTextarea.disabled = false; startButton.disabled = false; startButton.textContent = '開始批次處理'; } //等待生成過程 function waitForGeneration(button) { return new Promise(resolve => { const observer = new MutationObserver(() => { if (!button.disabled && !button.hidden) { // 等待按鈕 setTimeout(() => { observer.disconnect(); resolve(); }, 1500); } }); observer.observe(button, { attributes: true, attributeFilter: ['disabled', 'hidden'] }); if (stopRequested) resolve(); // 如果按鈕可用且沒有hidden,則繼續 if (!button.disabled && !button.hidden) { observer.disconnect(); resolve(); } }); } //將自訂UI添加到畫面上 function initScript() { const maxAttempts = 10; let attempts = 0; function tryAddUI() { if (document.querySelector('#component-3')) { addUI(); } else if (attempts < maxAttempts) { attempts++; setTimeout(tryAddUI, 1000); // 等待1秒重新嘗試 } else { console.error('Failed to find #component-3 after ' + maxAttempts + ' attempts'); } } tryAddUI(); } // 頁面完成加載UI到畫面 if (document.readyState === 'loading') { window.addEventListener('DOMContentLoaded', initScript); } else { initScript(); } })();
之後在Fooocus重新整理頁面,應該就能使用了~ 完整操作的看下方影片,用寫的怕解釋不清楚。
基本上就是
安裝竄改猴
新增腳本,貼入腳本
在重新整理Fooocus就能看到多了批次處理區
將多筆prompt輸入,按批次執行就可以了
文章轉載或引用,請先告知並保留原文出處與連結!!(單純分享或非營利的只需保留原文出處,不用告知)
原文連結:
https://blog.aidec.tw/post/fooocus-batch-prompt-task-run
若有業務合作需求,可寫信至: opweb666@gmail.com
創業、網站經營相關內容未來將發布在 小易創業筆記