Added tidal Settings and apply metadata functions

This commit is contained in:
Manuel Cortez 2020-07-08 13:12:33 -05:00
parent bb32d79dd7
commit c06df2092e
2 changed files with 38 additions and 11 deletions

View File

@ -1,4 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" Tidal service support for MusicDL.
this service allows users to choose between 3 different qualities. Low, high and lossless.
Lossless quality is only available to the account of tidal capable of playing such kind of audio files.
for low and high qualities, the file is transcoded to mp3 because Tidal gives us that as m4a.
"""
import logging import logging
import webbrowser import webbrowser
import wx import wx
@ -7,12 +12,12 @@ import config
from update.utils import seconds_to_string from update.utils import seconds_to_string
from .import base from .import base
log = logging.getLogger("extractors.tidal.com") log = logging.getLogger("services.tidal")
class interface(base.baseInterface): class interface(base.baseInterface):
name = "tidal" name = "tidal"
enabled = config.app["services"]["tidal"].get("enabled") enabled = config.app["services"]["tidal"].get("enabled")
# This should not be enabled if credentials are not in config. # This should not be enabled if credentials are not set in config.
if config.app["services"]["tidal"]["username"] == "" or config.app["services"]["tidal"]["password"] == "": if config.app["services"]["tidal"]["username"] == "" or config.app["services"]["tidal"]["password"] == "":
enabled = False enabled = False
@ -26,6 +31,7 @@ class interface(base.baseInterface):
quality = getattr(tidalapi.Quality, config.app["services"]["tidal"]["quality"]) quality = getattr(tidalapi.Quality, config.app["services"]["tidal"]["quality"])
else: else:
quality = tidalapi.Quality.high quality = tidalapi.Quality.high
# We need to instantiate a config object to pass quality settings.
_config = tidalapi.Config(quality=quality) _config = tidalapi.Config(quality=quality)
username = config.app["services"]["tidal"]["username"] username = config.app["services"]["tidal"]["username"]
password = config.app["services"]["tidal"]["password"] password = config.app["services"]["tidal"]["password"]
@ -34,6 +40,7 @@ class interface(base.baseInterface):
self.session.login(username=username, password=password) self.session.login(username=username, password=password)
def get_file_format(self): def get_file_format(self):
""" Returns the file format (mp3 or flac) depending in quality set. """
if config.app["services"]["tidal"]["quality"] == "lossless": if config.app["services"]["tidal"]["quality"] == "lossless":
self.file_extension = "flac" self.file_extension = "flac"
else: else:
@ -41,6 +48,8 @@ class interface(base.baseInterface):
return self.file_extension return self.file_extension
def transcoder_enabled(self): def transcoder_enabled(self):
# If quality is set to high, tidal returns audio in AAC format at 256 KBPS. So we convert it with vlc to mp3 at 320KBPS.
# toDo: Shall this be a setting and allow MusicDL to spit out the m4a file directly?
if config.app["services"]["tidal"]["quality"] == "lossless": if config.app["services"]["tidal"]["quality"] == "lossless":
return False return False
else: else:
@ -69,6 +78,7 @@ class interface(base.baseInterface):
for album in albums: for album in albums:
tracks = self.session.get_album_tracks(album.id) tracks = self.session.get_album_tracks(album.id)
for track in tracks: for track in tracks:
track.album = album
data.append(track) data.append(track)
if config.app["services"]["tidal"]["include_compilations"]: if config.app["services"]["tidal"]["include_compilations"]:
compilations = self.session.get_artist_albums_other(artist) compilations = self.session.get_artist_albums_other(artist)
@ -81,18 +91,21 @@ class interface(base.baseInterface):
for album in singles: for album in singles:
tracks = self.session.get_album_tracks(album.id) tracks = self.session.get_album_tracks(album.id)
for track in tracks: for track in tracks:
track.single = True # track.single = True
data.append(track) data.append(track)
for search_result in data: for search_result in data:
s = base.song(self) s = base.song(self)
if not hasattr(search_result, "single"): s.title = search_result.name
s.title = "{0}. {1}".format(self.format_number(search_result.track_num), search_result.name)
else:
s.title = search_result.name
s.artist = search_result.artist.name s.artist = search_result.artist.name
s.duration = seconds_to_string(search_result.duration) s.duration = seconds_to_string(search_result.duration)
s.url = search_result.id s.url = search_result.id
s.tracknumber = str(search_result.track_num)
s.album = search_result.album.name
print(search_result.album.num_tracks, search_result.album.name)
if search_result.album.num_tracks == None:
s.single = True
s.info = search_result s.info = search_result
print(s.info)
self.results.append(s) self.results.append(s)
log.debug("{0} results found.".format(len(self.results))) log.debug("{0} results found.".format(len(self.results)))
@ -109,7 +122,10 @@ class interface(base.baseInterface):
return url return url
def format_track(self, item): def format_track(self, item):
return "{title}. {artist}. {duration}".format(title=item.title, duration=item.duration, artist=item.artist) if not hasattr(item, "single"):
return "{0}. {1}".format(self.format_number(item.tracknumber), item.title)
else:
return "{title}. {artist}. {duration}".format(title=item.title, duration=item.duration, artist=item.artist)
class settings(base.baseSettings): class settings(base.baseSettings):
name = _("Tidal") name = _("Tidal")

View File

@ -52,7 +52,7 @@ class RepeatingTimer(threading.Thread):
except: except:
log.exception("Execution failed. Function: %r args: %r and kwargs: %r" % (self.function, self.args, self.kwargs)) log.exception("Execution failed. Function: %r args: %r and kwargs: %r" % (self.function, self.args, self.kwargs))
def download_file(url, local_filename): def download_file(url, local_filename, metadata=dict()):
log.debug("Download started: filename={0}, url={1}".format(local_filename, url)) log.debug("Download started: filename={0}, url={1}".format(local_filename, url))
r = requests.get(url, stream=True) r = requests.get(url, stream=True)
pub.sendMessage("change_status", status=_(u"Downloading {0}.").format(local_filename,)) pub.sendMessage("change_status", status=_(u"Downloading {0}.").format(local_filename,))
@ -61,7 +61,7 @@ def download_file(url, local_filename):
total_length = int(total_length) total_length = int(total_length)
log.debug("Downloading file of {0} bytes".format(total_length)) log.debug("Downloading file of {0} bytes".format(total_length))
with open(local_filename, 'wb') as f: with open(local_filename, 'wb') as f:
for chunk in r.iter_content(chunk_size=64): for chunk in r.iter_content(chunk_size=512*1024):
if chunk: # filter out keep-alive new chunks if chunk: # filter out keep-alive new chunks
dl += len(chunk) dl += len(chunk)
f.write(chunk) f.write(chunk)
@ -71,6 +71,7 @@ def download_file(url, local_filename):
pub.sendMessage("update-progress", value=done) pub.sendMessage("update-progress", value=done)
pub.sendMessage("download_finished", file=os.path.basename(local_filename)) pub.sendMessage("download_finished", file=os.path.basename(local_filename))
log.debug("Download finished successfully") log.debug("Download finished successfully")
apply_metadata(local_filename, metadata)
return local_filename return local_filename
def get_services(import_all=False): def get_services(import_all=False):
@ -85,3 +86,13 @@ def get_services(import_all=False):
else: else:
classes = [m for m in services.__dict__.values() if type(m) == module_type and hasattr(m, 'interface')] classes = [m for m in services.__dict__.values() if type(m) == module_type and hasattr(m, 'interface')]
return classes return classes
def apply_metadata(local_filename, metadata):
if local_filename.endswith(".mp3"):
from mutagen.easyid3 import EasyID3 as metadataeditor
elif local_filename.endswith(".flac"):
from mutagen.flac import FLAC as metadataeditor
audio = metadataeditor(local_filename)
for k in metadata.keys():
audio[k] = metadata[k]
audio.save()