最近使用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
若有業務合作需求,可寫信至: [email protected]
創業、網站經營相關內容未來將發布在 小易創業筆記