社区版Dify实现文生视频 LLM+ComfyUI+混元视频

news2025/1/19 6:51:08

社区版Dify实现文生视频 LLM+ComfyUI+混元视频

  • 一、 社区版Dify实现私有化混元视频效果
  • 二、为什么社区版Dify可以在对话框实现文生视频?
      • LLM+ComfyUI+混元视频 实现流程图(重点)
      • 1. 文生视频模型支持ComfyUI
      • 2. ComfyUI可以轻松导出API实现封装
      • 3. Dify 中可通过【代码运行】节点实现调用API
      • 4. Dify【直接回复节点】支持Markdown,可是轻易得到视频播放框
  • 三、Flask后端和【Dify 代码执行节点】代码和讲解
    • 1. Flask 后端代码
      • Flask后端的两个功能
    • 2. Dify 代码执行节点 代码
    • 3. Dify LLM节点 如何描述?
  • 四、Dify 安装和专栏的以往文章推荐

一、 社区版Dify实现私有化混元视频效果

在这里插入图片描述
在这里插入图片描述

二、为什么社区版Dify可以在对话框实现文生视频?

LLM+ComfyUI+混元视频 实现流程图(重点)

在这里插入图片描述
这个图就是我的Dify 实现LLM+ComfyUI+混元视频 的整个方案和思路,下面详细说说为什么可以这样做:(这部分可以结合我之前写的文章一起看,我会给出超链接)

1. 文生视频模型支持ComfyUI

其实文生视频的开源模型其实有很多,比如:Sora , Dynamicrafter,VideoCrafter, 混元视频, CogVideo 等等模型。
这些模型大部分都是支持ComfyUI,这里自己去找一找就好。本方法用的是 混元视频的ComfyUI

2. ComfyUI可以轻松导出API实现封装

这部分还不懂的强烈推荐看一下我前面写的 社区版Dify LLM+ComfyUI+代码执行 的方法,里面详细介绍了ComfyUI 的安装,以及调用的最基本的知识。
我这里简单点说就是 工作流其实就是一个JSON,可以通过网络请求实现你想生成的图。ComfyUI 就是一个(工作流)web 后台而已!

3. Dify 中可通过【代码运行】节点实现调用API

Dify 中安装了Sandbox ,支持运行python代码,既然可以跑代码,安装了requests库,那么也就是各种API都可以请求了,这也就是我为什么二次封装的原因,为了简化代码和过程,而不是直接请求ComfyUI。
但是,【代码执行节点】有总时间的约束限制(超时会报错),因为文生视频可能需要跑70~90 秒,但是Sandbox有代码运行时长限制 15S,通常会报timeout 错误!很简单,修改.env 配置文件里面的时间秒数限制即可。为此,可参考我的这一篇博客修改Dify Sandbox的一些配置:社区版Dify sandbox【Python代码执行】Run failed: error: timeout,if the sandbox service

4. Dify【直接回复节点】支持Markdown,可是轻易得到视频播放框

做过LLM开发的人都知道,LLM回复的前端是基于Markdown的,如果在对话框实现视频,安装整个格式输出即可,这就是我的【直接回复节点】的输出:

<video width="320" height="240" autoplay>
  <source src="视频的网络地址" type="video/mp4">
</video>

三、Flask后端和【Dify 代码执行节点】代码和讲解

请先根据 混元视频ComfyUI 安装好模型文件,先保证你的文生视频在ComfyUI 中正常运行。

1. Flask 后端代码

Flask后端的两个功能

第一个:接收 Dify 【代码执行节点】发送来的 文生图Prompt 来修改工作流JSON 文件。
第二个:发送文生图的工作流JSON(给ComfyUI来文生视频),然后等待生成的结果JSON(ComfyUI 告诉你,刚刚的那个请求完成了,生成的文件命名和路径等信息),解析然后得到视频链接(返回给【Dify 代码执行节点】)。

好了:结合我的注释来看Flask代码:

# -*- coding: utf-8 -*-
from flask import Flask, request, jsonify
import websocket
import uuid
import json
import urllib.request
import urllib.parse
import random

import string
import datetime

app = Flask(__name__)

# 设置服务器地址
SERVER_ADDRESS = "你的ComfyUI服务地址:8188"
CLIENT_ID = str(uuid.uuid4())

