Add smart playlists, ratings and Qobuz

Fixes #259
Fixes #264
This commit is contained in:
Jonas Kvinge
2020-09-17 17:50:17 +02:00
parent fdf96e8342
commit 89d6b7cec0
102 changed files with 10949 additions and 525 deletions

View File

@@ -56,7 +56,6 @@
#include "covermanager/discogscoverprovider.h"
#include "covermanager/musicbrainzcoverprovider.h"
#include "covermanager/deezercoverprovider.h"
#include "covermanager/qobuzcoverprovider.h"
#include "covermanager/musixmatchcoverprovider.h"
#include "covermanager/spotifycoverprovider.h"
@@ -83,6 +82,11 @@
# include "covermanager/tidalcoverprovider.h"
#endif
#ifdef HAVE_QOBUZ
# include "qobuz/qobuzservice.h"
# include "covermanager/qobuzcoverprovider.h"
#endif
#ifdef HAVE_MOODBAR
# include "moodbar/moodbarcontroller.h"
# include "moodbar/moodbarloader.h"
@@ -124,11 +128,13 @@ class ApplicationImpl {
cover_providers->AddProvider(new MusicbrainzCoverProvider(app, app));
cover_providers->AddProvider(new DiscogsCoverProvider(app, app));
cover_providers->AddProvider(new DeezerCoverProvider(app, app));
cover_providers->AddProvider(new QobuzCoverProvider(app, app));
cover_providers->AddProvider(new MusixmatchCoverProvider(app, app));
cover_providers->AddProvider(new SpotifyCoverProvider(app, app));
#ifdef HAVE_TIDAL
cover_providers->AddProvider(new TidalCoverProvider(app, app));
#endif
#ifdef HAVE_QOBUZ
cover_providers->AddProvider(new QobuzCoverProvider(app, app));
#endif
cover_providers->ReloadSettings();
return cover_providers;
@@ -159,6 +165,9 @@ class ApplicationImpl {
#endif
#ifdef HAVE_TIDAL
internet_services->AddService(new TidalService(app, internet_services));
#endif
#ifdef HAVE_QOBUZ
internet_services->AddService(new QobuzService(app, internet_services));
#endif
return internet_services;
}),

View File

@@ -54,7 +54,7 @@
#include "scopedtransaction.h"
const char *Database::kDatabaseFilename = "strawberry.db";
const int Database::kSchemaVersion = 12;
const int Database::kSchemaVersion = 13;
const char *Database::kMagicAllSongsTables = "%allsongstables";
int Database::sNextConnectionId = 1;

View File

