Changed all os.path to pathlib + cleanup

Bumped to 1.5.0
This commit is contained in:
RemixDev 2020-09-27 23:44:37 +02:00
parent 6e955ee01f
commit 798dcdd3d9
13 changed files with 285 additions and 294 deletions

View File

@ -1,3 +1,3 @@
#!/usr/bin/env python3
__version__ = "1.4.3"
__version__ = "1.5.0"

View File

@ -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,17 +11,17 @@ 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()

View File

@ -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:

View File

@ -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)

View File

@ -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":
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)
else:
track.artistsString = ", ".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):

View File

@ -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', {

View File

@ -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,14 +11,8 @@ logger = logging.getLogger('deemix')
import deemix.utils.localpaths as localpaths
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'),
DEFAULT_SETTINGS = {
"downloadLocation": str(localpaths.getHomeFolder() / 'deemix Music'),
"tracknameTemplate": "%artist% - %title%",
"albumTracknameTemplate": "%tracknumber% - %title%",
"playlistTracknameTemplate": "%position% - %artist% - %title%",
@ -97,16 +91,24 @@ class Settings:
}
}
class Settings:
def __init__(self, configFolder=None):
self.settings = {}
self.configFolder = configFolder
if not self.configFolder:
self.configFolder = localpaths.getConfigFolder()
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()

View File

@ -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)

View File

@ -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'])

View File

@ -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

View File

@ -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))

View File

@ -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()

View File

@ -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",