✨ Backend and GUI changed to device login. Login dialog needs to be threaded / non blocking.

This commit is contained in:
Robert Honz
2024-11-12 19:15:33 +01:00
parent 4cef07b08f
commit 618b16257b
5 changed files with 104 additions and 59 deletions
+3 -3
View File
@@ -168,9 +168,9 @@ class Tidal(BaseConfig, metaclass=SingletonMeta):
fn_print("You either do not have a token or your token is invalid.") fn_print("You either do not have a token or your token is invalid.")
fn_print("No worries, we will handle this...") fn_print("No worries, we will handle this...")
# Login method: Device linking # Login method: Device linking
# self.session.login_oauth_simple(fn_print) self.session.login_oauth_simple(fn_print)
# Login method: PKCE authorization (enables HiRes streaming) # Login method: PKCE authorization (was necessary for HI_RES_LOSSLESS streaming earlier)
self.session.login_pkce(fn_print) # self.session.login_pkce(fn_print)
is_login = self.login_finalize() is_login = self.login_finalize()
+7 -5
View File
@@ -1,6 +1,7 @@
import os.path import os.path
import shutil import shutil
import webbrowser import webbrowser
import datetime
from enum import Enum, StrEnum from enum import Enum, StrEnum
from pathlib import Path from pathlib import Path
@@ -83,24 +84,25 @@ class DialogLogin(QtWidgets.QDialog):
ui: Ui_DialogLogin ui: Ui_DialogLogin
url_redirect: str url_redirect: str
return_code: int
def __init__(self, url_login: str, hint: str, parent=None): def __init__(self, url_login: str, hint: str, expires_in: int, parent=None):
super().__init__(parent) super().__init__(parent)
datetime_current: datetime.datetime = datetime.datetime.now()
datetime_expires: datetime.datetime = datetime_current + datetime.timedelta(0, expires_in)
# Create an instance of the GUI # Create an instance of the GUI
self.ui = Ui_DialogLogin() self.ui = Ui_DialogLogin()
# Run the .setupUi() method to show the GUI # Run the .setupUi() method to show the GUI
self.ui.setupUi(self) self.ui.setupUi(self)
# Set data. # Set data.
self.ui.tb_url_login.setText(f'<a href="{url_login}">{url_login}</a>') self.ui.tb_url_login.setText(f'<a href="https://{url_login}">https://{url_login}</a>')
self.ui.l_hint.setText(hint) self.ui.l_hint.setText(hint)
self.ui.l_expires_date_time.setText(datetime_expires.strftime("%Y-%m-%d %H:%M"))
# Show # Show
self.return_code = self.exec() self.return_code = self.exec()
self.url_redirect = self.ui.te_url_redirect.toPlainText()
class DialogPreferences(QtWidgets.QDialog): class DialogPreferences(QtWidgets.QDialog):
"""Preferences dialog.""" """Preferences dialog."""
+6 -4
View File
@@ -128,14 +128,16 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
result = self.tidal.login_token() result = self.tidal.login_token()
if not result: if not result:
hint: str = "Watiting for user input." hint: str = "After you have finished the TIDAL login via web browser click the 'OK' button."
while not result: while not result:
url_login = self.tidal.session.pkce_login_url() login, future = self.tidal.session.login_oauth()
d_login: DialogLogin = DialogLogin(url_login=url_login, hint=hint, parent=self)
url_redirect: str = d_login.url_redirect d_login: DialogLogin = DialogLogin(url_login=login.verification_uri_complete, hint=hint, expires_in=login.expires_in, parent=self)
if d_login.return_code == 1: if d_login.return_code == 1:
try: try:
future.result()
token: dict[str, str | int] = self.tidal.session.pkce_get_auth_token(url_redirect) token: dict[str, str | int] = self.tidal.session.pkce_get_auth_token(url_redirect)
self.tidal.session.process_auth_token(token) self.tidal.session.process_auth_token(token)
self.tidal.login_finalize() self.tidal.login_finalize()
+54 -38
View File
@@ -1,42 +1,51 @@
# -*- coding: utf-8 -*-
################################################################################ ################################################################################
## Form generated from reading UI file 'dialog_login.ui' ## Form generated from reading UI file 'dialog_login.ui'
## ##
## Created by: Qt User Interface Compiler version 6.6.1 ## Created by: Qt User Interface Compiler version 6.8.0
## ##
## WARNING! All changes made in this file will be lost when recompiling UI file! ## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################ ################################################################################
from PySide6.QtCore import QCoreApplication, QMetaObject, QRect, Qt from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
from PySide6.QtGui import QFont QMetaObject, QObject, QPoint, QRect,
from PySide6.QtWidgets import QDialogButtonBox, QLabel, QSizePolicy, QTextBrowser, QTextEdit, QVBoxLayout, QWidget QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractButton, QApplication, QDialog, QDialogButtonBox,
QHBoxLayout, QLabel, QSizePolicy, QTextBrowser,
QVBoxLayout, QWidget)
class Ui_DialogLogin(object):
class Ui_DialogLogin:
def setupUi(self, DialogLogin): def setupUi(self, DialogLogin):
if not DialogLogin.objectName(): if not DialogLogin.objectName():
DialogLogin.setObjectName("DialogLogin") DialogLogin.setObjectName(u"DialogLogin")
DialogLogin.resize(451, 400) DialogLogin.resize(451, 400)
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(DialogLogin.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(DialogLogin.sizePolicy().hasHeightForWidth())
DialogLogin.setSizePolicy(sizePolicy) DialogLogin.setSizePolicy(sizePolicy)
self.bb_dialog = QDialogButtonBox(DialogLogin) self.bb_dialog = QDialogButtonBox(DialogLogin)
self.bb_dialog.setObjectName("bb_dialog") self.bb_dialog.setObjectName(u"bb_dialog")
self.bb_dialog.setGeometry(QRect(20, 350, 411, 32)) self.bb_dialog.setGeometry(QRect(20, 350, 411, 32))
sizePolicy.setHeightForWidth(self.bb_dialog.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.bb_dialog.sizePolicy().hasHeightForWidth())
self.bb_dialog.setSizePolicy(sizePolicy) self.bb_dialog.setSizePolicy(sizePolicy)
self.bb_dialog.setStyleSheet("") self.bb_dialog.setLayoutDirection(Qt.LayoutDirection.LeftToRight)
self.bb_dialog.setOrientation(Qt.Horizontal) self.bb_dialog.setStyleSheet(u"")
self.bb_dialog.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self.bb_dialog.setOrientation(Qt.Orientation.Horizontal)
self.bb_dialog.setStandardButtons(QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.Ok)
self.verticalLayoutWidget = QWidget(DialogLogin) self.verticalLayoutWidget = QWidget(DialogLogin)
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") self.verticalLayoutWidget.setObjectName(u"verticalLayoutWidget")
self.verticalLayoutWidget.setGeometry(QRect(20, 20, 411, 325)) self.verticalLayoutWidget.setGeometry(QRect(20, 20, 411, 325))
self.lv_main = QVBoxLayout(self.verticalLayoutWidget) self.lv_main = QVBoxLayout(self.verticalLayoutWidget)
self.lv_main.setObjectName("lv_main") self.lv_main.setObjectName(u"lv_main")
self.lv_main.setContentsMargins(0, 0, 0, 0) self.lv_main.setContentsMargins(0, 0, 0, 0)
self.l_header = QLabel(self.verticalLayoutWidget) self.l_header = QLabel(self.verticalLayoutWidget)
self.l_header.setObjectName("l_header") self.l_header.setObjectName(u"l_header")
sizePolicy.setHeightForWidth(self.l_header.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.l_header.sizePolicy().hasHeightForWidth())
self.l_header.setSizePolicy(sizePolicy) self.l_header.setSizePolicy(sizePolicy)
font = QFont() font = QFont()
@@ -47,7 +56,7 @@ class Ui_DialogLogin:
self.lv_main.addWidget(self.l_header) self.lv_main.addWidget(self.l_header)
self.l_description = QLabel(self.verticalLayoutWidget) self.l_description = QLabel(self.verticalLayoutWidget)
self.l_description.setObjectName("l_description") self.l_description.setObjectName(u"l_description")
sizePolicy.setHeightForWidth(self.l_description.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.l_description.sizePolicy().hasHeightForWidth())
self.l_description.setSizePolicy(sizePolicy) self.l_description.setSizePolicy(sizePolicy)
font1 = QFont() font1 = QFont()
@@ -58,47 +67,54 @@ class Ui_DialogLogin:
self.lv_main.addWidget(self.l_description) self.lv_main.addWidget(self.l_description)
self.tb_url_login = QTextBrowser(self.verticalLayoutWidget) self.tb_url_login = QTextBrowser(self.verticalLayoutWidget)
self.tb_url_login.setObjectName("tb_url_login") self.tb_url_login.setObjectName(u"tb_url_login")
self.tb_url_login.setOpenExternalLinks(True) self.tb_url_login.setOpenExternalLinks(True)
self.lv_main.addWidget(self.tb_url_login) self.lv_main.addWidget(self.tb_url_login)
self.te_url_redirect = QTextEdit(self.verticalLayoutWidget) self.horizontalLayout = QHBoxLayout()
self.te_url_redirect.setObjectName("te_url_redirect") self.horizontalLayout.setObjectName(u"horizontalLayout")
self.l_expires_description = QLabel(self.verticalLayoutWidget)
self.l_expires_description.setObjectName(u"l_expires_description")
self.lv_main.addWidget(self.te_url_redirect) self.horizontalLayout.addWidget(self.l_expires_description)
self.l_expires_date_time = QLabel(self.verticalLayoutWidget)
self.l_expires_date_time.setObjectName(u"l_expires_date_time")
font2 = QFont()
font2.setBold(True)
self.l_expires_date_time.setFont(font2)
self.l_expires_date_time.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter)
self.horizontalLayout.addWidget(self.l_expires_date_time)
self.lv_main.addLayout(self.horizontalLayout)
self.l_hint = QLabel(self.verticalLayoutWidget) self.l_hint = QLabel(self.verticalLayoutWidget)
self.l_hint.setObjectName("l_hint") self.l_hint.setObjectName(u"l_hint")
sizePolicy.setHeightForWidth(self.l_hint.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.l_hint.sizePolicy().hasHeightForWidth())
self.l_hint.setSizePolicy(sizePolicy) self.l_hint.setSizePolicy(sizePolicy)
self.l_hint.setFont(font1) self.l_hint.setFont(font1)
self.l_hint.setWordWrap(True)
self.lv_main.addWidget(self.l_hint) self.lv_main.addWidget(self.l_hint)
self.retranslateUi(DialogLogin) self.retranslateUi(DialogLogin)
self.bb_dialog.accepted.connect(DialogLogin.accept) self.bb_dialog.accepted.connect(DialogLogin.accept)
self.bb_dialog.rejected.connect(DialogLogin.reject) self.bb_dialog.rejected.connect(DialogLogin.reject)
QMetaObject.connectSlotsByName(DialogLogin) QMetaObject.connectSlotsByName(DialogLogin)
# setupUi # setupUi
def retranslateUi(self, DialogLogin): def retranslateUi(self, DialogLogin):
DialogLogin.setWindowTitle(QCoreApplication.translate("DialogLogin", "Dialog", None)) DialogLogin.setWindowTitle(QCoreApplication.translate("DialogLogin", u"Dialog", None))
self.l_header.setText(QCoreApplication.translate("DialogLogin", "TIDAL PKCE Login", None)) self.l_header.setText(QCoreApplication.translate("DialogLogin", u"TIDAL Login (as Device)", None))
self.l_description.setText( self.l_description.setText(QCoreApplication.translate("DialogLogin", u"Click the link below and login with your TIDAL credentials. TIDAL will ask you, if you like to add this app as a new device. You need to confirm this.", None))
QCoreApplication.translate( self.tb_url_login.setPlaceholderText(QCoreApplication.translate("DialogLogin", u"Copy this login URL...", None))
"DialogLogin", self.l_expires_description.setText(QCoreApplication.translate("DialogLogin", u"This link expires at:", None))
'Click the link below and login with your TIDAL credentials. You will be redirected to an "Ooops" page' self.l_expires_date_time.setText(QCoreApplication.translate("DialogLogin", u"COMPUTING", None))
' after a successful login. Copy the URL of this "Ooops" page and post it in the box below.', self.l_hint.setText(QCoreApplication.translate("DialogLogin", u"Waiting...", None))
None,
)
)
self.tb_url_login.setPlaceholderText(QCoreApplication.translate("DialogLogin", "Copy this login URL...", None))
self.te_url_redirect.setPlaceholderText(
QCoreApplication.translate("DialogLogin", "Copy and paste the redirect URL after login here...", None)
)
self.l_hint.setText(QCoreApplication.translate("DialogLogin", "Waiting...", None))
# retranslateUi # retranslateUi
+34 -9
View File
@@ -34,14 +34,17 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="layoutDirection">
<enum>Qt::LayoutDirection::LeftToRight</enum>
</property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true"/> <string notr="true"/>
</property> </property>
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Orientation::Horizontal</enum>
</property> </property>
<property name="standardButtons"> <property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property> </property>
</widget> </widget>
<widget class="QWidget" name="verticalLayoutWidget"> <widget class="QWidget" name="verticalLayoutWidget">
@@ -69,7 +72,7 @@
</font> </font>
</property> </property>
<property name="text"> <property name="text">
<string>TIDAL PKCE Login</string> <string>TIDAL Login (as Device)</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -87,7 +90,7 @@
</font> </font>
</property> </property>
<property name="text"> <property name="text">
<string>Click the link below and login with your TIDAL credentials. You will be redirected to an &quot;Ooops&quot; page after a successful login. Copy the URL of this &quot;Ooops&quot; page and post it in the box below.</string> <string>Click the link below and login with your TIDAL credentials. TIDAL will ask you, if you like to add this app as a new device. You need to confirm this.</string>
</property> </property>
<property name="wordWrap"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>
@@ -105,11 +108,30 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QTextEdit" name="te_url_redirect"> <layout class="QHBoxLayout" name="horizontalLayout">
<property name="placeholderText"> <item>
<string>Copy and paste the redirect URL after login here...</string> <widget class="QLabel" name="l_expires_description">
</property> <property name="text">
</widget> <string>This link expires at:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="l_expires_date_time">
<property name="font">
<font>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>COMPUTING</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item> </item>
<item> <item>
<widget class="QLabel" name="l_hint"> <widget class="QLabel" name="l_hint">
@@ -127,6 +149,9 @@
<property name="text"> <property name="text">
<string>Waiting...</string> <string>Waiting...</string>
</property> </property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>