Remove Last.fm radio support.

Fixes #4259
This commit is contained in:
John Maguire 2014-03-27 18:55:58 +01:00
parent 1b7f99127d
commit 4ab4bbc23f
34 changed files with 128 additions and 1282 deletions

View File

@ -245,18 +245,11 @@
<file>last.fm/as_disabled.png</file>
<file>last.fm/as_light.png</file>
<file>last.fm/as.png</file>
<file>last.fm/ban.png</file>
<file>last.fm/icon_radio.png</file>
<file>last.fm/icon_tag.png</file>
<file>last.fm/icon_user.png</file>
<file>last.fm/lastfm.png</file>
<file>last.fm/loved_radio.png</file>
<file>last.fm/love.png</file>
<file>last.fm/my_friends.png</file>
<file>last.fm/my_neighbours.png</file>
<file>last.fm/neighbour_radio.png</file>
<file>last.fm/personal_radio.png</file>
<file>last.fm/recommended_radio.png</file>
<file>last.fm/user_purple.png</file>
<file>logo.png</file>
<file>lumberjacksong.txt</file>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 580 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -494,6 +494,7 @@ set(HEADERS
internet/magnatunesettingspage.h
internet/oauthenticator.h
internet/savedradio.h
internet/scrobbler.h
internet/searchboxwidget.h
internet/somafmservice.h
internet/somafmurlhandler.h
@ -817,13 +818,10 @@ optional_source(ENABLE_VISUALISATIONS
optional_source(HAVE_LIBLASTFM
SOURCES
covers/lastfmcoverprovider.cpp
globalsearch/lastfmsearchprovider.cpp
internet/fixlastfm.cpp
internet/lastfmcompat.cpp
internet/lastfmservice.cpp
internet/lastfmsettingspage.cpp
internet/lastfmstationdialog.cpp
internet/lastfmurlhandler.cpp
songinfo/echonestsimilarartists.cpp
songinfo/echonesttags.cpp
songinfo/lastfmtrackinfoprovider.cpp
@ -832,14 +830,12 @@ optional_source(HAVE_LIBLASTFM
covers/lastfmcoverprovider.h
internet/lastfmservice.h
internet/lastfmsettingspage.h
internet/lastfmstationdialog.h
songinfo/echonestsimilarartists.h
songinfo/echonesttags.h
songinfo/lastfmtrackinfoprovider.h
songinfo/tagwidget.h
UI
internet/lastfmsettingspage.ui
internet/lastfmstationdialog.ui
)

View File

@ -39,6 +39,10 @@
#include "podcasts/podcastdownloader.h"
#include "podcasts/podcastupdater.h"
#ifdef HAVE_LIBLASTFM
#include "internet/lastfmservice.h"
#endif // HAVE_LIBLASTFM
#ifdef HAVE_MOODBAR
#include "moodbar/moodbarcontroller.h"
#include "moodbar/moodbarloader.h"
@ -69,7 +73,8 @@ Application::Application(QObject* parent)
moodbar_loader_(nullptr),
moodbar_controller_(nullptr),
network_remote_(nullptr),
network_remote_helper_(nullptr) {
network_remote_helper_(nullptr),
scrobbler_(nullptr) {
tag_reader_client_ = new TagReaderClient(this);
MoveToNewThread(tag_reader_client_);
tag_reader_client_->Start();
@ -116,6 +121,10 @@ Application::Application(QObject* parent)
// crash when a client connects before the manager is initialized!
network_remote_helper_ = new NetworkRemoteHelper(this);
#ifdef HAVE_LIBLASTFM
scrobbler_ = new LastFMService(this, this);
#endif // HAVE_LIBLASTFM
library_->Init();
DoInAMinuteOrSo(database_, SLOT(DoBackup()));

View File

@ -44,6 +44,7 @@ class PodcastDownloader;
class PlaylistManager;
class PodcastBackend;
class PodcastUpdater;
class Scrobbler;
class TagReaderClient;
class TaskManager;
@ -86,6 +87,7 @@ class Application : public QObject {
NetworkRemoteHelper* network_remote_helper() const {
return network_remote_helper_;
}
Scrobbler* scrobbler() const { return scrobbler_; }
LibraryBackend* library_backend() const;
LibraryModel* library_model() const;
@ -128,6 +130,7 @@ signals:
MoodbarController* moodbar_controller_;
NetworkRemote* network_remote_;
NetworkRemoteHelper* network_remote_helper_;
Scrobbler* scrobbler_;
QList<QObject*> objects_in_threads_;
QList<QThread*> threads_;

View File

@ -76,7 +76,7 @@ void Player::Init() {
engine_->SetVolume(settings_.value("volume", 50).toInt());
#ifdef HAVE_LIBLASTFM
lastfm_ = InternetModel::Service<LastFMService>();
lastfm_ = app_->scrobbler();
#endif
}

View File

@ -31,7 +31,7 @@
#include "playlist/playlistitem.h"
class Application;
class LastFMService;
class Scrobbler;
class PlayerInterface : public QObject {
Q_OBJECT
@ -173,7 +173,7 @@ class Player : public PlayerInterface {
private:
Application* app_;
LastFMService* lastfm_;
Scrobbler* lastfm_;
QSettings settings_;
PlaylistItemPtr current_item_;

View File

@ -1,86 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#include "lastfmsearchprovider.h"
#include "core/logging.h"
#include "internet/lastfmservice.h"
LastFMSearchProvider::LastFMSearchProvider(LastFMService* service,
Application* app, QObject* parent)
: SimpleSearchProvider(app, parent), service_(service) {
Init("Last.fm", "lastfm", QIcon(":last.fm/as.png"),
CanShowConfig | CanGiveSuggestions);
icon_ = ScaleAndPad(QImage(":last.fm/as.png"));
set_safe_words(QStringList() << "lastfm"
<< "last.fm");
set_max_suggestion_count(3);
connect(service, SIGNAL(SavedItemsChanged()), SLOT(MaybeRecreateItems()));
// Load the friends list on startup only if it doesn't involve going to update
// info from the server.
if (!service_->IsFriendsListStale()) RecreateItems();
}
void LastFMSearchProvider::LoadArtAsync(int id, const Result& result) {
// TODO: Maybe we should try to get user pictures for friends?
emit ArtLoaded(id, icon_);
}
void LastFMSearchProvider::RecreateItems() {
QList<Item> items;
items << Item(tr("My Last.fm Recommended Radio"),
QUrl("lastfm://user/USERNAME/recommended"), "recommended");
items << Item(tr("My Last.fm Library"),
QUrl("lastfm://user/USERNAME/library"), "radio");
items << Item(tr("My Last.fm Mix Radio"), QUrl("lastfm://user/USERNAME/mix"),
"mix");
items << Item(tr("My Last.fm Neighborhood"),
QUrl("lastfm://user/USERNAME/neighbours"), "neighborhood");
const QStringList artists = service_->SavedArtistRadioNames();
const QStringList tags = service_->SavedTagRadioNames();
const QStringList friends = service_->FriendNames();
for (const QString& name : artists) {
items << Item(tr(LastFMService::kTitleArtist).arg(name),
QUrl(QString(LastFMService::kUrlArtist).arg(name)), name);
}
for (const QString& name : tags) {
items << Item(tr(LastFMService::kTitleTag).arg(name),
QUrl(QString(LastFMService::kUrlTag).arg(name)), name);
}
for (const QString& name : friends) {
items << Item(tr("Last.fm Radio Station - %1").arg(name),
QUrl("lastfm://user/" + name + "/library"), name);
items << Item(tr("Last.fm Mix Radio - %1").arg(name),
QUrl("lastfm://user/" + name + "/mix"), name);
items << Item(tr("Last.fm Neighbor Radio - %1").arg(name),
QUrl("lastfm://user/" + name + "/neighbours"), name);
}
SetItems(items);
}
bool LastFMSearchProvider::IsLoggedIn() { return service_->IsAuthenticated(); }
void LastFMSearchProvider::ShowConfig() { service_->ShowConfig(); }

View File

@ -1,43 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#ifndef LASTFMSEARCHPROVIDER_H
#define LASTFMSEARCHPROVIDER_H
#include "simplesearchprovider.h"
class LastFMService;
class LastFMSearchProvider : public SimpleSearchProvider {
public:
LastFMSearchProvider(LastFMService* service, Application* app,
QObject* parent);
void LoadArtAsync(int id, const Result& result);
bool IsLoggedIn();
void ShowConfig();
protected:
void RecreateItems();
private:
LastFMService* service_;
QImage icon_;
};
#endif // LASTFMSEARCHPROVIDER_H

View File

@ -38,9 +38,6 @@
#include "podcasts/podcastservice.h"
#include "smartplaylists/generatormimedata.h"
#ifdef HAVE_LIBLASTFM
#include "lastfmservice.h"
#endif
#ifdef HAVE_GOOGLE_DRIVE
#include "googledriveservice.h"
#endif
@ -57,7 +54,7 @@
#include "boxservice.h"
#endif
#ifdef HAVE_VK
#include "vkservice.h"
#include "vkservice.h"
#endif
using smart_playlists::Generator;
@ -80,9 +77,6 @@ InternetModel::InternetModel(Application* app, QObject* parent)
AddService(new DigitallyImportedService(app, this));
AddService(new IcecastService(app, this));
AddService(new JamendoService(app, this));
#ifdef HAVE_LIBLASTFM
AddService(new LastFMService(app, this));
#endif
#ifdef HAVE_GOOGLE_DRIVE
AddService(new GoogleDriveService(app, this));
#endif
@ -172,13 +166,13 @@ InternetService* InternetModel::ServiceByName(const QString& name) {
return nullptr;
}
InternetService* InternetModel::ServiceForItem(const QStandardItem* item)
const {
InternetService* InternetModel::ServiceForItem(
const QStandardItem* item) const {
return ServiceForIndex(indexFromItem(item));
}
InternetService* InternetModel::ServiceForIndex(const QModelIndex& index)
const {
InternetService* InternetModel::ServiceForIndex(
const QModelIndex& index) const {
QModelIndex current_index = index;
while (current_index.isValid()) {
InternetService* service =

View File

@ -43,8 +43,6 @@
#endif
#include "lastfmcompat.h"
#include "lastfmstationdialog.h"
#include "lastfmurlhandler.h"
#include "internetmodel.h"
#include "internetplaylistitem.h"
#include "core/application.h"
@ -55,8 +53,6 @@
#include "core/taskmanager.h"
#include "covers/coverproviders.h"
#include "covers/lastfmcoverprovider.h"
#include "globalsearch/globalsearch.h"
#include "globalsearch/lastfmsearchprovider.h"
#include "ui/iconloader.h"
#include "ui/settingsdialog.h"
@ -73,72 +69,20 @@ const char* LastFMService::kAudioscrobblerClientId = "tng";
const char* LastFMService::kApiKey = "75d20fb472be99275392aefa2760ea09";
const char* LastFMService::kSecret = "d3072b60ae626be12be69448f5c46e70";
const char* LastFMService::kUrlArtist = "lastfm://artist/%1/similarartists";
const char* LastFMService::kUrlTag = "lastfm://globaltags/%1";
const char* LastFMService::kUrlCustom = "lastfm://rql/%1";
const char* LastFMService::kTitleArtist =
QT_TR_NOOP("Last.fm Similar Artists to %1");
const char* LastFMService::kTitleTag = QT_TR_NOOP("Last.fm Tag Radio: %1");
const char* LastFMService::kTitleCustom =
QT_TR_NOOP("Last.fm Custom Radio: %1");
const int LastFMService::kFriendsCacheDurationSecs = 60 * 60 * 24; // 1 day
LastFMService::LastFMService(Application* app, InternetModel* parent)
: InternetService(kServiceName, app, parent, parent),
url_handler_(new LastFMUrlHandler(this, this)),
LastFMService::LastFMService(Application* app, QObject* parent)
: Scrobbler(parent),
scrobbler_(nullptr),
already_scrobbled_(false),
station_dialog_(new LastFMStationDialog),
context_menu_(new QMenu),
initial_tune_(false),
tune_task_id_(0),
scrobbling_enabled_(false),
root_item_(nullptr),
artist_list_(nullptr),
tag_list_(nullptr),
custom_list_(nullptr),
friends_list_(nullptr),
neighbours_list_(nullptr),
friend_names_(kSettingsGroup, "friend_names", kFriendsCacheDurationSecs),
connection_problems_(false) {
connection_problems_(false),
app_(app) {
ReloadSettings();
// we emit the signal the first time to be sure the buttons are in the right
// state
emit ScrobblingEnabledChanged(scrobbling_enabled_);
context_menu_->addActions(GetPlaylistActions());
remove_action_ = context_menu_->addAction(IconLoader::Load("list-remove"),
tr("Remove"), this, SLOT(Remove()));
context_menu_->addSeparator();
add_artist_action_ = context_menu_->addAction(
QIcon(":last.fm/icon_radio.png"), tr("Play artist radio..."), this,
SLOT(AddArtistRadio()));
add_tag_action_ = context_menu_->addAction(QIcon(":last.fm/icon_tag.png"),
tr("Play tag radio..."), this,
SLOT(AddTagRadio()));
add_custom_action_ = context_menu_->addAction(
QIcon(":last.fm/icon_radio.png"), tr("Play custom radio..."), this,
SLOT(AddCustomRadio()));
refresh_friends_action_ = context_menu_->addAction(
IconLoader::Load("view-refresh"), tr("Refresh friends list"), this,
SLOT(ForceRefreshFriends()));
context_menu_->addAction(IconLoader::Load("configure"),
tr("Configure Last.fm..."), this,
SLOT(ShowConfig()));
remove_action_->setEnabled(false);
add_artist_action_->setEnabled(false);
add_tag_action_->setEnabled(false);
add_custom_action_->setEnabled(false);
app_->player()->RegisterUrlHandler(url_handler_);
app_->cover_providers()->AddProvider(new LastFmCoverProvider(this));
app_->global_search()->AddProvider(
new LastFMSearchProvider(this, app_, this));
}
LastFMService::~LastFMService() {}
@ -154,7 +98,6 @@ void LastFMService::ReloadSettings() {
scrobble_button_visible_ =
settings.value("ShowScrobbleButton", true).toBool();
prefer_albumartist_ = settings.value("PreferAlbumArtist", false).toBool();
friend_names_.Load();
// avoid emitting signal if it's not changed
if (scrobbling_enabled_old != scrobbling_enabled_)
@ -178,121 +121,6 @@ bool LastFMService::IsSubscriber() const {
return settings.value("Subscriber", false).toBool();
}
QStandardItem* LastFMService::CreateRootItem() {
root_item_ = new QStandardItem(QIcon(":last.fm/as.png"), kServiceName);
root_item_->setData(true, InternetModel::Role_CanLazyLoad);
return root_item_;
}
void LastFMService::LazyPopulate(QStandardItem* parent) {
switch (parent->data(InternetModel::Role_Type).toInt()) {
case InternetModel::Type_Service:
// Normal radio types
CreateStationItem(parent, tr("My Recommendations"),
":last.fm/recommended_radio.png",
QUrl("lastfm://user/USERNAME/recommended"),
tr("My Last.fm Recommended Radio"));
CreateStationItem(
parent, tr("My Radio Station"), ":last.fm/personal_radio.png",
QUrl("lastfm://user/USERNAME/library"), tr("My Last.fm Library"));
CreateStationItem(parent, tr("My Mix Radio"), ":last.fm/loved_radio.png",
QUrl("lastfm://user/USERNAME/mix"),
tr("My Last.fm Mix Radio"));
CreateStationItem(parent, tr("My Neighborhood"),
":last.fm/neighbour_radio.png",
QUrl("lastfm://user/USERNAME/neighbours"),
tr("My Last.fm Neighborhood"));
// Types that have children
artist_list_ = new QStandardItem(QIcon(":last.fm/icon_radio.png"),
tr("Artist radio"));
artist_list_->setData(Type_Artists, InternetModel::Role_Type);
parent->appendRow(artist_list_);
tag_list_ =
new QStandardItem(QIcon(":last.fm/icon_tag.png"), tr("Tag radio"));
tag_list_->setData(Type_Tags, InternetModel::Role_Type);
parent->appendRow(tag_list_);
custom_list_ = new QStandardItem(QIcon(":last.fm/icon_radio.png"),
tr("Custom radio"));
custom_list_->setData(Type_Custom, InternetModel::Role_Type);
parent->appendRow(custom_list_);
RestoreList("artists", kUrlArtist, tr(kTitleArtist),
QIcon(":last.fm/icon_radio.png"), artist_list_);
RestoreList("tags", kUrlTag, tr(kTitleTag),
QIcon(":last.fm/icon_tag.png"), tag_list_);
RestoreList("custom", kUrlCustom, tr(kTitleCustom),
QIcon(":last.fm/icon_radio.png"), custom_list_);
friends_list_ =
new QStandardItem(QIcon(":last.fm/my_friends.png"), tr("Friends"));
friends_list_->setData(Type_Friends, InternetModel::Role_Type);
friends_list_->setData(true, InternetModel::Role_CanLazyLoad);
parent->appendRow(friends_list_);
neighbours_list_ = new QStandardItem(QIcon(":last.fm/my_neighbours.png"),
tr("Neighbors"));
neighbours_list_->setData(Type_Neighbours, InternetModel::Role_Type);
neighbours_list_->setData(true, InternetModel::Role_CanLazyLoad);
parent->appendRow(neighbours_list_);
if (!IsAuthenticated()) ShowConfig();
add_artist_action_->setEnabled(true);
add_tag_action_->setEnabled(true);
add_custom_action_->setEnabled(true);
break;
case Type_Friends:
RefreshFriends(false);
break;
case Type_Neighbours:
RefreshNeighbours();
break;
case Type_OtherUser:
CreateStationItem(parent,
tr("Last.fm Radio Station - %1").arg(parent->text()),
":last.fm/personal_radio.png",
QUrl("lastfm://user/" + parent->text() + "/library"),
tr("Last.fm Library - %1").arg(parent->text()));
CreateStationItem(parent,
tr("Last.fm Mix Radio - %1").arg(parent->text()),
":last.fm/loved_radio.png",
QUrl("lastfm://user/" + parent->text() + "/mix"),
tr("Last.fm Mix Radio - %1").arg(parent->text()));
CreateStationItem(parent,
tr("Last.fm Neighbor Radio - %1").arg(parent->text()),
":last.fm/neighbour_radio.png",
QUrl("lastfm://user/" + parent->text() + "/neighbours"),
tr("Last.fm Neighbor Radio - %1").arg(parent->text()));
break;
default:
break;
}
}
QStandardItem* LastFMService::CreateStationItem(QStandardItem* parent,
const QString& name,
const QString& icon,
const QUrl& url,
const QString& title) {
Song song;
song.set_url(url);
song.set_title(title);
QStandardItem* ret = new QStandardItem(QIcon(icon), name);
ret->setData(QVariant::fromValue(song), InternetModel::Role_SongMetadata);
ret->setData(InternetModel::PlayBehaviour_SingleItem,
InternetModel::Role_PlayBehaviour);
parent->appendRow(ret);
return ret;
}
void LastFMService::Authenticate(const QString& username,
const QString& password) {
QMap<QString, QString> params;
@ -311,8 +139,6 @@ void LastFMService::SignOut() {
lastfm::ws::Username.clear();
lastfm::ws::SessionKey.clear();
friend_names_.Update(QStringList());
QSettings settings;
settings.beginGroup(kSettingsGroup);
@ -386,51 +212,6 @@ QUrl LastFMService::FixupUrl(const QUrl& url) {
return ret;
}
QUrl LastFMService::DeququeNextMediaUrl() {
if (playlist_.empty()) {
return QUrl();
}
lastfm::MutableTrack track = playlist_.dequeue();
track.stamp();
already_scrobbled_ = false;
last_track_ = track;
if (playlist_.empty()) {
FetchMoreTracks();
}
next_metadata_ = track;
StreamMetadataReady();
return last_track_.url();
}
void LastFMService::StreamMetadataReady() {
Song metadata;
metadata.InitFromLastFM(next_metadata_);
if (art_urls_.contains(next_metadata_))
metadata.set_art_automatic(art_urls_[next_metadata_]);
emit StreamMetadataFound(last_url_, metadata);
}
void LastFMService::TunerError(lastfm::ws::Error error) {
qLog(Warning) << "Last.fm error" << error;
if (!initial_tune_) return;
app_->task_manager()->SetTaskFinished(tune_task_id_);
tune_task_id_ = 0;
if (error == lastfm::ws::NotEnoughContent) {
url_handler_->TunerError();
return;
}
emit StreamError(ErrorString(error));
}
QString LastFMService::ErrorString(lastfm::ws::Error error) const {
switch (error) {
case lastfm::ws::InvalidService:
@ -477,13 +258,6 @@ QString LastFMService::ErrorString(lastfm::ws::Error error) const {
}
}
void LastFMService::TunerTrackAvailable() {
if (initial_tune_) {
url_handler_->TunerTrackAvailable();
initial_tune_ = false;
}
}
bool LastFMService::InitScrobbler() {
if (!IsAuthenticated() || !IsScrobblingEnabled()) return false;
@ -610,357 +384,6 @@ void LastFMService::Ban() {
app_->player()->Next();
}
void LastFMService::ShowContextMenu(const QPoint& global_pos) {
switch (model()
->current_index()
.parent()
.data(InternetModel::Role_Type)
.toInt()) {
case Type_Artists:
case Type_Tags:
case Type_Custom:
remove_action_->setEnabled(true);
break;
default:
remove_action_->setEnabled(false);
break;
}
const bool playable = model()->IsPlayable(model()->current_index());
GetAppendToPlaylistAction()->setEnabled(playable);
GetReplacePlaylistAction()->setEnabled(playable);
GetOpenInNewPlaylistAction()->setEnabled(playable);
context_menu_->popup(global_pos);
}
QStringList LastFMService::FriendNames() {
// Update the list for next time, in the main thread.
if (IsFriendsListStale())
metaObject()->invokeMethod(this, "RefreshFriends", Qt::QueuedConnection);
return friend_names_.Data();
}
static QStringList SavedArtistOrTagRadioNames(const QString& name) {
QStringList ret;
QSettings s;
s.beginGroup(LastFMService::kSettingsGroup);
int count = s.beginReadArray(name);
for (int i = 0; i < count; ++i) {
s.setArrayIndex(i);
ret << s.value("key").toString();
}
s.endArray();
return ret;
}
QStringList LastFMService::SavedArtistRadioNames() const {
return SavedArtistOrTagRadioNames("artists");
}
QStringList LastFMService::SavedTagRadioNames() const {
return SavedArtistOrTagRadioNames("tags");
}
void LastFMService::RefreshFriends() { RefreshFriends(false); }
void LastFMService::ForceRefreshFriends() { RefreshFriends(true); }
void LastFMService::RefreshFriends(bool force) {
if (!IsAuthenticated()) {
return;
}
if (!friends_list_) {
root_item_->setData(false, InternetModel::Role_CanLazyLoad);
LazyPopulate(root_item_);
}
if (!force && !IsFriendsListStale()) {
PopulateFriendsList();
return;
}
lastfm::compat::AuthenticatedUser user;
QNetworkReply* reply = user.getFriends();
NewClosure(reply, SIGNAL(finished()), this,
SLOT(RefreshFriendsFinished(QNetworkReply*)), reply);
}
void LastFMService::RefreshNeighbours() {
if (!neighbours_list_ || !IsAuthenticated()) return;
lastfm::compat::AuthenticatedUser user;
QNetworkReply* reply = user.getNeighbours();
NewClosure(reply, SIGNAL(finished()), this,
SLOT(RefreshNeighboursFinished(QNetworkReply*)), reply);
}
void LastFMService::RefreshFriendsFinished(QNetworkReply* reply) {
QList<lastfm::User> friends;
if (!lastfm::compat::ParseUserList(reply, &friends)) {
return;
}
QStringList names;
for (const lastfm::User& f : friends) {
names << f.name();
}
friend_names_.Update(names);
PopulateFriendsList();
emit SavedItemsChanged();
}
void LastFMService::PopulateFriendsList() {
if (friends_list_->hasChildren())
friends_list_->removeRows(0, friends_list_->rowCount());
for (const QString& name : friend_names_) {
Song song;
song.set_url(QUrl("lastfm://user/" + name + "/library"));
song.set_title(tr("Last.fm Library - %1").arg(name));
QStandardItem* item =
new QStandardItem(QIcon(":last.fm/icon_user.png"), name);
item->setData(QVariant::fromValue(song), InternetModel::Role_SongMetadata);
item->setData(true, InternetModel::Role_CanLazyLoad);
item->setData(Type_OtherUser, InternetModel::Role_Type);
item->setData(InternetModel::PlayBehaviour_SingleItem,
InternetModel::Role_PlayBehaviour);
friends_list_->appendRow(item);
}
}
void LastFMService::RefreshNeighboursFinished(QNetworkReply* reply) {
QList<lastfm::User> neighbours;
if (!lastfm::compat::ParseUserList(reply, &neighbours)) {
return;
}
if (neighbours_list_->hasChildren())
neighbours_list_->removeRows(0, neighbours_list_->rowCount());
for (const lastfm::User& n : neighbours) {
Song song;
song.set_url(QUrl("lastfm://user/" + n.name() + "/library"));
song.set_title(tr("Last.fm Library - %1").arg(n.name()));
QStandardItem* item =
new QStandardItem(QIcon(":last.fm/user_purple.png"), n.name());
item->setData(QVariant::fromValue(song), InternetModel::Role_SongMetadata);
item->setData(true, InternetModel::Role_CanLazyLoad);
item->setData(Type_OtherUser, InternetModel::Role_Type);
item->setData(InternetModel::PlayBehaviour_SingleItem,
InternetModel::Role_PlayBehaviour);
neighbours_list_->appendRow(item);
}
}
void LastFMService::AddArtistRadio() {
AddArtistOrTag("artists", LastFMStationDialog::Artist, kUrlArtist,
tr(kTitleArtist), ":last.fm/icon_radio.png", artist_list_);
}
void LastFMService::AddTagRadio() {
AddArtistOrTag("tags", LastFMStationDialog::Tag, kUrlTag, tr(kTitleTag),
":last.fm/icon_tag.png", tag_list_);
}
void LastFMService::AddCustomRadio() {
AddArtistOrTag("custom", LastFMStationDialog::Custom, kUrlCustom,
tr(kTitleCustom), ":last.fm/icon_radio.png", custom_list_);
}
void LastFMService::AddArtistOrTag(const QString& name,
LastFMStationDialog::Type dialog_type,
const QString& url_pattern,
const QString& title_pattern,
const QString& icon, QStandardItem* list) {
station_dialog_->SetType(dialog_type);
if (station_dialog_->exec() == QDialog::Rejected) return;
if (station_dialog_->content().isEmpty()) return;
QString content = station_dialog_->content();
QString url;
if (name == "custom" && content.startsWith("lastfm://")) {
url = content;
} else if (name == "custom") {
url = url_pattern.arg(QString(content.toUtf8().toBase64()));
} else {
url = url_pattern.arg(content);
}
Song song;
song.set_url(QUrl((url)));
song.set_title(title_pattern.arg(content));
QStandardItem* item = new QStandardItem(QIcon(icon), content);
item->setData(QVariant::fromValue(song), InternetModel::Role_SongMetadata);
item->setData(InternetModel::PlayBehaviour_SingleItem,
InternetModel::Role_PlayBehaviour);
list->appendRow(item);
emit AddItemToPlaylist(item->index(), AddMode_Append);
SaveList(name, list);
emit SavedItemsChanged();
}
void LastFMService::SaveList(const QString& name, QStandardItem* list) const {
QSettings settings;
settings.beginGroup(kSettingsGroup);
settings.beginWriteArray(name, list->rowCount());
for (int i = 0; i < list->rowCount(); ++i) {
settings.setArrayIndex(i);
settings.setValue("key", list->child(i)->text());
}
settings.endArray();
}
void LastFMService::RestoreList(const QString& name, const QString& url_pattern,
const QString& title_pattern, const QIcon& icon,
QStandardItem* parent) {
if (parent->hasChildren()) parent->removeRows(0, parent->rowCount());
const QStringList keys = SavedArtistOrTagRadioNames(name);
for (const QString& key : keys) {
QString url;
if (name == "custom" && key.startsWith("lastfm://")) {
url = key;
} else if (name == "custom") {
url = url_pattern.arg(QString(key.toUtf8().toBase64()));
} else {
url = url_pattern.arg(key);
}
Song song;
song.set_url(QUrl(url));
song.set_title(title_pattern.arg(key));
QStandardItem* item = new QStandardItem(icon, key);
item->setData(QVariant::fromValue(song), InternetModel::Role_SongMetadata);
item->setData(InternetModel::PlayBehaviour_SingleItem,
InternetModel::Role_PlayBehaviour);
parent->appendRow(item);
}
}
void LastFMService::Remove() {
QStandardItem* context_item =
model()->itemFromIndex(model()->current_index());
int type = context_item->parent()->data(InternetModel::Role_Type).toInt();
context_item->parent()->removeRow(context_item->row());
if (type == Type_Artists)
SaveList("artists", artist_list_);
else if (type == Type_Tags)
SaveList("tags", tag_list_);
else if (type == Type_Custom)
SaveList("custom", custom_list_);
}
void LastFMService::FetchMoreTracks() {
QMap<QString, QString> params;
params["method"] = "radio.getPlaylist";
params["rtp"] = "1";
QNetworkReply* reply = lastfm::ws::post(params);
NewClosure(reply, SIGNAL(finished()), this,
SLOT(FetchMoreTracksFinished(QNetworkReply*)), reply);
}
void LastFMService::FetchMoreTracksFinished(QNetworkReply* reply) {
reply->deleteLater();
app_->task_manager()->SetTaskFinished(tune_task_id_);
tune_task_id_ = 0;
lastfm::XmlQuery query(lastfm::compat::EmptyXmlQuery());
if (lastfm::compat::ParseQuery(reply->readAll(), &query)) {
const XmlQuery& playlist = query["playlist"];
for (const XmlQuery& q : playlist["trackList"].children("track")) {
lastfm::MutableTrack t;
t.setUrl(QUrl(q["location"].text()));
t.setExtra("trackauth", q["extension"]["trackauth"].text());
t.setTitle(q["title"].text());
t.setArtist(q["creator"].text());
t.setAlbum(q["album"].text());
t.setDuration(q["duration"].text().toInt() / 1000);
t.setSource(lastfm::Track::LastFmRadio);
art_urls_[t] = q["image"].text();
playlist_ << t;
}
} else {
emit StreamError(tr("Couldn't load the last.fm radio station"));
return;
}
TunerTrackAvailable();
}
void LastFMService::Tune(const QUrl& url) {
if (!tune_task_id_)
tune_task_id_ =
app_->task_manager()->StartTask(tr("Loading Last.fm radio"));
last_url_ = url;
initial_tune_ = true;
const lastfm::RadioStation station(FixupUrl(url).toString());
playlist_.clear();
// Remove all the old album art URLs
art_urls_.clear();
QMap<QString, QString> params;
params["method"] = "radio.tune";
params["station"] = station.url();
QNetworkReply* reply = lastfm::ws::post(params);
NewClosure(reply, SIGNAL(finished()), this,
SLOT(TuneFinished(QNetworkReply*)), reply);
}
void LastFMService::TuneFinished(QNetworkReply* reply) {
reply->deleteLater();
FetchMoreTracks();
}
PlaylistItem::Options LastFMService::playlistitem_options() const {
return PlaylistItem::LastFMControls | PlaylistItem::PauseDisabled |
PlaylistItem::SeekDisabled;
}
PlaylistItemPtr LastFMService::PlaylistItemForUrl(const QUrl& url) {
// This is a bit of a hack, it's only used by the artist/song info tag
// widgets for tag radio and similar artists radio.
if (url.scheme() != "lastfm") return PlaylistItemPtr();
QStringList sections(url.path().split("/", QString::SkipEmptyParts));
Song song;
song.set_url(url);
if (sections.count() == 2 && url.host() == "artist" &&
sections[1] == "similarartists") {
song.set_title(tr(kTitleArtist).arg(sections[0]));
} else if (sections.count() == 1 && url.host() == "globaltags") {
song.set_title(tr(kTitleTag).arg(sections[0]));
} else {
return PlaylistItemPtr();
}
return PlaylistItemPtr(new InternetPlaylistItem(this, song));
}
void LastFMService::ToggleScrobbling() {
// toggle status
scrobbling_enabled_ = !scrobbling_enabled_;

View File

@ -21,7 +21,6 @@
#include <memory>
namespace lastfm {
class RadioStation;
class Track;
}
@ -30,29 +29,19 @@ uint qHash(const lastfm::Track& track);
#include "lastfmcompat.h"
#include "internetmodel.h"
#include "internetservice.h"
#include "lastfmstationdialog.h"
#include "core/cachedlist.h"
#include "core/song.h"
#include "playlist/playlistitem.h"
#include <QDateTime>
#include <QMap>
#include <QMenu>
#include <QQueue>
#include "internet/scrobbler.h"
class Application;
class LastFMUrlHandler;
class QAction;
class QNetworkAccessManager;
class Song;
class LastFMService : public InternetService {
class LastFMService : public Scrobbler {
Q_OBJECT
friend class LastFMUrlHandler;
public:
LastFMService(Application* app, InternetModel* parent);
LastFMService(Application* app, QObject* parent = nullptr);
~LastFMService();
static const char* kServiceName;
@ -61,34 +50,6 @@ class LastFMService : public InternetService {
static const char* kApiKey;
static const char* kSecret;
static const char* kUrlArtist;
static const char* kUrlTag;
static const char* kUrlCustom;
static const char* kTitleArtist;
static const char* kTitleTag;
static const char* kTitleCustom;
static const int kFriendsCacheDurationSecs;
enum ItemType {
Type_Root = InternetModel::TypeCount,
Type_Artists,
Type_Tags,
Type_Custom,
Type_Friends,
Type_Neighbours,
Type_OtherUser,
};
// InternetService
QStandardItem* CreateRootItem();
void LazyPopulate(QStandardItem* parent);
void ShowContextMenu(const QPoint& global_pos);
PlaylistItem::Options playlistitem_options() const;
void ReloadSettings();
virtual QString Icon() { return ":last.fm/lastfm.png"; }
@ -106,18 +67,6 @@ class LastFMService : public InternetService {
void SignOut();
void UpdateSubscriberStatus();
void FetchMoreTracks();
QUrl DeququeNextMediaUrl();
PlaylistItemPtr PlaylistItemForUrl(const QUrl& url);
bool IsFriendsListStale() const { return friend_names_.IsStale(); }
// Thread safe
QStringList FriendNames();
QStringList SavedArtistRadioNames() const;
QStringList SavedTagRadioNames() const;
public slots:
void NowPlaying(const Song& song);
void Scrobble();
@ -142,91 +91,35 @@ signals:
private slots:
void AuthenticateReplyFinished(QNetworkReply* reply);
void UpdateSubscriberStatusFinished(QNetworkReply* reply);
void RefreshFriendsFinished(QNetworkReply* reply);
void RefreshNeighboursFinished(QNetworkReply* reply);
void TunerTrackAvailable();
void TunerError(lastfm::ws::Error error);
void ScrobblerStatus(int value);
void AddArtistRadio();
void AddTagRadio();
void AddCustomRadio();
void ForceRefreshFriends();
void RefreshFriends();
void Remove();
// Radio tuner.
void FetchMoreTracksFinished(QNetworkReply* reply);
void TuneFinished(QNetworkReply* reply);
void StreamMetadataReady();
private:
QStandardItem* CreateStationItem(QStandardItem* parent, const QString& name,
const QString& icon, const QUrl& url,
const QString& title);
QString ErrorString(lastfm::ws::Error error) const;
bool InitScrobbler();
lastfm::Track TrackFromSong(const Song& song) const;
void RefreshFriends(bool force);
void RefreshNeighbours();
void AddArtistOrTag(const QString& name,
LastFMStationDialog::Type dialog_type,
const QString& url_pattern, const QString& title_pattern,
const QString& icon, QStandardItem* list);
void SaveList(const QString& name, QStandardItem* list) const;
void RestoreList(const QString& name, const QString& url_pattern,
const QString& title_pattern, const QIcon& icon,
QStandardItem* parent);
static QUrl FixupUrl(const QUrl& url);
void Tune(const QUrl& station);
void PopulateFriendsList();
void AddSelectedToPlaylist(bool clear_first);
private:
LastFMUrlHandler* url_handler_;
lastfm::Audioscrobbler* scrobbler_;
lastfm::Track last_track_;
lastfm::Track next_metadata_;
QQueue<lastfm::Track> playlist_;
bool already_scrobbled_;
std::unique_ptr<LastFMStationDialog> station_dialog_;
std::unique_ptr<QMenu> context_menu_;
QAction* remove_action_;
QAction* add_artist_action_;
QAction* add_tag_action_;
QAction* add_custom_action_;
QAction* refresh_friends_action_;
QUrl last_url_;
bool initial_tune_;
int tune_task_id_;
bool scrobbling_enabled_;
bool buttons_visible_;
bool scrobble_button_visible_;
bool prefer_albumartist_;
QStandardItem* root_item_;
QStandardItem* artist_list_;
QStandardItem* tag_list_;
QStandardItem* custom_list_;
QStandardItem* friends_list_;
QStandardItem* neighbours_list_;
QHash<lastfm::Track, QString> art_urls_;
CachedList<QString> friend_names_;
// Useful to inform the user that we can't scrobble right now
bool connection_problems_;
Application* app_;
};
#endif // LASTFMSERVICE_H

View File

@ -16,21 +16,21 @@
*/
#include "lastfmsettingspage.h"
#include "lastfmservice.h"
#include "internetmodel.h"
#include "ui_lastfmsettingspage.h"
#include "ui/iconloader.h"
#include <lastfm/ws.h>
#include <QMessageBox>
#include <QMovie>
#include <QSettings>
#include "lastfmservice.h"
#include "internetmodel.h"
#include "core/application.h"
#include "ui/iconloader.h"
LastFMSettingsPage::LastFMSettingsPage(SettingsDialog* dialog)
: SettingsPage(dialog),
service_(
static_cast<LastFMService*>(InternetModel::ServiceByName("Last.fm"))),
service_(static_cast<LastFMService*>(dialog->app()->scrobbler())),
ui_(new Ui_LastFMSettingsPage),
waiting_for_auth_(false) {
ui_->setupUi(this);
@ -40,8 +40,6 @@ LastFMSettingsPage::LastFMSettingsPage(SettingsDialog* dialog)
connect(service_, SIGNAL(AuthenticationComplete(bool, QString)),
SLOT(AuthenticationComplete(bool, QString)));
connect(service_, SIGNAL(UpdatedSubscriberStatus(bool)),
SLOT(UpdatedSubscriberStatus(bool)));
connect(ui_->login_state, SIGNAL(LogoutClicked()), SLOT(Logout()));
connect(ui_->login_state, SIGNAL(LoginClicked()), SLOT(Login()));
connect(ui_->login, SIGNAL(clicked()), SLOT(Login()));
@ -83,7 +81,6 @@ void LastFMSettingsPage::AuthenticationComplete(bool success,
}
RefreshControls(success);
service_->UpdateSubscriberStatus();
}
void LastFMSettingsPage::Load() {
@ -92,31 +89,9 @@ void LastFMSettingsPage::Load() {
ui_->scrobble_button->setChecked(service_->IsScrobbleButtonVisible());
ui_->prefer_albumartist->setChecked(service_->PreferAlbumArtist());
if (service_->IsAuthenticated()) {
service_->UpdateSubscriberStatus();
}
RefreshControls(service_->IsAuthenticated());
}
void LastFMSettingsPage::UpdatedSubscriberStatus(bool is_subscriber) {
ui_->login_state->SetAccountTypeVisible(!is_subscriber);
if (!is_subscriber) {
if (service_->HasConnectionProblems()) {
ui_->login_state->SetAccountTypeText(
tr("Clementine couldn't fetch your subscription status since there "
"are problems "
"with your connection. Played tracks will be cached and sent "
"later to Last.fm."));
} else {
ui_->login_state->SetAccountTypeText(
tr("You will not be able to play Last.fm radio stations "
"as you are not a Last.fm subscriber."));
}
}
}
void LastFMSettingsPage::Save() {
QSettings s;
s.beginGroup(LastFMService::kSettingsGroup);
@ -141,12 +116,4 @@ void LastFMSettingsPage::RefreshControls(bool authenticated) {
ui_->login_state->SetLoggedIn(
authenticated ? LoginStateWidget::LoggedIn : LoginStateWidget::LoggedOut,
lastfm::ws::Username);
ui_->login_state->SetAccountTypeVisible(!authenticated);
if (!authenticated) {
ui_->login_state->SetAccountTypeText(
tr("You can scrobble tracks for free, but only "
"<span style=\" font-weight:600;\">paid subscribers</span> "
"can stream Last.fm radio from Clementine."));
}
}

View File

@ -37,12 +37,10 @@ class LastFMSettingsPage : public SettingsPage {
void Login();
void AuthenticationComplete(bool success, const QString& error_message);
void Logout();
void UpdatedSubscriberStatus(bool is_subscriber);
private:
LastFMService* service_;
Ui_LastFMSettingsPage* ui_;
QMovie* loading_icon_;
bool waiting_for_auth_;

View File

@ -83,7 +83,7 @@
<item>
<widget class="QCheckBox" name="love_ban_">
<property name="text">
<string>Show the &quot;love&quot; and &quot;ban&quot; buttons</string>
<string>Show the &quot;love&quot; button</string>
</property>
<property name="checked">
<bool>true</bool>

View File

@ -1,36 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#include "lastfmstationdialog.h"
#include "ui_lastfmstationdialog.h"
LastFMStationDialog::LastFMStationDialog(QWidget* parent)
: QDialog(parent), ui_(new Ui_LastFMStationDialog) {
ui_->setupUi(this);
resize(sizeHint());
}
LastFMStationDialog::~LastFMStationDialog() { delete ui_; }
void LastFMStationDialog::SetType(Type type) {
ui_->type->setCurrentIndex(type);
ui_->content->clear();
ui_->content->setFocus(Qt::OtherFocusReason);
}
QString LastFMStationDialog::content() const { return ui_->content->text(); }

View File

@ -1,41 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#ifndef LASTFMSTATIONDIALOG_H
#define LASTFMSTATIONDIALOG_H
#include <QDialog>
class Ui_LastFMStationDialog;
class LastFMStationDialog : public QDialog {
Q_OBJECT
public:
LastFMStationDialog(QWidget* parent = nullptr);
~LastFMStationDialog();
enum Type { Artist, Tag, Custom, };
void SetType(Type type);
QString content() const;
private:
Ui_LastFMStationDialog* ui_;
};
#endif // LASTFMSTATIONDIALOG_H

View File

@ -1,119 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LastFMStationDialog</class>
<widget class="QDialog" name="LastFMStationDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>407</width>
<height>126</height>
</rect>
</property>
<property name="windowTitle">
<string>Play Artist or Tag</string>
</property>
<property name="windowIcon">
<iconset resource="../../data/data.qrc">
<normaloff>:/last.fm/as.png</normaloff>:/last.fm/as.png</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Enter an &lt;b&gt;artist&lt;/b&gt; or &lt;b&gt;tag&lt;/b&gt; to start listening to Last.fm radio.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="type">
<item>
<property name="text">
<string>Artist</string>
</property>
</item>
<item>
<property name="text">
<string>Tag</string>
</property>
</item>
<item>
<property name="text">
<string>Custom</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLineEdit" name="content"/>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>7</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../../data/data.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>LastFMStationDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>LastFMStationDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -1,46 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#include "lastfmservice.h"
#include "lastfmurlhandler.h"
LastFMUrlHandler::LastFMUrlHandler(LastFMService* service, QObject* parent)
: UrlHandler(parent), service_(service) {}
UrlHandler::LoadResult LastFMUrlHandler::StartLoading(const QUrl& url) {
if (!service_->IsAuthenticated()) return LoadResult();
service_->Tune(url);
return LoadResult(url, LoadResult::WillLoadAsynchronously);
}
void LastFMUrlHandler::TunerTrackAvailable() {
emit AsyncLoadComplete(LoadNext(service_->last_url_));
}
void LastFMUrlHandler::TunerError() {
emit AsyncLoadComplete(
LoadResult(service_->last_url_, LoadResult::NoMoreTracks));
}
UrlHandler::LoadResult LastFMUrlHandler::LoadNext(const QUrl& url) {
const QUrl media_url = service_->DeququeNextMediaUrl();
if (media_url.isEmpty()) {
return LoadResult();
}
return LoadResult(url, LoadResult::TrackAvailable, media_url);
}

36
src/internet/scrobbler.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef SCROBBLER_H
#define SCROBBLER_H
#include <QObject>
class Song;
class Scrobbler : public QObject {
Q_OBJECT
public:
Scrobbler(QObject* parent = nullptr) {}
virtual bool IsAuthenticated() const = 0;
virtual bool IsScrobblingEnabled() const = 0;
virtual bool AreButtonsVisible() const = 0;
virtual bool IsScrobbleButtonVisible() const = 0;
virtual bool PreferAlbumArtist() const = 0;
public slots:
virtual void NowPlaying(const Song& song) = 0;
virtual void Scrobble() = 0;
virtual void Love() = 0;
virtual void ToggleScrobbling() = 0;
virtual void ShowConfig() = 0;
signals:
void AuthenticationComplete(bool success, const QString& error_message);
void ScrobblingEnabledChanged(bool value);
void ButtonVisibilityChanged(bool value);
void ScrobbleButtonVisibilityChanged(bool value);
void ScrobbleSubmitted();
void ScrobbleError(int value);
};
#endif // SCROBBLER_H

View File

@ -70,10 +70,7 @@ IncomingDataParser::IncomingDataParser(Application* app) : app_(app) {
SLOT(RateCurrentSong(double)));
#ifdef HAVE_LIBLASTFM
connect(this, SIGNAL(Love()), InternetModel::Service<LastFMService>(),
SLOT(Love()));
connect(this, SIGNAL(Ban()), InternetModel::Service<LastFMService>(),
SLOT(Ban()));
connect(this, SIGNAL(Love()), app_->scrobbler(), SLOT(Love()));
#endif
}
@ -261,7 +258,6 @@ void IncomingDataParser::RemoveSongs(const pb::remote::Message& msg) {
}
void IncomingDataParser::ClientConnect(const pb::remote::Message& msg) {
// Always sned the Clementine infos
emit SendClementineInfo();

View File

@ -33,7 +33,7 @@ class MacSystemTrayIcon : public SystemTrayIcon {
void SetupMenu(QAction* previous, QAction* play, QAction* stop,
QAction* stop_after, QAction* next, QAction* mute,
QAction* love, QAction* ban, QAction* quit);
QAction* love, QAction* quit);
void SetNowPlaying(const Song& song, const QString& image_path);
void ClearNowPlaying();

View File

@ -177,7 +177,7 @@ MacSystemTrayIcon::~MacSystemTrayIcon() {}
void MacSystemTrayIcon::SetupMenu(QAction* previous, QAction* play,
QAction* stop, QAction* stop_after,
QAction* next, QAction* mute, QAction* love,
QAction* ban, QAction* quit) {
QAction* quit) {
p_.reset(new MacSystemTrayIconPrivate());
SetupMenuItem(previous);
SetupMenuItem(play);
@ -188,7 +188,6 @@ void MacSystemTrayIcon::SetupMenu(QAction* previous, QAction* play,
SetupMenuItem(mute);
p_->AddSeparator();
SetupMenuItem(love);
SetupMenuItem(ban);
Q_UNUSED(quit); // Mac already has a Quit item.
}

View File

@ -149,7 +149,7 @@
#endif
#ifdef HAVE_VK
# include "internet/vkservice.h"
#include "internet/vkservice.h"
#endif
#ifdef Q_OS_DARWIN
@ -342,15 +342,13 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
SLOT(StopAfterCurrent()));
connect(ui_->action_mute, SIGNAL(triggered()), app_->player(), SLOT(Mute()));
#ifdef HAVE_LIBLASTFM
connect(ui_->action_ban, SIGNAL(triggered()),
InternetModel::Service<LastFMService>(), SLOT(Ban()));
connect(ui_->action_love, SIGNAL(triggered()), SLOT(Love()));
connect(ui_->action_toggle_scrobbling, SIGNAL(triggered()),
InternetModel::Service<LastFMService>(), SLOT(ToggleScrobbling()));
connect(ui_->action_toggle_scrobbling, SIGNAL(triggered()), app_->scrobbler(),
SLOT(ToggleScrobbling()));
#endif
#ifdef HAVE_VK
connect(ui_->action_love, SIGNAL(triggered()),
connect(ui_->action_love, SIGNAL(triggered()),
InternetModel::Service<VkService>(), SLOT(AddToMyMusicCurrent()));
#endif
@ -424,7 +422,6 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
ui_->pause_play_button->setDefaultAction(ui_->action_play_pause);
ui_->stop_button->setDefaultAction(ui_->action_stop);
ui_->love_button->setDefaultAction(ui_->action_love);
ui_->ban_button->setDefaultAction(ui_->action_ban);
ui_->scrobbling_button->setDefaultAction(ui_->action_toggle_scrobbling);
ui_->clear_playlist_button->setDefaultAction(ui_->action_clear_playlist);
ui_->playlist->SetActions(
@ -654,14 +651,13 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
connect(app_->internet_model(), SIGNAL(ScrollToIndex(QModelIndex)),
SLOT(ScrollToInternetIndex(QModelIndex)));
#ifdef HAVE_LIBLASTFM
LastFMService* lastfm_service = InternetModel::Service<LastFMService>();
connect(lastfm_service, SIGNAL(ButtonVisibilityChanged(bool)),
connect(app_->scrobbler(), SIGNAL(ButtonVisibilityChanged(bool)),
SLOT(LastFMButtonVisibilityChanged(bool)));
connect(lastfm_service, SIGNAL(ScrobbleButtonVisibilityChanged(bool)),
connect(app_->scrobbler(), SIGNAL(ScrobbleButtonVisibilityChanged(bool)),
SLOT(ScrobbleButtonVisibilityChanged(bool)));
connect(lastfm_service, SIGNAL(ScrobblingEnabledChanged(bool)),
connect(app_->scrobbler(), SIGNAL(ScrobblingEnabledChanged(bool)),
SLOT(ScrobblingEnabledChanged(bool)));
connect(lastfm_service, SIGNAL(ScrobbledRadioStream()),
connect(app_->scrobbler(), SIGNAL(ScrobbledRadioStream()),
SLOT(ScrobbledRadioStream()));
#endif
connect(app_->internet_model()->Service<MagnatuneService>(),
@ -681,7 +677,7 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
tray_icon_->SetupMenu(ui_->action_previous_track, ui_->action_play_pause,
ui_->action_stop, ui_->action_stop_after_this_track,
ui_->action_next_track, ui_->action_mute,
ui_->action_love, ui_->action_ban, ui_->action_quit);
ui_->action_love, ui_->action_quit);
connect(tray_icon_, SIGNAL(PlayPause()), app_->player(), SLOT(PlayPause()));
connect(tray_icon_, SIGNAL(SeekForward()), app_->player(),
SLOT(SeekForward()));
@ -698,7 +694,7 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
<< ui_->action_previous_track << ui_->action_play_pause
<< ui_->action_stop << ui_->action_next_track
<< nullptr // spacer
<< ui_->action_love << ui_->action_ban);
<< ui_->action_love);
#if (defined(Q_OS_DARWIN) && defined(HAVE_SPARKLE)) || defined(Q_OS_WIN32)
// Add check for updates item to application menu.
@ -744,8 +740,7 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
connect(global_shortcuts_, SIGNAL(TogglePrettyOSD()), app_->player(),
SLOT(TogglePrettyOSD()));
#ifdef HAVE_LIBLASTFM
connect(global_shortcuts_, SIGNAL(ToggleScrobbling()),
app_->internet_model()->InternetModel::Service<LastFMService>(),
connect(global_shortcuts_, SIGNAL(ToggleScrobbling()), app_->scrobbler(),
SLOT(ToggleScrobbling()));
#endif
@ -844,20 +839,14 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
SLOT(ShuffleModeChanged(PlaylistSequence::ShuffleMode)));
#ifdef HAVE_LIBLASTFM
connect(InternetModel::Service<LastFMService>(), SIGNAL(ScrobbleSubmitted()),
connect(app_->scrobbler(), SIGNAL(ScrobbleSubmitted()),
SLOT(ScrobbleSubmitted()));
connect(InternetModel::Service<LastFMService>(), SIGNAL(ScrobbleError(int)),
connect(app_->scrobbler(), SIGNAL(ScrobbleError(int)),
SLOT(ScrobbleError(int)));
LastFMButtonVisibilityChanged(app_->internet_model()
->InternetModel::Service<LastFMService>()
->AreButtonsVisible());
ScrobbleButtonVisibilityChanged(app_->internet_model()
->InternetModel::Service<LastFMService>()
->IsScrobbleButtonVisible());
ScrobblingEnabledChanged(app_->internet_model()
->InternetModel::Service<LastFMService>()
->IsScrobblingEnabled());
LastFMButtonVisibilityChanged(app_->scrobbler()->AreButtonsVisible());
ScrobbleButtonVisibilityChanged(app_->scrobbler()->IsScrobbleButtonVisible());
ScrobblingEnabledChanged(app_->scrobbler()->IsScrobblingEnabled());
#else
LastFMButtonVisibilityChanged(false);
ScrobbleButtonVisibilityChanged(false);
@ -869,7 +858,7 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
restoreGeometry(settings_.value("geometry").toByteArray());
if (!ui_->splitter->restoreState(
settings_.value("splitter_state").toByteArray())) {
settings_.value("splitter_state").toByteArray())) {
ui_->splitter->setSizes(QList<int>() << 300 << width() - 300);
}
ui_->tabs->SetCurrentIndex(
@ -989,10 +978,8 @@ void MainWindow::MediaStopped() {
ui_->action_play_pause->setEnabled(true);
ui_->action_ban->setEnabled(false);
ui_->action_love->setEnabled(false);
tray_icon_->LastFMButtonLoveStateChanged(false);
tray_icon_->LastFMButtonBanStateChanged(false);
track_position_timer_->stop();
ui_->track_slider->SetStopped();
@ -1028,18 +1015,10 @@ void MainWindow::MediaPlaying() {
ui_->track_slider->SetCanSeek(can_seek);
#ifdef HAVE_LIBLASTFM
bool is_lastfm = (app_->player()->GetCurrentItem()->options() &
PlaylistItem::LastFMControls);
LastFMService* lastfm = InternetModel::Service<LastFMService>();
bool enable_ban = lastfm->IsScrobblingEnabled() && is_lastfm;
bool enable_love = lastfm->IsScrobblingEnabled();
ui_->action_ban->setEnabled(enable_ban);
bool enable_love = app_->scrobbler()->IsScrobblingEnabled();
ui_->action_love->setEnabled(enable_love);
tray_icon_->LastFMButtonBanStateChanged(enable_ban);
tray_icon_->LastFMButtonLoveStateChanged(enable_love);
tray_icon_->SetPlaying(enable_play_pause, enable_ban, enable_love);
tray_icon_->SetPlaying(enable_play_pause, enable_love);
#else
tray_icon_->SetPlaying(enable_play_pause);
#endif
@ -1059,8 +1038,7 @@ void MainWindow::SongChanged(const Song& song) {
#ifdef HAVE_LIBLASTFM
if (ui_->action_toggle_scrobbling->isVisible())
SetToggleScrobblingIcon(
InternetModel::Service<LastFMService>()->IsScrobblingEnabled());
SetToggleScrobblingIcon(app_->scrobbler()->IsScrobblingEnabled());
#endif
}
@ -1104,17 +1082,12 @@ void MainWindow::ScrobblingEnabledChanged(bool value) {
}
}
bool is_lastfm = (app_->player()->GetCurrentItem()->options() &
PlaylistItem::LastFMControls);
ui_->action_ban->setEnabled(value && is_lastfm);
tray_icon_->LastFMButtonBanStateChanged(value && is_lastfm);
ui_->action_love->setEnabled(value);
tray_icon_->LastFMButtonLoveStateChanged(value);
}
#endif
void MainWindow::LastFMButtonVisibilityChanged(bool value) {
ui_->action_ban->setVisible(value);
ui_->action_love->setVisible(value);
ui_->last_fm_controls->setVisible(value);
tray_icon_->LastFMButtonVisibilityChanged(value);
@ -1132,9 +1105,7 @@ void MainWindow::ScrobbleButtonVisibilityChanged(bool value) {
ui_->action_toggle_scrobbling->setIcon(QIcon(":/last.fm/as.png"));
} else {
#ifdef HAVE_LIBLASTFM
SetToggleScrobblingIcon(app_->internet_model()
->InternetModel::Service<LastFMService>()
->IsScrobblingEnabled());
SetToggleScrobblingIcon(app_->scrobbler()->IsScrobblingEnabled());
#endif
}
}
@ -1300,20 +1271,19 @@ void MainWindow::UpdateTrackPosition() {
return;
}
#ifdef HAVE_LIBLASTFM
LastFMService* lastfm_service = InternetModel::Service<LastFMService>();
const bool last_fm_enabled = ui_->action_toggle_scrobbling->isVisible() &&
lastfm_service->IsScrobblingEnabled() &&
lastfm_service->IsAuthenticated();
app_->scrobbler()->IsScrobblingEnabled() &&
app_->scrobbler()->IsAuthenticated();
#endif
// Time to scrobble?
if (position >= scrobble_point) {
if (playlist->get_lastfm_status() == Playlist::LastFM_New) {
#ifdef HAVE_LIBLASTFM
if (lastfm_service->IsScrobblingEnabled() &&
lastfm_service->IsAuthenticated()) {
if (app_->scrobbler()->IsScrobblingEnabled() &&
app_->scrobbler()->IsAuthenticated()) {
qLog(Info) << "Scrobbling at" << scrobble_point;
lastfm_service->Scrobble();
app_->scrobbler()->Scrobble();
}
#endif
}
@ -1355,7 +1325,7 @@ void MainWindow::ScrobbledRadioStream() {
}
void MainWindow::Love() {
InternetModel::Service<LastFMService>()->Love();
app_->scrobbler()->Love();
ui_->action_love->setEnabled(false);
tray_icon_->LastFMButtonLoveStateChanged(false);
}
@ -1432,10 +1402,8 @@ void MainWindow::AddToPlaylist(QAction* action) {
PlaylistItemList items;
// get the selected playlist items
for (const QModelIndex& index : ui_->playlist->view()
->selectionModel()
->selection()
.indexes()) {
for (const QModelIndex& index :
ui_->playlist->view()->selectionModel()->selection().indexes()) {
if (index.column() != 0) continue;
int row =
app_->playlist_manager()->current()->proxy()->mapToSource(index).row();
@ -1693,10 +1661,8 @@ void MainWindow::EditTracks() {
SongList songs;
PlaylistItemList items;
for (const QModelIndex& index : ui_->playlist->view()
->selectionModel()
->selection()
.indexes()) {
for (const QModelIndex& index :
ui_->playlist->view()->selectionModel()->selection().indexes()) {
if (index.column() != 0) continue;
int row =
app_->playlist_manager()->current()->proxy()->mapToSource(index).row();
@ -2078,10 +2044,8 @@ void MainWindow::AddFilesToTranscoder() {
QStringList filenames;
for (const QModelIndex& index : ui_->playlist->view()
->selectionModel()
->selection()
.indexes()) {
for (const QModelIndex& index :
ui_->playlist->view()->selectionModel()->selection().indexes()) {
if (index.column() != 0) continue;
int row =
app_->playlist_manager()->current()->proxy()->mapToSource(index).row();
@ -2249,9 +2213,8 @@ void MainWindow::DeleteFinished(const SongList& songs_with_errors) {
void MainWindow::PlaylistQueue() {
QModelIndexList indexes;
for (const QModelIndex& proxy_index : ui_->playlist->view()
->selectionModel()
->selectedRows()) {
for (const QModelIndex& proxy_index :
ui_->playlist->view()->selectionModel()->selectedRows()) {
indexes << app_->playlist_manager()->current()->proxy()->mapToSource(
proxy_index);
}
@ -2261,9 +2224,8 @@ void MainWindow::PlaylistQueue() {
void MainWindow::PlaylistSkip() {
QModelIndexList indexes;
for (const QModelIndex& proxy_index : ui_->playlist->view()
->selectionModel()
->selectedRows()) {
for (const QModelIndex& proxy_index :
ui_->playlist->view()->selectionModel()->selectedRows()) {
indexes << app_->playlist_manager()->current()->proxy()->mapToSource(
proxy_index);
}
@ -2545,10 +2507,8 @@ void MainWindow::AutoCompleteTags() {
// Get the selected songs and start fetching tags for them
SongList songs;
autocomplete_tag_items_.clear();
for (const QModelIndex& index : ui_->playlist->view()
->selectionModel()
->selection()
.indexes()) {
for (const QModelIndex& index :
ui_->playlist->view()->selectionModel()->selection().indexes()) {
if (index.column() != 0) continue;
int row =
app_->playlist_manager()->current()->proxy()->mapToSource(index).row();
@ -2613,10 +2573,9 @@ void MainWindow::SetToggleScrobblingIcon(bool value) {
#ifdef HAVE_LIBLASTFM
void MainWindow::ScrobbleSubmitted() {
const LastFMService* lastfm_service = InternetModel::Service<LastFMService>();
const bool last_fm_enabled = ui_->action_toggle_scrobbling->isVisible() &&
lastfm_service->IsScrobblingEnabled() &&
lastfm_service->IsAuthenticated();
app_->scrobbler()->IsScrobblingEnabled() &&
app_->scrobbler()->IsAuthenticated();
app_->playlist_manager()->active()->set_lastfm_status(
Playlist::LastFM_Scrobbled);

View File

@ -200,19 +200,6 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="ban_button">
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -435,7 +422,6 @@
<addaction name="action_mute"/>
<addaction name="separator"/>
<addaction name="action_love"/>
<addaction name="action_ban"/>
<addaction name="separator"/>
<addaction name="action_quit"/>
</widget>
@ -567,21 +553,6 @@
<string>Ctrl+L</string>
</property>
</action>
<action name="action_ban">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset resource="../../data/data.qrc">
<normaloff>:/last.fm/ban.png</normaloff>:/last.fm/ban.png</iconset>
</property>
<property name="text">
<string>Ban</string>
</property>
<property name="shortcut">
<string>Ctrl+B</string>
</property>
</action>
<action name="action_clear_playlist">
<property name="text">
<string>Clear playlist</string>

View File

@ -36,8 +36,7 @@ QtSystemTrayIcon::QtSystemTrayIcon(QObject* parent)
action_stop_(nullptr),
action_stop_after_this_track_(nullptr),
action_mute_(nullptr),
action_love_(nullptr),
action_ban_(nullptr) {
action_love_(nullptr) {
QIcon theme_icon = IconLoader::Load("clementine-panel");
QIcon theme_icon_grey = IconLoader::Load("clementine-panel-grey");
@ -97,7 +96,7 @@ bool QtSystemTrayIcon::eventFilter(QObject* object, QEvent* event) {
void QtSystemTrayIcon::SetupMenu(QAction* previous, QAction* play,
QAction* stop, QAction* stop_after,
QAction* next, QAction* mute, QAction* love,
QAction* ban, QAction* quit) {
QAction* quit) {
// Creating new actions and connecting them to old ones. This allows us to
// use old actions without displaying shortcuts that can not be used when
// Clementine's window is hidden
@ -123,10 +122,6 @@ void QtSystemTrayIcon::SetupMenu(QAction* previous, QAction* play,
menu_->addAction(love->icon(), love->text(), love, SLOT(trigger()));
action_love_->setVisible(love->isVisible());
action_love_->setEnabled(love->isEnabled());
action_ban_ =
menu_->addAction(ban->icon(), ban->text(), ban, SLOT(trigger()));
action_ban_->setVisible(ban->isVisible());
action_ban_->setEnabled(ban->isEnabled());
#endif
menu_->addSeparator();
@ -171,8 +166,7 @@ void QtSystemTrayIcon::SetPaused() {
action_play_pause_->setEnabled(true);
}
void QtSystemTrayIcon::SetPlaying(bool enable_play_pause, bool enable_ban,
bool enable_love) {
void QtSystemTrayIcon::SetPlaying(bool enable_play_pause, bool enable_love) {
SystemTrayIcon::SetPlaying();
action_stop_->setEnabled(true);
@ -181,7 +175,6 @@ void QtSystemTrayIcon::SetPlaying(bool enable_play_pause, bool enable_ban,
action_play_pause_->setText(tr("Pause"));
action_play_pause_->setEnabled(enable_play_pause);
#ifdef HAVE_LIBLASTFM
action_ban_->setEnabled(enable_ban);
action_love_->setEnabled(enable_love);
#endif
}
@ -197,14 +190,12 @@ void QtSystemTrayIcon::SetStopped() {
action_play_pause_->setEnabled(true);
#ifdef HAVE_LIBLASTFM
action_ban_->setEnabled(false);
action_love_->setEnabled(false);
#endif
}
void QtSystemTrayIcon::LastFMButtonVisibilityChanged(bool value) {
#ifdef HAVE_LIBLASTFM
action_ban_->setVisible(value);
action_love_->setVisible(value);
#endif
}
@ -215,12 +206,6 @@ void QtSystemTrayIcon::LastFMButtonLoveStateChanged(bool value) {
#endif
}
void QtSystemTrayIcon::LastFMButtonBanStateChanged(bool value) {
#ifdef HAVE_LIBLASTFM
action_ban_->setEnabled(value);
#endif
}
void QtSystemTrayIcon::MuteButtonStateChanged(bool value) {
if (action_mute_) action_mute_->setChecked(value);
}

View File

@ -31,7 +31,7 @@ class QtSystemTrayIcon : public SystemTrayIcon {
void SetupMenu(QAction* previous, QAction* play, QAction* stop,
QAction* stop_after, QAction* next, QAction* mute,
QAction* love, QAction* ban, QAction* quit);
QAction* love, QAction* quit);
bool IsVisible() const;
void SetVisible(bool visible);
@ -44,12 +44,10 @@ class QtSystemTrayIcon : public SystemTrayIcon {
// SystemTrayIcon
void UpdateIcon();
void SetPaused();
void SetPlaying(bool enable_play_pause = false, bool enable_ban = false,
bool enable_love = false);
void SetPlaying(bool enable_play_pause = false, bool enable_love = false);
void SetStopped();
void LastFMButtonVisibilityChanged(bool value);
void LastFMButtonLoveStateChanged(bool value);
void LastFMButtonBanStateChanged(bool value);
void MuteButtonStateChanged(bool value);
// QObject
@ -66,7 +64,6 @@ class QtSystemTrayIcon : public SystemTrayIcon {
QAction* action_stop_after_this_track_;
QAction* action_mute_;
QAction* action_love_;
QAction* action_ban_;
QString pattern_;

View File

@ -86,8 +86,7 @@ void SystemTrayIcon::SetPaused() {
UpdateIcon();
}
void SystemTrayIcon::SetPlaying(bool enable_play_pause, bool enable_ban,
bool enable_love) {
void SystemTrayIcon::SetPlaying(bool enable_play_pause, bool enable_love) {
current_state_icon_ = playing_icon_;
UpdateIcon();
}

View File

@ -33,7 +33,7 @@ class SystemTrayIcon : public QObject {
// Called once to create the icon's context menu
virtual void SetupMenu(QAction* previous, QAction* play, QAction* stop,
QAction* stop_after, QAction* next, QAction* mute,
QAction* love, QAction* ban, QAction* quit) = 0;
QAction* love, QAction* quit) = 0;
virtual bool IsVisible() const { return true; }
virtual void SetVisible(bool visible) {}
@ -54,11 +54,10 @@ class SystemTrayIcon : public QObject {
void SetProgress(int percentage);
virtual void SetPaused();
virtual void SetPlaying(bool enable_play_pause = false,
bool enable_ban = false, bool enable_love = false);
bool enable_love = false);
virtual void SetStopped();
virtual void LastFMButtonVisibilityChanged(bool value) {}
virtual void LastFMButtonLoveStateChanged(bool value) {}
virtual void LastFMButtonBanStateChanged(bool value) {}
virtual void MuteButtonStateChanged(bool value) {}
signals: