/thirdparty/liblastfm2/src/scrobble/Audioscrobbler.cpp

http://github.com/tomahawk-player/tomahawk · C++ · 246 lines · 177 code · 44 blank · 25 comment · 32 complexity · a2c7c3200f27ed3ae42fa6ca852d98c9 MD5 · raw file

  1. /*
  2. Copyright 2009 Last.fm Ltd.
  3. - Primarily authored by Max Howell, Jono Cole and Doug Mansell
  4. This file is part of liblastfm.
  5. liblastfm is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9. liblastfm is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with liblastfm. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <QPointer>
  17. #include "Audioscrobbler.h"
  18. #include "ScrobbleCache.h"
  19. #include "../types/User.h"
  20. #include "../types/Track.h"
  21. #include "../ws/ws.h"
  22. #include "../core/XmlQuery.h"
  23. namespace lastfm
  24. {
  25. class AudioscrobblerPrivate
  26. {
  27. public:
  28. AudioscrobblerPrivate(const QString& id)
  29. : m_id( id )
  30. , m_cache( ws::Username )
  31. {}
  32. ~AudioscrobblerPrivate()
  33. {
  34. }
  35. const QString m_id;
  36. ScrobbleCache m_cache;
  37. QList<Track> m_batch;
  38. QPointer<QNetworkReply> m_nowPlayingReply;
  39. QPointer<QNetworkReply> m_scrobbleReply;
  40. Track m_nowPlayingTrack;
  41. };
  42. }
  43. lastfm::Audioscrobbler::Audioscrobbler( const QString& id )
  44. : d( new AudioscrobblerPrivate(id) )
  45. {
  46. submit();
  47. }
  48. lastfm::Audioscrobbler::~Audioscrobbler()
  49. {
  50. delete d;
  51. }
  52. void
  53. lastfm::Audioscrobbler::nowPlaying( const Track& track )
  54. {
  55. if ( d->m_nowPlayingReply.isNull())
  56. {
  57. d->m_nowPlayingTrack = track;
  58. d->m_nowPlayingReply = track.updateNowPlaying();
  59. connect( d->m_nowPlayingReply, SIGNAL(finished()), SLOT(onNowPlayingReturn()));
  60. }
  61. }
  62. void
  63. lastfm::Audioscrobbler::cache( const Track& track )
  64. {
  65. QList<Track> tracks;
  66. tracks.append( track );
  67. cacheBatch( tracks );
  68. }
  69. void
  70. lastfm::Audioscrobbler::cacheBatch( const QList<lastfm::Track>& tracks )
  71. {
  72. d->m_cache.add( tracks );
  73. foreach ( const Track& track, d->m_cache.tracks() )
  74. MutableTrack( track ).setScrobbleStatus( Track::Cached );
  75. emit scrobblesCached( tracks );
  76. submit();
  77. }
  78. void
  79. lastfm::Audioscrobbler::submit()
  80. {
  81. if (d->m_cache.tracks().isEmpty() // there are no tracks to submit
  82. || !d->m_scrobbleReply.isNull() ) // we are already submitting scrobbles
  83. return;
  84. // copy tracks to be submitted to a temporary list
  85. d->m_batch = d->m_cache.tracks().mid( 0, 50 );
  86. // if there is only one track use track.scrobble, otherwise use track.scrobbleBatch
  87. if (d->m_batch.count() == 1)
  88. d->m_scrobbleReply = d->m_batch[0].scrobble();
  89. else
  90. d->m_scrobbleReply = lastfm::Track::scrobble( d->m_batch );
  91. connect( d->m_scrobbleReply, SIGNAL(finished()), SLOT(onTrackScrobbleReturn()));
  92. }
  93. void
  94. lastfm::Audioscrobbler::parseTrack( const XmlQuery& trackXml, const Track& track )
  95. {
  96. MutableTrack mTrack = MutableTrack( track );
  97. bool isScrobble = QDomElement(trackXml).tagName() == "scrobble";
  98. if ( trackXml["ignoredMessage"].attribute("code") == "0" )
  99. {
  100. if ( isScrobble ) mTrack.setScrobbleStatus( Track::Submitted );
  101. // corrections!
  102. if ( trackXml["track"].attribute("corrected") == "1"
  103. || trackXml["artist"].attribute("corrected") == "1"
  104. || trackXml["album"].attribute("corrected") == "1"
  105. || trackXml["albumArtist"].attribute("corrected") == "1")
  106. {
  107. mTrack.setCorrections(trackXml["track"].text(),
  108. trackXml["album"].text(),
  109. trackXml["artist"].text(),
  110. trackXml["albumArtist"].text());
  111. }
  112. }
  113. else if ( isScrobble )
  114. {
  115. mTrack.setScrobbleError( static_cast<Track::ScrobbleError>(trackXml["ignoredMessage"].attribute("code").toInt()) );
  116. mTrack.setScrobbleStatus( Track::Error );
  117. }
  118. }
  119. void
  120. lastfm::Audioscrobbler::onNowPlayingReturn()
  121. {
  122. try
  123. {
  124. lastfm::XmlQuery lfm = static_cast<QNetworkReply*>(sender())->readAll();
  125. qDebug() << lfm;
  126. if ( lfm.attribute("status") == "ok" )
  127. parseTrack( lfm["nowplaying"], d->m_nowPlayingTrack );
  128. else
  129. emit nowPlayingError( lfm["error"].attribute("code").toInt(), lfm["error"].text() );
  130. d->m_nowPlayingTrack = Track();
  131. d->m_nowPlayingReply = 0;
  132. }
  133. catch ( lastfm::ws::ParseError p )
  134. {
  135. qDebug() << p.message() << p.enumValue();
  136. }
  137. catch ( lastfm::ws::Error p )
  138. {
  139. qDebug() << p;
  140. }
  141. d->m_nowPlayingTrack = Track();
  142. d->m_nowPlayingReply = 0;
  143. }
  144. void
  145. lastfm::Audioscrobbler::onTrackScrobbleReturn()
  146. {
  147. try
  148. {
  149. lastfm::XmlQuery lfm = d->m_scrobbleReply->readAll();
  150. qDebug() << lfm;
  151. if (lfm.attribute("status") == "ok")
  152. {
  153. int index = 0;
  154. foreach ( const XmlQuery& scrobble, lfm["scrobbles"].children("scrobble") )
  155. parseTrack( scrobble, d->m_batch.at( index++ ) );
  156. emit scrobblesSubmitted( d->m_batch );
  157. d->m_cache.remove( d->m_batch );
  158. d->m_batch.clear();
  159. }
  160. else if ( d->m_scrobbleReply->error() == QNetworkReply::NoError )
  161. {
  162. // The scrobble submission failed, but the http request was sucessful
  163. if ( !(lfm["error"].attribute("code") == "9" // Bad session
  164. || lfm["error"].attribute("code") == "11" // Service offline
  165. || lfm["error"].attribute("code") == "16") ) // Service temporarily unavailable
  166. {
  167. foreach ( const Track& track, d->m_batch )
  168. {
  169. MutableTrack mTrack = MutableTrack( track );
  170. mTrack.setScrobbleError( static_cast<Track::ScrobbleError>(lfm["error"].attribute("code").toInt()) );
  171. mTrack.setScrobbleErrorText( lfm["error"].text() );
  172. mTrack.setScrobbleStatus( Track::Error );
  173. }
  174. emit scrobblesSubmitted( d->m_batch );
  175. // clear the cache if it was not one of these error codes
  176. d->m_cache.remove( d->m_batch );
  177. d->m_batch.clear();
  178. }
  179. else
  180. {
  181. Q_ASSERT(false);
  182. }
  183. }
  184. d->m_scrobbleReply = 0;
  185. // check is there are anymore scrobbles to submit
  186. submit();
  187. }
  188. catch ( lastfm::ws::ParseError p )
  189. {
  190. qDebug() << p.message() << p.enumValue();
  191. d->m_scrobbleReply = 0;
  192. }
  193. catch ( lastfm::ws::Error p )
  194. {
  195. qDebug() << p;
  196. d->m_scrobbleReply = 0;
  197. }
  198. }