diff --git a/src/api/ApiClient.cpp b/src/api/ApiClient.cpp new file mode 100644 index 0000000..25d992a --- /dev/null +++ b/src/api/ApiClient.cpp @@ -0,0 +1,87 @@ +#include "ApiClient.h" + +#include +#include +#include +#include +#include +#include + +ApiClient::ApiClient(QObject* parent) : QObject(parent) {} + +static QString md5Hex(const QByteArray& data) { + return QString( + QCryptographicHash::hash(data, QCryptographicHash::Md5).toHex()); +} + +// Subsonic/OpenSubsonic Token-Auth: t = md5(password + salt), s = salt +// :contentReference[oaicite:5]{index=5} +QString ApiClient::buildAuthQuery(const QString& user, + const QString& password) const { + const QString salt = + QString::number(QRandomGenerator::global()->generate(), 16); + const QString token = md5Hex((password + salt).toUtf8()); + + QUrlQuery q; + q.addQueryItem("u", user); + q.addQueryItem("t", token); + q.addQueryItem("s", salt); + q.addQueryItem("v", "1.16.1"); // Navidrome ist kompatibel zu 1.16.1 + // :contentReference[oaicite:6]{index=6} + q.addQueryItem("c", "myQtClient"); + q.addQueryItem("f", "json"); + return q.toString(QUrl::FullyEncoded); +} + +void ApiClient::setLastStatus(const QString& s) { + if (m_lastStatus == s) return; + m_lastStatus = s; + emit lastStatusChanged(); +} + +void ApiClient::ping(const QString& host, const QString& user, + const QString& password) { + if (host.trimmed().isEmpty()) { + emit pingFinished(false, "Host ist leer."); + return; + } + + QUrl base(host); + if (!base.isValid()) { + emit pingFinished(false, "Host-URL ist ungültig."); + return; + } + + // Sauber zusammensetzen, egal ob host mit/ohne trailing slash kommt + QUrl url(base.toString().remove(QRegularExpression("/+$")) + + "/rest/ping.view"); + QUrlQuery q(buildAuthQuery(user, password)); + url.setQuery(q); + + setLastStatus("Sende Ping…"); + + QNetworkRequest req(url); + req.setHeader(QNetworkRequest::UserAgentHeader, "navidrome_client/0.1"); + + QNetworkReply* reply = m_manager.get(req); + + connect(reply, &QNetworkReply::finished, this, [this, reply]() { + const auto err = reply->error(); + const QByteArray body = reply->readAll(); + reply->deleteLater(); + + if (err != QNetworkReply::NoError) { + const QString msg = "Netzwerkfehler: " + reply->errorString(); + setLastStatus(msg); + emit pingFinished(false, msg); + return; + } + + // Fürs Erste: “kommt was zurück?” reicht. Später kannst du JSON parsen und + // "status":"ok" prüfen. + const QString msg = + "Ping OK, Antwort: " + QString::fromUtf8(body.left(300)); + setLastStatus("Ping OK"); + emit pingFinished(true, msg); + }); +} diff --git a/src/api/ApiClient.h b/src/api/ApiClient.h index e69de29..53403a8 100644 --- a/src/api/ApiClient.h +++ b/src/api/ApiClient.h @@ -0,0 +1,31 @@ +#ifndef APICLIENT_H +#define APICLIENT_H + +#include +#include + +class ApiClient : public QObject { + Q_OBJECT + Q_PROPERTY(QString lastStatus READ lastStatus NOTIFY lastStatusChanged) + + public: + explicit ApiClient(QObject* parent = nullptr); + + Q_INVOKABLE void ping(const QString& host, const QString& user, + const QString& password); + + QString lastStatus() const { return m_lastStatus; } + + signals: + void pingFinished(bool ok, QString message); + void lastStatusChanged(); + + private: + QString buildAuthQuery(const QString& user, const QString& password) const; + void setLastStatus(const QString& s); + + QNetworkAccessManager m_manager; + QString m_lastStatus; +}; + +#endif