玩客云部署EasyVoice实战:HiNas、Docker、ARMv7、9001与固定地址访问
前言
手里有大量小说、长文或视频旁白素材时,真正麻烦的往往不是"有没有TTS",而是在线工具的字数、时长和操作方式不适合长期使用。尤其是几十万字甚至更长的文本,如果每次都要拆段、上传、等待,再手动合并,做一本有声书会非常折腾。
这次我没有准备高配PC,而是直接用刷入HiNas的玩客云来跑EasyVoice。先通过HiNas自带脚本安装Docker和Portainer,再使用已经适配ARMv7的EasyVoice镜像,把9000映射到9001,并保留音频目录。
服务跑通以后,再实际测试文本转语音、超长文本流式生成、多角色配音,以及需要OpenAI兼容接口的AI角色解析。
最后再给9001增加外部访问入口,从随机公网地址切换到固定二级子域名。整篇重点不是把几十块的小盒子写成"本地大模型TTS服务器",而是把玩客云 → Docker → EasyVoice → TTS/多角色 → AI解析 → 远程访问这条链真正跑通。
这类方案真正有意思的地方,也不只是"低成本",而是把一台原本闲置的小盒子变成长期在线的语音入口。以后不管是小说、旁白还是多角色文本,都可以从同一个Web页面提交任务,而不必每次重新找在线网站。
更省事。

1 这套方案真正解决什么问题
EasyVoice更适合被理解成:
一套自托管的文本转语音Web界面和任务入口。
它适合这些场景:
- 小说转有声书;
- 长文转语音;
- 视频旁白;
- 多角色对白;
- 需要字幕和音频同时生成;
- 想把TTS入口部署到自己的NAS或小主机上。
本次使用的是基于上游项目二次开发的分支。
页面:

这套分支在现有功能基础上还加入了:
- 多角色自定义配音UI;
- 用户登录;
- 三栏Web布局;
- AMD64 / ARM64 / ARMv7适配。
2 为什么玩客云能跑这一版EasyVoice
玩客云这里最关键的不是算力,而是:
ARMv7架构适配。
上游项目在ARMv7上会碰到@node-rs/jieba原生模块缺少预编译二进制的问题。
本次分支做的处理是:
Jieba加载失败时,自动降级到简单分词器。
两者区别可以这样理解:
| 分词方式 | 特点 |
|---|---|
| Jieba | 基于词典和HMM,分词更细 |
| 简单分词器 | 主要按标点切分,兼容性更好 |
对这次TTS主流程来说,ARMv7降级以后仍然可以继续生成语音。
但长句的断句位置可能和Jieba版本略有差别。
这一点后面会从容器日志里直接看到。
3 先确认HiNas环境
这台玩客云已经刷入HiNas,并且能进入终端后台。
页面:

如果还没有HiNas环境,需要先把系统和终端登录准备好,再继续后面的Docker部署。
4 通过HiNas安装Docker和Portainer
先点击HiNas首页里的:
Docker面板
页面:

系统会提示安装Docker管理面板。
继续进入终端:

登录终端:

本次使用HiNas提供的安装脚本:
shell
install-portainer.sh
执行过程:

安装完成:

这条脚本会把Docker和Portainer一起准备好。
5 初始化Portainer
回到HiNas首页刷新,再进入Docker面板。
页面:

第一次进入Portainer时,需要创建管理员账号。
密码至少12位。
完成以后进入仪表盘:

做到这里,Docker环境和图形化管理入口都已经具备。
6 ARMv7镜像和普通镜像要分开选
本次分支专门提供了ARMv7镜像。
相关说明页面:

玩客云使用的是ARMv7,所以执行:
shell
docker run -d -p 9001:9000 --name easyvoice -v ./audio:/app/audio tannic666/easyvoice:v1.1-armv7
这里几个参数很关键:
- 容器名:
easyvoice - 外部端口:
9001 - 容器端口:
9000 - 音频目录:
./audio:/app/audio - 镜像:
tannic666/easyvoice:v1.1-armv7
如果是AMD64或ARM64,则使用:
shell
docker run -d -p 9001:9000 --name easyvoice -v ./audio:/app/audio tannic666/easyvoice:v1.1
也就是说:
架构不同,镜像不要混用。
7 检查EasyVoice容器状态
部署结果:

