diff --git a/data/data.qrc b/data/data.qrc index b5e8a9d41..129f6f24f 100644 --- a/data/data.qrc +++ b/data/data.qrc @@ -292,6 +292,7 @@ providers/myspace.png providers/podcast16.png providers/podcast32.png + providers/radiogfm.png providers/rockradio.png providers/skydrive.png providers/skyfm.png diff --git a/data/providers/radiogfm.png b/data/providers/radiogfm.png new file mode 100644 index 000000000..f7da69020 Binary files /dev/null and b/data/providers/radiogfm.png differ diff --git a/src/core/cachedlist.h b/src/core/cachedlist.h index 8f0a6b990..9f0c4c828 100644 --- a/src/core/cachedlist.h +++ b/src/core/cachedlist.h @@ -32,7 +32,7 @@ public: typedef QList ListType; - CachedList(const char* settings_group, const QString& name, + CachedList(const QString& settings_group, const QString& name, int cache_duration_secs) : settings_group_(settings_group), name_(name), @@ -92,7 +92,7 @@ public: const_iterator end() const { return data_.end(); } private: - const char* settings_group_; + const QString settings_group_; const QString name_; const int cache_duration_secs_; diff --git a/src/globalsearch/somafmsearchprovider.cpp b/src/globalsearch/somafmsearchprovider.cpp index 9fbc85040..207e26b4b 100644 --- a/src/globalsearch/somafmsearchprovider.cpp +++ b/src/globalsearch/somafmsearchprovider.cpp @@ -18,14 +18,16 @@ #include "somafmsearchprovider.h" #include "internet/somafmservice.h" -SomaFMSearchProvider::SomaFMSearchProvider(SomaFMService* service, Application* app, QObject* parent) +SomaFMSearchProvider::SomaFMSearchProvider( + SomaFMServiceBase* service, Application* app, QObject* parent) : SimpleSearchProvider(app, parent), service_(service) { - Init("SomaFM", "somafm", QIcon(":/providers/somafm.png"), CanGiveSuggestions); + Init(service->name(), service->url_scheme(), service->icon(), CanGiveSuggestions); set_result_limit(3); set_max_suggestion_count(3); - icon_ = ScaleAndPad(QImage(":/providers/somafm.png")); + icon_ = ScaleAndPad( + service->icon().pixmap(service->icon().availableSizes()[0]).toImage()); connect(service, SIGNAL(StreamsChanged()), SLOT(MaybeRecreateItems())); @@ -44,7 +46,7 @@ void SomaFMSearchProvider::RecreateItems() { foreach (const SomaFMService::Stream& stream, service_->Streams()) { Item item; - item.metadata_ = stream.ToSong(); + item.metadata_ = stream.ToSong(service_->name()); item.keyword_ = stream.title_; items << item; } diff --git a/src/globalsearch/somafmsearchprovider.h b/src/globalsearch/somafmsearchprovider.h index 836a046d2..5bd2f1971 100644 --- a/src/globalsearch/somafmsearchprovider.h +++ b/src/globalsearch/somafmsearchprovider.h @@ -20,11 +20,11 @@ #include "simplesearchprovider.h" -class SomaFMService; +class SomaFMServiceBase; class SomaFMSearchProvider : public SimpleSearchProvider { public: - SomaFMSearchProvider(SomaFMService* service, Application* app, QObject* parent); + SomaFMSearchProvider(SomaFMServiceBase* service, Application* app, QObject* parent); void LoadArtAsync(int id, const Result& result); @@ -32,7 +32,7 @@ protected: void RecreateItems(); private: - SomaFMService* service_; + SomaFMServiceBase* service_; QImage icon_; }; diff --git a/src/internet/internetmodel.cpp b/src/internet/internetmodel.cpp index a4e10dc2d..eb5b6dd78 100644 --- a/src/internet/internetmodel.cpp +++ b/src/internet/internetmodel.cpp @@ -91,6 +91,7 @@ InternetModel::InternetModel(Application* app, QObject* parent) AddService(new JazzRadioService(app, this)); AddService(new MagnatuneService(app, this)); AddService(new PodcastService(app, this)); + AddService(new RadioGFMService(app, this)); AddService(new RockRadioService(app, this)); AddService(new SavedRadio(app, this)); AddService(new SkyFmService(app, this)); diff --git a/src/internet/somafmservice.cpp b/src/internet/somafmservice.cpp index 520d79c18..5e3487619 100644 --- a/src/internet/somafmservice.cpp +++ b/src/internet/somafmservice.cpp @@ -37,25 +37,34 @@ #include #include -const char* SomaFMService::kServiceName = "SomaFM"; -const char* SomaFMService::kSettingsGroup = "SomaFM"; -const char* SomaFMService::kChannelListUrl = "http://somafm.com/channels.xml"; -const char* SomaFMService::kHomepage = "http://somafm.com"; -const int SomaFMService::kStreamsCacheDurationSecs = +const int SomaFMServiceBase::kStreamsCacheDurationSecs = 60 * 60 * 24 * 28; // 4 weeks -bool operator <(const SomaFMService::Stream& a, - const SomaFMService::Stream& b) { +bool operator <(const SomaFMServiceBase::Stream& a, + const SomaFMServiceBase::Stream& b) { return a.title_.compare(b.title_, Qt::CaseInsensitive) < 0; } -SomaFMService::SomaFMService(Application* app, InternetModel* parent) - : InternetService(kServiceName, app, parent, parent), +SomaFMServiceBase::SomaFMServiceBase( + Application* app, + InternetModel* parent, + const QString& name, + const QUrl& channel_list_url, + const QUrl& homepage_url, + const QUrl& donate_page_url, + const QIcon& icon) + : InternetService(name, app, parent, parent), + url_scheme_(name.toLower().remove(' ')), url_handler_(new SomaFMUrlHandler(app, this, this)), root_(NULL), context_menu_(NULL), network_(new NetworkAccessManager(this)), - streams_(kSettingsGroup, "streams", kStreamsCacheDurationSecs) + streams_(name, "streams", kStreamsCacheDurationSecs), + name_(name), + channel_list_url_(channel_list_url), + homepage_url_(homepage_url), + donate_page_url_(donate_page_url), + icon_(icon) { ReloadSettings(); @@ -63,17 +72,17 @@ SomaFMService::SomaFMService(Application* app, InternetModel* parent) app_->global_search()->AddProvider(new SomaFMSearchProvider(this, app_, this)); } -SomaFMService::~SomaFMService() { +SomaFMServiceBase::~SomaFMServiceBase() { delete context_menu_; } -QStandardItem* SomaFMService::CreateRootItem() { - root_ = new QStandardItem(QIcon(":/providers/somafm.png"), kServiceName); +QStandardItem* SomaFMServiceBase::CreateRootItem() { + root_ = new QStandardItem(icon_, name_); root_->setData(true, InternetModel::Role_CanLazyLoad); return root_; } -void SomaFMService::LazyPopulate(QStandardItem* item) { +void SomaFMServiceBase::LazyPopulate(QStandardItem* item) { switch (item->data(InternetModel::Role_Type).toInt()) { case InternetModel::Type_Service: RefreshStreams(); @@ -84,19 +93,24 @@ void SomaFMService::LazyPopulate(QStandardItem* item) { } } -void SomaFMService::ShowContextMenu(const QPoint& global_pos) { +void SomaFMServiceBase::ShowContextMenu(const QPoint& global_pos) { if (!context_menu_) { context_menu_ = new QMenu; context_menu_->addActions(GetPlaylistActions()); - context_menu_->addAction(IconLoader::Load("download"), tr("Open %1 in browser").arg("somafm.com"), this, SLOT(Homepage())); - context_menu_->addAction(IconLoader::Load("view-refresh"), tr("Refresh channels"), this, SLOT(RefreshStreams())); + context_menu_->addAction(IconLoader::Load("download"), tr("Open %1 in browser").arg(homepage_url_.host()), this, SLOT(Homepage())); + + if (!donate_page_url_.isEmpty()) { + context_menu_->addAction(IconLoader::Load("download"), tr("Donate"), this, SLOT(Donate())); + } + + context_menu_->addAction(IconLoader::Load("view-refresh"), tr("Refresh channels"), this, SLOT(ForceRefreshStreams())); } context_menu_->popup(global_pos); } -void SomaFMService::ForceRefreshStreams() { - QNetworkReply* reply = network_->get(QNetworkRequest(QUrl(kChannelListUrl))); +void SomaFMServiceBase::ForceRefreshStreams() { + QNetworkReply* reply = network_->get(QNetworkRequest(channel_list_url_)); int task_id = app_->task_manager()->StartTask(tr("Getting channels")); NewClosure(reply, SIGNAL(finished()), @@ -104,7 +118,7 @@ void SomaFMService::ForceRefreshStreams() { reply, task_id); } -void SomaFMService::RefreshStreamsFinished(QNetworkReply* reply, int task_id) { +void SomaFMServiceBase::RefreshStreamsFinished(QNetworkReply* reply, int task_id) { app_->task_manager()->SetTaskFinished(task_id); reply->deleteLater(); @@ -136,7 +150,7 @@ void SomaFMService::RefreshStreamsFinished(QNetworkReply* reply, int task_id) { emit StreamsChanged(); } -void SomaFMService::ReadChannel(QXmlStreamReader& reader, StreamList* ret) { +void SomaFMServiceBase::ReadChannel(QXmlStreamReader& reader, StreamList* ret) { Stream stream; while (!reader.atEnd()) { @@ -154,7 +168,7 @@ void SomaFMService::ReadChannel(QXmlStreamReader& reader, StreamList* ret) { stream.dj_ = reader.readElementText(); } else if (reader.name() == "fastpls" && reader.attributes().value("format") == "mp3") { QUrl url(reader.readElementText()); - url.setScheme("somafm"); + url.setScheme(url_handler_->scheme()); stream.url_ = url; } else { @@ -168,31 +182,40 @@ void SomaFMService::ReadChannel(QXmlStreamReader& reader, StreamList* ret) { } } -Song SomaFMService::Stream::ToSong() const { +Song SomaFMServiceBase::Stream::ToSong(const QString& prefix) const { + QString song_title = title_.trimmed(); + if (!song_title.startsWith(prefix)) { + song_title = prefix + " " + song_title; + } + Song ret; ret.set_valid(true); - ret.set_title("SomaFM " + title_); + ret.set_title(song_title); ret.set_artist(dj_); ret.set_url(url_); return ret; } -void SomaFMService::Homepage() { - QDesktopServices::openUrl(QUrl(kHomepage)); +void SomaFMServiceBase::Homepage() { + QDesktopServices::openUrl(homepage_url_); } -PlaylistItem::Options SomaFMService::playlistitem_options() const { +void SomaFMServiceBase::Donate() { + QDesktopServices::openUrl(donate_page_url_); +} + +PlaylistItem::Options SomaFMServiceBase::playlistitem_options() const { return PlaylistItem::PauseDisabled; } -SomaFMService::StreamList SomaFMService::Streams() { +SomaFMServiceBase::StreamList SomaFMServiceBase::Streams() { if (IsStreamListStale()) { metaObject()->invokeMethod(this, "ForceRefreshStreams", Qt::QueuedConnection); } return streams_; } -void SomaFMService::RefreshStreams() { +void SomaFMServiceBase::RefreshStreams() { if (IsStreamListStale()) { ForceRefreshStreams(); return; @@ -200,35 +223,59 @@ void SomaFMService::RefreshStreams() { PopulateStreams(); } -void SomaFMService::PopulateStreams() { +void SomaFMServiceBase::PopulateStreams() { if (root_->hasChildren()) root_->removeRows(0, root_->rowCount()); foreach (const Stream& stream, streams_) { QStandardItem* item = new QStandardItem(QIcon(":last.fm/icon_radio.png"), QString()); item->setText(stream.title_); - item->setData(QVariant::fromValue(stream.ToSong()), InternetModel::Role_SongMetadata); + item->setData(QVariant::fromValue(stream.ToSong(name_)), InternetModel::Role_SongMetadata); item->setData(InternetModel::PlayBehaviour_SingleItem, InternetModel::Role_PlayBehaviour); root_->appendRow(item); } } -QDataStream& operator<<(QDataStream& out, const SomaFMService::Stream& stream) { +QDataStream& operator<<(QDataStream& out, const SomaFMServiceBase::Stream& stream) { out << stream.title_ << stream.dj_ << stream.url_; return out; } -QDataStream& operator>>(QDataStream& in, SomaFMService::Stream& stream) { +QDataStream& operator>>(QDataStream& in, SomaFMServiceBase::Stream& stream) { in >> stream.title_ >> stream.dj_ >> stream.url_; return in; } -void SomaFMService::ReloadSettings() { +void SomaFMServiceBase::ReloadSettings() { streams_.Load(); streams_.Sort(); } + + +SomaFMService::SomaFMService(Application* app, InternetModel* parent) + : SomaFMServiceBase( + app, + parent, + "SomaFM", + QUrl("http://somafm.com/channels.xml"), + QUrl("http://somafm.com"), + QUrl(), + QIcon(":providers/somafm.png")) { +} + + +RadioGFMService::RadioGFMService(Application* app, InternetModel* parent) + : SomaFMServiceBase( + app, + parent, + "Radio GFM", + QUrl("http://streams.radio-gfm.net/channels.xml"), + QUrl("http://www.radio-gfm.net"), + QUrl("http://www.radio-gfm.net/spenden"), + QIcon(":providers/radiogfm.png")) { +} diff --git a/src/internet/somafmservice.h b/src/internet/somafmservice.h index 12c3f509e..7d14eb50b 100644 --- a/src/internet/somafmservice.h +++ b/src/internet/somafmservice.h @@ -29,12 +29,19 @@ class QNetworkAccessManager; class QNetworkReply; class QMenu; -class SomaFMService : public InternetService { +class SomaFMServiceBase : public InternetService { Q_OBJECT public: - SomaFMService(Application* app, InternetModel* parent); - ~SomaFMService(); + SomaFMServiceBase( + Application* app, + InternetModel* parent, + const QString& name, + const QUrl& channel_list_url, + const QUrl& homepage_url, + const QUrl& donate_page_url, + const QIcon& icon); + ~SomaFMServiceBase(); enum ItemType { Type_Stream = 2000, @@ -45,16 +52,15 @@ public: QString dj_; QUrl url_; - Song ToSong() const; + Song ToSong(const QString& prefix) const; }; typedef QList StreamList; - static const char* kServiceName; - static const char* kSettingsGroup; - static const char* kChannelListUrl; - static const char* kHomepage; static const int kStreamsCacheDurationSecs; + const QString& url_scheme() const { return url_scheme_; } + const QIcon& icon() const { return icon_; } + QStandardItem* CreateRootItem(); void LazyPopulate(QStandardItem* item); void ShowContextMenu(const QPoint& global_pos); @@ -76,12 +82,14 @@ private slots: void RefreshStreamsFinished(QNetworkReply* reply, int task_id); void Homepage(); + void Donate(); private: void ReadChannel(QXmlStreamReader& reader, StreamList* ret); void PopulateStreams(); private: + const QString url_scheme_; SomaFMUrlHandler* url_handler_; QStandardItem* root_; @@ -90,6 +98,22 @@ private: QNetworkAccessManager* network_; CachedList streams_; + + const QString name_; + const QUrl channel_list_url_; + const QUrl homepage_url_; + const QUrl donate_page_url_; + const QIcon icon_; +}; + +class SomaFMService : public SomaFMServiceBase { + public: + SomaFMService(Application* app, InternetModel* parent); +}; + +class RadioGFMService : public SomaFMServiceBase { + public: + RadioGFMService(Application* app, InternetModel* parent); }; QDataStream& operator<<(QDataStream& out, const SomaFMService::Stream& stream); diff --git a/src/internet/somafmurlhandler.cpp b/src/internet/somafmurlhandler.cpp index e724eb6f9..07f4c3a70 100644 --- a/src/internet/somafmurlhandler.cpp +++ b/src/internet/somafmurlhandler.cpp @@ -28,7 +28,8 @@ #include #include -SomaFMUrlHandler::SomaFMUrlHandler(Application* app, SomaFMService* service, +SomaFMUrlHandler::SomaFMUrlHandler(Application* app, + SomaFMServiceBase* service, QObject* parent) : UrlHandler(parent), app_(app), @@ -37,6 +38,14 @@ SomaFMUrlHandler::SomaFMUrlHandler(Application* app, SomaFMService* service, { } +QString SomaFMUrlHandler::scheme() const { + return service_->url_scheme(); +} + +QIcon SomaFMUrlHandler::icon() const { + return service_->icon(); +} + UrlHandler::LoadResult SomaFMUrlHandler::StartLoading(const QUrl& url) { QUrl playlist_url = url; playlist_url.setScheme("http"); @@ -57,7 +66,7 @@ void SomaFMUrlHandler::LoadPlaylistFinished() { task_id_ = 0; QUrl original_url(reply->url()); - original_url.setScheme("somafm"); + original_url.setScheme(scheme()); if (reply->error() != QNetworkReply::NoError) { // TODO: Error handling @@ -74,7 +83,7 @@ void SomaFMUrlHandler::LoadPlaylistFinished() { // Failed to get playlist? if (songs.count() == 0) { - qLog(Error) << "Error loading soma.fm playlist"; + qLog(Error) << "Error loading" << scheme() << "playlist"; emit AsyncLoadComplete(LoadResult(original_url, LoadResult::NoMoreTracks)); return; } diff --git a/src/internet/somafmurlhandler.h b/src/internet/somafmurlhandler.h index ca4367286..1e4ea0121 100644 --- a/src/internet/somafmurlhandler.h +++ b/src/internet/somafmurlhandler.h @@ -21,17 +21,20 @@ #include "core/urlhandler.h" class Application; -class SomaFMService; +class SomaFMServiceBase; class SomaFMUrlHandler : public UrlHandler { Q_OBJECT public: - SomaFMUrlHandler(Application* app, SomaFMService* service, QObject* parent); + SomaFMUrlHandler( + Application* app, + SomaFMServiceBase* service, + QObject* parent); - QString scheme() const { return "somafm"; } - QIcon icon() const { return QIcon(":providers/somafm.png"); } + QString scheme() const; + QIcon icon() const; LoadResult StartLoading(const QUrl& url); private slots: @@ -39,7 +42,7 @@ private slots: private: Application* app_; - SomaFMService* service_; + SomaFMServiceBase* service_; int task_id_; };