Merge refactoring (#4)

Removed saveDownloadQueue and tagsLanguage from lib settings

Revert embedded cover change

Fixed bitrate fallback check

Use overwriteFile setting when downloading embedded covers

Fixed bitrate fallback not working

Fixed some issues to make the lib work

Implemented spotify plugin back

Better handling of albums upcs

Fixed queue item not cancelling correctly

Code parity with deemix-js

Code cleanup with pylint

Even more rework on the library

More work on the library (WIP)

Total rework of the library (WIP)

Some rework done on types

Added start queue function

Made nextitem work on a thread

Removed dz as first parameter

Started queuemanager refactoring

Removed eventlet

Co-authored-by: RemixDev <RemixDev64@gmail.com>
Reviewed-on: https://git.freezer.life/RemixDev/deemix-py/pulls/4
Co-Authored-By: RemixDev <remixdev@noreply.localhost>
Co-Committed-By: RemixDev <remixdev@noreply.localhost>
This commit is contained in:
RemixDev
2021-06-27 16:29:41 -04:00
parent 67fcb7d37f
commit f530a4e89f
37 changed files with 2236 additions and 2463 deletions

View File

@ -4,47 +4,57 @@ from deemix.utils import removeDuplicateArtists, removeFeatures
from deemix.types.Artist import Artist
from deemix.types.Date import Date
from deemix.types.Picture import Picture
from deemix import VARIOUS_ARTISTS
from deemix.types import VARIOUS_ARTISTS
class Album:
def __init__(self, id="0", title="", pic_md5=""):
self.id = id
def __init__(self, alb_id="0", title="", pic_md5=""):
self.id = alb_id
self.title = title
self.pic = Picture(md5=pic_md5, type="cover")
self.pic = Picture(pic_md5, "cover")
self.artist = {"Main": []}
self.artists = []
self.mainArtist = None
self.dateString = None
self.barcode = "Unknown"
self.date = None
self.date = Date()
self.dateString = ""
self.trackTotal = "0"
self.discTotal = "0"
self.embeddedCoverPath = None
self.embeddedCoverURL = None
self.embeddedCoverPath = ""
self.embeddedCoverURL = ""
self.explicit = False
self.genre = []
self.barcode = "Unknown"
self.label = "Unknown"
self.copyright = ""
self.recordType = "album"
self.rootArtist = None
self.trackTotal = "0"
self.bitrate = 0
self.rootArtist = None
self.variousArtists = None
self.playlistId = None
self.owner = None
self.isPlaylist = False
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]
art_pic = albumAPI['artist']['picture_small']
art_pic = art_pic[art_pic.find('artist/') + 7:-24]
self.mainArtist = Artist(
id = albumAPI['artist']['id'],
name = albumAPI['artist']['name'],
pic_md5 = artistPicture
albumAPI['artist']['id'],
albumAPI['artist']['name'],
"Main",
art_pic
)
if albumAPI.get('root_artist'):
art_pic = albumAPI['root_artist']['picture_small']
art_pic = art_pic[art_pic.find('artist/') + 7:-24]
self.rootArtist = Artist(
id = albumAPI['root_artist']['id'],
name = albumAPI['root_artist']['name']
albumAPI['root_artist']['id'],
albumAPI['root_artist']['name'],
"Root",
art_pic
)
for artist in albumAPI['contributors']:
@ -53,7 +63,7 @@ class Album:
if isVariousArtists:
self.variousArtists = Artist(
id = artist['id'],
art_id = artist['id'],
name = artist['name'],
role = artist['role']
)
@ -74,18 +84,19 @@ class Album:
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.date.day = albumAPI["release_date"][8:10]
self.date.month = albumAPI["release_date"][5:7]
self.date.year = albumAPI["release_date"][0:4]
self.date.fixDayMonth()
self.discTotal = albumAPI.get('nb_disk')
self.copyright = albumAPI.get('copyright')
if not self.pic.md5:
if 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]
alb_pic = albumAPI['cover_small']
self.pic.md5 = alb_pic[alb_pic.find('cover/') + 6:-24]
if albumAPI.get('genres') and len(albumAPI['genres'].get('data', [])) > 0:
for genre in albumAPI['genres']['data']:
@ -94,8 +105,9 @@ class Album:
def parseAlbumGW(self, albumAPI_gw):
self.title = albumAPI_gw['ALB_TITLE']
self.mainArtist = Artist(
id = albumAPI_gw['ART_ID'],
name = albumAPI_gw['ART_NAME']
art_id = albumAPI_gw['ART_ID'],
name = albumAPI_gw['ART_NAME'],
role = "Main"
)
self.artists = [albumAPI_gw['ART_NAME']]
@ -106,13 +118,16 @@ class Album:
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.addExtraAlbumGWData(albumAPI_gw)
def addExtraAlbumGWData(self, albumAPI_gw):
if 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)
self.date.day = albumAPI_gw["PHYSICAL_RELEASE_DATE"][8:10]
self.date.month = albumAPI_gw["PHYSICAL_RELEASE_DATE"][5:7]
self.date.year = albumAPI_gw["PHYSICAL_RELEASE_DATE"][0:4]
self.date.fixDayMonth()
def makePlaylistCompilation(self, playlist):
self.variousArtists = playlist.variousArtists
@ -131,10 +146,12 @@ class Album:
self.playlistId = playlist.playlistId
self.owner = playlist.owner
self.pic = playlist.pic
self.isPlaylist = True
def removeDuplicateArtists(self):
"""Removes duplicate artists for both artist array and artists dict"""
(self.artist, self.artists) = removeDuplicateArtists(self.artist, self.artists)
# Removes featuring from the album name
def getCleanTitle(self):
"""Removes featuring from the album name"""
return removeFeatures(self.title)

