本地音訊變私人 Podcast RSS 的自架工具 Folder2Podcast 實測

Folder2Podcast 是把音訊資料夾變成 Podcast RSS 訂閱源的自架小工具,Docker 一行指令就能上線,讓播客 App 接手斷點續播與跨裝置進度。這篇實測拆解幾件事:集數發佈日期是從 2024 年 12 月起算出來的假時間線、整個服務沒有任何登入機制所以私人邊界只到區網、README 宣稱的 iTunes 標籤在實際輸出一個都不存在,加上遲遲沒發佈到 stable 的 v2 開發線與倉庫裡缺落的授權條款。

用 AI 摘要這篇文章:

硬碟裡躺著一套有聲書 MP3,手機通勤聽到第七章,回家想用平板接著聽,才發現兩台裝置各記各的進度。這是很多自己收藏音訊檔的人都撞過的牆:檔案是你的,但「聽到哪」這件事沒有地方記。Folder2Podcast 給的答案很直接,把資料夾變成一條 Podcast RSS 訂閱源,讓現成的播客 App 接手續播、進度與同步。

我實際用 Docker 把它跑起來測過:一行指令、一個裝了幾個音檔的資料夾,服務就列出訂閱網址,抓下來的 RSS 也符合規範。不過實測同時驗出兩件 README 沒說清楚的事,一是整個服務沒有任何登入機制,「私人」的邊界只到你家的網路;二是文件宣稱完整支援的 iTunes 標籤,實際輸出一個都不存在。這篇把可用的部分和該小心的部分一次攤開。

把「記住你聽到哪」外包給 Podcast App

先講清楚這個工具到底借了什麼巧力。Podcast 之所以好用,是因為十幾年累積下來的客戶端生態:Apple Podcasts、Pocket Casts 這些 App 天生就會記住每一集聽到第幾分鐘、換裝置時透過帳號同步進度、自動下載新集數。這些能力播客 App 原本就免費提供,問題只在於它們只認 RSS 訂閱源,不認你硬碟裡的資料夾。還在挑閱讀器的人可以順便看我們介紹過的 Readig 這類 RSS 與 Podcast 閱讀器,跨平台的選擇比想像中多。

Folder2Podcast 做的就是中間那層翻譯。它掃描資料夾、讀檔名、產生 RSS,之後的事全部交給播客 App。這個分工也是它最誠實的地方:工具本身不處理進度、不處理同步,那些是客戶端的工作。作者在文件裡對此有明確宣稱,我實測的範圍是服務端產生的 RSS 輸出,跨裝置同步的實際體驗取決於你選的 App,這段效果我沒有逐一驗證。

同樣的需求市場上早有重量級答案,audiobookshelf 這類自架有聲書伺服器就是標準解,功能全到有 App、有轉檔、有使用者管理。Folder2Podcast 走的是另一個極端,後面會對照兩者該怎麼選。

一行指令真的能跑:實測過程與結果

實測環境是本機 Docker,過程比說明文件寫的還短。建一個資料夾放幾個用序號命名的 WAV 檔,加上一個可有可無的 podcast.json 描述檔,然後一行指令:

docker run -d -p 3000:3000 -v /path/to/audio:/podcasts -e BASE_URL=http://192.168.1.10:3000 yaotutu/folder2podcast

容器啟動後終端直接印出掃描結果:找到幾個播客源、每個幾集、RSS 網址是什麼。打開網頁介面,清單頁列出播客名稱、集數與最新更新時間,旁邊附上複製訂閱網址的按鈕,以及 Apple Podcasts、Overcast、Pocket Casts、Castro、Moon FM 五個一鍵訂閱鈕。介面文字是簡體中文,台灣使用者看功能沒有障礙,但第一次接觸會明顯感覺到這是對岸專案。有個小地方值得記:清單頁顯示的「最新更新」日期也是算出來的那條假時間線,我的測試資料明明當天才建立,頁面卻寫著 2024 年 12 月,第一次看到先別以為是抓錯資料。

Folder2Podcast 網頁介面清單頁,顯示播客名稱、集數與一鍵訂閱按鈕Pin
Folder2Podcast 網頁清單頁,本機 Docker 實測截圖(2026-09-15)

兩個環境變數直接決定訂閱成敗。BASE_URL 決定 RSS 裡所有連結的開頭,本機測試填 localhost,要讓手機訂閱就得填區網內伺服器的實際位址,填錯的症狀是 App 訂閱成功但音檔全部載不到。PUID 和 PGID 則處理 Linux 上常見的資料夾權限問題,把音樂資料夾擁有者的使用者 ID 填進去即可。掛載時可以加上唯讀參數,原始碼裡掃描與產生 RSS 的流程也只讀來源檔,訂閱清單存在容器自己的工作目錄,不會回寫你的音樂資料夾。

資料夾怎麼組織也有講究:掃描器把根目錄底下的每個子資料夾當成一支獨立的播客,各自產生一條 RSS。換句話說,一個掛載目錄可以同時放有聲書、課程錄音和演講整理,訂閱時各訂各的,互不干擾。每個子資料夾裡還可以放一張命名固定的封面圖,掃到就用它當播客封面,沒放就套預設圖;podcast.json 也是每個子資料夾一份,標題、作者、語言、分類都能逐檔設定。想替不同類型的內容保持不同門面,靠資料夾結構就做得到,不需要任何額外設定介面。

