核心目标

  • 构建 “书籍内容结构化→多模态资源生成→剪映集成合成” 的闭环工作流,实现 “输入书籍信息→输出绘声绘色 AI 视频” 的自动化生产,达成 “10 分钟掌握书籍核心情感 + 知识点关联” 的效果。核心逻辑:通过 “多粒度摘要分解→情感与风格锚定→结构化思维导图生成→情感驱动多模态生成” 的递进流程,结合 Coze 工作流引擎整合大模型、语音合成、视频编辑等工具,实现 80% 可视化配置 + 20% 编码辅助的高效落地。

基础准备

  • 工具与平台配置

    • 类别 具体工具 / 平台 核心用途 关键配置要求 注意事项
      工作流引擎 Coze(扣子) 整合插件与大模型,编排工作流 完成实名认证,创建 “私人空间” 需提前开通插件调用权限(免费版满足基础需求)
      视频编辑 剪映电脑版(≥v2.5.0) 视频二次编辑与导出 支持导入 JSON 草稿,登录账号 专业版可解锁关键帧等高级功能
      大模型支持 豆包 1.5 Pro/DeepSeek-V3 对话生成、关键词提取、情感分析 Coze 内置集成,无需额外 API 密钥 长文本处理需确保上下文窗口≥128K
      多模态工具 语音合成(扣子官方)、画板组件 生成音频、背景图等多模态资源 语音合成需预设角色音色,画板设 1920×1080px 提前添加至 Coze 个人空间,避免构建时遗漏
      进阶工具(可选) ElevenLabs(TTS)、MidJourney(视觉) 提升语音 / 视觉质感 需绑定 API 密钥或 Discord 账号 适合对视频质量有更高要求的场景
  • 数据与资源准备,无需复杂向量库(按 JSON 格式整理书籍核心信息,存储于 Coze“资源管理→文件存储”,每本书单独配置)按以下结构整理成 JSON 文件(便于大模型调用):

    • {
        "book_info": {
          "title": "富爸爸与穷爸爸",
          "author": "罗伯特·清崎和莎朗·L·莱希特",
          "core_themes": ["资产与负债", "财商思维", "被动收入"],
          "pain_points": ["混淆资产与负债", "为钱工作而非让钱为工作", "月光族理财困境"],
          "real_cases": ["自住房是否为资产", "指数基金入门投资"]
        }
      }
      
    • 存储路径:Coze 工作流 “资源管理”→“文件存储”(支持直接调用),每本书单独配置,新增书籍时复制模板修改。

  • 标准化素材包(固定复用),角色图片:主持人头像(300×300px)、书籍拟人化图片(300×300px),存储为 URL 或本地文件,背景模板:1920×1080px 纯色 / 简约风格背景(避免遮挡字幕),音效素材:开场音效(5 秒内)、字幕出场音效(1-2 秒),上传至 Coze 文件存储。

  • 书籍类型 - 风格映射库(./config/style_mapping.json),按书籍类型预设视觉 / 听觉风格,解决 “风格适配” 问题:

    • {
        "小说": {
          "视觉风格": {
            "背景类型": "场景插画",
            "色调": "暖色调(文学小说)/冷色调(科幻小说)",
            "角色插画风格": "写实风(历史小说)/动漫风(青春小说)"
          },
          "听觉风格": {
            "主持人音色": "沉稳男声(历史)/清亮女声(青春)",
            "背景音乐类型": "纯音乐(钢琴+小提琴)"
          }
        },
        "科普": {
          "视觉风格": {
            "背景类型": "科技感网格",
            "色调": "蓝白冷色调",
            "动效": "知识点出现时用“缩放+渐显”"
          },
          "听觉风格": {
            "主持人音色": "清晰中性声",
            "情感音效": "知识点强调时用“叮”提示音"
          }
        }
        // 其他类型(历史等)省略
      }
      
  • 情感 - 资源映射库(./config/emotion_resource.csv),核心解决 “情感传递” 问题,关联 “情感标签→音色→背景音乐→视觉元素”

    • 情感标签 TTS 情感参数(讯飞) 背景音乐 BGM(本地路径) 背景色调 字幕动效 角色表情(插画)
      激昂 emotion=0,rate=110 ./bgm/ 激昂_英雄的黎明.mp3 红 + 金 快速闪烁 + 放大 握拳睁眼
      悲伤 emotion=4,rate=80 ./bgm/ 悲伤_月光下的凤尾竹.mp3 灰 + 蓝 缓慢淡入 + 下移 低头垂泪
      紧张 emotion=2,rate=120 ./bgm/ 紧张_碟中谍主题曲.mp3 黑 + 深红 抖动 + 渐暗 皱眉屏息
      平和 emotion=1,rate=95 ./bgm/ 平和_卡农.mp3 白 + 浅绿 平稳淡入 微笑平视
      疑惑 emotion=3,rate=100 ./bgm/ 疑惑_神秘园.mp3 紫 + 灰 左右摇摆 + 模糊 歪头皱眉

核心资源构建:插件、提示词、知识库

  • 插件集成与配置(Coze 可视化操作),打开 Coze→进入工作流编辑器→左侧 “插件”→“插件商店”,搜索目标插件→点击 “添加到我的智能体”→选择所在空间,关键插件配置示例:「语音合成」:默认音色设置(主持人→“沉稳男声”,书籍→“温和知性声”);「视频合成_剪映小助手」:预设分辨率(1080P)、帧率(30 帧 / 秒)

    • 插件 核心参数 配置要点
      语音合成 text(文本)、voice_type(音色) 通过变量传递台词,按角色动态切换 voice_type
      视频合成_剪映小助手 draft_url(草稿地址)、audio_infos(音频配置)、caption_infos(字幕配置) 需与数据整合节点输出的 JSON 格式严格匹配
      画板组件 text(水印文字)、width/height(尺寸) 水印位置设为右下角,字体大小 24 号
  • 提示词模板工程(核心生产力),对话文案生成模板(核心模板)

    • # 角色
      你是擅长书籍拟人化对话的文案师,能将书籍核心内容转化为“主持人+书籍”的访谈式文案。
      
      # 要求
      1. 角色固定:主持人(提问方)、书籍名称(回答方,拟人化称呼如“富爸爸老师”)
      2. 内容结构:每个核心知识点对应1个问题,包含“提问→回答→现实案例→行动建议”4部分
      3. 格式规范:
         - 台词≤10字/短句,用逗号拆分长句
         - 至少生成10个问题,总字数≥1000字
         - 关键知识点(如“资产”“被动收入”)需明确提及
         - 主持人需添加惊讶语气台词(如“啊!原来我一直搞错了?”)增强互动感
      4. 输出格式:
      {
        "role_list": [{"role_name": "主持人"}, {"role_name": "[书籍名称]"}],
        "text_list": [
          {"order": 1, "role_name": "主持人", "line": "台词内容"},
          {"order": 2, "role_name": "[书籍名称]", "line": "台词内容"}
        ]
      }
      
      # 输入信息
      书籍名称:{{book_name}}
      书籍核心信息:{{book_kb}}
      
    • 关键词提取模板

    • 从以下对话文案中提取10个核心关键词,需包含书籍核心概念、痛点、解决方案:
      对话文案:{{dialog_text}}
      输出格式:["关键词1", "关键词2", ...]
      
    • 角色音频区分模板

    • 根据以下台词列表,按角色分配音色:
      - 主持人:沉稳男声,语速正常
      - [书籍名称]:温和知性声,语速稍慢
      台词列表:{{text_list}}
      输出格式:[{"order": 1, "role_name": "主持人", "text": "台词", "voice_type": "沉稳男声"}, ...]
      
  • 知识库调用配置(轻量化 RAG),无需单独部署向量库,采用 Coze “文件检索” 功能:将整理好的书籍 JSON 文件(book_info.json)上传至 Coze “资源管理”→“文件存储”;在工作流 “大模型节点” 中启用 “文件检索”→选择目标书籍文件;配置检索参数:top_k=3(返回 3 条核心信息),确保大模型生成文案时引用书籍真实内容,避免幻觉。