View File

@ -1,12 +1,12 @@
from deemix.types.Picture import Picture
from deemix import VARIOUS_ARTISTS
from deemix.types import VARIOUS_ARTISTS
class Artist:
def __init__(self, id="0", name="", pic_md5="", role=""):
self.id = str(id)
def __init__(self, art_id="0", name="", role="", pic_md5=""):
self.id = str(art_id)
self.name = name
self.pic = Picture(md5=pic_md5, type="artist")
self.role = ""
self.pic = Picture(md5=pic_md5, pic_type="artist")
self.role = role
self.save = True
def isVariousArtists(self):

View File

@ -1,8 +1,8 @@
class Date(object):
def __init__(self, year="XXXX", month="00", day="00"):
self.year = year
self.month = month
class Date:
def __init__(self, day="00", month="00", year="XXXX"):
self.day = day
self.month = month
self.year = year
self.fixDayMonth()
# Fix incorrect day month when detectable

View File

@ -0,0 +1,126 @@
class IDownloadObject:
"""DownloadObject Interface"""
def __init__(self, obj):
self.type = obj['type']
self.id = obj['id']
self.bitrate = obj['bitrate']
self.title = obj['title']
self.artist = obj['artist']
self.cover = obj['cover']
self.explicit = obj.get('explicit', False)
self.size = obj.get('size', 0)
self.downloaded = obj.get('downloaded', 0)
self.failed = obj.get('failed', 0)
self.progress = obj.get('progress', 0)
self.errors = obj.get('errors', [])
self.files = obj.get('files', [])
self.progressNext = 0
self.uuid = f"{self.type}_{self.id}_{self.bitrate}"
self.isCanceled = False
self.__type__ = None
def toDict(self):
return {
'type': self.type,
'id': self.id,
'bitrate': self.bitrate,
'uuid': self.uuid,
'title': self.title,
'artist': self.artist,
'cover': self.cover,
'explicit': self.explicit,
'size': self.size,
'downloaded': self.downloaded,
'failed': self.failed,
'progress': self.progress,
'errors': self.errors,
'files': self.files,
'__type__': self.__type__
}
def getResettedDict(self):
item = self.toDict()
item['downloaded'] = 0
item['failed'] = 0
item['progress'] = 0
item['errors'] = []
item['files'] = []
return item
def getSlimmedDict(self):
light = self.toDict()
propertiesToDelete = ['single', 'collection', 'plugin', 'conversion_data']
for prop in propertiesToDelete:
if prop in light:
del light[prop]
return light
def getEssentialDict(self):
return {
'type': self.type,
'id': self.id,
'bitrate': self.bitrate,
'uuid': self.uuid,
'title': self.title,
'artist': self.artist,
'cover': self.cover,
'explicit': self.explicit,
'size': self.size
}
def updateProgress(self, listener=None):
if round(self.progressNext) != self.progress and round(self.progressNext) % 2 == 0:
self.progress = round(self.progressNext)
if listener: listener.send("updateQueue", {'uuid': self.uuid, 'progress': self.progress})
class Single(IDownloadObject):
def __init__(self, obj):
super().__init__(obj)
self.size = 1
self.single = obj['single']
self.__type__ = "Single"
def toDict(self):
item = super().toDict()
item['single'] = self.single
return item
def completeTrackProgress(self, listener=None):
self.progressNext = 100
self.updateProgress(listener)
def removeTrackProgress(self, listener=None):
self.progressNext = 0
self.updateProgress(listener)
class Collection(IDownloadObject):
def __init__(self, obj):
super().__init__(obj)
self.collection = obj['collection']
self.__type__ = "Collection"
def toDict(self):
item = super().toDict()
item['collection'] = self.collection
return item
def completeTrackProgress(self, listener=None):
self.progressNext += (1 / self.size) * 100
self.updateProgress(listener)
def removeTrackProgress(self, listener=None):
self.progressNext -= (1 / self.size) * 100
self.updateProgress(listener)
class Convertable(Collection):
def __init__(self, obj):
super().__init__(obj)
self.plugin = obj['plugin']
self.conversion_data = obj['conversion_data']
self.__type__ = "Convertable"
def toDict(self):
item = super().toDict()
item['plugin'] = self.plugin
item['conversion_data'] = self.conversion_data
return item

