feat: album/playlist header and playlist ownership filtering
- Add TrackContextHeader widget: shows album art (fetched via NAM), title, subtitle (artist/description), and metadata (year · tracks · duration) above the track list when an album or playlist is opened - Hide header for favorite tracks and search results - Store user ID in AppSettings on login - Only show "Delete playlist" for playlists the user owns - "Add to playlist" submenu only lists owned playlists - "Remove from this playlist" only appears when viewing an owned playlist Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ namespace List
|
|||||||
static constexpr int TypeRole = Qt::UserRole + 1;
|
static constexpr int TypeRole = Qt::UserRole + 1;
|
||||||
static constexpr int IdRole = Qt::UserRole + 2;
|
static constexpr int IdRole = Qt::UserRole + 2;
|
||||||
static constexpr int NameRole = Qt::UserRole + 3;
|
static constexpr int NameRole = Qt::UserRole + 3;
|
||||||
|
static constexpr int IsOwnerRole = Qt::UserRole + 4;
|
||||||
|
|
||||||
enum NodeType {
|
enum NodeType {
|
||||||
NodeFavTracks,
|
NodeFavTracks,
|
||||||
@@ -86,22 +87,28 @@ void Library::onUserPlaylistsLoaded(const QJsonObject &result)
|
|||||||
while (m_playlistsNode->childCount() > 0)
|
while (m_playlistsNode->childCount() > 0)
|
||||||
delete m_playlistsNode->takeChild(0);
|
delete m_playlistsNode->takeChild(0);
|
||||||
|
|
||||||
QVector<QPair<qint64, QString>> playlists;
|
QVector<QPair<qint64, QString>> editablePlaylists;
|
||||||
|
const qint64 myUserId = AppSettings::instance().userId();
|
||||||
const QJsonArray items = result["items"].toArray();
|
const QJsonArray items = result["items"].toArray();
|
||||||
for (const auto &v : items) {
|
for (const auto &v : items) {
|
||||||
const QJsonObject pl = v.toObject();
|
const QJsonObject pl = v.toObject();
|
||||||
const QString name = pl["name"].toString();
|
const QString name = pl["name"].toString();
|
||||||
const qint64 id = static_cast<qint64>(pl["id"].toDouble());
|
const qint64 id = static_cast<qint64>(pl["id"].toDouble());
|
||||||
|
const qint64 ownId = static_cast<qint64>(pl["owner"].toObject()["id"].toDouble());
|
||||||
|
const bool isOwner = (myUserId > 0 && ownId == myUserId);
|
||||||
|
|
||||||
auto *item = new QTreeWidgetItem(m_playlistsNode, QStringList{name});
|
auto *item = new QTreeWidgetItem(m_playlistsNode, QStringList{name});
|
||||||
item->setData(0, TypeRole, NodePlaylist);
|
item->setData(0, TypeRole, NodePlaylist);
|
||||||
item->setData(0, IdRole, id);
|
item->setData(0, IdRole, id);
|
||||||
item->setData(0, NameRole, name);
|
item->setData(0, NameRole, name);
|
||||||
|
item->setData(0, IsOwnerRole, isOwner);
|
||||||
|
|
||||||
playlists.append({id, name});
|
// Only include playlists we can edit in the "Add to playlist" submenu
|
||||||
|
if (isOwner)
|
||||||
|
editablePlaylists.append({id, name});
|
||||||
}
|
}
|
||||||
|
|
||||||
emit userPlaylistsChanged(playlists);
|
emit userPlaylistsChanged(editablePlaylists);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Library::onContextMenuRequested(const QPoint &pos)
|
void Library::onContextMenuRequested(const QPoint &pos)
|
||||||
@@ -130,7 +137,9 @@ void Library::onContextMenuRequested(const QPoint &pos)
|
|||||||
if (isPlaylistItem) {
|
if (isPlaylistItem) {
|
||||||
const qint64 plId = item->data(0, IdRole).toLongLong();
|
const qint64 plId = item->data(0, IdRole).toLongLong();
|
||||||
const QString plName = item->data(0, NameRole).toString();
|
const QString plName = item->data(0, NameRole).toString();
|
||||||
|
const bool isOwner = item->data(0, IsOwnerRole).toBool();
|
||||||
|
|
||||||
|
if (isOwner) {
|
||||||
menu.addSeparator();
|
menu.addSeparator();
|
||||||
auto *delPl = menu.addAction(tr("Delete \"%1\"…").arg(plName));
|
auto *delPl = menu.addAction(tr("Delete \"%1\"…").arg(plName));
|
||||||
connect(delPl, &QAction::triggered, this, [this, plId, plName] {
|
connect(delPl, &QAction::triggered, this, [this, plId, plName] {
|
||||||
@@ -144,6 +153,7 @@ void Library::onContextMenuRequested(const QPoint &pos)
|
|||||||
m_backend->deletePlaylist(plId);
|
m_backend->deletePlaylist(plId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
menu.exec(viewport()->mapToGlobal(pos));
|
menu.exec(viewport()->mapToGlobal(pos));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../backend/qobuzbackend.hpp"
|
#include "../backend/qobuzbackend.hpp"
|
||||||
|
#include "../util/settings.hpp"
|
||||||
|
|
||||||
#include <QTreeWidget>
|
#include <QTreeWidget>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "tracks.hpp"
|
#include "tracks.hpp"
|
||||||
|
#include "../util/settings.hpp"
|
||||||
|
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
@@ -53,7 +54,10 @@ void Tracks::loadAlbum(const QJsonObject &album)
|
|||||||
void Tracks::loadPlaylist(const QJsonObject &playlist)
|
void Tracks::loadPlaylist(const QJsonObject &playlist)
|
||||||
{
|
{
|
||||||
const qint64 id = static_cast<qint64>(playlist["id"].toDouble());
|
const qint64 id = static_cast<qint64>(playlist["id"].toDouble());
|
||||||
setPlaylistContext(id);
|
const qint64 ownId = static_cast<qint64>(playlist["owner"].toObject()["id"].toDouble());
|
||||||
|
const qint64 myId = AppSettings::instance().userId();
|
||||||
|
const bool isOwned = (myId > 0 && ownId == myId);
|
||||||
|
setPlaylistContext(id, isOwned);
|
||||||
const QJsonArray items = playlist["tracks"].toObject()["items"].toArray();
|
const QJsonArray items = playlist["tracks"].toObject()["items"].toArray();
|
||||||
m_model->setTracks(items, /*usePosition=*/true);
|
m_model->setTracks(items, /*usePosition=*/true);
|
||||||
}
|
}
|
||||||
@@ -64,9 +68,10 @@ void Tracks::loadSearchTracks(const QJsonArray &tracks)
|
|||||||
m_model->setTracks(tracks, false, /*useSequential=*/true);
|
m_model->setTracks(tracks, false, /*useSequential=*/true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tracks::setPlaylistContext(qint64 playlistId)
|
void Tracks::setPlaylistContext(qint64 playlistId, bool isOwned)
|
||||||
{
|
{
|
||||||
m_playlistId = playlistId;
|
m_playlistId = playlistId;
|
||||||
|
m_playlistIsOwned = isOwned;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tracks::setUserPlaylists(const QVector<QPair<qint64, QString>> &playlists)
|
void Tracks::setUserPlaylists(const QVector<QPair<qint64, QString>> &playlists)
|
||||||
@@ -164,7 +169,7 @@ void Tracks::onContextMenu(const QPoint &pos)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_playlistId > 0) {
|
if (m_playlistId > 0 && m_playlistIsOwned) {
|
||||||
const qint64 playlistTrackId =
|
const qint64 playlistTrackId =
|
||||||
m_model->data(index, TrackListModel::PlaylistTrackIdRole).toLongLong();
|
m_model->data(index, TrackListModel::PlaylistTrackIdRole).toLongLong();
|
||||||
if (playlistTrackId > 0) {
|
if (playlistTrackId > 0) {
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ namespace List
|
|||||||
void setPlayingTrackId(qint64 id);
|
void setPlayingTrackId(qint64 id);
|
||||||
|
|
||||||
/// Set which playlist is currently displayed (0 = none).
|
/// Set which playlist is currently displayed (0 = none).
|
||||||
void setPlaylistContext(qint64 playlistId);
|
/// isOwned controls whether "Remove from this playlist" is shown.
|
||||||
|
void setPlaylistContext(qint64 playlistId, bool isOwned = false);
|
||||||
qint64 playlistId() const { return m_playlistId; }
|
qint64 playlistId() const { return m_playlistId; }
|
||||||
/// Provide the user's playlist list for the "Add to playlist" submenu.
|
/// Provide the user's playlist list for the "Add to playlist" submenu.
|
||||||
void setUserPlaylists(const QVector<QPair<qint64, QString>> &playlists);
|
void setUserPlaylists(const QVector<QPair<qint64, QString>> &playlists);
|
||||||
@@ -44,6 +45,7 @@ namespace List
|
|||||||
QobuzBackend *m_backend = nullptr;
|
QobuzBackend *m_backend = nullptr;
|
||||||
PlayQueue *m_queue = nullptr;
|
PlayQueue *m_queue = nullptr;
|
||||||
qint64 m_playlistId = 0;
|
qint64 m_playlistId = 0;
|
||||||
|
bool m_playlistIsOwned = false;
|
||||||
QVector<QPair<qint64, QString>> m_userPlaylists;
|
QVector<QPair<qint64, QString>> m_userPlaylists;
|
||||||
|
|
||||||
void onDoubleClicked(const QModelIndex &index);
|
void onDoubleClicked(const QModelIndex &index);
|
||||||
|
|||||||
@@ -242,6 +242,9 @@ void MainWindow::onLoginSuccess(const QString &token, const QJsonObject &user)
|
|||||||
const QString email = user["email"].toString();
|
const QString email = user["email"].toString();
|
||||||
AppSettings::instance().setDisplayName(displayName);
|
AppSettings::instance().setDisplayName(displayName);
|
||||||
AppSettings::instance().setUserEmail(email);
|
AppSettings::instance().setUserEmail(email);
|
||||||
|
const qint64 userId = static_cast<qint64>(user["id"].toDouble());
|
||||||
|
if (userId > 0)
|
||||||
|
AppSettings::instance().setUserId(userId);
|
||||||
statusBar()->showMessage(tr("Signed in as %1").arg(
|
statusBar()->showMessage(tr("Signed in as %1").arg(
|
||||||
displayName.isEmpty() ? email : displayName));
|
displayName.isEmpty() ? email : displayName));
|
||||||
m_library->refresh();
|
m_library->refresh();
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ public:
|
|||||||
QString displayName() const { return m_settings.value("user/display_name").toString(); }
|
QString displayName() const { return m_settings.value("user/display_name").toString(); }
|
||||||
void setDisplayName(const QString &n) { m_settings.setValue("user/display_name", n); }
|
void setDisplayName(const QString &n) { m_settings.setValue("user/display_name", n); }
|
||||||
|
|
||||||
|
qint64 userId() const { return m_settings.value("user/id", 0LL).toLongLong(); }
|
||||||
|
void setUserId(qint64 id) { m_settings.setValue("user/id", id); }
|
||||||
|
|
||||||
// 5 = MP3, 6 = CD, 7 = HiRes96, 27 = HiRes192
|
// 5 = MP3, 6 = CD, 7 = HiRes96, 27 = HiRes192
|
||||||
int preferredFormat() const { return m_settings.value("playback/format", 6).toInt(); }
|
int preferredFormat() const { return m_settings.value("playback/format", 6).toInt(); }
|
||||||
void setPreferredFormat(int f) { m_settings.setValue("playback/format", f); }
|
void setPreferredFormat(int f) { m_settings.setValue("playback/format", f); }
|
||||||
|
|||||||
@@ -19,13 +19,24 @@ MainContent::MainContent(QobuzBackend *backend, PlayQueue *queue, QWidget *paren
|
|||||||
this);
|
this);
|
||||||
m_welcome->setAlignment(Qt::AlignCenter);
|
m_welcome->setAlignment(Qt::AlignCenter);
|
||||||
|
|
||||||
m_tracks = new List::Tracks(m_backend, queue, this);
|
// Tracks page: context header + track list
|
||||||
|
auto *tracksPage = new QWidget(this);
|
||||||
|
auto *tracksLayout = new QVBoxLayout(tracksPage);
|
||||||
|
tracksLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
tracksLayout->setSpacing(0);
|
||||||
|
|
||||||
|
m_header = new TrackContextHeader(tracksPage);
|
||||||
|
m_header->hide();
|
||||||
|
m_tracks = new List::Tracks(m_backend, queue, tracksPage);
|
||||||
|
tracksLayout->addWidget(m_header);
|
||||||
|
tracksLayout->addWidget(m_tracks, 1);
|
||||||
|
|
||||||
m_albumList = new AlbumListView(this);
|
m_albumList = new AlbumListView(this);
|
||||||
m_artistList = new ArtistListView(this);
|
m_artistList = new ArtistListView(this);
|
||||||
m_artistView = new ArtistView(this);
|
m_artistView = new ArtistView(this);
|
||||||
|
|
||||||
m_stack->addWidget(m_welcome); // 0
|
m_stack->addWidget(m_welcome); // 0
|
||||||
m_stack->addWidget(m_tracks); // 1
|
m_stack->addWidget(tracksPage); // 1
|
||||||
m_stack->addWidget(m_albumList); // 2
|
m_stack->addWidget(m_albumList); // 2
|
||||||
m_stack->addWidget(m_artistList); // 3
|
m_stack->addWidget(m_artistList); // 3
|
||||||
m_stack->addWidget(m_artistView); // 4
|
m_stack->addWidget(m_artistView); // 4
|
||||||
@@ -41,24 +52,28 @@ void MainContent::showWelcome() { m_stack->setCurrentIndex(0); }
|
|||||||
|
|
||||||
void MainContent::showAlbum(const QJsonObject &album)
|
void MainContent::showAlbum(const QJsonObject &album)
|
||||||
{
|
{
|
||||||
|
m_header->setAlbum(album);
|
||||||
m_tracks->loadAlbum(album);
|
m_tracks->loadAlbum(album);
|
||||||
m_stack->setCurrentIndex(1);
|
m_stack->setCurrentIndex(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainContent::showPlaylist(const QJsonObject &playlist)
|
void MainContent::showPlaylist(const QJsonObject &playlist)
|
||||||
{
|
{
|
||||||
|
m_header->setPlaylist(playlist);
|
||||||
m_tracks->loadPlaylist(playlist);
|
m_tracks->loadPlaylist(playlist);
|
||||||
m_stack->setCurrentIndex(1);
|
m_stack->setCurrentIndex(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainContent::showFavTracks(const QJsonObject &result)
|
void MainContent::showFavTracks(const QJsonObject &result)
|
||||||
{
|
{
|
||||||
|
m_header->hide();
|
||||||
m_tracks->loadTracks(result["items"].toArray());
|
m_tracks->loadTracks(result["items"].toArray());
|
||||||
m_stack->setCurrentIndex(1);
|
m_stack->setCurrentIndex(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainContent::showSearchTracks(const QJsonArray &tracks)
|
void MainContent::showSearchTracks(const QJsonArray &tracks)
|
||||||
{
|
{
|
||||||
|
m_header->hide();
|
||||||
m_tracks->loadSearchTracks(tracks);
|
m_tracks->loadSearchTracks(tracks);
|
||||||
m_stack->setCurrentIndex(1);
|
m_stack->setCurrentIndex(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "albumlistview.hpp"
|
#include "albumlistview.hpp"
|
||||||
#include "artistlistview.hpp"
|
#include "artistlistview.hpp"
|
||||||
#include "artistview.hpp"
|
#include "artistview.hpp"
|
||||||
|
#include "trackcontextheader.hpp"
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
@@ -40,6 +41,7 @@ private:
|
|||||||
QStackedWidget *m_stack = nullptr;
|
QStackedWidget *m_stack = nullptr;
|
||||||
QLabel *m_welcome = nullptr;
|
QLabel *m_welcome = nullptr;
|
||||||
List::Tracks *m_tracks = nullptr;
|
List::Tracks *m_tracks = nullptr;
|
||||||
|
TrackContextHeader *m_header = nullptr;
|
||||||
AlbumListView *m_albumList = nullptr;
|
AlbumListView *m_albumList = nullptr;
|
||||||
ArtistListView *m_artistList = nullptr;
|
ArtistListView *m_artistList = nullptr;
|
||||||
ArtistView *m_artistView = nullptr;
|
ArtistView *m_artistView = nullptr;
|
||||||
|
|||||||
162
src/view/trackcontextheader.hpp
Normal file
162
src/view/trackcontextheader.hpp
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QFont>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
/// Header strip shown above the track list when an album or playlist is open.
|
||||||
|
/// Displays album art, title, subtitle, and metadata.
|
||||||
|
class TrackContextHeader : public QWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit TrackContextHeader(QWidget *parent = nullptr)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
setFixedHeight(140);
|
||||||
|
|
||||||
|
auto *hlay = new QHBoxLayout(this);
|
||||||
|
hlay->setContentsMargins(12, 8, 12, 8);
|
||||||
|
hlay->setSpacing(14);
|
||||||
|
|
||||||
|
m_art = new QLabel(this);
|
||||||
|
m_art->setFixedSize(120, 120);
|
||||||
|
m_art->setScaledContents(true);
|
||||||
|
m_art->setAlignment(Qt::AlignCenter);
|
||||||
|
m_art->setStyleSheet(QStringLiteral("background: #1a1a1a; border-radius: 4px;"));
|
||||||
|
hlay->addWidget(m_art, 0, Qt::AlignVCenter);
|
||||||
|
|
||||||
|
auto *info = new QWidget(this);
|
||||||
|
auto *vlay = new QVBoxLayout(info);
|
||||||
|
vlay->setContentsMargins(0, 0, 0, 0);
|
||||||
|
vlay->setSpacing(4);
|
||||||
|
|
||||||
|
m_title = new QLabel(info);
|
||||||
|
QFont tf = m_title->font();
|
||||||
|
tf.setPointSize(tf.pointSize() + 5);
|
||||||
|
tf.setBold(true);
|
||||||
|
m_title->setFont(tf);
|
||||||
|
m_title->setWordWrap(true);
|
||||||
|
vlay->addWidget(m_title);
|
||||||
|
|
||||||
|
m_subtitle = new QLabel(info);
|
||||||
|
QFont sf = m_subtitle->font();
|
||||||
|
sf.setPointSize(sf.pointSize() + 1);
|
||||||
|
m_subtitle->setFont(sf);
|
||||||
|
m_subtitle->setWordWrap(true);
|
||||||
|
vlay->addWidget(m_subtitle);
|
||||||
|
|
||||||
|
m_meta = new QLabel(info);
|
||||||
|
QPalette mp = m_meta->palette();
|
||||||
|
mp.setColor(QPalette::WindowText, QColor(0xaa, 0xaa, 0xaa));
|
||||||
|
m_meta->setPalette(mp);
|
||||||
|
vlay->addWidget(m_meta);
|
||||||
|
|
||||||
|
vlay->addStretch();
|
||||||
|
hlay->addWidget(info, 1);
|
||||||
|
|
||||||
|
m_nam = new QNetworkAccessManager(this);
|
||||||
|
QObject::connect(m_nam, &QNetworkAccessManager::finished,
|
||||||
|
[this](QNetworkReply *reply) {
|
||||||
|
reply->deleteLater();
|
||||||
|
if (reply->error() != QNetworkReply::NoError)
|
||||||
|
return;
|
||||||
|
QPixmap pix;
|
||||||
|
if (pix.loadFromData(reply->readAll()))
|
||||||
|
m_art->setPixmap(pix);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAlbum(const QJsonObject &album)
|
||||||
|
{
|
||||||
|
m_title->setText(album["title"].toString());
|
||||||
|
m_subtitle->setText(album["artist"].toObject()["name"].toString());
|
||||||
|
m_meta->setText(buildAlbumMeta(album));
|
||||||
|
fetchArt(album["image"].toObject());
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPlaylist(const QJsonObject &playlist)
|
||||||
|
{
|
||||||
|
m_title->setText(playlist["name"].toString());
|
||||||
|
const QString desc = playlist["description"].toString();
|
||||||
|
const QString owner = playlist["owner"].toObject()["name"].toString();
|
||||||
|
m_subtitle->setText(desc.isEmpty() ? owner : desc);
|
||||||
|
m_meta->setText(buildPlaylistMeta(playlist));
|
||||||
|
|
||||||
|
const QJsonArray imgs = playlist["image_rectangle"].toArray();
|
||||||
|
if (!imgs.isEmpty())
|
||||||
|
fetchUrl(imgs.first().toString());
|
||||||
|
else
|
||||||
|
m_art->setPixmap(QPixmap());
|
||||||
|
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void fetchArt(const QJsonObject &img)
|
||||||
|
{
|
||||||
|
QString url = img["large"].toString();
|
||||||
|
if (url.isEmpty()) url = img["small"].toString();
|
||||||
|
fetchUrl(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fetchUrl(const QString &url)
|
||||||
|
{
|
||||||
|
if (url.isEmpty()) {
|
||||||
|
m_art->setPixmap(QPixmap());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (url == m_currentArtUrl)
|
||||||
|
return;
|
||||||
|
m_currentArtUrl = url;
|
||||||
|
m_nam->get(QNetworkRequest(QUrl(url)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString formatDuration(int totalSecs)
|
||||||
|
{
|
||||||
|
const int h = totalSecs / 3600;
|
||||||
|
const int m = (totalSecs % 3600) / 60;
|
||||||
|
if (h > 0)
|
||||||
|
return QStringLiteral("%1h %2m").arg(h).arg(m);
|
||||||
|
return QStringLiteral("%1 min").arg(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString buildAlbumMeta(const QJsonObject &album)
|
||||||
|
{
|
||||||
|
QStringList parts;
|
||||||
|
const QString year = album["release_date_original"].toString().left(4);
|
||||||
|
if (!year.isEmpty()) parts << year;
|
||||||
|
const int tracks = album["tracks_count"].toInt();
|
||||||
|
if (tracks > 0) parts << QStringLiteral("%1 tracks").arg(tracks);
|
||||||
|
const int dur = static_cast<int>(album["duration"].toDouble());
|
||||||
|
if (dur > 0) parts << formatDuration(dur);
|
||||||
|
return parts.join(QStringLiteral(" · "));
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString buildPlaylistMeta(const QJsonObject &playlist)
|
||||||
|
{
|
||||||
|
QStringList parts;
|
||||||
|
const int tracks = playlist["tracks_count"].toInt();
|
||||||
|
if (tracks > 0) parts << QStringLiteral("%1 tracks").arg(tracks);
|
||||||
|
const int dur = static_cast<int>(playlist["duration"].toDouble());
|
||||||
|
if (dur > 0) parts << formatDuration(dur);
|
||||||
|
return parts.join(QStringLiteral(" · "));
|
||||||
|
}
|
||||||
|
|
||||||
|
QLabel *m_art = nullptr;
|
||||||
|
QLabel *m_title = nullptr;
|
||||||
|
QLabel *m_subtitle = nullptr;
|
||||||
|
QLabel *m_meta = nullptr;
|
||||||
|
QNetworkAccessManager *m_nam = nullptr;
|
||||||
|
QString m_currentArtUrl;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user