工作流程

折騰數位音樂:從 Plex 到自建標籤管理系統

從全端開發角度折騰數位音樂——分享我如何用 Navidrome 搭配自建標籤管理系統,打造一套完整的數位音樂伺服器


音樂
自架服務
Docker
發佈於 2026年4月7日
折騰數位音樂:從 Plex 到自建標籤管理系統

我有買數位音樂的習慣,主要從 mora 這類平台購入 Hi-Res 音源。串流平台很方便,但如果你跟我一樣會買歌、會在意音質跟標籤資訊的完整性,那管理這些音樂檔案就會是一個持續要面對的問題。這幾年我從 Plex 開始,經過 Jellyfin,最後落腳在 Navidrome,再加上自己寫了一套標籤管理系統,才算把整個流程理順。這篇就來聊聊這條折騰之路,跟我最後的架構長什麼樣子。

從 Plex 到 Navidrome 的選擇過程

Plex:入門最簡單的選擇

Plex

一開始我用的是 Plex。它的優點很明確——直接下載安裝檔就能跑,不用折騰網路結構就有基本的音樂管理服務,內建的遠端存取功能讓你在外面也能連回家聯歌。如果你想要更進階的功能,繳訂閱費就可以了。對剛開始想自架音樂服務的人來說,Plex 的門檻是最低的。

Plex 有推出專門的音樂播放器 Plexamp,介面跟播放體驗的優化都做得不錯。但底層的問題還是在:標籤掃描速度偏慢,想自定義修改標籤欄位也很麻煩。

如果選擇 Plex,推薦搭配 Plexamp 作為播放器,音樂體驗比 Plex 本體好很多。

Jellyfin:功能很多,但有些問題

Jellyfin

後來我跳到 Jellyfin,一個完全開源的替代方案。它的功能同樣很豐富,也支援直接用安裝檔部署,可以設定多個資料夾來管理不同的音樂資料庫。我用了蠻長一段時間,整體來說堪用。

但讓我最後決定離開的原因是標籤掃描的問題。Jellyfin 的掃描速度真的太慢了,而且會有一些很惱人的快取問題——有些我明明已經更新過的標籤,Jellyfin 那邊就是讀不到新的,像是被快取住了一樣。對於一個很在意標籤正確性的人來說,這個問題很致命。

如果選擇 Jellyfin,推薦搭配 Finamp 作為手機端播放器,體驗比 Jellyfin 內建的好上不少。
Navidrome

最後我選擇了 Navidrome。跟前兩者比起來,它有幾個明確的取捨:

  • 只做音樂:沒有影片功能,但音樂的體驗做得更專注
  • 推薦用 Docker 部署:新版也提供 .msi 安裝檔跟獨立執行檔,但 Docker 在管理上還是最方便的
  • 標籤掃描快又準:這是我跳過來的最大理由,更新完標籤後幾乎是即時反映,不會有 Jellyfin 那種快取卡住的狀況

至於遠端存取的部分,我用 Cloudflare Tunnel 來做網路穿透,所以 Plex 內建遠端連線的優勢對我來說已經不重要了。

Navidrome 有一個限制是只支援單一音樂資料夾,如果想分開管理不同類型的音樂,可以跑多個 instance 搭配 Nginx 反向代理。詳細的配置方式我放在文末。

Navidrome 的新版本已經開始支援多資料庫功能,未來不需要跑多個 instance 也能分開管理不同類型的音樂了。
Navidrome 部署方式

三者比較

PlexJellyfinNavidrome
安裝方式安裝檔直接執行安裝檔直接執行Docker / 安裝檔 / 執行檔
功能定位多媒體平台多媒體平台純音樂串流
多資料夾支援支援支援單一資料夾
標籤掃描即時性普通慢,有快取問題快速且即時
遠端存取內建需自行處理需自行處理
費用免費 / 訂閱制完全免費完全免費

為什麼要自建標籤管理系統

Navidrome 解決了音樂串流跟播放的問題,但標籤管理這一塊還是得靠其他工具。市面上有 MusicBrainz Picard、Mp3tag 這類桌面軟體,功能都很完整,尤其 Mp3tag 在批量處理標籤的效率很高。但有兩個對我來說比較根本的問題:

第一,它們都是桌面應用程式

我的音樂檔案放在那台 Windows 小主機上,每次要改標籤就得遠端連線進去操作。Windows 遠端桌面的體驗本來就不算好,加上我日常用的是 Mac,來回切換很不順手。

第二,像曲風、語言這類欄位,每次都要手動輸入。這些欄位的值其實是有限的,但在 Mp3tag 裡你只能一個一個打字填,沒辦法用選單快速選取。自建系統的話,這些欄位可以做成下拉選單,選一下就好,效率差很多。

