refactor: 更新音频文件路径和UI样式调整
fix: 修正TTS提供商配置中的null值问题 chore: 清理无用文件和更新输入文本内容
This commit is contained in:
138
CLAUDE.md
138
CLAUDE.md
@@ -1,138 +0,0 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## 常用命令
|
||||
|
||||
### Python 后端播客生成器
|
||||
|
||||
* **生成播客**:
|
||||
```bash
|
||||
python podcast_generator.py [可选参数]
|
||||
```
|
||||
可选参数包括:
|
||||
* `--api-key <YOUR_OPENAI_API_KEY>`: OpenAI API 密钥。
|
||||
* `--base-url <YOUR_OPENAI_BASE_URL>`: OpenAI API 代理地址。
|
||||
* `--model <OPENAI_MODEL_NAME>`: 使用的 OpenAI 模型,默认为 `gpt-3.5-turbo`。
|
||||
* `--threads <NUMBER_OF_THREADS>`: 生成音频的并行线程数,默认为 `1`。
|
||||
|
||||
**示例**:
|
||||
```bash
|
||||
python podcast_generator.py --api-key sk-xxxxxx --model gpt-4o --threads 4
|
||||
```
|
||||
|
||||
* **启动 FastAPI Web 服务**:
|
||||
```bash
|
||||
python main.py
|
||||
```
|
||||
默认在 `http://localhost:8000` 启动,提供 REST API 接口。
|
||||
|
||||
* **检查 TTS 语音列表**:
|
||||
```bash
|
||||
python check/check_edgetts_voices.py
|
||||
python check/check_indextts_voices.py
|
||||
# 其他 TTS 服务检查脚本...
|
||||
```
|
||||
|
||||
### Next.js Web 应用 (web/ 目录)
|
||||
|
||||
* **开发模式**:
|
||||
```bash
|
||||
cd web
|
||||
npm run dev
|
||||
```
|
||||
在 `http://localhost:3000` 启动开发服务器。
|
||||
|
||||
* **构建生产版本**:
|
||||
```bash
|
||||
cd web
|
||||
npm run build
|
||||
```
|
||||
|
||||
* **启动生产服务器**:
|
||||
```bash
|
||||
cd web
|
||||
npm run start
|
||||
```
|
||||
|
||||
* **类型检查**:
|
||||
```bash
|
||||
cd web
|
||||
npm run type-check
|
||||
```
|
||||
|
||||
* **代码检查**:
|
||||
```bash
|
||||
cd web
|
||||
npm run lint
|
||||
```
|
||||
|
||||
* **安装依赖**:
|
||||
```bash
|
||||
cd web
|
||||
npm install
|
||||
```
|
||||
|
||||
## 高层代码架构
|
||||
|
||||
本项目是一个全栈播客生成器,包含 Python 后端和 Next.js Web 前端,核心功能是利用 AI 生成播客脚本并将其转换为音频。
|
||||
|
||||
### Python 后端架构
|
||||
|
||||
* **`podcast_generator.py`**: 主运行脚本,负责协调整个播客生成流程,包括:
|
||||
* 读取配置文件 (`config/*.json`)。
|
||||
* 读取输入文件 (`input.txt`) 和 AI 提示词文件 (`prompt/*.txt`)。
|
||||
* 调用 OpenAI API 生成播客大纲和详细脚本。
|
||||
* 调用配置的 TTS 服务生成音频。
|
||||
* 使用 FFmpeg 合并生成的音频文件。
|
||||
* 支持命令行参数配置 OpenAI API 和线程数。
|
||||
|
||||
* **`main.py`**: FastAPI Web 服务,提供 REST API 接口:
|
||||
* `/generate-podcast`: 启动播客生成任务
|
||||
* `/podcast-status`: 查询生成进度
|
||||
* `/download-podcast/`: 下载生成的音频文件
|
||||
* `/get-voices`: 获取可用的 TTS 语音列表
|
||||
|
||||
* **`tts_adapters.py`**: TTS 服务适配器,统一处理不同 TTS 服务的 API 调用。
|
||||
|
||||
* **`openai_cli.py`**: 负责与 OpenAI API 进行交互的模块。
|
||||
|
||||
* **`config/`**: 存放 TTS 服务和播客角色配置的 JSON 文件。例如 `edge-tts.json`。这些文件定义了 `podUsers` (播客角色)、`voices` (可用语音) 和 `apiUrl` (TTS 服务接口)。
|
||||
|
||||
* **`prompt/`**: 包含用于指导 AI 生成内容的提示词文件。
|
||||
* `prompt-overview.txt`: 用于生成播客整体大纲。
|
||||
* `prompt-podscript.txt`: 用于生成详细对话脚本,包含占位符 (`{{numSpeakers}}`, `{{turnPattern}}`)。
|
||||
|
||||
* **`check/`**: TTS 服务语音列表检查脚本,用于验证各种 TTS 服务的可用语音。
|
||||
|
||||
### Next.js Web 前端架构 (web/ 目录)
|
||||
|
||||
* **技术栈**: Next.js 14 (App Router), TypeScript, Tailwind CSS, Framer Motion
|
||||
|
||||
* **`src/app/`**: Next.js App Router 页面和 API 路由
|
||||
* `api/generate-podcast/route.ts`: 播客生成 API,与 Python 后端集成
|
||||
* `api/audio/[filename]/route.ts`: 音频文件服务 API
|
||||
* `api/config/route.ts`: 配置管理 API
|
||||
* `api/tts-voices/route.ts`: TTS 语音列表 API
|
||||
|
||||
* **`src/components/`**: React 组件
|
||||
* `PodcastCreator.tsx`: 播客创建器主组件
|
||||
* `AudioPlayer.tsx`: 音频播放器组件
|
||||
* `ProgressModal.tsx`: 生成进度显示模态框
|
||||
* `ConfigSelector.tsx`: TTS 配置选择器
|
||||
* `VoicesModal.tsx`: 语音选择模态框
|
||||
|
||||
* **`src/types/`**: TypeScript 类型定义,定义了播客生成请求/响应的数据结构
|
||||
|
||||
* **集成方式**: Web 应用通过 Node.js 子进程启动 Python 脚本,实时监控生成进度,并提供音频文件访问服务。
|
||||
|
||||
### TTS 服务集成
|
||||
|
||||
项目设计为高度灵活,支持多种 TTS 服务:
|
||||
* **本地服务**: Index-TTS, Edge-TTS
|
||||
* **网络服务**: 豆包 (Doubao), Minimax, Fish Audio, Gemini TTS
|
||||
* **配置方式**: 通过 `config/*.json` 中的 `apiUrl` 进行配置
|
||||
|
||||
### 音频处理
|
||||
|
||||
使用 FFmpeg 工具将各个角色的语音片段拼接成一个完整的播客音频文件。FFmpeg 必须安装并配置在系统环境变量中。
|
||||
@@ -9,17 +9,17 @@
|
||||
"api_url": null
|
||||
},
|
||||
"doubao": {
|
||||
"X-Api-App-Id": "null",
|
||||
"X-Api-Access-Key": "null"
|
||||
"X-Api-App-Id": null,
|
||||
"X-Api-Access-Key": null
|
||||
},
|
||||
"fish": {
|
||||
"api_key": "null"
|
||||
"api_key": null
|
||||
},
|
||||
"minimax": {
|
||||
"group_id": "null",
|
||||
"api_key": "null"
|
||||
"group_id": null,
|
||||
"api_key": null
|
||||
},
|
||||
"gemini": {
|
||||
"api_key": "null"
|
||||
"api_key": null
|
||||
}
|
||||
}
|
||||
14
input.txt
14
input.txt
@@ -1,14 +0,0 @@
|
||||
```custom-begin
|
||||
Start your podcast with “欢迎收听,来生小酒馆,客官不进来喝点吗?”,End with “感谢收听,欢迎下次再来”
|
||||
```custom-end
|
||||
|
||||
### AI产品与功能更新
|
||||
|
||||
1. B站最近推出了一项堪称"黑科技”的**AI原声翻译功能**,它能在翻译视频内容的同时,奇迹般地保留UP主独特的声线、音色和语气习惯 (o´ω'o)ノ。这项技术不仅解决了跨语言交流的生硬感,更通过[深度研究技术(AI资讯)](https://www.aibase.com/zh/news/20183)精准拿捏了游戏、二次元等领域的"行话”与"梗”,让文化出海之路变得既地道又充满人情味儿 🔥。这简直是为全球粉丝献上的一份原汁原味的大礼,确保了情感连接不会在翻译中"迷路”。
|
||||
<br/>
|
||||
|
||||
2. Figma开发者模式迎来史诗级更新,正式向设计师与开发者之间的"沟通地狱”宣战 (✧∀✧)!全新的**彩色交互式批注系统**,让交互逻辑、样式规范和无障碍需求一目了然,彻底告别了无休止的猜谜游戏。更具革命性的是,升级后的**MCP协议**能将设计系统的结构化数据直接喂给AI编码工具,这意味着AI生成的代码将前所未有地贴合设计稿,让[设计转代码的效率(AI资讯)](https://www.aibase.com/zh/news/20211)实现指数级暴增 🚀。
|
||||
<br/><br/>
|
||||
|
||||
3. 米哈游联合创始人蔡浩宇亲自操刀的AI互动游戏**《星之低语》**,即将在Steam平台开启一场前所未有的情感实验 🌌。玩家将通过麦克风,与坠落在异星的宇航员Stella进行完全由AI驱动的开放式对话,你的每一句话都将直接影响她的命运。这款游戏彻底抛弃了传统对话树,旨在探索人机之间建立深层情感连接的可能性,正如[这份游戏前瞻(AI资讯)](https://www.aibase.com/zh/news/20184)所说,未来每个人都可能拥有一个数字灵魂伴侣 💡。
|
||||
<br/>
|
||||
@@ -6,8 +6,8 @@ import os
|
||||
import json
|
||||
|
||||
def check_doubao_tts_voices():
|
||||
config_file_path = "config/doubao-tts.json"
|
||||
tts_providers_path = "config/tts_providers.json"
|
||||
config_file_path = "../config/doubao-tts.json"
|
||||
tts_providers_path = "../config/tts_providers.json"
|
||||
test_text = "你好" # 测试文本
|
||||
|
||||
try:
|
||||
@@ -5,8 +5,8 @@ import msgpack
|
||||
import json
|
||||
|
||||
def check_fishaudio_voices():
|
||||
config_file_path = "config/fish-audio.json"
|
||||
tts_providers_path = "config/tts_providers.json"
|
||||
config_file_path = "../config/fish-audio.json"
|
||||
tts_providers_path = "../config/tts_providers.json"
|
||||
test_text = "你好" # 测试文本
|
||||
|
||||
try:
|
||||
@@ -7,8 +7,8 @@ import base64
|
||||
import json
|
||||
|
||||
def check_gemini_voices():
|
||||
config_file_path = "config/gemini-tts.json"
|
||||
tts_providers_path = "config/tts_providers.json"
|
||||
config_file_path = "../config/gemini-tts.json"
|
||||
tts_providers_path = "../config/tts_providers.json"
|
||||
test_text = "你好" # 测试文本
|
||||
|
||||
try:
|
||||
@@ -5,8 +5,8 @@ import os
|
||||
import json
|
||||
|
||||
def check_minimax_voices():
|
||||
config_file_path = "config/minimax.json"
|
||||
tts_providers_path = "config/tts_providers.json"
|
||||
config_file_path = "../config/minimax.json"
|
||||
tts_providers_path = "../config/tts_providers.json"
|
||||
test_text = "你好" # 测试文本
|
||||
|
||||
try:
|
||||
59
server/input.txt
Normal file
59
server/input.txt
Normal file
@@ -0,0 +1,59 @@
|
||||
```custom-begin
|
||||
start with '欢迎收听来生小酒馆,客官不进来喝点吗?' , end with '感谢收听,下期再见'
|
||||
不要自称主理人,馆长。说话符合人物角色设定。
|
||||
```custom-end
|
||||
|
||||
|
||||
|
||||
### 产品与功能更新
|
||||
|
||||
1. DeepSeek V3.1 版本悄然上线,**上下文长度直接飙升至 128K**,处理十几万字的文档或整个代码库都变得轻而易举 (o´ω'o)ノ。本次升级不仅推理能力提升43%、幻觉减少38%,多语言支持也更上一层楼,唯一的美中不足是大家翘首以盼的R2模型仍是"犹抱琵琶半遮面”。现在就去[官网体验一下 - (AI资讯)](https://chat.deepseek.com/),感受超长文本的威力吧!
|
||||
|
||||
2. 还在为复杂的图文视频生成流程头疼吗?Higgsfield AI 推出的 **Draw-to-Video** 功能让你彻底告别繁琐的文本提示词,只需在图片上画个箭头或圈圈,AI就能心领神会地生成电影级动态视频 🔥。这种"指哪打哪”的直观创作方式在外网迅速爆火,让视频创作的门槛又降低了一大截。快来[这里体验这份快乐 - (AI资讯)](https://higgsfield.ai/),让你的图片动起来!<br/>
|
||||
|
||||
3. 小红书AIGC团队祭出大招,正式发布了名为 **DynamicFace 的可控人脸生成技术**,致力于解决图像和视频换脸中的老大难问题 🤔。这项技术的核心亮点在于"可控”与"高度一致性”,旨在消除视频换脸时常见的闪烁和不连贯感,为用户提供更精准、更个性的创作工具。正如[这篇(AI资讯)报道](https://www.aibase.com/zh/news/20613)所说,这是小红书在AI内容生成领域迈出的重要一步,让创意表达拥有了更多可能。
|
||||
|
||||
4. 英伟达发布了在排行榜上名列前茅的 **Nemotron Nano 2** 模型,这个仅 **9B 参数**的多语言推理小钢炮,正在重新定义AI的效率边界 🚀。它采用了独特的 **Transformer-Mamba 混合架构**,实现了比同类8B模型快6倍的吞吐量,同时通过"思考预算”机制将成本削减高达60%。想了解更多[技术细节可看这篇(AI资讯)](https://nvda.ws/3JfcKST),或者直接去[排行榜围观(AI资讯)](https://nvda.ws/47B7iUh),见证它的强大!<br/><video src="https://video.twimg.com/amplify_video/1957573291566063621/vid/avc1/720x1280/goPWf6djGgXEiqL5.mp4?tag=14" controls="controls" width="100%"></video>
|
||||
|
||||
5. Gemini API 迎来了一项超实用的更新,现在**直接支持对URL进行内容抓取**,无论是网页、PDF还是图片链接,统统可以一网打尽!这意味着开发者可以省去调用第三方抓取API的麻烦和费用,直接让模型处理网络上的实时内容,堪称是降本增效的一大利器 (✧∀✧)。快来[看看这篇(AI资讯)解读](https://x.com/dotey/status/1957579164363481114),了解如何用好这个新功能吧!<br/>
|
||||
|
||||
### 前沿研究
|
||||
|
||||
1. AI模型在理解图像时,会不会因为思维定式而"一叶障目”?一篇来自arXiv的[最新研究(AI资讯)](https://arxiv.org/abs/2404.10357)提出了**CoKnow框架**,通过引入多知识表征来优化提示学习,极大地丰富了模型的"视野”💡。简单说,它不再让模型只走一条路,而是给它提供了多种"知识视角”来分析问题,从而在11个公开数据集上超越了既有方法,让模型预测更准确。
|
||||
|
||||
2. 如何让AI不仅会说话,更能"共情”?一篇名为 E3RG 的[前沿论文(AI资讯)](https://arxiv.org/abs/2508.12854)提出了一种全新的多模态共情响应生成系统,将任务分解为**理解、记忆和生成**三部曲。该系统无需额外训练,就能生成包含丰富情感且身份一致的虚拟人形象,仿佛拥有了真正的"同理心”❤️。这项研究在ACM MM 25挑战赛中斩获头名,为构建更具人情味的人机交互开辟了新道路。
|
||||
|
||||
### 行业展望与社会影响
|
||||
|
||||
1. AI投资热潮之下,现实却有些骨感;麻省理工学院的一项研究发现,高达 **95% 的企业未能从其AI投入中获得任何回报**,总计约400亿美元的投资几乎打了水漂 💸。报告指出,"生成式AI鸿沟”的根源并非人才或资源匮乏,而是AI系统普遍缺乏记忆和适应能力,无法深度融入关键工作流程。正如[宝玉的这篇(AI资讯)分享](https://x.com/dotey/status/1957648622851428689)所言,成功的AI部署更像是建立深度合作关系,而非简单购买产品。
|
||||
|
||||
### 开源TOP项目
|
||||
|
||||
1. 腾讯为多模态和强化学习领域送上了一份大礼,正式开源了名为 **WeChat-YATT** 的大模型训练库,旨在解决两大核心瓶颈 🔥。通过创新的**并行控制器**机制和**异步交互**策略,它有效解决了多模态训练的可扩展性难题和动态采样下的效率短板,显著提升了GPU的利用率。想了解这一[开源利器的(AI资讯)详情](https://www.aibase.com/zh/news/20620),不妨深入看看官方发布的内容。<br/>
|
||||
|
||||
2. 谷歌的Genie 3还在闭源,国产开源版世界模型 **Matrix-Game 2.0** 已经横空出世,在社区引发热议!这个仅 **1.8B 参数**的模型,能在单块GPU上以 **25FPS** 的帧率实时生成可交互的虚拟世界,你只需上传一张图片,就能在其中自由探索 (✧∀✧)。昆仑万维的这一开源力作,以其惊人的轻量化和高性能,为游戏开发和智能体训练开启了无限想象,快去[GitHub主页 - (AI资讯)](https://github.com/SkyworkAI/Matrix-Game)一探究竟吧。<br/><br/>
|
||||
|
||||
3. 想摆脱商业邮件服务商的月费"绑架”吗?**BillionMail** 这个在 [GitHub 上 ⭐8.9k 星的(AI资讯)项目](https://github.com/aaPanel/BillionMail) 为你提供了一站式开源解决方案,集邮件服务器、新闻通讯和邮件营销于一身。它完全支持自托管,对开发者极其友好,让你能以零月费的方式掌控自己的邮件系统,实现真正的数字独立 🚀。
|
||||
|
||||
4. 如果你是追求极致简约的音乐爱好者,那么在 [GitHub 上拥有 ⭐4.7k 星的 SPlayer(AI资讯)](https://github.com/imsyy/SPlayer) 绝对值得一试。这款播放器不仅界面清爽,还支持**逐字歌词、歌曲下载、音乐云盘管理**等强大功能,甚至还有酷炫的音乐频谱,堪称简约而不简单 (o´ω'o)ノ。它完美诠释了如何在小巧的体积中,容纳一个完整的音乐世界。
|
||||
|
||||
5. 对于那些对数字踪迹充满好奇的技术爱好者,[GitHub 上的 GhostTrack(AI资讯)](https://github.com/HunxByts/GhostTrack) 项目提供了一个用于追踪位置或手机号码的实用工具,已收获 ⭐1.9k 星。它就像一个数字世界的侦探工具,虽然用途广泛,但也提醒着我们在探索技术边界的同时,必须时刻关注隐私与伦理 🤔。
|
||||
|
||||
6. 让你的电脑拥有一个AI管家是怎样的体验?在 [GitHub 上收获 ⭐1.9k 星的 bytebot(AI资讯)](https://github.com/bytebot-ai/bytebot) 就是这样一个自托管的AI桌面代理,它能通过自然语言命令自动化执行电脑任务。它在安全的**容器化Linux环境**中运行,让你只需动动嘴,就能完成复杂操作,真正实现"君子动口不动手”的智能生活 🔥。
|
||||
|
||||
### 社媒分享
|
||||
|
||||
1. 进入AI领域不只需要懂代码和数学,软技能同样关键!吴恩达发布了一本免费的[职业指导电子书(AI资讯)](https://hubs.la/Q03DgNQ50),堪称是为AI求职者量身打造的"通关秘籍”💡。书中涵盖了**简历制作、面试技巧**,甚至还包括如何克服"冒名顶替综合症”,帮助你规划清晰的职业路线图,向心仪的工作迈进。<br/>
|
||||
|
||||
2. 在AI绘画中,提示词是不是越长越好?一位Reddit用户发出了灵魂拷问,他发现自己用二三十个词的短提示词,生成效果和别人几百词的长篇大论相差无几,甚至模型还会忽略大部分细节 🤔。这篇引发热议的[帖子 - (AI资讯)](https://old.reddit.com/r/FluxAI/comments/1mtyikj/whats_the_point_of_overly_long_prompts/)探讨了"长提示词”的实际意义,或许有时候,简洁才是通往好作品的捷径。
|
||||
|
||||
3. DeepSeek V3.1 的前端代码能力似乎又在"闷声发大财”了,有用户惊喜地发现,以前搞不定的一个复杂提示词,新版模型居然轻松拿捏,而且没有出现其他模型的字体大小问题 (✧∀✧)。这个在[社交媒体上的(AI资讯)发现](https://x.com/op7418/status/1957784895952155089),再次印证了官方宣布的 **128k 上下文**升级背后,是实打实的性能提升。<br/>
|
||||
|
||||
4. 提示词工程也能成为一门艺术!用户李继刚分享了一段极具诗意的"视觉编织场”Prompt,用**光、张力、流**等充满美学的隐喻,指导AI将播客链接转化为设计感十足的可视化卡片 🎨。这种将设计哲学融入提示词的[高级玩法(AI资讯)](https://x.com/lijigang_com/status/1957756215653724324),展示了与AI沟通的全新境界,堪称一场人与机器的灵感共舞。<br/>
|
||||
|
||||
5. 千问最新开源的图像编辑模型与FLUX Kontext的对决结果出炉!根据[博主的(AI资讯)评测](https://weibo.com/6182606334/Q0yOekb6d),千问模型的最大亮点在于其**独一无二的中文生成和编辑能力**,但图像美学和细节处理上则稍逊于FLUX,AI感较重。总的来说,它为中文内容创作提供了新利器,但想达到顶级效果可能还需社区的LoRA模型来"画龙点睛”✨。
|
||||
|
||||
6. OpenAI正在让顶级AI变得更亲民,**ChatGPT Go** 计划已在印度率先启动,每月订阅费仅需约4.55美元 🇮🇳!根据[Greg Brockman的(AI资讯)分享](https://x.com/gdb/status/1957650320923979996),该计划提供了比免费版**高10倍的消息量和图像生成量**,以及更长的记忆力。此举被视为AI普惠的重要一步,让更多人能以低成本享受强大AI工具带来的便利。
|
||||
|
||||
7. 想和孩子一起创作一本独一无二的故事书吗?Google Gemini 的 **Storybook** 功能让这一切变得简单有趣,正如[这篇(AI资讯)教程](https://x.com/shao__meng/status/1957605772017430917)所分享的,你可以上传照片作为灵感,指定**漫画或黏土动画**等艺术风格。这不仅是一个AI工具,更是一个激发家庭创造力、记录温馨回忆的互动平台 (o´ω'o)ノ。<br/>
|
||||
|
||||
@@ -74,7 +74,7 @@ stop_scheduler_event = threading.Event()
|
||||
|
||||
# 全局配置
|
||||
output_dir = "output"
|
||||
time_after = 10
|
||||
time_after = 30
|
||||
|
||||
# 内存中存储任务结果
|
||||
# {task_id: {"auth_id": auth_id, "status": TaskStatus, "result": any, "timestamp": float}}
|
||||
@@ -86,12 +86,12 @@ audio_file_mapping: Dict[str, Dict] = {}
|
||||
SECRET_KEY = os.getenv("PODCAST_API_SECRET_KEY", "your-super-secret-key") # 在生产环境中请务必修改!
|
||||
# 定义从 tts_provider 名称到其配置文件路径的映射
|
||||
tts_provider_map = {
|
||||
"index-tts": "config/index-tts.json",
|
||||
"doubao-tts": "config/doubao-tts.json",
|
||||
"edge-tts": "config/edge-tts.json",
|
||||
"fish-audio": "config/fish-audio.json",
|
||||
"gemini-tts": "config/gemini-tts.json",
|
||||
"minimax": "config/minimax.json",
|
||||
"index-tts": "../config/index-tts.json",
|
||||
"doubao-tts": "../config/doubao-tts.json",
|
||||
"edge-tts": "../config/edge-tts.json",
|
||||
"fish-audio": "../config/fish-audio.json",
|
||||
"gemini-tts": "../config/gemini-tts.json",
|
||||
"minimax": "../config/minimax.json",
|
||||
}
|
||||
|
||||
# 定义一个函数来清理输出目录
|
||||
@@ -19,7 +19,7 @@ from tts_adapters import TTSAdapter, IndexTTSAdapter, EdgeTTSAdapter, FishAudioA
|
||||
# Global configuration
|
||||
output_dir = "output"
|
||||
file_list_path = os.path.join(output_dir, "file_list.txt")
|
||||
tts_providers_config_path = 'config/tts_providers.json'
|
||||
tts_providers_config_path = '../config/tts_providers.json'
|
||||
|
||||
def read_file_content(filepath):
|
||||
"""Reads content from a given file path."""
|
||||
@@ -39,7 +39,7 @@ def _load_json_config(file_path: str) -> dict:
|
||||
except json.JSONDecodeError as e:
|
||||
raise ValueError(f"Error decoding JSON from {file_path}: {e}")
|
||||
|
||||
def select_json_config(config_dir='config', return_file_path=False):
|
||||
def select_json_config(config_dir='../config', return_file_path=False):
|
||||
"""
|
||||
Reads JSON files from the specified directory and allows the user to select one.
|
||||
Returns the content of the selected JSON file.
|
||||
@@ -114,6 +114,7 @@ def generate_speaker_id_text(pod_users, voices_list):
|
||||
def merge_audio_files():
|
||||
# 生成一个唯一的UUID
|
||||
unique_id = str(uuid.uuid4())
|
||||
unique_id = unique_id.replace("-", "")
|
||||
# 获取当前时间戳
|
||||
timestamp = int(time.time())
|
||||
# 组合UUID和时间戳作为文件名,去掉 'podcast_' 前缀
|
||||
@@ -14,7 +14,7 @@ export async function GET(request: NextRequest) {
|
||||
}
|
||||
|
||||
// 构建文件路径
|
||||
const outputDir = path.join(process.cwd(), '..', 'output');
|
||||
const outputDir = path.join(process.cwd(), '..', 'server', 'output');
|
||||
const filePath = path.join(outputDir, filename);
|
||||
|
||||
try {
|
||||
|
||||
@@ -22,7 +22,11 @@ export async function GET() {
|
||||
}
|
||||
|
||||
// 缓存无效或不存在,读取文件并更新缓存
|
||||
const configPath = path.join(process.cwd(), '..', 'config', 'tts_providers.json');
|
||||
const ttsProvidersName = process.env.TTS_PROVIDERS_NAME;
|
||||
if (!ttsProvidersName) {
|
||||
throw new Error('TTS_PROVIDERS_NAME 环境变量未设置');
|
||||
}
|
||||
const configPath = path.join(process.cwd(), '..', 'config', ttsProvidersName);
|
||||
const configContent = await fs.readFile(configPath, 'utf-8');
|
||||
const config = JSON.parse(configContent);
|
||||
|
||||
|
||||
@@ -14,20 +14,24 @@ export const metadata: Metadata = {
|
||||
description: '使用AI技术将您的想法和内容转换为高质量的播客音频,支持多种语音和风格选择。',
|
||||
keywords: ['播客', 'AI', '语音合成', 'TTS', '音频生成'],
|
||||
authors: [{ name: 'PodcastHub Team' }],
|
||||
viewport: 'width=device-width, initial-scale=1',
|
||||
themeColor: '#000000',
|
||||
icons: {
|
||||
icon: '/favicon.webp',
|
||||
apple: '/favicon.webp',
|
||||
},
|
||||
openGraph: {
|
||||
title: 'PodcastHub - 给创意一个真实的声音',
|
||||
description: '使用AI技术将您的想法和内容转换为高质量的播客音频',
|
||||
description: '使用AI技术将您的想法和内容转换为高质量的播客音频,支持多种语音和风格选择。',
|
||||
type: 'website',
|
||||
locale: 'zh_CN',
|
||||
},
|
||||
};
|
||||
|
||||
export const viewport = {
|
||||
themeColor: '#000000',
|
||||
width: 'device-width',
|
||||
initialScale: 1,
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
|
||||
@@ -100,7 +100,7 @@ export default async function PodcastContent({ fileName }: PodcastContentProps)
|
||||
</div>
|
||||
|
||||
{/* 标题 */}
|
||||
<h1 className="text-3xl md:text-4xl font-bold text-gray-900 leading-tight break-words">
|
||||
<h1 className="text-3xl md:text-3xl font-bold text-gray-900 leading-tight break-words">
|
||||
{audioInfo.title}
|
||||
</h1>
|
||||
|
||||
|
||||
@@ -157,17 +157,34 @@ const PodcastCreator: React.FC<PodcastCreatorProps> = ({
|
||||
<div className="flex items-center justify-center gap-3 mb-4">
|
||||
<svg className="h-[80px] w-[300px] sm:h-[100px] sm:w-[600px]" viewBox="0 0 600 150" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="waveGradient" x1="49" y1="98" x2="140" y2="98" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#8E54E9"/>
|
||||
<stop offset="1" stop-color="#C26AE6"/>
|
||||
<linearGradient id="waveGradient" x1="0" y1="0" x2="140" y2="0" gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor="#D869E5">
|
||||
<animate attributeName="stop-color" values="#D069E6;#FB866C;#FA6F7E;#E968E2;" dur="5s" repeatCount="indefinite"/>
|
||||
</stop>
|
||||
<stop offset="1" stopColor="#D069E6">
|
||||
<animate attributeName="stop-color" values="#FB866C;#FA6F7E;#E968E2;#D869E5;" dur="5s" repeatCount="indefinite"/>
|
||||
</stop>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="textGradient" x1="175" y1="0" x2="810" y2="0" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.05" stop-color="#D069E6"/>
|
||||
<stop offset="0.35" stop-color="#FB866C"/>
|
||||
<stop offset="0.55" stop-color="#FA6F7E"/>
|
||||
<stop offset="0.85" stop-color="#E968E2"/>
|
||||
<stop offset="1" stop-color="#D869E5"/>
|
||||
<linearGradient id="textGradient" x1="600" y1="0" x2="150" y2="0" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stopColor="#C75AD4">
|
||||
<animate attributeName="stop-color" values="#C75AD4;#D85AD1;#F85F6F;#F9765B;#C15ED5;#C75AD4" dur="10s" repeatCount="indefinite" />
|
||||
</stop>
|
||||
<stop offset="0.1818" stopColor="#D85AD1">
|
||||
<animate attributeName="stop-color" values="#D85AD1;#F85F6F;#F9765B;#C15ED5;#C75AD4;#D85AD1" dur="10s" repeatCount="indefinite" />
|
||||
</stop>
|
||||
<stop offset="0.3636" stopColor="#F85F6F">
|
||||
<animate attributeName="stop-color" values="#F85F6F;#F9765B;#C15ED5;#C75AD4;#D85AD1;#F85F6F" dur="10s" repeatCount="indefinite" />
|
||||
</stop>
|
||||
<stop offset="0.5455" stopColor="#F9765B">
|
||||
<animate attributeName="stop-color" values="#F9765B;#C15ED5;#C75AD4;#D85AD1;#F85F6F;#F9765B" dur="10s" repeatCount="indefinite" />
|
||||
</stop>
|
||||
<stop offset="0.7273" stopColor="#C15ED5">
|
||||
<animate attributeName="stop-color" values="#C15ED5;#C75AD4;#D85AD1;#F85F6F;#F9765B;#C15ED5" dur="10s" repeatCount="indefinite" />
|
||||
</stop>
|
||||
<stop offset="0.9091" stopColor="#C75AD4">
|
||||
<animate attributeName="stop-color" values="#C75AD4;#D85AD1;#F85F6F;#F9765B;#C15ED5;#C75AD4" dur="10s" repeatCount="indefinite" />
|
||||
</stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
|
||||
@@ -139,21 +139,6 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||
{/* 品牌名称容器 - 慢慢收缩动画 */}
|
||||
<div className="overflow-hidden transition-all duration-500 ease-in-out w-auto ">
|
||||
<svg className="h-[30px] w-[180px] sm:h-[30px] sm:w-[180px]" viewBox="0 0 800 150" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="waveGradient" x1="49" y1="98" x2="140" y2="98" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#8E54E9"/>
|
||||
<stop offset="1" stop-color="#C26AE6"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="textGradient" x1="175" y1="0" x2="810" y2="0" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.05" stop-color="#D069E6"/>
|
||||
<stop offset="0.35" stop-color="#FB866C"/>
|
||||
<stop offset="0.55" stop-color="#FA6F7E"/>
|
||||
<stop offset="0.85" stop-color="#E968E2"/>
|
||||
<stop offset="1" stop-color="#D869E5"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<g>
|
||||
<path
|
||||
d="M49 98.5 C 56 56.5, 65 56.5, 73 90.5 C 79 120.5, 85 125.5, 91 100.5 C 96 80.5, 100 75.5, 106 95.5 C 112 115.5, 118 108.5, 125 98.5"
|
||||
|
||||
@@ -82,7 +82,7 @@ const Toast: React.FC<ToastProps> = ({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-start gap-3 p-4 rounded-lg shadow-lg bg-white border border-gray-200 backdrop-blur-md max-w-sm w-full transition-all duration-300 ease-in-out",
|
||||
"flex items-start gap-3 p-4 rounded-lg shadow-lg bg-white border border-gray-200 backdrop-blur-md max-w-sm w-full transition-all duration-300 ease-in-out pointer-events-auto",
|
||||
getAccentColor(), // 添加左侧强调色边框
|
||||
isVisible && !isLeaving ? "translate-y-0 opacity-100" : "-translate-y-4 opacity-0" // 向上弹出动画
|
||||
)}
|
||||
@@ -121,7 +121,7 @@ const Toast: React.FC<ToastProps> = ({
|
||||
onRemove,
|
||||
}) => {
|
||||
return (
|
||||
<div className="fixed top-4 left-1/2 -translate-x-1/2 z-50 w-full max-w-md pointer-events-none p-4 flex flex-col items-center space-y-3"> {/* 定位到顶部水平居中,并限制宽度,使用flex布局垂直居中,增加间距 */}
|
||||
<div className="fixed top-4 left-1/2 -translate-x-1/2 z-50 w-full max-w-md p-4 flex flex-col items-center space-y-3"> {/* 定位到顶部水平居中,并限制宽度,使用flex布局垂直居中,增加间距 */}
|
||||
{toasts.map((toast) => (
|
||||
<Toast
|
||||
key={toast.id}
|
||||
|
||||
Reference in New Issue
Block a user