PageRenderTime 84ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp

http://github.com/tomahawk-player/tomahawk
C++ | 628 lines | 484 code | 102 blank | 42 comment | 56 complexity | 70783b832ddff2aac741eab81bf93038 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, GPL-3.0, GPL-2.0
  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 "DynamicPlaylist_p.h"
  20. #include "collection/Collection.h"
  21. #include "database/Database.h"
  22. #include "database/DatabaseCommand.h"
  23. #include "database/DatabaseCommand_CreateDynamicPlaylist.h"
  24. #include "database/DatabaseCommand_SetDynamicPlaylistRevision.h"
  25. #include "database/DatabaseCommand_LoadDynamicPlaylistEntries.h"
  26. #include "database/DatabaseCommand_DeleteDynamicPlaylist.h"
  27. #include "utils/Json.h"
  28. #include "utils/Logger.h"
  29. #include "GeneratorFactory.h"
  30. #include "Playlist_p.h"
  31. #include "PlaylistInterface.h"
  32. #include "PlaylistEntry.h"
  33. #include "PlaylistInterface.h"
  34. #include "SourceList.h"
  35. using namespace Tomahawk;
  36. DynamicPlaylist::DynamicPlaylist( const Tomahawk::source_ptr& author, const QString& type )
  37. : Playlist( new DynamicPlaylistPrivate( this, author ) )
  38. {
  39. Q_D( DynamicPlaylist );
  40. qDebug() << Q_FUNC_INFO << "JSON";
  41. d->generator = geninterface_ptr( GeneratorFactory::create( type ) );
  42. }
  43. DynamicPlaylist::~DynamicPlaylist()
  44. {
  45. }
  46. // Called by loadAllPlaylists command
  47. DynamicPlaylist::DynamicPlaylist ( const Tomahawk::source_ptr& src,
  48. const QString& currentrevision,
  49. const QString& title,
  50. const QString& info,
  51. const QString& creator,
  52. uint createdOn,
  53. const QString& type,
  54. GeneratorMode mode,
  55. bool shared,
  56. int lastmod,
  57. const QString& guid )
  58. : Playlist( new DynamicPlaylistPrivate( this, src, currentrevision, title, info, creator, createdOn, shared, lastmod, guid, false ) )
  59. {
  60. // TODO instantiate generator
  61. Q_D( DynamicPlaylist );
  62. d->generator = geninterface_ptr( GeneratorFactory::create( type ) );
  63. d->generator->setMode( mode );
  64. }
  65. // called when a new playlist is created (no currentrevision, new guid)
  66. DynamicPlaylist::DynamicPlaylist( const Tomahawk::source_ptr& author,
  67. const QString& guid,
  68. const QString& title,
  69. const QString& info,
  70. const QString& creator,
  71. const QString& type,
  72. GeneratorMode mode,
  73. bool shared,
  74. bool autoLoad )
  75. : Playlist( new DynamicPlaylistPrivate( this, author, QString(), title, info, creator, 0, shared, 0, guid, autoLoad ) )
  76. {
  77. Q_D( DynamicPlaylist );
  78. d->generator = geninterface_ptr( GeneratorFactory::create( type ) );
  79. d->generator->setMode( mode );
  80. }
  81. geninterface_ptr
  82. DynamicPlaylist::generator() const
  83. {
  84. Q_D( const DynamicPlaylist );
  85. return d->generator;
  86. }
  87. int
  88. DynamicPlaylist::mode() const
  89. {
  90. Q_D( const DynamicPlaylist );
  91. return d->generator->mode();
  92. }
  93. void
  94. DynamicPlaylist::setGenerator( const Tomahawk::geninterface_ptr& gen_ptr )
  95. {
  96. Q_D( DynamicPlaylist );
  97. d->generator = gen_ptr;
  98. }
  99. QString
  100. DynamicPlaylist::type() const
  101. {
  102. Q_D( const DynamicPlaylist );
  103. return d->generator->type();
  104. }
  105. void
  106. DynamicPlaylist::setMode( int mode )
  107. {
  108. Q_D( DynamicPlaylist );
  109. d->generator->setMode( (GeneratorMode)mode );
  110. }
  111. dynplaylist_ptr
  112. DynamicPlaylist::get( const QString& guid )
  113. {
  114. dynplaylist_ptr p;
  115. foreach( const Tomahawk::source_ptr& source, SourceList::instance()->sources() )
  116. {
  117. p = source->dbCollection()->autoPlaylist( guid );
  118. if ( !p )
  119. p = source->dbCollection()->station( guid );
  120. if ( p )
  121. return p;
  122. }
  123. return p;
  124. }
  125. dynplaylist_ptr
  126. DynamicPlaylist::create( const Tomahawk::source_ptr& author,
  127. const QString& guid,
  128. const QString& title,
  129. const QString& info,
  130. const QString& creator,
  131. GeneratorMode mode,
  132. bool shared,
  133. const QString& type,
  134. bool autoLoad
  135. )
  136. {
  137. dynplaylist_ptr dynplaylist = Tomahawk::dynplaylist_ptr( new DynamicPlaylist( author, guid, title, info, creator, type, mode, shared, autoLoad ), &QObject::deleteLater );
  138. dynplaylist->setWeakSelf( dynplaylist.toWeakRef() );
  139. DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( author, dynplaylist, autoLoad );
  140. connect( cmd, SIGNAL(finished()), dynplaylist.data(), SIGNAL(created()) );
  141. Database::instance()->enqueue( Tomahawk::dbcmd_ptr(cmd) );
  142. if ( autoLoad )
  143. dynplaylist->reportCreated( dynplaylist );
  144. return dynplaylist;
  145. }
  146. void
  147. DynamicPlaylist::setWeakSelf( QWeakPointer< DynamicPlaylist > self )
  148. {
  149. Q_D( DynamicPlaylist );
  150. d->weakSelf = self;
  151. }
  152. void
  153. DynamicPlaylist::createNewRevision( const QString& newUuid )
  154. {
  155. if ( mode() == Static )
  156. {
  157. createNewRevision( newUuid.isEmpty() ? uuid() : newUuid, currentrevision(), type(), generator()->controls(), entries() );
  158. }
  159. else if ( mode() == OnDemand )
  160. {
  161. createNewRevision( newUuid.isEmpty() ? uuid() : newUuid, currentrevision(), type(), generator()->controls() );
  162. }
  163. }
  164. // create a new revision that will be a static playlist, as it has entries
  165. void
  166. DynamicPlaylist::createNewRevision( const QString& newrev,
  167. const QString& oldrev,
  168. const QString& type,
  169. const QList< dyncontrol_ptr>& controls,
  170. const QList< plentry_ptr >& entries )
  171. {
  172. Q_D( DynamicPlaylist );
  173. Q_ASSERT( Playlist::d_func()->source->isLocal() || newrev == oldrev );
  174. if ( busy() )
  175. {
  176. d->revisionQueue.enqueue( DynQueueItem( newrev, oldrev, type, controls, (int)Static, entries, oldrev == currentrevision() ) );
  177. return;
  178. }
  179. setBusy( true );
  180. // get the newly added tracks
  181. QList< plentry_ptr > added = newEntries( entries );
  182. QStringList orderedguids;
  183. for( int i = 0; i < entries.size(); ++i )
  184. orderedguids << entries.at(i)->guid();
  185. // no conflict resolution or partial updating for controls. all or nothing baby
  186. // source making the change (local user in this case)
  187. source_ptr author = SourceList::instance()->getLocal();
  188. // command writes new rev to DB and calls setRevision, which emits our signal
  189. DatabaseCommand_SetDynamicPlaylistRevision* cmd =
  190. new DatabaseCommand_SetDynamicPlaylistRevision( author,
  191. guid(),
  192. newrev,
  193. oldrev,
  194. orderedguids,
  195. added,
  196. entries,
  197. type,
  198. Static,
  199. controls );
  200. if ( !d->autoLoad )
  201. cmd->setPlaylist( d->weakSelf );
  202. connect( cmd, SIGNAL( finished() ),
  203. this, SLOT( setPlaylistRevisionFinished() ) );
  204. if ( d->queuedSetPlaylistRevision )
  205. {
  206. d->queuedSetPlaylistRevisionCmds.enqueue( cmd );
  207. }
  208. else
  209. {
  210. d->queuedSetPlaylistRevision = true;
  211. Database::instance()->enqueue( Tomahawk::dbcmd_ptr( cmd ) );
  212. }
  213. }
  214. // create a new revision that will be an ondemand playlist, as it has no entries
  215. void
  216. DynamicPlaylist::createNewRevision( const QString& newrev,
  217. const QString& oldrev,
  218. const QString& type,
  219. const QList< dyncontrol_ptr>& controls )
  220. {
  221. Q_D( DynamicPlaylist );
  222. Q_ASSERT( Playlist::d_func()->source->isLocal() || newrev == oldrev );
  223. if ( busy() )
  224. {
  225. d->revisionQueue.enqueue( DynQueueItem( newrev, oldrev, type, controls, (int)OnDemand, QList< plentry_ptr >(), oldrev == currentrevision() ) );
  226. return;
  227. }
  228. setBusy( true );
  229. // can skip the entry stuff. just overwrite with new info
  230. source_ptr author = SourceList::instance()->getLocal();
  231. // command writes new rev to DB and calls setRevision, which emits our signal
  232. DatabaseCommand_SetDynamicPlaylistRevision* cmd =
  233. new DatabaseCommand_SetDynamicPlaylistRevision( author,
  234. guid(),
  235. newrev,
  236. oldrev,
  237. type,
  238. OnDemand,
  239. controls );
  240. if ( !d->autoLoad )
  241. cmd->setPlaylist( d->weakSelf );
  242. connect( cmd, SIGNAL( finished() ),
  243. this, SLOT( setPlaylistRevisionFinished() ) );
  244. if ( d->queuedSetPlaylistRevision )
  245. {
  246. d->queuedSetPlaylistRevisionCmds.enqueue( cmd );
  247. }
  248. else
  249. {
  250. d->queuedSetPlaylistRevision = true;
  251. Database::instance()->enqueue( Tomahawk::dbcmd_ptr( cmd ) );
  252. }
  253. }
  254. void
  255. DynamicPlaylist::loadRevision( const QString& rev )
  256. {
  257. Q_D( DynamicPlaylist );
  258. //tDebug() << Q_FUNC_INFO << "Loading with:" << ( rev.isEmpty() ? currentrevision() : rev );
  259. setBusy( true );
  260. DatabaseCommand_LoadDynamicPlaylistEntries* cmd = new DatabaseCommand_LoadDynamicPlaylistEntries( rev.isEmpty() ? currentrevision() : rev );
  261. if ( d->generator->mode() == OnDemand )
  262. {
  263. connect( cmd, SIGNAL( done( QString,
  264. bool,
  265. QString,
  266. QList< QVariantMap >,
  267. bool ) ),
  268. SLOT( setRevision( QString,
  269. bool,
  270. QString,
  271. QList< QVariantMap >,
  272. bool ) ) );
  273. }
  274. else if ( d->generator->mode() == Static )
  275. {
  276. connect( cmd, SIGNAL( done( QString,
  277. QList< QString >,
  278. QList< QString >,
  279. QString,
  280. QList< QVariantMap >,
  281. bool,
  282. QMap< QString, Tomahawk::plentry_ptr >,
  283. bool ) ),
  284. SLOT( setRevision( QString,
  285. QList< QString >,
  286. QList< QString >,
  287. QString,
  288. QList< QVariantMap >,
  289. bool,
  290. QMap< QString, Tomahawk::plentry_ptr >,
  291. bool ) ) );
  292. }
  293. else
  294. Q_ASSERT( false );
  295. Database::instance()->enqueue( Tomahawk::dbcmd_ptr( cmd ) );
  296. }
  297. void
  298. DynamicPlaylist::reportCreated( const Tomahawk::dynplaylist_ptr& self )
  299. {
  300. // qDebug() << Q_FUNC_INFO;
  301. Q_ASSERT( self.data() == this );
  302. Q_ASSERT( !author().isNull() );
  303. Q_ASSERT( !author()->dbCollection().isNull() );
  304. // will emit Collection::playlistCreated(...)
  305. // qDebug() << "Creating dynplaylist belonging to:" << author().data() << author().isNull();
  306. // qDebug() << "REPORTING DYNAMIC PLAYLIST CREATED:" << this << author()->friendlyName();
  307. if ( self->mode() == Static )
  308. author()->dbCollection()->addAutoPlaylist( self );
  309. else
  310. author()->dbCollection()->addStation( self );
  311. }
  312. void
  313. DynamicPlaylist::reportDeleted( const Tomahawk::dynplaylist_ptr& self )
  314. {
  315. // qDebug() << Q_FUNC_INFO;
  316. Q_ASSERT( self.data() == this );
  317. // will emit Collection::playlistDeleted(...)
  318. if ( self->mode() == Static )
  319. author()->dbCollection()->deleteAutoPlaylist( self );
  320. else
  321. author()->dbCollection()->deleteStation( self );
  322. emit deleted( self );
  323. }
  324. void
  325. DynamicPlaylist::addEntries( const QList< query_ptr >& queries )
  326. {
  327. Q_D( DynamicPlaylist );
  328. Q_ASSERT( d->generator->mode() == Static );
  329. QList<plentry_ptr> el = entriesFromQueries( queries );
  330. QString newrev = uuid();
  331. createNewRevision( newrev, Playlist::d_func()->currentrevision, d->generator->type(), d->generator->controls(), el );
  332. }
  333. void
  334. DynamicPlaylist::addEntry( const Tomahawk::query_ptr& query )
  335. {
  336. QList<query_ptr> queries;
  337. queries << query;
  338. addEntries( queries );
  339. }
  340. void
  341. DynamicPlaylist::setRevision( const QString& rev,
  342. const QList< QString >& neworderedguids,
  343. const QList< QString >& oldorderedguids,
  344. const QString& type,
  345. const QList< dyncontrol_ptr >& controls,
  346. bool is_newest_rev,
  347. const QMap< QString, plentry_ptr >& addedmap,
  348. bool applied )
  349. {
  350. Q_D( DynamicPlaylist );
  351. // we're probably being called by a database worker thread
  352. if ( QThread::currentThread() != thread() )
  353. {
  354. QMetaObject::invokeMethod( this,
  355. "setRevision",
  356. Qt::BlockingQueuedConnection,
  357. Q_ARG( QString, rev ),
  358. Q_ARG( QList<QString> , neworderedguids ),
  359. Q_ARG( QList<QString> , oldorderedguids ),
  360. Q_ARG( QString , type ),
  361. QGenericArgument( "QList< Tomahawk::dyncontrol_ptr > " , (const void*)&controls ),
  362. Q_ARG( bool, is_newest_rev ),
  363. QGenericArgument( "QMap< QString,Tomahawk::plentry_ptr > " , (const void*)&addedmap ),
  364. Q_ARG( bool, applied ) );
  365. return;
  366. }
  367. if ( d->generator->type() != type ) { // new generator needed
  368. d->generator = GeneratorFactory::create( type );
  369. }
  370. d->generator->setControls( controls );
  371. d->generator->setMode( Static );
  372. DynamicPlaylistRevision dpr = setNewRevision( rev, neworderedguids, oldorderedguids, is_newest_rev, addedmap );
  373. dpr.applied = applied;
  374. dpr.controls = controls;
  375. dpr.type = type;
  376. dpr.mode = Static;
  377. if ( applied )
  378. setCurrentrevision( rev );
  379. //qDebug() << "EMITTING REVISION LOADED!";
  380. setBusy( false );
  381. setLoaded( true );
  382. emit dynamicRevisionLoaded( dpr );
  383. }
  384. void
  385. DynamicPlaylist::setRevision( const QString& rev,
  386. const QList< QString >& neworderedguids,
  387. const QList< QString >& oldorderedguids,
  388. const QString& type,
  389. const QList< QVariantMap>& controlsV,
  390. bool is_newest_rev,
  391. const QMap< QString, Tomahawk::plentry_ptr >& addedmap,
  392. bool applied )
  393. {
  394. if ( QThread::currentThread() != thread() )
  395. {
  396. QMetaObject::invokeMethod( this,
  397. "setRevision",
  398. Qt::BlockingQueuedConnection,
  399. Q_ARG( QString, rev ),
  400. Q_ARG( QList<QString> , neworderedguids ),
  401. Q_ARG( QList<QString> , oldorderedguids ),
  402. Q_ARG( QString , type ),
  403. QGenericArgument( "QList< QVariantMap > " , (const void*)&controlsV ),
  404. Q_ARG( bool, is_newest_rev ),
  405. QGenericArgument( "QMap< QString,Tomahawk::plentry_ptr > " , (const void*)&addedmap ),
  406. Q_ARG( bool, applied ) );
  407. return;
  408. }
  409. QList<dyncontrol_ptr> controls = variantsToControl( controlsV );
  410. setRevision( rev, neworderedguids, oldorderedguids, type, controls, is_newest_rev, addedmap, applied );
  411. }
  412. void
  413. DynamicPlaylist::setRevision( const QString& rev,
  414. bool is_newest_rev,
  415. const QString& type,
  416. const QList< dyncontrol_ptr >& controls,
  417. bool applied )
  418. {
  419. Q_D( DynamicPlaylist );
  420. if ( QThread::currentThread() != thread() )
  421. {
  422. QMetaObject::invokeMethod( this,
  423. "setRevision",
  424. Qt::BlockingQueuedConnection,
  425. Q_ARG( QString, rev ),
  426. Q_ARG( bool, is_newest_rev ),
  427. Q_ARG( QString, type ),
  428. QGenericArgument( "QList< Tomahawk::dyncontrol_ptr >" , (const void*)&controls ),
  429. Q_ARG( bool, applied ) );
  430. return;
  431. }
  432. if ( d->generator->type() != type )
  433. {
  434. // new generator needed
  435. d->generator = geninterface_ptr( GeneratorFactory::create( type ) );
  436. }
  437. d->generator->setControls( controls );
  438. d->generator->setMode( OnDemand );
  439. DynamicPlaylistRevision dpr;
  440. dpr.oldrevisionguid = currentrevision();
  441. dpr.revisionguid = rev;
  442. dpr.controls = controls;
  443. dpr.type = type;
  444. dpr.mode = OnDemand;
  445. if ( applied )
  446. setCurrentrevision( rev );
  447. // qDebug() << "EMITTING REVISION LOADED 2!";
  448. setBusy( false );
  449. setLoaded( true );
  450. emit dynamicRevisionLoaded( dpr );
  451. }
  452. void
  453. DynamicPlaylist::removeFromDatabase()
  454. {
  455. Q_D( DynamicPlaylist );
  456. emit aboutToBeDeleted( d->weakSelf.toStrongRef() );
  457. DatabaseCommand_DeletePlaylist* cmd = new DatabaseCommand_DeleteDynamicPlaylist( author(), guid() ) ;
  458. Database::instance()->enqueue( Tomahawk::dbcmd_ptr( cmd ) );
  459. }
  460. void
  461. DynamicPlaylist::setRevision( const QString& rev,
  462. bool is_newest_rev,
  463. const QString& type,
  464. const QList< QVariantMap >& controlsV,
  465. bool applied )
  466. {
  467. if ( QThread::currentThread() != thread() )
  468. {
  469. QMetaObject::invokeMethod( this,
  470. "setRevision",
  471. Qt::BlockingQueuedConnection,
  472. Q_ARG( QString, rev ),
  473. Q_ARG( bool, is_newest_rev ),
  474. Q_ARG( QString, type ),
  475. QGenericArgument( "QList< QVariantMap >" , (const void*)&controlsV ),
  476. Q_ARG( bool, applied ) );
  477. return;
  478. }
  479. QList<dyncontrol_ptr> controls = variantsToControl( controlsV );
  480. setRevision( rev, is_newest_rev, type, controls, applied );
  481. }
  482. QList< dyncontrol_ptr >
  483. DynamicPlaylist::variantsToControl( const QList< QVariantMap >& controlsV )
  484. {
  485. QList<dyncontrol_ptr> realControls;
  486. foreach( QVariantMap controlV, controlsV )
  487. {
  488. dyncontrol_ptr control = GeneratorFactory::createControl( controlV.value( "type" ).toString(), controlV.value( "selectedType" ).toString() );
  489. // qDebug() << "Creating control with data:" << controlV;
  490. if ( control )
  491. {
  492. TomahawkUtils::qvariant2qobject( controlV, control.data() );
  493. realControls << control;
  494. }
  495. }
  496. return realControls;
  497. }
  498. void
  499. DynamicPlaylist::checkRevisionQueue()
  500. {
  501. Q_D( DynamicPlaylist );
  502. if ( !d->revisionQueue.isEmpty() )
  503. {
  504. DynQueueItem item = d->revisionQueue.dequeue();
  505. if ( item.oldRev != currentrevision() && item.applyToTip )
  506. {
  507. // this was applied to the then-latest, but the already-running operation changed it so it's out of date now. fix it
  508. if ( item.oldRev == item.newRev )
  509. {
  510. checkRevisionQueue();
  511. return;
  512. }
  513. item.oldRev = currentrevision();
  514. }
  515. if ( item.mode == Static )
  516. createNewRevision( item.newRev, item.oldRev, item.type, item.controls, item.entries );
  517. else
  518. createNewRevision( item.newRev, item.oldRev, item.type, item.controls );
  519. }
  520. }
  521. bool
  522. DynamicPlaylist::autoLoad() const
  523. {
  524. Q_D( const DynamicPlaylist );
  525. return d->autoLoad;
  526. }