diff --git a/rust/include/qobuz_backend.h b/rust/include/qobuz_backend.h index 53b7830..c37caaf 100644 --- a/rust/include/qobuz_backend.h +++ b/rust/include/qobuz_backend.h @@ -35,6 +35,8 @@ enum QobuzEvent { EV_PLAYLIST_DELETED = 21, EV_PLAYLIST_TRACK_ADDED = 22, EV_USER_OK = 23, + EV_ARTIST_RELEASES_OK = 24, + EV_DEEP_SHUFFLE_OK = 25, }; // Callback signature @@ -84,9 +86,12 @@ uint32_t qobuz_backend_viz_read(QobuzBackendOpaque *backend, float *buf, uint32_ uint32_t qobuz_backend_viz_sample_rate(const QobuzBackendOpaque *backend); uint32_t qobuz_backend_viz_channels(const QobuzBackendOpaque *backend); -// Artist releases (full paginated list per release type) +// Artist releases (auto-paginates to fetch all) void qobuz_backend_get_artist_releases(QobuzBackendOpaque *backend, int64_t artist_id, const char *release_type, uint32_t limit, uint32_t offset); +// Deep shuffle: fetch tracks from multiple albums (album_ids_json is a JSON array of strings) +void qobuz_backend_get_albums_tracks(QobuzBackendOpaque *backend, const char *album_ids_json); + // Playlist management void qobuz_backend_create_playlist(QobuzBackendOpaque *backend, const char *name); void qobuz_backend_delete_playlist(QobuzBackendOpaque *backend, int64_t playlist_id); diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 421457b..7a8255d 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -69,6 +69,7 @@ pub const EV_TRACK_URL_OK: c_int = 17; pub const EV_TRACK_URL_ERR: c_int = 18; pub const EV_GENERIC_ERR: c_int = 19; pub const EV_ARTIST_RELEASES_OK: c_int = 24; +pub const EV_DEEP_SHUFFLE_OK: c_int = 25; // ---------- Callback ---------- @@ -263,7 +264,7 @@ pub unsafe extern "C" fn qobuz_backend_get_artist_releases( artist_id: i64, release_type: *const c_char, limit: u32, - offset: u32, + _offset: u32, ) { let inner = &(*ptr).0; let client = inner.client.clone(); @@ -271,19 +272,90 @@ pub unsafe extern "C" fn qobuz_backend_get_artist_releases( let rtype = CStr::from_ptr(release_type).to_string_lossy().into_owned(); spawn(inner, async move { - let result = client.lock().await - .get_artist_releases_list(artist_id, &rtype, limit, offset) - .await; - let (ev, json) = match result { - Ok(r) => { - // Wrap with the release_type so Qt can route to the right section - let mut obj = r.as_object().cloned().unwrap_or_default(); - obj.insert("release_type".to_string(), serde_json::Value::String(rtype)); - (EV_ARTIST_RELEASES_OK, serde_json::to_string(&obj).unwrap_or_default()) + // Auto-paginate: fetch all pages until has_more is false. + let mut all_items: Vec = Vec::new(); + let mut offset: u32 = 0; + loop { + let result = client.lock().await + .get_artist_releases_list(artist_id, &rtype, limit, offset) + .await; + match result { + Ok(r) => { + let obj = r.as_object().cloned().unwrap_or_default(); + if let Some(items) = obj.get("items").and_then(|v| v.as_array()) { + all_items.extend(items.iter().cloned()); + } + let has_more = obj.get("has_more").and_then(|v| v.as_bool()).unwrap_or(false); + if !has_more { + break; + } + offset += limit; + } + Err(e) => { + call_cb(cb, ud, EV_GENERIC_ERR, &err_json(&e.to_string())); + return; + } } - Err(e) => (EV_GENERIC_ERR, err_json(&e.to_string())), - }; - call_cb(cb, ud, ev, &json); + } + let result = serde_json::json!({ + "release_type": rtype, + "items": all_items, + "has_more": false, + "offset": 0 + }); + call_cb(cb, ud, EV_ARTIST_RELEASES_OK, &serde_json::to_string(&result).unwrap_or_default()); + }); +} + +// ---------- Deep shuffle (fetch tracks from multiple albums) ---------- + +#[no_mangle] +pub unsafe extern "C" fn qobuz_backend_get_albums_tracks( + ptr: *mut Backend, + album_ids_json: *const c_char, +) { + let inner = &(*ptr).0; + let client = inner.client.clone(); + let cb = inner.cb; let ud = inner.ud; + let ids_str = CStr::from_ptr(album_ids_json).to_string_lossy().into_owned(); + + let album_ids: Vec = match serde_json::from_str(&ids_str) { + Ok(v) => v, + Err(e) => { + call_cb(cb, ud, EV_GENERIC_ERR, &err_json(&e.to_string())); + return; + } + }; + + spawn(inner, async move { + let mut all_tracks: Vec = Vec::new(); + for id in &album_ids { + let result = client.lock().await.get_album(id).await; + if let Ok(album) = result { + if let Some(tracks) = album.tracks.as_ref().and_then(|t| t.items.as_ref()) { + for t in tracks { + // Serialize track and inject album info for playback context + if let Ok(mut tv) = serde_json::to_value(t) { + if let Some(obj) = tv.as_object_mut() { + // Ensure album context is present on each track + if obj.get("album").is_none() || obj["album"].is_null() { + obj.insert("album".to_string(), serde_json::json!({ + "id": album.id, + "title": album.title, + "artist": album.artist, + "image": album.image, + })); + } + } + all_tracks.push(tv); + } + } + } + } + // Skip albums that fail — don't abort the whole operation + } + let result = serde_json::json!({ "tracks": all_tracks }); + call_cb(cb, ud, EV_DEEP_SHUFFLE_OK, &serde_json::to_string(&result).unwrap_or_default()); }); } diff --git a/src/backend/qobuzbackend.cpp b/src/backend/qobuzbackend.cpp index 13814ef..6432bdb 100644 --- a/src/backend/qobuzbackend.cpp +++ b/src/backend/qobuzbackend.cpp @@ -67,6 +67,13 @@ void QobuzBackend::getArtistReleases(qint64 artistId, const QString &releaseType releaseType.toUtf8().constData(), limit, offset); } +void QobuzBackend::getAlbumsTracks(const QStringList &albumIds) +{ + const QJsonArray arr = QJsonArray::fromStringList(albumIds); + const QByteArray json = QJsonDocument(arr).toJson(QJsonDocument::Compact); + qobuz_backend_get_albums_tracks(m_backend, json.constData()); +} + void QobuzBackend::getPlaylist(qint64 playlistId, quint32 offset, quint32 limit) { qobuz_backend_get_playlist(m_backend, playlistId, offset, limit); @@ -266,6 +273,9 @@ void QobuzBackend::onEvent(int eventType, const QString &json) obj["offset"].toInt() ); break; + case 25: // EV_DEEP_SHUFFLE_OK + emit deepShuffleTracksLoaded(obj["tracks"].toArray()); + break; case EV_ARTIST_ERR: emit error(obj["error"].toString()); break; diff --git a/src/backend/qobuzbackend.hpp b/src/backend/qobuzbackend.hpp index 6d01620..0f28584 100644 --- a/src/backend/qobuzbackend.hpp +++ b/src/backend/qobuzbackend.hpp @@ -31,6 +31,7 @@ public: void getAlbum(const QString &albumId); void getArtist(qint64 artistId); void getArtistReleases(qint64 artistId, const QString &releaseType, quint32 limit = 50, quint32 offset = 0); + void getAlbumsTracks(const QStringList &albumIds); void getPlaylist(qint64 playlistId, quint32 offset = 0, quint32 limit = 500); // --- favorites --- @@ -88,6 +89,7 @@ signals: void albumLoaded(const QJsonObject &album); void artistLoaded(const QJsonObject &artist); void artistReleasesLoaded(const QString &releaseType, const QJsonArray &items, bool hasMore, int offset); + void deepShuffleTracksLoaded(const QJsonArray &tracks); void playlistLoaded(const QJsonObject &playlist); void playlistCreated(const QJsonObject &playlist); void playlistDeleted(const QJsonObject &result); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 269fda8..3362def 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -40,7 +40,8 @@ MainWindow::MainWindow(QobuzBackend *backend, QWidget *parent) m_libraryDock->setObjectName(QStringLiteral("libraryDock")); m_libraryDock->setFeatures(QDockWidget::DockWidgetMovable); m_libraryDock->setWidget(m_library); - m_libraryDock->setMinimumWidth(200); + m_libraryDock->setMinimumWidth(180); + m_library->setFixedWidth(220); addDockWidget(Qt::LeftDockWidgetArea, m_libraryDock); // ---- Now-playing context dock (left, below library) ---- @@ -86,6 +87,8 @@ MainWindow::MainWindow(QobuzBackend *backend, QWidget *parent) connect(m_backend, &QobuzBackend::artistLoaded, this, &MainWindow::onArtistLoaded); connect(m_backend, &QobuzBackend::artistReleasesLoaded, m_content, &MainContent::updateArtistReleases); + connect(m_backend, &QobuzBackend::deepShuffleTracksLoaded, + m_content, &MainContent::onDeepShuffleTracks); connect(m_backend, &QobuzBackend::playlistLoaded, this, &MainWindow::onPlaylistLoaded); connect(m_backend, &QobuzBackend::playlistCreated, this, &MainWindow::onPlaylistCreated); connect(m_backend, &QobuzBackend::playlistDeleted, this, [this](const QJsonObject &) { @@ -170,22 +173,6 @@ MainWindow::MainWindow(QobuzBackend *backend, QWidget *parent) connect(m_toolBar, &MainToolBar::queueToggled, this, [this](bool v) { m_queuePanel->setVisible(v); }); - connect(m_toolBar->backAction(), &QAction::triggered, this, [this] { - if (m_navIndex <= 0) return; - --m_navIndex; - m_navFromHistory = true; - navigateTo(m_navHistory[m_navIndex]); - m_navFromHistory = false; - updateNavButtons(); - }); - connect(m_toolBar->forwardAction(), &QAction::triggered, this, [this] { - if (m_navIndex >= m_navHistory.size() - 1) return; - ++m_navIndex; - m_navFromHistory = true; - navigateTo(m_navHistory[m_navIndex]); - m_navFromHistory = false; - updateNavButtons(); - }); connect(m_toolBar, &MainToolBar::albumRequested, this, &MainWindow::onSearchAlbumSelected); connect(m_toolBar, &MainToolBar::artistRequested, this, &MainWindow::onSearchArtistSelected); @@ -394,16 +381,12 @@ void MainWindow::onPlayTrackRequested(qint64 trackId) void MainWindow::onSearchAlbumSelected(const QString &albumId) { - NavPage p; p.type = NavPage::Album; p.albumId = albumId; - pushNav(p.type, p.albumId); m_backend->getAlbum(albumId); statusBar()->showMessage(tr("Loading album…")); } void MainWindow::onSearchArtistSelected(qint64 artistId) { - NavPage p; p.type = NavPage::Artist; p.artistId = artistId; - pushNav(p.type, {}, p.artistId); m_backend->getArtist(artistId); statusBar()->showMessage(tr("Loading artist…")); } @@ -429,38 +412,3 @@ void MainWindow::onUserPlaylistsChanged(const QVector> &p m_content->tracksList()->setUserPlaylists(playlists); } -void MainWindow::pushNav(NavPage::Type type, const QString &albumId, qint64 artistId) -{ - if (m_navFromHistory) return; - // Truncate any forward history - while (m_navHistory.size() > m_navIndex + 1) - m_navHistory.removeLast(); - NavPage p; - p.type = type; - p.albumId = albumId; - p.artistId = artistId; - m_navHistory.push_back(p); - m_navIndex = m_navHistory.size() - 1; - updateNavButtons(); -} - -void MainWindow::navigateTo(const NavPage &page) -{ - switch (page.type) { - case NavPage::Album: - m_backend->getAlbum(page.albumId); - statusBar()->showMessage(tr("Loading album…")); - break; - case NavPage::Artist: - m_backend->getArtist(page.artistId); - statusBar()->showMessage(tr("Loading artist…")); - break; - default: break; - } -} - -void MainWindow::updateNavButtons() -{ - m_toolBar->backAction()->setEnabled(m_navIndex > 0); - m_toolBar->forwardAction()->setEnabled(m_navIndex < m_navHistory.size() - 1); -} diff --git a/src/mainwindow.hpp b/src/mainwindow.hpp index 2b825a8..b3668bf 100644 --- a/src/mainwindow.hpp +++ b/src/mainwindow.hpp @@ -64,19 +64,6 @@ private: QDockWidget *m_libraryDock = nullptr; LastFmScrobbler *m_scrobbler = nullptr; - // Navigation history (browser-style Back / Forward) - struct NavPage { - enum Type { None, Album, Artist } type = None; - QString albumId; - qint64 artistId = 0; - }; - QVector m_navHistory; - int m_navIndex = -1; - bool m_navFromHistory = false; - void setupMenuBar(); void tryRestoreSession(); - void pushNav(NavPage::Type type, const QString &albumId = {}, qint64 artistId = 0); - void navigateTo(const NavPage &page); - void updateNavButtons(); }; diff --git a/src/view/albumlistview.hpp b/src/view/albumlistview.hpp index 306de8b..2a654ff 100644 --- a/src/view/albumlistview.hpp +++ b/src/view/albumlistview.hpp @@ -44,6 +44,20 @@ public: addAlbums(albums); } + /// Configure for artist page: hide Artist column, set fixed column widths + /// that match the Popular Tracks list for perfect vertical alignment. + void setArtistPageMode() + { + setColumnHidden(2, true); // Artist — redundant on artist page + header()->setSectionResizeMode(0, QHeaderView::Fixed); + header()->setSectionResizeMode(1, QHeaderView::Stretch); + header()->setSectionResizeMode(3, QHeaderView::Fixed); + header()->setSectionResizeMode(4, QHeaderView::Fixed); + header()->resizeSection(0, 40); + header()->resizeSection(3, 120); + header()->resizeSection(4, 70); + } + void addAlbums(const QJsonArray &albums) { QFont hiResFont; diff --git a/src/view/artistview.cpp b/src/view/artistview.cpp index 7efefed..abe0f70 100644 --- a/src/view/artistview.cpp +++ b/src/view/artistview.cpp @@ -1,9 +1,11 @@ #include "artistview.hpp" #include "albumlistview.hpp" +#include "../model/tracklistmodel.hpp" #include #include #include +#include #include #include #include @@ -49,50 +51,18 @@ ArtistSection::ArtistSection(const QString &title, const QString &releaseType, Q m_list = new AlbumListView(this); layout->addWidget(m_list); - // "Load more" button (shown when has_more is true) - m_loadMoreBtn = new QPushButton(tr("Load more…"), this); - m_loadMoreBtn->setStyleSheet(QStringLiteral( - "QPushButton { text-align: left; color: #FFB232; background: transparent;" - " border: none; padding: 6px 8px; }" - "QPushButton:hover { background: #1e1e1e; }")); - m_loadMoreBtn->setCursor(Qt::PointingHandCursor); - m_loadMoreBtn->setVisible(false); - layout->addWidget(m_loadMoreBtn); - connect(m_toggle, &QPushButton::toggled, this, [this](bool checked) { m_list->setVisible(checked); - m_loadMoreBtn->setVisible(checked && m_hasMore); updateToggleText(); }); connect(m_list, &AlbumListView::albumSelected, this, &ArtistSection::albumSelected); - connect(m_loadMoreBtn, &QPushButton::clicked, this, [this] { - m_loadMoreBtn->setEnabled(false); - m_loadMoreBtn->setText(tr("Loading…")); - emit loadMoreRequested(m_releaseType, m_loadedCount); - }); updateToggleText(); } -void ArtistSection::setAlbums(const QJsonArray &albums, bool hasMore) +void ArtistSection::setAlbums(const QJsonArray &albums) { m_list->setAlbums(albums); - m_loadedCount = albums.size(); - m_hasMore = hasMore; - m_loadMoreBtn->setVisible(hasMore && m_toggle->isChecked()); - m_loadMoreBtn->setEnabled(true); - m_loadMoreBtn->setText(tr("Load more…")); - updateToggleText(); -} - -void ArtistSection::appendAlbums(const QJsonArray &albums, bool hasMore) -{ - m_list->addAlbums(albums); - m_loadedCount += albums.size(); - m_hasMore = hasMore; - m_loadMoreBtn->setVisible(hasMore && m_toggle->isChecked()); - m_loadMoreBtn->setEnabled(true); - m_loadMoreBtn->setText(tr("Load more…")); updateToggleText(); } @@ -101,11 +71,28 @@ bool ArtistSection::isEmpty() const return m_list->topLevelItemCount() == 0; } +QStringList ArtistSection::albumIds() const +{ + QStringList ids; + for (int i = 0; i < m_list->topLevelItemCount(); ++i) { + const QString id = m_list->topLevelItem(i)->data(1, Qt::UserRole).toString(); + if (!id.isEmpty()) + ids.append(id); + } + return ids; +} + +void ArtistSection::setArtistPageMode() +{ + m_list->setArtistPageMode(); +} + void ArtistSection::updateToggleText() { + const int count = m_list->topLevelItemCount(); const QString arrow = m_toggle->isChecked() ? QStringLiteral("▼ ") : QStringLiteral("▶ "); - const QString text = m_loadedCount > 0 - ? QStringLiteral("%1%2 (%3)").arg(arrow, m_baseTitle).arg(m_loadedCount) + const QString text = count > 0 + ? QStringLiteral("%1%2 (%3)").arg(arrow, m_baseTitle).arg(count) : arrow + m_baseTitle; m_toggle->setText(text); } @@ -117,6 +104,7 @@ void ArtistSection::updateToggleText() ArtistView::ArtistView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent) : QWidget(parent) , m_backend(backend) + , m_queue(queue) { auto *outerLayout = new QVBoxLayout(this); outerLayout->setContentsMargins(0, 0, 0, 0); @@ -165,7 +153,7 @@ ArtistView::ArtistView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent) QStringLiteral("QPushButton { background: #FFB232; color: #000; }" "QPushButton:pressed { background: #e09e28; }")); - m_shuffleBtn = new QPushButton(tr("⇄ Shuffle"), info); + m_shuffleBtn = new QPushButton(tr("⇄ Shuffle All"), info); m_shuffleBtn->setStyleSheet(kBtnBase + QStringLiteral("QPushButton { background: #2a2a2a; color: #FFB232; border: 1px solid #FFB232; }" "QPushButton:pressed { background: #333; }")); @@ -222,6 +210,14 @@ ArtistView::ArtistView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent) m_topTracks = new List::Tracks(backend, queue, m_topTracksSection); m_topTracks->setMaximumHeight(320); + // Artist page column layout: hide Artist & Album, match album-section widths + m_topTracks->setColumnHidden(TrackListModel::ColArtist, true); + m_topTracks->setColumnHidden(TrackListModel::ColAlbum, true); + m_topTracks->header()->setSectionResizeMode(TrackListModel::ColNumber, QHeaderView::Fixed); + m_topTracks->header()->setSectionResizeMode(TrackListModel::ColTitle, QHeaderView::Stretch); + m_topTracks->header()->setSectionResizeMode(TrackListModel::ColDuration, QHeaderView::Fixed); + m_topTracks->header()->resizeSection(TrackListModel::ColNumber, 40); + m_topTracks->header()->resizeSection(TrackListModel::ColDuration, 70); ttLayout->addWidget(m_topTracks); connect(m_topTracksToggle, &QPushButton::toggled, m_topTracks, &QWidget::setVisible); @@ -236,6 +232,10 @@ ArtistView::ArtistView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent) m_secCompilations = new ArtistSection(tr("Compilations"), QStringLiteral("compilation"), content); m_secOther = new ArtistSection(tr("Other"), QStringLiteral("other"), content); + // Uniform column layout: hide Artist column, match fixed widths across all sections + for (ArtistSection *sec : {m_secAlbums, m_secEps, m_secLive, m_secCompilations, m_secOther}) + sec->setArtistPageMode(); + sectLayout->addWidget(m_secAlbums); sectLayout->addWidget(m_secEps); sectLayout->addWidget(m_secLive); @@ -246,9 +246,21 @@ ArtistView::ArtistView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent) scroll->setWidget(content); outerLayout->addWidget(scroll, 1); - // Playback connections - connect(m_playBtn, &QPushButton::clicked, m_topTracks, [this] { m_topTracks->playAll(false); }); - connect(m_shuffleBtn, &QPushButton::clicked, m_topTracks, [this] { m_topTracks->playAll(true); }); + // Play top tracks + connect(m_playBtn, &QPushButton::clicked, m_topTracks, [this] { m_topTracks->playAll(false); }); + + // Deep shuffle: fetch all album tracks, combine, shuffle, play + connect(m_shuffleBtn, &QPushButton::clicked, this, [this] { + const QStringList ids = allAlbumIds(); + if (ids.isEmpty()) { + // Fallback: just shuffle popular tracks + m_topTracks->playAll(true); + return; + } + m_shuffleBtn->setEnabled(false); + m_shuffleBtn->setText(tr("Loading…")); + m_backend->getAlbumsTracks(ids); + }); // Favourite button connect(m_favBtn, &QPushButton::clicked, this, [this] { @@ -270,20 +282,6 @@ ArtistView::ArtistView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent) connect(m_secLive, &ArtistSection::albumSelected, this, &ArtistView::albumSelected); connect(m_secCompilations, &ArtistSection::albumSelected, this, &ArtistView::albumSelected); connect(m_secOther, &ArtistSection::albumSelected, this, &ArtistView::albumSelected); - - // Load-more connections - auto connectLoadMore = [this](ArtistSection *sec) { - connect(sec, &ArtistSection::loadMoreRequested, this, - [this](const QString &releaseType, int nextOffset) { - if (m_artistId > 0) - m_backend->getArtistReleases(m_artistId, releaseType, 50, static_cast(nextOffset)); - }); - }; - connectLoadMore(m_secAlbums); - connectLoadMore(m_secEps); - connectLoadMore(m_secLive); - connectLoadMore(m_secCompilations); - connectLoadMore(m_secOther); } void ArtistView::setArtist(const QJsonObject &artist) @@ -352,6 +350,10 @@ void ArtistView::setArtist(const QJsonObject &artist) : QStringLiteral("▼ Popular Tracks")); m_topTracksSection->setVisible(!topTracks.isEmpty()); + // Reset shuffle button state + m_shuffleBtn->setEnabled(true); + m_shuffleBtn->setText(tr("⇄ Shuffle All")); + // Clear release sections for (ArtistSection *sec : {m_secAlbums, m_secEps, m_secLive, m_secCompilations, m_secOther}) { sec->setAlbums({}); @@ -360,7 +362,7 @@ void ArtistView::setArtist(const QJsonObject &artist) } void ArtistView::setReleases(const QString &releaseType, const QJsonArray &items, - bool hasMore, int offset) + bool /*hasMore*/, int /*offset*/) { ArtistSection *sec = nullptr; if (releaseType == QStringLiteral("album")) sec = m_secAlbums; @@ -369,22 +371,42 @@ void ArtistView::setReleases(const QString &releaseType, const QJsonArray &items else if (releaseType == QStringLiteral("compilation")) sec = m_secCompilations; else sec = m_secOther; - if (offset == 0) - sec->setAlbums(items, hasMore); - else - sec->appendAlbums(items, hasMore); - + // Rust auto-paginates, so we always get the full list at once + sec->setAlbums(items); sec->setVisible(!sec->isEmpty()); } void ArtistView::setFavArtistIds(const QSet &ids) { m_favArtistIds = ids; - // Update current state if we're showing an artist if (m_artistId > 0) setFaved(ids.contains(m_artistId)); } +void ArtistView::onDeepShuffleTracks(const QJsonArray &tracks) +{ + m_shuffleBtn->setEnabled(true); + m_shuffleBtn->setText(tr("⇄ Shuffle All")); + + if (tracks.isEmpty()) return; + + m_queue->setContext(tracks, 0); + m_queue->shuffleNow(); + + const QJsonObject first = m_queue->current(); + const qint64 id = static_cast(first["id"].toDouble()); + if (id > 0) + emit playTrackRequested(id); +} + +QStringList ArtistView::allAlbumIds() const +{ + QStringList ids; + for (const ArtistSection *sec : {m_secAlbums, m_secEps, m_secLive, m_secCompilations, m_secOther}) + ids.append(sec->albumIds()); + return ids; +} + void ArtistView::setFaved(bool faved) { m_isFaved = faved; diff --git a/src/view/artistview.hpp b/src/view/artistview.hpp index 440f309..3d881dc 100644 --- a/src/view/artistview.hpp +++ b/src/view/artistview.hpp @@ -23,22 +23,19 @@ class ArtistSection : public QWidget public: explicit ArtistSection(const QString &title, const QString &releaseType, QWidget *parent = nullptr); - void setAlbums(const QJsonArray &albums, bool hasMore = false); - void appendAlbums(const QJsonArray &albums, bool hasMore = false); + void setAlbums(const QJsonArray &albums); bool isEmpty() const; + QStringList albumIds() const; + void setArtistPageMode(); signals: void albumSelected(const QString &albumId); - void loadMoreRequested(const QString &releaseType, int nextOffset); private: QString m_baseTitle; QString m_releaseType; QPushButton *m_toggle = nullptr; AlbumListView *m_list = nullptr; - QPushButton *m_loadMoreBtn = nullptr; - bool m_hasMore = false; - int m_loadedCount = 0; void updateToggleText(); }; @@ -55,6 +52,7 @@ public: void setReleases(const QString &releaseType, const QJsonArray &items, bool hasMore = false, int offset = 0); void setFavArtistIds(const QSet &ids); + void onDeepShuffleTracks(const QJsonArray &tracks); signals: void albumSelected(const QString &albumId); @@ -62,6 +60,7 @@ signals: private: QobuzBackend *m_backend = nullptr; + PlayQueue *m_queue = nullptr; qint64 m_artistId = 0; // Header widgets @@ -88,5 +87,6 @@ private: ArtistSection *m_secCompilations = nullptr; ArtistSection *m_secOther = nullptr; + QStringList allAlbumIds() const; void setFaved(bool faved); }; diff --git a/src/view/maincontent.cpp b/src/view/maincontent.cpp index b2d047c..7d29b02 100644 --- a/src/view/maincontent.cpp +++ b/src/view/maincontent.cpp @@ -117,3 +117,8 @@ void MainContent::setFavArtistIds(const QSet &ids) { m_artistView->setFavArtistIds(ids); } + +void MainContent::onDeepShuffleTracks(const QJsonArray &tracks) +{ + m_artistView->onDeepShuffleTracks(tracks); +} diff --git a/src/view/maincontent.hpp b/src/view/maincontent.hpp index 3fae2e5..d8b3603 100644 --- a/src/view/maincontent.hpp +++ b/src/view/maincontent.hpp @@ -33,6 +33,9 @@ public: void showArtist(const QJsonObject &artist); void updateArtistReleases(const QString &releaseType, const QJsonArray &items, bool hasMore, int offset); void setFavArtistIds(const QSet &ids); + void onDeepShuffleTracks(const QJsonArray &tracks); + + ArtistView *artistView() const { return m_artistView; } signals: void albumRequested(const QString &albumId); diff --git a/src/view/maintoolbar.cpp b/src/view/maintoolbar.cpp index 96a27e9..ed29290 100644 --- a/src/view/maintoolbar.cpp +++ b/src/view/maintoolbar.cpp @@ -16,13 +16,6 @@ MainToolBar::MainToolBar(QobuzBackend *backend, PlayQueue *queue, QWidget *paren setContextMenuPolicy(Qt::PreventContextMenu); setIconSize(QSize(22, 22)); - // ---- Back / Forward navigation ---- - m_back = addAction(QIcon::fromTheme(QStringLiteral("go-previous")), tr("Back")); - m_back->setEnabled(false); - m_fwd = addAction(QIcon::fromTheme(QStringLiteral("go-next")), tr("Forward")); - m_fwd->setEnabled(false); - addSeparator(); - m_nam = new QNetworkAccessManager(this); connect(m_nam, &QNetworkAccessManager::finished, this, &MainToolBar::onAlbumArtReady); diff --git a/src/view/maintoolbar.hpp b/src/view/maintoolbar.hpp index 251991b..884d242 100644 --- a/src/view/maintoolbar.hpp +++ b/src/view/maintoolbar.hpp @@ -24,9 +24,6 @@ public: void setCurrentTrack(const QJsonObject &track); void updateProgress(quint64 position, quint64 duration); - QAction *backAction() const { return m_back; } - QAction *forwardAction() const { return m_fwd; } - signals: void searchToggled(bool visible); void queueToggled(bool visible); @@ -57,8 +54,6 @@ private: QobuzBackend *m_backend = nullptr; PlayQueue *m_queue = nullptr; - QAction *m_back = nullptr; - QAction *m_fwd = nullptr; QLabel *m_artLabel = nullptr; QLabel *m_trackLabel = nullptr; QAction *m_previous = nullptr;