/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp
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
- /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
- *
- * Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
- * Copyright 2010-2011, Jeff Mitchell <jeff@tomahawk-player.org>
- *
- * Tomahawk is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Tomahawk is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
- */
- #include "DynamicPlaylist_p.h"
- #include "collection/Collection.h"
- #include "database/Database.h"
- #include "database/DatabaseCommand.h"
- #include "database/DatabaseCommand_CreateDynamicPlaylist.h"
- #include "database/DatabaseCommand_SetDynamicPlaylistRevision.h"
- #include "database/DatabaseCommand_LoadDynamicPlaylistEntries.h"
- #include "database/DatabaseCommand_DeleteDynamicPlaylist.h"
- #include "utils/Json.h"
- #include "utils/Logger.h"
- #include "GeneratorFactory.h"
- #include "Playlist_p.h"
- #include "PlaylistInterface.h"
- #include "PlaylistEntry.h"
- #include "PlaylistInterface.h"
- #include "SourceList.h"
- using namespace Tomahawk;
- DynamicPlaylist::DynamicPlaylist( const Tomahawk::source_ptr& author, const QString& type )
- : Playlist( new DynamicPlaylistPrivate( this, author ) )
- {
- Q_D( DynamicPlaylist );
- qDebug() << Q_FUNC_INFO << "JSON";
- d->generator = geninterface_ptr( GeneratorFactory::create( type ) );
- }
- DynamicPlaylist::~DynamicPlaylist()
- {
- }
- // Called by loadAllPlaylists command
- DynamicPlaylist::DynamicPlaylist ( const Tomahawk::source_ptr& src,
- const QString& currentrevision,
- const QString& title,
- const QString& info,
- const QString& creator,
- uint createdOn,
- const QString& type,
- GeneratorMode mode,
- bool shared,
- int lastmod,
- const QString& guid )
- : Playlist( new DynamicPlaylistPrivate( this, src, currentrevision, title, info, creator, createdOn, shared, lastmod, guid, false ) )
- {
- // TODO instantiate generator
- Q_D( DynamicPlaylist );
- d->generator = geninterface_ptr( GeneratorFactory::create( type ) );
- d->generator->setMode( mode );
- }
- // called when a new playlist is created (no currentrevision, new guid)
- DynamicPlaylist::DynamicPlaylist( const Tomahawk::source_ptr& author,
- const QString& guid,
- const QString& title,
- const QString& info,
- const QString& creator,
- const QString& type,
- GeneratorMode mode,
- bool shared,
- bool autoLoad )
- : Playlist( new DynamicPlaylistPrivate( this, author, QString(), title, info, creator, 0, shared, 0, guid, autoLoad ) )
- {
- Q_D( DynamicPlaylist );
- d->generator = geninterface_ptr( GeneratorFactory::create( type ) );
- d->generator->setMode( mode );
- }
- geninterface_ptr
- DynamicPlaylist::generator() const
- {
- Q_D( const DynamicPlaylist );
- return d->generator;
- }
- int
- DynamicPlaylist::mode() const
- {
- Q_D( const DynamicPlaylist );
- return d->generator->mode();
- }
- void
- DynamicPlaylist::setGenerator( const Tomahawk::geninterface_ptr& gen_ptr )
- {
- Q_D( DynamicPlaylist );
- d->generator = gen_ptr;
- }
- QString
- DynamicPlaylist::type() const
- {
- Q_D( const DynamicPlaylist );
- return d->generator->type();
- }
- void
- DynamicPlaylist::setMode( int mode )
- {
- Q_D( DynamicPlaylist );
- d->generator->setMode( (GeneratorMode)mode );
- }
- dynplaylist_ptr
- DynamicPlaylist::get( const QString& guid )
- {
- dynplaylist_ptr p;
- foreach( const Tomahawk::source_ptr& source, SourceList::instance()->sources() )
- {
- p = source->dbCollection()->autoPlaylist( guid );
- if ( !p )
- p = source->dbCollection()->station( guid );
- if ( p )
- return p;
- }
- return p;
- }
- dynplaylist_ptr
- DynamicPlaylist::create( const Tomahawk::source_ptr& author,
- const QString& guid,
- const QString& title,
- const QString& info,
- const QString& creator,
- GeneratorMode mode,
- bool shared,
- const QString& type,
- bool autoLoad
- )
- {
- dynplaylist_ptr dynplaylist = Tomahawk::dynplaylist_ptr( new DynamicPlaylist( author, guid, title, info, creator, type, mode, shared, autoLoad ), &QObject::deleteLater );
- dynplaylist->setWeakSelf( dynplaylist.toWeakRef() );
- DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( author, dynplaylist, autoLoad );
- connect( cmd, SIGNAL(finished()), dynplaylist.data(), SIGNAL(created()) );
- Database::instance()->enqueue( Tomahawk::dbcmd_ptr(cmd) );
- if ( autoLoad )
- dynplaylist->reportCreated( dynplaylist );
- return dynplaylist;
- }
- void
- DynamicPlaylist::setWeakSelf( QWeakPointer< DynamicPlaylist > self )
- {
- Q_D( DynamicPlaylist );
- d->weakSelf = self;
- }
- void
- DynamicPlaylist::createNewRevision( const QString& newUuid )
- {
- if ( mode() == Static )
- {
- createNewRevision( newUuid.isEmpty() ? uuid() : newUuid, currentrevision(), type(), generator()->controls(), entries() );
- }
- else if ( mode() == OnDemand )
- {
- createNewRevision( newUuid.isEmpty() ? uuid() : newUuid, currentrevision(), type(), generator()->controls() );
- }
- }
- // create a new revision that will be a static playlist, as it has entries
- void
- DynamicPlaylist::createNewRevision( const QString& newrev,
- const QString& oldrev,
- const QString& type,
- const QList< dyncontrol_ptr>& controls,
- const QList< plentry_ptr >& entries )
- {
- Q_D( DynamicPlaylist );
- Q_ASSERT( Playlist::d_func()->source->isLocal() || newrev == oldrev );
- if ( busy() )
- {
- d->revisionQueue.enqueue( DynQueueItem( newrev, oldrev, type, controls, (int)Static, entries, oldrev == currentrevision() ) );
- return;
- }
- setBusy( true );
- // get the newly added tracks
- QList< plentry_ptr > added = newEntries( entries );
- QStringList orderedguids;
- for( int i = 0; i < entries.size(); ++i )
- orderedguids << entries.at(i)->guid();
- // no conflict resolution or partial updating for controls. all or nothing baby
- // source making the change (local user in this case)
- source_ptr author = SourceList::instance()->getLocal();
- // command writes new rev to DB and calls setRevision, which emits our signal
- DatabaseCommand_SetDynamicPlaylistRevision* cmd =
- new DatabaseCommand_SetDynamicPlaylistRevision( author,
- guid(),
- newrev,
- oldrev,
- orderedguids,
- added,
- entries,
- type,
- Static,
- controls );
- if ( !d->autoLoad )
- cmd->setPlaylist( d->weakSelf );
- connect( cmd, SIGNAL( finished() ),
- this, SLOT( setPlaylistRevisionFinished() ) );
- if ( d->queuedSetPlaylistRevision )
- {
- d->queuedSetPlaylistRevisionCmds.enqueue( cmd );
- }
- else
- {
- d->queuedSetPlaylistRevision = true;
- Database::instance()->enqueue( Tomahawk::dbcmd_ptr( cmd ) );
- }
- }
- // create a new revision that will be an ondemand playlist, as it has no entries
- void
- DynamicPlaylist::createNewRevision( const QString& newrev,
- const QString& oldrev,
- const QString& type,
- const QList< dyncontrol_ptr>& controls )
- {
- Q_D( DynamicPlaylist );
- Q_ASSERT( Playlist::d_func()->source->isLocal() || newrev == oldrev );
- if ( busy() )
- {
- d->revisionQueue.enqueue( DynQueueItem( newrev, oldrev, type, controls, (int)OnDemand, QList< plentry_ptr >(), oldrev == currentrevision() ) );
- return;
- }
- setBusy( true );
- // can skip the entry stuff. just overwrite with new info
- source_ptr author = SourceList::instance()->getLocal();
- // command writes new rev to DB and calls setRevision, which emits our signal
- DatabaseCommand_SetDynamicPlaylistRevision* cmd =
- new DatabaseCommand_SetDynamicPlaylistRevision( author,
- guid(),
- newrev,
- oldrev,
- type,
- OnDemand,
- controls );
- if ( !d->autoLoad )
- cmd->setPlaylist( d->weakSelf );
- connect( cmd, SIGNAL( finished() ),
- this, SLOT( setPlaylistRevisionFinished() ) );
- if ( d->queuedSetPlaylistRevision )
- {
- d->queuedSetPlaylistRevisionCmds.enqueue( cmd );
- }
- else
- {
- d->queuedSetPlaylistRevision = true;
- Database::instance()->enqueue( Tomahawk::dbcmd_ptr( cmd ) );
- }
- }
- void
- DynamicPlaylist::loadRevision( const QString& rev )
- {
- Q_D( DynamicPlaylist );
- //tDebug() << Q_FUNC_INFO << "Loading with:" << ( rev.isEmpty() ? currentrevision() : rev );
- setBusy( true );
- DatabaseCommand_LoadDynamicPlaylistEntries* cmd = new DatabaseCommand_LoadDynamicPlaylistEntries( rev.isEmpty() ? currentrevision() : rev );
- if ( d->generator->mode() == OnDemand )
- {
- connect( cmd, SIGNAL( done( QString,
- bool,
- QString,
- QList< QVariantMap >,
- bool ) ),
- SLOT( setRevision( QString,
- bool,
- QString,
- QList< QVariantMap >,
- bool ) ) );
- }
- else if ( d->generator->mode() == Static )
- {
- connect( cmd, SIGNAL( done( QString,
- QList< QString >,
- QList< QString >,
- QString,
- QList< QVariantMap >,
- bool,
- QMap< QString, Tomahawk::plentry_ptr >,
- bool ) ),
- SLOT( setRevision( QString,
- QList< QString >,
- QList< QString >,
- QString,
- QList< QVariantMap >,
- bool,
- QMap< QString, Tomahawk::plentry_ptr >,
- bool ) ) );
- }
- else
- Q_ASSERT( false );
- Database::instance()->enqueue( Tomahawk::dbcmd_ptr( cmd ) );
- }
- void
- DynamicPlaylist::reportCreated( const Tomahawk::dynplaylist_ptr& self )
- {
- // qDebug() << Q_FUNC_INFO;
- Q_ASSERT( self.data() == this );
- Q_ASSERT( !author().isNull() );
- Q_ASSERT( !author()->dbCollection().isNull() );
- // will emit Collection::playlistCreated(...)
- // qDebug() << "Creating dynplaylist belonging to:" << author().data() << author().isNull();
- // qDebug() << "REPORTING DYNAMIC PLAYLIST CREATED:" << this << author()->friendlyName();
- if ( self->mode() == Static )
- author()->dbCollection()->addAutoPlaylist( self );
- else
- author()->dbCollection()->addStation( self );
- }
- void
- DynamicPlaylist::reportDeleted( const Tomahawk::dynplaylist_ptr& self )
- {
- // qDebug() << Q_FUNC_INFO;
- Q_ASSERT( self.data() == this );
- // will emit Collection::playlistDeleted(...)
- if ( self->mode() == Static )
- author()->dbCollection()->deleteAutoPlaylist( self );
- else
- author()->dbCollection()->deleteStation( self );
- emit deleted( self );
- }
- void
- DynamicPlaylist::addEntries( const QList< query_ptr >& queries )
- {
- Q_D( DynamicPlaylist );
- Q_ASSERT( d->generator->mode() == Static );
- QList<plentry_ptr> el = entriesFromQueries( queries );
- QString newrev = uuid();
- createNewRevision( newrev, Playlist::d_func()->currentrevision, d->generator->type(), d->generator->controls(), el );
- }
- void
- DynamicPlaylist::addEntry( const Tomahawk::query_ptr& query )
- {
- QList<query_ptr> queries;
- queries << query;
- addEntries( queries );
- }
- void
- DynamicPlaylist::setRevision( const QString& rev,
- const QList< QString >& neworderedguids,
- const QList< QString >& oldorderedguids,
- const QString& type,
- const QList< dyncontrol_ptr >& controls,
- bool is_newest_rev,
- const QMap< QString, plentry_ptr >& addedmap,
- bool applied )
- {
- Q_D( DynamicPlaylist );
- // we're probably being called by a database worker thread
- if ( QThread::currentThread() != thread() )
- {
- QMetaObject::invokeMethod( this,
- "setRevision",
- Qt::BlockingQueuedConnection,
- Q_ARG( QString, rev ),
- Q_ARG( QList<QString> , neworderedguids ),
- Q_ARG( QList<QString> , oldorderedguids ),
- Q_ARG( QString , type ),
- QGenericArgument( "QList< Tomahawk::dyncontrol_ptr > " , (const void*)&controls ),
- Q_ARG( bool, is_newest_rev ),
- QGenericArgument( "QMap< QString,Tomahawk::plentry_ptr > " , (const void*)&addedmap ),
- Q_ARG( bool, applied ) );
- return;
- }
- if ( d->generator->type() != type ) { // new generator needed
- d->generator = GeneratorFactory::create( type );
- }
- d->generator->setControls( controls );
- d->generator->setMode( Static );
- DynamicPlaylistRevision dpr = setNewRevision( rev, neworderedguids, oldorderedguids, is_newest_rev, addedmap );
- dpr.applied = applied;
- dpr.controls = controls;
- dpr.type = type;
- dpr.mode = Static;
- if ( applied )
- setCurrentrevision( rev );
- //qDebug() << "EMITTING REVISION LOADED!";
- setBusy( false );
- setLoaded( true );
- emit dynamicRevisionLoaded( dpr );
- }
- void
- DynamicPlaylist::setRevision( const QString& rev,
- const QList< QString >& neworderedguids,
- const QList< QString >& oldorderedguids,
- const QString& type,
- const QList< QVariantMap>& controlsV,
- bool is_newest_rev,
- const QMap< QString, Tomahawk::plentry_ptr >& addedmap,
- bool applied )
- {
- if ( QThread::currentThread() != thread() )
- {
- QMetaObject::invokeMethod( this,
- "setRevision",
- Qt::BlockingQueuedConnection,
- Q_ARG( QString, rev ),
- Q_ARG( QList<QString> , neworderedguids ),
- Q_ARG( QList<QString> , oldorderedguids ),
- Q_ARG( QString , type ),
- QGenericArgument( "QList< QVariantMap > " , (const void*)&controlsV ),
- Q_ARG( bool, is_newest_rev ),
- QGenericArgument( "QMap< QString,Tomahawk::plentry_ptr > " , (const void*)&addedmap ),
- Q_ARG( bool, applied ) );
- return;
- }
- QList<dyncontrol_ptr> controls = variantsToControl( controlsV );
- setRevision( rev, neworderedguids, oldorderedguids, type, controls, is_newest_rev, addedmap, applied );
- }
- void
- DynamicPlaylist::setRevision( const QString& rev,
- bool is_newest_rev,
- const QString& type,
- const QList< dyncontrol_ptr >& controls,
- bool applied )
- {
- Q_D( DynamicPlaylist );
- if ( QThread::currentThread() != thread() )
- {
- QMetaObject::invokeMethod( this,
- "setRevision",
- Qt::BlockingQueuedConnection,
- Q_ARG( QString, rev ),
- Q_ARG( bool, is_newest_rev ),
- Q_ARG( QString, type ),
- QGenericArgument( "QList< Tomahawk::dyncontrol_ptr >" , (const void*)&controls ),
- Q_ARG( bool, applied ) );
- return;
- }
- if ( d->generator->type() != type )
- {
- // new generator needed
- d->generator = geninterface_ptr( GeneratorFactory::create( type ) );
- }
- d->generator->setControls( controls );
- d->generator->setMode( OnDemand );
- DynamicPlaylistRevision dpr;
- dpr.oldrevisionguid = currentrevision();
- dpr.revisionguid = rev;
- dpr.controls = controls;
- dpr.type = type;
- dpr.mode = OnDemand;
- if ( applied )
- setCurrentrevision( rev );
- // qDebug() << "EMITTING REVISION LOADED 2!";
- setBusy( false );
- setLoaded( true );
- emit dynamicRevisionLoaded( dpr );
- }
- void
- DynamicPlaylist::removeFromDatabase()
- {
- Q_D( DynamicPlaylist );
- emit aboutToBeDeleted( d->weakSelf.toStrongRef() );
- DatabaseCommand_DeletePlaylist* cmd = new DatabaseCommand_DeleteDynamicPlaylist( author(), guid() ) ;
- Database::instance()->enqueue( Tomahawk::dbcmd_ptr( cmd ) );
- }
- void
- DynamicPlaylist::setRevision( const QString& rev,
- bool is_newest_rev,
- const QString& type,
- const QList< QVariantMap >& controlsV,
- bool applied )
- {
- if ( QThread::currentThread() != thread() )
- {
- QMetaObject::invokeMethod( this,
- "setRevision",
- Qt::BlockingQueuedConnection,
- Q_ARG( QString, rev ),
- Q_ARG( bool, is_newest_rev ),
- Q_ARG( QString, type ),
- QGenericArgument( "QList< QVariantMap >" , (const void*)&controlsV ),
- Q_ARG( bool, applied ) );
- return;
- }
- QList<dyncontrol_ptr> controls = variantsToControl( controlsV );
- setRevision( rev, is_newest_rev, type, controls, applied );
- }
- QList< dyncontrol_ptr >
- DynamicPlaylist::variantsToControl( const QList< QVariantMap >& controlsV )
- {
- QList<dyncontrol_ptr> realControls;
- foreach( QVariantMap controlV, controlsV )
- {
- dyncontrol_ptr control = GeneratorFactory::createControl( controlV.value( "type" ).toString(), controlV.value( "selectedType" ).toString() );
- // qDebug() << "Creating control with data:" << controlV;
- if ( control )
- {
- TomahawkUtils::qvariant2qobject( controlV, control.data() );
- realControls << control;
- }
- }
- return realControls;
- }
- void
- DynamicPlaylist::checkRevisionQueue()
- {
- Q_D( DynamicPlaylist );
- if ( !d->revisionQueue.isEmpty() )
- {
- DynQueueItem item = d->revisionQueue.dequeue();
- if ( item.oldRev != currentrevision() && item.applyToTip )
- {
- // this was applied to the then-latest, but the already-running operation changed it so it's out of date now. fix it
- if ( item.oldRev == item.newRev )
- {
- checkRevisionQueue();
- return;
- }
- item.oldRev = currentrevision();
- }
- if ( item.mode == Static )
- createNewRevision( item.newRev, item.oldRev, item.type, item.controls, item.entries );
- else
- createNewRevision( item.newRev, item.oldRev, item.type, item.controls );
- }
- }
- bool
- DynamicPlaylist::autoLoad() const
- {
- Q_D( const DynamicPlaylist );
- return d->autoLoad;
- }