def queue_prompt(prompt):
    try:
        payload = {"prompt": prompt, "client_id": CLIENT_ID}
        data = json.dumps(payload).encode('utf-8')
        url = f"http://{SERVER_ADDRESS}/prompt"
        req = urllib.request.Request(url, data=data)
        response = urllib.request.urlopen(req)
        return json.loads(response.read())
    except Exception as e:
        print(f"Error in queue_prompt: {e}")
        return None

def get_image(filename, subfolder, folder_type):
    try:
        params = urllib.parse.urlencode({"filename": filename, "subfolder": subfolder, "type": folder_type})
        url = f"http://{SERVER_ADDRESS}/view?{params}"
        return url
    except Exception as e:
        print(f"Error in get_image: {e}")
        return None

def get_history(prompt_id):
    try:
        url = f"http://{SERVER_ADDRESS}/history/{prompt_id}"
        with urllib.request.urlopen(url) as response:
            return json.loads(response.read())
    except Exception as e:
        print(f"Error in get_history: {e}")
        return None

# 等待程序生成,生成后会返回一个JSON ,读取生成的视频地址文件名
def get_images(ws, prompt):
    try:
        prompt_response = queue_prompt(prompt)
        if not prompt_response:
            return None

        prompt_id = prompt_response['prompt_id']

        # 等待生成过程完成
        while True:
            out = ws.recv()
            if isinstance(out, str):
                message = json.loads(out)
                if message.get('type') == 'executing':
                    data = message['data']
                    if data.get('node') is None and data.get('prompt_id') == prompt_id:
                        break

        # 获取生成历史记录
        history = get_history(prompt_id)
        print(history)
        if history and prompt_id in history:
            for node_id, node_output in history[prompt_id]['outputs'].items():
                print(node_id,node_output)
                if 'gifs' in node_output:
                    for image in node_output['gifs']:
                        return get_image(image['filename'], image['subfolder'], image['type'])

    except Exception as e:
        print(f"Error in get_images: {e}")
        return None

# 在API的基础上再次封装修改 的内容,通常是Prompt,可灵活自定义设计
def update_prompt_from_file(filepath, text_prompt, noise_seed):
    """
    从文件加载 JSON 并更新提示信息。

    参数:
        filepath (str): JSON 文件路径。
        text_prompt (str): 新的文本提示。
        noise_seed (int): 随机种子值。

    返回:
        dict: 更新后的 JSON 数据。
    """
    try:
        with open(filepath, "r", encoding="utf-8") as f:
            prompt = json.load(f)

        prompt["25"]["inputs"]["noise_seed"] = noise_seed
        prompt["44"]["inputs"]["text"] = text_prompt
        return prompt
    except Exception as e:
        print(f"Error in update_prompt_from_file: {e}")
        return None

# 生成随机数
def generate_random_15_digit_number():
    return random.randint(10**14, 10**15 - 1)


# Flask 路由
@app.route('/generate_videos', methods=['POST'])
def generate_videos():

    data = request.json
    text_prompt = data.get('text_prompt')
    print("999")
    if not text_prompt:
        return jsonify({"error": "text_prompt is required"}), 400


    noise_seed = generate_random_15_digit_number()

    # 更新提示
    prompt_json = update_prompt_from_file(json_filepath, text_prompt, noise_seed,)
    if not prompt_json:
        return jsonify({"error": "Failed to update prompt"}), 500

    try:
        ws = websocket.WebSocket()
        ws.connect(f"ws://{SERVER_ADDRESS}/ws?clientId={CLIENT_ID}")
        url = get_images(ws, prompt_json)
        print(url)
        if url:
            return jsonify({"image_url": url})
        else:
            return jsonify({"error": "Failed to generate image"}), 500
    except Exception as e:
        print(f"Error in WebSocket connection: {e}")
        return jsonify({"error": "WebSocket connection failed"}), 500
    finally:
        ws.close()

if __name__ == '__main__':
    json_filepath = "hunyuan_00012.json" # 你的混元视频API
    app.run(host='0.0.0.0', port=3083)


发送的JSON 就是工作流,返回的呢?如果好奇可以看:

