Done more code rework

This commit is contained in:
RemixDev 2020-08-15 21:34:10 +02:00
parent 7a536caf1c
commit 4cfdc4872d
8 changed files with 1113 additions and 1173 deletions

636
deemix/app/DownloadJob.py Normal file
View File

@ -0,0 +1,636 @@
#!/usr/bin/env python3
import os.path
import re
from requests import get
from requests.exceptions import HTTPError, ConnectionError
from concurrent.futures import ThreadPoolExecutor
from os import makedirs, remove, system as execute
from tempfile import gettempdir
from time import sleep
from deemix.app.queueitem import QIConvertable, QISingle, QICollection
from deemix.app.Track import Track
from deemix.utils.misc import changeCase
from deemix.utils.pathtemplates import generateFilename, generateFilepath, settingsRegexAlbum, settingsRegexArtist, settingsRegexPlaylistFile
from deemix.api.deezer import USER_AGENT_HEADER
from deemix.utils.taggers import tagID3, tagFLAC
from Cryptodome.Cipher import Blowfish
from mutagen.flac import FLACNoHeaderError
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('deemix')
TEMPDIR = os.path.join(gettempdir(), 'deemix-imgs')
if not os.path.isdir(TEMPDIR):
makedirs(TEMPDIR)
extensions = {
9: '.flac',
0: '.mp3',
3: '.mp3',
1: '.mp3',
8: '.mp3',
15: '.mp4',
14: '.mp4',
13: '.mp4'
}
errorMessages = {
'notOnDeezer': "Track not available on Deezer!",
'notEncoded': "Track not yet encoded!",
'notEncodedNoAlternative': "Track not yet encoded and no alternative found!",
'wrongBitrate': "Track not found at desired bitrate.",
'wrongBitrateNoAlternative': "Track not found at desired bitrate and no alternative found!",
'no360RA': "Track is not available in Reality Audio 360.",
'notAvailable': "Track not available on deezer's servers!"
'notAvailableNoAlternative': "Track not available on deezer's servers and no alternative found!",
}
def after_download(tracks, settings, queueItem):
extrasPath = None
playlist = [None] * len(tracks)
playlistCover = None
playlistURLs = []
errors = ""
searched = ""
for index in range(len(tracks)):
result = tracks[index].result()
if 'cancel' in result:
return None
if 'error' in result:
if not 'data' in result['error']:
result['error']['data'] = {'id': 0, 'title': 'Unknown', 'artist': 'Unknown'}
errors += f"{result['error']['data']['id']} | {result['error']['data']['artist']} - {result['error']['data']['title']} | {result['error']['message']}\r\n"
if 'searched' in result:
searched += result['searched'] + "\r\n"
if not extrasPath and 'extrasPath' in result:
extrasPath = result['extrasPath']
if not playlistCover and 'playlistCover' in result:
playlistCover = result['playlistCover']
playlistURLs = result['playlistURLs']
if settings['saveArtwork'] and 'albumPath' in result:
for image in result['albumURLs']:
downloadImage(image['url'], f"{result['albumPath']}.{image['ext']}", settings['overwriteFile'])
if settings['saveArtworkArtist'] and 'artistPath' in result:
for image in result['artistURLs']:
downloadImage(image['url'], f"{result['artistPath']}.{image['ext']}", settings['overwriteFile'])
if 'playlistPosition' in result:
playlist[index] = result['playlistPosition']
else:
playlist[index] = ""
if not extrasPath:
extrasPath = settings['downloadLocation']
if settings['logErrors'] and errors != "":
with open(os.path.join(extrasPath, 'errors.txt'), 'wb') as f:
f.write(errors.encode('utf-8'))
if settings['saveArtwork'] and playlistCover and not settings['tags']['savePlaylistAsCompilation']:
for image in playlistURLs:
downloadImage(image['url'], os.path.join(extrasPath, playlistCover)+f".{image['ext']}", settings['overwriteFile'])
if settings['logSearched'] and searched != "":
with open(os.path.join(extrasPath, 'searched.txt'), 'wb') as f:
f.write(searched.encode('utf-8'))
if settings['createM3U8File']:
filename = settingsRegexPlaylistFile(settings['playlistFilenameTemplate'], queueItem, settings) or "playlist"
with open(os.path.join(extrasPath, filename+'.m3u8'), 'wb') as f:
for line in playlist:
f.write((line + "\n").encode('utf-8'))
if settings['executeCommand'] != "":
execute(settings['executeCommand'].replace("%folder%", extrasPath))
return extrasPath
def after_download_single(track, settings):
if 'cancel' in track:
return None
if 'extrasPath' not in track:
track['extrasPath'] = settings['downloadLocation']
if settings['saveArtwork'] and 'albumPath' in track:
for image in track['albumURLs']:
downloadImage(image['url'], f"{track['albumPath']}.{image['ext']}", settings['overwriteFile'])
if settings['saveArtworkArtist'] and 'artistPath' in track:
for image in track['artistURLs']:
downloadImage(image['url'], f"{track['artistPath']}.{image['ext']}", settings['overwriteFile'])
if settings['logSearched'] and 'searched' in track:
with open(os.path.join(track['extrasPath'], 'searched.txt'), 'wb+') as f:
orig = f.read().decode('utf-8')
if not track['searched'] in orig:
if orig != "":
orig += "\r\n"
orig += track['searched'] + "\r\n"
f.write(orig.encode('utf-8'))
if settings['executeCommand'] != "":
execute(settings['executeCommand'].replace("%folder%", track['extrasPath']).replace("%filename%", track['playlistPosition']))
return track['extrasPath']
def downloadImage(url, path, overwrite="n"):
if not os.path.isfile(path) or overwrite in ['y', 't', 'b']:
try:
image = get(url, headers={'User-Agent': USER_AGENT_HEADER}, timeout=30)
image.raise_for_status()
with open(path, 'wb') as f:
f.write(image.content)
return path
except HTTPError:
if 'cdns-images.dzcdn.net' in url:
urlBase = url[:url.rfind("/")+1]
pictureUrl = url[len(urlBase):]
pictureSize = int(pictureUrl[:pictureUrl.find("x")])
if pictureSize > 1200:
logger.warn("Couldn't download "+str(pictureSize)+"x"+str(pictureSize)+" image, falling back to 1200x1200")
sleep(1)
return downloadImage(urlBase+pictureUrl.replace(str(pictureSize)+"x"+str(pictureSize), '1200x1200'), path, overwrite)
logger.error("Couldn't download Image: "+url)
except:
sleep(1)
return downloadImage(url, path, overwrite)
remove(path)
return None
else:
return path
def formatDate(date, template):
elements = {
'year': ['YYYY', 'YY', 'Y'],
'month': ['MM', 'M'],
'day': ['DD', 'D']
}
for element, placeholders in elements.items():
for placeholder in placeholders:
if placeholder in template:
template = template.replace(placeholder, str(date[element]))
return template
class DownloadJob:
def __init__(self, dz, sp, queueItem, interface=None):
self.dz = dz
self.sp = sp
self.queueItem = queueItem
self.interface = interface
self.settings = queueItem.settings
self.bitrate = queueItem.bitrate
self.downloadPercentage = 0
self.lastPercentage = 0
self.extrasPath = self.settings['downloadLocation']
def start(self):
if isinstance(self.queueItem, QIConvertable):
self.sp.convert_spotify_playlist(self.dz, self.queueItem, self.settings, interface=self.interface)
if isinstance(self.queueItem, QISingle):
result = self.downloadWrapper(self.queueItem.single)
if result:
download_path = after_download_single(result, self.settings)
elif isinstance(self.queueItem, QICollection):
playlist = [None] * len(self.queueItem.collection)
with ThreadPoolExecutor(self.settings['queueConcurrency']) as executor:
for pos, track in enumerate(self.queueItem.collection, start=0):
playlist[pos] = executor.submit(self.downloadWrapper, track)
download_path = after_download(playlist, self.settings, self.queueItem)
if self.interface:
if self.queueItem.cancel:
self.interface.send('currentItemCancelled', self.queueItem.uuid)
self.interface.send("removedFromQueue", self.queueItem.uuid)
else:
self.interface.send("finishDownload", self.queueItem.uuid)
return download_path
def download(self, trackAPI_gw, track=None):
result = {}
if self.queueItem.cancel: raise DownloadCancelled
if trackAPI_gw['SNG_ID'] == "0":
raise DownloadFailed("notOnDeezer")
# Create Track object
if not track:
logger.info(f"[{trackAPI_gw['ART_NAME']} - {trackAPI_gw['SNG_TITLE']}] Getting the tags")
track = Track(self.dz,
settings=self.settings,
trackAPI_gw=trackAPI_gw,
trackAPI=trackAPI_gw['_EXTRA_TRACK'] if '_EXTRA_TRACK' in trackAPI_gw else None,
albumAPI=trackAPI_gw['_EXTRA_ALBUM'] if '_EXTRA_ALBUM' in trackAPI_gw else None
)
if self.queueItem.cancel: raise DownloadCancelled
if self.MD5 == '':
if track.fallbackId != "0":
logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not yet encoded, using fallback id")
newTrack = self.dz.get_track_gw(track.fallbackId)
track.parseEssentialData(self.dz, newTrack)
return self.download(trackAPI_gw, track)
elif not track.searched and self.settings['fallbackSearch']:
logger.warn(f"[{self.mainArtist['name']} - {self.title}] Track not yet encoded, searching for alternative")
searchedId = self.dz.get_track_from_metadata(self.mainArtist['name'], self.title, self.album['title'])
if searchedId != 0:
newTrack = self.dz.get_track_gw(searchedId)
track.parseEssentialData(self.dz, newTrack)
track.searched = True
return self.download(trackAPI_gw, track)
else:
raise DownloadFailed("notEncodedNoAlternative")
else:
raise DownloadFailed("notEncoded")
selectedFormat = self.getPreferredBitrate(track)
if selectedFormat == -100:
if track.fallbackId != "0":
logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not found at desired bitrate, using fallback id")
newTrack = self.dz.get_track_gw(track.fallbackId)
track.parseEssentialData(self.dz, newTrack)
return self.download(trackAPI_gw, track)
elif not track.searched and self.settings['fallbackSearch']:
logger.warn(f"[{self.mainArtist['name']} - {self.title}] Track not found at desired bitrate, searching for alternative")
searchedId = self.dz.get_track_from_metadata(self.mainArtist['name'], self.title, self.album['title'])
if searchedId != 0:
newTrack = self.dz.get_track_gw(searchedId)
track.parseEssentialData(self.dz, newTrack)
track.searched = True
return self.download(trackAPI_gw, track)
else:
raise DownloadFailed("wrongBitrateNoAlternative")
else:
raise DownloadFailed("wrongBitrate")
elif selectedFormat == -200:
raise DownloadFailed("no360RA")
track.selectedFormat = selectedFormat
if self.settings['tags']['savePlaylistAsCompilation'] and track.playlist:
track.trackNumber = track.position
track.discNumber = "1"
track.album = {**track.album, **track.playlist}
track.album['picPath'] = os.path.join(TEMPDIR,
f"pl{trackAPI_gw['_EXTRA_PLAYLIST']['id']}_{settings['embeddedArtworkSize']}.jpg")
else:
if track.album['date']:
track.date = track.album['date']
track.album['picUrl'] = "https://e-cdns-images.dzcdn.net/images/cover/{}/{}x{}-{}".format(
track.album['pic'],
self.settings['embeddedArtworkSize'], self.settings['embeddedArtworkSize'],
f'000000-{self.settings["jpegImageQuality"]}-0-0.jpg'
)
track.album['bitrate'] = selectedFormat
track.dateString = formatDate(track.date, settings['dateFormat'])
track.album['dateString'] = formatDate(track.album['date'], settings['dateFormat'])
# Check if user wants the feat in the title
# 0 => do not change
# 1 => remove from title
# 2 => add to title
# 3 => remove from title and album title
if self.settings['featuredToTitle'] == "1":
track.title = track.getCleanTitle()
elif self.settings['featuredToTitle'] == "2":
track.title = track.getFeatTitle()
elif self.settings['featuredToTitle'] == "3":
track.title = track.getCleanTitle()
track.album['title'] = track.getCleanAlbumTitle()
# Remove (Album Version) from tracks that have that
if self.settings['removeAlbumVersion']:
if "Album Version" in track.title:
track.title = re.sub(r' ?\(Album Version\)', "", track.title).strip()
# 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 self.settings['featuredToTitle'] != "2":
track.artistsString += " " + track.featArtistsString
else:
track.artistsString = self.settings['tags']['multiArtistSeparator'].join(track.artists)
else:
track.artistsString = ", ".join(track.artists)
# Change Title and Artists casing if needed
if self.settings['titleCasing'] != "nothing":
track.title = changeCase(track.title, self.settings['titleCasing'])
if self.settings['artistCasing'] != "nothing":
track.artistsString = changeCase(track.artistsString, self.settings['artistCasing'])
for i, artist in enumerate(track.artists):
track.artists[i] = changeCase(artist, self.settings['artistCasing'])
# Generate filename and filepath from metadata
filename = generateFilename(track, trackAPI_gw, self.settings)
(filepath, artistPath, coverPath, extrasPath) = generateFilepath(track, trackAPI_gw, self.settings)
if self.queueItem.cancel: raise DownloadCancelled
# Download and cache coverart
if self.settings['tags']['savePlaylistAsCompilation'] and track.playlist:
else:
track.album['picPath'] = os.path.join(TEMPDIR,
f"alb{track.album['id']}_{settings['embeddedArtworkSize']}.jpg")
logger.info(f"[{track.mainArtist['name']} - {track.title}] Getting the album cover")
track.album['picPath'] = downloadImage(track.album['picUrl'], track.album['picPath'])
# Save local album art
if coverPath:
result['albumURLs'] = []
for format in self.settings['localArtworkFormat'].split(","):
if format in ["png","jpg"]:
url = track.album['picUrl'].replace(
f"{self.settings['embeddedArtworkSize']}x{self.settings['embeddedArtworkSize']}",
f"{self.settings['localArtworkSize']}x{self.settings['localArtworkSize']}")
if format == "png":
url = url[:url.find("000000-")]+"none-100-0-0.png"
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)}")
# Save artist art
if artistPath:
result['artistURLs'] = []
for format in self.settings['localArtworkFormat'].split(","):
if format in ["png","jpg"]:
url = ""
if track.album['mainArtist']['pic'] != "":
url = "https://e-cdns-images.dzcdn.net/images/artist/{}/{}x{}-{}".format(
track.album['mainArtist']['pic'], self.settings['localArtworkSize'], self.settings['localArtworkSize'],
'none-100-0-0.png' if format == "png" else f'000000-{self.settings["jpegImageQuality"]}-0-0.jpg')
elif format == "jpg":
url = "https://e-cdns-images.dzcdn.net/images/artist//{}x{}-{}".format(
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)}")
# 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):]
# Make sure the filepath exsists
makedirs(filepath, exist_ok=True)
writepath = os.path.join(filepath, filename + extensions[track.selectedFormat])
# Save lyrics in lrc file
if self.settings['syncedLyrics'] and 'sync' in track.lyrics:
if not os.path.isfile(os.path.join(filepath, filename + '.lrc')) or settings['overwriteFile'] in ['y', 't']:
with open(os.path.join(filepath, filename + '.lrc'), 'wb') as f:
f.write(track.lyrics['sync'].encode('utf-8'))
trackAlreadyDownloaded = os.path.isfile(writepath)
if trackAlreadyDownloaded and self.settings['overwriteFile'] == 'b':
baseFilename = os.path.join(filepath, filename)
i = 1
currentFilename = baseFilename+' ('+str(i)+')'+ extensions[track.selectedFormat]
while os.path.isfile(currentFilename):
i += 1
currentFilename = baseFilename+' ('+str(i)+')'+ extensions[track.selectedFormat]
trackAlreadyDownloaded = False
writepath = currentFilename
if extrasPath:
if not self.extrasPath:
self.extrasPath = extrasPath
result['extrasPath'] = extrasPath
# Data for m3u file
result['playlistPosition'] = writepath[len(extrasPath):]
# Save playlist cover
if track.playlist:
result['playlistURLs'] = []
if 'dzcdn.net' in track.playlist['picUrl']:
for format in self.settings['localArtworkFormat'].split(","):
if format in ["png","jpg"]:
url = track.playlist['picUrl'].replace(
f"{self.settings['embeddedArtworkSize']}x{self.settings['embeddedArtworkSize']}",
f"{self.settings['localArtworkSize']}x{self.settings['localArtworkSize']}")
if format == "png":
url = url[:url.find("000000-")]+"none-100-0-0.png"
result['playlistURLs'].append({'url': url, 'ext': format})
else:
result['playlistURLs'].append({'url': track.playlist['picUrl'], 'ext': 'jpg'})
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'])
result['playlistCover'] = f"{settingsRegexAlbum(self.settings['coverImageTemplate'], track.playlist, self.settings, trackAPI_gw['_EXTRA_PLAYLIST'])}"
if not trackAlreadyDownloaded or self.settings['overwriteFile'] == 'y':
logger.info(f"[{track.mainArtist['name']} - {track.title}] Downloading the track")
track.downloadUrl = dz.get_track_stream_url(track.id, track.MD5, track.mediaVersion, track.selectedFormat)
def downloadMusic(track, trackAPI_gw):
try:
with open(writepath, 'wb') as stream:
self.streamTrack(stream, track, trackAPI_gw)
except DownloadCancelled:
remove(writepath)
raise DownloadCancelled
except HTTPError:
remove(writepath)
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)
track.parseEssentialData(self.dz, newTrack)
return False
elif not track.searched and self.settings['fallbackSearch']:
logger.warn(f"[{self.mainArtist['name']} - {self.title}] Track not available, searching for alternative")
searchedId = self.dz.get_track_from_metadata(self.mainArtist['name'], self.title, self.album['title'])
if searchedId != 0:
newTrack = self.dz.get_track_gw(searchedId)
track.parseEssentialData(self.dz, newTrack)
track.searched = True
return False
else:
raise DownloadFailed("notAvailableNoAlternative")
else:
raise DownloadFailed("notAvailable")
except ConnectionError as e:
logger.exception(str(e))
logger.warn(f"[{track.mainArtist['name']} - {track.title}] Error while downloading the track, trying again in 5s...")
sleep(5)
return downloadMusic(track, trackAPI_gw)
except Exception as e:
logger.exception(str(e))
logger.warn(f"[{track.mainArtist['name']} - {track.title}] Error while downloading the track, you should report this to the developers")
raise e
return True
try:
trackDownloaded = downloadMusic(track, trackAPI_gw)
except DownloadFailed as e:
raise DownloadFailed
except Exception as e:
raise e
if not trackDownloaded:
return self.download(trackAPI_gw, track)
else:
logger.info(f"[{track.mainArtist['name']} - {track.title}] Skipping track as it's already downloaded")
trackCompletePercentage(trackAPI, queueItem, interface)
# Adding tags
if (not trackAlreadyDownloaded or self.settings['overwriteFile'] in ['t', 'y']) and not track.localTrack:
logger.info(f"[{track.mainArtist['name']} - {track.title}] Applying tags to the track")
if track.selectedFormat in [3, 1, 8]:
tagID3(writepath, track, self.settings['tags'])
elif track.selectedFormat == 9:
try:
tagFLAC(writepath, track, self.settings['tags'])
except FLACNoHeaderError:
remove(writepath)
logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not available in FLAC, falling back if necessary")
self.removeTrackPercentage(trackAPI, queueItem, interface)
track.formats['FILESIZE_FLAC'] = "0"
return self.download(trackAPI_gw, track)
if track.searched:
result['searched'] = f'{track.mainArtist['name']} - {track.title}'
logger.info(f"[{track.mainArtist['name']} - {track.title}] Track download completed")
self.queueItem.downloaded += 1
if self.interface:
self.interface.send("updateQueue", {'uuid': queueItem.uuid, 'downloaded': True, 'downloadPath': writepath})
return result
def getPreferredBitrate(self, track):
if track.localTrack:
return 0
fallback = self.settings['fallbackBitrate']
formats_non_360 = {
9: "FLAC",
3: "MP3_320",
1: "MP3_128",
}
formats_360 = {
15: "MP4_RA3",
14: "MP4_RA2",
13: "MP4_RA1",
}
if not fallback:
error_num = -100
formats = formats_360
formats.update(formats_non_360)
elif int(self.bitrate) in formats_360:
error_num = -200
formats = formats_360
else:
error_num = 8
formats = formats_non_360
for format_num, format in formats.items():
if format_num <= int(self.bitrate):
if f"FILESIZE_{format}" in track.filesizes and int(track.filesizes[f"FILESIZE_{format}"]) != 0:
return format_num
else:
if fallback:
continue
else:
return error_num
return error_num # fallback is enabled and loop went through all formats
def stream_track(self, stream, track, trackAPI):
if self.queueItem.cancel: raise DownloadCancelled
try:
request = get(track.downloadUrl, headers=dz.http_headers, stream=True, timeout=30)
except ConnectionError:
sleep(2)
return stream_track(dz, track, stream, trackAPI, queueItem, interface)
request.raise_for_status()
blowfish_key = str.encode(dz._get_blowfish_key(str(track.id)))
complete = int(request.headers["Content-Length"])
chunkLength = 0
percentage = 0
i = 0
for chunk in request.iter_content(2048):
if self.queueItem.cancel: raise DownloadCancelled
if i % 3 == 0 and len(chunk) == 2048:
chunk = Blowfish.new(blowfish_key, Blowfish.MODE_CBC, b"\x00\x01\x02\x03\x04\x05\x06\x07").decrypt(chunk)
stream.write(chunk)
chunkLength += len(chunk)
if 'SINGLE_TRACK' in trackAPI:
percentage = (chunkLength / complete) * 100
self.downloadPercentage = percentage
else:
chunkProgres = (len(chunk) / complete) / trackAPI['SIZE'] * 100
self.downloadPercentage += chunkProgres
self.updatePercentage()
i += 1
def updatePercentage(self):
if round(self.downloadPercentage) != self.lastPercentage and round(self.downloadPercentage) % 2 == 0:
self.lastPercentage = round(self.downloadPercentage)
self.queueItem.progress = self.lastPercentage
if self.interface:
self.interface.send("updateQueue", {'uuid': self.queueItem.uuid, 'progress': self.lastPercentage})
def completeTrackPercentage(self):
if isinstance(self.queueItem, QISingle):
self.downloadPercentage = 100
else:
self.downloadPercentage += (1 / self.queueItem.size) * 100
self.updatePercentage()
def removeTrackPercentage(self):
if isinstance(self.queueItem, QISingle):
self.downloadPercentage = 0
else:
self.downloadPercentage -= (1 / self.queueItem.size) * 100
self.updatePercentage()
def downloadWrapper(self, trackAPI_gw):
track = {
'id': queueItem.single['SNG_ID'],
'title': queueItem.single['SNG_TITLE'] + (queueItem.single['VERSION'] if 'VERSION' in queueItem.single and queueItem.single['VERSION'] and not queueItem.single['VERSION'] in queueItem.single['SNG_TITLE'] else ""),
'mainArtist': {'name': queueItem.single['ART_NAME']}
}
try:
result = self.download(trackAPI_gw)
except DownloadCancelled:
return None
except DownloadFailed as error:
logger.error(f"[{track['mainArtist']['name']} - {track['title']}] {error.message}")
result = {'error': {
'message': error.message,
'errid': error.errid,
'data': track
}}
except Exception as e:
logger.exception(str(e))
result = {'error': {
'message': str(e),
'data': track
}}
if 'error' in result:
self.completeTrackPercentage()
self.queueItem.failed += 1
self.queueItem.errors.append(error.message)
if interface:
error = result['error']
interface.send("updateQueue", {
'uuid': self.queueItem.uuid,
'failed': True,
'data': error['data'],
'error': error['message'],
'errid': error['errid'] if 'errid' in error else None
})
return result
class DownloadError(Exception):
"""Base class for exceptions in this module."""
pass
class DownloadFailed(DownloadError):
def __init__(self, errid):
self.errid = errid
self.message = errorMessages[self.errid]
class DownloadCancelled(DownloadError):
pass