View File

@ -1,19 +1,17 @@
class Lyrics:
def __init__(self, id="0"):
self.id = id
self.sync = None
self.unsync = None
self.syncID3 = None
def __init__(self, lyr_id="0"):
self.id = lyr_id
self.sync = ""
self.unsync = ""
self.syncID3 = []
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)):
for line, _ in enumerate(syncLyricsJson):
if syncLyricsJson[line]["line"] != "":
timestamp = syncLyricsJson[line]["lrc_timestamp"]
milliseconds = int(syncLyricsJson[line]["milliseconds"])
@ -21,6 +19,6 @@ class Lyrics:
else:
notEmptyLine = line + 1
while syncLyricsJson[notEmptyLine]["line"] == "":
notEmptyLine = notEmptyLine + 1
notEmptyLine += 1
timestamp = syncLyricsJson[notEmptyLine]["lrc_timestamp"]
self.sync += timestamp + syncLyricsJson[line]["line"] + "\r\n"

View File

@ -1,27 +1,29 @@
class Picture:
def __init__(self, md5="", type=None, url=None):
def __init__(self, md5="", pic_type=""):
self.md5 = md5
self.type = type
self.url = url
self.type = pic_type
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'
)
def getURL(self, size, pic_format):
url = "https://e-cdns-images.dzcdn.net/images/{}/{}/{size}x{size}".format(
self.type,
self.md5,
size=size
)
if pic_format.startswith("jpg"):
quality = 80
if '-' in pic_format:
quality = pic_format[4:]
pic_format = 'jpg'
return url + f'-000000-{quality}-0-0.jpg'
if pic_format == 'png':
return url + '-none-100-0-0.png'
return url+'.jpg'
class StaticPicture:
def __init__(self, url):
self.staticURL = url
def getURL(self):
return self.staticURL

