Initial implementation of qobuz-qt
- Rust backend (qobuz-backend static lib): Qobuz API client (reqwest/tokio), Symphonia audio decoder, CPAL audio output, extern "C" FFI bridge - Qt 6 frontend mirroring spotify-qt layout: toolbar with playback controls, left library dock, central track list, right search panel - Auth: email/password login with MD5-signed requests; session token persisted via QSettings - Playback: double-click a track → Rust fetches stream URL → Symphonia decodes → CPAL outputs to default audio device - Dark Fusion palette matching spotify-qt feel Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
32
src/util/icon.hpp
Normal file
32
src/util/icon.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <QIcon>
|
||||
#include <QString>
|
||||
|
||||
namespace Icon
|
||||
{
|
||||
inline QIcon get(const QString &name)
|
||||
{
|
||||
// Try theme icon first, fall back to resource
|
||||
if (QIcon::hasThemeIcon(name))
|
||||
return QIcon::fromTheme(name);
|
||||
return QIcon(QStringLiteral(":/icons/%1.svg").arg(name));
|
||||
}
|
||||
|
||||
// Convenient aliases for common icons used throughout the app
|
||||
inline QIcon play() { return get("media-playback-start"); }
|
||||
inline QIcon pause() { return get("media-playback-pause"); }
|
||||
inline QIcon stop() { return get("media-playback-stop"); }
|
||||
inline QIcon next() { return get("media-skip-forward"); }
|
||||
inline QIcon previous() { return get("media-skip-backward"); }
|
||||
inline QIcon shuffle() { return get("media-playlist-shuffle"); }
|
||||
inline QIcon repeat() { return get("media-playlist-repeat"); }
|
||||
inline QIcon volumeHigh() { return get("audio-volume-high"); }
|
||||
inline QIcon volumeMid() { return get("audio-volume-medium"); }
|
||||
inline QIcon volumeMute() { return get("audio-volume-muted"); }
|
||||
inline QIcon search() { return get("edit-find"); }
|
||||
inline QIcon heart() { return get("emblem-favorite"); }
|
||||
inline QIcon album() { return get("media-optical"); }
|
||||
inline QIcon artist() { return get("system-users"); }
|
||||
inline QIcon playlist() { return get("view-list-symbolic"); }
|
||||
}
|
||||
37
src/util/settings.hpp
Normal file
37
src/util/settings.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QSettings>
|
||||
|
||||
class AppSettings
|
||||
{
|
||||
public:
|
||||
static AppSettings &instance()
|
||||
{
|
||||
static AppSettings inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
QString authToken() const { return m_settings.value("auth/token").toString(); }
|
||||
void setAuthToken(const QString &t) { m_settings.setValue("auth/token", t); }
|
||||
|
||||
QString userEmail() const { return m_settings.value("auth/email").toString(); }
|
||||
void setUserEmail(const QString &e) { m_settings.setValue("auth/email", e); }
|
||||
|
||||
QString displayName() const { return m_settings.value("user/display_name").toString(); }
|
||||
void setDisplayName(const QString &n) { m_settings.setValue("user/display_name", n); }
|
||||
|
||||
// 5 = MP3, 6 = CD, 7 = HiRes96, 27 = HiRes192
|
||||
int preferredFormat() const { return m_settings.value("playback/format", 6).toInt(); }
|
||||
void setPreferredFormat(int f) { m_settings.setValue("playback/format", f); }
|
||||
|
||||
int volume() const { return m_settings.value("playback/volume", 80).toInt(); }
|
||||
void setVolume(int v) { m_settings.setValue("playback/volume", v); }
|
||||
|
||||
bool rememberLogin() const { return m_settings.value("auth/remember", true).toBool(); }
|
||||
void setRememberLogin(bool r) { m_settings.setValue("auth/remember", r); }
|
||||
|
||||
private:
|
||||
AppSettings() : m_settings("qobuz-qt", "qobuz-qt") {}
|
||||
QSettings m_settings;
|
||||
};
|
||||
Reference in New Issue
Block a user