Rewrite track URL generation

128kbps & 320kbps for free users, FLAC requires a HiFi account
This commit is contained in:
Namkhai B 2021-07-31 16:16:24 -05:00
parent b95d319e13
commit 376d4cd19c
No known key found for this signature in database
GPG Key ID: 9DC021F538318528
2 changed files with 100 additions and 48 deletions

View File

@ -7,7 +7,7 @@ const md5 = (data: string, type: crypto.Encoding = 'ascii') => {
return md5sum.digest('hex');
};
const getSongFileName = ({MD5_ORIGIN, SNG_ID, MEDIA_VERSION}: trackType, quality: number) => {
export const getSongFileName = ({MD5_ORIGIN, SNG_ID, MEDIA_VERSION}: trackType, quality: number) => {
const step1 = [MD5_ORIGIN, quality, SNG_ID, MEDIA_VERSION].join('¤');
let step2 = md5(step1) + '¤' + step1 + '¤';
@ -68,12 +68,6 @@ export const decryptDownload = (source: Buffer, trackId: string) => {
return destBuffer;
};
/**
* @param track Track info json returned from `getTrackInfo`
* @param quality 1 = 128kbps, 3 = 320kbps and 9 = flac (around 1411kbps)
*/
export const getTrackDownloadUrl = (track: trackType, quality: number) => {
const cdn = track.MD5_ORIGIN[0]; // cdn destination
const filename = getSongFileName(track, quality); // encrypted file name
return `http://e-cdn-proxy-${cdn}.deezer.com/api/1/${filename}`;
};
export const trackIsEncrypted = (url: string) => {
return url.includes("/mobile/") || url.includes("/media/");
}

View File

@ -1,52 +1,49 @@
import axios from 'axios';
import {getSongFileName} from '../lib/decrypt';
import instance from '../lib/request';
import type {trackType} from '../types'
interface getUrlType {
data: [
{
media: [
{
cipher: {
type: 'BF_CBC_STRIPE';
};
exp: number;
format: string;
media_type: 'FULL';
nbf: number;
sources: [
{
provider: 'ec';
url: string;
},
{
provider: 'ak';
url: string;
},
];
},
];
},
];
interface userData {
license_token: string,
can_stream_lossless: boolean,
can_stream_hq: boolean,
}
let license_token: string | null = null;
class WrongLicense extends Error {
constructor(format: string) {
super()
this.name = "WrongLicense"
this.message = `Your account can't stream ${format} tracks`
}
}
const getLicenseToken = async (): Promise<string> => {
const {data} = await axios.get('https://www.deezer.com/ajax/gw-light.php', {
let user_data: userData | null = null;
const dzAuthenticate = async (): Promise<userData> => {
const {data} = await instance.get('https://www.deezer.com/ajax/gw-light.php', {
params: {
method: 'deezer.getUserData',
api_version: '1.0',
api_token: '',
cid: Math.floor(1e9 * Math.random()),
api_token: 'null',
},
});
license_token = data.results.USER.OPTIONS.license_token;
return data.results.USER.OPTIONS.license_token;
user_data = {
license_token: data.results.USER.OPTIONS.license_token,
can_stream_lossless: data.results.USER.OPTIONS.web_lossless || data.results.USER.OPTIONS.mobile_loseless,
can_stream_hq: data.results.USER.OPTIONS.web_hq || data.results.USER.OPTIONS.mobile_hq,
}
return user_data;
};
export const getTrackUrlFromServer = async (track_token: string, format: string): Promise<getUrlType> => {
const token = license_token ? license_token : await getLicenseToken();
const {data} = await axios.post('https://media.deezer.com/v1/get_url', {
license_token: token,
const getTrackUrlFromServer = async (track_token: string, format: string): Promise<string | null> => {
const user = user_data ? user_data : await dzAuthenticate();
if (
format === "FLAC" && !user.can_stream_lossless ||
format === "MP3_320" && !user.can_stream_hq
) throw new WrongLicense(format);
const {data} = await instance.post('https://media.deezer.com/v1/get_url', {
license_token: user.license_token,
media: [
{
type: 'FULL',
@ -56,5 +53,66 @@ export const getTrackUrlFromServer = async (track_token: string, format: string)
track_tokens: [track_token],
});
return data;
if (data.data.length) {
if (data.data[0].errors) {
throw new Error(`Deezer error: ${JSON.stringify(data)}`)
}
return data.data[0].media.length > 0 ? data.data[0].media[0].sources[0].url : null;
}
return null;
};
/**
* @param track Track info json returned from `getTrackInfo`
* @param quality 1 = 128kbps, 3 = 320kbps and 9 = flac (around 1411kbps)
*/
export const getTrackDownloadUrl = async (track: trackType, quality: number): Promise<string | null> => {
let url: string | null = null;
let wrongLicense;
let formatName;
if (quality === 9) {
formatName = 'FLAC';
} else if (quality === 3) {
formatName = 'MP3_320';
} else {
formatName = 'MP3_128';
}
// Get URL with the official API
try {
url = await getTrackUrlFromServer(track.TRACK_TOKEN, formatName)
if (url) {
let isUrlOk = await testUrl(url)
if (isUrlOk) return url;
}
url = null;
} catch (e) {
if (e instanceof WrongLicense) {
wrongLicense = true;
} else {
throw e;
}
url = null;
}
// Fallback to the old method
if (!url) {
const cdn = track.MD5_ORIGIN[0]; // cdn destination
const filename = getSongFileName(track, quality); // encrypted file name
url = `https://e-cdns-proxy-${cdn}.dzcdn.net/mobile/1/${filename}`;
let isUrlOk = await testUrl(url)
if (isUrlOk) return url
url = null
}
if (wrongLicense) throw new Error(`Your account can't stream ${formatName} tracks`);
return url
}
const testUrl = async (url: string): Promise<boolean> => {
try {
let response = await axios.head(url);
if (Number(response.headers["content-length"]) > 0) return true;
return false;
} catch (e) {
return false;
}
}