Friend and neighbour radio

This commit is contained in:
David Sansome 2009-12-30 00:31:00 +00:00
parent 92db175819
commit 56e685bb3b
16 changed files with 226 additions and 15 deletions

@ -41,5 +41,11 @@
<file>spinner.gif</file> <file>spinner.gif</file>
<file>last.fm/ban.png</file> <file>last.fm/ban.png</file>
<file>last.fm/love.png</file> <file>last.fm/love.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/my_friends.png</file>
<file>last.fm/my_neighbours.png</file>
<file>last.fm/user_purple.png</file>
</qresource> </qresource>
</RCC> </RCC>

BIN
data/last.fm/icon_radio.png Normal file

Binary file not shown.

After

(image error) Size: 398 B

BIN
data/last.fm/icon_tag.png Normal file

Binary file not shown.

After

(image error) Size: 600 B

BIN
data/last.fm/icon_user.png Normal file

Binary file not shown.

After

(image error) Size: 729 B

BIN
data/last.fm/my_friends.png Normal file

Binary file not shown.

After

(image error) Size: 1.4 KiB

Binary file not shown.

After

(image error) Size: 1.3 KiB

Binary file not shown.

After

(image error) Size: 798 B

@ -23,7 +23,9 @@ LastFMService::LastFMService(QObject* parent)
scrobbler_(NULL), scrobbler_(NULL),
context_menu_(new QMenu), context_menu_(new QMenu),
initial_tune_(false), initial_tune_(false),
scrobbling_enabled_(false) scrobbling_enabled_(false),
friends_list_(NULL),
neighbours_list_(NULL)
{ {
lastfm::ws::ApiKey = kApiKey; lastfm::ws::ApiKey = kApiKey;
lastfm::ws::SharedSecret = kSecret; lastfm::ws::SharedSecret = kSecret;
@ -70,9 +72,11 @@ RadioItem* LastFMService::CreateRootItem(RadioItem* parent) {
} }
void LastFMService::LazyPopulate(RadioItem *item) { void LastFMService::LazyPopulate(RadioItem *item) {
RadioItem* c = NULL;
switch (item->type) { switch (item->type) {
case RadioItem::Type_Service: case RadioItem::Type_Service:
// Create child items // Normal radio types
CreateStationItem(Type_MyRecommendations, "My Recommendations", CreateStationItem(Type_MyRecommendations, "My Recommendations",
":last.fm/recommended_radio.png", item); ":last.fm/recommended_radio.png", item);
CreateStationItem(Type_MyRadio, "My Radio Station", CreateStationItem(Type_MyRadio, "My Radio Station",
@ -82,10 +86,34 @@ void LastFMService::LazyPopulate(RadioItem *item) {
CreateStationItem(Type_MyNeighbourhood, "My Neighbourhood", CreateStationItem(Type_MyNeighbourhood, "My Neighbourhood",
":last.fm/neighbour_radio.png", item); ":last.fm/neighbour_radio.png", item);
// Types that spawn a popup dialog
c = CreateStationItem(Type_ArtistRadio, "Artist radio...",
":last.fm/icon_radio.png", item);
c->playable = false;
c = CreateStationItem(Type_TagRadio, "Tag radio...",
":last.fm/icon_tag.png", item);
c->playable = false;
// Types that have children
friends_list_ = new RadioItem(this, Type_MyFriends, "Friends", item);
friends_list_->icon = QIcon(":last.fm/my_friends.png");
neighbours_list_ = new RadioItem(this, Type_MyNeighbours, "Neighbours", item);
neighbours_list_->icon = QIcon(":last.fm/my_neighbours.png");
if (!IsAuthenticated()) if (!IsAuthenticated())
config_->show(); config_->show();
break; break;
case Type_MyFriends:
RefreshFriends();
break;
case Type_MyNeighbours:
RefreshNeighbours();
break;
default: default:
break; break;
} }
@ -158,18 +186,26 @@ QUrl LastFMService::UrlForItem(const RadioItem* item) const {
case Type_MyRadio: case Type_MyRadio:
return "lastfm://user/" + lastfm::ws::Username + "/library"; return "lastfm://user/" + lastfm::ws::Username + "/library";
case Type_FriendRadio:
return "lastfm://user/" + item->key + "/library";
case Type_NeighbourRadio:
return "lastfm://user/" + item->key + "/library";
} }
return QUrl(); return QUrl();
} }
QString LastFMService::TitleForItem(const RadioItem* item) const { QString LastFMService::TitleForItem(const RadioItem* item) const {
const QString user(lastfm::ws::Username); const QString me(lastfm::ws::Username);
switch (item->type) { switch (item->type) {
case Type_MyRecommendations: return user + "'s Recommended Radio"; case Type_MyRecommendations: return me + "'s Recommended Radio";
case Type_MyLoved: return user + "'s Loved Tracks"; case Type_MyLoved: return me + "'s Loved Tracks";
case Type_MyNeighbourhood: return user + "'s Neighbour Radio"; case Type_MyNeighbourhood: return me + "'s Neighbour Radio";
case Type_MyRadio: return user + "'s Library"; case Type_MyRadio: return me + "'s Library";
case Type_FriendRadio: return item->key + "'s Library";
case Type_NeighbourRadio: return item->key + "'s Library";
} }
return QString(); return QString();
} }
@ -321,3 +357,71 @@ void LastFMService::Ban() {
void LastFMService::ShowContextMenu(RadioItem *, const QPoint &global_pos) { void LastFMService::ShowContextMenu(RadioItem *, const QPoint &global_pos) {
context_menu_->popup(global_pos); context_menu_->popup(global_pos);
} }
void LastFMService::RefreshFriends() {
if (!friends_list_ || !IsAuthenticated())
return;
friends_list_->ClearNotify();
lastfm::AuthenticatedUser user;
QNetworkReply* reply = user.getFriends();
connect(reply, SIGNAL(finished()), SLOT(RefreshFriendsFinished()));
}
void LastFMService::RefreshNeighbours() {
if (!friends_list_ || !IsAuthenticated())
return;
neighbours_list_->ClearNotify();
lastfm::AuthenticatedUser user;
QNetworkReply* reply = user.getNeighbours();
connect(reply, SIGNAL(finished()), SLOT(RefreshNeighboursFinished()));
}
void LastFMService::RefreshFriendsFinished() {
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
if (!reply)
return;
QList<lastfm::User> friends;
try {
friends = lastfm::User::list(reply);
} catch (std::runtime_error& e) {
qDebug() << e.what();
return;
}
foreach (const lastfm::User& f, friends) {
RadioItem* item = new RadioItem(this, Type_FriendRadio, f);
item->icon = QIcon(":last.fm/icon_user.png");
item->playable = true;
item->lazy_loaded = true;
item->InsertNotify(friends_list_);
}
}
void LastFMService::RefreshNeighboursFinished() {
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
if (!reply)
return;
QList<lastfm::User> neighbours;
try {
neighbours = lastfm::User::list(reply);
} catch (std::runtime_error& e) {
qDebug() << e.what();
return;
}
foreach (const lastfm::User& n, neighbours) {
RadioItem* item = new RadioItem(this, Type_NeighbourRadio, n);
item->icon = QIcon(":last.fm/user_purple.png");
item->playable = true;
item->lazy_loaded = true;
item->InsertNotify(neighbours_list_);
}
}

@ -28,6 +28,12 @@ class LastFMService : public RadioService {
Type_MyRadio, Type_MyRadio,
Type_MyLoved, Type_MyLoved,
Type_MyNeighbourhood, Type_MyNeighbourhood,
Type_ArtistRadio,
Type_TagRadio,
Type_MyFriends,
Type_MyNeighbours,
Type_FriendRadio,
Type_NeighbourRadio,
}; };
// RadioService // RadioService
@ -48,11 +54,11 @@ class LastFMService : public RadioService {
// Last.fm specific stuff // Last.fm specific stuff
bool IsAuthenticated() const; bool IsAuthenticated() const;
bool IsScrobblingEnabled() const { return scrobbling_enabled_; } bool IsScrobblingEnabled() const { return scrobbling_enabled_; }
void Authenticate(const QString& username, const QString& password); void Authenticate(const QString& username, const QString& password);
void NowPlaying(const Song& song);
public slots: public slots:
void NowPlaying(const Song& song);
void Scrobble(); void Scrobble();
void Love(); void Love();
void Ban(); void Ban();
@ -64,6 +70,8 @@ class LastFMService : public RadioService {
private slots: private slots:
void AuthenticateReplyFinished(); void AuthenticateReplyFinished();
void ScrobblingEnabledChangedSlot(bool value); void ScrobblingEnabledChangedSlot(bool value);
void RefreshFriendsFinished();
void RefreshNeighboursFinished();
void TunerTrackAvailable(); void TunerTrackAvailable();
void TunerError(lastfm::ws::Error error); void TunerError(lastfm::ws::Error error);
@ -74,6 +82,8 @@ class LastFMService : public RadioService {
QString ErrorString(lastfm::ws::Error error) const; QString ErrorString(lastfm::ws::Error error) const;
bool InitScrobbler(); bool InitScrobbler();
lastfm::Track TrackFromSong(const Song& song) const; lastfm::Track TrackFromSong(const Song& song) const;
void RefreshFriends();
void RefreshNeighbours();
private: private:
lastfm::RadioTuner* tuner_; lastfm::RadioTuner* tuner_;
@ -87,6 +97,9 @@ class LastFMService : public RadioService {
bool initial_tune_; bool initial_tune_;
bool scrobbling_enabled_; bool scrobbling_enabled_;
RadioItem* friends_list_;
RadioItem* neighbours_list_;
}; };
#endif // LASTFMSERVICE_H #endif // LASTFMSERVICE_H

@ -11,7 +11,7 @@
Library::Library(EngineBase* engine, QObject* parent) Library::Library(EngineBase* engine, QObject* parent)
: SimpleTreeModel<LibraryItem>(new LibraryItem(LibraryItem::Type_Root), parent), : SimpleTreeModel<LibraryItem>(new LibraryItem(this), parent),
engine_(engine), engine_(engine),
backend_(new BackgroundThread<LibraryBackend>(this)), backend_(new BackgroundThread<LibraryBackend>(this)),
watcher_(new BackgroundThread<LibraryWatcher>(this)), watcher_(new BackgroundThread<LibraryWatcher>(this)),
@ -357,7 +357,7 @@ void Library::Reset() {
divider_nodes_.clear(); divider_nodes_.clear();
compilation_artist_node_ = NULL; compilation_artist_node_ = NULL;
root_ = new LibraryItem(LibraryItem::Type_Root); root_ = new LibraryItem(this);
root_->lazy_loaded = true; root_->lazy_loaded = true;
// Various artists? // Various artists?

@ -19,6 +19,8 @@ class LibraryItem : public SimpleTreeItem<LibraryItem> {
Type_Song, Type_Song,
}; };
LibraryItem(SimpleTreeModel<LibraryItem>* model)
: SimpleTreeItem<LibraryItem>(Type_Root, model) {}
LibraryItem(Type type, const QString& key = QString::null, LibraryItem* parent = NULL) LibraryItem(Type type, const QString& key = QString::null, LibraryItem* parent = NULL)
: SimpleTreeItem<LibraryItem>(type, key, parent) {} : SimpleTreeItem<LibraryItem>(type, key, parent) {}

@ -1,6 +1,12 @@
#include "radioitem.h" #include "radioitem.h"
#include "radioservice.h" #include "radioservice.h"
RadioItem::RadioItem(SimpleTreeModel<RadioItem> *model)
: SimpleTreeItem<RadioItem>(Type_Root, model),
service(NULL)
{
}
RadioItem::RadioItem(RadioService* _service, int type, const QString& key, RadioItem::RadioItem(RadioService* _service, int type, const QString& key,
RadioItem* parent) RadioItem* parent)
: SimpleTreeItem<RadioItem>(type, key, parent), : SimpleTreeItem<RadioItem>(type, key, parent),

@ -15,6 +15,7 @@ class RadioItem : public SimpleTreeItem<RadioItem> {
Type_Service, Type_Service,
}; };
RadioItem(SimpleTreeModel<RadioItem>* model);
RadioItem(RadioService* _service, int type, const QString& key = QString::null, RadioItem(RadioService* _service, int type, const QString& key = QString::null,
RadioItem* parent = NULL); RadioItem* parent = NULL);

@ -9,7 +9,7 @@
QMap<QString, RadioService*> RadioModel::sServices; QMap<QString, RadioService*> RadioModel::sServices;
RadioModel::RadioModel(QObject* parent) RadioModel::RadioModel(QObject* parent)
: SimpleTreeModel<RadioItem>(new RadioItem(NULL, RadioItem::Type_Root), parent) : SimpleTreeModel<RadioItem>(new RadioItem(this), parent)
{ {
Q_ASSERT(sServices.isEmpty()); Q_ASSERT(sServices.isEmpty());

@ -1,15 +1,22 @@
#ifndef SIMPLETREEITEM_H #ifndef SIMPLETREEITEM_H
#define SIMPLETREEITEM_H #define SIMPLETREEITEM_H
#include "simpletreemodel.h"
#include <QString> #include <QString>
#include <QList> #include <QList>
template <typename T> template <typename T>
class SimpleTreeItem { class SimpleTreeItem {
public: public:
SimpleTreeItem(int _type, SimpleTreeModel<T>* _model); // For the root item
SimpleTreeItem(int _type, const QString& _key = QString::null, T* _parent = NULL); SimpleTreeItem(int _type, const QString& _key = QString::null, T* _parent = NULL);
virtual ~SimpleTreeItem(); virtual ~SimpleTreeItem();
void InsertNotify(T* _parent);
void DeleteNotify(int child_row);
void ClearNotify();
void Delete(int child_row); void Delete(int child_row);
T* ChildByKey(const QString& key) const; T* ChildByKey(const QString& key) const;
@ -26,23 +33,67 @@ class SimpleTreeItem {
T* parent; T* parent;
QList<T*> children; QList<T*> children;
SimpleTreeModel<T>* model;
}; };
template <typename T>
SimpleTreeItem<T>::SimpleTreeItem(int _type, SimpleTreeModel<T>* _model)
: type(_type),
row(0),
lazy_loaded(true),
parent(NULL),
model(_model)
{
}
template <typename T> template <typename T>
SimpleTreeItem<T>::SimpleTreeItem(int _type, const QString& _key, T* _parent) SimpleTreeItem<T>::SimpleTreeItem(int _type, const QString& _key, T* _parent)
: type(_type), : type(_type),
key(_key), key(_key),
lazy_loaded(false), lazy_loaded(false),
parent(_parent) parent(_parent),
model(_parent ? _parent->model : NULL)
{ {
if (parent) { if (parent) {
row = parent->children.count(); row = parent->children.count();
parent->children << static_cast<T*>(this); parent->children << static_cast<T*>(this);
} else {
row = 0;
} }
} }
template <typename T>
void SimpleTreeItem<T>::InsertNotify(T* _parent) {
parent = _parent;
model = parent->model;
row = parent->children.count();
model->BeginInsert(parent, row);
parent->children << static_cast<T*>(this);
model->EndInsert();
}
template <typename T>
void SimpleTreeItem<T>::DeleteNotify(int child_row) {
model->BeginDelete(static_cast<T*>(this), child_row);
delete children.takeAt(child_row);
// Adjust row numbers of those below it :(
for (int i=child_row ; i<children.count() ; ++i)
children[i]->row --;
model->EndDelete();
}
template <typename T>
void SimpleTreeItem<T>::ClearNotify() {
model->BeginDelete(static_cast<T*>(this), 0, children.count()-1);
qDeleteAll(children);
children.clear();
model->EndDelete();
}
template <typename T> template <typename T>
SimpleTreeItem<T>::~SimpleTreeItem() { SimpleTreeItem<T>::~SimpleTreeItem() {
qDeleteAll(children); qDeleteAll(children);

@ -21,6 +21,12 @@ class SimpleTreeModel : public QAbstractItemModel {
T* IndexToItem(const QModelIndex& index) const; T* IndexToItem(const QModelIndex& index) const;
QModelIndex ItemToIndex(T* item) const; QModelIndex ItemToIndex(T* item) const;
// Called by items
void BeginInsert(T* parent, int start, int end = -1);
void EndInsert();
void BeginDelete(T* parent, int start, int end = -1);
void EndDelete();
protected: protected:
virtual void LazyPopulate(T* item) = 0; virtual void LazyPopulate(T* item) = 0;
@ -87,4 +93,26 @@ bool SimpleTreeModel<T>::hasChildren(const QModelIndex &parent) const {
return true; return true;
} }
template <typename T>
void SimpleTreeModel<T>::BeginInsert(T* parent, int start, int end) {
if (end == -1) end = start;
beginInsertRows(ItemToIndex(parent), start, end);
}
template <typename T>
void SimpleTreeModel<T>::EndInsert() {
endInsertRows();
}
template <typename T>
void SimpleTreeModel<T>::BeginDelete(T* parent, int start, int end) {
if (end == -1) end = start;
beginRemoveRows(ItemToIndex(parent), start, end);
}
template <typename T>
void SimpleTreeModel<T>::EndDelete() {
endRemoveRows();
}
#endif // SIMPLETREEMODEL_H #endif // SIMPLETREEMODEL_H