Skip to content

Commit

Permalink
Initial support for ratings - stored in MPD's sticker DB. This is NOT…
Browse files Browse the repository at this point in the history
… complete - support in dynamic playlists is still TODO...
  • Loading branch information
craig.p.drummond committed Jul 21, 2014
1 parent e045d12 commit ec6f631
Show file tree
Hide file tree
Showing 23 changed files with 587 additions and 39 deletions.
17 changes: 9 additions & 8 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,14 @@ if (ENABLE_QT5)
find_package(Qt5Xml REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5Concurrent REQUIRED)
find_package(Qt5Svg REQUIRED) # Only actually required for SVG icons...
find_package(Qt5Svg REQUIRED)
set(QTCORELIBS ${Qt5Core_LIBRARIES})
set(QTNETWORKLIBS ${Qt5Network_LIBRARIES})
set(QTGUILIBS ${Qt5Gui_LIBRARIES})
set(QTLIBS ${QTCORELIBS} ${Qt5Widgets_LIBRARIES} ${QTNETWORKLIBS} ${QTGUILIBS} ${Qt5Xml_LIBRARIES} ${Qt5Concurrent_LIBRARIES})
set(QTINCLUDES ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS} ${Qt5Xml_INCLUDE_DIRS} ${Qt5Core_INCLUDE_DIRS} ${Qt5Concurrent_INCLUDE_DIRS})
add_definitions(${Qt5Widgets_DEFINITIONS} ${Qt5Network_DEFINITIONS} ${Qt5Xml_DEFINITIONS} ${Qt5Concurrent_DEFINITIONS})
set(QTLIBS ${QTCORELIBS} ${Qt5Widgets_LIBRARIES} ${QTNETWORKLIBS} ${QTGUILIBS} ${Qt5Xml_LIBRARIES} ${Qt5Concurrent_LIBRARIES} ${Qt5Svg_LIBRARIES})
set(QTINCLUDES ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS} ${Qt5Xml_INCLUDE_DIRS} ${Qt5Core_INCLUDE_DIRS} ${Qt5Concurrent_INCLUDE_DIRS}
${Qt5Svg_INCLUDE_DIRS})
add_definitions(${Qt5Widgets_DEFINITIONS} ${Qt5Network_DEFINITIONS} ${Qt5Xml_DEFINITIONS} ${Qt5Concurrent_DEFINITIONS} ${Qt5Svg_DEFINITIONS})
set(CMAKE_CXX_FLAGS "${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
if (NOT ENABLE_UBUNTU)
# Does Ubuntu/Touch version need DBus?
Expand All @@ -242,11 +243,11 @@ if (ENABLE_QT5)
find_package(Qt5Qml REQUIRED)
endif (ENABLE_UBUNTU)
else (ENABLE_QT5)
find_package(Qt4 REQUIRED QtCore QtGui QtXml QtNetwork QtSvg) # SVG only actually required for SVG icons...
find_package(Qt4 REQUIRED QtCore QtGui QtXml QtNetwork QtSvg)
set(QTCORELIBS ${QT_QTCORE_LIBRARY})
set(QTNETWORKLIBS ${QT_QTNETWORK_LIBRARY})
set(QTGUILIBS ${QT_QTGUI_LIBRARY})
set(QTLIBS ${QT_QTXML_LIBRARY} ${QTCORELIBS} ${QTGUILIBS} ${QTNETWORKLIBS})
set(QTLIBS ${QT_QTXML_LIBRARY} ${QTCORELIBS} ${QTGUILIBS} ${QTNETWORKLIBS} ${QT_QTSVG_LIBRARY})
if (QT_QTDBUS_FOUND)
set(QTLIBS ${QTLIBS} ${QT_QTDBUS_LIBRARY})
endif (QT_QTDBUS_FOUND)
Expand Down Expand Up @@ -347,7 +348,7 @@ set(CANTATA_SRCS ${CANTATA_CORE_SRCS} ${CANTATA_SRCS}
widgets/volumeslider.cpp widgets/genrecombo.cpp widgets/menubutton.cpp widgets/icons.cpp widgets/toolbutton.cpp widgets/wizardpage.cpp
widgets/statuslabel.cpp widgets/searchwidget.cpp widgets/messageoverlay.cpp widgets/basicitemdelegate.cpp widgets/sizegrip.cpp
widgets/sizewidget.cpp widgets/servicestatuslabel.cpp widgets/spacerwidget.cpp widgets/songdialog.cpp widgets/stretchheaderview.cpp
widgets/tableview.cpp widgets/thinsplitterhandle.cpp widgets/coverwidget.cpp
widgets/tableview.cpp widgets/thinsplitterhandle.cpp widgets/coverwidget.cpp widgets/ratingwidget.cpp
context/lyricsettings.cpp context/ultimatelyricsprovider.cpp context/ultimatelyrics.cpp context/lyricsdialog.cpp
context/contextwidget.cpp context/view.cpp context/artistview.cpp context/albumview.cpp context/songview.cpp context/contextengine.cpp
context/wikipediaengine.cpp context/wikipediasettings.cpp context/othersettings.cpp context/contextsettings.cpp context/togglelist.cpp
Expand All @@ -361,7 +362,7 @@ set(CANTATA_MOC_HDRS ${CANTATA_CORE_MOC_HDRS} ${CANTATA_MOC_HDRS}
widgets/treeview.h widgets/listview.h widgets/itemview.h widgets/autohidingsplitter.h widgets/nowplayingwidget.h widgets/actionlabel.h
widgets/playqueueview.h widgets/groupedview.h widgets/actionitemdelegate.h widgets/volumeslider.h widgets/genrecombo.h
widgets/searchwidget.h widgets/messageoverlay.h widgets/servicestatuslabel.h widgets/stretchheaderview.h widgets/tableview.h
widgets/coverwidget.h
widgets/coverwidget.h widgets/ratingwidget.h
context/togglelist.h context/ultimatelyrics.h context/ultimatelyricsprovider.h context/lyricsdialog.h
context/contextwidget.h context/artistview.h context/albumview.h context/songview.h context/view.h context/contextengine.h
context/wikipediaengine.h context/wikipediasettings.h context/othersettings.h context/lastfmengine.h context/metaengine.h
Expand Down
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
6. Simplify view config pages.
7. Use QUrl with server details to build HTTP address used to compare MPD http
file paths against.
8. Store song ratings in MPD's sticker DB.

1.4.0
-----
Expand Down
5 changes: 0 additions & 5 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,6 @@
- Move code out of MainWindow class.

- General
- Ratings (use KRatingWidget?)
- Not sure, would need support in cantata-dynamic
- MPD's 'stickers' could be used, but there is no way to query tag+value
you can only query for tracks that have tag. So, could query for all
tracks with tag=rating, but not tag=rating and value=4
- mixramp tag calculation
- mixramp MPD settings
- Does MPD need to be restarted to change settings?
Expand Down
1 change: 1 addition & 0 deletions cantata.qrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="lastfm.svg">scrobbling/lastfm.svg</file>
<file alias="stars.svg">icons/stars.svg</file>

<file alias="repeat16.png">icons/view-media-repeat16.png</file>
<file alias="repeat22.png">icons/view-media-repeat22.png</file>
Expand Down
21 changes: 21 additions & 0 deletions gui/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ static int nextKey(int &key)
return k;
}

static const char *constRatingKey="rating";

MainWindow::MainWindow(QWidget *parent)
: MAIN_WINDOW_BASE_CLASS(parent)
, prevPage(-1)
Expand Down Expand Up @@ -725,6 +727,15 @@ MainWindow::MainWindow(QWidget *parent)
Action *sep=new Action(this);
sep->setSeparator(true);
playQueue->addAction(sep);
ratingAction=new Action(i18n("Set Rating"), this);
ratingAction->setMenu(new QMenu(0));
for (int i=0; i<6; ++i) {
Action *action=new Action(0==i ? i18n("No Rating") : QString::number(i), ratingAction);
action->setProperty(constRatingKey, i);
ratingAction->menu()->addAction(action);
connect(action, SIGNAL(triggered(bool)), SLOT(setRating()));
}
playQueue->addAction(ratingAction);
playQueue->addAction(stopAfterTrackAction);
playQueue->addAction(setPriorityAction);
playQueue->addAction(locateTrackAction);
Expand Down Expand Up @@ -797,6 +808,7 @@ MainWindow::MainWindow(QWidget *parent)
connect(stopAfterTrackAction, SIGNAL(triggered(bool)), this, SLOT(stopAfterTrack()));
connect(this, SIGNAL(setVolume(int)), MPDConnection::self(), SLOT(setVolume(int)));
connect(nowPlaying, SIGNAL(sliderReleased()), this, SLOT(setPosition()));
connect(&playQueueModel, SIGNAL(currentSongRating(QString,quint8)), nowPlaying, SLOT(rating(QString,quint8)));
connect(randomPlayQueueAction, SIGNAL(triggered(bool)), MPDConnection::self(), SLOT(setRandom(bool)));
connect(repeatPlayQueueAction, SIGNAL(triggered(bool)), MPDConnection::self(), SLOT(setRepeat(bool)));
connect(singlePlayQueueAction, SIGNAL(triggered(bool)), MPDConnection::self(), SLOT(setSingle(bool)));
Expand Down Expand Up @@ -1065,6 +1077,7 @@ void MainWindow::playQueueItemsSelected(bool s)
#endif
addPlayQueueToStoredPlaylistAction->setEnabled(haveItems);
stopAfterTrackAction->setEnabled(singleSelection);
ratingAction->setEnabled(s && haveItems);
}

void MainWindow::connectToMpd(const MPDConnectionDetails &details)
Expand Down Expand Up @@ -1337,6 +1350,14 @@ void MainWindow::controlDynamicButton()
#endif
}

