Yong's Blog
首页
整理
  •   归档
  •   分类
  •   标签
关于
  •   主页
  •   友链
Yong Liu
文章
25
分类
4
标签
4
首页
整理
归档
分类
标签
关于
主页
友链
技术
通过CF Worker每月白嫖1000次sd绘图(支持api,支持多key轮询,支持多种绘图样式模型)
发布于: 2024-11-4
最后更新: 2024-11-11
次查看
教程
type
status
date
slug
summary
category
tags
icon
password
AI 摘要

1.在 Prodiap 注册一个账户,并获取 PRODIA_API_KEY:

notion image
notion image

2.在Cloud Flare中的Workers 和 Pages中创建一个Worker项目并点击编辑代码:

notion image

3.复制下方的js项目粘贴至worker中修改相应参数后部署:

(如何获取cf_accountID以及cf_token请自行查阅学习)
API_KEY可自定义比如:sk-123456789
//prodia.com API_KEY const PRODIA_API_KEY = "XXX";
//本项目授权api_key,防止被恶意调用 const API_KEY = "sk-XXX"; //cloudflare账号列表,每次请求都会随机从列表里取一个账号 const CF_ACCOUNT_LIST = [ { account_id: "cf_accountID", token: "cf_token" } ]; //在你输入的prompt中添加 ---ntl可强制禁止提示词翻译、优化功能 //在你输入的prompt中添加 ---tl可强制开启提示词翻译、优化功能 //是否开启提示词翻译、优化功能 const CF_IS_TRANSLATE = true; //示词翻译、优化模型 const CF_TRANSLATE_MODEL = "@cf/qwen/qwen1.5-14b-chat-awq"; //模型映射,设置客户端可用的模型。one-api,new-api在添加渠道时可使用"获取模型列表"功能,一键添加模型 const CUSTOMER_MODEL_MAP = { "animagineXLV3_v30.safetensors": "animagineXLV3_v30.safetensors [75f2f05b]", "devlishphotorealism_sdxl15.safetensors": "devlishphotorealism_sdxl15.safetensors [77cba69f]", "dreamshaperXL10_alpha2.safetensors": "dreamshaperXL10_alpha2.safetensors [c8afe2ef]", "dynavisionXL_0411.safetensors": "dynavisionXL_0411.safetensors [c39cc051]", "juggernautXL_v45.safetensors": "juggernautXL_v45.safetensors [e75f5471]", "realismEngineSDXL_v10.safetensors": "realismEngineSDXL_v10.safetensors [af771c3f]", "realvisxlV40.safetensors": "realvisxlV40.safetensors [f7fdcb51]", "sd_xl_base_1.0.safetensors": "sd_xl_base_1.0.safetensors [be9edd61]", "sd_xl_base_1.0_inpainting_0.1.safetensors": "sd_xl_base_1.0_inpainting_0.1.safetensors [5679a81a]", "turbovisionXL_v431.safetensors": "turbovisionXL_v431.safetensors [78890989]" };
/**
  • Handles incoming requests to the Cloudflare Worker.
  • @param {Request} request - The incoming request object.
  • @returns {Response} - The response object.
  • @throws {Error} - If the request is invalid or the response fails. / async function handleRequest(request) { try { if (request.method === "OPTIONS") { return new Response("", { status: 204, headers: { 'Access-Control-Allow-Origin': '', "Access-Control-Allow-Headers": '*' } }); }
    • const authHeader = request.headers.get("Authorization"); if (!authHeader || !authHeader.startsWith("Bearer ") || authHeader.split(" ")[1] !== API_KEY) { return new Response("Unauthorized", { status: 401 }); }
      if (request.url.endsWith("/v1/models")) { const arrs = []; Object.keys(CUSTOMER_MODEL_MAP).map(element => arrs.push({ id: element, object: "model" })) const response = { data: arrs, success: true }; return new Response(JSON.stringify(response), { headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '', 'Access-Control-Allow-Headers': '' } }); }
      if (request.method !== "POST") { return new Response("Only POST requests are allowed", { status: 405, headers: { 'Access-Control-Allow-Origin': '', "Access-Control-Allow-Headers": '' } }); }
      if (!request.url.endsWith("/v1/chat/completions")) { return new Response("Not Found", { status: 404, headers: { 'Access-Control-Allow-Origin': '', "Access-Control-Allow-Headers": '' } }); }
      const data = await request.json(); const messages = data.messages || []; const model = CUSTOMER_MODEL_MAP[data.model] || CUSTOMER_MODEL_MAP["v1-5-inpainting.safetensors"]; const stream = data.stream || false; const userMessage = messages.reverse().find((msg) => msg.role === "user")?.content; if (!userMessage) { return new Response(JSON.stringify({ error: "未找到用户消息" }), { status: 400, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '', 'Access-Control-Allow-Headers': '' } }); }
      const is_translate = extractTranslate(userMessage); const originalPrompt = cleanPromptString(userMessage); const translatedPrompt = is_translate ? await getPrompt(originalPrompt) : originalPrompt;
      const imageUrl = await generateImageByText(model, translatedPrompt);
      if (stream) { return handleStreamResponse(originalPrompt, translatedPrompt,"1024x1024", model, imageUrl); } else { return handleNonStreamResponse(originalPrompt, translatedPrompt, "1024x1024", model, imageUrl); } } catch (error) { return new Response("Internal Server Error: " + error.message, { status: 500, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '', 'Access-Control-Allow-Headers': '' } }); } }
/**
  • @description
  • Translate a prompt into a stable diffusion prompt style.
  • @param {string} prompt - The prompt to translate.
  • @returns {Promise<string>} The translated prompt.
  • @throws {Error} If the translation fails. */ async function getPrompt(prompt) { const requestBodyJson = { messages: [ { role: "system", content: `作为 Stable Diffusion Prompt 提示词专家,您将从关键词中创建提示,通常来自 Danbooru 等数据库。
    • }, { role: "user", content: prompt } ] };
const response = await postRequest(CF_TRANSLATE_MODEL, requestBodyJson);
if (!response.ok) { return prompt; }
const jsonResponse = await response.json(); const res = jsonResponse.result.response; return res; }
/**
  • Generate an image from a given text prompt using the Prodia AI API
  • @param {string} model - The name of the AI model to use for image generation
  • @param {string} prompt - The text prompt to generate an image from
  • @returns {string} - The URL of the generated image
  • @throws {Error} - If the image generation fails
  • @see https://docs.prodia.ai/docs/api-reference */ async function generateImageByText(model, prompt) { // First request to generate the image const generateOptions = { method: 'POST', headers: { accept: 'application/json', 'content-type': 'application/json', 'X-Prodia-Key': PRODIA_API_KEY }, body: JSON.stringify({ model: model, prompt: prompt, negative_prompt: 'low resolution, blurry, distorted features, wrong fingers, extra numbers, watermarks, ugly, distorted, deformed, deformed, repetitive, missing arms and legs, multiple hands and legs, incomplete limbs, long neck, cross-eyed, glazed eyes, lax eyes, squinting, deformed eyes', steps: 20, cfg_scale: 7, seed: -1, sampler: 'DPM++ 2M Karras', width: 1024, height: 1024 }) };
try { const generateResponse = await fetch('https://api.prodia.com/v1/sdxl/generate', generateOptions); const generateData = await generateResponse.json();
} catch (error) { return "图像生成或转换失败,请检查!" + error.message; } }
/**
  • Return a streaming response with the generated image.
    • The response will contain the generated image as a base64 encoded string
    • and the original and translated prompts as text. The response will be sent
    • as a Server-Sent Event (SSE) stream, with the data event containing the
    • response payload.
      • @param {string} originalPrompt - The original prompt given to the model.
      • @param {string} translatedPrompt - The translated prompt given to the model.
      • @param {string} size - The size of the generated image.
      • @param {string} model - The model used to generate the image.
      • @param {string} imageUrl - The URL of the generated image.
      • @returns {Response} - The response object. */ function handleStreamResponse(originalPrompt, translatedPrompt, size, model, imageUrl) { const uniqueId = chatcmpl-${Date.now()}; const createdTimestamp = Math.floor(Date.now() / 1000); const systemFingerprint = "fp_" + Math.random().toString(36).substr(2, 9); const content = 🎨 原始提示词:${originalPrompt}\\n + 🌐 翻译后的提示词:${translatedPrompt}\\n + 📐 图像规格:${size}\\n + 🌟 图像生成成功!\\n + 以下是结果:\\n\\n + ![生成的图像](${imageUrl});
      const responsePayload = { id: uniqueId, object: "chat.completion.chunk", created: createdTimestamp, model: model, system_fingerprint: systemFingerprint, choices: [ { index: 0, delta: { content: content, }, finish_reason: "stop", }, ], };
      const dataString = JSON.stringify(responsePayload);
      return new Response(data: ${dataString}\\n\\n, { status: 200, headers: { "Content-Type": "text/event-stream", 'Access-Control-Allow-Origin': '', "Access-Control-Allow-Headers": '', }, }); }
      /**
      • Return a non-streaming response with the generated image.
        • The response will contain the generated image as a base64 encoded string
        • and the original and translated prompts as text.
          • @param {string} originalPrompt - The original prompt given to the model.
          • @param {string} translatedPrompt - The translated prompt given to the model.
          • @param {string} size - The size of the generated image (e.g. 1024x1024).
            • @param {string} model - The model used to generate the image (e.g. @cf/stabilityai/stable-diffusion-xl-base-1.0).
          • @param {string} imageUrl - The URL of the generated image.
          • @return {Response} - The response object with the generated image and prompts. */ function handleNonStreamResponse(originalPrompt, translatedPrompt, size, model, imageUrl) { const uniqueId = chatcmpl-${Date.now()}; const createdTimestamp = Math.floor(Date.now() / 1000); const systemFingerprint = "fp_" + Math.random().toString(36).substr(2, 9); const content = 🎨 原始提示词:${originalPrompt}\\n + 🌐 翻译后的提示词:${translatedPrompt}\\n + 📐 图像规格:${size}\\n + 🌟 图像生成成功!\\n + 以下是结果:\\n\\n + ![生成的图像](${imageUrl});
          const response = { id: uniqueId, object: "chat.completion", created: createdTimestamp, model: model, system_fingerprint: systemFingerprint, choices: [{ index: 0, message: { role: "assistant", content: content }, finish_reason: "stop" }], usage: { prompt_tokens: translatedPrompt.length, completion_tokens: content.length, total_tokens: translatedPrompt.length + content.length } };
          const dataString = JSON.stringify(response);
          return new Response(dataString, { status: 200, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '', 'Access-Control-Allow-Headers': '' } }); }
          /**
          • @description
          • POST request to Cloudflare AI API
          • @param {string} model - AI model name
          • @param {object} jsonBody - JSON object to be sent in the body of the request
          • @returns {Promise<Response>} - Response object
          • @throws {Error} - If response status is not OK */ async function postRequest(model, jsonBody) { const cf_account = CF_ACCOUNT_LIST[Math.floor(Math.random() * CF_ACCOUNT_LIST.length)]; const apiUrl = https://api.cloudflare.com/client/v4/accounts/${cf_account.account_id}/ai/run/${model}; const response = await fetch(apiUrl, { method: 'POST', headers: { 'Authorization': Bearer ${cf_account.token}, 'Content-Type': 'application/json' }, body: JSON.stringify(jsonBody) });
          if (!response.ok) { throw new Error('Unexpected response ' + response.status); } return response; }
          /**
          • Extract translate flag from prompt string.
            • This function will parse the flag from the given prompt string and return the
            • translate flag. If the flag is not found, it will return the default translate
            • flag set in CF_IS_TRANSLATE.
              • @param {string} prompt The prompt string to parse the flag from.
              • @return {boolean} The translate flag parsed from the prompt string. */ function extractTranslate(prompt) { const match = prompt.match(/---n?tl/); if (match && match[0]) { if (match[0] == "---ntl") { return false; } else if (match[0] == "---tl") { return true; } } return CF_IS_TRANSLATE; }
              /**
              • Remove translate flag from prompt string.
                • This function will remove the translate flag ("---ntl" or "---tl") from the
                • given prompt string and return the cleaned prompt string.
                  • @param {string} prompt The prompt string to clean.
                  • @return {string} The cleaned prompt string. */ function cleanPromptString(prompt) { return prompt.replace(/---n?tl/, "").trim(); }
                  addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)); });

                  4.在刚刚创建的worker中点击设置,在域和路由中添加自定义域,并填写自己的子域名,如image.xxxx.com:

                  (此处需自行查阅学习如何给cf添加自己的域名)
                  notion image

                  5.复制自己的域名在任意对话平台或API中转站中添加自定义接口以及自定义模型:

                  (此处以ChatGPT-Next-Web为例)
                  notion image
                  以下是所有模型的介绍,可以根据自己的需求来选择模型:
                  1. animagineXLV3_v30 :适合想要动画风格的创作者,可能在动画效果上表现优越。
                  1. devlishphotorealism_sdxl15 :如果你需要超真实的图像效果,这个模型可能是个好选择,特别适合用于摄影风格的创作。
                  1. dreamshaperXL10_alpha2 :注重创意和想象力,适合艺术风格较强的作品。
                  1. juggernautXL_v45 :可能在生成大型复杂场景方面表现出色,可以考虑。
                  1. realismEngineSDXL_v10 :专注于真实效果的生成,适合需要高度逼真图像的项目。
                  1. sd_xl_base_1.0 和 sd_xl_base_1.0_inpainting_0.1 :这些是基础模型,适合多种用途,也可以在需要时进行细微调整。
                  1. turbovisionXL_v431 :可能在速度和效率上有优势,适合需要快速生成的场景。
                  • 作者:Yong Liu
                  • 链接:https://ikiss.eu.org/article/abc/14
                  • 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
                  相关文章
                  使用Cloudflare Pages,零成本部署个人主页
                  Open WebUI 函数:使用当贝免费满血 DeepSeek R1
                  在cloudflare部署hugging face的免费api,可对接oneapi/newapi,免费使用Qwen2.5 72B等模型
                  永久免费,搭建AI自动优化生成英文提示词 调用FLUX.1绘画,并可接入NEW API
                  白嫖腾讯云GPU跑StableDiffusion
                  【小白的教程】无需服务器一键部署OneAPI? | OneAPI 部署使用指南
                  使用Sunshine + Moonlight 来实现毫秒级延迟的远程串流,外加虚拟显示器免费搭建一个new-api 适合小白的详细教程
                  Loading...
                  目录
                  0%
                  1.在 Prodiap 注册一个账户,并获取 PRODIA_API_KEY:2.在Cloud Flare中的Workers 和 Pages中创建一个Worker项目并点击编辑代码:3.复制下方的js项目粘贴至worker中修改相应参数后部署:4.在刚刚创建的worker中点击设置,在域和路由中添加自定义域,并填写自己的子域名,如image.xxxx.com:5.复制自己的域名在任意对话平台或API中转站中添加自定义接口以及自定义模型:
                  Yong Liu
                  Yong Liu
                  记录生活中的有趣瞬间⚡️
                  文章
                  25
                  分类
                  4
                  标签
                  4
                  最新发布
                  使用Cloudflare Pages,零成本部署个人主页
                  使用Cloudflare Pages,零成本部署个人主页
                  2025-7-28
                  永久免费,搭建AI自动优化生成英文提示词 调用FLUX.1绘画,并可接入NEW API
                  永久免费,搭建AI自动优化生成英文提示词 调用FLUX.1绘画,并可接入NEW API
                  2025-6-23
                  在cloudflare部署hugging face的免费api,可对接oneapi/newapi,免费使用Qwen2.5 72B等模型
                  在cloudflare部署hugging face的免费api,可对接oneapi/newapi,免费使用Qwen2.5 72B等模型
                  2025-6-23
                  Open WebUI 函数:使用当贝免费满血 DeepSeek R1
                  Open WebUI 函数:使用当贝免费满血 DeepSeek R1
                  2025-6-23
                  白嫖腾讯云GPU跑StableDiffusion
                  白嫖腾讯云GPU跑StableDiffusion
                  2024-11-19
                  免费搭建一个new-api 适合小白的详细教程
                  免费搭建一个new-api 适合小白的详细教程
                  2024-11-15
                  公告
                  欢迎来到我的笔记空间!🎉
                   
                   
                  目录
                  0%
                  1.在 Prodiap 注册一个账户,并获取 PRODIA_API_KEY:2.在Cloud Flare中的Workers 和 Pages中创建一个Worker项目并点击编辑代码:3.复制下方的js项目粘贴至worker中修改相应参数后部署:4.在刚刚创建的worker中点击设置,在域和路由中添加自定义域,并填写自己的子域名,如image.xxxx.com:5.复制自己的域名在任意对话平台或API中转站中添加自定义接口以及自定义模型:
                  2021-2025Yong Liu.

                  Yong's Blog | 记录生活中的有趣瞬间⚡️

                  Powered byNotionNext 4.7.7.