本地全自动AI配音方案NarratoAI+OmniVoice+FunASR

2026-06-21 22:15:27 0点赞 0收藏 0评论
本地全自动AI配音方案NarratoAI+OmniVoice+FunASR

经常做短视频、解说视频的朋友应该都懂,最耗时间的不是剪辑画面,而是写文案、配AI语音、自动识别字幕,来回切换好几个工具,还容易有水印、音色生硬、识别错别字多的问题。

今天给大家分享一套100%本地部署、全程无水印、免费开源的AI解说视频全自动制作套装:NarratoAI + OmniVoice本地配音 + FunASR精准字幕识别,一次性搞定AI文案生成、真人质感配音、高精度语音转字幕、视频自动合成,个人自用、批量做号都完全够用。

先简单白话讲下三个工具的分工和关联,新手也能一眼看懂:

1、NarratoAI(核心主控)

整套流程的“大脑”,是我们操作的主界面。支持AI自动生成短视频文案、批量剪辑、画面匹配,同时可以外接第三方TTS配音、ASR字幕识别服务。但它自带的Edge配音音色比较少、质感一般,而且没有本地高精度字幕功能,所以需要搭配另外两个工具补强。

2、OmniVoice-local(本地TTS配音)

专门解决NarratoAI原生配音拉胯的问题,提供超多真人质感音色,语速、语调、停顿都可以精细调节,全程本地部署,不用联网调用第三方接口,不会限流、不会泄露文案素材。作为外部API服务对接NarratoAI,替代原生配音。

3、FunASR(高精度语音字幕识别)

阿里开源的语音识别工具,准确率吊打普通在线字幕工具。官方没有适配Docker部署,我自己适配了专属Dockerfile和docker-compose,实现一键容器化部署,作为本地ASR接口给NarratoAI调用,自动生成精准字幕,错别字极少。

4、关于大模型

有两个免费的方案:1.本地部署ollama,然后拉取免费的云端模型gemma4:31b cloud,这个测试下来目前都可以免费用,不占用本地算力;2.使用智普的接口,免费模型分别为glm-4.6v-flash和glm-4.7-flash.不过对比测试下来还是gemma4:31b cloud生成的效果好一些,智普的模型有时候解说和视频对应不上。

三者+免费大模型api搭配就是最强本地AI视频工作流:NarratoAI统筹全程,OmniVoice负责好听的本地AI配音,FunASR负责精准字幕识别,全程本地化、无付费、无水印、无接口限制,下面直接上从零开始的保姆级部署教程,包含源码拉取、镜像构建、容器部署、报错修复全流程。

前置准备:一台装有Docker、Docker-Compose的Linux服务器(Centos、Ubuntu均可),提前装好git工具,所有命令全程复制粘贴即可。

一、部署NarratoAI主程序(修复OmniVoice接口报错版)

这一步是部署核心主控面板,重点解决原版源码OmniVoice接口调用失败、404报错、连接被拒绝的BUG,很多人部署成功却无法调用本地配音,都是代码接口路径不匹配导致的,下面直接给修复后的完整部署流程。

1.1 拉取官方源码

# 创建项目目录并进入 mkdir -p /data/NarratoAI && cd /data/NarratoAI # 拉取官方开源源码 git clone https://github.com/linyqh/NarratoAI.git

1.2 修复OmniVoice接口调用代码错误(关键步骤)

原版源码存在接口路径硬编码问题,固定请求/tts路径,部分版本端口映射后会出现404、连接拒绝报错,直接修改核心调用代码,适配标准OmniVoice接口格式,彻底解决报错。

编辑语音服务核心配置文件:

vim app/services/voice.py

找到 omnivoice_tts 函数,替换错误的接口请求逻辑,核心修复两点:

1. 适配OmniVoice标准POST请求格式,修正请求头、参数格式;

2. 移除错误的路径拼接,兼容本地服务端口映射,解决111连接拒绝、404未找到报错。

