Play last.fm tag radio or similar artists radio when clicking on a tag in the song/artist info pane.

This commit is contained in:
David Sansome 2010-10-16 15:22:14 +00:00
parent a41b6de040
commit b97b2138fa
15 changed files with 96 additions and 23 deletions

View File

@ -65,7 +65,7 @@ class Player : public QObject {
Engine::State GetState() const; Engine::State GetState() const;
int GetVolume() const; int GetVolume() const;
boost::shared_ptr<PlaylistItem> GetCurrentItem() const { return current_item_; } PlaylistItemPtr GetCurrentItem() const { return current_item_; }
// MPRIS // MPRIS
enum DBusCaps { enum DBusCaps {
@ -175,7 +175,7 @@ class Player : public QObject {
LastFMService* lastfm_; LastFMService* lastfm_;
QSettings settings_; QSettings settings_;
boost::shared_ptr<PlaylistItem> current_item_; PlaylistItemPtr current_item_;
boost::scoped_ptr<EngineBase> engine_; boost::scoped_ptr<EngineBase> engine_;
Engine::TrackChangeType stream_change_type_; Engine::TrackChangeType stream_change_type_;

View File

@ -673,7 +673,7 @@ QModelIndex Playlist::InsertItemsWithoutUndo(const PlaylistItemList& items,
beginInsertRows(QModelIndex(), start, end); beginInsertRows(QModelIndex(), start, end);
for (int i=start ; i<=end ; ++i) { for (int i=start ; i<=end ; ++i) {
boost::shared_ptr<PlaylistItem> item = items[i - start]; PlaylistItemPtr item = items[i - start];
items_.insert(i, item); items_.insert(i, item);
virtual_items_ << virtual_items_.count(); virtual_items_ << virtual_items_.count();
@ -948,7 +948,7 @@ PlaylistItemList Playlist::RemoveItemsWithoutUndo(int row, int count) {
// Remove items // Remove items
PlaylistItemList ret; PlaylistItemList ret;
for (int i=0 ; i<count ; ++i) { for (int i=0 ; i<count ; ++i) {
boost::shared_ptr<PlaylistItem> item(items_.takeAt(row)); PlaylistItemPtr item(items_.takeAt(row));
ret << item; ret << item;
if (item->type() == "Library") { if (item->type() == "Library") {
@ -1118,7 +1118,7 @@ QSortFilterProxyModel* Playlist::proxy() const {
SongList Playlist::GetAllSongs() const { SongList Playlist::GetAllSongs() const {
SongList ret; SongList ret;
foreach (boost::shared_ptr<PlaylistItem> item, items_) { foreach (PlaylistItemPtr item, items_) {
ret << item->Metadata(); ret << item->Metadata();
} }
return ret; return ret;
@ -1126,7 +1126,7 @@ SongList Playlist::GetAllSongs() const {
quint64 Playlist::GetTotalLength() const { quint64 Playlist::GetTotalLength() const {
quint64 ret = 0; quint64 ret = 0;
foreach (boost::shared_ptr<PlaylistItem> item, items_) { foreach (PlaylistItemPtr item, items_) {
int length = item->Metadata().length(); int length = item->Metadata().length();
if (length > 0) if (length > 0)
ret += length; ret += length;

View File

@ -94,8 +94,7 @@ class Playlist : public QAbstractListModel {
static const char* kPlayNowMimetype; static const char* kPlayNowMimetype;
static bool CompareItems(int column, Qt::SortOrder order, static bool CompareItems(int column, Qt::SortOrder order,
boost::shared_ptr<PlaylistItem> a, PlaylistItemPtr a, PlaylistItemPtr b);
boost::shared_ptr<PlaylistItem> b);
static QString column_name(Column column); static QString column_name(Column column);
static bool column_is_editable(Playlist::Column column); static bool column_is_editable(Playlist::Column column);
@ -116,8 +115,8 @@ class Playlist : public QAbstractListModel {
int previous_index() const; int previous_index() const;
bool stop_after_current() const; bool stop_after_current() const;
const boost::shared_ptr<PlaylistItem>& item_at(int index) const { return items_[index]; } const PlaylistItemPtr& item_at(int index) const { return items_[index]; }
boost::shared_ptr<PlaylistItem> current_item() const { return current_item_; } PlaylistItemPtr current_item() const { return current_item_; }
PlaylistItem::Options current_item_options() const; PlaylistItem::Options current_item_options() const;
Song current_item_metadata() const; Song current_item_metadata() const;
@ -226,7 +225,7 @@ class Playlist : public QAbstractListModel {
// that they will be played. // that they will be played.
// A map of library ID to playlist item - for fast lookups when library // A map of library ID to playlist item - for fast lookups when library
// items change. // items change.
QMultiMap<int, boost::shared_ptr<PlaylistItem> > library_items_by_id_; QMultiMap<int, PlaylistItemPtr> library_items_by_id_;
QPersistentModelIndex current_item_index_; QPersistentModelIndex current_item_index_;
QPersistentModelIndex last_played_item_index_; QPersistentModelIndex last_played_item_index_;
@ -234,7 +233,7 @@ class Playlist : public QAbstractListModel {
bool current_is_paused_; bool current_is_paused_;
int current_virtual_index_; int current_virtual_index_;
boost::shared_ptr<PlaylistItem> current_item_; PlaylistItemPtr current_item_;
bool is_shuffled_; bool is_shuffled_;

View File

@ -40,7 +40,7 @@ class PlaylistBackend : public QObject {
int last_played; int last_played;
}; };
typedef QList<Playlist> PlaylistList; typedef QList<Playlist> PlaylistList;
typedef QFuture<boost::shared_ptr<PlaylistItem> > PlaylistItemFuture; typedef QFuture<PlaylistItemPtr> PlaylistItemFuture;
PlaylistList GetAllPlaylists(); PlaylistList GetAllPlaylists();
Playlist GetPlaylist(int id); Playlist GetPlaylist(int id);
@ -57,7 +57,7 @@ class PlaylistBackend : public QObject {
void SavePlaylist(int playlist, const PlaylistItemList& items, int last_played); void SavePlaylist(int playlist, const PlaylistItemList& items, int last_played);
private: private:
static boost::shared_ptr<PlaylistItem> NewSongFromQuery(const SqlRow& row); static PlaylistItemPtr NewSongFromQuery(const SqlRow& row);
boost::shared_ptr<Database> db_; boost::shared_ptr<Database> db_;
}; };

View File

@ -63,7 +63,7 @@ void PlaylistItem::ClearTemporaryMetadata() {
temp_metadata_ = Song(); temp_metadata_ = Song();
} }
static void ReloadPlaylistItem(boost::shared_ptr<PlaylistItem> item) { static void ReloadPlaylistItem(PlaylistItemPtr item) {
item->Reload(); item->Reload();
} }

View File

@ -128,7 +128,8 @@ class PlaylistItem : public boost::enable_shared_from_this<PlaylistItem> {
Song temp_metadata_; Song temp_metadata_;
}; };
typedef QList<boost::shared_ptr<PlaylistItem> > PlaylistItemList; typedef boost::shared_ptr<PlaylistItem> PlaylistItemPtr;
typedef QList<PlaylistItemPtr> PlaylistItemList;
Q_DECLARE_OPERATORS_FOR_FLAGS(PlaylistItem::Options); Q_DECLARE_OPERATORS_FOR_FLAGS(PlaylistItem::Options);

View File

@ -263,7 +263,7 @@ void PlaylistManager::SongsDiscovered(const SongList& songs) {
foreach (const Song& song, songs) { foreach (const Song& song, songs) {
foreach (const Data& data, playlists_) { foreach (const Data& data, playlists_) {
PlaylistItemList items = data.p->library_items_by_id(song.id()); PlaylistItemList items = data.p->library_items_by_id(song.id());
foreach (boost::shared_ptr<PlaylistItem> item, items) { foreach (PlaylistItemPtr item, items) {
if (item->Metadata().directory_id() != song.directory_id()) if (item->Metadata().directory_id() != song.directory_id())
continue; continue;
static_cast<LibraryPlaylistItem*>(item.get())->SetMetadata(song); static_cast<LibraryPlaylistItem*>(item.get())->SetMetadata(song);

View File

@ -18,6 +18,7 @@
#include "radioitem.h" #include "radioitem.h"
#include "lastfmstationdialog.h" #include "lastfmstationdialog.h"
#include "radiomodel.h" #include "radiomodel.h"
#include "radioplaylistitem.h"
#include "core/networkaccessmanager.h" #include "core/networkaccessmanager.h"
#include "core/song.h" #include "core/song.h"
#include "core/taskmanager.h" #include "core/taskmanager.h"
@ -725,3 +726,25 @@ PlaylistItem::Options LastFMService::playlistitem_options() const {
PlaylistItem::PauseDisabled | PlaylistItem::PauseDisabled |
PlaylistItem::ContainsMultipleTracks; PlaylistItem::ContainsMultipleTracks;
} }
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.
PlaylistItemPtr ret;
if (url.scheme() != "lastfm")
return ret;
QStringList sections(url.path().split("/", QString::SkipEmptyParts));
if (sections.count() == 2 && url.host() == "artist" && sections[1] == "similarartists") {
ret.reset(new RadioPlaylistItem(this, url,
tr("Last.fm Similar Artists to %1").arg(sections[0]), QString()));
} else if (sections.count() == 1 && url.host() == "globaltags") {
ret.reset(new RadioPlaylistItem(this, url,
tr("Last.fm Tag Radio: %1").arg(sections[0]), QString()));
}
return ret;
}

View File

@ -32,6 +32,7 @@ uint qHash(const lastfm::Track& track);
#include "radioservice.h" #include "radioservice.h"
#include "lastfmstationdialog.h" #include "lastfmstationdialog.h"
#include "core/song.h" #include "core/song.h"
#include "playlist/playlistitem.h"
#include <QMap> #include <QMap>
#include <QMenu> #include <QMenu>
@ -104,11 +105,14 @@ class LastFMService : public RadioService {
void FetchMoreTracks(); void FetchMoreTracks();
PlaylistItemPtr PlaylistItemForUrl(const QUrl& url);
public slots: public slots:
void NowPlaying(const Song& song); void NowPlaying(const Song& song);
void Scrobble(); void Scrobble();
void Love(); void Love();
void Ban(); void Ban();
void ShowConfig();
signals: signals:
void AuthenticationComplete(bool success); void AuthenticationComplete(bool success);
@ -119,7 +123,6 @@ class LastFMService : public RadioService {
void AuthenticateReplyFinished(); void AuthenticateReplyFinished();
void RefreshFriendsFinished(); void RefreshFriendsFinished();
void RefreshNeighboursFinished(); void RefreshNeighboursFinished();
void ShowConfig();
void TunerTrackAvailable(); void TunerTrackAvailable();
void TunerError(lastfm::ws::Error error); void TunerError(lastfm::ws::Error error);

View File

@ -212,4 +212,8 @@ void SongInfoBase::ConnectWidget(QWidget* widget) {
if (widget->metaObject()->indexOfSignal("ShowSettingsDialog()") != -1) { if (widget->metaObject()->indexOfSignal("ShowSettingsDialog()") != -1) {
connect(widget, SIGNAL(ShowSettingsDialog()), SIGNAL(ShowSettingsDialog())); connect(widget, SIGNAL(ShowSettingsDialog()), SIGNAL(ShowSettingsDialog()));
} }
if (widget->metaObject()->indexOfSignal("AddPlaylistItems(PlaylistItemList)") != -1) {
connect(widget, SIGNAL(AddPlaylistItems(PlaylistItemList)), SIGNAL(AddPlaylistItems(PlaylistItemList)));
}
} }

View File

@ -22,6 +22,7 @@
#include "collapsibleinfopane.h" #include "collapsibleinfopane.h"
#include "songinfofetcher.h" #include "songinfofetcher.h"
#include "core/song.h" #include "core/song.h"
#include "playlist/playlistitem.h"
#include "widgets/widgetfadehelper.h" #include "widgets/widgetfadehelper.h"
class CollapsibleInfoPane; class CollapsibleInfoPane;
@ -46,6 +47,7 @@ public slots:
signals: signals:
void ShowSettingsDialog(); void ShowSettingsDialog();
void AddPlaylistItems(const PlaylistItemList& items);
protected: protected:
void showEvent(QShowEvent* e); void showEvent(QShowEvent* e);

View File

@ -15,6 +15,8 @@
*/ */
#include "tagwidget.h" #include "tagwidget.h"
#include "radio/lastfmservice.h"
#include "radio/radiomodel.h"
#include "ui/flowlayout.h" #include "ui/flowlayout.h"
#include <QPainter> #include <QPainter>
@ -93,6 +95,10 @@ void TagWidgetTag::paintEvent(QPaintEvent*) {
p.drawText(text_rect, text_); p.drawText(text_rect, text_);
} }
void TagWidgetTag::mouseReleaseEvent(QMouseEvent*) {
emit Clicked();
}
TagWidget::TagWidget(QWidget* parent) TagWidget::TagWidget(QWidget* parent)
: QWidget(parent) : QWidget(parent)
@ -105,6 +111,27 @@ void TagWidget::AddTag(const QString& tag) {
return; return;
TagWidgetTag* widget = new TagWidgetTag(icon_, tag, this); TagWidgetTag* widget = new TagWidgetTag(icon_, tag, this);
connect(widget, SIGNAL(Clicked()), SLOT(TagClicked()));
layout()->addWidget(widget); layout()->addWidget(widget);
tags_ << widget; tags_ << widget;
} }
void TagWidget::TagClicked() {
TagWidgetTag* tag = qobject_cast<TagWidgetTag*>(sender());
if (!tag)
return;
LastFMService* last_fm = RadioModel::Service<LastFMService>();
if (!last_fm->IsAuthenticated()) {
last_fm->ShowConfig();
return;
}
QUrl url(url_pattern_.arg(tag->text()));
PlaylistItemPtr item(last_fm->PlaylistItemForUrl(url));
if (!item)
return;
emit AddPlaylistItems(PlaylistItemList() << item);
}

View File

@ -17,6 +17,8 @@
#ifndef TAGWIDGET_H #ifndef TAGWIDGET_H
#define TAGWIDGET_H #define TAGWIDGET_H
#include "playlist/playlistitem.h"
#include <QIcon> #include <QIcon>
#include <QWidget> #include <QWidget>
@ -40,11 +42,16 @@ public:
void set_background_opacity(float opacity); void set_background_opacity(float opacity);
QSize sizeHint() const; QSize sizeHint() const;
QString text() const { return text_; }
signals:
void Clicked();
protected: protected:
void enterEvent(QEvent*); void enterEvent(QEvent*);
void leaveEvent(QEvent*); void leaveEvent(QEvent*);
void paintEvent(QPaintEvent*); void paintEvent(QPaintEvent*);
void mouseReleaseEvent(QMouseEvent*);
private: private:
QString text_; QString text_;
@ -66,6 +73,12 @@ public:
int count() const { return tags_.count(); } int count() const { return tags_.count(); }
signals:
void AddPlaylistItems(const PlaylistItemList& items);
private slots:
void TagClicked();
private: private:
QString url_pattern_; QString url_pattern_;
QIcon icon_; QIcon icon_;

View File

@ -1596,6 +1596,7 @@ void MainWindow::ConnectInfoView(SongInfoBase* view) {
connect(player_, SIGNAL(Stopped()), view, SLOT(SongFinished())); connect(player_, SIGNAL(Stopped()), view, SLOT(SongFinished()));
connect(view, SIGNAL(ShowSettingsDialog()), SLOT(ShowSongInfoConfig())); connect(view, SIGNAL(ShowSettingsDialog()), SLOT(ShowSongInfoConfig()));
connect(view, SIGNAL(AddPlaylistItems(PlaylistItemList)), SLOT(InsertRadioItems(PlaylistItemList)));
} }
void MainWindow::ShowSongInfoConfig() { void MainWindow::ShowSongInfoConfig() {

View File

@ -440,7 +440,7 @@ TEST_F(PlaylistTest, LibraryIdMapSingle) {
song.Init("title", "artist", "album", 123); song.Init("title", "artist", "album", 123);
song.set_id(1); song.set_id(1);
boost::shared_ptr<PlaylistItem> item(new LibraryPlaylistItem(song)); PlaylistItemPtr item(new LibraryPlaylistItem(song));
playlist_.InsertItems(PlaylistItemList() << item); playlist_.InsertItems(PlaylistItemList() << item);
EXPECT_EQ(0, playlist_.library_items_by_id(-1).count()); EXPECT_EQ(0, playlist_.library_items_by_id(-1).count());
@ -459,7 +459,7 @@ TEST_F(PlaylistTest, LibraryIdMapInvalid) {
invalid.Init("title", "artist", "album", 123); invalid.Init("title", "artist", "album", 123);
ASSERT_EQ(-1, invalid.id()); ASSERT_EQ(-1, invalid.id());
boost::shared_ptr<PlaylistItem> item(new LibraryPlaylistItem(invalid)); PlaylistItemPtr item(new LibraryPlaylistItem(invalid));
playlist_.InsertItems(PlaylistItemList() << item); playlist_.InsertItems(PlaylistItemList() << item);
EXPECT_EQ(0, playlist_.library_items_by_id(-1).count()); EXPECT_EQ(0, playlist_.library_items_by_id(-1).count());
@ -477,9 +477,9 @@ TEST_F(PlaylistTest, LibraryIdMapMulti) {
two.Init("title 2", "artist 2", "album 2", 123); two.Init("title 2", "artist 2", "album 2", 123);
two.set_id(2); two.set_id(2);
boost::shared_ptr<PlaylistItem> item_one(new LibraryPlaylistItem(one)); PlaylistItemPtr item_one(new LibraryPlaylistItem(one));
boost::shared_ptr<PlaylistItem> item_two(new LibraryPlaylistItem(two)); PlaylistItemPtr item_two(new LibraryPlaylistItem(two));
boost::shared_ptr<PlaylistItem> item_three(new LibraryPlaylistItem(one)); PlaylistItemPtr item_three(new LibraryPlaylistItem(one));
playlist_.InsertItems(PlaylistItemList() << item_one << item_two << item_three); playlist_.InsertItems(PlaylistItemList() << item_one << item_two << item_three);
EXPECT_EQ(2, playlist_.library_items_by_id(1).count()); EXPECT_EQ(2, playlist_.library_items_by_id(1).count());