void MainWindow::setRating()
{
Action *act=qobject_cast<Action *>(sender());
if (act) {
playQueueModel.setRating(playQueueProxyModel.mapToSourceRows(playQueue->selectedIndexes()), act->property(constRatingKey).toUInt());
}
}

void MainWindow::readSettings()
{
#ifndef QT_QTDBUS_FOUND
Expand Down
2 changes: 2 additions & 0 deletions gui/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ public Q_SLOTS:
void updateConnectionsMenu();
void controlConnectionsMenu(bool enable=true);
void controlDynamicButton();
void setRating();

private:
void expand();
Expand Down Expand Up @@ -345,6 +346,7 @@ private Q_SLOTS:
Action *clearPlayQueueAction;
Action *cancelAction;
Action *clearNewStateAction;
Action *ratingAction;
TrayItem *trayItem;
QPoint lastPos;
QSize expandedSize;
Expand Down
4 changes: 4 additions & 0 deletions icons/stars.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
81 changes: 79 additions & 2 deletions models/playqueuemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ QString PlayQueueModel::headerText(int col)
case COL_PRIO: return i18n("Priority");
case COL_COMPOSER: return i18n("Composer");
case COL_PERFORMER: return i18n("Performer");
case COL_RATING: return i18n("Rating");
default: return QString();
}
}
Expand Down Expand Up @@ -217,6 +218,10 @@ PlayQueueModel::PlayQueueModel(QObject *parent)
connect(this, SIGNAL(clearEntries()), MPDConnection::self(), SLOT(clear()));
connect(this, SIGNAL(addAndPlay(QString)), MPDConnection::self(), SLOT(addAndPlay(QString)));
connect(this, SIGNAL(startPlayingSongId(qint32)), MPDConnection::self(), SLOT(startPlayingSongId(qint32)));
connect(this, SIGNAL(getRating(QString)), MPDConnection::self(), SLOT(getRating(QString)));
connect(this, SIGNAL(setRating(QStringList,quint8)), MPDConnection::self(), SLOT(setRating(QStringList,quint8)));
connect(MPDConnection::self(), SIGNAL(rating(QString,quint8)), SLOT(ratingResult(QString,quint8)));
connect(MPDConnection::self(), SIGNAL(stickerDbChanged()), SLOT(stickerDbChanged()));
#ifdef ENABLE_DEVICES_SUPPORT //TODO: Problems here with devices support!!!
connect(DevicesModel::self(), SIGNAL(invalid(QList<Song>)), SLOT(remove(QList<Song>)));
connect(DevicesModel::self(), SIGNAL(updatedDetails(QList<Song>)), SLOT(updateDetails(QList<Song>)));
Expand Down Expand Up @@ -306,9 +311,11 @@ QVariant PlayQueueModel::headerData(int section, Qt::Orientation orientation, in
case COL_YEAR:
case COL_PRIO:
return int(Qt::AlignVCenter|Qt::AlignRight);
case COL_RATING:
return int(Qt::AlignCenter);
}
case Cantata::Role_Hideable:
return COL_YEAR==section || COL_DISC==section || COL_GENRE==section || COL_PRIO==section || COL_COMPOSER==section || COL_PERFORMER==section ? true : false;
return COL_YEAR==section || COL_DISC==section || COL_GENRE==section || COL_PRIO==section || COL_COMPOSER==section || COL_PERFORMER==section || COL_RATING==section;
case Cantata::Role_Width:
switch (section) {
case COL_TRACK: return 0.075;
Expand All @@ -322,6 +329,7 @@ QVariant PlayQueueModel::headerData(int section, Qt::Orientation orientation, in
case COL_PRIO: return 0.015;
case COL_COMPOSER: return 0.2;
case COL_PERFORMER: return 0.2;
case COL_RATING: return 0.08;
}
default:
break;
Expand Down Expand Up @@ -412,7 +420,12 @@ QVariant PlayQueueModel::data(const QModelIndex &index, int role) const
return songs.at(index.row()).id;
case Cantata::Role_Song: {
QVariant var;
var.setValue<Song>(songs.at(index.row()));
const Song &s=songs.at(index.row());
if (Song::Standard==s.type && Song::constNullRating==s.rating) {
emit getRating(s.file);
s.rating=Song::constRatingRequested;
}
var.setValue<Song>(s);
return var;
}
case Cantata::Role_AlbumDuration: {
Expand Down Expand Up @@ -557,6 +570,16 @@ QVariant PlayQueueModel::data(const QModelIndex &index, int role) const
return song.composer();
case COL_PERFORMER:
return song.performer();
// case COL_RATING:{
// QVariant var;
// const Song &s=songs.at(index.row());
// if (Song::Standard==s.type && Song::constNullRating==s.rating) {
// emit getRating(s.file);
// s.rating=Song::constRatingRequested;
// }
// var.setValue(s.rating);
// return var;
// }
default:
break;
}
Expand Down Expand Up @@ -586,6 +609,8 @@ QVariant PlayQueueModel::data(const QModelIndex &index, int role) const
case COL_YEAR:
case COL_PRIO:
return int(Qt::AlignVCenter|Qt::AlignRight);
case COL_RATING:
return int(Qt::AlignCenter);
}
#ifndef ENABLE_UBUNTU
case Cantata::Role_Decoration: {
Expand Down Expand Up @@ -840,6 +865,15 @@ void PlayQueueModel::updateCurrentSong(quint32 id)
}