@@ -161,6 +161,9 @@
# include "tidal/tidalservice.h"
# include "settings/tidalsettingspage.h"
#endif
#ifdef HAVE_QOBUZ
# include "settings/qobuzsettingspage.h"
#endif
#include "internet/internetservices.h"
#include "internet/internetservice.h"
@@ -185,6 +188,9 @@
# include "moodbar/moodbarproxystyle.h"
#endif
#include "smartplaylists/smartplaylistsviewcontainer.h"
#include "smartplaylists/smartplaylistsview.h"
#ifdef Q_OS_WIN
# include "windows7thumbbar.h"
#endif
@@ -257,11 +263,15 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSDBase *osd
connect(add_stream_dialog, SIGNAL(accepted()), this, SLOT(AddStreamAccepted()));
return add_stream_dialog;
}),
smartplaylists_view_(new SmartPlaylistsViewContainer(app, this)),
#ifdef HAVE_SUBSONIC
subsonic_view_(new InternetSongsView(app_, app->internet_services()->ServiceBySource(Song::Source_Subsonic), SubsonicSettingsPage::kSettingsGroup, SettingsDialog::Page_Subsonic, this)),
#endif
#ifdef HAVE_TIDAL
tidal_view_(new InternetTabsView(app_, app->internet_services()->ServiceBySource(Song::Source_Tidal), TidalSettingsPage::kSettingsGroup, SettingsDialog::Page_Tidal, this)),
#endif
#ifdef HAVE_QOBUZ
qobuz_view_(new InternetTabsView(app_, app->internet_services()->ServiceBySource(Song::Source_Qobuz), QobuzSettingsPage::kSettingsGroup, SettingsDialog::Page_Qobuz, this)),
#endif
lastfm_import_dialog_(new LastFMImportDialog(app_->lastfm_import(), this)),
collection_show_all_(nullptr),
@@ -296,6 +306,7 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSDBase *osd
playing_widget_(true),
doubleclick_addmode_(BehaviourSettingsPage::AddBehaviour_Append),
doubleclick_playmode_(BehaviourSettingsPage::PlayBehaviour_Never),
doubleclick_playlist_addmode_(BehaviourSettingsPage::PlaylistAddBehaviour_Play),
menu_playmode_(BehaviourSettingsPage::PlayBehaviour_Never),
exit_count_(0),
delete_files_(false)
@@ -321,9 +332,10 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSDBase *osd
// Add tabs to the fancy tab widget
ui_->tabs->AddTab(context_view_, "context", IconLoader::Load("strawberry"), tr("Context"));
ui_->tabs->AddTab(collection_view_, "collection", IconLoader::Load("library-music"), tr("Collection"));
ui_->tabs->AddTab(file_view_, "files", IconLoader::Load("document-open"), tr("Files"));
ui_->tabs->AddTab(playlist_list_, "playlists", IconLoader::Load("view-media-playlist"), tr("Playlists"));
ui_->tabs->AddTab(queue_view_, "queue", IconLoader::Load("footsteps"), tr("Queue"));
ui_->tabs->AddTab(playlist_list_, "playlists", IconLoader::Load("view-media-playlist"), tr("Playlists"));
ui_->tabs->AddTab(smartplaylists_view_, "smartplaylists", IconLoader::Load("view-media-playlist"), tr("Smart playlists"));
ui_->tabs->AddTab(file_view_, "files", IconLoader::Load("document-open"), tr("Files"));
#ifndef Q_OS_WIN
ui_->tabs->AddTab(device_view_, "devices", IconLoader::Load("device"), tr("Devices"));
#endif
@@ -333,6 +345,9 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSDBase *osd
#ifdef HAVE_TIDAL
ui_->tabs->AddTab(tidal_view_, "tidal", IconLoader::Load("tidal"), tr("Tidal"));
#endif
#ifdef HAVE_QOBUZ
ui_->tabs->AddTab(qobuz_view_, "qobuz", IconLoader::Load("qobuz"), tr("Qobuz"));
#endif
// Add the playing widget to the fancy tab widget
ui_->tabs->addBottomWidget(ui_->widget_playing);
@@ -644,6 +659,13 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSDBase *osd
connect(this, SIGNAL(AuthorizationUrlReceived(QUrl)), tidalservice, SLOT(AuthorizationUrlReceived(QUrl)));
#endif
#ifdef HAVE_QOBUZ
connect(qobuz_view_->artists_collection_view(), SIGNAL(AddToPlaylistSignal(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
connect(qobuz_view_->albums_collection_view(), SIGNAL(AddToPlaylistSignal(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
connect(qobuz_view_->songs_collection_view(), SIGNAL(AddToPlaylistSignal(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
connect(qobuz_view_->search_view(), SIGNAL(AddToPlaylist(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
#endif
// Playlist menu
connect(playlist_menu_, SIGNAL(aboutToHide()), SLOT(PlaylistMenuHidden()));
playlist_play_pause_ = playlist_menu_->addAction(tr("Play"), this, SLOT(PlaylistPlay()));
@@ -829,6 +851,9 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSDBase *osd
connect(app_->playlist_manager()->sequence(), SIGNAL(RepeatModeChanged(PlaylistSequence::RepeatMode)), osd_, SLOT(RepeatModeChanged(PlaylistSequence::RepeatMode)));
connect(app_->playlist_manager()->sequence(), SIGNAL(ShuffleModeChanged(PlaylistSequence::ShuffleMode)), osd_, SLOT(ShuffleModeChanged(PlaylistSequence::ShuffleMode)));
// Smart playlists
connect(smartplaylists_view_, SIGNAL(AddToPlaylist(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
ScrobbleButtonVisibilityChanged(app_->scrobbler()->ScrobbleButton());
LoveButtonVisibilityChanged(app_->scrobbler()->LoveButton());
ScrobblingEnabledChanged(app_->scrobbler()->IsEnabled());
@@ -849,8 +874,8 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSDBase *osd
restoreGeometry(settings_.value("geometry").toByteArray());
}
if (!ui_->splitter->restoreState(settings_.value("splitter_state").toByteArray())) {
ui_->splitter->setSizes(QList<int>() << 250 << width() - 250);
if (!settings_.contains("splitter_state") || !ui_->splitter->restoreState(settings_.value("splitter_state").toByteArray())) {
ui_->splitter->setSizes(QList<int>() << 20 << (width() - 20));
}
ui_->tabs->setCurrentIndex(settings_.value("current_tab", 1).toInt());
@@ -981,9 +1006,9 @@ void MainWindow::ReloadSettings() {
playing_widget_ = s.value("playing_widget", true).toBool();
if (playing_widget_ != ui_->widget_playing->IsEnabled()) TabSwitched();
doubleclick_addmode_ = BehaviourSettingsPage::AddBehaviour(s.value("doubleclick_addmode", BehaviourSettingsPage::AddBehaviour_Append).toInt());
doubleclick_playmode_ = BehaviourSettingsPage::PlayBehaviour(s.value("doubleclick_playmode", BehaviourSettingsPage::PlayBehaviour_IfStopped).toInt());
doubleclick_playlist_addmode_ = BehaviourSettingsPage::PlaylistAddBehaviour(s.value("doubleclick_playlist_addmode", BehaviourSettingsPage::PlaylistAddBehaviour_Play).toInt());
menu_playmode_ = BehaviourSettingsPage::PlayBehaviour(s.value("menu_playmode", BehaviourSettingsPage::PlayBehaviour_IfStopped).toInt());
doubleclick_playmode_ = BehaviourSettingsPage::PlayBehaviour(s.value("doubleclick_playmode", BehaviourSettingsPage::PlayBehaviour_Never).toInt());
doubleclick_playlist_addmode_ = BehaviourSettingsPage::PlaylistAddBehaviour(s.value("doubleclick_playlist_addmode", BehaviourSettingsPage::PlayBehaviour_Never).toInt());
menu_playmode_ = BehaviourSettingsPage::PlayBehaviour(s.value("menu_playmode", BehaviourSettingsPage::PlayBehaviour_Never).toInt());
s.endGroup();
s.beginGroup(AppearanceSettingsPage::kSettingsGroup);
@@ -1039,6 +1064,16 @@ void MainWindow::ReloadSettings() {
ui_->tabs->DisableTab(tidal_view_);
#endif
#ifdef HAVE_QOBUZ
s.beginGroup(QobuzSettingsPage::kSettingsGroup);
bool enable_qobuz = s.value("enabled", false).toBool();
s.endGroup();
if (enable_qobuz)
ui_->tabs->EnableTab(qobuz_view_);
else
ui_->tabs->DisableTab(qobuz_view_);
#endif
ui_->tabs->ReloadSettings();
}
@@ -1061,6 +1096,7 @@ void MainWindow::ReloadAllSettings() {
file_view_->ReloadSettings();
queue_view_->ReloadSettings();
playlist_list_->ReloadSettings();
smartplaylists_view_->ReloadSettings();
app_->cover_providers()->ReloadSettings();
app_->lyrics_providers()->ReloadSettings();
#ifdef HAVE_SUBSONIC
@@ -1069,6 +1105,9 @@ void MainWindow::ReloadAllSettings() {
#ifdef HAVE_TIDAL
tidal_view_->ReloadSettings();
#endif
#ifdef HAVE_QOBUZ
qobuz_view_->ReloadSettings();
#endif
}

View File

@@ -93,6 +93,7 @@ class TranscodeDialog;
class Ui_MainWindow;
class InternetSongsView;
class InternetTabsView;
class SmartPlaylistsViewContainer;
#ifdef Q_OS_WIN
class Windows7ThumbBar;
#endif
@@ -325,8 +326,11 @@ class MainWindow : public QMainWindow, public PlatformInterface {
std::unique_ptr<TrackSelectionDialog> track_selection_dialog_;
PlaylistItemList autocomplete_tag_items_;
SmartPlaylistsViewContainer *smartplaylists_view_;
InternetSongsView *subsonic_view_;
InternetTabsView *tidal_view_;
InternetTabsView *qobuz_view_;
LastFMImportDialog *lastfm_import_dialog_;

View File

@@ -69,6 +69,8 @@
#include "internet/internetsearchview.h"
#include "smartplaylists/playlistgenerator_fwd.h"
void RegisterMetaTypes() {
qRegisterMetaType<const char*>("const char*");
@@ -136,4 +138,6 @@ void RegisterMetaTypes() {
qRegisterMetaType<InternetSearchView::ResultList>("InternetSearchView::ResultList");
qRegisterMetaType<InternetSearchView::Result>("InternetSearchView::Result");
qRegisterMetaType<PlaylistGeneratorPtr>("PlaylistGeneratorPtr");
}

View File

@@ -125,6 +125,8 @@ const QStringList Song::kColumns = QStringList() << "title"
<< "cue_path"
<< "rating"
;
const QString Song::kColumnSpec = Song::kColumns.join(", ");
@@ -217,6 +219,8 @@ struct Song::Private : public QSharedData {
QString cue_path_; // If the song has a CUE, this contains it's path.
float rating_; // Database rating, not read from tags.
QUrl stream_url_; // Temporary stream url set by url handler.
QImage image_; // Album Cover image set by album cover loader.
bool init_from_file_; // Whether this song was loaded from a file using taglib.
@@ -259,6 +263,8 @@ Song::Private::Private(Song::Source source)
compilation_on_(false),
compilation_off_(false),
rating_(-1),
init_from_file_(false),
suspicious_tags_(false)
@@ -347,6 +353,8 @@ const QImage &Song::image() const { return d->image_; }
const QString &Song::cue_path() const { return d->cue_path_; }
bool Song::has_cue() const { return !d->cue_path_.isEmpty(); }
float Song::rating() const { return d->rating_; }
bool Song::is_collection_song() const { return d->source_ == Source_Collection; }
bool Song::is_metadata_good() const { return !d->url_.isEmpty() && !d->artist_.isEmpty() && !d->title_.isEmpty(); }
bool Song::is_stream() const { return d->source_ == Source_Stream || d->source_ == Source_Tidal || d->source_ == Source_Subsonic || d->source_ == Source_Qobuz; }
@@ -444,6 +452,8 @@ void Song::set_art_automatic(const QUrl &v) { d->art_automatic_ = v; }
void Song::set_art_manual(const QUrl &v) { d->art_manual_ = v; }
void Song::set_cue_path(const QString &v) { d->cue_path_ = v; }
void Song::set_rating(float v) { d->rating_ = v; }
void Song::set_stream_url(const QUrl &v) { d->stream_url_ = v; }
void Song::set_image(const QImage &i) { d->image_ = i; }
@@ -1000,6 +1010,10 @@ void Song::InitFromQuery(const SqlRow &q, bool reliable_metadata, int col) {
d->cue_path_ = tostr(x);
}
else if (Song::kColumns.value(i) == "rating") {
d->rating_ = tofloat(x);
}
else {
qLog(Error) << "Forgot to handle" << Song::kColumns.value(i);
}
@@ -1363,6 +1377,8 @@ void Song::BindToQuery(QSqlQuery *query) const {
query->bindValue(":cue_path", d->cue_path_);
query->bindValue(":rating", intval(d->rating_));
#undef intval
#undef notnullintval
#undef strval
@@ -1441,6 +1457,16 @@ QString Song::SampleRateBitDepthToText() const {
}
QString Song::PrettyRating() const {
float rating = d->rating_;
if (rating == -1.0f) return "0";
return QString::number(static_cast<int>(rating * 100));
}
bool Song::IsMetadataEqual(const Song &other) const {
return d->title_ == other.d->title_ &&
@@ -1547,5 +1573,6 @@ void Song::MergeUserSetData(const Song &other) {
set_art_manual(other.art_manual());
set_compilation_on(other.compilation_on());
set_compilation_off(other.compilation_off());
set_rating(other.rating());
}

View File

@@ -245,6 +245,8 @@ class Song {
const QString &cue_path() const;
bool has_cue() const;
float rating() const;
const QString &effective_album() const;
int effective_originalyear() const;
const QString &effective_albumartist() const;
@@ -288,6 +290,8 @@ class Song {
QString SampleRateBitDepthToText() const;
QString PrettyRating() const;
// Setters
bool IsEditable() const;
@@ -346,6 +350,8 @@ class Song {
void set_cue_path(const QString &v);
void set_rating(const float v);
void set_stream_url(const QUrl &v);
void set_image(const QImage &i);