feat: seeking support and Last.fm scrobbling

Seeking:
- Rust player: seek_requested/seek_target_secs atomics on PlayerStatus
- Decoder loop checks for seek each iteration, calls format.seek() and resets decoder
- New qobuz_backend_seek C FFI + QobuzBackend::seek(quint64)
- Progress slider onProgressReleased now seeks to the dragged position

Last.fm:
- LastFmScrobbler: now-playing + scrobble (50% or 240s threshold, min 30s)
- API signature follows Last.fm spec (sorted params, md5)
- Settings dialog: API key/secret, username/password, Connect button with status
- AppSettings: lastfm/enabled, api_key, api_secret, session_key
- Scrobbler wired to trackChanged, positionChanged, trackFinished in MainWindow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
joren
2026-03-24 01:13:06 +01:00
parent ecaee4d907
commit b9b47f80e7
14 changed files with 357 additions and 10 deletions

View File

@@ -3,6 +3,10 @@
#include <QDialog>
#include <QComboBox>
#include <QLabel>
#include <QLineEdit>
#include <QCheckBox>
#include <QPushButton>
#include <QNetworkAccessManager>
class SettingsDialog : public QDialog
{
@@ -12,7 +16,19 @@ public:
explicit SettingsDialog(QWidget *parent = nullptr);
private:
// Playback
QComboBox *m_formatBox = nullptr;
// Last.fm
QCheckBox *m_lastFmEnabled = nullptr;
QLineEdit *m_lastFmApiKey = nullptr;
QLineEdit *m_lastFmApiSecret = nullptr;
QLineEdit *m_lastFmUsername = nullptr;
QLineEdit *m_lastFmPassword = nullptr;
QPushButton *m_lastFmConnect = nullptr;
QLabel *m_lastFmStatus = nullptr;
QNetworkAccessManager *m_nam = nullptr;
void applyChanges();
void onLastFmConnect();
};