继续执行:
shell
docker ps
结果:

Portainer里也能看到对应容器:

这一步只能确认:
容器已经运行。
还需要继续看日志和Web页面。
8 从日志确认ARMv7降级逻辑
执行:
shell
docker logs easyvoice | head -n 10
结果:

可以看到ARMv7环境里Jieba加载失败以后,服务自动切换到简单分词器。
这正是这个分支能继续在玩客云上运行的关键兼容处理之一。
9 9000和9001不要搞混
EasyVoice容器内部运行端口是:
9000
Docker映射到玩客云以后,外部访问端口是:
9001
所以浏览器访问:
shell
http://192.168.50.179:9001
页面:

能够进入初始化页以后,可以确认:
浏览器 → 玩客云9001 → 容器9000 → EasyVoice
已经跑通。
10 创建管理员账号并登录
初始化页面完成账号设置以后:

进入EasyVoice主页。
到这里,本地Web服务才算真正可用。
11 先测试最基础的文本转语音
本次页面里提供了多种中文音色。
可以根据:
- 旁白;
- 男声;
- 女声;
- 年轻角色;
- 地方特色;
来选择。
这次测试文本保持如下:
shell
灾难爆发前,我像大多数学者一样,想用自己的学识造福人类。碳基生命是脆弱而短暂的,我想提升人类的寿命。如果有足够的时间积累世间所有的知识,如果可以用时间去弥补天赋的不足,那么每个人都有可能掌握所有的自然、生命、社会、人文等科学知识,让人们更全面地感受这个世界。设想一下,知识的边界一旦被打破,人们对意识、身体乃至整个世界的认知都将发生巨大的改变,人类将以无尽的生命探索整个宇宙化。
操作页面:

生成完成以后试听:
你的浏览器不支持 audio 标签。
这一层验证的是:
输入文本 → EasyVoice → 音频生成 → 浏览器播放
12 长文本更值得看"流式输出"
短文本能生成还不够。
本次继续用接近700万字的长篇文本进行测试:

运行过程:

页面显示在持续生成音频。
本次截图状态里,播放不到3分钟时,已经流式生成出约34分钟的音频时长。
这里更准确的理解是:
长文本任务不需要等整个文件全部生成完成以后才开始播放。
但整本长篇最终生成完成需要多久,仍然取决于文本长度、网络和上游语音服务状态。
13 "自建TTS"不等于语音模型完全离线运行
这套EasyVoice基于:
Edge TTS。
所以这里自己部署的是:
- Web页面;
- 任务入口;
- 用户系统;
- 音频管理;
- 多角色逻辑。
并不是在玩客云本机上运行一个大型语音模型。
这个区别很重要。
玩客云能够承担这套服务,核心原因之一正是它不需要本机进行重型神经网络推理。
14 接下来测试多角色配音
进入多角色页面,先加载示例:

每个段落可以分别调整:
- 音色;
- 语速;
- 音量;
- 音调。
页面:

生成完成:

试听:
你的浏览器不支持 audio 标签。
这样一段文本里,不同角色就可以使用不同声音。
15 多角色真正有价值的是"段落可单独调整"
如果只是整篇文章用一个声音,普通TTS已经足够。
多角色模式更适合:
- 小说对白;
- 短剧;
- 广播剧;
- 剧本;
- 多人对话。
因为每一个角色都可以单独分配声音。
这样后面发现某个角色不合适时,不必把整篇配置全部推翻。
16 AI解析角色属于另一条模型链
EasyVoice还提供:
AI解析多角色。
配置页面:

