From f8d8f08417083206e236a732d74d69ee47074e9e Mon Sep 17 00:00:00 2001 From: RemixDev Date: Mon, 17 Feb 2020 16:46:18 +0100 Subject: [PATCH] Restructure and fixed issues --- deemix/__init__.py | 2 + deemix/__main__.py | 8 + deemix/api/__init__.py | 2 + deemix/api/deezer.py | 309 +++++++++++++++++++++++++++++++++++++++ deemix/app/functions.py | 264 +++++++++++++++++++++++++++++++++ deemix/ui/MainFrame.py | 30 ++++ deemix/ui/__init__.py | 2 + deemix/utils/__init__.py | 2 + deemix/utils/taggers.py | 82 +++++++++++ 9 files changed, 701 insertions(+) create mode 100644 deemix/__init__.py create mode 100644 deemix/__main__.py create mode 100644 deemix/api/__init__.py create mode 100755 deemix/api/deezer.py create mode 100644 deemix/app/functions.py create mode 100644 deemix/ui/MainFrame.py create mode 100644 deemix/ui/__init__.py create mode 100644 deemix/utils/__init__.py create mode 100644 deemix/utils/taggers.py diff --git a/deemix/__init__.py b/deemix/__init__.py new file mode 100644 index 0000000..c5b2a57 --- /dev/null +++ b/deemix/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python3 +#Empty File diff --git a/deemix/__main__.py b/deemix/__main__.py new file mode 100644 index 0000000..15229c4 --- /dev/null +++ b/deemix/__main__.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +import wx +from deemix.ui.MainFrame import MainFrame + +if __name__ == '__main__': + app = wx.App() + frame = MainFrame() + app.MainLoop() diff --git a/deemix/api/__init__.py b/deemix/api/__init__.py new file mode 100644 index 0000000..c5b2a57 --- /dev/null +++ b/deemix/api/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python3 +#Empty File diff --git a/deemix/api/deezer.py b/deemix/api/deezer.py new file mode 100755 index 0000000..49c5df6 --- /dev/null +++ b/deemix/api/deezer.py @@ -0,0 +1,309 @@ +#!/usr/bin/env python3 +from urllib.request import urlopen +import requests +import json +import re + +import hashlib +import pyaes +import binascii +import blowfish + +class Deezer: + def __init__(self): + self.api_url = "http://www.deezer.com/ajax/gw-light.php" + self.legacy_api_url = "https://api.deezer.com/" + 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" + } + 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.user = {} + self.session = requests.Session() + self.logged_in = False + self.session.post("http://www.deezer.com/", headers=self.http_headers) + self.sid = self.session.cookies.get_dict()['sid'] + + def get_token(self): + tokenData = self.gw_api_call('deezer.getUserData') + return tokenData["results"]["checkForm"] + + def get_track_MD5(self, id): + site = self.session.post("https://api.deezer.com/1.0/gateway.php", + params = { + 'api_key' : "4VCYIJUCDLOUELGD1V8WBVYBNVDYOXEWSLLZDONGBBDFVXTZJRXPR29JRLQFO6ZE", + 'sid' : self.sid, + 'input' : '3', + 'output': '3', + 'method' : 'song_getData' + }, + data = json.dumps({'sng_id': id}), + headers = self.http_headers + ) + response = json.loads(site.text) + return response['results']['PUID'] + + def gw_api_call(self, method, args={}): + result = self.session.post( + self.api_url, + params = { + 'api_version' : "1.0", + 'api_token' : 'null' if method == 'deezer.getUserData' else self.get_token(), + 'input' : '3', + 'method' : method + }, + data = json.dumps(args), + headers = self.http_headers + ) + result = json.loads(result.text) + return result + + def api_call(self, method, args={}): + result = self.session.get( + self.legacy_api_url+method, + params = args, + headers = self.http_headers + ) + result_json = json.loads(result.text) + if 'error' in result_json.keys(): + raise APIError() + return result_json + + def login(self, email, password, reCaptchaToken): + checkFormLogin = self.gw_api_call("deezer.getUserData") + login = self.session.post( + "https://www.deezer.com/ajax/action.php", + data={ + 'type':'login', + 'mail':email, + 'password':password, + 'checkFormLogin':checkFormLogin['results']['checkFormLogin'], + 'reCaptchaToken': reCaptchaToken + }, + headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}.update(self.http_headers) + ) + if not 'success' in login.text: + self.logged_in = False + return False + userData = self.gw_api_call("deezer.getUserData") + self.user = { + 'email': email, + 'id': userData["results"]["USER"]["USER_ID"], + 'name': userData["results"]["USER"]["BLOG_NAME"], + 'picture': userData["results"]["USER"]["USER_PICTURE"] if "USER_PICTURE" in userData["results"]["USER"] else "" + } + self.logged_in = True + return True + + def login_via_arl(self, arl): + cookie_obj = requests.cookies.create_cookie( + domain='deezer.com', + name='arl', + value=arl, + path="/", + rest={'HttpOnly': True} + ) + self.session.cookies.set_cookie(cookie_obj) + userData = self.gw_api_call("deezer.getUserData") + if (userData["results"]["USER"]["USER_ID"] == 0): + self.logged_in = False + return False + self.user = { + 'id': userData["results"]["USER"]["USER_ID"], + 'name': userData["results"]["USER"]["BLOG_NAME"], + 'picture': userData["results"]["USER"]["USER_PICTURE"] if "USER_PICTURE" in userData["results"]["USER"] else "" + } + self.logged_in = True + return True + + def get_track_gw(self, id): + if (int(id)<0): + body = self.gw_api_call('song.getData', {'sng_id': id}) + else: + body = self.gw_api_call('deezer.pageTrack', {'sng_id': id}) + if 'LYRICS' in body['results']: + body['results']['DATA']['LYRICS'] = body['results']['LYRICS'] + body['results'] = body['results']['DATA'] + return body['results'] + + def get_tracks_gw(self, ids): + tracksArray = [] + body = self.gw_api_call('song.getListData', {'sng_ids': ids}) + errors = 0 + for i in range(len(ids)): + if ids[i] != 0: + tracksArray.append(body['results']['data'][i-errors]) + else: + errors += 1 + tracksArray.append({ + 'SNG_ID': 0, + 'SNG_TITLE': '', + 'DURATION': 0, + 'MD5_ORIGIN': 0, + 'MEDIA_VERSION': 0, + 'FILESIZE': 0, + 'ALB_TITLE': "", + 'ALB_PICTURE': "", + 'ART_ID': 0, + 'ART_NAME': "" + }) + return tracksArray + + def get_album_gw(self, id): + body = self.gw_api_call('album.getData', {'alb_id': id}) + return body['results'] + + def get_album_tracks_gw(self, id): + tracksArray = [] + body = self.gw_api_call('song.getListByAlbum', {'alb_id': id, 'nb': -1}) + for track in body['results']['data']: + _track = track + _track['position'] = body['results']['data'].index(track) + tracksArray.append(_track) + return tracksArray + + def get_artist_gw(self, id): + body = self.gw_api_call('deezer.pageArtist', {'art_id': id}) + return body + + def get_playlist_gw(self, id): + body = self.gw_api_call('deezer.pagePlaylist', {'playlist_id': id}) + return body + + def get_playlist_tracks_gw(self, id): + tracksArray = [] + body = self.gw_api_call('playlist.getSongs', {'playlist_id': id, 'nb': -1}) + for track in body['results']['data']: + _track = track + _track['position'] = body['results']['data'].index(track) + tracksArray.append(_track) + return tracksArray + + def get_artist_toptracks_gw(self, id): + tracksArray = [] + body = self.gw_api_call('artist.getTopTrack', {'art_id': id, 'nb': 100}) + for track in body['results']['data']: + _track = track + _track['position'] = body['results']['data'].index(track) + tracksArray.append(_track) + return tracksArray + + def get_lyrics_gw(self, id): + body = self.gw_api_call('song.getLyrics', {'sng_id': id}) + lyr = {} + lyr['unsyncLyrics'] = { + 'description': "", + 'lyrics': body["results"]["LYRICS_TEXT"] + } + lyr['syncLyrics'] = "" + for i in range(len(body["results"]["LYRICS_SYNC_JSON"])): + 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" + 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 + + def get_user_playlist(self, id): + body = self.api_call('user/'+str(id)+'/playlists', {'limit': -1}) + return body + + def get_track(self, id): + body = self.api_call('track/'+str(id)) + return body + + def get_track_by_ISRC(self, isrc): + body = self.api_call('track/isrc:'+isrc) + return body + + def get_charts_top_country(self): + return self.get_user_playlist('637006841') + + def get_playlist(self, id): + body = self.api_call('playlist/'+str(id)) + return body + + def get_playlist_tracks(self, id): + body = self.api_call('playlist/'+str(id)+'/tracks', {'limit': -1}) + return body + + def get_album(self, id): + body = self.api_call('album/'+str(id)) + return body + + def get_album_by_UPC(self, upc): + body = self.api_call('album/upc:'+str(upc)) + + def get_album_tracks(self, id): + body = self.api_call('album/'+str(id)+'/tracks', {'limit': -1}) + return body + + def get_artist(self, id): + body = self.api_call('artist/'+str(id)) + return body + + def get_artist_albums(self, id): + body = self.api_call('artist/'+str(id)+'/albums', {'limit': -1}) + return body + + def search(self, term, type, limit = 30): + body = self.api_call('search/'+type, {'q': term, 'limit': limit}) + return body + + def decrypt_track(self, trackId, input, output): + response = open(input, 'rb') + outfile = open(output, 'wb') + cipher = blowfish.Cipher(str.encode(self._get_blowfish_key(str(trackId)))) + i=0 + while True: + chunk = response.read(2048) + if not chunk: + break + if (i % 3)==0 and len(chunk)==2048: + chunk = b"".join(cipher.decrypt_cbc(chunk,b"\x00\x01\x02\x03\x04\x05\x06\x07")) + outfile.write(chunk) + i += 1 + + def stream_track(self, trackId, url, stream): + response = urlopen(url) + cipher = blowfish.Cipher(str.encode(self._get_blowfish_key(str(trackId)))) + i=0 + while True: + chunk = response.read(2048) + if not chunk: + break + if (i % 3)==0 and len(chunk)==2048: + chunk = b"".join(cipher.decrypt_cbc(chunk,b"\x00\x01\x02\x03\x04\x05\x06\x07")) + stream.write(chunk) + i += 1 + + def _md5(self, data): + h=hashlib.new("md5") + h.update(str.encode(data) if isinstance(data, str) else data) + return h.hexdigest() + + def _ecb_crypt(self, key, data): + res = b'' + for x in range(int(len(data)/16)): + res += binascii.hexlify(pyaes.AESModeOfOperationECB(key).encrypt(data[:16])) + data = data[16:] + return res + + def _get_blowfish_key(self, trackId): + SECRET = 'g4el58wc'+'0zvf9na1' + idMd5 = self._md5(trackId) + bfKey = "" + for i in range(16): + bfKey += chr(ord(idMd5[i]) ^ ord(idMd5[i+16]) ^ ord(SECRET[i])) + return bfKey + + 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))]) + md5val = self._md5(urlPart) + step2 = str.encode(md5val)+b'\xa4'+urlPart+b'\xa4' + while len(step2)%16 > 0: + step2 += b'.' + urlPart = self._ecb_crypt(b'jo6aey6haid2Teih', step2) + return "https://e-cdns-proxy-" + md5[0] + ".dzcdn.net/mobile/1/" + urlPart.decode("utf-8") + +class APIError(Exception): + pass diff --git a/deemix/app/functions.py b/deemix/app/functions.py new file mode 100644 index 0000000..fd8cb1b --- /dev/null +++ b/deemix/app/functions.py @@ -0,0 +1,264 @@ +#!/usr/bin/env python3 +from deemix.api.deezer import Deezer, APIError +from deemix.utils.taggers import tagID3, tagFLAC +import json +import re + +extensions = { + 9: '.flac', + 3: '.mp3', + 1: '.mp3', + 8: '.mp3', + 15: '.mp4', + 14: '.mp4', + 13: '.mp4' +} + +dz = Deezer() + +def getIDFromLink(link, type): + if '?' in link: + link = link[:link.find('?')] + + if link.startswith("http") and 'open.spotify.com/' in link: + if type == "spotifyplaylist": + return link[link.find("/playlist/")+10] + if type == "spotifytrack": + return link[link.find("/track/")+7] + if type == "spotifyalbum": + return link[link.find("/album/")+7] + elif link.startswith("spotify:"): + if type == "spotifyplaylist": + return link[link.find("playlist:")+9] + if type == "spotifytrack": + return link[link.find("track:")+6] + if type == "spotifyalbum": + return link[link.find("album:")+6] + elif type == "artisttop": + return re.search("\/artist\/(\d+)\/top_track",link)[1] + else: + return link[link.rfind("/")+1:] + +def getTypeFromLink(link): + type = '' + if 'spotify' in link: + type = 'spotify' + if 'playlist' in link: + type += 'playlist' + elif 'track' in link: + type += 'track' + elif 'album' in link: + type += 'album' + elif 'deezer' in link: + if '/track' in link: + type = 'track' + elif '/playlist' in link: + type = 'playlist' + elif '/album' in link: + type = 'album' + elif re.search("\/artist\/(\d+)\/top_track",link): + type = 'artisttop' + elif '/artist' in link: + type = 'artist' + return type + +def getTrackData(id): + if not id: + return None + trackAPI = dz.get_track_gw(id) + if not 'MD5_ORIGIN' in trackAPI: + trackAPI['MD5_ORIGIN'] = dz.get_track_MD5(id) + + track = {} + track['id'] = trackAPI['SNG_ID'] + track['title'] = trackAPI['SNG_TITLE'] + if trackAPI['VERSION']: + track['title'] += " "+trackAPI['VERSION'] + track['duration'] = trackAPI['DURATION'] + track['MD5'] = trackAPI['MD5_ORIGIN'] + track['mediaVersion'] = trackAPI['MEDIA_VERSION'] + + if int(track['id'])<0: + track['filesize'] = trackAPI['FILESIZE'] + track['album'] = {} + track['album']['id'] = 0 + track['album']['title'] = trackAPI['ALB_TITLE'] + if 'ALB_PICTURE' in trackAPI: + track['album']['pic'] = trackAPI['ALB_PICTURE'] + track['mainArtist'] = {} + track['mainArtist']['id'] = 0 + track['mainArtist']['name'] = trackAPI['ART_NAME'] + track['artistArray'] = [trackAPI['ART_NAME']] + track['date'] = { + 'day': 0, + 'month': 0, + 'year': 0 + } + track['localTrack'] = True + return track + + track['filesize'] = {} + track['filesize']['default'] = int(trackAPI['FILESIZE']) if 'FILESIZE' in trackAPI else None + track['filesize']['mp3_128'] = int(trackAPI['FILESIZE_MP3_128']) if 'FILESIZE_MP3_128' in trackAPI else None + track['filesize']['mp3_320'] = int(trackAPI['FILESIZE_MP3_320']) if 'FILESIZE_MP3_320' in trackAPI else None + track['filesize']['flac'] = int(trackAPI['FILESIZE_FLAC']) if 'FILESIZE_FLAC' in trackAPI else None + track['filesize']['mp4_ra1'] = int(trackAPI['FILESIZE_MP4_RA1']) if 'FILESIZE_MP4_RA1' in trackAPI else None + track['filesize']['mp4_ra2'] = int(trackAPI['FILESIZE_MP4_RA2']) if 'FILESIZE_MP4_RA2' in trackAPI else None + track['filesize']['mp4_ra3'] = int(trackAPI['FILESIZE_MP4_RA3']) if 'FILESIZE_MP4_RA3' in trackAPI else None + + if 'DISK_NUMBER' in trackAPI: + track['discNumber'] = trackAPI['DISK_NUMBER'] + if 'EXPLICIT_LYRICS' in trackAPI: + track['explicit'] = trackAPI['EXPLICIT_LYRICS'] != "0" + if 'COPYRIGHT' in trackAPI: + track['copyright'] = trackAPI['COPYRIGHT'] + track['replayGain'] = "{0:.2f} dB".format((float(trackAPI['GAIN']) + 18.4) * -1) + track['ISRC'] = trackAPI['ISRC'] + track['trackNumber'] = trackAPI['TRACK_NUMBER'] + if 'FALLBACK' in trackAPI: + track['fallbackId'] = trackAPI['FALLBACK']['SNG_ID'] + else: + track['fallbackId'] = 0 + track['contributors'] = trackAPI['SNG_CONTRIBUTORS'] + + track['lyrics'] = {} + if 'LYRICS_ID' in trackAPI: + track['lyrics']['id'] = trackAPI['LYRICS_ID'] + if "LYRICS" in trackAPI: + if "LYRICS_TEXT" in trackAPI["LYRICS"]: + track['lyrics']['unsync'] = trackAPI["LYRICS"]["LYRICS_TEXT"] + if "LYRICS_SYNC_JSON" in trackAPI["LYRICS"]: + track['lyrics']['sync'] = "" + for i in range(len(trackAPI["LYRICS"]["LYRICS_SYNC_JSON"])): + 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" + 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']['id'] = trackAPI['ART_ID'] + track['mainArtist']['name'] = trackAPI['ART_NAME'] + if 'ART_PICTURE' in trackAPI: + track['mainArtist']['pic'] = trackAPI['ART_PICTURE'] + + if 'PHYSICAL_RELEASE_DATE' in trackAPI: + track['date'] = { + 'day': trackAPI["PHYSICAL_RELEASE_DATE"][8:10], + 'month': trackAPI["PHYSICAL_RELEASE_DATE"][5:7], + 'year': trackAPI["PHYSICAL_RELEASE_DATE"][0:4] + } + + track['album'] = {} + track['album']['id'] = trackAPI['ALB_ID'] + track['album']['title'] = trackAPI['ALB_TITLE'] + if 'ALB_PICTURE' in trackAPI: + track['album']['pic'] = trackAPI['ALB_PICTURE'] + + try: + albumAPI = dz.get_album(track['album']['id']) + track['album']['artist'] = { + 'id': albumAPI['artist']['id'], + 'name': albumAPI['artist']['name'], + 'pic': albumAPI['artist']['picture_small'][46:-24] + } + track['album']['trackTotal'] = albumAPI['nb_tracks'] + track['album']['recordType'] = albumAPI['record_type'] + track['album']['barcode'] = albumAPI['upc'] if 'upc' in albumAPI else None + track['album']['label'] = albumAPI['label'] if 'label' in albumAPI else None + if not 'pic' in track['album']: + track['album']['pic'] = albumAPI['cover_small'][43:-24] + if 'release_date' in albumAPI: + track['date'] = { + 'day': albumAPI["release_date"][8:10], + 'month': albumAPI["release_date"][5:7], + 'year': albumAPI["release_date"][0:4] + } + track['album']['genre'] = [] + if 'genres' in albumAPI and 'data' in albumAPI['genres'] and len(albumAPI['genres']['data'])>0: + for genre in albumAPI['genres']['data']: + track['album']['genre'].append(genre['name']) + except APIError: + albumAPI = dz.get_album_gw(track['album']['id']) + track['album']['artist'] = { + 'id': albumAPI['ART_ID'], + 'name': albumAPI['ART_NAME'] + } + track['album']['trackTotal'] = albumAPI['NUMBER_TRACK'] + track['album']['discTotal'] = albumAPI['NUMBER_DISK'] + track['album']['recordType'] = trackAPI['TYPE'] + track['album']['barcode'] = None + track['album']['label'] = albumAPI['LABEL_NAME'] if 'LABEL_NAME' in albumAPI else None + if not 'pic' in track['album']: + track['album']['pic'] = albumAPI['ALB_PICTURE'] + if 'PHYSICAL_RELEASE_DATE' in albumAPI: + track['date'] = { + 'day': albumAPI["PHYSICAL_RELEASE_DATE"][8:10], + 'month': albumAPI["PHYSICAL_RELEASE_DATE"][5:7], + 'year': albumAPI["PHYSICAL_RELEASE_DATE"][0:4] + } + track['album']['genre'] = [] + + trackAPI2 = dz.get_track(track['id']) + track['bpm'] = trackAPI2['bpm'] + if not 'replayGain' in track: + track['replayGain'] = "{0:.2f} dB".format((float(trackAPI2['gain']) + 18.4) * -1) + if not 'explicit' in track: + track['explicit'] = trackAPI2['explicit_lyrics'] + track['artist'] = {} + track['artists'] = [] + for artist in trackAPI2['contributors']: + track['artists'].append(artist['name']) + if not artist['role'] in track['artist']: + track['artist'][artist['role']] = [] + track['artist'][artist['role']].append(artist['name']) + + if not 'discTotal' in track['album']: + albumAPI2 = dz.get_album_gw(track['album']['id']) + track['album']['discTotal'] = albumAPI2['NUMBER_DISK'] + return track + +def downloadTrack(id, bitrate): + # Get the metadata + track = getTrackData(id) + + # Get the selected bitrate + bitrateFound = False; + if int(bitrate) == 9: + track['selectedFormat'] = 9 + track['selectedFilesize'] = track['filesize']['flac'] + if track['filesize']['flac'] > 0: + bitrateFound = True + else: + bitrateFound = False + bitrate = 3 + if int(bitrate) == 3: + track['selectedFormat'] = 3 + track['selectedFilesize'] = track['filesize']['mp3_320'] + if track['filesize']['mp3_320'] > 0: + bitrateFound = True + else: + bitrateFound = False + bitrate = 1 + if int(bitrate) == 1: + track['selectedFormat'] = 3 + track['selectedFilesize'] = track['filesize']['mp3_320'] + if track['filesize']['mp3_320'] > 0: + bitrateFound = True + else: + bitrateFound = False + if not bitrateFound: + track['selectedFormat'] = 8 + track['selectedFilesize'] = track['filesize']['default'] + track['album']['bitrate'] = track['selectedFormat'] + + # Create the filename + filename = "{artist} - {title}".format(title=track['title'], artist=track['mainArtist']['name'])+extensions[track['selectedFormat']] + print(filename) + + track['downloadUrl'] = dz.get_track_stream_url(track['id'], track['MD5'], track['mediaVersion'], track['selectedFormat']) + with open(filename, 'wb') as stream: + dz.stream_track(track['id'], track['downloadUrl'], stream) + if track['selectedFormat'] in [3,1,8]: + tagID3(filename, track) + elif track['selectedFormat'] == 9: + tagFLAC(filename, track) diff --git a/deemix/ui/MainFrame.py b/deemix/ui/MainFrame.py new file mode 100644 index 0000000..190231b --- /dev/null +++ b/deemix/ui/MainFrame.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +import wx +from deemix.app.functions import downloadTrack, getIDFromLink, getTypeFromLink + +class MainFrame(wx.Frame): + def __init__(self): + super().__init__(parent=None, title='deemix') + panel = wx.Panel(self) + main_sizer = wx.BoxSizer(wx.VERTICAL) + search_sizer = wx.BoxSizer(wx.HORIZONTAL) + main_sizer.Add(search_sizer, 0, wx.EXPAND, 5) + self.text_ctrl = wx.TextCtrl(panel) + search_sizer.Add(self.text_ctrl, 1, wx.ALL, 5) + my_btn = wx.Button(panel, label='Download') + my_btn.Bind(wx.EVT_BUTTON, self.downloadTrack) + search_sizer.Add(my_btn, 0, wx.ALL, 5) + panel.SetSizer(main_sizer) + self.Show() + + def downloadTrack(self, event): + value = self.text_ctrl.GetValue() + if not value: + print("You didn't enter anything!") + return None + type = getTypeFromLink(value) + id = getIDFromLink(value,type) + print(type, id) + if type == "track": + downloadTrack(id,9) + self.text_ctrl.SetValue("") diff --git a/deemix/ui/__init__.py b/deemix/ui/__init__.py new file mode 100644 index 0000000..c5b2a57 --- /dev/null +++ b/deemix/ui/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python3 +#Empty File diff --git a/deemix/utils/__init__.py b/deemix/utils/__init__.py new file mode 100644 index 0000000..c5b2a57 --- /dev/null +++ b/deemix/utils/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python3 +#Empty File diff --git a/deemix/utils/taggers.py b/deemix/utils/taggers.py new file mode 100644 index 0000000..11a816a --- /dev/null +++ b/deemix/utils/taggers.py @@ -0,0 +1,82 @@ +#!/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 + +def tagID3(stream, track): + try: + tag = ID3(stream) + except ID3NoHeaderError: + tag = ID3() + + tag.add(TIT2(text=track['title'])) + tag.add(TPE1(text=track['artists'])) + tag.add(TALB(text=track['album']['title'])) + tag.add(TPE2(text=track['album']['artist']['name'])) + tag.add(TRCK(text=str(track['trackNumber']))) + tag.add(TPOS(text=str(track['discNumber']))) + tag.add(TCON(text=track['album']['genre'])) + tag.add(TYER(text=str(track['date']['year']))) + tag.add(TDAT(text=str(track['date']['month'])+str(track['date']['day']))) + tag.add(TLEN(text=str(track['duration']))) + tag.add(TBPM(text=str(track['bpm']))) + tag.add(TPUB(text=track['album']['label'])) + tag.add(TSRC(text=track['ISRC'])) + tag.add(TXXX(desc="BARCODE", text=track['album']['barcode'])) + tag.add(TXXX(desc="ITUNESADVISORY", text="1" if track['explicit'] else "0")) + tag.add(TXXX(desc="REPLAYGAIN_TRACK_GAIN", text=track['replayGain'])) + if 'unsync' in track['lyrics']: + tag.add(USLT(text=track['lyrics']['unsync'])) + involvedPeople = [] + for role in track['contributors']: + if role in ['author', 'engineer', 'mixer', 'producer', 'writer']: + for person in track['contributors'][role]: + involvedPeople.append([role,person]) + elif role == 'composer': + tag.add(TCOM(text=track['contributors']['composer'])) + if len(involvedPeople) > 0: + tag.add(IPLS(people=involvedPeople)) + 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.save(stream, v1=2, v2_version=3, v23_sep=None) + +def tagFLAC(stream, track): + tag = FLAC(stream) + + tag["TITLE"] = track['title'] + tag["ARTIST"] = track['artists'] + tag["ALBUM"] = track['album']['title'] + tag["ALBUMARTIST"] = track['album']['artist']['name'] + tag["TRACKNUMBER"] = str(track['trackNumber']) + tag["TRACKTOTAL"] = str(track['album']['trackTotal']) + tag["DISCNUMBER"] = str(track['discNumber']) + tag["DISCTOTAL"] = str(track['album']['discTotal']) + tag["GENRE"] = track['album']['genre'] + tag["YEAR"] = str(track['date']['year']) + tag["DATE"] = "{}-{}-{}".format(str(track['date']['year']), str(track['date']['month']), str(track['date']['day'])) + tag["LENGTH"] = str(track['duration']) + tag["BPM"] = str(track['bpm']) + tag["PUBLISHER"] = track['album']['label'] + tag["ISRC"] = track['ISRC'] + tag["BARCODE"] = track['album']['barcode'] + tag["ITUNESADVISORY"] = "1" if track['explicit'] else "0" + tag["REPLAYGAIN_TRACK_GAIN"] = track['replayGain'] + if 'unsync' in track['lyrics']: + tag["LYRICS"] = track['lyrics']['unsync'] + for role in track['contributors']: + if role in ['author', 'engineer', 'mixer', 'producer', 'writer', 'composer']: + tag[role.upper()] = track['contributors'][role] + elif role == 'musicpublisher': + tag["ORGANIZATION"] = track['contributors']['musicpublisher'] + tag["COPYRIGHT"] = track['copyright'] + + image = Picture() + image.type = 3 + image.mime = 'image/jpeg' + image.data = urlopen("http://e-cdn-images.deezer.com/images/cover/"+track["album"]['pic']+"/800x800.jpg").read() + tag.add_picture(image) + + tag.save(deleteid3=True)