修复后核心代码参考(直接覆盖原有报错代码):

# 修复后的OmniVoice调用核心代码 def omnivoice_tts(text: str, voice_name: str, voice_file: str, speed: float = 1.0) -> Union[SubMaker, None]: """ 使用 OmniVoice-Pack FastAPI 服务进行语音合成。 支持自动音色、指令音色和参考音频克隆三种模式。 """ omnivoice_config = getattr(config, "omnivoice", {}) or {} api_url = _normalize_omnivoice_api_url(omnivoice_config.get("api_url", "http://127.0.0.1:7866/tts")) mode = omnivoice_config.get("mode", "auto") language = (omnivoice_config.get("language", "zh") or "").strip() instruct = (omnivoice_config.get("instruct", "") or "").strip() ref_text = (omnivoice_config.get("ref_text", "") or "").strip() parsed_voice = parse_omnivoice_voice(voice_name) if mode != "voice_clone" and parsed_voice and os.path.isfile(parsed_voice): mode = "voice_clone" reference_audio_path = "" if mode == "voice_clone": candidate = parsed_voice if candidate and os.path.isfile(candidate): reference_audio_path = candidate else: reference_audio_path = parse_omnivoice_voice(omnivoice_config.get("reference_audio", "") or "") if not reference_audio_path or not os.path.exists(reference_audio_path): logger.error(f"OmniVoice 参考音频文件不存在: {reference_audio_path}") return None elif mode != "voice_design": instruct = "" data = { "text": text.strip(), "language": language, **_optional_omnivoice_generation_data(speed), } if mode == "voice_design" and instruct: data["instruct"] = instruct if mode == "voice_clone" and ref_text: data["ref_text"] = ref_text data["sample"]="/samples/觉醒年代 01.mp4_20251029_195734.mp3" proxies = _get_configured_proxies() for attempt in range(3): files = {} try: if reference_audio_path: files["ref_audio"] = open(reference_audio_path, "rb") logger.info(f"第 {attempt + 1} 次调用 OmniVoice API: {api_url}, mode={mode}") response = requests.post( api_url, files=files or None, # data=data, json=data, proxies=proxies, timeout=240, ) if response.status_code == 200 and _download_omnivoice_audio(response, api_url, voice_file, proxies): logger.info(f"OmniVoice 成功生成音频: {voice_file}, 大小: {os.path.getsize(voice_file)} 字节") sub_maker = new_sub_maker() duration = get_audio_duration_from_file(voice_file) duration_ms = int(duration * 1000) if duration > 0 else max(1000, int(len(text) * 200)) add_subtitle_event(sub_maker, 0, duration_ms * 10000, text) return sub_maker logger.error(f"OmniVoice API 调用失败: {response.status_code} - {response.text}") except requests.exceptions.Timeout: logger.error(f"OmniVoice API 调用超时 (尝试 {attempt + 1}/3)") except requests.exceptions.RequestException as e: logger.error(f"OmniVoice API 网络错误: {str(e)} (尝试 {attempt + 1}/3)") except Exception as e: logger.error(f"OmniVoice TTS 处理错误: {str(e)} (尝试 {attempt + 1}/3)") finally: for file_obj in files.values(): try: file_obj.close() except Exception: pass if attempt < 2: time.sleep(2) logger.error("OmniVoice TTS 生成失败,已达到最大重试次数") return None

保存退出,至此OmniVoice接口报错问题彻底修复,后续部署配音服务即可正常调用。

1.3 构建Docker镜像+部署容器

源码自带Dockerfile,直接一键构建部署,无需额外修改配置。

