Merge branch 'refactoring' into 'main'
Refactoring See merge request RemixDev/deemix!4
This commit is contained in:
commit
e9434ceeb6
|
@ -3,3 +3,4 @@
|
|||
__version__ = "2.0.11"
|
||||
USER_AGENT_HEADER = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) " \
|
||||
"Chrome/79.0.3945.130 Safari/537.36"
|
||||
VARIOUS_ARTISTS = "5080"
|
||||
|
|
|
@ -16,7 +16,7 @@ from tempfile import gettempdir
|
|||
from urllib3.exceptions import SSLError as u3SSLError
|
||||
|
||||
from deemix.app.queueitem import QISingle, QICollection
|
||||
from deemix.app.track import Track, AlbumDoesntExists
|
||||
from deemix.types.Track import Track, AlbumDoesntExists
|
||||
from deemix.utils import changeCase
|
||||
from deemix.utils.pathtemplates import generateFilename, generateFilepath, settingsRegexAlbum, settingsRegexArtist, settingsRegexPlaylistFile
|
||||
from deezer import TrackFormats
|
||||
|
@ -93,39 +93,6 @@ def downloadImage(url, path, overwrite=OverwriteOption.DONT_OVERWRITE):
|
|||
else:
|
||||
return path
|
||||
|
||||
def generatePictureURL(pic, size, format):
|
||||
if pic['url']: return pic['url']
|
||||
if format.startswith("jpg"):
|
||||
if '-' in format:
|
||||
quality = format[4:]
|
||||
else:
|
||||
quality = 80
|
||||
format = 'jpg'
|
||||
return "https://e-cdns-images.dzcdn.net/images/{}/{}/{}x{}-{}".format(
|
||||
pic['type'],
|
||||
pic['md5'],
|
||||
size, size,
|
||||
f'000000-{quality}-0-0.jpg'
|
||||
)
|
||||
if format == 'png':
|
||||
return "https://e-cdns-images.dzcdn.net/images/{}/{}/{}x{}-{}".format(
|
||||
pic['type'],
|
||||
pic['md5'],
|
||||
size, size,
|
||||
'none-100-0-0.png'
|
||||
)
|
||||
|
||||
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, queueItem, interface=None):
|
||||
|
@ -251,11 +218,13 @@ class DownloadJob:
|
|||
if not track:
|
||||
logger.info(f"[{trackAPI_gw['ART_NAME']} - {trackAPI_gw['SNG_TITLE']}] Getting the tags")
|
||||
try:
|
||||
track = Track(self.dz,
|
||||
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
|
||||
)
|
||||
track = Track().parseData(
|
||||
dz=self.dz,
|
||||
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,
|
||||
playlistAPI = trackAPI_gw['_EXTRA_PLAYLIST'] if '_EXTRA_PLAYLIST' in trackAPI_gw else None
|
||||
)
|
||||
except AlbumDoesntExists:
|
||||
raise DownloadError('albumDoesntExists')
|
||||
if self.queueItem.cancel: raise DownloadCancelled
|
||||
|
@ -263,16 +232,18 @@ class DownloadJob:
|
|||
# Check if track not yet encoded
|
||||
if track.MD5 == '':
|
||||
if track.fallbackId != "0":
|
||||
logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not yet encoded, using fallback id")
|
||||
logger.warn(f"[{track.mainArtist.name} - {track.title}] Track not yet encoded, using fallback id")
|
||||
newTrack = self.dz.gw.get_track_with_fallback(track.fallbackId)
|
||||
track.parseEssentialData(self.dz, newTrack)
|
||||
track.parseEssentialData(newTrack)
|
||||
track.retriveFilesizes(self.dz)
|
||||
return self.download(trackAPI_gw, track)
|
||||
elif not track.searched and self.settings['fallbackSearch']:
|
||||
logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not yet encoded, searching for alternative")
|
||||
searchedId = self.dz.api.get_track_id_from_metadata(track.mainArtist['name'], track.title, track.album['title'])
|
||||
logger.warn(f"[{track.mainArtist.name} - {track.title}] Track not yet encoded, searching for alternative")
|
||||
searchedId = self.dz.api.get_track_id_from_metadata(track.mainArtist.name, track.title, track.album.title)
|
||||
if searchedId != "0":
|
||||
newTrack = self.dz.gw.get_track_with_fallback(searchedId)
|
||||
track.parseEssentialData(self.dz, newTrack)
|
||||
track.parseEssentialData(newTrack)
|
||||
track.retriveFilesizes(self.dz)
|
||||
track.searched = True
|
||||
if self.interface:
|
||||
self.interface.send('queueUpdate', {
|
||||
|
@ -281,7 +252,7 @@ class DownloadJob:
|
|||
'data': {
|
||||
'id': track.id,
|
||||
'title': track.title,
|
||||
'artist': track.mainArtist['name']
|
||||
'artist': track.mainArtist.name
|
||||
},
|
||||
})
|
||||
return self.download(trackAPI_gw, track)
|
||||
|
@ -295,16 +266,18 @@ class DownloadJob:
|
|||
selectedFormat = self.getPreferredBitrate(track)
|
||||
except PreferredBitrateNotFound:
|
||||
if track.fallbackId != "0":
|
||||
logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not found at desired bitrate, using fallback id")
|
||||
logger.warn(f"[{track.mainArtist.name} - {track.title}] Track not found at desired bitrate, using fallback id")
|
||||
newTrack = self.dz.gw.get_track_with_fallback(track.fallbackId)
|
||||
track.parseEssentialData(self.dz, newTrack)
|
||||
track.parseEssentialData(newTrack)
|
||||
track.retriveFilesizes(self.dz)
|
||||
return self.download(trackAPI_gw, track)
|
||||
elif not track.searched and self.settings['fallbackSearch']:
|
||||
logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not found at desired bitrate, searching for alternative")
|
||||
searchedId = self.dz.api.get_track_id_from_metadata(track.mainArtist['name'], track.title, track.album['title'])
|
||||
logger.warn(f"[{track.mainArtist.name} - {track.title}] Track not found at desired bitrate, searching for alternative")
|
||||
searchedId = self.dz.api.get_track_id_from_metadata(track.mainArtist.name, track.title, track.album.title)
|
||||
if searchedId != "0":
|
||||
newTrack = self.dz.gw.get_track_with_fallback(searchedId)
|
||||
track.parseEssentialData(self.dz, newTrack)
|
||||
track.parseEssentialData(newTrack)
|
||||
track.retriveFilesizes(self.dz)
|
||||
track.searched = True
|
||||
if self.interface:
|
||||
self.interface.send('queueUpdate', {
|
||||
|
@ -313,7 +286,7 @@ class DownloadJob:
|
|||
'data': {
|
||||
'id': track.id,
|
||||
'title': track.title,
|
||||
'artist': track.mainArtist['name']
|
||||
'artist': track.mainArtist.name
|
||||
},
|
||||
})
|
||||
return self.download(trackAPI_gw, track)
|
||||
|
@ -324,7 +297,7 @@ class DownloadJob:
|
|||
except TrackNot360:
|
||||
raise DownloadFailed("no360RA")
|
||||
track.selectedFormat = selectedFormat
|
||||
track.album['bitrate'] = selectedFormat
|
||||
track.album.bitrate = selectedFormat
|
||||
|
||||
# Generate covers URLs
|
||||
embeddedImageFormat = f'jpg-{self.settings["jpegImageQuality"]}'
|
||||
|
@ -333,37 +306,37 @@ class DownloadJob:
|
|||
if self.settings['tags']['savePlaylistAsCompilation'] and track.playlist:
|
||||
track.trackNumber = track.position
|
||||
track.discNumber = "1"
|
||||
track.album = {**track.album, **track.playlist}
|
||||
track.album['embeddedCoverURL'] = generatePictureURL(track.playlist['pic'], self.settings['embeddedArtworkSize'], embeddedImageFormat)
|
||||
track.album.makePlaylistCompilation(track.playlist)
|
||||
track.album.embeddedCoverURL = track.playlist.pic.generatePictureURL(self.settings['embeddedArtworkSize'], embeddedImageFormat)
|
||||
|
||||
ext = track.album['embeddedCoverURL'][-4:]
|
||||
ext = track.album.embeddedCoverURL[-4:]
|
||||
if ext[0] != ".": ext = ".jpg" # Check for Spotify images
|
||||
|
||||
track.album['embeddedCoverPath'] = TEMPDIR / f"pl{trackAPI_gw['_EXTRA_PLAYLIST']['id']}_{self.settings['embeddedArtworkSize']}{ext}"
|
||||
track.album.embeddedCoverPath = TEMPDIR / f"pl{trackAPI_gw['_EXTRA_PLAYLIST']['id']}_{self.settings['embeddedArtworkSize']}{ext}"
|
||||
else:
|
||||
if track.album['date']: track.date = track.album['date']
|
||||
track.album['embeddedCoverURL'] = generatePictureURL(track.album['pic'], self.settings['embeddedArtworkSize'], embeddedImageFormat)
|
||||
if track.album.date: track.date = track.album.date
|
||||
track.album.embeddedCoverURL = track.album.pic.generatePictureURL(self.settings['embeddedArtworkSize'], embeddedImageFormat)
|
||||
|
||||
ext = track.album['embeddedCoverURL'][-4:]
|
||||
track.album['embeddedCoverPath'] = TEMPDIR / f"alb{track.album['id']}_{self.settings['embeddedArtworkSize']}{ext}"
|
||||
ext = track.album.embeddedCoverURL[-4:]
|
||||
track.album.embeddedCoverPath = TEMPDIR / f"alb{track.album.id}_{self.settings['embeddedArtworkSize']}{ext}"
|
||||
|
||||
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'])
|
||||
track.dateString = track.date.format(self.settings['dateFormat'])
|
||||
track.album.dateString = track.album.date.format(self.settings['dateFormat'])
|
||||
if track.playlist: track.playlist.dateString = track.playlist.date.format(self.settings['dateFormat'])
|
||||
|
||||
# Check various artist option
|
||||
if self.settings['albumVariousArtists'] and track.album['variousArtists']:
|
||||
artist = track.album['variousArtists']
|
||||
isMainArtist = artist['role'] == "Main"
|
||||
if self.settings['albumVariousArtists'] and track.album.variousArtists:
|
||||
artist = track.album.variousArtists
|
||||
isMainArtist = artist.role == "Main"
|
||||
|
||||
if artist['name'] not in track.album['artists']:
|
||||
track.album['artists'].insert(0, artist['name'])
|
||||
if artist.name not in track.album.artists:
|
||||
track.album.artists.insert(0, artist.name)
|
||||
|
||||
if isMainArtist or artist['name'] not in track.album['artist']['Main'] and not isMainArtist:
|
||||
if not artist['role'] in track.album['artist']:
|
||||
track.album['artist'][artist['role']] = []
|
||||
track.album['artist'][artist['role']].insert(0, artist['name'])
|
||||
track.album['mainArtist']['save'] = not track.album['mainArtist']['isVariousArtists'] or self.settings['albumVariousArtists'] and track.album['mainArtist']['isVariousArtists']
|
||||
if isMainArtist or artist.name not in track.album.artist['Main'] and not isMainArtist:
|
||||
if not artist.role in track.album.artist:
|
||||
track.album.artist[artist.role] = []
|
||||
track.album.artist[artist.role].insert(0, artist.name)
|
||||
track.album.mainArtist.save = not track.album.mainArtist.isVariousArtists() or self.settings['albumVariousArtists'] and track.album.mainArtist.isVariousArtists()
|
||||
|
||||
# Check removeDuplicateArtists
|
||||
if self.settings['removeDuplicateArtists']: track.removeDuplicateArtists()
|
||||
|
@ -375,7 +348,7 @@ class DownloadJob:
|
|||
track.title = track.getFeatTitle()
|
||||
elif str(self.settings['featuredToTitle']) == FeaturesOption.REMOVE_TITLE_ALBUM:
|
||||
track.title = track.getCleanTitle()
|
||||
track.album['title'] = track.getCleanAlbumTitle()
|
||||
track.album.title = track.album.getCleanTitle()
|
||||
|
||||
# Remove (Album Version) from tracks that have that
|
||||
if self.settings['removeAlbumVersion']:
|
||||
|
@ -386,7 +359,7 @@ class DownloadJob:
|
|||
if self.settings['titleCasing'] != "nothing":
|
||||
track.title = changeCase(track.title, self.settings['titleCasing'])
|
||||
if self.settings['artistCasing'] != "nothing":
|
||||
track.mainArtist['name'] = changeCase(track.mainArtist['name'], self.settings['artistCasing'])
|
||||
track.mainArtist.name = changeCase(track.mainArtist.name, self.settings['artistCasing'])
|
||||
for i, artist in enumerate(track.artists):
|
||||
track.artists[i] = changeCase(artist, self.settings['artistCasing'])
|
||||
for type in track.artist:
|
||||
|
@ -418,8 +391,8 @@ class DownloadJob:
|
|||
if self.queueItem.cancel: raise DownloadCancelled
|
||||
|
||||
# Download and cache coverart
|
||||
logger.info(f"[{track.mainArtist['name']} - {track.title}] Getting the album cover")
|
||||
track.album['embeddedCoverPath'] = downloadImage(track.album['embeddedCoverURL'], track.album['embeddedCoverPath'])
|
||||
logger.info(f"[{track.mainArtist.name} - {track.title}] Getting the album cover")
|
||||
track.album.embeddedCoverPath = downloadImage(track.album.embeddedCoverURL, track.album.embeddedCoverPath)
|
||||
|
||||
# Save local album art
|
||||
if coverPath:
|
||||
|
@ -428,10 +401,10 @@ class DownloadJob:
|
|||
if format in ["png","jpg"]:
|
||||
extendedFormat = format
|
||||
if extendedFormat == "jpg": extendedFormat += f"-{self.settings['jpegImageQuality']}"
|
||||
url = generatePictureURL(track.album['pic'], self.settings['localArtworkSize'], extendedFormat)
|
||||
url = track.album.pic.generatePictureURL(self.settings['localArtworkSize'], extendedFormat)
|
||||
if self.settings['tags']['savePlaylistAsCompilation'] \
|
||||
and track.playlist \
|
||||
and track.playlist['pic']['url'] \
|
||||
and track.playlist.pic.url \
|
||||
and not format.startswith("jpg"):
|
||||
continue
|
||||
result['albumURLs'].append({'url': url, 'ext': format})
|
||||
|
@ -445,11 +418,11 @@ class DownloadJob:
|
|||
if format in ["png","jpg"]:
|
||||
extendedFormat = format
|
||||
if extendedFormat == "jpg": extendedFormat += f"-{self.settings['jpegImageQuality']}"
|
||||
url = generatePictureURL(track.album['mainArtist']['pic'], self.settings['localArtworkSize'], extendedFormat)
|
||||
if track.album['mainArtist']['pic']['md5'] == "" and not format.startswith("jpg"): continue
|
||||
url = track.album.mainArtist.pic.generatePictureURL(self.settings['localArtworkSize'], extendedFormat)
|
||||
if track.album.mainArtist.pic.md5 == "" and not format.startswith("jpg"): continue
|
||||
result['artistURLs'].append({'url': url, 'ext': format})
|
||||
result['artistPath'] = artistPath
|
||||
result['artistFilename'] = f"{settingsRegexArtist(self.settings['artistImageTemplate'], track.album['mainArtist'], self.settings, rootArtist=track.album['rootArtist'])}"
|
||||
result['artistFilename'] = f"{settingsRegexArtist(self.settings['artistImageTemplate'], track.album.mainArtist, self.settings, rootArtist=track.album.rootArtist)}"
|
||||
|
||||
# Save playlist cover
|
||||
if track.playlist:
|
||||
|
@ -458,14 +431,12 @@ class DownloadJob:
|
|||
if format in ["png","jpg"]:
|
||||
extendedFormat = format
|
||||
if extendedFormat == "jpg": extendedFormat += f"-{self.settings['jpegImageQuality']}"
|
||||
url = generatePictureURL(track.playlist['pic'], self.settings['localArtworkSize'], extendedFormat)
|
||||
if track.playlist['pic']['url'] and not format.startswith("jpg"): continue
|
||||
url = track.playlist.pic.generatePictureURL(self.settings['localArtworkSize'], extendedFormat)
|
||||
if track.playlist.pic.url and not format.startswith("jpg"): continue
|
||||
self.playlistURLs.append({'url': url, 'ext': format})
|
||||
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'])
|
||||
track.playlist.bitrate = selectedFormat
|
||||
track.playlist.dateString = track.playlist.date.format(self.settings['dateFormat'])
|
||||
self.playlistCoverName = f"{settingsRegexAlbum(self.settings['coverImageTemplate'], track.playlist, self.settings, track.playlist)}"
|
||||
|
||||
# Remove subfolders from filename and add it to filepath
|
||||
|
@ -510,7 +481,7 @@ class DownloadJob:
|
|||
result['filename'] = str(writepath)[len(str(extrasPath))+ len(pathSep):]
|
||||
|
||||
if not trackAlreadyDownloaded or self.settings['overwriteFile'] == OverwriteOption.OVERWRITE:
|
||||
logger.info(f"[{track.mainArtist['name']} - {track.title}] Downloading the track")
|
||||
logger.info(f"[{track.mainArtist.name} - {track.title}] Downloading the track")
|
||||
track.downloadUrl = generateStreamURL(track.id, track.MD5, track.mediaVersion, track.selectedFormat)
|
||||
|
||||
def downloadMusic(track, trackAPI_gw):
|
||||
|
@ -523,16 +494,18 @@ class DownloadJob:
|
|||
except (request_exception.HTTPError, DownloadEmpty):
|
||||
if writepath.is_file(): writepath.unlink()
|
||||
if track.fallbackId != "0":
|
||||
logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not available, using fallback id")
|
||||
logger.warn(f"[{track.mainArtist.name} - {track.title}] Track not available, using fallback id")
|
||||
newTrack = self.dz.gw.get_track_with_fallback(track.fallbackId)
|
||||
track.parseEssentialData(self.dz, newTrack)
|
||||
track.parseEssentialData(newTrack)
|
||||
track.retriveFilesizes(self.dz)
|
||||
return False
|
||||
elif not track.searched and self.settings['fallbackSearch']:
|
||||
logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not available, searching for alternative")
|
||||
searchedId = self.dz.api.get_track_id_from_metadata(track.mainArtist['name'], track.title, track.album['title'])
|
||||
logger.warn(f"[{track.mainArtist.name} - {track.title}] Track not available, searching for alternative")
|
||||
searchedId = self.dz.api.get_track_id_from_metadata(track.mainArtist.name, track.title, track.album.title)
|
||||
if searchedId != "0":
|
||||
newTrack = self.dz.gw.get_track_with_fallback(searchedId)
|
||||
track.parseEssentialData(self.dz, newTrack)
|
||||
track.parseEssentialData(newTrack)
|
||||
track.retriveFilesizes(self.dz)
|
||||
track.searched = True
|
||||
if self.interface:
|
||||
self.interface.send('queueUpdate', {
|
||||
|
@ -541,7 +514,7 @@ class DownloadJob:
|
|||
'data': {
|
||||
'id': track.id,
|
||||
'title': track.title,
|
||||
'artist': track.mainArtist['name']
|
||||
'artist': track.mainArtist.name
|
||||
},
|
||||
})
|
||||
return False
|
||||
|
@ -551,7 +524,7 @@ class DownloadJob:
|
|||
raise DownloadFailed("notAvailable")
|
||||
except (request_exception.ConnectionError, request_exception.ChunkedEncodingError) as e:
|
||||
if writepath.is_file(): writepath.unlink()
|
||||
logger.warn(f"[{track.mainArtist['name']} - {track.title}] Error while downloading the track, trying again in 5s...")
|
||||
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)
|
||||
except OSError as e:
|
||||
|
@ -559,11 +532,11 @@ class DownloadJob:
|
|||
raise DownloadFailed("noSpaceLeft")
|
||||
else:
|
||||
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)}")
|
||||
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 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)}")
|
||||
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
|
||||
|
||||
|
@ -574,12 +547,12 @@ class DownloadJob:
|
|||
|
||||
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")
|
||||
logger.info(f"[{track.mainArtist.name} - {track.title}] Skipping track as it's already downloaded")
|
||||
self.completeTrackPercentage()
|
||||
|
||||
# Adding tags
|
||||
if (not trackAlreadyDownloaded or self.settings['overwriteFile'] in [OverwriteOption.ONLY_TAGS, OverwriteOption.OVERWRITE]) and not track.localTrack:
|
||||
logger.info(f"[{track.mainArtist['name']} - {track.title}] Applying tags to the track")
|
||||
logger.info(f"[{track.mainArtist.name} - {track.title}] Applying tags to the track")
|
||||
if track.selectedFormat in [TrackFormats.MP3_320, TrackFormats.MP3_128, TrackFormats.DEFAULT]:
|
||||
tagID3(writepath, track, self.settings['tags'])
|
||||
elif track.selectedFormat == TrackFormats.FLAC:
|
||||
|
@ -587,14 +560,14 @@ class DownloadJob:
|
|||
tagFLAC(writepath, track, self.settings['tags'])
|
||||
except (FLACNoHeaderError, FLACError):
|
||||
if writepath.is_file(): writepath.unlink()
|
||||
logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not available in FLAC, falling back if necessary")
|
||||
logger.warn(f"[{track.mainArtist.name} - {track.title}] Track not available in FLAC, falling back if necessary")
|
||||
self.removeTrackPercentage()
|
||||
track.filesizes['FILESIZE_FLAC'] = "0"
|
||||
track.filesizes['FILESIZE_FLAC_TESTED'] = True
|
||||
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\n{str(writepath)}")
|
||||
if track.searched: result['searched'] = f"{track.mainArtist.name} - {track.title}"
|
||||
logger.info(f"[{track.mainArtist.name} - {track.title}] Track download completed\n{str(writepath)}")
|
||||
self.queueItem.downloaded += 1
|
||||
self.queueItem.files.append(str(writepath))
|
||||
self.queueItem.extrasPath = str(self.extrasPath)
|
||||
|
@ -649,7 +622,7 @@ class DownloadJob:
|
|||
else:
|
||||
if not falledBack:
|
||||
falledBack = True
|
||||
logger.info(f"[{track.mainArtist['name']} - {track.title}] Fallback to lower bitrate")
|
||||
logger.info(f"[{track.mainArtist.name} - {track.title}] Fallback to lower bitrate")
|
||||
if self.interface:
|
||||
self.interface.send('queueUpdate', {
|
||||
'uuid': self.queueItem.uuid,
|
||||
|
@ -657,7 +630,7 @@ class DownloadJob:
|
|||
'data': {
|
||||
'id': track.id,
|
||||
'title': track.title,
|
||||
'artist': track.mainArtist['name']
|
||||
'artist': track.mainArtist.name
|
||||
},
|
||||
})
|
||||
if is360format: raise TrackNot360
|
||||
|
@ -671,7 +644,7 @@ class DownloadJob:
|
|||
chunkLength = start
|
||||
percentage = 0
|
||||
|
||||
itemName = f"[{track.mainArtist['name']} - {track.title}]"
|
||||
itemName = f"[{track.mainArtist.name} - {track.title}]"
|
||||
|
||||
try:
|
||||
with self.dz.session.get(track.downloadUrl, headers=headers, stream=True, timeout=10) as request:
|
||||
|
|
|
@ -1,476 +0,0 @@
|
|||
import eventlet
|
||||
requests = eventlet.import_patched('requests')
|
||||
|
||||
import logging
|
||||
|
||||
from deezer.gw import APIError as gwAPIError, LyricsStatus
|
||||
from deezer.api import APIError
|
||||
from deemix.utils import removeFeatures, andCommaConcat, uniqueArray, generateReplayGainString
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger('deemix')
|
||||
|
||||
VARIOUS_ARTISTS = 5080
|
||||
|
||||
class Track:
|
||||
def __init__(self, dz, trackAPI_gw, trackAPI=None, albumAPI_gw=None, albumAPI=None):
|
||||
self.parseEssentialData(dz, trackAPI_gw)
|
||||
|
||||
self.title = trackAPI_gw['SNG_TITLE'].strip()
|
||||
if trackAPI_gw.get('VERSION') and not trackAPI_gw['VERSION'] in trackAPI_gw['SNG_TITLE']:
|
||||
self.title += " " + trackAPI_gw['VERSION'].strip()
|
||||
|
||||
self.position = trackAPI_gw.get('POSITION')
|
||||
|
||||
self.localTrack = int(self.id) < 0
|
||||
if self.localTrack:
|
||||
self.parseLocalTrackData(trackAPI_gw)
|
||||
else:
|
||||
self.parseData(dz, trackAPI_gw, trackAPI, albumAPI_gw, albumAPI)
|
||||
|
||||
# Make sure there is at least one artist
|
||||
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.parsePlaylistData(trackAPI_gw["_EXTRA_PLAYLIST"])
|
||||
|
||||
self.singleDownload = trackAPI_gw.get('SINGLE_TRACK', False)
|
||||
|
||||
self.generateMainFeatStrings()
|
||||
|
||||
# Bits useful for later
|
||||
self.searched = False
|
||||
self.selectedFormat = 0
|
||||
self.dateString = None
|
||||
self.album['embeddedCoverURL'] = None
|
||||
self.album['embeddedCoverPath'] = 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']
|
||||
if int(self.id) > 0:
|
||||
self.filesizes = self.getFilesizes(dz)
|
||||
|
||||
def parseLocalTrackData(self, trackAPI_gw):
|
||||
# Local tracks has only the trackAPI_gw page and
|
||||
# contains only the tags provided by the file
|
||||
self.album = {
|
||||
'id': "0",
|
||||
'title': trackAPI_gw['ALB_TITLE'],
|
||||
'pic': {
|
||||
'md5': trackAPI_gw.get('ALB_PICTURE', ""),
|
||||
'type': "cover",
|
||||
'url': None
|
||||
}
|
||||
}
|
||||
self.mainArtist = {
|
||||
'id': "0",
|
||||
'name': trackAPI_gw['ART_NAME'],
|
||||
'pic': {
|
||||
'md5': "",
|
||||
'type': "artist",
|
||||
'url': None
|
||||
}
|
||||
}
|
||||
self.artists = [trackAPI_gw['ART_NAME']]
|
||||
self.artist = {
|
||||
'Main': [trackAPI_gw['ART_NAME']]
|
||||
}
|
||||
self.date = {
|
||||
'day': "00",
|
||||
'month': "00",
|
||||
'year': "XXXX"
|
||||
}
|
||||
# Defaulting 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['mainArtist']['isVariousArtists'] = False
|
||||
self.album['variousArtists'] = None
|
||||
self.album['rootArtist'] = None
|
||||
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, trackAPI_gw, trackAPI, albumAPI_gw, albumAPI):
|
||||
self.discNumber = trackAPI_gw.get('DISK_NUMBER')
|
||||
self.explicit = bool(int(trackAPI_gw.get('EXPLICIT_LYRICS', "0")))
|
||||
self.copyright = trackAPI_gw.get('COPYRIGHT')
|
||||
self.replayGain = ""
|
||||
if 'GAIN' in trackAPI_gw: self.replayGain = generateReplayGainString(trackAPI_gw['GAIN'])
|
||||
self.ISRC = trackAPI_gw.get('ISRC')
|
||||
self.trackNumber = trackAPI_gw['TRACK_NUMBER']
|
||||
self.contributors = trackAPI_gw['SNG_CONTRIBUTORS']
|
||||
|
||||
self.lyrics = {
|
||||
'id': int(trackAPI_gw.get('LYRICS_ID', "0")),
|
||||
'unsync': None,
|
||||
'sync': None,
|
||||
'syncID3': None
|
||||
}
|
||||
if not "LYRICS" in trackAPI_gw and self.lyrics['id'] != 0:
|
||||
logger.info(f"[{trackAPI_gw['ART_NAME']} - {self.title}] Getting lyrics")
|
||||
try:
|
||||
trackAPI_gw["LYRICS"] = dz.gw.get_track_lyrics(self.id)
|
||||
except gwAPIError:
|
||||
self.lyrics['id'] = 0
|
||||
if self.lyrics['id'] != 0:
|
||||
self.lyrics['unsync'] = trackAPI_gw["LYRICS"].get("LYRICS_TEXT")
|
||||
if "LYRICS_SYNC_JSON" in trackAPI_gw["LYRICS"]:
|
||||
syncLyricsJson = trackAPI_gw["LYRICS"]["LYRICS_SYNC_JSON"]
|
||||
self.lyrics['sync'] = ""
|
||||
self.lyrics['syncID3'] = []
|
||||
timestamp = ""
|
||||
milliseconds = 0
|
||||
for line in range(len(syncLyricsJson)):
|
||||
if syncLyricsJson[line]["line"] != "":
|
||||
timestamp = syncLyricsJson[line]["lrc_timestamp"]
|
||||
milliseconds = int(syncLyricsJson[line]["milliseconds"])
|
||||
self.lyrics['syncID3'].append((syncLyricsJson[line]["line"], milliseconds))
|
||||
else:
|
||||
notEmptyLine = line + 1
|
||||
while syncLyricsJson[notEmptyLine]["line"] == "":
|
||||
notEmptyLine = notEmptyLine + 1
|
||||
timestamp = syncLyricsJson[notEmptyLine]["lrc_timestamp"]
|
||||
self.lyrics['sync'] += timestamp + syncLyricsJson[line]["line"] + "\r\n"
|
||||
|
||||
self.mainArtist = {
|
||||
'id': trackAPI_gw['ART_ID'],
|
||||
'name': trackAPI_gw['ART_NAME'],
|
||||
'pic': {
|
||||
'md5': trackAPI_gw.get('ART_PICTURE'),
|
||||
'type': "artist",
|
||||
'url': None
|
||||
}
|
||||
}
|
||||
|
||||
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': {
|
||||
'md5': trackAPI_gw.get('ALB_PICTURE'),
|
||||
'type': "cover",
|
||||
'url': None
|
||||
},
|
||||
'barcode': "Unknown",
|
||||
'label': "Unknown",
|
||||
'explicit': False,
|
||||
'date': None,
|
||||
'genre': []
|
||||
}
|
||||
|
||||
# Try the public API first (as it has more data)
|
||||
if not albumAPI:
|
||||
logger.info(f"[{self.mainArtist['name']} - {self.title}] Getting album infos")
|
||||
try:
|
||||
albumAPI = dz.api.get_album(self.album['id'])
|
||||
except APIError:
|
||||
albumAPI = None
|
||||
|
||||
self.album['variousArtists'] = None
|
||||
if albumAPI:
|
||||
self.album['title'] = albumAPI['title']
|
||||
|
||||
# Getting artist image ID
|
||||
# ex: https://e-cdns-images.dzcdn.net/images/artist/f2bc007e9133c946ac3c3907ddc5d2ea/56x56-000000-80-0-0.jpg
|
||||
artistPicture = albumAPI['artist']['picture_small']
|
||||
artistPicture = artistPicture[artistPicture.find('artist/') + 7:-24]
|
||||
self.album['mainArtist'] = {
|
||||
'id': albumAPI['artist']['id'],
|
||||
'name': albumAPI['artist']['name'],
|
||||
'pic': {
|
||||
'md5': artistPicture,
|
||||
'type': "artist",
|
||||
'url': None
|
||||
}
|
||||
}
|
||||
self.album['rootArtist'] = albumAPI.get('root_artist', None)
|
||||
|
||||
self.album['artist'] = {'Main': []}
|
||||
self.album['artists'] = []
|
||||
for artist in albumAPI['contributors']:
|
||||
isVariousArtists = artist['id'] == VARIOUS_ARTISTS
|
||||
isMainArtist = artist['role'] == "Main"
|
||||
|
||||
if isVariousArtists:
|
||||
self.album['variousArtists'] = artist
|
||||
continue
|
||||
|
||||
if artist['name'] not in self.album['artists']:
|
||||
self.album['artists'].append(artist['name'])
|
||||
|
||||
if isMainArtist or artist['name'] not in self.album['artist']['Main'] and not isMainArtist:
|
||||
if not artist['role'] in self.album['artist']:
|
||||
self.album['artist'][artist['role']] = []
|
||||
self.album['artist'][artist['role']].append(artist['name'])
|
||||
|
||||
self.album['trackTotal'] = albumAPI['nb_tracks']
|
||||
self.album['recordType'] = albumAPI['record_type']
|
||||
|
||||
self.album['barcode'] = albumAPI.get('upc', self.album['barcode'])
|
||||
self.album['label'] = albumAPI.get('label', self.album['label'])
|
||||
self.album['explicit'] = bool(albumAPI.get('explicit_lyrics', False))
|
||||
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'] = albumAPI.get('nb_disk', "1")
|
||||
self.copyright = albumAPI.get('copyright')
|
||||
|
||||
if not self.album['pic']['md5']:
|
||||
# Getting album cover MD5
|
||||
# ex: https://e-cdns-images.dzcdn.net/images/cover/2e018122cb56986277102d2041a592c8/56x56-000000-80-0-0.jpg
|
||||
self.album['pic']['md5'] = albumAPI['cover_small'][albumAPI['cover_small'].find('cover/') + 6:-24]
|
||||
|
||||
if albumAPI.get('genres') and len(albumAPI['genres'].get('data', [])) > 0:
|
||||
for genre in albumAPI['genres']['data']:
|
||||
self.album['genre'].append(genre['name'])
|
||||
else:
|
||||
if not albumAPI_gw:
|
||||
logger.info(f"[{self.mainArtist['name']} - {self.title}] Getting more album infos")
|
||||
try:
|
||||
albumAPI_gw = dz.gw.get_album(self.album['id'])
|
||||
except gwAPIError:
|
||||
albumAPI_gw = None
|
||||
raise AlbumDoesntExists
|
||||
|
||||
self.album['title'] = albumAPI_gw['ALB_TITLE']
|
||||
self.album['mainArtist'] = {
|
||||
'id': albumAPI_gw['ART_ID'],
|
||||
'name': albumAPI_gw['ART_NAME'],
|
||||
'pic': {
|
||||
'md5': "",
|
||||
'type': "artist",
|
||||
'url': None
|
||||
}
|
||||
}
|
||||
self.album['rootArtist'] = None
|
||||
|
||||
# albumAPI_gw doesn't contain the artist cover
|
||||
# Getting artist image ID
|
||||
# ex: https://e-cdns-images.dzcdn.net/images/artist/f2bc007e9133c946ac3c3907ddc5d2ea/56x56-000000-80-0-0.jpg
|
||||
logger.info(f"[{self.mainArtist['name']} - {self.title}] Getting artist picture fallback")
|
||||
artistAPI = dz.api.get_artist(self.album['mainArtist']['id'])
|
||||
self.album['mainArtist']['pic']['md5'] = artistAPI['picture_small'][artistAPI['picture_small'].find('artist/') + 7:-24]
|
||||
|
||||
self.album['artists'] = [albumAPI_gw['ART_NAME']]
|
||||
self.album['trackTotal'] = albumAPI_gw['NUMBER_TRACK']
|
||||
self.album['discTotal'] = albumAPI_gw['NUMBER_DISK']
|
||||
self.album['recordType'] = "album"
|
||||
self.album['label'] = albumAPI_gw.get('LABEL_NAME', self.album['label'])
|
||||
|
||||
explicitLyricsStatus = albumAPI_gw.get('EXPLICIT_ALBUM_CONTENT', {}).get('EXPLICIT_LYRICS_STATUS', LyricsStatus.UNKNOWN)
|
||||
self.album['explicit'] = explicitLyricsStatus in [LyricsStatus.EXPLICIT, LyricsStatus.PARTIALLY_EXPLICIT]
|
||||
|
||||
if not self.album['pic']['md5']:
|
||||
self.album['pic']['md5'] = 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']['isVariousArtists'] = self.album['mainArtist']['id'] == VARIOUS_ARTISTS
|
||||
|
||||
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.api.get_track(self.id)
|
||||
self.bpm = trackAPI['bpm']
|
||||
|
||||
if not self.replayGain and 'gain' in trackAPI:
|
||||
self.replayGain = generateReplayGainString(trackAPI['gain'])
|
||||
if not self.explicit:
|
||||
self.explicit = trackAPI['explicit_lyrics']
|
||||
if not self.discNumber:
|
||||
self.discNumber = trackAPI['disk_number']
|
||||
|
||||
self.artist = {'Main': []}
|
||||
self.artists = []
|
||||
for artist in trackAPI['contributors']:
|
||||
isVariousArtists = artist['id'] == VARIOUS_ARTISTS
|
||||
isMainArtist = artist['role'] == "Main"
|
||||
|
||||
if len(trackAPI['contributors']) > 1 and isVariousArtists:
|
||||
continue
|
||||
|
||||
if artist['name'] not in self.artists:
|
||||
self.artists.append(artist['name'])
|
||||
|
||||
if isMainArtist or artist['name'] not in self.artist['Main'] and not isMainArtist:
|
||||
if not artist['role'] in self.artist:
|
||||
self.artist[artist['role']] = []
|
||||
self.artist[artist['role']].append(artist['name'])
|
||||
|
||||
if not self.album['discTotal']:
|
||||
if not albumAPI_gw:
|
||||
logger.info(f"[{self.mainArtist['name']} - {self.title}] Getting more album infos")
|
||||
albumAPI_gw = dz.gw.get_album(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.gw.get_album(self.album['id'])
|
||||
self.copyright = albumAPI_gw['COPYRIGHT']
|
||||
|
||||
def parsePlaylistData(self, playlist):
|
||||
self.playlist = {}
|
||||
playlist['various_artist']['role'] = "Main"
|
||||
if 'dzcdn.net' in playlist['picture_small']:
|
||||
url = playlist['picture_small']
|
||||
picType = url[url.find('images/')+7:]
|
||||
picType = picType[:picType.find('/')]
|
||||
self.playlist['pic'] = {
|
||||
'md5': url[url.find(picType+'/') + len(picType)+1:-24],
|
||||
'type': picType,
|
||||
'url': None
|
||||
}
|
||||
else:
|
||||
self.playlist['pic'] = {
|
||||
'md5': None,
|
||||
'type': None,
|
||||
'url': playlist['picture_xl']
|
||||
}
|
||||
self.playlist['title'] = playlist['title']
|
||||
self.playlist['mainArtist'] = {
|
||||
'id': playlist['various_artist']['id'],
|
||||
'name': playlist['various_artist']['name'],
|
||||
'isVariousArtists': True,
|
||||
'pic': {
|
||||
'md5': playlist['various_artist']['picture_small'][
|
||||
playlist['various_artist']['picture_small'].find('artist/') + 7:-24],
|
||||
'type': "artist",
|
||||
'url': None
|
||||
}
|
||||
}
|
||||
self.playlist['rootArtist'] = None
|
||||
self.playlist['artist'] = {"Main": []}
|
||||
self.playlist['artists'] = []
|
||||
self.playlist['variousArtists'] = playlist['various_artist']
|
||||
self.playlist['trackTotal'] = playlist['nb_tracks']
|
||||
self.playlist['recordType'] = "compile"
|
||||
self.playlist['barcode'] = ""
|
||||
self.playlist['label'] = ""
|
||||
self.playlist['explicit'] = playlist['explicit']
|
||||
self.playlist['date'] = {
|
||||
'day': playlist["creation_date"][8:10],
|
||||
'month': playlist["creation_date"][5:7],
|
||||
'year': playlist["creation_date"][0:4]
|
||||
}
|
||||
self.playlist['discTotal'] = "1"
|
||||
self.playlist['playlistId'] = playlist['id']
|
||||
self.playlist['owner'] = playlist['creator']
|
||||
|
||||
def removeDuplicateArtists(self):
|
||||
self.artists = uniqueArray(self.artists)
|
||||
for role in self.artist.keys():
|
||||
self.artist[role] = uniqueArray(self.artist[role])
|
||||
|
||||
self.album['artists'] = uniqueArray(self.album['artists'])
|
||||
for role in self.album['artist'].keys():
|
||||
self.album['artist'][role] = uniqueArray(self.album['artist'][role])
|
||||
|
||||
# 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
|
||||
|
||||
def generateMainFeatStrings(self):
|
||||
self.mainArtistsString = andCommaConcat(self.artist['Main'])
|
||||
self.featArtistsString = ""
|
||||
if 'Featured' in self.artist:
|
||||
self.featArtistsString = "feat. "+andCommaConcat(self.artist['Featured'])
|
||||
|
||||
def getFilesizes(self, dz):
|
||||
try:
|
||||
guest_sid = dz.session.cookies.get('sid')
|
||||
site = requests.post(
|
||||
"https://api.deezer.com/1.0/gateway.php",
|
||||
params={
|
||||
'api_key': "4VCYIJUCDLOUELGD1V8WBVYBNVDYOXEWSLLZDONGBBDFVXTZJRXPR29JRLQFO6ZE",
|
||||
'sid': guest_sid,
|
||||
'input': '3',
|
||||
'output': '3',
|
||||
'method': 'song_getData'
|
||||
},
|
||||
timeout=30,
|
||||
json={'sng_id': self.id},
|
||||
headers=dz.http_headers
|
||||
)
|
||||
result_json = site.json()
|
||||
except:
|
||||
eventlet.sleep(2)
|
||||
return self.getFilesizes(dz)
|
||||
if len(result_json['error']):
|
||||
raise APIError(json.dumps(result_json['error']))
|
||||
response = result_json.get("results")
|
||||
filesizes = {}
|
||||
for key, value in response.items():
|
||||
if key.startswith("FILESIZE_"):
|
||||
filesizes[key] = value
|
||||
filesizes[key+"_TESTED"] = False
|
||||
return filesizes
|
||||
|
||||
class TrackError(Exception):
|
||||
"""Base class for exceptions in this module."""
|
||||
pass
|
||||
|
||||
class AlbumDoesntExists(TrackError):
|
||||
pass
|
|
@ -0,0 +1,140 @@
|
|||
from deezer.gw import LyricsStatus
|
||||
|
||||
from deemix.utils import removeDuplicateArtists
|
||||
from deemix.types.Artist import Artist
|
||||
from deemix.types.Date import Date
|
||||
from deemix.types.Picture import Picture
|
||||
from deemix import VARIOUS_ARTISTS
|
||||
|
||||
class Album:
|
||||
def __init__(self, id="0", title="", pic_md5=""):
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.pic = Picture(md5=pic_md5, type="cover")
|
||||
self.artist = {"Main": []}
|
||||
self.artists = []
|
||||
self.mainArtist = None
|
||||
self.dateString = None
|
||||
self.barcode = "Unknown"
|
||||
self.date = None
|
||||
self.discTotal = "0"
|
||||
self.embeddedCoverPath = None
|
||||
self.embeddedCoverURL = None
|
||||
self.explicit = False
|
||||
self.genre = []
|
||||
self.label = "Unknown"
|
||||
self.recordType = "album"
|
||||
self.rootArtist = None
|
||||
self.trackTotal = "0"
|
||||
self.bitrate = 0
|
||||
self.variousArtists = None
|
||||
|
||||
def parseAlbum(self, albumAPI):
|
||||
self.title = albumAPI['title']
|
||||
|
||||
# Getting artist image ID
|
||||
# ex: https://e-cdns-images.dzcdn.net/images/artist/f2bc007e9133c946ac3c3907ddc5d2ea/56x56-000000-80-0-0.jpg
|
||||
artistPicture = albumAPI['artist']['picture_small']
|
||||
artistPicture = artistPicture[artistPicture.find('artist/') + 7:-24]
|
||||
self.mainArtist = Artist(
|
||||
id = albumAPI['artist']['id'],
|
||||
name = albumAPI['artist']['name'],
|
||||
pic_md5 = artistPicture
|
||||
)
|
||||
if albumAPI.get('root_artist'):
|
||||
self.rootArtist = Artist(
|
||||
id = albumAPI['root_artist']['id'],
|
||||
name = albumAPI['root_artist']['name']
|
||||
)
|
||||
|
||||
for artist in albumAPI['contributors']:
|
||||
isVariousArtists = str(artist['id']) == VARIOUS_ARTISTS
|
||||
isMainArtist = artist['role'] == "Main"
|
||||
|
||||
if isVariousArtists:
|
||||
self.variousArtists = Artist(
|
||||
id = artist['id'],
|
||||
name = artist['name'],
|
||||
role = artist['role']
|
||||
)
|
||||
continue
|
||||
|
||||
if artist['name'] not in self.artists:
|
||||
self.artists.append(artist['name'])
|
||||
|
||||
if isMainArtist or artist['name'] not in self.artist['Main'] and not isMainArtist:
|
||||
if not artist['role'] in self.artist:
|
||||
self.artist[artist['role']] = []
|
||||
self.artist[artist['role']].append(artist['name'])
|
||||
|
||||
self.trackTotal = albumAPI['nb_tracks']
|
||||
self.recordType = albumAPI['record_type']
|
||||
|
||||
self.barcode = albumAPI.get('upc', self.barcode)
|
||||
self.label = albumAPI.get('label', self.label)
|
||||
self.explicit = bool(albumAPI.get('explicit_lyrics', False))
|
||||
if 'release_date' in albumAPI:
|
||||
day = albumAPI["release_date"][8:10]
|
||||
month = albumAPI["release_date"][5:7]
|
||||
year = albumAPI["release_date"][0:4]
|
||||
self.date = Date(year, month, day)
|
||||
|
||||
self.discTotal = albumAPI.get('nb_disk')
|
||||
self.copyright = albumAPI.get('copyright')
|
||||
|
||||
if not self.pic.md5:
|
||||
# Getting album cover MD5
|
||||
# ex: https://e-cdns-images.dzcdn.net/images/cover/2e018122cb56986277102d2041a592c8/56x56-000000-80-0-0.jpg
|
||||
self.pic.md5 = albumAPI['cover_small'][albumAPI['cover_small'].find('cover/') + 6:-24]
|
||||
|
||||
if albumAPI.get('genres') and len(albumAPI['genres'].get('data', [])) > 0:
|
||||
for genre in albumAPI['genres']['data']:
|
||||
self.genre.append(genre['name'])
|
||||
|
||||
def parseAlbumGW(self, albumAPI_gw):
|
||||
self.title = albumAPI_gw['ALB_TITLE']
|
||||
self.mainArtist = Artist(
|
||||
id = albumAPI_gw['ART_ID'],
|
||||
name = albumAPI_gw['ART_NAME']
|
||||
)
|
||||
|
||||
self.artists = [albumAPI_gw['ART_NAME']]
|
||||
self.trackTotal = albumAPI_gw['NUMBER_TRACK']
|
||||
self.discTotal = albumAPI_gw['NUMBER_DISK']
|
||||
self.label = albumAPI_gw.get('LABEL_NAME', self.label)
|
||||
|
||||
explicitLyricsStatus = albumAPI_gw.get('EXPLICIT_ALBUM_CONTENT', {}).get('EXPLICIT_LYRICS_STATUS', LyricsStatus.UNKNOWN)
|
||||
self.explicit = explicitLyricsStatus in [LyricsStatus.EXPLICIT, LyricsStatus.PARTIALLY_EXPLICIT]
|
||||
|
||||
if not self.pic.md5:
|
||||
self.pic.md5 = albumAPI_gw['ALB_PICTURE']
|
||||
if 'PHYSICAL_RELEASE_DATE' in albumAPI_gw:
|
||||
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.date = Date(year, month, day)
|
||||
|
||||
def makePlaylistCompilation(self, playlist):
|
||||
self.variousArtists = playlist.variousArtists
|
||||
self.mainArtist = playlist.mainArtist
|
||||
self.title = playlist.title
|
||||
self.rootArtist = playlist.rootArtist
|
||||
self.artist = playlist.artist
|
||||
self.artists = playlist.artists
|
||||
self.trackTotal = playlist.trackTotal
|
||||
self.recordType = playlist.recordType
|
||||
self.barcode = playlist.barcode
|
||||
self.label = playlist.label
|
||||
self.explicit = playlist.explicit
|
||||
self.date = playlist.date
|
||||
self.discTotal = playlist.discTotal
|
||||
self.playlistId = playlist.playlistId
|
||||
self.owner = playlist.owner
|
||||
self.pic = playlist.pic
|
||||
|
||||
def removeDuplicateArtists(self):
|
||||
(self.artist, self.artists) = removeDuplicateArtists(self.artist, self.artists)
|
||||
|
||||
# Removes featuring from the album name
|
||||
def getCleanTitle(self):
|
||||
return removeFeatures(self.title)
|
|
@ -0,0 +1,13 @@
|
|||
from deemix.types.Picture import Picture
|
||||
from deemix import VARIOUS_ARTISTS
|
||||
|
||||
class Artist:
|
||||
def __init__(self, id="0", name="", pic_md5="", role=""):
|
||||
self.id = str(id)
|
||||
self.name = name
|
||||
self.pic = Picture(md5=pic_md5, type="artist")
|
||||
self.role = ""
|
||||
self.save = True
|
||||
|
||||
def isVariousArtists(self):
|
||||
return self.id == VARIOUS_ARTISTS
|
|
@ -0,0 +1,25 @@
|
|||
class Date(object):
|
||||
def __init__(self, year="XXXX", month="00", day="00"):
|
||||
self.year = year
|
||||
self.month = month
|
||||
self.day = day
|
||||
self.fixDayMonth()
|
||||
|
||||
# Fix incorrect day month when detectable
|
||||
def fixDayMonth(self):
|
||||
if int(self.month) > 12:
|
||||
monthTemp = self.month
|
||||
self.month = self.day
|
||||
self.day = monthTemp
|
||||
|
||||
def format(self, 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(getattr(self, element)))
|
||||
return template
|
|
@ -0,0 +1,26 @@
|
|||
class Lyrics:
|
||||
def __init__(self, id="0"):
|
||||
self.id = id
|
||||
self.sync = None
|
||||
self.unsync = None
|
||||
self.syncID3 = None
|
||||
|
||||
def parseLyrics(self, lyricsAPI):
|
||||
self.unsync = lyricsAPI.get("LYRICS_TEXT")
|
||||
if "LYRICS_SYNC_JSON" in lyricsAPI:
|
||||
syncLyricsJson = lyricsAPI["LYRICS_SYNC_JSON"]
|
||||
self.sync = ""
|
||||
self.syncID3 = []
|
||||
timestamp = ""
|
||||
milliseconds = 0
|
||||
for line in range(len(syncLyricsJson)):
|
||||
if syncLyricsJson[line]["line"] != "":
|
||||
timestamp = syncLyricsJson[line]["lrc_timestamp"]
|
||||
milliseconds = int(syncLyricsJson[line]["milliseconds"])
|
||||
self.syncID3.append((syncLyricsJson[line]["line"], milliseconds))
|
||||
else:
|
||||
notEmptyLine = line + 1
|
||||
while syncLyricsJson[notEmptyLine]["line"] == "":
|
||||
notEmptyLine = notEmptyLine + 1
|
||||
timestamp = syncLyricsJson[notEmptyLine]["lrc_timestamp"]
|
||||
self.sync += timestamp + syncLyricsJson[line]["line"] + "\r\n"
|
|
@ -0,0 +1,27 @@
|
|||
class Picture:
|
||||
def __init__(self, md5="", type=None, url=None):
|
||||
self.md5 = md5
|
||||
self.type = type
|
||||
self.url = url
|
||||
|
||||
def generatePictureURL(self, size, format):
|
||||
if self.url: return self.url
|
||||
if format.startswith("jpg"):
|
||||
if '-' in format:
|
||||
quality = format[4:]
|
||||
else:
|
||||
quality = 80
|
||||
format = 'jpg'
|
||||
return "https://e-cdns-images.dzcdn.net/images/{}/{}/{}x{}-{}".format(
|
||||
self.type,
|
||||
self.md5,
|
||||
size, size,
|
||||
f'000000-{quality}-0-0.jpg'
|
||||
)
|
||||
if format == 'png':
|
||||
return "https://e-cdns-images.dzcdn.net/images/{}/{}/{}x{}-{}".format(
|
||||
self.type,
|
||||
self.md5,
|
||||
size, size,
|
||||
'none-100-0-0.png'
|
||||
)
|
|
@ -0,0 +1,48 @@
|
|||
from deemix.types.Artist import Artist
|
||||
from deemix.types.Date import Date
|
||||
from deemix.types.Picture import Picture
|
||||
|
||||
class Playlist:
|
||||
def __init__(self, playlistAPI):
|
||||
if 'various_artist' in playlistAPI:
|
||||
playlistAPI['various_artist']['role'] = "Main"
|
||||
self.variousArtists = Artist(
|
||||
id = playlistAPI['various_artist']['id'],
|
||||
name = playlistAPI['various_artist']['name'],
|
||||
pic_md5 = playlistAPI['various_artist']['picture_small'][
|
||||
playlistAPI['various_artist']['picture_small'].find('artist/') + 7:-24],
|
||||
role = playlistAPI['various_artist']['role']
|
||||
)
|
||||
self.mainArtist = self.variousArtists
|
||||
|
||||
self.id = "pl_" + str(playlistAPI['id'])
|
||||
self.title = playlistAPI['title']
|
||||
self.rootArtist = None
|
||||
self.artist = {"Main": []}
|
||||
self.artists = []
|
||||
self.trackTotal = playlistAPI['nb_tracks']
|
||||
self.recordType = "compile"
|
||||
self.barcode = ""
|
||||
self.label = ""
|
||||
self.explicit = playlistAPI['explicit']
|
||||
self.genre = ["Compilation", ]
|
||||
|
||||
year = playlistAPI["creation_date"][0:4]
|
||||
month = playlistAPI["creation_date"][5:7]
|
||||
day = playlistAPI["creation_date"][8:10]
|
||||
self.date = Date(year, month, day)
|
||||
|
||||
self.discTotal = "1"
|
||||
self.playlistId = playlistAPI['id']
|
||||
self.owner = playlistAPI['creator']
|
||||
if 'dzcdn.net' in playlistAPI['picture_small']:
|
||||
url = playlistAPI['picture_small']
|
||||
picType = url[url.find('images/')+7:]
|
||||
picType = picType[:picType.find('/')]
|
||||
md5 = url[url.find(picType+'/') + len(picType)+1:-24]
|
||||
self.pic = Picture(
|
||||
md5 = md5,
|
||||
type = picType
|
||||
)
|
||||
else:
|
||||
self.pic = Picture(url = playlistAPI['picture_xl'])
|
|
@ -0,0 +1,269 @@
|
|||
import eventlet
|
||||
requests = eventlet.import_patched('requests')
|
||||
|
||||
import logging
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger('deemix')
|
||||
|
||||
from deezer.gw import APIError as gwAPIError
|
||||
from deezer.api import APIError
|
||||
from deemix.utils import removeFeatures, andCommaConcat, removeDuplicateArtists, generateReplayGainString
|
||||
from deemix.types.Album import Album
|
||||
from deemix.types.Artist import Artist
|
||||
from deemix.types.Date import Date
|
||||
from deemix.types.Picture import Picture
|
||||
from deemix.types.Playlist import Playlist
|
||||
from deemix.types.Lyrics import Lyrics
|
||||
from deemix import VARIOUS_ARTISTS
|
||||
|
||||
class Track:
|
||||
def __init__(self, id="0", name=""):
|
||||
self.id = id
|
||||
self.title = name
|
||||
self.MD5 = ""
|
||||
self.mediaVersion = ""
|
||||
self.duration = 0
|
||||
self.fallbackId = "0"
|
||||
self.filesizes = {}
|
||||
self.localTrack = False
|
||||
self.mainArtist = None
|
||||
self.artist = {"Main": []}
|
||||
self.artists = []
|
||||
self.album = None
|
||||
self.trackNumber = "0"
|
||||
self.discNumber = "0"
|
||||
self.date = None
|
||||
self.lyrics = None
|
||||
self.bpm = 0
|
||||
self.contributors = {}
|
||||
self.copyright = ""
|
||||
self.explicit = False
|
||||
self.ISRC = ""
|
||||
self.replayGain = ""
|
||||
self.playlist = None
|
||||
self.position = None
|
||||
self.searched = False
|
||||
self.selectedFormat = 0
|
||||
self.singleDownload = False
|
||||
self.dateString = None
|
||||
self.artistsString = ""
|
||||
self.mainArtistsString = ""
|
||||
self.featArtistsString = ""
|
||||
|
||||
def parseEssentialData(self, trackAPI_gw, trackAPI=None):
|
||||
self.id = str(trackAPI_gw['SNG_ID'])
|
||||
self.duration = trackAPI_gw['DURATION']
|
||||
self.MD5 = trackAPI_gw.get('MD5_ORIGIN')
|
||||
if not self.MD5:
|
||||
if trackAPI and trackAPI.get('md5_origin'):
|
||||
self.MD5 = trackAPI['md5_origin']
|
||||
else:
|
||||
raise MD5NotFound
|
||||
self.mediaVersion = trackAPI_gw['MEDIA_VERSION']
|
||||
self.fallbackId = "0"
|
||||
if 'FALLBACK' in trackAPI_gw:
|
||||
self.fallbackId = trackAPI_gw['FALLBACK']['SNG_ID']
|
||||
self.localTrack = int(self.id) < 0
|
||||
|
||||
def retriveFilesizes(self, dz):
|
||||
try:
|
||||
guest_sid = dz.session.cookies.get('sid')
|
||||
site = requests.post(
|
||||
"https://api.deezer.com/1.0/gateway.php",
|
||||
params={
|
||||
'api_key': "4VCYIJUCDLOUELGD1V8WBVYBNVDYOXEWSLLZDONGBBDFVXTZJRXPR29JRLQFO6ZE",
|
||||
'sid': guest_sid,
|
||||
'input': '3',
|
||||
'output': '3',
|
||||
'method': 'song_getData'
|
||||
},
|
||||
timeout=30,
|
||||
json={'sng_id': self.id},
|
||||
headers=dz.http_headers
|
||||
)
|
||||
result_json = site.json()
|
||||
except:
|
||||
eventlet.sleep(2)
|
||||
return self.retriveFilesizes(dz)
|
||||
if len(result_json['error']):
|
||||
raise APIError(json.dumps(result_json['error']))
|
||||
response = result_json.get("results")
|
||||
filesizes = {}
|
||||
for key, value in response.items():
|
||||
if key.startswith("FILESIZE_"):
|
||||
filesizes[key] = value
|
||||
filesizes[key+"_TESTED"] = False
|
||||
self.filesizes = filesizes
|
||||
|
||||
def parseData(self, dz, id=None, trackAPI_gw=None, trackAPI=None, albumAPI_gw=None, albumAPI=None, playlistAPI=None):
|
||||
if id:
|
||||
if not trackAPI_gw: trackAPI_gw = dz.gw.get_track_with_fallback(id)
|
||||
elif not trackAPI_gw: raise NoDataToParse
|
||||
if not trackAPI:
|
||||
try: trackAPI = dz.api.get_track(trackAPI_gw['SNG_ID'])
|
||||
except APIError: trackAPI = None
|
||||
|
||||
self.parseEssentialData(trackAPI_gw, trackAPI)
|
||||
|
||||
if self.localTrack:
|
||||
self.parseLocalTrackData(trackAPI_gw)
|
||||
else:
|
||||
self.retriveFilesizes(dz)
|
||||
|
||||
self.parseTrackGW(trackAPI_gw)
|
||||
# Get Lyrics data
|
||||
if not "LYRICS" in trackAPI_gw and self.lyrics.id != "0":
|
||||
try: trackAPI_gw["LYRICS"] = dz.gw.get_track_lyrics(self.id)
|
||||
except gwAPIError: self.lyrics.id = "0"
|
||||
if self.lyrics.id != "0": self.lyrics.parseLyrics(trackAPI_gw["LYRICS"])
|
||||
|
||||
# Parse Album data
|
||||
self.album = Album(
|
||||
id = trackAPI_gw['ALB_ID'],
|
||||
title = trackAPI_gw['ALB_TITLE'],
|
||||
pic_md5 = trackAPI_gw.get('ALB_PICTURE')
|
||||
)
|
||||
|
||||
# Get album Data
|
||||
if not albumAPI:
|
||||
try: albumAPI = dz.api.get_album(self.album.id)
|
||||
except APIError: albumAPI = None
|
||||
|
||||
# Get album_gw Data
|
||||
if not albumAPI_gw:
|
||||
try: albumAPI_gw = dz.gw.get_album(self.album.id)
|
||||
except gwAPIError: albumAPI_gw = None
|
||||
|
||||
if albumAPI:
|
||||
self.album.parseAlbum(albumAPI)
|
||||
elif albumAPI_gw:
|
||||
self.album.parseAlbumGW(albumAPI_gw)
|
||||
# albumAPI_gw doesn't contain the artist cover
|
||||
# Getting artist image ID
|
||||
# ex: https://e-cdns-images.dzcdn.net/images/artist/f2bc007e9133c946ac3c3907ddc5d2ea/56x56-000000-80-0-0.jpg
|
||||
artistAPI = dz.api.get_artist(self.album.mainArtist.id)
|
||||
self.album.mainArtist.pic.md5 = artistAPI['picture_small'][artistAPI['picture_small'].find('artist/') + 7:-24]
|
||||
else:
|
||||
raise AlbumDoesntExists
|
||||
|
||||
# Fill missing data
|
||||
if self.album.date and not self.date: self.date = self.album.date
|
||||
if not self.album.discTotal: self.album.discTotal = albumAPI_gw.get('NUMBER_DISK', "1")
|
||||
if not self.copyright: self.copyright = albumAPI_gw['COPYRIGHT']
|
||||
self.parseTrack(trackAPI)
|
||||
|
||||
# Make sure there is at least one artist
|
||||
if not len(self.artist['Main']):
|
||||
self.artist['Main'] = [self.mainArtist['name']]
|
||||
|
||||
self.singleDownload = trackAPI_gw.get('SINGLE_TRACK', False)
|
||||
self.position = trackAPI_gw.get('POSITION')
|
||||
|
||||
# Add playlist data if track is in a playlist
|
||||
if playlistAPI: self.playlist = Playlist(playlistAPI)
|
||||
|
||||
self.generateMainFeatStrings()
|
||||
return self
|
||||
|
||||
def parseLocalTrackData(self, trackAPI_gw):
|
||||
# Local tracks has only the trackAPI_gw page and
|
||||
# contains only the tags provided by the file
|
||||
self.title = trackAPI_gw['SNG_TITLE']
|
||||
self.album = Album(title=trackAPI_gw['ALB_TITLE'])
|
||||
self.album.pic = Picture(
|
||||
md5 = trackAPI_gw.get('ALB_PICTURE', ""),
|
||||
type = "cover"
|
||||
)
|
||||
self.mainArtist = Artist(name=trackAPI_gw['ART_NAME'])
|
||||
self.artists = [trackAPI_gw['ART_NAME']]
|
||||
self.artist = {
|
||||
'Main': [trackAPI_gw['ART_NAME']]
|
||||
}
|
||||
self.album.artist = self.artist
|
||||
self.album.artists = self.artists
|
||||
self.album.date = self.date
|
||||
self.album.mainArtist = self.mainArtist
|
||||
self.date = Date()
|
||||
|
||||
def parseTrackGW(self, trackAPI_gw):
|
||||
self.title = trackAPI_gw['SNG_TITLE'].strip()
|
||||
if trackAPI_gw.get('VERSION') and not trackAPI_gw['VERSION'] in trackAPI_gw['SNG_TITLE']:
|
||||
self.title += " " + trackAPI_gw['VERSION'].strip()
|
||||
|
||||
self.discNumber = trackAPI_gw.get('DISK_NUMBER')
|
||||
self.explicit = bool(int(trackAPI_gw.get('EXPLICIT_LYRICS', "0")))
|
||||
self.copyright = trackAPI_gw.get('COPYRIGHT')
|
||||
if 'GAIN' in trackAPI_gw: self.replayGain = generateReplayGainString(trackAPI_gw['GAIN'])
|
||||
self.ISRC = trackAPI_gw.get('ISRC')
|
||||
self.trackNumber = trackAPI_gw['TRACK_NUMBER']
|
||||
self.contributors = trackAPI_gw['SNG_CONTRIBUTORS']
|
||||
|
||||
self.lyrics = Lyrics(trackAPI_gw.get('LYRICS_ID', "0"))
|
||||
|
||||
self.mainArtist = Artist(
|
||||
id = trackAPI_gw['ART_ID'],
|
||||
name = trackAPI_gw['ART_NAME'],
|
||||
pic_md5 = trackAPI_gw.get('ART_PICTURE')
|
||||
)
|
||||
|
||||
if 'PHYSICAL_RELEASE_DATE' in trackAPI_gw:
|
||||
day = trackAPI_gw["PHYSICAL_RELEASE_DATE"][8:10]
|
||||
month = trackAPI_gw["PHYSICAL_RELEASE_DATE"][5:7]
|
||||
year = trackAPI_gw["PHYSICAL_RELEASE_DATE"][0:4]
|
||||
self.date = Date(year, month, day)
|
||||
|
||||
def parseTrack(self, trackAPI):
|
||||
self.bpm = trackAPI['bpm']
|
||||
|
||||
if not self.replayGain and 'gain' in trackAPI:
|
||||
self.replayGain = generateReplayGainString(trackAPI['gain'])
|
||||
if not self.explicit:
|
||||
self.explicit = trackAPI['explicit_lyrics']
|
||||
if not self.discNumber:
|
||||
self.discNumber = trackAPI['disk_number']
|
||||
|
||||
for artist in trackAPI['contributors']:
|
||||
isVariousArtists = str(artist['id']) == VARIOUS_ARTISTS
|
||||
isMainArtist = artist['role'] == "Main"
|
||||
|
||||
if len(trackAPI['contributors']) > 1 and isVariousArtists:
|
||||
continue
|
||||
|
||||
if artist['name'] not in self.artists:
|
||||
self.artists.append(artist['name'])
|
||||
|
||||
if isMainArtist or artist['name'] not in self.artist['Main'] and not isMainArtist:
|
||||
if not artist['role'] in self.artist:
|
||||
self.artist[artist['role']] = []
|
||||
self.artist[artist['role']].append(artist['name'])
|
||||
|
||||
def removeDuplicateArtists(self):
|
||||
(self.artist, self.artists) = removeDuplicateArtists(self.artist, self.artists)
|
||||
|
||||
# Removes featuring from the title
|
||||
def getCleanTitle(self):
|
||||
return removeFeatures(self.title)
|
||||
|
||||
def getFeatTitle(self):
|
||||
if self.featArtistsString and not "(feat." in self.title.lower():
|
||||
return self.title + " ({})".format(self.featArtistsString)
|
||||
return self.title
|
||||
|
||||
def generateMainFeatStrings(self):
|
||||
self.mainArtistsString = andCommaConcat(self.artist['Main'])
|
||||
self.featArtistsString = ""
|
||||
if 'Featured' in self.artist:
|
||||
self.featArtistsString = "feat. "+andCommaConcat(self.artist['Featured'])
|
||||
|
||||
class TrackError(Exception):
|
||||
"""Base class for exceptions in this module."""
|
||||
pass
|
||||
|
||||
class AlbumDoesntExists(TrackError):
|
||||
pass
|
||||
|
||||
class MD5NotFound(TrackError):
|
||||
pass
|
||||
|
||||
class NoDataToParse(TrackError):
|
||||
pass
|
|
@ -0,0 +1,7 @@
|
|||
from deemix.types.Date import Date
|
||||
from deemix.types.Picture import Picture
|
||||
from deemix.types.Lyrics import Lyrics
|
||||
from deemix.types.Album import Album
|
||||
from deemix.types.Artist import Artist
|
||||
from deemix.types.Playlist import Playlist
|
||||
from deemix.types.Track import Track
|
|
@ -122,6 +122,12 @@ def uniqueArray(arr):
|
|||
del arr[iRest]
|
||||
return arr
|
||||
|
||||
def removeDuplicateArtists(artist, artists):
|
||||
artists = uniqueArray(artists)
|
||||
for role in artist.keys():
|
||||
artist[role] = uniqueArray(artist[role])
|
||||
return (artist, artists)
|
||||
|
||||
def checkFolder(folder):
|
||||
try:
|
||||
os.makedirs(folder, exist_ok=True)
|
||||
|
|
|
@ -85,7 +85,7 @@ def generateFilepath(track, settings):
|
|||
(settings['createArtistFolder'] and track.playlist and settings['tags']['savePlaylistAsCompilation']) or
|
||||
(settings['createArtistFolder'] and track.playlist and settings['createStructurePlaylist'])
|
||||
):
|
||||
filepath = filepath / settingsRegexArtist(settings['artistNameTemplate'], track.album['mainArtist'], settings, rootArtist=track.album['rootArtist'])
|
||||
filepath = filepath / settingsRegexArtist(settings['artistNameTemplate'], track.album.mainArtist, settings, rootArtist=track.album.rootArtist)
|
||||
artistPath = filepath
|
||||
|
||||
if (settings['createAlbumFolder'] and
|
||||
|
@ -102,7 +102,7 @@ def generateFilepath(track, settings):
|
|||
extrasPath = filepath
|
||||
|
||||
if (
|
||||
int(track.album['discTotal']) > 1 and (
|
||||
int(track.album.discTotal) > 1 and (
|
||||
(settings['createAlbumFolder'] and settings['createCDFolder']) and
|
||||
(not track.singleDownload or (track.singleDownload and settings['createSingleFolder'])) and
|
||||
(not track.playlist or
|
||||
|
@ -117,7 +117,7 @@ def generateFilepath(track, settings):
|
|||
|
||||
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("%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']))
|
||||
|
@ -125,92 +125,92 @@ def settingsRegex(filename, track, settings):
|
|||
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'], settings))
|
||||
filename = filename.replace("%tracktotal%", str(track.album['trackTotal']))
|
||||
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, settings))
|
||||
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("%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("%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("%label%", fixName(track.album.label, settings['illegalCharacterReplacer']))
|
||||
filename = filename.replace("%isrc%", track.ISRC)
|
||||
filename = filename.replace("%upc%", track.album['barcode'])
|
||||
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("%album_id%", str(track.album.id))
|
||||
filename = filename.replace("%artist_id%", str(track.mainArtist.id))
|
||||
if track.playlist:
|
||||
filename = filename.replace("%playlist_id%", str(track.playlist['playlistId']))
|
||||
filename = filename.replace("%position%", pad(track.position, track.playlist['trackTotal'], settings))
|
||||
filename = filename.replace("%playlist_id%", str(track.playlist.playlistId))
|
||||
filename = filename.replace("%position%", pad(track.position, track.playlist.trackTotal, settings))
|
||||
else:
|
||||
filename = filename.replace("%playlist_id%", '')
|
||||
filename = filename.replace("%position%", pad(track.trackNumber, track.album['trackTotal'], settings))
|
||||
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['playlistId']))
|
||||
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:
|
||||
foldername = foldername.replace("%genre%", fixName(album['genre'][0], settings['illegalCharacterReplacer']))
|
||||
foldername = foldername.replace("%album_id%", str(album.id))
|
||||
if len(album.genre) > 0:
|
||||
foldername = foldername.replace("%genre%", fixName(album.genre[0], settings['illegalCharacterReplacer']))
|
||||
else:
|
||||
foldername = foldername.replace("%genre%", "Unknown")
|
||||
foldername = foldername.replace("%album%", fixName(album['title'], settings['illegalCharacterReplacer']))
|
||||
foldername = foldername.replace("%artist%", fixName(album['mainArtist']['name'], settings['illegalCharacterReplacer']))
|
||||
foldername = foldername.replace("%artist_id%", str(album['mainArtist']['id']))
|
||||
if album['rootArtist']:
|
||||
foldername = foldername.replace("%root_artist%", fixName(album['rootArtist']['name'], settings['illegalCharacterReplacer']))
|
||||
foldername = foldername.replace("%root_artist_id%", str(album['rootArtist']['id']))
|
||||
foldername = foldername.replace("%album%", fixName(album.title, settings['illegalCharacterReplacer']))
|
||||
foldername = foldername.replace("%artist%", fixName(album.mainArtist.name, settings['illegalCharacterReplacer']))
|
||||
foldername = foldername.replace("%artist_id%", str(album.mainArtist.id))
|
||||
if album.rootArtist:
|
||||
foldername = foldername.replace("%root_artist%", fixName(album.rootArtist.name, settings['illegalCharacterReplacer']))
|
||||
foldername = foldername.replace("%root_artist_id%", str(album.rootArtist.id))
|
||||
else:
|
||||
foldername = foldername.replace("%root_artist%", fixName(album['mainArtist']['name'], settings['illegalCharacterReplacer']))
|
||||
foldername = foldername.replace("%root_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'].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']))
|
||||
foldername = foldername.replace("%year%", str(album['date']['year']))
|
||||
foldername = foldername.replace("%date%", album['dateString'])
|
||||
foldername = foldername.replace("%bitrate%", bitrateLabels[int(album['bitrate'])])
|
||||
foldername = foldername.replace("%root_artist%", fixName(album.mainArtist.name, settings['illegalCharacterReplacer']))
|
||||
foldername = foldername.replace("%root_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.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']))
|
||||
foldername = foldername.replace("%year%", str(album.date.year))
|
||||
foldername = foldername.replace("%date%", album.dateString)
|
||||
foldername = foldername.replace("%bitrate%", bitrateLabels[int(album.bitrate)])
|
||||
|
||||
foldername = foldername.replace('\\', pathSep).replace('/', pathSep)
|
||||
return antiDot(fixLongName(foldername))
|
||||
|
||||
|
||||
def settingsRegexArtist(foldername, artist, settings, rootArtist=None):
|
||||
foldername = foldername.replace("%artist%", fixName(artist['name'], settings['illegalCharacterReplacer']))
|
||||
foldername = foldername.replace("%artist_id%", str(artist['id']))
|
||||
foldername = foldername.replace("%artist%", fixName(artist.name, settings['illegalCharacterReplacer']))
|
||||
foldername = foldername.replace("%artist_id%", str(artist.id))
|
||||
if rootArtist:
|
||||
foldername = foldername.replace("%root_artist%", fixName(rootArtist['name'], settings['illegalCharacterReplacer']))
|
||||
foldername = foldername.replace("%root_artist_id%", str(rootArtist['id']))
|
||||
foldername = foldername.replace("%root_artist%", fixName(rootArtist.name, settings['illegalCharacterReplacer']))
|
||||
foldername = foldername.replace("%root_artist_id%", str(rootArtist.id))
|
||||
else:
|
||||
foldername = foldername.replace("%root_artist%", fixName(artist['name'], settings['illegalCharacterReplacer']))
|
||||
foldername = foldername.replace("%root_artist_id%", str(artist['id']))
|
||||
foldername = foldername.replace("%root_artist%", fixName(artist.name, settings['illegalCharacterReplacer']))
|
||||
foldername = foldername.replace("%root_artist_id%", str(artist.id))
|
||||
foldername = foldername.replace('\\', pathSep).replace('/', pathSep)
|
||||
return antiDot(fixLongName(foldername))
|
||||
|
||||
|
||||
def settingsRegexPlaylist(foldername, playlist, settings):
|
||||
foldername = foldername.replace("%playlist%", fixName(playlist['title'], settings['illegalCharacterReplacer']))
|
||||
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("%playlist%", fixName(playlist.title, settings['illegalCharacterReplacer']))
|
||||
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))
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ def tagID3(stream, track, save):
|
|||
tag.add(TPE1(text=track.artists))
|
||||
else:
|
||||
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 ARTISTS is added to keep the multiartist support when using a non standard tagging method
|
||||
|
@ -28,56 +28,56 @@ def tagID3(stream, track, save):
|
|||
tag.add(TXXX(desc="ARTISTS", 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']:
|
||||
trackNumber = str(track.trackNumber)
|
||||
if save['trackTotal']:
|
||||
trackNumber += "/" + str(track.album['trackTotal'])
|
||||
trackNumber += "/" + str(track.album.trackTotal)
|
||||
tag.add(TRCK(text=trackNumber))
|
||||
if save['discNumber']:
|
||||
discNumber = str(track.discNumber)
|
||||
if save['discTotal']:
|
||||
discNumber += "/" + str(track.album['discTotal'])
|
||||
discNumber += "/" + str(track.album.discTotal)
|
||||
tag.add(TPOS(text=discNumber))
|
||||
|
||||
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']:
|
||||
# Referencing ID3 standard
|
||||
# https://id3.org/id3v2.3.0#TDAT
|
||||
# The 'Date' frame is a numeric string in the DDMM format.
|
||||
tag.add(TDAT(text=str(track.date['day']) + str(track.date['month'])))
|
||||
tag.add(TDAT(text=str(track.date.day) + str(track.date.month)))
|
||||
if save['length']:
|
||||
tag.add(TLEN(text=str(int(track.duration)*1000)))
|
||||
if save['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))
|
||||
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" ))
|
||||
if save['replayGain']:
|
||||
tag.add(TXXX(desc="REPLAYGAIN_TRACK_GAIN", text=track.replayGain))
|
||||
if track.lyrics['unsync'] and save['lyrics']:
|
||||
tag.add(USLT(text=track.lyrics['unsync']))
|
||||
if track.lyrics['syncID3'] and save['syncedLyrics']:
|
||||
if track.lyrics.unsync and save['lyrics']:
|
||||
tag.add(USLT(text=track.lyrics.unsync))
|
||||
if track.lyrics.syncID3 and save['syncedLyrics']:
|
||||
# Referencing ID3 standard
|
||||
# https://id3.org/id3v2.3.0#sec4.10
|
||||
# Type: 1 => is lyrics
|
||||
# Format: 2 => Absolute time, 32 bit sized, using milliseconds as unit
|
||||
tag.add(SYLT(Encoding.UTF8, type=1, format=2, text=track.lyrics['syncID3']))
|
||||
tag.add(SYLT(Encoding.UTF8, type=1, format=2, text=track.lyrics.syncID3))
|
||||
|
||||
involved_people = []
|
||||
for role in track.contributors:
|
||||
|
@ -91,24 +91,24 @@ def tagID3(stream, track, save):
|
|||
|
||||
if save['copyright']:
|
||||
tag.add(TCOP(text=track.copyright))
|
||||
if save['savePlaylistAsCompilation'] and track.playlist or track.album['recordType'] == "compile":
|
||||
if save['savePlaylistAsCompilation'] and track.playlist or track.album.recordType == "compile":
|
||||
tag.add(TCMP(text="1"))
|
||||
|
||||
if save['source']:
|
||||
tag.add(TXXX(desc="SOURCE", text='Deezer'))
|
||||
tag.add(TXXX(desc="SOURCEID", text=str(track.id)))
|
||||
|
||||
if save['cover'] and track.album['embeddedCoverPath']:
|
||||
if save['cover'] and track.album.embeddedCoverPath:
|
||||
|
||||
descEncoding = Encoding.LATIN1
|
||||
if save['coverDescriptionUTF8']:
|
||||
descEncoding = Encoding.UTF8
|
||||
|
||||
mimeType = 'image/jpeg'
|
||||
if str(track.album['embeddedCoverPath']).endswith('png'):
|
||||
if str(track.album.embeddedCoverPath).endswith('png'):
|
||||
mimeType = 'image/png'
|
||||
|
||||
with open(track.album['embeddedCoverPath'], 'rb') as f:
|
||||
with open(track.album.embeddedCoverPath, 'rb') as f:
|
||||
tag.add(APIC(descEncoding, mimeType, PictureType.COVER_FRONT, desc='cover', data=f.read()))
|
||||
|
||||
tag.save( stream,
|
||||
|
@ -131,7 +131,7 @@ def tagFLAC(stream, track, save):
|
|||
tag["ARTIST"] = track.artists
|
||||
else:
|
||||
if save['multiArtistSeparator'] == "nothing":
|
||||
tag["ARTIST"] = track.mainArtist['name']
|
||||
tag["ARTIST"] = track.mainArtist.name
|
||||
else:
|
||||
tag["ARTIST"] = track.artistsString
|
||||
# Tag ARTISTS is added to keep the multiartist support when using a non standard tagging method
|
||||
|
@ -139,24 +139,24 @@ def tagFLAC(stream, track, save):
|
|||
tag["ARTISTS"] = 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['singleAlbumArtist'] and track.album['mainArtist']['save']:
|
||||
tag["ALBUMARTIST"] = track.album['mainArtist']['name']
|
||||
if save['albumArtist'] and len(track.album.artists):
|
||||
if save['singleAlbumArtist'] and track.album.mainArtist.save:
|
||||
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)
|
||||
if save['trackTotal']:
|
||||
tag["TRACKTOTAL"] = str(track.album['trackTotal'])
|
||||
tag["TRACKTOTAL"] = str(track.album.trackTotal)
|
||||
if save['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
|
||||
|
||||
# YEAR tag is not suggested as a standard tag
|
||||
# Being YEAR already contained in DATE will only use DATE instead
|
||||
|
@ -164,24 +164,24 @@ def tagFLAC(stream, track, save):
|
|||
if save['date']:
|
||||
tag["DATE"] = track.dateString
|
||||
elif save['year']:
|
||||
tag["DATE"] = str(track.date['year'])
|
||||
tag["DATE"] = str(track.date.year)
|
||||
|
||||
if save['length']:
|
||||
tag["LENGTH"] = str(int(track.duration)*1000)
|
||||
if save['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
|
||||
if save['barcode']:
|
||||
tag["BARCODE"] = track.album['barcode']
|
||||
tag["BARCODE"] = track.album.barcode
|
||||
if save['explicit']:
|
||||
tag["ITUNESADVISORY"] = "1" if track.explicit else "0"
|
||||
if save['replayGain']:
|
||||
tag["REPLAYGAIN_TRACK_GAIN"] = track.replayGain
|
||||
if track.lyrics['unsync'] and save['lyrics']:
|
||||
tag["LYRICS"] = track.lyrics['unsync']
|
||||
if track.lyrics.unsync and save['lyrics']:
|
||||
tag["LYRICS"] = track.lyrics.unsync
|
||||
|
||||
for role in track.contributors:
|
||||
if role in ['author', 'engineer', 'mixer', 'producer', 'writer', 'composer']:
|
||||
|
@ -192,20 +192,20 @@ def tagFLAC(stream, track, save):
|
|||
|
||||
if save['copyright']:
|
||||
tag["COPYRIGHT"] = track.copyright
|
||||
if save['savePlaylistAsCompilation'] and track.playlist or track.album['recordType'] == "compile":
|
||||
if save['savePlaylistAsCompilation'] and track.playlist or track.album.recordType == "compile":
|
||||
tag["COMPILATION"] = "1"
|
||||
|
||||
if save['source']:
|
||||
tag["SOURCE"] = 'Deezer'
|
||||
tag["SOURCEID"] = str(track.id)
|
||||
|
||||
if save['cover'] and track.album['embeddedCoverPath']:
|
||||
if save['cover'] and track.album.embeddedCoverPath:
|
||||
image = Picture()
|
||||
image.type = PictureType.COVER_FRONT
|
||||
image.mime = 'image/jpeg'
|
||||
if str(track.album['embeddedCoverPath']).endswith('png'):
|
||||
if str(track.album.embeddedCoverPath).endswith('png'):
|
||||
image.mime = 'image/png'
|
||||
with open(track.album['embeddedCoverPath'], 'rb') as f:
|
||||
with open(track.album.embeddedCoverPath, 'rb') as f:
|
||||
image.data = f.read()
|
||||
tag.add_picture(image)
|
||||
|
||||
|
|
Loading…
Reference in New Issue