feat: restore most-popular search and top results badges
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:
@@ -37,6 +37,7 @@ enum QobuzEvent {
|
|||||||
EV_USER_OK = 23,
|
EV_USER_OK = 23,
|
||||||
EV_ARTIST_RELEASES_OK = 24,
|
EV_ARTIST_RELEASES_OK = 24,
|
||||||
EV_DEEP_SHUFFLE_OK = 25,
|
EV_DEEP_SHUFFLE_OK = 25,
|
||||||
|
EV_MOST_POPULAR_OK = 26,
|
||||||
EV_GENRES_OK = 27,
|
EV_GENRES_OK = 27,
|
||||||
EV_FEATURED_ALBUMS_OK = 28,
|
EV_FEATURED_ALBUMS_OK = 28,
|
||||||
};
|
};
|
||||||
@@ -55,6 +56,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_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);
|
||||||
|
|||||||
@@ -453,6 +453,24 @@ impl QobuzClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_most_popular(
|
||||||
|
&self,
|
||||||
|
query: &str,
|
||||||
|
offset: u32,
|
||||||
|
limit: u32,
|
||||||
|
) -> Result<Value> {
|
||||||
|
let resp = self
|
||||||
|
.get_request("most-popular/get")
|
||||||
|
.query(&[
|
||||||
|
("query", query.to_string()),
|
||||||
|
("offset", offset.to_string()),
|
||||||
|
("limit", limit.to_string()),
|
||||||
|
])
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
Self::check_response(resp).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn search_tracks(
|
async fn search_tracks(
|
||||||
&self,
|
&self,
|
||||||
query: &str,
|
query: &str,
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ pub const EV_TRACK_URL_ERR: c_int = 18;
|
|||||||
pub const EV_GENERIC_ERR: c_int = 19;
|
pub const EV_GENERIC_ERR: c_int = 19;
|
||||||
pub const EV_ARTIST_RELEASES_OK: c_int = 24;
|
pub const EV_ARTIST_RELEASES_OK: c_int = 24;
|
||||||
pub const EV_DEEP_SHUFFLE_OK: c_int = 25;
|
pub const EV_DEEP_SHUFFLE_OK: c_int = 25;
|
||||||
|
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;
|
||||||
|
|
||||||
@@ -203,6 +204,36 @@ pub unsafe extern "C" fn qobuz_backend_search(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn qobuz_backend_most_popular_search(
|
||||||
|
ptr: *mut Backend,
|
||||||
|
query: *const c_char,
|
||||||
|
limit: u32,
|
||||||
|
) {
|
||||||
|
let inner = &(*ptr).0;
|
||||||
|
let query = CStr::from_ptr(query).to_string_lossy().into_owned();
|
||||||
|
let client = inner.client.clone();
|
||||||
|
let cb = inner.cb;
|
||||||
|
let ud = inner.ud;
|
||||||
|
|
||||||
|
spawn(inner, async move {
|
||||||
|
let result = client
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.get_most_popular(&query, 0, limit)
|
||||||
|
.await;
|
||||||
|
match result {
|
||||||
|
Ok(r) => call_cb(
|
||||||
|
cb,
|
||||||
|
ud,
|
||||||
|
EV_MOST_POPULAR_OK,
|
||||||
|
&serde_json::to_string(&r).unwrap_or_default(),
|
||||||
|
),
|
||||||
|
Err(e) => call_cb(cb, ud, EV_SEARCH_ERR, &err_json(&e.to_string())),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ---------- Album ----------
|
// ---------- Album ----------
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ void QobuzBackend::search(const QString &query, quint32 offset, quint32 limit)
|
|||||||
qobuz_backend_search(m_backend, query.toUtf8().constData(), offset, limit);
|
qobuz_backend_search(m_backend, query.toUtf8().constData(), offset, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QobuzBackend::mostPopularSearch(const QString &query, quint32 limit)
|
||||||
|
{
|
||||||
|
qobuz_backend_most_popular_search(m_backend, query.toUtf8().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());
|
||||||
@@ -251,6 +256,9 @@ void QobuzBackend::onEvent(int eventType, const QString &json)
|
|||||||
case EV_SEARCH_OK:
|
case EV_SEARCH_OK:
|
||||||
emit searchResult(obj);
|
emit searchResult(obj);
|
||||||
break;
|
break;
|
||||||
|
case 26: // EV_MOST_POPULAR_OK
|
||||||
|
emit mostPopularResult(obj);
|
||||||
|
break;
|
||||||
case EV_SEARCH_ERR:
|
case EV_SEARCH_ERR:
|
||||||
emit error(obj["error"].toString());
|
emit error(obj["error"].toString());
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -28,6 +28,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 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);
|
||||||
@@ -83,6 +84,7 @@ signals:
|
|||||||
|
|
||||||
// catalog
|
// catalog
|
||||||
void searchResult(const QJsonObject &result);
|
void searchResult(const QJsonObject &result);
|
||||||
|
void mostPopularResult(const QJsonObject &result);
|
||||||
void albumLoaded(const QJsonObject &album);
|
void albumLoaded(const QJsonObject &album);
|
||||||
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);
|
||||||
|
|||||||
@@ -39,6 +39,14 @@ SearchTab::SearchTab(QobuzBackend *backend, PlayQueue *queue, QWidget *parent)
|
|||||||
// Result tabs
|
// Result tabs
|
||||||
m_resultTabs = new QTabWidget(this);
|
m_resultTabs = new QTabWidget(this);
|
||||||
|
|
||||||
|
m_topResults = new QTreeWidget(this);
|
||||||
|
m_topResults->setHeaderLabels({tr(""), tr("Top Result"), tr("Info")});
|
||||||
|
m_topResults->setRootIsDecorated(false);
|
||||||
|
m_topResults->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
||||||
|
m_topResults->header()->setSectionResizeMode(1, QHeaderView::Stretch);
|
||||||
|
m_topResults->header()->setSectionResizeMode(2, QHeaderView::Stretch);
|
||||||
|
m_topResults->header()->setStretchLastSection(false);
|
||||||
|
|
||||||
m_trackResults = new QTreeWidget(this);
|
m_trackResults = new QTreeWidget(this);
|
||||||
m_trackResults->setHeaderLabels({tr("Title"), tr("Artist"), tr("Album")});
|
m_trackResults->setHeaderLabels({tr("Title"), tr("Artist"), tr("Album")});
|
||||||
m_trackResults->setRootIsDecorated(false);
|
m_trackResults->setRootIsDecorated(false);
|
||||||
@@ -57,6 +65,7 @@ SearchTab::SearchTab(QobuzBackend *backend, PlayQueue *queue, QWidget *parent)
|
|||||||
m_artistResults->setHeaderLabels({tr("Artist")});
|
m_artistResults->setHeaderLabels({tr("Artist")});
|
||||||
m_artistResults->setRootIsDecorated(false);
|
m_artistResults->setRootIsDecorated(false);
|
||||||
|
|
||||||
|
m_resultTabs->addTab(m_topResults, tr("Top Results"));
|
||||||
m_resultTabs->addTab(m_trackResults, tr("Tracks"));
|
m_resultTabs->addTab(m_trackResults, tr("Tracks"));
|
||||||
m_resultTabs->addTab(m_albumResults, tr("Albums"));
|
m_resultTabs->addTab(m_albumResults, tr("Albums"));
|
||||||
m_resultTabs->addTab(m_artistResults, tr("Artists"));
|
m_resultTabs->addTab(m_artistResults, tr("Artists"));
|
||||||
@@ -66,7 +75,9 @@ SearchTab::SearchTab(QobuzBackend *backend, PlayQueue *queue, QWidget *parent)
|
|||||||
connect(m_searchBox, &QLineEdit::returnPressed, this, &SearchTab::onSearchSubmit);
|
connect(m_searchBox, &QLineEdit::returnPressed, this, &SearchTab::onSearchSubmit);
|
||||||
|
|
||||||
connect(m_backend, &QobuzBackend::searchResult, this, &SearchTab::onSearchResult);
|
connect(m_backend, &QobuzBackend::searchResult, this, &SearchTab::onSearchResult);
|
||||||
|
connect(m_backend, &QobuzBackend::mostPopularResult, this, &SearchTab::onMostPopularResult);
|
||||||
|
|
||||||
|
connect(m_topResults, &QTreeWidget::itemDoubleClicked, this, &SearchTab::onItemDoubleClicked);
|
||||||
connect(m_trackResults, &QTreeWidget::itemDoubleClicked, this, &SearchTab::onItemDoubleClicked);
|
connect(m_trackResults, &QTreeWidget::itemDoubleClicked, this, &SearchTab::onItemDoubleClicked);
|
||||||
connect(m_albumResults, &QTreeWidget::itemDoubleClicked, this, &SearchTab::onItemDoubleClicked);
|
connect(m_albumResults, &QTreeWidget::itemDoubleClicked, this, &SearchTab::onItemDoubleClicked);
|
||||||
connect(m_artistResults, &QTreeWidget::itemDoubleClicked, this, &SearchTab::onItemDoubleClicked);
|
connect(m_artistResults, &QTreeWidget::itemDoubleClicked, this, &SearchTab::onItemDoubleClicked);
|
||||||
@@ -86,8 +97,67 @@ void SearchTab::setUserPlaylists(const QVector<QPair<qint64, QString>> &playlist
|
|||||||
void SearchTab::onSearchSubmit()
|
void SearchTab::onSearchSubmit()
|
||||||
{
|
{
|
||||||
const QString q = m_searchBox->text().trimmed();
|
const QString q = m_searchBox->text().trimmed();
|
||||||
if (!q.isEmpty())
|
if (!q.isEmpty()) {
|
||||||
|
m_backend->mostPopularSearch(q, 8);
|
||||||
m_backend->search(q, 0, 20);
|
m_backend->search(q, 0, 20);
|
||||||
|
m_resultTabs->setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchTab::onMostPopularResult(const QJsonObject &result)
|
||||||
|
{
|
||||||
|
m_topResults->clear();
|
||||||
|
|
||||||
|
QFont badgeFont;
|
||||||
|
badgeFont.setBold(true);
|
||||||
|
|
||||||
|
const QJsonArray items = result["most_popular"].toObject()["items"].toArray();
|
||||||
|
for (const auto &value : items) {
|
||||||
|
const QJsonObject itemObj = value.toObject();
|
||||||
|
const QString type = itemObj["type"].toString();
|
||||||
|
const QJsonObject content = itemObj["content"].toObject();
|
||||||
|
|
||||||
|
auto *item = new QTreeWidgetItem(m_topResults);
|
||||||
|
item->setData(0, JsonRole, content);
|
||||||
|
|
||||||
|
if (type == QStringLiteral("tracks")) {
|
||||||
|
const QString title = content["title"].toString();
|
||||||
|
const QString artist = content["performer"].toObject()["name"].toString();
|
||||||
|
const QString album = content["album"].toObject()["title"].toString();
|
||||||
|
item->setText(0, QStringLiteral("T"));
|
||||||
|
item->setForeground(0, QColor(QStringLiteral("#2FA84F")));
|
||||||
|
item->setFont(0, badgeFont);
|
||||||
|
item->setTextAlignment(0, Qt::AlignCenter);
|
||||||
|
item->setText(1, title);
|
||||||
|
item->setText(2, artist.isEmpty() ? album : QStringLiteral("%1 - %2").arg(artist, album));
|
||||||
|
item->setData(0, TypeRole, QStringLiteral("track"));
|
||||||
|
item->setData(0, IdRole, static_cast<qint64>(content["id"].toDouble()));
|
||||||
|
} else if (type == QStringLiteral("albums")) {
|
||||||
|
const QString title = content["title"].toString();
|
||||||
|
const QString artist = content["artist"].toObject()["name"].toString();
|
||||||
|
const bool hiRes = content["hires_streamable"].toBool()
|
||||||
|
|| content["rights"].toObject()["hires_streamable"].toBool();
|
||||||
|
item->setText(0, hiRes ? QStringLiteral("H") : QStringLiteral("A"));
|
||||||
|
item->setForeground(0, hiRes
|
||||||
|
? QColor(QStringLiteral("#FFB232"))
|
||||||
|
: QColor(QStringLiteral("#8E8E93")));
|
||||||
|
item->setFont(0, badgeFont);
|
||||||
|
item->setTextAlignment(0, Qt::AlignCenter);
|
||||||
|
item->setText(1, title);
|
||||||
|
item->setText(2, artist);
|
||||||
|
item->setData(0, TypeRole, QStringLiteral("album"));
|
||||||
|
item->setData(1, IdRole, content["id"].toString());
|
||||||
|
} else if (type == QStringLiteral("artists")) {
|
||||||
|
item->setText(0, QStringLiteral("A"));
|
||||||
|
item->setForeground(0, QColor(QStringLiteral("#2B7CD3")));
|
||||||
|
item->setFont(0, badgeFont);
|
||||||
|
item->setTextAlignment(0, Qt::AlignCenter);
|
||||||
|
item->setText(1, content["name"].toString());
|
||||||
|
item->setText(2, tr("Artist"));
|
||||||
|
item->setData(0, TypeRole, QStringLiteral("artist"));
|
||||||
|
item->setData(0, IdRole, static_cast<qint64>(content["id"].toDouble()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchTab::onSearchResult(const QJsonObject &result)
|
void SearchTab::onSearchResult(const QJsonObject &result)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ namespace SidePanel
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onSearchResult(const QJsonObject &result);
|
void onSearchResult(const QJsonObject &result);
|
||||||
|
void onMostPopularResult(const QJsonObject &result);
|
||||||
void onSearchSubmit();
|
void onSearchSubmit();
|
||||||
void onItemDoubleClicked(QTreeWidgetItem *item, int column);
|
void onItemDoubleClicked(QTreeWidgetItem *item, int column);
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ namespace SidePanel
|
|||||||
PlayQueue *m_queue = nullptr;
|
PlayQueue *m_queue = nullptr;
|
||||||
QLineEdit *m_searchBox = nullptr;
|
QLineEdit *m_searchBox = nullptr;
|
||||||
QTabWidget *m_resultTabs = nullptr;
|
QTabWidget *m_resultTabs = nullptr;
|
||||||
|
QTreeWidget *m_topResults = nullptr;
|
||||||
QTreeWidget *m_trackResults = nullptr;
|
QTreeWidget *m_trackResults = nullptr;
|
||||||
QTreeWidget *m_albumResults = nullptr;
|
QTreeWidget *m_albumResults = nullptr;
|
||||||
QTreeWidget *m_artistResults = nullptr;
|
QTreeWidget *m_artistResults = nullptr;
|
||||||
|
|||||||
Reference in New Issue
Block a user