Add radios

This commit is contained in:
Jonas Kvinge
2021-07-11 01:02:53 +02:00
parent d07aff9872
commit 09bbf1f4d7
65 changed files with 2363 additions and 45 deletions

View File

@@ -91,6 +91,9 @@
# include "moodbar/moodbarloader.h"
#endif
#include "radios/radioservices.h"
#include "radios/radiobackend.h"
using namespace std::chrono_literals;
class ApplicationImpl {
@@ -171,15 +174,13 @@ class ApplicationImpl {
#endif
return internet_services;
}),
radio_services_([=]() { return new RadioServices(app, app); }),
scrobbler_([=]() { return new AudioScrobbler(app, app); }),
lastfm_import_([=]() { return new LastFMImport(app); }),
#ifdef HAVE_MOODBAR
moodbar_loader_([=]() { return new MoodbarLoader(app, app); }),
moodbar_controller_([=]() { return new MoodbarController(app, app); }),
#endif
dummy_([=]() { return nullptr; })
lastfm_import_([=]() { return new LastFMImport(app); })
{}
Lazy<TagReaderClient> tag_reader_client_;
@@ -199,13 +200,13 @@ class ApplicationImpl {
Lazy<CurrentAlbumCoverLoader> current_albumcover_loader_;
Lazy<LyricsProviders> lyrics_providers_;
Lazy<InternetServices> internet_services_;
Lazy<RadioServices> radio_services_;
Lazy<AudioScrobbler> scrobbler_;
Lazy<LastFMImport> lastfm_import_;
#ifdef HAVE_MOODBAR
Lazy<MoodbarLoader> moodbar_loader_;
Lazy<MoodbarController> moodbar_controller_;
#endif
Lazy<QVariant> dummy_;
Lazy<LastFMImport> 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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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<SystemTrayIcon> 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<SystemTrayIcon> 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<SystemTrayIcon> 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<SystemTrayIcon> 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<SystemTrayIcon> 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

View File

@@ -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_;

View File

@@ -72,6 +72,8 @@
#include "smartplaylists/playlistgenerator_fwd.h"
#include "radios/radiochannel.h"
void RegisterMetaTypes() {
qRegisterMetaType<const char*>("const char*");
@@ -141,4 +143,6 @@ void RegisterMetaTypes() {
qRegisterMetaType<PlaylistGeneratorPtr>("PlaylistGeneratorPtr");
qRegisterMetaType<RadioChannelList>("RadioChannelList");
}

View File

@@ -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";
}

View File

@@ -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()); }