Apple Music On Windows
Apple Music
的学生优惠只要5元每月,而且在Apple设备上可以提供无损音质和空间音频等体验,看上去似乎很香。但是当我尝试在Windows平台上使用AM时,却发现我似乎只有两种选择:Web
和iTunes
,后一种以它上古的外观和性能坚决劝退了我,前一种虽然有较为现代化的UI,但是以网页的形式存在很容易让我不小心关掉它,而不知道是网络问题还是其他问题,Web端在某些情况下非常卡顿。此外,无论是Web还是iTunes,都没有我很需要的桌面歌词功能。
直到我在V2ex上发现了Lito,一个基于Apple Music Kit开发的,针对Windows的第三方Apple Music客户端。可惜从这个项目当时的进展来看,似乎和我的需求相差有些远:没有搜索和歌单功能,只能按照专辑列表顺序播放主页推荐歌曲,展示歌手和专辑信息,更别提桌面歌词了。给作者提出了几个Feature Request,但是似乎作者本人不太需要这些功能,看了下技术栈是React
/Rust
,虽然没有一个是我熟悉的,但是用几天时间改出我需要的功能应该也不难,所以我Fork了一份决定自己动手。
Electron & Webview
好像几乎是一夜之间,随着Electron和Node.js的成熟,所有的前端程序员突然变成了全栈程序员。通过直接在Chromium的Runtime上构建逻辑,Electron绕过了Qt
等其他跨端框架在多端实现差异上的血海深坑(然后掉进了捆绑Chrome运行时带来的庞大安装包和巨大内存占用等性能优化深坑),连著名的密码管理软件1Password
都抛弃Mac端的原生界面转向Electron(然后被骂惨了XD).
而针对捆绑Chrome运行时导致安装包过大的问题,已经出现了很多替代方案,包括惰性加载Chromium,调用操作系统原生Webview等。
自从Edge弃用了EdgeHTML
转投Chromium
阵营后,Windows上的原生Webview2已经全面跟进Chromium
(版本号甚至比Chrome还要新),已经可以在Windows上替代Electron
了。Webview2已经在Win11以上版本全部预装,而且提供用时下载的方案,因此安装包可以精简至MB级别(Lito的安装包仅1.8MB)。同时Webview2允许JS与原生层互相调用函数,看上去是比Electron的IPC不知道高到哪里去了。
当然 由于Webview2是微软维护的项目,因此可以直接自带Widevine DRM的专利代码包,不需要从Chrome里扒了,对于Apple Music这种流媒体平台更为适合。
同时,我一直比较纠结Electron的内存占用问题,目前的体验来看,Webview2解决了安装包臃肿的问题,但是对内存占用似乎无能为力。Lito的内存占用稳定在100-150MB左右。但是我比较网易云音乐,发现网易云稳定在250MB+...相比之下性能似乎也不太重要了不是吗XD
桌面歌词实现?
如果涉及到桌面歌词,在Electron上的一种方式是新创建一个透明窗口来实现歌词显示,缺点当然是性能问题。所以在Lito中我采用了原生方法来完成这个功能,使用Direct2D来直接渲染歌词内容,当然也是轮子。
此外,两个窗口之间的通信也有点纠结,有些项目采用了IPC或者起一个Http的服务器的方式完成,纠结了半天,还是采用了比较传统的Win32 API窗口传递信息的方式。因为两个窗口同属一个父进程,因此这样子是可以实现的,否则若归属不同的进程,可能只能采用Websocket方式了(比如RainMeter的有些项目和iOS越狱项目就不得不采用Web Server的方式....)
最后具体逻辑为React更新歌词=>PostMessage to Webview2=>Webview2 Call Rust Callback fn=>Call Win32 API SendMessageW=>Windows Post Message to Lyrics Window=>D2D Render
(这样的性能哪里有好了....)
总之用Rust写C++绑定过来的Win32 API肯定不是啥很好的体验,特别是牵扯到FFI,只能通过CString\CStr的方式将OwnerShip转移到C语言,然后将地址使用SendMessageW发送出去,在另一个窗口的回调中通过地址获取CStr,并获得所有权。(当然用D2D写渲染的函数更加痛苦,好在大部分函数都可以仿照https://github.com/lujjjh/iLyrics这个项目中完成,不过将Windows绑定库从0.19.0
升级到0.20.0
也花费了很大功夫,比如HResult::fron_win32
这种函数莫名其妙就消失了,文档中没有替代函数,也没有废弃标志....
一通折腾总算是能跑了,如下:
Github: https://github.com/lx200916/lito
能看看代码吗 学习一下
啊抱歉 忘了写在文章里了 我Fork的Repo在这里 https://github.com/lx200916/lito. 但是说句老实话,学习就不必了,React和Rust都不是我熟悉的技术栈,特别是桌面歌词的实现部分非常Buggy.只能属于凑合用的程度.如果你发现Bug也欢迎提出PR或Issue.