From e486befd188a92f50bf1a54f651ed4879a3cc2a0 Mon Sep 17 00:00:00 2001 From: uh wot Date: Tue, 13 Jul 2021 10:50:33 +0200 Subject: [PATCH] updated to use url gen on deezer's player --- dzunlock.user.js | 168 ++++++++++++++++------------------------------- 1 file changed, 58 insertions(+), 110 deletions(-) diff --git a/dzunlock.user.js b/dzunlock.user.js index 95642fc..9df3364 100644 --- a/dzunlock.user.js +++ b/dzunlock.user.js @@ -3,7 +3,7 @@ // @namespace io.github.uhwot.dzunlock // @description enables deezer hifi features lol // @author uh wot -// @version 1.0.3 +// @version 1.1 // @homepageURL https://git.freezer.life/uhwot/dzunlock // @downloadURL https://git.freezer.life/uhwot/dzunlock/raw/branch/master/dzunlock.user.js // @icon https://cdns-files.dzcdn.net/cache/images/common/favicon/favicon-96x96.852baf648e79894b668670e115e4a375.png @@ -11,7 +11,6 @@ // @match https://www.deezer.com/search/* // @match https://www.deezer.com/account/* // @require https://cdnjs.cloudflare.com/ajax/libs/aes-js/3.1.2/index.min.js -// @require https://cdnjs.cloudflare.com/ajax/libs/spark-md5/3.0.0/spark-md5.min.js // @grant GM_getValue // @grant GM_setValue // ==/UserScript== @@ -86,45 +85,11 @@ fetchIntercept = { const clientId = '119915' const clientSecret = '2f5b4c9785ddc367975b83d90dc46f5c' const playerTokenKey = [102, 228, 95, 242, 215, 50, 122, 26, 57, 216, 206, 38, 164, 237, 200, 85] -const trackCDNKey = [106, 111, 54, 97, 101, 121, 54, 104, 97, 105, 100, 50, 84, 101, 105, 104] -const qualityToFormat = { - 'standard': '128', - 'high': '320', - 'lossless': 'flac' -} - -const formatToNum = { - '128': 1, - '320': 3, - 'flac': 9 -} - -let dataTemplate = { - data: [ - { - media: [ - { - cipher: { type: "BF_CBC_STRIPE" }, - exp: 694208008135, - format: "", - media_type: "FULL", - nbf: 694208008135, - sources: [ - { - provider: "ak", - url: "", - }, - { - provider: "ec", - url: "", - }, - ], - }, - ], - }, - ], -}; +const formats = [ + {api: '320', gw: 'MP3_320'}, + {api: 'flac', gw: 'FLAC'} +] function str2bin(str) { return Array.from(str).map(function (item) { @@ -161,15 +126,6 @@ function playerTokenPatch(playerToken) { return encryptHex(JSON.stringify(playerToken), playerTokenKey) } -function trackCDNGen(md5_origin, format, id, media_version) { - result = [md5_origin, format, id, media_version].join('\xa4') - log(result) - - result = SparkMD5.hashBinary(result) + '\xa4' + result + '\xa4' - log(result) - return encryptHex(result, trackCDNKey) -} - 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 resp = await fetch(url) @@ -183,10 +139,58 @@ async function renewAccessToken() { } unsafeWindow.dzPlayer.setTrackList = (function (old) { - return function (...args) { - // needed for player to accept flac url responses + return async function (...args) { + // don't get filesizes if account is hifi + if (unsafeWindow.dzPlayer.user_status.audio_qualities.wifi_download.includes('lossless')) { + return old(...args) + } + + let batchList = [] + for (let i = 0; i < args[0].data.length; i++) { - args[0].data[i].FILESIZE_FLAC = "1" + const id = Number(args[0].data[i].SNG_ID) + if (id >= 0) { // we don't need filesizes for user-upped tracks + batchList.push({ 'relative_url': `/track/${id}` }) + } + } + + // return if all the tracks are user-upped + if (batchList.length === 0) { + return old(...args) + } + + batchList = JSON.stringify(batchList) + + let access_token = GM_getValue('access_token', false) + const expiry = GM_getValue('access_token_expiry', 0) + if (!access_token || Math.floor(Date.now() / 1000) >= expiry) { + access_token = await renewAccessToken() + } + + url = `https://api.deezer.com/batch?methods=${batchList}&access_token=${access_token}` + let resp = await fetch(url) + let json = await resp.json() + + // renew access token if token is invalid + const error = json.batch_result[0].error + if (error && error.code === 300) { + access_token = await renewAccessToken() + url = `https://api.deezer.com/batch?methods=${batchList}&access_token=${access_token}` + resp = await fetch(url) + json = await resp.json() + } + + let userUppedSoFar = 0 + + for (let i = 0; i < args[0].data.length; i++) { + const id = Number(args[0].data[i].SNG_ID) + if (id < 0) { // user-uploaded track + userUppedSoFar++ + continue + } + 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] + } } log(args) @@ -221,6 +225,8 @@ fetchIntercept.register({ // disables ads json.results.USER.OPTIONS.ads_display = false json.results.USER.OPTIONS.ads_audio = false + // disables call to get_url endpoint and enables track url gen + json.results.__DZR_GATEKEEPS__.use_media_service = false json.results.PLAYER_TOKEN = playerTokenPatch(json.results.PLAYER_TOKEN) @@ -239,64 +245,6 @@ fetchIntercept.register({ return new Response(JSON.stringify(json)) } - if (response.url === 'https://media.deezer.com/v1/get_url') { - const id = Number(unsafeWindow.dzPlayer.getSongId()) - const quality = unsafeWindow.dzPlayer.control.getAudioQuality() - log(id, quality) - - // check if account is allowed to get quality, if so use the server's response instead - const orig = await response.json() - if (id < 0 /* user-upped track */ || (unsafeWindow.dzPlayer.user_status.audio_qualities.wifi_download.includes(quality) && orig.data && orig.data[0].media && orig.data[0].media[0])) { - return new Response(JSON.stringify(orig)) - } - - let access_token = GM_getValue('access_token', false) - const expiry = GM_getValue('access_token_expiry', 0) - if (!access_token || Math.floor(Date.now() / 1000) >= expiry) { - access_token = await renewAccessToken() - } - - url = `https://api.deezer.com/track/${id}?access_token=${access_token}` - resp = await fetch(url) - let json = await resp.json() - - // renew access token if token is invalid - if (json.error && json.error.code === 300) { - access_token = await renewAccessToken() - url = `https://api.deezer.com/track/${id}?access_token=${access_token}` - resp = await fetch(url) - json = await resp.json() - } - - const md5_origin = json['md5_origin'] - const media_version = json['media_version'] - - const formats = Object.values(qualityToFormat) - let format = qualityToFormat[quality] - while (format !== '128') { - if (json['filesize_' + format] !== "0") { - break - } - format = formats[formats.indexOf(format) - 1] - } - format_num = formatToNum[format] - - const trackCDNPath = trackCDNGen(md5_origin, format_num, id, media_version) - - if (format === 'flac') { - dataTemplate.data[0].media[0].format = 'FLAC' - } else { - dataTemplate.data[0].media[0].format = 'MP3_128' // doesn't matter whether it's 128 or 320 - } - - dataTemplate.data[0].media[0].sources[0].url = `https://cdns-proxy-${md5_origin[0]}.dzcdn.net/mobile/1/${trackCDNPath}` - dataTemplate.data[0].media[0].sources[1].url = `https://e-cdns-proxy-${md5_origin[0]}.dzcdn.net/mobile/1/${trackCDNPath}` - - log(dataTemplate) - - return new Response(JSON.stringify(dataTemplate)) - } - return response; },