currentSongRowNum=getRowById(currentSongId);
if (currentSongRowNum>0 && currentSongRowNum<=songs.count()) {
const Song &song=songs.at(currentSongRowNum);
if (Song::constNullRating==song.rating) {
song.rating=Song::constRatingRequested;
emit getRating(song.file);
} else if (Song::constRatingRequested!=song.rating) {
emit currentSongRating(song.file, song.rating);
}
}
emit dataChanged(index(currentSongRowNum, 0), index(currentSongRowNum, columnCount(QModelIndex())-1));

if (-1!=currentSongId && stopAfterTrackId==currentSongId) {
Expand Down Expand Up @@ -1084,6 +1118,20 @@ void PlayQueueModel::crop(const QList<int> &rowsToKeep)
}
}

void PlayQueueModel::setRating(const QList<int> &rows, quint8 rating) const
{
QSet<QString> files;
foreach (const int &r, rows) {
if (r>-1 && r<songs.count()) {
const Song &s=songs.at(r);
if (Song::Standard==s.type && !files.contains(s.file)) {
files.insert(s.file);
}
}
}
emit setRating(files.toList(), rating);
}

void PlayQueueModel::enableUndo(bool e)
{
if (e==undoEnabled) {
Expand Down Expand Up @@ -1280,6 +1328,35 @@ void PlayQueueModel::removeDuplicates()
}
}

