diff --git a/public/scripts/extensions/stable-diffusion/index.js b/public/scripts/extensions/stable-diffusion/index.js index f4e47ca84..eb58fae9f 100644 --- a/public/scripts/extensions/stable-diffusion/index.js +++ b/public/scripts/extensions/stable-diffusion/index.js @@ -48,6 +48,7 @@ const sources = { comfy: 'comfy', togetherai: 'togetherai', drawthings: 'drawthings', + pollinations: 'pollinations', }; const generationMode = { @@ -254,6 +255,10 @@ const defaultSettings = { // ComyUI settings comfy_url: 'http://127.0.0.1:8188', comfy_workflow: 'Default_Comfy_Workflow.json', + + // Pollinations settings + pollinations_enhance: false, + pollinations_refine: false, }; function processTriggers(chat, _, abort) { @@ -383,6 +388,8 @@ async function loadSettings() { $('#sd_novel_sm').prop('checked', extension_settings.sd.novel_sm); $('#sd_novel_sm_dyn').prop('checked', extension_settings.sd.novel_sm_dyn); $('#sd_novel_sm_dyn').prop('disabled', !extension_settings.sd.novel_sm); + $('#sd_pollinations_enhance').prop('checked', extension_settings.sd.pollinations_enhance); + $('#sd_pollinations_refine').prop('checked', extension_settings.sd.pollinations_refine); $('#sd_horde').prop('checked', extension_settings.sd.horde); $('#sd_horde_nsfw').prop('checked', extension_settings.sd.horde_nsfw); $('#sd_horde_karras').prop('checked', extension_settings.sd.horde_karras); @@ -828,6 +835,16 @@ function onNovelSmDynInput() { saveSettingsDebounced(); } +function onPollinationsEnhanceInput() { + extension_settings.sd.pollinations_enhance = !!$('#sd_pollinations_enhance').prop('checked'); + saveSettingsDebounced(); +} + +function onPollinationsRefineInput() { + extension_settings.sd.pollinations_refine = !!$('#sd_pollinations_refine').prop('checked'); + saveSettingsDebounced(); +} + function onHordeNsfwInput() { extension_settings.sd.horde_nsfw = !!$(this).prop('checked'); saveSettingsDebounced(); @@ -1023,7 +1040,7 @@ async function onModelChange() { extension_settings.sd.model = $('#sd_model').find(':selected').val(); saveSettingsDebounced(); - const cloudSources = [sources.horde, sources.novel, sources.openai, sources.togetherai]; + const cloudSources = [sources.horde, sources.novel, sources.openai, sources.togetherai, sources.pollinations]; if (cloudSources.includes(extension_settings.sd.source)) { return; @@ -1188,6 +1205,9 @@ async function loadSamplers() { case sources.togetherai: samplers = ['N/A']; break; + case sources.pollinations: + samplers = ['N/A']; + break; } for (const sampler of samplers) { @@ -1368,6 +1388,9 @@ async function loadModels() { case sources.togetherai: models = await loadTogetherAIModels(); break; + case sources.pollinations: + models = await loadPollinationsModels(); + break; } for (const model of models) { @@ -1384,6 +1407,55 @@ async function loadModels() { } } +async function loadPollinationsModels() { + return [ + { + value: 'pixart', + text: 'PixArt-αlpha', + }, + { + value: 'playground', + text: 'Playground v2', + }, + { + value: 'dalle3xl', + text: 'DALL•E 3 XL', + }, + { + value: 'formulaxl', + text: 'FormulaXL', + }, + { + value: 'dreamshaper', + text: 'DreamShaper', + }, + { + value: 'deliberate', + text: 'Deliberate', + }, + { + value: 'dpo', + text: 'SDXL-DPO', + }, + { + value: 'swizz8', + text: 'Swizz8', + }, + { + value: 'juggernaut', + text: 'Juggernaut', + }, + { + value: 'turbo', + text: 'SDXL Turbo', + }, + { + value: 'realvis', + text: 'Realistic Vision', + }, + ]; +} + async function loadTogetherAIModels() { if (!secret_state[SECRET_KEYS.TOGETHERAI]) { console.debug('TogetherAI API key is not set.'); @@ -1641,6 +1713,9 @@ async function loadSchedulers() { case sources.togetherai: schedulers = ['N/A']; break; + case sources.pollinations: + schedulers = ['N/A']; + break; case sources.comfy: schedulers = await loadComfySchedulers(); break; @@ -1706,6 +1781,9 @@ async function loadVaes() { case sources.togetherai: vaes = ['N/A']; break; + case sources.pollinations: + vaes = ['N/A']; + break; case sources.comfy: vaes = await loadComfyVaes(); break; @@ -2135,6 +2213,9 @@ async function sendGenerationRequest(generationType, prompt, characterName = nul case sources.togetherai: result = await generateTogetherAIImage(prefixedPrompt, negativePrompt); break; + case sources.pollinations: + result = await generatePollinationsImage(prefixedPrompt, negativePrompt); + break; } if (!result.data) { @@ -2181,6 +2262,30 @@ async function generateTogetherAIImage(prompt, negativePrompt) { } } +async function generatePollinationsImage(prompt, negativePrompt) { + const result = await fetch('/api/sd/pollinations/generate', { + method: 'POST', + headers: getRequestHeaders(), + body: JSON.stringify({ + prompt: prompt, + negative_prompt: negativePrompt, + model: extension_settings.sd.model, + width: extension_settings.sd.width, + height: extension_settings.sd.height, + enhance: extension_settings.sd.pollinations_enhance, + refine: extension_settings.sd.pollinations_refine, + }), + }); + + if (result.ok) { + const data = await result.json(); + return { format: 'jpg', data: data?.image }; + } else { + const text = await result.text(); + throw new Error(text); + } +} + /** * Generates an "extras" image using a provided prompt and other settings. * @@ -2775,6 +2880,8 @@ function isValidState() { return true; case sources.togetherai: return secret_state[SECRET_KEYS.TOGETHERAI]; + case sources.pollinations: + return true; } } @@ -2922,6 +3029,8 @@ jQuery(async () => { $('#sd_novel_view_anlas').on('click', onViewAnlasClick); $('#sd_novel_sm').on('input', onNovelSmInput); $('#sd_novel_sm_dyn').on('input', onNovelSmDynInput); + $('#sd_pollinations_enhance').on('input', onPollinationsEnhanceInput); + $('#sd_pollinations_refine').on('input', onPollinationsRefineInput); $('#sd_comfy_validate').on('click', validateComfyUrl); $('#sd_comfy_url').on('input', onComfyUrlInput); $('#sd_comfy_workflow').on('change', onComfyWorkflowChange); diff --git a/public/scripts/extensions/stable-diffusion/settings.html b/public/scripts/extensions/stable-diffusion/settings.html index cc2307e79..2e5687c4a 100644 --- a/public/scripts/extensions/stable-diffusion/settings.html +++ b/public/scripts/extensions/stable-diffusion/settings.html @@ -32,14 +32,15 @@
@@ -158,6 +159,22 @@
+
+
+ + +
+
diff --git a/src/endpoints/stable-diffusion.js b/src/endpoints/stable-diffusion.js index e2168cd80..104801538 100644 --- a/src/endpoints/stable-diffusion.js +++ b/src/endpoints/stable-diffusion.js @@ -684,7 +684,7 @@ drawthings.post('/generate', jsonParser, async (request, response) => { const url = new URL(request.body.url); url.pathname = '/sdapi/v1/txt2img'; - const body = {...request.body}; + const body = { ...request.body }; delete body.url; const result = await fetch(url, { @@ -710,8 +710,46 @@ drawthings.post('/generate', jsonParser, async (request, response) => { } }); +const pollinations = express.Router(); + +pollinations.post('/generate', jsonParser, async (request, response) => { + try { + const promptUrl = new URL(`https://image.pollinations.ai/prompt/${encodeURIComponent(request.body.prompt)}`); + const params = new URLSearchParams({ + model: String(request.body.model), + negative_prompt: String(request.body.negative_prompt), + seed: String(Math.floor(Math.random() * 10_000_000)), + enhance: String(request.body.enhance ?? false), + refine: String(request.body.refine ?? false), + width: String(request.body.width ?? 1024), + height: String(request.body.height ?? 1024), + nologo: String(true), + nofeed: String(true), + }); + promptUrl.search = params.toString(); + + console.log('Pollinations request URL:', promptUrl.toString()); + + const result = await fetch(promptUrl); + + if (!result.ok) { + console.log('Pollinations returned an error.', result.status, result.statusText); + throw new Error('Pollinations request failed.'); + } + + const buffer = await result.buffer(); + const base64 = buffer.toString('base64'); + + return response.send({ image: base64 }); + } catch (error) { + console.log(error); + return response.sendStatus(500); + } +}); + router.use('/comfy', comfy); router.use('/together', together); router.use('/drawthings', drawthings); +router.use('/pollinations', pollinations); module.exports = { router };