From 7a536caf1c1f50a3962078f896ccf6a043cb5d00 Mon Sep 17 00:00:00 2001 From: RemixDev Date: Sat, 15 Aug 2020 15:49:45 +0200 Subject: [PATCH] More code reworking --- deemix/api/deezer.py | 1 - deemix/app/__init__.py | 10 +- deemix/app/downloader.py | 193 +++++++++++++++++----------------- deemix/app/queuemanager.py | 17 ++- deemix/app/spotify.py | 144 ++++++++++++++----------- deemix/utils/pathtemplates.py | 12 +-- 6 files changed, 199 insertions(+), 178 deletions(-) diff --git a/deemix/api/deezer.py b/deemix/api/deezer.py index a2153fa..6cb96bf 100755 --- a/deemix/api/deezer.py +++ b/deemix/api/deezer.py @@ -258,7 +258,6 @@ class Deezer: def get_playlist_gw(self, playlist_id): playlistAPI = self.gw_api_call('deezer.pagePlaylist', {'playlist_id': playlist_id, 'lang': 'en'})['results']['DATA'] - print(json.dumps(playlistAPI)) return { 'id': playlistAPI['PLAYLIST_ID'], 'title': playlistAPI['TITLE'], diff --git a/deemix/app/__init__.py b/deemix/app/__init__.py index 106d316..f721132 100644 --- a/deemix/app/__init__.py +++ b/deemix/app/__init__.py @@ -1,6 +1,12 @@ #!/usr/bin/env python3 -# Empty File +from deemix.api.deezer import Deezer +from deemix.app.settings import Settings +from deemix.app.queuemanager import QueueManager +from deemix.app.spotify import SpotifyHelper class deemix: def __init__(self): - + self.set = Settings() + self.dz = Deezer() + self.sp = SpotifyHelper() + self.qm = QueueManager() diff --git a/deemix/app/downloader.py b/deemix/app/downloader.py index afd108e..4b91cb3 100644 --- a/deemix/app/downloader.py +++ b/deemix/app/downloader.py @@ -15,6 +15,7 @@ from deemix.api.deezer import APIError, USER_AGENT_HEADER from deemix.utils.misc import changeCase, uniqueArray from deemix.utils.pathtemplates import generateFilename, generateFilepath, settingsRegexAlbum, settingsRegexArtist, settingsRegexPlaylistFile from deemix.utils.taggers import tagID3, tagFLAC +from deemix.app.queueitem import QISingle, QICollection, QIConvertable from mutagen.flac import FLACNoHeaderError import logging @@ -41,7 +42,7 @@ lastPercentage = 0 def stream_track(dz, track, stream, trackAPI, queueItem, interface=None): global downloadPercentage, lastPercentage - if 'cancel' in queueItem: + if queueItem.cancel: raise downloadCancelled try: request = get(track['downloadUrl'], headers=dz.http_headers, stream=True, timeout=30) @@ -55,7 +56,7 @@ def stream_track(dz, track, stream, trackAPI, queueItem, interface=None): percentage = 0 i = 0 for chunk in request.iter_content(2048): - if 'cancel' in queueItem: + if queueItem.cancel: raise downloadCancelled if i % 3 == 0 and len(chunk) == 2048: chunk = Blowfish.new(blowfish_key, Blowfish.MODE_CBC, b"\x00\x01\x02\x03\x04\x05\x06\x07").decrypt(chunk) @@ -69,9 +70,9 @@ def stream_track(dz, track, stream, trackAPI, queueItem, interface=None): downloadPercentage += chunkProgres if round(downloadPercentage) != lastPercentage and round(downloadPercentage) % 2 == 0: lastPercentage = round(downloadPercentage) - queueItem['progress'] = lastPercentage + queueItem.progress = lastPercentage if interface: - interface.send("updateQueue", {'uuid': queueItem['uuid'], 'progress': lastPercentage}) + interface.send("updateQueue", {'uuid': queueItem.uuid, 'progress': lastPercentage}) i += 1 @@ -83,9 +84,9 @@ def trackCompletePercentage(trackAPI, queueItem, interface): downloadPercentage += 1 / trackAPI['SIZE'] * 100 if round(downloadPercentage) != lastPercentage and round(downloadPercentage) % 2 == 0: lastPercentage = round(downloadPercentage) - queueItem['progress'] = lastPercentage + queueItem.progress = lastPercentage if interface: - interface.send("updateQueue", {'uuid': queueItem['uuid'], 'progress': lastPercentage}) + interface.send("updateQueue", {'uuid': queueItem.uuid, 'progress': lastPercentage}) def trackRemovePercentage(trackAPI, queueItem, interface): global downloadPercentage, lastPercentage @@ -95,9 +96,9 @@ def trackRemovePercentage(trackAPI, queueItem, interface): downloadPercentage -= 1 / trackAPI['SIZE'] * 100 if round(downloadPercentage) != lastPercentage and round(downloadPercentage) % 2 == 0: lastPercentage = round(downloadPercentage) - queueItem['progress'] = lastPercentage + queueItem.progress = lastPercentage if interface: - interface.send("updateQueue", {'uuid': queueItem['uuid'], 'progress': lastPercentage}) + interface.send("updateQueue", {'uuid': queueItem.uuid, 'progress': lastPercentage}) def downloadImage(url, path, overwrite="n"): @@ -474,12 +475,45 @@ def getTrackData(dz, trackAPI_gw, settings, trackAPI=None, albumAPI_gw=None, alb else: track['title_feat'] = track['title'] + if "_EXTRA_PLAYLIST" in trackAPI: + track['playlist'] = {} + if 'dzcdn.net' in trackAPI["_EXTRA_PLAYLIST"]['picture_small']: + track['playlist']['picUrl'] = trackAPI["_EXTRA_PLAYLIST"]['picture_small'][:-24] + "/{}x{}-{}".format( + settings['embeddedArtworkSize'], settings['embeddedArtworkSize'], + f'000000-{settings["jpegImageQuality"]}-0-0.jpg') + else: + track['playlist']['picUrl'] = trackAPI["_EXTRA_PLAYLIST"]['picture_xl'] + track['playlist']['title'] = trackAPI["_EXTRA_PLAYLIST"]['title'] + track['playlist']['mainArtist'] = { + 'id': trackAPI["_EXTRA_PLAYLIST"]['various_artist']['id'], + 'name': trackAPI["_EXTRA_PLAYLIST"]['various_artist']['name'], + 'pic': trackAPI["_EXTRA_PLAYLIST"]['various_artist']['picture_small'][ + trackAPI["_EXTRA_PLAYLIST"]['various_artist']['picture_small'].find('artist/') + 7:-24] + } + if settings['albumVariousArtists']: + track['playlist']['artist'] = {"Main": [trackAPI["_EXTRA_PLAYLIST"]['various_artist']['name'], ]} + track['playlist']['artists'] = [trackAPI["_EXTRA_PLAYLIST"]['various_artist']['name'], ] + else: + track['playlist']['artist'] = {"Main": []} + track['playlist']['artists'] = [] + track['playlist']['trackTotal'] = trackAPI["_EXTRA_PLAYLIST"]['nb_tracks'] + track['playlist']['recordType'] = "Compilation" + track['playlist']['barcode'] = "" + track['playlist']['label'] = "" + track['playlist']['explicit'] = trackAPI['_EXTRA_PLAYLIST']['explicit'] + track['playlist']['date'] = { + 'day': trackAPI["_EXTRA_PLAYLIST"]["creation_date"][8:10], + 'month': trackAPI["_EXTRA_PLAYLIST"]["creation_date"][5:7], + 'year': trackAPI["_EXTRA_PLAYLIST"]["creation_date"][0:4] + } + track['playlist']['discTotal'] = "1" + return track def downloadTrackObj(dz, trackAPI, settings, bitrate, queueItem, extraTrack=None, interface=None): result = {} - if 'cancel' in queueItem: + if queueItem.cancel: result['cancel'] = True return result @@ -495,10 +529,10 @@ def downloadTrackObj(dz, trackAPI, settings, bitrate, queueItem, extraTrack=None 'artist': trackAPI['ART_NAME'] } logger.error(f"[{result['error']['data']['artist']} - {result['error']['data']['title']}] This track is not available on Deezer!") - queueItem['failed'] += 1 - queueItem['errors'].append(result['error']) + queueItem.failed += 1 + queueItem.errors.append(result['error']) if interface: - interface.send("updateQueue", {'uuid': queueItem['uuid'], 'failed': True, 'data': result['error']['data'], + interface.send("updateQueue", {'uuid': queueItem.uuid, 'failed': True, 'data': result['error']['data'], 'error': result['error']['message'], 'errid': result['error']['errid']}) return result # Get the metadata @@ -512,7 +546,7 @@ def downloadTrackObj(dz, trackAPI, settings, bitrate, queueItem, extraTrack=None trackAPI=trackAPI['_EXTRA_TRACK'] if '_EXTRA_TRACK' in trackAPI else None, albumAPI=trackAPI['_EXTRA_ALBUM'] if '_EXTRA_ALBUM' in trackAPI else None ) - if 'cancel' in queueItem: + if queueItem.cancel: result['cancel'] = True return result if track['MD5'] == '': @@ -545,10 +579,10 @@ def downloadTrackObj(dz, trackAPI, settings, bitrate, queueItem, extraTrack=None 'artist': track['mainArtist']['name'] } } - queueItem['failed'] += 1 - queueItem['errors'].append(result['error']) + queueItem.failed += 1 + queueItem.errors.append(result['error']) if interface: - interface.send("updateQueue", {'uuid': queueItem['uuid'], 'failed': True, 'data': result['error']['data'], + interface.send("updateQueue", {'uuid': queueItem.uuid, 'failed': True, 'data': result['error']['data'], 'error': result['error']['message'], 'errid': result['error']['errid']}) return result else: @@ -563,10 +597,10 @@ def downloadTrackObj(dz, trackAPI, settings, bitrate, queueItem, extraTrack=None 'artist': track['mainArtist']['name'] } } - queueItem['failed'] += 1 - queueItem['errors'].append(result['error']) + queueItem.failed += 1 + queueItem.errors.append(result['error']) if interface: - interface.send("updateQueue", {'uuid': queueItem['uuid'], 'failed': True, 'data': result['error']['data'], + interface.send("updateQueue", {'uuid': queueItem.uuid, 'failed': True, 'data': result['error']['data'], 'error': result['error']['message'], 'errid': result['error']['errid']}) return result @@ -602,10 +636,10 @@ def downloadTrackObj(dz, trackAPI, settings, bitrate, queueItem, extraTrack=None 'artist': track['mainArtist']['name'] } } - queueItem['failed'] += 1 - queueItem['errors'].append(result['error']) + queueItem.failed += 1 + queueItem.errors.append(result['error']) if interface: - interface.send("updateQueue", {'uuid': queueItem['uuid'], 'failed': True, 'data': result['error']['data'], + interface.send("updateQueue", {'uuid': queueItem.uuid, 'failed': True, 'data': result['error']['data'], 'error': result['error']['message'], 'errid': result['error']['errid']}) return result else: @@ -620,10 +654,10 @@ def downloadTrackObj(dz, trackAPI, settings, bitrate, queueItem, extraTrack=None 'artist': track['mainArtist']['name'] } } - queueItem['failed'] += 1 - queueItem['errors'].append(result['error']) + queueItem.failed += 1 + queueItem.errors.append(result['error']) if interface: - interface.send("updateQueue", {'uuid': queueItem['uuid'], 'failed': True, 'data': result['error']['data'], + interface.send("updateQueue", {'uuid': queueItem.uuid, 'failed': True, 'data': result['error']['data'], 'error': result['error']['message'], 'errid': result['error']['errid']}) return result elif selectedBitrate == -200: @@ -638,45 +672,13 @@ def downloadTrackObj(dz, trackAPI, settings, bitrate, queueItem, extraTrack=None 'artist': track['mainArtist']['name'] } } - queueItem['failed'] += 1 - queueItem['errors'].append(result['error']) + queueItem.failed += 1 + queueItem.errors.append(result['error']) if interface: - interface.send("updateQueue", {'uuid': queueItem['uuid'], 'failed': True, 'data': result['error']['data'], + interface.send("updateQueue", {'uuid': queueItem.uuid, 'failed': True, 'data': result['error']['data'], 'error': result['error']['message'], 'errid': result['error']['errid']}) return result track['selectedFormat'] = selectedBitrate - if "_EXTRA_PLAYLIST" in trackAPI: - track['playlist'] = {} - if 'dzcdn.net' in trackAPI["_EXTRA_PLAYLIST"]['picture_small']: - track['playlist']['picUrl'] = trackAPI["_EXTRA_PLAYLIST"]['picture_small'][:-24] + "/{}x{}-{}".format( - settings['embeddedArtworkSize'], settings['embeddedArtworkSize'], - f'000000-{settings["jpegImageQuality"]}-0-0.jpg') - else: - track['playlist']['picUrl'] = trackAPI["_EXTRA_PLAYLIST"]['picture_xl'] - track['playlist']['title'] = trackAPI["_EXTRA_PLAYLIST"]['title'] - track['playlist']['mainArtist'] = { - 'id': trackAPI["_EXTRA_PLAYLIST"]['various_artist']['id'], - 'name': trackAPI["_EXTRA_PLAYLIST"]['various_artist']['name'], - 'pic': trackAPI["_EXTRA_PLAYLIST"]['various_artist']['picture_small'][ - trackAPI["_EXTRA_PLAYLIST"]['various_artist']['picture_small'].find('artist/') + 7:-24] - } - if settings['albumVariousArtists']: - track['playlist']['artist'] = {"Main": [trackAPI["_EXTRA_PLAYLIST"]['various_artist']['name'], ]} - track['playlist']['artists'] = [trackAPI["_EXTRA_PLAYLIST"]['various_artist']['name'], ] - else: - track['playlist']['artist'] = {"Main": []} - track['playlist']['artists'] = [] - track['playlist']['trackTotal'] = trackAPI["_EXTRA_PLAYLIST"]['nb_tracks'] - track['playlist']['recordType'] = "Compilation" - track['playlist']['barcode'] = "" - track['playlist']['label'] = "" - track['playlist']['explicit'] = trackAPI['_EXTRA_PLAYLIST']['explicit'] - track['playlist']['date'] = { - 'day': trackAPI["_EXTRA_PLAYLIST"]["creation_date"][8:10], - 'month': trackAPI["_EXTRA_PLAYLIST"]["creation_date"][5:7], - 'year': trackAPI["_EXTRA_PLAYLIST"]["creation_date"][0:4] - } - track['playlist']['discTotal'] = "1" if settings['tags']['savePlaylistAsCompilation'] and "playlist" in track: track['trackNumber'] = trackAPI["POSITION"] track['discNumber'] = "1" @@ -732,7 +734,7 @@ def downloadTrackObj(dz, trackAPI, settings, bitrate, queueItem, extraTrack=None filename = generateFilename(track, trackAPI, settings) (filepath, artistPath, coverPath, extrasPath) = generateFilepath(track, trackAPI, settings) - if 'cancel' in queueItem: + if queueItem.cancel: result['cancel'] = True return result # Download and cache coverart @@ -865,10 +867,10 @@ def downloadTrackObj(dz, trackAPI, settings, bitrate, queueItem, extraTrack=None 'artist': track['mainArtist']['name'] } } - queueItem['failed'] += 1 - queueItem['errors'].append(result['error']) + queueItem.failed += 1 + queueItem.errors.append(result['error']) if interface: - interface.send("updateQueue", {'uuid': queueItem['uuid'], 'failed': True, 'data': result['error']['data'], + interface.send("updateQueue", {'uuid': queueItem.uuid, 'failed': True, 'data': result['error']['data'], 'error': result['error']['message'], 'errid': result['error']['errid']}) return 1 else: @@ -883,10 +885,10 @@ def downloadTrackObj(dz, trackAPI, settings, bitrate, queueItem, extraTrack=None 'artist': track['mainArtist']['name'] } } - queueItem['failed'] += 1 - queueItem['errors'].append(result['error']) + queueItem.failed += 1 + queueItem.errors.append(result['error']) if interface: - interface.send("updateQueue", {'uuid': queueItem['uuid'], 'failed': True, 'data': result['error']['data'], + interface.send("updateQueue", {'uuid': queueItem.uuid, 'failed': True, 'data': result['error']['data'], 'error': result['error']['message'], 'errid': result['error']['errid']}) return 1 except Exception as e: @@ -919,9 +921,9 @@ def downloadTrackObj(dz, trackAPI, settings, bitrate, queueItem, extraTrack=None if 'searched' in track: result['searched'] = f'{track["mainArtist"]["name"]} - {track["title"]}' logger.info(f"[{track['mainArtist']['name']} - {track['title']}] Track download completed") - queueItem['downloaded'] += 1 + queueItem.downloaded += 1 if interface: - interface.send("updateQueue", {'uuid': queueItem['uuid'], 'downloaded': True, 'downloadPath': writepath}) + interface.send("updateQueue", {'uuid': queueItem.uuid, 'downloaded': True, 'downloadPath': writepath}) return result @@ -939,61 +941,56 @@ def downloadTrackObj_wrap(dz, track, settings, bitrate, queueItem, interface): } } } - queueItem['failed'] += 1 - queueItem['errors'].append(result['error']) + queueItem.failed += 1 + queueItem.errors.append(result['error']) if interface: - interface.send("updateQueue", {'uuid': queueItem['uuid'], 'failed': True, 'data': result['error']['data'], + interface.send("updateQueue", {'uuid': queueItem.uuid, 'failed': True, 'data': result['error']['data'], 'error': result['error']['message']}) return result def download(dz, sp, queueItem, interface=None): global downloadPercentage, lastPercentage - settings = queueItem['settings'] - bitrate = queueItem['bitrate'] + settings = queueItem.settings + bitrate = queueItem.bitrate downloadPercentage = 0 lastPercentage = 0 - if '_EXTRA' in queueItem: + if isinstance(queueItem, QIConvertable): sp.convert_spotify_playlist(dz, queueItem, settings, interface=interface) - if 'single' in queueItem: + if isinstance(queueItem, QISingle): try: - result = downloadTrackObj(dz, queueItem['single'], settings, bitrate, queueItem, interface=interface) + result = downloadTrackObj(dz, queueItem.single, settings, bitrate, queueItem, interface=interface) except Exception as e: logger.exception(str(e)) result = {'error': { 'message': str(e), 'data': { - 'id': queueItem['single']['SNG_ID'], - 'title': queueItem['single']['SNG_TITLE'] + (queueItem['single']['VERSION'] if 'VERSION' in queueItem['single'] and queueItem['single']['VERSION'] and not queueItem['single']['VERSION'] in queueItem['single']['SNG_TITLE'] else ""), - 'mainArtist': {'name': queueItem['single']['ART_NAME']} + 'id': queueItem.single['SNG_ID'], + 'title': queueItem.single['SNG_TITLE'] + (queueItem.single['VERSION'] if 'VERSION' in queueItem.single and queueItem.single['VERSION'] and not queueItem.single['VERSION'] in queueItem.single['SNG_TITLE'] else ""), + 'mainArtist': {'name': queueItem.single['ART_NAME']} } } } - queueItem['failed'] += 1 - queueItem['errors'].append(result['error']) + queueItem.failed += 1 + queueItem.errors.append(result['error']) if interface: - interface.send("updateQueue", {'uuid': queueItem['uuid'], 'failed': True, 'data': result['error']['data'], + interface.send("updateQueue", {'uuid': queueItem.uuid, 'failed': True, 'data': result['error']['data'], 'error': result['error']['message']}) - download_path = after_download_single(result, settings, queueItem) - elif 'collection' in queueItem: - playlist = [None] * len(queueItem['collection']) + download_path = after_download_single(result, settings) + elif isinstance(queueItem, QICollection): + playlist = [None] * len(queueItem.collection) with ThreadPoolExecutor(settings['queueConcurrency']) as executor: - for pos, track in enumerate(queueItem['collection'], start=0): + for pos, track in enumerate(queueItem.collection, start=0): playlist[pos] = executor.submit(downloadTrackObj_wrap, dz, track, settings, bitrate, queueItem, interface=interface) download_path = after_download(playlist, settings, queueItem) if interface: - if 'cancel' in queueItem: - interface.send('currentItemCancelled', queueItem['uuid']) - interface.send("removedFromQueue", queueItem['uuid']) + if queueItem.cancel: + interface.send('currentItemCancelled', queueItem.uuid) + interface.send("removedFromQueue", queueItem.uuid) else: - interface.send("finishDownload", queueItem['uuid']) - return { - 'dz': dz, - 'sp': sp, - 'interface': interface, - 'download_path': download_path - } + interface.send("finishDownload", queueItem.uuid) + return download_path def after_download(tracks, settings, queueItem): @@ -1049,7 +1046,7 @@ def after_download(tracks, settings, queueItem): return extrasPath -def after_download_single(track, settings, queueItem): +def after_download_single(track, settings): if 'cancel' in track: return None if 'extrasPath' not in track: diff --git a/deemix/app/queuemanager.py b/deemix/app/queuemanager.py index d834992..d5252d8 100644 --- a/deemix/app/queuemanager.py +++ b/deemix/app/queuemanager.py @@ -101,7 +101,7 @@ class QueueManager: trackAPI['FILENAME_TEMPLATE'] = settings['albumTracknameTemplate'] collection.append(trackAPI) - return return QICollection( + return QICollection( id, bitrate, albumAPI['title'], @@ -128,7 +128,7 @@ class QueueManager: return QueueError(url, message) if not playlistAPI['public'] and playlistAPI['creator']['id'] != str(dz.user['id']): logger.warn("You can't download others private playlists.") - return return QueueError(url, "You can't download others private playlists.", "notYourPrivatePlaylist") + return QueueError(url, "You can't download others private playlists.", "notYourPrivatePlaylist") playlistTracksAPI = dz.get_playlist_tracks_gw(id) playlistAPI['various_artist'] = dz.get_artist(5080) @@ -146,7 +146,7 @@ class QueueManager: if not 'explicit' in playlistAPI: playlistAPI['explicit'] = False - return return QICollection( + return QICollection( id, bitrate, playlistAPI['title'], @@ -163,7 +163,7 @@ class QueueManager: artistAPI = dz.get_artist(id) except APIError as e: e = json.loads(str(e)) - return return QueueError(url, f"Wrong URL: {e['type']+': ' if 'type' in e else ''}{e['message'] if 'message' in e else ''}") + return QueueError(url, f"Wrong URL: {e['type']+': ' if 'type' in e else ''}{e['message'] if 'message' in e else ''}") if interface: interface.send("startAddingArtist", {'name': artistAPI['name'], 'id': artistAPI['id']}) @@ -183,7 +183,7 @@ class QueueManager: artistAPI = dz.get_artist(id) except APIError as e: e = json.loads(str(e)) - return return QueueError(url, f"Wrong URL: {e['type']+': ' if 'type' in e else ''}{e['message'] if 'message' in e else ''}") + return QueueError(url, f"Wrong URL: {e['type']+': ' if 'type' in e else ''}{e['message'] if 'message' in e else ''}") if interface: interface.send("startAddingArtist", {'name': artistAPI['name'], 'id': artistAPI['id']}) @@ -205,7 +205,7 @@ class QueueManager: artistAPI = dz.get_artist(id) except APIError as e: e = json.loads(str(e)) - return return QueueError(url, f"Wrong URL: {e['type']+': ' if 'type' in e else ''}{e['message'] if 'message' in e else ''}") + return QueueError(url, f"Wrong URL: {e['type']+': ' if 'type' in e else ''}{e['message'] if 'message' in e else ''}") playlistAPI = { 'id': str(artistAPI['id'])+"_top_track", @@ -252,7 +252,7 @@ class QueueManager: if not 'explicit' in playlistAPI: playlistAPI['explicit'] = False - return return QICollection( + return QICollection( id, bitrate, playlistAPI['title'], @@ -302,9 +302,8 @@ class QueueManager: return QueueError(url, "Spotify Features is not setted up correctly.", "spotifyDisabled") try: - playlist = sp.adapt_spotify_playlist(dz, id, settings) + playlist = sp.generate_playlist_queueitem(dz, id, settings) playlist['bitrate'] = bitrate - playlist['uuid'] = f"{playlist['type']}_{id}_{bitrate}" return playlist except SpotifyException as e: return QueueError(url, "Wrong URL: "+e.msg[e.msg.find('\n')+2:]) diff --git a/deemix/app/spotify.py b/deemix/app/spotify.py index 8bafc90..406441b 100644 --- a/deemix/app/spotify.py +++ b/deemix/app/spotify.py @@ -6,43 +6,45 @@ from os import mkdir import spotipy from spotipy.oauth2 import SpotifyClientCredentials from deemix.utils.localpaths import getConfigFolder -from deemix.app.queueitem import QIConvertable +from deemix.app.queueitem import QIConvertable, QICollection +emptyPlaylist = { + 'collaborative': False, + 'description': "", + 'external_urls': {'spotify': None}, + 'followers': {'total': 0, 'href': None}, + 'id': None, + 'images': [], + 'name': "Something went wrong", + 'owner': { + 'display_name': "Error", + 'id': None + }, + 'public': True, + 'tracks' : [], + 'type': 'playlist', + 'uri': None +} class SpotifyHelper: def __init__(self, configFolder=None): self.credentials = {} self.spotifyEnabled = False self.sp = None - if not configFolder: - self.configFolder = getConfigFolder() - else: - self.configFolder = configFolder - self.emptyPlaylist = { - 'collaborative': False, - 'description': "", - 'external_urls': {'spotify': None}, - 'followers': {'total': 0, 'href': None}, - 'id': None, - 'images': [], - 'name': "Something went wrong", - 'owner': { - 'display_name': "Error", - 'id': None - }, - 'public': True, - 'tracks' : [], - 'type': 'playlist', - 'uri': None - } - self.initCredentials() + self.configFolder = configFolder - def initCredentials(self): + # Make sure config folder exsists + if not self.configFolder: + self.configFolder = getConfigFolder() if not path.isdir(self.configFolder): mkdir(self.configFolder) + + # Make sure authCredentials exsits if not path.isfile(path.join(self.configFolder, 'authCredentials.json')): with open(path.join(self.configFolder, 'authCredentials.json'), 'w') as f: json.dump({'clientId': "", 'clientSecret': ""}, f, indent=2) + + # Load spotify id and secret and check if they are usable with open(path.join(self.configFolder, 'authCredentials.json'), 'r') as credentialsFile: self.credentials = json.load(credentialsFile) self.checkCredentials() @@ -52,7 +54,9 @@ class SpotifyHelper: spotifyEnabled = False else: try: - self.createSpotifyConnection() + client_credentials_manager = SpotifyClientCredentials(client_id=self.credentials['clientId'], + client_secret=self.credentials['clientSecret']) + self.sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager) self.sp.user_playlists('spotify') self.spotifyEnabled = True except Exception as e: @@ -63,18 +67,19 @@ class SpotifyHelper: return self.credentials def setCredentials(self, spotifyCredentials): + # Remove extra spaces, just to be sure spotifyCredentials['clientId'] = spotifyCredentials['clientId'].strip() spotifyCredentials['clientSecret'] = spotifyCredentials['clientSecret'].strip() + + # Save them to disk with open(path.join(self.configFolder, 'authCredentials.json'), 'w') as f: json.dump(spotifyCredentials, f, indent=2) + + # Check if they are usable self.credentials = spotifyCredentials self.checkCredentials() - def createSpotifyConnection(self): - client_credentials_manager = SpotifyClientCredentials(client_id=self.credentials['clientId'], - client_secret=self.credentials['clientSecret']) - self.sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager) - + # Converts spotify API playlist structure to deezer's playlist structure def _convert_playlist_structure(self, spotify_obj): if len(spotify_obj['images']): url = spotify_obj['images'][0]['url'] @@ -115,6 +120,7 @@ class SpotifyHelper: deezer_obj['picture_xl'] = "https://e-cdns-images.dzcdn.net/images/cover/d41d8cd98f00b204e9800998ecf8427e/1000x1000-000000-80-0-0.jpg" return deezer_obj + # Returns deezer song_id from spotify track_id or track dict def get_trackid_spotify(self, dz, track_id, fallbackSearch, spotifyTrack=None): if not self.spotifyEnabled: raise spotifyFeaturesNotEnabled @@ -148,6 +154,7 @@ class SpotifyHelper: json.dump(cache, spotifyCache) return dz_track + # Returns deezer album_id from spotify album_id def get_albumid_spotify(self, dz, album_id): if not self.spotifyEnabled: raise spotifyFeaturesNotEnabled @@ -175,33 +182,24 @@ class SpotifyHelper: json.dump(cache, spotifyCache) return dz_album - def adapt_spotify_playlist(self, dz, playlist_id, settings): + + def generate_playlist_queueitem(self, dz, playlist_id, settings): if not self.spotifyEnabled: raise spotifyFeaturesNotEnabled spotify_playlist = self.sp.playlist(playlist_id) - result = { - 'title': spotify_playlist['name'], - 'artist': spotify_playlist['owner']['display_name'], - 'size': spotify_playlist['tracks']['total'], - 'downloaded': 0, - 'failed': 0, - 'progress': 0, - 'errors': [], - 'type': 'spotify_playlist', - 'settings': settings or {}, - 'id': playlist_id - } + if len(spotify_playlist['images']): - result['cover'] = spotify_playlist['images'][0]['url'] + cover = spotify_playlist['images'][0]['url'] else: - result[ - 'cover'] = "https://e-cdns-images.dzcdn.net/images/cover/d41d8cd98f00b204e9800998ecf8427e/75x75-000000-80-0-0.jpg" + cover = "https://e-cdns-images.dzcdn.net/images/cover/d41d8cd98f00b204e9800998ecf8427e/75x75-000000-80-0-0.jpg" + playlistAPI = self._convert_playlist_structure(spotify_playlist) playlistAPI['various_artist'] = dz.get_artist(5080) + + extra = {} + extra['unconverted'] = [] + tracklistTmp = spotify_playlist['tracks']['items'] - result['collection'] = [] - result['_EXTRA'] = {} - result['_EXTRA']['unconverted'] = [] while spotify_playlist['tracks']['next']: spotify_playlist['tracks'] = self.sp.next(spotify_playlist['tracks']) tracklistTmp += spotify_playlist['tracks']['items'] @@ -209,13 +207,23 @@ class SpotifyHelper: if item['track']: if item['track']['explicit']: playlistAPI['explicit'] = True - result['_EXTRA']['unconverted'].append(item['track']) - totalSize = len(result['_EXTRA']['unconverted']) - result['size'] = totalSize + extra['unconverted'].append(item['track']) + + totalSize = len(extra['unconverted']) if not 'explicit' in playlistAPI: playlistAPI['explicit'] = False - result['_EXTRA']['playlistAPI'] = playlistAPI - return result + extra['playlistAPI'] = playlistAPI + return QICollection( + playlist_id, + 0, + spotify_playlist['name'], + spotify_playlist['owner']['display_name'], + cover, + totalSize, + 'spotify_playlist', + settings, + extra, + ) def convert_spotify_playlist(self, dz, item, settings, interface=None): convertPercentage = 0 @@ -226,8 +234,9 @@ class SpotifyHelper: else: cache = {'tracks': {}, 'albums': {}} if interface: - interface.send("startConversion", item['uuid']) - for pos, track in enumerate(item['_EXTRA']['unconverted'], start=1): + interface.send("startConversion", item.uuid) + collection = [] + for pos, track in enumerate(item.extra['unconverted'], start=1): if str(track['id']) in cache['tracks']: trackID = cache['tracks'][str(track['id'])] else: @@ -248,20 +257,31 @@ class SpotifyHelper: } else: deezerTrack = dz.get_track_gw(trackID) - deezerTrack['_EXTRA_PLAYLIST'] = item['_EXTRA']['playlistAPI'] + deezerTrack['_EXTRA_PLAYLIST'] = item.extra['playlistAPI'] deezerTrack['POSITION'] = pos - deezerTrack['SIZE'] = item['size'] + deezerTrack['SIZE'] = item.size deezerTrack['FILENAME_TEMPLATE'] = settings['playlistTracknameTemplate'] - item['collection'].append(deezerTrack) + collection.append(deezerTrack) - convertPercentage = (pos / item['size']) * 100 + convertPercentage = (pos / item.size) * 100 print(convertPercentage) if round(convertPercentage) != lastPercentage and round(convertPercentage) % 2 == 0: lastPercentage = round(convertPercentage) if interface: - interface.send("updateQueue", {'uuid': item['uuid'], 'conversion': lastPercentage}) + interface.send("updateQueue", {'uuid': item.uuid, 'conversion': lastPercentage}) - del item['_EXTRA'] + item = QICollection( + item.id, + item.bitrate, + item.title, + item.artist, + item.cover, + item.size, + item.type, + item.settings, + collection, + ) + with open(path.join(self.configFolder, 'spotifyCache.json'), 'w') as spotifyCache: json.dump(cache, spotifyCache) if interface: diff --git a/deemix/utils/pathtemplates.py b/deemix/utils/pathtemplates.py index 7b21ef6..cffcb83 100644 --- a/deemix/utils/pathtemplates.py +++ b/deemix/utils/pathtemplates.py @@ -218,11 +218,11 @@ def settingsRegexPlaylist(foldername, playlist, settings): return antiDot(fixLongName(foldername)) def settingsRegexPlaylistFile(foldername, queueItem, settings): - foldername = foldername.replace("%title%", fixName(queueItem['title'], settings['illegalCharacterReplacer'])) - foldername = foldername.replace("%artist%", fixName(queueItem['artist'], settings['illegalCharacterReplacer'])) - foldername = foldername.replace("%size%", str(queueItem['size'])) - foldername = foldername.replace("%type%", fixName(queueItem['type'], settings['illegalCharacterReplacer'])) - foldername = foldername.replace("%id%", fixName(queueItem['id'], settings['illegalCharacterReplacer'])) - foldername = foldername.replace("%bitrate%", bitrateLabels[int(queueItem['bitrate'])]) + foldername = foldername.replace("%title%", fixName(queueItem.title, settings['illegalCharacterReplacer'])) + foldername = foldername.replace("%artist%", fixName(queueItem.artist, settings['illegalCharacterReplacer'])) + foldername = foldername.replace("%size%", str(queueItem.size)) + foldername = foldername.replace("%type%", fixName(queueItem.type, settings['illegalCharacterReplacer'])) + foldername = foldername.replace("%id%", fixName(queueItem.id, settings['illegalCharacterReplacer'])) + foldername = foldername.replace("%bitrate%", bitrateLabels[int(queueItem.bitrate)]) foldername = foldername.replace('\\', pathSep).replace('/', pathSep).replace(pathSep, settings['illegalCharacterReplacer']) return antiDot(fixLongName(foldername))