Added tidal Settings and apply metadata functions
This commit is contained in:
parent
bb32d79dd7
commit
c06df2092e
@ -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 = "{0}. {1}".format(self.format_number(search_result.track_num), search_result.name)
|
|
||||||
else:
|
|
||||||
s.title = search_result.name
|
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,6 +122,9 @@ class interface(base.baseInterface):
|
|||||||
return url
|
return url
|
||||||
|
|
||||||
def format_track(self, item):
|
def format_track(self, item):
|
||||||
|
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)
|
return "{title}. {artist}. {duration}".format(title=item.title, duration=item.duration, artist=item.artist)
|
||||||
|
|
||||||
class settings(base.baseSettings):
|
class settings(base.baseSettings):
|
||||||
|
15
src/utils.py
15
src/utils.py
@ -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()
|
Loading…
Reference in New Issue
Block a user