refactor: reduce duplication and replace magic stack indices

- Extract parseTrackItem() static helper in TrackListModel to eliminate
  ~45 lines of duplicated JSON parsing between setTracks() and appendTracks()
- Extract notifyFavChanged() helper to deduplicate addFavId/removeFavId loops
- Add StackPage enum to MainContent replacing magic integers 0-5 with
  named constants (PageWelcome, PageTracks, PageAlbumList, etc.)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
joren
2026-03-31 10:48:21 +02:00
parent e453f8acf3
commit 86b5673e8a
4 changed files with 98 additions and 118 deletions

View File

@@ -9,6 +9,56 @@ TrackListModel::TrackListModel(QObject *parent)
: QAbstractTableModel(parent)
{}
TrackItem TrackListModel::parseTrackItem(const QJsonObject &t,
bool usePosition,
bool useSequential,
int &seq)
{
TrackItem item;
item.id = static_cast<qint64>(t["id"].toDouble());
item.playlistTrackId = static_cast<qint64>(t["playlist_track_id"].toDouble());
item.discNumber = t["media_number"].toInt(1);
item.duration = static_cast<qint64>(t["duration"].toDouble());
item.streamable = t["streamable"].toBool(true);
item.hiRes = t["hires_streamable"].toBool();
item.raw = t;
// Combine title + version ("Melody" + "Vocal Remix" → "Melody (Vocal Remix)")
const QString base = t["title"].toString();
const QString version = t["version"].toString().trimmed();
item.title = version.isEmpty() ? base
: base + QStringLiteral(" (") + version + QLatin1Char(')');
if (useSequential) {
item.number = seq++;
} else if (usePosition) {
const int pos = t["position"].toInt();
item.number = pos > 0 ? pos : seq;
++seq;
} else {
item.number = t["track_number"].toInt();
}
const QJsonObject performer = t["performer"].toObject();
item.artist = performer["name"].toString();
if (item.artist.isEmpty()) {
// album.artist.name may be a plain string or {display:"..."} object
const QJsonValue n = t["album"].toObject()["artist"].toObject()["name"];
item.artist = n.isObject() ? n.toObject()["display"].toString() : n.toString();
}
if (item.artist.isEmpty()) {
// top_tracks format: artist.name.display
const QJsonValue n = t["artist"].toObject()["name"];
item.artist = n.isObject() ? n.toObject()["display"].toString() : n.toString();
}
const QJsonObject album = t["album"].toObject();
item.album = album["title"].toString();
item.albumId = album["id"].toString();
return item;
}
void TrackListModel::setTracks(const QJsonArray &tracks,
bool usePosition,
bool useSequential)
@@ -22,52 +72,8 @@ void TrackListModel::setTracks(const QJsonArray &tracks,
parsed.reserve(tracks.size());
int seq = 1;
for (const QJsonValue &v : tracks) {
const QJsonObject t = v.toObject();
TrackItem item;
item.id = static_cast<qint64>(t["id"].toDouble());
item.playlistTrackId = static_cast<qint64>(t["playlist_track_id"].toDouble());
item.discNumber = t["media_number"].toInt(1);
item.duration = static_cast<qint64>(t["duration"].toDouble());
item.streamable = t["streamable"].toBool(true);
item.hiRes = t["hires_streamable"].toBool();
item.raw = t;
// Combine title + version ("Melody" + "Vocal Remix" → "Melody (Vocal Remix)")
const QString base = t["title"].toString();
const QString version = t["version"].toString().trimmed();
item.title = version.isEmpty() ? base
: base + QStringLiteral(" (") + version + QLatin1Char(')');
if (useSequential) {
item.number = seq++;
} else if (usePosition) {
const int pos = t["position"].toInt();
item.number = pos > 0 ? pos : seq;
++seq;
} else {
item.number = t["track_number"].toInt();
}
const QJsonObject performer = t["performer"].toObject();
item.artist = performer["name"].toString();
if (item.artist.isEmpty()) {
// album.artist.name may be a plain string or {display:"..."} object
const QJsonValue n = t["album"].toObject()["artist"].toObject()["name"];
item.artist = n.isObject() ? n.toObject()["display"].toString() : n.toString();
}
if (item.artist.isEmpty()) {
// top_tracks format: artist.name.display
const QJsonValue n = t["artist"].toObject()["name"];
item.artist = n.isObject() ? n.toObject()["display"].toString() : n.toString();
}
const QJsonObject album = t["album"].toObject();
item.album = album["title"].toString();
item.albumId = album["id"].toString();
parsed.append(item);
}
for (const QJsonValue &v : tracks)
parsed.append(parseTrackItem(v.toObject(), usePosition, useSequential, seq));
// Multi-disc only makes sense for album context (not playlists / fav / search)
int maxDisc = 1;
@@ -134,49 +140,8 @@ void TrackListModel::appendTracks(const QJsonArray &tracks,
QVector<TrackItem> parsed;
parsed.reserve(tracks.size());
for (const QJsonValue &v : tracks) {
const QJsonObject t = v.toObject();
TrackItem item;
item.id = static_cast<qint64>(t["id"].toDouble());
item.playlistTrackId = static_cast<qint64>(t["playlist_track_id"].toDouble());
item.discNumber = t["media_number"].toInt(1);
item.duration = static_cast<qint64>(t["duration"].toDouble());
item.streamable = t["streamable"].toBool(true);
item.hiRes = t["hires_streamable"].toBool();
item.raw = t;
const QString base = t["title"].toString();
const QString version = t["version"].toString().trimmed();
item.title = version.isEmpty() ? base
: base + QStringLiteral(" (") + version + QLatin1Char(')');
if (useSequential) {
item.number = seq++;
} else if (usePosition) {
const int pos = t["position"].toInt();
item.number = pos > 0 ? pos : seq;
++seq;
} else {
item.number = t["track_number"].toInt();
}
const QJsonObject performer = t["performer"].toObject();
item.artist = performer["name"].toString();
if (item.artist.isEmpty()) {
const QJsonValue n = t["album"].toObject()["artist"].toObject()["name"];
item.artist = n.isObject() ? n.toObject()["display"].toString() : n.toString();
}
if (item.artist.isEmpty()) {
const QJsonValue n = t["artist"].toObject()["name"];
item.artist = n.isObject() ? n.toObject()["display"].toString() : n.toString();
}
const QJsonObject album = t["album"].toObject();
item.album = album["title"].toString();
item.albumId = album["id"].toString();
parsed.append(item);
}
for (const QJsonValue &v : tracks)
parsed.append(parseTrackItem(v.toObject(), usePosition, useSequential, seq));
if (parsed.isEmpty())
return;
@@ -210,6 +175,16 @@ void TrackListModel::removeTrack(int row)
endRemoveRows();
}
void TrackListModel::notifyFavChanged(qint64 id)
{
for (int r = 0; r < m_tracks.size(); ++r) {
if (m_tracks[r].id == id) {
const auto idx = index(r, ColTitle);
emit dataChanged(idx, idx, {Qt::DecorationRole});
}
}
}
void TrackListModel::setFavIds(const QSet<qint64> &ids)
{
m_favIds = ids;
@@ -221,23 +196,13 @@ void TrackListModel::setFavIds(const QSet<qint64> &ids)
void TrackListModel::addFavId(qint64 id)
{
m_favIds.insert(id);
for (int r = 0; r < m_tracks.size(); ++r) {
if (m_tracks[r].id == id) {
const auto idx = index(r, ColTitle);
emit dataChanged(idx, idx, {Qt::DecorationRole});
}
}
notifyFavChanged(id);
}
void TrackListModel::removeFavId(qint64 id)
{
m_favIds.remove(id);
for (int r = 0; r < m_tracks.size(); ++r) {
if (m_tracks[r].id == id) {
const auto idx = index(r, ColTitle);
emit dataChanged(idx, idx, {Qt::DecorationRole});
}
}
notifyFavChanged(id);
}
void TrackListModel::setPlayingId(qint64 id)