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

View File

@ -41,5 +41,11 @@
<file>spinner.gif</file>
<file>last.fm/ban.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>
</RCC>

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 729 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 B

View File

@ -23,7 +23,9 @@ LastFMService::LastFMService(QObject* parent)
scrobbler_(NULL),
context_menu_(new QMenu),
initial_tune_(false),
scrobbling_enabled_(false)
scrobbling_enabled_(false),
friends_list_(NULL),
neighbours_list_(NULL)
{
lastfm::ws::ApiKey = kApiKey;
lastfm::ws::SharedSecret = kSecret;
@ -70,9 +72,11 @@ RadioItem* LastFMService::CreateRootItem(RadioItem* parent) {
}
void LastFMService::LazyPopulate(RadioItem *item) {
RadioItem* c = NULL;
switch (item->type) {
case RadioItem::Type_Service:
// Create child items
// Normal radio types
CreateStationItem(Type_MyRecommendations, "My Recommendations",
":last.fm/recommended_radio.png", item);
CreateStationItem(Type_MyRadio, "My Radio Station",
@ -82,10 +86,34 @@ void LastFMService::LazyPopulate(RadioItem *item) {
CreateStationItem(Type_MyNeighbourhood, "My Neighbourhood",
":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())
config_->show();
break;
case Type_MyFriends:
RefreshFriends();
break;
case Type_MyNeighbours:
RefreshNeighbours();
break;
default:
break;
}
@ -158,18 +186,26 @@ QUrl LastFMService::UrlForItem(const RadioItem* item) const {
case Type_MyRadio:
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();
}
QString LastFMService::TitleForItem(const RadioItem* item) const {
const QString user(lastfm::ws::Username);
const QString me(lastfm::ws::Username);
switch (item->type) {
case Type_MyRecommendations: return user + "'s Recommended Radio";
case Type_MyLoved: return user + "'s Loved Tracks";
case Type_MyNeighbourhood: return user + "'s Neighbour Radio";
case Type_MyRadio: return user + "'s Library";
case Type_MyRecommendations: return me + "'s Recommended Radio";
case Type_MyLoved: return me + "'s Loved Tracks";
case Type_MyNeighbourhood: return me + "'s Neighbour Radio";
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();
}
@ -321,3 +357,71 @@ void LastFMService::Ban() {
void LastFMService::ShowContextMenu(RadioItem *, const QPoint &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_);
}
}

View File

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

View File

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

View File

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

View File

@ -1,6 +1,12 @@
#include "radioitem.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* parent)
: SimpleTreeItem<RadioItem>(type, key, parent),

View File

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

View File

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

View File

@ -1,15 +1,22 @@
#ifndef SIMPLETREEITEM_H
#define SIMPLETREEITEM_H
#include "simpletreemodel.h"
#include <QString>
#include <QList>
template <typename T>
class SimpleTreeItem {
public:
SimpleTreeItem(int _type, SimpleTreeModel<T>* _model); // For the root item
SimpleTreeItem(int _type, const QString& _key = QString::null, T* _parent = NULL);
virtual ~SimpleTreeItem();
void InsertNotify(T* _parent);
void DeleteNotify(int child_row);
void ClearNotify();
void Delete(int child_row);
T* ChildByKey(const QString& key) const;
@ -26,23 +33,67 @@ class SimpleTreeItem {
T* parent;
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>
SimpleTreeItem<T>::SimpleTreeItem(int _type, const QString& _key, T* _parent)
: type(_type),
key(_key),
lazy_loaded(false),
parent(_parent)
parent(_parent),
model(_parent ? _parent->model : NULL)
{
if (parent) {
row = parent->children.count();
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>
SimpleTreeItem<T>::~SimpleTreeItem() {
qDeleteAll(children);

View File

@ -21,6 +21,12 @@ class SimpleTreeModel : public QAbstractItemModel {
T* IndexToItem(const QModelIndex& index) 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:
virtual void LazyPopulate(T* item) = 0;
@ -87,4 +93,26 @@ bool SimpleTreeModel<T>::hasChildren(const QModelIndex &parent) const {
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