这部分不是Edge TTS自己完成的。
需要先填写:
- API地址;
- API Key。
并使用兼容OpenAI v1格式的接口。
所以这一层链路是:
文本 → AI模型解析角色 → EasyVoice生成段落 → TTS生成语音
如果没有配置AI接口,普通文本转语音和手动多角色仍然是另一套能力。
17 用三角色加旁白测试AI解析
测试内容:
tex
清晨的山谷被薄雾覆盖,阳光像金色碎片洒在林间小路上。远处传来清脆的鸟鸣声。
沈澜提着工具箱走在最前方,脚步轻快。
沈澜:"今天一定能找到昨晚看到的光源位置。"
顾行远抬头望着树梢,手里握着便携探测仪。
顾行远:"如果真是反射物,那数据应该会有变化。"
就在他们讨论时,林青岚从溪边快步走来,指尖夹着一片透明碎片。
林青岚:"我在水边发现这个,边缘有烧蚀痕迹。"
顾行远立刻接过碎片,探测仪发出轻微的提示音。
沈澜凑近观察,神色变得专注。
沈澜:"痕迹并不像自然形成的。"
三人沉默片刻,周围只有风声掠过草叶。
雾逐渐散开,碎片在光下闪出微弱的蓝色光纹,像是隐藏着尚未被揭开的秘密。
填入以后点击开始解析:

解析完成:

本次自动生成了11个段落。
页面里已经把:
- 旁白;
- 沈澜;
- 顾行远;
- 林青岚;
分别拆开。
18 解析完成以后继续生成语音
点击生成:

试听:
你的浏览器不支持 audio 标签。
这一步真正证明的是:
AI角色解析 → 段落生成 → 多角色TTS
整条链已经跑通。
19 如果旁白被误判,可以给解析模型增加规则
本次还保留了一个提示词示例:
tex
解析规则:除非文本中明确出现角色说话的引号或冒号,否则所有内容都视为旁白,不视为角色语音。
这类规则的作用是:
约束AI模型怎么识别旁白和角色。
如果文本格式比较复杂,AI解析结果仍然需要人工检查。
不能把自动解析理解成每一篇小说都一定不会分错角色。
20 到这里,本地EasyVoice已经完整跑通
现在已经完成:
玩客云
↓
HiNas
↓
Docker / Portainer
↓
EasyVoice ARMv7
↓
9001 Web
↓
普通TTS
↓
长文本流式生成
↓
多角色配音
↓
AI角色解析
如果只在局域网里使用,到这里已经足够。
后面解决的是:
人在外面怎么继续进入EasyVoice。
21 公网访问之前先看账号和API Key
EasyVoice这套分支已经加入用户登录。
但如果又配置了AI解析,还可能涉及:
- API Key;
- 外部AI接口;
- 生成音频;
- 小说文本;
- 多角色配置。
所以远程访问时,至少要同时关注:
- 管理员密码;
- 用户账号;
- API Key是否暴露;
- 固定公网地址传播范围;
audio目录备份。
cpolar负责网络入口。
EasyVoice自己的用户和凭据仍然要自己管理。
22 安装cpolar
介绍图:

在玩客云终端执行:
shell
sudo curl https://get.cpolar.sh | sh
安装结果:

然后检查服务:
shell
sudo systemctl status cpolar
结果:

状态正常以后再进入Web管理页面。
23 打开cpolar Web UI
访问:
shell
# 请使用你的访问IP + 9200端口
http://192.168.50.179:9200/
页面:

登录以后进入后台:

24 给EasyVoice的9001创建公网入口
默认隧道页面:

编辑website隧道。
本次改成:
- 名称:
easyvoice-9001 - 协议:HTTP
- 本地地址:
9001 - 地区:
China Top
页面:

这里真正映射的是:
玩客云对外提供EasyVoice的9001端口。
25 先用随机公网地址验证
进入在线隧道列表:

使用生成的HTTPS地址访问:

能够打开EasyVoice登录页以后,可以确认:
外部浏览器 → 公网地址 → 玩客云9001 → EasyVoice
已经跑通。
26 随机地址更适合先做网络验证
本次材料里随机域名按约24小时变化来描述。
这种方式更适合:
- 临时测试;
- 偶尔使用;
- 先确认网络链路。
如果准备长期收藏,再继续配置固定二级子域名。
27 保留固定二级子域名
进入:
shell
https://dashboard.cpolar.com/reserved
预留页面:

