deemix-py/deemix/types/Track.py

323 lines
13 KiB
Python

import re
from datetime import datetime
from deezer.utils import map_track, map_album
from deezer.errors import APIError, GWAPIError
from deemix.errors import NoDataToParse, AlbumDoesntExists
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.types import VARIOUS_ARTISTS
from deemix.settings import FeaturesOption
class Track:
def __init__(self, sng_id="0", name=""):
self.id = sng_id
self.title = name
self.MD5 = ""
self.mediaVersion = ""
self.trackToken = ""
self.trackTokenExpiration = 0
self.duration = 0
self.fallbackID = "0"
self.albumsFallback = []
self.filesizes = {}
self.local = False
self.mainArtist = None
self.artist = {"Main": []}
self.artists = []
self.album = None
self.trackNumber = "0"
self.discNumber = "0"
self.date = Date()
self.lyrics = None
self.bpm = 0
self.contributors = {}
self.copyright = ""
self.explicit = False
self.ISRC = ""
self.replayGain = ""
self.rank = 0
self.playlist = None
self.position = None
self.searched = False
self.selectedFormat = 0
self.singleDownload = False
self.dateString = ""
self.artistsString = ""
self.mainArtistsString = ""
self.featArtistsString = ""
self.urls = {}
def parseEssentialData(self, trackAPI):
self.id = str(trackAPI['id'])
self.duration = trackAPI['duration']
self.trackToken = trackAPI['track_token']
self.trackTokenExpiration = trackAPI['track_token_expire']
self.MD5 = trackAPI.get('md5_origin')
self.mediaVersion = trackAPI['media_version']
self.filesizes = trackAPI['filesizes']
self.fallbackID = "0"
if 'fallback_id' in trackAPI:
self.fallbackID = trackAPI['fallback_id']
self.local = int(self.id) < 0
self.urls = {}
def parseData(self, dz, track_id=None, trackAPI=None, albumAPI=None, playlistAPI=None):
if track_id and (not trackAPI or trackAPI and not trackAPI.get('track_token')):
trackAPI_new = dz.gw.get_track_with_fallback(track_id)
trackAPI_new = map_track(trackAPI_new)
if not trackAPI: trackAPI = {}
trackAPI_new.update(trackAPI)
trackAPI = trackAPI_new
elif not trackAPI: raise NoDataToParse
self.parseEssentialData(trackAPI)
# only public api has bpm
if not trackAPI.get('bpm') and not self.local:
try:
trackAPI_new = dz.api.get_track(trackAPI['id'])
trackAPI_new['release_date'] = trackAPI['release_date']
trackAPI.update(trackAPI_new)
except APIError: pass
if self.local:
self.parseLocalTrackData(trackAPI)
else:
self.parseTrack(trackAPI)
# Get Lyrics data
if not trackAPI.get("lyrics") and self.lyrics.id != "0":
try: trackAPI["lyrics"] = dz.gw.get_track_lyrics(self.id)
except GWAPIError: self.lyrics.id = "0"
if self.lyrics.id != "0": self.lyrics.parseLyrics(trackAPI["lyrics"])
# Parse Album Data
self.album = Album(
alb_id = trackAPI['album']['id'],
title = trackAPI['album']['title'],
pic_md5 = trackAPI['album'].get('md5_origin')
)
# Get album Data
if not albumAPI:
try: albumAPI = dz.api.get_album(self.album.id)
except APIError: albumAPI = None
# Get album_gw Data
# Only gw has disk number
if not albumAPI or albumAPI and not albumAPI.get('nb_disk'):
try:
albumAPI_gw = dz.gw.get_album(self.album.id)
albumAPI_gw = map_album(albumAPI_gw)
except GWAPIError: albumAPI_gw = {}
if not albumAPI: albumAPI = {}
albumAPI_gw.update(albumAPI)
albumAPI = albumAPI_gw
if not albumAPI: raise AlbumDoesntExists
self.album.parseAlbum(albumAPI)
# 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
if not self.album.mainArtist.pic.md5 or self.album.mainArtist.pic.md5 == "":
artistAPI = dz.api.get_artist(self.album.mainArtist.id)
self.album.mainArtist.pic.md5 = artistAPI['picture_small'][artistAPI['picture_small'].find('artist/') + 7:-24]
# Fill missing data
if self.album.date and not self.date: self.date = self.album.date
if 'genres' in trackAPI:
for genre in trackAPI['genres']:
if genre not in self.album.genre: self.album.genre.append(genre)
# Remove unwanted charaters in track name
# Example: track/127793
self.title = ' '.join(self.title.split())
# Make sure there is at least one artist
if len(self.artist['Main']) == 0:
self.artist['Main'] = [self.mainArtist.name]
self.position = trackAPI.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):
# Local tracks has only the trackAPI_gw page and
# contains only the tags provided by the file
self.title = trackAPI['title']
self.album = Album(title=trackAPI['album']['title'])
self.album.pic = Picture(
md5 = trackAPI.get('md5_image', ""),
pic_type = "cover"
)
self.mainArtist = Artist(name=trackAPI['artist']['name'], role="Main")
self.artists = [trackAPI['artist']['name']]
self.artist = {
'Main': [trackAPI['artist']['name']]
}
self.album.artist = self.artist
self.album.artists = self.artists
self.album.date = self.date
self.album.mainArtist = self.mainArtist
def parseTrack(self, trackAPI):
self.title = trackAPI['title']
self.discNumber = trackAPI.get('disk_number')
self.explicit = trackAPI.get('explicit_lyrics', False)
self.copyright = trackAPI.get('copyright')
if 'gain' in trackAPI: self.replayGain = generateReplayGainString(trackAPI['gain'])
self.ISRC = trackAPI.get('isrc')
self.trackNumber = trackAPI['track_position']
self.contributors = trackAPI.get('song_contributors')
self.rank = trackAPI['rank']
self.bpm = trackAPI['bpm']
self.lyrics = Lyrics(trackAPI.get('lyrics_id', "0"))
self.mainArtist = Artist(
art_id = trackAPI['artist']['id'],
name = trackAPI['artist']['name'],
role = "Main",
pic_md5 = trackAPI['artist'].get('md5_image')
)
if 'physical_release_date' in trackAPI:
self.date.day = trackAPI["physical_release_date"][8:10]
self.date.month = trackAPI["physical_release_date"][5:7]
self.date.year = trackAPI["physical_release_date"][0:4]
self.date.fixDayMonth()
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'])
if 'alternative_albums' in trackAPI and trackAPI['alternative_albums']:
for album in trackAPI['alternative_albums']['data']:
if 'RIGHTS' in album and album['RIGHTS'].get('STREAM_ADS_AVAILABLE') or album['RIGHTS'].get('STREAM_SUB_AVAILABLE'):
self.albumsFallback.append(album['ALB_ID'])
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 "feat." not in self.title.lower():
return f"{self.title} ({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 checkAndRenewTrackToken(self, dz):
now = datetime.now()
expiration = datetime.fromtimestamp(self.trackTokenExpiration)
if now > expiration:
newTrack = dz.gw.get_track_with_fallback(self.id)
self.trackToken = newTrack['TRACK_TOKEN']
self.trackTokenExpiration = newTrack['TRACK_TOKEN_EXPIRE']
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)