699 lines
20 KiB
C++
699 lines
20 KiB
C++
/*
|
|
* Cantata
|
|
*
|
|
* Copyright (c) 2011-2012 Craig Drummond <craig.p.drummond@gmail.com>
|
|
*
|
|
*/
|
|
/*
|
|
* Copyright (c) 2008 Sander Knopper (sander AT knopper DOT tk) and
|
|
* Roeland Douma (roeland AT rullzer DOT com)
|
|
*
|
|
* This file is part of QtMPC.
|
|
*
|
|
* QtMPC is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* QtMPC is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with QtMPC. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <QtGui/QPalette>
|
|
#include <QtGui/QFont>
|
|
#include <QtGui/QIcon>
|
|
#include <QtCore/QModelIndex>
|
|
#include <QtCore/QMimeData>
|
|
#include <QtCore/QTextStream>
|
|
#include <QtCore/QSet>
|
|
#include <QtCore/QUrl>
|
|
#ifdef ENABLE_KDE_SUPPORT
|
|
#include <KDE/KLocale>
|
|
#endif
|
|
#include "playqueuemodel.h"
|
|
#include "groupedview.h"
|
|
#include "mpdconnection.h"
|
|
#include "mpdparseutils.h"
|
|
#include "mpdstats.h"
|
|
#include "mpdstatus.h"
|
|
#include "streamfetcher.h"
|
|
#include "httpserver.h"
|
|
#include "settings.h"
|
|
#include "debugtimer.h"
|
|
|
|
static QStringList reverseList(const QStringList &orig)
|
|
{
|
|
QStringList rev;
|
|
foreach (const QString &s, orig) {
|
|
rev.prepend(s);
|
|
}
|
|
return rev;
|
|
}
|
|
|
|
static QString unencodeUrl(QString u)
|
|
{
|
|
return u.replace("%20", " ").replace("%5C", "\\");
|
|
}
|
|
|
|
const QLatin1String PlayQueueModel::constMoveMimeType("cantata/move");
|
|
const QLatin1String PlayQueueModel::constFileNameMimeType("cantata/filename");
|
|
const QLatin1String PlayQueueModel::constUriMimeType("text/uri-list");
|
|
|
|
void PlayQueueModel::encode(QMimeData &mimeData, const QString &mime, const QStringList &values)
|
|
{
|
|
QByteArray encodedData;
|
|
QTextStream stream(&encodedData, QIODevice::WriteOnly);
|
|
|
|
foreach (const QString &v, values) {
|
|
stream << v << endl;
|
|
}
|
|
|
|
mimeData.setData(mime, encodedData);
|
|
}
|
|
|
|
QStringList PlayQueueModel::decode(const QMimeData &mimeData, const QString &mime)
|
|
{
|
|
QByteArray encodedData=mimeData.data(mime);
|
|
QTextStream stream(&encodedData, QIODevice::ReadOnly);
|
|
QStringList rv;
|
|
|
|
while (!stream.atEnd()) {
|
|
rv.append(stream.readLine().remove('\n'));
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
QString PlayQueueModel::headerText(int col)
|
|
{
|
|
switch (col) {
|
|
case COL_STATUS: return QString();
|
|
#ifdef ENABLE_KDE_SUPPORT
|
|
case COL_TITLE: return i18n("Title");
|
|
case COL_ARTIST: return i18n("Artist");
|
|
case COL_ALBUM: return i18n("Album");
|
|
case COL_TRACK: return i18nc("Track Number (#)", "#");
|
|
case COL_LENGTH: return i18n("Length");
|
|
case COL_DISC: return i18n("Disc");
|
|
case COL_YEAR: return i18n("Year");
|
|
case COL_GENRE: return i18n("Genre");
|
|
#else
|
|
case COL_TITLE: return tr("Title");
|
|
case COL_ARTIST: return tr("Artist");
|
|
case COL_ALBUM: return tr("Album");
|
|
case COL_TRACK: return tr("#");
|
|
case COL_LENGTH: return tr("Length");
|
|
case COL_DISC: return tr("Disc");
|
|
case COL_YEAR: return tr("Year");
|
|
case COL_GENRE: return tr("Genre");
|
|
#endif
|
|
default: return QString();
|
|
}
|
|
}
|
|
|
|
PlayQueueModel::PlayQueueModel(QObject *parent)
|
|
: QAbstractTableModel(parent)
|
|
, currentSongId(-1)
|
|
, mpdState(MPDState_Inactive)
|
|
, grouped(false)
|
|
, dropAdjust(0)
|
|
{
|
|
fetcher=new StreamFetcher(this);
|
|
connect(this, SIGNAL(modelReset()), this, SLOT(playListStats()));
|
|
connect(fetcher, SIGNAL(result(const QStringList &, int)), SLOT(addFiles(const QStringList &, int)));
|
|
connect(this, SIGNAL(filesAddedInPlaylist(const QStringList, quint32, quint32)),
|
|
MPDConnection::self(), SLOT(addid(const QStringList, quint32, quint32)));
|
|
connect(this, SIGNAL(moveInPlaylist(const QList<quint32> &, quint32, quint32)),
|
|
MPDConnection::self(), SLOT(move(const QList<quint32> &, quint32, quint32)));
|
|
}
|
|
|
|
PlayQueueModel::~PlayQueueModel()
|
|
{
|
|
}
|
|
|
|
QVariant PlayQueueModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
if (Qt::Horizontal==orientation) {
|
|
if (Qt::DisplayRole==role) {
|
|
return headerText(section);
|
|
} else if (Qt::TextAlignmentRole==role) {
|
|
switch (section) {
|
|
case COL_TITLE:
|
|
case COL_ARTIST:
|
|
case COL_ALBUM:
|
|
case COL_GENRE:
|
|
default:
|
|
return int(Qt::AlignVCenter|Qt::AlignLeft);
|
|
case COL_STATUS:
|
|
case COL_TRACK:
|
|
case COL_LENGTH:
|
|
case COL_DISC:
|
|
case COL_YEAR:
|
|
return int(Qt::AlignVCenter|Qt::AlignRight);
|
|
}
|
|
}
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
int PlayQueueModel::rowCount(const QModelIndex &) const
|
|
{
|
|
return songs.size();
|
|
}
|
|
|
|
int PlayQueueModel::columnCount(const QModelIndex &) const
|
|
{
|
|
return grouped ? 1 : COL_COUNT;
|
|
}
|
|
|
|
QVariant PlayQueueModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
if (!index.isValid() || index.row() >= songs.size()) {
|
|
return QVariant();
|
|
}
|
|
|
|
// Mark background of song currently being played
|
|
|
|
// if (role == Qt::BackgroundRole && songs.at(index.row()).id == currentSongId) {
|
|
// QPalette palette;
|
|
// QColor col(palette.color(QPalette::Highlight));
|
|
// col.setAlphaF(0.2);
|
|
// return QVariant(col);
|
|
// }
|
|
|
|
switch (role) {
|
|
case GroupedView::Role_IsCollection:
|
|
return false;
|
|
case GroupedView::Role_CollectionId:
|
|
return 0;
|
|
case GroupedView::Role_Key:
|
|
return songs.at(index.row()).key;
|
|
case GroupedView::Role_Id:
|
|
return songs.at(index.row()).id;
|
|
case GroupedView::Role_Song: {
|
|
QVariant var;
|
|
var.setValue<Song>(songs.at(index.row()));
|
|
return var;
|
|
}
|
|
case GroupedView::Role_AlbumDuration: {
|
|
const Song &first = songs.at(index.row());
|
|
quint32 d=first.time;
|
|
for (int i=index.row()+1; i<songs.count(); ++i) {
|
|
const Song &song = songs.at(i);
|
|
if (song.key!=first.key) {
|
|
break;
|
|
}
|
|
d+=song.time;
|
|
}
|
|
if (index.row()>1) {
|
|
for (int i=index.row()-1; i<=0; ++i) {
|
|
const Song &song = songs.at(i);
|
|
if (song.key!=first.key) {
|
|
break;
|
|
}
|
|
d+=song.time;
|
|
}
|
|
}
|
|
return d;
|
|
}
|
|
case GroupedView::Role_SongCount: {
|
|
const Song &first = songs.at(index.row());
|
|
quint32 count=1;
|
|
for (int i=index.row()+1; i<songs.count(); ++i) {
|
|
const Song &song = songs.at(i);
|
|
if (song.key!=first.key) {
|
|
break;
|
|
}
|
|
count++;
|
|
}
|
|
if (index.row()>1) {
|
|
for (int i=index.row()-1; i<=0; ++i) {
|
|
const Song &song = songs.at(i);
|
|
if (song.key!=first.key) {
|
|
break;
|
|
}
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
case GroupedView::Role_CurrentStatus: {
|
|
switch (mpdState) {
|
|
case MPDState_Inactive:
|
|
case MPDState_Stopped: return (int)GroupedView::State_Stopped;
|
|
case MPDState_Playing: return (int)GroupedView::State_Playing;
|
|
case MPDState_Paused: return (int)GroupedView::State_Paused;
|
|
}
|
|
}
|
|
case GroupedView::Role_Status:
|
|
if (songs.at(index.row()).id == currentSongId) {
|
|
switch (mpdState) {
|
|
case MPDState_Inactive:
|
|
case MPDState_Stopped: return (int)GroupedView::State_Stopped;
|
|
case MPDState_Playing: return (int)GroupedView::State_Playing;
|
|
case MPDState_Paused: return (int)GroupedView::State_Paused;
|
|
}
|
|
}
|
|
return (int)GroupedView::State_Default;
|
|
break;
|
|
case Qt::FontRole: {
|
|
Song s=songs.at(index.row());
|
|
|
|
if (s.isStream()) {
|
|
QFont font;
|
|
if (songs.at(index.row()).id == currentSongId) {
|
|
font.setBold(true);
|
|
}
|
|
font.setItalic(true);
|
|
return font;
|
|
}
|
|
else if (songs.at(index.row()).id == currentSongId) {
|
|
QFont font;
|
|
font.setBold(true);
|
|
return font;
|
|
}
|
|
break;
|
|
}
|
|
// case Qt::BackgroundRole:
|
|
// if (songs.at(index.row()).id == currentSongId) {
|
|
// QColor col(QPalette().color(QPalette::Highlight));
|
|
// col.setAlphaF(0.2);
|
|
// return QVariant(col);
|
|
// }
|
|
// break;
|
|
case Qt::DisplayRole: {
|
|
const Song &song = songs.at(index.row());
|
|
switch (index.column()) {
|
|
case COL_TITLE:
|
|
return song.title.isEmpty() ? song.file : song.title;
|
|
case COL_ARTIST:
|
|
return song.artist;
|
|
case COL_ALBUM:
|
|
return song.album.isEmpty() && !song.name.isEmpty() && song.isStream() ? song.name : song.album;
|
|
case COL_TRACK:
|
|
if (song.track <= 0)
|
|
return QVariant();
|
|
return song.track;
|
|
case COL_LENGTH:
|
|
return Song::formattedTime(song.time);
|
|
case COL_DISC:
|
|
if (song.disc <= 0)
|
|
return QVariant();
|
|
return song.disc;
|
|
case COL_YEAR:
|
|
if (song.year <= 0)
|
|
return QVariant();
|
|
return song.year;
|
|
case COL_GENRE:
|
|
return song.genre;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case Qt::TextAlignmentRole:
|
|
switch (index.column()) {
|
|
case COL_TITLE:
|
|
case COL_ARTIST:
|
|
case COL_ALBUM:
|
|
case COL_GENRE:
|
|
default:
|
|
return int(Qt::AlignVCenter|Qt::AlignLeft);
|
|
case COL_STATUS:
|
|
case COL_TRACK:
|
|
case COL_LENGTH:
|
|
case COL_DISC:
|
|
case COL_YEAR:
|
|
return int(Qt::AlignVCenter|Qt::AlignRight);
|
|
}
|
|
case Qt::DecorationRole:
|
|
if (COL_STATUS==index.column() && songs.at(index.row()).id == currentSongId) {
|
|
switch (mpdState) {
|
|
case MPDState_Inactive:
|
|
case MPDState_Stopped: return QIcon::fromTheme("media-playback-stop");
|
|
case MPDState_Playing: return QIcon::fromTheme("media-playback-start");
|
|
case MPDState_Paused: return QIcon::fromTheme("media-playback-pause");
|
|
}
|
|
}
|
|
break;
|
|
case Qt::SizeHintRole:
|
|
return QSize(18, 18);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
bool PlayQueueModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
|
{
|
|
if (GroupedView::Role_DropAdjust==role) {
|
|
dropAdjust=value.toUInt();
|
|
return true;
|
|
} else {
|
|
return QAbstractTableModel::setData(index, value, role);
|
|
}
|
|
}
|
|
|
|
Qt::DropActions PlayQueueModel::supportedDropActions() const
|
|
{
|
|
return Qt::CopyAction | Qt::MoveAction;
|
|
}
|
|
|
|
Qt::ItemFlags PlayQueueModel::flags(const QModelIndex &index) const
|
|
{
|
|
if (index.isValid()) {
|
|
return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled;
|
|
} else {
|
|
return Qt::ItemIsDropEnabled;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return A QStringList with the mimetypes we support
|
|
*/
|
|
QStringList PlayQueueModel::mimeTypes() const
|
|
{
|
|
QStringList types;
|
|
types << constMoveMimeType;
|
|
types << constFileNameMimeType;
|
|
if (MPDConnection::self()->isLocal() || HttpServer::self()->isAlive()) {
|
|
types << constUriMimeType;
|
|
}
|
|
return types;
|
|
}
|
|
|
|
/**
|
|
* Convert the data at indexes into mimedata ready for transport
|
|
*
|
|
* @param indexes The indexes to pack into mimedata
|
|
* @return The mimedata
|
|
*/
|
|
QMimeData *PlayQueueModel::mimeData(const QModelIndexList &indexes) const
|
|
{
|
|
QMimeData *mimeData = new QMimeData();
|
|
QStringList positions;
|
|
QStringList filenames;
|
|
/*
|
|
* Loop over all our indexes. However we have rows*columns indexes
|
|
* We pack per row so ingore the columns
|
|
*/
|
|
QList<int> rows;
|
|
foreach(QModelIndex index, indexes) {
|
|
if (index.isValid()) {
|
|
if (rows.contains(index.row())) {
|
|
continue;
|
|
}
|
|
|
|
positions.append(QString::number(getPosByRow(index.row())));
|
|
rows.append(index.row());
|
|
filenames.append(songs.at(index.row()).file);
|
|
}
|
|
}
|
|
|
|
encode(*mimeData, constMoveMimeType, positions);
|
|
encode(*mimeData, constFileNameMimeType, filenames);
|
|
|
|
return mimeData;
|
|
}
|
|
|
|
/**
|
|
* Act on mime data that is dropped in our model
|
|
*
|
|
* @param data The actual data that is dropped
|
|
* @param action The action. This could mean drop/copy etc
|
|
* @param row The row where it is dropper
|
|
* @param column The column where it is dropper
|
|
* @param parent The parent of where we have dropped it
|
|
*
|
|
* @return bool if we accest the drop
|
|
*/
|
|
bool PlayQueueModel::dropMimeData(const QMimeData *data,
|
|
Qt::DropAction action, int row, int /*column*/, const QModelIndex & /*parent*/)
|
|
{
|
|
if (Qt::IgnoreAction==action) {
|
|
return true;
|
|
}
|
|
|
|
row+=dropAdjust;
|
|
if (data->hasFormat(constMoveMimeType)) {
|
|
//Act on internal moves
|
|
QStringList positions=decode(*data, constMoveMimeType);
|
|
QList<quint32> items;
|
|
|
|
foreach (const QString &s, positions) {
|
|
items.append(s.toUInt());
|
|
}
|
|
|
|
if (row < 0) {
|
|
emit moveInPlaylist(items, songs.size(), songs.size());
|
|
} else {
|
|
emit moveInPlaylist(items, row, songs.size());
|
|
}
|
|
return true;
|
|
} else if (data->hasFormat(constFileNameMimeType)) {
|
|
//Act on moves from the music library and dir view
|
|
addItems(reverseList(decode(*data, constFileNameMimeType)), row);
|
|
return true;
|
|
} else if(data->hasFormat(constUriMimeType)/* && MPDConnection::self()->isLocal()*/) {
|
|
QStringList orig=reverseList(decode(*data, constUriMimeType));
|
|
QStringList useable;
|
|
bool haveHttp=HttpServer::self()->isAlive();
|
|
bool alwaysUseHttp=haveHttp && Settings::self()->alwaysUseHttp();
|
|
bool mpdLocal=MPDConnection::self()->isLocal();
|
|
bool allowLocal=haveHttp || mpdLocal;
|
|
|
|
foreach (QString u, orig) {
|
|
if (u.startsWith("http://")) {
|
|
useable.append(u);
|
|
} else if (allowLocal && (u.startsWith('/') || u.startsWith("file:///"))) {
|
|
if (u.startsWith("file://")) {
|
|
u=u.mid(7);
|
|
}
|
|
if (alwaysUseHttp || !mpdLocal) {
|
|
useable.append(HttpServer::self()->encodeUrl(unencodeUrl(u)));
|
|
} else {
|
|
useable.append(QLatin1String("file://")+unencodeUrl(u));
|
|
}
|
|
}
|
|
}
|
|
if (useable.count()) {
|
|
addItems(useable, row);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void PlayQueueModel::addItems(const QStringList &items, int row)
|
|
{
|
|
bool haveHttp=false;
|
|
|
|
foreach (const QString &f, items) {
|
|
QUrl u(f);
|
|
|
|
if (u.scheme()=="http") {
|
|
haveHttp=true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (haveHttp) {
|
|
fetcher->get(items, row);
|
|
} else {
|
|
addFiles(items, row);
|
|
}
|
|
}
|
|
|
|
void PlayQueueModel::addFiles(const QStringList &filenames, int row)
|
|
{
|
|
//Check for empty playlist
|
|
if (songs.size() == 1 && songs.at(0).artist.isEmpty() && songs.at(0).album.isEmpty() && songs.at(0).title.isEmpty()) {
|
|
emit filesAddedInPlaylist(filenames, 0, 0);
|
|
} else {
|
|
if (row < 0) {
|
|
emit filesAddedInPlaylist(filenames, songs.size(), songs.size());
|
|
} else {
|
|
emit filesAddedInPlaylist(filenames, row, songs.size());
|
|
}
|
|
}
|
|
}
|
|
|
|
qint32 PlayQueueModel::getIdByRow(qint32 row) const
|
|
{
|
|
return row>=songs.size() ? -1 : songs.at(row).id;
|
|
}
|
|
|
|
qint32 PlayQueueModel::getPosByRow(qint32 row) const
|
|
{
|
|
return row>=songs.size() ? -1 : songs.at(row).pos;
|
|
}
|
|
|
|
qint32 PlayQueueModel::getRowById(qint32 id) const
|
|
{
|
|
for (int i = 0; i < songs.size(); i++) {
|
|
if (songs.at(i).id == id) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
Song PlayQueueModel::getSongByRow(const qint32 row) const
|
|
{
|
|
return row>=songs.size() ? Song() : songs.at(row);
|
|
}
|
|
|
|
void PlayQueueModel::updateCurrentSong(quint32 id)
|
|
{
|
|
qint32 oldIndex = -1;
|
|
|
|
oldIndex = currentSongId;
|
|
currentSongId = id;
|
|
|
|
if (-1!=oldIndex) {
|
|
emit dataChanged(index(getRowById(oldIndex), 0), index(getRowById(oldIndex), columnCount(QModelIndex())-1));
|
|
}
|
|
|
|
emit dataChanged(index(getRowById(currentSongId), 0), index(getRowById(currentSongId), columnCount(QModelIndex())-1));
|
|
}
|
|
|
|
void PlayQueueModel::clear()
|
|
{
|
|
beginResetModel();
|
|
songs=QList<Song>();
|
|
endResetModel();
|
|
}
|
|
|
|
void PlayQueueModel::setState(MPDState st)
|
|
{
|
|
if (st!=mpdState) {
|
|
mpdState=st;
|
|
if (-1!=currentSongId) {
|
|
emit dataChanged(index(getRowById(currentSongId), 0), index(getRowById(currentSongId), 2));
|
|
}
|
|
}
|
|
}
|
|
|
|
void PlayQueueModel::setGrouped(bool g)
|
|
{
|
|
grouped=g;
|
|
}
|
|
|
|
// Update playqueue with contents returned from MPD.
|
|
// Also, return set of artist-album keys associated with songs in 'ids'
|
|
QSet<quint16> PlayQueueModel::updatePlaylist(const QList<Song> &songList, QSet<quint16> controlledAlbums)
|
|
{
|
|
TF_DEBUG
|
|
QSet<qint32> newIds;
|
|
QSet<qint32> controlledIds;
|
|
QSet<quint16> keys;
|
|
foreach (const Song &s, songList) {
|
|
newIds.insert(s.id);
|
|
}
|
|
|
|
// Map from album keys, into song ids...
|
|
foreach (const Song &s, songs) {
|
|
if (controlledAlbums.contains(s.key)) {
|
|
controlledIds.insert(s.id);
|
|
}
|
|
}
|
|
|
|
if (songs.isEmpty() || songList.isEmpty()) {
|
|
beginResetModel();
|
|
songs=songList;
|
|
ids=newIds;
|
|
endResetModel();
|
|
|
|
if (grouped && !controlledIds.isEmpty()) {
|
|
foreach (const Song &s, songs) {
|
|
if (controlledIds.contains(s.id)) {
|
|
keys.insert(s.key);
|
|
controlledIds.remove(s.id);
|
|
if (controlledIds.isEmpty()) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
QSet<QString> artists;
|
|
QSet<QString> albums;
|
|
quint32 time = 0;
|
|
|
|
QSet<qint32> removed=ids-newIds;
|
|
foreach (qint32 id, removed) {
|
|
qint32 row=getRowById(id);
|
|
if (row!=-1) {
|
|
beginRemoveRows(QModelIndex(), row, row);
|
|
songs.removeAt(row);
|
|
endRemoveRows();
|
|
}
|
|
}
|
|
for (qint32 i=0; i<songList.count(); ++i) {
|
|
Song s=songList.at(i);
|
|
if (i>=songs.count() || s.id!=songs.at(i).id) {
|
|
qint32 existing=getRowById(s.id);
|
|
if (-1==existing) {
|
|
beginInsertRows(QModelIndex(), i, i);
|
|
songs.insert(i, s);
|
|
endInsertRows();
|
|
} else {
|
|
beginMoveRows(QModelIndex(), existing, existing, QModelIndex(), i>existing ? i+1 : i);
|
|
songs.takeAt(existing);
|
|
songs.insert(i, s);
|
|
endMoveRows();
|
|
}
|
|
} else {
|
|
songs.replace(i, s);
|
|
}
|
|
|
|
artists.insert(s.artist);
|
|
albums.insert(s.album);
|
|
time += s.time;
|
|
|
|
if (controlledIds.contains(s.id)) {
|
|
keys.insert(s.key);
|
|
controlledIds.remove(s.id);
|
|
}
|
|
}
|
|
ids=newIds;
|
|
emit statsUpdated(artists.size(), albums.size(), songs.size(), time);
|
|
}
|
|
|
|
return keys;
|
|
}
|
|
|
|
void PlayQueueModel::playListStats()
|
|
{
|
|
QSet<QString> artists;
|
|
QSet<QString> albums;
|
|
quint32 time = 0;
|
|
|
|
//Loop over all songs
|
|
foreach(const Song &song, songs) {
|
|
artists.insert(song.artist);
|
|
albums.insert(song.album);
|
|
time += song.time;
|
|
}
|
|
|
|
emit statsUpdated(artists.size(), albums.size(), songs.size(), time);
|
|
}
|
|
|
|
QSet<qint32> PlayQueueModel::getSongIdSet()
|
|
{
|
|
QSet<qint32> ids;
|
|
|
|
foreach(const Song &song, songs) {
|
|
ids << song.id;
|
|
}
|
|
|
|
return ids;
|
|
}
|