From 798dcdd3d9ca6a5a34bd7b63b2a9693e4921811e Mon Sep 17 00:00:00 2001 From: RemixDev Date: Sun, 27 Sep 2020 23:44:37 +0200 Subject: [PATCH] Changed all os.path to pathlib + cleanup Bumped to 1.5.0 --- deemix/__init__.py | 2 +- deemix/__main__.py | 12 +-- deemix/api/deezer.py | 12 +-- deemix/app/cli.py | 20 ++-- deemix/app/downloadjob.py | 128 +++++++++++----------- deemix/app/queuemanager.py | 11 +- deemix/app/settings.py | 196 +++++++++++++++++----------------- deemix/app/spotifyhelper.py | 42 ++++---- deemix/app/track.py | 6 +- deemix/utils/localpaths.py | 12 +-- deemix/utils/pathtemplates.py | 132 ++++++++++------------- deemix/utils/taggers.py | 4 +- setup.py | 2 +- 13 files changed, 285 insertions(+), 294 deletions(-) diff --git a/deemix/__init__.py b/deemix/__init__.py index 65dbeb9..4cff6b3 100644 --- a/deemix/__init__.py +++ b/deemix/__init__.py @@ -1,3 +1,3 @@ #!/usr/bin/env python3 -__version__ = "1.4.3" +__version__ = "1.5.0" diff --git a/deemix/__main__.py b/deemix/__main__.py index 7b891e2..e947172 100644 --- a/deemix/__main__.py +++ b/deemix/__main__.py @@ -2,7 +2,7 @@ import click from deemix.app.cli import cli -import os.path +from pathlib import Path @click.command() @click.option('--portable', is_flag=True, help='Creates the config folder in the same directory where the script is launched') @@ -11,21 +11,21 @@ import os.path @click.argument('url', nargs=-1, required=True) def download(url, bitrate, portable, path): - localpath = os.path.realpath('.') - configFolder = os.path.join(localpath, 'config') if portable else None + localpath = Path('.') + configFolder = localpath / 'config' if portable else None if path is not None: if path == '': path = '.' - path = os.path.realpath(path) + path = Path(path) app = cli(path, configFolder) app.login() url = list(url) - if os.path.isfile(url[0]): + if Path(url[0]).is_file(): filename = url[0] with open(filename) as f: url = f.readlines() - + app.downloadLink(url, bitrate) click.echo("All done!") diff --git a/deemix/api/deezer.py b/deemix/api/deezer.py index 473d78a..aa322e3 100755 --- a/deemix/api/deezer.py +++ b/deemix/api/deezer.py @@ -215,7 +215,7 @@ class Deezer: user_data = self.gw_api_call("deezer.getUserData") if user_data["results"]["USER"]["USER_ID"] == 0: self.logged_in = False - return 0 + return False self.family = user_data["results"]["USER"]["MULTI_ACCOUNT"]["ENABLED"] if self.family: self.childs = self.get_child_accounts_gw() @@ -242,7 +242,7 @@ class Deezer: "USER"] else "" } self.logged_in = True - return 1 + return True def change_account(self, child): if len(self.childs)-1 >= child: @@ -309,7 +309,7 @@ class Deezer: duration += int(x['DURATION']) except: pass - + output['DURATION'] = duration output['NUMBER_TRACK'] = result['SONGS']['total'] output['LINK'] = f"https://deezer.com/album/{str(output['ALB_ID'])}" @@ -497,7 +497,7 @@ class Deezer: }) } return self.gw_api_call('page.get', params=params) - + def get_new_releases(self): explore = self.get_page_gw('channels/explore') music_section = next((x for x in explore['results']['sections'] if x['title'] == 'Music'), None) @@ -516,7 +516,7 @@ class Deezer: recent_releases.sort(key=lambda x: x['ALB_ID'], reverse=True) albums = [a for a in pool.imap(self.get_album_details_gw, [x['ALB_ID'] for x in recent_releases])] - + return albums def get_channel_new_releases(self, channel_name): @@ -527,7 +527,7 @@ class Deezer: show_all = self.get_page_gw(new_releases['target']) albums = [x['data'] for x in show_all['results']['sections'][0]['items']] return albums - + return [] def get_lyrics_gw(self, sng_id): diff --git a/deemix/app/cli.py b/deemix/app/cli.py index 1b40d18..32c638b 100644 --- a/deemix/app/cli.py +++ b/deemix/app/cli.py @@ -1,14 +1,14 @@ -import os.path as path +from pathlib import Path from os import makedirs from deemix.app import deemix class cli(deemix): - def __init__(self, path, configFolder=None): + def __init__(self, downloadpath, configFolder=None): super().__init__(configFolder) - if path: - self.set.settings['downloadLocation'] = str(path) - makedirs(path, exist_ok=True) + if downloadpath: + self.set.settings['downloadLocation'] = str(downloadpath) + makedirs(downloadpath, exist_ok=True) print("Using folder: "+self.set.settings['downloadLocation']) def downloadLink(self, url, bitrate=None): @@ -27,15 +27,15 @@ class cli(deemix): return arl def login(self): - configFolder = self.set.configFolder - if not path.isdir(configFolder): + configFolder = Path(self.set.configFolder) + if not configFolder.is_dir(): makedirs(configFolder, exist_ok=True) - if path.isfile(path.join(configFolder, '.arl')): - with open(path.join(configFolder, '.arl'), 'r') as f: + if (configFolder / '.arl').is_file(): + with open(configFolder / '.arl', 'r') as f: arl = f.readline().rstrip("\n") if not self.dz.login_via_arl(arl): arl = self.requestValidArl() else: arl = self.requestValidArl() - with open(path.join(configFolder, '.arl'), 'w') as f: + with open(configFolder / '.arl', 'w') as f: f.write(arl) diff --git a/deemix/app/downloadjob.py b/deemix/app/downloadjob.py index e77d4f5..1baa922 100644 --- a/deemix/app/downloadjob.py +++ b/deemix/app/downloadjob.py @@ -1,5 +1,6 @@ import eventlet -import os.path +from os.path import sep as pathSep +from pathlib import Path import re import errno @@ -25,8 +26,8 @@ import logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger('deemix') -TEMPDIR = os.path.join(gettempdir(), 'deemix-imgs') -if not os.path.isdir(TEMPDIR): +TEMPDIR = Path(gettempdir()) / 'deemix-imgs' +if not TEMPDIR.is_dir(): makedirs(TEMPDIR) extensions = { @@ -52,8 +53,9 @@ errorMessages = { 'noSpaceLeft': "No space left on target drive, clean up some space for the tracks", 'albumDoesntExsists': "Track's album does not exsist, failed to gather info" } + def downloadImage(url, path, overwrite="n"): - if not os.path.isfile(path) or overwrite in ['y', 't', 'b']: + if not path.is_file() or overwrite in ['y', 't', 'b']: try: image = get(url, headers={'User-Agent': USER_AGENT_HEADER}, timeout=30) image.raise_for_status() @@ -81,7 +83,7 @@ def downloadImage(url, path, overwrite="n"): logger.exception(f"Error while downloading an image, you should report this to the developers: {str(e)}") except Exception as e: logger.exception(f"Error while downloading an image, you should report this to the developers: {str(e)}") - if os.path.isfile(path): remove(path) + if path.is_file(): path.unlink() return None else: return path @@ -108,7 +110,7 @@ class DownloadJob: self.downloadPercentage = 0 self.lastPercentage = 0 self.extrasPath = None - self.playlistPath = None + self.playlistCoverName = None self.playlistURLs = [] def start(self): @@ -134,18 +136,18 @@ class DownloadJob: def singleAfterDownload(self, result): if not self.extrasPath: - self.extrasPath = self.settings['downloadLocation'] + self.extrasPath = Path(self.settings['downloadLocation']) # Save Album Cover if self.settings['saveArtwork'] and 'albumPath' in result: for image in result['albumURLs']: - downloadImage(image['url'], f"{result['albumPath']}.{image['ext']}", self.settings['overwriteFile']) + downloadImage(image['url'], result['albumPath'] / f"{result['albumFilename']}.{image['ext']}", self.settings['overwriteFile']) # Save Artist Artwork if self.settings['saveArtworkArtist'] and 'artistPath' in result: for image in result['artistURLs']: - downloadImage(image['url'], f"{result['artistPath']}.{image['ext']}", self.settings['overwriteFile']) + downloadImage(image['url'], result['artistPath'] / f"{result['artistFilename']}.{image['ext']}", self.settings['overwriteFile']) # Create searched logfile if self.settings['logSearched'] and 'searched' in result: - with open(os.path.join(self.extrasPath, 'searched.txt'), 'wb+') as f: + with open(self.extrasPath / 'searched.txt', 'wb+') as f: orig = f.read().decode('utf-8') if not result['searched'] in orig: if orig != "": @@ -154,11 +156,11 @@ class DownloadJob: f.write(orig.encode('utf-8')) # Execute command after download if self.settings['executeCommand'] != "": - execute(self.settings['executeCommand'].replace("%folder%", self.extrasPath).replace("%filename%", result['filename'])) + execute(self.settings['executeCommand'].replace("%folder%", str(self.extrasPath)).replace("%filename%", result['filename'])) def collectionAfterDownload(self, tracks): if not self.extrasPath: - self.extrasPath = self.settings['downloadLocation'] + self.extrasPath = Path(self.settings['downloadLocation']) playlist = [None] * len(tracks) errors = "" searched = "" @@ -179,11 +181,11 @@ class DownloadJob: # Save Album Cover if self.settings['saveArtwork'] and 'albumPath' in result: for image in result['albumURLs']: - downloadImage(image['url'], f"{result['albumPath']}.{image['ext']}", self.settings['overwriteFile']) + downloadImage(image['url'], result['albumPath'] / f"{result['albumFilename']}.{image['ext']}", self.settings['overwriteFile']) # Save Artist Artwork if self.settings['saveArtworkArtist'] and 'artistPath' in result: for image in result['artistURLs']: - downloadImage(image['url'], f"{result['artistPath']}.{image['ext']}", self.settings['overwriteFile']) + downloadImage(image['url'], result['artistPath'] / f"{result['artistFilename']}.{image['ext']}", self.settings['overwriteFile']) # Save filename for playlist file playlist[index] = "" if 'filename' in result: @@ -191,20 +193,20 @@ class DownloadJob: # Create errors logfile if self.settings['logErrors'] and errors != "": - with open(os.path.join(self.extrasPath, 'errors.txt'), 'wb') as f: + with open(self.extrasPath / 'errors.txt', 'wb') as f: f.write(errors.encode('utf-8')) # Create searched logfile if self.settings['logSearched'] and searched != "": - with open(os.path.join(self.extrasPath, 'searched.txt'), 'wb') as f: + with open(self.extrasPath / 'searched.txt', 'wb') as f: f.write(searched.encode('utf-8')) # Save Playlist Artwork - if self.settings['saveArtwork'] and self.playlistPath and not self.settings['tags']['savePlaylistAsCompilation']: + if self.settings['saveArtwork'] and self.playlistCoverName and not self.settings['tags']['savePlaylistAsCompilation']: for image in self.playlistURLs: - downloadImage(image['url'], os.path.join(self.extrasPath, self.playlistPath)+f".{image['ext']}", self.settings['overwriteFile']) + downloadImage(image['url'], self.extrasPath / f"{self.playlistCoverName}.{image['ext']}", self.settings['overwriteFile']) # Create M3U8 File if self.settings['createM3U8File']: filename = settingsRegexPlaylistFile(self.settings['playlistFilenameTemplate'], self.queueItem, self.settings) or "playlist" - with open(os.path.join(self.extrasPath, filename+'.m3u8'), 'wb') as f: + with open(self.extrasPath / f'{filename}.m3u8', 'wb') as f: for line in playlist: f.write((line + "\n").encode('utf-8')) # Execute command after download @@ -310,7 +312,7 @@ class DownloadJob: ext = track.playlist['picUrl'][-4:] if ext[0] != ".": ext = ".jpg" - track.album['picPath'] = os.path.join(TEMPDIR, f"pl{trackAPI_gw['_EXTRA_PLAYLIST']['id']}_{self.settings['embeddedArtworkSize']}{ext}") + track.album['picPath'] = TEMPDIR / f"pl{trackAPI_gw['_EXTRA_PLAYLIST']['id']}_{self.settings['embeddedArtworkSize']}{ext}" else: if track.album['date']: track.date = track.album['date'] @@ -319,11 +321,12 @@ class DownloadJob: self.settings['embeddedArtworkSize'], self.settings['embeddedArtworkSize'], 'none-100-0-0.png' if self.settings['embeddedArtworkPNG'] else f'000000-{self.settings["jpegImageQuality"]}-0-0.jpg' ) - track.album['picPath'] = os.path.join(TEMPDIR, f"alb{track.album['id']}_{self.settings['embeddedArtworkSize']}{track.album['picUrl'][-4:]}") + track.album['picPath'] = TEMPDIR / f"alb{track.album['id']}_{self.settings['embeddedArtworkSize']}{track.album['picUrl'][-4:]}" track.album['bitrate'] = selectedFormat track.dateString = formatDate(track.date, self.settings['dateFormat']) track.album['dateString'] = formatDate(track.album['date'], self.settings['dateFormat']) + if track.playlist: track.playlist['dateString'] = formatDate(track.playlist['date'], self.settings['dateFormat']) # Check if user wants the feat in the title # 0 => do not change @@ -356,19 +359,19 @@ class DownloadJob: track.generateMainFeatStrings() # Generate artist tag if needed - if self.settings['tags']['multiArtistSeparator'] != "default": - if self.settings['tags']['multiArtistSeparator'] == "andFeat": - track.artistsString = track.mainArtistsString - if track.featArtistsString and str(self.settings['featuredToTitle']) != "2": - track.artistsString += " " + track.featArtistsString - else: - track.artistsString = self.settings['tags']['multiArtistSeparator'].join(track.artists) - else: + if self.settings['tags']['multiArtistSeparator'] == "default": track.artistsString = ", ".join(track.artists) + elif self.settings['tags']['multiArtistSeparator'] == "andFeat": + track.artistsString = track.mainArtistsString + if track.featArtistsString and str(self.settings['featuredToTitle']) != "2": + track.artistsString += " " + track.featArtistsString + else: + track.artistsString = self.settings['tags']['multiArtistSeparator'].join(track.artists) + # Generate filename and filepath from metadata - filename = generateFilename(track, trackAPI_gw, self.settings) - (filepath, artistPath, coverPath, extrasPath) = generateFilepath(track, trackAPI_gw, self.settings) + filename = generateFilename(track, self.settings, trackAPI_gw['FILENAME_TEMPLATE']) + (filepath, artistPath, coverPath, extrasPath) = generateFilepath(track, self.settings) if self.queueItem.cancel: raise DownloadCancelled @@ -400,8 +403,8 @@ class DownloadJob: 'none-100-0-0.png' if format == "png" else f'000000-{self.settings["jpegImageQuality"]}-0-0.jpg' ) result['albumURLs'].append({'url': url, 'ext': format}) - result['albumPath'] = os.path.join(coverPath, - f"{settingsRegexAlbum(self.settings['coverImageTemplate'], track.album, self.settings, trackAPI_gw['_EXTRA_PLAYLIST'] if'_EXTRA_PLAYLIST' in trackAPI_gw else None)}") + result['albumPath'] = coverPath + result['albumFilename'] = f"{settingsRegexAlbum(self.settings['coverImageTemplate'], track.album, self.settings, track.playlist)}" # Save artist art if artistPath: @@ -418,50 +421,47 @@ class DownloadJob: self.settings['localArtworkSize'], self.settings['localArtworkSize'], f'000000-{self.settings["jpegImageQuality"]}-0-0.jpg') if url: result['artistURLs'].append({'url': url, 'ext': format}) - result['artistPath'] = os.path.join(artistPath, - f"{settingsRegexArtist(self.settings['artistImageTemplate'], track.album['mainArtist'], self.settings)}") + result['artistPath'] = artistPath + result['artistFilename'] = f"{settingsRegexArtist(self.settings['artistImageTemplate'], track.album['mainArtist'], self.settings)}" # Remove subfolders from filename and add it to filepath - if os.path.sep in filename: - tempPath = filename[:filename.rfind(os.path.sep)] - filepath = os.path.join(filepath, tempPath) - filename = filename[filename.rfind(os.path.sep) + len(os.path.sep):] + if pathSep in filename: + tempPath = filename[:filename.rfind(pathSep)] + filepath = filepath / tempPath + filename = filename[filename.rfind(pathSep) + len(pathSep):] # Make sure the filepath exsists makedirs(filepath, exist_ok=True) - writepath = os.path.join(filepath, filename + extensions[track.selectedFormat]) + writepath = filepath / f"{filename}{extensions[track.selectedFormat]}" # Save lyrics in lrc file if self.settings['syncedLyrics'] and track.lyrics['sync']: - if not os.path.isfile(os.path.join(filepath, filename + '.lrc')) or self.settings['overwriteFile'] in ['y', 't']: - with open(os.path.join(filepath, filename + '.lrc'), 'wb') as f: + if not (filepath / f"{filename}.lrc").is_file() or self.settings['overwriteFile'] in ['y', 't']: + with open(filepath / f"{filename}.lrc", 'wb') as f: f.write(track.lyrics['sync'].encode('utf-8')) - trackAlreadyDownloaded = os.path.isfile(writepath) + trackAlreadyDownloaded = writepath.is_file() if not trackAlreadyDownloaded and self.settings['overwriteFile'] == 'e': exts = ['.mp3', '.flac', '.opus', '.m4a'] - baseFilename = os.path.join(filepath, filename) + baseFilename = str(filepath / filename) for ext in exts: - trackAlreadyDownloaded = os.path.isfile(baseFilename+ext) + trackAlreadyDownloaded = Path(baseFilename+ext).is_file() if trackAlreadyDownloaded: break if trackAlreadyDownloaded and self.settings['overwriteFile'] == 'b': - baseFilename = os.path.join(filepath, filename) + baseFilename = str(filepath / filename) i = 1 currentFilename = baseFilename+' ('+str(i)+')'+ extensions[track.selectedFormat] - while os.path.isfile(currentFilename): + while Path(currentFilename).is_file(): i += 1 currentFilename = baseFilename+' ('+str(i)+')'+ extensions[track.selectedFormat] trackAlreadyDownloaded = False - writepath = currentFilename + writepath = Path(currentFilename) if extrasPath: - if not self.extrasPath: - self.extrasPath = extrasPath - - # Data for m3u file - result['filename'] = writepath[len(extrasPath):] + if not self.extrasPath: self.extrasPath = extrasPath + result['filename'] = str(writepath)[len(str(extrasPath)):] # Save playlist cover if track.playlist: @@ -478,12 +478,12 @@ class DownloadJob: self.playlistURLs.append({'url': url, 'ext': format}) else: self.playlistURLs.append({'url': track.playlist['pic'], 'ext': 'jpg'}) - if not self.playlistPath: + if not self.playlistCoverName: track.playlist['id'] = "pl_" + str(trackAPI_gw['_EXTRA_PLAYLIST']['id']) track.playlist['genre'] = ["Compilation", ] track.playlist['bitrate'] = selectedFormat track.playlist['dateString'] = formatDate(track.playlist['date'], self.settings['dateFormat']) - self.playlistPath = f"{settingsRegexAlbum(self.settings['coverImageTemplate'], track.playlist, self.settings, trackAPI_gw['_EXTRA_PLAYLIST'])}" + self.playlistCoverName = f"{settingsRegexAlbum(self.settings['coverImageTemplate'], track.playlist, self.settings, track.playlist)}" if not trackAlreadyDownloaded or self.settings['overwriteFile'] == 'y': logger.info(f"[{track.mainArtist['name']} - {track.title}] Downloading the track") @@ -494,10 +494,10 @@ class DownloadJob: with open(writepath, 'wb') as stream: self.streamTrack(stream, track) except DownloadCancelled: - if os.path.isfile(writepath): remove(writepath) + if writepath.is_file(): writepath.unlink() raise DownloadCancelled except (request_exception.HTTPError, DownloadEmpty): - if os.path.isfile(writepath): remove(writepath) + if writepath.is_file(): writepath.unlink() if track.fallbackId != "0": logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not available, using fallback id") newTrack = self.dz.get_track_gw(track.fallbackId) @@ -526,7 +526,7 @@ class DownloadJob: else: raise DownloadFailed("notAvailable") except (request_exception.ConnectionError, request_exception.ChunkedEncodingError) as e: - if os.path.isfile(writepath): remove(writepath) + if writepath.is_file(): writepath.unlink() logger.warn(f"[{track.mainArtist['name']} - {track.title}] Error while downloading the track, trying again in 5s...") eventlet.sleep(5) return downloadMusic(track, trackAPI_gw) @@ -534,11 +534,11 @@ class DownloadJob: if e.errno == errno.ENOSPC: raise DownloadFailed("noSpaceLeft") else: - if os.path.isfile(writepath): remove(writepath) + if writepath.is_file(): writepath.unlink() logger.exception(f"[{track.mainArtist['name']} - {track.title}] Error while downloading the track, you should report this to the developers: {str(e)}") raise e except Exception as e: - if os.path.isfile(writepath): remove(writepath) + if writepath.is_file(): writepath.unlink() logger.exception(f"[{track.mainArtist['name']} - {track.title}] Error while downloading the track, you should report this to the developers: {str(e)}") raise e return True @@ -565,7 +565,7 @@ class DownloadJob: try: tagFLAC(writepath, track, self.settings['tags']) except FLACNoHeaderError: - if os.path.isfile(writepath): remove(writepath) + if writepath.is_file(): writepath.unlink() logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not available in FLAC, falling back if necessary") self.removeTrackPercentage() track.filesizes['FILESIZE_FLAC'] = "0" @@ -574,11 +574,11 @@ class DownloadJob: if track.searched: result['searched'] = f"{track.mainArtist['name']} - {track.title}" - logger.info(f"[{track.mainArtist['name']} - {track.title}] Track download completed\n{writepath}") + logger.info(f"[{track.mainArtist['name']} - {track.title}] Track download completed\n{str(writepath)}") self.queueItem.downloaded += 1 - self.queueItem.files.append(writepath) + self.queueItem.files.append(str(writepath)) if self.interface: - self.interface.send("updateQueue", {'uuid': self.queueItem.uuid, 'downloaded': True, 'downloadPath': writepath}) + self.interface.send("updateQueue", {'uuid': self.queueItem.uuid, 'downloaded': True, 'downloadPath': str(writepath)}) return result def getPreferredBitrate(self, track): diff --git a/deemix/app/queuemanager.py b/deemix/app/queuemanager.py index 18456b9..e032be1 100644 --- a/deemix/app/queuemanager.py +++ b/deemix/app/queuemanager.py @@ -4,7 +4,7 @@ from deemix.api.deezer import APIError from spotipy.exceptions import SpotifyException from deemix.app.queueitem import QueueItem, QISingle, QICollection, QIConvertable import logging -import os.path as path +from pathlib import Path import json from os import remove import eventlet @@ -434,7 +434,7 @@ class QueueManager: if len(self.queueList) > 0: if self.currentItem != "": self.queue.insert(0, self.currentItem) - with open(path.join(configFolder, 'queue.json'), 'w') as f: + with open(Path(configFolder) / 'queue.json', 'w') as f: json.dump({ 'queue': self.queue, 'queueComplete': self.queueComplete, @@ -457,12 +457,13 @@ class QueueManager: return queueList def loadQueue(self, configFolder, settings, interface=None): - if path.isfile(path.join(configFolder, 'queue.json')) and not len(self.queue): + configFolder = Path(configFolder) + if (configFolder / 'queue.json').is_file() and not len(self.queue): if interface: interface.send('restoringQueue') - with open(path.join(configFolder, 'queue.json'), 'r') as f: + with open(configFolder / 'queue.json', 'r') as f: qd = json.load(f) - remove(path.join(configFolder, 'queue.json')) + remove(configFolder / 'queue.json') self.restoreQueue(qd['queue'], qd['queueComplete'], qd['queueList'], settings) if interface: interface.send('init_downloadQueue', { diff --git a/deemix/app/settings.py b/deemix/app/settings.py index 4d45bf3..741c021 100644 --- a/deemix/app/settings.py +++ b/deemix/app/settings.py @@ -1,6 +1,6 @@ import json -import os.path as path -from os import makedirs, listdir, remove +from pathlib import Path +from os import makedirs, listdir from deemix import __version__ as deemixVersion import logging import datetime @@ -11,102 +11,104 @@ logger = logging.getLogger('deemix') import deemix.utils.localpaths as localpaths +DEFAULT_SETTINGS = { + "downloadLocation": str(localpaths.getHomeFolder() / 'deemix Music'), + "tracknameTemplate": "%artist% - %title%", + "albumTracknameTemplate": "%tracknumber% - %title%", + "playlistTracknameTemplate": "%position% - %artist% - %title%", + "createPlaylistFolder": True, + "playlistNameTemplate": "%playlist%", + "createArtistFolder": False, + "artistNameTemplate": "%artist%", + "createAlbumFolder": True, + "albumNameTemplate": "%artist% - %album%", + "createCDFolder": True, + "createStructurePlaylist": False, + "createSingleFolder": False, + "padTracks": True, + "paddingSize": "0", + "illegalCharacterReplacer": "_", + "queueConcurrency": 3, + "maxBitrate": "3", + "fallbackBitrate": True, + "fallbackSearch": False, + "logErrors": True, + "logSearched": False, + "saveDownloadQueue": False, + "overwriteFile": "n", + "createM3U8File": False, + "playlistFilenameTemplate": "playlist", + "syncedLyrics": False, + "embeddedArtworkSize": 800, + "embeddedArtworkPNG": False, + "localArtworkSize": 1400, + "localArtworkFormat": "jpg", + "saveArtwork": True, + "coverImageTemplate": "cover", + "saveArtworkArtist": False, + "artistImageTemplate": "folder", + "jpegImageQuality": 80, + "dateFormat": "Y-M-D", + "albumVariousArtists": True, + "removeAlbumVersion": False, + "removeDuplicateArtists": False, + "tagsLanguage": "", + "featuredToTitle": "0", + "titleCasing": "nothing", + "artistCasing": "nothing", + "executeCommand": "", + "tags": { + "title": True, + "artist": True, + "album": True, + "cover": True, + "trackNumber": True, + "trackTotal": False, + "discNumber": True, + "discTotal": False, + "albumArtist": True, + "genre": True, + "year": True, + "date": True, + "explicit": False, + "isrc": True, + "length": True, + "barcode": True, + "bpm": True, + "replayGain": False, + "label": True, + "lyrics": False, + "syncedLyrics": False, + "copyright": False, + "composer": False, + "involvedPeople": False, + "savePlaylistAsCompilation": False, + "useNullSeparator": False, + "saveID3v1": True, + "multiArtistSeparator": "default", + "singleAlbumArtist": False, + "coverDescriptionUTF8": False + } +} + class Settings: def __init__(self, configFolder=None): self.settings = {} self.configFolder = configFolder if not self.configFolder: self.configFolder = localpaths.getConfigFolder() - self.defaultSettings = { - "downloadLocation": path.join(localpaths.getHomeFolder(), 'deemix Music'), - "tracknameTemplate": "%artist% - %title%", - "albumTracknameTemplate": "%tracknumber% - %title%", - "playlistTracknameTemplate": "%position% - %artist% - %title%", - "createPlaylistFolder": True, - "playlistNameTemplate": "%playlist%", - "createArtistFolder": False, - "artistNameTemplate": "%artist%", - "createAlbumFolder": True, - "albumNameTemplate": "%artist% - %album%", - "createCDFolder": True, - "createStructurePlaylist": False, - "createSingleFolder": False, - "padTracks": True, - "paddingSize": "0", - "illegalCharacterReplacer": "_", - "queueConcurrency": 3, - "maxBitrate": "3", - "fallbackBitrate": True, - "fallbackSearch": False, - "logErrors": True, - "logSearched": False, - "saveDownloadQueue": False, - "overwriteFile": "n", - "createM3U8File": False, - "playlistFilenameTemplate": "playlist", - "syncedLyrics": False, - "embeddedArtworkSize": 800, - "embeddedArtworkPNG": False, - "localArtworkSize": 1400, - "localArtworkFormat": "jpg", - "saveArtwork": True, - "coverImageTemplate": "cover", - "saveArtworkArtist": False, - "artistImageTemplate": "folder", - "jpegImageQuality": 80, - "dateFormat": "Y-M-D", - "albumVariousArtists": True, - "removeAlbumVersion": False, - "removeDuplicateArtists": False, - "tagsLanguage": "", - "featuredToTitle": "0", - "titleCasing": "nothing", - "artistCasing": "nothing", - "executeCommand": "", - "tags": { - "title": True, - "artist": True, - "album": True, - "cover": True, - "trackNumber": True, - "trackTotal": False, - "discNumber": True, - "discTotal": False, - "albumArtist": True, - "genre": True, - "year": True, - "date": True, - "explicit": False, - "isrc": True, - "length": True, - "barcode": True, - "bpm": True, - "replayGain": False, - "label": True, - "lyrics": False, - "syncedLyrics": False, - "copyright": False, - "composer": False, - "involvedPeople": False, - "savePlaylistAsCompilation": False, - "useNullSeparator": False, - "saveID3v1": True, - "multiArtistSeparator": "default", - "singleAlbumArtist": False, - "coverDescriptionUTF8": False - } - } + self.configFolder = Path(self.configFolder) # Create config folder if it doesn't exsist makedirs(self.configFolder, exist_ok=True) # Create config file if it doesn't exsist - if not path.isfile(path.join(self.configFolder, 'config.json')): - with open(path.join(self.configFolder, 'config.json'), 'w') as f: - json.dump(self.defaultSettings, f, indent=2) + if not (self.configFolder / 'config.json').is_file(): + with open(self.configFolder / 'config.json', 'w') as f: + json.dump(DEFAULT_SETTINGS, f, indent=2) # Read config file - with open(path.join(self.configFolder, 'config.json'), 'r') as configFile: + with open(self.configFolder / 'config.json', 'r') as configFile: self.settings = json.load(configFile) self.settingsCheck() @@ -117,13 +119,13 @@ class Settings: # LOGFILES # Create logfile name and path - logspath = path.join(self.configFolder, 'logs') + logspath = self.configFolder / 'logs' now = datetime.datetime.now() logfile = now.strftime("%Y-%m-%d_%H%M%S")+".log" makedirs(logspath, exist_ok=True) # Add handler for logging - fh = logging.FileHandler(path.join(logspath, logfile), 'w', 'utf-8') + fh = logging.FileHandler(logspath / logfile, 'w', 'utf-8') fh.setLevel(logging.DEBUG) fh.setFormatter(logging.Formatter('%(asctime)s - [%(levelname)s] %(message)s')) logger.addHandler(fh) @@ -134,33 +136,33 @@ class Settings: logslist.sort() if len(logslist)>5: for i in range(len(logslist)-5): - remove(path.join(logspath, logslist[i])) + (logspath / logslist[i]).unlink() # Saves the settings def saveSettings(self, newSettings=None, dz=None): if newSettings: if dz and newSettings.get('tagsLanguage') != self.settings.get('tagsLanguage'): dz.set_accept_language(newSettings.get('tagsLanguage')) self.settings = newSettings - with open(path.join(self.configFolder, 'config.json'), 'w') as configFile: + with open(self.configFolder / 'config.json', 'w') as configFile: json.dump(self.settings, configFile, indent=2) # Checks if the default settings have changed def settingsCheck(self): changes = 0 - for x in self.defaultSettings: - if not x in self.settings or type(self.settings[x]) != type(self.defaultSettings[x]): - self.settings[x] = self.defaultSettings[x] + for x in DEFAULT_SETTINGS: + if not x in self.settings or type(self.settings[x]) != type(DEFAULT_SETTINGS[x]): + self.settings[x] = DEFAULT_SETTINGS[x] changes += 1 - for x in self.defaultSettings['tags']: - if not x in self.settings['tags'] or type(self.settings['tags'][x]) != type(self.defaultSettings['tags'][x]): - self.settings['tags'][x] = self.defaultSettings['tags'][x] + for x in DEFAULT_SETTINGS['tags']: + if not x in self.settings['tags'] or type(self.settings['tags'][x]) != type(DEFAULT_SETTINGS['tags'][x]): + self.settings['tags'][x] = DEFAULT_SETTINGS['tags'][x] changes += 1 if self.settings['downloadLocation'] == "": - self.settings['downloadLocation'] = path.join(localpaths.getHomeFolder(), 'deemix Music') + self.settings['downloadLocation'] = str(localpaths.getHomeFolder() / 'deemix Music') changes += 1 for template in ['tracknameTemplate', 'albumTracknameTemplate', 'playlistTracknameTemplate', 'playlistNameTemplate', 'artistNameTemplate', 'albumNameTemplate', 'playlistFilenameTemplate', 'coverImageTemplate', 'artistImageTemplate', 'paddingSize']: if self.settings[template] == "": - self.settings[template] = self.defaultSettings[template] + self.settings[template] = DEFAULT_SETTINGS[template] changes += 1 if changes > 0: self.saveSettings() diff --git a/deemix/app/spotifyhelper.py b/deemix/app/spotifyhelper.py index 01a19a9..95ce7b6 100644 --- a/deemix/app/spotifyhelper.py +++ b/deemix/app/spotifyhelper.py @@ -1,7 +1,6 @@ import eventlet import json -import os.path as path -from os import mkdir, remove +from pathlib import Path eventlet.import_patched('requests.adapters') @@ -38,33 +37,34 @@ class SpotifyHelper: # Make sure config folder exsists if not self.configFolder: self.configFolder = getConfigFolder() - if not path.isdir(self.configFolder): - mkdir(self.configFolder) + self.configFolder = Path(self.configFolder) + if not self.configFolder.is_dir(): + self.configFolder.mkdir() # 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: + if not (self.configFolder / 'authCredentials.json').is_file(): + with open(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: + with open(self.configFolder / 'authCredentials.json', 'r') as credentialsFile: self.credentials = json.load(credentialsFile) self.checkCredentials() self.checkValidCache() def checkValidCache(self): - if path.isfile(path.join(self.configFolder, 'spotifyCache.json')): - with open(path.join(self.configFolder, 'spotifyCache.json'), 'r') as spotifyCache: + if (self.configFolder / 'spotifyCache.json').is_file(): + with open(self.configFolder / 'spotifyCache.json', 'r') as spotifyCache: try: cache = json.load(spotifyCache) except Exception as e: print(str(e)) - remove(path.join(self.configFolder, 'spotifyCache.json')) + (self.configFolder / 'spotifyCache.json').unlink() return # Remove old versions of cache if len(cache['tracks'].values()) and isinstance(list(cache['tracks'].values())[0], int) or \ len(cache['albums'].values()) and isinstance(list(cache['albums'].values())[0], int): - remove(path.join(self.configFolder, 'spotifyCache.json')) + (self.configFolder / 'spotifyCache.json').unlink() def checkCredentials(self): if self.credentials['clientId'] == "" or self.credentials['clientSecret'] == "": @@ -89,7 +89,7 @@ class SpotifyHelper: spotifyCredentials['clientSecret'] = spotifyCredentials['clientSecret'].strip() # Save them to disk - with open(path.join(self.configFolder, 'authCredentials.json'), 'w') as f: + with open(self.configFolder / 'authCredentials.json', 'w') as f: json.dump(spotifyCredentials, f, indent=2) # Check if they are usable @@ -143,8 +143,8 @@ class SpotifyHelper: raise spotifyFeaturesNotEnabled singleTrack = False if not spotifyTrack: - if path.isfile(path.join(self.configFolder, 'spotifyCache.json')): - with open(path.join(self.configFolder, 'spotifyCache.json'), 'r') as spotifyCache: + if (self.configFolder / 'spotifyCache.json').is_file(): + with open(self.configFolder / 'spotifyCache.json', 'r') as spotifyCache: cache = json.load(spotifyCache) else: cache = {'tracks': {}, 'albums': {}} @@ -175,7 +175,7 @@ class SpotifyHelper: spotify_track['album']['name']) if singleTrack: cache['tracks'][str(track_id)] = {'id': dz_id, 'isrc': isrc} - with open(path.join(self.configFolder, 'spotifyCache.json'), 'w') as spotifyCache: + with open(self.configFolder / 'spotifyCache.json', 'w') as spotifyCache: json.dump(cache, spotifyCache) return (dz_id, dz_track, isrc) @@ -183,8 +183,8 @@ class SpotifyHelper: def get_albumid_spotify(self, dz, album_id): if not self.spotifyEnabled: raise spotifyFeaturesNotEnabled - if path.isfile(path.join(self.configFolder, 'spotifyCache.json')): - with open(path.join(self.configFolder, 'spotifyCache.json'), 'r') as spotifyCache: + if (self.configFolder / 'spotifyCache.json').is_file(): + with open(self.configFolder / 'spotifyCache.json', 'r') as spotifyCache: cache = json.load(spotifyCache) else: cache = {'tracks': {}, 'albums': {}} @@ -205,7 +205,7 @@ class SpotifyHelper: except: dz_album = "0" cache['albums'][str(album_id)] = {'id': dz_album, 'upc': upc} - with open(path.join(self.configFolder, 'spotifyCache.json'), 'w') as spotifyCache: + with open(self.configFolder / 'spotifyCache.json', 'w') as spotifyCache: json.dump(cache, spotifyCache) return dz_album @@ -255,8 +255,8 @@ class SpotifyHelper: def convert_spotify_playlist(self, dz, queueItem, interface=None): convertPercentage = 0 lastPercentage = 0 - if path.isfile(path.join(self.configFolder, 'spotifyCache.json')): - with open(path.join(self.configFolder, 'spotifyCache.json'), 'r') as spotifyCache: + if (self.configFolder / 'spotifyCache.json').is_file(): + with open(self.configFolder / 'spotifyCache.json', 'r') as spotifyCache: cache = json.load(spotifyCache) else: cache = {'tracks': {}, 'albums': {}} @@ -309,7 +309,7 @@ class SpotifyHelper: queueItem.extra = None queueItem.collection = collection - with open(path.join(self.configFolder, 'spotifyCache.json'), 'w') as spotifyCache: + with open(self.configFolder / 'spotifyCache.json', 'w') as spotifyCache: json.dump(cache, spotifyCache) if interface: interface.send("startDownload", queueItem.uuid) diff --git a/deemix/app/track.py b/deemix/app/track.py index 6ee6d69..e89de55 100644 --- a/deemix/app/track.py +++ b/deemix/app/track.py @@ -43,6 +43,8 @@ class Track: if "_EXTRA_PLAYLIST" in trackAPI_gw: self.parsePlaylistData(trackAPI_gw["_EXTRA_PLAYLIST"], settings) + self.singleDownload = trackAPI_gw.get('SINGLE_TRACK', False) + self.generateMainFeatStrings() # Bits useful for later @@ -367,6 +369,8 @@ class Track: 'year': playlist["creation_date"][0:4] } self.playlist['discTotal'] = "1" + self.playlist['playlistId'] = playlist['id'] + self.playlist['owner'] = playlist['creator'] # Removes featuring from the title def getCleanTitle(self): @@ -383,7 +387,7 @@ class Track: def generateMainFeatStrings(self): self.mainArtistsString = andCommaConcat(self.artist['Main']) - self.featArtistsString = None + self.featArtistsString = "" if 'Featured' in self.artist: self.featArtistsString = "feat. "+andCommaConcat(self.artist['Featured']) diff --git a/deemix/utils/localpaths.py b/deemix/utils/localpaths.py index 5443373..b836ede 100644 --- a/deemix/utils/localpaths.py +++ b/deemix/utils/localpaths.py @@ -1,18 +1,18 @@ -import os.path as path +from pathlib import Path import sys from os import getenv userdata = "" -homedata = path.expanduser("~") +homedata = Path.home() if getenv("APPDATA"): - userdata = getenv("APPDATA") + path.sep + "deemix" + path.sep + userdata = Path(getenv("APPDATA")) / "deemix" elif sys.platform.startswith('darwin'): - userdata = homedata + '/Library/Application Support/deemix/' + userdata = homedata / 'Library' / 'Application Support' / 'deemix' elif getenv("XDG_CONFIG_HOME"): - userdata = getenv("XDG_CONFIG_HOME") + '/deemix/' + userdata = Path(getenv("XDG_CONFIG_HOME")) / 'deemix' else: - userdata = homedata + '/.config/deemix/' + userdata = homedata / '.config' / 'deemix' def getHomeFolder(): return homedata diff --git a/deemix/utils/pathtemplates.py b/deemix/utils/pathtemplates.py index f3e27fd..97bb58e 100644 --- a/deemix/utils/pathtemplates.py +++ b/deemix/utils/pathtemplates.py @@ -1,5 +1,6 @@ import re from os.path import sep as pathSep +from pathlib import Path from unicodedata import normalize bitrateLabels = { @@ -13,7 +14,6 @@ bitrateLabels = { 0: "MP3" } - def fixName(txt, char='_'): txt = str(txt) txt = re.sub(r'[\0\/\\:*?"<>|]', char, txt) @@ -29,13 +29,10 @@ def fixEndOfData(bString): def fixLongName(name): if pathSep in name: - name2 = name.split(pathSep) + sepName = name.split(pathSep) name = "" - for txt in name2: - txt = txt.encode('utf-8')[:200] - while not fixEndOfData(txt): - txt = txt[:-1] - txt = txt.decode() + for txt in sepName: + txt = fixLongName(txt) name += txt + pathSep name = name[:-1] else: @@ -54,92 +51,82 @@ def antiDot(string): return string -def pad(num, max, dopad=True): - paddingsize = len(str(max)) - if paddingsize == 1: - paddingsize = 2 - if dopad: - return str(num).zfill(paddingsize) +def pad(num, max, settings): + if int(settings['paddingSize']) == 0: + paddingSize = len(str(max)) + else: + paddingSize = 10 ** (int(settings['paddingSize']) - 1) + if paddingSize == 1: + paddingSize = 2 + if settings['padTracks']: + return str(num).zfill(paddingSize) else: return str(num) +def generateFilename(track, settings, template): + filename = template or "%artist% - %title%" + return settingsRegex(filename, track, settings) -def generateFilename(track, trackAPI, settings): - if trackAPI['FILENAME_TEMPLATE'] == "": - filename = "%artist% - %title%" - else: - filename = trackAPI['FILENAME_TEMPLATE'] - return settingsRegex(filename, track, settings, - trackAPI['_EXTRA_PLAYLIST'] if '_EXTRA_PLAYLIST' in trackAPI else None) - - -def generateFilepath(track, trackAPI, settings): - filepath = settings['downloadLocation'] - if filepath[-1:] != pathSep: - filepath += pathSep +def generateFilepath(track, settings): + filepath = Path(settings['downloadLocation']) artistPath = None coverPath = None extrasPath = None - if settings['createPlaylistFolder'] and '_EXTRA_PLAYLIST' in trackAPI and not settings['tags'][ - 'savePlaylistAsCompilation']: - filepath += antiDot( - settingsRegexPlaylist(settings['playlistNameTemplate'], trackAPI['_EXTRA_PLAYLIST'], settings)) + pathSep + if settings['createPlaylistFolder'] and track.playlist and not settings['tags']['savePlaylistAsCompilation']: + filepath = filepath / settingsRegexPlaylist(settings['playlistNameTemplate'], track.playlist, settings) - if '_EXTRA_PLAYLIST' in trackAPI and not settings['tags']['savePlaylistAsCompilation']: + if track.playlist and not settings['tags']['savePlaylistAsCompilation']: extrasPath = filepath if ( - settings['createArtistFolder'] and not '_EXTRA_PLAYLIST' in trackAPI or - (settings['createArtistFolder'] and '_EXTRA_PLAYLIST' in trackAPI and settings['tags'][ - 'savePlaylistAsCompilation']) or - (settings['createArtistFolder'] and '_EXTRA_PLAYLIST' in trackAPI and settings['createStructurePlaylist']) + (settings['createArtistFolder'] and not track.playlist) or + (settings['createArtistFolder'] and track.playlist and settings['tags']['savePlaylistAsCompilation']) or + (settings['createArtistFolder'] and track.playlist and settings['createStructurePlaylist']) ): - if (int(track.id) < 0 and not 'mainArtist' in track.album): - track.album['mainArtist'] = track.mainArtist - filepath += antiDot( - settingsRegexArtist(settings['artistNameTemplate'], track.album['mainArtist'], settings)) + pathSep + filepath = filepath / settingsRegexArtist(settings['artistNameTemplate'], track.album['mainArtist'], settings) artistPath = filepath if (settings['createAlbumFolder'] and - (not 'SINGLE_TRACK' in trackAPI or ('SINGLE_TRACK' in trackAPI and settings['createSingleFolder'])) and - (not '_EXTRA_PLAYLIST' in trackAPI or ( - '_EXTRA_PLAYLIST' in trackAPI and settings['tags']['savePlaylistAsCompilation']) or ( - '_EXTRA_PLAYLIST' in trackAPI and settings['createStructurePlaylist'])) + (not track.singleDownload or (track.singleDownload and settings['createSingleFolder'])) and + (not track.playlist or + (track.playlist and settings['tags']['savePlaylistAsCompilation']) or + (track.playlist and settings['createStructurePlaylist']) + ) ): - filepath += antiDot( - settingsRegexAlbum(settings['albumNameTemplate'], track.album, settings, - trackAPI['_EXTRA_PLAYLIST'] if'_EXTRA_PLAYLIST' in trackAPI else None)) + pathSep + filepath = filepath / settingsRegexAlbum(settings['albumNameTemplate'], track.album, settings, track.playlist if track.playlist else None) coverPath = filepath - if not ('_EXTRA_PLAYLIST' in trackAPI and not settings['tags']['savePlaylistAsCompilation']): + if not (track.playlist and not settings['tags']['savePlaylistAsCompilation']): extrasPath = filepath if ( int(track.album['discTotal']) > 1 and ( (settings['createAlbumFolder'] and settings['createCDFolder']) and - (not 'SINGLE_TRACK' in trackAPI or ('SINGLE_TRACK' in trackAPI and settings['createSingleFolder'])) and - (not '_EXTRA_PLAYLIST' in trackAPI or ( - '_EXTRA_PLAYLIST' in trackAPI and settings['tags']['savePlaylistAsCompilation']) or ( - '_EXTRA_PLAYLIST' in trackAPI and settings['createStructurePlaylist'])) + (not track.singleDownload or (track.singleDownload and settings['createSingleFolder'])) and + (not track.playlist or + (track.playlist and settings['tags']['savePlaylistAsCompilation']) or + (track.playlist and settings['createStructurePlaylist']) + ) )): - filepath += 'CD' + str(track.discNumber) + pathSep + filepath = filepath / 'CD' + str(track.discNumber) return (filepath, artistPath, coverPath, extrasPath) -def settingsRegex(filename, track, settings, playlist=None): +def settingsRegex(filename, track, settings): filename = filename.replace("%title%", fixName(track.title, settings['illegalCharacterReplacer'])) filename = filename.replace("%artist%", fixName(track.mainArtist['name'], settings['illegalCharacterReplacer'])) filename = filename.replace("%artists%", fixName(", ".join(track.artists), settings['illegalCharacterReplacer'])) filename = filename.replace("%allartists%", fixName(track.artistsString, settings['illegalCharacterReplacer'])) filename = filename.replace("%mainartists%", fixName(track.mainArtistsString, settings['illegalCharacterReplacer'])) - filename = filename.replace("%featartists%", fixName('('+track.featArtistsString+')', settings['illegalCharacterReplacer']) if track.featArtistsString else "") + if track.featArtistsString: + filename = filename.replace("%featartists%", fixName('('+track.featArtistsString+')', settings['illegalCharacterReplacer'])) + else: + filename = filename.replace("%featartists%", '') filename = filename.replace("%album%", fixName(track.album['title'], settings['illegalCharacterReplacer'])) - filename = filename.replace("%albumartist%", - fixName(track.album['mainArtist']['name'], settings['illegalCharacterReplacer'])) - filename = filename.replace("%tracknumber%", pad(track.trackNumber, track.album['trackTotal'] if int( - settings['paddingSize']) == 0 else 10 ** (int(settings['paddingSize']) - 1), settings['padTracks'])) + filename = filename.replace("%albumartist%", fixName(track.album['mainArtist']['name'], settings['illegalCharacterReplacer'])) + filename = filename.replace("%tracknumber%", pad(track.trackNumber, track.album['trackTotal'], settings)) filename = filename.replace("%tracktotal%", str(track.album['trackTotal'])) filename = filename.replace("%discnumber%", str(track.discNumber)) filename = filename.replace("%disctotal%", str(track.album['discTotal'])) @@ -159,21 +146,20 @@ def settingsRegex(filename, track, settings, playlist=None): filename = filename.replace("%track_id%", str(track.id)) filename = filename.replace("%album_id%", str(track.album['id'])) filename = filename.replace("%artist_id%", str(track.mainArtist['id'])) - if playlist: - filename = filename.replace("%playlist_id%", str(playlist['id'])) - filename = filename.replace("%position%", pad(track.position, playlist['nb_tracks'] if int( - settings['paddingSize']) == 0 else 10 ** (int(settings['paddingSize']) - 1), settings['padTracks'])) + if track.playlist: + filename = filename.replace("%playlist_id%", str(track.playlist['playlistId'])) + filename = filename.replace("%position%", pad(track.position, track.playlist['trackTotal'], settings)) else: - filename = filename.replace("%position%", pad(track.trackNumber, track.album['trackTotal'] if int( - settings['paddingSize']) == 0 else 10 ** (int(settings['paddingSize']) - 1), settings['padTracks'])) + filename = filename.replace("%playlist_id%", '') + filename = filename.replace("%position%", pad(track.trackNumber, track.album['trackTotal'], settings)) filename = filename.replace('\\', pathSep).replace('/', pathSep) return antiDot(fixLongName(filename)) def settingsRegexAlbum(foldername, album, settings, playlist=None): if playlist and settings['tags']['savePlaylistAsCompilation']: - foldername = foldername.replace("%album_id%", "pl_" + str(playlist['id'])) - foldername = foldername.replace("%genre%", "Compilation") + foldername = foldername.replace("%album_id%", "pl_" + str(playlist['playlistId'])) + foldername = foldername.replace("%genre%", "Compile") else: foldername = foldername.replace("%album_id%", str(album['id'])) if len(album['genre']) > 0: @@ -186,8 +172,7 @@ def settingsRegexAlbum(foldername, album, settings, playlist=None): foldername = foldername.replace("%artist_id%", str(album['mainArtist']['id'])) foldername = foldername.replace("%tracktotal%", str(album['trackTotal'])) foldername = foldername.replace("%disctotal%", str(album['discTotal'])) - foldername = foldername.replace("%type%", fixName(album['recordType'][0].upper() + album['recordType'][1:].lower(), - settings['illegalCharacterReplacer'])) + foldername = foldername.replace("%type%", fixName(album['recordType'].capitalize(), settings['illegalCharacterReplacer'])) foldername = foldername.replace("%upc%", album['barcode']) foldername = foldername.replace("%explicit%", "(Explicit)" if album['explicit'] else "") foldername = foldername.replace("%label%", fixName(album['label'], settings['illegalCharacterReplacer'])) @@ -208,12 +193,11 @@ def settingsRegexArtist(foldername, artist, settings): def settingsRegexPlaylist(foldername, playlist, settings): foldername = foldername.replace("%playlist%", fixName(playlist['title'], settings['illegalCharacterReplacer'])) - foldername = foldername.replace("%playlist_id%", fixName(playlist['id'], settings['illegalCharacterReplacer'])) - foldername = foldername.replace("%owner%", - fixName(playlist['creator']['name'], settings['illegalCharacterReplacer'])) - foldername = foldername.replace("%owner_id%", str(playlist['creator']['id'])) - foldername = foldername.replace("%year%", str(playlist['creation_date'][:4])) - foldername = foldername.replace("%date%", str(playlist['creation_date'][:10])) + foldername = foldername.replace("%playlist_id%", fixName(playlist['playlistId'], settings['illegalCharacterReplacer'])) + foldername = foldername.replace("%owner%", fixName(playlist['owner']['name'], settings['illegalCharacterReplacer'])) + foldername = foldername.replace("%owner_id%", str(playlist['owner']['id'])) + foldername = foldername.replace("%year%", str(playlist['date']['year'])) + foldername = foldername.replace("%date%", str(playlist['dateString'])) foldername = foldername.replace("%explicit%", "(Explicit)" if playlist['explicit'] else "") foldername = foldername.replace('\\', pathSep).replace('/', pathSep) return antiDot(fixLongName(foldername)) diff --git a/deemix/utils/taggers.py b/deemix/utils/taggers.py index e1eff1f..e1f2bf3 100644 --- a/deemix/utils/taggers.py +++ b/deemix/utils/taggers.py @@ -101,7 +101,7 @@ def tagID3(stream, track, save): descEncoding = Encoding.UTF8 mimeType = 'image/jpeg' - if track.album['picPath'].endswith('png'): + if str(track.album['picPath']).endswith('png'): mimeType = 'image/png' with open(track.album['picPath'], 'rb') as f: @@ -195,7 +195,7 @@ def tagFLAC(stream, track, save): image = Picture() image.type = PictureType.COVER_FRONT image.mime = 'image/jpeg' - if track.album['picPath'].endswith('png'): + if str(track.album['picPath']).endswith('png'): image.mime = 'image/png' with open(track.album['picPath'], 'rb') as f: image.data = f.read() diff --git a/setup.py b/setup.py index 81d73d0..e93c013 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ README = (HERE / "README.md").read_text() setup( name="deemix", - version="1.4.3", + version="1.5.0", description="A barebone deezer downloader library", long_description=README, long_description_content_type="text/markdown",