/src/libtomahawk/Playlist.cpp

http://github.com/tomahawk-player/tomahawk · C++ · 959 lines · 717 code · 190 blank · 52 comment · 56 complexity · d3eda2a3ef93461dfd716af9840c0f1e MD5 · raw file

  1. /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  2. *
  3. * Copyright 2010-2015, Christian Muehlhaeuser <muesli@tomahawk-player.org>
  4. * Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
  5. * Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
  6. *
  7. * Tomahawk is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * Tomahawk is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. #include "Playlist_p.h"
  21. #include "collection/Collection.h"
  22. #include "database/Database.h"
  23. #include "database/DatabaseCommand_LoadPlaylistEntries.h"
  24. #include "database/DatabaseCommand_SetPlaylistRevision.h"
  25. #include "database/DatabaseCommand_CreatePlaylist.h"
  26. #include "database/DatabaseCommand_DeletePlaylist.h"
  27. #include "database/DatabaseCommand_RenamePlaylist.h"
  28. #include "playlist/PlaylistUpdaterInterface.h"
  29. #include "playlist/PlaylistModel.h"
  30. #include "utils/Logger.h"
  31. #include "utils/Closure.h"
  32. #include "Pipeline.h"
  33. #include "PlaylistEntry.h"
  34. #include "PlaylistPlaylistInterface.h"
  35. #include "Source.h"
  36. #include "SourceList.h"
  37. #include <QDomDocument>
  38. #include <QDomElement>
  39. using namespace Tomahawk;
  40. static QSharedPointer<PlaylistRemovalHandler> s_removalHandler;
  41. Playlist::Playlist( const source_ptr& author )
  42. : d_ptr( new PlaylistPrivate( this, author ) )
  43. {
  44. }
  45. // used when loading from DB:
  46. Playlist::Playlist( const source_ptr& src,
  47. const QString& currentrevision,
  48. const QString& title,
  49. const QString& info,
  50. const QString& creator,
  51. uint createdOn,
  52. bool shared,
  53. int lastmod,
  54. const QString& guid )
  55. : QObject()
  56. , d_ptr( new PlaylistPrivate( this, src, currentrevision, title, info, creator, createdOn, shared, lastmod, guid ) )
  57. {
  58. init();
  59. }
  60. Playlist::Playlist( const source_ptr& author,
  61. const QString& guid,
  62. const QString& title,
  63. const QString& info,
  64. const QString& creator,
  65. bool shared,
  66. const QList< Tomahawk::plentry_ptr >& entries )
  67. : QObject()
  68. , d_ptr( new PlaylistPrivate( this, author, guid, title, info, creator, shared, entries ) )
  69. {
  70. init();
  71. }
  72. Playlist::Playlist( const source_ptr& author,
  73. const QString& guid,
  74. const QString& title,
  75. const QString& info,
  76. const QString& creator,
  77. bool shared)
  78. : QObject()
  79. , d_ptr( new PlaylistPrivate( this, author, guid, title, info, creator, shared, QList< Tomahawk::plentry_ptr >() ) )
  80. {
  81. init();
  82. }
  83. void
  84. Playlist::init()
  85. {
  86. Q_D( Playlist );
  87. d->busy = false;
  88. d->deleted = false;
  89. d->locallyChanged = false;
  90. d->loaded = false;
  91. connect( Pipeline::instance(), SIGNAL( idle() ), SLOT( onResolvingFinished() ) );
  92. }
  93. Playlist::~Playlist()
  94. {
  95. delete d_ptr;
  96. }
  97. QSharedPointer<PlaylistRemovalHandler> Playlist::removalHandler()
  98. {
  99. if ( s_removalHandler.isNull() )
  100. {
  101. s_removalHandler = QSharedPointer<PlaylistRemovalHandler>( new PlaylistRemovalHandler() );
  102. }
  103. return s_removalHandler;
  104. }
  105. playlist_ptr
  106. Playlist::create( const source_ptr& author,
  107. const QString& guid,
  108. const QString& title,
  109. const QString& info,
  110. const QString& creator,
  111. bool shared,
  112. const QList<Tomahawk::query_ptr>& queries )
  113. {
  114. QList< plentry_ptr > entries;
  115. foreach( const Tomahawk::query_ptr& query, queries )
  116. {
  117. plentry_ptr p( new PlaylistEntry );
  118. p->setGuid( uuid() );
  119. p->setDuration( query->track()->duration() );
  120. p->setLastmodified( 0 );
  121. p->setAnnotation( query->property( "annotation" ).toString() );
  122. p->setQuery( query );
  123. entries << p;
  124. }
  125. playlist_ptr playlist( new Playlist( author, guid, title, info, creator, shared, entries ), &QObject::deleteLater );
  126. playlist->setWeakSelf( playlist.toWeakRef() );
  127. // save to DB in the background
  128. // Watch for the created() signal if you need to be sure it's written.
  129. //
  130. // When a playlist is created it will reportCreated(), adding it to the
  131. // collection it belongs to and emitting the appropriate signal.
  132. // When we create a new playlist for the local Source.here we call reportCreated()
  133. // immediately, so the GUI can reflect the new playlist without waiting for the DB sync
  134. //
  135. // When createplaylist DBOPs come from peers, the postCommitHook will call
  136. // reportCreated for us automatically, which should cause new playlists to be added to the GUI.
  137. DatabaseCommand_CreatePlaylist* cmd = new DatabaseCommand_CreatePlaylist( author, playlist );
  138. connect( cmd, SIGNAL( finished() ), playlist.data(), SIGNAL( created() ) );
  139. Database::instance()->enqueue( dbcmd_ptr(cmd) );
  140. playlist->reportCreated( playlist );
  141. return playlist;
  142. }
  143. playlist_ptr
  144. Playlist::get( const QString& guid )
  145. {
  146. playlist_ptr p;
  147. foreach( const Tomahawk::source_ptr& source, SourceList::instance()->sources() )
  148. {
  149. p = source->dbCollection()->playlist( guid );
  150. if ( !p )
  151. p = source->dbCollection()->autoPlaylist( guid );
  152. if ( !p )
  153. p = source->dbCollection()->station( guid );
  154. if ( p )
  155. return p;
  156. }
  157. return p;
  158. }
  159. void
  160. Playlist::rename( const QString& title )
  161. {
  162. DatabaseCommand_RenamePlaylist* cmd = new DatabaseCommand_RenamePlaylist( author(), guid(), title );
  163. Database::instance()->enqueue( Tomahawk::dbcmd_ptr(cmd) );
  164. }
  165. void
  166. Playlist::setTitle( const QString& title )
  167. {
  168. Q_D( Playlist );
  169. if ( title == d->title )
  170. return;
  171. const QString oldTitle = d->title;
  172. d->title = title;
  173. emit changed();
  174. emit renamed( d->title, oldTitle );
  175. }
  176. void
  177. Playlist::reportCreated( const playlist_ptr& self )
  178. {
  179. Q_D( Playlist );
  180. Q_ASSERT( self.data() == this );
  181. d->source->dbCollection()->addPlaylist( self );
  182. }
  183. void
  184. Playlist::reportDeleted( const Tomahawk::playlist_ptr& self )
  185. {
  186. Q_D( Playlist );
  187. Q_ASSERT( self.data() == this );
  188. if ( !d->updaters.isEmpty() )
  189. {
  190. foreach( PlaylistUpdaterInterface* updater, d->updaters )
  191. {
  192. updater->remove();
  193. }
  194. }
  195. d->deleted = true;
  196. d->source->dbCollection()->deletePlaylist( self );
  197. emit deleted( self );
  198. }
  199. void
  200. Playlist::addUpdater( PlaylistUpdaterInterface* updater )
  201. {
  202. Q_D( Playlist );
  203. d->updaters << updater;
  204. connect( updater, SIGNAL( changed() ), this, SIGNAL( changed() ), Qt::UniqueConnection );
  205. connect( updater, SIGNAL( destroyed( QObject* ) ), this, SIGNAL( changed() ), Qt::QueuedConnection );
  206. emit changed();
  207. }
  208. void
  209. Playlist::removeUpdater( PlaylistUpdaterInterface* updater )
  210. {
  211. Q_D( Playlist );
  212. d->updaters.removeAll( updater );
  213. disconnect( updater, SIGNAL( changed() ), this, SIGNAL( changed() ) );
  214. disconnect( updater, SIGNAL( destroyed( QObject* ) ), this, SIGNAL( changed() ) );
  215. emit changed();
  216. }
  217. bool
  218. Playlist::hasCustomDeleter() const
  219. {
  220. Q_D( const Playlist );
  221. foreach ( PlaylistUpdaterInterface* updater, d->updaters )
  222. {
  223. if ( updater->hasCustomDeleter() )
  224. return true;
  225. }
  226. return false;
  227. }
  228. void
  229. Playlist::loadRevision( const QString& rev )
  230. {
  231. // qDebug() << Q_FUNC_INFO << currentrevision() << rev << m_title;
  232. setBusy( true );
  233. DatabaseCommand_LoadPlaylistEntries* cmd =
  234. new DatabaseCommand_LoadPlaylistEntries( rev.isEmpty() ? currentrevision() : rev );
  235. connect( cmd, SIGNAL( done( const QString&,
  236. const QList<QString>&,
  237. const QList<QString>&,
  238. bool,
  239. const QMap< QString, Tomahawk::plentry_ptr >&,
  240. bool ) ),
  241. SLOT( setRevision( const QString&,
  242. const QList<QString>&,
  243. const QList<QString>&,
  244. bool,
  245. const QMap< QString, Tomahawk::plentry_ptr >&,
  246. bool ) ) );
  247. Database::instance()->enqueue( Tomahawk::dbcmd_ptr( cmd ) );
  248. }
  249. //public, model can call this if user changes a playlist:
  250. void
  251. Playlist::createNewRevision( const QString& newrev, const QString& oldrev, const QList< plentry_ptr >& entries )
  252. {
  253. Q_D( Playlist );
  254. tDebug() << Q_FUNC_INFO << newrev << oldrev << entries.count();
  255. Q_ASSERT( d->source->isLocal() || newrev == oldrev );
  256. if ( busy() )
  257. {
  258. d->revisionQueue.enqueue( RevisionQueueItem( newrev, oldrev, entries, oldrev == currentrevision() ) );
  259. return;
  260. }
  261. if ( newrev != oldrev )
  262. setBusy( true );
  263. // calc list of newly added entries:
  264. QList<plentry_ptr> added = newEntries( entries );
  265. QStringList orderedguids;
  266. qDebug() << "Inserting ordered GUIDs:";
  267. foreach( const plentry_ptr& p, entries )
  268. {
  269. qDebug() << p->guid() << p->query()->track()->toString();
  270. orderedguids << p->guid();
  271. }
  272. foreach( const plentry_ptr& p, added )
  273. qDebug() << p->guid();
  274. // source making the change (local user in this case)
  275. source_ptr author = SourceList::instance()->getLocal();
  276. // command writes new rev to DB and calls setRevision, which emits our signal
  277. DatabaseCommand_SetPlaylistRevision* cmd =
  278. new DatabaseCommand_SetPlaylistRevision( author,
  279. guid(),
  280. newrev,
  281. oldrev,
  282. orderedguids,
  283. added,
  284. entries );
  285. connect( cmd, SIGNAL( finished() ),
  286. this, SLOT( setPlaylistRevisionFinished() ) );
  287. if ( d->queuedSetPlaylistRevision )
  288. {
  289. d->queuedSetPlaylistRevisionCmds.enqueue( cmd );
  290. }
  291. else
  292. {
  293. d->queuedSetPlaylistRevision = true;
  294. Database::instance()->enqueue( Tomahawk::dbcmd_ptr( cmd ) );
  295. }
  296. }
  297. void
  298. Playlist::updateEntries( const QString& newrev, const QString& oldrev, const QList< plentry_ptr >& entries )
  299. {
  300. Q_D( Playlist );
  301. tDebug() << Q_FUNC_INFO << newrev << oldrev << entries.count();
  302. Q_ASSERT( d->source->isLocal() || newrev == oldrev );
  303. if ( busy() )
  304. {
  305. d->updateQueue.enqueue( RevisionQueueItem( newrev, oldrev, entries, oldrev == currentrevision() ) );
  306. return;
  307. }
  308. if ( newrev != oldrev )
  309. setBusy( true );
  310. QStringList orderedguids;
  311. foreach( const plentry_ptr& p, d->entries )
  312. {
  313. orderedguids << p->guid();
  314. }
  315. qDebug() << "Updating playlist metadata:" << entries;
  316. DatabaseCommand_SetPlaylistRevision* cmd =
  317. new DatabaseCommand_SetPlaylistRevision( SourceList::instance()->getLocal(),
  318. guid(),
  319. newrev,
  320. oldrev,
  321. orderedguids,
  322. entries );
  323. connect( cmd, SIGNAL( finished() ),
  324. this, SLOT( setPlaylistRevisionFinished() ) );
  325. if ( d->queuedSetPlaylistRevision )
  326. {
  327. d->queuedSetPlaylistRevisionCmds.enqueue( cmd );
  328. }
  329. else
  330. {
  331. d->queuedSetPlaylistRevision = true;
  332. Database::instance()->enqueue( Tomahawk::dbcmd_ptr( cmd ) );
  333. }
  334. }
  335. // private, called when we loadRevision, or by our friend class DatabaseCommand_SetPlaylistRevision
  336. // used to save new revision data (either originating locally, or via network msg for syncing)
  337. void
  338. Playlist::setRevision( const QString& rev,
  339. const QList<QString>& neworderedguids,
  340. const QList<QString>& oldorderedguids,
  341. bool is_newest_rev,
  342. const QMap< QString, Tomahawk::plentry_ptr >& addedmap,
  343. bool applied )
  344. {
  345. Q_D( Playlist );
  346. if ( QThread::currentThread() != thread() )
  347. {
  348. QMetaObject::invokeMethod( this,
  349. "setRevision",
  350. Qt::BlockingQueuedConnection,
  351. Q_ARG( QString, rev ),
  352. Q_ARG( QList<QString>, neworderedguids ),
  353. Q_ARG( QList<QString>, oldorderedguids ),
  354. Q_ARG( bool, is_newest_rev ),
  355. QGenericArgument( "QMap< QString,Tomahawk::plentry_ptr >", (const void*)&addedmap ),
  356. Q_ARG( bool, applied )
  357. );
  358. return;
  359. }
  360. PlaylistRevision pr = setNewRevision( rev, neworderedguids, oldorderedguids, is_newest_rev, addedmap );
  361. Q_ASSERT( applied );
  362. if ( applied )
  363. d->currentrevision = rev;
  364. pr.applied = applied;
  365. foreach( const plentry_ptr& entry, d->entries )
  366. {
  367. connect( entry.data(), SIGNAL( resultChanged() ), SLOT( onResultsChanged() ), Qt::UniqueConnection );
  368. }
  369. setBusy( false );
  370. setLoaded( true );
  371. if ( !d->initEntries.isEmpty() && currentrevision().isEmpty() )
  372. {
  373. // add initial tracks
  374. createNewRevision( uuid(), currentrevision(), d->initEntries );
  375. d->initEntries.clear();
  376. }
  377. else
  378. emit revisionLoaded( pr );
  379. checkRevisionQueue();
  380. }
  381. PlaylistRevision
  382. Playlist::setNewRevision( const QString& rev,
  383. const QList<QString>& neworderedguids,
  384. const QList<QString>& oldorderedguids,
  385. bool is_newest_rev,
  386. const QMap< QString, Tomahawk::plentry_ptr >& addedmap )
  387. {
  388. Q_D( Playlist );
  389. Q_UNUSED( oldorderedguids );
  390. Q_UNUSED( is_newest_rev );
  391. // build up correctly ordered new list of plentry_ptrs from
  392. // existing ones, and the ones that have been added
  393. QMap<QString, plentry_ptr> entriesmap;
  394. foreach ( const plentry_ptr& p, d->entries )
  395. {
  396. entriesmap.insert( p->guid(), p );
  397. }
  398. // re-build m_entries from neworderedguids. plentries come either from the old m_entries OR addedmap.
  399. d->entries.clear();
  400. foreach ( const QString& id, neworderedguids )
  401. {
  402. if ( entriesmap.contains( id ) )
  403. {
  404. d->entries.append( entriesmap.value( id ) );
  405. }
  406. else if ( addedmap.contains( id ) )
  407. {
  408. d->entries.append( addedmap.value( id ) );
  409. }
  410. else
  411. {
  412. tDebug() << "id:" << id;
  413. tDebug() << "newordered:" << neworderedguids.count() << neworderedguids;
  414. tDebug() << "entriesmap:" << entriesmap.count() << entriesmap;
  415. tDebug() << "addedmap:" << addedmap.count() << addedmap;
  416. tDebug() << "m_entries" << d->entries;
  417. tLog() << "Playlist error for playlist with guid" << guid() << "from source" << author()->friendlyName();
  418. // Q_ASSERT( false ); // XXX
  419. }
  420. }
  421. PlaylistRevision pr;
  422. pr.oldrevisionguid = d->currentrevision;
  423. pr.revisionguid = rev;
  424. pr.added = addedmap.values();
  425. pr.newlist = d->entries;
  426. return pr;
  427. }
  428. void
  429. Playlist::removeFromDatabase()
  430. {
  431. Q_D( Playlist );
  432. emit aboutToBeDeleted( d->weakSelf.toStrongRef() );
  433. DatabaseCommand_DeletePlaylist* cmd = new DatabaseCommand_DeletePlaylist( d->source, d->guid );
  434. Database::instance()->enqueue( Tomahawk::dbcmd_ptr( cmd ) );
  435. }
  436. Playlist::Playlist( PlaylistPrivate *d )
  437. : d_ptr( d )
  438. {
  439. init();
  440. }
  441. source_ptr
  442. Playlist::author() const
  443. {
  444. Q_D( const Playlist );
  445. return d->source;
  446. }
  447. void
  448. Playlist::resolve()
  449. {
  450. Q_D( Playlist );
  451. QList< query_ptr > qlist;
  452. foreach( const plentry_ptr& p, d->entries )
  453. {
  454. qlist << p->query();
  455. }
  456. Pipeline::instance()->resolve( qlist );
  457. }
  458. void
  459. Playlist::onResultsChanged()
  460. {
  461. Q_D( Playlist );
  462. d->locallyChanged = true;
  463. }
  464. void
  465. Playlist::onResolvingFinished()
  466. {
  467. Q_D( Playlist );
  468. if ( d->locallyChanged && !d->deleted )
  469. {
  470. d->locallyChanged = false;
  471. createNewRevision( currentrevision(), currentrevision(), d->entries );
  472. }
  473. }
  474. void
  475. Playlist::setPlaylistRevisionFinished()
  476. {
  477. Q_D( Playlist );
  478. if ( !d->queuedSetPlaylistRevisionCmds.isEmpty() )
  479. {
  480. DatabaseCommand_SetPlaylistRevision* cmd = d->queuedSetPlaylistRevisionCmds.dequeue();
  481. // Update oldrev to currentRevision so that optimistic locking works correctly.
  482. cmd->setOldrev( currentrevision() );
  483. Database::instance()->enqueue( Tomahawk::dbcmd_ptr( cmd ) );
  484. }
  485. else
  486. {
  487. d->queuedSetPlaylistRevision = false;
  488. }
  489. }
  490. void
  491. Playlist::addEntry( const query_ptr& query )
  492. {
  493. QList<query_ptr> queries;
  494. queries << query;
  495. addEntries( queries );
  496. }
  497. void
  498. Playlist::addEntries( const QList<query_ptr>& queries )
  499. {
  500. Q_D( Playlist );
  501. if ( !d->loaded )
  502. {
  503. tDebug() << Q_FUNC_INFO << "Queueing addEntries call!";
  504. loadRevision();
  505. d->queuedOps << NewClosure( 0, "", this, SLOT( addEntries( QList<Tomahawk::query_ptr> ) ), queries );
  506. return;
  507. }
  508. const QList<plentry_ptr> el = entriesFromQueries( queries );
  509. const int prevSize = d->entries.size();
  510. QString newrev = uuid();
  511. createNewRevision( newrev, d->currentrevision, el );
  512. // We are appending at end, so notify listeners.
  513. // PlaylistModel also emits during appends, but since we call
  514. // createNewRevision, it reloads instead of appends.
  515. const QList<plentry_ptr> added = el.mid( prevSize );
  516. tDebug( LOGVERBOSE ) << "Playlist got" << queries.size() << "tracks added, emitting tracksInserted with:" << added.size() << "at pos:" << prevSize - 1;
  517. emit tracksInserted( added, prevSize );
  518. }
  519. void
  520. Playlist::insertEntries( const QList< query_ptr >& queries, const int position )
  521. {
  522. Q_D( Playlist );
  523. if ( !d->loaded )
  524. {
  525. tDebug() << Q_FUNC_INFO << "Queueing insertEntries call!";
  526. loadRevision();
  527. d->queuedOps << NewClosure( 0, "", this, SLOT( insertEntries( QList<Tomahawk::query_ptr>, int ) ), queries, position );
  528. return;
  529. }
  530. QList<plentry_ptr> toInsert = entriesFromQueries( queries, true );
  531. QList<plentry_ptr> entries = d->entries;
  532. Q_ASSERT( position <= d->entries.size() );
  533. if ( position > d->entries.size() )
  534. {
  535. tDebug() << "ERROR trying to insert tracks past end of playlist! Appending!";
  536. addEntries( queries );
  537. return;
  538. }
  539. for ( int i = toInsert.size()-1; i >= 0; --i )
  540. entries.insert( position, toInsert.at(i) );
  541. createNewRevision( uuid(), d->currentrevision, entries );
  542. // We are appending at end, so notify listeners.
  543. // PlaylistModel also emits during appends, but since we call
  544. // createNewRevision, it reloads instead of appends.
  545. qDebug() << "Playlist got" << toInsert.size() << "tracks added, emitting tracksInserted at pos:" << position;
  546. emit tracksInserted( toInsert, position );
  547. }
  548. QList<plentry_ptr>
  549. Playlist::entriesFromQueries( const QList<Tomahawk::query_ptr>& queries, bool clearFirst )
  550. {
  551. QList<plentry_ptr> el;
  552. if ( !clearFirst )
  553. el = entries();
  554. foreach( const query_ptr& query, queries )
  555. {
  556. plentry_ptr e( new PlaylistEntry() );
  557. e->setGuid( uuid() );
  558. e->setDuration( query->track()->duration() );
  559. e->setLastmodified( 0 );
  560. QString annotation = "";
  561. if ( !query->property( "annotation" ).toString().isEmpty() )
  562. annotation = query->property( "annotation" ).toString();
  563. e->setAnnotation( annotation ); // FIXME
  564. e->setQuery( query );
  565. el << e;
  566. }
  567. return el;
  568. }
  569. QList< plentry_ptr >
  570. Playlist::newEntries( const QList< plentry_ptr >& entries )
  571. {
  572. Q_D( Playlist );
  573. QSet<QString> currentguids;
  574. foreach( const plentry_ptr& p, d->entries )
  575. currentguids.insert( p->guid() ); // could be cached as member?
  576. // calc list of newly added entries:
  577. QList<plentry_ptr> added;
  578. foreach( const plentry_ptr& p, entries )
  579. {
  580. if ( !currentguids.contains( p->guid() ) )
  581. added << p;
  582. }
  583. return added;
  584. }
  585. void
  586. Playlist::setBusy( bool b )
  587. {
  588. Q_D( Playlist );
  589. d->busy = b;
  590. emit changed();
  591. }
  592. void
  593. Playlist::setLoaded( bool b )
  594. {
  595. Q_D( Playlist );
  596. d->loaded = b;
  597. }
  598. void
  599. Playlist::checkRevisionQueue()
  600. {
  601. Q_D( Playlist );
  602. if ( !d->revisionQueue.isEmpty() )
  603. {
  604. RevisionQueueItem item = d->revisionQueue.dequeue();
  605. if ( item.oldRev != currentrevision() && item.applyToTip )
  606. {
  607. // this was applied to the then-latest, but the already-running operation changed it so it's out of date now. fix it
  608. if ( item.oldRev == item.newRev )
  609. {
  610. checkRevisionQueue();
  611. return;
  612. }
  613. item.oldRev = currentrevision();
  614. }
  615. createNewRevision( item.newRev, item.oldRev, item.entries );
  616. }
  617. if ( !d->updateQueue.isEmpty() )
  618. {
  619. RevisionQueueItem item = d->updateQueue.dequeue();
  620. if ( item.oldRev != currentrevision() && item.applyToTip )
  621. {
  622. // this was applied to the then-latest, but the already-running operation changed it so it's out of date now. fix it
  623. if ( item.oldRev == item.newRev )
  624. {
  625. checkRevisionQueue();
  626. return;
  627. }
  628. item.oldRev = currentrevision();
  629. }
  630. updateEntries( item.newRev, item.oldRev, item.entries );
  631. }
  632. if ( !d->queuedOps.isEmpty() )
  633. {
  634. _detail::Closure* next = d->queuedOps.dequeue();
  635. next->forceInvoke();
  636. }
  637. }
  638. void
  639. Playlist::setWeakSelf( QWeakPointer< Playlist > self )
  640. {
  641. Q_D( Playlist );
  642. d->weakSelf = self;
  643. }
  644. Tomahawk::playlistinterface_ptr
  645. Playlist::playlistInterface()
  646. {
  647. Q_D( Playlist );
  648. if ( d->playlistInterface.isNull() )
  649. {
  650. d->playlistInterface = Tomahawk::playlistinterface_ptr( new Tomahawk::PlaylistPlaylistInterface( this ) );
  651. }
  652. return d->playlistInterface;
  653. }
  654. QString
  655. Playlist::currentrevision() const
  656. {
  657. Q_D( const Playlist );
  658. return d->currentrevision;
  659. }
  660. QString
  661. Playlist::title() const
  662. {
  663. Q_D( const Playlist );
  664. return d->title;
  665. }
  666. QString
  667. Playlist::info() const
  668. {
  669. Q_D( const Playlist );
  670. return d->info;
  671. }
  672. QString
  673. Playlist::creator() const
  674. {
  675. Q_D( const Playlist );
  676. return d->creator;
  677. }
  678. QString
  679. Playlist::guid() const
  680. {
  681. Q_D( const Playlist );
  682. return d->guid;
  683. }
  684. bool
  685. Playlist::shared() const
  686. {
  687. Q_D( const Playlist );
  688. return d->shared;
  689. }
  690. unsigned int
  691. Playlist::lastmodified() const
  692. {
  693. Q_D( const Playlist );
  694. return d->lastmodified;
  695. }
  696. uint
  697. Playlist::createdOn() const
  698. {
  699. Q_D( const Playlist );
  700. return d->createdOn;
  701. }
  702. bool
  703. Playlist::busy() const
  704. {
  705. Q_D( const Playlist );
  706. return d->busy;
  707. }
  708. bool
  709. Playlist::loaded() const
  710. {
  711. Q_D( const Playlist );
  712. return d->loaded;
  713. }
  714. const QList<plentry_ptr>&
  715. Playlist::entries()
  716. {
  717. Q_D( const Playlist );
  718. return d->entries;
  719. }
  720. void
  721. Playlist::setCurrentrevision( const QString& s )
  722. {
  723. Q_D( Playlist );
  724. d->currentrevision = s;
  725. }
  726. void
  727. Playlist::setInfo( const QString& s )
  728. {
  729. Q_D( Playlist );
  730. d->info = s;
  731. }
  732. void
  733. Playlist::setCreator( const QString& s )
  734. {
  735. Q_D( Playlist );
  736. d->creator = s;
  737. }
  738. void
  739. Playlist::setGuid( const QString& s )
  740. {
  741. Q_D( Playlist );
  742. d->guid = s;
  743. }
  744. void
  745. Playlist::setShared( bool b )
  746. {
  747. Q_D( Playlist );
  748. d->shared = b;
  749. }
  750. void
  751. Playlist::setCreatedOn( uint createdOn )
  752. {
  753. Q_D( Playlist );
  754. d->createdOn = createdOn;
  755. }
  756. QList<PlaylistUpdaterInterface *>
  757. Playlist::updaters() const
  758. {
  759. Q_D( const Playlist );
  760. return d->updaters;
  761. }
  762. void
  763. PlaylistRemovalHandler::remove( const playlist_ptr& playlist )
  764. {
  765. playlist->removeFromDatabase();
  766. }
  767. PlaylistRemovalHandler::PlaylistRemovalHandler()
  768. {
  769. }