Merge branch 'pythonish' of nomorecoffee/deemix1 into master
This commit is contained in:
commit
9e831328c4
|
@ -1,2 +1,2 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
#Empty File
|
# Empty File
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import wx
|
import wx
|
||||||
|
|
||||||
from deemix.ui.MainFrame import MainFrame
|
from deemix.ui.MainFrame import MainFrame
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -1,20 +1,21 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from urllib.request import urlopen
|
|
||||||
import requests
|
|
||||||
import json
|
|
||||||
import re
|
|
||||||
|
|
||||||
import hashlib
|
|
||||||
import pyaes
|
|
||||||
import binascii
|
import binascii
|
||||||
|
import hashlib
|
||||||
|
from urllib.request import urlopen
|
||||||
|
|
||||||
import blowfish
|
import blowfish
|
||||||
|
import pyaes
|
||||||
|
import requests
|
||||||
|
|
||||||
|
USER_AGENT_HEADER = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36"
|
||||||
|
|
||||||
|
|
||||||
class Deezer:
|
class Deezer:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.api_url = "http://www.deezer.com/ajax/gw-light.php"
|
self.api_url = "http://www.deezer.com/ajax/gw-light.php"
|
||||||
self.legacy_api_url = "https://api.deezer.com/"
|
self.legacy_api_url = "https://api.deezer.com/"
|
||||||
self.http_headers = {
|
self.http_headers = {
|
||||||
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36"
|
"User-Agent": USER_AGENT_HEADER
|
||||||
}
|
}
|
||||||
self.album_pictures_host = "https://e-cdns-images.dzcdn.net/images/cover/"
|
self.album_pictures_host = "https://e-cdns-images.dzcdn.net/images/cover/"
|
||||||
self.artist_pictures_host = "https://e-cdns-images.dzcdn.net/images/artist/"
|
self.artist_pictures_host = "https://e-cdns-images.dzcdn.net/images/artist/"
|
||||||
|
@ -22,75 +23,76 @@ class Deezer:
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
self.logged_in = False
|
self.logged_in = False
|
||||||
self.session.post("http://www.deezer.com/", headers=self.http_headers)
|
self.session.post("http://www.deezer.com/", headers=self.http_headers)
|
||||||
self.sid = self.session.cookies.get_dict()['sid']
|
self.sid = self.session.cookies.get('sid')
|
||||||
|
|
||||||
def get_token(self):
|
def get_token(self):
|
||||||
tokenData = self.gw_api_call('deezer.getUserData')
|
token_data = self.gw_api_call('deezer.getUserData')
|
||||||
return tokenData["results"]["checkForm"]
|
return token_data["results"]["checkForm"]
|
||||||
|
|
||||||
def get_track_MD5(self, id):
|
def get_track_md5(self, sng_id):
|
||||||
site = self.session.post("https://api.deezer.com/1.0/gateway.php",
|
site = self.session.post(
|
||||||
params = {
|
"https://api.deezer.com/1.0/gateway.php",
|
||||||
'api_key' : "4VCYIJUCDLOUELGD1V8WBVYBNVDYOXEWSLLZDONGBBDFVXTZJRXPR29JRLQFO6ZE",
|
params={
|
||||||
'sid' : self.sid,
|
'api_key': "4VCYIJUCDLOUELGD1V8WBVYBNVDYOXEWSLLZDONGBBDFVXTZJRXPR29JRLQFO6ZE",
|
||||||
'input' : '3',
|
'sid': self.sid,
|
||||||
|
'input': '3',
|
||||||
'output': '3',
|
'output': '3',
|
||||||
'method' : 'song_getData'
|
'method': 'song_getData'
|
||||||
},
|
},
|
||||||
data = json.dumps({'sng_id': id}),
|
json={'sng_id': sng_id},
|
||||||
headers = self.http_headers
|
headers=self.http_headers
|
||||||
)
|
)
|
||||||
response = json.loads(site.text)
|
response = site.json()
|
||||||
return response['results']['PUID']
|
return response['results']['PUID']
|
||||||
|
|
||||||
def gw_api_call(self, method, args={}):
|
def gw_api_call(self, method, args={}):
|
||||||
result = self.session.post(
|
result = self.session.post(
|
||||||
self.api_url,
|
self.api_url,
|
||||||
params = {
|
params={
|
||||||
'api_version' : "1.0",
|
'api_version': "1.0",
|
||||||
'api_token' : 'null' if method == 'deezer.getUserData' else self.get_token(),
|
'api_token': 'null' if method == 'deezer.getUserData' else self.get_token(),
|
||||||
'input' : '3',
|
'input': '3',
|
||||||
'method' : method
|
'method': method
|
||||||
},
|
},
|
||||||
data = json.dumps(args),
|
json=args,
|
||||||
headers = self.http_headers
|
headers=self.http_headers
|
||||||
)
|
)
|
||||||
result = json.loads(result.text)
|
return result.json()
|
||||||
return result
|
|
||||||
|
|
||||||
def api_call(self, method, args={}):
|
def api_call(self, method, args={}):
|
||||||
result = self.session.get(
|
result = self.session.get(
|
||||||
self.legacy_api_url+method,
|
self.legacy_api_url + method,
|
||||||
params = args,
|
params=args,
|
||||||
headers = self.http_headers
|
headers=self.http_headers
|
||||||
)
|
)
|
||||||
result_json = json.loads(result.text)
|
result_json = result.json()
|
||||||
if 'error' in result_json.keys():
|
if 'error' in result_json.keys():
|
||||||
raise APIError()
|
raise APIError()
|
||||||
return result_json
|
return result_json
|
||||||
|
|
||||||
def login(self, email, password, reCaptchaToken):
|
def login(self, email, password, re_captcha_token):
|
||||||
checkFormLogin = self.gw_api_call("deezer.getUserData")
|
check_form_login = self.gw_api_call("deezer.getUserData")
|
||||||
login = self.session.post(
|
login = self.session.post(
|
||||||
"https://www.deezer.com/ajax/action.php",
|
"https://www.deezer.com/ajax/action.php",
|
||||||
data={
|
data={
|
||||||
'type':'login',
|
'type': 'login',
|
||||||
'mail':email,
|
'mail': email,
|
||||||
'password':password,
|
'password': password,
|
||||||
'checkFormLogin':checkFormLogin['results']['checkFormLogin'],
|
'checkFormLogin': check_form_login['results']['checkFormLogin'],
|
||||||
'reCaptchaToken': reCaptchaToken
|
'reCaptchaToken': re_captcha_token
|
||||||
},
|
},
|
||||||
headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}.update(self.http_headers)
|
headers={'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', **self.http_headers}
|
||||||
)
|
)
|
||||||
if not 'success' in login.text:
|
if 'success' not in login.text:
|
||||||
self.logged_in = False
|
self.logged_in = False
|
||||||
return False
|
return False
|
||||||
userData = self.gw_api_call("deezer.getUserData")
|
user_data = self.gw_api_call("deezer.getUserData")
|
||||||
self.user = {
|
self.user = {
|
||||||
'email': email,
|
'email': email,
|
||||||
'id': userData["results"]["USER"]["USER_ID"],
|
'id': user_data["results"]["USER"]["USER_ID"],
|
||||||
'name': userData["results"]["USER"]["BLOG_NAME"],
|
'name': user_data["results"]["USER"]["BLOG_NAME"],
|
||||||
'picture': userData["results"]["USER"]["USER_PICTURE"] if "USER_PICTURE" in userData["results"]["USER"] else ""
|
'picture': user_data["results"]["USER"]["USER_PICTURE"] if "USER_PICTURE" in user_data["results"][
|
||||||
|
"USER"] else ""
|
||||||
}
|
}
|
||||||
self.logged_in = True
|
self.logged_in = True
|
||||||
return True
|
return True
|
||||||
|
@ -104,38 +106,39 @@ class Deezer:
|
||||||
rest={'HttpOnly': True}
|
rest={'HttpOnly': True}
|
||||||
)
|
)
|
||||||
self.session.cookies.set_cookie(cookie_obj)
|
self.session.cookies.set_cookie(cookie_obj)
|
||||||
userData = self.gw_api_call("deezer.getUserData")
|
user_data = self.gw_api_call("deezer.getUserData")
|
||||||
if (userData["results"]["USER"]["USER_ID"] == 0):
|
if user_data["results"]["USER"]["USER_ID"] == 0:
|
||||||
self.logged_in = False
|
self.logged_in = False
|
||||||
return False
|
return False
|
||||||
self.user = {
|
self.user = {
|
||||||
'id': userData["results"]["USER"]["USER_ID"],
|
'id': user_data["results"]["USER"]["USER_ID"],
|
||||||
'name': userData["results"]["USER"]["BLOG_NAME"],
|
'name': user_data["results"]["USER"]["BLOG_NAME"],
|
||||||
'picture': userData["results"]["USER"]["USER_PICTURE"] if "USER_PICTURE" in userData["results"]["USER"] else ""
|
'picture': user_data["results"]["USER"]["USER_PICTURE"] if "USER_PICTURE" in user_data["results"][
|
||||||
|
"USER"] else ""
|
||||||
}
|
}
|
||||||
self.logged_in = True
|
self.logged_in = True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_track_gw(self, id):
|
def get_track_gw(self, sng_id):
|
||||||
if (int(id)<0):
|
if int(sng_id) < 0:
|
||||||
body = self.gw_api_call('song.getData', {'sng_id': id})
|
body = self.gw_api_call('song.getData', {'sng_id': sng_id})
|
||||||
else:
|
else:
|
||||||
body = self.gw_api_call('deezer.pageTrack', {'sng_id': id})
|
body = self.gw_api_call('deezer.pageTrack', {'sng_id': sng_id})
|
||||||
if 'LYRICS' in body['results']:
|
if 'LYRICS' in body['results']:
|
||||||
body['results']['DATA']['LYRICS'] = body['results']['LYRICS']
|
body['results']['DATA']['LYRICS'] = body['results']['LYRICS']
|
||||||
body['results'] = body['results']['DATA']
|
body['results'] = body['results']['DATA']
|
||||||
return body['results']
|
return body['results']
|
||||||
|
|
||||||
def get_tracks_gw(self, ids):
|
def get_tracks_gw(self, ids):
|
||||||
tracksArray = []
|
tracks_array = []
|
||||||
body = self.gw_api_call('song.getListData', {'sng_ids': ids})
|
body = self.gw_api_call('song.getListData', {'sng_ids': ids})
|
||||||
errors = 0
|
errors = 0
|
||||||
for i in range(len(ids)):
|
for i in range(len(ids)):
|
||||||
if ids[i] != 0:
|
if ids[i] != 0:
|
||||||
tracksArray.append(body['results']['data'][i-errors])
|
tracks_array.append(body['results']['data'][i - errors])
|
||||||
else:
|
else:
|
||||||
errors += 1
|
errors += 1
|
||||||
tracksArray.append({
|
tracks_array.append({
|
||||||
'SNG_ID': 0,
|
'SNG_ID': 0,
|
||||||
'SNG_TITLE': '',
|
'SNG_TITLE': '',
|
||||||
'DURATION': 0,
|
'DURATION': 0,
|
||||||
|
@ -147,163 +150,166 @@ class Deezer:
|
||||||
'ART_ID': 0,
|
'ART_ID': 0,
|
||||||
'ART_NAME': ""
|
'ART_NAME': ""
|
||||||
})
|
})
|
||||||
return tracksArray
|
return tracks_array
|
||||||
|
|
||||||
def get_album_gw(self, id):
|
def get_album_gw(self, alb_id):
|
||||||
body = self.gw_api_call('album.getData', {'alb_id': id})
|
body = self.gw_api_call('album.getData', {'alb_id': alb_id})
|
||||||
return body['results']
|
return body['results']
|
||||||
|
|
||||||
def get_album_tracks_gw(self, id):
|
def get_album_tracks_gw(self, alb_id):
|
||||||
tracksArray = []
|
tracks_array = []
|
||||||
body = self.gw_api_call('song.getListByAlbum', {'alb_id': id, 'nb': -1})
|
body = self.gw_api_call('song.getListByAlbum', {'alb_id': alb_id, 'nb': -1})
|
||||||
for track in body['results']['data']:
|
for track in body['results']['data']:
|
||||||
_track = track
|
_track = track
|
||||||
_track['position'] = body['results']['data'].index(track)
|
_track['position'] = body['results']['data'].index(track)
|
||||||
tracksArray.append(_track)
|
tracks_array.append(_track)
|
||||||
return tracksArray
|
return tracks_array
|
||||||
|
|
||||||
def get_artist_gw(self, id):
|
def get_artist_gw(self, art_id):
|
||||||
body = self.gw_api_call('deezer.pageArtist', {'art_id': id})
|
body = self.gw_api_call('deezer.pageArtist', {'art_id': art_id})
|
||||||
return body
|
return body
|
||||||
|
|
||||||
def get_playlist_gw(self, id):
|
def get_playlist_gw(self, playlist_id):
|
||||||
body = self.gw_api_call('deezer.pagePlaylist', {'playlist_id': id})
|
body = self.gw_api_call('deezer.pagePlaylist', {'playlist_id': playlist_id})
|
||||||
return body
|
return body
|
||||||
|
|
||||||
def get_playlist_tracks_gw(self, id):
|
def get_playlist_tracks_gw(self, playlist_id):
|
||||||
tracksArray = []
|
tracks_array = []
|
||||||
body = self.gw_api_call('playlist.getSongs', {'playlist_id': id, 'nb': -1})
|
body = self.gw_api_call('playlist.getSongs', {'playlist_id': playlist_id, 'nb': -1})
|
||||||
for track in body['results']['data']:
|
for track in body['results']['data']:
|
||||||
_track = track
|
track['position'] = body['results']['data'].index(track)
|
||||||
_track['position'] = body['results']['data'].index(track)
|
tracks_array.append(track)
|
||||||
tracksArray.append(_track)
|
return tracks_array
|
||||||
return tracksArray
|
|
||||||
|
|
||||||
def get_artist_toptracks_gw(self, id):
|
def get_artist_toptracks_gw(self, art_id):
|
||||||
tracksArray = []
|
tracks_array = []
|
||||||
body = self.gw_api_call('artist.getTopTrack', {'art_id': id, 'nb': 100})
|
body = self.gw_api_call('artist.getTopTrack', {'art_id': art_id, 'nb': 100})
|
||||||
for track in body['results']['data']:
|
for track in body['results']['data']:
|
||||||
_track = track
|
track['position'] = body['results']['data'].index(track)
|
||||||
_track['position'] = body['results']['data'].index(track)
|
tracks_array.append(track)
|
||||||
tracksArray.append(_track)
|
return tracks_array
|
||||||
return tracksArray
|
|
||||||
|
|
||||||
def get_lyrics_gw(self, id):
|
def get_lyrics_gw(self, sng_id):
|
||||||
body = self.gw_api_call('song.getLyrics', {'sng_id': id})
|
body = self.gw_api_call('song.getLyrics', {'sng_id': sng_id})
|
||||||
lyr = {}
|
lyr = {
|
||||||
lyr['unsyncLyrics'] = {
|
'unsyncLyrics': {
|
||||||
'description': "",
|
'description': "",
|
||||||
'lyrics': body["results"]["LYRICS_TEXT"]
|
'lyrics': body["results"]["LYRICS_TEXT"]
|
||||||
|
},
|
||||||
|
'syncLyrics': "",
|
||||||
}
|
}
|
||||||
lyr['syncLyrics'] = ""
|
|
||||||
for i in range(len(body["results"]["LYRICS_SYNC_JSON"])):
|
for i in range(len(body["results"]["LYRICS_SYNC_JSON"])):
|
||||||
if "lrc_timestamp" in body["results"]["LYRICS_SYNC_JSON"][i]:
|
if "lrc_timestamp" in body["results"]["LYRICS_SYNC_JSON"][i]:
|
||||||
lyr['syncLyrics'] += body["results"]["LYRICS_SYNC_JSON"][i]["lrc_timestamp"] + body["results"]["LYRICS_SYNC_JSON"][i]["line"]+"\r\n"
|
lyr['syncLyrics'] += body["results"]["LYRICS_SYNC_JSON"][i]["lrc_timestamp"] + \
|
||||||
elif i+1 < len(body["results"]["LYRICS_SYNC_JSON"]):
|
body["results"]["LYRICS_SYNC_JSON"][i]["line"] + "\r\n"
|
||||||
lyr['syncLyrics'] += body["results"]["LYRICS_SYNC_JSON"][i+1]["lrc_timestamp"] + body["results"]["LYRICS_SYNC_JSON"][i]["line"]+"\r\n"
|
elif i + 1 < len(body["results"]["LYRICS_SYNC_JSON"]):
|
||||||
|
lyr['syncLyrics'] += body["results"]["LYRICS_SYNC_JSON"][i + 1]["lrc_timestamp"] + \
|
||||||
|
body["results"]["LYRICS_SYNC_JSON"][i]["line"] + "\r\n"
|
||||||
return lyr
|
return lyr
|
||||||
|
|
||||||
def get_user_playlist(self, id):
|
def get_user_playlist(self, user_id):
|
||||||
body = self.api_call('user/'+str(id)+'/playlists', {'limit': -1})
|
body = self.api_call('user/' + str(user_id) + '/playlists', {'limit': -1})
|
||||||
return body
|
return body
|
||||||
|
|
||||||
def get_track(self, id):
|
def get_track(self, user_id):
|
||||||
body = self.api_call('track/'+str(id))
|
body = self.api_call('track/' + str(user_id))
|
||||||
return body
|
return body
|
||||||
|
|
||||||
def get_track_by_ISRC(self, isrc):
|
def get_track_by_ISRC(self, isrc):
|
||||||
body = self.api_call('track/isrc:'+isrc)
|
body = self.api_call('track/isrc:' + isrc)
|
||||||
return body
|
return body
|
||||||
|
|
||||||
def get_charts_top_country(self):
|
def get_charts_top_country(self):
|
||||||
return self.get_user_playlist('637006841')
|
return self.get_user_playlist('637006841')
|
||||||
|
|
||||||
def get_playlist(self, id):
|
def get_playlist(self, playlist_id):
|
||||||
body = self.api_call('playlist/'+str(id))
|
body = self.api_call('playlist/' + str(playlist_id))
|
||||||
return body
|
return body
|
||||||
|
|
||||||
def get_playlist_tracks(self, id):
|
def get_playlist_tracks(self, playlist_id):
|
||||||
body = self.api_call('playlist/'+str(id)+'/tracks', {'limit': -1})
|
body = self.api_call('playlist/' + str(playlist_id) + '/tracks', {'limit': -1})
|
||||||
return body
|
return body
|
||||||
|
|
||||||
def get_album(self, id):
|
def get_album(self, album_id):
|
||||||
body = self.api_call('album/'+str(id))
|
body = self.api_call('album/' + str(album_id))
|
||||||
return body
|
return body
|
||||||
|
|
||||||
def get_album_by_UPC(self, upc):
|
def get_album_by_UPC(self, upc):
|
||||||
body = self.api_call('album/upc:'+str(upc))
|
body = self.api_call('album/upc:' + str(upc))
|
||||||
|
|
||||||
def get_album_tracks(self, id):
|
def get_album_tracks(self, album_id):
|
||||||
body = self.api_call('album/'+str(id)+'/tracks', {'limit': -1})
|
body = self.api_call('album/' + str(album_id) + '/tracks', {'limit': -1})
|
||||||
return body
|
return body
|
||||||
|
|
||||||
def get_artist(self, id):
|
def get_artist(self, artist_id):
|
||||||
body = self.api_call('artist/'+str(id))
|
body = self.api_call('artist/' + str(artist_id))
|
||||||
return body
|
return body
|
||||||
|
|
||||||
def get_artist_albums(self, id):
|
def get_artist_albums(self, artist_id):
|
||||||
body = self.api_call('artist/'+str(id)+'/albums', {'limit': -1})
|
body = self.api_call('artist/' + str(artist_id) + '/albums', {'limit': -1})
|
||||||
return body
|
return body
|
||||||
|
|
||||||
def search(self, term, type, limit = 30):
|
def search(self, term, search_type, limit=30):
|
||||||
body = self.api_call('search/'+type, {'q': term, 'limit': limit})
|
body = self.api_call('search/' + search_type, {'q': term, 'limit': limit})
|
||||||
return body
|
return body
|
||||||
|
|
||||||
def decrypt_track(self, trackId, input, output):
|
def decrypt_track(self, track_id, input, output):
|
||||||
response = open(input, 'rb')
|
response = open(input, 'rb')
|
||||||
outfile = open(output, 'wb')
|
outfile = open(output, 'wb')
|
||||||
cipher = blowfish.Cipher(str.encode(self._get_blowfish_key(str(trackId))))
|
cipher = blowfish.Cipher(str.encode(self._get_blowfish_key(str(track_id))))
|
||||||
i=0
|
i = 0
|
||||||
while True:
|
while True:
|
||||||
chunk = response.read(2048)
|
chunk = response.read(2048)
|
||||||
if not chunk:
|
if not chunk:
|
||||||
break
|
break
|
||||||
if (i % 3)==0 and len(chunk)==2048:
|
if (i % 3) == 0 and len(chunk) == 2048:
|
||||||
chunk = b"".join(cipher.decrypt_cbc(chunk,b"\x00\x01\x02\x03\x04\x05\x06\x07"))
|
chunk = b"".join(cipher.decrypt_cbc(chunk, b"\x00\x01\x02\x03\x04\x05\x06\x07"))
|
||||||
outfile.write(chunk)
|
outfile.write(chunk)
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
def stream_track(self, trackId, url, stream):
|
def stream_track(self, track_id, url, stream):
|
||||||
response = urlopen(url)
|
response = urlopen(url)
|
||||||
cipher = blowfish.Cipher(str.encode(self._get_blowfish_key(str(trackId))))
|
cipher = blowfish.Cipher(str.encode(self._get_blowfish_key(str(track_id))))
|
||||||
i=0
|
i = 0
|
||||||
while True:
|
while True:
|
||||||
chunk = response.read(2048)
|
chunk = response.read(2048)
|
||||||
if not chunk:
|
if not chunk:
|
||||||
break
|
break
|
||||||
if (i % 3)==0 and len(chunk)==2048:
|
if (i % 3) == 0 and len(chunk) == 2048:
|
||||||
chunk = b"".join(cipher.decrypt_cbc(chunk,b"\x00\x01\x02\x03\x04\x05\x06\x07"))
|
chunk = b"".join(cipher.decrypt_cbc(chunk, b"\x00\x01\x02\x03\x04\x05\x06\x07"))
|
||||||
stream.write(chunk)
|
stream.write(chunk)
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
def _md5(self, data):
|
def _md5(self, data):
|
||||||
h=hashlib.new("md5")
|
h = hashlib.new("md5")
|
||||||
h.update(str.encode(data) if isinstance(data, str) else data)
|
h.update(str.encode(data) if isinstance(data, str) else data)
|
||||||
return h.hexdigest()
|
return h.hexdigest()
|
||||||
|
|
||||||
def _ecb_crypt(self, key, data):
|
def _ecb_crypt(self, key, data):
|
||||||
res = b''
|
res = b''
|
||||||
for x in range(int(len(data)/16)):
|
for x in range(int(len(data) / 16)):
|
||||||
res += binascii.hexlify(pyaes.AESModeOfOperationECB(key).encrypt(data[:16]))
|
res += binascii.hexlify(pyaes.AESModeOfOperationECB(key).encrypt(data[:16]))
|
||||||
data = data[16:]
|
data = data[16:]
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def _get_blowfish_key(self, trackId):
|
def _get_blowfish_key(self, trackId):
|
||||||
SECRET = 'g4el58wc'+'0zvf9na1'
|
SECRET = 'g4el58wc' + '0zvf9na1'
|
||||||
idMd5 = self._md5(trackId)
|
idMd5 = self._md5(trackId)
|
||||||
bfKey = ""
|
bfKey = ""
|
||||||
for i in range(16):
|
for i in range(16):
|
||||||
bfKey += chr(ord(idMd5[i]) ^ ord(idMd5[i+16]) ^ ord(SECRET[i]))
|
bfKey += chr(ord(idMd5[i]) ^ ord(idMd5[i + 16]) ^ ord(SECRET[i]))
|
||||||
return bfKey
|
return bfKey
|
||||||
|
|
||||||
def get_track_stream_url(self, sng_id, md5, media_version, format):
|
def get_track_stream_url(self, sng_id, md5, media_version, format):
|
||||||
urlPart = b'\xa4'.join([str.encode(md5), str.encode(str(format)), str.encode(str(sng_id)), str.encode(str(media_version))])
|
urlPart = b'\xa4'.join(
|
||||||
|
[str.encode(md5), str.encode(str(format)), str.encode(str(sng_id)), str.encode(str(media_version))])
|
||||||
md5val = self._md5(urlPart)
|
md5val = self._md5(urlPart)
|
||||||
step2 = str.encode(md5val)+b'\xa4'+urlPart+b'\xa4'
|
step2 = str.encode(md5val) + b'\xa4' + urlPart + b'\xa4'
|
||||||
while len(step2)%16 > 0:
|
while len(step2) % 16 > 0:
|
||||||
step2 += b'.'
|
step2 += b'.'
|
||||||
urlPart = self._ecb_crypt(b'jo6aey6haid2Teih', step2)
|
urlPart = self._ecb_crypt(b'jo6aey6haid2Teih', step2)
|
||||||
return "https://e-cdns-proxy-" + md5[0] + ".dzcdn.net/mobile/1/" + urlPart.decode("utf-8")
|
return "https://e-cdns-proxy-" + md5[0] + ".dzcdn.net/mobile/1/" + urlPart.decode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
class APIError(Exception):
|
class APIError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
import re
|
||||||
|
|
||||||
from deemix.api.deezer import Deezer, APIError
|
from deemix.api.deezer import Deezer, APIError
|
||||||
from deemix.utils.taggers import tagID3, tagFLAC
|
from deemix.utils.taggers import tagID3, tagFLAC
|
||||||
import json
|
|
||||||
import re
|
|
||||||
|
|
||||||
extensions = {
|
extensions = {
|
||||||
9: '.flac',
|
9: '.flac',
|
||||||
|
@ -16,28 +16,30 @@ extensions = {
|
||||||
|
|
||||||
dz = Deezer()
|
dz = Deezer()
|
||||||
|
|
||||||
|
|
||||||
def getIDFromLink(link, type):
|
def getIDFromLink(link, type):
|
||||||
if '?' in link:
|
if '?' in link:
|
||||||
link = link[:link.find('?')]
|
link = link[:link.find('?')]
|
||||||
|
|
||||||
if link.startswith("http") and 'open.spotify.com/' in link:
|
if link.startswith("http") and 'open.spotify.com/' in link:
|
||||||
if type == "spotifyplaylist":
|
if type == "spotifyplaylist":
|
||||||
return link[link.find("/playlist/")+10]
|
return link[link.find("/playlist/") + 10]
|
||||||
if type == "spotifytrack":
|
if type == "spotifytrack":
|
||||||
return link[link.find("/track/")+7]
|
return link[link.find("/track/") + 7]
|
||||||
if type == "spotifyalbum":
|
if type == "spotifyalbum":
|
||||||
return link[link.find("/album/")+7]
|
return link[link.find("/album/") + 7]
|
||||||
elif link.startswith("spotify:"):
|
elif link.startswith("spotify:"):
|
||||||
if type == "spotifyplaylist":
|
if type == "spotifyplaylist":
|
||||||
return link[link.find("playlist:")+9]
|
return link[link.find("playlist:") + 9]
|
||||||
if type == "spotifytrack":
|
if type == "spotifytrack":
|
||||||
return link[link.find("track:")+6]
|
return link[link.find("track:") + 6]
|
||||||
if type == "spotifyalbum":
|
if type == "spotifyalbum":
|
||||||
return link[link.find("album:")+6]
|
return link[link.find("album:") + 6]
|
||||||
elif type == "artisttop":
|
elif type == "artisttop":
|
||||||
return re.search("\/artist\/(\d+)\/top_track",link)[1]
|
return re.search(r"\/artist\/(\d+)\/top_track", link)[1]
|
||||||
else:
|
else:
|
||||||
return link[link.rfind("/")+1:]
|
return link[link.rfind("/") + 1:]
|
||||||
|
|
||||||
|
|
||||||
def getTypeFromLink(link):
|
def getTypeFromLink(link):
|
||||||
type = ''
|
type = ''
|
||||||
|
@ -56,29 +58,30 @@ def getTypeFromLink(link):
|
||||||
type = 'playlist'
|
type = 'playlist'
|
||||||
elif '/album' in link:
|
elif '/album' in link:
|
||||||
type = 'album'
|
type = 'album'
|
||||||
elif re.search("\/artist\/(\d+)\/top_track",link):
|
elif re.search("\/artist\/(\d+)\/top_track", link):
|
||||||
type = 'artisttop'
|
type = 'artisttop'
|
||||||
elif '/artist' in link:
|
elif '/artist' in link:
|
||||||
type = 'artist'
|
type = 'artist'
|
||||||
return type
|
return type
|
||||||
|
|
||||||
|
|
||||||
def getTrackData(id):
|
def getTrackData(id):
|
||||||
if not id:
|
if not id:
|
||||||
return None
|
return None
|
||||||
trackAPI = dz.get_track_gw(id)
|
trackAPI = dz.get_track_gw(id)
|
||||||
if not 'MD5_ORIGIN' in trackAPI:
|
if not 'MD5_ORIGIN' in trackAPI:
|
||||||
trackAPI['MD5_ORIGIN'] = dz.get_track_MD5(id)
|
trackAPI['MD5_ORIGIN'] = dz.get_track_md5(id)
|
||||||
|
|
||||||
track = {}
|
track = {}
|
||||||
track['id'] = trackAPI['SNG_ID']
|
track['id'] = trackAPI['SNG_ID']
|
||||||
track['title'] = trackAPI['SNG_TITLE']
|
track['title'] = trackAPI['SNG_TITLE']
|
||||||
if trackAPI['VERSION']:
|
if trackAPI['VERSION']:
|
||||||
track['title'] += " "+trackAPI['VERSION']
|
track['title'] += " " + trackAPI['VERSION']
|
||||||
track['duration'] = trackAPI['DURATION']
|
track['duration'] = trackAPI['DURATION']
|
||||||
track['MD5'] = trackAPI['MD5_ORIGIN']
|
track['MD5'] = trackAPI['MD5_ORIGIN']
|
||||||
track['mediaVersion'] = trackAPI['MEDIA_VERSION']
|
track['mediaVersion'] = trackAPI['MEDIA_VERSION']
|
||||||
|
|
||||||
if int(track['id'])<0:
|
if int(track['id']) < 0:
|
||||||
track['filesize'] = trackAPI['FILESIZE']
|
track['filesize'] = trackAPI['FILESIZE']
|
||||||
track['album'] = {}
|
track['album'] = {}
|
||||||
track['album']['id'] = 0
|
track['album']['id'] = 0
|
||||||
|
@ -131,9 +134,11 @@ def getTrackData(id):
|
||||||
track['lyrics']['sync'] = ""
|
track['lyrics']['sync'] = ""
|
||||||
for i in range(len(trackAPI["LYRICS"]["LYRICS_SYNC_JSON"])):
|
for i in range(len(trackAPI["LYRICS"]["LYRICS_SYNC_JSON"])):
|
||||||
if "lrc_timestamp" in trackAPI["LYRICS"]["LYRICS_SYNC_JSON"][i]:
|
if "lrc_timestamp" in trackAPI["LYRICS"]["LYRICS_SYNC_JSON"][i]:
|
||||||
track['lyrics']['sync'] += trackAPI["LYRICS"]["LYRICS_SYNC_JSON"][i]["lrc_timestamp"] + trackAPI["LYRICS"]["LYRICS_SYNC_JSON"][i]["line"]+"\r\n"
|
track['lyrics']['sync'] += trackAPI["LYRICS"]["LYRICS_SYNC_JSON"][i]["lrc_timestamp"] + \
|
||||||
elif i+1 < len(trackAPI["LYRICS"]["LYRICS_SYNC_JSON"]):
|
trackAPI["LYRICS"]["LYRICS_SYNC_JSON"][i]["line"] + "\r\n"
|
||||||
track['lyrics']['sync'] += trackAPI["LYRICS"]["LYRICS_SYNC_JSON"][i+1]["lrc_timestamp"] + trackAPI["LYRICS"]["LYRICS_SYNC_JSON"][i]["line"]+"\r\n"
|
elif i + 1 < len(trackAPI["LYRICS"]["LYRICS_SYNC_JSON"]):
|
||||||
|
track['lyrics']['sync'] += trackAPI["LYRICS"]["LYRICS_SYNC_JSON"][i + 1]["lrc_timestamp"] + \
|
||||||
|
trackAPI["LYRICS"]["LYRICS_SYNC_JSON"][i]["line"] + "\r\n"
|
||||||
|
|
||||||
track['mainArtist'] = {}
|
track['mainArtist'] = {}
|
||||||
track['mainArtist']['id'] = trackAPI['ART_ID']
|
track['mainArtist']['id'] = trackAPI['ART_ID']
|
||||||
|
@ -174,7 +179,7 @@ def getTrackData(id):
|
||||||
'year': albumAPI["release_date"][0:4]
|
'year': albumAPI["release_date"][0:4]
|
||||||
}
|
}
|
||||||
track['album']['genre'] = []
|
track['album']['genre'] = []
|
||||||
if 'genres' in albumAPI and 'data' in albumAPI['genres'] and len(albumAPI['genres']['data'])>0:
|
if 'genres' in albumAPI and 'data' in albumAPI['genres'] and len(albumAPI['genres']['data']) > 0:
|
||||||
for genre in albumAPI['genres']['data']:
|
for genre in albumAPI['genres']['data']:
|
||||||
track['album']['genre'].append(genre['name'])
|
track['album']['genre'].append(genre['name'])
|
||||||
except APIError:
|
except APIError:
|
||||||
|
@ -217,6 +222,7 @@ def getTrackData(id):
|
||||||
track['album']['discTotal'] = albumAPI2['NUMBER_DISK']
|
track['album']['discTotal'] = albumAPI2['NUMBER_DISK']
|
||||||
return track
|
return track
|
||||||
|
|
||||||
|
|
||||||
def downloadTrack(id, bitrate):
|
def downloadTrack(id, bitrate):
|
||||||
# Get the metadata
|
# Get the metadata
|
||||||
track = getTrackData(id)
|
track = getTrackData(id)
|
||||||
|
@ -252,13 +258,15 @@ def downloadTrack(id, bitrate):
|
||||||
track['album']['bitrate'] = track['selectedFormat']
|
track['album']['bitrate'] = track['selectedFormat']
|
||||||
|
|
||||||
# Create the filename
|
# Create the filename
|
||||||
filename = "{artist} - {title}".format(title=track['title'], artist=track['mainArtist']['name'])+extensions[track['selectedFormat']]
|
filename = "{artist} - {title}".format(title=track['title'], artist=track['mainArtist']['name']) + extensions[
|
||||||
|
track['selectedFormat']]
|
||||||
print(filename)
|
print(filename)
|
||||||
|
|
||||||
track['downloadUrl'] = dz.get_track_stream_url(track['id'], track['MD5'], track['mediaVersion'], track['selectedFormat'])
|
track['downloadUrl'] = dz.get_track_stream_url(track['id'], track['MD5'], track['mediaVersion'],
|
||||||
|
track['selectedFormat'])
|
||||||
with open(filename, 'wb') as stream:
|
with open(filename, 'wb') as stream:
|
||||||
dz.stream_track(track['id'], track['downloadUrl'], stream)
|
dz.stream_track(track['id'], track['downloadUrl'], stream)
|
||||||
if track['selectedFormat'] in [3,1,8]:
|
if track['selectedFormat'] in [3, 1, 8]:
|
||||||
tagID3(filename, track)
|
tagID3(filename, track)
|
||||||
elif track['selectedFormat'] == 9:
|
elif track['selectedFormat'] == 9:
|
||||||
tagFLAC(filename, track)
|
tagFLAC(filename, track)
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import wx
|
import wx
|
||||||
|
|
||||||
from deemix.app.functions import downloadTrack, getIDFromLink, getTypeFromLink
|
from deemix.app.functions import downloadTrack, getIDFromLink, getTypeFromLink
|
||||||
|
|
||||||
|
|
||||||
class MainFrame(wx.Frame):
|
class MainFrame(wx.Frame):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(parent=None, title='deemix')
|
super().__init__(parent=None, title='deemix')
|
||||||
|
@ -12,19 +14,19 @@ class MainFrame(wx.Frame):
|
||||||
self.text_ctrl = wx.TextCtrl(panel)
|
self.text_ctrl = wx.TextCtrl(panel)
|
||||||
search_sizer.Add(self.text_ctrl, 1, wx.ALL, 5)
|
search_sizer.Add(self.text_ctrl, 1, wx.ALL, 5)
|
||||||
my_btn = wx.Button(panel, label='Download')
|
my_btn = wx.Button(panel, label='Download')
|
||||||
my_btn.Bind(wx.EVT_BUTTON, self.downloadTrack)
|
my_btn.Bind(wx.EVT_BUTTON, self.download_track)
|
||||||
search_sizer.Add(my_btn, 0, wx.ALL, 5)
|
search_sizer.Add(my_btn, 0, wx.ALL, 5)
|
||||||
panel.SetSizer(main_sizer)
|
panel.SetSizer(main_sizer)
|
||||||
self.Show()
|
self.Show()
|
||||||
|
|
||||||
def downloadTrack(self, event):
|
def download_track(self, event):
|
||||||
value = self.text_ctrl.GetValue()
|
value = self.text_ctrl.GetValue()
|
||||||
if not value:
|
if not value:
|
||||||
print("You didn't enter anything!")
|
print("You didn't enter anything!")
|
||||||
return None
|
return None
|
||||||
type = getTypeFromLink(value)
|
type = getTypeFromLink(value)
|
||||||
id = getIDFromLink(value,type)
|
id = getIDFromLink(value, type)
|
||||||
print(type, id)
|
print(type, id)
|
||||||
if type == "track":
|
if type == "track":
|
||||||
downloadTrack(id,9)
|
downloadTrack(id, 9)
|
||||||
self.text_ctrl.SetValue("")
|
self.text_ctrl.SetValue("")
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
#Empty File
|
# Empty File
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
#Empty File
|
# Empty File
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from mutagen.id3 import ID3, ID3NoHeaderError
|
|
||||||
from mutagen.id3 import TXXX, TIT2, TPE1, TALB, TPE2, TRCK, TPOS, TCON, TYER, TDAT, TLEN, TBPM, TPUB, TSRC, USLT, APIC, IPLS, TCOM, TCOP, TCMP
|
|
||||||
from mutagen.flac import FLAC, Picture
|
|
||||||
from urllib.request import urlopen
|
from urllib.request import urlopen
|
||||||
|
|
||||||
|
from mutagen.flac import FLAC, Picture
|
||||||
|
from mutagen.id3 import ID3, ID3NoHeaderError, TXXX, TIT2, TPE1, TALB, TPE2, TRCK, TPOS, TCON, TYER, TDAT, TLEN, TBPM, \
|
||||||
|
TPUB, TSRC, USLT, APIC, IPLS, TCOM, TCOP
|
||||||
|
|
||||||
|
|
||||||
def tagID3(stream, track):
|
def tagID3(stream, track):
|
||||||
try:
|
try:
|
||||||
tag = ID3(stream)
|
tag = ID3(stream)
|
||||||
|
@ -18,7 +20,7 @@ def tagID3(stream, track):
|
||||||
tag.add(TPOS(text=str(track['discNumber'])))
|
tag.add(TPOS(text=str(track['discNumber'])))
|
||||||
tag.add(TCON(text=track['album']['genre']))
|
tag.add(TCON(text=track['album']['genre']))
|
||||||
tag.add(TYER(text=str(track['date']['year'])))
|
tag.add(TYER(text=str(track['date']['year'])))
|
||||||
tag.add(TDAT(text=str(track['date']['month'])+str(track['date']['day'])))
|
tag.add(TDAT(text=str(track['date']['month']) + str(track['date']['day'])))
|
||||||
tag.add(TLEN(text=str(track['duration'])))
|
tag.add(TLEN(text=str(track['duration'])))
|
||||||
tag.add(TBPM(text=str(track['bpm'])))
|
tag.add(TBPM(text=str(track['bpm'])))
|
||||||
tag.add(TPUB(text=track['album']['label']))
|
tag.add(TPUB(text=track['album']['label']))
|
||||||
|
@ -28,21 +30,23 @@ def tagID3(stream, track):
|
||||||
tag.add(TXXX(desc="REPLAYGAIN_TRACK_GAIN", text=track['replayGain']))
|
tag.add(TXXX(desc="REPLAYGAIN_TRACK_GAIN", text=track['replayGain']))
|
||||||
if 'unsync' in track['lyrics']:
|
if 'unsync' in track['lyrics']:
|
||||||
tag.add(USLT(text=track['lyrics']['unsync']))
|
tag.add(USLT(text=track['lyrics']['unsync']))
|
||||||
involvedPeople = []
|
involved_people = []
|
||||||
for role in track['contributors']:
|
for role in track['contributors']:
|
||||||
if role in ['author', 'engineer', 'mixer', 'producer', 'writer']:
|
if role in ['author', 'engineer', 'mixer', 'producer', 'writer']:
|
||||||
for person in track['contributors'][role]:
|
for person in track['contributors'][role]:
|
||||||
involvedPeople.append([role,person])
|
involved_people.append([role, person])
|
||||||
elif role == 'composer':
|
elif role == 'composer':
|
||||||
tag.add(TCOM(text=track['contributors']['composer']))
|
tag.add(TCOM(text=track['contributors']['composer']))
|
||||||
if len(involvedPeople) > 0:
|
if len(involved_people) > 0:
|
||||||
tag.add(IPLS(people=involvedPeople))
|
tag.add(IPLS(people=involved_people))
|
||||||
tag.add(TCOP(text=track['copyright']))
|
tag.add(TCOP(text=track['copyright']))
|
||||||
|
|
||||||
tag.add(APIC(3, 'image/jpeg', 3, data=urlopen("http://e-cdn-images.deezer.com/images/cover/"+track["album"]['pic']+"/800x800.jpg").read()))
|
tag.add(APIC(3, 'image/jpeg', 3, data=urlopen(
|
||||||
|
"http://e-cdn-images.deezer.com/images/cover/" + track["album"]['pic'] + "/800x800.jpg").read()))
|
||||||
|
|
||||||
tag.save(stream, v1=2, v2_version=3, v23_sep=None)
|
tag.save(stream, v1=2, v2_version=3, v23_sep=None)
|
||||||
|
|
||||||
|
|
||||||
def tagFLAC(stream, track):
|
def tagFLAC(stream, track):
|
||||||
tag = FLAC(stream)
|
tag = FLAC(stream)
|
||||||
|
|
||||||
|
@ -76,7 +80,7 @@ def tagFLAC(stream, track):
|
||||||
image = Picture()
|
image = Picture()
|
||||||
image.type = 3
|
image.type = 3
|
||||||
image.mime = 'image/jpeg'
|
image.mime = 'image/jpeg'
|
||||||
image.data = urlopen("http://e-cdn-images.deezer.com/images/cover/"+track["album"]['pic']+"/800x800.jpg").read()
|
image.data = urlopen("http://e-cdn-images.deezer.com/images/cover/" + track["album"]['pic'] + "/800x800.jpg").read()
|
||||||
tag.add_picture(image)
|
tag.add_picture(image)
|
||||||
|
|
||||||
tag.save(deleteid3=True)
|
tag.save(deleteid3=True)
|
||||||
|
|
Loading…
Reference in New Issue