本次示例:
- 地区:
China Top - 二级域名:
easyvoice01
实际名称以自己账号最终成功保留的结果为准。
28 编辑EasyVoice隧道
回到隧道列表:

把域名类型改成:
二级子域名
并填写前面保留的名称:

29 查看固定地址
在线隧道列表:

原来的随机地址已经切换成固定二级子域名形式。
30 最后从外部设备访问
使用固定HTTPS地址:

能够正常进入以后,长期远程入口完成。
整条链现在变成:
外部浏览器 → 固定公网地址 → 玩客云9001 → EasyVoice
31 固定地址解决的是"入口稳定"
固定二级子域名主要解决:
- 手机长期收藏;
- 电脑不用反复查新地址;
- 外出时使用同一个入口;
- 多设备访问更方便。
它解决的是:
访问入口不频繁变化。
并不代表:
- EasyVoice变成完全离线TTS;
- Edge TTS永远可用;
- AI解析API不再需要网络;
- API Key自动安全;
- 任何超长文本都能瞬间生成完毕。
32 这台玩客云真正承担的是什么
这篇最容易被误解的地方,是把玩客云想成:
"在本机跑大型TTS模型。"
实际上它承担的是:
- Docker;
- EasyVoice Web服务;
- 用户管理;
- 音频目录;
- 文本任务;
- 多角色配置;
- AI解析流程组织;
- 对外访问入口。
这也是为什么几十块的ARMv7设备仍然能把这套服务跑起来。
33 这套方案更适合什么人
比较适合:
- 手里已经有玩客云;
- 已经刷入HiNas;
- 想做长文本转语音;
- 想用浏览器操作TTS;
- 需要多角色配音;
- 希望多设备访问;
- 能接受Edge TTS和外部AI接口的网络依赖。
如果追求:
- 完全离线;
- 本地神经网络语音模型;
- 高度自定义声音克隆;
- 本机GPU推理;
那就属于另一类TTS方案。
34 最后把完整技术链重新看一遍
硬件:
玩客云 ARMv7
系统:
HiNas
容器:
Docker + Portainer
ARMv7镜像:
tannic666/easyvoice:v1.1-armv7
普通架构镜像:
tannic666/easyvoice:v1.1
端口:
9001:9000
数据:
./audio:/app/audio
本地功能:
文本TTS → 流式输出 → 多角色配音 → AI角色解析
随机远程:
公网随机地址 → 9001
固定远程:
固定二级子域名 → 9001
实际完成:
- HiNas终端;
- Docker / Portainer安装;
- Portainer初始化;
- ARMv7 EasyVoice镜像;
- 9001端口;
- 容器状态;
- 日志验证;
- 简单分词器降级;
- 管理员初始化;
- 文本转语音;
- 长篇流式生成;
- 多角色配音;
- AI角色解析;
- OpenAI兼容接口;
- cpolar安装;
- 随机公网访问;
- 固定二级子域名;
- 外部设备访问。
这样玩客云才真正从一台低功耗小盒子,变成一套可以长期提供EasyVoice Web入口、管理TTS任务并支持远程使用的轻量语音服务主机。
总结
这次真正跑通的是一条完整的玩客云 + EasyVoice链路:先在HiNas里通过脚本安装Docker和Portainer,再使用专门适配ARMv7的EasyVoice镜像,把容器9000映射到玩客云9001,并把音频目录挂载出来。
本地验证没有停在"容器显示Up"。后面继续检查日志,确认ARMv7环境下Jieba加载失败后已经自动降级到简单分词器,再实际完成普通文本转语音、长文本流式生成、多角色配音和AI角色解析。
需要特别区分的是,这是一套自托管的TTS入口和任务系统,并不等于在玩客云本机运行大型语音模型。EasyVoice基于Edge TTS,AI角色解析还需要额外的OpenAI兼容接口,所以"服务部署在本地"和"所有语音、AI能力完全离线"不是一回事。
远程部分是在局域网使用正常以后再增加的能力。随机公网地址先验证9001链路,固定二级子域名再解决长期入口变化的问题;如果里面配置了用户账号和AI API Key,长期公网访问时仍然要把账号、凭据和地址传播范围一起管理。