354
deemix/app/Track.py Normal file
View File

@ -0,0 +1,354 @@
#!/usr/bin/env python3
import logging
from deemix.api.deezer import APIError
from deemix.utils.misc import removeFeatures, andCommaConcat, uniqueArray
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('deemix')
class Track:
def __init__(self, dz, settings, trackAPI_gw, trackAPI=None, albumAPI_gw=None, albumAPI=None):
self.parseEssentialData(dz, trackAPI_gw)
self.title = trackAPI_gw['SNG_TITLE'].strip()
if 'VERSION' in trackAPI_gw and trackAPI_gw['VERSION'] and not trackAPI_gw['VERSION'] in trackAPI_gw['SNG_TITLE']:
track.title += " " + trackAPI_gw['VERSION'].strip()
self.position = None
if 'POSITION' in trackAPI_gw:
self.position = trackAPI_gw['POSITION']
self.localTrack = int(self.id) < 0
if self.localTrack:
self.parseLocalTrackData(trackAPI_gw)
else:
self.parseData(dz, settings, trackAPI_gw, trackAPI, albumAPI_gw, albumAPI)
if not 'Main' in self.artist:
self.artist['Main'] = [self.mainArtist['name']]
# Fix incorrect day month when detectable
if int(self.date['month']) > 12:
monthTemp = self.date['month']
self.date['month'] = self.date['day']
self.date['day'] = monthTemp
if int(self.album['date']['month']) > 12:
monthTemp = self.album['date']['month']
self.album['date']['month'] = self.album['date']['day']
self.album['date']['day'] = monthTemp
# Add playlist data if track is in a playlist
self.playlist = None
if "_EXTRA_PLAYLIST" in trackAPI_gw:
self.playlist = {}
if 'dzcdn.net' in trackAPI_gw["_EXTRA_PLAYLIST"]['picture_small']:
self.playlist['picUrl'] = trackAPI_gw["_EXTRA_PLAYLIST"]['picture_small'][:-24] + "/{}x{}-{}".format(
settings['embeddedArtworkSize'], settings['embeddedArtworkSize'],
f'000000-{settings["jpegImageQuality"]}-0-0.jpg')
else:
self.playlist['picUrl'] = trackAPI_gw["_EXTRA_PLAYLIST"]['picture_xl']
self.playlist['title'] = trackAPI_gw["_EXTRA_PLAYLIST"]['title']
self.playlist['mainArtist'] = {
'id': trackAPI_gw["_EXTRA_PLAYLIST"]['various_artist']['id'],
'name': trackAPI_gw["_EXTRA_PLAYLIST"]['various_artist']['name'],
'pic': trackAPI_gw["_EXTRA_PLAYLIST"]['various_artist']['picture_small'][
trackAPI_gw["_EXTRA_PLAYLIST"]['various_artist']['picture_small'].find('artist/') + 7:-24]
}
if settings['albumVariousArtists']:
self.playlist['artist'] = {"Main": [trackAPI_gw["_EXTRA_PLAYLIST"]['various_artist']['name'], ]}
self.playlist['artists'] = [trackAPI_gw["_EXTRA_PLAYLIST"]['various_artist']['name'], ]
else:
self.playlist['artist'] = {"Main": []}
self.playlist['artists'] = []
self.playlist['trackTotal'] = trackAPI_gw["_EXTRA_PLAYLIST"]['nb_tracks']
self.playlist['recordType'] = "Compilation"
self.playlist['barcode'] = ""
self.playlist['label'] = ""
self.playlist['explicit'] = trackAPI_gw['_EXTRA_PLAYLIST']['explicit']
self.playlist['date'] = {
'day': trackAPI_gw["_EXTRA_PLAYLIST"]["creation_date"][8:10],
'month': trackAPI_gw["_EXTRA_PLAYLIST"]["creation_date"][5:7],
'year': trackAPI_gw["_EXTRA_PLAYLIST"]["creation_date"][0:4]
}
self.playlist['discTotal'] = "1"
self.mainArtistsString = andCommaConcat(self.artist['Main'])
self.featArtistsString = None
if 'Featured' in self.artist:
self.featArtistsString = "feat. "+andCommaConcat(self.artist['Featured'])
# Bits useful for later
self.searched = False
self.selectedFormat = 0
self.dateString = None
self.album['picUrl'] = None
self.album['picPath'] = None
self.album['bitrate'] = 0
self.album['dateString'] = None
self.artistsString
def parseEssentialData(self, dz, trackAPI_gw):
self.id = trackAPI_gw['SNG_ID']
self.duration = trackAPI_gw['DURATION']
self.MD5 = trackAPI_gw['MD5_ORIGIN']
self.mediaVersion = trackAPI_gw['MEDIA_VERSION']
self.fallbackId = "0"
if 'FALLBACK' in trackAPI_gw:
self.fallbackId = trackAPI_gw['FALLBACK']['SNG_ID']
self.formats = dz.get_track_filesizes(track["id"])
def parseLocalTrackData(self, trackAPI_gw):
self.album = {
'id': "0",
'title': trackAPI_gw['ALB_TITLE'],
'pic': ""
}
if 'ALB_PICTURE' in trackAPI_gw:
self.album['pic'] = trackAPI_gw['ALB_PICTURE']
self.mainArtist = {
'id': "0",
'name': trackAPI_gw['ART_NAME'],
'pic': ""
}
self.artists = [trackAPI_gw['ART_NAME']]
self.artist = {
'Main': [trackAPI_gw['ART_NAME']]
}
self.date = {
'day': "00",
'month': "00",
'year': "XXXX"
}
# All the missing data
self.ISRC = ""
self.album['artist'] = self.artist
self.album['artists'] = self.artists
self.album['barcode'] = "Unknown"
self.album['date'] = self.date
self.album['discTotal'] = "0"
self.album['explicit'] = False
self.album['genre'] = []
self.album['label'] = "Unknown"
self.album['mainArtist'] = self.mainArtist
self.album['recordType'] = "Album"
self.album['trackTotal'] = "0"
self.bpm = 0
self.contributors = {}
self.copyright = ""
self.discNumber = "0"
self.explicit = False
self.lyrics = {}
self.replayGain = ""
self.trackNumber = "0"
def parseData(self, dz, settings, trackAPI_gw, trackAPI, albumAPI_gw, albumAPI):
self.discNumber = None
if 'DISK_NUMBER' in trackAPI_gw:
self.discNumber = trackAPI_gw['DISK_NUMBER']
self.explicit = None
if 'EXPLICIT_LYRICS' in trackAPI_gw:
self.explicit = trackAPI_gw['EXPLICIT_LYRICS']
self.copyright = None
if 'COPYRIGHT' in trackAPI_gw:
self.copyright = trackAPI_gw['COPYRIGHT']
self.replayGain = ""
if 'GAIN' in trackAPI_gw:
self.replayGain = "{0:.2f} dB".format((float(trackAPI_gw['GAIN']) + 18.4) * -1)
self.ISRC = trackAPI_gw['ISRC']
self.trackNumber = trackAPI_gw['TRACK_NUMBER']
self.contributors = trackAPI_gw['SNG_CONTRIBUTORS']
track.lyrics = {
'id': None,
'unsync': None,
'sync': None
}
if 'LYRICS_ID' in trackAPI_gw:
self.lyrics['id'] = trackAPI_gw['LYRICS_ID']
if not "LYRICS" in trackAPI_gw and int(self.lyrics['id']) != 0:
logger.info(f"[{trackAPI_gw['ART_NAME']} - {self.title}] Getting lyrics")
trackAPI_gw["LYRICS"] = dz.get_lyrics_gw(self.id)
if int(self.lyrics['id']) != 0:
if "LYRICS_TEXT" in trackAPI_gw["LYRICS"]:
self.lyrics['unsync'] = trackAPI_gw["LYRICS"]["LYRICS_TEXT"]
if "LYRICS_SYNC_JSON" in trackAPI_gw["LYRICS"]:
self.lyrics['sync'] = ""
lastTimestamp = ""
for i in range(len(trackAPI_gw["LYRICS"]["LYRICS_SYNC_JSON"])):
if "lrc_timestamp" in trackAPI_gw["LYRICS"]["LYRICS_SYNC_JSON"][i]:
self.lyrics['sync'] += trackAPI_gw["LYRICS"]["LYRICS_SYNC_JSON"][i]["lrc_timestamp"]
lastTimestamp = trackAPI_gw["LYRICS"]["LYRICS_SYNC_JSON"][i]["lrc_timestamp"]
else:
self.lyrics['sync'] += lastTimestamp
self.lyrics['sync'] += trackAPI_gw["LYRICS"]["LYRICS_SYNC_JSON"][i]["line"] + "\r\n"
track.mainArtist = {
'id': trackAPI_gw['ART_ID'],
'name': trackAPI_gw['ART_NAME'],
'pic': None
}
if 'ART_PICTURE' in trackAPI_gw:
track.mainArtist['pic'] = trackAPI_gw['ART_PICTURE']
self.date = None
if 'PHYSICAL_RELEASE_DATE' in trackAPI_gw:
self.date = {
'day': trackAPI_gw["PHYSICAL_RELEASE_DATE"][8:10],
'month': trackAPI_gw["PHYSICAL_RELEASE_DATE"][5:7],
'year': trackAPI_gw["PHYSICAL_RELEASE_DATE"][0:4]
}
self.album = {
'id': trackAPI_gw['ALB_ID'],
'title': trackAPI_gw['ALB_TITLE'],
'pic': None,
'barcode': "Unknown",
'label': "Unknown",
'explicit': False,
'date': None,
'genre': []
}
if 'ALB_PICTURE' in trackAPI_gw:
self.album['pic'] = trackAPI_gw['ALB_PICTURE']
try:
# Try the public API first (as it has more data)
if not albumAPI:
logger.info(f"[{self.mainArtist['name']} - {self.title}] Getting album infos")
albumAPI = dz.get_album(self.album['id'])
self.album['title'] = albumAPI['title']
self.album['mainArtist'] = {
'id': albumAPI['artist']['id'],
'name': albumAPI['artist']['name'],
'pic': albumAPI['artist']['picture_small'][albumAPI['artist']['picture_small'].find('artist/') + 7:-24]
}
self.album['artist'] = {}
self.album['artists'] = []
for artist in albumAPI['contributors']:
if artist['id'] != 5080 or artist['id'] == 5080 and settings['albumVariousArtists']:
if artist['name'] not in self.album['artists']:
self.album['artists'].append(artist['name'])
if artist['role'] == "Main" or artist['role'] != "Main" and artist['name'] not in self.album['artist']['Main']:
if not artist['role'] in self.album['artist']:
self.album['artist'][artist['role']] = []
self.album['artist'][artist['role']].append(artist['name'])
if settings['removeDuplicateArtists']:
self.album['artists'] = uniqueArray(self.album['artists'])
for role in self.album['artist'].keys():
self.album['artist'][role] = uniqueArray(self.album['artist'][role])
self.album['trackTotal'] = albumAPI['nb_tracks']
self.album['recordType'] = albumAPI['record_type']
if 'upc' in albumAPI:
self.album['barcode'] = albumAPI['upc']
if 'label' in albumAPI:
self.album['label'] = albumAPI['label']
if 'explicit_lyrics' in albumAPI:
self.album['explicit'] = albumAPI['explicit_lyrics']
if 'release_date' in albumAPI:
self.album['date'] = {
'day': albumAPI["release_date"][8:10],
'month': albumAPI["release_date"][5:7],
'year': albumAPI["release_date"][0:4]
}
self.album['discTotal'] = None
if 'nb_disk' in albumAPI:
self.album['discTotal'] = albumAPI['nb_disk']
self.copyright = None
if 'copyright' in albumAPI:
self.copyright = albumAPI['copyright']
if not track.album['pic']:
self.album['pic'] = albumAPI['cover_small'][albumAPI['cover_small'].find('cover/') + 6:-24]
if 'genres' in albumAPI and 'data' in albumAPI['genres'] and len(albumAPI['genres']['data']) > 0:
for genre in albumAPI['genres']['data']:
self.album['genre'].append(genre['name'])
except APIError:
if not albumAPI_gw:
logger.info(f"[{self.mainArtist['name']} - {self.title}] Getting more album infos")
albumAPI_gw = dz.get_album_gw(self.album['id'])
self.album['title'] = albumAPI_gw['ALB_TITLE']
self.album['mainArtist'] = {
'id': albumAPI_gw['ART_ID'],
'name': albumAPI_gw['ART_NAME'],
'pic': None
}
logger.info(f"[{self.mainArtist['name']} - {self.title}] Getting artist picture fallback")
artistAPI = dz.get_artist(self.album['mainArtist']['id'])
self.album['artists'] = [albumAPI_gw['ART_NAME']]
self.album['mainArtist']['pic'] = artistAPI['picture_small'][artistAPI['picture_small'].find('artist/') + 7:-24]
self.album['trackTotal'] = albumAPI_gw['NUMBER_TRACK']
self.album['discTotal'] = albumAPI_gw['NUMBER_DISK']
self.album['recordType'] = "Album"
if 'LABEL_NAME' in albumAPI_gw:
self.album['label'] = albumAPI_gw['LABEL_NAME']
if 'EXPLICIT_ALBUM_CONTENT' in albumAPI_gw and 'EXPLICIT_LYRICS_STATUS' in albumAPI_gw['EXPLICIT_ALBUM_CONTENT']:
self.album['explicit'] = albumAPI_gw['EXPLICIT_ALBUM_CONTENT']['EXPLICIT_LYRICS_STATUS'] in [1,4]
if not self.album['pic']:
self.album['pic'] = albumAPI_gw['ALB_PICTURE']
if 'PHYSICAL_RELEASE_DATE' in albumAPI_gw:
self.album['date'] = {
'day': albumAPI_gw["PHYSICAL_RELEASE_DATE"][8:10],
'month': albumAPI_gw["PHYSICAL_RELEASE_DATE"][5:7],
'year': albumAPI_gw["PHYSICAL_RELEASE_DATE"][0:4]
}
self.album['mainArtist']['save'] = self.album['mainArtist']['id'] != 5080 or self.album['mainArtist']['id'] == 5080 and settings['albumVariousArtists']
if self.album['date'] and not self.date:
self.date = self.album['date']
if not trackAPI:
logger.info(f"[{self.mainArtist['name']} - {self.title}] Getting extra track infos")
trackAPI = dz.get_track(self.id)
self.bpm = trackAPI['bpm']
if not self.replayGain and 'gain' in trackAPI:
self.replayGain = "{0:.2f} dB".format((float(trackAPI['gain']) + 18.4) * -1)
if not self.explicit:
self.explicit = trackAPI['explicit_lyrics']
if not self.discNumber:
self.discNumber = trackAPI['disk_number']
self.artist = {}
self.artists = []
for artist in trackAPI['contributors']:
if artist['id'] != 5080 or artist['id'] == 5080 and len(trackAPI['contributors']) == 1:
if artist['name'] not in self.artists:
self.artists.append(artist['name'])
if artist['role'] != "Main" and artist['name'] not in self.artist['Main'] or artist['role'] == "Main":
if not artist['role'] in self.artist:
self.artist[artist['role']] = []
self.artist[artist['role']].append(artist['name'])
if settings['removeDuplicateArtists']:
self.artists = uniqueArray(self.artists)
for role in self.artist.keys():
self.artist[role] = uniqueArray(self.artist[role])
if not self.album['discTotal']:
if not albumAPI_gw:
logger.info(f"[{self.mainArtist['name']} - {self.title}] Getting more album infos")
albumAPI_gw = dz.get_album_gw(self.album['id'])
self.album['discTotal'] = albumAPI_gw['NUMBER_DISK']
if not self.copyright:
if not albumAPI_gw:
logger.info(f"[{self.mainArtist['name']} - {self.title}] Getting more album infos")
albumAPI_gw = dz.get_album_gw(self.album['id'])
self.copyright = albumAPI_gw['COPYRIGHT']
# Removes featuring from the title
def getCleanTitle(self):
return removeFeatures(self.title)
# Removes featuring from the album name
def getCleanAlbumTitle(self):
return removeFeatures(self.album['title'])
def getFeatTitle(self):
if self.featArtistsString and not "(feat." in self.title.lower():
return self.title + " ({})".format(self.featArtistsString)
return self.title

