easyVoice

cosin2077活跃维护

开源文本转语音工具,能把超长文本拆成多角色配音,适合做有声书和长内容朗读。

开源文本转语音工具,支持超长文本,多角色配音

v0.0.23发布于 2026-11-04 00:08 UTC2,381star +6 / 7 天388fork同步于 2026-11-10 23:45 UTC

怎么上手

由 README 提炼,执行前请确认
docker run -d -p 3000:3000 -v $(pwd)/audio:/app/audio cosincox/easyvoice:latest
硬件要求按 README 说明

README 未说明显存或 GPU 要求,语音合成依赖 Edge-TTS 云端 API,本地只需能跑 Node.js 20+ 或 Docker 即可

有 Docker 镜像
下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

开源文本转语音工具,支持超长文本与多角色配音,一键把小说转成有声书

开源文本转语音工具,能把超长文本拆成多角色配音,适合做有声书和长内容朗读。

核心能力

  • 流式传输,超长文本可立即播放
  • 自动生成 SRT 字幕并同步时间轴
  • 支持多角色配音,可调语速、音调、音量
  • 用停顿标记插入真实静音,最长 30 秒
  • AI 智能推荐各段落的配音参数

容易踩的坑

本地运行需 Node.js 20 及以上并先启用 pnpm;AI 推荐功能需另行配置 OPENAI_BASE_URL、OPENAI_API_KEY、MODEL_NAME;用户自填的 OpenAI 地址默认禁止指向本机或内网,需设 ALLOW_PRIVATE_OPENAI_URL=true

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v0.0.23
  1. v0.0.23最新正式版2026-11-04 · 2026-11-04 00:08 UTC

    🔒 安全修复 / Security fix

    修复 SSRF 漏洞(#87) — 用户自填的 openaiBaseUrl 现在默认禁止指向本机、内网、链路本地和云厂商元数据等地址(返回 400)。 Fixes an SSRF vulnerability (#87) — a user-supplied openaiBaseUrl can no longer target loopback, private, link-local or cloud-metadata addresses by default (rejected with 400).

    • 覆盖 IPv4/IPv6、IPv4 映射 IPv6、十进制/八进制/十六进制 IP、localhost、0.0.0.0、userinfo 等绕过写法;只允许 http/https。 Covers IPv4/IPv6, IPv4-mapped IPv6, decimal/octal/hex IP forms, localhost, 0.0.0.0 and userinfo tricks; only http/https allowed.
    • 先解析域名校验 IP,并把连接固定到校验过的 IP(防 DNS rebinding);不跟随重定向、不走环境代理。 DNS is resolved and validated, and the connection is pinned to the validated IP (no DNS rebinding); redirects and env proxies are not followed.
    • 返回给调用方的 AI 错误信息不再包含内部地址/端口/错误码。 AI error messages returned to callers no longer leak internal addresses, ports or errno.
    • LLM 请求不再带上 JWT_TOKEN。 LLM requests no longer carry JWT_TOKEN.

    ⚙️ 配置变化 / Config change

    服务器自己的 OPENAI_BASE_URL 不受限制(本地大模型照常可用)。如需让用户填写内网地址,请显式设置 ALLOW_PRIVATE_OPENAI_URL=true(以前是默认允许);元数据等保留地址始终拦截。 The server's own OPENAI_BASE_URL is unrestricted (local LLMs keep working). To let users enter private/LAN URLs, set ALLOW_PRIVATE_OPENAI_URL=true explicitly (previously allowed by default); metadata/reserved addresses stay blocked.

    Docker: cosincox/easyvoice:0.0.23 · PR #88

  2. v0.0.222026-10-20 · 2026-10-20 02:23 UTC
    查看更新说明

    亮点 / Highlights (#86)

    • 手机 / 平板适配:360–768px 宽度下都是单列布局,顺序为输入文本 → 语音设置 → 生成,所有页面都没有横向滚动;横屏手机也可以正常使用。桌面端布局不变。

    • 顶部导航:新增吸顶导航栏,手机上收起为菜单按钮和下拉面板。

    • 触屏友好:输入框、下拉框、按钮、滑块的点击区域都是 44px;表单字号 16px,iOS 上点输入框不再自动放大;对话框、提示和下拉框不会超出屏幕。

    • 底部操作栏:手机上「生成 / 停止」和播放器固定在底部,并避开 iPhone 底部的安全区;播放器进度条单独占满一行。

    • 下载列表:手机上文件名单独一行,操作按钮在下一行,删除按钮更容易点。

    • Mobile & tablet layout: single column from 360 to 768 px (text → voice settings → generate), with no horizontal scrolling on any page. Landscape phones work too, and the desktop layout is unchanged.

    • Header navigation: a new sticky top bar that collapses into a menu button and sheet on phones.

    • Touch-friendly controls: 44 px targets for inputs, selects, buttons and sliders; 16 px form text so iOS doesn't zoom in; dialogs, toasts and dropdowns stay within the screen.

    • Sticky bottom bar: on phones the generate/stop actions and the player dock to the bottom, clear of the iPhone home indicator. The player's scrubber gets its own full-width row.

    • Download list: on phones the file name gets its own line, actions go on the line below, and delete is easier to tap.

    修复 / Fixes

    • 生成完成时的彩纸不再从左上角飞出;手机上数量减少,开启「减少动态效果」时不显示。

    • 首页演示播放器在手机上可以拖动进度了。

    • 页脚图标之前没有显示,现在正常显示。

    • 修复了 index.html 里无效的 preload(控制台一直有警告);加上 viewport-fit=cover,页面语言改为 zh-CN。

    • Confetti no longer bursts from the top-left corner. It is lighter on phones and skipped when reduced motion is on.

    • The home demo player can be scrubbed by touch.

    • Footer icons now render (they were missing before).

    • Fixed a broken index.html preload that caused a console warning; added viewport-fit=cover and set the page language to zh-CN.

    Docker: cosincox/easyvoice:0.0.22 / latest (amd64 + arm64)

    Full changelog: https://github.com/cosin2077/easyVoice/compare/v0.0.21...v0.0.22

  3. v0.0.212026-10-20 · 2026-10-20 01:34 UTC
    查看更新说明

    亮点 / Highlights (#85)

    • 数字读法:修复 v0.0.20 把 300元 读成「三零零元」、1.5倍 读成「一.五倍」的回归。数量、金额、小数、年份交给 Edge 正常朗读;中文里像编号的数字逐位读(股票代码601012、尾号8888、(601012)、000001、长单号),字幕仍显示原文数字(#47)。

    • 停顿 = 真静音:(((停顿3秒)))、[pause:1.5s]、<break time="2s"/> 会插入真正的静音(最长 30 秒),字幕按静音时长顺延,不再出现填充文字(#73、#51)。

    • AI 使用服务器配置:服务端配置了 OPENAI_BASE_URL / OPENAI_API_KEY / MODEL_NAME 时,页面会显示模型和地址,Key 留空就能直接用 AI 分角,密钥不会下发到浏览器(#72)。

    • 生成计时:生成时显示已用时间,完成后显示总用时(#11)。

    • Number reading: fixes the v0.0.20 regression that read 300元 digit by digit. Quantities, prices, decimals and years are read natively by Edge. Code-like numbers in Chinese text (stock codes, IDs, phone numbers, leading-zero codes) are read digit by digit, and subtitles still show the original digits (#47).

    • Real-silence pauses: (((停顿3秒))), [pause:1.5s] and <break time="2s"/> insert actual silence (up to 30 s). Subtitles shift by the pause, with no filler text (#73, #51).

    • Server AI config: when the server has an LLM configured, the page shows the model and host and AI mode works with an empty key field. The key is never sent to the browser (#72).

    • Elapsed timer while generating, plus the total time when done (#11).

    修复 / Fixes

    • /generateJson 会自动切分过长的条目(#62)。

    • 较老的 Node 版本上 ESM 依赖报 ERR_REQUIRE_ESM 的问题已修复(#57);同时移除了无用代码和依赖。

    • 任务结束后释放请求上下文,过期时间由 TASK_TTL_MS 控制,默认 1 小时;任务接口在流式过程中也能正常返回 JSON。

    • 文档:停顿语法、数字读法、命令行下载 SRT(取响应头 x-generate-tts-id,把扩展名换成 .srt,见 #30);修正了 pnpm dev/build/start 命令。

    • Oversized /generateJson items are split automatically (#62).

    • ESM dependencies no longer fail with ERR_REQUIRE_ESM on older Node (#57). Dead code and unused deps were removed.

    • Task context is released once a task settles; records expire after TASK_TTL_MS (default 1 h). The task API returns JSON safely during a stream.

    • Docs: pause syntax, number reading, CLI SRT download via the x-generate-tts-id header (#30), and corrected pnpm dev/build/start commands.

    Docker: cosincox/easyvoice:0.0.21 / latest (amd64 + arm64)

    Full changelog: https://github.com/cosin2077/easyVoice/compare/v0.0.20...v0.0.21

  4. v0.0.202026-10-20 · 2026-10-20 00:00 UTC
    查看更新说明

    亮点 / Highlights

    • 长文本字幕与音频对齐:每段偏移改为按真实 MP3 帧数累加(此前丢失段尾静音,越往后越错位)。实测 6 千字英文 16 段,各段边界误差 ≤ 56ms,没有累积漂移。

    • 流式生成更可靠:Edge 中途断连(1006)会自动重试,不再静默截断;如果确实失败,前端会明确报错,而不是“生成成功”却只有半截音频。修复了流式结果一直不收尾、停止按钮无效、进度卡在 0% 的问题。

    • AI 分角重做:结构化选角 schema、会过滤不存在的音色、规范语速/音调;某段失败时用默认音色朗读该段并给出提示。API Key 按请求传递,不再跨请求泄漏;服务端 OPENAI_API_KEY 只会发往服务端自己配置的地址;Base URL 加了 SSRF 防护。

    • 界面:Linear 风格的生成页,可搜索的语言分组,底部常驻播放器显示真实时长,移动端操作栏吸底;全站统一为一个强调色。

    • Long-text subtitle sync: segment offsets now come from frame-accurate MP3 durations, so trailing silence no longer accumulates into drift. On 6k chars of English in 16 segments, max boundary error was 56 ms with no cumulative drift.

    • Reliable streaming: mid-segment Edge drops are retried instead of truncating the audio. A real failure now shows an error instead of a fake success. Also fixed: streams that never finalized, a stop button that did nothing, and progress stuck at 0%.

    • AI casting: structured schema, unknown voices are filtered out, and prosody is normalized. A failed chunk is read with the default voice. API keys are passed per request and never shared across requests. The server key is only sent to the server's own endpoint, and the Base URL has SSRF guards.

    • UI: Linear-style generate flow, searchable grouped locales, docked stream player with the real duration, sticky mobile action bar, and one restrained accent color.

    修复 / Fixes (#83)

    • 只有文本明显不是英文时才拒绝英文音色(#43、#76)。

    • 非流式路径按段重试(#54)。

    • 解析 LLM 返回的 JSON 时先去掉代码块标记(#71)。

    • 加载完整音色列表,并支持多语言(#52)。

    • 流式结果也能下载 SRT(#38)。

    • 修复中文流式请求头导致 ERR_INVALID_CHAR 崩溃的问题。

    • English voices are only rejected for clearly non-English text (#43, #76).

    • Per-segment retries on the non-stream path (#54).

    • Code fences are stripped before parsing LLM JSON (#71).

    • The full voice list loads, with multi-locale support (#52).

    • Stream results can download SRT (#38).

    • CJK stream headers are percent-encoded, fixing an ERR_INVALID_CHAR crash.

    Docker: cosincox/easyvoice:0.0.20 / latest (amd64 + arm64)

    Full changelog: https://github.com/cosin2077/easyVoice/compare/v0.0.18...v0.0.20

  5. v0.0.182026-10-19 · 2026-10-19 13:01 UTC
    查看更新说明

    修复 / Fixes

    • 修复 Edge TTS 合成返回 403(#82):微软 Edge 朗读接口于 2026-10 起要求新的 Sec-MS-GEC v2 令牌(<16 hex>-<64 hex>)及 Sec-MS-GEC-Version=2-…。移植了上游 rany2/edge-tts 7.3.x 的 v2 算法,补齐 ConnectionId、muid Cookie 等请求头;遇到 403 时按服务器 Date 校正时钟偏差并重试一次。
    • Fix Edge TTS 403 (#82): Microsoft now requires the Sec-MS-GEC v2 token (<16 hex>-<64 hex>) with Sec-MS-GEC-Version=2-…. Ported the v2 algorithm from upstream rany2/edge-tts 7.3.x, added ConnectionId / muid cookie headers, and retry once on 403 after correcting clock skew from the server Date header.

    其他 / Other

    • 新增 Sec-MS-GEC v2 单元测试(与上游 Python 实现对照);backend 增加 ts-jest 配置。
    • Added Sec-MS-GEC v2 unit tests (vectors from upstream Python); added ts-jest config for backend.

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    EasyVoice 🎙️

    项目简介 ✨

    EasyVoice 是一个开源的文本、小说智能转语音解决方案,旨在帮助用户轻松将文本内容转换为高质量的语音输出。

    • 一键生成语音和字幕

    • AI 智能推荐配音

    • 完全免费,无时长、无字数限制

    • 支持将 10 万字以上的小说一键转为有声书!

    • 流式传输,多长的文本都能立刻播放

    • 支持自定义多角色配音

    无论你是想听小说、为创作配音,还是打造个性化音频,EasyVoice 都是你的最佳助手!

    你可以轻松的将 EasyVoice 部署到你的云服务器或者本地!

    体验一下

    easyvoice.ioplus.tech

    核心功能 🌟

    • 文本转语音 📝 ➡️ 🎵
      一键将大段文本转为语音,高效又省时。
    • 流式传输 🌊
      再多的文本,都可以迅速返回音频直接开始试听!
    • 多语言支持 🌍
      支持中文、英文等多种语言。
    • 字幕支持 💬
      自动生成字幕文件,方便视频制作和字幕翻译。
    • 角色配音 🎭
      提供多种声音选项,完美适配不同角色。
    • 自定义设置 ⚙️
      可调整语速、音调等参数,打造专属语音风格。
    • AI 推荐 🧠
      通过 AI 智能推荐最适合的语音配置,省心又贴心。
    • 试听功能 🎧
      生成前可试听效果,确保每一句都如你所愿!

    Screenshots📸

    Home Generate

    快速开始 🚀

    1. 通过 docker 运行

    # 极简运行,你可以通过 -e 指定环境变量
    docker run -d -p 3000:3000 -v $(pwd)/audio:/app/audio cosincox/easyvoice:latest
    

    or 将仓库克隆到本地,使用 Docker Compose 一键运行!

    docker-compose up -d
    

    2. 本地运行项目(需要 Node.js 20 及以上,参考:安装 Node.js)

    # 开启/安装 pnpm
    corepack enable
    # 或者使用 npm 安装 pnpm
    npm install -g pnpm
    
    # 克隆仓库
    git clone [email protected]:cosin2077/easyVoice.git
    cd easyVoice
    # 安装依赖
    pnpm i -r
    
    # 开发模式
    pnpm dev
    
    # 生产模式
    pnpm build
    pnpm start
    

    3. 生成的音频、字幕保存位置

    • Docker 部署: 保存在挂载的 audio 目录下
    • Node.js 运行保存在 ./packages/backend/audio 目录下

    高级

    插入停顿(静音)

    在文本里写停顿标记,会在该位置插入真实静音(不是用省略号凑),字幕时间轴会同步顺延,SRT 里也不会出现标记文字。单个停顿最长 30 秒。

    春江潮水连海平,(((停顿3秒)))海上明月共潮生。
    (((暂停1.5秒)))  (((静音2秒)))  (((pause 2s)))  [pause:500ms]  <break time="2s"/>
    

    网页、/generate、/createStream、/generateJson(每个 text 里都可以写)均支持。

    数字读法

    数量、金额、小数、年份、日期(如 300元、2000人、1.5倍、2024年10月20日)交给 Edge 按正常数字读法朗读。中文里像编号的数字会逐位读出:

    • 前面有关键字:股票代码601012、手机尾号8888、订单号20241020123456、验证码4821
    • 括号里的 5 位以上数字:隆基绿能(601012)
    • 以 0 开头的 4 位以上数字:000001
    • 7 位以上、后面不跟单位的数字:手机号、单号等

    逐位读只影响发音,字幕里仍显示原文数字(601012,不会变成 六零一零一二)。

    命令行生成并保存字幕(SRT)

    /generateJson 和 /createStream 直接返回 MP3 流,响应头 x-generate-tts-id 是文件名(URL 编码)。把扩展名换成 .srt 就能下载对应的字幕:

    curl -sS -D headers.txt -o output.mp3 -X POST http://localhost:3000/api/v1/tts/createStream \
      -H "Content-Type: application/json" \
      -d '{"text":"你好,这是一段命令行生成的语音。(((停顿1秒)))再见。","voice":"zh-CN-XiaoxiaoNeural"}'
    ID=$(grep -i '^x-generate-tts-id' headers.txt | cut -d' ' -f2 | tr -d '\r')
    curl -sS -o output.srt "http://localhost:3000/api/v1/tts/download/${ID%.mp3}.srt"
    

    短文本(不超过 DIRECT_GEN_LIMIT)也可以用 /generate,返回的 JSON 里直接带 file 和 srt 文件名。

    角色自定义

    启动服务后尝试在命令行运行下述命令:

    curl -X POST http://localhost:3000/api/v1/tts/generateJson \
      -H "Content-Type: application/json" \
      -d '{
      "data": [
        {
          "desc": "徐凤年",
          "text": "你敢动他,我会穷尽一生毁掉卢家,说到做到",
          "voice": "zh-CN-YunjianNeural",
          "volume": "40%"
        },
        {
          "desc": "姜泥",
          "text": "徐凤年,你快走,你打不过的",
          "voice": "zh-CN-XiaoyiNeural"
        },
        {
          "desc": "路人甲",
          "text": "他可是堂堂棠溪剑仙,这小子真是遇到强敌了",
          "voice": "zh-CN-XiaoniNeural",
          "volume": "-20%"
        },
        {
          "desc": "路人乙",
          "text": "这小子真是不知死活,竟然敢挑战卢白撷",
          "voice": "zh-TW-HsiaoChenNeural",
          "volume": "-20%"
        },
        {
          "desc": "旁白",
          "text": "面对棠溪剑仙卢白撷的杀意,徐凤年按住剑柄蓄势待发,他将姜泥放在心尖上,话锋一句比一句犀利,威逼利诱的要求卢白撷放姜泥一条生路。卢白撷也是不撞南墙不回头的人,他与西楚有深仇大恨不得不报...",
          "voice": "zh-CN-YunxiNeural",
          "rate": "0%",
          "pitch": "0Hz",
          "volume": "0%"
        },
        {
          "desc": "旁白",
          "text": "卢白撷凝聚剑气,剑光如虹,直指姜泥。剑气快到姜泥的时候,竟然被一颗小石子打破!万千剑气瞬间消散。居然就是刚刚进入山门的青衣男子。卢白撷心中警铃大作,再次凝结千万水剑想要先下手为强,青衣男子竟然一只手就挡下了,随之飓风盘起,竟然有山呼海啸之势,众人分分被逼退。随后的打斗,青衣男子每一步都精准预测了卢白撷的动作,卢白撷心中惊骇不已。",
          "voice": "zh-CN-YunxiNeural",
          "rate": "0%",
          "pitch": "0Hz",
          "volume": "0%"
        },
        {
          "desc": "卢白撷",
          "text": "人心入局,观子无敌,棋局未央,棋子难逃。你是!? 曹长卿!",
          "voice": "zh-CN-YunyangNeural",
          "rate": "-2%",
          "pitch": "2Hz",
          "volume": "10%"
        }
      ]
    }' \
    -o output.mp3
    
    

    你将看到output.mp3文件的生成,并立即可以播放。单条 text 很长时会自动切成多段合成,不会再触发 Edge 单次 10 分钟的上限;字幕的获取方式见上文「命令行生成并保存字幕」。

    参数说明
    • text: 你需要转语音的文字。
    • voice: 你需要用到的声音,参考:支持的声音列表
    • rate: 语速调整,百分比形式,默认 +0%(正常),如 "+50%"(加快 50%),"-20%"(减慢 20%)。
    • volume: 音量调整,百分比形式,默认 +0%(正常),如 "+20%"(增 20%),"-10%"(减 10%)。
    • pitch: 音调调整,默认 +0Hz(正常),如 "+10Hz"(提高 10 赫兹),"-5Hz"(降低 5 赫兹)。

    接入其他 TTS 服务

    • TODO

    技术实现 🛠️

    • 前端:Vue 3 + TypeScript + Element Plus 🌐
    • 后端:Node.js + Express + TypeScript ⚡
    • 语音合成:Microsoft Azure TTS(更多引擎接入中) + OpenAI(OpenAI 兼容即可) + ffmpeg 🎤
    • 部署:Node.js + Docker + Docker Compose 🐳

    快速开发 🚀

    1.克隆仓库

    git clone https://github.com/cosin2077/easyVoice.git
    

    2.安装依赖

    pnpm i -r
    

    3.启动项目

    pnpm dev
    

    4.打开浏览器,访问 http://localhost:5173/,开始体验吧!

    环境变量 ⚙️

    变量名默认值描述
    PORT3000服务端口
    OPENAI_BASE_URLhttps://api.openai.com/v1OpenAI 兼容 API 地址
    OPENAI_API_KEY-OpenAI API Key
    MODEL_NAME-使用的模型名称
    RATE_LIMIT_WINDOW1速率限制窗口大小(分钟)
    RATE_LIMIT10速率限制次数
    EDGE_API_LIMIT3Edge-TTS API 并发数
    TASK_TTL_MS3600000/create 任务完成后可查询的保留时长(毫秒)
    ALLOW_PRIVATE_OPENAI_URLfalse是否允许用户在网页/API 中填写的 OpenAI API 地址指向本机/内网(如 http://127.0.0.1:11434/v1、192.168.x.x)。仅在自托管、服务不对公网开放时设为 true
    • AI 推荐:同时配置了 OPENAI_BASE_URL、OPENAI_API_KEY、MODEL_NAME 后,网页的「AI 推荐」里会显示「服务器已配置 AI:模型 · 域名」,Key 留空就能直接用(Key 不会下发到浏览器)。服务端 Key 只会发给 OPENAI_BASE_URL;用户自己填了别的 API 地址时,必须同时填自己的 Key。
    • API 地址安全(SSRF 防护):用户自己填写的 openaiBaseUrl 只能是 http/https 公网地址;指向本机、内网、链路本地、云厂商元数据(如 169.254.169.254)等地址会被拒绝(HTTP 400),服务端会先解析域名校验 IP,并把连接固定到校验过的 IP(防 DNS rebinding),且不跟随重定向。服务器自己配置的 OPENAI_BASE_URL 不受此限制,本地大模型直接写在 OPENAI_BASE_URL 里即可;若确实需要让用户填写内网地址,设置 ALLOW_PRIVATE_OPENAI_URL=true(元数据等保留地址仍会被拦截)。
    • 配置文件:可在 .env 或 packages/backend/.env 中设置,优先级为 packages/backend/.env > .env。
    • Docker 配置:通过 -e 参数传入环境变量,如上文示例。

    FAQ

    • Q: 如何配置 OpenAI 相关信息?

    • A: 在 .env 文件中添加 OPENAI_API_KEY=your_api_key OPENAI_BASE_URL=openai_compatible_base_url MODEL_NAME=openai_model_name,你可以用任何 openai compatible 的 API 地址和模型名称,例如 https://openrouter.ai/api/v1/ 和 deepseek。

    • Q: 为什么我的AI配音效果不好?

    • A: AI 推荐配音是通过大模型来决定不同的段落的配音参数,大模型的能力直接影响配音结果,你可以尝试更换不同的大模型,或者是用 Edge-TTS 选择固定的声音配音。

    • Q: 速度太慢?

    • A: AI 推荐配音需要把输入的文本分段、然后让 AI 分析、推荐每一分段的配音参数,最后再生成音频、拼接。速度会比直接用 Edge-TTS慢。你可以更换相应更快的大模型,或者尝试调节 Edge-TTS 的并发参数:EDGE_API_LIMIT为更大的值(10 以下),注意并发太高可能会有限制。

    Tips

    • 当前主要通过 Edge-TTS API 提供免费语音合成。

    • 未来计划支持官方 API、Google TTS、声音克隆等功能。