Temporary metadata from Last.fm

This commit is contained in:
David Sansome 2009-12-26 22:15:57 +00:00
parent 7a3678e806
commit 9e285efea7
16 changed files with 137 additions and 28 deletions

View File

@ -1,6 +1,7 @@
#include "lastfmservice.h"
#include "lastfmconfig.h"
#include "radioitem.h"
#include "song.h"
#include <lastfm/ws.h>
#include <lastfm/misc.h>
@ -107,24 +108,30 @@ void LastFMService::AuthenticateReplyFinished() {
emit AuthenticationComplete(true);
}
QList<QUrl> LastFMService::UrlsForItem(RadioItem* item) {
QList<QUrl> ret;
QList<RadioItem::PlaylistData> LastFMService::DataForItem(RadioItem* item) {
QList<RadioItem::PlaylistData> ret;
const QString user(lastfm::ws::Username);
switch (item->type) {
case Type_MyRecommendations:
ret << QUrl("lastfm://user/" + lastfm::ws::Username + "/recommended");
ret << RadioItem::PlaylistData(user + "'s Recommended Radio",
"lastfm://user/" + lastfm::ws::Username + "/recommended");
break;
case Type_MyLoved:
ret << QUrl("lastfm://user/" + lastfm::ws::Username + "/loved");
ret << RadioItem::PlaylistData(user + "'s Loved Tracks",
"lastfm://user/" + lastfm::ws::Username + "/loved");
break;
case Type_MyNeighbourhood:
ret << QUrl("lastfm://user/" + lastfm::ws::Username + "/neighbours");
ret << RadioItem::PlaylistData(user + "'s Neighbour Radio",
"lastfm://user/" + lastfm::ws::Username + "/neighbours");
break;
case Type_MyRadio:
ret << QUrl("lastfm://user/" + lastfm::ws::Username + "/library");
ret << RadioItem::PlaylistData(user + "'s Library",
"lastfm://user/" + lastfm::ws::Username + "/library");
break;
}
@ -192,4 +199,8 @@ void LastFMService::TunerTrackAvailable() {
lastfm::Track track = tuner_->takeNextTrack();
emit StreamReady(last_url_, track.url());
Song metadata;
metadata.InitFromLastFM(track);
emit StreamMetadataFound(last_url_, metadata);
}

View File

@ -26,7 +26,7 @@ class LastFMService : public RadioService {
// RadioService
RadioItem* CreateRootItem(RadioItem* parent);
void LazyPopulate(RadioItem *item);
QList<QUrl> UrlsForItem(RadioItem* item);
QList<RadioItem::PlaylistData> DataForItem(RadioItem* item);
void StartLoading(const QUrl& url);
void Authenticate(const QString& username, const QString& password);

View File

@ -143,6 +143,7 @@ MainWindow::MainWindow(QWidget *parent)
connect(radio_model_, SIGNAL(StreamError(QString)), SLOT(ReportError(QString)));
connect(radio_model_, SIGNAL(StreamFinished()), player_, SLOT(Next()));
connect(radio_model_, SIGNAL(StreamReady(QUrl,QUrl)), player_, SLOT(StreamReady(QUrl,QUrl)));
connect(radio_model_, SIGNAL(StreamMetadataFound(QUrl,Song)), playlist_, SLOT(SetStreamMetadata(QUrl,Song)));
// Tray icon
QMenu* tray_menu = new QMenu(this);

View File

@ -93,6 +93,8 @@ int Playlist::previous_item() const {
void Playlist::set_current_item(int i) {
QModelIndex old_current = current_item_;
ClearStreamMetadata();
current_item_ = QPersistentModelIndex(index(i, 0, QModelIndex()));
if (old_current.isValid())
@ -124,7 +126,7 @@ bool Playlist::dropMimeData(const QMimeData* data, Qt::DropAction action, int ro
InsertSongs(song_data->songs, row);
} else if (const RadioMimeData* radio_data = qobject_cast<const RadioMimeData*>(data)) {
// Dragged from the Radio pane
InsertRadioStations(radio_data->services, radio_data->urls(), row);
InsertRadioStations(radio_data->services, radio_data->urls(), radio_data->titles, row);
} else if (data->hasFormat(kRowsMimetype)) {
// Dragged from the playlist
// Rearranging it is tricky...
@ -237,12 +239,14 @@ QModelIndex Playlist::InsertSongs(const SongList& songs, int after) {
}
QModelIndex Playlist::InsertRadioStations(const QList<RadioService*>& services,
const QList<QUrl>& urls, int after) {
const QList<QUrl>& urls,
const QStringList& titles, int after) {
Q_ASSERT(services.count() == urls.count());
Q_ASSERT(services.count() == titles.count());
QList<PlaylistItem*> items;
for (int i=0 ; i<services.count() ; ++i) {
items << new RadioPlaylistItem(services[i], urls[i]);
items << new RadioPlaylistItem(services[i], urls[i], titles[i]);
}
return InsertItems(items, after);
}
@ -403,3 +407,26 @@ void Playlist::StopAfter(int row) {
if (stop_after_.isValid())
emit dataChanged(stop_after_, stop_after_.sibling(stop_after_.row(), ColumnCount));
}
void Playlist::SetStreamMetadata(const QUrl& url, const Song& song) {
if (!current_item_.isValid())
return;
PlaylistItem* item = items_[current_item_.row()];
if (item->Url() != url)
return;
item->SetTemporaryMetadata(song);
emit dataChanged(index(current_item_.row(), 0), index(current_item_.row(), ColumnCount));
}
void Playlist::ClearStreamMetadata() {
if (!current_item_.isValid())
return;
PlaylistItem* item = items_[current_item_.row()];
item->ClearTemporaryMetadata();
emit dataChanged(index(current_item_.row(), 0), index(current_item_.row(), ColumnCount));
}

View File

@ -52,7 +52,8 @@ class Playlist : public QAbstractListModel {
QModelIndex InsertItems(const QList<PlaylistItem*>& items, int after = -1);
QModelIndex InsertSongs(const SongList& items, int after = -1);
QModelIndex InsertRadioStations(const QList<RadioService*>& services,
const QList<QUrl>& urls, int after = -1);
const QList<QUrl>& urls,
const QStringList& titles, int after = -1);
QModelIndex InsertPaths(QList<QUrl> urls, int after = -1);
void StopAfter(int row);
@ -76,6 +77,9 @@ class Playlist : public QAbstractListModel {
void Stopped();
void IgnoreSorting(bool value) { ignore_sorting_ = value; }
void ClearStreamMetadata();
void SetStreamMetadata(const QUrl& url, const Song& song);
private:
void SetCurrentIsPaused(bool paused);

View File

@ -6,6 +6,8 @@
class QSettings;
class Song;
class PlaylistItem {
public:
PlaylistItem() {}
@ -36,6 +38,9 @@ class PlaylistItem {
// directly to xine instead.
virtual bool StartLoading() { return false; }
virtual QUrl Url() = 0;
virtual void SetTemporaryMetadata(const Song& metadata) {Q_UNUSED(metadata)}
virtual void ClearTemporaryMetadata() {}
};
#endif // PLAYLISTITEM_H

View File

@ -73,7 +73,7 @@ LengthItemDelegate::LengthItemDelegate(QTreeView* view)
{
}
QString LengthItemDelegate::displayText(const QVariant& value, const QLocale& locale) const {
QString LengthItemDelegate::displayText(const QVariant& value, const QLocale&) const {
bool ok = false;
int seconds = value.toInt(&ok);
QString ret = "-";

View File

@ -2,6 +2,7 @@
#define RADIOITEM_H
#include <QIcon>
#include <QUrl>
#include "simpletreeitem.h"
@ -14,6 +15,13 @@ class RadioItem : public SimpleTreeItem<RadioItem> {
Type_Service,
};
struct PlaylistData {
PlaylistData(const QString& _title, const QUrl& _url) : title(_title), url(_url) {}
QString title;
QUrl url;
};
RadioItem(RadioService* _service, int type, const QString& key = QString::null,
RadioItem* parent = NULL);

View File

@ -10,6 +10,7 @@ class RadioMimeData : public QMimeData {
public:
QList<RadioService*> services;
QList<QString> titles;
};
#endif // RADIOMIMEDATA_H

View File

@ -27,6 +27,7 @@ void RadioModel::AddService(RadioService *service) {
connect(service, SIGNAL(StreamReady(QUrl,QUrl)), SIGNAL(StreamReady(QUrl,QUrl)));
connect(service, SIGNAL(StreamFinished()), SIGNAL(StreamFinished()));
connect(service, SIGNAL(StreamError(QString)), SIGNAL(StreamError(QString)));
connect(service, SIGNAL(StreamMetadataFound(QUrl,Song)), SIGNAL(StreamMetadataFound(QUrl,Song)));
}
RadioService* RadioModel::ServiceByName(const QString& name) {
@ -85,16 +86,18 @@ QStringList RadioModel::mimeTypes() const {
QMimeData* RadioModel::mimeData(const QModelIndexList& indexes) const {
QList<QUrl> urls;
QList<RadioService*> services;
QStringList titles;
foreach (const QModelIndex& index, indexes) {
RadioItem* item = IndexToItem(index);
if (!item || !item->service || !item->playable)
continue;
QList<QUrl> service_urls(item->service->UrlsForItem(item));
foreach (const QUrl& url, service_urls) {
urls << url;
QList<RadioItem::PlaylistData> item_data(item->service->DataForItem(item));
foreach (const RadioItem::PlaylistData& data, item_data) {
urls << data.url;
services << item->service;
titles << data.title;
}
}
@ -104,6 +107,7 @@ QMimeData* RadioModel::mimeData(const QModelIndexList& indexes) const {
RadioMimeData* data = new RadioMimeData;
data->setUrls(urls);
data->services = services;
data->titles = titles;
return data;
}

View File

@ -5,6 +5,7 @@
#include "simpletreemodel.h"
class RadioService;
class Song;
class RadioModel : public SimpleTreeModel<RadioItem> {
Q_OBJECT
@ -33,6 +34,7 @@ class RadioModel : public SimpleTreeModel<RadioItem> {
void StreamReady(const QUrl& original_url, const QUrl& media_url);
void StreamFinished();
void StreamError(const QString& message);
void StreamMetadataFound(const QUrl& original_url, const Song& song);
protected:
void LazyPopulate(RadioItem* parent);

View File

@ -9,51 +9,70 @@ RadioPlaylistItem::RadioPlaylistItem()
{
}
RadioPlaylistItem::RadioPlaylistItem(RadioService* service, const QUrl& url)
RadioPlaylistItem::RadioPlaylistItem(RadioService* service, const QUrl& url,
const QString& title)
: service_(service),
url_(url)
url_(url),
title_(title)
{
}
void RadioPlaylistItem::Save(QSettings& settings) const {
settings.setValue("service", service_->name());
settings.setValue("url", url_.toString());
settings.setValue("title", title_);
}
void RadioPlaylistItem::Restore(const QSettings& settings) {
service_ = RadioModel::ServiceByName(settings.value("service").toString());
url_ = settings.value("url").toString();
title_ = settings.value("title").toString();
}
QString RadioPlaylistItem::Title() const {
if (service_)
return url_.toString();
return "Radio service couldn't be loaded :-(";
if (!service_)
return "Radio service couldn't be loaded :-(";
if (metadata_.is_valid())
return metadata_.title();
if (!title_.isEmpty())
return title_;
return url_.toString();
}
QString RadioPlaylistItem::Artist() const {
return QString::null;
return metadata_.is_valid() ? metadata_.artist() : QString::null;
}
QString RadioPlaylistItem::Album() const {
return QString::null;
return metadata_.is_valid() ? metadata_.album() : QString::null;
}
int RadioPlaylistItem::Length() const {
return -1;
return metadata_.is_valid() ? metadata_.length() : -1;
}
int RadioPlaylistItem::Track() const {
return -1;
return metadata_.is_valid() ? metadata_.track() : -1;
}
bool RadioPlaylistItem::StartLoading() {
if (service_)
service_->StartLoading(url_);
return false;
return true;
}
QUrl RadioPlaylistItem::Url() {
return url_;
}
void RadioPlaylistItem::SetTemporaryMetadata(const Song& metadata) {
metadata_ = metadata;
}
void RadioPlaylistItem::ClearTemporaryMetadata() {
metadata_ = Song();
}

View File

@ -2,6 +2,7 @@
#define RADIOPLAYLISTITEM_H
#include "playlistitem.h"
#include "song.h"
#include <QUrl>
@ -10,7 +11,7 @@ class RadioService;
class RadioPlaylistItem : public PlaylistItem {
public:
RadioPlaylistItem();
RadioPlaylistItem(RadioService* service, const QUrl& url);
RadioPlaylistItem(RadioService* service, const QUrl& url, const QString& title);
Type type() const { return Type_Radio; }
@ -26,9 +27,15 @@ class RadioPlaylistItem : public PlaylistItem {
bool StartLoading();
QUrl Url();
void SetTemporaryMetadata(const Song& metadata);
void ClearTemporaryMetadata();
private:
RadioService* service_;
QUrl url_;
QString title_;
Song metadata_;
};
#endif // RADIOPLAYLISTITEM_H

View File

@ -5,7 +5,9 @@
#include <QList>
#include <QUrl>
class RadioItem;
#include "radioitem.h"
class Song;
class RadioService : public QObject {
Q_OBJECT
@ -19,7 +21,7 @@ class RadioService : public QObject {
virtual RadioItem* CreateRootItem(RadioItem* parent) = 0;
virtual void LazyPopulate(RadioItem* item) = 0;
virtual QList<QUrl> UrlsForItem(RadioItem* item) = 0;
virtual QList<RadioItem::PlaylistData> DataForItem(RadioItem* item) = 0;
virtual void StartLoading(const QUrl& url) = 0;
signals:
@ -28,6 +30,7 @@ class RadioService : public QObject {
void StreamReady(const QUrl& original_url, const QUrl& media_url);
void StreamFinished();
void StreamError(const QString& message);
void StreamMetadataFound(const QUrl& original_url, const Song& song);
private:
QString name_;

View File

@ -9,6 +9,8 @@
#include <taglib/vorbisfile.h>
#include <taglib/flacfile.h>
#include <lastfm/Track>
#include <QFile>
#include <QFileInfo>
#include <QTime>
@ -193,6 +195,16 @@ void Song::InitFromQuery(const QSqlQuery& q) {
#undef tofloat
}
void Song::InitFromLastFM(const lastfm::Track& track) {
valid_ = true;
title_ = track.title();
album_ = track.album();
artist_ = track.artist();
track_ = track.trackNumber();
length_ = track.duration();
}
void Song::BindToQuery(QSqlQuery *query) const {
#define intval(x) (x == -1 ? QVariant() : x)

View File

@ -5,6 +5,10 @@
#include <QList>
#include <QSqlQuery>
namespace lastfm {
class Track;
}
class Song {
public:
Song();
@ -16,6 +20,7 @@ class Song {
// Constructors
void InitFromFile(const QString& filename, int directory_id);
void InitFromQuery(const QSqlQuery& query);
void InitFromLastFM(const lastfm::Track& track);
// Save
void BindToQuery(QSqlQuery* query) const;