从 YouTube Music 到 Navidrome:我搭建了一套属于自己的音乐库
免责声明:本文涉及的技术方案(如使用 yt-dlp 等工具下载网络资源)仅用于个人学习、技术研究与个人音源备份,请勿用于任何商业用途或侵权传播。尊重版权,请支持正版音乐。
这次折腾下来,我最大的收获反而不是记住了多少命令,而是逐渐理解了一件事情:一个真正好用的个人音乐库,不应该追求绝对完美,而应该追求能够长期、低成本地维护。
为什么要搭建
最开始的原因其实特别直接:我不想再一直为各个平台的音乐会员买单了。
能少花钱,同时又能通过折腾这套系统提升自己解决问题的能力,何乐而不为呢?而且我之前用 Apple Music 养成了按“专辑”听歌的习惯,很喜欢从一张完整专辑里去发现歌曲,而不是听算法推荐的单曲碎片。
但这又带来了一个很棘手的问题:去哪里找一个靠谱、稳定、内容又全的音源?
我实际使用下来发现,不同音乐平台的覆盖完全不同。比如 HiFiTi 的资源确实很丰富,很多经典 CD Rip 和无损都能找到,但它依赖社区用户上传,并不是“要啥有啥”。
比如之前看韩剧《机智医生生活》,我超级超级喜欢里面的一首插曲 《Let’s Forget It》。曹政奭那种带着一丝沧桑感的嗓音一出来,一下子就把我拉回到了金隽婠和李翼纯那段让人上头的甜蜜爱情里。当时我兴冲冲地跑去 HiFiTi 搜,结果整部剧的 OST 居然一首都没有!而且这种第三方分享网站随时可能关停跑路,作为长期依靠的音源,心里终究不踏实。

那种急着想听歌却找不到资源(而且是长期稳定资源)的挫败感,让我开始寻找替代方案——最后发现 YouTube Music 上几乎一应俱全。
这是当时在 YouTube Music 上搜索《机智医生生活OST》,得到的一系列结果:

点进插曲 《Let’s Forget It》的详细界面,发现音乐的 Title、Artist、Album、Release Date 等元数据非常完整,甚至还附带封面缩略图。

这让我意识到:YouTube Music 是一个很不错的长期音源储备。
我不再执着于找到一个“万能”的音乐网站,而是开始搭一个可以接受不同来源的音乐库,最后统一交给 Navidrome 来播放。我需要的不是另一个“音乐商店”,而是一个能把自己的音乐文件变成个人流媒体服务的服务器。
除了 YouTube Music ,我的 Navidrome 音乐库还可以接受不同的音乐来源:

怎么搭建系统
搭着搭着,我发现一个很重要的原则:不要找一个软件包办所有事情,而是让每个工具只负责它最擅长的事情。
整体架构
先看整套系统的运作流程:
yt-dlp 负责下载音频 -> beets 负责匹配 MusicBrainz 整理、刮削封面、拉取同步歌词 -> 下载整理好的歌曲文件存入 Navidrome 音乐文件夹 -> 客户端通过 IPv6 / tailscale 访问服务器对应端口,进行播放音乐