{
  "8efd022e-fa4c-454d-b885-9aed9e3435a6": {
    "prompt": [
      40,
      "8efd022e-fa4c-454d-b885-9aed9e3435a6",
      {
        "10": {
          "inputs": {
            "vae_name": "hunyuan_video_vae_bf16.safetensors"
          },
          "class_type": "VAELoader",
          "_meta": {
            "title": "Load VAE"
          }
        },
        "11": {
          "inputs": {
            "clip_name1": "clip_l.safetensors",
            "clip_name2": "llava_llama3_fp8_scaled.safetensors",
            "type": "hunyuan_video"
          },
          "class_type": "DualCLIPLoader",
          "_meta": {
            "title": "DualCLIPLoader"
          }
        },
        // ... 其他节点配置
        "78": {
          "inputs": {
            "frame_rate": 35.0,
            "loop_count": 0,
            "filename_prefix": "hunyuan",
            "format": "video/h265-mp4",
            "pix_fmt": "yuv420p10le",
            "crf": 22,
            "save_metadata": true,
            "pingpong": false,
            "save_output": true,
            "images": ["73", 0]
          },
          "class_type": "VHS_VideoCombine",
          "_meta": {
            "title": "Video Combine 🎥🅥🅗🅢"
          }
        }
      },
      {
        "client_id": "7e3ec27b-c922-442b-96e6-d8afa853bd70"
      },
      ["78"]
    ],
    "outputs": {
      "78": {
        "gifs": [
          {
            "filename": "hunyuan_00032.mp4",
            "subfolder": "",
            "type": "output",
            "format": "video/h265-mp4",
            "frame_rate": 35.0,
            "workflow": "hunyuan_00032.png",
            "fullpath": "/**********/ComfyUI/ComfyUI-master-main/output/hunyuan_00032.mp4"
          }
        ]
      }
    },
    "status": {
      "status_str": "success",
      "completed": true,
      "messages": [
        [
          "execution_start",
          {
            "prompt_id": "8efd022e-fa4c-454d-b885-9aed9e3435a6",
            "timestamp": 1737096231965
          }
        ],
        [
          "execution_cached",
          {
            "nodes": ["10", "11", "12", "16", "17", "45", "67"],
            "prompt_id": "8efd022e-fa4c-454d-b885-9aed9e3435a6",
            "timestamp": 1737096231995
          }
        ],
        [
          "execution_success",
          {
            "prompt_id": "8efd022e-fa4c-454d-b885-9aed9e3435a6",
            "timestamp": 1737096309647
          }
        ]
      ]
    },
    "meta": {
      "78": {
        "node_id": "78",
        "display_node": "78",
        "parent_node": null,
        "real_node_id": "78"
      }
    }
  }
}

通过这个返回的JSON地址可以得到一个返回的视频链接:

http://你的ComfyUI地址:8188/view?filename=hunyuan_00061.mp4&subfolder=&type=output

这个地址是Flask 后台返回给 【Dify 代码执行节点】,随后这个【Dify 直接回复节点】按照这样:

<video width="320" height="240" autoplay>
  <source src="http://你的ComfyUI地址:8188/view?filename=hunyuan_00061.mp4&subfolder=&type=output" type="video/mp4">
</video>

即可显示视频了
在这里插入图片描述

2. Dify 代码执行节点 代码

在这里插入图片描述

代码很简单:

import requests
import json
from typing import Dict

def main(prompt) -> Dict[str, str]:
    # 服务器地址
    url = "http://你的Flask后端地址:3083/generate_videos"

    # 请求数据
    data = {
        "text_prompt": prompt,
    }

    # 发送 POST 请求并传递 JSON 数据
    response = requests.post(url, json=data)
    
    if response.status_code == 200:
        result = eval(response.text)["image_url"]
        return {'result': result}
    else:
        return {'error': 'Request failed with status code {}'.format(response.status_code)}

3. Dify LLM节点 如何描述?

这部分其实很灵活,你可以用很多种大模型,我是用deepseek,当然也可以用Ollama本地,等等。我在之前的文章也有写过。
这里就是转换一下英文的文生图Prompt即可:
在这里插入图片描述

四、Dify 安装和专栏的以往文章推荐

  1. Dify安装时会遇到的网络问题,已成功安装Dify教程
  2. Dify 部署LLM 可以参考这里,Dify实现Ollama3.2-vision多模态聊天
  3. 社区版Dify +ComfyUI 实现 Flux 文生图
  4. 并且欢迎关注我的 社区版 Dify 开发专栏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2278819.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot的Bean-中级-作用域

