epically disabled decryption
This commit is contained in:
parent
ebfed57c9d
commit
96ccb5c218
146
dzunlock.user.js
146
dzunlock.user.js
|
@ -3,7 +3,7 @@
|
||||||
// @namespace io.github.uhwot.dzunlock
|
// @namespace io.github.uhwot.dzunlock
|
||||||
// @description enables deezer hifi features lol
|
// @description enables deezer hifi features lol
|
||||||
// @author uh wot
|
// @author uh wot
|
||||||
// @version 1.1.6
|
// @version 1.2
|
||||||
// @license GPL-3.0-only
|
// @license GPL-3.0-only
|
||||||
// @homepageURL https://git.freezer.life/uhwot/dzunlock
|
// @homepageURL https://git.freezer.life/uhwot/dzunlock
|
||||||
// @downloadURL https://git.freezer.life/uhwot/dzunlock/raw/branch/master/dzunlock.user.js
|
// @downloadURL https://git.freezer.life/uhwot/dzunlock/raw/branch/master/dzunlock.user.js
|
||||||
|
@ -12,7 +12,6 @@
|
||||||
// @match https://www.deezer.com/search/*
|
// @match https://www.deezer.com/search/*
|
||||||
// @match https://www.deezer.com/account/*
|
// @match https://www.deezer.com/account/*
|
||||||
// @match https://www.deezer.com/smarttracklist/*
|
// @match https://www.deezer.com/smarttracklist/*
|
||||||
// @require https://cdnjs.cloudflare.com/ajax/libs/aes-js/3.1.2/index.min.js
|
|
||||||
// @grant GM_getValue
|
// @grant GM_getValue
|
||||||
// @grant GM_setValue
|
// @grant GM_setValue
|
||||||
// @run-at document-start
|
// @run-at document-start
|
||||||
|
@ -87,52 +86,12 @@ fetchIntercept = {
|
||||||
|
|
||||||
const clientId = '119915'
|
const clientId = '119915'
|
||||||
const clientSecret = '2f5b4c9785ddc367975b83d90dc46f5c'
|
const clientSecret = '2f5b4c9785ddc367975b83d90dc46f5c'
|
||||||
const playerTokenKey = [102, 228, 95, 242, 215, 50, 122, 26, 57, 216, 206, 38, 164, 237, 200, 85]
|
|
||||||
|
|
||||||
const formats = [
|
const formats = [
|
||||||
{ api: '320', gw: 'MP3_320' },
|
{ api: '320', gw: 'MP3_320' },
|
||||||
{ api: 'flac', gw: 'FLAC' }
|
{ api: 'flac', gw: 'FLAC' }
|
||||||
]
|
]
|
||||||
|
|
||||||
function str2bin(str) {
|
|
||||||
return Array.from(str).map(function (item) {
|
|
||||||
return item.charCodeAt(0);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function bin2str(bin) {
|
|
||||||
return String.fromCharCode.apply(String, bin);
|
|
||||||
}
|
|
||||||
|
|
||||||
function decryptHex(hex, key) {
|
|
||||||
hex = aesjs.utils.hex.toBytes(hex)
|
|
||||||
let aesEcb = new aesjs.ModeOfOperation.ecb(key)
|
|
||||||
return bin2str(aesEcb.decrypt(hex)).replace(/\0+$/, '') // removes zero-padding
|
|
||||||
}
|
|
||||||
|
|
||||||
function encryptHex(str, key) {
|
|
||||||
// zero-padding
|
|
||||||
if (str.length % 16) {
|
|
||||||
str += '\x00'.repeat(16 - str.length % 16)
|
|
||||||
}
|
|
||||||
|
|
||||||
let aesEcb = new aesjs.ModeOfOperation.ecb(key)
|
|
||||||
return aesjs.utils.hex.fromBytes(aesEcb.encrypt(str2bin(str)))
|
|
||||||
}
|
|
||||||
|
|
||||||
function playerTokenPatch(playerToken) {
|
|
||||||
playerToken = JSON.parse(decryptHex(playerToken, playerTokenKey))
|
|
||||||
|
|
||||||
// enables 320/flac quality selection
|
|
||||||
playerToken.audio_qualities.wifi_streaming = ['low', 'standard', 'high', 'lossless']
|
|
||||||
// disables previews
|
|
||||||
playerToken.streaming = true
|
|
||||||
|
|
||||||
log(playerToken)
|
|
||||||
|
|
||||||
return encryptHex(JSON.stringify(playerToken), playerTokenKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function renewAccessToken() {
|
async function renewAccessToken() {
|
||||||
let url = `https://connect.deezer.com/oauth/access_token.php?grant_type=client_credentials&client_id=${clientId}&client_secret=${clientSecret}&output=json`
|
let url = `https://connect.deezer.com/oauth/access_token.php?grant_type=client_credentials&client_id=${clientId}&client_secret=${clientSecret}&output=json`
|
||||||
let resp = await fetch(url)
|
let resp = await fetch(url)
|
||||||
|
@ -147,16 +106,16 @@ async function renewAccessToken() {
|
||||||
|
|
||||||
window.addEventListener('DOMContentLoaded', (_) => {
|
window.addEventListener('DOMContentLoaded', (_) => {
|
||||||
unsafeWindow.dzPlayer.setTrackList = (function (old) {
|
unsafeWindow.dzPlayer.setTrackList = (function (old) {
|
||||||
return async function (...args) {
|
return async function (data, ...args) {
|
||||||
// don't get filesizes if account is hifi
|
// don't get filesizes if account is hifi
|
||||||
if (unsafeWindow.dzPlayer.user_status.lossless) {
|
if (unsafeWindow.dzPlayer.user_status.lossless) {
|
||||||
return old(...args)
|
return old(data, ...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
let batchList = []
|
let batchList = []
|
||||||
|
|
||||||
for (let i = 0; i < args[0].data.length; i++) {
|
for (let i = 0; i < data.data.length; i++) {
|
||||||
const id = Number(args[0].data[i].SNG_ID)
|
const id = Number(data.data[i].SNG_ID)
|
||||||
if (id >= 0) { // we don't need filesizes for user-upped tracks
|
if (id >= 0) { // we don't need filesizes for user-upped tracks
|
||||||
batchList.push({ 'relative_url': `/track/${id}` })
|
batchList.push({ 'relative_url': `/track/${id}` })
|
||||||
}
|
}
|
||||||
|
@ -164,7 +123,7 @@ window.addEventListener('DOMContentLoaded', (_) => {
|
||||||
|
|
||||||
// return if all the tracks are user-upped
|
// return if all the tracks are user-upped
|
||||||
if (batchList.length === 0) {
|
if (batchList.length === 0) {
|
||||||
return old(...args)
|
return old(data, ...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
batchList = JSON.stringify(batchList)
|
batchList = JSON.stringify(batchList)
|
||||||
|
@ -190,20 +149,20 @@ window.addEventListener('DOMContentLoaded', (_) => {
|
||||||
|
|
||||||
let userUppedSoFar = 0
|
let userUppedSoFar = 0
|
||||||
|
|
||||||
for (let i = 0; i < args[0].data.length; i++) {
|
for (let i = 0; i < data.data.length; i++) {
|
||||||
const id = Number(args[0].data[i].SNG_ID)
|
const id = Number(data.data[i].SNG_ID)
|
||||||
if (id < 0) { // user-uploaded track
|
if (id < 0) { // user-uploaded track
|
||||||
userUppedSoFar++
|
userUppedSoFar++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for (let j = 0; j < formats.length; j++) {
|
for (let j = 0; j < formats.length; j++) {
|
||||||
args[0].data[i]['FILESIZE_' + formats[j].gw] = json.batch_result[i - userUppedSoFar]['filesize_' + formats[j].api]
|
data.data[i]['FILESIZE_' + formats[j].gw] = json.batch_result[i - userUppedSoFar]['filesize_' + formats[j].api]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log(args)
|
log(data)
|
||||||
|
|
||||||
return old(...args)
|
return old(data, ...args)
|
||||||
};
|
};
|
||||||
})(unsafeWindow.dzPlayer.setTrackList);
|
})(unsafeWindow.dzPlayer.setTrackList);
|
||||||
});
|
});
|
||||||
|
@ -237,23 +196,11 @@ fetchIntercept.register({
|
||||||
// disables call to get_url endpoint and enables track url gen
|
// disables call to get_url endpoint and enables track url gen
|
||||||
json.results.__DZR_GATEKEEPS__.use_media_service = false
|
json.results.__DZR_GATEKEEPS__.use_media_service = false
|
||||||
|
|
||||||
json.results.PLAYER_TOKEN = playerTokenPatch(json.results.PLAYER_TOKEN)
|
|
||||||
|
|
||||||
log(json)
|
log(json)
|
||||||
|
|
||||||
return new Response(JSON.stringify(json))
|
return new Response(JSON.stringify(json))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.url.startsWith('https://www.deezer.com/ajax/gw-light.php?method=log.listen')) {
|
|
||||||
const json = await response.json()
|
|
||||||
|
|
||||||
if (typeof json.results === 'string') {
|
|
||||||
json.results = playerTokenPatch(json.results)
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Response(JSON.stringify(json))
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -263,3 +210,74 @@ fetchIntercept.register({
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
worker_input = function(e) {
|
||||||
|
let json = e.data
|
||||||
|
if (typeof json !== 'object') {
|
||||||
|
json = JSON.parse(json)
|
||||||
|
}
|
||||||
|
//console.log("input", json)
|
||||||
|
input = json
|
||||||
|
|
||||||
|
if (json.message.md5) {
|
||||||
|
// track url gen
|
||||||
|
|
||||||
|
// decrypted track endpoint
|
||||||
|
json.message.cdn = 'https://cdns-proxy-{0}.dzcdn.net/api/1/'
|
||||||
|
worker.postMessage(JSON.stringify(json))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (json.message.buffer) {
|
||||||
|
// track buffer decryption
|
||||||
|
// this code skips decryption entirely, since the track from the CDN is already unencrypted
|
||||||
|
|
||||||
|
let out = { message: { error: undefined } }
|
||||||
|
out['Symbol(PromisingWorker.id)'] = json['Symbol(PromisingWorker.id)']
|
||||||
|
out.message.result = json.message.buffer
|
||||||
|
//console.log("track buffer output", out)
|
||||||
|
postMessage(out)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
worker.postMessage(e.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
worker_output = function(e) {
|
||||||
|
let json = e.data
|
||||||
|
if (typeof json !== 'object') {
|
||||||
|
json = JSON.parse(json)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input) {
|
||||||
|
if (input.message.token && !input.message.id) {
|
||||||
|
// player token decryption
|
||||||
|
|
||||||
|
// enables 320/flac quality selection
|
||||||
|
json.message.result.audio_qualities.wifi_streaming = ['low', 'standard', 'high', 'lossless']
|
||||||
|
// disables previews
|
||||||
|
json.message.result.streaming = true
|
||||||
|
|
||||||
|
//console.log("player token output", json)
|
||||||
|
postMessage(JSON.stringify(json))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log("output", json)
|
||||||
|
postMessage(e.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafeWindow.URL.createObjectURL = (function (old) {
|
||||||
|
return function (object) {
|
||||||
|
if (object instanceof Blob) {
|
||||||
|
let worker = `
|
||||||
|
let worker = new Worker('${old(object)}')
|
||||||
|
let input
|
||||||
|
worker.onmessage = ${String(worker_output)}
|
||||||
|
onmessage = ${String(worker_input)}
|
||||||
|
`
|
||||||
|
return old(new Blob([worker]))
|
||||||
|
}
|
||||||
|
return old(object);
|
||||||
|
};
|
||||||
|
})(unsafeWindow.URL.createObjectURL);
|
Loading…
Reference in New Issue