MetingJS 是如何配合 Aplayer 加载歌单的?
Meting.js 介绍

Meting.js 依赖 APlayer.js,扩展了 APlayer.js 的功能,能够使 APlayer.js 加载网易云音乐、QQ 音乐、虾米音乐中的歌单。
安装
<!-- require APlayer -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css">
<script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js"></script>
<!-- require MetingJS -->
<script src="https://cdn.jsdelivr.net/npm/meting@2/dist/Meting.min.js"></script>
使用
加载歌单
<meting-js
server="netease"
type="playlist"
id="60198">
</meting-js>
通过 auto 属性加载单曲:
<meting-js
auto="https://y.qq.com/n/yqq/song/001RGrEX3ija5X.html">
</meting-js>
通过 url 加载单曲:
<meting-js
name="rainymood"
artist="rainymood"
url="https://rainymood.com/audio1110/0.m4a"
cover="https://rainymood.com/i/badge.jpg">
</meting-js>
加载托管在其他服务器上的单曲:
<meting-js
name="rainymood"
artist="rainymood"
url="https://rainymood.com/audio1110/0.m4a"
cover="https://rainymood.com/i/badge.jpg"
fixed="true">
<pre hidden>
[00:00.00]This
[00:04.01]is
[00:08.02]lyric
</pre>
</meting-js>
源码解析
class MetingJSElement extends HTMLElement {
/**
* 当自定义元素第一次被连接到文档 DOM 时被调用
* connectedCallback
* https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#using_the_lifecycle_callbacks
*/
connectedCallback() {
if (window.APlayer && window.fetch) {
this._init()
this._parse()
}
}
/**
* 与 connectedCallback 反
*/
disconnectedCallback() {
if (!this.lock) {
this.aplayer.destroy()
}
}
/**
* 驼峰化
* @param { string } str
* @returns { string } str
*/
_camelize(str) {
return str
.replace(/^[_.\- ]+/, '')
.toLowerCase()
.replace(/[_.\- ]+(\w|$)/g, (m, p1) => p1.toUpperCase())
}
/**
* 初始化
*/
_init() {
let config = {}
// attributes -> NamedNodeMap
// https://developer.mozilla.org/zh-CN/docs/Web/API/NamedNodeMap
for (let i = 0; i < this.attributes.length; i += 1) {
config[this._camelize(this.attributes[i].name)] = this.attributes[i].value
}
let keys = [
'server',
'type',
'id',
'api',
'auth',
'auto',
'lock',
'name',
'title',
'artist',
'author',
'url',
'cover',
'pic',
'lyric',
'lrc',
]
this.meta = {}
// 构建 meta
// config 保留 keys 数组中没有的属性
// keys 中有 config 中也有的属性给 meta 赋值,没有的先设为 undefined
for (let key of keys) {
this.meta[key] = config[key]
delete config[key]
}
this.config = config
this.api =
this.meta.api ||
window.meting_api ||
'https://api.i-meto.com/meting/api?server=:server&type=:type&id=:id&r=:r'
if (this.meta.auto) this._parse_link()
}
/**
* 解析 auto 属性的值
* 将解析后的结果赋值给 meta 对象的 server、type、id
*/
_parse_link() {
let rules = [
['music.163.com.*song.*id=(\\d+)', 'netease', 'song'],
['music.163.com.*album.*id=(\\d+)', 'netease', 'album'],
['music.163.com.*artist.*id=(\\d+)', 'netease', 'artist'],
['music.163.com.*playlist.*id=(\\d+)', 'netease', 'playlist'],
['music.163.com.*discover/toplist.*id=(\\d+)', 'netease', 'playlist'],
['y.qq.com.*song/(\\w+).html', 'tencent', 'song'],
['y.qq.com.*album/(\\w+).html', 'tencent', 'album'],
['y.qq.com.*singer/(\\w+).html', 'tencent', 'artist'],
['y.qq.com.*playsquare/(\\w+).html', 'tencent', 'playlist'],
['y.qq.com.*playlist/(\\w+).html', 'tencent', 'playlist'],
['xiami.com.*song/(\\w+)', 'xiami', 'song'],
['xiami.com.*album/(\\w+)', 'xiami', 'album'],
['xiami.com.*artist/(\\w+)', 'xiami', 'artist'],
['xiami.com.*collect/(\\w+)', 'xiami', 'playlist'],
]
for (let rule of rules) {
// 返回匹配
// eg: "https://y.qq.com/n/yqq/song/001RGrEX3ija5X.html"
// ["y.qq.com/n/yqq/song/001RGrEX3ija5X.html", "001RGrEX3ija5X"]
let patt = new RegExp(rule[0])
let res = patt.exec(this.meta.auto)
if (res !== null) {
this.meta.server = rule[1]
this.meta.type = rule[2]
this.meta.id = res[1]
return
}
}
}
/**
* 对不同 url 仅行处理
* 生成配置并加载 APlayer
*/
_parse() {
if (this.meta.url) {
// 直接构建 APlayer 配置并加载 APlayer
let result = {
name: this.meta.name || this.meta.title || 'Audio name',
artist: this.meta.artist || this.meta.author || 'Audio artist',
url: this.meta.url,
cover: this.meta.cover || this.meta.pic,
lrc: this.meta.lrc || this.meta.lyric || '',
type: this.meta.type || 'auto',
}
if (!result.lrc) {
this.meta.lrcType = 0
}
if (this.innerText) {
result.lrc = this.innerText
this.meta.lrcType = 2
}
this._loadPlayer([result])
return
}
// 1. 通过 meta 拼凑接口参数获得完整接口 (_init 中存放的默认 api)
// 2. 请求接口,得到播放列表数据
// 3. 加载 APlayer
let url = this.api
.replace(':server', this.meta.server)
.replace(':type', this.meta.type)
.replace(':id', this.meta.id)
.replace(':auth', this.meta.auth)
.replace(':r', Math.random())
fetch(url)
.then(res => res.json())
.then(result => this._loadPlayer(result))
}
_loadPlayer(data) {
let defaultOption = {
audio: data,
mutex: true,
lrcType: this.meta.lrcType || 3,
storageName: 'metingjs',
}
if (!data.length) return
let options = {
...defaultOption,
...this.config,
}
//TODO
for (let optkey in options) {
if (options[optkey] === 'true' || options[optkey] === 'false') {
options[optkey] = options[optkey] === 'true'
}
}
let div = document.createElement('div')
options.container = div
this.appendChild(div)
this.aplayer = new APlayer(options)
}
}
// 创建标签
// customElements -> https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements
if (window.customElements && !window.customElements.get('meting-js')) {
window.MetingJSElement = MetingJSElement
window.customElements.define('meting-js', MetingJSElement)
}
总结
Meting.js 支持加载歌单的的核心在于路径解析以及通过请求内置接口返回歌单列表数据。关键一点, Meting.js 使用了 JavaScript customElements API,可能为了使用者方便,但这导致没有显式向外暴露任何 APlayer 实例,对于稍复杂的场景可能无法处理,只能进行魔改。Meting.js 比较适合较简单的 SPA 应用。关于兼容性,IE11 不支持 customElements API。

理想情况是:Meting.js 与 APlayer.js 解耦,Meting.js 以工具函数的形式存在,仅处理 url 并抛出歌单数据,供 APlayer.js 使用。
MetingJS 是如何配合 Aplayer 加载歌单的?的更多相关文章
- webpack入坑之旅(五)加载vue单文件组件
这是一系列文章,此系列所有的练习都存在了我的github仓库中vue-webpack,在本人有了新的理解与认识之后,会对文章有不定时的更正与更新.下面是目前完成的列表: webpack入坑之旅(一)不 ...
- 【Spring源码分析】非懒加载的单例Bean初始化过程(上篇)
代码入口 上文[Spring源码分析]Bean加载流程概览,比较详细地分析了Spring上下文加载的代码入口,并且在AbstractApplicationContext的refresh方法中,点出了f ...
- 【Spring源码分析】非懒加载的单例Bean初始化过程(下篇)
doCreateBean方法 上文[Spring源码分析]非懒加载的单例Bean初始化过程(上篇),分析了单例的Bean初始化流程,并跟踪代码进入了主流程,看到了Bean是如何被实例化出来的.先贴一下 ...
- 【Spring源码分析】非懒加载的单例Bean初始化前后的一些操作
前言 之前两篇文章[Spring源码分析]非懒加载的单例Bean初始化过程(上篇)和[Spring源码分析]非懒加载的单例Bean初始化过程(下篇)比较详细地分析了非懒加载的单例Bean的初始化过程, ...
- Spring源码分析:非懒加载的单例Bean初始化前后的一些操作
之前两篇文章Spring源码分析:非懒加载的单例Bean初始化过程(上)和Spring源码分析:非懒加载的单例Bean初始化过程(下)比较详细地分析了非懒加载的单例Bean的初始化过程,整个流程始于A ...
- Spring源码分析:非懒加载的单例Bean初始化过程(下)
上文Spring源码分析:非懒加载的单例Bean初始化过程(上),分析了单例的Bean初始化流程,并跟踪代码进入了主流程,看到了Bean是如何被实例化出来的.先贴一下AbstractAutowireC ...
- Spring源码分析:非懒加载的单例Bean初始化过程(上)
上文[Spring源码分析]Bean加载流程概览,比较详细地分析了Spring上下文加载的代码入口,并且在AbstractApplicationContext的refresh方法中,点出了finish ...
- Spring源码剖析4:懒加载的单例Bean获取过程分析
本文转自五月的仓颉 https://www.cnblogs.com/xrq730 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https ...
- easyUi load方法重新加载表单的数据
1.表单回显数据的方法 <script> //方法一 function loadLocal(){ $('#ff').form('load',{ name:'myname', email:' ...
随机推荐
- Jmeter(一) - 从入门到精通 - 环境搭建(详解教程)
1.JMeter 介绍 Apache JMeter是100%纯JAVA桌面应用程序,被设计为用于测试客户端/服务端结构的软件(例如web应用程序).它可以用来测试静态和动态资源的性能,例如:静态文件, ...
- wrk 及扩展支持 tcp 字节流协议压测
wrk 及扩展支持 tcp 字节流协议压测 高性能.方便使用的 HTTP(s) 的流量压测工具,结合了多个开源项目开发而成: redis 的 ae 事件框架 luajit openssl http-p ...
- Linux进阶之链路聚合
CentOS7用命令配置链路聚合 链路聚合是一个计算机网络术语,将多个物理端口汇聚在一起,形成一个逻辑端口,以实现出入流量在各成员端口的负荷分担,交换机根据用户配置的端口负荷分担策略决定网络封包从哪个 ...
- C语言变量和函数命名规范
C 语言变量和函数命名规范: 关于C语言变量和函数命名规范 据考察,没有一种命名规则可以让所有的程序员赞同,程序设计教科书一般都不指定命名规则.命名规则对软件产品而言并不是"成败悠关&quo ...
- easyUI中datagrid展示对象下属性以及显示多个子属性(Day_37)
easyUI中datagrid展示对象下属性以及显示多个子属性 显示对象单个属性值 添加formatter属性 <th field="decidedzone" width=& ...
- Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].StandardContext[]] 错误(Day_25)
错误: 在maven项目,web启动的时候报这个错误 Failed to start component [StandardEngine[Tomcat].StandardHost[localho ...
- html-新闻滚动条
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- Dubbo-admin安装测试
1.下载dubbo-admin 下载地址 2.解压到本地 3.在项目目录下打包dubbo-admin 使用命令 mvn clean package -Dmaven.test.skip=true 第一次 ...
- openresty 学习笔记小结:综合应用实例
openresty 学习笔记小结:综合应用实例 这个综合实验实现的功能其实很简单,用户访问一个页面,显示一个默认页面.输入参数(post或者get都可以),如果参数在数据库查询得到并满足一定条件,根据 ...
- Vue的常用特性
Vue的常用特性 一.表单基本操作 都是通过v-model 单选框 1. 两个单选框需要同时通过v-model 双向绑定 一个值 2. 每一个单选框必须要有value属性 且value值不能一样 3. ...