feat: add autoplay suggestions when queue runs out
Some checks failed
Build for Windows / build-windows (push) Has been cancelled
Some checks failed
Build for Windows / build-windows (push) Has been cancelled
This commit is contained in:
@@ -40,6 +40,7 @@ enum QobuzEvent {
|
|||||||
EV_MOST_POPULAR_OK = 26,
|
EV_MOST_POPULAR_OK = 26,
|
||||||
EV_GENRES_OK = 27,
|
EV_GENRES_OK = 27,
|
||||||
EV_FEATURED_ALBUMS_OK = 28,
|
EV_FEATURED_ALBUMS_OK = 28,
|
||||||
|
EV_DYNAMIC_SUGGEST_OK = 29,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Callback signature
|
// Callback signature
|
||||||
@@ -57,6 +58,7 @@ void qobuz_backend_get_user(QobuzBackendOpaque *backend);
|
|||||||
// Catalog
|
// Catalog
|
||||||
void qobuz_backend_search(QobuzBackendOpaque *backend, const char *query, uint32_t offset, uint32_t limit);
|
void qobuz_backend_search(QobuzBackendOpaque *backend, const char *query, uint32_t offset, uint32_t limit);
|
||||||
void qobuz_backend_most_popular_search(QobuzBackendOpaque *backend, const char *query, uint32_t limit);
|
void qobuz_backend_most_popular_search(QobuzBackendOpaque *backend, const char *query, uint32_t limit);
|
||||||
|
void qobuz_backend_get_dynamic_suggestions(QobuzBackendOpaque *backend, const char *listened_track_ids_json, const char *tracks_to_analyze_json, uint32_t limit);
|
||||||
void qobuz_backend_get_album(QobuzBackendOpaque *backend, const char *album_id);
|
void qobuz_backend_get_album(QobuzBackendOpaque *backend, const char *album_id);
|
||||||
void qobuz_backend_get_artist(QobuzBackendOpaque *backend, int64_t artist_id);
|
void qobuz_backend_get_artist(QobuzBackendOpaque *backend, int64_t artist_id);
|
||||||
void qobuz_backend_get_playlist(QobuzBackendOpaque *backend, int64_t playlist_id, uint32_t offset, uint32_t limit);
|
void qobuz_backend_get_playlist(QobuzBackendOpaque *backend, int64_t playlist_id, uint32_t offset, uint32_t limit);
|
||||||
|
|||||||
@@ -471,6 +471,24 @@ impl QobuzClient {
|
|||||||
Self::check_response(resp).await
|
Self::check_response(resp).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_dynamic_suggestions(
|
||||||
|
&self,
|
||||||
|
limit: u32,
|
||||||
|
listened_tracks_ids: Value,
|
||||||
|
tracks_to_analyze: Value,
|
||||||
|
) -> Result<Value> {
|
||||||
|
let resp = self
|
||||||
|
.post_request("dynamic/suggest")
|
||||||
|
.json(&serde_json::json!({
|
||||||
|
"limit": limit,
|
||||||
|
"listened_tracks_ids": listened_tracks_ids,
|
||||||
|
"track_to_analysed": tracks_to_analyze,
|
||||||
|
}))
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
Self::check_response(resp).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn search_tracks(
|
async fn search_tracks(
|
||||||
&self,
|
&self,
|
||||||
query: &str,
|
query: &str,
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ pub const EV_DEEP_SHUFFLE_OK: c_int = 25;
|
|||||||
pub const EV_MOST_POPULAR_OK: c_int = 26;
|
pub const EV_MOST_POPULAR_OK: c_int = 26;
|
||||||
pub const EV_GENRES_OK: c_int = 27;
|
pub const EV_GENRES_OK: c_int = 27;
|
||||||
pub const EV_FEATURED_ALBUMS_OK: c_int = 28;
|
pub const EV_FEATURED_ALBUMS_OK: c_int = 28;
|
||||||
|
pub const EV_DYNAMIC_SUGGEST_OK: c_int = 29;
|
||||||
|
|
||||||
// ---------- Callback ----------
|
// ---------- Callback ----------
|
||||||
|
|
||||||
@@ -234,6 +235,59 @@ pub unsafe extern "C" fn qobuz_backend_most_popular_search(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn qobuz_backend_get_dynamic_suggestions(
|
||||||
|
ptr: *mut Backend,
|
||||||
|
listened_track_ids_json: *const c_char,
|
||||||
|
tracks_to_analyze_json: *const c_char,
|
||||||
|
limit: u32,
|
||||||
|
) {
|
||||||
|
let inner = &(*ptr).0;
|
||||||
|
let client = inner.client.clone();
|
||||||
|
let cb = inner.cb;
|
||||||
|
let ud = inner.ud;
|
||||||
|
|
||||||
|
let listened_str = CStr::from_ptr(listened_track_ids_json)
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned();
|
||||||
|
let analyze_str = CStr::from_ptr(tracks_to_analyze_json)
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned();
|
||||||
|
|
||||||
|
let listened: serde_json::Value = match serde_json::from_str(&listened_str) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
call_cb(cb, ud, EV_GENERIC_ERR, &err_json(&e.to_string()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let to_analyze: serde_json::Value = match serde_json::from_str(&analyze_str) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
call_cb(cb, ud, EV_GENERIC_ERR, &err_json(&e.to_string()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
spawn(inner, async move {
|
||||||
|
let result = client
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.get_dynamic_suggestions(limit, listened, to_analyze)
|
||||||
|
.await;
|
||||||
|
match result {
|
||||||
|
Ok(r) => call_cb(
|
||||||
|
cb,
|
||||||
|
ud,
|
||||||
|
EV_DYNAMIC_SUGGEST_OK,
|
||||||
|
&serde_json::to_string(&r).unwrap_or_default(),
|
||||||
|
),
|
||||||
|
Err(e) => call_cb(cb, ud, EV_GENERIC_ERR, &err_json(&e.to_string())),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ---------- Album ----------
|
// ---------- Album ----------
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
|||||||
@@ -56,6 +56,17 @@ void QobuzBackend::mostPopularSearch(const QString &query, quint32 limit)
|
|||||||
qobuz_backend_most_popular_search(m_backend, query.toUtf8().constData(), limit);
|
qobuz_backend_most_popular_search(m_backend, query.toUtf8().constData(), limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QobuzBackend::getDynamicSuggestions(const QJsonArray &listenedTrackIds, const QJsonArray &tracksToAnalyze, quint32 limit)
|
||||||
|
{
|
||||||
|
const QByteArray listened = QJsonDocument(listenedTrackIds).toJson(QJsonDocument::Compact);
|
||||||
|
const QByteArray analyze = QJsonDocument(tracksToAnalyze).toJson(QJsonDocument::Compact);
|
||||||
|
qobuz_backend_get_dynamic_suggestions(
|
||||||
|
m_backend,
|
||||||
|
listened.constData(),
|
||||||
|
analyze.constData(),
|
||||||
|
limit);
|
||||||
|
}
|
||||||
|
|
||||||
void QobuzBackend::getAlbum(const QString &albumId)
|
void QobuzBackend::getAlbum(const QString &albumId)
|
||||||
{
|
{
|
||||||
qobuz_backend_get_album(m_backend, albumId.toUtf8().constData());
|
qobuz_backend_get_album(m_backend, albumId.toUtf8().constData());
|
||||||
@@ -282,6 +293,9 @@ void QobuzBackend::onEvent(int eventType, const QString &json)
|
|||||||
case 25: // EV_DEEP_SHUFFLE_OK
|
case 25: // EV_DEEP_SHUFFLE_OK
|
||||||
emit deepShuffleTracksLoaded(obj["tracks"].toArray());
|
emit deepShuffleTracksLoaded(obj["tracks"].toArray());
|
||||||
break;
|
break;
|
||||||
|
case 29: // EV_DYNAMIC_SUGGEST_OK
|
||||||
|
emit dynamicSuggestionsLoaded(obj);
|
||||||
|
break;
|
||||||
case 27: // EV_GENRES_OK
|
case 27: // EV_GENRES_OK
|
||||||
emit genresLoaded(obj);
|
emit genresLoaded(obj);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public:
|
|||||||
// --- catalog ---
|
// --- catalog ---
|
||||||
void search(const QString &query, quint32 offset = 0, quint32 limit = 20);
|
void search(const QString &query, quint32 offset = 0, quint32 limit = 20);
|
||||||
void mostPopularSearch(const QString &query, quint32 limit = 8);
|
void mostPopularSearch(const QString &query, quint32 limit = 8);
|
||||||
|
void getDynamicSuggestions(const QJsonArray &listenedTrackIds, const QJsonArray &tracksToAnalyze, quint32 limit = 50);
|
||||||
void getAlbum(const QString &albumId);
|
void getAlbum(const QString &albumId);
|
||||||
void getArtist(qint64 artistId);
|
void getArtist(qint64 artistId);
|
||||||
void getArtistReleases(qint64 artistId, const QString &releaseType, quint32 limit = 50, quint32 offset = 0);
|
void getArtistReleases(qint64 artistId, const QString &releaseType, quint32 limit = 50, quint32 offset = 0);
|
||||||
@@ -89,6 +90,7 @@ signals:
|
|||||||
void artistLoaded(const QJsonObject &artist);
|
void artistLoaded(const QJsonObject &artist);
|
||||||
void artistReleasesLoaded(const QString &releaseType, const QJsonArray &items, bool hasMore, int offset);
|
void artistReleasesLoaded(const QString &releaseType, const QJsonArray &items, bool hasMore, int offset);
|
||||||
void deepShuffleTracksLoaded(const QJsonArray &tracks);
|
void deepShuffleTracksLoaded(const QJsonArray &tracks);
|
||||||
|
void dynamicSuggestionsLoaded(const QJsonObject &result);
|
||||||
void genresLoaded(const QJsonObject &result);
|
void genresLoaded(const QJsonObject &result);
|
||||||
void featuredAlbumsLoaded(const QJsonObject &result);
|
void featuredAlbumsLoaded(const QJsonObject &result);
|
||||||
void playlistLoaded(const QJsonObject &playlist);
|
void playlistLoaded(const QJsonObject &playlist);
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent)
|
|||||||
m_gapless->setChecked(AppSettings::instance().gaplessEnabled());
|
m_gapless->setChecked(AppSettings::instance().gaplessEnabled());
|
||||||
playLayout->addRow(m_gapless);
|
playLayout->addRow(m_gapless);
|
||||||
|
|
||||||
|
m_autoplay = new QCheckBox(tr("Autoplay recommendations when queue ends"), playGroup);
|
||||||
|
m_autoplay->setChecked(AppSettings::instance().autoplayEnabled());
|
||||||
|
playLayout->addRow(m_autoplay);
|
||||||
|
|
||||||
layout->addWidget(playGroup);
|
layout->addWidget(playGroup);
|
||||||
|
|
||||||
// --- Last.fm group ---
|
// --- Last.fm group ---
|
||||||
@@ -101,6 +105,7 @@ void SettingsDialog::applyChanges()
|
|||||||
AppSettings::instance().setPreferredFormat(m_formatBox->currentData().toInt());
|
AppSettings::instance().setPreferredFormat(m_formatBox->currentData().toInt());
|
||||||
AppSettings::instance().setReplayGainEnabled(m_replayGain->isChecked());
|
AppSettings::instance().setReplayGainEnabled(m_replayGain->isChecked());
|
||||||
AppSettings::instance().setGaplessEnabled(m_gapless->isChecked());
|
AppSettings::instance().setGaplessEnabled(m_gapless->isChecked());
|
||||||
|
AppSettings::instance().setAutoplayEnabled(m_autoplay->isChecked());
|
||||||
AppSettings::instance().setLastFmEnabled(m_lastFmEnabled->isChecked());
|
AppSettings::instance().setLastFmEnabled(m_lastFmEnabled->isChecked());
|
||||||
AppSettings::instance().setLastFmApiKey(m_lastFmApiKey->text().trimmed());
|
AppSettings::instance().setLastFmApiKey(m_lastFmApiKey->text().trimmed());
|
||||||
AppSettings::instance().setLastFmApiSecret(m_lastFmApiSecret->text().trimmed());
|
AppSettings::instance().setLastFmApiSecret(m_lastFmApiSecret->text().trimmed());
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ private:
|
|||||||
QComboBox *m_formatBox = nullptr;
|
QComboBox *m_formatBox = nullptr;
|
||||||
QCheckBox *m_replayGain = nullptr;
|
QCheckBox *m_replayGain = nullptr;
|
||||||
QCheckBox *m_gapless = nullptr;
|
QCheckBox *m_gapless = nullptr;
|
||||||
|
QCheckBox *m_autoplay = nullptr;
|
||||||
|
|
||||||
// Last.fm
|
// Last.fm
|
||||||
QCheckBox *m_lastFmEnabled = nullptr;
|
QCheckBox *m_lastFmEnabled = nullptr;
|
||||||
|
|||||||
@@ -241,6 +241,17 @@ public:
|
|||||||
emit queueChanged();
|
emit queueChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Append tracks to the main queue tail (autoplay/discovery).
|
||||||
|
void appendToContext(const QJsonArray &tracks)
|
||||||
|
{
|
||||||
|
for (const auto &v : tracks) {
|
||||||
|
const QJsonObject t = v.toObject();
|
||||||
|
if (t["streamable"].toBool(true))
|
||||||
|
m_queue.append(t);
|
||||||
|
}
|
||||||
|
emit queueChanged();
|
||||||
|
}
|
||||||
|
|
||||||
/// Move an upcoming item (by its index in upcomingTracks()) to the front of playNext.
|
/// Move an upcoming item (by its index in upcomingTracks()) to the front of playNext.
|
||||||
void moveUpcomingToTop(int upcomingIndex)
|
void moveUpcomingToTop(int upcomingIndex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ public:
|
|||||||
bool gaplessEnabled() const { return m_settings.value("playback/gapless", false).toBool(); }
|
bool gaplessEnabled() const { return m_settings.value("playback/gapless", false).toBool(); }
|
||||||
void setGaplessEnabled(bool v) { m_settings.setValue("playback/gapless", v); }
|
void setGaplessEnabled(bool v) { m_settings.setValue("playback/gapless", v); }
|
||||||
|
|
||||||
|
bool autoplayEnabled() const { return m_settings.value("playback/autoplay", false).toBool(); }
|
||||||
|
void setAutoplayEnabled(bool v) { m_settings.setValue("playback/autoplay", v); }
|
||||||
|
|
||||||
// --- Last.fm ---
|
// --- Last.fm ---
|
||||||
bool lastFmEnabled() const { return m_settings.value("lastfm/enabled", false).toBool(); }
|
bool lastFmEnabled() const { return m_settings.value("lastfm/enabled", false).toBool(); }
|
||||||
void setLastFmEnabled(bool v) { m_settings.setValue("lastfm/enabled", v); }
|
void setLastFmEnabled(bool v) { m_settings.setValue("lastfm/enabled", v); }
|
||||||
|
|||||||
@@ -94,6 +94,11 @@ MainToolBar::MainToolBar(QobuzBackend *backend, PlayQueue *queue, QWidget *paren
|
|||||||
m_shuffle->setCheckable(true);
|
m_shuffle->setCheckable(true);
|
||||||
connect(m_shuffle, &QAction::toggled, this, &MainToolBar::onShuffleToggled);
|
connect(m_shuffle, &QAction::toggled, this, &MainToolBar::onShuffleToggled);
|
||||||
|
|
||||||
|
m_autoplay = addAction(Icon::repeat(), tr("Autoplay"));
|
||||||
|
m_autoplay->setCheckable(true);
|
||||||
|
m_autoplay->setChecked(AppSettings::instance().autoplayEnabled());
|
||||||
|
connect(m_autoplay, &QAction::toggled, this, &MainToolBar::onAutoplayToggled);
|
||||||
|
|
||||||
// ---- Volume ----
|
// ---- Volume ----
|
||||||
m_volume = new VolumeButton(this);
|
m_volume = new VolumeButton(this);
|
||||||
addWidget(m_volume);
|
addWidget(m_volume);
|
||||||
@@ -118,6 +123,8 @@ MainToolBar::MainToolBar(QobuzBackend *backend, PlayQueue *queue, QWidget *paren
|
|||||||
connect(m_backend, &QobuzBackend::positionChanged, this, &MainToolBar::onPositionChanged);
|
connect(m_backend, &QobuzBackend::positionChanged, this, &MainToolBar::onPositionChanged);
|
||||||
connect(m_backend, &QobuzBackend::trackFinished, this, &MainToolBar::onTrackFinished);
|
connect(m_backend, &QobuzBackend::trackFinished, this, &MainToolBar::onTrackFinished);
|
||||||
connect(m_backend, &QobuzBackend::trackTransitioned, this, &MainToolBar::onTrackTransitioned);
|
connect(m_backend, &QobuzBackend::trackTransitioned, this, &MainToolBar::onTrackTransitioned);
|
||||||
|
connect(m_backend, &QobuzBackend::dynamicSuggestionsLoaded,
|
||||||
|
this, &MainToolBar::onDynamicSuggestionsLoaded);
|
||||||
|
|
||||||
// ---- Queue signals ----
|
// ---- Queue signals ----
|
||||||
connect(m_queue, &PlayQueue::queueChanged, this, &MainToolBar::onQueueChanged);
|
connect(m_queue, &PlayQueue::queueChanged, this, &MainToolBar::onQueueChanged);
|
||||||
@@ -232,6 +239,12 @@ void MainToolBar::onBackendStateChanged(const QString &state)
|
|||||||
void MainToolBar::onTrackChanged(const QJsonObject &track)
|
void MainToolBar::onTrackChanged(const QJsonObject &track)
|
||||||
{
|
{
|
||||||
setCurrentTrack(track);
|
setCurrentTrack(track);
|
||||||
|
|
||||||
|
if (track.contains("id")) {
|
||||||
|
m_recentTracks.append(track);
|
||||||
|
while (m_recentTracks.size() > 32)
|
||||||
|
m_recentTracks.removeFirst();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainToolBar::onPositionChanged(quint64 position, quint64 duration)
|
void MainToolBar::onPositionChanged(quint64 position, quint64 duration)
|
||||||
@@ -243,6 +256,8 @@ void MainToolBar::onTrackFinished()
|
|||||||
{
|
{
|
||||||
if (m_queue->canGoNext()) {
|
if (m_queue->canGoNext()) {
|
||||||
onNext();
|
onNext();
|
||||||
|
} else if (AppSettings::instance().autoplayEnabled()) {
|
||||||
|
requestAutoplaySuggestions();
|
||||||
} else {
|
} else {
|
||||||
setPlaying(false);
|
setPlaying(false);
|
||||||
m_progress->setValue(0);
|
m_progress->setValue(0);
|
||||||
@@ -272,6 +287,81 @@ void MainToolBar::onShuffleToggled(bool checked)
|
|||||||
m_queue->setShuffle(checked);
|
m_queue->setShuffle(checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainToolBar::onAutoplayToggled(bool checked)
|
||||||
|
{
|
||||||
|
AppSettings::instance().setAutoplayEnabled(checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainToolBar::requestAutoplaySuggestions()
|
||||||
|
{
|
||||||
|
if (m_fetchingAutoplay)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QJsonArray listenedIds;
|
||||||
|
QJsonArray analyze;
|
||||||
|
|
||||||
|
const int n = m_recentTracks.size();
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
const QJsonObject t = m_recentTracks.at(i);
|
||||||
|
const qint64 trackId = static_cast<qint64>(t["id"].toDouble());
|
||||||
|
if (trackId <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
listenedIds.append(trackId);
|
||||||
|
|
||||||
|
if (i < qMax(0, n - 5))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const qint64 artistId = static_cast<qint64>(
|
||||||
|
t["performer"].toObject()["id"].toDouble());
|
||||||
|
const qint64 genreId = static_cast<qint64>(
|
||||||
|
t["album"].toObject()["genre"].toObject()["id"].toDouble());
|
||||||
|
const qint64 labelId = static_cast<qint64>(
|
||||||
|
t["album"].toObject()["label"].toObject()["id"].toDouble());
|
||||||
|
|
||||||
|
analyze.append(QJsonObject{
|
||||||
|
{"track_id", trackId},
|
||||||
|
{"artist_id", artistId},
|
||||||
|
{"genre_id", genreId},
|
||||||
|
{"label_id", labelId},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listenedIds.isEmpty() || analyze.isEmpty()) {
|
||||||
|
setPlaying(false);
|
||||||
|
m_progress->setValue(0);
|
||||||
|
m_position->setText(QStringLiteral("0:00 / 0:00"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_fetchingAutoplay = true;
|
||||||
|
m_backend->getDynamicSuggestions(listenedIds, analyze, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainToolBar::onDynamicSuggestionsLoaded(const QJsonObject &result)
|
||||||
|
{
|
||||||
|
m_fetchingAutoplay = false;
|
||||||
|
|
||||||
|
QJsonArray items;
|
||||||
|
if (result["tracks"].isObject())
|
||||||
|
items = result["tracks"].toObject()["items"].toArray();
|
||||||
|
if (items.isEmpty() && result["dynamic"].isObject())
|
||||||
|
items = result["dynamic"].toObject()["items"].toArray();
|
||||||
|
if (items.isEmpty())
|
||||||
|
items = result["items"].toArray();
|
||||||
|
|
||||||
|
if (items.isEmpty()) {
|
||||||
|
setPlaying(false);
|
||||||
|
m_progress->setValue(0);
|
||||||
|
m_position->setText(QStringLiteral("0:00 / 0:00"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_queue->appendToContext(items);
|
||||||
|
if (m_queue->canGoNext())
|
||||||
|
onNext();
|
||||||
|
}
|
||||||
|
|
||||||
void MainToolBar::fetchAlbumArt(const QString &url)
|
void MainToolBar::fetchAlbumArt(const QString &url)
|
||||||
{
|
{
|
||||||
m_nam->get(QNetworkRequest(QUrl(url)));
|
m_nam->get(QNetworkRequest(QUrl(url)));
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
class MainToolBar : public QToolBar
|
class MainToolBar : public QToolBar
|
||||||
{
|
{
|
||||||
@@ -47,6 +48,8 @@ private slots:
|
|||||||
void onTrackTransitioned();
|
void onTrackTransitioned();
|
||||||
void onQueueChanged();
|
void onQueueChanged();
|
||||||
void onShuffleToggled(bool checked);
|
void onShuffleToggled(bool checked);
|
||||||
|
void onAutoplayToggled(bool checked);
|
||||||
|
void onDynamicSuggestionsLoaded(const QJsonObject &result);
|
||||||
|
|
||||||
void fetchAlbumArt(const QString &url);
|
void fetchAlbumArt(const QString &url);
|
||||||
void onAlbumArtReady(QNetworkReply *reply);
|
void onAlbumArtReady(QNetworkReply *reply);
|
||||||
@@ -65,6 +68,7 @@ private:
|
|||||||
QLabel *m_position = nullptr;
|
QLabel *m_position = nullptr;
|
||||||
QWidget *m_rightSpacer = nullptr;
|
QWidget *m_rightSpacer = nullptr;
|
||||||
QAction *m_shuffle = nullptr;
|
QAction *m_shuffle = nullptr;
|
||||||
|
QAction *m_autoplay = nullptr;
|
||||||
VolumeButton *m_volume = nullptr;
|
VolumeButton *m_volume = nullptr;
|
||||||
QAction *m_queueBtn = nullptr;
|
QAction *m_queueBtn = nullptr;
|
||||||
QAction *m_search = nullptr;
|
QAction *m_search = nullptr;
|
||||||
@@ -72,6 +76,10 @@ private:
|
|||||||
QNetworkAccessManager *m_nam = nullptr;
|
QNetworkAccessManager *m_nam = nullptr;
|
||||||
QString m_currentArtUrl;
|
QString m_currentArtUrl;
|
||||||
QJsonObject m_currentTrack;
|
QJsonObject m_currentTrack;
|
||||||
|
QVector<QJsonObject> m_recentTracks;
|
||||||
bool m_playing = false;
|
bool m_playing = false;
|
||||||
bool m_seeking = false;
|
bool m_seeking = false;
|
||||||
|
bool m_fetchingAutoplay = false;
|
||||||
|
|
||||||
|
void requestAutoplaySuggestions();
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user