2020-10-09 20:52:45 +02:00
package f.f.freezer ;
import android.util.Log ;
import org.jaudiotagger.audio.AudioFile ;
import org.jaudiotagger.audio.AudioFileIO ;
import org.jaudiotagger.tag.FieldKey ;
import org.jaudiotagger.tag.Tag ;
import org.jaudiotagger.tag.TagOptionSingleton ;
import org.jaudiotagger.tag.flac.FlacTag ;
import org.jaudiotagger.tag.id3.ID3v23Tag ;
import org.jaudiotagger.tag.id3.valuepair.ImageFormats ;
2020-10-19 21:28:45 +02:00
import org.jaudiotagger.tag.images.Artwork ;
import org.jaudiotagger.tag.images.ArtworkFactory ;
2020-10-09 20:52:45 +02:00
import org.jaudiotagger.tag.reference.PictureTypes ;
import org.json.JSONArray ;
import org.json.JSONObject ;
import java.io.ByteArrayOutputStream ;
import java.io.DataOutputStream ;
import java.io.File ;
import java.io.RandomAccessFile ;
import java.net.URL ;
import java.nio.charset.StandardCharsets ;
import java.security.MessageDigest ;
import java.util.Arrays ;
import java.util.Scanner ;
import javax.crypto.Cipher ;
import javax.crypto.spec.SecretKeySpec ;
import javax.net.ssl.HttpsURLConnection ;
public class Deezer {
2020-10-10 22:51:20 +02:00
DownloadLog logger ;
2020-10-11 22:06:29 +02:00
String token ;
String arl ;
String sid ;
boolean authorized = false ;
boolean authorizing = false ;
2020-10-10 22:51:20 +02:00
2020-10-14 21:09:16 +02:00
Deezer ( ) { }
2020-10-10 22:51:20 +02:00
//Initialize for logging
2020-10-11 22:06:29 +02:00
void init ( DownloadLog logger , String arl ) {
2020-10-10 22:51:20 +02:00
this . logger = logger ;
2020-10-11 22:06:29 +02:00
this . arl = arl ;
}
//Authorize GWLight API
public void authorize ( ) {
if ( ! authorized | | sid = = null | | token = = null ) {
authorizing = true ;
try {
callGWAPI ( " deezer.getUserData " , " {} " ) ;
authorized = true ;
} catch ( Exception e ) {
logger . warn ( " Error authorizing to Deezer API! " + e . toString ( ) ) ;
}
}
authorizing = false ;
2020-10-10 22:51:20 +02:00
}
2020-10-11 22:06:29 +02:00
public JSONObject callGWAPI ( String method , String params ) throws Exception {
//Get token
if ( token = = null ) {
token = " null " ;
callGWAPI ( " deezer.getUserData " , " {} " ) ;
}
//Call
URL url = new URL ( " https://www.deezer.com/ajax/gw-light.php?method= " + method + " &input=3&api_version=1.0&api_token= " + token ) ;
2020-10-09 20:52:45 +02:00
HttpsURLConnection connection = ( HttpsURLConnection ) url . openConnection ( ) ;
connection . setConnectTimeout ( 20000 ) ;
connection . setDoOutput ( true ) ;
connection . setRequestMethod ( " POST " ) ;
connection . setRequestProperty ( " User-Agent " , " Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36 " ) ;
connection . setRequestProperty ( " Accept-Language " , " * " ) ;
connection . setRequestProperty ( " Content-Type " , " application/json " ) ;
connection . setRequestProperty ( " Accept " , " */* " ) ;
connection . setRequestProperty ( " Content-Length " , Integer . toString ( params . getBytes ( StandardCharsets . UTF_8 ) . length ) ) ;
2020-10-11 22:06:29 +02:00
String cookies = " arl= " + arl + " ; sid= " + sid ;
connection . setRequestProperty ( " Cookie " , cookies ) ;
2020-10-09 20:52:45 +02:00
//Write body
DataOutputStream wr = new DataOutputStream ( connection . getOutputStream ( ) ) ;
wr . writeBytes ( params ) ;
wr . close ( ) ;
//Get response
String data = " " ;
Scanner scanner = new Scanner ( connection . getInputStream ( ) ) ;
while ( scanner . hasNext ( ) ) {
data + = scanner . nextLine ( ) ;
}
2020-10-19 21:28:45 +02:00
//End
try {
connection . disconnect ( ) ;
scanner . close ( ) ;
} catch ( Exception e ) { }
2020-10-09 20:52:45 +02:00
//Parse JSON
JSONObject out = new JSONObject ( data ) ;
2020-10-11 22:06:29 +02:00
//Save token
if ( ( token = = null | | token . equals ( " null " ) ) & & method . equals ( " deezer.getUserData " ) ) {
token = out . getJSONObject ( " results " ) . getString ( " checkForm " ) ;
//SID
try {
String newSid = null ;
for ( String cookie : connection . getHeaderFields ( ) . get ( " Set-Cookie " ) ) {
if ( cookie . startsWith ( " sid= " ) ) {
newSid = cookie . split ( " ; " ) [ 0 ] . split ( " = " ) [ 1 ] ;
}
}
this . sid = newSid ;
} catch ( Exception ignored ) { }
}
2020-10-09 20:52:45 +02:00
return out ;
}
2020-10-11 22:06:29 +02:00
2020-10-09 20:52:45 +02:00
//api.deezer.com/$method/$param
public static JSONObject callPublicAPI ( String method , String param ) throws Exception {
URL url = new URL ( " https://api.deezer.com/ " + method + " / " + param ) ;
HttpsURLConnection connection = ( HttpsURLConnection ) url . openConnection ( ) ;
connection . setRequestMethod ( " GET " ) ;
connection . setConnectTimeout ( 20000 ) ;
connection . connect ( ) ;
//Get string data
String data = " " ;
Scanner scanner = new Scanner ( url . openStream ( ) ) ;
while ( scanner . hasNext ( ) ) {
data + = scanner . nextLine ( ) ;
}
2020-10-19 21:28:45 +02:00
//Close
try {
connection . disconnect ( ) ;
scanner . close ( ) ;
} catch ( Exception e ) { }
2020-10-09 20:52:45 +02:00
//Parse JSON
JSONObject out = new JSONObject ( data ) ;
return out ;
}
//Generate track download URL
public static String getTrackUrl ( String trackId , String md5origin , String mediaVersion , int quality ) {
try {
int magic = 164 ;
ByteArrayOutputStream step1 = new ByteArrayOutputStream ( ) ;
step1 . write ( md5origin . getBytes ( ) ) ;
step1 . write ( magic ) ;
step1 . write ( Integer . toString ( quality ) . getBytes ( ) ) ;
step1 . write ( magic ) ;
step1 . write ( trackId . getBytes ( ) ) ;
step1 . write ( magic ) ;
step1 . write ( mediaVersion . getBytes ( ) ) ;
//Get MD5
MessageDigest md5 = MessageDigest . getInstance ( " MD5 " ) ;
md5 . update ( step1 . toByteArray ( ) ) ;
byte [ ] digest = md5 . digest ( ) ;
String md5hex = bytesToHex ( digest ) . toLowerCase ( ) ;
//Step 2
ByteArrayOutputStream step2 = new ByteArrayOutputStream ( ) ;
step2 . write ( md5hex . getBytes ( ) ) ;
step2 . write ( magic ) ;
step2 . write ( step1 . toByteArray ( ) ) ;
step2 . write ( magic ) ;
//Pad step2 with dots, to get correct length
while ( step2 . size ( ) % 16 > 0 ) step2 . write ( 46 ) ;
//Prepare AES encryption
Cipher cipher = Cipher . getInstance ( " AES/ECB/NoPadding " ) ;
SecretKeySpec key = new SecretKeySpec ( " jo6aey6haid2Teih " . getBytes ( ) , " AES " ) ;
cipher . init ( Cipher . ENCRYPT_MODE , key ) ;
//Encrypt
StringBuilder step3 = new StringBuilder ( ) ;
for ( int i = 0 ; i < step2 . size ( ) / 16 ; i + + ) {
byte [ ] b = Arrays . copyOfRange ( step2 . toByteArray ( ) , i * 16 , ( i + 1 ) * 16 ) ;
step3 . append ( bytesToHex ( cipher . doFinal ( b ) ) . toLowerCase ( ) ) ;
}
//Join to URL
2021-02-09 21:14:14 +01:00
return " https://e-cdns-proxy- " + md5origin . charAt ( 0 ) + " .dzcdn.net/api/1/ " + step3 . toString ( ) ;
2020-10-09 20:52:45 +02:00
} catch ( Exception e ) {
e . printStackTrace ( ) ;
}
return null ;
}
public static String bytesToHex ( byte [ ] bytes ) {
final char [ ] HEX_ARRAY = " 0123456789ABCDEF " . toCharArray ( ) ;
char [ ] hexChars = new char [ bytes . length * 2 ] ;
for ( int j = 0 ; j < bytes . length ; j + + ) {
int v = bytes [ j ] & 0xFF ;
hexChars [ j * 2 ] = HEX_ARRAY [ v > > > 4 ] ;
hexChars [ j * 2 + 1 ] = HEX_ARRAY [ v & 0x0F ] ;
}
return new String ( hexChars ) ;
}
public static String sanitize ( String input ) {
return input . replaceAll ( " [ \\ \\ /?*:%<>| \" ] " , " " ) . replace ( " $ " , " \\ $ " ) ;
}
public static String generateFilename ( String original , JSONObject publicTrack , JSONObject publicAlbum , int newQuality ) throws Exception {
original = original . replaceAll ( " %title% " , sanitize ( publicTrack . getString ( " title " ) ) ) ;
original = original . replaceAll ( " %album% " , sanitize ( publicTrack . getJSONObject ( " album " ) . getString ( " title " ) ) ) ;
original = original . replaceAll ( " %artist% " , sanitize ( publicTrack . getJSONObject ( " artist " ) . getString ( " name " ) ) ) ;
2021-02-09 21:14:14 +01:00
original = original . replaceAll ( " %albumArtist% " , sanitize ( publicAlbum . getJSONObject ( " artist " ) . getString ( " name " ) ) ) ;
2020-10-09 20:52:45 +02:00
//Artists
String artists = " " ;
String feats = " " ;
for ( int i = 0 ; i < publicTrack . getJSONArray ( " contributors " ) . length ( ) ; i + + ) {
2020-10-19 21:28:45 +02:00
String artist = publicTrack . getJSONArray ( " contributors " ) . getJSONObject ( i ) . getString ( " name " ) ;
if ( ! artists . contains ( artist ) )
artists + = " , " + artist ;
if ( i > 0 & & ! artists . contains ( artist ) & & ! feats . contains ( artist ) )
feats + = " , " + artist ;
2020-10-09 20:52:45 +02:00
}
original = original . replaceAll ( " %artists% " , sanitize ( artists ) . substring ( 2 ) ) ;
if ( feats . length ( ) > = 2 )
original = original . replaceAll ( " %feats% " , sanitize ( feats ) . substring ( 2 ) ) ;
//Track number
int trackNumber = publicTrack . getInt ( " track_position " ) ;
original = original . replaceAll ( " %trackNumber% " , Integer . toString ( trackNumber ) ) ;
original = original . replaceAll ( " %0trackNumber% " , String . format ( " %02d " , trackNumber ) ) ;
//Year
original = original . replaceAll ( " %year% " , publicTrack . getString ( " release_date " ) . substring ( 0 , 4 ) ) ;
2020-10-10 22:51:20 +02:00
original = original . replaceAll ( " %date% " , publicTrack . getString ( " release_date " ) ) ;
2020-10-09 20:52:45 +02:00
2020-10-19 21:28:45 +02:00
//Remove leading dots
original = original . replaceAll ( " / \\ .+ " , " / " ) ;
2020-10-09 20:52:45 +02:00
if ( newQuality = = 9 ) return original + " .flac " ;
return original + " .mp3 " ;
}
2020-12-27 19:33:59 +01:00
//Deezer patched something so getting metadata of user uploaded MP3s is not working anymore
public static String generateUserUploadedMP3Filename ( String original , String title ) throws Exception {
2021-02-09 21:14:14 +01:00
String [ ] ignored = { " %feats% " , " %trackNumber% " , " %0trackNumber% " , " %year% " , " %date% " , " %album% " , " %artist% " , " %artists% " , " %albumArtist% " } ;
2020-10-12 22:49:13 +02:00
for ( String i : ignored ) {
original = original . replaceAll ( i , " " ) ;
}
2020-12-27 19:33:59 +01:00
original = original . replace ( " %title% " , sanitize ( title ) ) ;
2020-10-12 22:49:13 +02:00
return original ;
}
2020-10-09 20:52:45 +02:00
//Tag track with data from API
2020-12-04 18:02:50 +01:00
public void tagTrack ( String path , JSONObject publicTrack , JSONObject publicAlbum , String cover , JSONObject lyricsData , JSONObject privateJson , DownloadService . DownloadSettings settings ) throws Exception {
2020-10-09 20:52:45 +02:00
TagOptionSingleton . getInstance ( ) . setAndroid ( true ) ;
//Load file
AudioFile f = AudioFileIO . read ( new File ( path ) ) ;
boolean isFlac = true ;
if ( f . getAudioHeader ( ) . getFormat ( ) . contains ( " MPEG " ) ) {
f . setTag ( new ID3v23Tag ( ) ) ;
isFlac = false ;
}
Tag tag = f . getTag ( ) ;
2021-02-09 21:14:14 +01:00
if ( settings . tags . title ) tag . setField ( FieldKey . TITLE , publicTrack . getString ( " title " ) ) ;
if ( settings . tags . album ) tag . setField ( FieldKey . ALBUM , publicTrack . getJSONObject ( " album " ) . getString ( " title " ) ) ;
2020-10-09 20:52:45 +02:00
//Artist
2020-10-11 22:06:29 +02:00
String artists = " " ;
2020-10-09 20:52:45 +02:00
for ( int i = 0 ; i < publicTrack . getJSONArray ( " contributors " ) . length ( ) ; i + + ) {
2020-10-19 21:28:45 +02:00
String artist = publicTrack . getJSONArray ( " contributors " ) . getJSONObject ( i ) . getString ( " name " ) ;
if ( ! artists . contains ( artist ) )
2020-12-04 18:02:50 +01:00
artists + = settings . artistSeparator + artist ;
2020-10-09 20:52:45 +02:00
}
2021-02-09 21:14:14 +01:00
if ( settings . tags . artist ) tag . addField ( FieldKey . ARTIST , artists . substring ( settings . artistSeparator . length ( ) ) ) ;
if ( settings . tags . track ) tag . setField ( FieldKey . TRACK , String . format ( " %02d " , publicTrack . getInt ( " track_position " ) ) ) ;
if ( settings . tags . disc ) tag . setField ( FieldKey . DISC_NO , Integer . toString ( publicTrack . getInt ( " disk_number " ) ) ) ;
if ( settings . tags . albumArtist ) tag . setField ( FieldKey . ALBUM_ARTIST , publicAlbum . getJSONObject ( " artist " ) . getString ( " name " ) ) ;
if ( settings . tags . date ) tag . setField ( FieldKey . YEAR , publicTrack . getString ( " release_date " ) . substring ( 0 , 4 ) ) ;
if ( settings . tags . label ) tag . setField ( FieldKey . RECORD_LABEL , publicAlbum . getString ( " label " ) ) ;
if ( settings . tags . isrc ) tag . setField ( FieldKey . ISRC , publicTrack . getString ( " isrc " ) ) ;
if ( settings . tags . upc ) tag . setField ( FieldKey . BARCODE , publicAlbum . getString ( " upc " ) ) ;
if ( settings . tags . trackTotal ) tag . setField ( FieldKey . TRACK_TOTAL , Integer . toString ( publicAlbum . getInt ( " nb_tracks " ) ) ) ;
2020-10-11 22:06:29 +02:00
2020-10-15 20:37:36 +02:00
//BPM
if ( publicTrack . has ( " bpm " ) & & ( int ) publicTrack . getDouble ( " bpm " ) > 0 )
2021-02-09 21:14:14 +01:00
if ( settings . tags . bpm ) tag . setField ( FieldKey . BPM , Integer . toString ( ( int ) publicTrack . getDouble ( " bpm " ) ) ) ;
2020-10-15 20:37:36 +02:00
2020-10-11 22:06:29 +02:00
//Unsynced lyrics
2021-02-09 21:14:14 +01:00
if ( lyricsData ! = null & & settings . tags . lyrics ) {
2020-10-11 22:06:29 +02:00
try {
2020-10-14 21:09:16 +02:00
String lyrics = lyricsData . getString ( " LYRICS_TEXT " ) ;
2020-10-11 22:06:29 +02:00
tag . setField ( FieldKey . LYRICS , lyrics ) ;
} catch ( Exception e ) {
Log . w ( " WARN " , " Error adding unsynced lyrics! " ) ;
}
}
2020-10-09 20:52:45 +02:00
//Genres
2020-10-11 22:06:29 +02:00
String genres = " " ;
2020-10-09 20:52:45 +02:00
for ( int i = 0 ; i < publicAlbum . getJSONObject ( " genres " ) . getJSONArray ( " data " ) . length ( ) ; i + + ) {
2020-11-09 22:05:47 +01:00
String genre = publicAlbum . getJSONObject ( " genres " ) . getJSONArray ( " data " ) . getJSONObject ( 0 ) . getString ( " name " ) ;
if ( ! genres . contains ( genre ) ) {
genres + = " , " + genre ;
}
2020-10-09 20:52:45 +02:00
}
2021-02-09 21:14:14 +01:00
if ( genres . length ( ) > 2 & & settings . tags . genre )
2020-10-14 21:09:16 +02:00
tag . setField ( FieldKey . GENRE , genres . substring ( 2 ) ) ;
//Additional tags from private api
2021-02-09 21:14:14 +01:00
if ( settings . tags . contributors )
2020-10-19 21:28:45 +02:00
try {
if ( privateJson ! = null & & privateJson . has ( " SNG_CONTRIBUTORS " ) ) {
JSONObject contrib = privateJson . getJSONObject ( " SNG_CONTRIBUTORS " ) ;
//Composer
if ( contrib . has ( " composer " ) ) {
JSONArray composers = contrib . getJSONArray ( " composer " ) ;
String composer = " " ;
for ( int i = 0 ; i < composers . length ( ) ; i + + )
2020-12-04 18:02:50 +01:00
composer + = settings . artistSeparator + composers . getString ( i ) ;
2020-10-19 21:28:45 +02:00
if ( composer . length ( ) > 2 )
2020-12-04 18:02:50 +01:00
tag . setField ( FieldKey . COMPOSER , composer . substring ( settings . artistSeparator . length ( ) ) ) ;
2020-10-19 21:28:45 +02:00
}
//Engineer
if ( contrib . has ( " engineer " ) ) {
JSONArray engineers = contrib . getJSONArray ( " engineer " ) ;
String engineer = " " ;
for ( int i = 0 ; i < engineers . length ( ) ; i + + )
2020-12-04 18:02:50 +01:00
engineer + = settings . artistSeparator + engineers . getString ( i ) ;
2020-10-19 21:28:45 +02:00
if ( engineer . length ( ) > 2 )
2020-12-04 18:02:50 +01:00
tag . setField ( FieldKey . ENGINEER , engineer . substring ( settings . artistSeparator . length ( ) ) ) ;
2020-10-14 21:09:16 +02:00
}
2020-10-19 21:28:45 +02:00
//Mixer
if ( contrib . has ( " mixer " ) ) {
JSONArray mixers = contrib . getJSONArray ( " mixer " ) ;
String mixer = " " ;
for ( int i = 0 ; i < mixers . length ( ) ; i + + )
2020-12-04 18:02:50 +01:00
mixer + = settings . artistSeparator + mixers . getString ( i ) ;
2020-10-19 21:28:45 +02:00
if ( mixer . length ( ) > 2 )
2020-12-04 18:02:50 +01:00
tag . setField ( FieldKey . MIXER , mixer . substring ( settings . artistSeparator . length ( ) ) ) ;
2020-10-19 21:28:45 +02:00
}
//Producer
if ( contrib . has ( " producer " ) ) {
JSONArray producers = contrib . getJSONArray ( " producer " ) ;
String producer = " " ;
for ( int i = 0 ; i < producers . length ( ) ; i + + )
2020-12-04 18:02:50 +01:00
producer + = settings . artistSeparator + producers . getString ( i ) ;
2020-10-19 21:28:45 +02:00
if ( producer . length ( ) > 2 )
2020-12-04 18:02:50 +01:00
tag . setField ( FieldKey . MIXER , producer . substring ( settings . artistSeparator . length ( ) ) ) ;
2020-10-19 21:28:45 +02:00
}
//FLAC Only
if ( isFlac ) {
//Author
if ( contrib . has ( " author " ) ) {
JSONArray authors = contrib . getJSONArray ( " author " ) ;
String author = " " ;
for ( int i = 0 ; i < authors . length ( ) ; i + + )
2020-12-04 18:02:50 +01:00
author + = settings . artistSeparator + authors . getString ( i ) ;
2020-10-19 21:28:45 +02:00
if ( author . length ( ) > 2 )
2020-12-04 18:02:50 +01:00
( ( FlacTag ) tag ) . setField ( " AUTHOR " , author . substring ( settings . artistSeparator . length ( ) ) ) ;
2020-10-19 21:28:45 +02:00
}
//Writer
if ( contrib . has ( " writer " ) ) {
JSONArray writers = contrib . getJSONArray ( " writer " ) ;
String writer = " " ;
for ( int i = 0 ; i < writers . length ( ) ; i + + )
2020-12-04 18:02:50 +01:00
writer + = settings . artistSeparator + writers . getString ( i ) ;
2020-10-19 21:28:45 +02:00
if ( writer . length ( ) > 2 )
2020-12-04 18:02:50 +01:00
( ( FlacTag ) tag ) . setField ( " WRITER " , writer . substring ( settings . artistSeparator . length ( ) ) ) ;
2020-10-19 21:28:45 +02:00
}
2020-10-14 21:09:16 +02:00
}
}
2020-10-19 21:28:45 +02:00
} catch ( Exception e ) {
logger . warn ( " Error writing contributors data: " + e . toString ( ) ) ;
2020-10-14 21:09:16 +02:00
}
2020-10-09 20:52:45 +02:00
File coverFile = new File ( cover ) ;
boolean addCover = ( coverFile . exists ( ) & & coverFile . length ( ) > 0 ) ;
if ( isFlac ) {
//FLAC Specific tags
2021-02-09 21:14:14 +01:00
if ( settings . tags . date ) ( ( FlacTag ) tag ) . setField ( " DATE " , publicTrack . getString ( " release_date " ) ) ;
2020-10-09 20:52:45 +02:00
//Cover
2021-02-09 21:14:14 +01:00
if ( addCover & & settings . tags . albumArt ) {
2020-10-09 20:52:45 +02:00
RandomAccessFile cf = new RandomAccessFile ( coverFile , " r " ) ;
byte [ ] coverData = new byte [ ( int ) cf . length ( ) ] ;
cf . read ( coverData ) ;
tag . setField ( ( ( FlacTag ) tag ) . createArtworkField (
coverData ,
PictureTypes . DEFAULT_ID ,
ImageFormats . MIME_TYPE_JPEG ,
" cover " ,
2020-12-14 18:29:28 +01:00
settings . albumArtResolution ,
settings . albumArtResolution ,
2020-10-09 20:52:45 +02:00
24 ,
0
) ) ;
}
} else {
2021-02-09 21:14:14 +01:00
if ( addCover & & settings . tags . albumArt ) {
2020-10-19 21:28:45 +02:00
Artwork art = ArtworkFactory . createArtworkFromFile ( coverFile ) ;
//Artwork art = Artwork.createArtworkFromFile(coverFile);
2020-10-09 20:52:45 +02:00
tag . addField ( art ) ;
}
}
//Save
AudioFileIO . write ( f ) ;
}
//Create JSON file, privateJsonData = `song.getLyrics`
public static String generateLRC ( JSONObject privateJsonData , JSONObject publicTrack ) throws Exception {
String output = " " ;
//Create metadata
String title = publicTrack . getString ( " title " ) ;
String album = publicTrack . getJSONObject ( " album " ) . getString ( " title " ) ;
String artists = " " ;
for ( int i = 0 ; i < publicTrack . getJSONArray ( " contributors " ) . length ( ) ; i + + ) {
artists + = " , " + publicTrack . getJSONArray ( " contributors " ) . getJSONObject ( i ) . getString ( " name " ) ;
}
//Write metadata
output + = " [ar: " + artists . substring ( 2 ) + " ] \ r \ n[al: " + album + " ] \ r \ n[ti: " + title + " ] \ r \ n " ;
//Get lyrics
int counter = 0 ;
2020-10-14 21:09:16 +02:00
JSONArray syncLyrics = privateJsonData . getJSONArray ( " LYRICS_SYNC_JSON " ) ;
2020-10-09 20:52:45 +02:00
for ( int i = 0 ; i < syncLyrics . length ( ) ; i + + ) {
JSONObject lyric = syncLyrics . getJSONObject ( i ) ;
if ( lyric . has ( " lrc_timestamp " ) & & lyric . has ( " line " ) ) {
output + = lyric . getString ( " lrc_timestamp " ) + lyric . getString ( " line " ) + " \ r \ n " ;
counter + = 1 ;
}
}
if ( counter = = 0 ) throw new Exception ( " Empty Lyrics! " ) ;
return output ;
}
2020-11-28 22:32:17 +01:00
static class QualityInfo {
int quality ;
String md5origin ;
String mediaVersion ;
String trackId ;
int initialQuality ;
DownloadLog logger ;
QualityInfo ( int quality , String trackId , String md5origin , String mediaVersion , DownloadLog logger ) {
this . quality = quality ;
this . initialQuality = quality ;
this . trackId = trackId ;
this . mediaVersion = mediaVersion ;
this . md5origin = md5origin ;
this . logger = logger ;
}
boolean fallback ( Deezer deezer ) {
//Quality fallback
try {
qualityFallback ( ) ;
//No quality
if ( quality = = - 1 )
throw new Exception ( " No quality to fallback to! " ) ;
//Success
return true ;
} catch ( Exception e ) {
logger . warn ( " Quality fallback failed! ID: " + trackId + " " + e . toString ( ) ) ;
quality = initialQuality ;
}
//Track ID Fallback
JSONObject privateJson = null ;
try {
//Fetch meta
JSONObject privateRaw = deezer . callGWAPI ( " deezer.pageTrack " , " { \" sng_id \" : \" " + trackId + " \" } " ) ;
privateJson = privateRaw . getJSONObject ( " results " ) . getJSONObject ( " DATA " ) ;
if ( privateJson . has ( " FALLBACK " ) ) {
//Fetch new track
String fallbackId = privateJson . getJSONObject ( " FALLBACK " ) . getString ( " SNG_ID " ) ;
if ( ! fallbackId . equals ( trackId ) ) {
JSONObject newPrivate = deezer . callGWAPI ( " song.getListData " , " { \" sng_ids \" : [ " + fallbackId + " ]} " ) ;
JSONObject trackData = newPrivate . getJSONObject ( " results " ) . getJSONArray ( " data " ) . getJSONObject ( 0 ) ;
trackId = trackData . getString ( " SNG_ID " ) ;
md5origin = trackData . getString ( " MD5_ORIGIN " ) ;
mediaVersion = trackData . getString ( " MEDIA_VERSION " ) ;
return fallback ( deezer ) ;
}
}
} catch ( Exception e ) {
logger . error ( " ID fallback failed! ID: " + trackId + " " + e . toString ( ) ) ;
}
//ISRC Fallback
try {
JSONObject newTrackJson = Deezer . callPublicAPI ( " track " , " isrc: " + privateJson . getString ( " ISRC " ) ) ;
//Same track check
if ( newTrackJson . getInt ( " id " ) = = Integer . parseInt ( trackId ) ) throw new Exception ( " No more to ISRC fallback! " ) ;
//Get private data
privateJson = deezer . callGWAPI ( " song.getListData " , " { \" sng_ids \" : [ " + newTrackJson . getInt ( " id " ) + " ]} " ) ;
JSONObject trackData = privateJson . getJSONObject ( " results " ) . getJSONArray ( " data " ) . getJSONObject ( 0 ) ;
trackId = trackData . getString ( " SNG_ID " ) ;
md5origin = trackData . getString ( " MD5_ORIGIN " ) ;
mediaVersion = trackData . getString ( " MEDIA_VERSION " ) ;
return fallback ( deezer ) ;
} catch ( Exception e ) {
logger . error ( " ISRC Fallback failed, track unavailable! ID: " + trackId + " " + e . toString ( ) ) ;
}
return false ;
}
private void qualityFallback ( ) throws Exception {
//Create HEAD requests to check if exists
URL url = new URL ( getTrackUrl ( trackId , md5origin , mediaVersion , quality ) ) ;
HttpsURLConnection connection = ( HttpsURLConnection ) url . openConnection ( ) ;
connection . setRequestMethod ( " HEAD " ) ;
connection . setRequestProperty ( " User-Agent " , " Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36 " ) ;
connection . setRequestProperty ( " Accept-Language " , " * " ) ;
connection . setRequestProperty ( " Accept " , " */* " ) ;
int rc = connection . getResponseCode ( ) ;
//Track not available
if ( rc > 400 ) {
logger . warn ( " Quality fallback, response code: " + Integer . toString ( rc ) + " , current: " + Integer . toString ( quality ) ) ;
//-1 if no quality available
if ( quality = = 1 ) {
quality = - 1 ;
return ;
}
if ( quality = = 3 ) quality = 1 ;
if ( quality = = 9 ) quality = 3 ;
qualityFallback ( ) ;
}
}
}
2020-10-09 20:52:45 +02:00
}