Initial implementation of qobuz-qt
- Rust backend (qobuz-backend static lib): Qobuz API client (reqwest/tokio), Symphonia audio decoder, CPAL audio output, extern "C" FFI bridge - Qt 6 frontend mirroring spotify-qt layout: toolbar with playback controls, left library dock, central track list, right search panel - Auth: email/password login with MD5-signed requests; session token persisted via QSettings - Playback: double-click a track → Rust fetches stream URL → Symphonia decodes → CPAL outputs to default audio device - Dark Fusion palette matching spotify-qt feel Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
113
src/model/tracklistmodel.cpp
Normal file
113
src/model/tracklistmodel.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
#include "tracklistmodel.hpp"
|
||||
|
||||
#include <QJsonValue>
|
||||
#include <QColor>
|
||||
|
||||
TrackListModel::TrackListModel(QObject *parent)
|
||||
: QAbstractTableModel(parent)
|
||||
{}
|
||||
|
||||
void TrackListModel::setTracks(const QJsonArray &tracks)
|
||||
{
|
||||
beginResetModel();
|
||||
m_tracks.clear();
|
||||
m_tracks.reserve(tracks.size());
|
||||
|
||||
for (const QJsonValue &v : tracks) {
|
||||
const QJsonObject t = v.toObject();
|
||||
TrackItem item;
|
||||
item.id = static_cast<qint64>(t["id"].toDouble());
|
||||
item.number = t["track_number"].toInt();
|
||||
item.title = t["title"].toString();
|
||||
item.duration = static_cast<qint64>(t["duration"].toDouble());
|
||||
item.hiRes = t["hires_streamable"].toBool();
|
||||
item.streamable = t["streamable"].toBool();
|
||||
item.raw = t;
|
||||
|
||||
// Performer / artist
|
||||
const QJsonObject performer = t["performer"].toObject();
|
||||
item.artist = performer["name"].toString();
|
||||
if (item.artist.isEmpty()) {
|
||||
const QJsonObject album = t["album"].toObject();
|
||||
const QJsonObject artist = album["artist"].toObject();
|
||||
item.artist = artist["name"].toString();
|
||||
}
|
||||
|
||||
// Album
|
||||
const QJsonObject album = t["album"].toObject();
|
||||
item.album = album["title"].toString();
|
||||
item.albumId = album["id"].toString();
|
||||
|
||||
m_tracks.append(item);
|
||||
}
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void TrackListModel::clear()
|
||||
{
|
||||
beginResetModel();
|
||||
m_tracks.clear();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
int TrackListModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid()) return 0;
|
||||
return m_tracks.size();
|
||||
}
|
||||
|
||||
int TrackListModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid()) return 0;
|
||||
return ColCount;
|
||||
}
|
||||
|
||||
QVariant TrackListModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid() || index.row() >= m_tracks.size())
|
||||
return {};
|
||||
|
||||
const TrackItem &t = m_tracks.at(index.row());
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
switch (index.column()) {
|
||||
case ColNumber: return t.number > 0 ? QString::number(t.number) : QString();
|
||||
case ColTitle: return t.title;
|
||||
case ColArtist: return t.artist;
|
||||
case ColAlbum: return t.album;
|
||||
case ColDuration: return formatDuration(t.duration);
|
||||
}
|
||||
}
|
||||
|
||||
if (role == Qt::ForegroundRole && !t.streamable) {
|
||||
return QColor(Qt::gray);
|
||||
}
|
||||
|
||||
if (role == TrackIdRole) return t.id;
|
||||
if (role == TrackJsonRole) return t.raw;
|
||||
if (role == HiResRole) return t.hiRes;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QVariant TrackListModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
|
||||
return {};
|
||||
|
||||
switch (section) {
|
||||
case ColNumber: return tr("#");
|
||||
case ColTitle: return tr("Title");
|
||||
case ColArtist: return tr("Artist");
|
||||
case ColAlbum: return tr("Album");
|
||||
case ColDuration: return tr("Duration");
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QString TrackListModel::formatDuration(qint64 secs)
|
||||
{
|
||||
const int m = static_cast<int>(secs / 60);
|
||||
const int s = static_cast<int>(secs % 60);
|
||||
return QStringLiteral("%1:%2").arg(m).arg(s, 2, 10, QLatin1Char('0'));
|
||||
}
|
||||
Reference in New Issue
Block a user