我想要的是一套透過瀏覽器就能操作的 Web 介面,在任何裝置上打開網頁就能管理標籤,同時把重複性高的欄位用選單簡化操作。另外,自建系統還有一個好處是可以串接 AI 來整理歌詞文本跟翻譯,這在現成的桌面工具上是做不到的。

找了一圈發現沒有現成的工具能滿足這個需求,所以我自己建了一套。

新音樂入庫流程

這是整套系統最核心的功能。買完音樂之後,從上傳到歸檔的完整流程大致是這樣的:

1. 上傳與轉檔

透過 Web 介面把音樂檔案拉上去。如果上傳的是 M4A 格式,系統會自動用 FFmpeg 轉成 FLAC。

2. 提取標籤與音量標準化

系統會用 Mutagen 讀取檔案裡現有的標籤資訊,同時用 r128gain 計算 ReplayGain 數值,確保不同來源的音樂在播放時音量是一致的。

3. 編輯標籤

進入標籤編輯介面,可以調整歌名、歌手、專輯、曲風等所有欄位。這一步是整個流程裡最需要人工介入的地方,因為不同來源買到的音樂,標籤的完整度跟格式都不太一樣。

4. 檢查歌手資料夾

系統會檢查目標歌手的資料夾是否已經存在。如果是新歌手,會自動建立資料夾,也可以在這一步上傳歌手的封面圖片。

5. 歸檔

按下確認後,系統會根據標籤資訊自動把檔案搬到對應的位置,遵照 歌手/專輯/曲目.flac 的標準資料夾結構。專輯封面也會從音樂檔案中自動提取出來放到專輯資料夾裡。整個過程跑完之後,Navidrome 那邊很快就會掃描到新檔案。

兩個實用的附加功能

標籤管理之外,這套系統還有兩個我覺得很實用的功能。

拼音排序

很多播放器在排序中文歌曲時會出問題,因為中文字沒有天然的字母順序。我的系統會用 pypinyin 把中文標題轉換成拼音,寫入標籤的 SortedTitle 欄位。這樣在支援這個欄位的播放器上,中文歌曲就能按照拼音正確排序了。

智慧播放清單

可以透過語言、曲風、收藏狀態等條件做複合篩選,自動產生播放清單。設定好條件之後,系統會輸出 M3U 格式的播放清單檔案,大部分的播放器都能直接讀取。

這兩個功能解決的是「檔案整理好之後,怎麼更方便地找到想聽的歌」這個問題。入庫流程處理的是音樂進來的那一刻,而排序跟播放清單處理的是之後每一次想聽歌的時候。

備份架構

我的音樂伺服器跑在一台 Windows 小主機上。音樂檔案存在本機,透過 rclone 腳本備份到 Google Drive。需要備份的時候執行一下腳本就會自動把音樂資料夾同步上去,算是一個簡單但夠用的備份方案。

整體架構

最後整理一下整套系統的全貌。所有服務都跑在同一台 Windows 小主機上,透過 Docker Compose 管理:

  • Navidrome:負責音樂串流跟播放
  • Music Manager:負責標籤管理跟入庫流程(React + FastAPI + PostgreSQL + Redis)
  • Cloudflare Tunnel:負責網路穿透,讓外部裝置可以連進來
  • rclone:負責備份,把音樂檔案同步到 Google Drive

播放器推薦

伺服器跟標籤管理都搞定之後,最後一塊拼圖就是播放器。Navidrome 本身有內建的 Web 介面可以直接聽歌,但如果你想要更好的播放體驗,可以搭配專門的播放器使用。Navidrome 支援 Subsonic API,所以市面上大部分相容 Subsonic 的播放器都能連上。

我目前在用的組合是:

Android:Symfonium

Symfonium,介面乾淨、支援離線下載,是我用過體驗最好的手機端播放器。

Symfonium

Symfonium 播放器介面

Android / iOS:音流

免費 音流,用 Flutter 開發的跨平台播放器,同時支援 Android 跟 iOS。介面風格偏向 QQ 音樂那種路線,支援 Subsonic / Navidrome API。

音流

音流播放器介面

iOS:Arpeggi

免費 Arpeggi,目前是 iOS 上體驗最好的 Subsonic 播放器。透過 TestFlight 提供,支援 CarPlay、離線緩存穩定,UI 走 iOS 原生風格,操作起來很順手。

Arpeggi

Arpeggi 播放器介面

iOS:Manet

免費增值 Manet,介面走 iOS 原生風格,免費版功能就很完整。

Manet

Manet 播放器介面

iOS:LMP - Music Hub

LMP - Music Hub,支援 Navidrome、Emby、WebDAV、SMB 等多種後端。它最特別的地方是可以同時呈現多個資料庫的內容——假設你有 A、B、C 三個音樂庫,設定好之後不用切換就能一次看到全部,搜尋也是跨庫一起搜,這對我這種分開管理多個音樂庫的人來說超級方便。