View File

@ -1,20 +1,9 @@
from deemix.types.Artist import Artist
from deemix.types.Date import Date
from deemix.types.Picture import Picture
from deemix.types.Picture import Picture, StaticPicture
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
@ -30,19 +19,28 @@ class Playlist:
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.date = Date(day, month, year)
self.discTotal = "1"
self.playlistId = playlistAPI['id']
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
)
self.pic = Picture(md5, picType)
else:
self.pic = Picture(url = playlistAPI['picture_xl'])
self.pic = StaticPicture(playlistAPI['picture_xl'])
if 'various_artist' in playlistAPI:
pic_md5 = playlistAPI['various_artist']['picture_small']
pic_md5 = pic_md5[pic_md5.find('artist/') + 7:-24]
self.variousArtists = Artist(
playlistAPI['various_artist']['id'],
playlistAPI['various_artist']['name'],
"Main",
pic_md5
)
self.mainArtist = self.variousArtists

View File

@ -1,38 +1,39 @@
import eventlet
requests = eventlet.import_patched('requests')
from time import sleep
import re
import requests
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('deemix')
from deezer.gw import APIError as gwAPIError
from deezer.gw import GWAPIError
from deezer.api import APIError
from deemix.utils import removeFeatures, andCommaConcat, removeDuplicateArtists, generateReplayGainString
from deemix.utils import removeFeatures, andCommaConcat, removeDuplicateArtists, generateReplayGainString, changeCase
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
from deemix.types import VARIOUS_ARTISTS
from deemix.settings import FeaturesOption
class Track:
def __init__(self, id="0", name=""):
self.id = id
def __init__(self, sng_id="0", name=""):
self.id = sng_id
self.title = name
self.MD5 = ""
self.mediaVersion = ""
self.duration = 0
self.fallbackId = "0"
self.fallbackID = "0"
self.filesizes = {}
self.localTrack = False
self.local = False
self.mainArtist = None
self.artist = {"Main": []}
self.artists = []
self.album = None
self.trackNumber = "0"
self.discNumber = "0"
self.date = None
self.date = Date()
self.lyrics = None
self.bpm = 0
self.contributors = {}
@ -45,7 +46,7 @@ class Track:
self.searched = False
self.selectedFormat = 0
self.singleDownload = False
self.dateString = None
self.dateString = ""
self.artistsString = ""
self.mainArtistsString = ""
self.featArtistsString = ""
@ -60,14 +61,14 @@ class Track:
else:
raise MD5NotFound
self.mediaVersion = trackAPI_gw['MEDIA_VERSION']
self.fallbackId = "0"
self.fallbackID = "0"
if 'FALLBACK' in trackAPI_gw:
self.fallbackId = trackAPI_gw['FALLBACK']['SNG_ID']
self.localTrack = int(self.id) < 0
self.fallbackID = trackAPI_gw['FALLBACK']['SNG_ID']
self.local = int(self.id) < 0
def retriveFilesizes(self, dz):
guest_sid = dz.session.cookies.get('sid')
try:
guest_sid = dz.session.cookies.get('sid')
site = requests.post(
"https://api.deezer.com/1.0/gateway.php",
params={
@ -83,21 +84,20 @@ class Track:
)
result_json = site.json()
except:
eventlet.sleep(2)
return self.retriveFilesizes(dz)
sleep(2)
self.retriveFilesizes(dz)
if len(result_json['error']):
raise APIError(json.dumps(result_json['error']))
response = result_json.get("results")
raise TrackError(result_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] = int(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)
def parseData(self, dz, track_id=None, trackAPI_gw=None, trackAPI=None, albumAPI_gw=None, albumAPI=None, playlistAPI=None):
if track_id and not trackAPI_gw: trackAPI_gw = dz.gw.get_track_with_fallback(track_id)
elif not trackAPI_gw: raise NoDataToParse
if not trackAPI:
try: trackAPI = dz.api.get_track(trackAPI_gw['SNG_ID'])
@ -105,21 +105,21 @@ class Track:
self.parseEssentialData(trackAPI_gw, trackAPI)
if self.localTrack:
if self.local:
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"
except GWAPIError: self.lyrics.id = "0"
if self.lyrics.id != "0": self.lyrics.parseLyrics(trackAPI_gw["LYRICS"])
# Parse Album data
# Parse Album Data
self.album = Album(
id = trackAPI_gw['ALB_ID'],
alb_id = trackAPI_gw['ALB_ID'],
title = trackAPI_gw['ALB_TITLE'],
pic_md5 = trackAPI_gw.get('ALB_PICTURE')
)
@ -132,7 +132,7 @@ class Track:
# Get album_gw Data
if not albumAPI_gw:
try: albumAPI_gw = dz.gw.get_album(self.album.id)
except gwAPIError: albumAPI_gw = None
except GWAPIError: albumAPI_gw = None
if albumAPI:
self.album.parseAlbum(albumAPI)
@ -147,6 +147,7 @@ class Track:
raise AlbumDoesntExists
# Fill missing data
if albumAPI_gw: self.album.addExtraAlbumGWData(albumAPI_gw)
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']
@ -157,10 +158,9 @@ class Track:
self.title = ' '.join(self.title.split())
# Make sure there is at least one artist
if not len(self.artist['Main']):
if len(self.artist['Main']) == 0:
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
@ -176,9 +176,9 @@ class Track:
self.album = Album(title=trackAPI_gw['ALB_TITLE'])
self.album.pic = Picture(
md5 = trackAPI_gw.get('ALB_PICTURE', ""),
type = "cover"
pic_type = "cover"
)
self.mainArtist = Artist(name=trackAPI_gw['ART_NAME'])
self.mainArtist = Artist(name=trackAPI_gw['ART_NAME'], role="Main")
self.artists = [trackAPI_gw['ART_NAME']]
self.artist = {
'Main': [trackAPI_gw['ART_NAME']]
@ -187,12 +187,11 @@ class Track:
self.album.artists = self.artists
self.album.date = self.date
self.album.mainArtist = self.mainArtist
self.date = Date()
def parseTrackGW(self, trackAPI_gw):
self.title = trackAPI_gw['SNG_TITLE'].strip()
if trackAPI_gw.get('VERSION') and not trackAPI_gw['VERSION'] in trackAPI_gw['SNG_TITLE']:
self.title += " " + trackAPI_gw['VERSION'].strip()
if trackAPI_gw.get('VERSION') and not trackAPI_gw['VERSION'].strip() in self.title:
self.title += f" {trackAPI_gw['VERSION'].strip()}"
self.discNumber = trackAPI_gw.get('DISK_NUMBER')
self.explicit = bool(int(trackAPI_gw.get('EXPLICIT_LYRICS', "0")))
@ -205,16 +204,17 @@ class Track:
self.lyrics = Lyrics(trackAPI_gw.get('LYRICS_ID', "0"))
self.mainArtist = Artist(
id = trackAPI_gw['ART_ID'],
art_id = trackAPI_gw['ART_ID'],
name = trackAPI_gw['ART_NAME'],
role = "Main",
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)
self.date.day = trackAPI_gw["PHYSICAL_RELEASE_DATE"][8:10]
self.date.month = trackAPI_gw["PHYSICAL_RELEASE_DATE"][5:7]
self.date.year = trackAPI_gw["PHYSICAL_RELEASE_DATE"][0:4]
self.date.fixDayMonth()
def parseTrack(self, trackAPI):
self.bpm = trackAPI['bpm']
@ -249,8 +249,8 @@ class Track:
return removeFeatures(self.title)
def getFeatTitle(self):
if self.featArtistsString and not "(feat." in self.title.lower():
return self.title + " ({})".format(self.featArtistsString)
if self.featArtistsString and "feat." not in self.title.lower():
return f"{self.title} ({self.featArtistsString})"
return self.title
def generateMainFeatStrings(self):
@ -259,9 +259,81 @@ class Track:
if 'Featured' in self.artist:
self.featArtistsString = "feat. "+andCommaConcat(self.artist['Featured'])
def applySettings(self, settings):
# Check if should save the playlist as a compilation
if self.playlist and settings['tags']['savePlaylistAsCompilation']:
self.trackNumber = self.position
self.discNumber = "1"
self.album.makePlaylistCompilation(self.playlist)
else:
if self.album.date: self.date = self.album.date
self.dateString = self.date.format(settings['dateFormat'])
self.album.dateString = self.album.date.format(settings['dateFormat'])
if self.playlist: self.playlist.dateString = self.playlist.date.format(settings['dateFormat'])
# Check various artist option
if settings['albumVariousArtists'] and self.album.variousArtists:
artist = self.album.variousArtists
isMainArtist = artist.role == "Main"
if artist.name not in self.album.artists:
self.album.artists.insert(0, artist.name)
if isMainArtist or artist.name not in self.album.artist['Main'] and not isMainArtist:
if artist.role not in self.album.artist:
self.album.artist[artist.role] = []
self.album.artist[artist.role].insert(0, artist.name)
self.album.mainArtist.save = not self.album.mainArtist.isVariousArtists() or settings['albumVariousArtists'] and self.album.mainArtist.isVariousArtists()
# Check removeDuplicateArtists
if settings['removeDuplicateArtists']: self.removeDuplicateArtists()
# Check if user wants the feat in the title
if str(settings['featuredToTitle']) == FeaturesOption.REMOVE_TITLE:
self.title = self.getCleanTitle()
elif str(settings['featuredToTitle']) == FeaturesOption.MOVE_TITLE:
self.title = self.getFeatTitle()
elif str(settings['featuredToTitle']) == FeaturesOption.REMOVE_TITLE_ALBUM:
self.title = self.getCleanTitle()
self.album.title = self.album.getCleanTitle()
# Remove (Album Version) from tracks that have that
if settings['removeAlbumVersion'] and "Album Version" in self.title:
self.title = re.sub(r' ?\(Album Version\)', "", self.title).strip()
# Change Title and Artists casing if needed
if settings['titleCasing'] != "nothing":
self.title = changeCase(self.title, settings['titleCasing'])
if settings['artistCasing'] != "nothing":
self.mainArtist.name = changeCase(self.mainArtist.name, settings['artistCasing'])
for i, artist in enumerate(self.artists):
self.artists[i] = changeCase(artist, settings['artistCasing'])
for art_type in self.artist:
for i, artist in enumerate(self.artist[art_type]):
self.artist[art_type][i] = changeCase(artist, settings['artistCasing'])
self.generateMainFeatStrings()
# Generate artist tag
if settings['tags']['multiArtistSeparator'] == "default":
if str(settings['featuredToTitle']) == FeaturesOption.MOVE_TITLE:
self.artistsString = ", ".join(self.artist['Main'])
else:
self.artistsString = ", ".join(self.artists)
elif settings['tags']['multiArtistSeparator'] == "andFeat":
self.artistsString = self.mainArtistsString
if self.featArtistsString and str(settings['featuredToTitle']) != FeaturesOption.MOVE_TITLE:
self.artistsString += " " + self.featArtistsString
else:
separator = settings['tags']['multiArtistSeparator']
if str(settings['featuredToTitle']) == FeaturesOption.MOVE_TITLE:
self.artistsString = separator.join(self.artist['Main'])
else:
self.artistsString = separator.join(self.artists)
class TrackError(Exception):
"""Base class for exceptions in this module."""
pass
class AlbumDoesntExists(TrackError):
pass

View File

@ -1,7 +1 @@
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
VARIOUS_ARTISTS = "5080"