void PlayQueueModel::ratingResult(const QString &file, quint8 r)
{
QList<Song>::iterator it=songs.begin();
QList<Song>::iterator end=songs.end();
int numCols=columnCount(QModelIndex())-1;

for (int row=0; it!=end; ++it, ++row) {
if (Song::Standard==(*it).type && r!=(*it).rating && (*it).file==file) {
(*it).rating=r;
emit dataChanged(index(row, 0), index(row, numCols));
if ((*it).id==currentSongId) {
emit currentSongRating(file, r);
}
}
}
}

void PlayQueueModel::stickerDbChanged()
{
// Sticker DB changed, need to re-request ratings...
QSet<QString> requests;
foreach (const Song &song, songs) {
if (Song::Standard==song.type && song.rating<Song::constRatingRequested && !requests.contains(song.file)) {
emit getRating(song.file);
requests.insert(song.file);
}
}
}

void PlayQueueModel::setCover(const Song &song, const QImage &img, const QString &file)
{
Q_UNUSED(img)
Expand Down
7 changes: 7 additions & 0 deletions models/playqueuemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class PlayQueueModel : public QAbstractItemModel
COL_PRIO,
COL_COMPOSER,
COL_PERFORMER,
COL_RATING,

COL_COUNT
};
Expand Down Expand Up @@ -124,6 +125,7 @@ class PlayQueueModel : public QAbstractItemModel
void removeAll();
void remove(const QList<int> &rowsToRemove);
void crop(const QList<int> &rowsToKeep);
void setRating(const QList<int> &rows, quint8 rating) const;
#ifndef ENABLE_UBUNTU
Action * shuffleAct() { return shuffleAction; }
Action * removeDuplicatesAct() { return removeDuplicatesAction; }
Expand Down Expand Up @@ -159,6 +161,8 @@ private Q_SLOTS:
void undo();
void redo();
void removeDuplicates();
void ratingResult(const QString &file, quint8 r);
void stickerDbChanged();
// Touch version...
void setCover(const Song &song, const QImage &img, const QString &file);

Expand All @@ -168,6 +172,8 @@ private Q_SLOTS:
void filesAdded(const QStringList filenames, const quint32 row, const quint32 size, int action, quint8 priority);
void populate(const QStringList &items, const QList<quint8> &priority);
void move(const QList<quint32> &items, const quint32 row, const quint32 size);
void getRating(const QString &file) const;
void setRating(const QStringList &files, quint8 rating) const;
void statsUpdated(int songs, quint32 time);
void fetchingStreams();
void streamsFetched();
Expand All @@ -177,6 +183,7 @@ private Q_SLOTS:
void clearEntries();
void addAndPlay(const QString &file);
void startPlayingSongId(qint32 id);
void currentSongRating(const QString &file, quint8 r);

private:
QList<Song> songs;
Expand Down
Loading

0 comments on commit ec6f631

Please sign in to comment.