劇集日期是算出來的:2024 年 12 月開始的假時間線

實測最有趣的發現在 RSS 內容裡。我把三個檔案放進去,檔名開頭分別是 01、02、10,抓下來的 RSS 裡三集的發佈日期分別是 2024 年 12 月 18 日、19 日、27 日。檔案明明是我當天才產生的,日期卻落在 2024 年底。

瀏覽器檢視 Folder2Podcast 產生的 RSS XML,三個 item 的發佈日期顯示 2024 年 12 月Pin
實際產生的 RSS XML,第 1、2 集的 pubDate 落在 2024 年 12 月 18、19 日(2026-09-15 實測截圖)

翻原始碼就知道為什麼。episode.ts 裡寫死了一個基準日期常數 BASE_DATE,值是 2024 年 12 月 18 日,然後 generatePubDate 函式把集數當位移量加上去,第 N 集的發佈日就是基準日加 N 減一天。第 1 集落在 12 月 18 日,第 10 集落在 12 月 27 日,一天一集,整整齊齊。

這個設計有其道理。播客客戶端大多依日期排序集數,如果用檔案的真實建立時間,同時批次丟進去的幾百個檔案會擠在同一天,排序就亂了。用集數反推一條一天一集的時間線,客戶端看到的順序就和檔名序號一致。副作用是你在 App 裡看到的「發佈時間」是虛構的,這對私人收聽無妨,但如果你打算把這條 RSS 給別人訂閱,對方看到的日期全部是假的。podcast.json 裡另有 useMTime 選項可以改成使用檔案真實時間,此時排序改由檔案時間決定,魚與熊掌只能選一邊。

補檔不用重開機,但只認得三種副檔名

測到一半我往資料夾補了一個 05 開頭的新檔,沒有重啟容器,幾秒後重新查詢清單,集數從 3 變 4。底層用的是 chokidar 檔案監看,寫入穩定兩秒後才觸發、再加一秒防抖,避免大檔複製到一半被掃進去。日常把新下載的章節丟進資料夾,其他什麼都不用作。

檔名規則預設抓開頭數字(01-、001_ 這類);結尾是數字的命名(例如第一集01.mp3)要另外把擷取策略設成後綴模式才認得,也可以直接給自訂正規表示式。完全沒有數字的檔案有後備機制,原始碼會拿檔案時間戳記加上檔案大小湊出一個排序值,清單照樣排得出來,只是順序不如序號命名那樣可控。清理模式則只剝開頭或結尾的數字,01-第一章 會變成 第一章。文件裡有個例子寫第01期清理後會只剩第和期兩個字,我拿實際程式碼核對的結果是夾在中間的數字根本不會被處理,這個檔名清理後原樣不動,文件又一次比實作樂觀。

硬限制是格式。原始碼裡的副檔名判斷只接受 MP3、M4A、WAV 三種,FLAC、OGG 連出現在清單裡的機會都沒有。收藏裡有無損音樂的人要先轉檔,而它自己也完全不轉檔,只做清單。

「私人」的邊界:整個服務沒有密碼

這是要放最前面講的那件事。我把 server.ts 從頭讀到尾,全部路由裡找不到任何一行認證程式碼:沒有帳號、沒有密碼、沒有 API 金鑰,服務直接監聽所有網路介面。也就是說,只要能連到這台機器的這個連接埠,任何人都可以列出你全部的播客清單、下載全部音檔、拿到全部 RSS。我實測時不帶任何憑證,照樣把所有內容抓了下來。

在家用區網裡這通常可接受,路由器就是你的防火牆。真正的風險情境是把連接埠轉發到公網,想在公司用手機聽家裡的書,直覺做法就是開埠,這一開等於把整個音訊資料夾對全世界裸奔,而且 RSS 一旦被 Google 索引就連「知道網址才知道」這層保護都沒有。要有對外存取,正確做法是在前面加一層帶認證的反向代理,或走 VPN 連回家裡再存取。這些在目前發佈的版本裡都不提供,自己想;開發中的 v2 已經補了帳號機制,但何時變成正式發佈沒有時間表。

同場加映一個小提醒:podcast.json 裡的電子郵件欄位會原封不動寫進 RSS 的作者欄位,對外提供訂閱時記得填丟棄式信箱。

README 說的 iTunes 標籤,輸出裡一個都沒有

說明文件宣稱完整支援 Podcast RSS 2.0 規範與 iTunes 專有標籤,這句話我只信了一半。實際輸出裡,RSS 2.0 的部分確實齊全,標題、連結、描述、每集的 enclosure 音檔網址都在,多數客戶端靠這些就能訂閱播放。但 iTunes 命名空間的東西,實際輸出的 XML 裡一個 itunes 開頭的標籤都沒有,連命名空間宣告都不存在。我測了 Docker Hub 上兩個版本,穩定版和主線版結果相同。

回頭看原始碼,feed.ts 裡寫了加入 iTunes 標籤的程式碼,呼叫的是 feed 這個套件的擴充介面。問題是這個擴充機制在該套件的產出流程裡根本不會被讀取,呼叫了等於沒呼叫,產生出來的 XML 自然一個都不含。作者寫了、沒驗證輸出,文件就照設計意圖寫了。對私人收聽的實際影響有限,分類欄位、節目類型這些 iTunes 欄位缺席不影響播放;但如果你照文件期待拿它做需要 iTunes 欄位的應用,會直接落空。

和 audiobookshelf 是同一種需求的兩端

要判斷它值不值得裝,最快的座標是拿 audiobookshelf 對比。同樣自架音訊庫,audiobookshelf 在 GitHub 上有一萬四千多顆星、GPL 授權、到我查證當天前一天還有程式更新;Folder2Podcast 一百七十八顆星、沒有授權條款、主線功能程式碼停在 2025 年 2 月。數字差距就是定位差距。

audiobookshelf 是平台:有自己的手機 App、掃描時自動抓 metadata、轉檔、多使用者帳號。Folder2Podcast 整個原始碼樹只有四十多個檔案,核心邏輯集中在幾支 TypeScript 檔,本質是把資料夾對應到 RSS 的翻譯層,其餘全交給別人。前者的部署與維護成本換來完整功能,後者五分鐘上線但功能邊界就是 RSS 規範本身。你的收藏如果需要 App 播放介面與豐富 metadata,走 audiobookshelf,或參考我們寫過的 qm-music-server 自架音樂伺服器;如果只是要把一批有聲書塞進既有的播客 App,這個小工具反而更輕。

兩年的時間線,與一條看不見原始碼的 v2

評估自架工具離不開看它的呼吸狀態。專案 2025 年 1 月底開張,兩週內衝了十個版號到 v0.1.9,主線最後一次功能性提交在 2025 年 2 月,其後只剩文件修補。Docker Hub 的下載次數七千三百多次,規模是社群工具等級。

不過開發並沒有停下來,只是搬了地方。倉庫上有一條公開的 dev-v2 分支,提交一路排到 2026 年 1 月,Docker Hub 的 dev 映像跟著同步更新。從分支原始碼看,v2 是一次大改版:介面換成 Next.js,加上資料庫、檔案上傳、影片連結下載,連接埠也換了,前面提過的帳號機制就在這條線上,倉庫描述寫的「上傳本地音訊、貼上影片連結自動生成 RSS」對應的也是它。真正的落差在版本策略:這條線從未合回主線,也沒有發佈過 stable 映像,README 教人部署的 stable 標籤停在 2025 年 2 月的舊架構,描述裡的新功能全在還沒正式發佈的開發線上,兩版之間沒有任何過渡說明。你照文件裝,拿到的是堪用且我實測正常的舊架構;期待新功能,只能等作者哪天把 v2 扶正,而這件事沒有承諾也沒有時間表。

開發者帳號本身是活的,這幾個月還在推其他專案,folder2podcast 比較像是被他放到穩定維護、主力轉向商業版的狀態。另外唯一開著的 issue 是 2025 年 2 月留下的版號顯示建議,開的人是作者自己,等於一張沒兌現的待辦,與「穩定但低維護」的整體訊號一致。

授權部分要單獨講:倉庫裡找不到授權條款檔,GitHub 顯示授權狀態 None,只有套件定義檔裡一行 ISC 的宣告,嚴格說構不成完整授權。原始碼公開不等於開放授權,法律上預設保留所有權利。自己下載、自己跑,風險很低;要改作、散佈或整合進商業服務,嚴格說需要先向作者取得授權,這和 immich 自架相簿那類正式開源專案是不同的法律處境。

三種人三種答案

自己聽、家人一起聽、資料都在區網裡的人,這工具給的價值很實在,五分鐘把死檔案變成活的訂閱源,續播和進度從此交給播客 App 傷腦筋。若你的音訊來自 yt-dlp 這類下載工具,下來就照集數命名,這條路幾乎不用額外加工。

想在外面透過公網聽、又不會架反向代理的人,先停一下,它的無認證設計讓這個情境變成安全洞,用 VPN 或改用 audiobookshelf 這類有帳號機制的方案才是對的。需要豐富 metadata、App 播放介面、轉檔的人也一樣,直接往平台級方案走。

最後一種是把它當長期基礎設施的人。功能凍結對 RSS 這種穩定規範不算致命傷,我實測的一切也都正常,但 stable 版停留在舊架構、新功能全押在未發佈的 v2 開發線上,依賴它的同時最好把這個斷層算進風險。工具本身做得簡單而誠實,連它沒做的部分都一目了然,這在小型自架專案裡已經是中上水準,用對場景它就是好工具。

Sliven 褚崇名
Sliven 褚崇名

每日分享科技新知、免費資源以及 WordPress、虛擬主機相關主題,任何問題歡迎在科技月球下方留言,或是發送 Email 至 [email protected] 與我聯繫。

文章: 1330

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *


Share to...