前言
云音乐
相信大家都有过这样的经历,在各个音乐平台之间挣扎的经历。常常是平台 A 下架了某一首歌,平台 B 依靠独占吸收来自平台 A 的难民。
这种现象很普遍,倒不如说在版权时代这是再正常不过的风景了。没有版权就不能上架,天经地义的事情。平台不过是希望吸引流量又不希望承担流量带来的法律风险罢了,否则一开始又为何要上架呢?
罢了,下载到本地听吧。
本地音乐
本地音乐的来源就非常广泛了。
一是从各大云音乐平台上下载得到的音频。其是否存在加密并不是什么麻烦的事情,因为音乐终究是要播放的,最不济你从操作系统层面把送给音频 API 的每一个采样都截下来不就拿到 wav
了吗(笑)
二是各大公网音乐包,比如 [Nemuri]
的合集。这种合集的特点是音乐全,格式整齐,通常以统一的格式进行分享(如 FLAC
、MP3
等)。
有了音频文件,再配上音乐播放器就可以和云音乐一样听歌了。本地音乐的一大缺点在于更新,没有了云音乐运营,一切的一切都要靠你自己了。
当然了,这并不是本地音乐唯一的问题。本地音乐最大的问题恰恰在于本地。且不论本地音乐增多后多端的存储成本,单单是音乐的同步就够你喝一壶的。就以基本的 PC
、手机、平板三端同步为例,你需要在这三台设备之间反复横跳——一旦某一台设备没有同步,而你恰巧想要听某一张新发售的专辑——噩梦就降临了。
于是,我们发现,云音乐恰巧是为了解决这些痛点而存在的。那我们要回到云音乐平台吗?
自建音乐平台
既希望得到本地音乐的便利,又想要云音乐的便利,这就是自建音乐平台的意义所在了。
最常见的自建音乐平台应该就是 Airsonic
了,这是一个用 Java
写成,基于 Subsonic
的最后一个开源版本 fork
而出的项目,而 Subsonic
则是著名的自建音乐平台。
Airsonic
的存在已经能够满足很多人的需求了,但它还是无法完全满足我当前的需求,原因有以下几点:
Airsonic
的编写语言是Java
,其需要的资源对小型VPS
而言还是太多了。以我目前运行的Docker
为例,没有任何人使用的情况下,基本内存占用维持在800MiB
左右。- 自建音乐平台需要服务器有较大的硬盘空间,而这一点面对只会增多不会减少的音乐资源难以永久实现。目前我整理的音乐资源有
185GiB
,这对于我部署Airsonic
的20G
小鸡肯定是不现实的。 - 为了应对硬盘问题,我使用了
rclone
挂载。但在音乐更新后,Airsonic
需要花费大量的时间(小时级别)重新扫描整个音频目录,读取每一个音频文件的元数据。这么长的时间差完全不能满足「马上就听」的需求。 Airsonic
支持部分更新,根据目录和文件的lastModifiedTime
决定是否扫描。但Google Drive
对于这部分的实现并不遵循一般文件系统的行为,对内层文件的更新并不会导致外层目录的修改时间变化,宣告了Airsonic
的这个特性完全无法使用。Airsonic
诸如音乐上传、下载、last.fm
的功能是我不需要的,而且没有办法完全关闭。使用这些功能可能会导致未定义行为(如上传到rclone
挂载的目录可能会打乱原本的目录结构)。尽管我可以不使用这些功能,但我不能要求和我一起整理资源,共用平台的朋友永远都不使用这些功能。- 更新缓慢,
Bug
丛生。尽管有另一个fork
但问题还是很多,且对我而言维护起来过于困难。(更新:现在只有这个fork
在积极维护,原仓库已经Archive
了)
当然了,多多少少还有一些没有列出来的问题。由于这些问题的存在,最终促使我决定在另一个意义上自建音乐平台:自己造轮子。
我们需要什么?
在 Project Anni
发起半年后,我再次回头来看这个问题:我们需要什么?
从听音乐的角度来看,最基础的需求是我们需要听音乐,在此之上的是我们需要听高音质的音乐。更进一步,我们希望在不同的时间根据不同的需求听不同种类的音乐。
从信息的角度来看,最基础的是我们需要知道音乐的标题,然后是艺术家、封面等其他信息。更进一步,我们希望在专辑、艺术家等信息之间快速跳转。最重要的是,我们不希望信息消失(点名批评网易云音乐)。
理想的 Anni 部署结构
Anni
在设计之初是针对资源所有者的。理想的部署模型是资源所有者通过 Annil
共享自己已有的资源,用户通过添加所有者的 Annil
地址以访问资源所有者共享的资源。资源所有者通过凭据限制可以访问资源者的身份、人数等。
但随着资源整理的深入,Annil
对个人呈中心化的趋势已日趋明显。Anni
逐渐成为我个人从各资源平台获取音乐资源、整理后收听的唯一途径。在这种变化下,各设备只与一个 Annil
通信并获取音频已成为目前我个人使用中的事实架构。
与辅种的矛盾?
Anni
的设计目标是没有考虑到辅种的,或者换句话说,Anni
的目标是将通过 Anni
整理后的音频作为做种的目标。Anni
整理后的音频资源包含相对统一的音频标签和内嵌的封面,并以统一的文件名和格式呈现。Anni
本身也提供了对标签等信息进行检查的能力,确保结果音频在元数据这一层面的质量相对较高。
这样的设计考量意味着如果你希望在使用 Anni
的同时保种,那你就需要在硬盘上存放两份音频数据。不过一般音频相比其他资源而言占用空间还是较小的,我整理到现在近 1000
张单曲/专辑的空间占用也只有 300GiB
不到(不含 Hi-res
),个人认为空间问题还是相对较小的。
当然了,如果你整理的目标是 TLMC
这样的巨型合集,那就是另一个故事了(93661
个项目,总计 1.7 TiB
,笑)。
与 Airsonic
对比
和 Airsonic
相比,Anni
的优势如下:
- 扫描速度快。对于以
Google Drive
形式存储在远端的370G
音频资源,Annil
目前的扫描只需要20
秒左右就可以完成。相比之下,在资源只有不到100G
的时候,Airsonic
就需要扫描两小时以上了。这是因为Anni
的扫描只需要扫描目录结构,并以专辑为单位定位其所在的目录,并不需要实际读取音频。如果将所有专辑都存放于单一目录下,或使用严格目录格式的情况下,扫描的时间理论上可以基本无视。 - 元数据更新方便。由于元数据和音频文件脱钩,因此修改元数据只需要对元数据仓库中的文本进行修改即可。将错误修正写回音频文件的过程并不是必须的(尽管一般个人因为强迫症会选择写回)。
- 歌单等附加性功能(
Anniv
)与音频分发(Annil
)脱钩。这意味着如果我们不需要多余的功能,只想专心听歌,完全可以只使用Annil
。而分拆出来的Anniv
则可以放手实现各种附加功能。并且数据还可以跨Annil
存在。 - 资源占用小。个人实际使用下
Annil
的待机内存占用在15MiB
以内,客户端访问时(在不转码的情况下,转码调用的是ffmpeg
)也不超过20MiB
。考虑到Annil
目前的部署场景基本是Self Host
,这样的资源消耗对我个人而言可以说是完美的。
而目前的劣势也很明显:
- 需要手动整理。这是
Anni
存在的基石,Anni
只会提供更便于整理资源的工具,一定不会跳过资源整理这一步。 - 对收藏、歌单等功能的支持还没有开始。
Anniv
的协议还没有完全敲定,也没有可用的前端、后端、客户端。目前的客户端是实现了Airsonic
协议后借用的DSub
等Airsonic
客户端,只是还算能用的状态。