-
Notifications
You must be signed in to change notification settings - Fork 0
/
match.js
99 lines (92 loc) · 4 KB
/
match.js
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
const request = require('./UnblockNeteaseMusic/src/request')
const find = require("./UnblockNeteaseMusic/src/provider/find")
const provider = {
netease: require('./UnblockNeteaseMusic/src/provider/netease'),
qq: require('./UnblockNeteaseMusic/src/provider/qq'),
xiami: require('./UnblockNeteaseMusic/src/provider/xiami'),
baidu: require('./UnblockNeteaseMusic/src/provider/baidu'),
kugou: require('./UnblockNeteaseMusic/src/provider/kugou'),
kuwo: require('./UnblockNeteaseMusic/src/provider/kuwo'),
migu: require('./UnblockNeteaseMusic/src/provider/migu'),
joox: require('./UnblockNeteaseMusic/src/provider/joox'),
youtube: require('./UnblockNeteaseMusic/src/provider/youtube')
}
const match = (id, source) => {
let meta = {}
const candidate = (source || global.source || ['qq', 'kuwo', 'migu']).filter(name => name in provider)
return find(id)
.then(async info => {
meta = info
return Promise.all(candidate.map(name => provider[name].check(info).catch(() => {
})))
})
.then(results => {
results = results.filter(result => result && result.url)
return Promise.all(results.map(result => check(result)))
})
.then(songs => {
songs = songs.filter(song => song.url)
if (!songs.length) return Promise.reject()
return [meta, songs]
})
}
const check = result => {
const {url, info} = result
const song = {size: 0, br: null, url: null, md5: null, info}
return Promise.race([request('GET', url, {'range': 'bytes=0-8191'}), new Promise((_, reject) => setTimeout(() => reject(504), 5 * 1000))])
.then(response => {
if (!response.statusCode.toString().startsWith('2')) return Promise.reject()
if (url.includes('qq.com'))
song.md5 = response.headers['server-md5']
else if (url.includes('xiami.net') || url.includes('qianqian.com'))
song.md5 = response.headers['etag'].replace(/"/g, '').toLowerCase()
song.size = parseInt((response.headers['content-range'] || '').split('/').pop() || response.headers['content-length']) || 0
song.url = response.url.href
return response.headers['content-length'] === '8192' ? response.body(true) : Promise.reject()
})
.then(data => {
const bitrate = decode(data)
song.br = (bitrate && !isNaN(bitrate)) ? bitrate * 1000 : null
})
.catch(() => {
})
.then(() => song)
}
const decode = buffer => {
const map = {
3: {
3: ['free', 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 'bad'],
2: ['free', 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 'bad'],
1: ['free', 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 'bad']
},
2: {
3: ['free', 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 'bad'],
2: ['free', 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 'bad']
}
}
map[2][1] = map[2][2]
map[0] = map[2]
let pointer = 0
if (buffer.slice(0, 4).toString() === 'fLaC') return 999
if (buffer.slice(0, 3).toString() === 'ID3') {
pointer = 6
const size = buffer.slice(pointer, pointer + 4).reduce((summation, value, index) => summation + (value & 0x7f) << (7 * (3 - index)), 0)
pointer = 10 + size
}
const header = buffer.slice(pointer, pointer + 4)
// https://www.allegro.cc/forums/thread/591512/674023
if (
header.length === 4 &&
header[0] === 0xff &&
((header[1] >> 5) & 0x7) === 0x7 &&
((header[1] >> 1) & 0x3) !== 0 &&
((header[2] >> 4) & 0xf) !== 0xf &&
((header[2] >> 2) & 0x3) !== 0x3
) {
const version = (header[1] >> 3) & 0x3
const layer = (header[1] >> 1) & 0x3
const bitrate = header[2] >> 4
return map[version][layer][bitrate]
}
}
module.exports = match