2021-07-11 11:24:25 +00:00
|
|
|
// ==UserScript==
|
|
|
|
// @name dzunlock
|
|
|
|
// @namespace io.github.uhwot.dzunlock
|
2021-09-15 14:13:49 +00:00
|
|
|
// @description enables deezer hifi features lol
|
2021-07-11 11:24:25 +00:00
|
|
|
// @author uh wot
|
2022-08-23 12:55:07 +00:00
|
|
|
// @version 1.4.3
|
2021-07-13 11:30:22 +00:00
|
|
|
// @license GPL-3.0-only
|
2022-08-23 12:55:07 +00:00
|
|
|
// @homepageURL https://git.uhwot.cf/uhwot/dzunlock
|
2022-08-17 12:31:20 +00:00
|
|
|
// @downloadURL https://uhwotgit.fly.dev/uhwot/dzunlock/raw/branch/master/dzunlock.user.js
|
2021-07-11 11:24:25 +00:00
|
|
|
// @icon https://cdns-files.dzcdn.net/cache/images/common/favicon/favicon-96x96.852baf648e79894b668670e115e4a375.png
|
2021-10-18 21:45:17 +00:00
|
|
|
// @include /^https:\/\/www\.deezer\.com\/[a-z]{2}\/($|track|album|artist|playlist|episode|show|profile|channels|podcasts|radio|\?|#)/
|
2021-07-11 11:24:25 +00:00
|
|
|
// @match https://www.deezer.com/search/*
|
|
|
|
// @match https://www.deezer.com/account/*
|
2021-09-25 17:05:09 +00:00
|
|
|
// @match https://www.deezer.com/concert/*
|
2021-07-13 12:43:33 +00:00
|
|
|
// @match https://www.deezer.com/smarttracklist/*
|
2021-08-10 13:21:57 +00:00
|
|
|
// @require https://cdnjs.cloudflare.com/ajax/libs/aes-js/3.1.2/index.min.js
|
2021-07-13 12:41:25 +00:00
|
|
|
// @grant GM_getValue
|
2021-07-13 23:20:50 +00:00
|
|
|
// @run-at document-start
|
2021-07-11 11:24:25 +00:00
|
|
|
// ==/UserScript==
|
|
|
|
|
2021-07-11 16:26:04 +00:00
|
|
|
const debug = false
|
2021-07-11 11:24:25 +00:00
|
|
|
|
|
|
|
function log(...args) {
|
|
|
|
if (debug) {
|
|
|
|
return console.log(...args)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-10 13:21:57 +00:00
|
|
|
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)
|
|
|
|
|
2021-09-15 14:13:49 +00:00
|
|
|
const quality_to_format = {
|
|
|
|
"standard": "MP3_128",
|
|
|
|
"high": "MP3_320",
|
|
|
|
"lossless": "FLAC"
|
|
|
|
}
|
|
|
|
|
2021-08-10 13:21:57 +00:00
|
|
|
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) {
|
|
|
|
hex = aesjs.utils.hex.toBytes(hex)
|
|
|
|
return bin2str(cipher.decrypt(hex)).replace(/\0+$/, '') // removes zero-padding
|
|
|
|
}
|
|
|
|
|
|
|
|
function encryptHex(str) {
|
|
|
|
// zero-padding
|
|
|
|
if (str.length % 16) {
|
|
|
|
str += '\x00'.repeat(16 - str.length % 16)
|
|
|
|
}
|
|
|
|
|
|
|
|
return aesjs.utils.hex.fromBytes(cipher.encrypt(str2bin(str)))
|
|
|
|
}
|
|
|
|
|
|
|
|
function playerTokenPatch(playerToken) {
|
|
|
|
playerToken = JSON.parse(decryptHex(playerToken))
|
|
|
|
|
|
|
|
// enables 320/flac quality selection
|
2021-09-15 14:13:49 +00:00
|
|
|
playerToken.audio_qualities.wifi_streaming = ['low', 'standard', 'high', 'lossless']
|
2021-08-10 13:21:57 +00:00
|
|
|
// disables previews
|
|
|
|
playerToken.streaming = true
|
|
|
|
playerToken.limited = false
|
2022-02-09 23:42:01 +00:00
|
|
|
// disables skip limit on mixes
|
|
|
|
playerToken.radio_skips = 0
|
2021-08-10 13:21:57 +00:00
|
|
|
|
|
|
|
log(playerToken)
|
|
|
|
|
|
|
|
return encryptHex(JSON.stringify(playerToken))
|
|
|
|
}
|
2021-07-11 11:24:25 +00:00
|
|
|
|
2021-07-13 23:20:50 +00:00
|
|
|
window.addEventListener('DOMContentLoaded', (_) => {
|
|
|
|
unsafeWindow.dzPlayer.setTrackList = (function (old) {
|
2021-09-15 14:13:49 +00:00
|
|
|
return function (data, ...args) {
|
|
|
|
// needed for deezer's player to accept 320/flac responses
|
2021-07-13 23:20:50 +00:00
|
|
|
|
2021-07-16 11:13:43 +00:00
|
|
|
for (let i = 0; i < data.data.length; i++) {
|
2021-09-15 14:13:49 +00:00
|
|
|
const id = parseInt(data.data[i].SNG_ID)
|
|
|
|
if (id >= 0) { // don't change filesizes on user-upped tracks
|
|
|
|
data.data[i].FILESIZE_MP3_320 = '1'
|
|
|
|
data.data[i].FILESIZE_FLAC = '1'
|
2021-07-13 23:20:50 +00:00
|
|
|
}
|
2021-07-13 08:50:33 +00:00
|
|
|
}
|
2021-07-11 11:24:25 +00:00
|
|
|
|
2021-07-16 11:13:43 +00:00
|
|
|
log(data)
|
2021-07-11 11:24:25 +00:00
|
|
|
|
2021-07-16 11:13:43 +00:00
|
|
|
return old(data, ...args)
|
2021-07-13 23:20:50 +00:00
|
|
|
};
|
|
|
|
})(unsafeWindow.dzPlayer.setTrackList);
|
|
|
|
});
|
2021-07-11 11:24:25 +00:00
|
|
|
|
2022-07-24 19:09:05 +00:00
|
|
|
// https://greasyfork.org/en/scripts/38248-websocket-logger/code
|
|
|
|
unsafeWindow.WebSocket = new Proxy(unsafeWindow.WebSocket, {
|
|
|
|
construct: function (target, args, _) {
|
|
|
|
const url = args[0]
|
|
|
|
const ws = new target(url)
|
|
|
|
|
|
|
|
if (url !== 'wss://messaging.deezer.com/websocket') {
|
|
|
|
return ws
|
|
|
|
} else {
|
|
|
|
log('hooking websocket')
|
|
|
|
|
|
|
|
return new Proxy(ws, {
|
|
|
|
set: function (target, prop, val) {
|
|
|
|
if (prop == 'onmessage') {
|
|
|
|
var onmsg = val;
|
|
|
|
val = function (e) {
|
|
|
|
if (e.data.includes('SingleInstancePlayback')) {
|
|
|
|
log('preventing SingleInstancePlayback pubsub receive')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
onmsg(e)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return target[prop] = val
|
|
|
|
},
|
|
|
|
get: function (target, prop) {
|
|
|
|
var val = target[prop];
|
|
|
|
if (prop == 'send') val = function (data) {
|
|
|
|
if (data.includes('SingleInstancePlayback')) {
|
|
|
|
log('preventing SingleInstancePlayback pubsub send')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
target.send(data)
|
|
|
|
};
|
|
|
|
else if (typeof val == 'function') val = val.bind(target)
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2022-03-18 19:40:02 +00:00
|
|
|
unsafeWindow.fetch = (function (fetch) {
|
|
|
|
return async function (url, init) {
|
2021-09-15 14:13:49 +00:00
|
|
|
if (url === 'https://media.deezer.com/v1/get_url') {
|
2022-06-21 00:36:45 +00:00
|
|
|
let track
|
|
|
|
if (unsafeWindow.dzPlayer.getPosition() === 0) {
|
|
|
|
track = unsafeWindow.dzPlayer.getCurrentSong()
|
|
|
|
} else {
|
|
|
|
track = unsafeWindow.dzPlayer.getNextSong() // gapless playback
|
|
|
|
}
|
|
|
|
|
2021-09-15 14:13:49 +00:00
|
|
|
const quality = unsafeWindow.dzPlayer.control.getAudioQuality()
|
|
|
|
const id = parseInt(track.SNG_ID)
|
|
|
|
|
|
|
|
let is_subbed = !unsafeWindow.dzPlayer.user_status.can_subscribe
|
2021-09-15 15:43:53 +00:00
|
|
|
let is_quality_available = unsafeWindow.dzPlayer.user_status.audio_qualities.wifi_download.includes(quality)
|
2021-09-15 14:13:49 +00:00
|
|
|
// STREAM_ADS_AVAILABLE is used to check if track is restricted to premium/hifi
|
2021-09-15 15:43:53 +00:00
|
|
|
if (track.RIGHTS.STREAM_ADS_AVAILABLE !== true && !is_subbed) {
|
|
|
|
is_quality_available = false
|
2021-09-15 14:13:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (id >= 0 && !is_quality_available) {
|
2022-08-17 13:06:44 +00:00
|
|
|
const media_server = GM_getValue('media_server', 'https://dzmedia.fly.dev')
|
2021-09-15 14:13:49 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-18 19:40:02 +00:00
|
|
|
init.body = JSON.stringify(body)
|
2021-09-15 14:13:49 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-11 11:24:25 +00:00
|
|
|
|
2022-03-18 19:40:02 +00:00
|
|
|
let resp = await fetch(url, init)
|
2021-07-11 11:24:25 +00:00
|
|
|
|
2022-03-18 19:40:02 +00:00
|
|
|
if (url.startsWith('https://www.deezer.com/ajax/gw-light.php?method=deezer.getUserData')) {
|
|
|
|
let json = await resp.json()
|
2021-07-11 11:24:25 +00:00
|
|
|
|
|
|
|
// removes upgrade popup stuff
|
|
|
|
json.results.USER.ENTRYPOINTS = {}
|
|
|
|
// needed to play premium-restricted albums like https://www.deezer.com/album/801279
|
|
|
|
json.results.OFFER_ID = 600
|
|
|
|
// disables ads
|
|
|
|
json.results.USER.OPTIONS.ads_display = false
|
|
|
|
json.results.USER.OPTIONS.ads_audio = false
|
|
|
|
|
2021-08-10 13:21:57 +00:00
|
|
|
json.results.PLAYER_TOKEN = playerTokenPatch(json.results.PLAYER_TOKEN)
|
|
|
|
|
2021-07-11 11:24:25 +00:00
|
|
|
log(json)
|
|
|
|
|
2022-06-16 23:56:23 +00:00
|
|
|
resp = new Response(JSON.stringify(json), resp)
|
|
|
|
} else if (url.startsWith('https://www.deezer.com/ajax/gw-light.php?method=deezer.userMenu')) {
|
|
|
|
let json = await resp.json()
|
|
|
|
|
|
|
|
delete json.results.MARKETING_PUSH
|
|
|
|
delete json.results.MARKETING_PUSH_DATA
|
|
|
|
|
2022-03-18 19:40:02 +00:00
|
|
|
resp = new Response(JSON.stringify(json), resp)
|
|
|
|
} else if (url.startsWith('https://www.deezer.com/ajax/gw-light.php?method=log.listen')) {
|
|
|
|
const json = await resp.json()
|
2021-08-10 13:21:57 +00:00
|
|
|
|
|
|
|
if (typeof json.results === 'string') {
|
|
|
|
json.results = playerTokenPatch(json.results)
|
|
|
|
}
|
|
|
|
|
2022-03-18 19:40:02 +00:00
|
|
|
resp = new Response(JSON.stringify(json), resp)
|
2021-08-10 13:21:57 +00:00
|
|
|
}
|
|
|
|
|
2022-03-18 19:40:02 +00:00
|
|
|
return resp
|
|
|
|
};
|
2022-08-17 12:31:20 +00:00
|
|
|
})(unsafeWindow.fetch);
|