5个作用域&#xff1a; 初级演示的是第一种默认的singleton&#xff1a;SpringBoot的Bean-初级获取bean对象-CSDN博客 中级-1&#xff1a;Lazy注解使其在使用的时候再实例化 中级-2&#xff1a;Scope("prototype")使其每次需要注入的时候都实例化新的对象 测试程序&…

放大芯片参数阅读

一、芯片的增益能力 1. GBW&#xff08;增益带宽积&#xff09; 例如&#xff0c;GBW (typ) 1 MHz。 增益带宽积&#xff08;Gain Bandwidth Product&#xff09;是一个关键参数&#xff0c;用于计算在特定频率下的最大增益。 定义公式为&#xff1a; 增益带宽G…

蓝桥杯算法日常|枚举[*找到最多的数]

**找到最多的数** 重点疑问总结&#xff1a; 1、数组输入输出c一般会采用那种方便的方式&#xff1f;&#xff1f; 用的就是我想的那种&#xff0c;就是用的最大范围定义的。 2、怎样方便给数组中每个数出现的次数计数&#xff1f;&#xff1f; 刚开始想的是&#xff1a;每个数…

Docker安装PostGreSQL docker安装PostGreSQL 完整详细教程

Docker安装PostGreSQL docker安装PostGreSQL 完整详细教程 Docker常用命令大全Docker 运行命令生成Docker 上安装 PostGreSQL 14.15 的步骤&#xff1a;1、拉取 PostGreSQL 14.15 镜像2、创建并运行容器3、测试连接4、设置所有IP都可以运行连接进入容器内 修改配置文件关闭容器…

基于机器学习随机森林算法的个人职业预测研究

1.背景调研 随着信息技术的飞速发展&#xff0c;特别是大数据和云计算技术的广泛应用&#xff0c;各行各业都积累了大量的数据。这些数据中蕴含着丰富的信息和模式&#xff0c;为利用机器学习进行职业预测提供了可能。机器学习算法的不断进步&#xff0c;如深度学习、强化学习等…

Go 语言 select 的实现原理

介绍 select是Go在语言层面提供的I/O多路复用的机制&#xff0c;其专门用来让Goroutine同时等待多个channel是否准备完毕:可读或可写。在Channel状态改变之前&#xff0c;select会一直阻塞当前线程或者goroutine。 特性&#xff1a; case 必须是一个通信操作&#xff0c;主要是…

Java 视频处理:基于 MD5 校验秒传及 ffmpeg 切片合并的实现

本文介绍两种网络技术实现方法。一是 MD5 校验秒传&#xff0c;服务器端用数据库记上传文件 MD5 值及存储路径&#xff0c;Java 代码接收客户端 MD5 值并查询校验&#xff0c;返回状态码。二是用 ffmpeg 切片视频成 m3u8 上传&#xff0c;异步合并文件实现视频按需加载。 1. …

一文读懂iOS中的Crash捕获、分析以及防治

Crash系统性总结 Crash捕获与分析Crash收集符号化分析 Crash类别以及解法分析子线程访问UI而导致的崩溃unrecognized selector send to instance xxxKVO crashKVC造成的crashNSTimer导致的Crash野指针Watch Dog超时造成的crash其他crash待补充 参考文章&#xff1a; 对于iOS端开…

RK3576 Android14 状态栏和导航栏增加显示控制功能

问题背景&#xff1a; 因为RK3576 Android14用户需要手动控制状态栏和导航栏显示隐藏控制&#xff0c;包括对锁屏后下拉状态栏的屏蔽&#xff0c;在设置功能里增加此功能的控制&#xff0c;故参考一些博客完成此功能&#xff0c;以下是具体代码路径的修改内容。 解决方案&…

【Rust自学】13.5. 迭代器 Pt.1:迭代器的定义、iterator trait和next方法

13.5.0. 写在正文之前 Rust语言在设计过程中收到了很多语言的启发&#xff0c;而函数式编程对Rust产生了非常显著的影响。函数式编程通常包括通过将函数作为值传递给参数、从其他函数返回它们、将它们分配给变量以供以后执行等等。 在本章中&#xff0c;我们会讨论 Rust 的一…

