From 09bbf1f4d720da52ad937dd39b92d865bd355972 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Sun, 11 Jul 2021 01:02:53 +0200 Subject: [PATCH] Add radios --- data/data.qrc | 1 + data/icons.qrc | 15 + data/icons/128x128/radio.png | Bin 0 -> 34041 bytes data/icons/128x128/radioparadise.png | Bin 0 -> 9016 bytes data/icons/128x128/somafm.png | Bin 0 -> 1801 bytes data/icons/22x22/radio.png | Bin 0 -> 1103 bytes data/icons/22x22/radioparadise.png | Bin 0 -> 1208 bytes data/icons/22x22/somafm.png | Bin 0 -> 639 bytes data/icons/32x32/radio.png | Bin 0 -> 2116 bytes data/icons/32x32/radioparadise.png | Bin 0 -> 1870 bytes data/icons/32x32/somafm.png | Bin 0 -> 626 bytes data/icons/48x48/radio.png | Bin 0 -> 5326 bytes data/icons/48x48/radioparadise.png | Bin 0 -> 3004 bytes data/icons/48x48/somafm.png | Bin 0 -> 1080 bytes data/icons/64x64/radio.png | Bin 0 -> 9819 bytes data/icons/64x64/radioparadise.png | Bin 0 -> 3782 bytes data/icons/64x64/somafm.png | Bin 0 -> 996 bytes data/icons/full/radio.png | Bin 0 -> 34041 bytes data/icons/full/radioparadise.png | Bin 0 -> 45447 bytes data/icons/full/somafm.png | Bin 0 -> 1801 bytes data/schema/schema-15.sql | 8 + data/schema/schema.sql | 9 +- src/CMakeLists.txt | 23 ++ src/context/contextalbumsmodel.cpp | 1 - src/core/application.cpp | 20 +- src/core/application.h | 5 +- src/core/database.cpp | 2 +- src/core/mainwindow.cpp | 17 +- src/core/mainwindow.h | 3 + src/core/metatypes.cpp | 4 + src/core/song.cpp | 79 +++- src/core/song.h | 4 + .../albumcoverchoicecontroller.cpp | 2 + src/covermanager/albumcoverloader.cpp | 2 + src/internet/internetservices.cpp | 2 +- src/internet/internetservices.h | 2 +- src/playlist/playlist.cpp | 25 +- src/playlist/playlist.h | 5 +- src/playlist/playlistitem.cpp | 4 + src/radios/radiobackend.cpp | 135 +++++++ src/radios/radiobackend.h | 61 ++++ src/radios/radiochannel.cpp | 36 ++ src/radios/radiochannel.h | 43 +++ src/radios/radioitem.h | 49 +++ src/radios/radiomimedata.h | 36 ++ src/radios/radiomodel.cpp | 344 ++++++++++++++++++ src/radios/radiomodel.h | 102 ++++++ src/radios/radioparadiseservice.cpp | 45 +++ src/radios/radioparadiseservice.h | 45 +++ src/radios/radioplaylistitem.cpp | 77 ++++ src/radios/radioplaylistitem.h | 62 ++++ src/radios/radioservice.cpp | 106 ++++++ src/radios/radioservice.h | 118 ++++++ src/radios/radioservices.cpp | 138 +++++++ src/radios/radioservices.h | 74 ++++ src/radios/radioview.cpp | 192 ++++++++++ src/radios/radioview.h | 68 ++++ src/radios/radioviewcontainer.cpp | 54 +++ src/radios/radioviewcontainer.h | 47 +++ src/radios/radioviewcontainer.ui | 110 ++++++ src/radios/somafmservice.cpp | 166 +++++++++ src/radios/somafmservice.h | 61 ++++ src/settings/qobuzsettingspage.cpp | 2 - src/settings/subsonicsettingspage.cpp | 2 - src/settings/tidalsettingspage.cpp | 2 - 65 files changed, 2363 insertions(+), 45 deletions(-) create mode 100644 data/icons/128x128/radio.png create mode 100644 data/icons/128x128/radioparadise.png create mode 100644 data/icons/128x128/somafm.png create mode 100644 data/icons/22x22/radio.png create mode 100644 data/icons/22x22/radioparadise.png create mode 100644 data/icons/22x22/somafm.png create mode 100644 data/icons/32x32/radio.png create mode 100644 data/icons/32x32/radioparadise.png create mode 100644 data/icons/32x32/somafm.png create mode 100644 data/icons/48x48/radio.png create mode 100644 data/icons/48x48/radioparadise.png create mode 100644 data/icons/48x48/somafm.png create mode 100644 data/icons/64x64/radio.png create mode 100644 data/icons/64x64/radioparadise.png create mode 100644 data/icons/64x64/somafm.png create mode 100644 data/icons/full/radio.png create mode 100644 data/icons/full/radioparadise.png create mode 100644 data/icons/full/somafm.png create mode 100644 data/schema/schema-15.sql create mode 100644 src/radios/radiobackend.cpp create mode 100644 src/radios/radiobackend.h create mode 100644 src/radios/radiochannel.cpp create mode 100644 src/radios/radiochannel.h create mode 100644 src/radios/radioitem.h create mode 100644 src/radios/radiomimedata.h create mode 100644 src/radios/radiomodel.cpp create mode 100644 src/radios/radiomodel.h create mode 100644 src/radios/radioparadiseservice.cpp create mode 100644 src/radios/radioparadiseservice.h create mode 100644 src/radios/radioplaylistitem.cpp create mode 100644 src/radios/radioplaylistitem.h create mode 100644 src/radios/radioservice.cpp create mode 100644 src/radios/radioservice.h create mode 100644 src/radios/radioservices.cpp create mode 100644 src/radios/radioservices.h create mode 100644 src/radios/radioview.cpp create mode 100644 src/radios/radioview.h create mode 100644 src/radios/radioviewcontainer.cpp create mode 100644 src/radios/radioviewcontainer.h create mode 100644 src/radios/radioviewcontainer.ui create mode 100644 src/radios/somafmservice.cpp create mode 100644 src/radios/somafmservice.h diff --git a/data/data.qrc b/data/data.qrc index 3027a9f3c..233d07bfc 100644 --- a/data/data.qrc +++ b/data/data.qrc @@ -15,6 +15,7 @@ schema/schema-12.sql schema/schema-13.sql schema/schema-14.sql + schema/schema-15.sql schema/device-schema.sql style/strawberry.css style/smartplaylistsearchterm.css diff --git a/data/icons.qrc b/data/icons.qrc index 0b7b484a5..f5c4dfb6d 100644 --- a/data/icons.qrc +++ b/data/icons.qrc @@ -92,6 +92,9 @@ icons/128x128/tidal.png icons/128x128/qobuz.png icons/128x128/multimedia-player-ipod-standard-black.png + icons/128x128/radio.png + icons/128x128/somafm.png + icons/128x128/radioparadise.png icons/64x64/albums.png icons/64x64/alsa.png icons/64x64/application-exit.png @@ -185,6 +188,9 @@ icons/64x64/tidal.png icons/64x64/qobuz.png icons/64x64/multimedia-player-ipod-standard-black.png + icons/64x64/radio.png + icons/64x64/somafm.png + icons/64x64/radioparadise.png icons/48x48/albums.png icons/48x48/alsa.png icons/48x48/application-exit.png @@ -282,6 +288,9 @@ icons/48x48/tidal.png icons/48x48/qobuz.png icons/48x48/multimedia-player-ipod-standard-black.png + icons/48x48/radio.png + icons/48x48/somafm.png + icons/48x48/radioparadise.png icons/32x32/albums.png icons/32x32/alsa.png icons/32x32/application-exit.png @@ -379,6 +388,9 @@ icons/32x32/tidal.png icons/32x32/qobuz.png icons/32x32/multimedia-player-ipod-standard-black.png + icons/32x32/radio.png + icons/32x32/somafm.png + icons/32x32/radioparadise.png icons/22x22/albums.png icons/22x22/alsa.png icons/22x22/application-exit.png @@ -476,5 +488,8 @@ icons/22x22/tidal.png icons/22x22/qobuz.png icons/22x22/multimedia-player-ipod-standard-black.png + icons/22x22/radio.png + icons/22x22/somafm.png + icons/22x22/radioparadise.png diff --git a/data/icons/128x128/radio.png b/data/icons/128x128/radio.png new file mode 100644 index 0000000000000000000000000000000000000000..eaa960e3c057e6e40f66ad7041237466a76b2003 GIT binary patch literal 34041 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 literal 0 HcmV?d00001 diff --git a/data/icons/128x128/radioparadise.png b/data/icons/128x128/radioparadise.png new file mode 100644 index 0000000000000000000000000000000000000000..6daac41222c2b735ba480adadf9a78ac1be5082f GIT binary patch literal 9016 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 NKv`Zxu1W?T{y&~W;#~j$ literal 0 HcmV?d00001 diff --git a/data/icons/128x128/somafm.png b/data/icons/128x128/somafm.png new file mode 100644 index 0000000000000000000000000000000000000000..41aad993880ce7f03cef3f812187e4aa5868bcb3 GIT binary patch literal 1801 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 Z;SX65*$6&A5zGJp002ovPDHLkV1iVuB0&HE literal 0 HcmV?d00001 diff --git a/data/icons/32x32/radio.png b/data/icons/32x32/radio.png new file mode 100644 index 0000000000000000000000000000000000000000..8c9a0f27e844eb93b83363f01b3e6d09c2e52e18 GIT binary patch literal 2116 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 IM6N<$f?lMEF#rGn literal 0 HcmV?d00001 diff --git a/data/icons/32x32/somafm.png b/data/icons/32x32/somafm.png new file mode 100644 index 0000000000000000000000000000000000000000..82d1ac8b7abb9bef2cf938007651e8715a5938cb GIT binary patch literal 626 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 literal 0 HcmV?d00001 diff --git a/data/icons/48x48/radioparadise.png b/data/icons/48x48/radioparadise.png new file mode 100644 index 0000000000000000000000000000000000000000..e23cd4e3d82061f80a55f48f13bfc8600cd23d72 GIT binary patch literal 3004 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 literal 0 HcmV?d00001 diff --git a/data/icons/64x64/radioparadise.png b/data/icons/64x64/radioparadise.png new file mode 100644 index 0000000000000000000000000000000000000000..901e0d92b2ea3fb4ed70a16b936bffcd3f7a1b57 GIT binary patch literal 3782 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 w8vnl|RQSO5S3 literal 0 HcmV?d00001 diff --git a/data/icons/64x64/somafm.png b/data/icons/64x64/somafm.png new file mode 100644 index 0000000000000000000000000000000000000000..9de62ab0ac90cd95909e48215a3a304fd150f4e9 GIT binary patch literal 996 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 literal 0 HcmV?d00001 diff --git a/data/icons/full/radioparadise.png b/data/icons/full/radioparadise.png new file mode 100644 index 0000000000000000000000000000000000000000..10f8a1415aa0433c83b70df66dd15917f57327ea GIT binary patch literal 45447 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 Q!ul-EomC|KbhG3LI3~& literal 0 HcmV?d00001 diff --git a/data/icons/full/somafm.png b/data/icons/full/somafm.png new file mode 100644 index 0000000000000000000000000000000000000000..41aad993880ce7f03cef3f812187e4aa5868bcb3 GIT binary patch literal 1801 zcmZ`)d0f&77XN`j1c+c{UTKke<(4a!WUZp&H6EFY<0S4#B`QrGnMxibqL^YS zb_gD5X-H%CbHS9H!EJb~Lfd?2LB%Di#3H{htMtcL=Qq0I*JXvV%W!>TRjVH5>(( zx$Q>@o(K;1f}84LddE@Z+?_vWghyAxvR!N#c#S;_a?G6L{!CZ=p?S3|-2@k?yLIzE zb2m29Fx$df*FV`v?ZEj-l2PGO(Mkhv#qO}a@h4(n#DQgr`jOGnHmuWc?KRn|J`e*)&p&uU zy&~;NyHExRaCIUd<14s~7H)j!2xZi=J^=Y!;$8<#g>P-0rPtaOpz{_&39+&N`r-6N ze?Gr!=-vjEbgiJjqdgLxbI z;Qr0miz#bI!qVXJV5a-gSQ7_1B#_V*;+G4>2Uc^BnD(@M^*5+Z>ieWk$2>4K=c8Yn zY*dycE&)E^>_FdOStkG8%g**i*^>t0?=}zUrvXP$$5Tlmq4U$HH1m>z7tS}EP%IM? zVr!f+M;CKb{(?#edmg;bp?w1qqMa%NYk-J%T=+|hbBjk~Dv>K_ebHZq)+~uU_G>6k zR0%9yx(C-%o8)s}b;hW*=GF^uJU^+C(}))>(RT%<7;bbYXh7YH^~eGbNO`TykMhtY zZu{bv9%Ry*m~}PBc_wT+FuVw02?LM=y_j+v-Edu)d8-%bSsl;y9KfVP9)Tz8xyW0W zyYb+$HR+}u`j{?pvaQ)HpQf!msqwui%9dc#!{jNNe!+?ifYn;rRrijtK1-ike+&dv zWvaU*p%ob;IAHyOq1W;-U)Hbw6JPf25{dD|yWDjO*7KRWkA>E03pxLeuN;yUL`j&z z0P8swJ9>(F#J=soSYHVG%ZbJ$1FbVdQLDz-E|64>lKkYKqe7sXuNY~_x$4XOb_&l7 zfIQ;MN)OXvxR4UcZ!2fi?Om{~x5f8sFqJR&vmf^rilSyV-^hV{o+9z__0>L1ss{RO zQv0>$dN#IAVKY_Z|wG)?2l2FEPI>U(&;w|AeYF=W*0=ilLKqlh;e-4 z%fUUC5$9oswK7ZS;D-aExZn(0mN0Q|F%0HAf-1v`E{+=!2H!px;lTqjE(kImHtxyt zD7ZT$39&`?NLXM?LIof}nF0hg#Gkt)m{oc)C>}W$L|0yTkiFL|6!u1|tWP3+A5B-X zmt~@}*zehpKSK3$66pfYN=;QAL01NA%Nqaf81Bd)LxC-q2+XI$s4|N@!ME%}P$y^L zHG`1eaWp;bGF|EL_x-U4xPyp zwH|5L+P5O9i1eSpvwdqb94lFhe_&cS?Sh`%9bwX8aeSUZsg4qR(`v18yyf{J)sPW; zsPjZnM=(WKcEawgmGy16Bi`=(76iF5>+~# zkZvppIX5C5(ks!qtuz{(N0oJnjBfrP_549bEFKImi>`fll?Tg)-k)IH6E9rS$S0)! z#Axx$h7uE2X1)s*|8Sb6|LoPPRHG&!B_(BZain-XYq=w*z2@Vm_vOc9<@4Up7&F;TN`AH4WO3o?vpXJ!r6&?r)Ebr# zpQRN%W;X`#OBJiD_EXc-NrDN6x}@N*^J8ggATJ{Tz#%w}IA;&g?Y5uVD^`f!Q5XnK z=GrsJ!B_EYzy(Jwz$XajbB?iWSvi?BYWSn+;e-Eq9n_&z tag_reader_client_; @@ -199,13 +200,13 @@ class ApplicationImpl { Lazy current_albumcover_loader_; Lazy lyrics_providers_; Lazy internet_services_; + Lazy radio_services_; Lazy scrobbler_; - Lazy lastfm_import_; #ifdef HAVE_MOODBAR Lazy moodbar_loader_; Lazy moodbar_controller_; #endif - Lazy dummy_; + Lazy lastfm_import_; }; @@ -265,7 +266,8 @@ void Application::Exit() { #ifndef Q_OS_WIN << device_manager() #endif - << internet_services(); + << internet_services() + << radio_services()->radio_backend(); QObject::connect(tag_reader_client(), &TagReaderClient::ExitFinished, this, &Application::ExitReceived); tag_reader_client()->ExitAsync(); @@ -287,6 +289,9 @@ void Application::Exit() { QObject::connect(internet_services(), &InternetServices::ExitFinished, this, &Application::ExitReceived); internet_services()->Exit(); + QObject::connect(radio_services()->radio_backend(), &RadioBackend::ExitFinished, this, &Application::ExitReceived); + radio_services()->radio_backend()->ExitAsync(); + } void Application::ExitReceived() { @@ -328,6 +333,7 @@ LyricsProviders *Application::lyrics_providers() const { return p_->lyrics_provi PlaylistBackend *Application::playlist_backend() const { return p_->playlist_backend_.get(); } PlaylistManager *Application::playlist_manager() const { return p_->playlist_manager_.get(); } InternetServices *Application::internet_services() const { return p_->internet_services_.get(); } +RadioServices *Application::radio_services() const { return p_->radio_services_.get(); } AudioScrobbler *Application::scrobbler() const { return p_->scrobbler_.get(); } LastFMImport *Application::lastfm_import() const { return p_->lastfm_import_.get(); } #ifdef HAVE_MOODBAR diff --git a/src/core/application.h b/src/core/application.h index 139b3028f..ffabfa2ed 100644 --- a/src/core/application.h +++ b/src/core/application.h @@ -58,6 +58,7 @@ class LyricsProviders; class AudioScrobbler; class LastFMImport; class InternetServices; +class RadioServices; #ifdef HAVE_MOODBAR class MoodbarController; class MoodbarLoader; @@ -94,15 +95,17 @@ class Application : public QObject { LyricsProviders *lyrics_providers() const; AudioScrobbler *scrobbler() const; - LastFMImport *lastfm_import() const; InternetServices *internet_services() const; + RadioServices *radio_services() const; #ifdef HAVE_MOODBAR MoodbarController *moodbar_controller() const; MoodbarLoader *moodbar_loader() const; #endif + LastFMImport *lastfm_import() const; + void Exit(); QThread *MoveToNewThread(QObject *object); diff --git a/src/core/database.cpp b/src/core/database.cpp index 36b4e46e8..f0c922f4f 100644 --- a/src/core/database.cpp +++ b/src/core/database.cpp @@ -54,7 +54,7 @@ #include "scopedtransaction.h" const char *Database::kDatabaseFilename = "strawberry.db"; -const int Database::kSchemaVersion = 14; +const int Database::kSchemaVersion = 15; const char *Database::kMagicAllSongsTables = "%allsongstables"; int Database::sNextConnectionId = 1; diff --git a/src/core/mainwindow.cpp b/src/core/mainwindow.cpp index da79a829f..6c6a5250e 100644 --- a/src/core/mainwindow.cpp +++ b/src/core/mainwindow.cpp @@ -180,6 +180,9 @@ #include "internet/internetcollectionview.h" #include "internet/internetsearchview.h" +#include "radios/radioservices.h" +#include "radios/radioviewcontainer.h" + #include "scrobbler/audioscrobbler.h" #include "scrobbler/lastfmimport.h" @@ -277,6 +280,7 @@ MainWindow::MainWindow(Application *app, std::shared_ptr tray_ic #ifdef HAVE_QOBUZ qobuz_view_(new InternetTabsView(app_, app->internet_services()->ServiceBySource(Song::Source_Qobuz), QobuzSettingsPage::kSettingsGroup, SettingsDialog::Page_Qobuz, this)), #endif + radio_view_(new RadioViewContainer(this)), lastfm_import_dialog_(new LastFMImportDialog(app_->lastfm_import(), this)), collection_show_all_(nullptr), collection_show_duplicates_(nullptr), @@ -316,8 +320,7 @@ MainWindow::MainWindow(Application *app, std::shared_ptr tray_ic hidden_(false), exit_(false), exit_count_(0), - delete_files_(false) - { + delete_files_(false) { qLog(Debug) << "Starting"; @@ -343,6 +346,7 @@ MainWindow::MainWindow(Application *app, std::shared_ptr tray_ic 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")); + ui_->tabs->AddTab(radio_view_, "radios", IconLoader::Load("radio"), tr("Radios")); #ifndef Q_OS_WIN ui_->tabs->AddTab(device_view_, "devices", IconLoader::Load("device"), tr("Devices")); #endif @@ -401,6 +405,8 @@ MainWindow::MainWindow(Application *app, std::shared_ptr tray_ic organize_dialog_->SetDestinationModel(app_->collection()->model()->directory_model()); + radio_view_->view()->setModel(app_->radio_services()->radio_model()); + // Icons qLog(Debug) << "Creating UI"; @@ -675,6 +681,10 @@ MainWindow::MainWindow(Application *app, std::shared_ptr tray_ic QObject::connect(qobuz_view_->search_view(), &InternetSearchView::AddToPlaylist, this, &MainWindow::AddToPlaylist); #endif + QObject::connect(radio_view_, &RadioViewContainer::Refresh, app_->radio_services(), &RadioServices::RefreshChannels); + QObject::connect(radio_view_->view(), &RadioView::GetChannels, app_->radio_services(), &RadioServices::GetChannels); + QObject::connect(radio_view_->view(), &RadioView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist); + // Playlist menu QObject::connect(playlist_menu_, &QMenu::aboutToHide, this, &MainWindow::PlaylistMenuHidden); playlist_play_pause_ = playlist_menu_->addAction(tr("Play"), this, &MainWindow::PlaylistPlay); @@ -1126,6 +1136,9 @@ void MainWindow::ReloadAllSettings() { queue_view_->ReloadSettings(); playlist_list_->ReloadSettings(); smartplaylists_view_->ReloadSettings(); + radio_view_->ReloadSettings(); + app_->internet_services()->ReloadSettings(); + app_->radio_services()->ReloadSettings(); app_->cover_providers()->ReloadSettings(); app_->lyrics_providers()->ReloadSettings(); #ifdef HAVE_MOODBAR diff --git a/src/core/mainwindow.h b/src/core/mainwindow.h index af4db2fcc..a4df4f662 100644 --- a/src/core/mainwindow.h +++ b/src/core/mainwindow.h @@ -100,6 +100,7 @@ class Windows7ThumbBar; #endif class AddStreamDialog; class LastFMImportDialog; +class RadioViewContainer; class MainWindow : public QMainWindow, public PlatformInterface { Q_OBJECT @@ -338,6 +339,8 @@ class MainWindow : public QMainWindow, public PlatformInterface { InternetTabsView *tidal_view_; InternetTabsView *qobuz_view_; + RadioViewContainer *radio_view_; + LastFMImportDialog *lastfm_import_dialog_; QAction *collection_show_all_; diff --git a/src/core/metatypes.cpp b/src/core/metatypes.cpp index 1d72435aa..3290c03ce 100644 --- a/src/core/metatypes.cpp +++ b/src/core/metatypes.cpp @@ -72,6 +72,8 @@ #include "smartplaylists/playlistgenerator_fwd.h" +#include "radios/radiochannel.h" + void RegisterMetaTypes() { qRegisterMetaType("const char*"); @@ -141,4 +143,6 @@ void RegisterMetaTypes() { qRegisterMetaType("PlaylistGeneratorPtr"); + qRegisterMetaType("RadioChannelList"); + } diff --git a/src/core/song.cpp b/src/core/song.cpp index 106646817..1012f56ac 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -375,7 +375,13 @@ double 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; } +bool Song::is_stream() const { return d->source_ == Source_Stream || + d->source_ == Source_Tidal || + d->source_ == Source_Subsonic || + d->source_ == Source_Qobuz || + d->source_ == Source_SomaFM || + d->source_ == Source_RadioParadise; +} bool Song::is_cdda() const { return d->source_ == Source_CDDA; } bool Song::is_compilation() const { return (d->compilation_ || d->compilation_detected_ || d->compilation_on_) && !d->compilation_off_; } bool Song::stream_url_can_expire() const { return d->source_ == Song::Source_Tidal || d->source_ == Song::Source_Qobuz; } @@ -490,7 +496,13 @@ Song::Source Song::SourceFromURL(const QUrl &url) { else if (url.scheme() == "tidal") return Source_Tidal; else if (url.scheme() == "subsonic") return Source_Subsonic; else if (url.scheme() == "qobuz") return Source_Qobuz; - else if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "rtsp") return Source_Stream; + else if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "rtsp") { + if (url.host().endsWith("tidal.com", Qt::CaseInsensitive)) { return Source_Tidal; } + if (url.host().endsWith("qobuz.com", Qt::CaseInsensitive)) { return Source_Qobuz; } + if (url.host().endsWith("somafm.com", Qt::CaseInsensitive)) { return Source_SomaFM; } + if (url.host().endsWith("radioparadise.com", Qt::CaseInsensitive)) { return Source_RadioParadise; } + return Source_Stream; + } else return Source_Unknown; } @@ -498,15 +510,36 @@ Song::Source Song::SourceFromURL(const QUrl &url) { QString Song::TextForSource(Source source) { switch (source) { - case Song::Source_LocalFile: return "file"; - case Song::Source_Collection: return "collection"; - case Song::Source_CDDA: return "cd"; - case Song::Source_Device: return "device"; - case Song::Source_Stream: return "stream"; - case Song::Source_Tidal: return "tidal"; - case Song::Source_Subsonic: return "subsonic"; - case Song::Source_Qobuz: return "qobuz"; - case Song::Source_Unknown: return "unknown"; + case Song::Source_LocalFile: return "file"; + case Song::Source_Collection: return "collection"; + case Song::Source_CDDA: return "cd"; + case Song::Source_Device: return "device"; + case Song::Source_Stream: return "stream"; + case Song::Source_Tidal: return "tidal"; + case Song::Source_Subsonic: return "subsonic"; + case Song::Source_Qobuz: return "qobuz"; + case Song::Source_SomaFM: return "somafm"; + case Song::Source_RadioParadise: return "radioparadise"; + case Song::Source_Unknown: return "unknown"; + } + return "unknown"; + +} + +QString Song::DescriptionForSource(Source source) { + + switch (source) { + case Song::Source_LocalFile: return "File"; + case Song::Source_Collection: return "Collection"; + case Song::Source_CDDA: return "CD"; + case Song::Source_Device: return "Device"; + case Song::Source_Stream: return "Stream"; + case Song::Source_Tidal: return "Tidal"; + case Song::Source_Subsonic: return "Subsonic"; + case Song::Source_Qobuz: return "Qobuz"; + case Song::Source_SomaFM: return "SomaFM"; + case Song::Source_RadioParadise: return "Radio Paradise"; + case Song::Source_Unknown: return "Unknown"; } return "unknown"; @@ -522,6 +555,8 @@ Song::Source Song::SourceFromText(const QString &source) { if (source == "tidal") return Source_Tidal; if (source == "subsonic") return Source_Subsonic; if (source == "qobuz") return Source_Qobuz; + if (source == "somafm") return Source_SomaFM; + if (source == "radioparadise") return Source_RadioParadise; return Source_Unknown; @@ -530,15 +565,17 @@ Song::Source Song::SourceFromText(const QString &source) { QIcon Song::IconForSource(Source source) { switch (source) { - case Song::Source_LocalFile: return IconLoader::Load("folder-sound"); - case Song::Source_Collection: return IconLoader::Load("library-music"); - case Song::Source_CDDA: return IconLoader::Load("media-optical"); - case Song::Source_Device: return IconLoader::Load("device"); - case Song::Source_Stream: return IconLoader::Load("applications-internet"); - case Song::Source_Tidal: return IconLoader::Load("tidal"); - case Song::Source_Subsonic: return IconLoader::Load("subsonic"); - case Song::Source_Qobuz: return IconLoader::Load("qobuz"); - case Song::Source_Unknown: return IconLoader::Load("edit-delete"); + case Song::Source_LocalFile: return IconLoader::Load("folder-sound"); + case Song::Source_Collection: return IconLoader::Load("library-music"); + case Song::Source_CDDA: return IconLoader::Load("media-optical"); + case Song::Source_Device: return IconLoader::Load("device"); + case Song::Source_Stream: return IconLoader::Load("applications-internet"); + case Song::Source_Tidal: return IconLoader::Load("tidal"); + case Song::Source_Subsonic: return IconLoader::Load("subsonic"); + case Song::Source_Qobuz: return IconLoader::Load("qobuz"); + case Song::Source_SomaFM: return IconLoader::Load("somafm"); + case Song::Source_RadioParadise: return IconLoader::Load("radioparadise"); + case Song::Source_Unknown: return IconLoader::Load("edit-delete"); } return IconLoader::Load("edit-delete"); @@ -721,6 +758,8 @@ QString Song::ImageCacheDir(const Song::Source source) { case Song::Source_LocalFile: case Song::Source_CDDA: case Song::Source_Stream: + case Song::Source_SomaFM: + case Song::Source_RadioParadise: case Song::Source_Unknown: return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/albumcovers"; } diff --git a/src/core/song.h b/src/core/song.h index 78a27b9ae..86c6e00b7 100644 --- a/src/core/song.h +++ b/src/core/song.h @@ -75,6 +75,8 @@ class Song { Source_Tidal = 6, Source_Subsonic = 7, Source_Qobuz = 8, + Source_SomaFM = 9, + Source_RadioParadise = 10 }; // Don't change these values - they're stored in the database, and defined in the tag reader protobuf. @@ -134,6 +136,7 @@ class Song { static Source SourceFromURL(const QUrl &url); static QString TextForSource(Source source); + static QString DescriptionForSource(Source source); static Song::Source SourceFromText(const QString &source); static QIcon IconForSource(Source source); static QString TextForFiletype(FileType filetype); @@ -141,6 +144,7 @@ class Song { static QIcon IconForFiletype(FileType filetype); QString TextForSource() const { return TextForSource(source()); } + QString DescriptionForSource() const { return DescriptionForSource(source()); } QIcon IconForSource() const { return IconForSource(source()); } QString TextForFiletype() const { return TextForFiletype(filetype()); } QIcon IconForFiletype() const { return IconForFiletype(filetype()); } diff --git a/src/covermanager/albumcoverchoicecontroller.cpp b/src/covermanager/albumcoverchoicecontroller.cpp index d4875a2ef..7d894e7aa 100644 --- a/src/covermanager/albumcoverchoicecontroller.cpp +++ b/src/covermanager/albumcoverchoicecontroller.cpp @@ -534,6 +534,8 @@ void AlbumCoverChoiceController::SaveArtManualToSong(Song *song, const QUrl &art case Song::Source_CDDA: case Song::Source_Device: case Song::Source_Stream: + case Song::Source_RadioParadise: + case Song::Source_SomaFM: case Song::Source_Unknown: break; case Song::Source_Tidal: diff --git a/src/covermanager/albumcoverloader.cpp b/src/covermanager/albumcoverloader.cpp index 22957e8f8..0ff014d4a 100644 --- a/src/covermanager/albumcoverloader.cpp +++ b/src/covermanager/albumcoverloader.cpp @@ -200,6 +200,8 @@ QString AlbumCoverLoader::CoverFilenameFromSource(const Song::Source source, con case Song::Source_CDDA: case Song::Source_Device: case Song::Source_Stream: + case Song::Source_SomaFM: + case Song::Source_RadioParadise: case Song::Source_Unknown: filename = Utilities::Sha1CoverHash(artist, album).toHex(); break; diff --git a/src/internet/internetservices.cpp b/src/internet/internetservices.cpp index e873715be..a8de20dcc 100644 --- a/src/internet/internetservices.cpp +++ b/src/internet/internetservices.cpp @@ -60,7 +60,7 @@ void InternetServices::RemoveService(InternetService *service) { } -InternetService *InternetServices::ServiceBySource(const Song::Source source) { +InternetService *InternetServices::ServiceBySource(const Song::Source source) const { if (services_.contains(source)) return services_.value(source); return nullptr; diff --git a/src/internet/internetservices.h b/src/internet/internetservices.h index 75035d723..53029c756 100644 --- a/src/internet/internetservices.h +++ b/src/internet/internetservices.h @@ -40,7 +40,7 @@ class InternetServices : public QObject { explicit InternetServices(QObject *parent = nullptr); ~InternetServices() override; - InternetService *ServiceBySource(const Song::Source source); + InternetService *ServiceBySource(const Song::Source source) const; template T *Service() { return static_cast(this->ServiceBySource(T::kSource)); diff --git a/src/playlist/playlist.cpp b/src/playlist/playlist.cpp index fed92c4b9..495571741 100644 --- a/src/playlist/playlist.cpp +++ b/src/playlist/playlist.cpp @@ -93,6 +93,10 @@ #include "internet/internetplaylistitem.h" #include "internet/internetsongmimedata.h" +#include "radios/radioservice.h" +#include "radios/radiomimedata.h" +#include "radios/radioplaylistitem.h" + using namespace std::chrono_literals; const char *Playlist::kCddaMimeType = "x-content/audio-cdda"; @@ -787,11 +791,14 @@ bool Playlist::dropMimeData(const QMimeData *data, Qt::DropAction action, int ro else if (const PlaylistItemMimeData *item_data = qobject_cast(data)) { InsertItems(item_data->items_, row, play_now, enqueue_now, enqueue_next_now); } + else if (const PlaylistGeneratorMimeData *generator_data = qobject_cast(data)) { + InsertSmartPlaylist(generator_data->generator_, row, play_now, enqueue_now, enqueue_next_now); + } else if (const InternetSongMimeData *internet_song_data = qobject_cast(data)) { InsertInternetItems(internet_song_data->service, internet_song_data->songs, row, play_now, enqueue_now, enqueue_next_now); } - else if (const PlaylistGeneratorMimeData *generator_data = qobject_cast(data)) { - InsertSmartPlaylist(generator_data->generator_, row, play_now, enqueue_now, enqueue_next_now); + else if (const RadioMimeData *radio_data = qobject_cast(data)) { + InsertRadioItems(radio_data->songs, row, play_now, enqueue_now, enqueue_next_now); } else if (data->hasFormat(kRowsMimetype)) { // Dragged from the playlist @@ -1156,6 +1163,20 @@ void Playlist::InsertInternetItems(InternetService *service, const SongList &son } +void Playlist::InsertRadioItems(const SongList &songs, const int pos, const bool play_now, const bool enqueue, const bool enqueue_next) { + + PlaylistItemList playlist_items; + QList song_urls; + playlist_items.reserve(songs.count()); + for (const Song &song : songs) { + playlist_items << std::make_shared(song); + song_urls << song.url(); + } + + InsertItems(playlist_items, pos, play_now, enqueue, enqueue_next); + +} + void Playlist::UpdateItems(SongList songs) { qLog(Debug) << "Updating playlist with new tracks' info"; diff --git a/src/playlist/playlist.h b/src/playlist/playlist.h index 7c6b51d0b..81649ca54 100644 --- a/src/playlist/playlist.h +++ b/src/playlist/playlist.h @@ -58,8 +58,8 @@ class PlaylistBackend; class PlaylistFilter; class Queue; class TaskManager; -class InternetServices; class InternetService; +class RadioService; namespace PlaylistUndoCommands { class InsertItems; @@ -243,8 +243,9 @@ class Playlist : public QAbstractListModel { void InsertCollectionItems(const SongList &songs, const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool enqueue_next = false); void InsertSongs(const SongList &songs, const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool enqueue_next = false); void InsertSongsOrCollectionItems(const SongList &songs, const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool enqueue_next = false); - void InsertInternetItems(InternetService *service, const SongList &songs, const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool enqueue_next = false); void InsertSmartPlaylist(PlaylistGeneratorPtr gen, const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool enqueue_next = false); + void InsertInternetItems(InternetService *service, const SongList &songs, const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool enqueue_next = false); + void InsertRadioItems(const SongList &songs, const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool enqueue_next = false); void ReshuffleIndices(); diff --git a/src/playlist/playlistitem.cpp b/src/playlist/playlistitem.cpp index 706619d86..32fd127ee 100644 --- a/src/playlist/playlistitem.cpp +++ b/src/playlist/playlistitem.cpp @@ -46,6 +46,8 @@ PlaylistItem *PlaylistItem::NewFromSource(const Song::Source source) { case Song::Source_Subsonic: case Song::Source_Tidal: case Song::Source_Qobuz: + case Song::Source_RadioParadise: + case Song::Source_SomaFM: case Song::Source_Stream: return new InternetPlaylistItem(source); case Song::Source_LocalFile: @@ -67,6 +69,8 @@ PlaylistItem *PlaylistItem::NewFromSong(const Song &song) { case Song::Source_Subsonic: case Song::Source_Tidal: case Song::Source_Qobuz: + case Song::Source_RadioParadise: + case Song::Source_SomaFM: case Song::Source_Stream: return new InternetPlaylistItem(song); case Song::Source_LocalFile: diff --git a/src/radios/radiobackend.cpp b/src/radios/radiobackend.cpp new file mode 100644 index 000000000..165ff2386 --- /dev/null +++ b/src/radios/radiobackend.cpp @@ -0,0 +1,135 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#include +#include +#include + +#include "core/logging.h" +#include "core/application.h" +#include "core/database.h" +#include "core/song.h" +#include "radiobackend.h" +#include "radiochannel.h" + +RadioBackend::RadioBackend(Application *app, Database *db, QObject *parent) + : QObject(parent), + app_(app), + db_(db), + original_thread_(thread()) {} + +void RadioBackend::Close() { + + if (db_) { + QMutexLocker l(db_->Mutex()); + db_->Close(); + } + +} + +void RadioBackend::ExitAsync() { + metaObject()->invokeMethod(this, "Exit", Qt::QueuedConnection); +} + +void RadioBackend::Exit() { + + assert(QThread::currentThread() == thread()); + + moveToThread(original_thread_); + emit ExitFinished(); + +} + +void RadioBackend::AddChannelsAsync(const RadioChannelList &channels) { + metaObject()->invokeMethod(this, "AddChannels", Qt::QueuedConnection, Q_ARG(RadioChannelList, channels)); +} + +void RadioBackend::AddChannels(const RadioChannelList &channels) { + + QMutexLocker l(db_->Mutex()); + QSqlDatabase db(db_->Connect()); + + QSqlQuery q(db); + q.prepare(QString("INSERT INTO radio_channels (source, name, url, thumbnail_url) VALUES (:source, :name, :url, :thumbnail_url)")); + + RadioChannelList streams; + for (const RadioChannel &channel : channels) { + q.bindValue(":source", channel.source); + q.bindValue(":name", channel.name); + q.bindValue(":url", channel.url); + q.bindValue(":thumbnail_url", channel.thumbnail_url); + if (!q.exec()) { + db_->CheckErrors(q); + } + } + + emit NewChannels(channels); + +} + +void RadioBackend::GetChannelsAsync() { + + metaObject()->invokeMethod(this, "GetChannels", Qt::QueuedConnection); + +} + +void RadioBackend::GetChannels() { + + QMutexLocker l(db_->Mutex()); + QSqlDatabase db(db_->Connect()); + + QSqlQuery q(db); + q.prepare("SELECT source, name, url, thumbnail_url FROM radio_channels"); + + if (!q.exec()) { + db_->CheckErrors(q); + return; + } + + RadioChannelList channels; + while (q.next()) { + RadioChannel channel; + channel.source = Song::Source(q.value(0).toInt()); + channel.name = q.value(1).toString(); + channel.url.setUrl(q.value(2).toString()); + channel.thumbnail_url.setUrl(q.value(3).toString()); + channels << channel; + } + + emit NewChannels(channels); + +} + +void RadioBackend::DeleteChannelsAsync() { + metaObject()->invokeMethod(this, "DeleteChannels", Qt::QueuedConnection); +} + +void RadioBackend::DeleteChannels() { + + QMutexLocker l(db_->Mutex()); + QSqlDatabase db(db_->Connect()); + + QSqlQuery q(db); + q.prepare("DELETE FROM radio_channels"); + + if (!q.exec()) { + db_->CheckErrors(q); + } + +} diff --git a/src/radios/radiobackend.h b/src/radios/radiobackend.h new file mode 100644 index 000000000..ccc3465eb --- /dev/null +++ b/src/radios/radiobackend.h @@ -0,0 +1,61 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#ifndef RADIOBACKEND_H +#define RADIOBACKEND_H + +#include + +#include "radiochannel.h" + +class Application; +class Database; + +class RadioBackend : public QObject { + Q_OBJECT + + public: + explicit RadioBackend(Application *app, Database *db, QObject *parent = nullptr); + + void Close(); + void ExitAsync(); + + void AddChannelsAsync(const RadioChannelList &channels); + void GetChannelsAsync(); + void DeleteChannelsAsync(); + + private slots: + void AddChannels(const RadioChannelList &channels); + void GetChannels(); + void DeleteChannels(); + + signals: + void NewChannels(RadioChannelList); + void ExitFinished(); + + private slots: + void Exit(); + + private: + Application *app_; + Database *db_; + QThread *original_thread_; +}; + +#endif // RADIOBACKEND_H diff --git a/src/radios/radiochannel.cpp b/src/radios/radiochannel.cpp new file mode 100644 index 000000000..41be309c1 --- /dev/null +++ b/src/radios/radiochannel.cpp @@ -0,0 +1,36 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#include + +#include "core/song.h" + +#include "radiochannel.h" + +Song RadioChannel::ToSong() const { + + Song song(source); + song.set_valid(true); + song.set_filetype(Song::FileType_Stream); + song.set_title(Song::DescriptionForSource(source) + " " + name); + song.set_url(url); + + return song; + +} diff --git a/src/radios/radiochannel.h b/src/radios/radiochannel.h new file mode 100644 index 000000000..734df92d3 --- /dev/null +++ b/src/radios/radiochannel.h @@ -0,0 +1,43 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#ifndef RADIOCHANNEL_H +#define RADIOCHANNEL_H + +#include +#include +#include + +#include "core/song.h" + +struct RadioChannel { + explicit RadioChannel(const Song::Source _source = Song::Source_Unknown, const QString &_name = QString(), const QUrl &_url = QUrl(), const QUrl &_thumbnail_url = QUrl()) : source(_source), name(_name), url(_url), thumbnail_url(_thumbnail_url) {} + + Song::Source source; + QString name; + QUrl url; + QUrl thumbnail_url; + + Song ToSong() const; +}; +typedef QList RadioChannelList; + +Q_DECLARE_METATYPE(RadioChannel) + +#endif // RADIOCHANNEL_H diff --git a/src/radios/radioitem.h b/src/radios/radioitem.h new file mode 100644 index 000000000..0f9e1d303 --- /dev/null +++ b/src/radios/radioitem.h @@ -0,0 +1,49 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#ifndef RADIOITEM_H +#define RADIOITEM_H + +#include "config.h" + +#include "core/simpletreeitem.h" +#include "core/song.h" +#include "radiochannel.h" + +class RadioItem : public SimpleTreeItem { + public: + + enum Type { + Type_LoadingIndicator, + Type_Root, + Type_Service, + Type_Channel, + }; + + explicit RadioItem(SimpleTreeModel *_model) : SimpleTreeItem(Type_Root, _model) {} + explicit RadioItem(Type _type, RadioItem *_parent = nullptr) : SimpleTreeItem(_type, _parent) {} + + Song::Source source; + RadioChannel channel; + + private: + Q_DISABLE_COPY(RadioItem) +}; + +#endif // RADIOITEM_H diff --git a/src/radios/radiomimedata.h b/src/radios/radiomimedata.h new file mode 100644 index 000000000..a7ba928de --- /dev/null +++ b/src/radios/radiomimedata.h @@ -0,0 +1,36 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#ifndef RADIOMIMEDATA_H +#define RADIOMIMEDATA_H + +#include + +#include "core/mimedata.h" +#include "core/song.h" + +class RadioMimeData : public MimeData { + Q_OBJECT + + public: + explicit RadioMimeData() {} + SongList songs; +}; + +#endif // RADIOMIMEDATA_H diff --git a/src/radios/radiomodel.cpp b/src/radios/radiomodel.cpp new file mode 100644 index 000000000..7aaa93367 --- /dev/null +++ b/src/radios/radiomodel.cpp @@ -0,0 +1,344 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/application.h" +#include "core/simpletreemodel.h" +#include "playlist/playlistmanager.h" +#include "covermanager/albumcoverloader.h" +#include "covermanager/albumcoverloaderoptions.h" +#include "covermanager/albumcoverloaderresult.h" +#include "radiomodel.h" +#include "radioservices.h" +#include "radioservice.h" +#include "radiomimedata.h" +#include "radiochannel.h" + +const int RadioModel::kTreeIconSize = 22; + +RadioModel::RadioModel(Application *app, QObject *parent) + : SimpleTreeModel(new RadioItem(this), parent), + app_(app) { + + root_->lazy_loaded = true; + + cover_loader_options_.get_image_data_ = false; + cover_loader_options_.get_image_ = true; + cover_loader_options_.scale_output_image_ = true; + cover_loader_options_.pad_output_image_ = true; + cover_loader_options_.desired_height_ = kTreeIconSize; + + if (app_) { + QObject::connect(app_->album_cover_loader(), &AlbumCoverLoader::AlbumCoverLoaded, this, &RadioModel::AlbumCoverLoaded); + } + +} + +Qt::ItemFlags RadioModel::flags(const QModelIndex &idx) const { + + switch (IndexToItem(idx)->type) { + case RadioItem::Type_Service: + case RadioItem::Type_Channel: + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled; + case RadioItem::Type_Root: + case RadioItem::Type_LoadingIndicator: + default: + return Qt::ItemIsEnabled; + } + +} + +QVariant RadioModel::data(const QModelIndex &idx, int role) const { + + if (!idx.isValid()) return QVariant(); + + const RadioItem *item = IndexToItem(idx); + if (!item) return QVariant(); + + if (role == Qt::DecorationRole && item->type == RadioItem::Type_Channel) { + return const_cast(this)->ChannelIcon(idx); + } + + return data(item, role); + +} + +QVariant RadioModel::data(const RadioItem *item, int role) const { + + switch(role) { + case Qt::DecorationRole: + if (item->type == RadioItem::Type_Service) { + return Song::IconForSource(item->source); + } + break; + case Qt::DisplayRole: + return item->DisplayText(); + break; + case Role_Type: + return item->type; + break; + case Role_SortText: + return item->SortText(); + break; + case Role_Source: + return item->source; + break; + case Role_Homepage:{ + RadioService *service = app_->radio_services()->ServiceBySource(item->source); + if (service) return service->Homepage(); + break; + } + case Role_Donate:{ + RadioService *service = app_->radio_services()->ServiceBySource(item->source); + if (service) return service->Donate(); + break; + } + } + + return QVariant(); + +} + +QStringList RadioModel::mimeTypes() const { + return QStringList() << "text/uri-list"; +} + +QMimeData *RadioModel::mimeData(const QModelIndexList &indexes) const { + + if (indexes.isEmpty()) return nullptr; + + RadioMimeData *data = new RadioMimeData; + QList urls; + for (const QModelIndex &idx : indexes) { + GetChildSongs(IndexToItem(idx), &urls, &data->songs); + } + + data->setUrls(urls); + data->name_for_new_playlist_ = PlaylistManager::GetNameForNewPlaylist(data->songs); + + return data; + +} + +void RadioModel::Reset() { + + beginResetModel(); + container_nodes_.clear(); + items_.clear(); + pixmap_cache_.clear(); + pending_art_.clear(); + pending_cache_keys_.clear(); + delete root_; + root_ = new RadioItem(this); + root_->lazy_loaded = true; + endResetModel(); + +} + +void RadioModel::AddChannels(const RadioChannelList &channels) { + + for (const RadioChannel &channel : channels) { + RadioItem *container = root_; + if (container_nodes_.contains(channel.source)) { + container = container_nodes_[channel.source]; + } + else { + RadioItem *item = new RadioItem(RadioItem::Type_Service, root_); + item->source = channel.source; + item->display_text = Song::DescriptionForSource(channel.source); + item->sort_text = SortText(Song::TextForSource(channel.source)); + item->lazy_loaded = true; + beginInsertRows(ItemToIndex(root_), root_->children.count(), root_->children.count()); + container_nodes_.insert(channel.source, item); + endInsertRows(); + container = item; + } + RadioItem *item = new RadioItem(RadioItem::Type_Channel, container); + item->source = channel.source; + item->display_text = channel.name; + item->sort_text = SortText(Song::TextForSource(channel.source) + " - " + channel.name); + item->channel = channel; + item->lazy_loaded = true; + beginInsertRows(ItemToIndex(container), container->children.count(), container->children.count()); + items_ << item; + endInsertRows(); + } + +} + +bool RadioModel::IsPlayable(const QModelIndex &idx) const { + + return idx.data(Role_Type) == RadioItem::Type_Channel; + +} + +bool RadioModel::CompareItems(const RadioItem *a, const RadioItem *b) const { + + QVariant left(data(a, RadioModel::Role_SortText)); + QVariant right(data(b, RadioModel::Role_SortText)); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + if (left.metaType().id() == QMetaType::Int) +#else + if (left.type() == QVariant::Int) +#endif + return left.toInt() < right.toInt(); + else return left.toString() < right.toString(); + +} + +void RadioModel::GetChildSongs(RadioItem *item, QList *urls, SongList *songs) const { + + switch (item->type) { + case RadioItem::Type_Service:{ + QList children = item->children; + std::sort(children.begin(), children.end(), std::bind(&RadioModel::CompareItems, this, std::placeholders::_1, std::placeholders::_2)); + for (RadioItem *child : children) { + GetChildSongs(child, urls, songs); + } + break; + } + case RadioItem::Type_Channel: + if (!urls->contains(item->channel.url)) { + urls->append(item->channel.url); + songs->append(item->channel.ToSong()); + } + break; + default: + break; + } + +} + +SongList RadioModel::GetChildSongs(const QModelIndexList &indexes) const { + + QList urls; + SongList songs; + for (const QModelIndex &idx : indexes) { + GetChildSongs(IndexToItem(idx), &urls, &songs); + } + return songs; + +} + +SongList RadioModel::GetChildSongs(const QModelIndex &idx) const { + return GetChildSongs(QModelIndexList() << idx); +} + +QString RadioModel::ChannelIconPixmapCacheKey(const QModelIndex &idx) const { + + QStringList path; + QModelIndex idx_copy = idx; + while (idx_copy.isValid()) { + path.prepend(idx_copy.data().toString()); + idx_copy = idx_copy.parent(); + } + + return path.join('/'); + +} + +QPixmap RadioModel::ServiceIcon(const QModelIndex &idx) const { + return Song::IconForSource(Song::Source(idx.data(Role_Source).toInt())).pixmap(kTreeIconSize, kTreeIconSize); +} + +QPixmap RadioModel::ServiceIcon(RadioItem *item) const { + return Song::IconForSource(item->source).pixmap(kTreeIconSize, kTreeIconSize); +} + +QPixmap RadioModel::ChannelIcon(const QModelIndex &idx) { + + if (!idx.isValid()) return QPixmap(); + + RadioItem *item = IndexToItem(idx); + if (!item) return ServiceIcon(idx); + + const QString cache_key = ChannelIconPixmapCacheKey(idx); + + QPixmap cached_pixmap; + if (pixmap_cache_.find(cache_key, &cached_pixmap)) { + return cached_pixmap; + } + + if (pending_cache_keys_.contains(cache_key)) { + return ServiceIcon(idx); + } + + SongList songs = GetChildSongs(idx); + if (!songs.isEmpty()) { + Song song = songs.first(); + song.set_art_automatic(item->channel.thumbnail_url); + const quint64 id = app_->album_cover_loader()->LoadImageAsync(cover_loader_options_, song); + pending_art_[id] = ItemAndCacheKey(item, cache_key); + pending_cache_keys_.insert(cache_key); + } + + return ServiceIcon(idx); + +} + +void RadioModel::AlbumCoverLoaded(const quint64 id, const AlbumCoverLoaderResult &result) { + + if (!pending_art_.contains(id)) return; + + ItemAndCacheKey item_and_cache_key = pending_art_.take(id); + RadioItem *item = item_and_cache_key.first; + if (!item) return; + + const QString &cache_key = item_and_cache_key.second; + + pending_cache_keys_.remove(cache_key); + + if (!result.success || result.image_scaled.isNull() || result.type == AlbumCoverLoaderResult::Type_ManuallyUnset) { + pixmap_cache_.insert(cache_key, ServiceIcon(item)); + } + else { + pixmap_cache_.insert(cache_key, QPixmap::fromImage(result.image_scaled)); + } + + const QModelIndex idx = ItemToIndex(item); + if (!idx.isValid()) return; + + emit dataChanged(idx, idx); + +} + +QString RadioModel::SortText(QString text) { + + if (text.isEmpty()) { + text = " unknown"; + } + else { + text = text.toLower(); + } + text = text.remove(QRegularExpression("[^\\w ]", QRegularExpression::UseUnicodePropertiesOption)); + + return text; + +} diff --git a/src/radios/radiomodel.h b/src/radios/radiomodel.h new file mode 100644 index 000000000..1d5c00eaa --- /dev/null +++ b/src/radios/radiomodel.h @@ -0,0 +1,102 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#ifndef RADIOMODEL_H +#define RADIOMODEL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/song.h" +#include "core/simpletreemodel.h" +#include "covermanager/albumcoverloaderoptions.h" +#include "covermanager/albumcoverloaderresult.h" +#include "radioitem.h" +#include "radiochannel.h" + +class QMimeData; + +class Application; +class Database; + +class RadioModel : public SimpleTreeModel { + Q_OBJECT + + public: + explicit RadioModel(Application *app, QObject *parent = nullptr); + + enum Role { + Role_Type = Qt::UserRole + 1, + Role_SortText, + Role_Source, + Role_Url, + Role_Homepage, + Role_Donate, + RoleCount, + }; + + // QAbstractItemModel + Qt::ItemFlags flags(const QModelIndex &index) const override; + QVariant data(const QModelIndex &idx, int role) const override; + QStringList mimeTypes() const override; + QMimeData *mimeData(const QModelIndexList &indexes) const override; + + QVariant data(const RadioItem *item, int role) const; + + void Reset(); + void AddChannels(const RadioChannelList &channels); + + private: + bool IsPlayable(const QModelIndex &idx) const; + bool CompareItems(const RadioItem *a, const RadioItem *b) const; + void GetChildSongs(RadioItem *item, QList *urls, SongList *songs) const; + SongList GetChildSongs(const QModelIndexList &indexes) const; + SongList GetChildSongs(const QModelIndex &idx) const; + QString ChannelIconPixmapCacheKey(const QModelIndex &idx) const; + QPixmap ServiceIcon(const QModelIndex &idx) const; + QPixmap ServiceIcon(RadioItem *item) const; + QPixmap ChannelIcon(const QModelIndex &idx); + QString SortText(QString text); + + private slots: + void AlbumCoverLoaded(const quint64 id, const AlbumCoverLoaderResult &result); + + private: + static const int kTreeIconSize; + typedef QPair ItemAndCacheKey; + + Application *app_; + AlbumCoverLoaderOptions cover_loader_options_; + QMap container_nodes_; + QList items_; + QPixmapCache pixmap_cache_; + QMap pending_art_; + QSet pending_cache_keys_; +}; + +#endif // RADIOMODEL_H diff --git a/src/radios/radioparadiseservice.cpp b/src/radios/radioparadiseservice.cpp new file mode 100644 index 000000000..5daad93ff --- /dev/null +++ b/src/radios/radioparadiseservice.cpp @@ -0,0 +1,45 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#include +#include + +#include "core/iconloader.h" +#include "radioparadiseservice.h" +#include "radiochannel.h" + +RadioParadiseService::RadioParadiseService(Application *app, NetworkAccessManager *network, QObject *parent) : RadioService(Song::Source_RadioParadise, "Radio Paradise", IconLoader::Load("radioparadise"), app, network, parent) {} + +QUrl RadioParadiseService::Homepage() { return QUrl("https://radioparadise.com/"); } +QUrl RadioParadiseService::Donate() { return QUrl("https://payments.radioparadise.com/rp2s-content.php?name=Support&file=support"); } + +void RadioParadiseService::GetChannels() { + + emit NewChannels(RadioChannelList() + << RadioChannel(source_, "Main Mix 320k AAC", QUrl("https://stream.radioparadise.com/aac-320")) + << RadioChannel(source_, "Mellow Mix 320k AAC", QUrl("https://stream.radioparadise.com/mellow-320")) + << RadioChannel(source_, "Rock Mix 320k AAC", QUrl("https://stream.radioparadise.com/rock-320")) + << RadioChannel(source_, "World/Etc Mix 320k AAC", QUrl("https://stream.radioparadise.com/world-etc-320")) + << RadioChannel(source_, "Main Mix FLAC", QUrl("http://stream.radioparadise.com/flacm")) + << RadioChannel(source_, "Mellow Mix FLAC", QUrl("http://stream.radioparadise.com/mellow-flacm")) + << RadioChannel(source_, "Rock Mix FLAC", QUrl("http://stream.radioparadise.com/rock-flacm")) + << RadioChannel(source_, "World/Etc Mix FLAC", QUrl("http://stream.radioparadise.com/world-etc-flacm")) + ); + +} diff --git a/src/radios/radioparadiseservice.h b/src/radios/radioparadiseservice.h new file mode 100644 index 000000000..8ac02e47e --- /dev/null +++ b/src/radios/radioparadiseservice.h @@ -0,0 +1,45 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#ifndef RADIOPARADISESERVICE_H +#define RADIOPARADISESERVICE_H + +#include +#include + +#include "radioservice.h" + +class Application; +class NetworkAccessManager; + +class RadioParadiseService : public RadioService { + Q_OBJECT + + public: + explicit RadioParadiseService(Application *app, NetworkAccessManager *network, QObject *parent = nullptr); + + QUrl Homepage() override; + QUrl Donate() override; + + public slots: + void GetChannels() override; + +}; + +#endif // RADIOPARADISESERVICE_H diff --git a/src/radios/radioplaylistitem.cpp b/src/radios/radioplaylistitem.cpp new file mode 100644 index 000000000..574983695 --- /dev/null +++ b/src/radios/radioplaylistitem.cpp @@ -0,0 +1,77 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "radioplaylistitem.h" +#include "collection/sqlrow.h" + +RadioPlaylistItem::RadioPlaylistItem(const Song::Source source) + : PlaylistItem(source), source_(source) {} + +RadioPlaylistItem::RadioPlaylistItem(const Song &metadata) + : PlaylistItem(metadata.source()), + source_(metadata.source()), + metadata_(metadata) { + InitMetadata(); +} + +bool RadioPlaylistItem::InitFromQuery(const SqlRow &query) { + + metadata_.InitFromQuery(query, false, (static_cast(Song::kColumns.count()) + 1) * 1); + InitMetadata(); + return true; + +} + +QVariant RadioPlaylistItem::DatabaseValue(DatabaseColumn column) const { + return PlaylistItem::DatabaseValue(column); +} + +void RadioPlaylistItem::InitMetadata() { + + if (metadata_.title().isEmpty()) metadata_.set_title(metadata_.url().toString()); + if (metadata_.source() == Song::Source_Unknown) metadata_.set_source(Song::Source_Stream); + if (metadata_.filetype() == Song::FileType_Unknown) metadata_.set_filetype(Song::FileType_Stream); + metadata_.set_valid(true); + +} + +Song RadioPlaylistItem::Metadata() const { + + if (HasTemporaryMetadata()) return temp_metadata_; + return metadata_; + +} + +QUrl RadioPlaylistItem::Url() const { return metadata_.url(); } + +void RadioPlaylistItem::SetArtManual(const QUrl &cover_url) { + + metadata_.set_art_manual(cover_url); + temp_metadata_.set_art_manual(cover_url); + +} diff --git a/src/radios/radioplaylistitem.h b/src/radios/radioplaylistitem.h new file mode 100644 index 000000000..8a973127e --- /dev/null +++ b/src/radios/radioplaylistitem.h @@ -0,0 +1,62 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#ifndef RADIOPLAYLISTITEM_H +#define RADIOPLAYLISTITEM_H + +#include "config.h" + +#include +#include + +#include "core/song.h" +#include "collection/sqlrow.h" +#include "playlist/playlistitem.h" + +class RadioService; + +class RadioPlaylistItem : public PlaylistItem { + + public: + explicit RadioPlaylistItem(const Song::Source source); + explicit RadioPlaylistItem(const Song &metadata); + + bool InitFromQuery(const SqlRow &query) override; + Song Metadata() const override; + Song OriginalMetadata() const override { return metadata_; } + QUrl Url() const override; + + void SetMetadata(const Song &metadata) override { metadata_ = metadata; } + void SetArtManual(const QUrl &cover_url) override; + + protected: + QVariant DatabaseValue(DatabaseColumn) const override; + Song DatabaseSongMetadata() const override { return metadata_; } + + private: + void InitMetadata(); + + private: + Song::Source source_; + Song metadata_; + + Q_DISABLE_COPY(RadioPlaylistItem) +}; + +#endif // INTERNETPLAYLISTITEM_H diff --git a/src/radios/radioservice.cpp b/src/radios/radioservice.cpp new file mode 100644 index 000000000..47266e7b1 --- /dev/null +++ b/src/radios/radioservice.cpp @@ -0,0 +1,106 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/application.h" +#include "core/logging.h" +#include "radioservice.h" + +RadioService::RadioService(const Song::Source source, const QString &name, const QIcon &icon, Application *app, NetworkAccessManager *network, QObject *parent) + : QObject(parent), + app_(app), + network_(network), + source_(source), + name_(name), + icon_(icon) { + +} + +QByteArray RadioService::ExtractData(QNetworkReply *reply) { + + if (reply->error() != QNetworkReply::NoError) { + Error(QString("Failed to retrieve data from %1: %2 (%3)").arg(name_).arg(reply->errorString()).arg(reply->error())); + if (reply->error() < 200) { + return QByteArray(); + } + } + else if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { + Error(QString("Received HTTP code %1").arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt())); + } + + return reply->readAll(); + +} + +QJsonObject RadioService::ExtractJsonObj(const QByteArray &data) { + + if (data.isEmpty()) { + return QJsonObject(); + } + + QJsonParseError json_error; + QJsonDocument json_doc = QJsonDocument::fromJson(data, &json_error); + + if (json_error.error != QJsonParseError::NoError) { + Error(QString("Failed to parse Json data from %1: %2").arg(name_).arg(json_error.errorString())); + return QJsonObject(); + } + + if (json_doc.isEmpty()) { + Error(QString("%1: Received empty Json document.").arg(name_), data); + return QJsonObject(); + } + + if (!json_doc.isObject()) { + Error(QString("%1: Json document is not an object.").arg(name_), json_doc); + return QJsonObject(); + } + + QJsonObject json_obj = json_doc.object(); + if (json_obj.isEmpty()) { + Error(QString("%1: Received empty Json object.").arg(name_), json_doc); + return QJsonObject(); + } + + return json_obj; + +} + +QJsonObject RadioService::ExtractJsonObj(QNetworkReply *reply) { + + return ExtractJsonObj(ExtractData(reply)); + +} + +void RadioService::Error(const QString &error, const QVariant &debug) { + + qLog(Error) << name_ << error; + if (debug.isValid()) qLog(Debug) << debug; + +} diff --git a/src/radios/radioservice.h b/src/radios/radioservice.h new file mode 100644 index 000000000..61fcf8778 --- /dev/null +++ b/src/radios/radioservice.h @@ -0,0 +1,118 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#ifndef RADIOSERVICE_H +#define RADIOSERVICE_H + +#include +#include +#include +#include +#include +#include +#include + +#include "core/song.h" +#include "playlist/playlistitem.h" +#include "settings/settingsdialog.h" +#include "widgets/multiloadingindicator.h" +#include "radiochannel.h" + +class QNetworkReply; +class QStandardItem; + +class Application; +class NetworkAccessManager; +class RadioModel; + +class RadioService : public QObject { + Q_OBJECT + + public: + explicit RadioService(const Song::Source source, const QString &name, const QIcon &icon, Application *app, NetworkAccessManager *network, QObject *parent = nullptr); + + Song::Source source() const { return source_; } + QString name() const { return name_; } + + virtual void ReloadSettings() {} + + virtual QUrl Homepage() = 0; + virtual QUrl Donate() = 0; + + signals: + void StreamError(const QString& message); + void StreamMetadataFound(const QUrl& original_url, const Song& song); + + void NewChannels(RadioChannelList channels = RadioChannelList()); + + public slots: + virtual void ShowConfig() {} + virtual void GetChannels() = 0; + + private slots: + + protected: + // Called once when context menu is created + virtual void PopulateContextMenu(){}; + // Called every time context menu is shown + virtual void UpdateContextMenu(){}; + + // Returns all the playlist insertion related QActions (see below). + QList GetPlaylistActions(); + + // Returns the 'append to playlist' QAction. + QAction *GetAppendToPlaylistAction(); + // Returns the 'replace playlist' QAction. + QAction *GetReplacePlaylistAction(); + // Returns the 'open in new playlist' QAction. + QAction *GetOpenInNewPlaylistAction(); + + QByteArray ExtractData(QNetworkReply *reply); + QJsonObject ExtractJsonObj(const QByteArray &data); + QJsonObject ExtractJsonObj(QNetworkReply *reply); + void Error(const QString &error, const QVariant &debug = QVariant()); + + // Describes how songs should be added to playlist. + enum AddMode { + // appends songs to the current playlist + AddMode_Append, + // clears the current playlist and then appends all songs to it + AddMode_Replace, + // creates a new, empty playlist and then adds all songs to it + AddMode_OpenInNew + }; + + // Adds the 'index' element to playlist using the 'add_mode' mode. + void AddItemToPlaylist(const QModelIndex& index, AddMode add_mode); + // Adds the 'indexes' elements to playlist using the 'add_mode' mode. + void AddItemsToPlaylist(const QModelIndexList& indexes, AddMode add_mode); + + protected: + Application *app_; + NetworkAccessManager *network_; + RadioModel *model_; + Song::Source source_; + QString name_; + QIcon icon_; + +}; + +Q_DECLARE_METATYPE(RadioService*) + +#endif // RADIOSERVICE_H diff --git a/src/radios/radioservices.cpp b/src/radios/radioservices.cpp new file mode 100644 index 000000000..ac3520d6c --- /dev/null +++ b/src/radios/radioservices.cpp @@ -0,0 +1,138 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#include + +#include "core/logging.h" +#include "core/application.h" +#include "core/database.h" +#include "core/networkaccessmanager.h" +#include "radioservices.h" +#include "radiobackend.h" +#include "radiomodel.h" +#include "radioservice.h" +#include "radiochannel.h" +#include "somafmservice.h" +#include "radioparadiseservice.h" + +RadioServices::RadioServices(Application *app, QObject *parent) + : QObject(parent), + network_(new NetworkAccessManager(this)), + backend_(nullptr), + model_(new RadioModel(app, this)), + channels_refresh_(false) { + + backend_ = new RadioBackend(app, app->database()); + app->MoveToThread(backend_, app->database()->thread()); + + QObject::connect(backend_, &RadioBackend::NewChannels, this, &RadioServices::GotChannelsFromBackend); + + AddService(new SomaFMService(app, network_, this)); + AddService(new RadioParadiseService(app, network_, this)); + +} + +RadioServices::~RadioServices() { + + backend_->deleteLater(); + +} + +void RadioServices::AddService(RadioService *service) { + + qLog(Debug) << "Adding radio service:" << service->name(); + services_.insert(service->source(), service); + + QObject::connect(service, &RadioService::NewChannels, this, &RadioServices::GotChannelsFromService); + QObject::connect(service, &RadioService::destroyed, this, &RadioServices::ServiceDeleted); + +} + +void RadioServices::RemoveService(RadioService *service) { + + if (!services_.contains(service->source())) return; + + services_.remove(service->source()); + QObject::disconnect(service, nullptr, this, nullptr); + +} + +void RadioServices::ServiceDeleted() { + + RadioService *service = qobject_cast(sender()); + if (service) RemoveService(service); + +} + +RadioService *RadioServices::ServiceBySource(const Song::Source source) const { + + if (services_.contains(source)) return services_.value(source); + return nullptr; + +} + +void RadioServices::ReloadSettings() { + + QList services = services_.values(); + for (RadioService *service : services) { + service->ReloadSettings(); + } + +} + +void RadioServices::GetChannels() { + + model_->Reset(); + backend_->GetChannelsAsync(); + +} + +void RadioServices::RefreshChannels() { + + channels_refresh_ = true; + model_->Reset(); + backend_->DeleteChannelsAsync(); + + for (RadioService *service : services_) { + service->GetChannels(); + } + +} + +void RadioServices::GotChannelsFromBackend(const RadioChannelList &channels) { + + if (channels.isEmpty()) { + if (!channels_refresh_) { + RefreshChannels(); + } + } + else { + model_->AddChannels(channels); + } + +} + +void RadioServices::GotChannelsFromService(const RadioChannelList &channels) { + + RadioService *service = qobject_cast(sender()); + if (!service) return; + + backend_->AddChannelsAsync(channels); + +} diff --git a/src/radios/radioservices.h b/src/radios/radioservices.h new file mode 100644 index 000000000..571a8593a --- /dev/null +++ b/src/radios/radioservices.h @@ -0,0 +1,74 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#ifndef RADIOSERVICES_H +#define RADIOSERVICES_H + +#include +#include + +#include "core/song.h" +#include "radiochannel.h" + +class Application; +class NetworkAccessManager; +class RadioBackend; +class RadioModel; +class RadioService; + +class RadioServices : public QObject { + Q_OBJECT + + public: + explicit RadioServices(Application *app, QObject *parent = nullptr); + ~RadioServices(); + + void AddService(RadioService *service); + void RemoveService(RadioService *service); + + RadioService *ServiceBySource(const Song::Source source) const; + + template + T *Service() { + return static_cast(this->ServiceBySource(T::source)); + } + + void ReloadSettings(); + + RadioBackend *radio_backend() const { return backend_; } + RadioModel *radio_model() const { return model_; } + + private slots: + void ServiceDeleted(); + void GotChannelsFromBackend(const RadioChannelList &channels); + void GotChannelsFromService(const RadioChannelList &channels); + + public slots: + void GetChannels(); + void RefreshChannels(); + + private: + NetworkAccessManager *network_; + RadioBackend *backend_; + RadioModel *model_; + QMap services_; + bool channels_refresh_; +}; + +#endif // RADIOSERVICES_H diff --git a/src/radios/radioview.cpp b/src/radios/radioview.cpp new file mode 100644 index 000000000..6f559a8d2 --- /dev/null +++ b/src/radios/radioview.cpp @@ -0,0 +1,192 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "core/mimedata.h" +#include "core/iconloader.h" +#include "radiomodel.h" +#include "radioview.h" +#include "radioservice.h" +#include "radiomimedata.h" +#include "collection/collectionitemdelegate.h" + +RadioView::RadioView(QWidget *parent) + : AutoExpandingTreeView(parent), + menu_(nullptr), + action_playlist_append_(nullptr), + action_playlist_replace_(nullptr), + action_playlist_new_(nullptr), + action_homepage_(nullptr), + action_donate_(nullptr), + initialized_(false) { + + setItemDelegate(new CollectionItemDelegate(this)); + SetExpandOnReset(false); + setAttribute(Qt::WA_MacShowFocusRect, false); + setSelectionMode(QAbstractItemView::ExtendedSelection); + + QObject::connect(this, &RadioView::doubleClicked, this, &RadioView::DoubleClicked); + +} + +RadioView::~RadioView() { delete menu_; } + +void RadioView::setModel(RadioModel *model) { + + AutoExpandingTreeView::setModel(model); + +} + +void RadioView::showEvent(QShowEvent*) { + + if (!initialized_) { + emit GetChannels(); + initialized_ = true; + } + +} + +void RadioView::contextMenuEvent(QContextMenuEvent *e) { + + if (!menu_) { + menu_ = new QMenu; + + action_playlist_append_ = new QAction(IconLoader::Load("media-playback-start"), tr("Append to current playlist"), this); + QObject::connect(action_playlist_append_, &QAction::triggered, this, &RadioView::AddToPlaylist); + menu_->addAction(action_playlist_append_); + + action_playlist_replace_ = new QAction(IconLoader::Load("media-playback-start"), tr("Replace current playlist"), this); + QObject::connect(action_playlist_replace_, &QAction::triggered, this, &RadioView::ReplacePlaylist); + menu_->addAction(action_playlist_replace_); + + action_playlist_new_ = new QAction(IconLoader::Load("document-new"), tr("Open in new playlist"), this); + QObject::connect(action_playlist_new_, &QAction::triggered, this, &RadioView::OpenInNewPlaylist); + menu_->addAction(action_playlist_new_); + + action_homepage_ = new QAction(IconLoader::Load("download"), tr("Open homepage"), this); + QObject::connect(action_homepage_, &QAction::triggered, this, &RadioView::Homepage); + menu_->addAction(action_homepage_); + + action_donate_ = new QAction(IconLoader::Load("download"), tr("Donate"), this); + QObject::connect(action_donate_, &QAction::triggered, this, &RadioView::Donate); + menu_->addAction(action_donate_); + + menu_->addAction(IconLoader::Load("view-refresh"), tr("Refresh channels"), this, &RadioView::GetChannels); + } + + const bool channels_selected = !selectedIndexes().isEmpty(); + + action_playlist_append_->setVisible(channels_selected); + action_playlist_replace_->setVisible(channels_selected); + action_playlist_new_->setVisible(channels_selected); + action_homepage_->setVisible(channels_selected); + action_donate_->setVisible(channels_selected); + + menu_->popup(e->globalPos()); + +} + +void RadioView::AddToPlaylist() { + + emit AddToPlaylistSignal(model()->mimeData(selectedIndexes())); + +} + +void RadioView::ReplacePlaylist() { + + QMimeData *qmimedata = model()->mimeData(selectedIndexes()); + if (MimeData *mimedata = qobject_cast(qmimedata)) { + mimedata->clear_first_ = true; + } + + emit AddToPlaylistSignal(qmimedata); + +} + +void RadioView::OpenInNewPlaylist() { + + QMimeData *qmimedata = model()->mimeData(selectedIndexes()); + if (RadioMimeData *mimedata = qobject_cast(qmimedata)) { + mimedata->open_in_new_playlist_ = true; + if (!mimedata->songs.isEmpty()) { + mimedata->name_for_new_playlist_ = mimedata->songs.first().title(); + } + } + + emit AddToPlaylistSignal(qmimedata); + +} + +void RadioView::Homepage() { + + const QModelIndexList indexes = selectedIndexes(); + + QList urls; + for (const QModelIndex &idx : indexes) { + QUrl url = idx.data(RadioModel::Role_Homepage).toUrl(); + if (!urls.contains(url)) { + urls << url; + } + } + + for (const QUrl &url : urls) { + QDesktopServices::openUrl(url); + } + +} + +void RadioView::Donate() { + + const QModelIndexList indexes = selectedIndexes(); + + QList urls; + for (const QModelIndex &idx : indexes) { + QUrl url = idx.data(RadioModel::Role_Donate).toUrl(); + if (!urls.contains(url)) { + urls << url; + } + } + + for (const QUrl &url : urls) { + QDesktopServices::openUrl(url); + } + +} + +void RadioView::DoubleClicked() { + + const QModelIndexList indexes = selectedIndexes(); + if (indexes.isEmpty()) return; + + for (const QModelIndex &idx : indexes) { + if (idx.data(RadioModel::Role_Type).toInt() != RadioItem::Type_Channel) { + return; + } + } + + AddToPlaylist(); + +} diff --git a/src/radios/radioview.h b/src/radios/radioview.h new file mode 100644 index 000000000..602469400 --- /dev/null +++ b/src/radios/radioview.h @@ -0,0 +1,68 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#ifndef RADIOVIEW_H +#define RADIOVIEW_H + +#include + +#include "widgets/autoexpandingtreeview.h" + +class QMimeData; +class QMenu; +class QAction; +class QShowEvent; +class QContextMenuEvent; + +class RadioModel; + +class RadioView : public AutoExpandingTreeView { + Q_OBJECT + + public: + explicit RadioView(QWidget *parent = nullptr); + ~RadioView(); + + void setModel(RadioModel *model); + void showEvent(QShowEvent *e); + void contextMenuEvent(QContextMenuEvent *e) override; + + signals: + void GetChannels(); + void AddToPlaylistSignal(QMimeData*); + + private slots: + void AddToPlaylist(); + void ReplacePlaylist(); + void OpenInNewPlaylist(); + void Homepage(); + void Donate(); + void DoubleClicked(); + + private: + QMenu *menu_; + QAction *action_playlist_append_; + QAction *action_playlist_replace_; + QAction *action_playlist_new_; + QAction *action_homepage_; + QAction *action_donate_; + bool initialized_; +}; + +#endif // RADIOVIEW_H diff --git a/src/radios/radioviewcontainer.cpp b/src/radios/radioviewcontainer.cpp new file mode 100644 index 000000000..80803b4d8 --- /dev/null +++ b/src/radios/radioviewcontainer.cpp @@ -0,0 +1,54 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#include +#include +#include + +#include "core/iconloader.h" +#include "settings/appearancesettingspage.h" +#include "radioviewcontainer.h" +#include "ui_radioviewcontainer.h" + +RadioViewContainer::RadioViewContainer(QWidget *parent) + : QWidget(parent), + ui_(new Ui_RadioViewContainer) { + + ui_->setupUi(this); + + QObject::connect(ui_->refresh, &QToolButton::clicked, this, &RadioViewContainer::Refresh); + + ui_->refresh->setIcon(IconLoader::Load("view-refresh")); + + ReloadSettings(); + +} + +RadioViewContainer::~RadioViewContainer() { delete ui_; } + +void RadioViewContainer::ReloadSettings() { + + QSettings s; + s.beginGroup(AppearanceSettingsPage::kSettingsGroup); + int iconsize = s.value(AppearanceSettingsPage::kIconSizeLeftPanelButtons, 22).toInt(); + s.endGroup(); + + ui_->refresh->setIconSize(QSize(iconsize, iconsize)); + +} diff --git a/src/radios/radioviewcontainer.h b/src/radios/radioviewcontainer.h new file mode 100644 index 000000000..11135f73a --- /dev/null +++ b/src/radios/radioviewcontainer.h @@ -0,0 +1,47 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#ifndef RADIOVIEWCONTAINER_H +#define RADIOVIEWCONTAINER_H + +#include + +#include "ui_radioviewcontainer.h" + +class RadioView; + +class RadioViewContainer : public QWidget { + Q_OBJECT + + public: + explicit RadioViewContainer(QWidget *parent = nullptr); + ~RadioViewContainer(); + + void ReloadSettings(); + + RadioView *view() const { return ui_->view; } + + signals: + void Refresh(); + + private: + Ui_RadioViewContainer *ui_; +}; + +#endif // RADIOVIEWCONTAINER_H diff --git a/src/radios/radioviewcontainer.ui b/src/radios/radioviewcontainer.ui new file mode 100644 index 000000000..de107c201 --- /dev/null +++ b/src/radios/radioviewcontainer.ui @@ -0,0 +1,110 @@ + + + RadioViewContainer + + + + 0 + 0 + 424 + 395 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + true + + + + + + + 22 + 22 + + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + false + + + true + + + QAbstractItemView::DragDrop + + + true + + + + 16 + 16 + + + + true + + + true + + + true + + + + + + + + RadioView + QTreeView +
radios/radioview.h
+
+
+ + +
diff --git a/src/radios/somafmservice.cpp b/src/radios/somafmservice.cpp new file mode 100644 index 000000000..b0ef8ac3e --- /dev/null +++ b/src/radios/somafmservice.cpp @@ -0,0 +1,166 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/application.h" +#include "core/logging.h" +#include "core/networkaccessmanager.h" +#include "core/taskmanager.h" +#include "core/iconloader.h" +#include "playlistparsers/playlistparser.h" +#include "somafmservice.h" +#include "radiochannel.h" + +const char *SomaFMService::kApiChannelsUrl = "https://somafm.com/channels.json"; + +SomaFMService::SomaFMService(Application *app, NetworkAccessManager *network, QObject *parent) : RadioService(Song::Source_SomaFM, "SomaFM", IconLoader::Load("somafm"), app, network, parent) {} + +SomaFMService::~SomaFMService() { + Abort(); +} + +QUrl SomaFMService::Homepage() { return QUrl("https://somafm.com/"); } +QUrl SomaFMService::Donate() { return QUrl("https://somafm.com/support/"); } + +void SomaFMService::Abort() { + + while (!replies_.isEmpty()) { + QNetworkReply *reply = replies_.takeFirst(); + QObject::disconnect(reply, nullptr, this, nullptr); + if (reply->isRunning()) reply->abort(); + reply->deleteLater(); + } + + channels_.clear(); + +} + +void SomaFMService::GetChannels() { + + Abort(); + + QUrl url(kApiChannelsUrl); + QNetworkRequest req(url); + QNetworkReply *reply = network_->get(req); + replies_ << reply; + const int task_id = app_->task_manager()->StartTask(tr("Getting %1 channels").arg(name_)); + QObject::connect(reply, &QNetworkReply::finished, this, [=]() { GetChannelsReply(reply, task_id); }); + +} + +void SomaFMService::GetChannelsReply(QNetworkReply *reply, const int task_id) { + + if (replies_.contains(reply)) replies_.removeAll(reply); + reply->deleteLater(); + + QJsonObject object = ExtractJsonObj(reply); + if (object.isEmpty()) { + app_->task_manager()->SetTaskFinished(task_id); + emit NewChannels(); + return; + } + + if (!object.contains("channels") || !object["channels"].isArray()) { + Error("Missing JSON channels array.", object); + app_->task_manager()->SetTaskFinished(task_id); + emit NewChannels(); + return; + } + QJsonArray array_channels = object["channels"].toArray(); + + RadioChannelList channels; + for (const QJsonValueRef value_channel : array_channels) { + if (!value_channel.isObject()) continue; + QJsonObject obj_channel = value_channel.toObject(); + if (!obj_channel.contains("title") || !obj_channel.contains("image")) { + continue; + } + QString name = obj_channel["title"].toString(); + QString image = obj_channel["image"].toString(); + QJsonArray playlists = obj_channel["playlists"].toArray(); + for (const QJsonValueRef playlist : playlists) { + if (!playlist.isObject()) continue; + QJsonObject obj_playlist = playlist.toObject(); + if (!obj_playlist.contains("url") || !obj_playlist.contains("quality")) { + continue; + } + RadioChannel channel; + QString quality = obj_playlist["quality"].toString(); + if (quality != "highest") continue; + channel.source = source_; + channel.name = name; + channel.url.setUrl(obj_playlist["url"].toString()); + channel.thumbnail_url.setUrl(image); + if (obj_playlist.contains("format")) { + channel.name.append(" " + obj_playlist["format"].toString().toUpper()); + } + channels << channel; + } + } + + if (channels.isEmpty()) { + app_->task_manager()->SetTaskFinished(task_id); + emit NewChannels(); + } + else { + for (const RadioChannel &channel : channels) { + GetStreamUrl(task_id, channel); + } + } + +} + +void SomaFMService::GetStreamUrl(const int task_id, const RadioChannel &channel) { + + QNetworkRequest req(channel.url); + QNetworkReply *reply = network_->get(req); + replies_ << reply; + QObject::connect(reply, &QNetworkReply::finished, this, [=]() { GetStreamUrlsReply(reply, task_id, channel); }); + +} + +void SomaFMService::GetStreamUrlsReply(QNetworkReply *reply, const int task_id, RadioChannel channel) { + + if (replies_.contains(reply)) replies_.removeAll(reply); + reply->deleteLater(); + + PlaylistParser parser; + QList songs = parser.LoadFromDevice(reply); + if (!songs.isEmpty()) { + channel.url = songs.first().url(); + } + + channels_ << channel; + + if (replies_.isEmpty()) { + app_->task_manager()->SetTaskFinished(task_id); + emit NewChannels(channels_); + channels_.clear(); + } + +} diff --git a/src/radios/somafmservice.h b/src/radios/somafmservice.h new file mode 100644 index 000000000..c30ab8709 --- /dev/null +++ b/src/radios/somafmservice.h @@ -0,0 +1,61 @@ +/* + * Strawberry Music Player + * Copyright 2021, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#ifndef SOMAFMSERVICE_H +#define SOMAFMSERVICE_H + +#include +#include +#include + +#include "radioservice.h" +#include "radiochannel.h" + +class QNetworkReply; +class NetworkAccessManager; + +class SomaFMService : public RadioService { + Q_OBJECT + + public: + explicit SomaFMService(Application *app, NetworkAccessManager *network, QObject *parent = nullptr); + ~SomaFMService(); + + QUrl Homepage() override; + QUrl Donate() override; + + void Abort(); + + public slots: + void GetChannels() override; + + private: + void GetStreamUrl(const int task_id, const RadioChannel &channel); + + private slots: + void GetChannelsReply(QNetworkReply *reply, const int task_id); + void GetStreamUrlsReply(QNetworkReply *reply, const int task_id, RadioChannel channel); + + private: + static const char *kApiChannelsUrl; + QList replies_; + RadioChannelList channels_; +}; + +#endif // SOMAFMSERVICE_H diff --git a/src/settings/qobuzsettingspage.cpp b/src/settings/qobuzsettingspage.cpp index 160de206f..2f1da4689 100644 --- a/src/settings/qobuzsettingspage.cpp +++ b/src/settings/qobuzsettingspage.cpp @@ -121,8 +121,6 @@ void QobuzSettingsPage::Save() { s.setValue("downloadalbumcovers", ui_->checkbox_download_album_covers->isChecked()); s.endGroup(); - service_->ReloadSettings(); - } void QobuzSettingsPage::LoginClicked() { diff --git a/src/settings/subsonicsettingspage.cpp b/src/settings/subsonicsettingspage.cpp index a5b04f50c..bbabd1945 100644 --- a/src/settings/subsonicsettingspage.cpp +++ b/src/settings/subsonicsettingspage.cpp @@ -102,8 +102,6 @@ void SubsonicSettingsPage::Save() { s.setValue("serversidescrobbling", ui_->checkbox_server_scrobbling->isChecked()); s.endGroup(); - service_->ReloadSettings(); - } void SubsonicSettingsPage::TestClicked() { diff --git a/src/settings/tidalsettingspage.cpp b/src/settings/tidalsettingspage.cpp index abe6719d2..bd7701e56 100644 --- a/src/settings/tidalsettingspage.cpp +++ b/src/settings/tidalsettingspage.cpp @@ -148,8 +148,6 @@ void TidalSettingsPage::Save() { s.setValue("album_explicit", ui_->checkbox_album_explicit->isChecked()); s.endGroup(); - service_->ReloadSettings(); - } void TidalSettingsPage::LoginClicked() {