From 758be9d6dbf394663dc69d17449740fcec819a72 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Sun, 30 Sep 2018 22:19:27 +0200 Subject: [PATCH 1/5] Add missing ftsyear in device schema --- data/schema/device-schema.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/schema/device-schema.sql b/data/schema/device-schema.sql index 9b24f94f6..c41ee3728 100644 --- a/data/schema/device-schema.sql +++ b/data/schema/device-schema.sql @@ -70,7 +70,7 @@ CREATE INDEX idx_device_%deviceid_songs_album ON device_%deviceid_songs (album); CREATE INDEX idx_device_%deviceid_songs_comp_artist ON device_%deviceid_songs (effective_compilation, artist); CREATE VIRTUAL TABLE device_%deviceid_fts USING fts3( - ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment, + ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment, ftsyear tokenize=unicode ); From c264725e78d99c3e3040f0d61ada3787c1891ee3 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Mon, 1 Oct 2018 10:38:29 +0200 Subject: [PATCH 2/5] Save tags for WavPack files (#6152) --- ext/libclementine-tagreader/tagreader.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ext/libclementine-tagreader/tagreader.cpp b/ext/libclementine-tagreader/tagreader.cpp index da93c2303..d3a89099f 100644 --- a/ext/libclementine-tagreader/tagreader.cpp +++ b/ext/libclementine-tagreader/tagreader.cpp @@ -42,6 +42,7 @@ #ifdef TAGLIB_HAS_OPUS #include #endif +#include #include #include #include @@ -722,6 +723,17 @@ bool TagReader::SaveFile(const QString& filename, tag->itemListMap()["aART"] = TagLib::StringList(song.albumartist().c_str()); tag->itemListMap()["cpil"] = TagLib::StringList(song.compilation() ? "1" : "0"); + } else if (TagLib::WavPack::File* file = + dynamic_cast(fileref->file())) { + TagLib::APE::Tag* tag = file->APETag(true); + if (!tag) return false; + tag->setArtist(StdStringToTaglibString(song.artist())); + tag->setAlbum(StdStringToTaglibString(song.album())); + tag->setTitle(StdStringToTaglibString(song.title())); + tag->setGenre(StdStringToTaglibString(song.genre())); + tag->setComment(StdStringToTaglibString(song.comment())); + tag->setYear(song.year()); + tag->setTrack(song.track()); } // Handle all the files which have VorbisComments (Ogg, OPUS, ...) in the same From 6432a9c39927438b11c1dc876c8dcd8eeba14274 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Mon, 1 Oct 2018 10:39:37 +0200 Subject: [PATCH 3/5] Make systray settings available only if there is a systray on the system (#6153) --- src/ui/behavioursettingspage.cpp | 33 ++++++++-- src/ui/behavioursettingspage.ui | 102 +++++++++++++------------------ 2 files changed, 71 insertions(+), 64 deletions(-) diff --git a/src/ui/behavioursettingspage.cpp b/src/ui/behavioursettingspage.cpp index 2cf889daf..740fbe91d 100644 --- a/src/ui/behavioursettingspage.cpp +++ b/src/ui/behavioursettingspage.cpp @@ -23,6 +23,7 @@ #include "playlist/playlisttabbar.h" #include +#include namespace { bool LocaleAwareCompare(const QString& a, const QString& b) { @@ -96,6 +97,14 @@ BehaviourSettingsPage::BehaviourSettingsPage(SettingsDialog* dialog) #ifdef Q_OS_DARWIN ui_->b_show_tray_icon_->setEnabled(false); ui_->startup_group_->setEnabled(false); +#else + if (QSystemTrayIcon::isSystemTrayAvailable()) { + ui_->b_show_tray_icon_->setEnabled(true); + ui_->startup_group_->setEnabled(true); + } else { + ui_->b_show_tray_icon_->setEnabled(false); + ui_->startup_group_->setEnabled(false); + } #endif } @@ -105,11 +114,25 @@ void BehaviourSettingsPage::Load() { QSettings s; s.beginGroup(MainWindow::kSettingsGroup); - ui_->b_show_tray_icon_->setChecked(s.value("showtray", true).toBool()); - ui_->b_scroll_tray_icon_->setChecked( - s.value("scrolltrayicon", ui_->b_show_tray_icon_->isChecked()).toBool()); - ui_->b_keep_running_->setChecked( - s.value("keeprunning", ui_->b_show_tray_icon_->isChecked()).toBool()); +#ifdef Q_OS_DARWIN + ui_->b_show_tray_icon_->setChecked(false); + ui_->b_scroll_tray_icon_->setChecked(false); + ui_->b_keep_running_->setChecked(false); +#else + if (QSystemTrayIcon::isSystemTrayAvailable()) { + ui_->b_show_tray_icon_->setChecked(s.value("showtray", true).toBool()); + ui_->b_scroll_tray_icon_->setChecked( + s.value("scrolltrayicon", ui_->b_show_tray_icon_->isChecked()) + .toBool()); + ui_->b_keep_running_->setChecked( + s.value("keeprunning", ui_->b_show_tray_icon_->isChecked()).toBool()); + } else { + ui_->b_show_tray_icon_->setChecked(false); + ui_->b_scroll_tray_icon_->setChecked(false); + ui_->b_keep_running_->setChecked(false); + } +#endif + ui_->doubleclick_addmode->setCurrentIndex(ui_->doubleclick_addmode->findData( s.value("doubleclick_addmode", MainWindow::AddBehaviour_Append).toInt())); ui_->doubleclick_playmode->setCurrentIndex( diff --git a/src/ui/behavioursettingspage.ui b/src/ui/behavioursettingspage.ui index 080071b39..2307a2d90 100644 --- a/src/ui/behavioursettingspage.ui +++ b/src/ui/behavioursettingspage.ui @@ -7,7 +7,7 @@ 0 0 516 - 851 + 1081 @@ -89,83 +89,67 @@ - Always show the main window + Always show &the main window - Always hide the main window + Alwa&ys hide the main window - Remember from last time + Remember from &last time true - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - - - - - Resume playback on start - - - false - - - - - - - Stop playback if song fails to play - - - false - - - - - - - Grey out non existent songs in my playlists - - - - - - - If activated, clicking a selected song in the playlist view will let you edit the tag value directly - - - Enable song metadata inline edition with click - - - + + + + If activated, clicking a selected song in the playlist view will let you edit the tag value directly + + + Enable song metadata inline edition with click + + + + + + + Resume playback on start + + + false + + + + + + + Stop playback if song fails to play + + + false + + + + + + + Grey out non existent songs in my playlists + + + @@ -364,7 +348,7 @@ - Automatic + A&utomatic true @@ -388,7 +372,7 @@ - Ask when saving + As&k when saving From 25ef0ff2117bd694f3ef19bf70a4e70421ce34f6 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Mon, 1 Oct 2018 10:40:31 +0200 Subject: [PATCH 4/5] Only use system tray if it's available on the system (#6154) --- src/ui/mainwindow.cpp | 91 ++++++++++++++++++++++----------------- src/ui/systemtrayicon.cpp | 8 +++- src/widgets/osd.cpp | 6 +-- 3 files changed, 61 insertions(+), 44 deletions(-) diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp index de62186d8..f55abdc43 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -792,20 +793,22 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd, mac::SetApplicationHandler(this); #endif // Tray icon - tray_icon_->SetupMenu(ui_->action_previous_track, ui_->action_play_pause, - ui_->action_stop, ui_->action_stop_after_this_track, - ui_->action_next_track, ui_->action_mute, - ui_->action_love, ui_->action_quit); - connect(tray_icon_, SIGNAL(PlayPause()), app_->player(), SLOT(PlayPause())); - connect(tray_icon_, SIGNAL(SeekForward()), app_->player(), - SLOT(SeekForward())); - connect(tray_icon_, SIGNAL(SeekBackward()), app_->player(), - SLOT(SeekBackward())); - connect(tray_icon_, SIGNAL(NextTrack()), app_->player(), SLOT(Next())); - connect(tray_icon_, SIGNAL(PreviousTrack()), app_->player(), - SLOT(Previous())); - connect(tray_icon_, SIGNAL(ShowHide()), SLOT(ToggleShowHide())); - connect(tray_icon_, SIGNAL(ChangeVolume(int)), SLOT(VolumeWheelEvent(int))); + if (tray_icon_) { + tray_icon_->SetupMenu(ui_->action_previous_track, ui_->action_play_pause, + ui_->action_stop, ui_->action_stop_after_this_track, + ui_->action_next_track, ui_->action_mute, + ui_->action_love, ui_->action_quit); + connect(tray_icon_, SIGNAL(PlayPause()), app_->player(), SLOT(PlayPause())); + connect(tray_icon_, SIGNAL(SeekForward()), app_->player(), + SLOT(SeekForward())); + connect(tray_icon_, SIGNAL(SeekBackward()), app_->player(), + SLOT(SeekBackward())); + connect(tray_icon_, SIGNAL(NextTrack()), app_->player(), SLOT(Next())); + connect(tray_icon_, SIGNAL(PreviousTrack()), app_->player(), + SLOT(Previous())); + connect(tray_icon_, SIGNAL(ShowHide()), SLOT(ToggleShowHide())); + connect(tray_icon_, SIGNAL(ChangeVolume(int)), SLOT(VolumeWheelEvent(int))); + } // Windows 7 thumbbar buttons thumbbar_->SetActions(QList() @@ -1015,7 +1018,10 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd, // Reload playlist settings, for BG and glowing ui_->playlist->view()->ReloadSettings(); -#ifndef Q_OS_DARWIN +#ifdef Q_OS_DARWIN + // Always show mainwindow on startup on OS X. + show(); +#else StartupBehaviour behaviour = StartupBehaviour( settings_.value("startupbehaviour", Startup_Remember).toInt()); bool hidden = settings_.value("hidden", false).toBool(); @@ -1034,13 +1040,11 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd, // Force the window to show in case somehow the config has tray and window set // to hide - if (hidden && !tray_icon_->IsVisible()) { + if (hidden && (!QSystemTrayIcon::isSystemTrayAvailable() || !tray_icon_ || + !tray_icon_->IsVisible())) { settings_.setValue("hidden", false); show(); } -#else // Q_OS_DARWIN - // Always show mainwindow on startup on OS X. - show(); #endif QShortcut* close_window_shortcut = new QShortcut(this); @@ -1071,10 +1075,13 @@ MainWindow::~MainWindow() { void MainWindow::ReloadSettings() { #ifndef Q_OS_DARWIN - bool show_tray = settings_.value("showtray", true).toBool(); + bool show_tray = + settings_.value("showtray", QSystemTrayIcon::isSystemTrayAvailable()) + .toBool(); - tray_icon_->SetVisible(show_tray); - if (!show_tray && !isVisible()) show(); + if (tray_icon_) tray_icon_->SetVisible(show_tray); + if ((!show_tray || !QSystemTrayIcon::isSystemTrayAvailable()) && !isVisible()) + show(); #endif QSettings s; @@ -1128,13 +1135,15 @@ void MainWindow::MediaStopped() { ui_->action_play_pause->setEnabled(true); ui_->action_love->setEnabled(false); - tray_icon_->LastFMButtonLoveStateChanged(false); + if (tray_icon_) tray_icon_->LastFMButtonLoveStateChanged(false); track_position_timer_->stop(); track_slider_timer_->stop(); ui_->track_slider->SetStopped(); - tray_icon_->SetProgress(0); - tray_icon_->SetStopped(); + if (tray_icon_) { + tray_icon_->SetProgress(0); + tray_icon_->SetStopped(); + } } void MainWindow::MediaPaused() { @@ -1149,7 +1158,7 @@ void MainWindow::MediaPaused() { track_position_timer_->stop(); track_slider_timer_->stop(); - tray_icon_->SetPaused(); + if (tray_icon_) tray_icon_->SetPaused(); } void MainWindow::MediaPlaying() { @@ -1170,10 +1179,12 @@ void MainWindow::MediaPlaying() { #ifdef HAVE_LIBLASTFM bool enable_love = app_->scrobbler()->IsScrobblingEnabled(); ui_->action_love->setEnabled(enable_love); - tray_icon_->LastFMButtonLoveStateChanged(enable_love); - tray_icon_->SetPlaying(enable_play_pause, enable_love); + if (tray_icon_) { + tray_icon_->LastFMButtonLoveStateChanged(enable_love); + tray_icon_->SetPlaying(enable_play_pause, enable_love); + } #else - tray_icon_->SetPlaying(enable_play_pause); + if (tray_icon_) tray_icon_->SetPlaying(enable_play_pause); #endif track_position_timer_->start(); @@ -1183,12 +1194,12 @@ void MainWindow::MediaPlaying() { void MainWindow::VolumeChanged(int volume) { ui_->action_mute->setChecked(!volume); - tray_icon_->MuteButtonStateChanged(!volume); + if (tray_icon_) tray_icon_->MuteButtonStateChanged(!volume); } void MainWindow::SongChanged(const Song& song) { setWindowTitle(song.PrettyTitleWithArtist()); - tray_icon_->SetProgress(0); + if (tray_icon_) tray_icon_->SetProgress(0); #ifdef HAVE_LIBLASTFM if (ui_->action_toggle_scrobbling->isVisible()) @@ -1237,14 +1248,14 @@ void MainWindow::ScrobblingEnabledChanged(bool value) { } ui_->action_love->setEnabled(value); - tray_icon_->LastFMButtonLoveStateChanged(value); + if (tray_icon_) tray_icon_->LastFMButtonLoveStateChanged(value); } #endif void MainWindow::LastFMButtonVisibilityChanged(bool value) { ui_->action_love->setVisible(value); ui_->last_fm_controls->setVisible(value); - tray_icon_->LastFMButtonVisibilityChanged(value); + if (tray_icon_) tray_icon_->LastFMButtonVisibilityChanged(value); } void MainWindow::ScrobbleButtonVisibilityChanged(bool value) { @@ -1414,7 +1425,9 @@ void MainWindow::closeEvent(QCloseEvent* event) { QSettings s; s.beginGroup(kSettingsGroup); - bool keep_running = s.value("keeprunning", tray_icon_->IsVisible()).toBool(); + bool keep_running(false); + if (tray_icon_) + keep_running = s.value("keeprunning", tray_icon_->IsVisible()).toBool(); if (keep_running && event->spontaneous()) { event->ignore(); @@ -1448,7 +1461,7 @@ void MainWindow::Seeked(qlonglong microseconds) { const int length = app_->player()->GetCurrentItem()->Metadata().length_nanosec() / kNsecPerSec; - tray_icon_->SetProgress(double(position) / length * 100); + if (tray_icon_) tray_icon_->SetProgress(double(position) / length * 100); // if we seeked, scrobbling is canceled, update the icon if (ui_->action_toggle_scrobbling->isVisible()) SetToggleScrobblingIcon(true); @@ -1507,7 +1520,7 @@ void MainWindow::UpdateTrackPosition() { if (position % 10 == 0) { qLog(Debug) << "position" << position << "scrobble point" << scrobble_point << "status" << playlist->get_lastfm_status(); - tray_icon_->SetProgress(double(position) / length * 100); + if (tray_icon_) tray_icon_->SetProgress(double(position) / length * 100); // if we're waiting for the scrobble point, update the icon #ifdef HAVE_LIBLASTFM @@ -1536,13 +1549,13 @@ void MainWindow::UpdateTrackSliderPosition() { #ifdef HAVE_LIBLASTFM void MainWindow::ScrobbledRadioStream() { ui_->action_love->setEnabled(true); - tray_icon_->LastFMButtonLoveStateChanged(true); + if (tray_icon_) tray_icon_->LastFMButtonLoveStateChanged(true); } void MainWindow::Love() { app_->scrobbler()->Love(); ui_->action_love->setEnabled(false); - tray_icon_->LastFMButtonLoveStateChanged(false); + if (tray_icon_) tray_icon_->LastFMButtonLoveStateChanged(false); } #endif @@ -2775,7 +2788,7 @@ void MainWindow::Exit() { if (app_->player()->GetState() == Engine::Playing) { app_->player()->Stop(); hide(); - tray_icon_->SetVisible(false); + if (tray_icon_) tray_icon_->SetVisible(false); return; // Don't quit the application now: wait for the fadeout finished // signal } diff --git a/src/ui/systemtrayicon.cpp b/src/ui/systemtrayicon.cpp index 069143197..7aaeb5e51 100644 --- a/src/ui/systemtrayicon.cpp +++ b/src/ui/systemtrayicon.cpp @@ -21,8 +21,9 @@ #include #include -#include #include +#include +#include #include #include @@ -105,6 +106,9 @@ SystemTrayIcon* SystemTrayIcon::CreateSystemTrayIcon(QObject* parent) { #ifdef Q_OS_DARWIN return new MacSystemTrayIcon(parent); #else - return new QtSystemTrayIcon(parent); + if (QSystemTrayIcon::isSystemTrayAvailable()) + return new QtSystemTrayIcon(parent); + else + return nullptr; #endif } diff --git a/src/widgets/osd.cpp b/src/widgets/osd.cpp index 54ef30881..2d81eb12f 100644 --- a/src/widgets/osd.cpp +++ b/src/widgets/osd.cpp @@ -95,7 +95,7 @@ void OSD::ReshowCurrentSong() { void OSD::AlbumArtLoaded(const Song& song, const QString& uri, const QImage& image) { // Don't change tray icon details if it's a preview - if (!preview_mode_) { + if (!preview_mode_ && tray_icon_) { tray_icon_->SetNowPlaying(song, uri); } @@ -159,7 +159,7 @@ void OSD::Paused() { } void OSD::Stopped() { - tray_icon_->ClearNowPlaying(); + if (tray_icon_) tray_icon_->ClearNowPlaying(); if (ignore_next_stopped_) { ignore_next_stopped_ = false; return; @@ -215,7 +215,7 @@ void OSD::ShowMessage(const QString& summary, const QString& message, #ifndef Q_OS_DARWIN case TrayPopup: - tray_icon_->ShowPopup(summary, message, timeout_msec_); + if (tray_icon_) tray_icon_->ShowPopup(summary, message, timeout_msec_); break; #endif From 5511583966c3108b4f35e562e616858d8ac00ceb Mon Sep 17 00:00:00 2001 From: John Maguire Date: Mon, 1 Oct 2018 17:46:24 +0100 Subject: [PATCH 5/5] Remove SoundCloud support Closes #5766, closes #5538, closes #6114, closes #5914 --- src/CMakeLists.txt | 7 - src/globalsearch/soundcloudsearchprovider.cpp | 91 --- src/globalsearch/soundcloudsearchprovider.h | 53 -- src/internet/core/internetmodel.cpp | 2 - src/internet/soundcloud/soundcloudservice.cpp | 549 ------------------ src/internet/soundcloud/soundcloudservice.h | 150 ----- .../soundcloud/soundcloudsettingspage.cpp | 77 --- .../soundcloud/soundcloudsettingspage.h | 51 -- .../soundcloud/soundcloudsettingspage.ui | 106 ---- src/ui/settingsdialog.cpp | 2 - 10 files changed, 1088 deletions(-) delete mode 100644 src/globalsearch/soundcloudsearchprovider.cpp delete mode 100644 src/globalsearch/soundcloudsearchprovider.h delete mode 100644 src/internet/soundcloud/soundcloudservice.cpp delete mode 100644 src/internet/soundcloud/soundcloudservice.h delete mode 100644 src/internet/soundcloud/soundcloudsettingspage.cpp delete mode 100644 src/internet/soundcloud/soundcloudsettingspage.h delete mode 100644 src/internet/soundcloud/soundcloudsettingspage.ui diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8f258af17..8ed886174 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -153,7 +153,6 @@ set(SOURCES globalsearch/simplesearchprovider.cpp globalsearch/somafmsearchprovider.cpp globalsearch/intergalacticfmsearchprovider.cpp - globalsearch/soundcloudsearchprovider.cpp globalsearch/spotifysearchprovider.cpp globalsearch/suggestionwidget.cpp globalsearch/urlsearchprovider.cpp @@ -191,8 +190,6 @@ set(SOURCES internet/somafm/somafmurlhandler.cpp internet/intergalacticfm/intergalacticfmservice.cpp internet/intergalacticfm/intergalacticfmurlhandler.cpp - internet/soundcloud/soundcloudservice.cpp - internet/soundcloud/soundcloudsettingspage.cpp internet/spotify/spotifyserver.cpp internet/spotify/spotifyservice.cpp internet/spotify/spotifysettingspage.cpp @@ -465,7 +462,6 @@ set(HEADERS globalsearch/globalsearchview.h globalsearch/searchprovider.h globalsearch/simplesearchprovider.h - globalsearch/soundcloudsearchprovider.h globalsearch/spotifysearchprovider.h globalsearch/suggestionwidget.h @@ -499,8 +495,6 @@ set(HEADERS internet/somafm/somafmurlhandler.h internet/intergalacticfm/intergalacticfmservice.h internet/intergalacticfm/intergalacticfmurlhandler.h - internet/soundcloud/soundcloudservice.h - internet/soundcloud/soundcloudsettingspage.h internet/spotify/spotifyserver.h internet/spotify/spotifyservice.h internet/spotify/spotifysettingspage.h @@ -701,7 +695,6 @@ set(UI internet/magnatune/magnatunedownloaddialog.ui internet/magnatune/magnatunesettingspage.ui internet/core/searchboxwidget.ui - internet/soundcloud/soundcloudsettingspage.ui internet/spotify/spotifysettingspage.ui internet/subsonic/subsonicsettingspage.ui diff --git a/src/globalsearch/soundcloudsearchprovider.cpp b/src/globalsearch/soundcloudsearchprovider.cpp deleted file mode 100644 index cd03ecb43..000000000 --- a/src/globalsearch/soundcloudsearchprovider.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* This file is part of Clementine. - Copyright 2011, David Sansome - - Clementine 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 3 of the License, or - (at your option) any later version. - - Clementine 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 Clementine. If not, see . -*/ - -#include "soundcloudsearchprovider.h" - -#include - -#include "core/application.h" -#include "core/logging.h" -#include "covers/albumcoverloader.h" -#include "internet/soundcloud/soundcloudservice.h" -#include "ui/iconloader.h" - -SoundCloudSearchProvider::SoundCloudSearchProvider(Application* app, - QObject* parent) - : SearchProvider(app, parent), service_(nullptr) {} - -void SoundCloudSearchProvider::Init(SoundCloudService* service) { - service_ = service; - SearchProvider::Init( - "SoundCloud", "soundcloud", IconLoader::Load("soundcloud", - IconLoader::Provider), WantsDelayedQueries | ArtIsProbablyRemote | - CanShowConfig); - - connect(service_, SIGNAL(SimpleSearchResults(int, SongList)), - SLOT(SearchDone(int, SongList))); - - cover_loader_options_.desired_height_ = kArtHeight; - cover_loader_options_.pad_output_image_ = true; - cover_loader_options_.scale_output_image_ = true; - - connect(app_->album_cover_loader(), SIGNAL(ImageLoaded(quint64, QImage)), - SLOT(AlbumArtLoaded(quint64, QImage))); -} - -void SoundCloudSearchProvider::SearchAsync(int id, const QString& query) { - const int service_id = service_->SimpleSearch(query); - pending_searches_[service_id] = PendingState(id, TokenizeQuery(query)); - ; -} - -void SoundCloudSearchProvider::SearchDone(int id, const SongList& songs) { - // Map back to the original id. - const PendingState state = pending_searches_.take(id); - const int global_search_id = state.orig_id_; - - ResultList ret; - for (const Song& song : songs) { - Result result(this); - result.metadata_ = song; - - ret << result; - } - - emit ResultsAvailable(global_search_id, ret); - MaybeSearchFinished(global_search_id); -} - -void SoundCloudSearchProvider::MaybeSearchFinished(int id) { - if (pending_searches_.keys(PendingState(id, QStringList())).isEmpty()) { - emit SearchFinished(id); - } -} - -void SoundCloudSearchProvider::LoadArtAsync(int id, const Result& result) { - quint64 loader_id = app_->album_cover_loader()->LoadImageAsync( - cover_loader_options_, result.metadata_); - cover_loader_tasks_[loader_id] = id; -} - -void SoundCloudSearchProvider::AlbumArtLoaded(quint64 id, const QImage& image) { - if (!cover_loader_tasks_.contains(id)) { - return; - } - int original_id = cover_loader_tasks_.take(id); - emit ArtLoaded(original_id, image); -} diff --git a/src/globalsearch/soundcloudsearchprovider.h b/src/globalsearch/soundcloudsearchprovider.h deleted file mode 100644 index 9942aff27..000000000 --- a/src/globalsearch/soundcloudsearchprovider.h +++ /dev/null @@ -1,53 +0,0 @@ -/* This file is part of Clementine. - Copyright 2012, David Sansome - - Clementine 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 3 of the License, or - (at your option) any later version. - - Clementine 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 Clementine. If not, see . -*/ - -#ifndef SOUNDCLOUDSEARCHPROVIDER_H -#define SOUNDCLOUDSEARCHPROVIDER_H - -#include "searchprovider.h" -#include "covers/albumcoverloaderoptions.h" -#include "internet/soundcloud/soundcloudservice.h" - -class AlbumCoverLoader; - -class SoundCloudSearchProvider : public SearchProvider { - Q_OBJECT - - public: - explicit SoundCloudSearchProvider(Application* app, QObject* parent = nullptr); - void Init(SoundCloudService* service); - - // SearchProvider - void SearchAsync(int id, const QString& query); - void LoadArtAsync(int id, const Result& result); - InternetService* internet_service() { return service_; } - - private slots: - void AlbumArtLoaded(quint64 id, const QImage& image); - void SearchDone(int id, const SongList& songs); - - private: - void MaybeSearchFinished(int id); - - SoundCloudService* service_; - QMap pending_searches_; - - AlbumCoverLoaderOptions cover_loader_options_; - QMap cover_loader_tasks_; -}; - -#endif diff --git a/src/internet/core/internetmodel.cpp b/src/internet/core/internetmodel.cpp index fced864bd..d7db2975b 100644 --- a/src/internet/core/internetmodel.cpp +++ b/src/internet/core/internetmodel.cpp @@ -41,7 +41,6 @@ #include "internet/magnatune/magnatuneservice.h" #include "internet/podcasts/podcastservice.h" #include "internet/somafm/somafmservice.h" -#include "internet/soundcloud/soundcloudservice.h" #include "internet/spotify/spotifyservice.h" #include "internet/subsonic/subsonicservice.h" #include "smartplaylists/generatormimedata.h" @@ -93,7 +92,6 @@ InternetModel::InternetModel(Application* app, QObject* parent) AddService(new RadioTunesService(app, this)); AddService(new SomaFMService(app, this)); AddService(new IntergalacticFMService(app, this)); - AddService(new SoundCloudService(app, this)); AddService(new SpotifyService(app, this)); AddService(new SubsonicService(app, this)); #ifdef HAVE_BOX diff --git a/src/internet/soundcloud/soundcloudservice.cpp b/src/internet/soundcloud/soundcloudservice.cpp deleted file mode 100644 index 369f34b19..000000000 --- a/src/internet/soundcloud/soundcloudservice.cpp +++ /dev/null @@ -1,549 +0,0 @@ -/* This file is part of Clementine. - Copyright 2012, 2014, Arnaud Bienner - Copyright 2014, maximko - Copyright 2014, Krzysztof Sobiecki - Copyright 2014, John Maguire - - Clementine 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 3 of the License, or - (at your option) any later version. - - Clementine 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 Clementine. If not, see . - */ - -#include "soundcloudservice.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include "internet/core/internetmodel.h" -#include "internet/core/oauthenticator.h" -#include "internet/core/searchboxwidget.h" - -#include "core/application.h" -#include "core/closure.h" -#include "core/logging.h" -#include "core/mergedproxymodel.h" -#include "core/network.h" -#include "core/song.h" -#include "core/taskmanager.h" -#include "core/timeconstants.h" -#include "core/utilities.h" -#include "globalsearch/globalsearch.h" -#include "globalsearch/soundcloudsearchprovider.h" -#include "ui/iconloader.h" - -const char* SoundCloudService::kApiClientId = - "2add0f709fcfae1fd7a198ec7573d2d4"; -const char* SoundCloudService::kApiClientSecret = - "d1cd7829da2e98e1e0621d85d57a2077"; - -const char* SoundCloudService::kServiceName = "SoundCloud"; -const char* SoundCloudService::kSettingsGroup = "SoundCloud"; -const char* SoundCloudService::kUrl = "https://api.soundcloud.com/"; -const char* SoundCloudService::kOAuthEndpoint = "https://soundcloud.com/connect"; -const char* SoundCloudService::kOAuthTokenEndpoint = "https://api.soundcloud.com/oauth2/token"; -const char* SoundCloudService::kOAuthScope = "non-expiring"; -const char* SoundCloudService::kHomepage = "http://soundcloud.com/"; - -const int SoundCloudService::kSearchDelayMsec = 400; -const int SoundCloudService::kSongSearchLimit = 100; -const int SoundCloudService::kSongSimpleSearchLimit = 100; - -typedef QPair Param; - -SoundCloudService::SoundCloudService(Application* app, InternetModel* parent) - : InternetService(kServiceName, app, parent, parent), - root_(nullptr), - search_(nullptr), - user_tracks_(nullptr), - user_playlists_(nullptr), - user_activities_(nullptr), - user_favorites_(nullptr), - network_(new NetworkAccessManager(this)), - context_menu_(nullptr), - search_box_(new SearchBoxWidget(this)), - search_delay_(new QTimer(this)), - next_pending_search_id_(0) { - search_delay_->setInterval(kSearchDelayMsec); - search_delay_->setSingleShot(true); - connect(search_delay_, SIGNAL(timeout()), SLOT(DoSearch())); - - SoundCloudSearchProvider* search_provider = - new SoundCloudSearchProvider(app_, this); - search_provider->Init(this); - app_->global_search()->AddProvider(search_provider); - - connect(search_box_, SIGNAL(TextChanged(QString)), SLOT(Search(QString))); -} - -SoundCloudService::~SoundCloudService() {} - -QStandardItem* SoundCloudService::CreateRootItem() { - root_ = new QStandardItem(IconLoader::Load("soundcloud", - IconLoader::Provider), kServiceName); - root_->setData(true, InternetModel::Role_CanLazyLoad); - root_->setData(InternetModel::PlayBehaviour_DoubleClickAction, - InternetModel::Role_PlayBehaviour); - return root_; -} - -void SoundCloudService::LazyPopulate(QStandardItem* item) { - switch (item->data(InternetModel::Role_Type).toInt()) { - case InternetModel::Type_Service: { - EnsureItemsCreated(); - break; - } - default: - break; - } -} - -void SoundCloudService::EnsureItemsCreated() { - if (!search_) { - search_ = - new QStandardItem(IconLoader::Load("edit-find", IconLoader::Base), - tr("Search results")); - search_->setToolTip( - tr("Start typing something on the search box above to " - "fill this search results list")); - search_->setData(InternetModel::PlayBehaviour_MultipleItems, - InternetModel::Role_PlayBehaviour); - root_->appendRow(search_); - } - if (!user_tracks_ && !user_activities_ && !user_playlists_ && IsLoggedIn()) { - user_activities_ = new QStandardItem(tr("Activities stream")); - user_activities_->setData(InternetModel::PlayBehaviour_MultipleItems, - InternetModel::Role_PlayBehaviour); - root_->appendRow(user_activities_); - - user_playlists_ = new QStandardItem(tr("Playlists")); - root_->appendRow(user_playlists_); - - user_tracks_ = new QStandardItem(tr("Tracks")); - user_tracks_->setData(InternetModel::PlayBehaviour_MultipleItems, - InternetModel::Role_PlayBehaviour); - root_->appendRow(user_tracks_); - - user_favorites_ = new QStandardItem(tr("Favorites")); - root_->appendRow(user_favorites_); - - RetrieveUserData(); // at least, try to (this will do nothing if user isn't - // logged) - } -} - -QWidget* SoundCloudService::HeaderWidget() const { return search_box_; } - -void SoundCloudService::ShowConfig() { - app_->OpenSettingsDialogAtPage(SettingsDialog::Page_SoundCloud); -} - -void SoundCloudService::Homepage() { - QDesktopServices::openUrl(QUrl(kHomepage)); -} - -void SoundCloudService::Connect() { - OAuthenticator* oauth = new OAuthenticator( - kApiClientId, kApiClientSecret, - OAuthenticator::RedirectStyle::REMOTE_WITH_STATE, this); - - oauth->StartAuthorisation(kOAuthEndpoint, kOAuthTokenEndpoint, kOAuthScope); - - NewClosure(oauth, SIGNAL(Finished()), this, - SLOT(ConnectFinished(OAuthenticator*)), oauth); -} - -void SoundCloudService::ConnectFinished(OAuthenticator* oauth) { - oauth->deleteLater(); - - access_token_ = oauth->access_token(); - if (!access_token_.isEmpty()) { - emit Connected(); - } - expiry_time_ = oauth->expiry_time(); - QSettings s; - s.beginGroup(kSettingsGroup); - s.setValue("access_token", access_token_); - - EnsureItemsCreated(); -} - -void SoundCloudService::LoadAccessTokenIfEmpty() { - if (access_token_.isEmpty()) { - QSettings s; - s.beginGroup(kSettingsGroup); - if (!s.contains("access_token")) { - return; - } - access_token_ = s.value("access_token").toString(); - } -} - -bool SoundCloudService::IsLoggedIn() { - LoadAccessTokenIfEmpty(); - return !access_token_.isEmpty(); -} - -void SoundCloudService::Logout() { - QSettings s; - s.beginGroup(kSettingsGroup); - - access_token_.clear(); - s.remove("access_token"); - pending_playlists_requests_.clear(); - if (user_activities_) root_->removeRow(user_activities_->row()); - if (user_tracks_) root_->removeRow(user_tracks_->row()); - if (user_playlists_) root_->removeRow(user_playlists_->row()); - user_activities_ = nullptr; - user_tracks_ = nullptr; - user_playlists_ = nullptr; -} - -void SoundCloudService::RetrieveUserData() { - LoadAccessTokenIfEmpty(); - RetrieveUserActivities(); - RetrieveUserTracks(); - RetrieveUserPlaylists(); - RetrieveUserFavorites(); -} - -void SoundCloudService::RetrieveUserTracks() { - QList parameters; - parameters << Param("oauth_token", access_token_); - QNetworkReply* reply = CreateRequest("me/tracks", parameters); - NewClosure(reply, SIGNAL(finished()), this, - SLOT(UserTracksRetrieved(QNetworkReply*)), reply); -} - -void SoundCloudService::UserTracksRetrieved(QNetworkReply* reply) { - reply->deleteLater(); - - SongList songs = ExtractSongs(ExtractResult(reply)); - // Fill results list - for (const Song& song : songs) { - QStandardItem* child = CreateSongItem(song); - user_tracks_->appendRow(child); - } -} - -void SoundCloudService::RetrieveUserActivities() { - QList parameters; - parameters << Param("oauth_token", access_token_); - QNetworkReply* reply = CreateRequest("me/activities", parameters); - NewClosure(reply, SIGNAL(finished()), this, - SLOT(UserActivitiesRetrieved(QNetworkReply*)), reply); -} - -void SoundCloudService::UserActivitiesRetrieved(QNetworkReply* reply) { - reply->deleteLater(); - - QList activities = ExtractActivities(ExtractResult(reply)); - // Fill results list - for (QStandardItem* activity : activities) { - user_activities_->appendRow(activity); - } -} - -void SoundCloudService::RetrieveUserPlaylists() { - QList parameters; - parameters << Param("oauth_token", access_token_); - QNetworkReply* reply = CreateRequest("me/playlists", parameters); - NewClosure(reply, SIGNAL(finished()), this, - SLOT(UserPlaylistsRetrieved(QNetworkReply*)), reply); -} - -void SoundCloudService::RetrieveUserFavorites() { - QList parameters; - parameters << Param("oauth_token", access_token_); - QNetworkReply* reply = CreateRequest("me/favorites", parameters); - NewClosure(reply, SIGNAL(finished()), this, - SLOT(UserFavoritesRetrieved(QNetworkReply*)), reply); -} - -void SoundCloudService::UserPlaylistsRetrieved(QNetworkReply* reply) { - reply->deleteLater(); - - QList playlists = ExtractResult(reply).toList(); - for (const QVariant& playlist : playlists) { - QMap playlist_map = playlist.toMap(); - - QStandardItem* playlist_item = CreatePlaylistItem(playlist_map["title"].toString()); - SongList songs = ExtractSongs(playlist_map["tracks"]); - for (const Song& song : songs) { - playlist_item->appendRow(CreateSongItem(song)); - } - user_playlists_->appendRow(playlist_item); - } -} - -void SoundCloudService::UserFavoritesRetrieved(QNetworkReply* reply) { - reply->deleteLater(); - - SongList songs = ExtractSongs(ExtractResult(reply)); - // Fill results list - for (const Song& song : songs) { - QStandardItem* child = CreateSongItem(song); - user_favorites_->appendRow(child); - } -} - -void SoundCloudService::Search(const QString& text, bool now) { - pending_search_ = text; - - // If there is no text (e.g. user cleared search box), we don't need to do a - // real query that will return nothing: we can clear the playlist now - if (text.isEmpty()) { - search_delay_->stop(); - ClearSearchResults(); - return; - } - - if (now) { - search_delay_->stop(); - DoSearch(); - } else { - search_delay_->start(); - } -} - -void SoundCloudService::DoSearch() { - ClearSearchResults(); - - QList parameters; - parameters << Param("q", pending_search_) << Param("limit", QString::number(kSongSearchLimit)); - QNetworkReply* reply = CreateRequest("tracks", parameters); - const int id = next_pending_search_id_++; - NewClosure(reply, SIGNAL(finished()), this, - SLOT(SearchFinished(QNetworkReply*, int)), reply, id); -} - -void SoundCloudService::SearchFinished(QNetworkReply* reply, int task_id) { - reply->deleteLater(); - - SongList songs = ExtractSongs(ExtractResult(reply)); - // Fill results list - for (const Song& song : songs) { - QStandardItem* child = CreateSongItem(song); - search_->appendRow(child); - } - - QModelIndex index = model()->merged_model()->mapFromSource(search_->index()); - ScrollToIndex(index); -} - -void SoundCloudService::ClearSearchResults() { - if (search_) { - search_->removeRows(0, search_->rowCount()); - } -} - -int SoundCloudService::SimpleSearch(const QString& text) { - QList parameters; - parameters << Param("q", text) << Param("limit", QString::number(kSongSimpleSearchLimit)); - QNetworkReply* reply = CreateRequest("tracks", parameters); - const int id = next_pending_search_id_++; - NewClosure(reply, SIGNAL(finished()), this, - SLOT(SimpleSearchFinished(QNetworkReply*, int)), reply, id); - return id; -} - -void SoundCloudService::SimpleSearchFinished(QNetworkReply* reply, int id) { - reply->deleteLater(); - - SongList songs = ExtractSongs(ExtractResult(reply)); - emit SimpleSearchResults(id, songs); -} - -void SoundCloudService::EnsureMenuCreated() { - if (!context_menu_) { - context_menu_ = new QMenu; - context_menu_->addActions(GetPlaylistActions()); - context_menu_->addSeparator(); - context_menu_->addAction(IconLoader::Load("download", IconLoader::Base), - tr("Open %1 in browser").arg("soundcloud.com"), - this, SLOT(Homepage())); - context_menu_->addSeparator(); - context_menu_->addAction(IconLoader::Load("configure", IconLoader::Base), - tr("Configure SoundCloud..."), - this, SLOT(ShowConfig())); - } -} - -void SoundCloudService::ShowContextMenu(const QPoint& global_pos) { - EnsureMenuCreated(); - - context_menu_->popup(global_pos); -} - -QStandardItem* SoundCloudService::CreatePlaylistItem(const QString& playlist_name) { - QStandardItem* item = new QStandardItem(playlist_name); - item->setData(true, InternetModel::Role_CanLazyLoad); - item->setData(InternetModel::PlayBehaviour_MultipleItems, - InternetModel::Role_PlayBehaviour); - return item; -} - -QNetworkReply* SoundCloudService::CreateRequest(const QString& ressource_name, - const QList& params) { - QUrl url(kUrl); - - url.setPath(ressource_name); - - url.addQueryItem("client_id", kApiClientId); - for (const Param& param : params) { - url.addQueryItem(param.first, param.second); - } - - qLog(Debug) << "Request Url: " << url.toEncoded(); - - QNetworkRequest req(url); - req.setRawHeader("Accept", "application/json"); - QNetworkReply* reply = network_->get(req); - return reply; -} - -QVariant SoundCloudService::ExtractResult(QNetworkReply* reply) { - if (reply->error() != QNetworkReply::NoError) { - qLog(Error) << "Error when retrieving SoundCloud results:" - << reply->errorString() << QString(" (%1)").arg(reply->error()); - if (reply->error() == QNetworkReply::ContentAccessDenied || - reply->error() == QNetworkReply::ContentOperationNotPermittedError || - reply->error() == QNetworkReply::ContentNotFoundError || - reply->error() == QNetworkReply::AuthenticationRequiredError) { - // In case of access denied errors (invalid token?) logout - Logout(); - return QVariant(); - } - } - QJson::Parser parser; - bool ok; - QVariant result = parser.parse(reply, &ok); - if (!ok) { - qLog(Error) << "Error while parsing SoundCloud result"; - } - return result; -} - -void SoundCloudService::RetrievePlaylist(int playlist_id, - QStandardItem* playlist_item) { - const int request_id = next_retrieve_playlist_id_++; - pending_playlists_requests_.insert(request_id, - PlaylistInfo(playlist_id, playlist_item)); - QList parameters; - parameters << Param("oauth_token", access_token_); - QNetworkReply* reply = - CreateRequest("playlists/" + QString::number(playlist_id), parameters); - NewClosure(reply, SIGNAL(finished()), this, - SLOT(PlaylistRetrieved(QNetworkReply*, int)), reply, request_id); -} - -void SoundCloudService::PlaylistRetrieved(QNetworkReply* reply, - int request_id) { - if (!pending_playlists_requests_.contains(request_id)) return; - PlaylistInfo playlist_info = pending_playlists_requests_.take(request_id); - QVariant res = ExtractResult(reply); - SongList songs = ExtractSongs(res.toMap()["tracks"]); - for (const Song& song : songs) { - QStandardItem* child = CreateSongItem(song); - playlist_info.item_->appendRow(child); - } -} - -QList SoundCloudService::ExtractActivities(const QVariant& result) { - QList activities; - QVariantList q_variant_list = result.toMap()["collection"].toList(); - for (const QVariant& q : q_variant_list) { - QMap activity = q.toMap(); - const QString type = activity["type"].toString(); - if (type == "track") { - Song song = ExtractSong(activity["origin"].toMap()); - if (song.is_valid()) { - activities << CreateSongItem(song); - } - } else if (type == "playlist") { - QMap origin_map = activity["origin"].toMap(); - QStandardItem* playlist_item = - CreatePlaylistItem(origin_map["title"].toString()); - activities << playlist_item; - RetrievePlaylist(origin_map["id"].toInt(), playlist_item); - } - } - return activities; -} - -SongList SoundCloudService::ExtractSongs(const QVariant& result) { - SongList songs; - - QVariantList q_variant_list = result.toList(); - for (const QVariant& q : q_variant_list) { - Song song = ExtractSong(q.toMap()); - if (song.is_valid()) { - songs << song; - } - } - return songs; -} - -Song SoundCloudService::ExtractSong(const QVariantMap& result_song) { - Song song; - if (!result_song.isEmpty() && result_song["streamable"].toBool()) { - QUrl stream_url = result_song["stream_url"].toUrl(); - stream_url.addQueryItem("client_id", kApiClientId); - song.set_url(stream_url); - - QString username = result_song["user"].toMap()["username"].toString(); - // We don't have a real artist name, but username is the most similar thing - // we have - song.set_artist(username); - - QString title = result_song["title"].toString(); - song.set_title(title); - - QString genre = result_song["genre"].toString(); - song.set_genre(genre); - - float bpm = result_song["bpm"].toFloat(); - song.set_bpm(bpm); - - QVariant cover = result_song["artwork_url"]; - if (cover.isValid()) { - // Increase cover size. - // See https://developers.soundcloud.com/docs/api/reference#artwork_url - QString big_cover = cover.toString().replace("large", "t500x500"); - QUrl cover_url(big_cover, QUrl::StrictMode); - - // SoundCloud covers URL are https, but our cover loader doesn't seem to - // deal well with https URL. Anyway, we don't need a secure connection to - // get a cover image. - cover_url.setScheme("http"); - song.set_art_automatic(cover_url.toEncoded()); - } - - int playcount = result_song["playback_count"].toInt(); - song.set_playcount(playcount); - - int year = result_song["release_year"].toInt(); - song.set_year(year); - - QVariant q_duration = result_song["duration"]; - quint64 duration = q_duration.toULongLong() * kNsecPerMsec; - song.set_length_nanosec(duration); - - song.set_valid(true); - } - return song; -} diff --git a/src/internet/soundcloud/soundcloudservice.h b/src/internet/soundcloud/soundcloudservice.h deleted file mode 100644 index 3ebcb1f96..000000000 --- a/src/internet/soundcloud/soundcloudservice.h +++ /dev/null @@ -1,150 +0,0 @@ -/* This file is part of Clementine. - Copyright 2012, 2014, Arnaud Bienner - Copyright 2014, Krzysztof Sobiecki - Copyright 2014, John Maguire - - Clementine 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 3 of the License, or - (at your option) any later version. - - Clementine 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 Clementine. If not, see . - */ - -#ifndef INTERNET_SOUNDCLOUD_SOUNDCLOUDSERVICE_H_ -#define INTERNET_SOUNDCLOUD_SOUNDCLOUDSERVICE_H_ - -#include "internet/core/internetmodel.h" -#include "internet/core/internetservice.h" - -class NetworkAccessManager; -class OAuthenticator; -class SearchBoxWidget; - -class QMenu; -class QNetworkReply; - -class SoundCloudService : public InternetService { - Q_OBJECT - - public: - SoundCloudService(Application* app, InternetModel* parent); - ~SoundCloudService(); - - // Internet Service methods - QStandardItem* CreateRootItem(); - void LazyPopulate(QStandardItem* parent); - - // TODO(Arnaud Bienner) - // QList playlistitem_actions(const Song& song); - void ShowContextMenu(const QPoint& global_pos); - QWidget* HeaderWidget() const; - - void Connect(); - bool IsLoggedIn(); - void Logout(); - - int SimpleSearch(const QString& query); - - static const char* kServiceName; - static const char* kSettingsGroup; - - signals: - void SimpleSearchResults(int id, SongList songs); - void Connected(); - - public slots: - void ShowConfig(); - - private slots: - void ConnectFinished(OAuthenticator* oauth); - void UserTracksRetrieved(QNetworkReply* reply); - void UserActivitiesRetrieved(QNetworkReply* reply); - void UserPlaylistsRetrieved(QNetworkReply* reply); - void UserFavoritesRetrieved(QNetworkReply* reply); - void PlaylistRetrieved(QNetworkReply* reply, int request_id); - void Search(const QString& text, bool now = false); - void DoSearch(); - void SearchFinished(QNetworkReply* reply, int task); - void SimpleSearchFinished(QNetworkReply* reply, int id); - - void Homepage(); - - private: - struct PlaylistInfo { - PlaylistInfo() {} - PlaylistInfo(int id, QStandardItem* item) : id_(id), item_(item) {} - - int id_; - QStandardItem* item_; - }; - - // Try to load "access_token" from preferences if the current access_token's - // value is empty - void LoadAccessTokenIfEmpty(); - void RetrieveUserData(); - void RetrieveUserTracks(); - void RetrieveUserActivities(); - void RetrieveUserPlaylists(); - void RetrieveUserFavorites(); - void RetrievePlaylist(int playlist_id, QStandardItem* playlist_item); - void ClearSearchResults(); - void EnsureItemsCreated(); - void EnsureMenuCreated(); - - QStandardItem* CreatePlaylistItem(const QString& playlist_name); - - QNetworkReply* CreateRequest(const QString& ressource_name, - const QList>& params); - // Convenient function for extracting result from reply - QVariant ExtractResult(QNetworkReply* reply); - // Returns items directly, as activities can be playlists or songs - QList ExtractActivities(const QVariant& result); - SongList ExtractSongs(const QVariant& result); - Song ExtractSong(const QVariantMap& result_song); - - QStandardItem* root_; - QStandardItem* search_; - QStandardItem* user_tracks_; - QStandardItem* user_playlists_; - QStandardItem* user_activities_; - QStandardItem* user_favorites_; - - NetworkAccessManager* network_; - - QMenu* context_menu_; - SearchBoxWidget* search_box_; - QTimer* search_delay_; - QString pending_search_; - // Request IDs - int next_pending_search_id_; - int next_retrieve_playlist_id_; - - QMap pending_playlists_requests_; - - QByteArray api_key_; - - QString access_token_; - QDateTime expiry_time_; - - static const char* kUrl; - static const char* kOAuthEndpoint; - static const char* kOAuthTokenEndpoint; - static const char* kOAuthScope; - static const char* kHomepage; - - static const int kSongSearchLimit; - static const int kSongSimpleSearchLimit; - static const int kSearchDelayMsec; - - static const char* kApiClientId; - static const char* kApiClientSecret; -}; - -#endif // INTERNET_SOUNDCLOUD_SOUNDCLOUDSERVICE_H_ diff --git a/src/internet/soundcloud/soundcloudsettingspage.cpp b/src/internet/soundcloud/soundcloudsettingspage.cpp deleted file mode 100644 index ac51242f9..000000000 --- a/src/internet/soundcloud/soundcloudsettingspage.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* This file is part of Clementine. - Copyright 2014, Arnaud Bienner - Copyright 2014, Krzysztof Sobiecki - - Clementine 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 3 of the License, or - (at your option) any later version. - - Clementine 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 Clementine. If not, see . -*/ - -#include "soundcloudservice.h" -#include "soundcloudsettingspage.h" -#include "ui_soundcloudsettingspage.h" -#include "core/application.h" -#include "internet/core/internetmodel.h" -#include "ui/iconloader.h" - -SoundCloudSettingsPage::SoundCloudSettingsPage(SettingsDialog* parent) - : SettingsPage(parent), - ui_(new Ui::SoundCloudSettingsPage), - service_( - dialog()->app()->internet_model()->Service()) { - ui_->setupUi(this); - setWindowIcon(IconLoader::Load("soundcloud", IconLoader::Provider)); - - ui_->login_state->AddCredentialGroup(ui_->login_container); - - connect(ui_->login_button, SIGNAL(clicked()), SLOT(LoginClicked())); - connect(ui_->login_state, SIGNAL(LogoutClicked()), SLOT(LogoutClicked())); - connect(service_, SIGNAL(Connected()), SLOT(Connected())); - - dialog()->installEventFilter(this); -} - -SoundCloudSettingsPage::~SoundCloudSettingsPage() { delete ui_; } - -void SoundCloudSettingsPage::Load() { - if (service_->IsLoggedIn()) { - ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn); - } -} - -void SoundCloudSettingsPage::Save() { - // Everything is done in the service: nothing to do here -} - -void SoundCloudSettingsPage::LoginClicked() { - service_->Connect(); - ui_->login_button->setEnabled(false); -} - -bool SoundCloudSettingsPage::eventFilter(QObject* object, QEvent* event) { - if (object == dialog() && event->type() == QEvent::Enter) { - ui_->login_button->setEnabled(true); - return false; - } - - return SettingsPage::eventFilter(object, event); -} - -void SoundCloudSettingsPage::LogoutClicked() { - service_->Logout(); - ui_->login_button->setEnabled(true); - ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedOut); -} - -void SoundCloudSettingsPage::Connected() { - ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn); -} diff --git a/src/internet/soundcloud/soundcloudsettingspage.h b/src/internet/soundcloud/soundcloudsettingspage.h deleted file mode 100644 index 25b5ea3fe..000000000 --- a/src/internet/soundcloud/soundcloudsettingspage.h +++ /dev/null @@ -1,51 +0,0 @@ -/* This file is part of Clementine. - Copyright 2014, Arnaud Bienner - Copyright 2014, Krzysztof Sobiecki - - Clementine 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 3 of the License, or - (at your option) any later version. - - Clementine 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 Clementine. If not, see . -*/ - -#ifndef INTERNET_SOUNDCLOUD_SOUNDCLOUDSETTINGSPAGE_H_ -#define INTERNET_SOUNDCLOUD_SOUNDCLOUDSETTINGSPAGE_H_ - -#include "ui/settingspage.h" - -class SoundCloudService; -class Ui_SoundCloudSettingsPage; - -class SoundCloudSettingsPage : public SettingsPage { - Q_OBJECT - - public: - explicit SoundCloudSettingsPage(SettingsDialog* parent = nullptr); - ~SoundCloudSettingsPage(); - - void Load(); - void Save(); - - // QObject - bool eventFilter(QObject* object, QEvent* event); - - private slots: - void LoginClicked(); - void LogoutClicked(); - void Connected(); - - private: - Ui_SoundCloudSettingsPage* ui_; - - SoundCloudService* service_; -}; - -#endif // INTERNET_SOUNDCLOUD_SOUNDCLOUDSETTINGSPAGE_H_ diff --git a/src/internet/soundcloud/soundcloudsettingspage.ui b/src/internet/soundcloud/soundcloudsettingspage.ui deleted file mode 100644 index 6d5272cf6..000000000 --- a/src/internet/soundcloud/soundcloudsettingspage.ui +++ /dev/null @@ -1,106 +0,0 @@ - - - SoundCloudSettingsPage - - - - 0 - 0 - 569 - 491 - - - - SoundCloud - - - - - - true - - - You don't need to be logged in to search and to listen to music on SoundCloud. However, you need to login to access your playlists and your stream. - - - true - - - - - - - - - - - 28 - - - 0 - - - 0 - - - - - - - Login - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Clicking the Login button will open a web browser. You should return to Clementine after you have logged in. - - - true - - - - - - - - - - Qt::Vertical - - - - 20 - 357 - - - - - - - - - LoginStateWidget - QWidget -
widgets/loginstatewidget.h
- 1 -
-
- -
diff --git a/src/ui/settingsdialog.cpp b/src/ui/settingsdialog.cpp index 6165c8a33..7549446e1 100644 --- a/src/ui/settingsdialog.cpp +++ b/src/ui/settingsdialog.cpp @@ -34,7 +34,6 @@ #include "internet/digitally/digitallyimportedsettingspage.h" #include "internet/magnatune/magnatunesettingspage.h" #include "internet/podcasts/podcastsettingspage.h" -#include "internet/soundcloud/soundcloudsettingspage.h" #include "internet/spotify/spotifysettingspage.h" #include "internet/subsonic/subsonicsettingspage.h" #include "library/librarysettingspage.h" @@ -175,7 +174,6 @@ SettingsDialog::SettingsDialog(Application* app, BackgroundStreams* streams, AddPage(Page_Skydrive, new SkydriveSettingsPage(this), providers); #endif - AddPage(Page_SoundCloud, new SoundCloudSettingsPage(this), providers); AddPage(Page_Spotify, new SpotifySettingsPage(this), providers); #ifdef HAVE_SEAFILE