diff --git a/deemix/__init__.py b/deemix/__init__.py index 8f10dc1..f5caa61 100644 --- a/deemix/__init__.py +++ b/deemix/__init__.py @@ -23,13 +23,13 @@ def parseLink(link): if '/track' in link: type = 'track' - id = link[link.rfind("/") + 1:] + id = re.search("\/track\/(.+)", link).group(1) elif '/playlist' in link: type = 'playlist' id = re.search("\/playlist\/(\d+)", link).group(1) elif '/album' in link: type = 'album' - id = link[link.rfind("/") + 1:] + id = re.search("\/album\/(.+)", link).group(1) elif re.search("\/artist\/(\d+)\/top_track", link): type = 'artist_top' id = re.search("\/artist\/(\d+)\/top_track", link).group(1) @@ -42,7 +42,7 @@ def parseLink(link): return (link, type, id) -def generateDownloadItem(dz, link, bitrate): +def generateDownloadObject(dz, link, bitrate): (link, type, id) = parseLink(link) if type == None or id == None: return None diff --git a/deemix/__main__.py b/deemix/__main__.py index fde6781..5602e36 100644 --- a/deemix/__main__.py +++ b/deemix/__main__.py @@ -5,7 +5,7 @@ from pathlib import Path from deezer import Deezer from deezer import TrackFormats -from deemix import generateDownloadItem +from deemix import generateDownloadObject from deemix.settings import loadSettings from deemix.utils import getBitrateNumberFromText import deemix.utils.localpaths as localpaths @@ -49,8 +49,8 @@ def download(url, bitrate, portable, path): links.append(link) for link in links: - downloadItem = generateDownloadItem(dz, link, bitrate) - Downloader(dz, downloadItem, settings).start() + downloadObject = generateDownloadObject(dz, link, bitrate) + Downloader(dz, downloadObject, settings).start() if path is not None: if path == '': path = '.' diff --git a/deemix/downloader.py b/deemix/downloader.py index f2a985f..8f6273c 100644 --- a/deemix/downloader.py +++ b/deemix/downloader.py @@ -321,9 +321,9 @@ class Downloader: raise DownloadCancelled except (requests.exceptions.HTTPError, DownloadEmpty): if writepath.is_file(): writepath.unlink() - if track.fallbackId != "0": + if track.fallbackID != "0": logger.warn(f"[{track.mainArtist.name} - {track.title}] Track not available, using fallback id") - newTrack = self.dz.gw.get_track_with_fallback(track.fallbackId) + newTrack = self.dz.gw.get_track_with_fallback(track.fallbackID) track.parseEssentialData(newTrack) track.retriveFilesizes(self.dz) return False @@ -418,9 +418,9 @@ class Downloader: except DownloadFailed as error: if error.track: track = error.track - if track.fallbackId != "0": + if track.fallbackID != "0": logger.warn(f"[{track.mainArtist.name} - {track.title}] {error.message} Using fallback id") - newTrack = self.dz.gw.get_track_with_fallback(track.fallbackId) + newTrack = self.dz.gw.get_track_with_fallback(track.fallbackID) track.parseEssentialData(newTrack) track.retriveFilesizes(self.dz) return self.downloadWrapper(trackAPI_gw, trackAPI, albumAPI, playlistAPI, track) diff --git a/deemix/itemgen.py b/deemix/itemgen.py index 15d6eb0..6087d2c 100644 --- a/deemix/itemgen.py +++ b/deemix/itemgen.py @@ -42,18 +42,20 @@ def generateTrackItem(dz, id, bitrate, trackAPI=None, albumAPI=None): title += f" {trackAPI_gw['VERSION']}".strip() explicit = bool(int(trackAPI_gw.get('EXPLICIT_LYRICS', 0))) - return Single( - 'track', - id, - bitrate, - title, - trackAPI_gw['ART_NAME'], - f"https://e-cdns-images.dzcdn.net/images/cover/{trackAPI_gw['ALB_PICTURE']}/75x75-000000-80-0-0.jpg", - explicit, - trackAPI_gw, - trackAPI, - albumAPI - ) + return Single({ + 'type': 'track', + 'id': id, + 'bitrate': bitrate, + 'title': title, + 'artist': trackAPI_gw['ART_NAME'], + 'cover': f"https://e-cdns-images.dzcdn.net/images/cover/{trackAPI_gw['ALB_PICTURE']}/75x75-000000-80-0-0.jpg", + 'explicit': explicit, + 'single': { + 'trackAPI_gw': trackAPI_gw, + 'trackAPI': trackAPI, + 'albumAPI': albumAPI + } + }) def generateAlbumItem(dz, id, bitrate, rootArtist=None): # Get essential album info @@ -93,18 +95,20 @@ def generateAlbumItem(dz, id, bitrate, rootArtist=None): explicit = albumAPI_gw.get('EXPLICIT_ALBUM_CONTENT', {}).get('EXPLICIT_LYRICS_STATUS', LyricsStatus.UNKNOWN) in [LyricsStatus.EXPLICIT, LyricsStatus.PARTIALLY_EXPLICIT] - return Collection( - 'album', - id, - bitrate, - albumAPI['title'], - albumAPI['artist']['name'], - cover, - explicit, - totalSize, - tracks_gw=collection, - albumAPI=albumAPI - ) + return Collection({ + 'type': 'album', + 'id': id, + 'bitrate': bitrate, + 'title': albumAPI['title'], + 'artist': albumAPI['artist']['name'], + 'cover': cover, + 'explicit': explicit, + 'size': totalSize, + 'collection': { + 'tracks_gw': collection, + 'albumAPI': albumAPI + } + }) def generatePlaylistItem(dz, id, bitrate, playlistAPI=None, playlistTracksAPI=None): if not playlistAPI: @@ -146,18 +150,20 @@ def generatePlaylistItem(dz, id, bitrate, playlistAPI=None, playlistTracksAPI=No if not 'explicit' in playlistAPI: playlistAPI['explicit'] = False - return Collection( - 'playlist', - id, - bitrate, - playlistAPI['title'], - playlistAPI['creator']['name'], - playlistAPI['picture_small'][:-24] + '/75x75-000000-80-0-0.jpg', - playlistAPI['explicit'], - totalSize, - tracks_gw=collection, - playlistAPI=playlistAPI - ) + return Collection({ + 'type': 'playlist', + 'id': id, + 'bitrate': bitrate, + 'title': playlistAPI['title'], + 'artist': playlistAPI['creator']['name'], + 'cover': playlistAPI['picture_small'][:-24] + '/75x75-000000-80-0-0.jpg', + 'explicit': playlistAPI['explicit'], + 'size': totalSize, + 'collection': { + 'tracks_gw': collection, + 'playlistAPI': playlistAPI + } + }) def generateArtistItem(dz, id, bitrate, interface=None): # Get essential artist info diff --git a/deemix/types/DownloadObjects.py b/deemix/types/DownloadObjects.py index 0fd2664..c0e6736 100644 --- a/deemix/types/DownloadObjects.py +++ b/deemix/types/DownloadObjects.py @@ -1,33 +1,18 @@ class IDownloadObject: - def __init__(self, type=None, id=None, bitrate=None, title=None, artist=None, cover=None, explicit=False, size=None, dictItem=None): - if dictItem: - self.type = dictItem['type'] - self.id = dictItem['id'] - self.bitrate = dictItem['bitrate'] - self.title = dictItem['title'] - self.artist = dictItem['artist'] - self.cover = dictItem['cover'] - self.explicit = dictItem.get('explicit', False) - self.size = dictItem['size'] - self.downloaded = dictItem['downloaded'] - self.failed = dictItem['failed'] - self.progress = dictItem['progress'] - self.errors = dictItem['errors'] - self.files = dictItem['files'] - else: - self.type = type - self.id = id - self.bitrate = bitrate - self.title = title - self.artist = artist - self.cover = cover - self.explicit = explicit - self.size = size - self.downloaded = 0 - self.failed = 0 - self.progress = 0 - self.errors = [] - self.files = [] + def __init__(self, obj): + self.type = obj['type'] + self.id = obj['id'] + self.bitrate = obj['bitrate'] + self.title = obj['title'] + self.artist = obj['artist'] + self.cover = obj['cover'] + self.explicit = obj.get('explicit', False) + self.size = obj['size'] + self.downloaded = obj.get('downloaded', 0) + self.failed = obj.get('failed', 0) + self.progress = obj.get('progress', 0) + self.errors = obj.get('errors', []) + self.files = obj.get('files', []) self.progressNext = 0 self.uuid = f"{self.type}_{self.id}_{self.bitrate}" self.ack = None @@ -76,17 +61,10 @@ class IDownloadObject: if interface: interface.send("updateQueue", {'uuid': self.uuid, 'progress': self.progress}) class Single(IDownloadObject): - def __init__(self, type=None, id=None, bitrate=None, title=None, artist=None, cover=None, explicit=False, trackAPI_gw=None, trackAPI=None, albumAPI=None, dictItem=None): - if dictItem: - super().__init__(dictItem=dictItem) - self.single = dictItem['single'] - else: - super().__init__(type, id, bitrate, title, artist, cover, explicit, 1) - self.single = { - 'trackAPI_gw': trackAPI_gw, - 'trackAPI': trackAPI, - 'albumAPI': albumAPI - } + def __init__(self, obj): + super().__init__(obj) + self.size = 1 + self.single = obj['single'] self.__type__ = "Single" def toDict(self): @@ -103,17 +81,9 @@ class Single(IDownloadObject): self.updateProgress(interface) class Collection(IDownloadObject): - def __init__(self, type=None, id=None, bitrate=None, title=None, artist=None, cover=None, explicit=False, size=None, tracks_gw=None, albumAPI=None, playlistAPI=None, dictItem=None): - if dictItem: - super().__init__(dictItem=dictItem) - self.collection = dictItem['collection'] - else: - super().__init__(type, id, bitrate, title, artist, cover, explicit, size) - self.collection = { - 'tracks_gw': tracks_gw, - 'albumAPI': albumAPI, - 'playlistAPI': playlistAPI - } + def __init__(self, obj): + super().__init__(obj) + self.collection = obj['collection'] self.__type__ = "Collection" def toDict(self): @@ -130,15 +100,10 @@ class Collection(IDownloadObject): self.updateProgress(interface) class Convertable(Collection): - def __init__(self, type=None, id=None, bitrate=None, title=None, artist=None, cover=None, explicit=False, size=None, plugin=None, conversion_data=None, dictItem=None): - if dictItem: - super().__init__(dictItem=dictItem) - self.plugin = dictItem['plugin'] - self.conversion_data = dictItem['conversion_data'] - else: - super().__init__(type, id, bitrate, title, artist, cover, explicit, size) - self.plugin = plugin - self.conversion_data = conversion_data + def __init__(self, obj): + super().__init__(obj) + self.plugin = obj['plugin'] + self.conversion_data = obj['conversion_data'] self.__type__ = "Convertable" def toDict(self): diff --git a/deemix/types/Picture.py b/deemix/types/Picture.py index 6cf3d99..859e333 100644 --- a/deemix/types/Picture.py +++ b/deemix/types/Picture.py @@ -14,10 +14,9 @@ class Picture: ) if format.startswith("jpg"): + quality = 80 if '-' in format: quality = format[4:] - else: - quality = 80 format = 'jpg' return url + f'-000000-{quality}-0-0.jpg' if format == 'png': diff --git a/deemix/types/Track.py b/deemix/types/Track.py index e130578..43c814c 100644 --- a/deemix/types/Track.py +++ b/deemix/types/Track.py @@ -1,12 +1,9 @@ -from time import sleep import requests - -import logging -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger('deemix') +from time import sleep from deezer.gw import GWAPIError from deezer.api import APIError + from deemix.utils import removeFeatures, andCommaConcat, removeDuplicateArtists, generateReplayGainString, changeCase from deemix.types.Album import Album @@ -24,7 +21,7 @@ class Track: self.MD5 = "" self.mediaVersion = "" self.duration = 0 - self.fallbackId = "0" + self.fallbackID = "0" self.filesizes = {} self.localTrack = False self.mainArtist = None @@ -46,7 +43,7 @@ class Track: self.searched = False self.selectedFormat = 0 self.singleDownload = False - self.dateString = None + self.dateString = "" self.artistsString = "" self.mainArtistsString = "" self.featArtistsString = "" @@ -61,14 +58,14 @@ class Track: else: raise MD5NotFound self.mediaVersion = trackAPI_gw['MEDIA_VERSION'] - self.fallbackId = "0" + self.fallbackID = "0" if 'FALLBACK' in trackAPI_gw: - self.fallbackId = trackAPI_gw['FALLBACK']['SNG_ID'] + self.fallbackID = trackAPI_gw['FALLBACK']['SNG_ID'] self.localTrack = int(self.id) < 0 def retriveFilesizes(self, dz): + guest_sid = dz.session.cookies.get('sid') try: - guest_sid = dz.session.cookies.get('sid') site = requests.post( "https://api.deezer.com/1.0/gateway.php", params={ @@ -97,8 +94,7 @@ class Track: self.filesizes = filesizes def parseData(self, dz, id=None, trackAPI_gw=None, trackAPI=None, albumAPI_gw=None, albumAPI=None, playlistAPI=None): - if id: - if not trackAPI_gw: trackAPI_gw = dz.gw.get_track_with_fallback(id) + if id and not trackAPI_gw: trackAPI_gw = dz.gw.get_track_with_fallback(id) elif not trackAPI_gw: raise NoDataToParse if not trackAPI: try: trackAPI = dz.api.get_track(trackAPI_gw['SNG_ID']) @@ -110,15 +106,15 @@ class Track: self.parseLocalTrackData(trackAPI_gw) else: self.retriveFilesizes(dz) - self.parseTrackGW(trackAPI_gw) + # Get Lyrics data if not "LYRICS" in trackAPI_gw and self.lyrics.id != "0": try: trackAPI_gw["LYRICS"] = dz.gw.get_track_lyrics(self.id) except GWAPIError: self.lyrics.id = "0" if self.lyrics.id != "0": self.lyrics.parseLyrics(trackAPI_gw["LYRICS"]) - # Parse Album data + # Parse Album Data self.album = Album( id = trackAPI_gw['ALB_ID'], title = trackAPI_gw['ALB_TITLE'], @@ -161,7 +157,7 @@ class Track: if not len(self.artist['Main']): self.artist['Main'] = [self.mainArtist['name']] - self.singleDownload = trackAPI_gw.get('SINGLE_TRACK', False) + self.singleDownload = trackAPI_gw.get('SINGLE_TRACK', False) # TODO: To change self.position = trackAPI_gw.get('POSITION') # Add playlist data if track is in a playlist @@ -184,16 +180,16 @@ class Track: self.artist = { 'Main': [trackAPI_gw['ART_NAME']] } + self.date = Date() self.album.artist = self.artist self.album.artists = self.artists self.album.date = self.date self.album.mainArtist = self.mainArtist - self.date = Date() def parseTrackGW(self, trackAPI_gw): self.title = trackAPI_gw['SNG_TITLE'].strip() - if trackAPI_gw.get('VERSION') and not trackAPI_gw['VERSION'] in trackAPI_gw['SNG_TITLE']: - self.title += " " + trackAPI_gw['VERSION'].strip() + if trackAPI_gw.get('VERSION') and not trackAPI_gw['VERSION'].strip() in this.title: + self.title += f" {trackAPI_gw['VERSION'].strip()}" self.discNumber = trackAPI_gw.get('DISK_NUMBER') self.explicit = bool(int(trackAPI_gw.get('EXPLICIT_LYRICS', "0"))) @@ -215,7 +211,7 @@ class Track: day = trackAPI_gw["PHYSICAL_RELEASE_DATE"][8:10] month = trackAPI_gw["PHYSICAL_RELEASE_DATE"][5:7] year = trackAPI_gw["PHYSICAL_RELEASE_DATE"][0:4] - self.date = Date(year, month, day) + self.date = Date(day, month, year) def parseTrack(self, trackAPI): self.bpm = trackAPI['bpm'] @@ -250,8 +246,8 @@ class Track: return removeFeatures(self.title) def getFeatTitle(self): - if self.featArtistsString and not "(feat." in self.title.lower(): - return self.title + " ({})".format(self.featArtistsString) + if self.featArtistsString and not "feat." in self.title.lower(): + return f"{self.title} ({self.featArtistsString})" return self.title def generateMainFeatStrings(self):