Architecture & Workflow

從本機 AI 生成到 Apple Podcasts:
基於 Cloudflare R2 的私人有聲書 RSS 串流架構

IndexTTS 本地推理、AAC 輕量化轉檔、RSS 2.0 協定規格到 iPhone / Apple Watch 運動無縫同步

作者: Howard / PAI Infrastructure
日期: 2026-09-19
核心技術: IndexTTS-MLX · FFmpeg · Cloudflare R2 · Apple Podcasts RSS
狀態: 已部署上線 (Production Ready)

一、背景與核心痛點

在個人 AI 工作流中,我們透過本機大型語音合成模型(如 IndexTTS-2.5 / MLX 1.5 加速版)將投資經典《量價分析》(A Complete Guide to Volume Price Analysis)全書 13 章成功渲染為中文有聲書。第 1 至第 10 章累計產出了超過 7 小時 40 分鐘 的高品質無損語音。

然而,當希望在外出運動(慢跑、健身、散步)時透過手機收聽時,遇到了以下關鍵瓶頸:

🎯 核心目標: 打造一條零伺服器維護成本、零對外流量費用、具備完全隱私性的端到端管線,讓本機產生的音檔自動轉化為原生 Podcast,讓 iPhone 上的 Apple Podcasts App 像追蹤普通節目一樣,自動背景下載、記憶進度,甚至離線推送到 Apple Watch。

二、架構全貌:音訊管線與串接流程

1. 本機 TTS 生成
IndexTTS 產生 24kHz PCM 16-bit Mono WAV 切片並串接為完整章節。
2. AAC 輕量轉碼
FFmpeg 轉為 64kbps AAC (M4A),體積暴減 88%,並嵌入完整元資料。
3. RSS 2.0 自動建構
掃描音檔計算時長與 Byte 數,產生符合 iTunes 規範的 feed.xml。
4. Cloudflare R2 託管
上傳至具備 HTTP Range 支援的 R2 Bucket,享 0 流量費用邊緣分發。
5. Apple Podcasts 訂閱
手機端以私人 URL 追蹤節目,實現鎖屏背景播放與進度記憶。

三、底層協定:Podcast 與 RSS 究竟是什麼格式?

很多人誤以為 Podcast 是某個公司的專有應用,但其本質是一個開放、去中心化的 XML 資料傳輸協定。任何一個 Podcast 播放器(Apple Podcasts、Overcast、Pocket Casts)在訂閱時,做的事情只有一件:定期去抓取並解析一個遠端的 XML 檔案。

1. RSS 2.0 + iTunes Namespace 標準

現代 Podcast 規範建立在 RSS 2.0 之上,並引入 Apple 訂定的 itunes 擴充標籤。整體架構分為兩大部分:

2. 最核心的靈魂標籤:<enclosure>

在每個 <item> 中,最關鍵的標籤就是 <enclosure>,它告訴播放器音檔在哪裡、有多大、是什麼類型:

<enclosure url="https://.../01-chapter-one.m4a" length="17591227" type="audio/x-m4a"/>
屬性名稱 範例值 技術意義與重要性
url https://.../01.m4a 音訊檔案的公開直接下載位址(Direct Link),不得有跳轉登入阻攔。
length 17591227 精確到 Byte 的檔案大小。播放器用於計算下載進度條與預分配儲存空間。
type audio/x-m4a MIME Type。告訴播放器解碼器格式(例如 AAC 為 audio/x-m4a,MP3 為 audio/mpeg)。
<guid> 01-chapter-one 全域唯一識別碼。播放器依據 GUID 判斷「這集是否已聽過」或「是否已下載」,即便檔名改變只要 GUID 不變就不會重複下載。
<itunes:duration> 00:31:50 章節總時長(HH:MM:SS)。讓播放器在未開始下載音檔前即可展示正確時長。

四、雲端樞紐:為什麼選擇 Cloudflare R2?

在自建私人 Podcast 的架構中,音訊儲存與伺服器的選擇是成敗關鍵。傳統使用 AWS S3 或 Google Cloud Storage 面臨高昂的頻寬計費,而 Cloudflare R2 具有決定性的三大優勢:

1. 真正的零出站流量費(Zero Egress Fees)

有聲書單一章節往往達到 15~35 MB,全書完整下載多次會產生數十 GB 的流量。在 AWS S3 上,每 GB 出口流量需支付約 $0.09 美元;而 Cloudflare R2 不收取任何數據傳輸流量費,且提供每月 10 GB 的免費儲存額度,對個人自用而言等同於「永久 0 元」。

2. 原生支援 HTTP Range Request(斷點續傳與隨意拖動進度)

這是許多開發者自建串流最容易踩的坑:Apple Podcasts 要求音訊伺服器必須完整支援 HTTP Range 請求。當用戶在 1 小時的章節中拖動播放進度條、或者從上次停止處繼續播放時,播放器會送出 Range: bytes=1048576- 標頭。若伺服器不支援,播放器將被迫從頭下載或直接報錯。

Cloudflare R2 經由全球邊緣節點原生回傳 Accept-Ranges: bytes 與 HTTP 206 Partial Content,完美滿足 Apple 官方技術標準。

3. 隱私安全性與防爬機制

我們啟用了 R2 的專屬 Public Dev URL(格式如 https://pub-<hash>.r2.dev)。該網址具備高熵值的亂數特徵,不在任何公共目錄登記,搜尋引擎亦不會索引。只有知道專屬 Feed URL 的設備才能拉取,兼具便利與版權自用安全性。

五、實作工程落地:自動化轉換與上傳腳本

1. 音訊輕量化轉碼:FFmpeg 參數調優

IndexTTS 生成的無損 WAV 採樣率為 24,000Hz PCM 16-bit,體積龐大。我們採用 AAC 核心編碼器,保留原始 24kHz 單聲道特徵以避免二次重採樣失真,設定碼率為 64 kbps:

ffmpeg -i 01-chapter-one.wav -c:a aac -b:a 64k -vn \
  -metadata title="第01章:交易市場中沒有新鮮事" \
  -metadata artist="Anna Coulling / 圓圓朗讀" \
  -metadata album="量價分析 (Volume Price Analysis)" \
  -metadata genre="Audiobook" \
  01-chapter-one.m4a -y

成效對比: 第 1 至 10 章原始 WAV 體積為 1.15 GB,轉碼後降至 178 MB,體積縮減 84.5%,外出運動以行動網路下載數秒內即可完成。

2. 方形書封適配(Apple 1400x1400 規範)

Apple Podcasts 要求封面圖必須為 1:1 正方形且解析度介於 1400x1400 至 3000x3000。我們從電子書解出原封面(469x751),利用 FFmpeg 雙層濾鏡自動合成高斯模糊背景:

ffmpeg -i cover_raw.jpeg -filter_complex \
  "[0:v]scale=1400:1400,boxblur=20:20[bg];[0:v]scale=1400:1400:force_original_aspect_ratio=decrease[fg];[bg][fg]overlay=(W-w)/2:(H-h)/2" \
  -q:v 2 cover.jpg -y

3. 自動生成 feed.xml 核心邏輯

透過 Python 呼叫 ffprobe 取得精確時長與檔案大小,動態拼裝標準 XML:

# 關鍵資訊提取與 enclosure 組裝片段
file_size = os.path.getsize(m4a_path)
duration_sec = int(get_duration(m4a_path))
duration_str = f"{duration_sec // 3600:02d}:{(duration_sec % 3600) // 60:02d}:{duration_sec % 60:02d}"

item_xml = f"""
<item>
  <title><![CDATA[{chapter_title}]]></title>
  <itunes:author>Anna Coulling / 圓圓朗讀</itunes:author>
  <pubDate>{pub_date}</pubDate>
  <enclosure url="{r2_url}/{filename}" length="{file_size}" type="audio/x-m4a"/>
  <guid isPermaLink="false">{chapter_id}</guid>
  <itunes:duration>{duration_str}</itunes:duration>
</item>"""

4. Cloudflare R2 一鍵發布命令

利用 wrangler CLI,直接指定 --remote 參數與對應 MIME 類型推送:

# 上傳音檔
wrangler r2 object put "howard-audiobooks/01-chapter-one.m4a" \
  --file "audio/01-chapter-one.m4a" --content-type "audio/x-m4a" --remote

# 上傳 RSS 清單
wrangler r2 object put "howard-audiobooks/feed.xml" \
  --file "audio/feed.xml" --content-type "application/rss+xml" --remote

六、iPhone 客戶端訂閱與運動場景體驗

當所有物件上傳至 Cloudflare R2 後,訂閱與串接完全自動化:

  1. 開啟 iPhone 「Podcast (播客)」 App。
  2. 點選 「資料庫」 ➔ 右上角 「⋯」 ➔ 選擇 「透過 URL 加入節目 (Follow a Show by URL)」。
  3. 輸入專屬連結:https://pub-43dc4106670549999990fa09d26d8316.r2.dev/feed.xml。

運動場景體驗優勢:

七、專案結構與維護指令

所有轉檔、組裝與推送程式碼均已集中維護於專案目錄:

zh-simplified-tts/
├── audio/                   # M4A 音訊、cover.jpg、feed.xml 與原始 WAV
├── scripts/                 # OpenCC 簡體處理後的章節文本
├── publish_podcast.py       # 自動執行 FFmpeg 轉碼並產生 feed.xml
├── upload_to_r2.py          # 自動將最新檔案同步至 Cloudflare R2
└── docs/kami_podcast_guide/ # Kami 排版報告原始檔與 PDF

後續章節(第 11、12 章)生成完成後,僅需依序執行:

python3 publish_podcast.py
python3 upload_to_r2.py

手機端 Apple Podcasts 將在下一次背景檢查時自動跳出「新章節已發布」推播通知。