Shows album art for last.fm radio.

Update issue 17
This commit is contained in:
John Maguire 2010-02-25 00:18:32 +00:00
parent 73739af762
commit 9767843caf
5 changed files with 147 additions and 21 deletions

View File

@ -4,14 +4,23 @@
#include "lastfmstationdialog.h"
#include "lastfmconfigdialog.h"
#include <lastfm/ws.h>
#include <lastfm/misc.h>
#include <lastfm/XmlQuery>
#include <lastfm/Audioscrobbler>
#include <lastfm/misc.h>
#include <lastfm/RadioStation>
#include <lastfm/ws.h>
#include <lastfm/XmlQuery>
#include <QSettings>
#include <QMenu>
using lastfm::XmlQuery;
uint qHash(const lastfm::Track& track) {
return qHash(track.title()) ^
qHash(track.artist().name()) ^
qHash(track.album().title());
}
const char* LastFMService::kServiceName = "Last.fm";
const char* LastFMService::kSettingsGroup = "Last.fm";
const char* LastFMService::kAudioscrobblerClientId = "tng";
@ -20,7 +29,6 @@ const char* LastFMService::kSecret = "d3072b60ae626be12be69448f5c46e70";
LastFMService::LastFMService(QObject* parent)
: RadioService(kServiceName, parent),
tuner_(NULL),
scrobbler_(NULL),
config_(NULL),
station_dialog_(new LastFMStationDialog),
@ -254,26 +262,27 @@ void LastFMService::StartLoading(const QUrl& url) {
emit TaskStarted(MultiLoadingIndicator::LoadingLastFM);
delete tuner_;
last_url_ = url;
initial_tune_ = true;
tuner_ = new lastfm::RadioTuner(lastfm::RadioStation(url));
connect(tuner_, SIGNAL(trackAvailable()), SLOT(TunerTrackAvailable()));
connect(tuner_, SIGNAL(error(lastfm::ws::Error)), SLOT(TunerError(lastfm::ws::Error)));
Tune(lastfm::RadioStation(url));
}
void LastFMService::LoadNext(const QUrl &) {
last_track_ = tuner_->takeNextTrack();
if (last_track_.isNull()) {
if (playlist_.empty()) {
emit StreamFinished();
return;
}
last_track_ = playlist_.dequeue();
if (playlist_.empty()) {
FetchMoreTracks();
}
Song metadata;
metadata.InitFromLastFM(last_track_);
if (cached_images_.contains(last_track_)) {
metadata.set_image(cached_images_.take(last_track_));
}
emit StreamMetadataFound(last_url_, metadata);
emit StreamReady(last_url_, last_track_.url());
@ -547,3 +556,78 @@ void LastFMService::Remove() {
else if (type == Type_Tag)
SaveList("tags", tag_list_);
}
void LastFMService::FetchMoreTracks() {
QMap<QString, QString> params;
params["method"] = "radio.getPlaylist";
params["rtp"] = "1";
QNetworkReply* reply = lastfm::ws::post(params);
connect(reply, SIGNAL(finished()), SLOT(FetchMoreTracksFinished()));
}
void LastFMService::FetchMoreTracksFinished() {
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
if (!reply) {
qWarning() << "Invalid reply on radio.getPlaylist";
return;
}
const XmlQuery query = lastfm::ws::parse(reply);
const XmlQuery playlist = query["playlist"];
foreach (XmlQuery q, playlist["trackList"].children("track")) {
lastfm::MutableTrack t;
t.setUrl(q["location"].text());
t.setExtra("trackauth", q["extension"]["trackauth"].text());
t.setTitle(q["title"].text());
t.setArtist(q["creator"].text());
t.setAlbum(q["album"].text());
t.setDuration(q["duration"].text().toInt() / 1000);
const QString& image = q["image"].text();
if (!image.isEmpty()) {
FetchImage(t, q["image"].text());
}
playlist_ << t;
qDebug() << "Adding track to playlist: " << t.title();
}
TunerTrackAvailable();
}
void LastFMService::Tune(const lastfm::RadioStation& station) {
QMap<QString, QString> params;
params["method"] = "radio.tune";
params["station"] = station.url();
QNetworkReply* reply = lastfm::ws::post(params);
connect(reply, SIGNAL(finished()), SLOT(TuneFinished()));
}
void LastFMService::TuneFinished() {
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
if (!reply) {
qWarning() << "Invalid reply on radio.tune";
return;
}
FetchMoreTracks();
reply->deleteLater();
}
void LastFMService::FetchImage(lastfm::Track track, const QString& image_url) {
QUrl url(image_url);
QNetworkReply* reply = network_.get(QNetworkRequest(url));
connect(reply, SIGNAL(finished()), SLOT(FetchImageFinished()));
image_requests_[reply] = track;
}
void LastFMService::FetchImageFinished() {
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
if (!reply) {
qWarning() << "Invalid reply on track image fetch.";
return;
}
lastfm::Track track = image_requests_.take(reply);
QImage image = QImage::fromData(reply->readAll());
cached_images_[track] = image;
reply->deleteLater();
}

View File

@ -1,11 +1,24 @@
#ifndef LASTFMSERVICE_H
#define LASTFMSERVICE_H
namespace lastfm {
class RadioStation;
class Track;
}
#include <QtGlobal>
uint qHash(const lastfm::Track& track);
#include <lastfm/Track>
#include <lastfm/ws.h>
#include "radioservice.h"
#include "song.h"
#include "lastfmstationdialog.h"
#include <lastfm/RadioTuner>
#include <QMap>
#include <QNetworkAccessManager>
#include <QQueue>
class QMenu;
class QAction;
@ -65,6 +78,8 @@ class LastFMService : public RadioService {
void Authenticate(const QString& username, const QString& password);
void FetchMoreTracks();
public slots:
void NowPlaying(const Song& song);
void Scrobble();
@ -89,6 +104,12 @@ class LastFMService : public RadioService {
void AddTagRadio();
void Remove();
// Radio tuner.
void FetchMoreTracksFinished();
void TuneFinished();
void FetchImageFinished();
private:
RadioItem* CreateStationItem(ItemType type, const QString& name,
const QString& icon, RadioItem* parent);
@ -104,11 +125,15 @@ class LastFMService : public RadioService {
void RestoreList(const QString &name, ItemType item_type,
const QIcon& icon, RadioItem *list);
void Tune(const lastfm::RadioStation& station);
void FetchImage(lastfm::Track track, const QString& image_url);
private:
lastfm::RadioTuner* tuner_;
lastfm::Audioscrobbler* scrobbler_;
lastfm::Track last_track_;
QQueue<lastfm::Track> playlist_;
LastFMConfigDialog* config_;
LastFMStationDialog* station_dialog_;
@ -128,6 +153,10 @@ class LastFMService : public RadioService {
RadioItem* tag_list_;
RadioItem* friends_list_;
RadioItem* neighbours_list_;
QNetworkAccessManager network_;
QMap<QNetworkReply*, lastfm::Track> image_requests_;
QHash<lastfm::Track, QImage> cached_images_;
};
#endif // LASTFMSERVICE_H

View File

@ -27,6 +27,7 @@ void OSD::ReloadSettings() {
}
void OSD::SongChanged(const Song &song) {
qDebug() << __PRETTY_FUNCTION__;
QString summary(song.PrettyTitle());
if (!song.artist().isNull())
summary = QString("%1 - %2").arg(song.artist(), summary);
@ -39,7 +40,7 @@ void OSD::SongChanged(const Song &song) {
if (song.track() > 0)
message_parts << QString("track %1").arg(song.track());
ShowMessage(summary, message_parts.join(", "), "notification-audio-play");
ShowMessage(summary, message_parts.join(", "), "notification-audio-play", song.image());
}
void OSD::Paused() {
@ -56,10 +57,15 @@ void OSD::VolumeChanged(int value) {
void OSD::ShowMessage(const QString& summary,
const QString& message,
const QString& icon) {
const QString& icon,
const QImage& image) {
switch (behaviour_) {
case Native:
ShowMessageNative(summary, message, icon);
if (image.isNull()) {
ShowMessageNative(summary, message, icon);
} else {
ShowMessageNative(summary, message, image);
}
break;
case TrayPopup:

View File

@ -40,7 +40,8 @@ class OSD : public QObject {
private:
void ShowMessage(const QString& summary,
const QString& message = QString(),
const QString& icon = QString());
const QString& icon = QString(),
const QImage& image = QImage());
// These are implemented in the OS-specific files
void Init();

View File

@ -1,11 +1,12 @@
#ifndef SONG_H
#define SONG_H
#include <QString>
#include <QImage>
#include <QList>
#include <QSqlQuery>
#include <QSharedData>
#include <QSharedDataPointer>
#include <QSqlQuery>
#include <QString>
#include "engine_fwd.h"
@ -41,6 +42,8 @@ struct SongData : public QSharedData {
int mtime_;
int ctime_;
int filesize_;
QImage image_;
};
class Song {
@ -89,6 +92,8 @@ class Song {
uint ctime() const { return d->ctime_; }
int filesize() const { return d->filesize_; }
const QImage& image() const { return d->image_; }
// Pretty accessors
QString PrettyTitle() const;
QString PrettyTitleWithArtist() const;
@ -118,6 +123,7 @@ class Song {
void set_mtime(int v) { d->mtime_ = v; }
void set_ctime(int v) { d->ctime_ = v; }
void set_filesize(int v) { d->filesize_ = v; }
void set_image(const QImage& i) { d->image_ = i; }
// Comparison functions
bool IsMetadataEqual(const Song& other) const;