├── LICENSE ├── README.md ├── js_lib └── functions.js ├── projects ├── .DS_Store ├── project_2_1 │ ├── wechat-example │ │ └── txt2img │ │ │ ├── app.js │ │ │ ├── app.json │ │ │ ├── app.wxss │ │ │ ├── data │ │ │ └── api_json.js │ │ │ ├── pages │ │ │ ├── index │ │ │ │ ├── index.js │ │ │ │ ├── index.json │ │ │ │ ├── index.wxml │ │ │ │ └── index.wxss │ │ │ └── logs │ │ │ │ ├── logs.js │ │ │ │ ├── logs.json │ │ │ │ ├── logs.wxml │ │ │ │ └── logs.wxss │ │ │ ├── project.config.json │ │ │ ├── project.private.config.json │ │ │ └── sitemap.json │ └── workflow_api.json ├── project_2_3 │ ├── wechat-example │ │ └── img2img │ │ │ ├── app.js │ │ │ ├── app.json │ │ │ ├── app.wxss │ │ │ ├── data │ │ │ └── api_json.js │ │ │ ├── pages │ │ │ ├── index │ │ │ │ ├── index.js │ │ │ │ ├── index.json │ │ │ │ ├── index.wxml │ │ │ │ └── index.wxss │ │ │ └── logs │ │ │ │ ├── logs.js │ │ │ │ ├── logs.json │ │ │ │ ├── logs.wxml │ │ │ │ └── logs.wxss │ │ │ ├── project.config.json │ │ │ ├── project.private.config.json │ │ │ ├── sitemap.json │ │ │ └── utils │ │ │ └── util.js │ └── workflow_api.json ├── project_3_5 │ ├── wechat-example │ │ └── video2video │ │ │ ├── app.js │ │ │ ├── app.json │ │ │ ├── app.wxss │ │ │ ├── pages │ │ │ ├── index │ │ │ │ ├── index.js │ │ │ │ ├── index.json │ │ │ │ ├── index.wxml │ │ │ │ └── index.wxss │ │ │ └── logs │ │ │ │ ├── logs.js │ │ │ │ ├── logs.json │ │ │ │ ├── logs.wxml │ │ │ │ └── logs.wxss │ │ │ ├── project.config.json │ │ │ ├── project.private.config.json │ │ │ └── sitemap.json │ ├── workflow_api.json │ └── workflow_api.py ├── project_4_1 │ ├── wechat-example │ │ └── crazy_sticker │ │ │ ├── app.js │ │ │ ├── app.json │ │ │ ├── app.wxss │ │ │ ├── pages │ │ │ ├── images │ │ │ │ ├── bg.png │ │ │ │ ├── btn_download.png │ │ │ │ ├── btn_upload.png │ │ │ │ ├── completed.png │ │ │ │ ├── ele.png │ │ │ │ ├── ele_1.png │ │ │ │ ├── ele_2.png │ │ │ │ ├── ele_3.png │ │ │ │ └── ele_4.png │ │ │ ├── index │ │ │ │ ├── index.js │ │ │ │ ├── index.json │ │ │ │ ├── index.wxml │ │ │ │ └── index.wxss │ │ │ └── logs │ │ │ │ ├── logs.js │ │ │ │ ├── logs.json │ │ │ │ ├── logs.wxml │ │ │ │ └── logs.wxss │ │ │ ├── project.config.json │ │ │ ├── project.private.config.json │ │ │ └── sitemap.json │ ├── workflow_api.json │ └── workflow_api.py ├── project_4_2 │ ├── enamel_adapter │ │ ├── enamel34.png │ │ ├── enamel37.png │ │ ├── enamel39.png │ │ ├── enamel74.png │ │ └── enamel75.png │ ├── wechat-example │ │ └── pet_enamel │ │ │ ├── app.js │ │ │ ├── app.json │ │ │ ├── app.wxss │ │ │ ├── pages │ │ │ ├── images │ │ │ │ ├── bg.png │ │ │ │ ├── btn_download.png │ │ │ │ ├── btn_upload.png │ │ │ │ ├── dog_1.png │ │ │ │ └── dog_text.png │ │ │ ├── index │ │ │ │ ├── index.js │ │ │ │ ├── index.json │ │ │ │ ├── index.wxml │ │ │ │ └── index.wxss │ │ │ └── logs │ │ │ │ ├── logs.js │ │ │ │ ├── logs.json │ │ │ │ ├── logs.wxml │ │ │ │ └── logs.wxss │ │ │ ├── project.config.json │ │ │ ├── project.private.config.json │ │ │ └── sitemap.json │ ├── workflow_api.json │ └── workflow_api.py ├── project_5_1 │ ├── .DS_Store │ ├── web_takephoto │ │ ├── .DS_Store │ │ ├── assets │ │ │ ├── .DS_Store │ │ │ ├── css │ │ │ │ └── style.css │ │ │ ├── images │ │ │ │ ├── .DS_Store │ │ │ │ ├── bg_1.png │ │ │ │ ├── bg_2.png │ │ │ │ ├── bg_3.png │ │ │ │ ├── button_2.png │ │ │ │ ├── button_3.png │ │ │ │ ├── loading.png │ │ │ │ └── start.png │ │ │ ├── js │ │ │ │ ├── app.js │ │ │ │ └── utils.js │ │ │ └── services │ │ │ │ └── api.js │ │ ├── index.html │ │ └── libs │ │ │ └── jquery-2.1.3.min.js │ ├── workflow_api.json │ └── workflow_api.py ├── requirement.txt └── test.py ├── sdk └── comfy_sdk.py └── workflows ├── 2-1默认工作流.png ├── 2-2黏土小猫.png ├── 2-3图片转黏土.png ├── 3-5视频liveportrait.png ├── 4-1贴纸小程序.png ├── 4-2宠物珐琅生成器.png ├── 5-1圣诞节照相馆.png └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 余悠 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ComfyUI_workflow2app 2 | 5 | 6 | ## 课程地址 7 | Bilibili课堂 https://www.bilibili.com/cheese/play/ss33322 8 | 9 | ## 步骤说明 10 | 11 | - 1.在ComfyUI中搭建并运行成功任意工作流。 12 | - 2.在ComfyUI中,配置开发模式,允许通过Save(API Format)的形式下载可用于API搭建的工作流版本。 13 | - 3.将编辑完成的workflow工作流下载到本地。 14 | - 4.部署workflow的后端模块,开发对应的API接口。 15 | - 5.开发「app / 小程序」的前端业务逻辑。 16 | - 6.调试 / 部署 / 上线 17 | 18 | ## 文件结构 19 | 20 | ### projects 21 | 工程文件目录 22 | 23 | ### workflows 24 | 工作流目录 25 | -------------------------------------------------------------------------------- /js_lib/functions.js: -------------------------------------------------------------------------------- 1 | /* 2 | 调用微信小程序的相册 / 后置摄像头,获取图片,上传到ComfyUI服务器端的input文件夹 3 | */ 4 | uploadImage() { 5 | let that = this; 6 | wx.chooseMedia({ 7 | count: 9, 8 | mediaType: ['image','video'], 9 | sourceType: ['album', 'camera'], 10 | maxDuration: 30, 11 | camera: 'back', 12 | success (res) { 13 | const tempFilePath = res.tempFiles[0].tempFilePath 14 | wx.uploadFile({ 15 | url: `${that.data.baseUrl}/api/upload/image`, 16 | filePath: tempFilePath, 17 | name: 'image', 18 | success: function(res) { 19 | if (res.statusCode === 200 && res.data.name) { 20 | let image_name = res.data.name 21 | console.log(image_name) 22 | prompt_json['11']['inputs']['image'] = image_name 23 | } else { 24 | console.error('Upload failed:', res.statusCode, res.data); 25 | } 26 | }, 27 | fail: function(error) { 28 | console.error('Upload error:', error); 29 | } 30 | }) 31 | } 32 | }) 33 | }, 34 | 35 | // 通用请求方法 36 | makeRequest(endpoint, method, data = {}) { 37 | return new Promise((resolve, reject) => { 38 | wx.request({ 39 | url: `${this.data.baseUrl}${endpoint}`, 40 | method, 41 | data, 42 | timeout: 20000, 43 | header: { 44 | 'content-type': 'application/json' 45 | }, 46 | success: resolve, 47 | fail: reject 48 | }); 49 | }); 50 | }, 51 | 52 | // 列队 53 | queue(prompt) { 54 | return this.makeRequest('/api/prompt','POST',{prompt:prompt}) 55 | .then(res => { 56 | if (res.data && res.data.prompt_id) { 57 | return res.data.prompt_id; 58 | } else { 59 | throw new Error('Failed to get prompt_id'); 60 | } 61 | }); 62 | }, 63 | -------------------------------------------------------------------------------- /projects/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuyou-dev/ComfyUI_workflow2app/3e6087453fc1889c9451714108ecc0ae130c0d28/projects/.DS_Store -------------------------------------------------------------------------------- /projects/project_2_1/wechat-example/txt2img/app.js: -------------------------------------------------------------------------------- 1 | // app.js 2 | App({ 3 | onLaunch() { 4 | // 展示本地存储能力 5 | const logs = wx.getStorageSync('logs') || [] 6 | logs.unshift(Date.now()) 7 | wx.setStorageSync('logs', logs) 8 | 9 | // 登录 10 | wx.login({ 11 | success: res => { 12 | // 发送 res.code 到后台换取 openId, sessionKey, unionId 13 | } 14 | }) 15 | }, 16 | globalData: { 17 | userInfo: null 18 | } 19 | }) 20 | -------------------------------------------------------------------------------- /projects/project_2_1/wechat-example/txt2img/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/index/index", 4 | "pages/logs/logs" 5 | ], 6 | "window": { 7 | "navigationBarTextStyle": "black", 8 | "navigationBarTitleText": "文生图", 9 | "navigationBarBackgroundColor": "#ffffff" 10 | }, 11 | "style": "v2", 12 | "componentFramework": "glass-easel", 13 | "sitemapLocation": "sitemap.json", 14 | "lazyCodeLoading": "requiredComponents" 15 | } 16 | -------------------------------------------------------------------------------- /projects/project_2_1/wechat-example/txt2img/app.wxss: -------------------------------------------------------------------------------- 1 | /**app.wxss**/ 2 | .container { 3 | height: 100%; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | box-sizing: border-box; 8 | padding: 20px; 9 | } 10 | -------------------------------------------------------------------------------- /projects/project_2_1/wechat-example/txt2img/data/api_json.js: -------------------------------------------------------------------------------- 1 | let prompt_json = "" // 替换为从 Save(API Format)下载下来的JSON对象 2 | module.exports = prompt_json; 3 | -------------------------------------------------------------------------------- /projects/project_2_1/wechat-example/txt2img/pages/index/index.js: -------------------------------------------------------------------------------- 1 | const prompt_json = require('../../data/api_json') 2 | 3 | Page({ 4 | data: { 5 | filename: '', 6 | baseUrl: "http://192.168.1.11:6008" //替换成ComfyUI的运行url 7 | }, 8 | 9 | // 处理文本输入 10 | handleTextInput(e) { 11 | this.setData({ 12 | textVal: e.detail.value 13 | }); 14 | }, 15 | 16 | // 上传文本并获取图片 17 | uploadTextAndFetchImage() { 18 | const { textVal } = this.data; 19 | 20 | wx.showLoading({ title: '请等待...' }); 21 | 22 | this.uploadPrompt(textVal) 23 | .then(this.fetchImageByPromptId) 24 | .then(imgUrls => { 25 | this.setData({ imgReturns: imgUrls }); 26 | }) 27 | .catch(error => { 28 | console.error(error); 29 | }) 30 | .finally(() => { 31 | wx.hideLoading(); 32 | }); 33 | }, 34 | 35 | // 上传文本,返回 prompt_id 36 | uploadPrompt(text) { 37 | prompt_json['6']['inputs']['text_positive'] = text; // 在API配置文件中查找对应的属性 38 | return this.makeRequest('/prompt', 'POST', { prompt: prompt_json }) 39 | .then(res => { 40 | if (res.data && res.data.prompt_id) { 41 | return res.data.prompt_id; 42 | } else { 43 | throw new Error('Failed to get prompt_id'); 44 | } 45 | }); 46 | }, 47 | 48 | // 根据 prompt_id 获取图片 49 | fetchImageByPromptId(prompt_id) { 50 | return new Promise((resolve, reject) => { 51 | const fetchImage = () => { 52 | this.makeRequest(`/history/${prompt_id}`, 'GET') 53 | .then(res => { 54 | if (res.data && res.data[prompt_id]) { 55 | this.setData({ 56 | filename: res.data[prompt_id]["outputs"][9]['images'][0]['filename'] 57 | }); 58 | resolve(res.data[prompt_id]["outputs"][9]['images']); 59 | } else { 60 | setTimeout(fetchImage, 2000); // 轮询直到获取到图片 61 | } 62 | }) 63 | .catch(reject); 64 | }; 65 | 66 | fetchImage(); 67 | }); 68 | }, 69 | 70 | // 通用请求方法 71 | makeRequest(endpoint, method, data = {}) { 72 | return new Promise((resolve, reject) => { 73 | wx.request({ 74 | url: `${this.data.baseUrl}${endpoint}`, 75 | method, 76 | data, 77 | timeout: 20000, 78 | header: { 79 | 'content-type': 'application/json' 80 | }, 81 | success: resolve, 82 | fail: reject 83 | }); 84 | }); 85 | }, 86 | 87 | // 重置文件名 88 | resetFilename() { 89 | this.setData({ 90 | filename: '' 91 | }); 92 | } 93 | }); 94 | -------------------------------------------------------------------------------- /projects/project_2_1/wechat-example/txt2img/pages/index/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | } 4 | } -------------------------------------------------------------------------------- /projects/project_2_1/wechat-example/txt2img/pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 |