工作流构建

  • 通过与大语言模型,沟通交互而来,主要是智能体工作流开发思路分析和学习记录,对于设计需求,拆解需求,联动分析需求目的和技术实现的过程探讨。暂时对后文的可行性没有完备性实现和讨论分析。

  • 节点 1:输入参数配置(工作流入口)

    • 接收用户输入,标准化任务参数,Coze “输入节点”,设置 3 个参数:book_name(必填,文本类型,提示 “输入书籍名称”);watermark(可选,文本类型,默认 “我的书籍解读”);video_style(可选,下拉选择,选项 “科普风”“访谈风”“趣味风”)
  • 节点 2:书籍知识库检索(RAG 增强)

    • 获取书籍核心信息(作者、主题、案例),Coze “大模型节点”→启用 “文件检索”→选择对应书籍 JSON 文件。提示词:“提取《{{book_name}}》的作者、核心主题、3 个用户痛点、2 个现实案例,输出 JSON 格式”
  • 节点 3:对话文案生成(核心内容生产)

    • 生成 “主持人 + 书籍” 的访谈式文案,Coze “大模型节点”→选择 “豆包 1.5 Pro-128K”→调用 “对话文案生成模板”。输入参数:book_name(节点 1 输出)、book_kb(节点 2 输出)
  • 节点 4:关键词提取(字幕高亮准备)

    • 提取文案核心关键词,用于字幕特殊标记,Coze “大模型节点”→选择 “DeepSeek-V3”→调用 “关键词提取模板”,输入参数:dialog_text(节点 3 输出的 text_list 字段)
  • 节点 5:角色音频生成与时长计算(多线程并行)

    • 区分角色生成音频,并获取每段音频时长(用于音画同步)。配置:先通过 “大模型节点” 调用 “角色音频区分模板”,输出带音色的台词列表;添加 “循环节点”→遍历台词列表,调用 “语音合成插件”:输入:text = 台词内容,voice_type = 角色对应音色,输出:每段音频的 URL;调用 “视频合成_剪映小助手” 的 “get_audio_duration” 功能,获取每段音频时长(秒)
  • 节点 6:背景与角色图片生成(多模态资源)

    • 生成视频背景图和角色头像,配置:背景图:Coze “画板组件”→输入 watermark 参数→生成 1920×1080px 背景(右下角水印);角色图片:直接引用提前准备的主持人 / 书籍拟人化图片 URL(或通过 Stable Diffusion API 生成)。
  • 节点 7:数据整合与时间轴生成(核心编码节点)

    • 整合音频、字幕、图片信息,生成剪映可识别的时间轴数据,Coze “代码节点”→编写 JavaScript 代码(核心逻辑如下),核心功能:合并音频列表与时长列表,计算每段音频的开始 / 结束时间;为字幕添加关键词高亮配置(关键词标红,字体放大);生成角色图片的显示时间轴(与对应角色台词同步)。

    • async function main({ params }) {
        const { audio_list, duration_list, text_list, keywords, bg_image_url } = params;
        let audioStartTime = 0;
        const audioData = [];
        const captions = [];
      
        // 处理音频时间轴
        for (let i = 0; i < audio_list.length; i++) {
          const duration = duration_list[i];
          audioData.push({
            audio_url: audio_list[i],
            start: audioStartTime,
            end: audioStartTime + duration,
            volume: 2
          });
      
          // 处理字幕(关键词高亮)
          const text = text_list[i];
          let captionText = text.line;
          const matchedKeys = keywords.filter(k => captionText.includes(k));
          if (matchedKeys.length > 0) {
            captionText = captionText.replace(new RegExp(`(${matchedKeys.join('|')})`, 'g'), '<span style="color:#fe8a80;font-size:10px;">$1</span>');
          }
      
          captions.push({
            start: audioStartTime,
            end: audioStartTime + duration,
            text: captionText,
            in_animation: "羽化向右擦开"
          });
      
          audioStartTime += duration;
        }
      
        // 处理背景图片
        const bgImageData = [{
          image_url: bg_image_url,
          start: 0,
          end: audioStartTime + 1000,
          width: 1920,
          height: 1080
        }];
      
        return { audioData: JSON.stringify(audioData), captions: JSON.stringify(captions), bgImageData: JSON.stringify(bgImageData) };
      }
      
  • 节点 8:剪映草稿生成与导出(工作流收尾)

    • 生成剪映可导入的 JSON 草稿,完成视频合成,配置:调用 “视频合成_剪映小助手” 的系列功能(按顺序执行):
    • create_draft:创建剪映草稿(参数:width=1920,height=1080)
    • add_images:添加背景图(参数:draft_url = 创建草稿返回值,image_infos = 节点 7 输出的 bgImageData)
    • add_audios:添加音频(参数:draft_url,audio_infos = 节点 7 输出的 audioData)
    • add_captions:添加字幕(参数:draft_url,caption_infos = 节点 7 输出的 captions)
    • save_draft:保存草稿(返回 draft_url)
  • 输入参数 → 知识库检索 → 对话文案生成 → 关键词提取 → 角色音频生成 → 背景/角色图生成 → 数据整合(编码) → 剪映草稿导出 → 剪映客户端编辑导出。

  • AgentLLM 在功能和应用场景上有明显的互补关系。Agent 智能体作为一个综合性的概念,涵盖了从感知到决策再到行动的全过程,而LLM则专注于自然语言的理解和生成。通过将两者结合起来,可以创建更加智能、高效和人性化的系统,应用于各种复杂的任务和场景中。

