Aplayer 介绍


Aplayer是一个功能强大的HTML5音乐播放器,兼容所有浏览器。

Meting.js 介绍

Meting JS

Meting.js 依赖 APlayer.js,扩展了 APlayer.js 的功能,能够使 APlayer.js 加载网易云音乐、QQ 音乐、虾米音乐中的歌单。

apayer+meting 简单调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!-- require APlayer -->
<link rel="stylesheet" href="https://testingcf.jsdelivr.net/npm/aplayer/dist/APlayer.min.css">
<script src="https://testingcf.jsdelivr.net/npm/aplayer/dist/APlayer.min.js"></script>
<!-- require MetingJS -->
<script src="https://testingcf.jsdelivr.net/npm/[email protected]/dist/Meting.min.js"></script>
</head>
<body>

<meting-js server="netease" type="playlist" id="60198"></meting-js>

</body>
</html>

Metingjs 的简单说明

解析:server=”netease” type=”playlist” id=”60198”

server指音乐平台,netease指网易云音乐, type类型,playlist列表,id指歌曲的i或者专辑或列表外链id
因此重点在于指定平台,指定外链id

Metingjs 的源码解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
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.injahow.cn/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,
}

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)
}

Metingjs的高级调用方法

当我们需要听vip版权歌曲,需要调用私人api,所以不能再使用无登陆访客状态,需更改cookie值,下面看下代码解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
private function curlset()
{
switch ($this->server) {
case 'netease':
return array(
'Referer' => 'https://music.163.com/',
'Cookie' => 'appver=8.2.30; os=iPhone OS; osver=15.0; EVNSM=1.0.0; buildver=2206; channel=distribution; machineid=iPhone13.3',
'User-Agent' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 CloudMusic/0.1.1 NeteaseMusic/8.2.30',
'X-Real-IP' => long2ip(mt_rand(1884815360, 1884890111)),
'Accept' => '*/*',
'Accept-Language' => 'zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4',
'Connection' => 'keep-alive',
'Content-Type' => 'application/x-www-form-urlencoded',
);
case 'tencent':
return array(
'Referer' => 'http://y.qq.com',
'Cookie' => 'pgv_pvi=22038528; pgv_si=s3156287488; pgv_pvid=5535248600; yplayer_open=1; ts_last=y.qq.com/portal/player.html; ts_uid=4847550686; yq_index=0; qqmusic_fromtag=66; player_exist=1',
'User-Agent' => 'QQ%E9%9F%B3%E4%B9%90/54409 CFNetwork/901.1 Darwin/17.6.0 (x86_64)',
'Accept' => '*/*',
'Accept-Language' => 'zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4',
'Connection' => 'keep-alive',
'Content-Type' => 'application/x-www-form-urlencoded',
);
case 'xiami':
return array(
'Cookie' => '_m_h5_tk=15d3402511a022796d88b249f83fb968_1511163656929; _m_h5_tk_enc=b6b3e64d81dae577fc314b5c5692df3c',
'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) XIAMI-MUSIC/3.1.1 Chrome/56.0.2924.87 Electron/1.6.11 Safari/537.36',
'Accept' => 'application/json',
'Content-type' => 'application/x-www-form-urlencoded',
'Accept-Language' => 'zh-CN',
);
case 'kugou':
return array(
'User-Agent' => 'IPhone-8990-searchSong',
'UNI-UserAgent' => 'iOS11.4-Phone8990-1009-0-WiFi',
);
case 'baidu':
return array(
'Cookie' => 'BAIDUID='.$this->getRandomHex(32).':FG=1',
'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) baidu-music/1.2.1 Chrome/66.0.3359.181 Electron/3.0.5 Safari/537.36',
'Accept' => '*/*',
'Content-type' => 'application/json;charset=UTF-8',
'Accept-Language' => 'zh-CN',
);
case 'kuwo':
return array(
'Cookie' => 'Hm_lvt_cdb524f42f0ce19b169a8071123a4797=1623339177,1623339183; _ga=GA1.2.1195980605.1579367081; Hm_lpvt_cdb524f42f0ce19b169a8071123a4797=1623339982; kw_token=3E7JFQ7MRPL; _gid=GA1.2.747985028.1623339179; _gat=1',
'csrf' => '3E7JFQ7MRPL',
'Host' => 'www.kuwo.cn',
'Referer' => 'http://www.kuwo.cn/',
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36',
);
}
}

我们需要自行更改对应平台的cookie值,然后调用私人api。

私人api好处:稳定不会挂,可以听版权音乐,即使是某抑变灰歌曲。