为博客添加APlayer音乐播放器InstantClick,Pajx实现无缝播放

839次阅读
2 条评论

共计 7869 个字符,预计需要花费 20 分钟才能阅读完成。

最近想在博客上添加上BGM😂. 开动!

APlayer

APlayer 是一款 HTML 音乐播放器。目前被广泛使用在各大知名社区论坛上。相比其他音乐播放器,APlayer的优势很明显:界面美观,轻量易用。 为博客添加APlayer音乐播放器InstantClick,Pajx实现无缝播放

APlayer仓库 | APlayer文档

APlayer 播放器强调在初始化时传入选项参数并写入一个传入一个html节点用来绘制播放器以及控制播放.我查看了APlayer的源码需要注意的是APlayer并不支持反复使用同一个播放实例, 只提供了播放控制功能.并不类似单例播放器. 使用 new APlayer(optons) 的方式实例化一个播放器. 开始时我尝试自己在代码中安装这个播放器.

下载 APlayer 只需要APlayer发行页面下载最新的源码包解压获取dist目录中的 APlayer.min.cssAPlayer.min.js 文件. 当然如果你需要在页面中调试,可以复制这个文件下面的两个map 文件.我在 wordpress 中的 /var/www/html/wp-content 目录创建了一个 my-assets 目录. 我对 APlayer.min.cssAPlayer.min.js 进行了重命名将文件名修改为了小写存放在aplayer目录下,在’music’ 目录下的三个文件分别是需要播放歌曲的专辑图片jpg,歌词lrc,音频mp3. 目录结构如下.

.
├── aplayer
│   ├── aplayer.min.css
│   └── aplayer.min.js
├── music
│   ├── Stefanie-Against-the-Light.jpg
│   ├── Stefanie-Against-the-Light.lrc   
│   └── Stefanie-Against-the-Light.mp3

以下是我参考文档基础安装代码.

<!-- <head> -->
    <!--...-->
    <link rel="stylesheet" href="/wp-content/my-assets/aplayer/aplayer.min.css">
    <!--...-->
<!-- </head> -->
 <!--...-->
<!-- <body> -->
    <!--...-->
    <div id="aplayer"></div>
    <script type='text/javascript' src="/wp-content/my-assets/aplayer/aplayer.min.js"></script>
    <script type='text/javascript'>
    const ap = new APlayer({
        container: document.getElementById('aplayer'),
        fixed: true,
        mini: false,
        autoplay: true,
        loop: 'all',
        order: 'random',
        volume: 0.9,
        mutex: true,
        listFolded: true,
        lrcType: 3,
        audio: [
            {
                name: '逆光',
                artist: '孙燕姿',
                url: '/wp-content/my-assets/music/Stefanie-Against-the-Light.mp3',
                cover: '/wp-content/my-assets/music/Stefanie-Against-the-Light.jpg',
                lrc: '/wp-content/my-assets/music/Stefanie-Against-the-Light.lrc',
            },
        ]
    });
    </script>
    <!--...-->
<!-- </body> -->

如上代码, 需要注意的是 link 需要放在页面的head 标签中其余代码放在页面的body标签中尽量放在body内容靠后位置.

<head>
    <!--...-->
    <link rel="stylesheet" href="/wp-content/my-assets/aplayer/aplayer.min.css">
    <!--...-->
</head>

<div id="aplayer"></div>
<script type='text/javascript' src="/wp-content/my-assets/aplayer/aplayer.min.js"></script>
<script type='text/javascript'>
const ap = new APlayer({
    container: document.getElementById('aplayer'),
    fixed: true,
    mini: false,
    autoplay: true,
    theme: '#FADFA3',
    loop: 'all',
    order: 'random',
    preload: 'auto',
    volume: 0.7,
    mutex: true,
    listFolded: false,
    listMaxHeight: 90,
    lrcType: 3,
    audio: [
        {
            name: '逆光',
            artist: '孙燕姿',
            url: '/wp-content/my-assets/music/Stefanie-Against-the-Light.mp3',
            cover: '/wp-content/my-assets/music/Stefanie-Against-the-Light.jpg',
            lrc: '/wp-content/my-assets/music/Stefanie-Against-the-Light.lrc',
        },
    ]
});
</script>

需要注意的是有些音乐是有版权的, 比如示例中的 逆光 我已经在我的站点中删除了它虽然我很喜欢这首歌但…我们应当支持原创作品. 这首歌很好听… 只是需要vip🤣. 这是孙燕姿姐姐的博客MAKE / MUSIC博客上有燕姿姐姐还在创作的作品:跳舞的梵谷

这种加载方式也存在着弊端. 第一点我们的站点往往不拥有这些音乐的版权, 第二点音乐媒体文件往往都有几兆大小, 这对于博主这种服务器带宽只有2M的服务器宽带压力明显, 当用户打开页面就会占满服务器的所有宽带下载mp3文件,用户在点击其他页面时会等待很久才会加载出来. 我使用了Meting的API加载网易云音乐中的歌单来解决这个问题.