# 构建镜像 docker-compose build --no-cache # 启动容器 docker-compose up -d #docker-compose文件参考如下: services: narratoai-webui: build: context: . dockerfile: Dockerfile image: narratoai:latest container_name: narratoai-webui privileged: true ports: - "8501:8501" volumes: - ./storage:/NarratoAI/storage - ./config.toml:/NarratoAI/config.toml:rw - ./resource:/NarratoAI/resource:rw environment: - PYTHONUNBUFFERED=1 - TZ=Asia/Shanghai restart: unless-stopped deploy: resources: reservations: devices: - driver: nvidia count: all capabilities: [gpu] devices: - "/dev/dri:/dev/dri" # 健康检查 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8501/_stcore/health"] interval: 30s timeout: 10s retries: 3 start_period: 60s #networks: # - default1 #networks: # default1: # external: true

1.4 访问测试

浏览器打开:服务器IP:8501,即可进入NarratoAI主界面,部署完成。此时主程序可正常运行,但OmniVoice配音、FunASR字幕功能还无法使用,需继续部署下方两个外部API服务。

二、部署OmniVoice-local本地TTS配音服务

专门为NarratoAI提供本地AI配音接口,音色丰富、音质自然,无调用次数限制,全程离线可用,适配上面修复后的接口代码。

2.1 拉取OmniVoice官方源码

# 创建目录并拉取源码 mkdir -p /data/OmniVoice && cd /data/OmniVoice git clone https://github.com/pasadei/OmniVoice-local.git .

2.2 构建镜像并启动容器

# 构建镜像 docker-compose build --no-cache # 启动容器,固定7866端口,和NarratoAI配置对应 docker-compose up -d

2.3 接口连通性测试

# 本地测试接口是否正常启动 curl http://127.0.0.1:7866/tts #改成自己的ip

无报错即为部署成功,随后进入NarratoAI配置文件,绑定该接口地址:

编辑 /data/NarratoAI/config.toml,修改如下配置:

[tts] default_engine = "omnivoice" omnivoice_api_url = "http://192.168.2.99:7866/tts" #改成自己的ip

修改后重启NarratoAI容器即可生效。

三、部署FunASR本地字幕识别服务(自定义Docker部署)

重点说明:阿里官方FunASR不支持Docker部署,没有现成容器配置,我已经通过豆包适配好了专属的 Dockerfiledocker-compose.yml,直接复制使用,一键容器化部署,完美对接NarratoAI。

3.1 拉取FunASR官方源码

mkdir -p /data/FunASR && cd /data/FunASR git clone https://github.com/modelscope/FunASR.git

3.2 新建自定义Dockerfile(适配容器部署)

在FunASR根目录新建 Dockerfile 文件,复制以下全部内容:

# 基础镜像:PyTorch 2.4 + CUDA 12.1 适配 GPU,支持 vLLM FROM pytorch/pytorch:2.4.1-cuda12.1-cudnn9-runtime # 设置环境变量 ENV PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1 PIP_NO_CACHE_DIR=off PIP_DISABLE_PIP_VERSION_CHECK=on FUNASR_HOME=/app/FunASR HF_HOME=/app/model_cache # 创建工作目录 WORKDIR /app # 安装系统依赖 RUN apt-get update && apt-get install -y --no-install-recommends git ffmpeg curl vim && rm -rf /var/lib/apt/lists/* # 【核心:复制本地仓库源码到容器】 # 前置要求:Dockerfile 和 FunASR 项目文件夹放在同一级目录 COPY ./funasrsource ${FUNASR_HOME} # 切换到项目目录 WORKDIR ${FUNASR_HOME} # 升级pip,安装项目依赖 RUN pip install --upgrade pip setuptools wheel # 方式1:本地源码可编辑安装 RUN pip install -e ./ # 安装API服务、vLLM加速依赖 RUN pip install vllm fastapi uvicorn python-multipart # 暴露FunASR默认API端口 EXPOSE 8000 # 模型缓存持久化挂载目录 VOLUME ["/app/model_cache", "/app/audio_data"] # 默认启动命令:OpenAI兼容API服务 # 稳定轻量模型推荐CMD CMD ["funasr-server", "--device", "cuda", "--host", "0.0.0.0", "--port", "8000", "--model", "iic/SenseVoiceSmall"]

