From 243cf3dfa6cbec31201d5c9c5797df7735536bda Mon Sep 17 00:00:00 2001 From: RemixDev Date: Fri, 14 Aug 2020 22:28:26 +0200 Subject: [PATCH] Continued code reworking --- deemix/api/deezer.py | 31 +++- deemix/app/queueitem.py | 34 ++-- deemix/app/queuemanager.py | 326 +++++++++++++++++-------------------- deemix/app/spotify.py | 1 + 4 files changed, 194 insertions(+), 198 deletions(-) diff --git a/deemix/api/deezer.py b/deemix/api/deezer.py index e8c25aa..a2153fa 100755 --- a/deemix/api/deezer.py +++ b/deemix/api/deezer.py @@ -257,7 +257,36 @@ class Deezer: return self.gw_api_call('deezer.pageArtist', {'art_id': art_id}) def get_playlist_gw(self, playlist_id): - return self.gw_api_call('deezer.pagePlaylist', {'playlist_id': playlist_id, 'lang': 'en'}) + 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'], + 'description': playlistAPI['DESCRIPTION'], + 'duration': playlistAPI['DURATION'], + 'public': playlistAPI['STATUS'] == 1, + 'is_loved_track': playlistAPI['TYPE'] == 4, + 'collaborative': playlistAPI['STATUS'] == 2, + 'nb_tracks': playlistAPI['NB_SONG'], + 'fans': playlistAPI['NB_FAN'], + 'link': "https://www.deezer.com/playlist/"+playlistAPI['PLAYLIST_ID'], + 'share': "https://www.deezer.com/playlist/"+playlistAPI['PLAYLIST_ID'], + 'picture': "https://api.deezer.com/playlist/"+playlistAPI['PLAYLIST_ID']+"/image", + 'picture_small': "https://cdns-images.dzcdn.net/images/"+playlistAPI['PICTURE_TYPE']+"/"+playlistAPI['PLAYLIST_PICTURE']+"/56x56-000000-80-0-0.jpg", + 'picture_medium': "https://cdns-images.dzcdn.net/images/"+playlistAPI['PICTURE_TYPE']+"/"+playlistAPI['PLAYLIST_PICTURE']+"/250x250-000000-80-0-0.jpg", + 'picture_big': "https://cdns-images.dzcdn.net/images/"+playlistAPI['PICTURE_TYPE']+"/"+playlistAPI['PLAYLIST_PICTURE']+"/500x500-000000-80-0-0.jpg", + 'picture_xl': "https://cdns-images.dzcdn.net/images/"+playlistAPI['PICTURE_TYPE']+"/"+playlistAPI['PLAYLIST_PICTURE']+"/1000x1000-000000-80-0-0.jpg", + 'checksum': playlistAPI['CHECKSUM'], + 'tracklist': "https://api.deezer.com/playlist/"+playlistAPI['PLAYLIST_ID']+"/tracks", + 'creation_date': playlistAPI['DATE_ADD'], + 'creator': { + 'id': playlistAPI['PARENT_USER_ID'], + 'name': playlistAPI['PARENT_USERNAME'], + 'tracklist': "https://api.deezer.com/user/"+playlistAPI['PARENT_USER_ID']+"/flow", + 'type': "user" + }, + 'type': "playlist" + } def get_playlist_tracks_gw(self, playlist_id): tracks_array = [] diff --git a/deemix/app/queueitem.py b/deemix/app/queueitem.py index 4eccd7e..b61f61f 100644 --- a/deemix/app/queueitem.py +++ b/deemix/app/queueitem.py @@ -1,21 +1,5 @@ #!/usr/bin/env python3 -""" -queueItem base structure - title - artist - cover - size - downloaded - failed - errors - progress - type - id - bitrate - uuid: type+id+bitrate -""" - class QueueItem: def __init__(self, id=None, bitrate=None, title=None, artist=None, cover=None, size=None, type=None, settings=None, queueItemList=None): if queueItemList: @@ -41,9 +25,10 @@ class QueueItem: self.errors = [] self.progress = 0 self.uuid = f"{self.type}_{self.id}_{self.bitrate}" + self.cancel = False def toDict(self): - queueItem = { + return { 'title': self.title, 'artist': self.artist, 'cover': self.cover, @@ -57,7 +42,6 @@ class QueueItem: 'bitrate': self.bitrate, 'uuid': self.uuid } - return queueItem def getResettedItem(self): item = self.toDict() @@ -102,3 +86,17 @@ class QICollection(QueueItem): queueItem = super().toDict() queueItem['collection'] = self.collection return queueItem + +class QIConvertable(QueueItem): + def __init__(self, id=None, bitrate=None, title=None, artist=None, cover=None, size=None, type=None, settings=None, extra=None, queueItemList=None): + if queueItemList: + super().__init__(queueItemList=queueItemList) + self.extra = queueItemList['_EXTRA'] + else: + super().__init__(id, bitrate, title, artist, cover, size, type, settings) + self.extra = extra + + def toDict(self): + queueItem = super().toDict() + queueItem['_EXTRA'] = self.extra + return queueItem diff --git a/deemix/app/queuemanager.py b/deemix/app/queuemanager.py index 989dd41..d834992 100644 --- a/deemix/app/queuemanager.py +++ b/deemix/app/queuemanager.py @@ -26,7 +26,7 @@ class QueueManager: if type == None or id == None: logger.warn("URL not recognized") - return queueError(url, "URL not recognized", "invalidURL") + return QueueError(url, "URL not recognized", "invalidURL") elif type == "track": if id.startswith("isrc"): @@ -38,7 +38,7 @@ class QueueManager: except APIError as e: e = json.loads(str(e)) - 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 ''}") try: trackAPI = dz.get_track_gw(id) except APIError as e: @@ -46,7 +46,7 @@ class QueueManager: message = "Wrong URL" if "DATA_ERROR" in e: message += f": {e['DATA_ERROR']}" - return queueError(url, message) + return QueueError(url, message) if albumAPI: trackAPI['_EXTRA_ALBUM'] = albumAPI if settings['createSingleFolder']: @@ -74,7 +74,7 @@ class QueueManager: albumAPI = dz.get_album(id) except APIError as e: e = json.loads(str(e)) - 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 id.startswith('upc'): id = albumAPI['id'] albumAPI_gw = dz.get_album_gw(id) @@ -119,45 +119,16 @@ class QueueManager: playlistAPI = dz.get_playlist(id) except: try: - playlistAPI = dz.get_playlist_gw(id)['results']['DATA'] + playlistAPI = dz.get_playlist_gw(id) except APIError as e: e = json.loads(str(e)) message = "Wrong URL" if "DATA_ERROR" in e: message += f": {e['DATA_ERROR']}" - return queueError(url, message) - newPlaylist = { - 'id': playlistAPI['PLAYLIST_ID'], - 'title': playlistAPI['TITLE'], - 'description': playlistAPI['DESCRIPTION'], - 'duration': playlistAPI['DURATION'], - 'public': False, - 'is_loved_track': False, - 'collaborative': False, - 'nb_tracks': playlistAPI['NB_SONG'], - 'fans': playlistAPI['NB_FAN'], - 'link': "https://www.deezer.com/playlist/"+playlistAPI['PLAYLIST_ID'], - 'share': None, - 'picture': "https://api.deezer.com/playlist/"+playlistAPI['PLAYLIST_ID']+"/image", - 'picture_small': "https://cdns-images.dzcdn.net/images/"+playlistAPI['PICTURE_TYPE']+"/"+playlistAPI['PLAYLIST_PICTURE']+"/56x56-000000-80-0-0.jpg", - 'picture_medium': "https://cdns-images.dzcdn.net/images/"+playlistAPI['PICTURE_TYPE']+"/"+playlistAPI['PLAYLIST_PICTURE']+"/250x250-000000-80-0-0.jpg", - 'picture_big': "https://cdns-images.dzcdn.net/images/"+playlistAPI['PICTURE_TYPE']+"/"+playlistAPI['PLAYLIST_PICTURE']+"/500x500-000000-80-0-0.jpg", - 'picture_xl': "https://cdns-images.dzcdn.net/images/"+playlistAPI['PICTURE_TYPE']+"/"+playlistAPI['PLAYLIST_PICTURE']+"/1000x1000-000000-80-0-0.jpg", - 'checksum': playlistAPI['CHECKSUM'], - 'tracklist': "https://api.deezer.com/playlist/"+playlistAPI['PLAYLIST_ID']+"/tracks", - 'creation_date': playlistAPI['DATE_ADD'], - 'creator': { - 'id': playlistAPI['PARENT_USER_ID'], - 'name': playlistAPI['PARENT_USERNAME'], - 'tracklist': "https://api.deezer.com/user/"+playlistAPI['PARENT_USER_ID']+"/flow", - 'type': "user" - }, - 'type': "playlist" - } - playlistAPI = newPlaylist + 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 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) @@ -192,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 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']}) @@ -212,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 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']}) @@ -234,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 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", @@ -296,39 +267,39 @@ class QueueManager: elif type == "spotifytrack": if not sp.spotifyEnabled: logger.warn("Spotify Features is not setted up correctly.") - return queueError(url, "Spotify Features is not setted up correctly.", "spotifyDisabled") + return QueueError(url, "Spotify Features is not setted up correctly.", "spotifyDisabled") try: track_id = sp.get_trackid_spotify(dz, id, settings['fallbackSearch']) except SpotifyException as e: - return queueError(url, "Wrong URL: "+e.msg[e.msg.find('\n')+2:]) + return QueueError(url, "Wrong URL: "+e.msg[e.msg.find('\n')+2:]) if track_id != 0: return generateQueueItem(dz, sp, f'https://www.deezer.com/track/{track_id}', settings, bitrate) else: logger.warn("Track not found on deezer!") - return queueError(url, "Track not found on deezer!", "trackNotOnDeezer") + return QueueError(url, "Track not found on deezer!", "trackNotOnDeezer") elif type == "spotifyalbum": if not sp.spotifyEnabled: logger.warn("Spotify Features is not setted up correctly.") - return queueError(url, "Spotify Features is not setted up correctly.", "spotifyDisabled") + return QueueError(url, "Spotify Features is not setted up correctly.", "spotifyDisabled") try: album_id = sp.get_albumid_spotify(dz, id) except SpotifyException as e: - return queueError(url, "Wrong URL: "+e.msg[e.msg.find('\n')+2:]) + return QueueError(url, "Wrong URL: "+e.msg[e.msg.find('\n')+2:]) if album_id != 0: return generateQueueItem(dz, sp, f'https://www.deezer.com/album/{album_id}', settings, bitrate) else: logger.warn("Album not found on deezer!") - return queueError(url, "Album not found on deezer!", "albumNotOnDeezer") + return QueueError(url, "Album not found on deezer!", "albumNotOnDeezer") elif type == "spotifyplaylist": if not sp.spotifyEnabled: logger.warn("Spotify Features is not setted up correctly.") - return queueError(url, "Spotify Features is not setted up correctly.", "spotifyDisabled") + return QueueError(url, "Spotify Features is not setted up correctly.", "spotifyDisabled") try: playlist = sp.adapt_spotify_playlist(dz, id, settings) @@ -336,156 +307,153 @@ class QueueManager: 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:]) + return QueueError(url, "Wrong URL: "+e.msg[e.msg.find('\n')+2:]) else: logger.warn("URL not supported yet") - return queueError(url, "URL not supported yet", "unsupportedURL") + return QueueError(url, "URL not supported yet", "unsupportedURL") - -def addToQueue(dz, sp, url, settings, bitrate=None, interface=None): - global currentItem, queueList, queue - if not dz.logged_in: - return "Not logged in" - if type(url) is list: - queueItem = [] - for link in url: + def addToQueue(self, dz, sp, url, settings, bitrate=None, interface=None): + if not dz.logged_in: + if interface: + interface.send("loginNeededToDownload") + return False + def parseLink(link): link = link.strip() if link == "": - continue + return False logger.info("Generating queue item for: "+link) - item = generateQueueItem(dz, sp, link, settings, bitrate, interface=interface) - if type(item) is list: - queueItem += item - else: - queueItem.append(item) - else: - url = url.strip() - if url == "": - return False - logger.info("Generating queue item for: "+url) - queueItem = generateQueueItem(dz, sp, url, settings, bitrate, interface=interface) - if type(queueItem) is list: - for x in queueItem: - if 'error' in x: - logger.error(f"[{x['link']}] {x['error']}") - continue - if x['uuid'] in list(queueList.keys()): - logger.warn(f"[{x['uuid']}] Already in queue, will not be added again.") - continue - if interface: - interface.send("addedToQueue", slimQueueItem(x)) - queue.append(x['uuid']) - queueList[x['uuid']] = x - logger.info(f"[{x['uuid']}] Added to queue.") - else: - if 'error' in queueItem: - logger.error(f"[{queueItem['link']}] {queueItem['error']}") - if interface: - interface.send("queueError", queueItem) - return False - if queueItem['uuid'] in list(queueList.keys()): - logger.warn(f"[{queueItem['uuid']}] Already in queue, will not be added again.") - if interface: - interface.send("alreadyInQueue", {'uuid': queueItem['uuid'], 'title': queueItem['title']}) - return False - if interface: - interface.send("addedToQueue", slimQueueItem(queueItem)) - logger.info(f"[{queueItem['uuid']}] Added to queue.") - queue.append(queueItem['uuid']) - queueList[queueItem['uuid']] = queueItem - nextItem(dz, sp, interface) - return True - - -def nextItem(dz, sp, interface=None): - global currentItem, queueList, queue - if currentItem != "": - return None - else: - if len(queue) > 0: - currentItem = queue.pop(0) + return self.generateQueueItem(dz, sp, link, settings, bitrate, interface=interface) + if type(url) is list: + queueItem = [] + for link in url: + item = parseLink(link) + if not item: + continue + elif type(item) is list: + queueItem += item + else: + queueItem.append(item) + if not len(queueItem): + return False else: + queueItem = parseLink(url) + if not queueItem: + return False + if type(queueItem) is list: + ogLen = len(self.queue) + for x in queueItem: + if isinstance(x, QueueError): + logger.error(f"[{x.link}] {x.message}") + continue + if x.uuid in list(self.queueList.keys()): + logger.warn(f"[{x.uuid}] Already in queue, will not be added again.") + continue + self.queue.append(x.uuid) + self.queueList[x.uuid] = x + logger.info(f"[{x.uuid}] Added to queue.") + if ogLen <= len(self.queue): + return False + else: + if isinstance(queueItem, QueueError): + logger.error(f"[{x.link}] {x.message}") + if interface: + interface.send("queueError", queueItem.toDict()) + return False + if queueItem.uuid in list(self.queueList.keys()): + logger.warn(f"[{queueItem.uuid}] Already in queue, will not be added again.") + if interface: + interface.send("alreadyInQueue", {'uuid': queueItem.uuid, 'title': queueItem.title}) + return False + if interface: + interface.send("addedToQueue", queueItem.getSlimmedItem()) + logger.info(f"[{queueItem.uuid}] Added to queue.") + self.queue.append(queueItem.uuid) + self.queueList[queueItem.uuid] = queueItem + self.nextItem(dz, sp, interface) + return True + + def nextItem(self, dz, sp, interface=None): + if self.currentItem != "": return None + else: + if len(self.queue) > 0: + self.currentItem = self.queue.pop(0) + else: + return None + if interface: + interface.send("startDownload", self.currentItem) + logger.info(f"[{self.currentItem}] Started downloading.") + download(dz, sp, self.queueList[self.currentItem], interface) + self.afterDownload(dz, sp, interface) + + def afterDownload(self, dz, sp, interface): + if self.queueList[self.currentItem].cancel: + del self.queueList[self.currentItem] + else: + self.queueComplete.append(self.currentItem) + logger.info(f"[{self.currentItem}] Finished downloading.") + self.currentItem = "" + self.nextItem(dz, sp, interface) + + + def getQueue(self): + return (self.queue, self.queueComplete, self.queueList, self.currentItem) + + # TODO: Convert dicts to QueueItem Objects when restoring + def restoreQueue(self, queue, queueComplete, queueList, dz, sp, interface): + self.queueComplete = queueComplete + self.queueList = queueList + self.queue = queue + nextItem(dz, sp, interface) + + def removeFromQueue(self, uuid, interface=None): + if uuid == self.currentItem: + if interface: + interface.send("cancellingCurrentItem", uuid) + self.queueList[uuid].cancel = True + elif uuid in self.queue: + self.queue.remove(uuid) + del self.queueList[uuid] + if interface: + interface.send("removedFromQueue", uuid) + elif uuid in self.queueComplete: + self.queueComplete.remove(uuid) + del self.queueList[uuid] + if interface: + interface.send("removedFromQueue", uuid) + + + def cancelAllDownloads(self, interface=None): + self.queue = [] + self.queueComplete = [] + if self.currentItem != "": + if interface: + interface.send("cancellingCurrentItem", self.currentItem) + self.queueList[self.currentItem].cancel = True + for uuid in list(self.queueList.keys()): + if uuid != self.currentItem: + del self.queueList[uuid] if interface: - interface.send("startDownload", currentItem) - logger.info(f"[{currentItem}] Started downloading.") - result = download(dz, sp, queueList[currentItem], interface) - callbackQueueDone(result) + interface.send("removedAllDownloads", self.currentItem) -def callbackQueueDone(result): - global currentItem, queueList, queueComplete - if 'cancel' in queueList[currentItem]: - del queueList[currentItem] - else: - queueComplete.append(currentItem) - logger.info(f"[{currentItem}] Finished downloading.") - currentItem = "" - nextItem(result['dz'], result['sp'], result['interface']) - - -def getQueue(): - global currentItem, queueList, queue, queueComplete - return (queue, queueComplete, queueList, currentItem) - - -def restoreQueue(pqueue, pqueueComplete, pqueueList, dz, interface): - global currentItem, queueList, queue, queueComplete - queueComplete = pqueueComplete - queueList = pqueueList - queue = pqueue - nextItem(dz, interface) - - -def removeFromQueue(uuid, interface=None): - global currentItem, queueList, queue, queueComplete - if uuid == currentItem: + def removeFinishedDownloads(self, interface=None): + for uuid in self.queueComplete: + del self.queueList[self.uuid] + self.queueComplete = [] if interface: - interface.send("cancellingCurrentItem", currentItem) - queueList[uuid]['cancel'] = True - elif uuid in queue: - queue.remove(uuid) - del queueList[uuid] - if interface: - interface.send("removedFromQueue", uuid) - elif uuid in queueComplete: - queueComplete.remove(uuid) - del queueList[uuid] - if interface: - interface.send("removedFromQueue", uuid) + interface.send("removedFinishedDownloads") - -def cancelAllDownloads(interface=None): - global currentItem, queueList, queue, queueComplete - queue = [] - queueComplete = [] - if currentItem != "": - if interface: - interface.send("cancellingCurrentItem", currentItem) - queueList[currentItem]['cancel'] = True - for uuid in list(queueList.keys()): - if uuid != currentItem: - del queueList[uuid] - if interface: - interface.send("removedAllDownloads", currentItem) - - -def removeFinishedDownloads(interface=None): - global queueList, queueComplete - for uuid in queueComplete: - del queueList[uuid] - queueComplete = [] - if interface: - interface.send("removedFinishedDownloads") - -class queueError: +class QueueError: def __init__(self, link, message, errid=None): self.link = link self.message = message self.errid = errid - def toList(self): - error = { - 'link' + def toDict(self): + return { + 'link': self.link, + 'error': self.message, + 'errid': self.errid } diff --git a/deemix/app/spotify.py b/deemix/app/spotify.py index 5e2d2f4..8bafc90 100644 --- a/deemix/app/spotify.py +++ b/deemix/app/spotify.py @@ -6,6 +6,7 @@ from os import mkdir import spotipy from spotipy.oauth2 import SpotifyClientCredentials from deemix.utils.localpaths import getConfigFolder +from deemix.app.queueitem import QIConvertable class SpotifyHelper: