/src/provider/match.js

https://github.com/nondanee/UnblockNeteaseMusic · JavaScript · 97 lines · 94 code · 3 blank · 0 comment · 8 complexity · 9da8377aa97d0db66c0e0ed5d5397729 MD5 · raw file

  1. const find = require('./find')
  2. const request = require('../request')
  3. const provider = {
  4. netease: require('./netease'),
  5. qq: require('./qq'),
  6. xiami: require('./xiami'),
  7. baidu: require('./baidu'),
  8. kugou: require('./kugou'),
  9. kuwo: require('./kuwo'),
  10. migu: require('./migu'),
  11. joox: require('./joox'),
  12. youtube: require('./youtube')
  13. }
  14. const match = (id, source) => {
  15. let meta = {}
  16. const candidate = (source || global.source || ['qq', 'kuwo', 'migu']).filter(name => name in provider)
  17. return find(id)
  18. .then(info => {
  19. meta = info
  20. return Promise.all(candidate.map(name => provider[name].check(info).catch(() => {})))
  21. })
  22. .then(urls => {
  23. urls = urls.filter(url => url)
  24. return Promise.all(urls.map(url => check(url)))
  25. })
  26. .then(songs => {
  27. songs = songs.filter(song => song.url)
  28. if (!songs.length) return Promise.reject()
  29. console.log(`[${meta.id}] ${meta.name}\n${songs[0].url}`)
  30. return songs[0]
  31. })
  32. }
  33. const check = url => {
  34. const song = {size: 0, br: null, url: null, md5: null}
  35. return Promise.race([request('GET', url, {'range': 'bytes=0-8191'}), new Promise((_, reject) => setTimeout(() => reject(504), 5 * 1000))])
  36. .then(response => {
  37. if (!response.statusCode.toString().startsWith('2')) return Promise.reject()
  38. if (url.includes('qq.com'))
  39. song.md5 = response.headers['server-md5']
  40. else if (url.includes('xiami.net') || url.includes('qianqian.com'))
  41. song.md5 = response.headers['etag'].replace(/"/g, '').toLowerCase()
  42. song.size = parseInt((response.headers['content-range'] || '').split('/').pop() || response.headers['content-length']) || 0
  43. song.url = response.url.href
  44. return response.headers['content-length'] === '8192' ? response.body(true) : Promise.reject()
  45. })
  46. .then(data => {
  47. const bitrate = decode(data)
  48. song.br = (bitrate && !isNaN(bitrate)) ? bitrate * 1000 : null
  49. })
  50. .catch(() => {})
  51. .then(() => song)
  52. }
  53. const decode = buffer => {
  54. const map = {
  55. 3: {
  56. 3: ['free', 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 'bad'],
  57. 2: ['free', 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 'bad'],
  58. 1: ['free', 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 'bad']
  59. },
  60. 2: {
  61. 3: ['free', 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 'bad'],
  62. 2: ['free', 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 'bad']
  63. }
  64. }
  65. map[2][1] = map[2][2]
  66. map[0] = map[2]
  67. let pointer = 0
  68. if (buffer.slice(0, 4).toString() === 'fLaC') return 999
  69. if (buffer.slice(0, 3).toString() === 'ID3') {
  70. pointer = 6
  71. const size = buffer.slice(pointer, pointer + 4).reduce((summation, value, index) => summation + (value & 0x7f) << (7 * (3 - index)), 0)
  72. pointer = 10 + size
  73. }
  74. const header = buffer.slice(pointer, pointer + 4)
  75. // https://www.allegro.cc/forums/thread/591512/674023
  76. if (
  77. header.length === 4 &&
  78. header[0] === 0xff &&
  79. ((header[1] >> 5) & 0x7) === 0x7 &&
  80. ((header[1] >> 1) & 0x3) !== 0 &&
  81. ((header[2] >> 4) & 0xf) !== 0xf &&
  82. ((header[2] >> 2) & 0x3) !== 0x3
  83. ) {
  84. const version = (header[1] >> 3) & 0x3
  85. const layer = (header[1] >> 1) & 0x3
  86. const bitrate = header[2] >> 4
  87. return map[version][layer][bitrate]
  88. }
  89. }
  90. module.exports = match