因为最近两个月比较无聊,在家里各种折腾我的老NAS,搭了一个音乐服务器,但是找不到特别满意的播放器,在桌面端还有个Sonixd颜值比较高,而且没有花里胡哨的功能,但是手机端的话就不甚满意,只有substreamer可以用,但是不太好看。
最关键的是这两个播放器都不支持歌词,而且还不能模糊查找以及简繁体查找。比如我想听周杰伦的退后,但是我挂搜出来的名字是退後,那我搜索的时候还需要切到繁体去才能搜出来,这就比较烦了。
我第一反应是去Github上找找有没有现成的多端轮子,但是看得上的都是本地播放的,或者有个仿网易云音乐播放器是通过网易云的API搞的,随时可能会被断掉不说,也不支持连Navidrome或者Subsonic的API。实在是找不到了,一咬牙一跺脚自己搞一个吧!
Subsonic API
Navidrome本身有原生的API,但是不多,而且作者正在计划重写,不过好在他针对Subsonic的API做了几乎全部支持,所以即使Navidrome本身没有API文档,看着Subsonic的文档也能放到Postman上试试看
验证服务器
猛看稍微比较懵逼,其实很简单,验证分两种:要么传入密码,要么传入加密后的(密码+六位盐值)再加上六位盐值
先Ping
一下服务器进行测试(使用dio: ^4.0.6可以省却不少操作)
1 | try { |
做完两步验证服务器和Subsonic的api都没有问题了就可以保存服务器信息了,后面的api都使用盐值来获取,其实无所谓啦,都是免费的
加密登陆
利用
Rondom
写一个随机生成6位字符1
2
3
4
5
6
7
8
9
10String generateRandomString() {
final _random = Random();
const _availableChars =
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
final randomString = List.generate(6,
(index) => _availableChars[_random.nextInt(_availableChars.length)])
.join();
return randomString;
}然后拼接之后用MD5加密
把这两个字符保存起来用于后面API的使用
获取目录
Subsonic里面的目录是这样自上而下分级的
- 音乐文件夹
- 音乐人文件夹
- 专辑
- 歌曲
不过Navidrome好像就只有一个音乐文件夹,所以我直接用了第二级的getIndexes
来做我的目录主列表,然后根据音乐人ID
再下到专辑列表
播放音乐
使用ValueNotifier
来进行变量监听,不要轻易使用provider: ^6.0.5 ,虽然说真的方便但是全局变量总会出现各种各样的问题,尤其是当你依赖他之后,想要再把代码改回去,那工作量简直想让你重新写一遍!
其实ValueNotifier
使用起来也挺方便的,他后面可以带<int>
参数来指定值的类型,也可以自定义监控数据类型,我是根据这篇文章来操作的
定义数据监控器
ValueNotifier<String> activeSongValue = ValueNotifier<String>("1");
理论上这个放在哪个文件里面都可以,不过建议放到统一的全局变量定义文件里便于管理,我这里传入歌曲的
id
,给一个默认值做过滤,如果是"1"
就不调用接口获取歌曲信息了(flutter2.12后的Null safety
强制要求,刚开始不适应,现在觉得挺好的)改变数据的值
activeSongValue.value = _tem["id"];
直接使用就可以,这样看来其实比
provider
还要简单监听数据的变化
使用
ValueListenableBuilder
构造来构造监听部分1
2
3
4
5
6
7
8return ValueListenableBuilder<String>(
valueListenable: activeSongValue, //监控器
builder: ((context, value, child) {
if (value != "1") { //改变值
_setAudioSource(value); //直接使用id进行查询等后续工作
}
return Container()...获取歌曲信息
有了歌曲
id
之后,就可以使用getSong
来获取音乐信息,使用getCoverArt
来获取专辑图片,再使用stream
来获取流文件播放音乐流
使用just_audio: ^0.9.31 可以大大加速开发进度,它是排名第二的dependencies,第一是audioplayers,不过正好这几天他更新了版本,官方文档的地址给搞没了,这就叫做缘分
使用起来也极其简单,如果不管按钮滑块之类的控制展现,从点击音乐到开始播放就三行代码:
1
2
3
4
5
6
7
8
9
10
11//定义组件
final _player = AudioPlayer();
//拼接流的url
String _url = await getSongStreamUrl(_id);
//获取流并播放
try {
await _player.setAudioSource(AudioSource.uri(Uri.parse(_url)));
_player.play();
} catch (e) {
print("Error loading audio source: $e");
}
以上就是目前的进度,包含了桌面端的服务器测试、然后目录进到歌曲级,然后播放音乐。
后面还有很多工作,不过Flutter是我从1.x的版本就在关注的,一度吐槽华为的工程师不用Flutter就是个废物,Google更是不遗余力的投入,我相信会很容易完成(写桌面端的时候就留意移动端的适配了,都写完估计简单调整一下移动端就可以了,目前只做功能)
这里是Github的地址,愿意下来玩也挺好,不过仅用于开发,离正常用还差很远。