From abe8d10b9c777ef6bea3e6797ea88798b723d4a9 Mon Sep 17 00:00:00 2001 From: David Sansome Date: Thu, 28 Jun 2012 17:41:51 +0100 Subject: [PATCH] Add a compatibility header so liblastfm and liblastfm1 can both be used with the same source. --- CMakeLists.txt | 5 ++ src/CMakeLists.txt | 3 +- src/config.h.in | 1 + src/core/song.cpp | 6 +- src/covers/lastfmcoverprovider.cpp | 9 +- src/internet/lastfmcompat.cpp | 110 +++++++++++++++++++++++ src/internet/lastfmcompat.h | 65 ++++++++++++++ src/internet/lastfmservice.cpp | 97 ++++++++++---------- src/internet/lastfmservice.h | 6 +- src/songinfo/lastfmtrackinfoprovider.cpp | 10 +-- src/suggesters/lastfmsuggester.cpp | 61 ------------- src/suggesters/lastfmsuggester.h | 30 ------- tests/mpris1_test.cpp | 3 +- tests/song_test.cpp | 3 +- 14 files changed, 248 insertions(+), 161 deletions(-) create mode 100644 src/internet/lastfmcompat.cpp create mode 100644 src/internet/lastfmcompat.h delete mode 100644 src/suggesters/lastfmsuggester.cpp delete mode 100644 src/suggesters/lastfmsuggester.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 52cdf264d..4afee9d90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,11 @@ endif (WIN32) find_library(LASTFM_LIBRARIES lastfm) find_path(LASTFM_INCLUDE_DIRS lastfm/ws.h) +find_path(LASTFM1_INCLUDE_DIRS lastfm/Track.h) + +if(LASTFM_INCLUDE_DIRS AND LASTFM1_INCLUDE_DIRS) + set(HAVE_LIBLASTFM1 ON) +endif() if (APPLE) find_library(GROWL Growl) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0f10922da..f08a41134 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -744,6 +744,7 @@ optional_source(HAVE_LIBLASTFM covers/lastfmcoverprovider.cpp globalsearch/lastfmsearchprovider.cpp internet/fixlastfm.cpp + internet/lastfmcompat.cpp internet/lastfmservice.cpp internet/lastfmsettingspage.cpp internet/lastfmstationdialog.cpp @@ -752,7 +753,6 @@ optional_source(HAVE_LIBLASTFM songinfo/echonesttags.cpp songinfo/lastfmtrackinfoprovider.cpp songinfo/tagwidget.cpp - suggesters/lastfmsuggester.cpp HEADERS covers/lastfmcoverprovider.h internet/lastfmservice.h @@ -762,7 +762,6 @@ optional_source(HAVE_LIBLASTFM songinfo/echonesttags.h songinfo/lastfmtrackinfoprovider.h songinfo/tagwidget.h - suggesters/lastfmsuggester.h UI internet/lastfmsettingspage.ui internet/lastfmstationdialog.ui diff --git a/src/config.h.in b/src/config.h.in index a3f0329f6..9a6750420 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -31,6 +31,7 @@ #cmakedefine HAVE_LIBGPOD #cmakedefine HAVE_LIBINDICATE #cmakedefine HAVE_LIBLASTFM +#cmakedefine HAVE_LIBLASTFM1 #cmakedefine HAVE_LIBMTP #cmakedefine HAVE_MOODBAR #cmakedefine HAVE_QCA diff --git a/src/core/song.cpp b/src/core/song.cpp index 28fd26a65..1387c2232 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -26,7 +26,11 @@ #ifdef HAVE_LIBLASTFM #include "internet/fixlastfm.h" - #include + #ifdef HAVE_LIBLASTFM1 + #include + #else + #include + #endif #endif #include diff --git a/src/covers/lastfmcoverprovider.cpp b/src/covers/lastfmcoverprovider.cpp index 6a2d7341c..0207b795b 100644 --- a/src/covers/lastfmcoverprovider.cpp +++ b/src/covers/lastfmcoverprovider.cpp @@ -18,10 +18,7 @@ #include "albumcoverfetcher.h" #include "coverprovider.h" #include "lastfmcoverprovider.h" - -#include -#include -#include +#include "internet/lastfmcompat.h" #include @@ -52,8 +49,8 @@ void LastFmCoverProvider::QueryFinished() { CoverSearchResults results; - lastfm::XmlQuery query; - if (query.parse(reply->readAll())) { + lastfm::XmlQuery query(lastfm::compat::EmptyXmlQuery()); + if (lastfm::compat::ParseQuery(reply->readAll(), &query)) { // parse the list of search results QList elements = query["results"]["albummatches"].children("album"); diff --git a/src/internet/lastfmcompat.cpp b/src/internet/lastfmcompat.cpp new file mode 100644 index 000000000..4b843bb43 --- /dev/null +++ b/src/internet/lastfmcompat.cpp @@ -0,0 +1,110 @@ +/* This file is part of Clementine. + Copyright 2012, David Sansome + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#include "lastfmcompat.h" +#include "core/logging.h" + +namespace lastfm { +namespace compat { + +#ifdef HAVE_LIBLASTFM1 + +XmlQuery EmptyXmlQuery() { + return XmlQuery(); +} + +bool ParseQuery(const QByteArray& data, XmlQuery* query, bool* connection_problems) { + const bool ret = query->parse(data); + + if (connection_problems) { + *connection_problems = + !ret && query->parseError().enumValue() == lastfm::ws::MalformedResponse; + } + + return ret; +} + +bool ParseUserList(QNetworkReply* reply, QList* users) { + lastfm::XmlQuery lfm; + if (!lfm.parse(reply->readAll())) { + return false; + } + + *users = lastfm::UserList(lfm).users(); + return true; +} + +uint ScrobbleTimeMin() { + return lastfm::ScrobblePoint::scrobbleTimeMin(); +} + +#else // HAVE_LIBLASTFM1 + +XmlQuery EmptyXmlQuery() { + QByteArray dummy; + return XmlQuery(dummy); +} + +bool ParseQuery(const QByteArray& data, XmlQuery* query, bool* connection_problems) { + try { + *query = lastfm::XmlQuery(data); + #ifdef Q_OS_WIN32 + if (lastfm::ws::last_parse_error != lastfm::ws::NoError) { + return false; + } + #endif // Q_OS_WIN32 + } catch (lastfm::ws::ParseError e) { + qLog(Error) << "Last.fm parse error: " << e.enumValue(); + if (connection_problems) { + *connection_problems = e.enumValue() == lastfm::ws::MalformedResponse; + } + return false; + } catch(std::runtime_error& e) { + qLog(Error) << e.what(); + return false; + } + + if (connection_problems) { + *connection_problems = false; + } + + return true; +} + +bool ParseUserList(QNetworkReply* reply, QList* users) { + try { + *users = lastfm::User::list(reply); + #ifdef Q_OS_WIN32 + if (lastfm::ws::last_parse_error != lastfm::ws::NoError) { + return false; + } + #endif // Q_OS_WIN32 + } catch(std::runtime_error& e) { + qLog(Error) << e.what(); + return false; + } + return true; +} + +uint ScrobbleTimeMin() { + return ScrobblePoint::kScrobbleMinLength; +} + +#endif // HAVE_LIBLASTFM1 + +} +} diff --git a/src/internet/lastfmcompat.h b/src/internet/lastfmcompat.h new file mode 100644 index 000000000..ca24d2e8d --- /dev/null +++ b/src/internet/lastfmcompat.h @@ -0,0 +1,65 @@ +/* This file is part of Clementine. + Copyright 2012, David Sansome + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#ifndef LASTFMCOMPAT_H +#define LASTFMCOMPAT_H + +#include "config.h" +#include "fixlastfm.h" + +#ifdef HAVE_LIBLASTFM1 + #include + #include + #include + #include + #include + #include + #include +#else + #include + #include + #include + #include + #include + #include + #include +#endif + +namespace lastfm { +namespace compat { + +lastfm::XmlQuery EmptyXmlQuery(); + +bool ParseQuery(const QByteArray& data, lastfm::XmlQuery* query, + bool* connection_problems = NULL); + +bool ParseUserList(QNetworkReply* reply, QList* users); + +uint ScrobbleTimeMin(); + +#ifdef HAVE_LIBLASTFM1 + typedef lastfm::ScrobbleCache ScrobbleCache; + typedef lastfm::User AuthenticatedUser; +#else + typedef ::ScrobbleCache ScrobbleCache; + typedef lastfm::AuthenticatedUser AuthenticatedUser; +#endif + +} +} + +#endif // LASTFMCOMPAT_H diff --git a/src/internet/lastfmservice.cpp b/src/internet/lastfmservice.cpp index 962c2b3c0..e1447aa30 100644 --- a/src/internet/lastfmservice.cpp +++ b/src/internet/lastfmservice.cpp @@ -31,6 +31,8 @@ #endif // QT_VERSION >= 0x040600 #include "lastfmservice.h" + +#include "lastfmcompat.h" #include "lastfmstationdialog.h" #include "lastfmurlhandler.h" #include "internetmodel.h" @@ -49,17 +51,15 @@ #include -#include -#include -#include -#include -#include -#include -#include - #include #include +#ifdef HAVE_LIBLASTFM1 + #include +#else + #include +#endif + using boost::scoped_ptr; using lastfm::XmlQuery; @@ -316,8 +316,8 @@ void LastFMService::AuthenticateReplyFinished() { reply->deleteLater(); // Parse the reply - lastfm::XmlQuery lfm; - if (lfm.parse(reply->readAll())) { + lastfm::XmlQuery lfm(lastfm::compat::EmptyXmlQuery()); + if (lastfm::compat::ParseQuery(reply->readAll(), &lfm)) { lastfm::ws::Username = lfm["session"]["name"].text(); lastfm::ws::SessionKey = lfm["session"]["key"].text(); QString subscribed = lfm["session"]["subscriber"].text(); @@ -330,7 +330,6 @@ void LastFMService::AuthenticateReplyFinished() { settings.setValue("Session", lastfm::ws::SessionKey); settings.setValue("Subscriber", is_subscriber); } else { - qLog(Error) << lfm.parseError().message(); emit AuthenticationComplete(false); return; } @@ -358,9 +357,8 @@ void LastFMService::UpdateSubscriberStatusFinished() { bool is_subscriber = false; - lastfm::XmlQuery lfm; - if (lfm.parse(reply->readAll())) { - connection_problems_ = false; + lastfm::XmlQuery lfm(lastfm::compat::EmptyXmlQuery()); + if (lastfm::compat::ParseQuery(reply->readAll(), &lfm, &connection_problems_)) { QString subscriber = lfm["user"]["subscriber"].text(); is_subscriber = (subscriber.toInt() == 1); @@ -368,9 +366,6 @@ void LastFMService::UpdateSubscriberStatusFinished() { settings.beginGroup(kSettingsGroup); settings.setValue("Subscriber", is_subscriber); qLog(Info) << lastfm::ws::Username << "Subscriber status:" << is_subscriber; - } else { - qLog(Error) << "Last.fm parse error: " << lfm.parseError().message(); - connection_problems_ = lfm.parseError().enumValue() == lastfm::ws::MalformedResponse; } emit UpdatedSubscriberStatus(is_subscriber); @@ -473,11 +468,28 @@ bool LastFMService::InitScrobbler() { scrobbler_ = new lastfm::Audioscrobbler(kAudioscrobblerClientId); //reemit the signal since the sender is private +#ifdef HAVE_LIBLASTFM1 connect(scrobbler_, SIGNAL(scrobblesSubmitted(QList)), SIGNAL(ScrobbleSubmitted())); connect(scrobbler_, SIGNAL(nowPlayingError(int,QString)), SIGNAL(ScrobbleError(int))); +#else + connect(scrobbler_, SIGNAL(status(int)), SIGNAL(ScrobblerStatus(int))); +#endif return true; } +void LastFMService::ScrobblerStatus(int value) { + switch (value) { + case 2: + case 3: + emit ScrobbleSubmitted(); + break; + + default: + emit ScrobbleError(value); + break; + } +} + lastfm::Track LastFMService::TrackFromSong(const Song &song) const { if (song.title() == last_track_.title() && song.artist() == last_track_.artist() && @@ -502,7 +514,7 @@ void LastFMService::NowPlaying(const Song &song) { if (!last_track_.isNull() && last_track_.source() == lastfm::Track::NonPersonalisedBroadcast) { const int duration_secs = last_track_.timestamp().secsTo(QDateTime::currentDateTime()); - if (duration_secs >= lastfm::ScrobblePoint::scrobbleTimeMin()) { + if (duration_secs >= lastfm::compat::ScrobbleTimeMin()) { lastfm::MutableTrack mtrack(last_track_); mtrack.setDuration(duration_secs); @@ -519,16 +531,18 @@ void LastFMService::NowPlaying(const Song &song) { already_scrobbled_ = false; last_track_ = mtrack; -// TODO: validity was removed from liblastfm1 but might reappear, it should have -// no impact as we get a different error when actually trying to scrobble. -// //check immediately if the song is valid -// Scrobble::Invalidity invalidity; -// -// if (!lastfm::Scrobble(last_track_).isValid( &invalidity )) { -// //for now just notify this, we can also see the cause -// emit ScrobbleError(-1); -// return; -// } +#ifndef HAVE_LIBLASTFM1 + // Check immediately if the song is valid + Scrobble::Invalidity invalidity; + if (!lastfm::Scrobble(last_track_).isValid( &invalidity )) { + //for now just notify this, we can also see the cause + emit ScrobbleError(-1); + return; + } +#else + // TODO: validity was removed from liblastfm1 but might reappear, it should have + // no impact as we get a different error when actually trying to scrobble. +#endif scrobbler_->nowPlaying(mtrack); } @@ -537,7 +551,7 @@ void LastFMService::Scrobble() { if (!InitScrobbler()) return; - lastfm::ScrobbleCache cache(lastfm::ws::Username); + lastfm::compat::ScrobbleCache cache(lastfm::ws::Username); qLog(Debug) << "There are" << cache.tracks().count() << "tracks in the last.fm cache."; scrobbler_->cache(last_track_); @@ -646,7 +660,7 @@ void LastFMService::RefreshFriends(bool force) { return; } - lastfm::User user; + lastfm::compat::AuthenticatedUser user; QNetworkReply* reply = user.getFriends(); connect(reply, SIGNAL(finished()), SLOT(RefreshFriendsFinished())); } @@ -655,7 +669,7 @@ void LastFMService::RefreshNeighbours() { if (!neighbours_list_ || !IsAuthenticated()) return; - lastfm::User user; + lastfm::compat::AuthenticatedUser user; QNetworkReply* reply = user.getNeighbours(); connect(reply, SIGNAL(finished()), SLOT(RefreshNeighboursFinished())); } @@ -666,12 +680,7 @@ void LastFMService::RefreshFriendsFinished() { return; QList friends; - - lastfm::XmlQuery lfm; - if (lfm.parse(reply->readAll())) { - friends = lastfm::UserList(lfm).users(); - } else { - qLog(Error) << lfm.parseError().message(); + if (!lastfm::compat::ParseUserList(reply, &friends)) { return; } @@ -710,12 +719,7 @@ void LastFMService::RefreshNeighboursFinished() { return; QList neighbours; - - lastfm::XmlQuery lfm; - if (lfm.parse(reply->readAll())) { - neighbours = lastfm::UserList(lfm).users(); - } else { - qLog(Error) << lfm.parseError().message(); + if (!lastfm::compat::ParseUserList(reply, &neighbours)) { return; } @@ -866,8 +870,8 @@ void LastFMService::FetchMoreTracksFinished() { app_->task_manager()->SetTaskFinished(tune_task_id_); tune_task_id_ = 0; - XmlQuery query; - if (query.parse(reply->readAll())) { + lastfm::XmlQuery query(lastfm::compat::EmptyXmlQuery()); + if (lastfm::compat::ParseQuery(reply->readAll(), &query)) { const XmlQuery& playlist = query["playlist"]; foreach (const XmlQuery& q, playlist["trackList"].children("track")) { lastfm::MutableTrack t; @@ -883,8 +887,7 @@ void LastFMService::FetchMoreTracksFinished() { playlist_ << t; } } else { - emit StreamError(tr("Couldn't load the last.fm radio station") - .arg(query.parseError().message())); + emit StreamError(tr("Couldn't load the last.fm radio station")); return; } diff --git a/src/internet/lastfmservice.h b/src/internet/lastfmservice.h index 112b8ec89..f9a0b8c20 100644 --- a/src/internet/lastfmservice.h +++ b/src/internet/lastfmservice.h @@ -26,10 +26,7 @@ class Track; #include uint qHash(const lastfm::Track& track); -#include "fixlastfm.h" -#include -#include -#include +#include "lastfmcompat.h" #include "internetmodel.h" #include "internetservice.h" @@ -150,6 +147,7 @@ class LastFMService : public InternetService { void TunerTrackAvailable(); void TunerError(lastfm::ws::Error error); + void ScrobblerStatus(int value); void AddArtistRadio(); void AddTagRadio(); diff --git a/src/songinfo/lastfmtrackinfoprovider.cpp b/src/songinfo/lastfmtrackinfoprovider.cpp index 031139553..e60ab0ea3 100644 --- a/src/songinfo/lastfmtrackinfoprovider.cpp +++ b/src/songinfo/lastfmtrackinfoprovider.cpp @@ -19,11 +19,9 @@ #include "songinfotextview.h" #include "songplaystats.h" #include "tagwidget.h" +#include "internet/lastfmcompat.h" #include "ui/iconloader.h" -#include -#include - void LastfmTrackInfoProvider::FetchInfo(int id, const Song& metadata) { QMap params; params["method"] = "track.getInfo"; @@ -50,13 +48,13 @@ void LastfmTrackInfoProvider::RequestFinished() { return; } - lastfm::XmlQuery query; - if (query.parse(reply->readAll())) { + lastfm::XmlQuery query(lastfm::compat::EmptyXmlQuery()); + if (lastfm::compat::ParseQuery(reply->readAll(), &query)) { GetPlayCounts(id, query); GetWiki(id, query); GetTags(id, query); - } + emit Finished(id); } diff --git a/src/suggesters/lastfmsuggester.cpp b/src/suggesters/lastfmsuggester.cpp deleted file mode 100644 index a5557cc69..000000000 --- a/src/suggesters/lastfmsuggester.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "lastfmsuggester.h" - -#include - -#include "core/logging.h" -#include "core/timeconstants.h" - -using lastfm::XmlQuery; - -LastFMSuggester::LastFMSuggester(QObject* parent) - : QObject(parent), - next_id_(0) { -} - -int LastFMSuggester::SuggestSongs(const Song& song) { - QMap params; - params["method"] = "track.getsimilar"; - params["track"] = song.title(); - params["artist"] = song.artist(); - params["limit"] = "25"; - QNetworkReply* reply = lastfm::ws::get(params); - connect(reply, SIGNAL(finished()), SLOT(RequestFinished())); - - int id = next_id_++; - replies_[reply] = id; - - return id; -} - -void LastFMSuggester::RequestFinished() { - QNetworkReply* reply = qobject_cast(sender()); - Q_ASSERT(reply); - reply->deleteLater(); - - QMap::iterator it = replies_.find(reply); - if (it == replies_.end()) { - return; - } - int id = it.value(); - replies_.erase(it); - - lastfm::XmlQuery lfm; - if (lfm.parse(reply->readAll())) { - const QList tracks = lfm["similartracks"].children("track"); - SongList songs; - foreach (const XmlQuery& q, tracks) { - Song song; - song.Init( - q["name"].text(), - q["artist"]["name"].text(), - QString::null, - QString(q["duration"].text()).toInt() * kNsecPerMsec); - songs << song; - } - qLog(Debug) << songs.length() << "suggested songs from Last.fm"; - emit SuggestSongsFinished(id, songs); - } else { - qLog(Error) << lfm.parseError().message(); - emit SuggestSongsFinished(id, SongList()); - } -} diff --git a/src/suggesters/lastfmsuggester.h b/src/suggesters/lastfmsuggester.h deleted file mode 100644 index 992c7694e..000000000 --- a/src/suggesters/lastfmsuggester.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef LASTFMSUGGESTER_H -#define LASTFMSUGGESTER_H - -#include -#include - -#include - -#include "core/song.h" - -class LastFMSuggester : public QObject { - Q_OBJECT - public: - LastFMSuggester(QObject* parent = 0); - - public slots: - int SuggestSongs(const Song& song); - - signals: - void SuggestSongsFinished(int id, const SongList& songs); - - private slots: - void RequestFinished(); - - private: - QMap replies_; - int next_id_; -}; - -#endif // LASTFMSUGGESTER_H diff --git a/tests/mpris1_test.cpp b/tests/mpris1_test.cpp index f5af0b7a7..61a153368 100644 --- a/tests/mpris1_test.cpp +++ b/tests/mpris1_test.cpp @@ -21,8 +21,7 @@ #include "playlist/playlistmanager.h" #include "playlist/playlistsequence.h" #ifdef HAVE_LIBLASTFM -#include "internet/fixlastfm.h" -#include + #include "internet/lastfmcompat.h" #endif #include "gmock/gmock.h" diff --git a/tests/song_test.cpp b/tests/song_test.cpp index 265564aad..0e5d5404f 100644 --- a/tests/song_test.cpp +++ b/tests/song_test.cpp @@ -19,8 +19,7 @@ #include "core/encoding.h" #include "core/song.h" #ifdef HAVE_LIBLASTFM -#include "internet/fixlastfm.h" -#include + #include "internet/lastfmcompat.h" #endif #include "gmock/gmock.h"