From c56dd59a0b97d7469b961a94bc18bb88c1cf790b Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 16 Nov 2024 13:13:33 +0100 Subject: [PATCH 01/32] Remove unused link library --- lib/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 07a4b0f4..201500d3 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -28,7 +28,6 @@ target_include_directories(spotify-qt-lib PUBLIC # Third-party libraries add_subdirectory(thirdparty) -target_link_libraries(spotify-qt-lib PUBLIC spotify-qt-lib-third-party) # Version macros target_compile_definitions(spotify-qt-lib PUBLIC LIB_VERSION="v${PROJECT_VERSION}") From bbe737bb738de95b5f0e3ecef679d9e165ba07f4 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 16 Nov 2024 13:19:03 +0100 Subject: [PATCH 02/32] Replace autoplay check with oauth check --- src/enum/autoplaysupport.hpp | 19 ------------------- src/spotifyclient/helper.cpp | 16 +++------------- src/spotifyclient/helper.hpp | 5 ++--- 3 files changed, 5 insertions(+), 35 deletions(-) delete mode 100644 src/enum/autoplaysupport.hpp diff --git a/src/enum/autoplaysupport.hpp b/src/enum/autoplaysupport.hpp deleted file mode 100644 index 382aec92..00000000 --- a/src/enum/autoplaysupport.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -enum class AutoplaySupport: short -{ - /** - * No support for overriding autoplay - */ - None, - - /** - * Overriden by flag, (--autoplay) - */ - Flag, - - /** - * Overriden by option (--autoplay true) - */ - Option, -}; diff --git a/src/spotifyclient/helper.cpp b/src/spotifyclient/helper.cpp index 1f29ed9e..a4c1fb9f 100644 --- a/src/spotifyclient/helper.cpp +++ b/src/spotifyclient/helper.cpp @@ -143,26 +143,16 @@ auto SpotifyClient::Helper::running(const QString &path) -> bool return QString(out).contains(path); } -auto SpotifyClient::Helper::getAutoplaySupport(const QString &path) -> AutoplaySupport +auto SpotifyClient::Helper::getOAuthSupport(const QString &path) -> bool { if (clientType(path) != lib::client_type::librespot) { - return AutoplaySupport::None; + return false; } const auto help = clientExec(path, { QStringLiteral("--help"), }); - if (help.contains(QStringLiteral("--autoplay OVERRIDE"))) - { - return AutoplaySupport::Option; - } - - if (help.contains(QStringLiteral("--autoplay"))) - { - return AutoplaySupport::Flag; - } - - return AutoplaySupport::None; + return help.contains(QStringLiteral("--enable-oauth")); } diff --git a/src/spotifyclient/helper.hpp b/src/spotifyclient/helper.hpp index 0f5250de..b9425fac 100644 --- a/src/spotifyclient/helper.hpp +++ b/src/spotifyclient/helper.hpp @@ -1,7 +1,6 @@ #pragma once #include "lib/enum/clienttype.hpp" -#include "enum/autoplaysupport.hpp" #include #include @@ -21,10 +20,10 @@ namespace SpotifyClient static auto running(const QString &path) -> bool; /** - * What type of autoplay the client supports + * If the client supports OAuth authentication * @param path Path to client */ - static auto getAutoplaySupport(const QString &path) -> AutoplaySupport; + static auto getOAuthSupport(const QString &path) -> bool; private: Helper() = default; From 265ecee067f34449d443ce564596bd7da459ef97 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 16 Nov 2024 13:20:59 +0100 Subject: [PATCH 03/32] Add logged in method --- src/spotifyclient/runner.cpp | 13 ++++++++++++- src/spotifyclient/runner.hpp | 3 +++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/spotifyclient/runner.cpp b/src/spotifyclient/runner.cpp index f9f7f087..396886bc 100644 --- a/src/spotifyclient/runner.cpp +++ b/src/spotifyclient/runner.cpp @@ -68,7 +68,7 @@ void SpotifyClient::Runner::start() arguments.append({ "--name", QString("%1 (librespot)").arg(APP_NAME), "--initial-volume", initialVolume, - "--cache", QString::fromStdString((paths.cache() / "librespot").string()), + "--cache", QString::fromStdString(getCachePath().string()), "--autoplay", "on", }); } @@ -169,6 +169,17 @@ auto SpotifyClient::Runner::joinArgs(const QStringList &args) -> QString return result; } +auto SpotifyClient::Runner::getCachePath() const -> ghc::filesystem::path +{ + return paths.cache() / "librespot"; +} + +auto SpotifyClient::Runner::isLoggedIn() const -> bool +{ + const auto path = getCachePath() / "credentials.json"; + return ghc::filesystem::exists(path); +} + void SpotifyClient::Runner::onReadyReadOutput() { logOutput(process->readAllStandardOutput(), lib::log_type::information); diff --git a/src/spotifyclient/runner.hpp b/src/spotifyclient/runner.hpp index d9a55f60..9c951056 100644 --- a/src/spotifyclient/runner.hpp +++ b/src/spotifyclient/runner.hpp @@ -51,6 +51,9 @@ namespace SpotifyClient void logOutput(const QByteArray &output, lib::log_type logType); static auto joinArgs(const QStringList &args) -> QString; + auto getCachePath() const -> ghc::filesystem::path; + auto isLoggedIn() const -> bool; + void onReadyReadOutput(); void onReadyReadError(); void onStarted(); From 011a3bf682503aef0502c899a448eb4d8a13810a Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 16 Nov 2024 13:32:30 +0100 Subject: [PATCH 04/32] Check for OAuth login support --- src/spotifyclient/runner.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/spotifyclient/runner.cpp b/src/spotifyclient/runner.cpp index 396886bc..23964a4a 100644 --- a/src/spotifyclient/runner.cpp +++ b/src/spotifyclient/runner.cpp @@ -47,6 +47,13 @@ void SpotifyClient::Runner::start() return; } + // Check if supporting OAuth login + if (!isLoggedIn() && !Helper::getOAuthSupport(path)) + { + emit statusChanged(QStringLiteral("Client unsupported, please upgrade and try again")); + return; + } + // If using global config, just start if (settings.spotify.global_config && clientType == lib::client_type::spotifyd) { From 455ebc041079ed88385ff89f231341c1dc81679c Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 16 Nov 2024 14:02:15 +0100 Subject: [PATCH 05/32] Move process error to string to helper --- src/spotifyclient/helper.cpp | 24 ++++++++++++++++++++++++ src/spotifyclient/helper.hpp | 2 ++ src/spotifyclient/runner.cpp | 30 +----------------------------- 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/spotifyclient/helper.cpp b/src/spotifyclient/helper.cpp index a4c1fb9f..4c77bc9e 100644 --- a/src/spotifyclient/helper.cpp +++ b/src/spotifyclient/helper.cpp @@ -156,3 +156,27 @@ auto SpotifyClient::Helper::getOAuthSupport(const QString &path) -> bool return help.contains(QStringLiteral("--enable-oauth")); } + +auto SpotifyClient::Helper::processErrorToString(const QProcess::ProcessError error) +{ + switch (error) + { + case QProcess::FailedToStart: + return QStringLiteral("Process failed to start"); + + case QProcess::Crashed: + return QStringLiteral("Process stopped or crashed"); + + case QProcess::Timedout: + return QStringLiteral("Process timed out"); + + case QProcess::WriteError: + return QStringLiteral("Process with write error"); + + case QProcess::ReadError: + return QStringLiteral("Process with read error"); + + default: + return QStringLiteral("Process with unknown error"); + } +} diff --git a/src/spotifyclient/helper.hpp b/src/spotifyclient/helper.hpp index b9425fac..6da5a65e 100644 --- a/src/spotifyclient/helper.hpp +++ b/src/spotifyclient/helper.hpp @@ -19,6 +19,8 @@ namespace SpotifyClient static auto version(const QString &path) -> QString; static auto running(const QString &path) -> bool; + static auto processErrorToString(QProcess::ProcessError error); + /** * If the client supports OAuth authentication * @param path Path to client diff --git a/src/spotifyclient/runner.cpp b/src/spotifyclient/runner.cpp index 23964a4a..1d3e045c 100644 --- a/src/spotifyclient/runner.cpp +++ b/src/spotifyclient/runner.cpp @@ -204,35 +204,7 @@ void SpotifyClient::Runner::onStarted() void SpotifyClient::Runner::onErrorOccurred(QProcess::ProcessError error) { - QString message; - - switch (error) - { - case QProcess::FailedToStart: - message = QStringLiteral("Process failed to start"); - break; - - case QProcess::Crashed: - message = QStringLiteral("Process stopped or crashed"); - break; - - case QProcess::Timedout: - message = QStringLiteral("Process timed out"); - break; - - case QProcess::WriteError: - message = QStringLiteral("Process with write error"); - break; - - case QProcess::ReadError: - message = QStringLiteral("Process with read error"); - break; - - default: - message = QStringLiteral("Process with unknown error"); - break; - } - + const auto message = Helper::processErrorToString(error); emit statusChanged(message); } From adfb8260c7a92f8736fa2d596f5ca14460553cf9 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 16 Nov 2024 18:15:23 +0100 Subject: [PATCH 06/32] Fix missing return type --- src/spotifyclient/helper.cpp | 2 +- src/spotifyclient/helper.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/spotifyclient/helper.cpp b/src/spotifyclient/helper.cpp index 4c77bc9e..35383cf3 100644 --- a/src/spotifyclient/helper.cpp +++ b/src/spotifyclient/helper.cpp @@ -157,7 +157,7 @@ auto SpotifyClient::Helper::getOAuthSupport(const QString &path) -> bool return help.contains(QStringLiteral("--enable-oauth")); } -auto SpotifyClient::Helper::processErrorToString(const QProcess::ProcessError error) +auto SpotifyClient::Helper::processErrorToString(const QProcess::ProcessError error) -> QString { switch (error) { diff --git a/src/spotifyclient/helper.hpp b/src/spotifyclient/helper.hpp index 6da5a65e..275caa3b 100644 --- a/src/spotifyclient/helper.hpp +++ b/src/spotifyclient/helper.hpp @@ -19,7 +19,7 @@ namespace SpotifyClient static auto version(const QString &path) -> QString; static auto running(const QString &path) -> bool; - static auto processErrorToString(QProcess::ProcessError error); + static auto processErrorToString(QProcess::ProcessError error) -> QString; /** * If the client supports OAuth authentication From ab544bfc349f405fd4eafbdfaf7fa28499589522 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 16 Nov 2024 18:16:09 +0100 Subject: [PATCH 07/32] Fix some slightly broken english --- src/spotifyclient/helper.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/spotifyclient/helper.cpp b/src/spotifyclient/helper.cpp index 35383cf3..ee9a2f37 100644 --- a/src/spotifyclient/helper.cpp +++ b/src/spotifyclient/helper.cpp @@ -171,12 +171,12 @@ auto SpotifyClient::Helper::processErrorToString(const QProcess::ProcessError er return QStringLiteral("Process timed out"); case QProcess::WriteError: - return QStringLiteral("Process with write error"); + return QStringLiteral("Process write error"); case QProcess::ReadError: - return QStringLiteral("Process with read error"); + return QStringLiteral("Process read error"); default: - return QStringLiteral("Process with unknown error"); + return QStringLiteral("Unknown error"); } } From 8cd43fa5cb4b256e5fee26905397693504e8f7e3 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 16 Nov 2024 18:19:12 +0100 Subject: [PATCH 08/32] Fix CMake deprecation warnings --- CMakeLists.txt | 2 +- lib/CMakeLists.txt | 2 +- lib/test/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 654b8f0f..cf03e9e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(spotify-qt LANGUAGES CXX VERSION 3.11) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 201500d3..51fb3920 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(spotify-qt-lib LANGUAGES CXX VERSION 0.9) set(CMAKE_CXX_STANDARD 11) diff --git a/lib/test/CMakeLists.txt b/lib/test/CMakeLists.txt index f48a6e6a..9e34a0a1 100644 --- a/lib/test/CMakeLists.txt +++ b/lib/test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(spotify-qt-lib-test) From 35e8516d22ad1424d98b55c5bea279b24e19f787 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 16 Nov 2024 18:26:52 +0100 Subject: [PATCH 09/32] Initial login helper --- src/spotifyclient/CMakeLists.txt | 4 +- src/spotifyclient/login.cpp | 76 ++++++++++++++++++++++++++++++++ src/spotifyclient/login.hpp | 29 ++++++++++++ 3 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 src/spotifyclient/login.cpp create mode 100644 src/spotifyclient/login.hpp diff --git a/src/spotifyclient/CMakeLists.txt b/src/spotifyclient/CMakeLists.txt index f57a6d75..fc6d5e29 100644 --- a/src/spotifyclient/CMakeLists.txt +++ b/src/spotifyclient/CMakeLists.txt @@ -1,3 +1,5 @@ target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/helper.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/runner.cpp) \ No newline at end of file + ${CMAKE_CURRENT_SOURCE_DIR}/login.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/runner.cpp +) \ No newline at end of file diff --git a/src/spotifyclient/login.cpp b/src/spotifyclient/login.cpp new file mode 100644 index 00000000..ac7d7115 --- /dev/null +++ b/src/spotifyclient/login.cpp @@ -0,0 +1,76 @@ +#include "spotifyclient/login.hpp" + +#include "spotifyclient/helper.hpp" +#include "util/url.hpp" + +#include +#include + +SpotifyClient::Login::Login(QWidget *parent) + : QObject(parent), + process(new QProcess(this)) +{ + connect(process, &QProcess::readyReadStandardOutput, + this, &Login::onReadyReadOutput); + + connect(process, &QProcess::readyReadStandardError, + this, &Login::onReadyReadError); + + connect(process, &QProcess::errorOccurred, + this, &Login::onErrorOccurred); +} + +void SpotifyClient::Login::run(const QString &path) +{ + if (!Helper::getOAuthSupport(path)) + { + emit loginFailed(QStringLiteral("Client unsupported")); + return; + } + + const QStringList arguments({ + "--enable-oath" + }); + + process->start(path, arguments); +} + +void SpotifyClient::Login::onReadyReadOutput() +{ + const auto output = process->readAllStandardOutput(); + + for (auto &line: QString(output).split('\n')) + { + if (line.isEmpty()) + { + continue; + } + + const auto urlIndex = line.indexOf(QStringLiteral("https://accounts.spotify.com/authorize")); + if (urlIndex >= 0) + { + const auto url = line.right(line.length() - urlIndex - 1); + auto *parent = qobject_cast(QObject::parent()); + Url::open(url, LinkType::Web, parent); + continue; + } + + if (line.contains(QStringLiteral("Authenticated"))) + { + emit loginSuccess(); + return; + } + } +} + +void SpotifyClient::Login::onReadyReadError() +{ + const auto output = process->readAllStandardError(); + emit loginFailed(QString(output)); +} + +void SpotifyClient::Login::onErrorOccurred(const QProcess::ProcessError error) +{ + const auto message = Helper::processErrorToString(error); + emit loginFailed(message); +} diff --git a/src/spotifyclient/login.hpp b/src/spotifyclient/login.hpp new file mode 100644 index 00000000..f84d65c0 --- /dev/null +++ b/src/spotifyclient/login.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +namespace SpotifyClient +{ + class Login final: public QObject + { + Q_OBJECT + + public: + explicit Login(QWidget *parent); + + void run(const QString &path); + + signals: + void loginSuccess(); + void loginFailed(const QString &message); + + private: + QProcess *process; + + void onReadyReadOutput(); + void onReadyReadError(); + void onErrorOccurred(QProcess::ProcessError error); + }; +} From 48171310a5b7bcd56f81fec1c1134cc32fc3c504 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 16 Nov 2024 18:50:42 +0100 Subject: [PATCH 10/32] Initial OAuth login support --- src/spotifyclient/runner.cpp | 63 ++++++++++++++++++++++++++++++++---- src/spotifyclient/runner.hpp | 6 ++++ 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/src/spotifyclient/runner.cpp b/src/spotifyclient/runner.cpp index 1d3e045c..07ab3f0b 100644 --- a/src/spotifyclient/runner.cpp +++ b/src/spotifyclient/runner.cpp @@ -47,13 +47,6 @@ void SpotifyClient::Runner::start() return; } - // Check if supporting OAuth login - if (!isLoggedIn() && !Helper::getOAuthSupport(path)) - { - emit statusChanged(QStringLiteral("Client unsupported, please upgrade and try again")); - return; - } - // If using global config, just start if (settings.spotify.global_config && clientType == lib::client_type::spotifyd) { @@ -62,6 +55,19 @@ void SpotifyClient::Runner::start() return; } + // Check if not logged in + if (!isLoggedIn()) + { + if (!Helper::getOAuthSupport(path)) + { + emit statusChanged(QStringLiteral("Client unsupported, please upgrade and try again")); + return; + } + + login(); + return; + } + // Common arguments QStringList arguments({ "--bitrate", QString::number(static_cast(settings.spotify.bitrate)), @@ -138,6 +144,25 @@ void SpotifyClient::Runner::start() process->start(path, arguments); } +void SpotifyClient::Runner::login() +{ + if (loginHelper != nullptr) + { + loginHelper->deleteLater(); + loginHelper = nullptr; + } + + loginHelper = new Login(parentWidget); + + connect(loginHelper, &Login::loginSuccess, + this, &Runner::onLoginSuccess); + + connect(loginHelper, &Login::loginFailed, + this, &Runner::onLoginFailed); + + loginHelper->run(path); +} + auto SpotifyClient::Runner::isRunning() const -> bool { return process == nullptr @@ -208,6 +233,30 @@ void SpotifyClient::Runner::onErrorOccurred(QProcess::ProcessError error) emit statusChanged(message); } +void SpotifyClient::Runner::onLoginSuccess() +{ + if (!isLoggedIn()) + { + lib::log::warn("Login successful, but not login found"); + emit statusChanged(QStringLiteral("Unknown error")); + return; + } + + loginHelper->deleteLater(); + loginHelper = nullptr; + + start(); +} + +void SpotifyClient::Runner::onLoginFailed(const QString &message) +{ + lib::log::warn(message.toStdString()); + emit statusChanged(message); + + loginHelper->deleteLater(); + loginHelper = nullptr; +} + auto SpotifyClient::Runner::getLog() -> const std::vector & { return log; diff --git a/src/spotifyclient/runner.hpp b/src/spotifyclient/runner.hpp index 9c951056..ca111e1b 100644 --- a/src/spotifyclient/runner.hpp +++ b/src/spotifyclient/runner.hpp @@ -5,6 +5,7 @@ #include "lib/logmessage.hpp" #include "spotifyclient/helper.hpp" +#include "spotifyclient/login.hpp" #include #include @@ -47,9 +48,11 @@ namespace SpotifyClient const lib::settings &settings; const lib::paths &paths; lib::client_type clientType; + Login *loginHelper = nullptr; void logOutput(const QByteArray &output, lib::log_type logType); static auto joinArgs(const QStringList &args) -> QString; + void login(); auto getCachePath() const -> ghc::filesystem::path; auto isLoggedIn() const -> bool; @@ -58,5 +61,8 @@ namespace SpotifyClient void onReadyReadError(); void onStarted(); void onErrorOccurred(QProcess::ProcessError error); + + void onLoginSuccess(); + void onLoginFailed(const QString &message); }; } From e54953f26e2319cbbec6079f720295ae6b83bac0 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 16 Nov 2024 19:04:04 +0100 Subject: [PATCH 11/32] Add word wrap for client errors --- src/settingspage/spotify.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/settingspage/spotify.cpp b/src/settingspage/spotify.cpp index 5c947477..15f8c6f7 100644 --- a/src/settingspage/spotify.cpp +++ b/src/settingspage/spotify.cpp @@ -121,6 +121,7 @@ auto SettingsPage::Spotify::spotify() -> QWidget * clientStatus = new QLabel(this); clientStatus->setEnabled(false); + clientStatus->setWordWrap(true); statusLayout->addWidget(clientStatus, 1); content->addLayout(statusLayout); updateClientStatus(); From 445fb98dee35e9d6496cee75b428adaa5a53242b Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 16 Nov 2024 19:04:31 +0100 Subject: [PATCH 12/32] Reset credentials if invalid --- src/spotifyclient/runner.cpp | 11 +++++++++++ src/spotifyclient/runner.hpp | 1 + 2 files changed, 12 insertions(+) diff --git a/src/spotifyclient/runner.cpp b/src/spotifyclient/runner.cpp index 07ab3f0b..632031af 100644 --- a/src/spotifyclient/runner.cpp +++ b/src/spotifyclient/runner.cpp @@ -184,6 +184,11 @@ void SpotifyClient::Runner::logOutput(const QByteArray &output, lib::log_type lo if (line.contains(QStringLiteral("Bad credentials"))) { emit statusChanged(QStringLiteral("Bad credentials, please try again")); + + if (resetCredentials()) + { + lib::log::debug("Credentials reset"); + } } } } @@ -212,6 +217,12 @@ auto SpotifyClient::Runner::isLoggedIn() const -> bool return ghc::filesystem::exists(path); } +auto SpotifyClient::Runner::resetCredentials() const -> bool +{ + const auto path = getCachePath() / "credentials.json"; + return ghc::filesystem::remove(path); +} + void SpotifyClient::Runner::onReadyReadOutput() { logOutput(process->readAllStandardOutput(), lib::log_type::information); diff --git a/src/spotifyclient/runner.hpp b/src/spotifyclient/runner.hpp index ca111e1b..43e2c272 100644 --- a/src/spotifyclient/runner.hpp +++ b/src/spotifyclient/runner.hpp @@ -56,6 +56,7 @@ namespace SpotifyClient auto getCachePath() const -> ghc::filesystem::path; auto isLoggedIn() const -> bool; + auto resetCredentials() const -> bool; void onReadyReadOutput(); void onReadyReadError(); From 301231d3916ad5238171b0b85f68522553e1b7de Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 16 Nov 2024 19:12:49 +0100 Subject: [PATCH 13/32] Fix client caching --- src/spotifyclient/login.cpp | 13 ++++--------- src/spotifyclient/login.hpp | 2 +- src/spotifyclient/runner.cpp | 3 ++- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/spotifyclient/login.cpp b/src/spotifyclient/login.cpp index ac7d7115..2d3a6ad1 100644 --- a/src/spotifyclient/login.cpp +++ b/src/spotifyclient/login.cpp @@ -20,19 +20,14 @@ SpotifyClient::Login::Login(QWidget *parent) this, &Login::onErrorOccurred); } -void SpotifyClient::Login::run(const QString &path) +void SpotifyClient::Login::run(const QString &clientPath, const QString &cachePath) const { - if (!Helper::getOAuthSupport(path)) - { - emit loginFailed(QStringLiteral("Client unsupported")); - return; - } - const QStringList arguments({ - "--enable-oath" + "--enable-oauth", + "--cache", cachePath, }); - process->start(path, arguments); + process->start(clientPath, arguments); } void SpotifyClient::Login::onReadyReadOutput() diff --git a/src/spotifyclient/login.hpp b/src/spotifyclient/login.hpp index f84d65c0..cd1ad384 100644 --- a/src/spotifyclient/login.hpp +++ b/src/spotifyclient/login.hpp @@ -13,7 +13,7 @@ namespace SpotifyClient public: explicit Login(QWidget *parent); - void run(const QString &path); + void run(const QString &clientPath, const QString &cachePath) const; signals: void loginSuccess(); diff --git a/src/spotifyclient/runner.cpp b/src/spotifyclient/runner.cpp index 632031af..1f37dcd3 100644 --- a/src/spotifyclient/runner.cpp +++ b/src/spotifyclient/runner.cpp @@ -160,7 +160,8 @@ void SpotifyClient::Runner::login() connect(loginHelper, &Login::loginFailed, this, &Runner::onLoginFailed); - loginHelper->run(path); + const auto cachePath = QString::fromStdString(getCachePath().string()); + loginHelper->run(path, cachePath); } auto SpotifyClient::Runner::isRunning() const -> bool From 6765aa33432f871f38239233acaa3363f5117741 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 16 Nov 2024 19:12:57 +0100 Subject: [PATCH 14/32] Fix invalid url --- src/spotifyclient/login.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spotifyclient/login.cpp b/src/spotifyclient/login.cpp index 2d3a6ad1..691f927c 100644 --- a/src/spotifyclient/login.cpp +++ b/src/spotifyclient/login.cpp @@ -44,7 +44,7 @@ void SpotifyClient::Login::onReadyReadOutput() const auto urlIndex = line.indexOf(QStringLiteral("https://accounts.spotify.com/authorize")); if (urlIndex >= 0) { - const auto url = line.right(line.length() - urlIndex - 1); + const auto url = line.right(line.length() - urlIndex); auto *parent = qobject_cast(QObject::parent()); Url::open(url, LinkType::Web, parent); continue; From ebc65a6a47f19d53d5b644a738e507672a8b0ed5 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 16 Nov 2024 19:22:56 +0100 Subject: [PATCH 15/32] Don't listen for errors --- src/spotifyclient/login.cpp | 9 --------- src/spotifyclient/login.hpp | 1 - 2 files changed, 10 deletions(-) diff --git a/src/spotifyclient/login.cpp b/src/spotifyclient/login.cpp index 691f927c..e0bc7b2a 100644 --- a/src/spotifyclient/login.cpp +++ b/src/spotifyclient/login.cpp @@ -13,9 +13,6 @@ SpotifyClient::Login::Login(QWidget *parent) connect(process, &QProcess::readyReadStandardOutput, this, &Login::onReadyReadOutput); - connect(process, &QProcess::readyReadStandardError, - this, &Login::onReadyReadError); - connect(process, &QProcess::errorOccurred, this, &Login::onErrorOccurred); } @@ -58,12 +55,6 @@ void SpotifyClient::Login::onReadyReadOutput() } } -void SpotifyClient::Login::onReadyReadError() -{ - const auto output = process->readAllStandardError(); - emit loginFailed(QString(output)); -} - void SpotifyClient::Login::onErrorOccurred(const QProcess::ProcessError error) { const auto message = Helper::processErrorToString(error); diff --git a/src/spotifyclient/login.hpp b/src/spotifyclient/login.hpp index cd1ad384..5a41adde 100644 --- a/src/spotifyclient/login.hpp +++ b/src/spotifyclient/login.hpp @@ -23,7 +23,6 @@ namespace SpotifyClient QProcess *process; void onReadyReadOutput(); - void onReadyReadError(); void onErrorOccurred(QProcess::ProcessError error); }; } From 1c066f3b435891e67b838283327fa4fedb3a28a9 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 16 Nov 2024 19:23:39 +0100 Subject: [PATCH 16/32] Terminate process on success --- src/spotifyclient/login.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/spotifyclient/login.cpp b/src/spotifyclient/login.cpp index e0bc7b2a..00f32173 100644 --- a/src/spotifyclient/login.cpp +++ b/src/spotifyclient/login.cpp @@ -49,6 +49,9 @@ void SpotifyClient::Login::onReadyReadOutput() if (line.contains(QStringLiteral("Authenticated"))) { + process->terminate(); + process->waitForFinished(); + emit loginSuccess(); return; } From a6907d5539887c3773d565f3173a551328119c54 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 16 Nov 2024 19:26:34 +0100 Subject: [PATCH 17/32] Fix crash on close with client running --- src/spotifyclient/runner.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/spotifyclient/runner.cpp b/src/spotifyclient/runner.cpp index 1f37dcd3..49719b02 100644 --- a/src/spotifyclient/runner.cpp +++ b/src/spotifyclient/runner.cpp @@ -19,7 +19,8 @@ SpotifyClient::Runner::~Runner() { if (process != nullptr) { - process->close(); + process->terminate(); + process->waitForFinished(); } } From 42a9bdf4e1a979ae452d9543e0f58c0cc9c13d65 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sun, 17 Nov 2024 18:12:06 +0100 Subject: [PATCH 18/32] Also listen to stderr --- src/spotifyclient/login.cpp | 19 ++++++++++++++++--- src/spotifyclient/login.hpp | 2 ++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/spotifyclient/login.cpp b/src/spotifyclient/login.cpp index 00f32173..be327024 100644 --- a/src/spotifyclient/login.cpp +++ b/src/spotifyclient/login.cpp @@ -13,6 +13,9 @@ SpotifyClient::Login::Login(QWidget *parent) connect(process, &QProcess::readyReadStandardOutput, this, &Login::onReadyReadOutput); + connect(process, &QProcess::readyReadStandardError, + this, &Login::onReadyReadError); + connect(process, &QProcess::errorOccurred, this, &Login::onErrorOccurred); } @@ -27,10 +30,8 @@ void SpotifyClient::Login::run(const QString &clientPath, const QString &cachePa process->start(clientPath, arguments); } -void SpotifyClient::Login::onReadyReadOutput() +void SpotifyClient::Login::onOutput(const QString &output) { - const auto output = process->readAllStandardOutput(); - for (auto &line: QString(output).split('\n')) { if (line.isEmpty()) @@ -58,6 +59,18 @@ void SpotifyClient::Login::onReadyReadOutput() } } +void SpotifyClient::Login::onReadyReadOutput() +{ + const auto output = process->readAllStandardOutput(); + onOutput(QString(output)); +} + +void SpotifyClient::Login::onReadyReadError() +{ + const auto output = process->readAllStandardError(); + onOutput(QString(output)); +} + void SpotifyClient::Login::onErrorOccurred(const QProcess::ProcessError error) { const auto message = Helper::processErrorToString(error); diff --git a/src/spotifyclient/login.hpp b/src/spotifyclient/login.hpp index 5a41adde..26b1f28f 100644 --- a/src/spotifyclient/login.hpp +++ b/src/spotifyclient/login.hpp @@ -22,7 +22,9 @@ namespace SpotifyClient private: QProcess *process; + void onOutput(const QString &output); void onReadyReadOutput(); + void onReadyReadError(); void onErrorOccurred(QProcess::ProcessError error); }; } From c79c4acd4850ca520552cb0a95cc4e33f5059941 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sun, 17 Nov 2024 18:46:12 +0100 Subject: [PATCH 19/32] Replace login helper with argument on run --- src/spotifyclient/CMakeLists.txt | 1 - src/spotifyclient/login.cpp | 78 -------------------------------- src/spotifyclient/login.hpp | 30 ------------ src/spotifyclient/runner.cpp | 65 ++++++-------------------- src/spotifyclient/runner.hpp | 6 --- 5 files changed, 14 insertions(+), 166 deletions(-) delete mode 100644 src/spotifyclient/login.cpp delete mode 100644 src/spotifyclient/login.hpp diff --git a/src/spotifyclient/CMakeLists.txt b/src/spotifyclient/CMakeLists.txt index fc6d5e29..cef5a291 100644 --- a/src/spotifyclient/CMakeLists.txt +++ b/src/spotifyclient/CMakeLists.txt @@ -1,5 +1,4 @@ target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/helper.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/login.cpp ${CMAKE_CURRENT_SOURCE_DIR}/runner.cpp ) \ No newline at end of file diff --git a/src/spotifyclient/login.cpp b/src/spotifyclient/login.cpp deleted file mode 100644 index be327024..00000000 --- a/src/spotifyclient/login.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "spotifyclient/login.hpp" - -#include "spotifyclient/helper.hpp" -#include "util/url.hpp" - -#include -#include - -SpotifyClient::Login::Login(QWidget *parent) - : QObject(parent), - process(new QProcess(this)) -{ - connect(process, &QProcess::readyReadStandardOutput, - this, &Login::onReadyReadOutput); - - connect(process, &QProcess::readyReadStandardError, - this, &Login::onReadyReadError); - - connect(process, &QProcess::errorOccurred, - this, &Login::onErrorOccurred); -} - -void SpotifyClient::Login::run(const QString &clientPath, const QString &cachePath) const -{ - const QStringList arguments({ - "--enable-oauth", - "--cache", cachePath, - }); - - process->start(clientPath, arguments); -} - -void SpotifyClient::Login::onOutput(const QString &output) -{ - for (auto &line: QString(output).split('\n')) - { - if (line.isEmpty()) - { - continue; - } - - const auto urlIndex = line.indexOf(QStringLiteral("https://accounts.spotify.com/authorize")); - if (urlIndex >= 0) - { - const auto url = line.right(line.length() - urlIndex); - auto *parent = qobject_cast(QObject::parent()); - Url::open(url, LinkType::Web, parent); - continue; - } - - if (line.contains(QStringLiteral("Authenticated"))) - { - process->terminate(); - process->waitForFinished(); - - emit loginSuccess(); - return; - } - } -} - -void SpotifyClient::Login::onReadyReadOutput() -{ - const auto output = process->readAllStandardOutput(); - onOutput(QString(output)); -} - -void SpotifyClient::Login::onReadyReadError() -{ - const auto output = process->readAllStandardError(); - onOutput(QString(output)); -} - -void SpotifyClient::Login::onErrorOccurred(const QProcess::ProcessError error) -{ - const auto message = Helper::processErrorToString(error); - emit loginFailed(message); -} diff --git a/src/spotifyclient/login.hpp b/src/spotifyclient/login.hpp deleted file mode 100644 index 26b1f28f..00000000 --- a/src/spotifyclient/login.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace SpotifyClient -{ - class Login final: public QObject - { - Q_OBJECT - - public: - explicit Login(QWidget *parent); - - void run(const QString &clientPath, const QString &cachePath) const; - - signals: - void loginSuccess(); - void loginFailed(const QString &message); - - private: - QProcess *process; - - void onOutput(const QString &output); - void onReadyReadOutput(); - void onReadyReadError(); - void onErrorOccurred(QProcess::ProcessError error); - }; -} diff --git a/src/spotifyclient/runner.cpp b/src/spotifyclient/runner.cpp index 49719b02..07a673e4 100644 --- a/src/spotifyclient/runner.cpp +++ b/src/spotifyclient/runner.cpp @@ -56,6 +56,11 @@ void SpotifyClient::Runner::start() return; } + // Common arguments + QStringList arguments({ + "--bitrate", QString::number(static_cast(settings.spotify.bitrate)), + }); + // Check if not logged in if (!isLoggedIn()) { @@ -65,15 +70,9 @@ void SpotifyClient::Runner::start() return; } - login(); - return; + arguments.append(QStringLiteral("--enable-oauth")); } - // Common arguments - QStringList arguments({ - "--bitrate", QString::number(static_cast(settings.spotify.bitrate)), - }); - const auto initialVolume = QString::number(settings.spotify.volume); // librespot specific @@ -145,26 +144,6 @@ void SpotifyClient::Runner::start() process->start(path, arguments); } -void SpotifyClient::Runner::login() -{ - if (loginHelper != nullptr) - { - loginHelper->deleteLater(); - loginHelper = nullptr; - } - - loginHelper = new Login(parentWidget); - - connect(loginHelper, &Login::loginSuccess, - this, &Runner::onLoginSuccess); - - connect(loginHelper, &Login::loginFailed, - this, &Runner::onLoginFailed); - - const auto cachePath = QString::fromStdString(getCachePath().string()); - loginHelper->run(path, cachePath); -} - auto SpotifyClient::Runner::isRunning() const -> bool { return process == nullptr @@ -183,6 +162,14 @@ void SpotifyClient::Runner::logOutput(const QByteArray &output, lib::log_type lo log.emplace_back(lib::date_time::now(), logType, line.toStdString()); + const auto urlIndex = line.indexOf(QStringLiteral("https://accounts.spotify.com/authorize")); + if (urlIndex >= 0) + { + const auto url = line.right(line.length() - urlIndex); + auto *parent = qobject_cast(QObject::parent()); + Url::open(url, LinkType::Web, parent); + } + if (line.contains(QStringLiteral("Bad credentials"))) { emit statusChanged(QStringLiteral("Bad credentials, please try again")); @@ -246,30 +233,6 @@ void SpotifyClient::Runner::onErrorOccurred(QProcess::ProcessError error) emit statusChanged(message); } -void SpotifyClient::Runner::onLoginSuccess() -{ - if (!isLoggedIn()) - { - lib::log::warn("Login successful, but not login found"); - emit statusChanged(QStringLiteral("Unknown error")); - return; - } - - loginHelper->deleteLater(); - loginHelper = nullptr; - - start(); -} - -void SpotifyClient::Runner::onLoginFailed(const QString &message) -{ - lib::log::warn(message.toStdString()); - emit statusChanged(message); - - loginHelper->deleteLater(); - loginHelper = nullptr; -} - auto SpotifyClient::Runner::getLog() -> const std::vector & { return log; diff --git a/src/spotifyclient/runner.hpp b/src/spotifyclient/runner.hpp index 43e2c272..c5491b61 100644 --- a/src/spotifyclient/runner.hpp +++ b/src/spotifyclient/runner.hpp @@ -5,7 +5,6 @@ #include "lib/logmessage.hpp" #include "spotifyclient/helper.hpp" -#include "spotifyclient/login.hpp" #include #include @@ -48,11 +47,9 @@ namespace SpotifyClient const lib::settings &settings; const lib::paths &paths; lib::client_type clientType; - Login *loginHelper = nullptr; void logOutput(const QByteArray &output, lib::log_type logType); static auto joinArgs(const QStringList &args) -> QString; - void login(); auto getCachePath() const -> ghc::filesystem::path; auto isLoggedIn() const -> bool; @@ -62,8 +59,5 @@ namespace SpotifyClient void onReadyReadError(); void onStarted(); void onErrorOccurred(QProcess::ProcessError error); - - void onLoginSuccess(); - void onLoginFailed(const QString &message); }; } From ca414b80b675332621a56faa4ec08730a70e0ff5 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sun, 17 Nov 2024 18:49:58 +0100 Subject: [PATCH 20/32] Always attach events --- src/spotifyclient/runner.cpp | 24 ++++++++++++------------ src/spotifyclient/runner.hpp | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/spotifyclient/runner.cpp b/src/spotifyclient/runner.cpp index 07a673e4..aba426a3 100644 --- a/src/spotifyclient/runner.cpp +++ b/src/spotifyclient/runner.cpp @@ -13,6 +13,18 @@ SpotifyClient::Runner::Runner(const lib::settings &settings, path = QString::fromStdString(settings.spotify.path); process = new QProcess(parent); clientType = SpotifyClient::Helper::clientType(path); + + connect(process, &QProcess::readyReadStandardOutput, + this, &Runner::onReadyReadOutput); + + connect(process, &QProcess::readyReadStandardError, + this, &Runner::onReadyReadError); + + connect(process, &QProcess::started, + this, &Runner::onStarted); + + connect(process, &QProcess::errorOccurred, + this, &Runner::onErrorOccurred); } SpotifyClient::Runner::~Runner() @@ -126,18 +138,6 @@ void SpotifyClient::Runner::start() arguments.append(additional_arguments.split(' ')); } - QProcess::connect(process, &QProcess::readyReadStandardOutput, - this, &Runner::onReadyReadOutput); - - QProcess::connect(process, &QProcess::readyReadStandardError, - this, &Runner::onReadyReadError); - - QProcess::connect(process, &QProcess::started, - this, &Runner::onStarted); - - QProcess::connect(process, &QProcess::errorOccurred, - this, &Runner::onErrorOccurred); - lib::log::debug("starting: {} {}", path.toStdString(), joinArgs(arguments).toStdString()); diff --git a/src/spotifyclient/runner.hpp b/src/spotifyclient/runner.hpp index c5491b61..c46205a7 100644 --- a/src/spotifyclient/runner.hpp +++ b/src/spotifyclient/runner.hpp @@ -40,7 +40,7 @@ namespace SpotifyClient void statusChanged(const QString &status); private: - QProcess *process = nullptr; + QProcess *process; QWidget *parentWidget = nullptr; QString path; static std::vector log; From 843057b89ab6821c13aa3549174803d49450de4a Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sun, 17 Nov 2024 18:55:32 +0100 Subject: [PATCH 21/32] Disconnect all signals before terminating the process --- src/spotifyclient/runner.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/spotifyclient/runner.cpp b/src/spotifyclient/runner.cpp index aba426a3..dce7f2f5 100644 --- a/src/spotifyclient/runner.cpp +++ b/src/spotifyclient/runner.cpp @@ -1,4 +1,7 @@ #include "spotifyclient/runner.hpp" + +#include "lib/log.hpp" + #include "mainwindow.hpp" std::vector SpotifyClient::Runner::log; @@ -31,6 +34,11 @@ SpotifyClient::Runner::~Runner() { if (process != nullptr) { + if (process->disconnect()) + { + lib::log::debug("Disconnected events from client process"); + } + process->terminate(); process->waitForFinished(); } From f44b6ffe5b2b6f4ef61ada2c20e7e9f79633fd40 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 23 Nov 2024 19:39:50 +0100 Subject: [PATCH 22/32] Fix quit --- src/view/trayicon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/trayicon.cpp b/src/view/trayicon.cpp index 317d19c4..066ff747 100644 --- a/src/view/trayicon.cpp +++ b/src/view/trayicon.cpp @@ -41,7 +41,7 @@ TrayIcon::TrayIcon(lib::spt::api &spotify, lib::settings &settings, lib::cache & #endif auto *quit = contextMenu->addAction(Icon::get("application-exit"), "Quit"); - QAction::connect(quit, &QAction::triggered, QCoreApplication::quit); + QAction::connect(quit, &QAction::triggered, &QCoreApplication::quit); setDefaultPixmap(); setContextMenu(contextMenu); From 7a0ae1eea89476b91a395adc32ed80766ee47dc0 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 23 Nov 2024 20:01:10 +0100 Subject: [PATCH 23/32] Show "Show window" on "close to tray" instead --- src/view/trayicon.cpp | 5 +---- src/view/trayicon.hpp | 2 -- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/view/trayicon.cpp b/src/view/trayicon.cpp index 066ff747..9acf3a7d 100644 --- a/src/view/trayicon.cpp +++ b/src/view/trayicon.cpp @@ -35,10 +35,9 @@ TrayIcon::TrayIcon(lib::spt::api &spotify, lib::settings &settings, lib::cache & contextMenu->addSeparator(); -#ifdef __APPLE__ showApp = contextMenu->addAction(Icon::get("window"), QStringLiteral("Show")); + showApp->setVisible(settings.general.close_to_tray); QAction::connect(showApp, &QAction::triggered, this, &TrayIcon::onShowWindow); -#endif auto *quit = contextMenu->addAction(Icon::get("application-exit"), "Quit"); QAction::connect(quit, &QAction::triggered, &QCoreApplication::quit); @@ -47,10 +46,8 @@ TrayIcon::TrayIcon(lib::spt::api &spotify, lib::settings &settings, lib::cache & setContextMenu(contextMenu); show(); -#ifndef __APPLE__ QSystemTrayIcon::connect(this, &QSystemTrayIcon::activated, this, &TrayIcon::onActivated); -#endif QMenu::connect(contextMenu, &QMenu::aboutToShow, this, &TrayIcon::onMenuAboutToShow); diff --git a/src/view/trayicon.hpp b/src/view/trayicon.hpp index 58e52ed9..d909a26e 100644 --- a/src/view/trayicon.hpp +++ b/src/view/trayicon.hpp @@ -49,9 +49,7 @@ Q_OBJECT lib::cache &cache; const lib::http_client &httpClient; -#ifdef __APPLE__ QAction *showApp = nullptr; -#endif std::function callback; From 40f0706f93e46949c0ca7733a9d6abbe2c4f5db8 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 23 Nov 2024 20:21:15 +0100 Subject: [PATCH 24/32] Reload tray icon on "close to tray" change --- src/settingspage/interface.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/settingspage/interface.cpp b/src/settingspage/interface.cpp index c1c07a2f..58208617 100644 --- a/src/settingspage/interface.cpp +++ b/src/settingspage/interface.cpp @@ -462,6 +462,11 @@ void SettingsPage::Interface::saveTrayIcon() if (closeToTray != nullptr) { + if (settings.general.close_to_tray != closeToTray->isChecked()) + { + reloadTray = true; + } + settings.general.close_to_tray = closeToTray->isChecked(); } From 8b04118b8de275b188cb76f424cad12ded90a0f3 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 23 Nov 2024 20:21:31 +0100 Subject: [PATCH 25/32] Fix tray icon reload --- src/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 8e104f3b..1daee52e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -664,7 +664,7 @@ void MainWindow::reloadTrayIcon() { if (trayIcon != nullptr) { - delete trayIcon; + trayIcon->deleteLater(); trayIcon = nullptr; } From f914624805955bd6735524a95d6d09e6c95ea0d7 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Tue, 26 Nov 2024 23:07:39 +0100 Subject: [PATCH 26/32] Fix search, fixes #268 --- lib/include/lib/spotify/show.hpp | 5 ----- lib/src/spotify/show.cpp | 1 - 2 files changed, 6 deletions(-) diff --git a/lib/include/lib/spotify/show.hpp b/lib/include/lib/spotify/show.hpp index 0c72db1a..bc7b20d0 100644 --- a/lib/include/lib/spotify/show.hpp +++ b/lib/include/lib/spotify/show.hpp @@ -16,11 +16,6 @@ namespace lib class show: public entity { public: - /** - * Countries, in ISO 3166, which show can be played in - */ - std::vector available_markets; - /** * Description in plain text */ diff --git a/lib/src/spotify/show.cpp b/lib/src/spotify/show.cpp index ec3bcbc9..24ff6d4c 100644 --- a/lib/src/spotify/show.cpp +++ b/lib/src/spotify/show.cpp @@ -7,7 +7,6 @@ void lib::spt::from_json(const nlohmann::json &j, show &s) return; } - j.at("available_markets").get_to(s.available_markets); j.at("description").get_to(s.description); j.at("explicit").get_to(s.is_explicit); j.at("external_urls").get_to(s.external_urls); From f88addd4307acac1ba12070887f45388a79b9536 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 30 Nov 2024 13:14:04 +0100 Subject: [PATCH 27/32] Hide playlists unavailable from the API --- src/list/playlist.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/list/playlist.cpp b/src/list/playlist.cpp index 42f752c4..2f91f400 100644 --- a/src/list/playlist.cpp +++ b/src/list/playlist.cpp @@ -122,6 +122,11 @@ void List::Playlist::load(const std::vector &playlists, cons for (const auto &playlist: playlists) { + if (playlist.id.empty()) + { + continue; + } + auto *item = new QListWidgetItem(QString::fromStdString(playlist.name), this); item->setData(static_cast(DataRole::Playlist), QVariant::fromValue(playlist)); item->setData(static_cast(DataRole::DefaultIndex), index); From 3ae7adf496cd5403cc86e9ba88e96c2f7acd3922 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 30 Nov 2024 13:33:42 +0100 Subject: [PATCH 28/32] Lazily load deprecated "related artists" --- src/view/artist/view.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/view/artist/view.cpp b/src/view/artist/view.cpp index 8e42d052..1dd160c9 100644 --- a/src/view/artist/view.cpp +++ b/src/view/artist/view.cpp @@ -45,13 +45,6 @@ Artist::View::View(lib::spt::api &spotify, const std::string &artistId, lib::cac albumList = new Artist::AlbumsList(spotify, cache, httpClient, settings, this); tabs->addTab(albumList, "Discography"); - // Related artists - relatedList = new QListWidget(tabs); - relatedList->setEnabled(false); - QListWidget::connect(relatedList, &QListWidget::itemClicked, - this, &Artist::View::relatedClick); - tabs->addTab(relatedList, "Related"); - spotify.artist(this->artistId, [this](const lib::spt::artist &loadedArtist) { artistLoaded(loadedArtist); @@ -155,14 +148,22 @@ void Artist::View::topTracksLoaded(const std::vector &tracks) void Artist::View::relatedArtistsLoaded(const std::vector &artists) { + // Related artists are deprecated and not always available, + // lazily load tab instead in case it isn't + + relatedList = new QListWidget(tabs); + + connect(relatedList, &QListWidget::itemClicked, + this, &View::relatedClick); + + tabs->addTab(relatedList, "Related"); + for (const auto &related: artists) { auto *item = new QListWidgetItem(QString::fromStdString(related.name), relatedList); item->setData(static_cast(DataRole::ArtistId), QString::fromStdString(related.id)); } - - relatedList->setEnabled(true); } void Artist::View::relatedClick(QListWidgetItem *item) From 143b534b111faca9f02462642a27e99ad26a2ce1 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 30 Nov 2024 13:41:32 +0100 Subject: [PATCH 29/32] Remove deprecated audio features/analysis --- lib/include/lib/enum/audiofeature.hpp | 87 ----- lib/include/lib/enum/audiokey.hpp | 48 --- lib/include/lib/enum/audiomode.hpp | 17 - lib/include/lib/spotify/api.hpp | 7 - lib/include/lib/spotify/audiofeature.hpp | 87 ----- lib/include/lib/spotify/audiofeatures.hpp | 43 --- lib/src/spotify/audiofeature.cpp | 436 ---------------------- lib/src/spotify/audiofeatures.cpp | 119 ------ lib/src/spotifyapi/tracks.cpp | 19 - 9 files changed, 863 deletions(-) delete mode 100644 lib/include/lib/enum/audiofeature.hpp delete mode 100644 lib/include/lib/enum/audiokey.hpp delete mode 100644 lib/include/lib/enum/audiomode.hpp delete mode 100644 lib/include/lib/spotify/audiofeature.hpp delete mode 100644 lib/include/lib/spotify/audiofeatures.hpp delete mode 100644 lib/src/spotify/audiofeature.cpp delete mode 100644 lib/src/spotify/audiofeatures.cpp diff --git a/lib/include/lib/enum/audiofeature.hpp b/lib/include/lib/enum/audiofeature.hpp deleted file mode 100644 index 88fa1060..00000000 --- a/lib/include/lib/enum/audiofeature.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -namespace lib -{ - /** - * Category of audio analysis - */ - enum class audio_feature: unsigned char - { - /** - * Invalid, or missing, value - */ - unknown, - - /** - * Confidence track is acoustic - * @return 0.0 - 1.0 - */ - acousticness, - - /** - * How suitable track is for dancing - * @return 0.0 - 1.0 - */ - danceability, - - /** - * Track's intensity and activity - * @return 0.0 - 1.0 - */ - energy, - - /** - * Prediction of no vocals - * @return 0.0 - 1.0 - */ - instrumentalness, - - /** - * Key track is in - * @return audio_key - */ - key, - - /** - * Detects audience in recording - * @return 0.8> if track is live - */ - liveness, - - /** - * Average volume - * @return -60.0 - 0.0 dB - */ - loudness, - - /** - * Track modality - * @return audio_mode - */ - mode, - - /** - * Presence of spoken words (not vocals) - * @return <0.33 if music, 0.33 - 0.66 if music and speech, 0.66> if speech - */ - speechiness, - - /** - * Tempo - * @return BPM - */ - tempo, - - /** - * Overall time signature - * @return meter - */ - time_signature, - - /** - * How positive/happy the track is - * @return 0.0 - 1.0 - */ - valence, - }; -} diff --git a/lib/include/lib/enum/audiokey.hpp b/lib/include/lib/enum/audiokey.hpp deleted file mode 100644 index 509de1c7..00000000 --- a/lib/include/lib/enum/audiokey.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -namespace lib -{ - /** - * Key of track as pitch - * @note I'm no musician, so I have no idea what this means, or if it's accurate - * @see https://en.wikipedia.org/wiki/Pitch_class - */ - enum class audio_key: char - { - /** C */ - c = 0, - - /** C♯ or D♭ */ - c_sharp = 1, - - /** D */ - d = 2, - - /** D♯ or E♭ */ - d_sharp = 3, - - /** E */ - e = 4, - - /** F */ - f = 5, - - /** F♯ or G♭ */ - f_sharp = 6, - - /** G */ - g = 7, - - /** G♯ or A♭ */ - g_sharp = 8, - - /** A */ - a = 9, - - /** A♯, B♭ */ - a_sharp = 10, - - /** B */ - b = 11, - }; -} diff --git a/lib/include/lib/enum/audiomode.hpp b/lib/include/lib/enum/audiomode.hpp deleted file mode 100644 index c06f7956..00000000 --- a/lib/include/lib/enum/audiomode.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -namespace lib -{ - /** - * Audio mode, indicated by modality - * @note I'm no musician, so I have no idea what this means, or if it's accurate - */ - enum class audio_mode: char - { - /** Minor modality */ - minor = 0, - - /** Major modality */ - major = 1, - }; -} diff --git a/lib/include/lib/spotify/api.hpp b/lib/include/lib/spotify/api.hpp index 967d9a3e..b3f0181a 100644 --- a/lib/include/lib/spotify/api.hpp +++ b/lib/include/lib/spotify/api.hpp @@ -7,7 +7,6 @@ #include "lib/enum/repeatstate.hpp" #include "lib/spotify/album.hpp" #include "lib/spotify/artist.hpp" -#include "lib/spotify/audiofeatures.hpp" #include "lib/spotify/callback.hpp" #include "lib/spotify/episode.hpp" #include "lib/spotify/page.hpp" @@ -315,12 +314,6 @@ namespace lib void track(const std::string &track_id, lib::callback &callback); - void track_audio_features(const std::string &track_id, - lib::callback &callback); - - void track_audio_features(const std::vector &track_ids, - lib::callback> &callback); - //endregion //region User Profile diff --git a/lib/include/lib/spotify/audiofeature.hpp b/lib/include/lib/spotify/audiofeature.hpp deleted file mode 100644 index 2b41ccae..00000000 --- a/lib/include/lib/spotify/audiofeature.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -#include "lib/enum/audiofeature.hpp" -#include "lib/enum/audiokey.hpp" -#include "lib/enum/audiomode.hpp" -#include "lib/log.hpp" - -#include - -namespace lib -{ - namespace spt - { - class audio_feature - { - public: - /** - * Audio feature with value - * @param feature Category - * @param value Value - */ - audio_feature(lib::audio_feature feature, float value); - - /** Audio feature with key */ - explicit audio_feature(audio_key key); - - /** Audio feature with mode */ - explicit audio_feature(audio_mode mode); - - /** - * Get name of audio feature - */ - auto get_feature_string() const -> std::string; - - /** - * Get audio feature - */ - auto get_feature() const -> lib::audio_feature; - - /** - * Get name of value - */ - auto get_value_string() const -> std::string; - - /** - * Get value - */ - auto get_value() const -> float; - - /** - * Get minimum possible value - */ - auto get_min() const -> float; - - /** - * Get maximum possible value - */ - auto get_max() const -> float; - - /** - * Get value description - */ - auto get_description() const -> std::string; - - private: - lib::audio_feature feature = lib::audio_feature::unknown; - float minimum = 0.F; - float maximum = 1.F; - float value = 0.F; - std::string name; - - auto acousticness() const -> std::string; - auto danceability() const -> std::string; - auto energy() const -> std::string; - auto instrumentalness() const -> std::string; - auto liveness() const -> std::string; - auto loudness(float &min, float &max) const -> std::string; - auto speechiness() const -> std::string; - auto tempo(float &max) const -> std::string; - auto valence() const -> std::string; - auto time_signature(float &max) const -> std::string; - - static auto to_string(audio_key key) -> std::string; - static auto to_string(audio_mode mode) -> std::string; - }; - } -} diff --git a/lib/include/lib/spotify/audiofeatures.hpp b/lib/include/lib/spotify/audiofeatures.hpp deleted file mode 100644 index 6083b76e..00000000 --- a/lib/include/lib/spotify/audiofeatures.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "lib/format.hpp" -#include "lib/strings.hpp" -#include "lib/enum/audiokey.hpp" -#include "lib/enum/audiomode.hpp" -#include "lib/spotify/audiofeature.hpp" -#include "thirdparty/json.hpp" - -#include -#include -#include - -namespace lib -{ - namespace spt - { - /** - * Audio analysis features - */ - class audio_features - { - public: - audio_features() = default; - - auto items() const -> const std::vector &; - - void add(lib::audio_feature feature, float value); - void add(audio_key key); - void add(audio_mode mode); - - static auto to_audio_feature(const std::string &feature) -> lib::audio_feature; - - std::string track_uri; - - private: - std::vector values; - }; - - /** JSON -> Audio features */ - void from_json(const nlohmann::json &j, audio_features &a); - } -} diff --git a/lib/src/spotify/audiofeature.cpp b/lib/src/spotify/audiofeature.cpp deleted file mode 100644 index 4d8a9542..00000000 --- a/lib/src/spotify/audiofeature.cpp +++ /dev/null @@ -1,436 +0,0 @@ -#include "lib/spotify/audiofeature.hpp" - -/* - * The values here are based off the average distribution, not necessarily - * what actually "makes sense". The data used to be available from the - * Web API, but has been removed in the new design. - */ - -lib::spt::audio_feature::audio_feature(lib::audio_feature feature, float value) - : feature(feature), - value(value) -{ - switch (feature) - { - case lib::audio_feature::acousticness: - name = acousticness(); - break; - - case lib::audio_feature::danceability: - name = danceability(); - break; - - case lib::audio_feature::energy: - name = energy(); - break; - - case lib::audio_feature::instrumentalness: - name = instrumentalness(); - break; - - case lib::audio_feature::key: - break; - - case lib::audio_feature::liveness: - name = liveness(); - break; - - case lib::audio_feature::loudness: - name = loudness(minimum, maximum); - break; - - case lib::audio_feature::mode: - break; - - case lib::audio_feature::speechiness: - name = speechiness(); - break; - - case lib::audio_feature::tempo: - name = tempo(maximum); - break; - - case lib::audio_feature::valence: - name = valence(); - break; - - case lib::audio_feature::time_signature: - name = time_signature(maximum); - break; - - default: - lib::log::warn("Invalid value: {}", get_feature_string()); - break; - } -} - -lib::spt::audio_feature::audio_feature(lib::audio_key key) - : feature(lib::audio_feature::key), - value(static_cast(key)), - name(to_string(key)) -{ - // These values don't really make sense here, - // but it looks better - minimum = static_cast(audio_key::c); - maximum = static_cast(audio_key::b); -} - -lib::spt::audio_feature::audio_feature(lib::audio_mode mode) - : feature(lib::audio_feature::mode), - value(static_cast(mode)), - name(to_string(mode)) -{ - // These values don't really make sense here, - // but it looks better - minimum = static_cast(audio_mode::minor); - maximum = static_cast(audio_mode::major); -} - -auto lib::spt::audio_feature::acousticness() const -> std::string -{ - constexpr float no = 0.1F; - constexpr float probably_no = 0.4F; - constexpr float probably_yes = 0.7F; - - if (value <= no) - { - return "Not acoustic"; - } - - if (value <= probably_no) - { - return "Probably not acoustic"; - } - - if (value <= probably_yes) - { - return "Probably acoustic"; - } - - return "Acoustic"; -} - -auto lib::spt::audio_feature::danceability() const -> std::string -{ - constexpr float no_lower = 0.4F; - constexpr float no_higher = 0.85F; - - return value <= no_lower || value >= no_higher - ? "Not suitable" - : "Suitable"; -} - -auto lib::spt::audio_feature::energy() const -> std::string -{ - constexpr float very_low = 0.4F; - constexpr float low = 0.65F; - constexpr float medium = 0.8F; - constexpr float high = 0.95F; - - if (value <= very_low) - { - return "Very low"; - } - - if (value <= low) - { - return "Low"; - } - - if (value <= medium) - { - return "Medium"; - } - - if (value <= high) - { - return "High"; - } - - return "Very high"; -} - -auto lib::spt::audio_feature::instrumentalness() const -> std::string -{ - constexpr float no = 0.05F; - - return value <= no - ? "Not instrumental" - : "Instrumental"; -} - -auto lib::spt::audio_feature::liveness() const -> std::string -{ - constexpr float no = 0.05F; - constexpr float probably_no = 0.15F; - constexpr float probably_yes = 0.4F; - - if (value <= no) - { - return "Not live"; - } - - if (value <= probably_no) - { - return "Probably not live"; - } - - if (value <= probably_yes) - { - return "Probably live"; - } - - return "Live"; -} - -auto lib::spt::audio_feature::loudness(float &min, float &max) const -> std::string -{ - constexpr float min_value = -60.F; - constexpr float max_value = 0.F; - - min = min_value; - max = max_value; - - constexpr float very_quiet = -25.F; - constexpr float quiet = -15.F; - constexpr float loud = -2.5F; - - if (value <= very_quiet) - { - return "Very quiet"; - } - - if (value <= quiet) - { - return "Quiet"; - } - - if (value <= loud) - { - return "Loud"; - } - - return "Very loud"; -} - -auto lib::spt::audio_feature::speechiness() const -> std::string -{ - constexpr float speech = 0.6F; - constexpr float music = 0.8F; - - if (value <= speech) - { - return "Speech"; - } - - if (value >= music) - { - return "Music"; - } - - return "Mixed"; -} - -auto lib::spt::audio_feature::tempo(float &max) const -> std::string -{ - constexpr float max_value = 250.F; - max = max_value; - - constexpr float slow = 60.F; - constexpr float fast = 150.F; - - if (value <= slow) - { - return "Slow"; - } - - if (value >= fast) - { - return "Fast"; - } - - return "Average"; -} - -auto lib::spt::audio_feature::valence() const -> std::string -{ - constexpr float sad = 0.2F; - constexpr float happy = 0.7F; - - if (value <= sad) - { - return "Sad"; - } - - if (value >= happy) - { - return "Happy"; - } - - return "Mixed"; -} - -auto lib::spt::audio_feature::time_signature(float &max) const -> std::string -{ - // Time signature doesn't really have a maximum value? - max = value; - - return lib::fmt::format("{} m", static_cast(value)); -} - -auto lib::spt::audio_feature::get_feature_string() const -> std::string -{ - switch (feature) - { - case lib::audio_feature::unknown: - return "Unknown"; - - case lib::audio_feature::acousticness: - return "Acousticness"; - - case lib::audio_feature::danceability: - return "Danceability"; - - case lib::audio_feature::energy: - return "Energy"; - - case lib::audio_feature::instrumentalness: - return "Instrumentalness"; - - case lib::audio_feature::key: - return "Key"; - - case lib::audio_feature::liveness: - return "Liveness"; - - case lib::audio_feature::loudness: - return "Loudness"; - - case lib::audio_feature::mode: - return "Mode"; - - case lib::audio_feature::speechiness: - return "Speechiness"; - - case lib::audio_feature::tempo: - return "Tempo"; - - case lib::audio_feature::time_signature: - return "Time signature"; - - case lib::audio_feature::valence: - return "Valence"; - - default: - return "Unknown"; - } -} - -auto lib::spt::audio_feature::get_feature() const -> lib::audio_feature -{ - return feature; -} - -auto lib::spt::audio_feature::get_value_string() const -> std::string -{ - return name; -} - -auto lib::spt::audio_feature::get_value() const -> float -{ - return value; -} - -auto lib::spt::audio_feature::get_min() const -> float -{ - return minimum; -} - -auto lib::spt::audio_feature::get_max() const -> float -{ - return maximum; -} - -auto lib::spt::audio_feature::get_description() const -> std::string -{ - if (get_min() == 0.F && get_max() == 1.F) - { - return lib::fmt::format("{}%", get_value() * 100.F); - } - - std::string suffix; - if (feature == lib::audio_feature::loudness) - { - suffix = " dB"; - } - else if (feature == lib::audio_feature::tempo) - { - suffix = " BPM"; - } - else if (feature == lib::audio_feature::time_signature) - { - suffix = " m"; - } - - return lib::fmt::format("{}{}", get_value(), suffix); -} - -auto lib::spt::audio_feature::to_string(lib::audio_key key) -> std::string -{ - switch (key) - { - case lib::audio_key::c: - return "C"; - - case lib::audio_key::c_sharp: - return "C♯, D♭"; - - case lib::audio_key::d: - return "D"; - - case lib::audio_key::d_sharp: - return "D♯, E♭"; - - case lib::audio_key::e: - return "E"; - - case lib::audio_key::f: - return "F"; - - case lib::audio_key::f_sharp: - return "F♯, G♭"; - - case lib::audio_key::g: - return "G"; - - case lib::audio_key::g_sharp: - return "G♯, A♭"; - - case lib::audio_key::a: - return "A"; - - case lib::audio_key::a_sharp: - return "A♯, B♭"; - - case lib::audio_key::b: - return "B"; - - default: - return "?"; - } -} - -auto lib::spt::audio_feature::to_string(lib::audio_mode mode) -> std::string -{ - switch (mode) - { - case lib::audio_mode::minor: - return "Minor"; - - case lib::audio_mode::major: - return "Major"; - - default: - return "Unknown"; - } -} diff --git a/lib/src/spotify/audiofeatures.cpp b/lib/src/spotify/audiofeatures.cpp deleted file mode 100644 index 9aa82532..00000000 --- a/lib/src/spotify/audiofeatures.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "lib/spotify/audiofeatures.hpp" - -auto lib::spt::audio_features::items() const -> const std::vector & -{ - return values; -} - -void lib::spt::audio_features::add(lib::audio_feature feature, float value) -{ - values.emplace_back(feature, value); -} - -void lib::spt::audio_features::add(audio_key key) -{ - values.emplace_back(key); -} - -void lib::spt::audio_features::add(audio_mode mode) -{ - values.emplace_back(mode); -} - -auto lib::spt::audio_features::to_audio_feature(const std::string &feature) -> lib::audio_feature -{ - if (feature == "acousticness") - { - return lib::audio_feature::acousticness; - } - - if (feature == "danceability") - { - return lib::audio_feature::danceability; - } - - if (feature == "energy") - { - return lib::audio_feature::energy; - } - - if (feature == "instrumentalness") - { - return lib::audio_feature::instrumentalness; - } - - if (feature == "key") - { - return lib::audio_feature::key; - } - - if (feature == "liveness") - { - return lib::audio_feature::liveness; - } - - if (feature == "loudness") - { - return lib::audio_feature::loudness; - } - - if (feature == "mode") - { - return lib::audio_feature::mode; - } - - if (feature == "speechiness") - { - return lib::audio_feature::speechiness; - } - - if (feature == "tempo") - { - return lib::audio_feature::tempo; - } - - if (feature == "time_signature") - { - return lib::audio_feature::time_signature; - } - - if (feature == "valence") - { - return lib::audio_feature::valence; - } - - return lib::audio_feature::unknown; -} - -void lib::spt::from_json(const nlohmann::json &j, audio_features &a) -{ - if (!j.is_object()) - { - return; - } - - j.at("uri").get_to(a.track_uri); - - for (const auto &item: j.items()) - { - const auto feature = lib::spt::audio_features::to_audio_feature(item.key()); - if (feature == lib::audio_feature::unknown) - { - continue; - } - - if (feature == lib::audio_feature::key) - { - a.add(item.value().get()); - continue; - } - - if (feature == lib::audio_feature::mode) - { - a.add(item.value().get()); - continue; - } - - a.add(feature, item.value().get()); - } -} diff --git a/lib/src/spotifyapi/tracks.cpp b/lib/src/spotifyapi/tracks.cpp index 96819e60..4eaa03ef 100644 --- a/lib/src/spotifyapi/tracks.cpp +++ b/lib/src/spotifyapi/tracks.cpp @@ -2,28 +2,9 @@ // Currently unavailable: // tracks -// audio-analysis/{id} void lib::spt::api::track(const std::string &track_id, lib::callback &callback) { get(lib::fmt::format("tracks/{}", track_id), callback); } - -void lib::spt::api::track_audio_features(const std::string &track_id, - lib::callback &callback) -{ - get(lib::fmt::format("audio-features/{}", - lib::spt::uri_to_id(track_id)), callback); -} - -void lib::spt::api::track_audio_features(const std::vector &track_ids, - lib::callback> &callback) -{ - get(lib::fmt::format("audio-features?ids={}", - lib::strings::join(track_ids, ",")), - [callback](const nlohmann::json &json) - { - callback(json.at("audio_features")); - }); -} From ff18160e2001b8586895ab493082dac5f78480a4 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 30 Nov 2024 13:42:06 +0100 Subject: [PATCH 30/32] Remove deprecated audio features/analysis from side panel --- src/enum/sidepaneltype.hpp | 5 -- src/view/CMakeLists.txt | 1 - src/view/audiofeatures.cpp | 108 ------------------------------------ src/view/audiofeatures.hpp | 28 ---------- src/view/sidepanel/view.cpp | 34 ------------ src/view/sidepanel/view.hpp | 2 - 6 files changed, 178 deletions(-) delete mode 100644 src/view/audiofeatures.cpp delete mode 100644 src/view/audiofeatures.hpp diff --git a/src/enum/sidepaneltype.hpp b/src/enum/sidepaneltype.hpp index 912910ce..ab5d0b7a 100644 --- a/src/enum/sidepaneltype.hpp +++ b/src/enum/sidepaneltype.hpp @@ -15,11 +15,6 @@ enum class SidePanelType: char */ Search, - /** - * Audio features, paired with track ID - */ - AudioFeatures, - /** * Lyrics, paired with track ID */ diff --git a/src/view/CMakeLists.txt b/src/view/CMakeLists.txt index ba9fa81b..190be7b0 100644 --- a/src/view/CMakeLists.txt +++ b/src/view/CMakeLists.txt @@ -5,7 +5,6 @@ add_subdirectory(search) add_subdirectory(sidepanel) target_sources(${PROJECT_NAME} PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/audiofeatures.cpp ${CMAKE_CURRENT_SOURCE_DIR}/cacheview.cpp ${CMAKE_CURRENT_SOURCE_DIR}/configview.cpp ${CMAKE_CURRENT_SOURCE_DIR}/crashes.cpp diff --git a/src/view/audiofeatures.cpp b/src/view/audiofeatures.cpp deleted file mode 100644 index 93f6518c..00000000 --- a/src/view/audiofeatures.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include "view/audiofeatures.hpp" - -View::AudioFeatures::AudioFeatures(QWidget *parent) - : QTreeWidget(parent) -{ - setEnabled(false); - - setEditTriggers(QAbstractItemView::NoEditTriggers); - header()->hide(); - setSelectionMode(QAbstractItemView::NoSelection); - setRootIsDecorated(false); - setAllColumnsShowFocus(true); - setColumnCount(2); - header()->setSectionResizeMode(QHeaderView::ResizeToContents); -} - -View::AudioFeatures::AudioFeatures(lib::spt::api &spotify, - const std::string &trackId, QWidget *parent) - : AudioFeatures(parent) -{ - spotify.track_audio_features(trackId, - [this](const lib::spt::audio_features &features) - { - loaded(features); - }); -} - -View::AudioFeatures::AudioFeatures(lib::spt::api &spotify, - const std::vector &trackIds, QWidget *parent) - : AudioFeatures(parent) -{ - spotify.track_audio_features(trackIds, - [this](const std::vector &features) - { - loaded(features); - }); -} - -void View::AudioFeatures::loaded(const lib::spt::audio_features &features) -{ - for (const auto &item: features.items()) - { - auto *treeItem = new QTreeWidgetItem(this, { - QString::fromStdString(item.get_feature_string()), - QString::fromStdString(item.get_value_string()), - }); - - treeItem->setToolTip(1, - QString::fromStdString(item.get_description())); - } - - setEnabled(true); -} - -void View::AudioFeatures::loaded(const std::vector &features) -{ - if (features.empty()) - { - return; - } - - for (const auto &frontItem: features.front().items()) - { - const auto feature = frontItem.get_feature(); - std::vector values; - std::string tooltip; - constexpr size_t maxTooltip = 5; - - // TODO: These can't easily be averaged - if (feature == lib::audio_feature::key - || feature == lib::audio_feature::mode) - { - continue; - } - - // Loop through other tracks - for (const auto ¤t: features) - { - // Loop through features for that track - for (const auto &item: current.items()) - { - if (item.get_feature() == feature) - { - values.push_back(item.get_value()); - if (features.size() <= maxTooltip) - { - tooltip += lib::fmt::format("{}\n", item.get_description()); - } - break; - } - } - } - - const auto value = lib::vector::average(values); - lib::spt::audio_feature item(feature, value); - - auto *treeItem = new QTreeWidgetItem(this, { - QString::fromStdString(item.get_feature_string()), - QString::fromStdString(item.get_value_string()), - }); - - treeItem->setToolTip(1, QString::fromStdString(tooltip - + (tooltip.empty() ? "" : "= ") - + item.get_description())); - } - - setEnabled(true); -} diff --git a/src/view/audiofeatures.hpp b/src/view/audiofeatures.hpp deleted file mode 100644 index 7a6642d4..00000000 --- a/src/view/audiofeatures.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "lib/spotify/api.hpp" - -#include -#include -#include -#include - -namespace View -{ - class AudioFeatures: public QTreeWidget - { - Q_OBJECT - - public: - AudioFeatures(lib::spt::api &spotify, const std::string &trackId, QWidget *parent); - - AudioFeatures(lib::spt::api &spotify, const std::vector &trackIds, - QWidget *parent); - - private: - explicit AudioFeatures(QWidget *parent); - - void loaded(const lib::spt::audio_features &features); - void loaded(const std::vector &features); - }; -} diff --git a/src/view/sidepanel/view.cpp b/src/view/sidepanel/view.cpp index 496ef72b..621242e3 100644 --- a/src/view/sidepanel/view.cpp +++ b/src/view/sidepanel/view.cpp @@ -32,37 +32,6 @@ void SidePanel::View::openArtist(const std::string &artistId) SidePanelType::Artist, QString::fromStdString(artistId)); } -void SidePanel::View::openAudioFeatures(const std::vector &tracks) -{ - QWidget *view; - QString tabTitle; - QString tabId; - - if (tracks.size() == 1) - { - const auto &track = tracks.front(); - view = new ::View::AudioFeatures(spotify, track.id, this); - tabTitle = QString::fromStdString(track.title()); - tabId = QString::fromStdString(track.id); - } - else - { - std::vector trackIds; - trackIds.reserve(tracks.size()); - for (const auto &track: tracks) - { - trackIds.push_back(track.id); - tabId += QString::fromStdString(track.id); - } - - view = new ::View::AudioFeatures(spotify, trackIds, this); - tabTitle = QString("%1 tracks").arg(tracks.size()); - } - - addTab(view, "view-statistics", tabTitle, - SidePanelType::AudioFeatures, tabId); -} - void SidePanel::View::openLyrics(const lib::spt::track &track) { auto *view = new ::View::Lyrics(httpClient, cache, this); @@ -111,9 +80,6 @@ auto SidePanel::View::findTab(SidePanelType type, const QString &name) -> QWidge case SidePanelType::Search: return nullptr; - case SidePanelType::AudioFeatures: - return find<::View::AudioFeatures *>(name); - case SidePanelType::Lyrics: return find<::View::Lyrics *>(name); diff --git a/src/view/sidepanel/view.hpp b/src/view/sidepanel/view.hpp index ad9c82da..c01631b6 100644 --- a/src/view/sidepanel/view.hpp +++ b/src/view/sidepanel/view.hpp @@ -3,7 +3,6 @@ #include "lib/spotify/track.hpp" #include "view/artist/view.hpp" #include "view/search/view.hpp" -#include "view/audiofeatures.hpp" #include "view/lyrics.hpp" #include "view/sidepanel/title.hpp" #include "enum/sidepaneltype.hpp" @@ -22,7 +21,6 @@ namespace SidePanel const lib::http_client &httpClient, QWidget *parent); void openArtist(const std::string &artistId); - void openAudioFeatures(const std::vector &tracks); void openLyrics(const lib::spt::track &track); void openLyrics(int lyricsId); From d4285778251bb60772af9347943996c7610eaa98 Mon Sep 17 00:00:00 2001 From: kraxarn Date: Sat, 30 Nov 2024 13:42:34 +0100 Subject: [PATCH 31/32] Remove deprecated audio features/analysis from track menu --- src/menu/track.cpp | 25 ------------------------- src/menu/track.hpp | 1 - 2 files changed, 26 deletions(-) diff --git a/src/menu/track.cpp b/src/menu/track.cpp index c1d4e7bb..204ca647 100644 --- a/src/menu/track.cpp +++ b/src/menu/track.cpp @@ -52,15 +52,6 @@ Menu::Track::Track(const QList &tracks, lib::spt::api &spotify, addSeparator(); } - if (tracks.length() <= 100) - { - const auto icon = Icon::get(QStringLiteral("view-statistics")); - const auto text = QStringLiteral("Audio features"); - auto *trackFeatures = addAction(icon, text); - QAction::connect(trackFeatures, &QAction::triggered, - this, &Menu::Track::onAudioFeatures); - } - if (isSingle) { auto *lyrics = addAction(Icon::get("view-media-lyrics"), "Lyrics"); @@ -410,22 +401,6 @@ void Menu::Track::onRemoveFromPlaylist(bool /*checked*/) }); } -void Menu::Track::onAudioFeatures(bool /*checked*/) -{ - if (tracks.empty()) - { - return; - } - - auto *sidePanel = SidePanel::View::find(this); - if (sidePanel == nullptr) - { - return; - } - - sidePanel->openAudioFeatures(getTracks()); -} - void Menu::Track::onLyrics(bool /*checked*/) { if (tracks.empty()) diff --git a/src/menu/track.hpp b/src/menu/track.hpp index 952ee9ba..790614e1 100644 --- a/src/menu/track.hpp +++ b/src/menu/track.hpp @@ -70,7 +70,6 @@ namespace Menu /** All tracks have the same album */ auto allSameAlbum() const -> bool; - void onAudioFeatures(bool checked); void onLyrics(bool checked); void onCopySongLink(bool checked); void onCopySongName(bool checked); From 0045d7ee7a8bcea77614f67d689faa6529529f7f Mon Sep 17 00:00:00 2001 From: Matthew McElroy Date: Sun, 1 Dec 2024 02:09:09 +1000 Subject: [PATCH 32/32] fix: Off-by-one error for added time hidden state (#267) --- src/list/tracks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/list/tracks.cpp b/src/list/tracks.cpp index 2f6c08b0..4e7ef705 100644 --- a/src/list/tracks.cpp +++ b/src/list/tracks.cpp @@ -528,7 +528,7 @@ auto List::Tracks::load(const lib::spt::page &page, constexpr int addedColumn = static_cast(Column::Added); const auto &hiddenHeaders = settings.general.hidden_song_headers; - const auto isAddedHidden = lib::set::contains(hiddenHeaders, addedColumn); + const auto isAddedHidden = lib::set::contains(hiddenHeaders, addedColumn - 1); // Hide until track with date is inserted header()->setSectionHidden(addedColumn, true);