File diff suppressed because it is too large Load Diff

View File

@ -34,6 +34,31 @@ def changeCase(str, type):
return str
def removeFeatures(title):
clean = title
if "(feat." in clean.lower():
pos = clean.lower().find("(feat.")
tempTrack = clean[:pos]
if ")" in clean:
tempTrack += clean[clean.find(")", pos + 1) + 1:]
clean = tempTrack.strip()
return clean
def andCommaConcat(lst):
tot = len(lst)
result = ""
for i, art in enumerate(lst):
result += art
track['commaArtistsString'] += art
if tot != i + 1:
if tot - 1 == i + 1:
result += " & "
else:
result += ", "
return result
def getIDFromLink(link, type):
if '?' in link:
link = link[:link.find('?')]

View File

@ -94,10 +94,10 @@ def generateFilepath(track, trackAPI, settings):
'savePlaylistAsCompilation']) or
(settings['createArtistFolder'] and '_EXTRA_PLAYLIST' in trackAPI and settings['createStructurePlaylist'])
):
if (int(track['id']) < 0 and not 'mainArtist' in track['album']):
track['album']['mainArtist'] = track['mainArtist']
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
settingsRegexArtist(settings['artistNameTemplate'], track.album['mainArtist'], settings)) + pathSep
artistPath = filepath
if (settings['createAlbumFolder'] and
@ -107,7 +107,7 @@ def generateFilepath(track, trackAPI, settings):
'_EXTRA_PLAYLIST' in trackAPI and settings['createStructurePlaylist']))
):
filepath += antiDot(
settingsRegexAlbum(settings['albumNameTemplate'], track['album'], settings,
settingsRegexAlbum(settings['albumNameTemplate'], track.album, settings,
trackAPI['_EXTRA_PLAYLIST'] if'_EXTRA_PLAYLIST' in trackAPI else None)) + pathSep
coverPath = filepath
@ -115,55 +115,55 @@ def generateFilepath(track, trackAPI, settings):
extrasPath = filepath
if (
int(track['album']['discTotal']) > 1 and (
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']))
)):
filepath += 'CD' + str(track['discNumber']) + pathSep
filepath += 'CD' + str(track.discNumber) + pathSep
return (filepath, artistPath, coverPath, extrasPath)
def settingsRegex(filename, track, settings, playlist=None):
filename = filename.replace("%title%", fixName(track['title'], settings['illegalCharacterReplacer']))
filename = filename.replace("%artist%", fixName(track['mainArtist']['name'], settings['illegalCharacterReplacer']))
filename = filename.replace("%artists%", fixName(track['commaArtistsString'], 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 'featArtistsString' in track else "")
filename = filename.replace("%album%", fixName(track['album']['title'], settings['illegalCharacterReplacer']))
filename = filename.replace("%title%", fixName(track.title, settings['illegalCharacterReplacer']))
filename = filename.replace("%artist%", fixName(track.mainArtist['name'], settings['illegalCharacterReplacer']))
filename = filename.replace("%artists%", fixName(track.commaArtistsString, 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 'featArtistsString' in track else "")
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(
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("%tracktotal%", str(track['album']['trackTotal']))
filename = filename.replace("%discnumber%", str(track['discNumber']))
filename = filename.replace("%disctotal%", str(track['album']['discTotal']))
if len(track['album']['genre']) > 0:
filename = filename.replace("%tracktotal%", str(track.album['trackTotal']))
filename = filename.replace("%discnumber%", str(track.discNumber))
filename = filename.replace("%disctotal%", str(track.album['discTotal']))
if len(track.album['genre']) > 0:
filename = filename.replace("%genre%",
fixName(track['album']['genre'][0], settings['illegalCharacterReplacer']))
fixName(track.album['genre'][0], settings['illegalCharacterReplacer']))
else:
filename = filename.replace("%genre%", "Unknown")
filename = filename.replace("%year%", str(track['date']['year']))
filename = filename.replace("%date%", track['dateString'])
filename = filename.replace("%bpm%", str(track['bpm']))
filename = filename.replace("%label%", fixName(track['album']['label'], settings['illegalCharacterReplacer']))
filename = filename.replace("%isrc%", track['ISRC'])
filename = filename.replace("%upc%", track['album']['barcode'])
filename = filename.replace("%explicit%", "(Explicit)" if track['explicit'] else "")
filename = filename.replace("%year%", str(track.date['year']))
filename = filename.replace("%date%", track.dateString)
filename = filename.replace("%bpm%", str(track.bpm))
filename = filename.replace("%label%", fixName(track.album['label'], settings['illegalCharacterReplacer']))
filename = filename.replace("%isrc%", track.ISRC)
filename = filename.replace("%upc%", track.album['barcode'])
filename = filename.replace("%explicit%", "(Explicit)" if track.explicit else "")
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']))
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(
filename = filename.replace("%position%", pad(track.position, playlist['nb_tracks'] if int(
settings['paddingSize']) == 0 else 10 ** (int(settings['paddingSize']) - 1), settings['padTracks']))
else:
filename = filename.replace("%position%", pad(track['trackNumber'], track['album']['trackTotal'] if int(
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('\\', pathSep).replace('/', pathSep)
return antiDot(fixLongName(filename))

View File

@ -13,75 +13,75 @@ def tagID3(stream, track, save):
tag = ID3()
if save['title']:
tag.add(TIT2(text=track['title']))
tag.add(TIT2(text=track.title))
if save['artist'] and len(track['artists']):
if save['artist'] and len(track.artists):
if save['multiArtistSeparator'] != "default":
if save['multiArtistSeparator'] == "nothing":
tag.add(TPE1(text=track['mainArtist']['name']))
tag.add(TPE1(text=track.mainArtist['name']))
else:
tag.add(TPE1(text=track['artistsString']))
tag.add(TXXX(desc="ARTISTS", text=track['artists']))
tag.add(TPE1(text=track.artistsString))
tag.add(TXXX(desc="ARTISTS", text=track.artists))
else:
tag.add(TPE1(text=track['artists']))
tag.add(TPE1(text=track.artists))
if save['album']:
tag.add(TALB(text=track['album']['title']))
tag.add(TALB(text=track.album['title']))
if save['albumArtist'] and len(track['album']['artists']):
if save['singleAlbumArtist'] and track['album']['mainArtist']['save']:
tag.add(TPE2(text=track['album']['mainArtist']['name']))
if save['albumArtist'] and len(track.album['artists']):
if save['singleAlbumArtist'] and track.album['mainArtist']['save']:
tag.add(TPE2(text=track.album['mainArtist']['name']))
else:
tag.add(TPE2(text=track['album']['artists']))
tag.add(TPE2(text=track.album['artists']))
if save['trackNumber']:
tag.add(TRCK(
text=str(track['trackNumber']) + ("/" + str(track['album']['trackTotal']) if save['trackTotal'] else "")))
text=str(track.trackNumber) + ("/" + str(track.album['trackTotal']) if save['trackTotal'] else "")))
if save['discNumber']:
tag.add(
TPOS(text=str(track['discNumber']) + ("/" + str(track['album']['discTotal']) if save['discTotal'] else "")))
TPOS(text=str(track.discNumber) + ("/" + str(track.album['discTotal']) if save['discTotal'] else "")))
if save['genre']:
tag.add(TCON(text=track['album']['genre']))
tag.add(TCON(text=track.album['genre']))
if save['year']:
tag.add(TYER(text=str(track['date']['year'])))
tag.add(TYER(text=str(track.date['year'])))
if save['date']:
tag.add(TDAT(text=str(track['date']['month']) + str(track['date']['day'])))
tag.add(TDAT(text=str(track.date['month']) + str(track.date['day'])))
if save['length']:
tag.add(TLEN(text=str(int(track['duration'])*1000)))
tag.add(TLEN(text=str(int(track.duration)*1000)))
if save['bpm']:
tag.add(TBPM(text=str(track['bpm'])))
tag.add(TBPM(text=str(track.bpm)))
if save['label']:
tag.add(TPUB(text=track['album']['label']))
tag.add(TPUB(text=track.album['label']))
if save['isrc']:
tag.add(TSRC(text=track['ISRC']))
tag.add(TSRC(text=track.ISRC))
if save['barcode']:
tag.add(TXXX(desc="BARCODE", text=track['album']['barcode']))
tag.add(TXXX(desc="BARCODE", text=track.album['barcode']))
if save['explicit']:
tag.add(TXXX(desc="ITUNESADVISORY", text="1" if track['explicit'] else "0"))
tag.add(TXXX(desc="ITUNESADVISORY", text="1" if track.explicit else "0"))
if save['replayGain']:
tag.add(TXXX(desc="REPLAYGAIN_TRACK_GAIN", text=track['replayGain']))
if 'unsync' in track['lyrics'] and save['lyrics']:
tag.add(USLT(text=track['lyrics']['unsync']))
tag.add(TXXX(desc="REPLAYGAIN_TRACK_GAIN", text=track.replayGain))
if 'unsync' in track.lyrics and save['lyrics']:
tag.add(USLT(text=track.lyrics['unsync']))
involved_people = []
for role in track['contributors']:
for role in track.contributors:
if role in ['author', 'engineer', 'mixer', 'producer', 'writer']:
for person in track['contributors'][role]:
for person in track.contributors[role]:
involved_people.append([role, person])
elif role == 'composer' and save['composer']:
tag.add(TCOM(text=track['contributors']['composer']))
tag.add(TCOM(text=track.contributors['composer']))
if len(involved_people) > 0 and save['involvedPeople']:
tag.add(IPLS(people=involved_people))
if save['copyright']:
tag.add(TCOP(text=track['copyright']))
tag.add(TCOP(text=track.copyright))
if save['savePlaylistAsCompilation'] and "playlist" in track:
tag.add(TCMP(text="1"))
if save['cover'] and track['album']['picPath']:
with open(track['album']['picPath'], 'rb') as f:
if save['cover'] and track.album['picPath']:
with open(track.album['picPath'], 'rb') as f:
tag.add(
APIC(3, 'image/jpeg' if track['album']['picPath'].endswith('jpg') else 'image/png', 3, desc='cover', data=f.read()))
APIC(3, 'image/jpeg' if track.album['picPath'].endswith('jpg') else 'image/png', 3, desc='cover', data=f.read()))
tag.save(stream, v1=2 if save['saveID3v1'] else 0, v2_version=3,
v23_sep=None if save['useNullSeparator'] else '/')
@ -94,75 +94,75 @@ def tagFLAC(stream, track, save):
tag.clear_pictures()
if save['title']:
tag["TITLE"] = track['title']
tag["TITLE"] = track.title
if save['artist'] and len(track['artists']):
if save['artist'] and len(track.artists):
if save['multiArtistSeparator'] != "default":
if save['multiArtistSeparator'] == "nothing":
tag["ARTIST"] = track['mainArtist']['name']
tag["ARTIST"] = track.mainArtist['name']
else:
tag["ARTIST"] = track['artistsString']
tag["ARTISTS"] = track['artists']
tag["ARTIST"] = track.artistsString
tag["ARTISTS"] = track.artists
else:
tag["ARTIST"] = track['artists']
tag["ARTIST"] = track.artists
if save['album']:
tag["ALBUM"] = track['album']['title']
tag["ALBUM"] = track.album['title']
if save['albumArtist'] and len(track['album']['artists']):
if save['albumArtist'] and len(track.album['artists']):
if save['singleAlbumArtist']:
tag["ALBUMARTIST"] = track['album']['mainArtist']['name']
tag["ALBUMARTIST"] = track.album['mainArtist']['name']
else:
tag["ALBUMARTIST"] = track['album']['artists']
tag["ALBUMARTIST"] = track.album['artists']
if save['trackNumber']:
tag["TRACKNUMBER"] = str(track['trackNumber'])
tag["TRACKNUMBER"] = str(track.trackNumber)
if save['trackTotal']:
tag["TRACKTOTAL"] = str(track['album']['trackTotal'])
tag["TRACKTOTAL"] = str(track.album['trackTotal'])
if save['discNumber']:
tag["DISCNUMBER"] = str(track['discNumber'])
tag["DISCNUMBER"] = str(track.discNumber)
if save['discTotal']:
tag["DISCTOTAL"] = str(track['album']['discTotal'])
tag["DISCTOTAL"] = str(track.album['discTotal'])
if save['genre']:
tag["GENRE"] = track['album']['genre']
tag["GENRE"] = track.album['genre']
if save['date']:
tag["DATE"] = track['dateString']
tag["DATE"] = track.dateString
elif save['year']:
tag["YEAR"] = str(track['date']['year'])
tag["YEAR"] = str(track.date['year'])
if save['length']:
tag["LENGTH"] = str(track['duration'])
tag["LENGTH"] = str(track.duration)
if save['bpm']:
tag["BPM"] = str(track['bpm'])
tag["BPM"] = str(track.bpm)
if save['label']:
tag["PUBLISHER"] = track['album']['label']
tag["PUBLISHER"] = track.album['label']
if save['isrc']:
tag["ISRC"] = track['ISRC']
tag["ISRC"] = track.ISRC
if save['barcode']:
tag["BARCODE"] = track['album']['barcode']
tag["BARCODE"] = track.album['barcode']
if save['explicit']:
tag["ITUNESADVISORY"] = "1" if track['explicit'] else "0"
tag["ITUNESADVISORY"] = "1" if track.explicit else "0"
if save['replayGain']:
tag["REPLAYGAIN_TRACK_GAIN"] = track['replayGain']
if 'unsync' in track['lyrics'] and save['lyrics']:
tag["LYRICS"] = track['lyrics']['unsync']
tag["REPLAYGAIN_TRACK_GAIN"] = track.replayGain
if 'unsync' in track.lyrics and save['lyrics']:
tag["LYRICS"] = track.lyrics['unsync']
for role in track['contributors']:
for role in track.contributors:
if role in ['author', 'engineer', 'mixer', 'producer', 'writer', 'composer']:
if save['involvedPeople'] and role != 'composer' or role == 'composer' and save['composer']:
tag[role] = track['contributors'][role]
tag[role] = track.contributors[role]
elif role == 'musicpublisher' and save['involvedPeople']:
tag["ORGANIZATION"] = track['contributors']['musicpublisher']
tag["ORGANIZATION"] = track.contributors['musicpublisher']
if save['copyright']:
tag["COPYRIGHT"] = track['copyright']
tag["COPYRIGHT"] = track.copyright
if save['savePlaylistAsCompilation'] and "playlist" in track:
tag["COMPILATION"] = "1"
if save['cover'] and track['album']['picPath']:
if save['cover'] and track.album['picPath']:
image = Picture()
image.type = 3
image.mime = 'image/jpeg' if track['album']['picPath'].endswith('jpg') else 'image/png'
with open(track['album']['picPath'], 'rb') as f:
image.mime = 'image/jpeg' if track.album['picPath'].endswith('jpg') else 'image/png'
with open(track.album['picPath'], 'rb') as f:
image.data = f.read()
tag.add_picture(image)