另外針對大量曲庫做了效能優化,就算匯入 7 萬首以上的歌也能跑得順,CarPlay 跟 Siri 整合也做得很完整,支援 LRC 跟逐字歌詞。比較可惜的是只支援線上串流播放,沒有離線快取功能,所以出門在外網路不穩的時候會比較吃虧。

LMP - Music Hub

LMP - Music Hub 播放器介面

電腦:Feishin

免費 Feishin,開源的桌面播放器,支援 Navidrome 跟 Jellyfin,可以用 Docker 部署成 Web 版,在任何裝置的瀏覽器上使用。

Feishin

Feishin 播放器介面

從最早用 Plex 單純聽歌,到現在有一整套自動化的入庫跟管理流程,中間確實折騰了不少。但現在買完一首歌,打開瀏覽器上傳、調整標籤、按下確認,幾分鐘就能搞定歸檔,之後在任何裝置上打開播放器就能聽到。對我來說,這套系統最大的價值不是技術上有多複雜,而是它讓「管理音樂」這件事變得不再是一個負擔。

補充:部署方式

新版:單一 instance + 多資料庫

Navidrome 新版已經支援多資料庫功能,只需要起一個 instance,把所有音樂資料夾都掛載進去,然後在 Navidrome 的 UI 介面裡新增資料庫就好。比起舊版要跑多個 instance 加 Nginx,設定簡單很多。

docker-compose.yml

services:
  navidrome:
    image: deluan/navidrome:latest
    user: 1000:1000
    ports:
      - "4533:4533"
    restart: unless-stopped
    environment:
      ND_MUSICFOLDER: "/music/music-a"
      ND_ENABLETRANSCODINGCONFIG: "true"
      ND_LOGLEVEL: "info"
    volumes:
      - "./data:/data"
      - "/path/to/your/music-a:/music/music-a:ro"
      - "/path/to/your/music-b:/music/music-b:ro"
ND_MUSICFOLDER 指定的是啟動時的預設資料庫路徑,其他音樂資料夾(像 music-b)則是啟動之後進入 Navidrome 的管理介面,透過 UI 新增資料庫來加入。每個資料庫獨立管理,播放器那邊也能分開切換。

舊版:多個 instance + Nginx 反向代理

如果你的 Navidrome 版本還沒支援多資料庫,或是你有其他原因需要完全隔離不同的音樂庫,可以跑多個 instance,再用 Nginx 做反向代理,透過不同的路徑來區分。

docker-compose.yml

services:
  nginx:
    image: nginx:alpine
    ports:
      - "8900:80"
    volumes:
      - "./nginx.conf:/etc/nginx/nginx.conf:ro"
    depends_on:
      - navidrome-a
      - navidrome-b
    restart: unless-stopped

  navidrome-a:
    image: deluan/navidrome:latest
    user: 1000:1000
    expose:
      - "4533"
    restart: unless-stopped
    environment:
      ND_BASEURL: "/music-a/"
      ND_ENABLETRANSCODINGCONFIG: "true"
      ND_LOGLEVEL: "info"
    volumes:
      - "./data/a:/data"
      - "/path/to/your/music-a:/music:ro"

  navidrome-b:
    image: deluan/navidrome:latest
    user: 1000:1000
    expose:
      - "4533"
    restart: unless-stopped
    environment:
      ND_BASEURL: "/music-b/"
      ND_ENABLETRANSCODINGCONFIG: "true"
      ND_LOGLEVEL: "info"
    volumes:
      - "./data/b:/data"
      - "/path/to/your/music-b:/music:ro"

nginx.conf

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    upstream navidrome_a {
        server navidrome-a:4533;
    }

    upstream navidrome_b {
        server navidrome-b:4533;
    }

    server {
        listen 80;

        location /music-a/ {
            proxy_pass http://navidrome_a;
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        location /music-b/ {
            proxy_pass http://navidrome_b;
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

共通注意事項

  • user: 1000:1000 要對應到你主機上音樂資料夾的擁有者,權限不對的話 Navidrome 會讀不到檔案
  • 音樂資料夾掛載建議用 :ro(唯讀),Navidrome 只需要讀取權限,不需要寫入你的音樂檔案
  • 如果你有 Last.fm 的 API Key,可以透過環境變數 ND_LASTFM_APIKEYND_LASTFM_SECRET 設定,Navidrome 會自動抓取專輯封面跟歌手資訊
  • 舊版的 ND_BASEURL 是搭配 Nginx 路由用的,新版單一 instance 不需要設定
  • 新版的 ND_MUSICFOLDER 指定預設資料庫路徑,額外的音樂資料夾透過 UI 新增