CoCo Downloader:从几个简单的接口开始

最开始做 CoCo Downloader,并不是因为我有一个特别完整的产品计划。
只是偶然发现,有几个第三方音乐网站的接口比较简单,分析起来并不困难。于是我顺手写了一个小工具,把歌曲搜索出来,再把相关的信息和地址整理到一起。
那时的想法很直接,甚至算不上什么正式项目。能搜到歌,能看到歌名和歌手,基本就已经够用了。
后来这个项目被越来越多的人发现,开始有人提建议,也有人提出新的需求。我原来只接入了几个来源,后来便陆续增加了更多平台。项目也从一个简单的页面,慢慢变成了现在的 CoCo Downloader。
项目地址:
https://github.com/markcxx/coco-downloader
从几个接口开始
不同音乐平台之间,搜索结果和接口返回的数据并不完全一样。
有的接口返回歌曲名、歌手和专辑信息,有的还会提供歌词、音质或播放地址。有些来源可以直接拿到结果,有些则需要再请求一次接口,才能得到实际的播放地址。
刚开始接入的来源不多,所以这些差异还可以直接写在页面逻辑里。但随着平台越来越多,继续这样处理就会变得麻烦:页面需要知道每个来源的参数、字段和异常情况,修改一个地方,也可能影响其他来源。
后来我把这些实现拆到了 provider 层。
现在 Web 端的结构大致是:
页面组件 ↓API Routes ↓统一的数据处理 ↓不同音源的 Provider页面只处理统一格式的歌曲数据,具体某个平台要怎样搜索、怎样解析、怎样获取播放地址,则交给对应的 provider 完成。
目前项目中接入的来源包括爱听、波点音乐、布谷音乐、歌曲宝、歌曲海、煎饼音乐系列、JOOX、米兔音乐、LivePoo、咪咕音乐,以及一些其他第三方 API。
这些来源并不是永久稳定的。接口可能变化,返回字段可能调整,某些来源也可能暂时无法使用。因此 provider 层除了让结构更清晰,也方便在某个来源发生变化时单独修改。
Web 端
Web 端使用 Next.js、React 和 TypeScript 构建。它现在主要围绕搜索结果展开:输入歌曲名或歌手,得到多个来源的结果,然后试听、查看歌词,或者发起下载。
默认页面并不复杂,搜索框放在比较显眼的位置,下面是歌曲列表和播放器。对我来说,这种界面已经足够了。音乐工具不需要在第一屏放太多东西,能尽快找到想找的歌更重要。

Web 端的 API Routes 分别处理不同的事情:
api/search负责搜索;api/url负责获取播放地址;api/download负责下载流程;api/lyric负责歌词;lib/providers/impl保存各个来源的具体实现。
搜索之后,结果会按照统一的数据结构展示出来。不同来源的字段差异被留在 provider 内部处理,页面不需要知道某个来源的接口具体长什么样。

播放器只是为了确认歌曲
CoCo Downloader 里有播放器,但它的目的并不是做一个完整的在线音乐平台。
很多时候,同一首歌会有不同的版本,歌名可能相同,歌手名称也可能相近。只看搜索结果,很难确认自己找到的到底是不是想要的那一首。
所以我加了播放功能。
搜索到歌曲之后,可以先试听一下,确认歌手、版本和音质是否符合预期,再决定要不要下载。播放器的作用就是这么简单:让用户在保存之前,先听一听这首歌是不是自己要找的歌。
播放器目前支持播放、暂停、上一曲、下一曲、进度拖动、音量控制和播放模式切换。底部播放栏会一直保留在页面中,切换搜索结果时,不需要离开当前页面。
歌词页面则是播放器的另一种展开。当需要确认一首歌,或者只是想跟着听的时候,歌词会比一行标题更有用。

下载和音质选择
确认歌曲之后,可以从搜索结果直接发起下载。单首歌曲适合临时保存,多首歌曲则会进入下载任务列表。
批量下载时,任务会集中显示在下载抽屉中,可以看到每一首歌曲的处理状态。这样至少能够知道哪些任务已经完成,哪些还在进行中。