3.3 新建docker-compose.yml文件

同目录下新建 docker-compose.yml,实现便捷启停、开机自启:

version: "3.8" services: funasr-gpu: build: context: ./ dockerfile: Dockerfile container_name: funasr-api image: funasr-api:latest privileged: true # GPU 调度(必须安装nvidia-docker container) deploy: resources: reservations: devices: - driver: nvidia count: all capabilities: [gpu] # 端口映射:宿主机8000 -> 容器8000 ports: - "8005:8000" # 目录挂载 volumes: # 模型缓存持久化,避免每次拉取模型 - ./model_cache:/app/model_cache # 本地音频目录挂载,容器内可读取本地音频文件 - ./audio_data:/app/audio_data # 新增持久化tmp目录,避免容器内部tmp空间不足 #- ./tmp_upload:/tmp # 环境变量 environment: - TZ=Asia/Shanghai - HF_HOME=/app/model_cache # HF国内镜像,加速模型下载,规避海外网络 - HF_ENDPOINT=https://hf-mirror.com #- CUDA_VISIBLE_DEVICES=0 # 指定使用0号显卡,多卡可修改为0,1 # 关键:全局禁用vLLM自动预加载,消除报错 - FUNASR_DISABLE_VLLM=1 # 容器重启策略 restart: unless-stopped tmpfs: /tmp:size=5G command: funasr-server --device cuda --host 0.0.0.0 --port 8000 --model iic/SenseVoiceSmall # 网络 networks: - default1 # 顶层声明外部网络 #networks: #default1: #external: true

3.4 一键部署启动FunASR服务

# 构建并启动容器 docker-compose up -d # 查看运行状态 docker-compose ps

3.6 对接NarratoAI

编辑NarratoAI配置文件 config.toml,添加FunASR接口地址:

[asr] default_engine = "funasr" funasr_api_url = "http://192.168.2.99:8000/asr" #修改为自己的ip

重启NarratoAI容器,即可实现本地高精度语音转字幕功能。

四、最终总结&常见问题排查

整套部署完成后的完整工作流

1、NarratoAI主界面生成视频文案、剪辑任务;

2、自动调用OmniVoice本地接口,生成自然AI配音;

3、通过FunASR本地接口,自动识别音频生成精准字幕;

4、全程本地运行,无水印、无付费、无接口限制。

常见报错解决(遇到任何问题直接复制日志到豆包问)

2、FunASR报错:

/tmp/tmp6bly4qge.wav: Invalid data found when processing input

解决:编辑源代码funasr/bin/_server_app.py,找到

result = processfallback("fun-asr-nano", tmp_path, language=language)(一共两个位置)

替换为

# 注释掉原来的 fallback 降级逻辑

# result = processfallback("fun-asr-nano", tmp_path, language=language)

# 直接加载你启动命令指定的 iic/SenseVoiceSmall

from funasr import AutoModel

model = AutoModel(model="iic/SenseVoiceSmall", device="cuda")

result = model.generate(input=tmp_path, language=language)

备份原来的dockerfile文件,使用新的dockerfile重新构建(docker-compose build --no-cache):

FROM funasr-api:latest

# 本地修复好的voice.py复制到容器对应路径

COPY ./funasrsource/funasr/bin/_server_app.py /app/FunASR/funasr/bin/_server_app.py

CMD ["funasr-server", "--device", "cuda", "--host", "0.0.0.0", "--port", "8000", "--model", "iic/SenseVoiceSmall"]

停止和重启容器:

docker-compose down

docker-compose up -d

这套方案是目前个人本地部署最稳定、功能最全的AI短视频自动生成方案,全程开源免费,修复了网上大部分教程的源码BUG,新手直接复刻即可完美落地!

展开 收起
0评论

当前文章无评论,是时候发表评论了
提示信息

取消
确认
评论举报

相关文章推荐

更多精彩文章
更多精彩文章
最新文章 热门文章
0
扫一下,分享更方便,购买更轻松