diff --git a/src/lastfmservice.cpp b/src/lastfmservice.cpp index 361d7263e..2d0a85b42 100644 --- a/src/lastfmservice.cpp +++ b/src/lastfmservice.cpp @@ -1,6 +1,7 @@ #include "lastfmservice.h" #include "lastfmconfig.h" #include "radioitem.h" +#include "song.h" #include #include @@ -107,24 +108,30 @@ void LastFMService::AuthenticateReplyFinished() { emit AuthenticationComplete(true); } -QList LastFMService::UrlsForItem(RadioItem* item) { - QList ret; +QList LastFMService::DataForItem(RadioItem* item) { + QList 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); } diff --git a/src/lastfmservice.h b/src/lastfmservice.h index 3d0d9709a..f96502324 100644 --- a/src/lastfmservice.h +++ b/src/lastfmservice.h @@ -26,7 +26,7 @@ class LastFMService : public RadioService { // RadioService RadioItem* CreateRootItem(RadioItem* parent); void LazyPopulate(RadioItem *item); - QList UrlsForItem(RadioItem* item); + QList DataForItem(RadioItem* item); void StartLoading(const QUrl& url); void Authenticate(const QString& username, const QString& password); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 43d331a81..647b6b082 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -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); diff --git a/src/playlist.cpp b/src/playlist.cpp index 962c38f4c..813a981fb 100644 --- a/src/playlist.cpp +++ b/src/playlist.cpp @@ -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(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& services, - const QList& urls, int after) { + const QList& urls, + const QStringList& titles, int after) { Q_ASSERT(services.count() == urls.count()); + Q_ASSERT(services.count() == titles.count()); QList items; for (int i=0 ; iUrl() != 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)); +} diff --git a/src/playlist.h b/src/playlist.h index 2af4d3dc1..10b47cd8b 100644 --- a/src/playlist.h +++ b/src/playlist.h @@ -52,7 +52,8 @@ class Playlist : public QAbstractListModel { QModelIndex InsertItems(const QList& items, int after = -1); QModelIndex InsertSongs(const SongList& items, int after = -1); QModelIndex InsertRadioStations(const QList& services, - const QList& urls, int after = -1); + const QList& urls, + const QStringList& titles, int after = -1); QModelIndex InsertPaths(QList 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); diff --git a/src/playlistitem.h b/src/playlistitem.h index 6b2554156..6a90f03f2 100644 --- a/src/playlistitem.h +++ b/src/playlistitem.h @@ -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 diff --git a/src/playlistview.cpp b/src/playlistview.cpp index 7fb8b8ae4..66af8e61c 100644 --- a/src/playlistview.cpp +++ b/src/playlistview.cpp @@ -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 = "-"; diff --git a/src/radioitem.h b/src/radioitem.h index 6b514731d..44b5d7b8d 100644 --- a/src/radioitem.h +++ b/src/radioitem.h @@ -2,6 +2,7 @@ #define RADIOITEM_H #include +#include #include "simpletreeitem.h" @@ -14,6 +15,13 @@ class RadioItem : public SimpleTreeItem { 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); diff --git a/src/radiomimedata.h b/src/radiomimedata.h index 9e3362e77..75f2ec5ff 100644 --- a/src/radiomimedata.h +++ b/src/radiomimedata.h @@ -10,6 +10,7 @@ class RadioMimeData : public QMimeData { public: QList services; + QList titles; }; #endif // RADIOMIMEDATA_H diff --git a/src/radiomodel.cpp b/src/radiomodel.cpp index b99316cff..e1f4ae09f 100644 --- a/src/radiomodel.cpp +++ b/src/radiomodel.cpp @@ -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 urls; QList services; + QStringList titles; foreach (const QModelIndex& index, indexes) { RadioItem* item = IndexToItem(index); if (!item || !item->service || !item->playable) continue; - QList service_urls(item->service->UrlsForItem(item)); - foreach (const QUrl& url, service_urls) { - urls << url; + QList 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; } diff --git a/src/radiomodel.h b/src/radiomodel.h index b059b5e61..b6746db37 100644 --- a/src/radiomodel.h +++ b/src/radiomodel.h @@ -5,6 +5,7 @@ #include "simpletreemodel.h" class RadioService; +class Song; class RadioModel : public SimpleTreeModel { Q_OBJECT @@ -33,6 +34,7 @@ class RadioModel : public SimpleTreeModel { 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); diff --git a/src/radioplaylistitem.cpp b/src/radioplaylistitem.cpp index f903477c0..d5bbb071c 100644 --- a/src/radioplaylistitem.cpp +++ b/src/radioplaylistitem.cpp @@ -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(); +} diff --git a/src/radioplaylistitem.h b/src/radioplaylistitem.h index 7e9fdd5ed..a47a9c1dd 100644 --- a/src/radioplaylistitem.h +++ b/src/radioplaylistitem.h @@ -2,6 +2,7 @@ #define RADIOPLAYLISTITEM_H #include "playlistitem.h" +#include "song.h" #include @@ -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 diff --git a/src/radioservice.h b/src/radioservice.h index 3a8a08ff7..700f9ff9b 100644 --- a/src/radioservice.h +++ b/src/radioservice.h @@ -5,7 +5,9 @@ #include #include -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 UrlsForItem(RadioItem* item) = 0; + virtual QList 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_; diff --git a/src/song.cpp b/src/song.cpp index d38be064a..86adf32a7 100644 --- a/src/song.cpp +++ b/src/song.cpp @@ -9,6 +9,8 @@ #include #include +#include + #include #include #include @@ -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) diff --git a/src/song.h b/src/song.h index f8dd0f635..743955fa8 100644 --- a/src/song.h +++ b/src/song.h @@ -5,6 +5,10 @@ #include #include +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;