/thirdparty/liblastfm2/src/radio/RadioTuner.cpp

http://github.com/tomahawk-player/tomahawk · C++ · 219 lines · 148 code · 37 blank · 34 comment · 14 complexity · efe1f41755406f2abab737a26d632eb5 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 <QTimer>
  17. #include "RadioTuner.h"
  18. #include "../core/XmlQuery.h"
  19. #include "../types/Xspf.h"
  20. #include "../ws/ws.h"
  21. using namespace lastfm;
  22. //TODO skips left
  23. //TODO multiple locations for the same track
  24. //TODO set rtp flag in getPlaylist (whether user is scrobbling this radio session or not)
  25. // limit the number of retries following empty playlists:
  26. #define MAX_TUNING_ATTEMPTS 3
  27. RadioTuner::RadioTuner( const RadioStation& station )
  28. : m_retry_counter( 0 ), m_fetchingPlaylist( false ), m_requestedPlaylist(false)
  29. {
  30. m_twoSecondTimer = new QTimer( this );
  31. m_twoSecondTimer->setSingleShot( true );
  32. connect( m_twoSecondTimer, SIGNAL(timeout()), SLOT(onTwoSecondTimeout()));
  33. qDebug() << station.url();
  34. //Empty RadioStation implies that the radio
  35. //should tune to the previous station.
  36. if( station.url().isEmpty() )
  37. {
  38. fetchFiveMoreTracks();
  39. }
  40. else
  41. {
  42. QMap<QString, QString> map;
  43. map["method"] = "radio.tune";
  44. map["station"] = station.url();
  45. map["additional_info"] = "1";
  46. connect( ws::post(map), SIGNAL(finished()), SLOT(onTuneReturn()) );
  47. }
  48. }
  49. void
  50. RadioTuner::retune( const RadioStation& station )
  51. {
  52. m_playlistQueue.clear();
  53. m_retuneStation = station;
  54. qDebug() << station.url();
  55. }
  56. void
  57. RadioTuner::onTuneReturn()
  58. {
  59. try {
  60. XmlQuery lfm = qobject_cast<QNetworkReply*>(sender())->readAll();
  61. emit title( lfm["station"]["name"].text() );
  62. qDebug() << lfm;
  63. emit supportsDisco( lfm["station"]["supportsdiscovery"].text() == "1" );
  64. fetchFiveMoreTracks();
  65. }
  66. catch (ws::ParseError& e)
  67. {
  68. emit error( e.enumValue(), e.message() );
  69. }
  70. }
  71. void
  72. RadioTuner::fetchFiveMoreTracks()
  73. {
  74. if ( !m_retuneStation.url().isEmpty() )
  75. {
  76. // We have been asked to retune so do it now
  77. QMap<QString, QString> map;
  78. map["method"] = "radio.tune";
  79. map["station"] = m_retuneStation.url();
  80. map["additional_info"] = "1";
  81. QNetworkReply* reply = ws::post(map);
  82. connect( reply, SIGNAL(finished()), SLOT(onTuneReturn()) );
  83. m_retuneStation = RadioStation();
  84. m_twoSecondTimer->stop();
  85. }
  86. else
  87. {
  88. if ( !m_twoSecondTimer->isActive() )
  89. {
  90. //TODO check documentation, I figure this needs a session key
  91. QMap<QString, QString> map;
  92. map["method"] = "radio.getPlaylist";
  93. map["additional_info"] = "1";
  94. map["rtp"] = "1"; // see above
  95. connect( ws::post( map ), SIGNAL(finished()), SLOT(onGetPlaylistReturn()) );
  96. m_fetchingPlaylist = true;
  97. }
  98. else
  99. m_requestedPlaylist = true;
  100. }
  101. }
  102. bool
  103. RadioTuner::tryAgain()
  104. {
  105. qDebug() << "Bad response count" << m_retry_counter;
  106. if (++m_retry_counter > MAX_TUNING_ATTEMPTS)
  107. return false;
  108. fetchFiveMoreTracks();
  109. return true;
  110. }
  111. void
  112. RadioTuner::onGetPlaylistReturn()
  113. {
  114. // We shouldn't request another playlist for 2 seconds because we'll get the same one
  115. // in a different order. This QTimer will block until it has finished. If one or more
  116. // playlists have been requested in the meantime, it will fetch one on timeout
  117. m_twoSecondTimer->start( 2000 );
  118. // This will block us fetching two playlists at once
  119. m_fetchingPlaylist = false;
  120. try {
  121. XmlQuery lfm = qobject_cast<QNetworkReply*>(sender())->readAll();
  122. emit title( lfm["playlist"]["title"].text() );
  123. qDebug() << lfm;
  124. Xspf* xspf = new Xspf( lfm["playlist"], this );
  125. connect( xspf, SIGNAL(expired()), SLOT(onXspfExpired()) );
  126. if ( xspf->isEmpty() )
  127. {
  128. // give up after too many empty playlists :(
  129. if (!tryAgain())
  130. emit error( ws::NotEnoughContent, tr("Not enough content") );
  131. }
  132. else
  133. {
  134. m_retry_counter = 0;
  135. m_playlistQueue << xspf;
  136. emit trackAvailable();
  137. }
  138. }
  139. catch (ws::ParseError& e)
  140. {
  141. qWarning() << e.what();
  142. emit error( e.enumValue(), e.message() );
  143. }
  144. }
  145. void
  146. RadioTuner::onTwoSecondTimeout()
  147. {
  148. if (m_requestedPlaylist)
  149. {
  150. m_requestedPlaylist = false;
  151. fetchFiveMoreTracks();
  152. }
  153. }
  154. void
  155. RadioTuner::onXspfExpired()
  156. {
  157. int index = m_playlistQueue.indexOf( static_cast<Xspf*>(sender()) );
  158. if ( index != -1 )
  159. m_playlistQueue.takeAt( index )->deleteLater();
  160. }
  161. Track
  162. RadioTuner::takeNextTrack()
  163. {
  164. if ( m_playlistQueue.isEmpty() )
  165. {
  166. // If there are no tracks here and we're not fetching tracks
  167. // it's probably because the playlist expired so fetch more now
  168. if ( !m_fetchingPlaylist )
  169. fetchFiveMoreTracks();
  170. return Track();
  171. }
  172. Track result = m_playlistQueue[0]->takeFirst();
  173. if ( m_playlistQueue[0]->isEmpty() )
  174. m_playlistQueue.removeFirst();
  175. if ( m_playlistQueue.isEmpty() )
  176. fetchFiveMoreTracks();
  177. return result;
  178. }