LabVIEW 蔬菜精密播种监测系统

在当前蔬菜播种工作中&#xff0c;存在着诸多问题。一方面&#xff0c;播种精度难以达到现代农业的高标准要求&#xff0c;导致种子分布不均&#xff0c;影响作物的生长发育和最终产量&#xff1b;另一方面&#xff0c;对于小粒径种子&#xff0c;传统的监测手段难以实现有效监…

2024年年终总结——坎坷与坚持,焦虑与收获

不知不觉间&#xff0c;2024年已经悄然过去&#xff0c;回望这一年的时间&#xff0c;一时间竟感觉混混沌沌无法形容&#xff0c;选择一些时间坐下来让自己简单回忆一下自己的2024。 先简单回望一下24年一整年的工作情况&#xff1a; 24年一开始&#xff0c;工作最期待的的节点…

无人机技术架构剖析!

一、飞机平台系统 飞机平台系统是无人机飞行的主体平台&#xff0c;主要提供飞行能力和装载功能。它由机体结构、动力装置、电气设备等组成。 机体结构&#xff1a;无人机的机身是其核心结构&#xff0c;承载着其他各个组件并提供稳定性。常见的机身材料包括碳纤维、铝合金、…

springboot基于微信小程序的传统美食文化宣传平台小程序

Spring Boot 基于微信小程序的传统美食文化宣传平台 一、平台概述 Spring Boot 基于微信小程序的传统美食文化宣传平台是一个集传统美食展示、文化传承、美食制作教程分享、用户互动交流以及美食相关活动推广为一体的综合性线上平台。它借助 Spring Boot 强大的后端开发框架构…

Android系统开发(八):从麦克风到扬声器,音频HAL框架的奇妙之旅

引言&#xff1a;音浪太强&#xff0c;我稳如老 HAL&#xff01; 如果有一天你的耳机里传来的不是《咱们屯里人》&#xff0c;而是金属碰撞般的杂音&#xff0c;那你可能已经感受到了 Android 音频硬件抽象层 (HAL) 出问题的后果&#xff01;在 Android 音频架构中&#xff0c…

51.WPF应用加图标指南 C#例子 WPF例子

完整步骤&#xff1a; 先使用文心一言生成一个图标如左边使用Windows图片编辑器编辑&#xff0c;去除背景使用正方形&#xff0c;放大图片使图标铺满图片使用格式工程转换为ico格式&#xff0c;分辨率为最大 在资源管理器中右键项目添加ico类型图片到项目里图片属性设置为始终…

运行fastGPT 第四步 配置ONE API 添加模型

上次已经装好了所有的依赖和程序。 下面在网页中配置One API &#xff0c;这个是大模型的接口。配置好了之后&#xff0c;就可以配置fastGPT了。 打开 OneAPI 页面 添加模型 这里要添加具体的付费模型的API接口填进来。 可以通过ip:3001访问OneAPI后台&#xff0c;**默认账号…

道旅科技借助云消息队列 Kafka 版加速旅游大数据创新发展

作者&#xff1a;寒空、横槊、娜米、公仪 道旅科技&#xff1a;科技驱动&#xff0c;引领全球旅游分销服务 道旅科技 &#xff08;https://www.didatravel.com/home&#xff09; 成立于 2012 年&#xff0c;总部位于中国深圳&#xff0c;是一家以科技驱动的全球酒店资源批发商…

51单片机——DS18B20温度传感器

由于DS18B20数字温度传感器是单总线接口&#xff0c;所以需要使用51单片机的一个IO口模拟单总线时序与DS18B20通信&#xff0c;将检测的环境温度读取出来 1、DS18B20模块电路 传感器接口的单总线管脚接至单片机P3.7IO口上 2、DS18B20介绍 2.1 DS18B20外观实物图 管脚1为GN…

Redis的安装和配置、基本命令

一、实验目的 本实验旨在帮助学生熟悉Redis的安装、配置和基本使用&#xff0c;包括启动Redis服务、使用命令行客户端进行操作、配置Redis、进行多数据库操作以及掌握键值相关和服务器相关的命令。 二、实验环境准备 1. JAVA环境准备&#xff1a;确保Java Development Kit …