mirror of
https://github.com/clementine-player/Clementine
synced 2025-01-20 13:39:00 +01:00
Temporary metadata from Last.fm
This commit is contained in:
parent
7a3678e806
commit
9e285efea7
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 = "-";
|
||||
|
@ -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);
|
||||
|
||||
|
@ -10,6 +10,7 @@ class RadioMimeData : public QMimeData {
|
||||
|
||||
public:
|
||||
QList<RadioService*> services;
|
||||
QList<QString> titles;
|
||||
};
|
||||
|
||||
#endif // RADIOMIMEDATA_H
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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_;
|
||||
|
12
src/song.cpp
12
src/song.cpp
@ -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)
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user