不同来源提供的音质并不一样。大部分逆向接口并不支持自定义音质,有些来源默认会返回较高音质;网易云相关的部分接口则提供了音质选择。
因此项目里的音质选择不是一个对所有来源都适用的统一开关,而是根据具体来源返回的内容决定。能选择什么,最终还是要看对应接口提供了什么。
后来又做了桌面端
Web 端完成之后,我又做了桌面端。
桌面端使用 PyQt5 和 QFluentWidgets 编写,代码放在项目的 desktop 目录中。它没有复用 Web 页面,而是重新实现了一套更接近本地应用的界面。
桌面端包含主窗口、首页、下载页和设置页,也有搜索卡片、播放栏和下载任务卡片。用户可以设置下载目录和文件命名规则,应用配置会保存到用户自己的系统目录中,而不是直接写入安装目录。
桌面端的目录大致分成几层:
desktop/├─ app/view/ # 页面和窗口├─ app/components/ # 播放栏、搜索卡片、下载任务卡片├─ app/services/ # 搜索、播放、下载服务├─ app/services/providers/ # 不同音源├─ app/common/ # 配置、资源、样式和信号└─ app/resource/ # 图片、图标、翻译和样式资源Web 端和桌面端的界面不同,但背后的问题很接近:搜索需要统一,播放需要统一,下载也需要统一,只有不同平台的接口实现各自保留差异。

桌面端还可以继续制作 Windows 安装包或便携版本。相比 Web 端,它需要处理更多窗口、资源和打包相关的问题,不过安装之后直接从桌面打开,也有另一种方便。

项目是怎样慢慢变大的
CoCo Downloader 的很多功能,并不是一开始就计划好的。
最初只是接入几个比较容易分析的第三方网站。后来承蒙站内朋友推广,项目获得了一些关注,也有人希望搜索结果更多,于是我继续增加音源。
音源变多之后,需要重新整理 provider 结构;搜索结果变多之后,需要考虑重复歌曲和不同来源之间的数据差异;加入下载之后,又需要处理下载目录、文件名和任务状态。
播放器也是在这个过程中加进去的。它不负责建立一个完整的播放生态,只是让搜索和下载之间多一个确认步骤。
这些功能单独看都不大,但组合起来之后,项目就不再是一个简单的请求接口页面了。需要处理的事情变多,代码也需要不断整理。
有时候是先把功能做出来,再回头调整结构;有时候是某个来源突然变化,才发现原来的写法不够灵活。开发过程并不总是按照计划推进,更多时候是遇到一个问题,再解决一个问题。
开源之后
项目放到 GitHub 上之后,慢慢有了更多关注。
截至目前,仓库已经有五百多个 Star。对一个从临时想法开始的项目来说,这已经是比较意外的结果。
最初我只是觉得几个接口比较有意思,想把它们组合起来试试看。后来看到有人真的在使用,才开始认真考虑项目的目录结构、部署方式和不同客户端的维护。
一个项目被别人使用之后,很多原本可以忽略的问题都会变得明显。
自己只在本地运行时,某些异常情况可能很难遇到;别人使用时,网络环境、系统版本和操作习惯都不一样。反馈并不一定都是功能需求,有时只是一个很小的错误提示,或者一个不太容易发现的操作问题。
这些反馈让项目慢慢变得比最开始更完整。
使用和部署
Web 端可以直接在本地启动:
npm installnpm run dev也可以使用 Docker 部署:
docker compose up -d桌面端则需要进入 desktop 目录:
cd desktoppip install -r requirements.txtpython CoCo-downloader.py项目的具体说明和更新内容,可以在 GitHub 仓库中查看:
关于第三方接口
CoCo Downloader 不存储音乐文件。
项目中的歌曲信息、歌词、播放地址和下载地址,来自不同的第三方接口。接口的稳定性、返回结果、音质和版权情况,都由对应的第三方服务决定。
所以项目更接近一个音乐检索、试听和下载工具,而不是音乐资源仓库。
项目使用 MIT License,主要用于个人学习、技术研究和交流。使用时请尊重音乐版权,并遵守所在地的法律法规。
最后
现在回头看,CoCo Downloader 的起点其实很小。
只是发现几个接口容易分析,于是写了一个页面;后来因为被更多人看到,才继续增加平台和功能。它没有一个特别宏大的开始,也没有一条完全按照计划铺开的路线。
项目现在还会继续维护。第三方接口会变化,新的来源可能还会加入,Web 端和桌面端也有一些地方需要继续整理。
不过这些事情并不着急。能在需要的时候打开它,搜索一首歌,试听一下,然后确认这就是自己想要的版本,已经是这个项目最初也最实际的用途了。
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
