updated to use url gen on deezer's player

This commit is contained in:
uh wot 2021-07-13 10:50:33 +02:00
parent ce3c31514f
commit e486befd18
Signed by: uhwot
GPG Key ID: CB2454984587B781
1 changed files with 58 additions and 110 deletions

View File

@ -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.0.3 // @version 1.1
// @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
// @icon https://cdns-files.dzcdn.net/cache/images/common/favicon/favicon-96x96.852baf648e79894b668670e115e4a375.png // @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/search/*
// @match https://www.deezer.com/account/* // @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/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_getValue
// @grant GM_setValue // @grant GM_setValue
// ==/UserScript== // ==/UserScript==
@ -86,45 +85,11 @@ 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 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 = { const formats = [
'standard': '128', {api: '320', gw: 'MP3_320'},
'high': '320', {api: 'flac', gw: 'FLAC'}
'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: "",
},
],
},
],
},
],
};
function str2bin(str) { function str2bin(str) {
return Array.from(str).map(function (item) { return Array.from(str).map(function (item) {
@ -161,15 +126,6 @@ function playerTokenPatch(playerToken) {
return encryptHex(JSON.stringify(playerToken), playerTokenKey) 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() { 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)
@ -183,10 +139,58 @@ async function renewAccessToken() {
} }
unsafeWindow.dzPlayer.setTrackList = (function (old) { unsafeWindow.dzPlayer.setTrackList = (function (old) {
return function (...args) { return async function (...args) {
// needed for player to accept flac url responses // 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++) { 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) log(args)
@ -221,6 +225,8 @@ fetchIntercept.register({
// disables ads // disables ads
json.results.USER.OPTIONS.ads_display = false json.results.USER.OPTIONS.ads_display = false
json.results.USER.OPTIONS.ads_audio = 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) json.results.PLAYER_TOKEN = playerTokenPatch(json.results.PLAYER_TOKEN)
@ -239,64 +245,6 @@ fetchIntercept.register({
return new Response(JSON.stringify(json)) 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; return response;
}, },