summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Honz <exislow+github@gmail.com>2024-04-21 15:58:31 +0200
committerRobert Honz <exislow+github@gmail.com>2024-04-21 15:58:31 +0200
commitf9862e0646032628923dc696afb0fe04884eddfb (patch)
tree266a029363b050998eb2227d59cdde68f3db80f7
parent1224e42689e27fb75b5a4a742ac37f66e1c6eb07 (diff)
downloadtidal-dl-ng-f9862e0646032628923dc696afb0fe04884eddfb.tar.gz
✨ PKCE / OAuth login will be chosen on the fly, depending on the desired audio quality of the track to speed up non `HIRES_LOSSLESS` downloads. Quality for tracks in download queue can be chosen individually. Fixes #73
-rw-r--r--tidal_dl_ng/config.py10
-rw-r--r--tidal_dl_ng/download.py31
-rw-r--r--tidal_dl_ng/gui.py23
-rw-r--r--tidal_dl_ng/helper/gui.py15
-rw-r--r--tidal_dl_ng/helper/tidal.py6
-rw-r--r--tidal_dl_ng/model/gui_data.py4
6 files changed, 73 insertions, 16 deletions
diff --git a/tidal_dl_ng/config.py b/tidal_dl_ng/config.py
index 7258c8f..7b86f1c 100644
--- a/tidal_dl_ng/config.py
+++ b/tidal_dl_ng/config.py
@@ -86,6 +86,7 @@ class Tidal(BaseConfig, metaclass=SingletonMeta):
session: tidalapi.Session
token_from_storage: bool = False
settings: Settings
+ is_pkce: bool
def __init__(self, settings: Settings = None):
self.cls_model = ModelToken
@@ -110,9 +111,13 @@ class Tidal(BaseConfig, metaclass=SingletonMeta):
return True
- def login_token(self) -> bool:
+ def login_token(self, do_pkce: bool = True) -> bool:
result = False
+ # Do not login via PKCE, if HiRes LOSSLESS is not set as global audio quality.
+ if self.session.audio_quality != tidalapi.media.Quality.hi_res_lossless:
+ do_pkce = False
+
if self.token_from_storage:
try:
result = self.session.load_oauth_session(
@@ -120,8 +125,9 @@ class Tidal(BaseConfig, metaclass=SingletonMeta):
self.data.access_token,
self.data.refresh_token,
self.data.expiry_time,
- is_pkce=True,
+ is_pkce=do_pkce,
)
+ self.is_pkce = do_pkce
except HTTPError:
result = False
diff --git a/tidal_dl_ng/download.py b/tidal_dl_ng/download.py
index be7fb13..da3d3a0 100644
--- a/tidal_dl_ng/download.py
+++ b/tidal_dl_ng/download.py
@@ -12,9 +12,9 @@ import requests
from requests.exceptions import HTTPError
from rich.progress import Progress, TaskID
from tidalapi import Album, Mix, Playlist, Session, Track, UserPlaylist, Video
-from tidalapi.media import AudioExtensions, StreamManifest, VideoExtensions
+from tidalapi.media import AudioExtensions, Quality, StreamManifest, VideoExtensions
-from tidal_dl_ng.config import Settings
+from tidal_dl_ng.config import Settings, Tidal
from tidal_dl_ng.constants import EXTENSION_LYRICS, REQUESTS_TIMEOUT_SEC, MediaType, SkipExisting
from tidal_dl_ng.helper.decryption import decrypt_file, decrypt_security_token
from tidal_dl_ng.helper.exceptions import MediaMissing
@@ -174,6 +174,7 @@ class Download:
media_type: MediaType = None,
video_download: bool = True,
download_delay: bool = False,
+ quality: Quality | None = None,
) -> (bool, str):
try:
if media_id and media_type:
@@ -202,6 +203,10 @@ class Download:
return False, ""
+ # If a quality is explicitly set, change it.
+ if quality:
+ quality_old: Quality = self.quality_adjust(quality)
+
# Get extension.
file_extension: str
@@ -251,6 +256,10 @@ class Download:
status_download: bool = not file_exists
+ if quality:
+ # Set quality back to the global user value
+ self.quality_adjust(quality_old)
+
# Whether a file was downloaded or skipped and the download delay is enabled, wait until the next download.
# Only use this, if you have a list of several Track items.
if download_delay:
@@ -261,6 +270,20 @@ class Download:
return status_download, path_file
+ def quality_adjust(self, quality) -> Quality:
+ # Save original quality settings
+ quality_old: Quality = self.session.audio_quality
+ self.session.audio_quality = quality
+ tidal: Tidal = Tidal()
+
+ # If track is not requested as hires_lossless do not use PKCE, because BTS downloads are faster than MPD.
+ if quality == Quality.hi_res_lossless and not tidal.is_pkce:
+ tidal.login_token(do_pkce=True)
+ elif quality != Quality.hi_res_lossless and tidal.is_pkce:
+ tidal.login_token(do_pkce=False)
+
+ return quality_old
+
def _move_lyrics(self, file_media_dst: str, file_media_src: str):
# Build tmp lyrics filename
tmp_lyrics_file_path: str = file_media_src + EXTENSION_LYRICS
@@ -340,6 +363,7 @@ class Download:
media_type: MediaType = None,
video_download: bool = False,
download_delay: bool = True,
+ quality: Quality | None = None,
):
# If no media instance is provided, we need to create the media instance.
if media_id and media_type:
@@ -381,8 +405,7 @@ class Download:
for media in items:
# Download the item.
status_download, result_path_file = self.item(
- media=media,
- file_template=file_name_relative,
+ media=media, file_template=file_name_relative, quality=quality
)
# Advance progress bar.
diff --git a/tidal_dl_ng/gui.py b/tidal_dl_ng/gui.py
index dd500fc..903e8e8 100644
--- a/tidal_dl_ng/gui.py
+++ b/tidal_dl_ng/gui.py
@@ -9,6 +9,7 @@ from tidal_dl_ng import __version__, update_available
from tidal_dl_ng.dialog import DialogLogin, DialogPreferences, DialogVersion
from tidal_dl_ng.helper.gui import (
get_queue_download_media,
+ get_queue_download_quality,
get_results_media_item,
get_user_list_media_item,
set_queue_download_media,
@@ -542,7 +543,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
) -> QueueDownloadItem | bool:
result: QueueDownloadItem | False
name: str = ""
- quality: str = ""
+ quality: Quality
explicit: str = ""
# Check if item is available on TIDAL.
@@ -798,10 +799,11 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
result: QueueDownloadStatus
item: QtWidgets.QTreeWidgetItem = items[0]
media: Track | Album | Playlist | Video | Mix | Artist = get_queue_download_media(item)
+ quality: Quality = get_queue_download_quality(item)
try:
self.s_queue_download_item_downloading.emit(item)
- result = self.on_queue_download(media)
+ result = self.on_queue_download(media, quality=quality)
if result == QueueDownloadStatus.Finished:
self.s_queue_download_item_finished.emit(item)
@@ -827,7 +829,9 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def queue_download_item_status(self, item: QtWidgets.QTreeWidgetItem, status: str) -> None:
item.setText(0, status)
- def on_queue_download(self, media: Track | Album | Playlist | Video | Mix | Artist) -> QueueDownloadStatus:
+ def on_queue_download(
+ self, media: Track | Album | Playlist | Video | Mix | Artist, quality: Quality | None = None
+ ) -> QueueDownloadStatus:
result: QueueDownloadStatus
items_media: [Track | Album | Playlist | Video | Mix | Artist]
@@ -839,12 +843,16 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
download_delay: bool = bool(isinstance(media, Track | Video) and self.settings.data.download_delay)
for item_media in items_media:
- result = self.download(item_media, self.dl, delay_track=download_delay)
+ result = self.download(item_media, self.dl, delay_track=download_delay, quality=quality)
return result
def download(
- self, media: Track | Album | Playlist | Video | Mix | Artist, dl: Download, delay_track: bool = False
+ self,
+ media: Track | Album | Playlist | Video | Mix | Artist,
+ dl: Download,
+ delay_track: bool = False,
+ quality: Quality | None = None,
) -> QueueDownloadStatus:
result_dl: bool
path_file: str
@@ -855,13 +863,16 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
file_template = get_format_template(media, self.settings)
if isinstance(media, Track | Video):
- result_dl, path_file = dl.item(media=media, file_template=file_template, download_delay=delay_track)
+ result_dl, path_file = dl.item(
+ media=media, file_template=file_template, download_delay=delay_track, quality=quality
+ )
elif isinstance(media, Album | Playlist | Mix):
dl.items(
media=media,
file_template=file_template,
video_download=self.settings.data.video_download,
download_delay=self.settings.data.download_delay,
+ quality=quality,
)
# Dummy values
diff --git a/tidal_dl_ng/helper/gui.py b/tidal_dl_ng/helper/gui.py
index b18c24e..610be80 100644
--- a/tidal_dl_ng/helper/gui.py
+++ b/tidal_dl_ng/helper/gui.py
@@ -1,6 +1,7 @@
from PySide6 import QtCore, QtWidgets
from tidalapi import Album, Mix, Playlist, Track, UserPlaylist, Video
from tidalapi.artist import Artist
+from tidalapi.media import Quality
def get_table_data(
@@ -11,6 +12,12 @@ def get_table_data(
return result
+def get_table_text(item: QtWidgets.QTreeWidgetItem, column: int) -> str:
+ result: str = item.text(column)
+
+ return result
+
+
def get_results_media_item(item: QtWidgets.QTreeWidgetItem) -> Track | Video | Album | Artist | Playlist | Mix:
result: Track | Video | Album | Artist | Playlist | Mix = get_table_data(item, 1)
@@ -31,6 +38,14 @@ def get_queue_download_media(
return result
+def get_queue_download_quality(
+ item: QtWidgets.QTreeWidgetItem,
+) -> Quality:
+ result: Quality = get_table_text(item, 4)
+
+ return result
+
+
def set_table_data(
item: QtWidgets.QTreeWidgetItem, data: Track | Video | Album | Artist | Mix | Playlist | UserPlaylist, column: int
):
diff --git a/tidal_dl_ng/helper/tidal.py b/tidal_dl_ng/helper/tidal.py
index 052f721..dbc31aa 100644
--- a/tidal_dl_ng/helper/tidal.py
+++ b/tidal_dl_ng/helper/tidal.py
@@ -154,13 +154,13 @@ def instantiate_media(
return media
-def quality_audio_highest(media: Track | Album) -> str:
- quality: str
+def quality_audio_highest(media: Track | Album) -> Quality:
+ quality: Quality
if MediaMetadataTags.hires_lossless in media.media_metadata_tags:
quality = Quality.hi_res_lossless
elif MediaMetadataTags.mqa in media.media_metadata_tags:
- quality = f"{Quality.hi_res}"
+ quality = Quality.hi_res
elif MediaMetadataTags.lossless in media.media_metadata_tags:
quality = Quality.high_lossless
else:
diff --git a/tidal_dl_ng/model/gui_data.py b/tidal_dl_ng/model/gui_data.py
index 56b5e2b..8c7c30e 100644
--- a/tidal_dl_ng/model/gui_data.py
+++ b/tidal_dl_ng/model/gui_data.py
@@ -1,5 +1,7 @@
from dataclasses import dataclass
+from tidalapi.media import Quality
+
try:
from PySide6 import QtCore
@@ -39,5 +41,5 @@ class QueueDownloadItem:
status: str
name: str
type_media: str
- quality: str
+ quality: Quality
obj: object