feat: artist/page endpoint, multi-disc fix, playlist ownership, UX improvements
- Switch artist view to artist/page API (proper sections: Albums, Singles & EPs, Live, Compilations; version in titles like "Deluxe") - Fix artist sections categorization using releases[].type from artist/page - Add getUser() backend call; fetch on session restore when userId=0 to fix playlist ownership (Remove from playlist / Delete playlist were missing) - Fix multi-disc double-click / Play Now queue start index (disc headers were counted in row index but excluded from currentTracksJson) - Hide redundant Album column when viewing an album - Make artist name in context header clickable (navigates to artist page) - Fix gap between title and artist name in context header (addStretch) - Fix queue panel track titles to include version field - Fix album list to show version in title column Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include <QScrollArea>
|
||||
#include <QFont>
|
||||
#include <QJsonValue>
|
||||
#include <QRegularExpression>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ArtistSection
|
||||
@@ -104,50 +105,74 @@ ArtistView::ArtistView(QWidget *parent)
|
||||
sectLayout->setContentsMargins(0, 0, 0, 0);
|
||||
sectLayout->setSpacing(8);
|
||||
|
||||
m_secAlbums = new ArtistSection(tr("Albums"), content);
|
||||
m_secEps = new ArtistSection(tr("EPs & Singles"), content);
|
||||
m_secOther = new ArtistSection(tr("Other"), content);
|
||||
m_secAlbums = new ArtistSection(tr("Albums"), content);
|
||||
m_secEps = new ArtistSection(tr("Singles & EPs"), content);
|
||||
m_secLive = new ArtistSection(tr("Live"), content);
|
||||
m_secCompilations = new ArtistSection(tr("Compilations"), content);
|
||||
m_secOther = new ArtistSection(tr("Other"), content);
|
||||
|
||||
sectLayout->addWidget(m_secAlbums);
|
||||
sectLayout->addWidget(m_secEps);
|
||||
sectLayout->addWidget(m_secLive);
|
||||
sectLayout->addWidget(m_secCompilations);
|
||||
sectLayout->addWidget(m_secOther);
|
||||
sectLayout->addStretch();
|
||||
|
||||
scroll->setWidget(content);
|
||||
outerLayout->addWidget(scroll, 1);
|
||||
|
||||
connect(m_secAlbums, &ArtistSection::albumSelected, this, &ArtistView::albumSelected);
|
||||
connect(m_secEps, &ArtistSection::albumSelected, this, &ArtistView::albumSelected);
|
||||
connect(m_secOther, &ArtistSection::albumSelected, this, &ArtistView::albumSelected);
|
||||
connect(m_secAlbums, &ArtistSection::albumSelected, this, &ArtistView::albumSelected);
|
||||
connect(m_secEps, &ArtistSection::albumSelected, this, &ArtistView::albumSelected);
|
||||
connect(m_secLive, &ArtistSection::albumSelected, this, &ArtistView::albumSelected);
|
||||
connect(m_secCompilations, &ArtistSection::albumSelected, this, &ArtistView::albumSelected);
|
||||
connect(m_secOther, &ArtistSection::albumSelected, this, &ArtistView::albumSelected);
|
||||
}
|
||||
|
||||
void ArtistView::setArtist(const QJsonObject &artist)
|
||||
{
|
||||
m_nameLabel->setText(artist["name"].toString());
|
||||
// artist/page: name is {"display": "..."}
|
||||
m_nameLabel->setText(artist["name"].toObject()["display"].toString());
|
||||
|
||||
const QString summary = artist["biography"].toObject()["summary"].toString();
|
||||
m_bioLabel->setText(summary);
|
||||
m_bioLabel->setVisible(!summary.isEmpty());
|
||||
// biography.content is HTML — strip tags for the summary label
|
||||
const QString bioHtml = artist["biography"].toObject()["content"].toString();
|
||||
if (!bioHtml.isEmpty()) {
|
||||
// Remove HTML tags for plain-text display
|
||||
QString plain = bioHtml;
|
||||
plain.remove(QRegularExpression(QStringLiteral("<[^>]*>")));
|
||||
plain = plain.trimmed();
|
||||
m_bioLabel->setText(plain);
|
||||
m_bioLabel->setVisible(true);
|
||||
} else {
|
||||
m_bioLabel->setVisible(false);
|
||||
}
|
||||
|
||||
const QJsonArray allAlbums = artist["albums"].toObject()["items"].toArray();
|
||||
|
||||
QJsonArray albums, eps, other;
|
||||
for (const QJsonValue &v : allAlbums) {
|
||||
const QJsonObject a = v.toObject();
|
||||
const QString rt = a["release_type"].toString();
|
||||
if (rt == QStringLiteral("album"))
|
||||
albums.append(a);
|
||||
else if (rt == QStringLiteral("epSingle"))
|
||||
eps.append(a);
|
||||
else
|
||||
other.append(a);
|
||||
// releases is an array of {type, has_more, items[]}
|
||||
// types we care about: "album", "epSingle", "live"
|
||||
const QJsonArray releases = artist["releases"].toArray();
|
||||
QJsonArray albums, eps, live, compilations;
|
||||
for (const QJsonValue &rv : releases) {
|
||||
const QJsonObject rg = rv.toObject();
|
||||
const QString type = rg["type"].toString();
|
||||
const QJsonArray items = rg["items"].toArray();
|
||||
if (type == QStringLiteral("album"))
|
||||
albums = items;
|
||||
else if (type == QStringLiteral("epSingle"))
|
||||
eps = items;
|
||||
else if (type == QStringLiteral("live"))
|
||||
live = items;
|
||||
else if (type == QStringLiteral("compilation"))
|
||||
compilations = items;
|
||||
}
|
||||
|
||||
m_secAlbums->setAlbums(albums);
|
||||
m_secEps->setAlbums(eps);
|
||||
m_secOther->setAlbums(other);
|
||||
m_secLive->setAlbums(live);
|
||||
m_secCompilations->setAlbums(compilations);
|
||||
m_secOther->setAlbums({});
|
||||
|
||||
m_secAlbums->setVisible(!m_secAlbums->isEmpty());
|
||||
m_secEps->setVisible(!m_secEps->isEmpty());
|
||||
m_secOther->setVisible(!m_secOther->isEmpty());
|
||||
m_secLive->setVisible(!m_secLive->isEmpty());
|
||||
m_secCompilations->setVisible(!m_secCompilations->isEmpty());
|
||||
m_secOther->setVisible(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user