added decryption bullshit, also flac ded
This commit is contained in:
parent
705da64066
commit
026edf3808
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
73
index.js
73
index.js
|
@ -1,12 +1,14 @@
|
||||||
const Router = require('./router')
|
const Router = require('./router')
|
||||||
const aesjs = require('aes-js');
|
const aesjs = require('aes-js');
|
||||||
const ID3Writer = require('browser-id3-writer');
|
const ID3Writer = require('browser-id3-writer');
|
||||||
|
const Blowfish = require('./blowfish');
|
||||||
|
|
||||||
addEventListener('fetch', event => {
|
addEventListener('fetch', event => {
|
||||||
event.respondWith(handleRequest(event.request))
|
event.respondWith(handleRequest(event.request))
|
||||||
})
|
})
|
||||||
|
|
||||||
const AESKEY = [106, 111, 54, 97, 101, 121, 54, 104, 97, 105, 100, 50, 84, 101, 105, 104]
|
const trackCDNKey = [106, 111, 54, 97, 101, 121, 54, 104, 97, 105, 100, 50, 84, 101, 105, 104]
|
||||||
|
const bfSecret = [103, 52, 101, 108, 53, 56, 119, 99, 48, 122, 118, 102, 57, 110, 97, 49]
|
||||||
|
|
||||||
function str2bin(str) {
|
function str2bin(str) {
|
||||||
return Array.from(str).map(function (item) {
|
return Array.from(str).map(function (item) {
|
||||||
|
@ -14,8 +16,23 @@ function str2bin(str) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function bin2str(bin) {
|
||||||
|
return String.fromCharCode.apply(String, bin);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function bf_key(id) {
|
||||||
|
let id_md5 = await crypto.subtle.digest('MD5', new Uint8Array(str2bin(id)))
|
||||||
|
id_md5 = aesjs.utils.hex.fromBytes(new Uint8Array(id_md5))
|
||||||
|
|
||||||
|
key = []
|
||||||
|
for (let i = 0; i < 16; i++) {
|
||||||
|
key.push(id_md5[i].charCodeAt(0) ^ id_md5[i + 16].charCodeAt(0) ^ bfSecret[i])
|
||||||
|
}
|
||||||
|
return bin2str(key)
|
||||||
|
}
|
||||||
|
|
||||||
async function url_gen(md5_origin, format, id, media_version) {
|
async function url_gen(md5_origin, format, id, media_version) {
|
||||||
const cipher = new aesjs.ModeOfOperation.ecb(AESKEY)
|
const cipher = new aesjs.ModeOfOperation.ecb(trackCDNKey)
|
||||||
|
|
||||||
let result = [md5_origin, format, id, media_version].join('\xa4')
|
let result = [md5_origin, format, id, media_version].join('\xa4')
|
||||||
|
|
||||||
|
@ -33,14 +50,13 @@ async function url_gen(md5_origin, format, id, media_version) {
|
||||||
result = aesjs.utils.hex.fromBytes(cipher.encrypt(str2bin(result)))
|
result = aesjs.utils.hex.fromBytes(cipher.encrypt(str2bin(result)))
|
||||||
|
|
||||||
// cdn template with first character of md5 string + hash
|
// cdn template with first character of md5 string + hash
|
||||||
return `https://cdns-proxy-${md5_origin[0]}.dzcdn.net/api/1/${result}`
|
return `https://cdns-proxy-${md5_origin[0]}.dzcdn.net/mobile/1/${result}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const format_string_to_num = {
|
const format_string_to_num = {
|
||||||
'64': '10',
|
'64': '10',
|
||||||
'128': '1',
|
'128': '1',
|
||||||
'320': '3',
|
'320': '3',
|
||||||
'flac': '9',
|
|
||||||
'misc': '0',
|
'misc': '0',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +95,7 @@ async function handler(type, request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var tagging = url.searchParams.get('t')
|
var tagging = url.searchParams.get('t')
|
||||||
tagging = (tagging === 'true' || tagging === '1') && format !== 'flac'
|
tagging = tagging === 'true' || tagging === '1'
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'track':
|
case 'track':
|
||||||
|
@ -102,15 +118,14 @@ async function track(id, format, access_token, tagging) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tagging) {
|
|
||||||
return new Response(null, {status: 302, headers: {'location': result}})
|
|
||||||
} else {
|
|
||||||
const track = await fetch(result)
|
const track = await fetch(result)
|
||||||
if (track.status !== 200) {
|
if (track.status !== 200) {
|
||||||
return new Response("Couldn't get track stream", { status: 403, headers: { 'content-type': 'text/plain' } })
|
return new Response("Couldn't get track stream", { status: 403, headers: { 'content-type': 'text/plain' } })
|
||||||
}
|
}
|
||||||
|
|
||||||
const id3 = new ID3Writer(Buffer.alloc(0));
|
let id3
|
||||||
|
if (tagging) {
|
||||||
|
id3 = new ID3Writer(Buffer.alloc(0));
|
||||||
id3.padding = 0
|
id3.padding = 0
|
||||||
id3.setFrame('TIT2', json.title)
|
id3.setFrame('TIT2', json.title)
|
||||||
.setFrame('TALB', json.album.title)
|
.setFrame('TALB', json.album.title)
|
||||||
|
@ -160,17 +175,41 @@ async function track(id, format, access_token, tagging) {
|
||||||
}
|
}
|
||||||
|
|
||||||
id3.addTag();
|
id3.addTag();
|
||||||
|
}
|
||||||
|
|
||||||
let { readable, writable } = new TransformStream()
|
let { readable, writable } = new TransformStream()
|
||||||
const writer = writable.getWriter()
|
const writer = writable.getWriter()
|
||||||
|
|
||||||
|
if (tagging) {
|
||||||
writer.write(id3.arrayBuffer)
|
writer.write(id3.arrayBuffer)
|
||||||
writer.releaseLock()
|
}
|
||||||
track.body.pipeTo(writable)
|
|
||||||
|
const bfKey = await bf_key(id)
|
||||||
|
console.log(bfKey)
|
||||||
|
const cipher = new Blowfish(bfKey, Blowfish.MODE.CBC, Blowfish.PADDING.NULL)
|
||||||
|
cipher.setIv('\x00\x01\x02\x03\x04\x05\x06\x07')
|
||||||
|
|
||||||
|
const buffer = await track.arrayBuffer()
|
||||||
|
const length = buffer.byteLength
|
||||||
|
let byteCount = 0
|
||||||
|
let end = false
|
||||||
|
while (!end) {
|
||||||
|
let chunkEnd = byteCount + 2048
|
||||||
|
if (chunkEnd > length) {
|
||||||
|
chunkEnd = length
|
||||||
|
end = true
|
||||||
|
}
|
||||||
|
let chunk = buffer.slice(byteCount, byteCount+2048)
|
||||||
|
if (byteCount % 6144 === 0 && !end) {
|
||||||
|
// encrypted chunk
|
||||||
|
chunk = cipher.decode(chunk, Blowfish.TYPE.UINT8_ARRAY)
|
||||||
|
}
|
||||||
|
writer.write(chunk)
|
||||||
|
byteCount += 2048
|
||||||
|
}
|
||||||
|
|
||||||
return new Response(readable, { status: 200, headers: { 'content-type': 'audio/mpeg' } })
|
return new Response(readable, { status: 200, headers: { 'content-type': 'audio/mpeg' } })
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async function track_url(json, format) {
|
async function track_url(json, format) {
|
||||||
// needed if track has fallback, like https://www.deezer.com/track/1398759152
|
// needed if track has fallback, like https://www.deezer.com/track/1398759152
|
||||||
|
@ -205,18 +244,10 @@ async function m3u8(type, id, format, access_token, tagging) {
|
||||||
var list = '#EXTM3U\n'
|
var list = '#EXTM3U\n'
|
||||||
|
|
||||||
for (const track of json.tracks.data) {
|
for (const track of json.tracks.data) {
|
||||||
let result
|
|
||||||
if (!tagging) {
|
|
||||||
result = await track_url(track, format)
|
|
||||||
if (typeof result === 'object') {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (track.id < 0) { // user-uploaded track
|
if (track.id < 0) { // user-uploaded track
|
||||||
format = 'misc'
|
format = 'misc'
|
||||||
}
|
}
|
||||||
result = `https://dz.uhwot.workers.dev/track/${track.id}?f=${format}&t=1`
|
let result = `https://dz.uhwot.workers.dev/track/${track.id}?f=${format}&t=${Number(tagging)}`
|
||||||
}
|
|
||||||
list += `#EXTINF:${track.duration},${track.title}\n${result}\n`
|
list += `#EXTINF:${track.duration},${track.title}\n${result}\n`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,5 +5,5 @@ workers_dev = true
|
||||||
route = ""
|
route = ""
|
||||||
zone_id = ""
|
zone_id = ""
|
||||||
kv_namespaces = [
|
kv_namespaces = [
|
||||||
{ binding = "KV", id = "08543eae8cb7494f8f17addaf70f4143", preview_id = "66868b338ff34776a7d8a5d1877c8181" }
|
{ binding = "KV", id = "974c0967a84e415daa054bbbcc7f80c6", preview_id = "cfcc6491f3484cbca664913836635113" }
|
||||||
]
|
]
|
Loading…
Reference in New Issue