parent
73739af762
commit
9767843caf
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
12
src/osd.cpp
12
src/osd.cpp
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
|
|
10
src/song.h
10
src/song.h
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue