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:' ...
随机推荐
- input type
input的type有: text 文本输入 password密码输入 file选择文件 radio单选按钮 checkbox复选按钮 submit对应form的action按钮 button 普通按 ...
- 【大白话 mysql】mysql 事务与日志原理
在后端面试中,mysql是比不可少的一环,其中对事务和日志的考察更是"重灾区", 大部分同学可能都知道mysql通过redolog.binlog和undolog保证了sql的事务性 ...
- 认识 Spring Cloud Alibaba
个人理解 Spring Cloud Alibaba 就是 Spring Cloud 的微服务规范的一种实现,外加一些阿里云的商业组件 Spring Cloud 是什么 Spring Cloud 为开发 ...
- Node.js核心模块API之文件操作
参考:https://www.runoob.com/nodejs/nodejs-fs.html 异步I/O 1,文件操作 2,网络操作 在浏览器中也存在异步操作 1,定时任务 2,事件处理 3,Aja ...
- 使用U盘软碟通安装原版Windows10
https://zhuanlan.zhihu.com/p/171534675 使用U盘软碟通安装原版Windows10 一.准备:8G U盘一个,电脑装好UltraISO这个软件下载地址 二.Wind ...
- Linux ln命令:在文件之间建立链接(硬链接和软链接)详解版1
Linux ln命令:在文件之间建立链接(硬链接和软链接)详解版 < Linux创建文件及修改文件时间戳(touch命令)Linux复制文件和目录(cp命令) > <Linux就该这 ...
- Linux 根目录所在分区被脏数据占满
背景: 公司在做一个项目,大概功能就是一个通行闸机的人脸识别系统,要经过门禁的人注册了之后,系统就会存储一张原始的图片在服务器的数据文件夹里面,包括了永久的存储和一些访客注册临时存储.一天周五的时 ...
- Ansible_处理失败的任务
一.Ansible处理任务失败 1.管理play中任务错误 1️⃣:Ansible评估任务的返回代码,从而确定任务是成功还是失败 2️⃣:通常而言,当任务失败时,Ansible将立即在该主机上中止pl ...
- linux进程间通信-(转自 临水)
一.进程间通信概述进程通信有如下一些目的:A.数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间B.共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别 ...
- Python 递归函数详解
递归函数的概念: 直接或间接的调用自身的函数,称为递归函数. 每调用一次自身,相当于复制一份该函数,只不过参数有变化,参数的变化,就是重要的结束条件 下面是一个递归函数的实例: #coding=utf ...