Meting

Meting 是一个开源的解析音乐平台连接的一个 php 应用 项目地址 我并未实际部署这个应用 作者提供了一个api 可以直接访问. 地址: https://api.i-meto.com/meting/api?server=:server&type=:type&id=:id&r=:r

其中参数为

  • server : 解析平台支持 netease, tencent, xiami, kugou, baidu, kuwo
  • type: 固定为playlist
  • id: 你所使用平台的歌单id
  • r: 时间戳

api我只了解了网易云音乐的相关参数,的额外参数可以自行查看源码.或者自行部署实例尝试.以下是接入Meting API的相关代码.aplayer.min.cssaplayer.min.js 默认已经引入了只需要修改初始化的js代码.

(function () {
    function loadAPlayer(container, meting, arg) {
        const meting_api = 'https://api.i-meto.com/meting/api?server=:server&type=:type&id=:id&r=:r';
        let url = meting_api
            .replace(':server', meting.server)
            .replace(':type', meting.type)
            .replace(':id', meting.id)
            .replace(':auth', meting.auth)
            .replace(':r', Math.random());

        return new Promise((resolve) => {
            fetch(url)
                .then(res => res.json())
                .then(result => {
                    const ap = _loadPlayer(result);
                    resolve(ap);
                });
        });

        function _loadPlayer(music) {
            let defaultOption = {
                container: container,
                audio: music,
                lrcType: 3,
                loop: 'all',
                storageName: 'metingjs'
            };
            if (!music.length) {
                return;
            }
            if (!music[0].lrc) {
                defaultOption['lrcType'] = 0;
            }
            let options = {
                ...defaultOption,
                ...arg,
            };
            return new APlayer(options);
        }
    }
    async function initAPlayer(meting, arg) {
        const div = document.createElement("div");
        document.body.appendChild(div);
        const ap = await loadAPlayer(div, meting, arg);
        ap.lrc.hide();
    }
    if (window.APlayer && window.fetch) {
        const meting = {
            server: "netease",
            type: "playlist",
            id: "8819869418",
        };
        const arg = {
            fixed: true,
            mutex: true,
            volume: 1,
            // 音频循环播放, 可选值: 'all', 'one', 'none'
            loop: 'all',
            // 音频循环顺序, 可选值: 'list', 'random'
            order: 'random',
            // 预加载,可选值: 'none', 'metadata', 'auto'
            preload: 'none',
            autoplay: false,
        };
        initAPlayer(meting, arg);
    }
})();

这样播放器就可以播放网易我们自己网易云音乐歌单上的歌啦. 在上方代码中 8819869418 即我的歌单. 要添加歌曲只需在手机上将音乐收藏到这个歌单下,需要注意的是不支持VIP音乐的播放,VIP音乐只会播放前三十秒的音频.最终播放器界面如图所示. 为博客添加APlayer音乐播放器InstantClick,Pajx实现无缝播放

注: 第一听 僕らの手には何もないけど、 当时是看着 mv 听的. 差一点没忍住眼泪.

Pajx

上面实现的代码播放会有一个问题, 播放器在播放过程中用户在点击本站连接跳转页面时, 播放会中断播放进度会消失, 页面会整个刷新一次. 这时就需要用到 Pajx 技术. 当然如果你的网站本身就是单页应用就不用担心这个问题.

什么是Pajx 为博客添加APlayer音乐播放器InstantClick,Pajx实现无缝播放

ajax 可以实现网站的局部刷新,但是 ajax 不会修改网站的 URL,它主要是为了异步的获取某个资源(可能是一段 JSON 或者一个页面)。很多人可能都听过目前流行的 SPA (Single Page Application)开发,它可以实现加载单个 HTML 页面并在用户与应用交互时动态更新该页面的内容,页面不会刷新;切换页面时 URL 会发生改变,这样比起刷新整个页面的体验要好的多,一般使用 MVVM 框架来实现这种单页应用,比如流行的 Vue.js。pjax 的全称是 pushState + ajax ,它其实是 JQuery 的一个插件,你可以了解一下 jquery-pjax 这个项目。pushState 是 HTML5 出现的 API,可以实现将要访问的请求压入一个历史堆栈中,pjax 本质上是对 ajax 的封装,通过 pushState + ajax 实现页面的缓存和本地存储,让我们看起来页面没有刷新,URL 却会发生改变.

本博客使用了Wordpress的Puock主题. 主题实现了页面无刷新加载. 但并不是使用的 jquery-pjax 而是使用的 instantclick.

Instantclick

InstantClick 是一个 JavaScript 库,可以显着加快网站的加载速度,在大多数情况下使导航有效即时。

官方网站| 官方仓库

InstantClick 似乎是一个很老的项目了, 在官方仓库最后一次提交代码是在六年前. 我在网上找了很久的相关文档, 非常稀少. 但是我使用的主题是使用的这个刷新技术. 所以我了解了一下.

instantclick 配置选项并不多, 我暂时并没有去研究它预加载配置那些. 只是了解了一下它的刷新过程. 当使用了instantclick用户点击站内连接跳转时, 浏览器并不会重载整个页面. 这为我保留播放进度提供了机会. 但是刷新的页面脚本会重复执行所以使用 instantclick 需要了解

  • data-no-instant这个标签属性在script标签中, data-no-instant 标注标识当页面加载后 script 标签内或者 src 加载的代码不会再次执行.
  • receive 页面已预加载, 可以修改其内容

在实验如何保持音乐播放进度时我尝试过许多方法, 如保存将播放进度的实践保存到本地存储中打开页面又再次加载跳转到指定进度,但APlayer的ap.seek(time: number): 跳转到特定时间,时间的单位为秒函数貌似存在无法跳转的问题. 也尝试过将APlayer中的原生audio对象保存到页面中, 但APlayer并未提供api重新渲染播放器. 也尝试过不释放APlayer实例化出来的对象并挂在页面的window对象中.但是在跳转后音乐还在继续播放,播放器却消失不见了,最后发现是播放器的显示节点被释放掉了.

最终我找到了一种解决方案. js中动态创建播放器节点添加到body里面并在函数闭包中保存这个节点的引用. 在页面刷新之后, 通过监听instantclickreceive事件,再次将播放器节点插入body显示. 由此完美实现了APlayer播放器在InstantClick加载页面时保持播放的问题.播放器加载完整代码如下.

html页面代码

<!-- <head> -->
    <!--...-->
    <link rel="stylesheet" href="/wp-content/my-assets/aplayer/aplayer.min.css">
    <!--...-->
<!-- </head> -->
<!-- <body> -->
    <!--...-->
    <script type='text/javascript' data-no-instant src="/wp-content/my-assets/aplayer/aplayer.min.js"></script>
    <script type='text/javascript' data-no-instant src="/wp-content/my-assets/aplayer/aplayer.auto.js"></script>
    <!--...-->
<!-- </body> -->

上面的代码中需要注意在script中添加data-no-instant属性这样可以让InstantClick在加载了新的页面后不会在此执行script里面的代码, 这里我创建 aplayer.auto.js文件用来存放自动加载播放器的代码. 可以在script标签中直接直接编写代码只需要添加好data-no-instant属性就可以了.

(function () {
    function loadAPlayer(container, meting, arg) {
        const meting_api = 'https://api.i-meto.com/meting/api?server=:server&type=:type&id=:id&r=:r';
        let url = meting_api
            .replace(':server', meting.server)
            .replace(':type', meting.type)
            .replace(':id', meting.id)
            .replace(':auth', meting.auth)
            .replace(':r', Math.random());

        return new Promise((resolve) => {
            fetch(url)
                .then(res => res.json())
                .then(result => {
                    const ap = _loadPlayer(result);
                    resolve(ap);
                });
        });

        function _loadPlayer(music) {
            let defaultOption = {
                container: container,
                audio: music,
                lrcType: 3,
                loop: 'all',
                storageName: 'metingjs'
            };
            if (!music.length) {
                return;
            }
            if (!music[0].lrc) {
                defaultOption['lrcType'] = 0;
            }
            let options = {
                ...defaultOption,
                ...arg,
            };
            return new APlayer(options);
        }
    }

    async function initAPlayer(meting, arg) {
        // 创建播放器节点并添加到body中
        const div = document.createElement("div");
        document.body.appendChild(div);
        // 创建播放器
        const ap = await loadAPlayer(div, meting, arg);
        // 监听更改
        InstantClick.on('receive', function (url, body, title) {
            body.appendChild(div);
            return {
                body: body,
                title: title
            };
        });
        ap.lrc.hide();
    }

    jQuery(() => {
        if (window.APlayer && window.fetch) {
            const meting = {
                server: "netease",
                type: "playlist",
                id: "8819869418",
            };
            const arg = {
                fixed: true,
                mutex: true,
                volume: 1,
                loop: 'none',
                order: 'random',
                preload: 'none',
                autoplay: false,
            };
            initAPlayer(meting, arg);
        }
    });
})();

总结

这样我博客上的播放器就诞生啦, 很高兴与你分享我喜欢的音乐.

正文完
 5
太阳
版权声明:本站原创文章,由 太阳 2023-10-25发表,共计7869字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(2 条评论)
111 评论达人 LV.1
2024-03-09 10:12:00 回复

有点意思

 Windows  Chrome  中国广东省深圳市联通
太阳 博主
2023-10-25 22:12:49 回复

:evil: 发现一个问题 当阅读这篇文章的时候因为 文章中含有data-no-instant 会被处理导致点击其他连接 前两次会无效 :sad: 老代码果然…

 Windows  Chrome