/src/libtomahawk/playlist/dynamic/DynamicModel.cpp

http://github.com/tomahawk-player/tomahawk · C++ · 350 lines · 257 code · 70 blank · 23 comment · 67 complexity · 931e0f95cb4f041b9ba8f42537657a7d MD5 · raw file

  1. /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  2. *
  3. * Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
  4. * Copyright 2010-2011, Jeff Mitchell <jeff@tomahawk-player.org>
  5. *
  6. * Tomahawk is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Tomahawk is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "playlist/dynamic/DynamicModel.h"
  20. #include "DynamicPlaylist.h"
  21. #include "GeneratorInterface.h"
  22. #include "Pipeline.h"
  23. #include "Query.h"
  24. #include "Source.h"
  25. #include "audio/AudioEngine.h"
  26. #include "utils/Logger.h"
  27. using namespace Tomahawk;
  28. DynamicModel::DynamicModel( QObject* parent )
  29. : PlaylistModel( parent )
  30. , m_onDemandRunning( false )
  31. , m_changeOnNext( false )
  32. , m_searchingForNext( false )
  33. , m_filterUnresolvable( true )
  34. , m_startingAfterFailed( false )
  35. , m_currentAttempts( 0 )
  36. , m_lastResolvedRow( 0 )
  37. {
  38. }
  39. DynamicModel::~DynamicModel()
  40. {
  41. }
  42. void
  43. DynamicModel::loadPlaylist( const Tomahawk::dynplaylist_ptr& playlist, bool loadEntries )
  44. {
  45. Q_UNUSED( loadEntries );
  46. if ( !m_playlist.isNull() )
  47. {
  48. disconnect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( newTrackGenerated( Tomahawk::query_ptr ) ) );
  49. }
  50. const int oldCount = rowCount( QModelIndex() );
  51. m_playlist = playlist;
  52. m_deduper.clear();
  53. if ( m_playlist->mode() == OnDemand )
  54. setFilterUnresolvable( true );
  55. connect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( newTrackGenerated( Tomahawk::query_ptr ) ) );
  56. PlaylistModel::loadPlaylist( m_playlist, m_playlist->mode() == Static );
  57. if ( m_playlist->mode() == OnDemand && oldCount != rowCount( QModelIndex() ) )
  58. emit itemCountChanged( rowCount( QModelIndex() ) );
  59. }
  60. QString
  61. DynamicModel::description() const
  62. {
  63. if ( !m_playlist.isNull() && !m_playlist->generator().isNull() )
  64. return m_playlist->generator()->sentenceSummary();
  65. else
  66. return QString();
  67. }
  68. void
  69. DynamicModel::startOnDemand()
  70. {
  71. connect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) );
  72. m_playlist->generator()->startOnDemand();
  73. m_onDemandRunning = true;
  74. }
  75. void
  76. DynamicModel::newTrackGenerated( const Tomahawk::query_ptr& query )
  77. {
  78. if ( m_onDemandRunning )
  79. {
  80. bool isDuplicate = false;
  81. for ( int i = 0; i < m_deduper.size(); i++ )
  82. {
  83. if ( m_deduper[ i ].first == query->track()->track() && m_deduper[ i ].second == query->track()->artist() )
  84. isDuplicate = true;
  85. }
  86. if ( isDuplicate )
  87. {
  88. m_playlist->generator()->fetchNext();
  89. return;
  90. }
  91. else
  92. {
  93. m_deduper.append( QPair< QString, QString >( query->track()->track(), query->track()->artist() ) );
  94. }
  95. connect( query.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( trackResolveFinished( bool ) ) );
  96. m_waitingFor << query.data();
  97. appendQuery( query );
  98. }
  99. }
  100. void
  101. DynamicModel::stopOnDemand( bool stopPlaying )
  102. {
  103. m_onDemandRunning = false;
  104. if ( stopPlaying )
  105. AudioEngine::instance()->stop();
  106. disconnect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) );
  107. }
  108. void
  109. DynamicModel::changeStation()
  110. {
  111. if ( m_onDemandRunning )
  112. m_changeOnNext = true;
  113. else // if we're not running, just start
  114. m_playlist->generator()->startOnDemand();
  115. }
  116. void
  117. DynamicModel::trackResolveFinished( bool success )
  118. {
  119. Q_UNUSED( success );
  120. Query* q = qobject_cast<Query*>( sender() );
  121. tDebug() << "Got resolveFinished in DynamicModel" << q->track()->toString();
  122. if ( !m_waitingFor.contains( q ) )
  123. return;
  124. if ( !q->playable() )
  125. {
  126. tDebug() << "Got not playable or resolved track:" << q->track()->toString() << m_lastResolvedRow << m_currentAttempts;
  127. m_currentAttempts++;
  128. int curAttempts = m_startingAfterFailed ? m_currentAttempts - 20 : m_currentAttempts; // if we just failed, m_currentAttempts includes those failures
  129. if( curAttempts < 20 ) {
  130. qDebug() << "FETCHING MORE!";
  131. m_playlist->generator()->fetchNext();
  132. } else {
  133. m_startingAfterFailed = true;
  134. emit trackGenerationFailure( tr( "Could not find a playable track.\n\nPlease change the filters or try again." ) );
  135. }
  136. }
  137. else
  138. {
  139. qDebug() << "Got successful resolved track:" << q->track()->toString() << m_lastResolvedRow << m_currentAttempts;
  140. if ( m_currentAttempts > 0 ) {
  141. qDebug() << "EMITTING AN ASK FOR COLLAPSE:" << m_lastResolvedRow << m_currentAttempts;
  142. emit collapseFromTo( m_lastResolvedRow, m_currentAttempts );
  143. }
  144. m_currentAttempts = 0;
  145. m_searchingForNext = false;
  146. emit checkForOverflow();
  147. }
  148. m_waitingFor.removeAll( q );
  149. }
  150. void
  151. DynamicModel::newTrackLoading()
  152. {
  153. qDebug() << "Got NEW TRACK LOADING signal";
  154. if ( m_changeOnNext )
  155. { // reset instead of getting the next one
  156. m_lastResolvedRow = rowCount( QModelIndex() );
  157. m_searchingForNext = true;
  158. m_playlist->generator()->startOnDemand();
  159. }
  160. else if ( m_onDemandRunning && m_currentAttempts == 0 && !m_searchingForNext )
  161. { // if we're in dynamic mode and we're also currently idle
  162. m_lastResolvedRow = rowCount( QModelIndex() );
  163. m_searchingForNext = true;
  164. qDebug() << "IDLE fetching new track!";
  165. m_playlist->generator()->fetchNext();
  166. }
  167. }
  168. void
  169. DynamicModel::tracksGenerated( const QList< query_ptr > entries, int limitResolvedTo )
  170. {
  171. if ( m_filterUnresolvable && m_playlist->mode() == OnDemand )
  172. { // wait till we get them resolved (for previewing stations)
  173. m_limitResolvedTo = limitResolvedTo;
  174. filterUnresolved( entries );
  175. }
  176. else
  177. {
  178. addToPlaylist( entries, m_playlist->mode() == OnDemand ); // if ondemand, we're previewing, so clear old
  179. if ( m_playlist->mode() == OnDemand )
  180. {
  181. m_lastResolvedRow = rowCount( QModelIndex() );
  182. }
  183. }
  184. if ( m_playlist->mode() == OnDemand && entries.isEmpty() )
  185. emit trackGenerationFailure( tr( "Failed to generate preview with the desired filters" ) );
  186. }
  187. void
  188. DynamicModel::filterUnresolved( const QList< query_ptr >& entries )
  189. {
  190. m_toResolveList = entries;
  191. foreach ( const query_ptr& q, entries )
  192. connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( filteringTrackResolved( bool ) ) );
  193. Pipeline::instance()->resolve( entries, true );
  194. }
  195. void
  196. DynamicModel::filteringTrackResolved( bool successful )
  197. {
  198. // arg, we don't have the query_ptr, just the Query
  199. Query* q = qobject_cast< Query* >( sender() );
  200. Q_ASSERT( q );
  201. // if meantime the user began the station, abort
  202. qDebug() << "Got filtering resolved finished for track, was it successful?:" << q->track()->toString() << successful << q->playable();
  203. if ( m_onDemandRunning )
  204. {
  205. m_toResolveList.clear();
  206. m_resolvedList.clear();
  207. return;
  208. }
  209. query_ptr realptr;
  210. foreach ( const query_ptr& qptr, m_toResolveList )
  211. {
  212. if ( qptr.data() == q )
  213. {
  214. realptr = qptr;
  215. break;
  216. }
  217. }
  218. if( realptr.isNull() ) // we already finished
  219. return;
  220. m_toResolveList.removeAll( realptr );
  221. if ( realptr->playable() )
  222. {
  223. m_resolvedList << realptr;
  224. // append and update internal lastResolvedRow
  225. addToPlaylist( QList< query_ptr >() << realptr, false );
  226. if ( m_playlist->mode() == OnDemand )
  227. {
  228. m_lastResolvedRow = rowCount( QModelIndex() );
  229. }
  230. if ( m_toResolveList.isEmpty() || m_resolvedList.size() == m_limitResolvedTo )
  231. { // done
  232. m_toResolveList.clear();
  233. m_resolvedList.clear();
  234. }
  235. }
  236. else
  237. {
  238. qDebug() << "Got unsuccessful resolve request for this track" << realptr->track()->toString();
  239. }
  240. if ( m_toResolveList.isEmpty() && rowCount( QModelIndex() ) == 0 ) // we failed
  241. emit trackGenerationFailure( tr( "Could not find a playable track.\n\nPlease change the filters or try again." ) );
  242. }
  243. void
  244. DynamicModel::addToPlaylist( const QList< query_ptr >& entries, bool clearFirst )
  245. {
  246. if ( clearFirst )
  247. clear();
  248. foreach ( const query_ptr& q, entries )
  249. m_deduper.append( QPair< QString, QString >( q->track()->track(), q->track()->artist() ) );
  250. if ( m_playlist->author()->isLocal() && m_playlist->mode() == Static )
  251. {
  252. m_playlist->addEntries( entries );
  253. }
  254. else
  255. {
  256. // read-only, so add tracks only in the GUI, not to the playlist itself
  257. appendQueries( entries );
  258. }
  259. emit tracksAdded();
  260. }
  261. void
  262. DynamicModel::removeIndex( const QModelIndex& idx, bool moreToCome )
  263. {
  264. if ( m_playlist->mode() == Static && isReadOnly() )
  265. return;
  266. qDebug() << Q_FUNC_INFO << "DYNAMIC MODEL REMOVIN!" << moreToCome << ( idx == index( rowCount( QModelIndex() ) - 1, 0, QModelIndex() ) );
  267. if ( m_playlist->mode() == OnDemand )
  268. {
  269. if ( !moreToCome && idx == index( rowCount( QModelIndex() ) - 1, 0, QModelIndex() ) )
  270. { // if the user is manually removing the last one, re-add as we're a station
  271. newTrackLoading();
  272. }
  273. PlayableModel::removeIndex( idx );
  274. }
  275. else
  276. PlaylistModel::removeIndex( idx, moreToCome );
  277. // don't call onPlaylistChanged.
  278. if( !moreToCome )
  279. m_lastResolvedRow = rowCount( QModelIndex() );
  280. }