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