init
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
qt-navidrome-client/.qtcreator
|
||||||
|
qt-navidrome-client/build
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{ lib, stdenv, cmake, ninja, pkg-config, qt6 }:
|
||||||
|
|
||||||
|
stdenv.mkDerivation {
|
||||||
|
pname = "qt-example";
|
||||||
|
version = "1.0";
|
||||||
|
|
||||||
|
src = ./qt-example;
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
cmake
|
||||||
|
ninja
|
||||||
|
pkg-config
|
||||||
|
qt6.wrapQtAppsHook
|
||||||
|
];
|
||||||
|
|
||||||
|
buildInputs = [
|
||||||
|
qt6.qtbase
|
||||||
|
qt6.qtdeclarative
|
||||||
|
qt6.qtwayland
|
||||||
|
];
|
||||||
|
|
||||||
|
cmakeFlags = [
|
||||||
|
"-DCMAKE_BUILD_TYPE=Release"
|
||||||
|
];
|
||||||
|
}
|
||||||
Generated
+27
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1767379071,
|
||||||
|
"narHash": "sha256-EgE0pxsrW9jp9YFMkHL9JMXxcqi/OoumPJYwf+Okucw=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "fb7944c166a3b630f177938e478f0378e64ce108",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs }:
|
||||||
|
let
|
||||||
|
system = "x86_64-linux";
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
|
||||||
|
pkg = pkgs.callPackage ./build.nix {};
|
||||||
|
in {
|
||||||
|
packages.${system}.default = pkg;
|
||||||
|
|
||||||
|
apps.${system}.default = {
|
||||||
|
type = "app";
|
||||||
|
program = "${pkg}/bin/appuntitled";
|
||||||
|
};
|
||||||
|
|
||||||
|
devShells.${system}.default = pkgs.mkShell {
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
cmake
|
||||||
|
gdb
|
||||||
|
qt6.qtbase
|
||||||
|
qt6.qtdeclarative
|
||||||
|
qt6.qtwayland
|
||||||
|
qtcreator
|
||||||
|
|
||||||
|
qt6.wrapQtAppsHook
|
||||||
|
makeWrapper
|
||||||
|
bashInteractive
|
||||||
|
];
|
||||||
|
shellHook = ''
|
||||||
|
export QT_QPA_PLATFORM=wayland
|
||||||
|
bashdir=$(mktemp -d)
|
||||||
|
makeWrapper "$(type -p bash)" "$bashdir/bash" "''${qtWrapperArgs[@]}"
|
||||||
|
exec "$bashdir/bash"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
project(navidrome_client VERSION 0.1 LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS Quick Network)
|
||||||
|
|
||||||
|
qt_standard_project_setup(REQUIRES 6.8)
|
||||||
|
|
||||||
|
qt_add_executable(appnavidrome_client
|
||||||
|
src/main.cpp
|
||||||
|
src/navidrome/NavidromeApi.cpp
|
||||||
|
src/navidrome/NavidromeApi.h
|
||||||
|
)
|
||||||
|
|
||||||
|
qt_add_qml_module(appnavidrome_client
|
||||||
|
URI Navidrome
|
||||||
|
VERSION 1.0
|
||||||
|
QML_FILES
|
||||||
|
qml/Main.qml
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties(appnavidrome_client PROPERTIES
|
||||||
|
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
|
||||||
|
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
|
||||||
|
MACOSX_BUNDLE TRUE
|
||||||
|
WIN32_EXECUTABLE TRUE
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(appnavidrome_client
|
||||||
|
PRIVATE src
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(appnavidrome_client
|
||||||
|
PRIVATE Qt6::Quick Qt6::Network
|
||||||
|
)
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
install(TARGETS appnavidrome_client
|
||||||
|
BUNDLE DESTINATION .
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"cmakeMinimumRequired": {
|
||||||
|
"major": 3,
|
||||||
|
"minor": 20,
|
||||||
|
"patch": 0
|
||||||
|
},
|
||||||
|
"configurePresets": [
|
||||||
|
{
|
||||||
|
"name": "dev-debug",
|
||||||
|
"displayName": "Debug (Nix Umgebung)",
|
||||||
|
"description": "Debug Build mit Compiler aus der Flake",
|
||||||
|
"generator": "Unix Makefiles",
|
||||||
|
"binaryDir": "${sourceDir}/build",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug",
|
||||||
|
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
|
||||||
|
"CMAKE_PREFIX_PATH": "$env{CMAKE_PREFIX_PATH}"
|
||||||
|
},
|
||||||
|
"environment": {
|
||||||
|
"QT_QPA_PLATFORM": "wayland;xcb"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buildPresets": [
|
||||||
|
{
|
||||||
|
"name": "build-debug",
|
||||||
|
"configurePreset": "dev-debug"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
ApplicationWindow {
|
||||||
|
id: win
|
||||||
|
width: 420
|
||||||
|
height: 320
|
||||||
|
visible: true
|
||||||
|
title: "Navidrome Login"
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 16
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "Server"
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: urlField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
placeholderText: "https://navidrome.timvandenboom.eth64.de"
|
||||||
|
text: "https://navidrome.timvandenboom.eth64.de"
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "Username"
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: userField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
placeholderText: "username"
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "Password"
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: passField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
placeholderText: "password"
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: navidromeApi.busy ? "Connecting..." : "Login (Ping)"
|
||||||
|
enabled: !navidromeApi.busy
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
navidromeApi.ping(urlField.text, userField.text, passField.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
height: 1
|
||||||
|
opacity: 0.2
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
|
||||||
|
text: {
|
||||||
|
if (navidromeApi.busy) return "Verbinde…"
|
||||||
|
if (navidromeApi.lastError.length > 0) return "❌ " + navidromeApi.lastError
|
||||||
|
if (navidromeApi.lastStatus.length > 0) return "✅ status: " + navidromeApi.lastStatus
|
||||||
|
return "Noch nicht verbunden."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QQmlApplicationEngine>
|
||||||
|
#include <QQmlContext>
|
||||||
|
|
||||||
|
#include "navidrome/NavidromeApi.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QGuiApplication app(argc, argv);
|
||||||
|
|
||||||
|
QQmlApplicationEngine engine;
|
||||||
|
|
||||||
|
NavidromeApi api;
|
||||||
|
engine.rootContext()->setContextProperty("navidromeApi", &api);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
&engine, &QQmlApplicationEngine::objectCreationFailed,
|
||||||
|
&app, []() { QCoreApplication::exit(1); },
|
||||||
|
Qt::QueuedConnection
|
||||||
|
);
|
||||||
|
|
||||||
|
engine.loadFromModule("Navidrome", "Main");
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
#include "NavidromeApi.h"
|
||||||
|
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QUrlQuery>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
NavidromeApi::NavidromeApi(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void NavidromeApi::setBusy(bool v)
|
||||||
|
{
|
||||||
|
if (m_busy == v) return;
|
||||||
|
m_busy = v;
|
||||||
|
emit busyChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavidromeApi::setLastError(const QString &e)
|
||||||
|
{
|
||||||
|
if (m_lastError == e) return;
|
||||||
|
m_lastError = e;
|
||||||
|
emit lastErrorChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavidromeApi::setLastStatus(const QString &s)
|
||||||
|
{
|
||||||
|
if (m_lastStatus == s) return;
|
||||||
|
m_lastStatus = s;
|
||||||
|
emit lastStatusChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavidromeApi::ping(const QString &baseUrl,
|
||||||
|
const QString &user,
|
||||||
|
const QString &password)
|
||||||
|
{
|
||||||
|
setLastError({});
|
||||||
|
setLastStatus({});
|
||||||
|
|
||||||
|
QUrl url(baseUrl.trimmed());
|
||||||
|
if (!url.isValid() || url.scheme().isEmpty()) {
|
||||||
|
setLastError("Ungültige URL (z.B. http://localhost:4533)");
|
||||||
|
emit pingFailed(lastError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /rest/ping.view anhängen
|
||||||
|
url.setPath("/rest/ping.view");
|
||||||
|
|
||||||
|
QUrlQuery q;
|
||||||
|
q.addQueryItem("u", user);
|
||||||
|
q.addQueryItem("p", password); // später Token-Auth (t/s)
|
||||||
|
q.addQueryItem("v", "1.16.1");
|
||||||
|
q.addQueryItem("c", "qt-navidrome");
|
||||||
|
q.addQueryItem("f", "json");
|
||||||
|
url.setQuery(q);
|
||||||
|
|
||||||
|
setBusy(true);
|
||||||
|
|
||||||
|
auto *reply = m_net.get(QNetworkRequest(url));
|
||||||
|
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
||||||
|
setBusy(false);
|
||||||
|
|
||||||
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
|
setLastError(reply->errorString());
|
||||||
|
emit pingFailed(lastError());
|
||||||
|
reply->deleteLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional: JSON prüfen, ob status == "ok"
|
||||||
|
const QByteArray body = reply->readAll();
|
||||||
|
const auto doc = QJsonDocument::fromJson(body);
|
||||||
|
if (doc.isObject()) {
|
||||||
|
const auto root = doc.object();
|
||||||
|
const auto ssr = root.value("subsonic-response").toObject();
|
||||||
|
const QString status = ssr.value("status").toString();
|
||||||
|
if (!status.isEmpty()) setLastStatus(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastStatus().isEmpty())
|
||||||
|
setLastStatus("ok");
|
||||||
|
|
||||||
|
emit pingOk();
|
||||||
|
reply->deleteLater();
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
class NavidromeApi : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
|
||||||
|
Q_PROPERTY(QString lastError READ lastError NOTIFY lastErrorChanged)
|
||||||
|
Q_PROPERTY(QString lastStatus READ lastStatus NOTIFY lastStatusChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit NavidromeApi(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
// QML Entry: baseUrl als String, damit TextField direkt passt
|
||||||
|
Q_INVOKABLE void ping(const QString &baseUrl,
|
||||||
|
const QString &user,
|
||||||
|
const QString &password);
|
||||||
|
|
||||||
|
bool busy() const { return m_busy; }
|
||||||
|
QString lastError() const { return m_lastError; }
|
||||||
|
QString lastStatus() const { return m_lastStatus; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void pingOk();
|
||||||
|
void pingFailed(const QString &error);
|
||||||
|
|
||||||
|
void busyChanged();
|
||||||
|
void lastErrorChanged();
|
||||||
|
void lastStatusChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QNetworkAccessManager m_net;
|
||||||
|
|
||||||
|
bool m_busy = false;
|
||||||
|
QString m_lastError;
|
||||||
|
QString m_lastStatus;
|
||||||
|
|
||||||
|
void setBusy(bool v);
|
||||||
|
void setLastError(const QString &e);
|
||||||
|
void setLastStatus(const QString &s);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user