diff --git a/src/core/cachedlist.h b/src/core/cachedlist.h new file mode 100644 index 000000000..50661c092 --- /dev/null +++ b/src/core/cachedlist.h @@ -0,0 +1,92 @@ +/* This file is part of Clementine. + Copyright 2011, David Sansome + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#ifndef CACHEDLIST_H +#define CACHEDLIST_H + +#include +#include + +template +class CachedList { +public: + typedef QList ListType; + typedef typename ListType::const_iterator const_iterator; + + CachedList(const char* settings_group, const QString& name, + int cache_duration_secs) + : settings_group_(settings_group), + name_(name), + cache_duration_secs_(cache_duration_secs) { + } + + void Load() { + QSettings s; + s.beginGroup(settings_group_); + + last_updated_ = s.value("last_refreshed_" + name_).toDateTime(); + data_.clear(); + + const int count = s.beginReadArray(name_ + "_data"); + for (int i=0 ; i(); + } + s.endArray(); + } + + void Save() const { + QSettings s; + s.beginGroup(settings_group_); + + s.setValue("last_refreshed_" + name_, last_updated_); + + s.beginWriteArray(name_ + "_data", data_.size()); + for (int i=0 ; i cache_duration_secs_; + } + + const ListType& Data() const { return data_; } + operator ListType() const { return data_; } + + const_iterator begin() const { return data_.begin(); } + const_iterator end() const { return data_.end(); } + +private: + const char* settings_group_; + const QString name_; + const int cache_duration_secs_; + + QDateTime last_updated_; + ListType data_; +}; + +#endif // CACHEDLIST_H diff --git a/src/internet/digitallyimportedclient.cpp b/src/internet/digitallyimportedclient.cpp index 30975efed..ed18f5554 100644 --- a/src/internet/digitallyimportedclient.cpp +++ b/src/internet/digitallyimportedclient.cpp @@ -137,18 +137,20 @@ DigitallyImportedClient::ParseChannelList(QNetworkReply* reply) const { return ret; } -void DigitallyImportedClient::Channel::Load(const QSettings& s) { - art_url_ = s.value("art_url").toUrl(); - director_ = s.value("director").toString(); - description_ = s.value("description").toString(); - name_ = s.value("name").toString(); - key_ = s.value("key").toString(); +QDataStream& operator<<(QDataStream& out, const DigitallyImportedClient::Channel& channel) { + out << channel.art_url_ + << channel.director_ + << channel.description_ + << channel.name_ + << channel.key_; + return out; } -void DigitallyImportedClient::Channel::Save(QSettings* s) const { - s->setValue("art_url", art_url_); - s->setValue("director", director_); - s->setValue("description", description_); - s->setValue("name", name_); - s->setValue("key", key_); +QDataStream& operator>>(QDataStream& in, DigitallyImportedClient::Channel& channel) { + in >> channel.art_url_ + >> channel.director_ + >> channel.description_ + >> channel.name_ + >> channel.key_; + return in; } diff --git a/src/internet/digitallyimportedclient.h b/src/internet/digitallyimportedclient.h index 543447ad5..e42508f16 100644 --- a/src/internet/digitallyimportedclient.h +++ b/src/internet/digitallyimportedclient.h @@ -59,9 +59,6 @@ public: QString name_; QString key_; - void Save(QSettings* s) const; - void Load(const QSettings& s); - bool operator <(const Channel& other) const { return name_ < other.name_; } }; typedef QList ChannelList; @@ -81,4 +78,8 @@ private: QString service_name_; }; +QDataStream& operator<<(QDataStream& out, const DigitallyImportedClient::Channel& channel); +QDataStream& operator>>(QDataStream& in, DigitallyImportedClient::Channel& channel); +Q_DECLARE_METATYPE(DigitallyImportedClient::Channel) + #endif // DIGITALLYIMPORTEDCLIENT_H diff --git a/src/internet/digitallyimportedservicebase.cpp b/src/internet/digitallyimportedservicebase.cpp index 95db1484f..39ef9e8e5 100644 --- a/src/internet/digitallyimportedservicebase.cpp +++ b/src/internet/digitallyimportedservicebase.cpp @@ -56,6 +56,7 @@ DigitallyImportedServiceBase::DigitallyImportedServiceBase( premium_audio_type_(2), root_(NULL), context_item_(NULL), + saved_channels_(kSettingsGroup, api_service_name, kStreamsCacheDurationSecs), api_client_(new DigitallyImportedClient(api_service_name, this)) { model->player()->RegisterUrlHandler(url_handler_); @@ -112,14 +113,13 @@ void DigitallyImportedServiceBase::RefreshStreamsFinished(QNetworkReply* reply, model()->task_manager()->SetTaskFinished(task_id); reply->deleteLater(); - saved_channels_ = api_client_->ParseChannelList(reply); + // Parse the list and sort by name + DigitallyImportedClient::ChannelList channels = api_client_->ParseChannelList(reply); + qSort(channels); - // Sort by name - qSort(saved_channels_); + saved_channels_.Update(channels); - SaveChannels(saved_channels_); PopulateStreams(); - emit StreamsChanged(); } @@ -161,8 +161,7 @@ void DigitallyImportedServiceBase::ReloadSettings() { premium_audio_type_ = s.value("premium_audio_type", 2).toInt(); username_ = s.value("username").toString(); listen_hash_ = s.value("listen_hash").toString(); - last_refreshed_channels_ = s.value("last_refreshed_" + api_service_name_).toDateTime(); - saved_channels_ = LoadChannels(); + saved_channels_.Load(); } void DigitallyImportedServiceBase::ShowContextMenu( @@ -210,48 +209,6 @@ void DigitallyImportedServiceBase::ShowSettingsDialog() { emit OpenSettingsAtPage(SettingsDialog::Page_DigitallyImported); } -DigitallyImportedClient::ChannelList DigitallyImportedServiceBase::LoadChannels() const { - DigitallyImportedClient::ChannelList ret; - - QSettings s; - s.beginGroup(kSettingsGroup); - - int count = s.beginReadArray(api_service_name_); - for (int i=0 ; i - kStreamsCacheDurationSecs; -} - DigitallyImportedClient::ChannelList DigitallyImportedServiceBase::Channels() { if (IsChannelListStale()) { metaObject()->invokeMethod(this, "ForceRefreshStreams", Qt::QueuedConnection); diff --git a/src/internet/digitallyimportedservicebase.h b/src/internet/digitallyimportedservicebase.h index 0a8a4d33e..db5b89fdf 100644 --- a/src/internet/digitallyimportedservicebase.h +++ b/src/internet/digitallyimportedservicebase.h @@ -20,6 +20,7 @@ #include "digitallyimportedclient.h" #include "internetservice.h" +#include "core/cachedlist.h" #include @@ -59,7 +60,7 @@ public: const QString& service_description() const { return service_description_; } const QString& api_service_name() const { return api_service_name_; } - bool IsChannelListStale() const; + bool IsChannelListStale() const { return saved_channels_.IsStale(); } DigitallyImportedClient::ChannelList Channels(); void SongFromChannel(const DigitallyImportedClient::Channel& channel, Song* song) const; @@ -80,8 +81,6 @@ private slots: private: void PopulateStreams(); - DigitallyImportedClient::ChannelList LoadChannels() const; - void SaveChannels(const DigitallyImportedClient::ChannelList& streams); void LoadStation(const QString& key); @@ -108,8 +107,7 @@ private: boost::scoped_ptr context_menu_; QStandardItem* context_item_; - DigitallyImportedClient::ChannelList saved_channels_; - QDateTime last_refreshed_channels_; + CachedList saved_channels_; DigitallyImportedClient* api_client_; }; diff --git a/src/internet/lastfmservice.cpp b/src/internet/lastfmservice.cpp index c8ca814aa..29383f08b 100644 --- a/src/internet/lastfmservice.cpp +++ b/src/internet/lastfmservice.cpp @@ -87,6 +87,7 @@ LastFMService::LastFMService(InternetModel* parent) custom_list_(NULL), friends_list_(NULL), neighbours_list_(NULL), + friend_names_(kSettingsGroup, "friend_names", kFriendsCacheDurationSecs), connection_problems_(false) { //we emit the signal the first time to be sure the buttons are in the right state @@ -130,9 +131,7 @@ void LastFMService::ReloadSettings() { scrobbling_enabled_ = settings.value("ScrobblingEnabled", true).toBool(); buttons_visible_ = settings.value("ShowLoveBanButtons", true).toBool(); scrobble_button_visible_ = settings.value("ShowScrobbleButton", true).toBool(); - - last_refreshed_friends_ = settings.value("LastRefreshedFriends").toDateTime(); - friend_names_ = settings.value("FriendNames").toStringList(); + friend_names_.Load(); //avoid emitting signal if it's not changed if(scrobbling_enabled_old != scrobbling_enabled_) @@ -281,16 +280,13 @@ void LastFMService::SignOut() { lastfm::ws::Username.clear(); lastfm::ws::SessionKey.clear(); - friend_names_ = QStringList(); - last_refreshed_friends_ = QDateTime(); + friend_names_.Update(QStringList()); QSettings settings; settings.beginGroup(kSettingsGroup); settings.setValue("Username", QString()); settings.setValue("Session", QString()); - settings.setValue("FriendNames", friend_names_); - settings.setValue("LastRefreshedFriends", last_refreshed_friends_); } void LastFMService::AuthenticateReplyFinished() { @@ -589,20 +585,12 @@ void LastFMService::ShowContextMenu(const QModelIndex& index, const QPoint &glob context_menu_->popup(global_pos); } -bool LastFMService::IsFriendsListStale() const { - return last_refreshed_friends_.isNull() || - last_refreshed_friends_.secsTo(QDateTime::currentDateTime()) > - kFriendsCacheDurationSecs; -} - QStringList LastFMService::FriendNames() { // Update the list for next time, in the main thread. if (IsFriendsListStale()) metaObject()->invokeMethod(this, "RefreshFriends", Qt::QueuedConnection); - QSettings s; - s.beginGroup(LastFMService::kSettingsGroup); - return s.value("FriendNames").toStringList(); + return friend_names_.Data(); } static QStringList SavedArtistOrTagRadioNames(const QString& name) { @@ -682,20 +670,14 @@ void LastFMService::RefreshFriendsFinished() { return; } - last_refreshed_friends_ = QDateTime::currentDateTime(); - - friend_names_ = QStringList(); + QStringList names; foreach (const lastfm::User& f, friends) { - friend_names_ << f.name(); + names << f.name(); } - QSettings s; - s.beginGroup(kSettingsGroup); - s.setValue("FriendNames", friend_names_); - s.setValue("LastRefreshedFriends", last_refreshed_friends_); + friend_names_.Update(names); PopulateFriendsList(); - emit SavedItemsChanged(); } diff --git a/src/internet/lastfmservice.h b/src/internet/lastfmservice.h index 6ca061a36..3537ebf55 100644 --- a/src/internet/lastfmservice.h +++ b/src/internet/lastfmservice.h @@ -33,6 +33,7 @@ uint qHash(const lastfm::Track& track); #include "internetmodel.h" #include "internetservice.h" #include "lastfmstationdialog.h" +#include "core/cachedlist.h" #include "core/song.h" #include "playlist/playlistitem.h" @@ -111,7 +112,7 @@ class LastFMService : public InternetService { PlaylistItemPtr PlaylistItemForUrl(const QUrl& url); - bool IsFriendsListStale() const; + bool IsFriendsListStale() const { return friend_names_.IsStale(); } // Thread safe QStringList FriendNames(); @@ -225,8 +226,7 @@ class LastFMService : public InternetService { QHash art_urls_; - QStringList friend_names_; - QDateTime last_refreshed_friends_; + CachedList friend_names_; // Useful to inform the user that we can't scrobble right now bool connection_problems_; diff --git a/src/main.cpp b/src/main.cpp index ce756468f..16a1f092d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -44,6 +44,7 @@ #include "covers/coverproviders.h" #include "engines/enginebase.h" #include "globalsearch/globalsearch.h" +#include "internet/digitallyimportedclient.h" #include "internet/internetmodel.h" #include "library/directory.h" #include "playlist/playlist.h" @@ -227,6 +228,8 @@ int main(int argc, char *argv[]) { qRegisterMetaType >("QList"); qRegisterMetaType("SearchProvider::Result"); qRegisterMetaType("SearchProvider::ResultList"); + qRegisterMetaType("DigitallyImportedClient::Channel"); + qRegisterMetaTypeStreamOperators("DigitallyImportedClient::Channel"); qRegisterMetaType("GstBuffer*"); qRegisterMetaType("GstElement*"); diff --git a/src/translations/translations.pot b/src/translations/translations.pot index 58cdf257a..1b6212149 100644 --- a/src/translations/translations.pot +++ b/src/translations/translations.pot @@ -589,7 +589,7 @@ msgstr "" msgid "An error occurred writing metadata to '%1'" msgstr "" -#: internet/lastfmservice.cpp:918 +#: internet/lastfmservice.cpp:900 #, qt-format msgid "An unknown last.fm error occurred: %1" msgstr "" @@ -646,7 +646,7 @@ msgstr "" msgid "Artist info" msgstr "" -#: internet/lastfmservice.cpp:190 +#: internet/lastfmservice.cpp:189 msgid "Artist radio" msgstr "" @@ -667,7 +667,7 @@ msgid "Authenticating..." msgstr "" #: internet/digitallyimportedsettingspage.cpp:82 -#: internet/magnatunesettingspage.cpp:113 internet/lastfmservice.cpp:434 +#: internet/magnatunesettingspage.cpp:113 internet/lastfmservice.cpp:430 #: internet/lastfmsettingspage.cpp:79 remote/remotesettingspage.cpp:113 msgid "Authentication failed" msgstr "" @@ -987,7 +987,7 @@ msgstr "" msgid "Configure Grooveshark..." msgstr "" -#: internet/lastfmservice.cpp:108 +#: internet/lastfmservice.cpp:109 msgid "Configure Last.fm..." msgstr "" @@ -1007,7 +1007,7 @@ msgstr "" msgid "Configure library..." msgstr "" -#: internet/digitallyimportedservicebase.cpp:181 +#: internet/digitallyimportedservicebase.cpp:180 #: ../bin/src/ui_globalsearchsettingspage.h:167 msgid "Configure..." msgstr "" @@ -1071,7 +1071,7 @@ msgid "" "plugins installed" msgstr "" -#: internet/lastfmservice.cpp:915 +#: internet/lastfmservice.cpp:897 msgid "Couldn't load the last.fm radio station" msgstr "" @@ -1204,7 +1204,7 @@ msgstr "" msgid "Custom message settings" msgstr "" -#: internet/lastfmservice.cpp:198 +#: internet/lastfmservice.cpp:197 msgid "Custom radio" msgstr "" @@ -1606,7 +1606,7 @@ msgstr "" msgid "Error loading %1" msgstr "" -#: internet/digitallyimportedservicebase.cpp:203 +#: internet/digitallyimportedservicebase.cpp:202 #: internet/digitallyimportedurlhandler.cpp:73 msgid "Error loading di.fm playlist" msgstr "" @@ -1829,7 +1829,7 @@ msgstr "" msgid "Frames per buffer" msgstr "" -#: internet/lastfmservice.cpp:206 +#: internet/lastfmservice.cpp:205 msgid "Friends" msgstr "" @@ -1867,7 +1867,7 @@ msgstr "" msgid "Getting channels" msgstr "" -#: internet/digitallyimportedservicebase.cpp:103 +#: internet/digitallyimportedservicebase.cpp:104 msgid "Getting streams" msgstr "" @@ -2080,31 +2080,31 @@ msgstr "" msgid "Internet providers" msgstr "" -#: internet/lastfmservice.cpp:440 +#: internet/lastfmservice.cpp:436 msgid "Invalid API key" msgstr "" -#: internet/lastfmservice.cpp:435 +#: internet/lastfmservice.cpp:431 msgid "Invalid format" msgstr "" -#: internet/lastfmservice.cpp:433 +#: internet/lastfmservice.cpp:429 msgid "Invalid method" msgstr "" -#: internet/lastfmservice.cpp:436 +#: internet/lastfmservice.cpp:432 msgid "Invalid parameters" msgstr "" -#: internet/lastfmservice.cpp:437 +#: internet/lastfmservice.cpp:433 msgid "Invalid resource specified" msgstr "" -#: internet/lastfmservice.cpp:432 +#: internet/lastfmservice.cpp:428 msgid "Invalid service" msgstr "" -#: internet/lastfmservice.cpp:439 +#: internet/lastfmservice.cpp:435 msgid "Invalid session key" msgstr "" @@ -2198,25 +2198,25 @@ msgstr "" msgid "Last.fm Custom Radio: %1" msgstr "" -#: internet/lastfmservice.cpp:237 internet/lastfmservice.cpp:709 -#: internet/lastfmservice.cpp:744 +#: internet/lastfmservice.cpp:236 internet/lastfmservice.cpp:691 +#: internet/lastfmservice.cpp:726 #, qt-format msgid "Last.fm Library - %1" msgstr "" -#: globalsearch/lastfmsearchprovider.cpp:69 internet/lastfmservice.cpp:239 -#: internet/lastfmservice.cpp:242 +#: globalsearch/lastfmsearchprovider.cpp:69 internet/lastfmservice.cpp:238 +#: internet/lastfmservice.cpp:241 #, qt-format msgid "Last.fm Mix Radio - %1" msgstr "" -#: globalsearch/lastfmsearchprovider.cpp:71 internet/lastfmservice.cpp:244 -#: internet/lastfmservice.cpp:247 +#: globalsearch/lastfmsearchprovider.cpp:71 internet/lastfmservice.cpp:243 +#: internet/lastfmservice.cpp:246 #, qt-format msgid "Last.fm Neighbor Radio - %1" msgstr "" -#: globalsearch/lastfmsearchprovider.cpp:67 internet/lastfmservice.cpp:234 +#: globalsearch/lastfmsearchprovider.cpp:67 internet/lastfmservice.cpp:233 #, qt-format msgid "Last.fm Radio Station - %1" msgstr "" @@ -2231,7 +2231,7 @@ msgstr "" msgid "Last.fm Tag Radio: %1" msgstr "" -#: internet/lastfmservice.cpp:444 +#: internet/lastfmservice.cpp:440 msgid "Last.fm is currently busy, please try again in a few minutes" msgstr "" @@ -2320,7 +2320,7 @@ msgstr "" msgid "Load playlist..." msgstr "" -#: internet/lastfmservice.cpp:928 +#: internet/lastfmservice.cpp:910 msgid "Loading Last.fm radio" msgstr "" @@ -2449,7 +2449,7 @@ msgstr "" msgid "Make playlist available offline" msgstr "" -#: internet/lastfmservice.cpp:451 +#: internet/lastfmservice.cpp:447 msgid "Malformed response" msgstr "" @@ -2549,35 +2549,35 @@ msgstr "" msgid "Mute" msgstr "" -#: globalsearch/lastfmsearchprovider.cpp:45 internet/lastfmservice.cpp:177 +#: globalsearch/lastfmsearchprovider.cpp:45 internet/lastfmservice.cpp:176 msgid "My Last.fm Library" msgstr "" -#: globalsearch/lastfmsearchprovider.cpp:47 internet/lastfmservice.cpp:182 +#: globalsearch/lastfmsearchprovider.cpp:47 internet/lastfmservice.cpp:181 msgid "My Last.fm Mix Radio" msgstr "" -#: globalsearch/lastfmsearchprovider.cpp:49 internet/lastfmservice.cpp:187 +#: globalsearch/lastfmsearchprovider.cpp:49 internet/lastfmservice.cpp:186 msgid "My Last.fm Neighborhood" msgstr "" -#: globalsearch/lastfmsearchprovider.cpp:43 internet/lastfmservice.cpp:172 +#: globalsearch/lastfmsearchprovider.cpp:43 internet/lastfmservice.cpp:171 msgid "My Last.fm Recommended Radio" msgstr "" -#: internet/lastfmservice.cpp:179 +#: internet/lastfmservice.cpp:178 msgid "My Mix Radio" msgstr "" -#: internet/lastfmservice.cpp:184 +#: internet/lastfmservice.cpp:183 msgid "My Neighborhood" msgstr "" -#: internet/lastfmservice.cpp:174 +#: internet/lastfmservice.cpp:173 msgid "My Radio Station" msgstr "" -#: internet/lastfmservice.cpp:169 +#: internet/lastfmservice.cpp:168 msgid "My Recommendations" msgstr "" @@ -2597,7 +2597,7 @@ msgstr "" msgid "Narrow band (NB)" msgstr "" -#: internet/lastfmservice.cpp:211 +#: internet/lastfmservice.cpp:210 msgid "Neighbors" msgstr "" @@ -2689,19 +2689,19 @@ msgstr "" msgid "Not connected" msgstr "" -#: internet/lastfmservice.cpp:446 +#: internet/lastfmservice.cpp:442 msgid "Not enough content" msgstr "" -#: internet/lastfmservice.cpp:448 +#: internet/lastfmservice.cpp:444 msgid "Not enough fans" msgstr "" -#: internet/lastfmservice.cpp:447 +#: internet/lastfmservice.cpp:443 msgid "Not enough members" msgstr "" -#: internet/lastfmservice.cpp:449 +#: internet/lastfmservice.cpp:445 msgid "Not enough neighbors" msgstr "" @@ -2750,7 +2750,7 @@ msgstr "" msgid "Only show the first" msgstr "" -#: internet/digitallyimportedservicebase.cpp:174 +#: internet/digitallyimportedservicebase.cpp:173 #, qt-format msgid "Open %1 in browser" msgstr "" @@ -2794,7 +2794,7 @@ msgstr "" msgid "Open..." msgstr "" -#: internet/lastfmservice.cpp:438 +#: internet/lastfmservice.cpp:434 msgid "Operation failed" msgstr "" @@ -2893,7 +2893,7 @@ msgstr "" msgid "Play Artist or Tag" msgstr "" -#: internet/lastfmservice.cpp:100 +#: internet/lastfmservice.cpp:101 msgid "Play artist radio..." msgstr "" @@ -2901,7 +2901,7 @@ msgstr "" msgid "Play count" msgstr "" -#: internet/lastfmservice.cpp:104 +#: internet/lastfmservice.cpp:105 msgid "Play custom radio..." msgstr "" @@ -2926,7 +2926,7 @@ msgstr "" msgid "Play last.fm tag radio" msgstr "" -#: internet/lastfmservice.cpp:102 +#: internet/lastfmservice.cpp:103 msgid "Play tag radio..." msgstr "" @@ -3152,7 +3152,7 @@ msgstr "" msgid "Refresh channels" msgstr "" -#: internet/lastfmservice.cpp:106 +#: internet/lastfmservice.cpp:107 msgid "Refresh friends list" msgstr "" @@ -3160,7 +3160,7 @@ msgstr "" msgid "Refresh station list" msgstr "" -#: internet/digitallyimportedservicebase.cpp:177 +#: internet/digitallyimportedservicebase.cpp:176 msgid "Refresh streams" msgstr "" @@ -3180,7 +3180,7 @@ msgstr "" msgid "Remote Control" msgstr "" -#: internet/savedradio.cpp:94 internet/lastfmservice.cpp:97 +#: internet/savedradio.cpp:94 internet/lastfmservice.cpp:98 #: ../bin/src/ui_queuemanager.h:135 ../bin/src/ui_searchtermwidget.h:282 #: ../bin/src/ui_transcodedialog.h:207 msgid "Remove" @@ -3445,7 +3445,7 @@ msgstr "" msgid "Serial number" msgstr "" -#: internet/lastfmservice.cpp:441 +#: internet/lastfmservice.cpp:437 msgid "Service offline" msgstr "" @@ -3828,7 +3828,7 @@ msgstr "" msgid "Tag fetcher" msgstr "" -#: internet/lastfmservice.cpp:194 +#: internet/lastfmservice.cpp:193 msgid "Tag radio" msgstr "" @@ -3967,7 +3967,7 @@ msgid "" "want to continue?" msgstr "" -#: internet/lastfmservice.cpp:442 +#: internet/lastfmservice.cpp:438 msgid "This stream is for paid subscribers only" msgstr "" @@ -4085,7 +4085,7 @@ msgstr "" msgid "Unknown" msgstr "" -#: internet/digitallyimportedclient.cpp:68 internet/lastfmservice.cpp:455 +#: internet/digitallyimportedclient.cpp:68 internet/lastfmservice.cpp:451 msgid "Unknown error" msgstr ""