真正让我觉得这套架构舒服的地方,是每个环节可以独立出问题、独立排查。
前置准备:工具依赖与代理配置
在正式开始搭建前,先说明一下整套系统依赖的核心工具与安装方法。
强烈推荐使用 Windows 官方包管理器 winget,Win11 自带,Win10 不一定自带,但可以很方便地装上。它可以极大地简化命令行工具的安装与更新,避免手忙脚乱地去官网寻找解压包和手动配置环境变量。如果你的系统还没有安装 winget,可以通过 Microsoft Store 快速获取,非常值得安装的一款小工具!
通过 winget,只需在终端中运行以下命令即可完成 yt-dlp 的安装:
1 | winget install yt-dlp |
关于 FFmpeg 的说明:
yt-dlp进行音频提取和转码需要依赖FFmpeg。好消息是,通过winget install yt-dlp安装时,会自动将ffmpeg、ffprobe等依赖一并安装到位,无需额外操作。
此外,整套方案还有一个很现实的前提:网络必须能够正常访问 YouTube。 整个获取链路的入口是 yt-dlp,如果 YouTube 无法访问,后面的 beets、MusicBrainz、封面和歌词都没有意义。
我在 Windows 上使用 v2rayN,本地代理监听在 10808 端口(注意:不同软件的端口不同,需按实际情况修改)。为了避免每次下载都输入代理参数,首先找到 yt-dlp 的 config 文件的位置:
在文件资源管理器输入以下命令:
1 | %APPDATA%\yt-dlp\ |
把代理写进了配置文件:
1 | --proxy http://127.0.0.1:10808 |
这样以后只需要运行:
1 | yt-dlp "YouTube Music URL" |
就可以自动走代理下载。
关于代理的问题,在这里不多说。
yt-dlp:负责“拿到音乐”
我常用的下载命令:
1 | yt-dlp ` |
这里有一个很有价值的技巧:--no-playlist 参数控制的是只下载当前单曲。如果想下载整个歌单或专辑,只要去掉 --no-playlist 即可。
单曲适合“发现一首收一首”,Playlist 和专辑适合“发现一个喜欢的集合,就一次性处理”。把 Playlist 的 URL 交给 yt-dlp,原本需要重复几十次的操作就变成了一次批处理。
日志输出示例:
1 | [info] Downloading 1 format(s): 251 |
beets:负责“整理与补全”的核心大脑
beets 是整个系统里最像“大脑”的部分。需要说明的是,fetchart 和 lyrics 并不是独立的第三方软件,它们全都是 beets 官方自带的强大扩展插件。
安装与验证
在 Windows 环境下,通过 pip 安装 beets:
1 | python -m pip install --user beets |
安装完成后,在 PowerShell 中验证版本及默认插件加载情况:
1 | PS C:\Users\DEL> beet version |
beets 的数据库文件位于 %APPDATA%\beets\library.db
配置文件位于 %APPDATA%\beets\config.yaml
需要注意的是:数据库本身体积非常小,因为它保存的是索引和音轨元数据,而不是复制一份真实的音频文件。
我的 beets 配置
需要注意的是,首次安装 beets 后,配置文件默认并不存在。如果文件夹或文件不存在,可以通过以下 PowerShell 命令安全创建(若文件已存在则跳过,不会覆盖现有配置):
1 | if (-not (Test-Path "$env:APPDATA\beets\config.yaml")) { New-Item -ItemType File -Path "$env:APPDATA\beets\config.yaml" -Force } |
下面是我整理后的 config.yaml 配置文件:
1 | # ========================= |
插件协同
在配置 config.yaml 开启插件后,beets 便化身为全能中枢:
-
MusicBrainz(辅助识别):
负责识别歌曲、确认身份与标准命名。最开始我以为:MusicBrainz 既然是权威数据库,那匹配之后照着改就行了。后来发现完全不是这样。
例如 YouTube Music 上的艺人名和歌名是英文Mido and Falasol - Me to You, You to Me,而 MusicBrainz 匹配到的全韩文미도와 파라솔 - 너에게 난, 나에게 넌,虽然是同一首歌,但全韩文不符合我的检索习惯;又比如戴佩妮与 GAI 周延合唱的《明知山》,MusicBrainz 甚至可能给出一个完全不相关的候选。所以我形成了自己的原则:MusicBrainz 负责确认“这是什么”,但不一定决定“我应该怎么显示它”。
- 匹配明显正确:执行
Apply - 明显错误或语言不合习惯:执行
Use as-is(保持原样) - 模棱两可:人工看一眼对比结果再决定
- 匹配明显正确:执行
-
fetchart&embedart插件(正方形封面补全与嵌入):
自动解决封面问题。yt-dlp 抓到的 YouTube 封面往往是 16:9 的视频缩略图,在播放器里很不美观。fetchart会根据 Artist / Album 自动拉取高质量正方形专辑封面,并由embedart嵌进音频文件本身中。
YouTube 封面和 fetchart 封面的对比:
lyrics插件(同步歌词拉取):
自动通过 lrclib、lrcmux 等源拉取 LRC 同步歌词并嵌入绑定。
一次单曲导入流程
当通过 yt-dlp 下载好一首单曲后,我们运行 beet import 命令将其打标并归档。下面是一次真实的终端导入演示:
一般通过以下命令导入单曲:
1 | beet import "音乐文件所在的路径" |
1 | PS C:\Users\DEL> beet import "E:\yt-dlp\伍佰 & China Blue - 挪威的森林.opus" |
在这段交互中,非常形象地展示了 beets 工作的细节与坑点应对:
- 匹配机制与报警:
beets默认会优先把单曲当成完整专辑来匹配,因此列出了候选专辑并警报Missing tracks (9/10 - 90.0%)。 - 正确应对选项:面对提示
➜ Apply, More candidates, Skip, Use as-is, as Tracks...时,按下键盘a(即Apply),确认接受当前专辑匹配结果,选定后,beets就会以此为准抓取封面歌词并归入专辑档案,自动整理归档入E:\\\\navidrome\\\\music正式音乐库:

不过这里的选择因人而异:如果你本来就想按专辑归档管理,选 Apply 没问题;但如果你只是想收藏某一首单曲(甚至这首歌压根没有所属专辑),那更合适的做法是在导入时加上 -s 参数,直接以单曲模式导入,跳过专辑匹配。具体用法见后文「补充技巧-单曲的识别」部分。
专辑导入流程补充
专辑可以从 YouTube music 下载获取,也可以从其他渠道获得,在这里以网络上下载的 wav 未分轨的专辑文件为例子说明:
首先,需要下载一个分轨工具:CUETools
至于你问为什么要分轨,因为如果不分轨,一张专辑都在一个 wav 里面,beets是无法识别的。

进入 CUETools 进行整轨文件的分轨:

可以看到,歌曲分轨成功,就是这么舒服,就是这么简单!

剩下的导入流程和单曲导入大差不差,唯一的差别在于 beet import 后面接的路径需要是整张专辑歌曲所在的路径,注意一下这一点应该就没有什么问题了。
Navidrome:负责向外展示音乐
Navidrome 的部署可以在 Windows 本地部署,也可以通过 Docker 部署(适合后续迁移)。在 Windows 下,Navidrome 原生支持通过自身命令将其注册为 Windows 后台服务(Navidrome.exe service execute 机制),实现开机自启与后台无感运行。
修改主文件库配置时没有图形交互界面,需要直接修改安装路径下的 ini 配置文件。
首先进入安装目录:
1 | C:\Program Files\Navidrome |
打开 navidrome.ini,通过更改 MusicFolder 的值来指定 Navidrome 的主音乐文件库:
1 | [default] |
更改完成后,通过 PowerShell 以管理员权限重启 Navidrome 服务即可生效:
1 | Restart-Service Navidrome |
或者使用服务控制命令:
1 | net stop Navidrome |
日常管理小技巧:
- 查看服务状态:
Get-Service Navidrome- 实时查看运行日志:
Get-Content "C:\ProgramData\Navidrome\navidrome.log" -Wait
重启服务后,进入后台管理界面 http://localhost:4533,观察音乐库路径更改是否生效:

客户端访问
关于客户端的选用,可以看看官方的网站:Client Apps | Navidrome。
我日常使用的比较多的是 iOS 端的 Amcfy Music,选择它的理由有很多,比如有实时的息屏歌词显示,而且有小组件可以使用,缺点是界面和 Apple Music 相比不够苹果化,界面没有那么圆润流畅。
服务搭建好后,在局域网内直接访问 服务端 IP 就能听歌。但如果出门在外,手机客户端该如何连接后端的 Navidrome 呢?
目前我有两种成熟的连接策略:
- IPv6 直连访问:如果家里宽带带有公网 IPv6 地址,配置好 DDNS 动态域名解析与端口转发,就能以极低延迟直连家里的音乐服务。具体配置流程可以参考我之前写的这篇文章:基于原生 IPv6 与 NPM 实现 Win11 本地服务低延迟公网暴露 。
- Tailscale 虚拟局域网:如果不具备公网 IP 环境,或者不想将服务暴露在公网上,直接开启 Tailscale 构建异地虚拟局域网是最方便的安全选择。详细走通与实战流程可以看我的另一篇记录:老毛子 Padavan 路由器 IPv6 踩坑全记录与 Tailscale 直连优化,这种方法延迟可能会比较大,但是如果客户端和服务器连接在同一个网络的情况下,通常可以做到 direct 直连。
长期维护
有人可能会问:Navidrome 本身或其它第三方客户端难道不能直接自动刮削封面和歌词吗?为什么还要费劲用 beets 在本地处理好再导入?
对我来说,这种数据与服务解耦的做法有着极其关键的意义:
- 封面与歌词质量完全可控:不用忍受服务端刮削错乱的低清图片或匹配失败的歌词;
- 元数据直接写入音频文件,随时自由迁移:所有 Metadata、封面和 LRC 歌词全部内嵌在文件本身中。以后哪怕我换掉 Navidrome,换成 Plex、Jellyfin 甚至移动到云端,所有音乐数据依然完好无损,绝不会因为服务器迁移而丢失;

- “前置自动化整理”的架构:我将“下载区”与“正式库”彻底分离:
- 下载区:
E:\yt-dlp(临时暂存) - 正式库:
E:\navidrome\music(只存放经过 beets 筛选整理后的音乐)
- 下载区:
这样的架构让各个模块彻底解耦:换下载工具不影响 Navidrome,换歌词/封面插件不影响音频存储,换播放服务器不影响数据资产。
补充技巧
-
下载报
HTTP Error 403: Forbidden异常的避坑经验:
如果运行yt-dlp下载时提示 403 错误或被 YouTube 识别为“未登录机器人流量拦截”,往往是因为手动下载的可执行文件版本过老,触发了 YouTube 接口的风控机制。此时最快最省心的解决方法不是繁琐地配置 Cookie,而是直接使用包管理器重新覆盖安装最新版:1
winget install yt-dlp --force
或者通过
yt-dlp -U强制更新到最新版本,问题即可迎刃而解! -
如果想强制重新获取某一首歌的歌词,可以执行:
1 | beet lyrics -f title:"月亮" |
对于已有同步歌词的歌曲,提示 Keeping synced lyrics 说明原有的歌词已被妥善保留。
- 单曲的识别,只需要在命令里加一个小小的
-s参数:
1 | beet import -s "E:\test\2015-清风徐来[单曲]" |
这是为了明确告诉 beets:“这是一首独立的单曲,不需要去找整张专辑”。
未来展望
目前这套系统保存在我Windows 11 系统的笔记本电脑里,受限于内网环境,虽然有 IPv6 和 Tailscale 加持,但面对一些复杂网络场景(比如网络波动或主机未开机),依然无法做到 100% 随时随地瞬间响应。
因此,我后续有两个很明确的升级和探索小计划:
-
迁移至公网云服务器:购买一个带有固定公网 IP 的云服务器,直接把 Navidrome 部署到云端。得益于此前做好的“数据解耦”设计,到时候我只需要把
E:\navidrome\music整个音乐库打包同步上传,就能以极低的成本完成迁移,实现毫无顾虑的“随时随地无缝听歌”; -
开发并开源“一键自动化导入工具”:我打算把整套流程封装,作为我在 GitHub 上的第一个个人项目发布!它将提供傻瓜式环境一键配置(自动处理代理环境),用户只需要输入歌名或 URL,系统便全自动完成获取、匹配、正方形封面刮削、歌词拉取并写入目录。
工具的流程图如下:

我最终想建立的,不是一个“全世界最高音质”的展示柜,而是一套属于我自己、可以不断积累、可以随时访问、可以逐步升级,而且多年以后依然愿意维护的音乐系统。