320/flac is bacc

This commit is contained in:
uh wot 2021-09-15 16:13:49 +02:00
parent 29202f98a9
commit 3861935ddc
Signed by: uhwot
GPG Key ID: CB2454984587B781
2 changed files with 53 additions and 70 deletions

View File

@ -1,6 +1,6 @@
# dzunlock # dzunlock
enables deezer premium features lol enables deezer hifi features lol
# how 2 install # how 2 install

View File

@ -1,9 +1,9 @@
// ==UserScript== // ==UserScript==
// @name dzunlock // @name dzunlock
// @namespace io.github.uhwot.dzunlock // @namespace io.github.uhwot.dzunlock
// @description enables deezer premium features lol // @description enables deezer hifi features lol
// @author uh wot // @author uh wot
// @version 1.2.6 // @version 1.3
// @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
@ -14,7 +14,6 @@
// @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 // @require https://cdnjs.cloudflare.com/ajax/libs/aes-js/3.1.2/index.min.js
// @grant GM_getValue // @grant GM_getValue
// @grant GM_setValue
// @run-at document-start // @run-at document-start
// ==/UserScript== // ==/UserScript==
@ -85,11 +84,15 @@ fetchIntercept = {
// main code starts here // main code starts here
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 playerTokenKey = [102, 228, 95, 242, 215, 50, 122, 26, 57, 216, 206, 38, 164, 237, 200, 85]
const cipher = new aesjs.ModeOfOperation.ecb(playerTokenKey) const cipher = new aesjs.ModeOfOperation.ecb(playerTokenKey)
const quality_to_format = {
"standard": "MP3_128",
"high": "MP3_320",
"lossless": "FLAC"
}
function str2bin(str) { function str2bin(str) {
return Array.from(str).map(function (item) { return Array.from(str).map(function (item) {
return item.charCodeAt(0); return item.charCodeAt(0);
@ -118,7 +121,7 @@ function playerTokenPatch(playerToken) {
playerToken = JSON.parse(decryptHex(playerToken)) playerToken = JSON.parse(decryptHex(playerToken))
// enables 320/flac quality selection // enables 320/flac quality selection
playerToken.audio_qualities.wifi_streaming = ['low', 'standard', 'high'] playerToken.audio_qualities.wifi_streaming = ['low', 'standard', 'high', 'lossless']
// disables previews // disables previews
playerToken.streaming = true playerToken.streaming = true
playerToken.limited = false playerToken.limited = false
@ -128,74 +131,19 @@ function playerTokenPatch(playerToken) {
return encryptHex(JSON.stringify(playerToken)) return encryptHex(JSON.stringify(playerToken))
} }
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)
let json = await resp.json()
access_token = json['access_token']
// cache access token
GM_setValue('access_token', access_token)
GM_setValue('access_token_expiry', Math.floor(Date.now() / 1000) + Number(json['expires']))
return access_token
}
window.addEventListener('DOMContentLoaded', (_) => { window.addEventListener('DOMContentLoaded', (_) => {
unsafeWindow.dzPlayer.setTrackList = (function (old) { unsafeWindow.dzPlayer.setTrackList = (function (old) {
return async function (data, ...args) { return function (data, ...args) {
// don't get filesizes if account is hifi // needed for deezer's player to accept 320/flac responses
if (unsafeWindow.dzPlayer.user_status.lossless) {
return old(data, ...args)
}
let batchList = []
for (let i = 0; i < data.data.length; i++) { for (let i = 0; i < data.data.length; i++) {
const id = Number(data.data[i].SNG_ID) const id = parseInt(data.data[i].SNG_ID)
if (id >= 0) { // we don't need filesizes for user-upped tracks if (id >= 0) { // don't change filesizes on user-upped tracks
batchList.push({ 'relative_url': `/track/${id}` }) data.data[i].FILESIZE_MP3_320 = '1'
data.data[i].FILESIZE_FLAC = '1'
} }
} }
// return if all the tracks are user-upped
if (batchList.length === 0) {
return old(data, ...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 < data.data.length; i++) {
const id = Number(data.data[i].SNG_ID)
if (id < 0) { // user-uploaded track
userUppedSoFar++
continue
}
data.data[i].FILESIZE_MP3_320 = json.batch_result[i - userUppedSoFar].filesize_320
// for streaming while unlogged
data.data[i].MD5_ORIGIN = json.batch_result[i - userUppedSoFar].md5_origin
}
log(data) log(data)
return old(data, ...args) return old(data, ...args)
@ -206,6 +154,43 @@ window.addEventListener('DOMContentLoaded', (_) => {
fetchIntercept.register({ fetchIntercept.register({
request: function (url, config) { request: function (url, config) {
// Modify the url or config here // Modify the url or config here
if (url === 'https://media.deezer.com/v1/get_url') {
const quality = unsafeWindow.dzPlayer.control.getAudioQuality()
const track = unsafeWindow.dzPlayer.getCurrentSong()
const id = parseInt(track.SNG_ID)
let user_status_quality = quality
if (user_status_quality === 'high') {
user_status_quality = 'hq'
}
let is_subbed = !unsafeWindow.dzPlayer.user_status.can_subscribe
let is_quality_available = unsafeWindow.dzPlayer.user_status[user_status_quality]
// STREAM_ADS_AVAILABLE is used to check if track is restricted to premium/hifi
if (quality === 'standard' && (track.RIGHTS.STREAM_ADS_AVAILABLE === true || is_subbed)) {
is_quality_available = true
}
if (id >= 0 && !is_quality_available) {
const media_server = GM_getValue('media_server', 'https://dzmedia.herokuapp.com')
url = `${media_server}/get_url`
const body = {
formats: ['FLAC', 'MP3_320', 'MP3_128', 'MP3_64', 'MP3_MISC'],
ids: [id]
}
for (let i = 0; i < body.formats.length; i++) {
if (body.formats[0] !== quality_to_format[quality]) {
body.formats.shift()
} else {
break
}
}
config.body = JSON.stringify(body)
}
}
return [url, config]; return [url, config];
}, },
@ -229,8 +214,6 @@ 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)