2010-03-24 00:11:46 +01:00
|
|
|
/* This file is part of Clementine.
|
2010-11-20 14:27:10 +01:00
|
|
|
Copyright 2010, David Sansome <me@davidsansome.com>
|
2010-03-24 00:11:46 +01:00
|
|
|
|
|
|
|
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/>.
|
|
|
|
*/
|
|
|
|
|
2012-02-19 14:44:33 +01:00
|
|
|
// StringBuilder is activated to speed-up QString concatenation. As explained here:
|
|
|
|
// http://labs.qt.nokia.com/2011/06/13/string-concatenation-with-qstringbuilder/
|
|
|
|
// this cause some compilation errors in some cases. As Lasfm library inlines
|
|
|
|
// some functions in their includes files, which aren't compatible with
|
|
|
|
// QStringBuilder, we undef it here
|
|
|
|
#include <QtGlobal>
|
|
|
|
#if QT_VERSION >= 0x040600
|
|
|
|
#if QT_VERSION >= 0x040800
|
|
|
|
#undef QT_USE_QSTRINGBUILDER
|
|
|
|
#else
|
|
|
|
#undef QT_USE_FAST_CONCATENATION
|
|
|
|
#undef QT_USE_FAST_OPERATOR_PLUS
|
|
|
|
#endif // QT_VERSION >= 0x040800
|
|
|
|
#endif // QT_VERSION >= 0x040600
|
|
|
|
|
2009-12-26 16:13:38 +01:00
|
|
|
#include "lastfmservice.h"
|
2012-06-28 18:41:51 +02:00
|
|
|
|
2012-10-12 14:31:31 +02:00
|
|
|
#include <boost/scoped_ptr.hpp>
|
|
|
|
|
|
|
|
#include <QMenu>
|
|
|
|
#include <QSettings>
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBLASTFM1
|
|
|
|
#include <lastfm/RadioStation.h>
|
|
|
|
#else
|
|
|
|
#include <lastfm/RadioStation>
|
|
|
|
#endif
|
|
|
|
|
2012-06-28 18:41:51 +02:00
|
|
|
#include "lastfmcompat.h"
|
2009-12-30 03:15:38 +01:00
|
|
|
#include "lastfmstationdialog.h"
|
2011-04-28 17:10:28 +02:00
|
|
|
#include "lastfmurlhandler.h"
|
2011-07-15 15:27:50 +02:00
|
|
|
#include "internetmodel.h"
|
|
|
|
#include "internetplaylistitem.h"
|
2012-02-12 14:41:50 +01:00
|
|
|
#include "core/application.h"
|
2012-10-12 14:31:31 +02:00
|
|
|
#include "core/closure.h"
|
2011-04-22 18:50:29 +02:00
|
|
|
#include "core/logging.h"
|
2011-04-28 17:10:28 +02:00
|
|
|
#include "core/player.h"
|
2010-05-10 23:50:31 +02:00
|
|
|
#include "core/song.h"
|
2010-06-23 15:21:30 +02:00
|
|
|
#include "core/taskmanager.h"
|
2011-06-20 01:15:51 +02:00
|
|
|
#include "covers/coverproviders.h"
|
|
|
|
#include "covers/lastfmcoverprovider.h"
|
2012-10-12 14:31:31 +02:00
|
|
|
#include "globalsearch/globalsearch.h"
|
|
|
|
#include "globalsearch/lastfmsearchprovider.h"
|
2010-05-19 17:45:29 +02:00
|
|
|
#include "ui/iconloader.h"
|
2010-06-09 00:56:31 +02:00
|
|
|
#include "ui/settingsdialog.h"
|
2009-12-26 16:13:38 +01:00
|
|
|
|
2010-02-25 02:16:58 +01:00
|
|
|
using boost::scoped_ptr;
|
2010-02-25 01:18:32 +01:00
|
|
|
using lastfm::XmlQuery;
|
|
|
|
|
|
|
|
uint qHash(const lastfm::Track& track) {
|
|
|
|
return qHash(track.title()) ^
|
|
|
|
qHash(track.artist().name()) ^
|
|
|
|
qHash(track.album().title());
|
|
|
|
}
|
|
|
|
|
2009-12-29 20:22:02 +01:00
|
|
|
const char* LastFMService::kServiceName = "Last.fm";
|
2009-12-26 18:19:14 +01:00
|
|
|
const char* LastFMService::kSettingsGroup = "Last.fm";
|
2009-12-29 20:22:02 +01:00
|
|
|
const char* LastFMService::kAudioscrobblerClientId = "tng";
|
|
|
|
const char* LastFMService::kApiKey = "75d20fb472be99275392aefa2760ea09";
|
|
|
|
const char* LastFMService::kSecret = "d3072b60ae626be12be69448f5c46e70";
|
2009-12-26 18:19:14 +01:00
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
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");
|
|
|
|
|
2011-09-24 14:48:14 +02:00
|
|
|
const int LastFMService::kFriendsCacheDurationSecs = 60 * 60 * 24; // 1 day
|
|
|
|
|
2012-02-12 14:41:50 +01:00
|
|
|
LastFMService::LastFMService(Application* app, InternetModel* parent)
|
|
|
|
: InternetService(kServiceName, app, parent, parent),
|
2011-04-28 17:10:28 +02:00
|
|
|
url_handler_(new LastFMUrlHandler(this, this)),
|
2009-12-29 20:22:02 +01:00
|
|
|
scrobbler_(NULL),
|
2011-04-16 17:27:41 +02:00
|
|
|
already_scrobbled_(false),
|
2009-12-30 03:15:38 +01:00
|
|
|
station_dialog_(new LastFMStationDialog),
|
2009-12-30 00:01:07 +01:00
|
|
|
context_menu_(new QMenu),
|
2009-12-29 21:48:50 +01:00
|
|
|
initial_tune_(false),
|
2010-06-23 15:21:30 +02:00
|
|
|
tune_task_id_(0),
|
2009-12-30 01:31:00 +01:00
|
|
|
scrobbling_enabled_(false),
|
2011-09-24 18:01:18 +02:00
|
|
|
root_item_(NULL),
|
2009-12-30 03:15:38 +01:00
|
|
|
artist_list_(NULL),
|
|
|
|
tag_list_(NULL),
|
2010-10-07 15:59:24 +02:00
|
|
|
custom_list_(NULL),
|
2009-12-30 01:31:00 +01:00
|
|
|
friends_list_(NULL),
|
2011-04-28 12:32:56 +02:00
|
|
|
neighbours_list_(NULL),
|
2011-11-05 23:56:10 +01:00
|
|
|
friend_names_(kSettingsGroup, "friend_names", kFriendsCacheDurationSecs),
|
2011-04-28 12:32:56 +02:00
|
|
|
connection_problems_(false)
|
2009-12-26 16:13:38 +01:00
|
|
|
{
|
2011-11-08 19:35:33 +01:00
|
|
|
ReloadSettings();
|
|
|
|
|
2011-04-07 18:25:52 +02:00
|
|
|
//we emit the signal the first time to be sure the buttons are in the right state
|
|
|
|
emit ScrobblingEnabledChanged(scrobbling_enabled_);
|
2009-12-30 00:01:07 +01:00
|
|
|
|
2011-02-10 23:24:17 +01:00
|
|
|
context_menu_->addActions(GetPlaylistActions());
|
2010-02-23 19:33:09 +01:00
|
|
|
remove_action_ = context_menu_->addAction(
|
2010-05-19 17:45:29 +02:00
|
|
|
IconLoader::Load("list-remove"), tr("Remove"), this, SLOT(Remove()));
|
2009-12-30 02:41:37 +01:00
|
|
|
context_menu_->addSeparator();
|
2010-02-23 19:33:09 +01:00
|
|
|
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()));
|
2010-10-07 15:59:24 +02:00
|
|
|
add_custom_action_ = context_menu_->addAction(
|
|
|
|
QIcon(":last.fm/icon_radio.png"), tr("Play custom radio..."), this, SLOT(AddCustomRadio()));
|
2011-09-24 14:48:14 +02:00
|
|
|
refresh_friends_action_ = context_menu_->addAction(
|
|
|
|
IconLoader::Load("view-refresh"), tr("Refresh friends list"), this, SLOT(ForceRefreshFriends()));
|
2010-02-23 19:33:09 +01:00
|
|
|
context_menu_->addAction(
|
2010-05-19 17:45:29 +02:00
|
|
|
IconLoader::Load("configure"), tr("Configure Last.fm..."), this, SLOT(ShowConfig()));
|
2009-12-30 02:41:37 +01:00
|
|
|
|
2009-12-30 03:23:09 +01:00
|
|
|
remove_action_->setEnabled(false);
|
2009-12-30 03:15:38 +01:00
|
|
|
add_artist_action_->setEnabled(false);
|
|
|
|
add_tag_action_->setEnabled(false);
|
2010-10-07 15:59:24 +02:00
|
|
|
add_custom_action_->setEnabled(false);
|
2011-04-28 17:10:28 +02:00
|
|
|
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->player()->RegisterUrlHandler(url_handler_);
|
|
|
|
app_->cover_providers()->AddProvider(new LastFmCoverProvider(this));
|
2011-09-24 18:01:18 +02:00
|
|
|
|
2012-02-13 21:44:04 +01:00
|
|
|
app_->global_search()->AddProvider(new LastFMSearchProvider(this, app_, this));
|
2009-12-26 18:19:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
LastFMService::~LastFMService() {
|
2009-12-26 16:13:38 +01:00
|
|
|
}
|
|
|
|
|
2010-02-03 19:32:48 +01:00
|
|
|
void LastFMService::ReloadSettings() {
|
2011-04-07 18:25:52 +02:00
|
|
|
bool scrobbling_enabled_old = scrobbling_enabled_;
|
2010-02-03 19:32:48 +01:00
|
|
|
QSettings settings;
|
|
|
|
settings.beginGroup(kSettingsGroup);
|
|
|
|
lastfm::ws::Username = settings.value("Username").toString();
|
|
|
|
lastfm::ws::SessionKey = settings.value("Session").toString();
|
|
|
|
scrobbling_enabled_ = settings.value("ScrobblingEnabled", true).toBool();
|
2010-04-07 21:26:49 +02:00
|
|
|
buttons_visible_ = settings.value("ShowLoveBanButtons", true).toBool();
|
2011-04-07 18:25:52 +02:00
|
|
|
scrobble_button_visible_ = settings.value("ShowScrobbleButton", true).toBool();
|
2012-05-13 17:04:55 +02:00
|
|
|
prefer_albumartist_ = settings.value("PreferAlbumArtist", false).toBool();
|
2011-11-05 23:56:10 +01:00
|
|
|
friend_names_.Load();
|
2011-09-24 14:48:14 +02:00
|
|
|
|
2011-04-07 18:25:52 +02:00
|
|
|
//avoid emitting signal if it's not changed
|
|
|
|
if(scrobbling_enabled_old != scrobbling_enabled_)
|
|
|
|
emit ScrobblingEnabledChanged(scrobbling_enabled_);
|
2010-04-07 21:26:49 +02:00
|
|
|
emit ButtonVisibilityChanged(buttons_visible_);
|
2011-04-07 18:25:52 +02:00
|
|
|
emit ScrobbleButtonVisibilityChanged(scrobble_button_visible_);
|
2012-05-13 17:04:55 +02:00
|
|
|
emit PreferAlbumArtistChanged(prefer_albumartist_);
|
2009-12-29 20:22:02 +01:00
|
|
|
}
|
|
|
|
|
2010-02-03 19:32:48 +01:00
|
|
|
void LastFMService::ShowConfig() {
|
2012-12-05 10:36:22 +01:00
|
|
|
app_->OpenSettingsDialogAtPage(SettingsDialog::Page_Lastfm);
|
2010-02-03 19:32:48 +01:00
|
|
|
}
|
2009-12-29 21:48:50 +01:00
|
|
|
|
2010-02-03 19:32:48 +01:00
|
|
|
bool LastFMService::IsAuthenticated() const {
|
|
|
|
return !lastfm::ws::SessionKey.isEmpty();
|
2009-12-29 21:48:50 +01:00
|
|
|
}
|
|
|
|
|
2011-04-13 17:54:15 +02:00
|
|
|
bool LastFMService::IsSubscriber() const {
|
|
|
|
QSettings settings;
|
|
|
|
settings.beginGroup(kSettingsGroup);
|
|
|
|
return settings.value("Subscriber", false).toBool();
|
|
|
|
}
|
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
QStandardItem* LastFMService::CreateRootItem() {
|
2011-09-24 18:01:18 +02:00
|
|
|
root_item_ = new QStandardItem(QIcon(":last.fm/as.png"), kServiceName);
|
|
|
|
root_item_->setData(true, InternetModel::Role_CanLazyLoad);
|
|
|
|
return root_item_;
|
2009-12-26 16:13:38 +01:00
|
|
|
}
|
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
void LastFMService::LazyPopulate(QStandardItem* parent) {
|
2011-07-15 15:27:50 +02:00
|
|
|
switch (parent->data(InternetModel::Role_Type).toInt()) {
|
|
|
|
case InternetModel::Type_Service:
|
2009-12-30 01:31:00 +01:00
|
|
|
// Normal radio types
|
2011-01-09 19:27:41 +01:00
|
|
|
CreateStationItem(parent,
|
|
|
|
tr("My Recommendations"),
|
|
|
|
":last.fm/recommended_radio.png",
|
2011-04-28 14:27:53 +02:00
|
|
|
QUrl("lastfm://user/USERNAME/recommended"),
|
2011-01-09 19:27:41 +01:00
|
|
|
tr("My Last.fm Recommended Radio"));
|
|
|
|
CreateStationItem(parent,
|
|
|
|
tr("My Radio Station"),
|
|
|
|
":last.fm/personal_radio.png",
|
2011-04-28 14:27:53 +02:00
|
|
|
QUrl("lastfm://user/USERNAME/library"),
|
2011-01-09 19:27:41 +01:00
|
|
|
tr("My Last.fm Library"));
|
|
|
|
CreateStationItem(parent,
|
|
|
|
tr("My Mix Radio"),
|
|
|
|
":last.fm/loved_radio.png",
|
2011-04-28 14:27:53 +02:00
|
|
|
QUrl("lastfm://user/USERNAME/mix"),
|
2011-01-09 19:27:41 +01:00
|
|
|
tr("My Last.fm Mix Radio"));
|
|
|
|
CreateStationItem(parent,
|
|
|
|
tr("My Neighborhood"),
|
|
|
|
":last.fm/neighbour_radio.png",
|
2011-04-28 14:27:53 +02:00
|
|
|
QUrl("lastfm://user/USERNAME/neighbours"),
|
2011-01-09 19:27:41 +01:00
|
|
|
tr("My Last.fm Neighborhood"));
|
2009-12-26 18:19:14 +01:00
|
|
|
|
2009-12-30 03:15:38 +01:00
|
|
|
// Types that have children
|
2011-01-09 19:27:41 +01:00
|
|
|
artist_list_ = new QStandardItem(QIcon(":last.fm/icon_radio.png"), tr("Artist radio"));
|
2011-07-15 15:27:50 +02:00
|
|
|
artist_list_->setData(Type_Artists, InternetModel::Role_Type);
|
2011-01-09 19:27:41 +01:00
|
|
|
parent->appendRow(artist_list_);
|
2009-12-30 01:31:00 +01:00
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
tag_list_ = new QStandardItem(QIcon(":last.fm/icon_tag.png"), tr("Tag radio"));
|
2011-07-15 15:27:50 +02:00
|
|
|
tag_list_->setData(Type_Tags, InternetModel::Role_Type);
|
2011-01-09 19:27:41 +01:00
|
|
|
parent->appendRow(tag_list_);
|
2009-12-30 03:15:38 +01:00
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
custom_list_ = new QStandardItem(QIcon(":last.fm/icon_radio.png"), tr("Custom radio"));
|
2011-07-15 15:27:50 +02:00
|
|
|
custom_list_->setData(Type_Custom, InternetModel::Role_Type);
|
2011-01-09 19:27:41 +01:00
|
|
|
parent->appendRow(custom_list_);
|
2010-10-07 15:59:24 +02:00
|
|
|
|
2011-01-10 23:26:13 +01:00
|
|
|
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_);
|
2009-12-30 01:31:00 +01:00
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
friends_list_ = new QStandardItem(QIcon(":last.fm/my_friends.png"), tr("Friends"));
|
2011-07-15 15:27:50 +02:00
|
|
|
friends_list_->setData(Type_Friends, InternetModel::Role_Type);
|
|
|
|
friends_list_->setData(true, InternetModel::Role_CanLazyLoad);
|
2011-01-09 19:27:41 +01:00
|
|
|
parent->appendRow(friends_list_);
|
2009-12-30 01:31:00 +01:00
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
neighbours_list_ = new QStandardItem(QIcon(":last.fm/my_neighbours.png"), tr("Neighbors"));
|
2011-07-15 15:27:50 +02:00
|
|
|
neighbours_list_->setData(Type_Neighbours, InternetModel::Role_Type);
|
|
|
|
neighbours_list_->setData(true, InternetModel::Role_CanLazyLoad);
|
2011-01-09 19:27:41 +01:00
|
|
|
parent->appendRow(neighbours_list_);
|
2009-12-30 01:31:00 +01:00
|
|
|
|
2009-12-29 20:22:02 +01:00
|
|
|
if (!IsAuthenticated())
|
2010-02-03 19:32:48 +01:00
|
|
|
ShowConfig();
|
2009-12-30 03:15:38 +01:00
|
|
|
|
|
|
|
add_artist_action_->setEnabled(true);
|
|
|
|
add_tag_action_->setEnabled(true);
|
2010-10-07 15:59:24 +02:00
|
|
|
add_custom_action_->setEnabled(true);
|
2009-12-26 18:19:14 +01:00
|
|
|
break;
|
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
case Type_Friends:
|
2011-09-24 14:48:14 +02:00
|
|
|
RefreshFriends(false);
|
2009-12-30 01:31:00 +01:00
|
|
|
break;
|
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
case Type_Neighbours:
|
2009-12-30 01:31:00 +01:00
|
|
|
RefreshNeighbours();
|
|
|
|
break;
|
|
|
|
|
2009-12-30 02:29:47 +01:00
|
|
|
case Type_OtherUser:
|
2011-01-09 19:27:41 +01:00
|
|
|
CreateStationItem(parent,
|
|
|
|
tr("Last.fm Radio Station - %1").arg(parent->text()),
|
|
|
|
":last.fm/personal_radio.png",
|
2011-04-28 14:27:53 +02:00
|
|
|
QUrl("lastfm://user/" + parent->text() + "/library"),
|
2011-01-09 19:27:41 +01:00
|
|
|
tr("Last.fm Library - %1").arg(parent->text()));
|
|
|
|
CreateStationItem(parent,
|
|
|
|
tr("Last.fm Mix Radio - %1").arg(parent->text()),
|
|
|
|
":last.fm/loved_radio.png",
|
2011-04-28 14:27:53 +02:00
|
|
|
QUrl("lastfm://user/" + parent->text() + "/mix"),
|
2011-01-09 19:27:41 +01:00
|
|
|
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",
|
2011-04-28 14:27:53 +02:00
|
|
|
QUrl("lastfm://user/" + parent->text() + "/neighbours"),
|
2011-01-09 19:27:41 +01:00
|
|
|
tr("Last.fm Neighbor Radio - %1").arg(parent->text()));
|
2009-12-30 02:29:47 +01:00
|
|
|
break;
|
|
|
|
|
2009-12-26 18:19:14 +01:00
|
|
|
default:
|
|
|
|
break;
|
2009-12-26 16:13:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
QStandardItem* LastFMService::CreateStationItem(
|
|
|
|
QStandardItem* parent, const QString& name, const QString& icon,
|
2011-04-28 14:27:53 +02:00
|
|
|
const QUrl& url, const QString& title) {
|
2011-04-27 00:06:58 +02:00
|
|
|
Song song;
|
2011-04-28 14:27:53 +02:00
|
|
|
song.set_url(url);
|
2011-04-27 00:06:58 +02:00
|
|
|
song.set_title(title);
|
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
QStandardItem* ret = new QStandardItem(QIcon(icon), name);
|
2011-07-15 15:27:50 +02:00
|
|
|
ret->setData(QVariant::fromValue(song), InternetModel::Role_SongMetadata);
|
|
|
|
ret->setData(InternetModel::PlayBehaviour_SingleItem, InternetModel::Role_PlayBehaviour);
|
2011-01-09 19:27:41 +01:00
|
|
|
parent->appendRow(ret);
|
2009-12-26 16:13:38 +01:00
|
|
|
return ret;
|
|
|
|
}
|
2009-12-26 18:19:14 +01:00
|
|
|
|
|
|
|
void LastFMService::Authenticate(const QString& username, const QString& password) {
|
|
|
|
QMap<QString, QString> params;
|
|
|
|
params["method"] = "auth.getMobileSession";
|
|
|
|
params["username"] = username;
|
|
|
|
params["authToken"] = lastfm::md5((username + lastfm::md5(password.toUtf8())).toUtf8());
|
|
|
|
|
|
|
|
QNetworkReply* reply = lastfm::ws::post(params);
|
2012-10-12 14:31:31 +02:00
|
|
|
NewClosure(reply, SIGNAL(finished()), this,
|
|
|
|
SLOT(AuthenticateReplyFinished(QNetworkReply*)), reply);
|
2011-04-28 12:32:56 +02:00
|
|
|
// If we need more detailed error reporting, handle error(NetworkError) signal
|
2009-12-26 18:19:14 +01:00
|
|
|
}
|
|
|
|
|
2010-04-07 21:26:49 +02:00
|
|
|
void LastFMService::SignOut() {
|
|
|
|
lastfm::ws::Username.clear();
|
|
|
|
lastfm::ws::SessionKey.clear();
|
|
|
|
|
2011-11-05 23:56:10 +01:00
|
|
|
friend_names_.Update(QStringList());
|
2011-09-24 14:48:14 +02:00
|
|
|
|
2010-04-07 21:26:49 +02:00
|
|
|
QSettings settings;
|
|
|
|
settings.beginGroup(kSettingsGroup);
|
2011-09-24 18:01:18 +02:00
|
|
|
|
2010-04-07 21:26:49 +02:00
|
|
|
settings.setValue("Username", QString());
|
|
|
|
settings.setValue("Session", QString());
|
|
|
|
}
|
|
|
|
|
2012-10-12 14:31:31 +02:00
|
|
|
void LastFMService::AuthenticateReplyFinished(QNetworkReply* reply) {
|
2011-04-14 13:11:34 +02:00
|
|
|
reply->deleteLater();
|
2009-12-26 18:19:14 +01:00
|
|
|
|
|
|
|
// Parse the reply
|
2012-06-28 18:41:51 +02:00
|
|
|
lastfm::XmlQuery lfm(lastfm::compat::EmptyXmlQuery());
|
|
|
|
if (lastfm::compat::ParseQuery(reply->readAll(), &lfm)) {
|
2009-12-26 18:19:14 +01:00
|
|
|
lastfm::ws::Username = lfm["session"]["name"].text();
|
|
|
|
lastfm::ws::SessionKey = lfm["session"]["key"].text();
|
2011-04-13 17:54:15 +02:00
|
|
|
QString subscribed = lfm["session"]["subscriber"].text();
|
|
|
|
const bool is_subscriber = (subscribed.toInt() == 1);
|
|
|
|
|
|
|
|
// Save the session key
|
|
|
|
QSettings settings;
|
|
|
|
settings.beginGroup(kSettingsGroup);
|
|
|
|
settings.setValue("Username", lastfm::ws::Username);
|
|
|
|
settings.setValue("Session", lastfm::ws::SessionKey);
|
|
|
|
settings.setValue("Subscriber", is_subscriber);
|
2012-06-25 12:30:53 +02:00
|
|
|
} else {
|
2013-02-05 14:12:39 +01:00
|
|
|
emit AuthenticationComplete(false, lfm["error"].text().trimmed());
|
2009-12-26 18:19:14 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-12-29 20:22:02 +01:00
|
|
|
// Invalidate the scrobbler - it will get recreated later
|
|
|
|
delete scrobbler_;
|
|
|
|
scrobbler_ = NULL;
|
|
|
|
|
2013-02-05 14:12:39 +01:00
|
|
|
emit AuthenticationComplete(true, QString());
|
2009-12-26 18:19:14 +01:00
|
|
|
}
|
2009-12-26 22:35:45 +01:00
|
|
|
|
2011-04-14 13:11:34 +02:00
|
|
|
void LastFMService::UpdateSubscriberStatus() {
|
|
|
|
QMap<QString, QString> params;
|
|
|
|
params["method"] = "user.getInfo";
|
|
|
|
params["user"] = lastfm::ws::Username;
|
|
|
|
|
|
|
|
QNetworkReply* reply = lastfm::ws::post(params);
|
2012-10-12 14:31:31 +02:00
|
|
|
NewClosure(reply, SIGNAL(finished()), this,
|
|
|
|
SLOT(UpdateSubscriberStatusFinished(QNetworkReply*)), reply);
|
2011-04-14 13:11:34 +02:00
|
|
|
}
|
|
|
|
|
2012-10-12 14:31:31 +02:00
|
|
|
void LastFMService::UpdateSubscriberStatusFinished(QNetworkReply* reply) {
|
2011-04-14 13:11:34 +02:00
|
|
|
reply->deleteLater();
|
|
|
|
|
2011-08-27 23:01:28 +02:00
|
|
|
bool is_subscriber = false;
|
|
|
|
|
2012-06-28 18:41:51 +02:00
|
|
|
lastfm::XmlQuery lfm(lastfm::compat::EmptyXmlQuery());
|
|
|
|
if (lastfm::compat::ParseQuery(reply->readAll(), &lfm, &connection_problems_)) {
|
2011-04-14 13:11:34 +02:00
|
|
|
QString subscriber = lfm["user"]["subscriber"].text();
|
2011-08-27 23:01:28 +02:00
|
|
|
is_subscriber = (subscriber.toInt() == 1);
|
2011-04-28 12:32:56 +02:00
|
|
|
|
2011-04-14 13:11:34 +02:00
|
|
|
QSettings settings;
|
|
|
|
settings.beginGroup(kSettingsGroup);
|
|
|
|
settings.setValue("Subscriber", is_subscriber);
|
2011-04-22 18:50:29 +02:00
|
|
|
qLog(Info) << lastfm::ws::Username << "Subscriber status:" << is_subscriber;
|
2011-04-14 13:11:34 +02:00
|
|
|
}
|
2011-08-27 23:01:28 +02:00
|
|
|
|
|
|
|
emit UpdatedSubscriberStatus(is_subscriber);
|
2011-04-14 13:11:34 +02:00
|
|
|
}
|
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
QUrl LastFMService::FixupUrl(const QUrl& url) {
|
|
|
|
QUrl ret;
|
|
|
|
ret.setEncodedUrl(url.toEncoded().replace(
|
|
|
|
"USERNAME", QUrl::toPercentEncoding(lastfm::ws::Username)));
|
|
|
|
return ret;
|
2009-12-26 22:35:45 +01:00
|
|
|
}
|
|
|
|
|
2011-04-28 17:10:28 +02:00
|
|
|
QUrl LastFMService::DeququeNextMediaUrl() {
|
2010-02-25 01:18:32 +01:00
|
|
|
if (playlist_.empty()) {
|
2011-04-28 17:10:28 +02:00
|
|
|
return QUrl();
|
2009-12-26 23:59:11 +01:00
|
|
|
}
|
|
|
|
|
2010-03-21 01:22:15 +01:00
|
|
|
lastfm::MutableTrack track = playlist_.dequeue();
|
|
|
|
track.stamp();
|
|
|
|
|
2011-04-16 17:27:41 +02:00
|
|
|
already_scrobbled_ = false;
|
2010-02-26 15:50:02 +01:00
|
|
|
last_track_ = track;
|
2010-02-25 01:18:32 +01:00
|
|
|
if (playlist_.empty()) {
|
|
|
|
FetchMoreTracks();
|
|
|
|
}
|
|
|
|
|
2010-02-26 15:50:02 +01:00
|
|
|
next_metadata_ = track;
|
2010-05-17 00:46:56 +02:00
|
|
|
StreamMetadataReady();
|
2010-05-18 22:43:10 +02:00
|
|
|
|
2011-04-28 17:10:28 +02:00
|
|
|
return last_track_.url();
|
2010-02-26 15:50:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void LastFMService::StreamMetadataReady() {
|
2009-12-26 23:59:11 +01:00
|
|
|
Song metadata;
|
2010-02-26 15:50:02 +01:00
|
|
|
metadata.InitFromLastFM(next_metadata_);
|
2010-05-17 00:46:56 +02:00
|
|
|
|
|
|
|
if (art_urls_.contains(next_metadata_))
|
|
|
|
metadata.set_art_automatic(art_urls_[next_metadata_]);
|
2009-12-29 20:22:02 +01:00
|
|
|
|
2009-12-26 23:59:11 +01:00
|
|
|
emit StreamMetadataFound(last_url_, metadata);
|
|
|
|
}
|
|
|
|
|
2009-12-26 22:35:45 +01:00
|
|
|
void LastFMService::TunerError(lastfm::ws::Error error) {
|
2011-04-22 19:07:57 +02:00
|
|
|
qLog(Warning) << "Last.fm error" << error;
|
2009-12-26 23:59:11 +01:00
|
|
|
if (!initial_tune_)
|
|
|
|
return;
|
|
|
|
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->task_manager()->SetTaskFinished(tune_task_id_);
|
2010-06-23 15:21:30 +02:00
|
|
|
tune_task_id_ = 0;
|
2009-12-26 22:35:45 +01:00
|
|
|
|
|
|
|
if (error == lastfm::ws::NotEnoughContent) {
|
2011-04-28 17:10:28 +02:00
|
|
|
url_handler_->TunerError();
|
2009-12-26 22:35:45 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
emit StreamError(ErrorString(error));
|
|
|
|
}
|
|
|
|
|
|
|
|
QString LastFMService::ErrorString(lastfm::ws::Error error) const {
|
|
|
|
switch (error) {
|
2010-02-23 19:33:09 +01:00
|
|
|
case lastfm::ws::InvalidService: return tr("Invalid service");
|
|
|
|
case lastfm::ws::InvalidMethod: return tr("Invalid method");
|
|
|
|
case lastfm::ws::AuthenticationFailed: return tr("Authentication failed");
|
|
|
|
case lastfm::ws::InvalidFormat: return tr("Invalid format");
|
|
|
|
case lastfm::ws::InvalidParameters: return tr("Invalid parameters");
|
|
|
|
case lastfm::ws::InvalidResourceSpecified: return tr("Invalid resource specified");
|
|
|
|
case lastfm::ws::OperationFailed: return tr("Operation failed");
|
|
|
|
case lastfm::ws::InvalidSessionKey: return tr("Invalid session key");
|
|
|
|
case lastfm::ws::InvalidApiKey: return tr("Invalid API key");
|
|
|
|
case lastfm::ws::ServiceOffline: return tr("Service offline");
|
|
|
|
case lastfm::ws::SubscribersOnly: return tr("This stream is for paid subscribers only");
|
|
|
|
|
|
|
|
case lastfm::ws::TryAgainLater: return tr("Last.fm is currently busy, please try again in a few minutes");
|
|
|
|
|
|
|
|
case lastfm::ws::NotEnoughContent: return tr("Not enough content");
|
|
|
|
case lastfm::ws::NotEnoughMembers: return tr("Not enough members");
|
|
|
|
case lastfm::ws::NotEnoughFans: return tr("Not enough fans");
|
2010-04-15 18:12:44 +02:00
|
|
|
case lastfm::ws::NotEnoughNeighbours: return tr("Not enough neighbors");
|
2010-02-23 19:33:09 +01:00
|
|
|
|
|
|
|
case lastfm::ws::MalformedResponse: return tr("Malformed response");
|
2009-12-26 22:35:45 +01:00
|
|
|
|
|
|
|
case lastfm::ws::UnknownError:
|
|
|
|
default:
|
2010-02-23 19:33:09 +01:00
|
|
|
return tr("Unknown error");
|
2009-12-26 22:35:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LastFMService::TunerTrackAvailable() {
|
2009-12-26 23:59:11 +01:00
|
|
|
if (initial_tune_) {
|
2011-04-28 17:10:28 +02:00
|
|
|
url_handler_->TunerTrackAvailable();
|
2009-12-26 23:59:11 +01:00
|
|
|
initial_tune_ = false;
|
|
|
|
}
|
2009-12-26 22:35:45 +01:00
|
|
|
}
|
2009-12-29 20:22:02 +01:00
|
|
|
|
|
|
|
bool LastFMService::InitScrobbler() {
|
2009-12-29 21:48:50 +01:00
|
|
|
if (!IsAuthenticated() || !IsScrobblingEnabled())
|
2009-12-29 20:22:02 +01:00
|
|
|
return false;
|
|
|
|
|
2009-12-29 20:57:33 +01:00
|
|
|
if (!scrobbler_)
|
2009-12-29 20:22:02 +01:00
|
|
|
scrobbler_ = new lastfm::Audioscrobbler(kAudioscrobblerClientId);
|
|
|
|
|
2011-04-08 17:35:08 +02:00
|
|
|
//reemit the signal since the sender is private
|
2012-06-28 18:41:51 +02:00
|
|
|
#ifdef HAVE_LIBLASTFM1
|
2012-06-25 12:30:53 +02:00
|
|
|
connect(scrobbler_, SIGNAL(scrobblesSubmitted(QList<lastfm::Track>)), SIGNAL(ScrobbleSubmitted()));
|
|
|
|
connect(scrobbler_, SIGNAL(nowPlayingError(int,QString)), SIGNAL(ScrobbleError(int)));
|
2012-06-28 18:41:51 +02:00
|
|
|
#else
|
2013-04-15 13:22:45 +02:00
|
|
|
connect(scrobbler_, SIGNAL(status(int)), SLOT(ScrobblerStatus(int)));
|
2012-06-28 18:41:51 +02:00
|
|
|
#endif
|
2009-12-29 20:22:02 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-06-28 18:41:51 +02:00
|
|
|
void LastFMService::ScrobblerStatus(int value) {
|
|
|
|
switch (value) {
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
emit ScrobbleSubmitted();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
emit ScrobbleError(value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-29 20:22:02 +01:00
|
|
|
lastfm::Track LastFMService::TrackFromSong(const Song &song) const {
|
|
|
|
if (song.title() == last_track_.title() &&
|
|
|
|
song.artist() == last_track_.artist() &&
|
|
|
|
song.album() == last_track_.album())
|
|
|
|
return last_track_;
|
|
|
|
|
|
|
|
lastfm::Track ret;
|
2012-05-13 17:04:55 +02:00
|
|
|
song.ToLastFM(&ret, PreferAlbumArtist());
|
2009-12-29 20:22:02 +01:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void LastFMService::NowPlaying(const Song &song) {
|
|
|
|
if (!InitScrobbler())
|
|
|
|
return;
|
|
|
|
|
2011-04-16 17:13:53 +02:00
|
|
|
// Scrobbling streams is difficult if we don't have length of each individual
|
|
|
|
// part. In Song::ToLastFm we set the Track's source to
|
|
|
|
// NonPersonalisedBroadcast if it's such a stream, so we have to scrobble it
|
|
|
|
// when we change to a different track, but only if enough time has elapsed
|
|
|
|
// since it started playing.
|
|
|
|
if (!last_track_.isNull() &&
|
|
|
|
last_track_.source() == lastfm::Track::NonPersonalisedBroadcast) {
|
|
|
|
const int duration_secs = last_track_.timestamp().secsTo(QDateTime::currentDateTime());
|
2012-06-28 18:41:51 +02:00
|
|
|
if (duration_secs >= lastfm::compat::ScrobbleTimeMin()) {
|
2011-04-16 17:13:53 +02:00
|
|
|
lastfm::MutableTrack mtrack(last_track_);
|
|
|
|
mtrack.setDuration(duration_secs);
|
|
|
|
|
2011-04-22 18:50:29 +02:00
|
|
|
qLog(Info) << "Scrobbling stream track" << mtrack.title() << "length" << duration_secs;
|
2011-04-16 17:13:53 +02:00
|
|
|
scrobbler_->cache(mtrack);
|
|
|
|
scrobbler_->submit();
|
2011-04-16 17:27:34 +02:00
|
|
|
|
|
|
|
emit ScrobbledRadioStream();
|
2011-04-16 17:13:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-07 21:18:24 +02:00
|
|
|
lastfm::MutableTrack mtrack(TrackFromSong(song));
|
|
|
|
mtrack.stamp();
|
2011-04-16 17:27:41 +02:00
|
|
|
already_scrobbled_ = false;
|
2011-04-07 21:18:24 +02:00
|
|
|
last_track_ = mtrack;
|
2009-12-29 21:11:03 +01:00
|
|
|
|
2012-06-28 18:41:51 +02:00
|
|
|
#ifndef HAVE_LIBLASTFM1
|
|
|
|
// Check immediately if the song is valid
|
|
|
|
Scrobble::Invalidity invalidity;
|
|
|
|
if (!lastfm::Scrobble(last_track_).isValid( &invalidity )) {
|
|
|
|
//for now just notify this, we can also see the cause
|
|
|
|
emit ScrobbleError(-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
// TODO: validity was removed from liblastfm1 but might reappear, it should have
|
|
|
|
// no impact as we get a different error when actually trying to scrobble.
|
|
|
|
#endif
|
2011-04-07 18:25:52 +02:00
|
|
|
|
2010-03-21 01:22:15 +01:00
|
|
|
scrobbler_->nowPlaying(mtrack);
|
2009-12-29 20:22:02 +01:00
|
|
|
}
|
|
|
|
|
2009-12-29 21:11:03 +01:00
|
|
|
void LastFMService::Scrobble() {
|
2009-12-29 20:22:02 +01:00
|
|
|
if (!InitScrobbler())
|
|
|
|
return;
|
|
|
|
|
2012-06-28 18:41:51 +02:00
|
|
|
lastfm::compat::ScrobbleCache cache(lastfm::ws::Username);
|
2011-04-30 10:36:44 +02:00
|
|
|
qLog(Debug) << "There are" << cache.tracks().count() << "tracks in the last.fm cache.";
|
2009-12-29 21:11:03 +01:00
|
|
|
scrobbler_->cache(last_track_);
|
2011-04-28 12:32:56 +02:00
|
|
|
|
|
|
|
// Let's mark a track as cached, useful when the connection is down
|
2012-06-25 12:30:53 +02:00
|
|
|
emit ScrobbleError(30);
|
2009-12-29 21:11:03 +01:00
|
|
|
scrobbler_->submit();
|
2011-04-28 12:32:56 +02:00
|
|
|
|
2011-04-16 17:27:41 +02:00
|
|
|
already_scrobbled_ = true;
|
2009-12-29 20:22:02 +01:00
|
|
|
}
|
|
|
|
|
2009-12-29 21:11:03 +01:00
|
|
|
void LastFMService::Love() {
|
2009-12-29 21:48:50 +01:00
|
|
|
if (!IsAuthenticated())
|
2010-02-03 19:32:48 +01:00
|
|
|
ShowConfig();
|
2009-12-29 21:48:50 +01:00
|
|
|
|
2009-12-29 21:11:03 +01:00
|
|
|
lastfm::MutableTrack mtrack(last_track_);
|
2009-12-29 20:22:02 +01:00
|
|
|
mtrack.love();
|
2010-03-21 01:22:15 +01:00
|
|
|
last_track_ = mtrack;
|
2011-04-16 17:27:41 +02:00
|
|
|
|
|
|
|
if (already_scrobbled_) {
|
|
|
|
// The love only takes effect when the song is scrobbled, but we've already
|
|
|
|
// scrobbled this one so we have to do it again.
|
|
|
|
Scrobble();
|
|
|
|
}
|
2009-12-29 20:22:02 +01:00
|
|
|
}
|
|
|
|
|
2009-12-29 21:11:03 +01:00
|
|
|
void LastFMService::Ban() {
|
|
|
|
lastfm::MutableTrack mtrack(last_track_);
|
2009-12-29 20:22:02 +01:00
|
|
|
mtrack.ban();
|
2010-03-21 01:22:15 +01:00
|
|
|
last_track_ = mtrack;
|
2009-12-29 21:48:50 +01:00
|
|
|
|
|
|
|
Scrobble();
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->player()->Next();
|
2009-12-29 20:22:02 +01:00
|
|
|
}
|
2009-12-30 00:01:07 +01:00
|
|
|
|
2012-03-11 15:44:43 +01:00
|
|
|
void LastFMService::ShowContextMenu(const QPoint& global_pos) {
|
|
|
|
switch (model()->current_index().parent().data(InternetModel::Role_Type).toInt()) {
|
2011-01-09 19:27:41 +01:00
|
|
|
case Type_Artists:
|
|
|
|
case Type_Tags:
|
2010-10-07 15:59:24 +02:00
|
|
|
case Type_Custom:
|
2009-12-30 03:23:09 +01:00
|
|
|
remove_action_->setEnabled(true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
remove_action_->setEnabled(false);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-03-11 15:44:43 +01:00
|
|
|
const bool playable = model()->IsPlayable(model()->current_index());
|
2011-02-10 23:24:17 +01:00
|
|
|
GetAppendToPlaylistAction()->setEnabled(playable);
|
|
|
|
GetReplacePlaylistAction()->setEnabled(playable);
|
|
|
|
GetOpenInNewPlaylistAction()->setEnabled(playable);
|
2009-12-30 00:01:07 +01:00
|
|
|
context_menu_->popup(global_pos);
|
|
|
|
}
|
2009-12-30 01:31:00 +01:00
|
|
|
|
2011-09-24 18:01:18 +02:00
|
|
|
QStringList LastFMService::FriendNames() {
|
|
|
|
// Update the list for next time, in the main thread.
|
|
|
|
if (IsFriendsListStale())
|
|
|
|
metaObject()->invokeMethod(this, "RefreshFriends", Qt::QueuedConnection);
|
|
|
|
|
2011-11-05 23:56:10 +01:00
|
|
|
return friend_names_.Data();
|
2011-09-24 18:01:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2012-01-29 17:32:58 +01:00
|
|
|
s.setArrayIndex(i);
|
2011-09-24 18:01:18 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2011-09-24 14:48:14 +02:00
|
|
|
void LastFMService::ForceRefreshFriends() {
|
|
|
|
RefreshFriends(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LastFMService::RefreshFriends(bool force) {
|
2011-09-24 18:01:18 +02:00
|
|
|
if (!IsAuthenticated()) {
|
2009-12-30 01:31:00 +01:00
|
|
|
return;
|
2011-09-24 18:01:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!friends_list_) {
|
|
|
|
root_item_->setData(false, InternetModel::Role_CanLazyLoad);
|
|
|
|
LazyPopulate(root_item_);
|
|
|
|
}
|
2009-12-30 01:31:00 +01:00
|
|
|
|
2011-09-24 14:48:14 +02:00
|
|
|
if (!force && !IsFriendsListStale()) {
|
|
|
|
PopulateFriendsList();
|
|
|
|
return;
|
|
|
|
}
|
2009-12-30 01:31:00 +01:00
|
|
|
|
2012-06-28 18:41:51 +02:00
|
|
|
lastfm::compat::AuthenticatedUser user;
|
2009-12-30 01:31:00 +01:00
|
|
|
QNetworkReply* reply = user.getFriends();
|
2012-10-12 14:31:31 +02:00
|
|
|
NewClosure(reply, SIGNAL(finished()), this,
|
|
|
|
SLOT(RefreshFriendsFinished(QNetworkReply*)), reply);
|
2009-12-30 01:31:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void LastFMService::RefreshNeighbours() {
|
2011-01-09 19:27:41 +01:00
|
|
|
if (!neighbours_list_ || !IsAuthenticated())
|
2009-12-30 01:31:00 +01:00
|
|
|
return;
|
|
|
|
|
2012-06-28 18:41:51 +02:00
|
|
|
lastfm::compat::AuthenticatedUser user;
|
2009-12-30 01:31:00 +01:00
|
|
|
QNetworkReply* reply = user.getNeighbours();
|
2012-10-12 14:31:31 +02:00
|
|
|
NewClosure(reply, SIGNAL(finished()), this,
|
|
|
|
SLOT(RefreshNeighboursFinished(QNetworkReply*)), reply);
|
2009-12-30 01:31:00 +01:00
|
|
|
}
|
|
|
|
|
2012-10-12 14:31:31 +02:00
|
|
|
void LastFMService::RefreshFriendsFinished(QNetworkReply* reply) {
|
2009-12-30 01:31:00 +01:00
|
|
|
QList<lastfm::User> friends;
|
2012-06-28 18:41:51 +02:00
|
|
|
if (!lastfm::compat::ParseUserList(reply, &friends)) {
|
2009-12-30 01:31:00 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-11-05 23:56:10 +01:00
|
|
|
QStringList names;
|
2009-12-30 01:31:00 +01:00
|
|
|
foreach (const lastfm::User& f, friends) {
|
2011-11-05 23:56:10 +01:00
|
|
|
names << f.name();
|
2011-09-24 14:48:14 +02:00
|
|
|
}
|
|
|
|
|
2011-11-05 23:56:10 +01:00
|
|
|
friend_names_.Update(names);
|
2011-09-24 14:48:14 +02:00
|
|
|
|
|
|
|
PopulateFriendsList();
|
2011-09-24 18:01:18 +02:00
|
|
|
emit SavedItemsChanged();
|
2011-09-24 14:48:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void LastFMService::PopulateFriendsList() {
|
|
|
|
if (friends_list_->hasChildren())
|
|
|
|
friends_list_->removeRows(0, friends_list_->rowCount());
|
|
|
|
|
|
|
|
foreach (const QString& name, friend_names_) {
|
2011-04-27 00:06:58 +02:00
|
|
|
Song song;
|
2011-09-24 14:48:14 +02:00
|
|
|
song.set_url(QUrl("lastfm://user/" + name + "/library"));
|
|
|
|
song.set_title(tr("Last.fm Library - %1").arg(name));
|
2011-04-27 00:06:58 +02:00
|
|
|
|
2011-09-24 14:48:14 +02:00
|
|
|
QStandardItem* item = new QStandardItem(QIcon(":last.fm/icon_user.png"), name);
|
2011-07-15 15:27:50 +02:00
|
|
|
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);
|
2011-01-09 19:27:41 +01:00
|
|
|
friends_list_->appendRow(item);
|
2009-12-30 01:31:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-12 14:31:31 +02:00
|
|
|
void LastFMService::RefreshNeighboursFinished(QNetworkReply* reply) {
|
2009-12-30 01:31:00 +01:00
|
|
|
QList<lastfm::User> neighbours;
|
2012-06-28 18:41:51 +02:00
|
|
|
if (!lastfm::compat::ParseUserList(reply, &neighbours)) {
|
2009-12-30 01:31:00 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-09-24 14:48:14 +02:00
|
|
|
if (neighbours_list_->hasChildren())
|
|
|
|
neighbours_list_->removeRows(0, neighbours_list_->rowCount());
|
|
|
|
|
2009-12-30 01:31:00 +01:00
|
|
|
foreach (const lastfm::User& n, neighbours) {
|
2011-04-27 00:06:58 +02:00
|
|
|
Song song;
|
2011-04-28 14:27:53 +02:00
|
|
|
song.set_url(QUrl("lastfm://user/" + n.name() + "/library"));
|
2011-04-27 00:06:58 +02:00
|
|
|
song.set_title(tr("Last.fm Library - %1").arg(n.name()));
|
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
QStandardItem* item = new QStandardItem(QIcon(":last.fm/user_purple.png"), n.name());
|
2011-07-15 15:27:50 +02:00
|
|
|
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);
|
2011-01-09 19:27:41 +01:00
|
|
|
neighbours_list_->appendRow(item);
|
2009-12-30 01:31:00 +01:00
|
|
|
}
|
|
|
|
}
|
2009-12-30 02:41:37 +01:00
|
|
|
|
2009-12-30 03:15:38 +01:00
|
|
|
void LastFMService::AddArtistRadio() {
|
2011-01-09 19:27:41 +01:00
|
|
|
AddArtistOrTag("artists", LastFMStationDialog::Artist,
|
|
|
|
kUrlArtist, tr(kTitleArtist),
|
|
|
|
":last.fm/icon_radio.png", artist_list_);
|
2009-12-30 03:15:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void LastFMService::AddTagRadio() {
|
2011-01-09 19:27:41 +01:00
|
|
|
AddArtistOrTag("tags", LastFMStationDialog::Tag,
|
|
|
|
kUrlTag, tr(kTitleTag),
|
|
|
|
":last.fm/icon_tag.png", tag_list_);
|
2009-12-30 03:15:38 +01:00
|
|
|
}
|
|
|
|
|
2010-10-07 15:59:24 +02:00
|
|
|
void LastFMService::AddCustomRadio() {
|
2011-01-09 19:27:41 +01:00
|
|
|
AddArtistOrTag("custom", LastFMStationDialog::Custom,
|
|
|
|
kUrlCustom, tr(kTitleCustom),
|
|
|
|
":last.fm/icon_radio.png", custom_list_);
|
2010-10-07 15:59:24 +02:00
|
|
|
}
|
|
|
|
|
2009-12-30 03:15:38 +01:00
|
|
|
void LastFMService::AddArtistOrTag(const QString& name,
|
2011-01-09 19:27:41 +01:00
|
|
|
LastFMStationDialog::Type dialog_type,
|
|
|
|
const QString& url_pattern,
|
|
|
|
const QString& title_pattern,
|
|
|
|
const QString& icon, QStandardItem* list) {
|
2009-12-30 03:15:38 +01:00
|
|
|
station_dialog_->SetType(dialog_type);
|
|
|
|
if (station_dialog_->exec() == QDialog::Rejected)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (station_dialog_->content().isEmpty())
|
|
|
|
return;
|
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
QString content = station_dialog_->content();
|
2011-06-17 22:24:35 +02:00
|
|
|
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);
|
|
|
|
}
|
2011-01-09 19:27:41 +01:00
|
|
|
|
2011-04-27 00:06:58 +02:00
|
|
|
Song song;
|
2011-06-17 22:24:35 +02:00
|
|
|
song.set_url(QUrl((url)));
|
2011-04-27 00:06:58 +02:00
|
|
|
song.set_title(title_pattern.arg(content));
|
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
QStandardItem* item = new QStandardItem(QIcon(icon), content);
|
2011-07-15 15:27:50 +02:00
|
|
|
item->setData(QVariant::fromValue(song), InternetModel::Role_SongMetadata);
|
|
|
|
item->setData(InternetModel::PlayBehaviour_SingleItem, InternetModel::Role_PlayBehaviour);
|
2011-01-09 19:27:41 +01:00
|
|
|
list->appendRow(item);
|
2011-02-09 19:23:14 +01:00
|
|
|
emit AddItemToPlaylist(item->index(), AddMode_Append);
|
2009-12-30 03:15:38 +01:00
|
|
|
|
|
|
|
SaveList(name, list);
|
2011-09-24 18:01:18 +02:00
|
|
|
|
|
|
|
emit SavedItemsChanged();
|
2009-12-30 03:15:38 +01:00
|
|
|
}
|
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
void LastFMService::SaveList(const QString& name, QStandardItem* list) const {
|
2009-12-30 03:15:38 +01:00
|
|
|
QSettings settings;
|
|
|
|
settings.beginGroup(kSettingsGroup);
|
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
settings.beginWriteArray(name, list->rowCount());
|
|
|
|
for (int i=0 ; i<list->rowCount() ; ++i) {
|
2009-12-30 03:15:38 +01:00
|
|
|
settings.setArrayIndex(i);
|
2011-01-09 19:27:41 +01:00
|
|
|
settings.setValue("key", list->child(i)->text());
|
2009-12-30 03:15:38 +01:00
|
|
|
}
|
|
|
|
settings.endArray();
|
|
|
|
}
|
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
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());
|
2009-12-30 03:15:38 +01:00
|
|
|
|
2011-09-24 18:01:18 +02:00
|
|
|
const QStringList keys = SavedArtistOrTagRadioNames(name);
|
|
|
|
|
|
|
|
foreach (const QString& key, keys) {
|
2011-06-17 22:24:35 +02:00
|
|
|
QString url;
|
2011-09-24 18:01:18 +02:00
|
|
|
if (name == "custom" && key.startsWith("lastfm://")) {
|
|
|
|
url = key;
|
2011-06-17 22:24:35 +02:00
|
|
|
} else if (name == "custom") {
|
2011-09-24 18:01:18 +02:00
|
|
|
url = url_pattern.arg(QString(key.toUtf8().toBase64()));
|
2011-06-17 22:24:35 +02:00
|
|
|
} else {
|
2011-09-24 18:01:18 +02:00
|
|
|
url = url_pattern.arg(key);
|
2011-06-17 22:24:35 +02:00
|
|
|
}
|
2011-01-09 19:27:41 +01:00
|
|
|
|
2011-04-27 00:06:58 +02:00
|
|
|
Song song;
|
2011-06-17 22:24:35 +02:00
|
|
|
song.set_url(QUrl(url));
|
2011-09-24 18:01:18 +02:00
|
|
|
song.set_title(title_pattern.arg(key));
|
2011-04-27 00:06:58 +02:00
|
|
|
|
2011-09-24 18:01:18 +02:00
|
|
|
QStandardItem* item = new QStandardItem(icon, key);
|
2011-07-15 15:27:50 +02:00
|
|
|
item->setData(QVariant::fromValue(song), InternetModel::Role_SongMetadata);
|
|
|
|
item->setData(InternetModel::PlayBehaviour_SingleItem, InternetModel::Role_PlayBehaviour);
|
2011-01-09 19:27:41 +01:00
|
|
|
parent->appendRow(item);
|
2009-12-30 03:15:38 +01:00
|
|
|
}
|
|
|
|
}
|
2009-12-30 03:23:09 +01:00
|
|
|
|
|
|
|
void LastFMService::Remove() {
|
2012-03-11 15:44:43 +01:00
|
|
|
QStandardItem* context_item = model()->itemFromIndex(model()->current_index());
|
|
|
|
int type = context_item->parent()->data(InternetModel::Role_Type).toInt();
|
2009-12-30 03:23:09 +01:00
|
|
|
|
2012-03-11 15:44:43 +01:00
|
|
|
context_item->parent()->removeRow(context_item->row());
|
2009-12-30 03:23:09 +01:00
|
|
|
|
2011-01-09 19:27:41 +01:00
|
|
|
if (type == Type_Artists)
|
2009-12-30 03:23:09 +01:00
|
|
|
SaveList("artists", artist_list_);
|
2011-01-09 19:27:41 +01:00
|
|
|
else if (type == Type_Tags)
|
2009-12-30 03:23:09 +01:00
|
|
|
SaveList("tags", tag_list_);
|
2010-10-07 15:59:24 +02:00
|
|
|
else if (type == Type_Custom)
|
|
|
|
SaveList("custom", custom_list_);
|
2009-12-30 03:23:09 +01:00
|
|
|
}
|
2010-02-25 01:18:32 +01:00
|
|
|
|
|
|
|
void LastFMService::FetchMoreTracks() {
|
|
|
|
QMap<QString, QString> params;
|
|
|
|
params["method"] = "radio.getPlaylist";
|
|
|
|
params["rtp"] = "1";
|
|
|
|
QNetworkReply* reply = lastfm::ws::post(params);
|
2012-10-12 14:31:31 +02:00
|
|
|
NewClosure(reply, SIGNAL(finished()), this,
|
|
|
|
SLOT(FetchMoreTracksFinished(QNetworkReply*)), reply);
|
2010-02-25 01:18:32 +01:00
|
|
|
}
|
|
|
|
|
2012-10-12 14:31:31 +02:00
|
|
|
void LastFMService::FetchMoreTracksFinished(QNetworkReply* reply) {
|
2010-03-18 18:18:54 +01:00
|
|
|
reply->deleteLater();
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->task_manager()->SetTaskFinished(tune_task_id_);
|
2010-06-23 15:21:30 +02:00
|
|
|
tune_task_id_ = 0;
|
2010-02-25 01:18:32 +01:00
|
|
|
|
2012-06-28 18:41:51 +02:00
|
|
|
lastfm::XmlQuery query(lastfm::compat::EmptyXmlQuery());
|
|
|
|
if (lastfm::compat::ParseQuery(reply->readAll(), &query)) {
|
2010-03-18 18:18:54 +01:00
|
|
|
const XmlQuery& playlist = query["playlist"];
|
|
|
|
foreach (const XmlQuery& q, playlist["trackList"].children("track")) {
|
|
|
|
lastfm::MutableTrack t;
|
2012-09-26 17:09:13 +02:00
|
|
|
t.setUrl(QUrl(q["location"].text()));
|
2010-03-18 18:18:54 +01:00
|
|
|
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);
|
2010-05-17 00:46:56 +02:00
|
|
|
|
|
|
|
art_urls_[t] = q["image"].text();
|
2010-03-18 18:18:54 +01:00
|
|
|
playlist_ << t;
|
2010-02-25 01:18:32 +01:00
|
|
|
}
|
2012-06-25 12:30:53 +02:00
|
|
|
} else {
|
2012-06-28 18:41:51 +02:00
|
|
|
emit StreamError(tr("Couldn't load the last.fm radio station"));
|
2010-03-18 18:18:54 +01:00
|
|
|
return;
|
2010-02-25 01:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
TunerTrackAvailable();
|
|
|
|
}
|
|
|
|
|
2011-04-28 17:10:28 +02:00
|
|
|
void LastFMService::Tune(const QUrl& url) {
|
|
|
|
if (!tune_task_id_)
|
2012-02-12 14:41:50 +01:00
|
|
|
tune_task_id_ = app_->task_manager()->StartTask(tr("Loading Last.fm radio"));
|
2011-04-28 17:10:28 +02:00
|
|
|
|
|
|
|
last_url_ = url;
|
|
|
|
initial_tune_ = true;
|
2012-06-25 12:30:53 +02:00
|
|
|
const lastfm::RadioStation station(FixupUrl(url).toString());
|
2011-04-28 17:10:28 +02:00
|
|
|
|
2010-02-25 02:20:59 +01:00
|
|
|
playlist_.clear();
|
2010-05-17 00:46:56 +02:00
|
|
|
|
|
|
|
// Remove all the old album art URLs
|
|
|
|
art_urls_.clear();
|
2010-02-25 18:47:01 +01:00
|
|
|
|
2010-02-25 01:18:32 +01:00
|
|
|
QMap<QString, QString> params;
|
|
|
|
params["method"] = "radio.tune";
|
|
|
|
params["station"] = station.url();
|
|
|
|
QNetworkReply* reply = lastfm::ws::post(params);
|
2012-10-12 14:31:31 +02:00
|
|
|
NewClosure(reply, SIGNAL(finished()), this,
|
|
|
|
SLOT(TuneFinished(QNetworkReply*)), reply);
|
2010-02-25 01:18:32 +01:00
|
|
|
}
|
|
|
|
|
2012-10-12 14:31:31 +02:00
|
|
|
void LastFMService::TuneFinished(QNetworkReply* reply) {
|
2010-02-25 01:18:32 +01:00
|
|
|
reply->deleteLater();
|
2012-10-12 14:31:31 +02:00
|
|
|
FetchMoreTracks();
|
2010-02-25 01:18:32 +01:00
|
|
|
}
|
2010-05-18 22:43:10 +02:00
|
|
|
|
|
|
|
PlaylistItem::Options LastFMService::playlistitem_options() const {
|
2011-04-28 17:10:28 +02:00
|
|
|
return PlaylistItem::LastFMControls |
|
2011-11-28 19:19:11 +01:00
|
|
|
PlaylistItem::PauseDisabled |
|
|
|
|
PlaylistItem::SeekDisabled;
|
2010-05-18 22:43:10 +02:00
|
|
|
}
|
2010-10-16 17:22:14 +02:00
|
|
|
|
|
|
|
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")
|
2011-04-27 00:06:58 +02:00
|
|
|
return PlaylistItemPtr();
|
2010-10-16 17:22:14 +02:00
|
|
|
|
|
|
|
QStringList sections(url.path().split("/", QString::SkipEmptyParts));
|
|
|
|
|
2011-04-27 00:06:58 +02:00
|
|
|
Song song;
|
2011-04-28 14:27:53 +02:00
|
|
|
song.set_url(url);
|
2011-04-27 00:06:58 +02:00
|
|
|
|
2010-10-16 17:22:14 +02:00
|
|
|
if (sections.count() == 2 && url.host() == "artist" && sections[1] == "similarartists") {
|
2011-04-27 00:06:58 +02:00
|
|
|
song.set_title(tr(kTitleArtist).arg(sections[0]));
|
2010-10-16 17:22:14 +02:00
|
|
|
} else if (sections.count() == 1 && url.host() == "globaltags") {
|
2011-04-27 00:06:58 +02:00
|
|
|
song.set_title(tr(kTitleTag).arg(sections[0]));
|
|
|
|
} else {
|
|
|
|
return PlaylistItemPtr();
|
2010-10-16 17:22:14 +02:00
|
|
|
}
|
|
|
|
|
2011-07-15 15:27:50 +02:00
|
|
|
return PlaylistItemPtr(new InternetPlaylistItem(this, song));
|
2010-10-16 17:22:14 +02:00
|
|
|
}
|
2011-04-07 18:25:52 +02:00
|
|
|
|
|
|
|
void LastFMService::ToggleScrobbling() {
|
|
|
|
//toggle status
|
|
|
|
scrobbling_enabled_ = !scrobbling_enabled_;
|
|
|
|
|
|
|
|
//save to the settings
|
|
|
|
QSettings s;
|
|
|
|
s.beginGroup(kSettingsGroup);
|
|
|
|
s.setValue("ScrobblingEnabled", scrobbling_enabled_);
|
|
|
|
s.endGroup();
|
|
|
|
|
|
|
|
emit ScrobblingEnabledChanged(scrobbling_enabled_);
|
|
|
|
}
|