add podcast
This commit is contained in:
@@ -41,6 +41,7 @@
|
||||
| `llms[].api_key` | `string` | LLM 的 API 密钥。 | | 是 |
|
||||
| `llms[].model` | `string` | LLM 的模型。例如 `gpt-4o-mini`。如果用于生成任务 (如总结),则不能为空。如果此 LLM 被使用,则不能与 `embedding_model` 同时为空。 | | 条件性必需 |
|
||||
| `llms[].embedding_model` | `string` | LLM 的 Embedding 模型。例如 `text-embedding-3-small`。如果用于 Embedding,则不能为空。如果此 LLM 被使用,则不能与 `model` 同时为空。**注意:** 初次使用后请勿直接修改,应添加新的 LLM 配置。 | | 条件性必需 |
|
||||
| `llms[].tts_model` | `string` | LLM 的文本转语音 (TTS) 模型。 | | 否 |
|
||||
| `llms[].temperature` | `float32` | LLM 的温度 (0-2)。 | `0.0` | 否 |
|
||||
|
||||
### Jina AI 配置 (`jina`)
|
||||
@@ -80,10 +81,11 @@
|
||||
|
||||
### 存储配置 (`storage`)
|
||||
|
||||
| 字段 | 类型 | 描述 | 默认值 | 是否必需 |
|
||||
| :------------- | :------- | :-------------------------------------------- | :----------- | :------- |
|
||||
| `storage.dir` | `string` | 所有存储的基础目录。应用运行后不可更改。 | `./data` | 否 |
|
||||
| `storage.feed` | `object` | Feed 存储配置。详见下方的 **Feed 存储配置**。 | (见具体字段) | 否 |
|
||||
| 字段 | 类型 | 描述 | 默认值 | 是否必需 |
|
||||
| :--------------- | :------- | :-------------------------------------------------------------- | :----------- | :------- |
|
||||
| `storage.dir` | `string` | 所有存储的基础目录。应用运行后不可更改。 | `./data` | 否 |
|
||||
| `storage.feed` | `object` | Feed 存储配置。详见下方的 **Feed 存储配置**。 | (见具体字段) | 否 |
|
||||
| `storage.object` | `object` | 对象存储配置,用于存储播客等文件。详见下方的 **对象存储配置**。 | (见具体字段) | 否 |
|
||||
|
||||
### Feed 存储配置 (`storage.feed`)
|
||||
|
||||
@@ -95,6 +97,16 @@
|
||||
| `storage.feed.retention` | `time.Duration` | Feed 的保留时长。 | `8d` | 否 |
|
||||
| `storage.feed.block_duration` | `time.Duration` | 每个基于时间的 Feed 存储块的保留时长 (类似于 Prometheus TSDB Block)。 | `25h` | 否 |
|
||||
|
||||
### 对象存储配置 (`storage.object`)
|
||||
|
||||
| 字段 | 类型 | 描述 | 默认值 | 是否必需 |
|
||||
| :--------------------------------- | :------- | :----------------------------- | :----- | :-------------------- |
|
||||
| `storage.object.endpoint` | `string` | 对象存储的端点。 | | 是 (如果使用播客功能) |
|
||||
| `storage.object.access_key_id` | `string` | 对象存储的 Access Key ID。 | | 是 (如果使用播客功能) |
|
||||
| `storage.object.secret_access_key` | `string` | 对象存储的 Secret Access Key。 | | 是 (如果使用播客功能) |
|
||||
| `storage.object.bucket` | `string` | 对象存储的存储桶名称。 | | 是 (如果使用播客功能) |
|
||||
| `storage.object.bucket_url` | `string` | 对象存储的桶访问 URL。 | | 否 |
|
||||
|
||||
### 重写规则配置 (`storage.feed.rewrites[]`)
|
||||
|
||||
定义在存储前处理 Feed 的规则。规则按顺序应用。
|
||||
@@ -109,12 +121,8 @@
|
||||
| `...rewrites[].match_re` | `string` | 用于匹配 (转换后) 文本的正则表达式。 | `.*` (匹配所有) | 否 (使用 `match` 或 `match_re`) |
|
||||
| `...rewrites[].action` | `string` | 匹配时执行的操作: `create_or_update_label` (使用匹配/转换后的文本添加/更新标签), `drop_feed` (完全丢弃该 Feed)。 | `create_or_update_label` | 否 |
|
||||
| `...rewrites[].label` | `string` | 要创建或更新的 Feed 标签名称。 | | 是 (如果 `action` 是 `create_or_update_label`) |
|
||||
|
||||
### 重写规则转换配置 (`storage.feed.rewrites[].transform`)
|
||||
|
||||
| 字段 | 类型 | 描述 | 默认值 | 是否必需 |
|
||||
| :--------------------- | :------- | :------------------------------------------------------------------- | :----- | :------- |
|
||||
| `...transform.to_text` | `object` | 使用 LLM 将源文本转换为文本。详见下方的 **重写规则转换为文本配置**。 | `nil` | 否 |
|
||||
| `...transform.to_text` | `object` | 使用 LLM 将源文本转换为文本。详见下方的 **重写规则转换为文本配置**。 | `nil` | 否 |
|
||||
| `...transform.to_podcast` | `object` | 将源文本转换为播客。详见下方的 **重写规则转换为播客配置**。 | `nil` | 否 |
|
||||
|
||||
### 重写规则转换为文本配置 (`storage.feed.rewrites[].transform.to_text`)
|
||||
|
||||
@@ -126,6 +134,25 @@
|
||||
| `...to_text.llm` | `string` | **仅当 `type` 为 `prompt` 时有效。** 用于转换的 LLM 名称 (来自 `llms` 部分)。如果未指定,将使用在 `llms` 部分中标记为 `default: true` 的 LLM。 | `llms` 部分中的默认 LLM | 否 |
|
||||
| `...to_text.prompt` | `string` | **仅当 `type` 为 `prompt` 时有效。** 用于转换的 Prompt。源文本将被注入。可以使用 Go 模板语法引用内置 Prompt: `{{ .summary }}`, `{{ .category }}`, `{{ .tags }}`, `{{ .score }}`, `{{ .comment_confucius }}`, `{{ .summary_html_snippet }}`, `{{ .summary_html_snippet_for_small_model }}`。 | | 是 (如果 `type` 是 `prompt`) |
|
||||
|
||||
### 重写规则转换为播客配置 (`storage.feed.rewrites[].transform.to_podcast`)
|
||||
|
||||
此配置定义了如何将 `source_label` 的文本转换为播客。
|
||||
|
||||
| 字段 | 类型 | 描述 | 默认值 | 是否必需 |
|
||||
| :------------------------------------------- | :--------- | :-------------------------------------------------------------------------------------------------------- | :---------------------- | :------- |
|
||||
| `...to_podcast.llm` | `string` | 用于生成播客稿件的 LLM 名称 (来自 `llms` 部分)。 | `llms` 部分中的默认 LLM | 否 |
|
||||
| `...to_podcast.transcript_additional_prompt` | `string` | 附加到播客稿件生成 Prompt 的额外指令。 | | 否 |
|
||||
| `...to_podcast.tts_llm` | `string` | 用于文本转语音 (TTS) 的 LLM 名称 (来自 `llms` 部分)。**注意:目前仅支持 `provider` 为 `gemini` 的 LLM**。 | `llms` 部分中的默认 LLM | 否 |
|
||||
| `...to_podcast.speakers` | `对象列表` | 播客的演讲者列表。详见下方的 **演讲者配置**。 | `[]` | 是 |
|
||||
|
||||
#### 演讲者配置 (`...to_podcast.speakers[]`)
|
||||
|
||||
| 字段 | 类型 | 描述 | 默认值 | 是否必需 |
|
||||
| :-------------------- | :------- | :------------------------ | :----- | :------- |
|
||||
| `...speakers[].name` | `string` | 演讲者的名字。 | | 是 |
|
||||
| `...speakers[].role` | `string` | 演讲者的角色描述 (人设)。 | | 否 |
|
||||
| `...speakers[].voice` | `string` | 演讲者的声音。 | | 是 |
|
||||
|
||||
### 调度配置 (`scheduls`)
|
||||
|
||||
定义查询和监控 Feed 的规则。
|
||||
@@ -173,10 +200,11 @@
|
||||
|
||||
定义*谁*接收通知。
|
||||
|
||||
| 字段 | 类型 | 描述 | 默认值 | 是否必需 |
|
||||
| :------------------------- | :------- | :------------------------------- | :----- | :------------------ |
|
||||
| `notify.receivers[].name` | `string` | 接收者的唯一名称。在路由中使用。 | | 是 |
|
||||
| `notify.receivers[].email` | `string` | 接收者的电子邮件地址。 | | 是 (如果使用 Email) |
|
||||
| 字段 | 类型 | 描述 | 默认值 | 是否必需 |
|
||||
| :--------------------------- | :------- | :------------------------------------------------------- | :----- | :-------------------- |
|
||||
| `notify.receivers[].name` | `string` | 接收者的唯一名称。在路由中使用。 | | 是 |
|
||||
| `notify.receivers[].email` | `string` | 接收者的电子邮件地址。 | | 是 (如果使用 Email) |
|
||||
| `notify.receivers[].webhook` | `object` | 接收者的 Webhook 配置。例如: `webhook: { "url": "xxx" }` | | 是 (如果使用 Webhook) |
|
||||
|
||||
### 通知渠道配置 (`notify.channels`)
|
||||
|
||||
@@ -194,4 +222,4 @@
|
||||
| `...email.from` | `string` | 发件人 Email 地址。 | | 是 |
|
||||
| `...email.password` | `string` | 发件人 Email 的应用专用密码。(对于 Gmail, 参见 [Google 应用密码](https://support.google.com/mail/answer/185833))。 | | 是 |
|
||||
| `...email.feed_markdown_template` | `string` | 用于在 Email 正文中格式化每个 Feed 的 Markdown 模板。默认渲染 Feed 内容。不能与 `feed_html_snippet_template` 同时设置。可用的模板变量取决于 Feed 标签。 | `{{ .content }}` | 否 |
|
||||
| `...email.feed_html_snippet_template` | `string` | 用于格式化每个 Feed 的 HTML 片段模板。不能与 `feed_markdown_template` 同时设置。可用的模板变量取决于 Feed 标签。 | | 否 |
|
||||
| `...email.feed_html_snippet_template` | `string` | 用于格式化每个 Feed 的 HTML 片段模板。不能与 `feed_markdown_template` 同时设置。可用的模板变量取决于 Feed 标签。 | | 否 |
|
||||
|
||||
@@ -41,6 +41,7 @@ This section defines the list of available Large Language Models. At least one L
|
||||
| `llms[].api_key` | `string` | API key for the LLM. | | Yes |
|
||||
| `llms[].model` | `string` | Model of the LLM. E.g., `gpt-4o-mini`. Cannot be empty if used for generation tasks (e.g., summarization). If this LLM is used, cannot be empty along with `embedding_model`. | | Conditionally Required |
|
||||
| `llms[].embedding_model` | `string` | Embedding model of the LLM. E.g., `text-embedding-3-small`. Cannot be empty if used for embedding. If this LLM is used, cannot be empty along with `model`. **Note:** Do not modify directly after initial use; add a new LLM configuration instead. | | Conditionally Required |
|
||||
| `llms[].tts_model` | `string` | The Text-to-Speech (TTS) model of the LLM. | | No |
|
||||
| `llms[].temperature` | `float32` | Temperature of the LLM (0-2). | `0.0` | No |
|
||||
|
||||
### Jina AI Configuration (`jina`)
|
||||
@@ -80,10 +81,11 @@ Describes each source to be scraped.
|
||||
|
||||
### Storage Configuration (`storage`)
|
||||
|
||||
| Field | Type | Description | Default Value | Required |
|
||||
| :------------- | :------- | :------------------------------------------------------------------------------ | :-------------------- | :------- |
|
||||
| `storage.dir` | `string` | Base directory for all storage. Cannot be changed after the application starts. | `./data` | No |
|
||||
| `storage.feed` | `object` | Feed storage configuration. See **Feed Storage Configuration** below. | (See specific fields) | No |
|
||||
| Field | Type | Description | Default Value | Required |
|
||||
| :--------------- | :------- | :-------------------------------------------------------------------------------------------------------- | :-------------------- | :------- |
|
||||
| `storage.dir` | `string` | Base directory for all storage. Cannot be changed after the application starts. | `./data` | No |
|
||||
| `storage.feed` | `object` | Feed storage configuration. See **Feed Storage Configuration** below. | (See specific fields) | No |
|
||||
| `storage.object` | `object` | Object storage configuration for storing files like podcasts. See **Object Storage Configuration** below. | (See specific fields) | No |
|
||||
|
||||
### Feed Storage Configuration (`storage.feed`)
|
||||
|
||||
@@ -95,6 +97,16 @@ Describes each source to be scraped.
|
||||
| `storage.feed.retention` | `time.Duration` | Retention duration for feeds. | `8d` | No |
|
||||
| `storage.feed.block_duration` | `time.Duration` | Retention duration for each time-based feed storage block (similar to Prometheus TSDB Block). | `25h` | No |
|
||||
|
||||
### Object Storage Configuration (`storage.object`)
|
||||
|
||||
| Field | Type | Description | Default Value | Required |
|
||||
| :--------------------------------- | :------- | :------------------------------------------- | :------------ | :----------------------------- |
|
||||
| `storage.object.endpoint` | `string` | The endpoint of the object storage. | | Yes (if using podcast feature) |
|
||||
| `storage.object.access_key_id` | `string` | The access key id of the object storage. | | Yes (if using podcast feature) |
|
||||
| `storage.object.secret_access_key` | `string` | The secret access key of the object storage. | | Yes (if using podcast feature) |
|
||||
| `storage.object.bucket` | `string` | The bucket of the object storage. | | Yes (if using podcast feature) |
|
||||
| `storage.object.bucket` | `string` | The URL of the object storage bucket. | | No |
|
||||
|
||||
### Rewrite Rule Configuration (`storage.feed.rewrites[]`)
|
||||
|
||||
Defines rules to process feeds before storage. Rules are applied sequentially.
|
||||
@@ -109,12 +121,8 @@ Defines rules to process feeds before storage. Rules are applied sequentially.
|
||||
| `...rewrites[].match_re` | `string` | Regular expression to match against the (transformed) text. | `.*` (matches all) | No (use `match` or `match_re`) |
|
||||
| `...rewrites[].action` | `string` | Action to perform on match: `create_or_update_label` (adds/updates a label with the matched/transformed text), `drop_feed` (discards the feed entirely). | `create_or_update_label` | No |
|
||||
| `...rewrites[].label` | `string` | Name of the feed label to create or update. | | Yes (if `action` is `create_or_update_label`) |
|
||||
|
||||
### Rewrite Rule Transform Configuration (`storage.feed.rewrites[].transform`)
|
||||
|
||||
| Field | Type | Description | Default Value | Required |
|
||||
| :--------------------- | :------- | :--------------------------------------------------------------------------------------------- | :------------ | :------- |
|
||||
| `...transform.to_text` | `object` | Transforms source text to text using an LLM. See **Rewrite Rule To Text Configuration** below. | `nil` | No |
|
||||
| `...transform.to_text` | `object` | Transforms source text to text using an LLM. See **Rewrite Rule To Text Configuration** below. | `nil` | No |
|
||||
| `...transform.to_podcast` | `object` | Transforms source text to a podcast. See **Rewrite Rule To Podcast Configuration** below. | `nil` | No |
|
||||
|
||||
### Rewrite Rule To Text Configuration (`storage.feed.rewrites[].transform.to_text`)
|
||||
|
||||
@@ -124,7 +132,26 @@ This configuration defines how to transform the text from `source_label`.
|
||||
| :------------------ | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------- | :-------------------------- |
|
||||
| `...to_text.type` | `string` | Type of transformation. Options: <ul><li>`prompt` (default): Uses an LLM and a specified prompt to transform the source text.</li><li>`crawl`: Treats the source text as a URL, directly crawls the web page content pointed to by the URL, and converts it to Markdown format. This method performs local crawling and attempts to follow `robots.txt`.</li><li>`crawl_by_jina`: Treats the source text as a URL, crawls and processes web page content via the [Jina AI Reader API](https://jina.ai/reader/), and returns Markdown. Potentially more powerful, e.g., for handling dynamic pages, but relies on the Jina AI service.</li></ul> | `prompt` | No |
|
||||
| `...to_text.llm` | `string` | **Only valid if `type` is `prompt`.** Name of the LLM used for transformation (from `llms` section). If not specified, the LLM marked as `default: true` in the `llms` section will be used. | Default LLM in `llms` section | No |
|
||||
| `...to_text.prompt` | `string` | **Only valid if `type` is `prompt`.** Prompt used for transformation. The source text will be injected. You can use Go template syntax to reference built-in prompts: `{{ .summary }}`, `{{ .category }}`, `{{ .tags }}`, `{{ .score }}`, `{{ .comment_confucius }}`, `{{ .summary_html_snippet }}`. | | Yes (if `type` is `prompt`) |
|
||||
| `...to_text.prompt` | `string` | **Only valid if `type` is `prompt`.** Prompt used for transformation. The source text will be injected. You can use Go template syntax to reference built-in prompts: `{{ .summary }}`, `{{ .category }}`, `{{ .tags }}`, `{{ .score }}`, `{{ .comment_confucius }}`, `{{ .summary_html_snippet }}`, `{{ .summary_html_snippet_for_small_model }}`. | | Yes (if `type` is `prompt`) |
|
||||
|
||||
### Rewrite Rule To Podcast Configuration (`storage.feed.rewrites[].transform.to_podcast`)
|
||||
|
||||
This configuration defines how to transform the text from `source_label` into a podcast.
|
||||
|
||||
| Field | Type | Description | Default Value | Required |
|
||||
| :------------------------------------------- | :---------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------- | :------- |
|
||||
| `...to_podcast.llm` | `string` | The name of the LLM (from the `llms` section) to use for generating the podcast script. | Default LLM in `llms` section | No |
|
||||
| `...to_podcast.transcript_additional_prompt` | `string` | Additional instructions to append to the prompt for generating the podcast script. | | No |
|
||||
| `...to_podcast.tts_llm` | `string` | The name of the LLM (from the `llms` section) to use for Text-to-Speech (TTS). **Note: Currently only supports LLMs with `provider: gemini`**. | Default LLM in `llms` section | No |
|
||||
| `...to_podcast.speakers` | `list of objects` | A list of speakers for the podcast. See **Speaker Configuration** below. | `[]` | Yes |
|
||||
|
||||
#### Speaker Configuration (`...to_podcast.speakers[]`)
|
||||
|
||||
| Field | Type | Description | Default Value | Required |
|
||||
| :-------------------- | :------- | :----------------------------------- | :------------ | :------- |
|
||||
| `...speakers[].name` | `string` | The name of the speaker. | | Yes |
|
||||
| `...speakers[].role` | `string` | The role description of the speaker. | | No |
|
||||
| `...speakers[].voice` | `string` | The voice of the speaker. | | Yes |
|
||||
|
||||
### Scheduling Configuration (`scheduls`)
|
||||
|
||||
@@ -173,10 +200,11 @@ This structure can be nested using `sub_routes`. Feeds will first try to match s
|
||||
|
||||
Defines *who* receives notifications.
|
||||
|
||||
| Field | Type | Description | Default Value | Required |
|
||||
| :------------------------- | :------- | :------------------------------------------- | :------------ | :------------------- |
|
||||
| `notify.receivers[].name` | `string` | Unique name of the receiver. Used in routes. | | Yes |
|
||||
| `notify.receivers[].email` | `string` | Email address of the receiver. | | Yes (if using Email) |
|
||||
| Field | Type | Description | Default Value | Required |
|
||||
| :--------------------------- | :------- | :----------------------------------------------------------------------- | :------------ | :--------------------- |
|
||||
| `notify.receivers[].name` | `string` | Unique name of the receiver. Used in routes. | | Yes |
|
||||
| `notify.receivers[].email` | `string` | Email address of the receiver. | | Yes (if using Email) |
|
||||
| `notify.receivers[].webhook` | `object` | Webhook configuration for the receiver. E.g. `webhook: { "url": "xxx" }` | | Yes (if using Webhook) |
|
||||
|
||||
### Notification Channel Configuration (`notify.channels`)
|
||||
|
||||
|
||||
104
docs/podcast.md
Normal file
104
docs/podcast.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# 使用 Zenfeed 将文章转换为播客
|
||||
|
||||
Zenfeed 的播客功能可以将任何文章源自动转换为一场引人入胜的多人对话式播客。该功能利用大语言模型(LLM)生成对话脚本和文本转语音(TTS),并将最终的音频文件托管在您自己的对象存储中。
|
||||
|
||||
## 工作原理
|
||||
|
||||
1. **提取内容**: Zenfeed 首先通过重写规则提取文章的全文内容。
|
||||
2. **生成脚本**: 使用一个指定的 LLM(如 GPT-4o-mini)将文章内容改编成一个由多位虚拟主播对话的脚本。您可以定义每个主播的角色(人设)来控制对话风格。
|
||||
3. **语音合成**: 调用另一个支持 TTS 的 LLM(目前仅支持 Google Gemini)将脚本中的每一句对话转换为语音。
|
||||
4. **音频合并**: 将所有语音片段合成为一个完整的 WAV 音频文件。
|
||||
5. **上传存储**: 将生成的播客文件上传到您配置的 S3 兼容对象存储中。
|
||||
6. **保存链接**: 最后,将播客文件的公开访问 URL 保存为一个新的 Feed 标签,方便您在通知、API 或其他地方使用。
|
||||
|
||||
## 配置步骤
|
||||
|
||||
要启用播客功能,您需要完成以下三项配置:LLM、对象存储和重写规则。
|
||||
|
||||
### 1. 配置 LLM
|
||||
|
||||
您需要至少配置两个 LLM:一个用于生成对话脚本,另一个用于文本转语音(TTS)。
|
||||
|
||||
- **脚本生成 LLM**: 可以是任何性能较好的聊天模型,例如 OpenAI 的 `gpt-4o-mini` 或 Google 的 `gemini-1.5-pro`。
|
||||
- **TTS LLM**: 用于将文本转换为语音。**注意:目前此功能仅支持 `provider` 为 `gemini` 的 LLM。**
|
||||
|
||||
**示例 `config.yaml`:**
|
||||
|
||||
```yaml
|
||||
llms:
|
||||
# 用于生成播客脚本的 LLM
|
||||
- name: openai-chat
|
||||
provider: openai
|
||||
api_key: "sk-..."
|
||||
model: gpt-4o-mini
|
||||
default: true
|
||||
|
||||
# 用于文本转语音 (TTS) 的 LLM
|
||||
- name: gemini-tts
|
||||
provider: gemini
|
||||
api_key: "..." # 你的 Google AI Studio API Key
|
||||
tts_model: "gemini-2.5-flash-preview-tts" # Gemini 的 TTS 模型
|
||||
```
|
||||
|
||||
### 2. 配置对象存储
|
||||
|
||||
生成的播客音频文件需要一个地方存放。Zenfeed 支持任何 S3 兼容的对象存储服务。这里我们以 [Cloudflare R2](https://www.cloudflare.com/zh-cn/products/r2/) 为例。
|
||||
|
||||
首先,您需要在 Cloudflare R2 中创建一个存储桶(Bucket)。然后获取以下信息:
|
||||
|
||||
- `endpoint`: 您的 R2 API 端点。通常格式为 `https://<account_id>.r2.cloudflarestorage.com`。您可以在 R2 存储桶的主页找到它。
|
||||
- `access_key_id` 和 `secret_access_key`: R2 API 令牌。您可以在 "R2" -> "管理 R2 API 令牌" 页面创建。
|
||||
- `bucket`: 您创建的存储桶的名称。
|
||||
- `bucket_url`: 存储桶的公开访问 URL。要获取此 URL,您需要将存储桶连接到一个自定义域,或者使用 R2 提供的 `r2.dev` 公开访问地址。
|
||||
|
||||
**示例 `config.yaml`:**
|
||||
|
||||
```yaml
|
||||
storage:
|
||||
object:
|
||||
endpoint: "https://<your_account_id>.r2.cloudflarestorage.com"
|
||||
access_key_id: "..."
|
||||
secret_access_key: "..."
|
||||
bucket: "zenfeed-podcasts"
|
||||
bucket_url: "https://pub-xxxxxxxx.r2.dev"
|
||||
```
|
||||
|
||||
### 3. 配置重写规则
|
||||
|
||||
最后一步是创建一个重写规则,告诉 Zenfeed 如何将文章转换为播客。这个规则定义了使用哪个标签作为源文本、由谁来对话、使用什么声音等。
|
||||
|
||||
**关键配置项:**
|
||||
|
||||
- `source_label`: 包含文章全文的标签。
|
||||
- `label`: 用于存储最终播客 URL 的新标签名称。
|
||||
- `transform.to_podcast`: 播客转换的核心配置。
|
||||
- `llm`: 用于生成脚本的 LLM 名称(来自 `llms` 配置)。
|
||||
- `tts_llm`: 用于 TTS 的 LLM 名称(来自 `llms` 配置)。
|
||||
- `speakers`: 定义播客的演讲者。
|
||||
- `name`: 演讲者的名字。
|
||||
- `role`: 演讲者的角色和人设,将影响脚本内容。
|
||||
- `voice`: 演讲者的声音。请参考 [Google Cloud TTS 文档](https://cloud.google.com/text-to-speech/docs/voices) 获取可用的声音名称(例如 `en-US-Standard-C`,`en-US-News-N`)。
|
||||
|
||||
**示例 `config.yaml`:**
|
||||
|
||||
```yaml
|
||||
storage:
|
||||
feed:
|
||||
rewrites:
|
||||
- source_label: "content"
|
||||
label: "podcast_url"
|
||||
transform:
|
||||
to_podcast:
|
||||
llm: "openai-chat"
|
||||
tts_llm: "gemini-tts"
|
||||
transcript_additional_prompt: "请让对话更生动有趣一些。使用中文回复"
|
||||
speakers:
|
||||
- name: "主持人小雅"
|
||||
role: "一位经验丰富、声音甜美、风格活泼的科技播客主持人。"
|
||||
voice: "zh-CN-Standard-A" # 女声
|
||||
- name: "技术评论员老王"
|
||||
role: "一位对技术有深入见解、观点犀利的评论员,说话直接,偶尔有些愤世嫉俗。"
|
||||
voice: "zh-CN-Standard-B" # 男声
|
||||
```
|
||||
|
||||
配置完成后,Zenfeed 将在每次抓取到新文章时,自动执行上述流程。可以在通知模版中使用 podcast_url label,或 Web 中直接收听(Web 固定读取 podcast_url label,若使用别的名称则无法读取)
|
||||
Reference in New Issue
Block a user