书籍 AI 视频工作流

  • 针对 “输入书籍名 / PDF→输出绘声绘色 AI 视频” 的核心需求,围绕 “情感传递 + 知识结构化” 双目标。通过 “多粒度摘要分解→情感与风格锚定→结构化思维导图生成→情感驱动多模态生成” 的递进逻辑,强化模型对书籍内容的理解深度,最终输出 “风格适配、情感饱满、知识清晰” 的视频,实现 “10 分钟掌握书籍核心情感 + 知识点关联” 的目标。

    • 工具类型 基础免费方案 进阶付费方案(提升质感) 核心用途 配置要求
      书籍解析工具 PyPDF2(PDF 解析)+ 豆瓣 API(元数据) Adobe Acrobat Pro(PDF 精准提取) 解析 PDF 文本 / 提取书籍元数据(作者 / 类型 / 评分) 免费版需处理 PDF 扫描件 OCR:搭配百度 OCR API(每日免费 100 次)
      大模型平台 豆包 1.5 Pro(Coze 内置) 豆包 Ultra + GPT-4o(情感分析专用) 摘要分解 / 情感分析 / 文案生成 绑定 API 密钥,设置上下文窗口≥128K(适配长文本 PDF)
      语音合成(TTS) 讯飞 TTS 免费版(情感合成) ElevenLabs(角色音色定制) 生成带情感的角色语音(主持人 / 书籍拟人化) 免费版需配置 “情感参数”(如 “兴奋度 = 0.8”“语速 = 0.9”)
      视觉生成工具 Stable Diffusion WebUI(本地部署) MidJourney(风格化插画)+ Canva 专业版 生成风格化背景 / 角色插画 / 知识点可视化图 本地部署需配置显存≥8G;MidJourney 需加入 Discord 频道,绑定支付方式
      视频合成工具 MoviePy(Python 库)+ 剪映免费版 Adobe Premiere Pro + AE(动效) 音画同步 / 字幕动效 / 知识点高亮 MoviePy 需安装 ffmpeg(配置环境变量);剪映需升级至 “专业版”(解锁关键帧)
      知识库工具 Chroma(本地向量库) Pinecone(云端向量库) 存储书籍分块文本 / 思维导图节点 / 情感标签 本地部署 Chroma 需 Python≥3.8,初始化时设置persist_directory="./book_kb"
      工作流引擎 Python + Airflow(本地调度) Coze 专业版(可视化编排 + 定时触发) 串联 12 个节点,管理状态与重试机制 Airflow 需配置 DAG 文件,设置retries=3(节点失败重试)
  • 精细化资源库构建,书籍类型 - 风格映射库(手动配置,可复用),核心解决 “风格适配” 问题,按书籍类型预设视觉 / 听觉风格,存储为 JSON 文件(路径:./config/style_mapping.json):

    • {
        "小说": {
          "视觉风格": {
            "背景类型": "场景插画",
            "色调": "暖色调(文学小说)/冷色调(科幻小说)",
            "角色插画风格": "写实风(历史小说)/动漫风(青春小说)",
            "知识点可视化": "人物关系图(用虚线连接次要关系)+ 情节时间轴"
          },
          "听觉风格": {
            "主持人音色": "沉稳男声(历史)/清亮女声(青春)",
            "背景音乐类型": "纯音乐(钢琴+小提琴)",
            "情感音效": "场景匹配(如战争小说加炮火背景音)"
          },
          "文案风格": "故事化叙事(含角色对话引用),情感词占比≥30%"
        },
        "科普": {
          "视觉风格": {
            "背景类型": "科技感网格",
            "色调": "蓝白冷色调",
            "知识点可视化": "流程图(因果关系)+ 数据图表(对比类知识)",
            "动效": "知识点出现时用“缩放+渐显”"
          },
          "听觉风格": {
            "主持人音色": "清晰中性声",
            "背景音乐类型": "轻电子音效(节奏≤100BPM)",
            "情感音效": "知识点强调时用“叮”提示音"
          },
          "文案风格": "逻辑化拆解(分点但不直白说“第一”),专业术语解释占比≥20%"
        },
        "历史": {
          "视觉风格": {
            "背景类型": "古卷纹理",
            "色调": "复古黄+褐色",
            "知识点可视化": "时间轴(标重点事件)+ 人物关系树",
            "动效": "画面切换用“翻页”效果"
          },
          "听觉风格": {
            "主持人音色": "浑厚男声",
            "背景音乐类型": "古筝+鼓点(慢节奏)",
            "情感音效": "朝代更替时用“钟鸣”音效"
          },
          "文案风格": "时空叙事(含“公元XX年”时间锚点),历史背景补充占比≥25%"
        }
      }
      
  • 预处理资源包(批量准备,提升效率),通用元素:主持人固定形象(3 个角度:正面讲解 / 侧面指向屏幕 / 低头翻书)、转场动画(5 种基础款:淡入淡出 / 推拉 / 旋转 / 翻页 / 缩放)、字幕模板(按书籍类型分 3 套:小说用圆角气泡 / 科普用方角框 / 历史用古卷边)。工具脚本:preprocess_resource.py(批量处理图片尺寸为 1920×1080、音频格式为 MP3。

核心知识强化

  • 多粒度摘要分解(避免漏内容,分 3 层递进),分解逻辑(按 “书籍→章节→段落” 拆解,每层绑定情感标签)

    • 粒度 核心目标 输出格式(JSON) 分析工具 人工确认点
      书籍级 抓核心主题 + 整体情感基调 {"title":"三体","core_theme":["黑暗森林法则","人性与文明冲突"],"emotion_tone":"紧张+悲凉","author_view":"对技术失控的警惕"} 大模型(豆包 Ultra)+ 豆瓣书评分析 主题是否完整、情感基调是否准确
      章节级 抓核心事件 + 情感转折 {"chapter_id":"c01","title":"红岸基地","core_event":"叶文洁触发信号","emotion_change":["压抑→震惊→绝望"],"key_character":["叶文洁"]} 大模型 + TextRank 关键词提取 核心事件是否遗漏、情感转折是否合理
      段落级 抓关键句 + 局部情感 {"paragraph_id":"p03","text":"叶文洁按下按钮的瞬间...","key_sentence":"人类文明的命运在此刻转向","local_emotion":"绝望","relevance":0.92} 大模型 + SnowNLP 情感分析 关键句是否精准、局部情感是否匹配
    • 实现代码(./core/multi_granularity_summary.py

    • import json
      import jieba
      from snownlp import SnowNLP
      from textrank4zh import TextRank4Keyword
      from langchain.chat_models import ChatDoubao
      
      # 初始化工具
      llm = ChatDoubao(model="doubao-pro-128k", api_key="key")
      tr4k = TextRank4Keyword()
      
      def parse_book_level_summary(book_text: str, book_name: str) -> dict:
          """书籍级摘要:核心主题+情感基调"""
          prompt = f"""
          分析书籍《{book_name}》的以下核心信息:
          1. 3个以内核心主题(用短语,避免宽泛)
          2. 1个整体情感基调(如“紧张+悲凉”,限2个词)
          3. 作者对核心主题的核心观点(1句话)
          输入文本:{book_text[:10000]}(截取前10000字,保证覆盖核心)
          输出严格JSON,无多余内容。
          """
          response = llm.predict(prompt)
          book_summary = json.loads(response)
          # 补充豆瓣书评情感验证(可选,提升准确性)
          # douban_reviews = get_douban_reviews(book_name)  # 需实现豆瓣API调用
          # book_summary["emotion_tone"] = verify_emotion_with_reviews(book_summary["emotion_tone"], douban_reviews)
          return book_summary
      
      def parse_chapter_level_summary(chapter_text: str, chapter_id: str, chapter_title: str) -> dict:
          """章节级摘要:核心事件+情感转折"""
          # 提取核心事件(TextRank关键词辅助)
          tr4k.analyze(text=chapter_text, lower=True, window=2)
          keywords = [item.word for item in tr4k.get_keywords(5, word_min_len=2)]
          
          prompt = f"""
          分析章节《{chapter_title}》的以下信息:
          1. 1个核心事件(含人物+动作+结果)
          2. 情感转折(按顺序,限3个词,如“压抑→震惊→绝望”)
          3. 关键人物(限2个以内)
          输入文本:{chapter_text}
          关键词参考:{keywords}
          输出严格JSON,无多余内容。
          """
          response = llm.predict(prompt)
          chapter_summary = json.loads(response)
          chapter_summary["chapter_id"] = chapter_id
          chapter_summary["title"] = chapter_title
          return chapter_summary
      
      def parse_paragraph_level_summary(paragraph_text: str, paragraph_id: str, chapter_id: str) -> dict:
          """段落级摘要:关键句+局部情感"""
          # 关键句提取(TextRank)
          tr4k.analyze(text=paragraph_text, lower=True, window=1)
          key_sentences = [item.sentence for item in tr4k.get_key_sentences(num=1)]
          
          # 局部情感分析(SnowNLP,0-1分,0=极悲,1=极喜)
          s = SnowNLP(paragraph_text)
          emotion_score = s.sentiments
          # 映射情感标签
          if emotion_score < 0.3:
              local_emotion = "悲伤"
          elif 0.3 <= emotion_score < 0.5:
              local_emotion = "压抑"
          elif 0.5 <= emotion_score < 0.7:
              local_emotion = "平和"
          elif 0.7 <= emotion_score < 0.9:
              local_emotion = "愉悦"
          else:
              local_emotion = "激昂"
          
          return {
              "paragraph_id": paragraph_id,
              "chapter_id": chapter_id,
              "text": paragraph_text,
              "key_sentence": key_sentences[0] if key_sentences else paragraph_text[:50],
              "local_emotion": local_emotion,
              "emotion_score": round(emotion_score, 2),
              "relevance": round(min(1.0, len(key_sentences[0])/len(paragraph_text)*1.2), 2)  # 相关性评分
          }
      
      # 批量处理示例(调用入口)
      def batch_multi_granularity_summary(book_pdf_path: str = None, book_name: str = None) -> dict:
          """
          批量生成3级摘要:
          输入:二选一(PDF路径/书籍名,PDF优先,书籍名需调用API获取文本)
          输出:包含书籍/章节/段落级摘要的完整字典
          """
          # 1. 解析书籍文本(PDF或API获取)
          if book_pdf_path:
              book_text, chapters = parse_pdf_to_text(book_pdf_path)  # 需实现PDF解析函数
          else:
              book_text, chapters = get_book_text_from_api(book_name)  # 需实现书籍API调用
          
          # 2. 生成3级摘要
          book_summary = parse_book_level_summary(book_text, book_name or chapters[0]["title"])
          chapter_summaries = []
          paragraph_summaries = []
          
          for chapter in chapters:
              chapter_summary = parse_chapter_level_summary(chapter["text"], chapter["chapter_id"], chapter["title"])
              chapter_summaries.append(chapter_summary)
              
              # 拆分段落(按换行符)
              paragraphs = [p.strip() for p in chapter["text"].split("\n") if p.strip()]
              for i, para in enumerate(paragraphs):
                  para_id = f"p{chapter['chapter_id'][1:]}_{i+1}"  # 格式:p01_03(第1章第3段)
                  para_summary = parse_paragraph_level_summary(para, para_id, chapter["chapter_id"])
                  paragraph_summaries.append(para_summary)
          
          # 3. 关联3级摘要(添加父ID)
          book_summary["chapter_ids"] = [c["chapter_id"] for c in chapter_summaries]
          for c in chapter_summaries:
              c["paragraph_ids"] = [p["paragraph_id"] for p in paragraph_summaries if p["chapter_id"] == c["chapter_id"]]
          
          return {
              "book_level": book_summary,
              "chapter_level": chapter_summaries,
              "paragraph_level": paragraph_summaries,
              "create_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
          }
      
  • 结构化思维导图生成(理清关系,关联情感),思维导图核心结构(分 “知识层” 和 “情感层” 双维度)

    • 维度 层级结构 关联内容 可视化规则
      知识层 根节点→核心主题→子主题→知识点 书籍级主题→章节级事件→段落级关键句 根节点(书籍名,红色)→核心主题(蓝色)→子主题(绿色)→知识点(黑色)
      情感层 核心基调→章节情感转折→段落局部情感 书籍级基调→章节转折→段落情感标签 用 “情感图标” 标注(激昂 =🔥/ 悲伤 =💧/ 紧张 =⚠️)
      关系层 人物关系 / 知识点关联 关键人物→互动关系;知识点→因果关系 人物用 “头像图标”,因果关系用 “箭头”(正向 =→/ 反向 =←)
    • 实现代码(./core/mindmap_generator.py,关联摘要结果)

    • import json
      import svgwrite
      from graphviz import Digraph
      from typing import List, Dict
      
      def generate_structured_mindmap(summary_data: dict, output_format: str = "svg+json") -> dict:
          """
          生成结构化思维导图:
          输入:多粒度摘要数据(batch_multi_granularity_summary的输出)
          输出:JSON结构+可视化文件(SVG/PNG)
          """
          book_summary = summary_data["book_level"]
          chapter_summaries = summary_data["chapter_level"]
          paragraph_summaries = summary_data["paragraph_level"]
          
          # 1. 构建思维导图JSON结构(知识+情感+关系)
          mindmap_json = {
              "root": {
                  "id": "root",
                  "label": book_summary["title"],
                  "type": "book",
                  "emotion_tone": book_summary["emotion_tone"],
                  "children": []  # 核心主题节点
              }
          }
          
          # 1.1 构建知识层(核心主题→子主题→知识点)
          core_themes = book_summary["core_theme"]
          for theme_idx, theme in enumerate(core_themes):
              theme_node = {
                  "id": f"theme_{theme_idx+1}",
                  "label": theme,
                  "type": "core_theme",
                  "children": []  # 子主题(章节级事件)
              }
              mindmap_json["root"]["children"].append(theme_node)
              
              # 关联章节级子主题(核心事件)
              for chapter in chapter_summaries:
                  # 匹配主题与章节(大模型判断关联性)
                  relevance = judge_relevance(theme, chapter["core_event"])  # 需实现关联性判断函数
                  if relevance > 0.6:
                      chapter_node = {
                          "id": chapter["chapter_id"],
                          "label": chapter["core_event"],
                          "type": "chapter_event",
                          "emotion_change": chapter["emotion_change"],
                          "children": []  # 知识点(段落级关键句)
                      }
                      theme_node["children"].append(chapter_node)
                      
                      # 关联段落级知识点(关键句)
                      for para in paragraph_summaries:
                          if para["chapter_id"] == chapter["chapter_id"] and para["relevance"] > 0.8:
                              para_node = {
                                  "id": para["paragraph_id"],
                                  "label": para["key_sentence"],
                                  "type": "paragraph_key",
                                  "local_emotion": para["local_emotion"],
                                  "emotion_score": para["emotion_score"]
                              }
                              chapter_node["children"].append(para_node)
          
          # 1.2 构建关系层(人物关系/知识点关联)
          # 提取关键人物
          key_characters = list(set([c for chapter in chapter_summaries for c in chapter.get("key_character", [])]))
          # 生成人物关系(大模型分析)
          character_relations = analyze_character_relations(key_characters, summary_data)  # 需实现人物关系分析
          # 生成知识点关联(大模型分析因果)
          knowledge_relations = analyze_knowledge_relations(mindmap_json, summary_data)  # 需实现知识点关联分析
          
          mindmap_json["relations"] = {
              "character": character_relations,
              "knowledge": knowledge_relations
          }
          
          # 2. 生成可视化文件(SVG+Graphviz PNG)
          outputs = {"json": mindmap_json}
          
          if "svg" in output_format:
              svg_path = f"./output/mindmap_{book_summary['title'].replace(' ', '_')}.svg"
              generate_mindmap_svg(mindmap_json, svg_path)
              outputs["svg_path"] = svg_path
          
          if "png" in output_format:
              png_path = f"./output/mindmap_{book_summary['title'].replace(' ', '_')}.png"
              generate_mindmap_png(mindmap_json, png_path)
              outputs["png_path"] = png_path
          
          return outputs
      
      def generate_mindmap_svg(mindmap_json: dict, output_path: str):
          """生成可交互SVG思维导图(支持点击展开/折叠)"""
          dwg = svgwrite.Drawing(output_path, profile='full', size=('1200px', '800px'))
          # 根节点位置(中心)
          root_x, root_y = 600, 100
          # 绘制根节点
          dwg.add(dwg.circle(center=(root_x, root_y), r=30, fill="#E74C3C", stroke="#000", stroke_width=2))
          dwg.add(dwg.text(mindmap_json["root"]["label"], insert=(root_x-50, root_y+5), font_size=14, fill="white", text_anchor="middle"))
          
          # 递归绘制子节点(核心主题→章节→知识点)
          def draw_child_nodes(parent_node, parent_x, parent_y, level: int, direction: str):
              """
              level:层级(1=核心主题,2=章节,3=知识点)
              direction:方向(left/right,避免重叠)
              """
              child_count = len(parent_node.get("children", []))
              if child_count == 0:
                  return
              # 层级间距
              level_spacing = 200 if level == 1 else 150 if level == 2 else 100
              # 子节点间距
              child_spacing = 120 if level == 1 else 80 if level == 2 else 50
              # 起始位置
              start_y = parent_y - (child_count - 1) * child_spacing / 2
              
              for i, child in enumerate(parent_node["children"]):
                  child_y = start_y + i * child_spacing
                  child_x = parent_x - level_spacing if direction == "left" else parent_x + level_spacing
                  
                  # 节点颜色(按类型)
                  color_map = {"core_theme": "#3498DB", "chapter_event": "#2ECC71", "paragraph_key": "#95A5A6"}
                  fill_color = color_map.get(child["type"], "#BDC3C7")
                  
                  # 绘制连接线
                  dwg.add(dwg.line(start=(parent_x, parent_y), end=(child_x, child_y), stroke="#000", stroke_width=1.5))
                  # 绘制节点
                  dwg.add(dwg.circle(center=(child_x, child_y), r=20 if level < 3 else 15, fill=fill_color, stroke="#000", stroke_width=1))
                  # 节点文本(截断长文本)
                  label = child["label"][:15] + "..." if len(child["label"]) > 15 else child["label"]
                  dwg.add(dwg.text(label, insert=(child_x-30, child_y+3), font_size=12 if level < 3 else 10, fill="white", text_anchor="middle"))
                  
                  # 绘制情感图标(如果有)
                  if "emotion_tone" in child or "local_emotion" in child:
                      emotion = child.get("emotion_tone") or child.get("local_emotion")
                      emoji = {"激昂": "🔥", "悲伤": "💧", "紧张": "⚠️", "平和": "😌", "疑惑": "❓"}.get(emotion, "❓")
                      dwg.add(dwg.text(emoji, insert=(child_x+15, child_y-15), font_size=12))
                  
                  # 递归绘制下一级(交替方向)
                  next_direction = "right" if direction == "left" else "left"
                  if level < 3:  # 只画3级,避免过于复杂
                      draw_child_nodes(child, child_x, child_y, level + 1, next_direction)
          
          # 绘制核心主题(左右分栏)
          core_themes = mindmap_json["root"]["children"]
          left_themes = core_themes[:len(core_themes)//2]
          right_themes = core_themes[len(core_themes)//2:]
          
          # 左侧核心主题
          for theme in left_themes:
              draw_child_nodes(theme, root_x, root_y, level=1, direction="left")
          # 右侧核心主题
          for theme in right_themes:
              draw_child_nodes(theme, root_x, root_y, level=1, direction="right")
          
          # 保存SVG
          dwg.save()
      
      def generate_mindmap_png(mindmap_json: dict, output_path: str):
          """生成Graphviz PNG(适合打印,结构清晰)"""
          dot = Digraph(comment=mindmap_json["root"]["label"], format="png")
          dot.attr(size='10,8', rankdir='TB', bgcolor='white')
          
          # 根节点
          dot.node("root", mindmap_json["root"]["label"], shape="ellipse", style="filled", color="#E74C3C", fontcolor="white")
          
          # 递归添加节点和边
          def add_nodes_edges(parent_id: str, parent_node: dict):
              for child in parent_node.get("children", []):
                  # 节点样式(按类型)
                  style_map = {
                      "core_theme": "filled,rounded",
                      "chapter_event": "filled,rounded",
                      "paragraph_key": "rounded"
                  }
                  color_map = {
                      "core_theme": "#3498DB",
                      "chapter_event": "#2ECC71",
                      "paragraph_key": "#95A5A6"
                  }
                  # 节点标签(含情感)
                  label = child["label"]
                  if "local_emotion" in child:
                      label += f"\n({child['local_emotion']})"
                  dot.node(child["id"], label, style=style_map.get(child["type"], "rounded"), color=color_map.get(child["type"], "#BDC3C7"), fontcolor="white" if child["type"] != "paragraph_key" else "black")
                  # 边
                  dot.edge(parent_id, child["id"], style="solid")
                  # 递归
                  add_nodes_edges(child["id"], child)
          
          # 添加知识层节点
          add_nodes_edges("root", mindmap_json["root"])
          
          # 添加关系层(人物关系用虚线)
          for rel in mindmap_json["relations"]["character"]:
              dot.edge(rel["from"], rel["to"], label=rel["relation"], style="dashed", color="#F39C12")
          
          # 保存PNG
          dot.render(output_path.replace(".png", ""), view=False)  # 自动添加.png后缀
      
  • 工作流总览(输入→输出全链路)

    • 输入:书籍名/PDF
      节点1:书籍解析与文本提取
      节点2:多粒度摘要分解,书籍>章节>段落
      节点3:书籍类型与风格匹配
      节点4:结构化思维导图生成
      节点5:情感与内容锚定,关联摘要+思维导图
      节点6:情感驱动文案生成,主持人+拟人化书籍
      节点7:多角色TTS生成'情感匹配'
      节点8:风格化视觉素材生成'背景+插画+知识点图'
      节点9:字幕生成与情感动效绑定
      节点10:音画同步与视频粗剪
      节点11:人工审核与微调'关键节点'
      节点12:视频渲染与输出'多格式适配'
    • 节点 1:书籍解析与文本提取(基础输入处理)

      • 将书籍名 / PDF 转化为结构化文本(分章节),解决 “输入格式不统一” 问题。输入:书籍名(字符串)/PDF 路径(字符串,优先),输出:结构化文本字典({"book_name":"三体","total_text":"完整文本","chapters":[{"chapter_id":"c01","title":"红岸基地","text":"章节文本","page_range":"1-25"}]}
      • 实现方式:编码(Python)PDF 解析:用PyPDF2读取文本,扫描件用pytesseract+PIL做 OCR(需安装 Tesseract 中文包);书籍名解析:调用豆瓣 API+Google Books API 获取文本,优先选带章节划分的版本;代码核心逻辑:./core/book_parser.py(含 PDF 解析、API 调用、章节拆分)
    • 节点 2:多粒度摘要分解(知识强化核心)

      • 生成 “书籍→章节→段落”3 级摘要,绑定情感标签,避免漏内容。输入:节点 1 的结构化文本字典,输出:3 级摘要字典。实现方式:编码(Python)+ 大模型调用;核心代码:./core/multi_granularity_summary.py
    • 节点 3:书籍类型与风格匹配(风格适配核心)

      • 根据书籍文本 / 元数据判断类型,匹配预设风格库(视觉 + 听觉 + 文案),输入:节点 1 的书籍名 / 元数据、节点 2 的书籍级摘要(核心主题),输出:风格匹配结果字典({"book_type":"科幻小说","visual_style":{"背景类型":"场景插画","色调":"冷色调"},"audio_style":{"主持人音色":"沉稳男声"},"copy_style":"故事化叙事"})。实现方式:编码(Python)+ 规则匹配:书籍类型判断:用关键词匹配(如 “宇宙”“星球”→科幻;“公元”“朝代”→历史)+ 大模型辅助校正;风格匹配:读取./config/style_mapping.json,按类型提取风格参数;核心代码:./core/style_matcher.py
    • 节点 4:结构化思维导图生成(知识可视化核心)

      • 生成 “知识 + 情感 + 关系” 三维思维导图(JSON+SVG+PNG),输入:节点 2 的 3 级摘要字典,输出:思维导图结果字典(含 JSON 结构、SVG 路径、PNG 路径)。实现方式:编码(Python)+ 可视化库,核心代码:./core/mindmap_generator.py
    • 节点 5:情感与内容锚定(情感传递核心)

      • 建立 “摘要段落→情感标签→多模态资源” 的映射关系,确保情感一致性,输入:节点 2 的段落级摘要(含局部情感)、节点 3 的风格结果、节点 4 的思维导图(情感层);输出:情感 - 内容映射表(JSON)

      • {
          "mapping_list": [
            {
              "paragraph_id": "p01_03",
              "key_sentence": "叶文洁按下了发射按钮",
              "local_emotion": "绝望",
              "tts_params": {"emotion":4,"rate":80,"voice":"沉稳女声"},
              "bgm_params": {"path":"./bgm/悲伤_月光下的凤尾竹.mp3","volume":0.3},
              "visual_params": {"bg_color":"灰+蓝","illustration":"叶文洁低头按按钮(悲伤表情)","motion":"缓慢淡入"},
              "mindmap_node_id": "p01_03"
            }
          ],
          "emotion_statistics": {"绝望":3,"紧张":5,"平和":2}  // 情感分布,用于整体节奏把控
        }
        
      • 实现方式:编码(Python)+ 规则匹配,读取./config/emotion_resource.csv,按段落情感标签匹配资源参数;结合风格结果微调(如科幻小说的 “悲伤” 背景色调整为 “深蓝 + 灰”),代码:./core/emotion_content_anchor.py

    • 节点 6:情感驱动文案生成(内容呈现核心)

      • 生成 “主持人 + 拟人化书籍” 的访谈式文案,绑定情感节奏与知识点,输入:节点 5 的情感 - 内容映射表、节点 4 的思维导图(知识层)、节点 3 的风格结果,输出:结构化文案字典(含角色、台词、情感标签、关联知识点)

      • {
          "role_list": [
            {"role_id":"host","name":"主持人","style":"科幻风:理性中带共情","voice_type":"沉稳男声"},
            {"role_id":"book","name":"《三体》","style":"拟人化:经历者口吻","voice_type":"温和女声(带沧桑感)"}
          ],
          "script_list": [
            {
              "script_id":"s01",
              "order":1,
              "role_id":"host",
              "line":"大家好,今天我们邀请到《三体》,聊聊红岸基地的故事。",
              "emotion":"平和",
              "related_mindmap_node":"root",
              "duration":5  // 预估朗读时长(秒)
            },
            {
              "script_id":"s02",
              "order":2,
              "role_id":"book",
              "line":"1967年的大兴安岭,我见证了改变文明的一刻。",
              "emotion":"压抑",
              "related_mindmap_node":"c01",
              "duration":4
            },
            {
              "script_id":"s03",
              "order":3,
              "role_id":"host",
              "line":"是叶文洁按下发射按钮的瞬间吗?那刻您是什么感受?",
              "emotion":"好奇",
              "related_mindmap_node":"p01_03",
              "duration":6
            },
            {
              "script_id":"s04",
              "order":4,
              "role_id":"book",
              "line":"那不是勇气,是绝望后的破罐破摔。",
              "emotion":"绝望",
              "related_mindmap_node":"p01_03",
              "duration":5
            }
          ],
          "knowledge_check": {"覆盖核心主题数":2,"遗漏知识点":[]}  // 知识点覆盖校验
        }
        
      • 实现方式:大模型调用(豆包 Ultra)+ 提示词工程,核心提示词模板(./config/prompt/script_generator.txt):

      • 角色:
        1. 主持人:按【{style.copy_style}】风格提问,引导书籍讲出核心知识点,每3句加1个情感互动(如“那刻一定很绝望吧?”)
        2. 书籍拟人化:以“经历者”口吻回答,引用【{key_sentence}】,带【{local_emotion}】情感,补充1句背景细节
        
        要求:
        1. 结构:按“引入→知识点1→情感互动→知识点2→总结”流转,每知识点对应1组问答
        2. 情感节奏:参考【{emotion_statistics}】,避免连续3句同一情感,高潮部分(如紧张)集中在中间1/3
        3. 知识点:必须覆盖思维导图【{core_theme}】,关联节点ID标注在related_mindmap_node
        4. 格式:严格JSON,无多余内容,line字段每句≤15字,用口语化表达
        
        输入:
        情感-内容映射表:{mapping_list}
        核心主题:{core_theme}
        风格:{style}
        
      • 代码逻辑:./core/script_generator.py(加载模板→填充参数→调用大模型→JSON 解析→知识点覆盖校验)

    • 节点 7:多角色 TTS 生成(情感听觉传递)

      • 按角色 + 情感标签生成带情感的语音,确保 “音色 + 情感” 双匹配。输入:节点 6 的结构化文案字典、节点 5 的情感 - 内容映射表;输出:多角色音频字典(含分段音频 + 完整音频)

      • {
          "audio_list": [
            {"script_id":"s01","role_id":"host","audio_path":"./audio/s01_host.mp3","duration":4.8,"emotion":"平和"},
            {"script_id":"s02","role_id":"book","audio_path":"./audio/s02_book.mp3","duration":3.9,"emotion":"压抑"}
          ],
          "total_audio_path":"./audio/total_audio.mp3",
          "emotion_check": {"匹配度":0.92,"不匹配项":[{"script_id":"s05","reason":"情感为“激昂”但语速不足"}]}
        }
        
      • 实现方式:编码(Python)+ TTS API 调用(讯飞 / ElevenLabs),遍历文案列表→按角色 + 情感标签取 TTS 参数(如讯飞emotion=4代表悲伤)→ 调用 API 生成音频→ 用pydub合并为完整音频→ 校验音频时长与预估时长偏差(≤0.5 秒),代码:./core/tts_generator.py。同一角色用固定音色 ID,确保一致性;情感高潮部分提升音量 10%

    • 节点 8:风格化视觉素材生成(风格视觉传递)

      • 生成 “背景 + 角色插画 + 知识点可视化图”,匹配书籍风格与情感,输入:节点 3 的风格结果、节点 4 的思维导图(可视化文件)、节点 5 的情感 - 内容映射表,输出:视觉素材字典(含路径 + 关联文案 ID)

      • {
          "background_list": [
            {"scene_id":"bg01","emotion":"平和","path":"./visual/bg01_peaceful.png","style":"科幻风:深蓝网格背景"},
            {"scene_id":"bg02","emotion":"绝望","path":"./visual/bg02_desperate.png","style":"科幻风:灰蓝废墟背景"}
          ],
          "illustration_list": [
            {"script_id":"s02","role":"《三体》拟人化","path":"./visual/ill01_book.png","style":"动漫风:女性轮廓+星空背景","emotion":"压抑"},
            {"script_id":"s04","role":"叶文洁","path":"./visual/ill02_ye.png","style":"写实风:低头按按钮","emotion":"绝望"}
          ],
          "knowledge_visual_list": [
            {"mindmap_node_id":"theme_1","path":"./visual/knowledge01_dark_forest.png","type":"流程图","style":"科幻风:黑色背景+白色线条"}
          ]
        }
        
      • 实现方式:AI 绘画 API(Stable Diffusion/MidJourney)+ 编码。提示词工程:按 “风格 + 场景 + 情感 + 细节” 结构生成提示词(如科幻绝望背景:“科幻风格,废墟红岸基地,灰蓝色调,下雨,昏暗灯光,细节丰富,8k”),代码:./core/visual_generator.py

    • 节点 9:字幕生成与情感动效绑定(情感可视化传递)

      • 生成带情感动效的字幕,关联音频时长,强化情感传递,输入:节点 6 的结构化文案字典、节点 7 的音频列表、节点 5 的情感 - 内容映射表。输出:字幕文件(SRT+JSON 动效配置),SRT 文件(./subtitle/script.srt):标准字幕格式,含时间轴;动效配置(./subtitle/effect_config.json):

      • {
          "subtitle_list": [
            {
              "script_id":"s01",
              "start_time":"00:00:00,000",
              "end_time":"00:00:04,800",
              "text":"大家好,今天我们邀请到《三体》。",
              "emotion":"平和",
              "effect": {"type":"淡入","duration":0.5,"color":"#FFFFFF","font_size":24},
              "position":"bottom-center"
            },
            {
              "script_id":"s04",
              "start_time":"00:00:14,700",
              "end_time":"00:00:19,700",
              "text":"那不是勇气,是绝望后的破罐破摔。",
              "emotion":"绝望",
              "effect": {"type":"缓慢下移+淡暗","duration":0.8,"color":"#FF6B6B","font_size":26,"bold":true},
              "position":"bottom-center"
            }
          ]
        }
        
      • 实现方式:编码(Python):遍历文案→ 按音频时长计算字幕时间轴(开始时间 = 前一段结束时间,结束时间 = 开始时间 + 音频时长)→ 按情感标签匹配动效(读取./config/emotion_resource.csv的 “字幕动效” 字段)→ 生成 SRT 和 JSON 配置。代码:./core/subtitle_generator.py

    • 节点 10:音画同步与视频粗剪(核心合成环节)

      • 实现 “音频(情感 TTS)+ 视觉(风格化素材)+ 字幕(情感动效)” 的精准同步,生成可编辑的粗剪视频与工程文件。输入:音频列表:节点 7 输出的audio_list(分段音频路径 + 时长);视觉素材:节点 8 输出的visual_dict(背景 / 插画 / 知识点图路径 + 关联脚本 ID);字幕配置:节点 9 输出的subtitle_config(SRT 文件 + 动效 JSON);文案字典:节点 6 输出的script_dict(脚本顺序 + 情感标签);风格配置:节点 3 输出的style_dict(转场 / 音量 / 分辨率参数)。输出:粗剪视频:./output/rough_cut/[书籍名]_rough.mp4(1080P,30 帧);工程文件:./output/project/[书籍名]_project.xml(支持剪映 / Pr 导入);同步校验报告:./output/log/[书籍名]_sync_report.json(音画同步偏差记录)。代码:./core/video_rough_cut.py

      • import os
        import json
        import xml.etree.ElementTree as ET
        from xml.dom import minidom
        from moviepy.editor import (
            VideoClip, AudioFileClip, ImageClip, CompositeVideoClip,
            TextClip, concatenate_videoclips, TransitionClip, ColorClip
        )
        from moviepy.video.fx.all import resize, fadein, fadeout
        from typing import Dict, List
        
        # 全局配置(与风格配置联动)
        GLOBAL_CONFIG = {
            "resolution": (1920, 1080),  # 固定1080P
            "fps": 30,
            "transition_duration": 0.5,  # 转场时长(秒)
            "bgm_volume_ratio": 0.2,     # 背景音乐音量占比
            "font_path": "./resources/fonts/SimHei.ttf",  # 中文字体路径(需提前放置)
            "temp_dir": "./temp/rough_cut",  # 临时文件目录
        }
        os.makedirs(GLOBAL_CONFIG["temp_dir"], exist_ok=True)
        
        def load_resources(audio_list: List[Dict], visual_dict: Dict, subtitle_config: Dict) -> Dict:
            """加载并预处理所有音视频资源(统一格式+缓存)"""
            resources = {"audio": {}, "visual": {}, "subtitle": {}}
            
            # 1. 加载音频(统一MP3格式,缓存时长)
            for audio in audio_list:
                script_id = audio["script_id"]
                clip = AudioFileClip(audio["audio_path"]).set_fps(44100)
                resources["audio"][script_id] = {
                    "clip": clip,
                    "duration": clip.duration,
                    "start_time": 0.0,  # 后续计算时间轴
                    "end_time": 0.0
                }
            
            # 2. 加载视觉素材(统一分辨率,预处理动效)
            # 背景素材(按情感分类缓存)
            resources["visual"]["background"] = {}
            for bg in visual_dict["background_list"]:
                emotion = bg["emotion"]
                clip = ImageClip(bg["path"]).resize(GLOBAL_CONFIG["resolution"]).set_fps(GLOBAL_CONFIG["fps"])
                resources["visual"]["background"][emotion] = clip
            
            # 插画素材(按脚本ID关联)
            resources["visual"]["illustration"] = {}
            for ill in visual_dict["illustration_list"]:
                script_id = ill.get("script_id")
                if script_id:
                    clip = ImageClip(ill["path"]).resize((400, 400)).set_fps(GLOBAL_CONFIG["fps"])
                    # 预处理入场动效(淡入+轻微缩放)
                    clip = clip.fadein(0.3).resize(lambda t: 1 + 0.05 * t if t < 1 else 1.05)
                    resources["visual"]["illustration"][script_id] = clip
            
            # 知识点图(按思维导图节点ID关联)
            resources["visual"]["knowledge"] = {}
            for kv in visual_dict["knowledge_visual_list"]:
                node_id = kv["mindmap_node_id"]
                clip = ImageClip(kv["path"]).resize((800, 600)).set_fps(GLOBAL_CONFIG["fps"])
                resources["visual"]["knowledge"][node_id] = clip
            
            # 3. 加载字幕(关联脚本ID,缓存动效参数)
            for sub in subtitle_config["subtitle_list"]:
                script_id = sub["script_id"]
                resources["subtitle"][script_id] = sub
            
            return resources
        
        def calculate_timeline(script_list: List[Dict], audio_resources: Dict) -> List[Dict]:
            """计算每段脚本的时间轴(确保音频+字幕+视觉同步)"""
            timeline = []
            current_time = 0.0  # 起始时间(秒)
            
            for script in script_list:
                script_id = script["script_id"]
                emotion = script["emotion"]
                related_node = script["related_mindmap_node"]
                
                # 匹配音频并更新时间轴
                audio = audio_resources.get(script_id)
                if not audio:
                    raise ValueError(f"脚本{script_id}未找到对应音频")
                
                audio["start_time"] = current_time
                audio["end_time"] = current_time + audio["duration"]
                
                # 组装时间轴信息
                timeline.append({
                    "script_id": script_id,
                    "emotion": emotion,
                    "related_node": related_node,
                    "audio_start": audio["start_time"],
                    "audio_end": audio["end_time"],
                    "duration": audio["duration"]
                })
                
                # 更新当前时间(叠加转场时长,最后一段不叠加)
                if script != script_list[-1]:
                    current_time = audio["end_time"] + GLOBAL_CONFIG["transition_duration"]
                else:
                    current_time = audio["end_time"]
            
            return timeline
        
        def generate_script_clip(
            timeline_item: Dict,
            resources: Dict,
            style_dict: Dict
        ) -> CompositeVideoClip:
            """生成单段脚本的视频片段(背景+插画+字幕+音频)"""
            script_id = timeline_item["script_id"]
            emotion = timeline_item["emotion"]
            duration = timeline_item["duration"]
            related_node = timeline_item["related_node"]
            
            # 1. 背景层(按情感匹配)
            bg_clip = resources["visual"]["background"].get(emotion)
            if not bg_clip:
                bg_clip = resources["visual"]["background"]["平和"]  # 默认平和背景
            bg_clip = bg_clip.set_duration(duration)
            
            # 2. 插画层(按脚本ID匹配,无则跳过)
            ill_clip = resources["visual"]["illustration"].get(script_id)
            visual_clips = [bg_clip]
            if ill_clip:
                # 位置:右侧中间,避免遮挡字幕
                ill_clip = ill_clip.set_duration(duration).set_position(("right", "center"))
                visual_clips.append(ill_clip)
            
            # 3. 知识点图层(按思维导图节点匹配,核心主题才显示)
            if related_node.startswith("theme_"):
                kv_clip = resources["visual"]["knowledge"].get(related_node)
                if kv_clip:
                    kv_clip = kv_clip.set_duration(duration).set_position(("left", "center"))
                    visual_clips.append(kv_clip)
            
            # 4. 字幕层(带情感动效)
            sub = resources["subtitle"].get(script_id)
            if sub:
                # 基础字幕配置
                text_clip = TextClip(
                    sub["text"],
                    fontsize=sub["effect"]["font_size"],
                    color=sub["effect"]["color"],
                    font=GLOBAL_CONFIG["font_path"],
                    size=(1600, 60),
                    method="label"  # 抗锯齿
                ).set_duration(duration).set_position(sub["position"])
                
                # 绑定情感动效
                effect_type = sub["effect"]["type"]
                if effect_type == "淡入":
                    text_clip = text_clip.fadein(sub["effect"]["duration"])
                elif effect_type == "缓慢下移+淡暗":
                    text_clip = text_clip.fadein(0.3).set_position(
                        lambda t: (960, 800 + 20 * t) if t < 2 else (960, 840)
                    ).fadeout(sub["effect"]["duration"])
                elif effect_type == "快速闪烁+放大":
                    text_clip = text_clip.resize(lambda t: 1 + 0.1 * (t % 1)).fadein(0.2)
                
                visual_clips.append(text_clip)
            
            # 5. 合成视觉片段
            visual_clip = CompositeVideoClip(visual_clips, size=GLOBAL_CONFIG["resolution"])
            
            # 6. 叠加音频(角色音频+背景音乐)
            audio_clip = resources["audio"][script_id]["clip"]
            # 匹配风格化背景音乐(如科幻用电子乐,历史用古乐)
            bgm_path = style_dict["audio_style"].get("bgm_path", "./resources/bgm/default.mp3")
            bgm_clip = AudioFileClip(bgm_path).set_duration(duration).volumex(GLOBAL_CONFIG["bgm_volume_ratio"])
            # 混合音频(角色音频为主,背景音乐为辅)
            final_audio = audio_clip.volumex(1.0).set_duration(duration)
            final_audio = final_audio.set_audio(final_audio.audio.overlay(bgm_clip.audio))
            
            # 7. 绑定音视频
            clip = visual_clip.set_audio(final_audio)
            return clip
        
        def generate_project_xml(timeline: List[Dict], resources: Dict, book_name: str) -> str:
            """生成剪映/Pr可导入的XML工程文件(便于人工微调)"""
            root = ET.Element("xmeml", version="5")
            project = ET.SubElement(root, "project")
            ET.SubElement(project, "name").text = f"{book_name}_AI视频工程"
            
            # 序列配置(匹配1080P/30帧)
            sequence = ET.SubElement(project, "sequence")
            ET.SubElement(sequence, "name").text = "主序列"
            settings = ET.SubElement(sequence, "settings")
            ET.SubElement(settings, "width").text = str(GLOBAL_CONFIG["resolution"][0])
            ET.SubElement(settings, "height").text = str(GLOBAL_CONFIG["resolution"][1])
            ET.SubElement(settings, "frameRate").text = str(GLOBAL_CONFIG["fps"])
            
            # 轨道配置(视频3轨+音频2轨)
            tracks = ET.SubElement(sequence, "tracks")
            # 视频轨1(背景)、轨2(插画)、轨3(字幕+知识点图)
            for i in range(3):
                video_track = ET.SubElement(tracks, "track", type="video")
                ET.SubElement(video_track, "name").text = f"视频轨{i+1}"
            # 音频轨1(角色音频)、轨2(背景音乐)
            for i in range(2):
                audio_track = ET.SubElement(tracks, "track", type="audio")
                ET.SubElement(audio_track, "name").text = f"音频轨{i+1}"
            
            # 按时间轴添加素材到轨道
            current_time = 0.0
            for item in timeline:
                script_id = item["script_id"]
                duration = item["duration"]
                start_time = current_time
                
                # 1. 视频轨1:背景
                bg_clip = resources["visual"]["background"][item["emotion"]]
                bg_path = bg_clip.filename
                video1_clip = ET.SubElement(tracks[0], "clip")
                ET.SubElement(video1_clip, "name").text = f"背景_{script_id}"
                ET.SubElement(video1_clip, "start").text = str(start_time)
                ET.SubElement(video1_clip, "duration").text = str(duration)
                ET.SubElement(video1_clip, "path").text = bg_path
                
                # 2. 视频轨2:插画(如有)
                if script_id in resources["visual"]["illustration"]:
                    ill_path = resources["visual"]["illustration"][script_id].filename
                    video2_clip = ET.SubElement(tracks[1], "clip")
                    ET.SubElement(video2_clip, "name").text = f"插画_{script_id}"
                    ET.SubElement(video2_clip, "start").text = str(start_time)
                    ET.SubElement(video2_clip, "duration").text = str(duration)
                    ET.SubElement(video2_clip, "path").text = ill_path
                
                # 3. 音频轨1:角色音频
                audio_path = resources["audio"][script_id]["clip"].filename
                audio1_clip = ET.SubElement(tracks[3], "clip")
                ET.SubElement(audio1_clip, "name").text = f"音频_{script_id}"
                ET.SubElement(audio1_clip, "start").text = str(start_time)
                ET.SubElement(audio1_clip, "duration").text = str(duration)
                ET.SubElement(audio1_clip, "path").text = audio_path
                
                # 更新当前时间(叠加转场)
                if item != timeline[-1]:
                    current_time = start_time + duration + GLOBAL_CONFIG["transition_duration"]
                else:
                    current_time = start_time + duration
            
            # 美化XML格式并保存
            xml_str = minidom.parseString(ET.tostring(root)).toprettyxml(indent="  ")
            xml_path = f"./output/project/{book_name}_project.xml"
            os.makedirs(os.path.dirname(xml_path), exist_ok=True)
            with open(xml_path, "w", encoding="utf-8") as f:
                f.write(xml_str)
            
            return xml_path
        
        def video_rough_cut(
            audio_list: List[Dict],
            visual_dict: Dict,
            subtitle_config: Dict,
            script_dict: Dict,
            style_dict: Dict,
            book_name: str
        ) -> Dict:
            """
            视频粗剪主函数
            返回:粗剪视频路径、工程文件路径、同步校验报告
            """
            try:
                # 1. 初始化输出目录
                output_dir = f"./output/rough_cut"
                os.makedirs(output_dir, exist_ok=True)
                log_dir = f"./output/log"
                os.makedirs(log_dir, exist_ok=True)
                
                # 2. 加载并预处理资源
                print(f"[节点10] 加载资源...")
                resources = load_resources(audio_list, visual_dict, subtitle_config)
                
                # 3. 计算时间轴
                print(f"[节点10] 计算时间轴...")
                timeline = calculate_timeline(script_dict["script_list"], resources["audio"])
                
                # 4. 生成单段脚本片段
                print(f"[节点10] 生成脚本片段...")
                script_clips = []
                for item in timeline:
                    clip = generate_script_clip(item, resources, style_dict)
                    script_clips.append(clip)
                
                # 5. 添加转场效果(按风格匹配)
                print(f"[节点10] 添加转场效果...")
                transition_type = style_dict["visual_style"].get("transition", "fade")  # 风格化转场
                final_clips = []
                for i in range(len(script_clips)):
                    final_clips.append(script_clips[i])
                    # 非最后一段添加转场
                    if i != len(script_clips) - 1:
                        if transition_type == "fade":
                            trans_clip = TransitionClip(
                                [script_clips[i], script_clips[i+1]],
                                duration=GLOBAL_CONFIG["transition_duration"],
                                transition="fade"
                            )
                            final_clips.append(trans_clip)
                        elif transition_type == "slide":
                            trans_clip = TransitionClip(
                                [script_clips[i], script_clips[i+1]],
                                duration=GLOBAL_CONFIG["transition_duration"],
                                transition="slide_right"
                            )
                            final_clips.append(trans_clip)
                
                # 6. 拼接所有片段
                final_clip = concatenate_videoclips(final_clips, method="compose")
                
                # 7. 导出粗剪视频
                rough_video_path = f"{output_dir}/{book_name}_rough.mp4"
                final_clip.write_videofile(
                    rough_video_path,
                    fps=GLOBAL_CONFIG["fps"],
                    codec="libx264",  # H.264编码,兼容性强
                    audio_codec="aac",
                    bitrate="8000k",  # 保证画质
                    threads=4  # 多线程加速
                )
                
                # 8. 生成工程文件
                project_xml_path = generate_project_xml(timeline, resources, book_name)
                
                # 9. 生成同步校验报告(检查音画偏差)
                sync_report = {
                    "book_name": book_name,
                    "total_clips": len(script_clips),
                    "sync_errors": [],
                    "total_duration": final_clip.duration
                }
                for item in timeline:
                    script_id = item["script_id"]
                    audio_duration = resources["audio"][script_id]["duration"]
                    visual_duration = item["duration"]
                    deviation = abs(audio_duration - visual_duration)
                    if deviation > 0.5:  # 偏差>0.5秒记录为错误
                        sync_report["sync_errors"].append({
                            "script_id": script_id,
                            "audio_duration": audio_duration,
                            "visual_duration": visual_duration,
                            "deviation": deviation,
                            "suggestion": "调整视觉素材时长或重新生成音频"
                        })
                
                # 保存校验报告
                sync_report_path = f"{log_dir}/{book_name}_sync_report.json"
                with open(sync_report_path, "w", encoding="utf-8") as f:
                    json.dump(sync_report, f, ensure_ascii=False, indent=2)
                
                print(f"[节点10] 粗剪完成:{rough_video_path}")
                return {
                    "rough_video_path": rough_video_path,
                    "project_xml_path": project_xml_path,
                    "sync_report_path": sync_report_path,
                    "status": "success"
                }
            
            except Exception as e:
                error_msg = f"[节点10] 粗剪失败:{str(e)}"
                print(error_msg)
                # 保存错误日志
                with open(f"{log_dir}/{book_name}_cut_error.log", "w", encoding="utf-8") as f:
                    f.write(error_msg)
                return {
                    "status": "failed",
                    "error_msg": error_msg,
                    "fallback_path": f"{GLOBAL_CONFIG['temp_dir']}/{book_name}_fallback.mp4"  # 降级输出
                }
        
    • 节点 11:人工审核与微调(质量把控核心)

      • 通过可视化工具检查 “情感匹配度、音画同步、知识完整性”,进行精细化调整,避免自动化流程的疏漏。输入:粗剪视频:节点 10 输出的rough_video_path;工程文件:节点 10 输出的project_xml_path;同步校验报告:节点 10 输出的sync_report_path;所有中间资源:前 9 个节点的输出文件(便于回溯修改)。输出:微调后工程文件:./output/project/[书籍名]_project_finetuned.xml;审核报告:./output/log/[书籍名]_audit_report.json(含修改记录);确认信号:./output/flag/[书籍名]_audit_pass.flag(标记通过审核)。自动化辅助脚本./utils/audit_helper.py):

      • import json
        import os
        from typing import List
        
        def generate_audit_checklist(sync_report_path: str, mindmap_json_path: str) -> str:
            """生成审核清单(自动标记高风险项)"""
            # 加载同步报告
            with open(sync_report_path, "r", encoding="utf-8") as f:
                sync_report = json.load(f)
            # 加载思维导图(检查知识点覆盖)
            with open(mindmap_json_path, "r", encoding="utf-8") as f:
                mindmap = json.load(f)
            core_themes = [t["label"] for t in mindmap["root"]["children"]]
            
            # 生成清单
            checklist = {
                "high_risk_items": [],  # 高风险项(优先审核)
                "normal_items": []      # 常规项
            }
            
            # 高风险项:同步偏差>0.5秒
            if sync_report["sync_errors"]:
                for err in sync_report["sync_errors"]:
                    checklist["high_risk_items"].append({
                        "type": "sync_deviation",
                        "desc": f"脚本{err['script_id']}音画偏差{err['deviation']:.2f}秒",
                        "action": err["suggestion"]
                    })
            
            # 高风险项:核心主题未覆盖(需人工确认)
            checklist["high_risk_items"].append({
                "type": "knowledge_coverage",
                "desc": f"需确认是否覆盖所有核心主题:{core_themes}",
                "action": "未覆盖则新增对应脚本片段"
            })
            
            # 常规项:情感/风格/字幕检查
            checklist["normal_items"].extend([
                {
                    "type": "emotion_match",
                    "desc": "检查TTS音色、字幕动效、背景是否与情感标签一致",
                    "action": "不一致则替换对应资源"
                },
                {
                    "type": "style_consistency",
                    "desc": "检查所有素材风格是否与书籍类型匹配",
                    "action": "批量调整风格参数"
                },
                {
                    "type": "subtitle_readability",
                    "desc": "检查字幕字体、大小、位置、动效是否影响阅读",
                    "action": "在剪映中微调字幕属性"
                }
            ])
            
            # 保存清单
            book_name = sync_report["book_name"]
            checklist_path = f"./output/log/{book_name}_audit_checklist.json"
            with open(checklist_path, "w", encoding="utf-8") as f:
                json.dump(checklist, f, ensure_ascii=False, indent=2)
            
            return checklist_path
        
        def record_audit_result(book_name: str, changes: List[Dict]) -> str:
            """记录审核修改结果(生成审核报告)"""
            audit_report = {
                "book_name": book_name,
                "audit_time": os.path.getmtime(f"./output/flag/{book_name}_audit_pass.flag"),
                "changes": changes,
                "final_status": "passed"
            }
            
            report_path = f"./output/log/{book_name}_audit_report.json"
            with open(report_path, "w", encoding="utf-8") as f:
                json.dump(audit_report, f, ensure_ascii=False, indent=2)
            
            return report_path
        
        # 示例:生成审核清单
        if __name__ == "__main__":
            sync_report_path = "./output/log/三体_sync_report.json"
            mindmap_json_path = "./output/mindmap_三体.json"
            generate_audit_checklist(sync_report_path, mindmap_json_path)
        
    • 节点 12:视频渲染与输出(最终交付环节)

      • 将微调后的工程文件渲染为多平台适配的最终视频,支持批量导出与格式优化。输入:微调后工程文件:节点 11 输出的project_finetuned.xml;审核报告:节点 11 输出的audit_report_path;风格配置:节点 3 输出的style_dict(平台适配参数)。输出:主视频(高清):./output/final/[书籍名]_1080P.mp4(B 站 / YouTube 适配);短视频(适配抖音 / 小红书):./output/final/[书籍名]_720P_short.mp4(9:16 竖屏);压缩版(快速分享):./output/final/[书籍名]_compressed.mp4(体积≤100MB);交付清单:./output/final/[书籍名]_delivery.json(所有输出文件路径)。代码./core/video_render.py):

      • import os
        import json
        import subprocess
        from typing import Dict, List
        
        # 平台适配配置(可扩展)
        PLATFORM_CONFIG = {
            "bilibili": {
                "resolution": (1920, 1080),
                "aspect_ratio": "16:9",
                "bitrate": "10000k",
                "format": "mp4",
                "codec": "libx265"  # 高效编码,体积小画质好
            },
            "douyin": {
                "resolution": (1080, 1920),
                "aspect_ratio": "9:16",
                "bitrate": "6000k",
                "format": "mp4",
                "codec": "libx264",
                "watermark": "./resources/watermark/douyin_wm.png"  # 平台水印
            },
            "compressed": {
                "resolution": (1280, 720),
                "aspect_ratio": "16:9",
                "bitrate": "3000k",
                "format": "mp4",
                "codec": "libx264"
            }
        }
        
        def render_from_xml(project_xml_path: str, output_dir: str, book_name: str, platform: str) -> str:
            """
            从XML工程文件渲染视频(支持多平台适配)
            依赖:剪映专业版(需安装并配置环境变量)或FFmpeg
            """
            if platform not in PLATFORM_CONFIG:
                raise ValueError(f"不支持的平台:{platform},可选:{list(PLATFORM_CONFIG.keys())}")
            
            config = PLATFORM_CONFIG[platform]
            output_filename = f"{book_name}_{platform}.mp4"
            output_path = os.path.join(output_dir, output_filename)
            
            try:
                # 方案1:使用剪映API渲染(推荐,保留工程所有效果)
                print(f"[节点12] 用剪映渲染{platform}平台视频...")
                # 剪映API调用(需提前开启剪映开发者模式,获取API密钥)
                cmd = [
                    "jianying-cli",  # 剪映命令行工具
                    "render",
                    "--project", project_xml_path,
                    "--output", output_path,
                    "--width", str(config["resolution"][0]),
                    "--height", str(config["resolution"][1]),
                    "--bitrate", config["bitrate"],
                    "--codec", config["codec"],
                    "--fps", "30"
                ]
                # 添加水印(如抖音平台)
                if "watermark" in config:
                    cmd.extend(["--watermark", config["watermark"], "--watermark-pos", "bottom-right"])
                
                # 执行命令
                result = subprocess.run(cmd, check=True, capture_output=True, text=True)
                print(f"[节点12] 剪映渲染完成:{output_path}")
                return output_path
            
            except Exception as e:
                # 方案2:降级使用FFmpeg渲染(兼容性更强,无剪映依赖)
                print(f"[节点12] 剪映渲染失败,使用FFmpeg降级渲染:{str(e)}")
                # 先将XML工程转换为FFmpeg可识别的文件列表(需提前生成)
                file_list_path = generate_ffmpeg_filelist(project_xml_path, platform)
                cmd = [
                    "ffmpeg",
                    "-f", "concat",
                    "-safe", "0",
                    "-i", file_list_path,
                    "-s", f"{config['resolution'][0]}x{config['resolution'][1]}",
                    "-b:v", config["bitrate"],
                    "-c:v", config["codec"],
                    "-c:a", "aac",
                    "-fps_mode", "cfr",
                    "-r", "30",
                    "-y",  # 覆盖已有文件
                    output_path
                ]
                result = subprocess.run(cmd, check=True, capture_output=True, text=True)
                print(f"[节点12] FFmpeg渲染完成:{output_path}")
                return output_path
        
        def generate_ffmpeg_filelist(project_xml_path: str, platform: str) -> str:
            """生成FFmpeg concat需要的文件列表(适配平台分辨率)"""
            # 解析XML工程文件,提取音视频素材路径和时间轴
            tree = ET.parse(project_xml_path)
            root = tree.getroot()
            config = PLATFORM_CONFIG[platform]
            
            filelist = []
            # 提取视频轨1(背景)和音频轨1(角色音频)
            video_tracks = root.findall(".//track[@type='video']")
            audio_tracks = root.findall(".//track[@type='audio']")
            
            # 假设视频轨1和音频轨1是主素材
            video_clips = video_tracks[0].findall("clip")
            audio_clips = audio_tracks[0].findall("clip")
            
            # 按时间顺序生成文件列表
            for i in range(len(video_clips)):
                video_clip = video_clips[i]
                audio_clip = audio_clips[i] if i < len(audio_clips) else None
                
                # 视频素材
                video_path = video_clip.find("path").text
                start = float(video_clip.find("start").text)
                duration = float(video_clip.find("duration").text)
                # 调整分辨率
                filelist.append(f"file '{video_path}'")
                filelist.append(f"inpoint {start}")
                filelist.append(f"outpoint {start + duration}")
                
                # 音频素材(与视频同步)
                if audio_clip:
                    audio_path = audio_clip.find("path").text
                    filelist.append(f"file '{audio_path}'")
                    filelist.append(f"inpoint {start}")
                    filelist.append(f"outpoint {start + duration}")
            
            # 保存文件列表
            filelist_path = f"./temp/ffmpeg_filelist_{platform}.txt"
            with open(filelist_path, "w", encoding="utf-8") as f:
                f.write("\n".join(filelist))
            
            return filelist_path
        
        def batch_render(
            project_finetuned_path: str,
            audit_report_path: str,
            style_dict: Dict,
            book_name: str
        ) -> Dict:
            """批量渲染多平台视频,生成交付清单"""
            # 1. 初始化输出目录
            final_dir = f"./output/final"
            os.makedirs(final_dir, exist_ok=True)
            
            # 2. 读取审核报告(确认通过审核)
            with open(audit_report_path, "r", encoding="utf-8") as f:
                audit_report = json.load(f)
            if audit_report["final_status"] != "passed":
                raise ValueError("审核未通过,无法渲染最终视频")
            
            # 3. 批量渲染(默认渲染3个平台,可扩展)
            platforms = ["bilibili", "douyin", "compressed"]
            rendered_paths = {}
            for platform in platforms:
                rendered_path = render_from_xml(project_finetuned_path, final_dir, book_name, platform)
                rendered_paths[platform] = rendered_path
            
            # 4. 生成交付清单
            delivery清单 = {
                "book_name": book_name,
                "audit_time": audit_report["audit_time"],
                "render_time": os.path.getmtime(rendered_paths["bilibili"]),
                "files": rendered_paths,
                "platform_info": {
                    "bilibili": "1080P横屏,适合长视频平台",
                    "douyin": "720P竖屏,适合短视频平台",
                    "compressed": "720P压缩版,适合快速分享"
                },
                "md5_checksum": {
                    platform: subprocess.run(
                        ["md5sum", path], capture_output=True, text=True
                    ).stdout.split()[0] for platform, path in rendered_paths.items()
                }
            }
            
            # 保存交付清单
            delivery_path = f"{final_dir}/{book_name}_delivery.json"
            with open(delivery_path, "w", encoding="utf-8") as f:
                json.dump(delivery清单, f, ensure_ascii=False, indent=2)
            
            print(f"[节点12] 所有视频渲染完成,交付清单:{delivery_path}")
            return {
                "delivery_path": delivery_path,
                "rendered_files": rendered_paths,
                "status": "success"
            }
        
        # 示例:单独渲染B站平台视频
        if __name__ == "__main__":
            project_path = "./output/project/三体_project_finetuned.xml"
            output_dir = "./output/final"
            book_name = "三体"
            render_from_xml(project_path, output_dir, book_name, "bilibili")
        
  • 工程目录结构

    • book_ai_video/
      ├── core/                  # 核心节点实现(12节点完整代码)
      │   ├── book_parser.py     # 节点1:书籍解析与文本提取
      │   ├── multi_granularity_summary.py  # 节点2:多粒度摘要分解
      │   ├── style_matcher.py   # 节点3:书籍类型与风格匹配
      │   ├── mindmap_generator.py  # 节点4:结构化思维导图生成
      │   ├── emotion_content_anchor.py  # 节点5:情感与内容锚定
      │   ├── script_generator.py  # 节点6:情感驱动文案生成
      │   ├── tts_generator.py   # 节点7:多角色TTS生成
      │   ├── visual_generator.py  # 节点8:风格化视觉素材生成
      │   ├── subtitle_generator.py  # 节点9:字幕生成与情感动效绑定
      │   ├── video_rough_cut.py  # 节点10:音画同步与视频粗剪
      │   ├── audit_helper.py    # 节点11:人工审核辅助脚本
      │   └── video_render.py    # 节点12:视频渲染与输出
      ├── config/                # 配置文件
      │   ├── style_mapping.json  # 书籍类型-风格映射库
      │   ├── emotion_resource.csv  # 情感-多模态资源映射库
      │   ├── prompt/            # 提示词模板
      │   │   └── script_generator.txt  # 文案生成提示词
      │   └── audit_standards.json  # 审核标准
      ├── resources/             # 静态资源
      │   ├── fonts/             # 中文字体(需自行放置SimHei.ttf)
      │   ├── bgm/               # 背景音乐
      │   │   └── default.mp3    # 默认背景音乐
      │   └── watermark/         # 平台水印
      │       └── douyin_wm.png  # 抖音水印
      ├── temp/                  # 临时文件目录(自动生成)
      ├── output/                # 输出目录(自动生成)
      │   ├── rough_cut/         # 粗剪视频
      │   ├── project/           # 工程文件
      │   ├── final/             # 最终视频
      │   └── log/               # 日志文件
      ├── utils/                 # 通用工具函数
      │   ├── api_client.py      # API调用(豆瓣/Google Books/TTS)
      │   ├── error_handler.py   # 全局错误处理
      │   └── file_utils.py      # 文件读写工具
      ├── main.py                # 主运行脚本
      └── requirements.txt       # 依赖清单
      
  • 多节点覆盖 “输入→知识强化→情感驱动→多模态生成→审核→输出” 全链路,无功能遗漏。融合 “多粒度摘要 + 结构化思维导图 + 情感动效绑定”,兼顾知识传递与情感共鸣。后续可以将轻量化 JSON 知识库替换为 Chroma/FAISS 向量库,支持长文本 PDF 的精准检索,加入 AI 数字人驱动(如用 D-ID 生成主持人视频),提升视频感染力

Logo

更多推荐