1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-04-13 12:31:52 +02:00

first saving non-working commit

This commit is contained in:
Leo Franchi 2010-12-02 22:15:43 -05:00
parent b45c254081
commit 5b23b6ad81
11 changed files with 609 additions and 97 deletions

View File

@ -118,7 +118,7 @@ public:
// these need to exist and be public for the json serialization stuff
// you SHOULD NOT call them. They are used for an alternate CTOR method from json.
// maybe friend QObjectHelper and make them private?
Playlist( const source_ptr& author )
explicit Playlist( const source_ptr& author )
: m_source( author )
, m_lastmodified( 0 )
{
@ -155,7 +155,7 @@ public slots:
void resolve();
private:
protected:
// called from loadAllPlaylists DB cmd:
explicit Playlist( const source_ptr& src,
const QString& currentrevision,
@ -173,9 +173,14 @@ private:
const QString& info,
const QString& creator,
bool shared );
void rundb();
QList<plentry_ptr> newEntries( const QList< plentry_ptr >& entries );
PlaylistRevision setNewRevision( const QString& rev,
const QList<QString>& neworderedguids,
const QList<QString>& oldorderedguids,
bool is_newest_rev,
const QMap< QString, Tomahawk::plentry_ptr >& addedmap );
private:
source_ptr m_source;
QString m_currentrevision;
QString m_guid, m_title, m_info, m_creator;

View File

@ -11,6 +11,7 @@ namespace Tomahawk
class Collection;
class Playlist;
class PlaylistEntry;
class DynamicPlaylist;
class Query;
class Result;
class Source;
@ -18,6 +19,7 @@ namespace Tomahawk
typedef QSharedPointer<Collection> collection_ptr;
typedef QSharedPointer<Playlist> playlist_ptr;
typedef QSharedPointer<PlaylistEntry> plentry_ptr;
typedef QSharedPointer<DynamicPlaylist> dynplaylist_ptr;
typedef QSharedPointer<Query> query_ptr;
typedef QSharedPointer<Result> result_ptr;
typedef QSharedPointer<Source> source_ptr;

View File

@ -91,6 +91,10 @@ SET( tomahawkSources ${tomahawkSources}
database/databasecommand_updatesearchindex.cpp
database/databasecollection.cpp
dynamic/dynamicplaylist.cpp
dynamic/dynamiccontrol.cpp
dynamic/generatorfactory.cpp
scrobbler.cpp
xmppbot/xmppbot.cpp
web/api_v1.cpp
@ -222,6 +226,11 @@ SET( tomahawkHeaders ${tomahawkHeaders}
network/filetransferconnection.h
network/dbsyncconnection.h
dynamic/dynamicplaylist.h
dynamic/dynamiccontrol.h
dynamic/generatorfactory.h
dynamic/generatorinterface.h
musicscanner.h
scriptresolver.h
tomahawksettings.h
@ -308,6 +317,7 @@ INCLUDE_DIRECTORIES(
sourcetree
topbar
utils
dynamic
../rtaudio
../alsa-playback

View File

View File

@ -0,0 +1,6 @@
#ifndef DYNAMIC_PLAYLIST_CONTROL
#define DYNAMIC_PLAYLIST_CONTROL
#endif

View File

@ -0,0 +1,238 @@
#include "dynamicplaylist.h"
#include "tomahawk/tomahawkapp.h"
using namespace Tomahawk;
// Called by loadAllPlaylists command
DynamicPlaylist::DynamicPlaylist ( const Tomahawk::source_ptr& src,
const QString& currentrevision,
const QString& title,
const QString& info,
const QString& creator,
const QString& type,
bool shared,
int lastmod,
const QString& guid )
: Playlist( src, currentrevision, title, info, creator, shared, lastmod, guid )
{
qDebug() << "Creating Dynamic Playlist 1";
// TODO instantiate generator
}
// 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,
bool shared )
: Playlist ( author, guid, title, info, creator, shared )
{
qDebug() << "Creating Dynamic Playlist 2";
// TODO instantiate generator
}
dynplaylist_ptr DynamicPlaylist::create( const Tomahawk::source_ptr& author,
const QString& guid,
const QString& title,
const QString& info,
const QString& creator,
bool shared )
{
// TODO default generator?
QString type = "default_generator";
dynplaylist_ptr dynplaylist = new dynplaylist_ptr( new DynamicPlaylist( author, guid, title, info, creator, type, shared ) );
DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( author, dynplaylist );
connect( cmd, SIGNAL(finished()), dynplaylist.data(), SIGNAL(created()) );
APP->database()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
dynplaylist->reportCreated( dynplaylist );
return dynplaylist;
}
// 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 )
{
// get the newly added tracks
QList< plentry_ptr > added = newEntries( entries );
QStringList orderedguids;
foreach( plentry_ptr p, entries )
orderedguids << p->guid();
// no conflict resolution or partial updating for controls. all or nothing baby
// source making the change (localy user in this case)
source_ptr author = APP->sourcelist().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,
type,
STATIC,
controls );
APP->database()->enqueue( QSharedPointer<DatabaseCommand>( 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 )
{
// can skip the entry stuff. just overwrite with new info
source_ptr author = APP->sourcelist().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,
STATIC,
controls );
APP->database()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
}
void DynamicPlaylist::loadRevision( const QString& rev )
{
qDebug() << Q_FUNC_INFO;
DatabaseCommand_LoadDynamicPlaylist* cmd =
new DatabaseCommand_LoadDynamicPlaylist( rev.isEmpty() ? currentrevision() : rev, m_generator->mode() );
if( m_generator->mode() == ONDEMAND ) {
connect( cmd, SIGNAL( done( QString,
bool,
const QString,
QList< dyncontrol_ptr>,
bool ) ),
SLOT( setRevision( QString,
bool,
const QString,
QList< dyncontrol_ptr>,
bool) ) );
} else if( m_generator->mode() == STATIC ) {
connect( cmd, SIGNAL( done( QString,
QList< QString >,
QList< QString >,
QString,
QList< dyncontrol_ptr>,
bool,
const QMap< QString, Tomahawk::plentry_ptr >,
bool ) ),
SLOT( setRevision( QString,
QList< QString >,
QList< QString >,
QString,
QList< dyncontrol_ptr>,
bool,
const QMap< QString, Tomahawk::plentry_ptr >,
bool ) ) );
}
APP->database()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
}
bool DynamicPlaylist::remove( const Tomahawk::dynplaylist_ptr& playlist )
{
return remove( playlist.staticCast<Tomahawk::playlist_ptr>() );
}
void DynamicPlaylist::reportCreated( const Tomahawk::dynplaylist_ptr& self )
{
qDebug() << Q_FUNC_INFO;
Q_ASSERT( self.data() == this );
// will emit Collection::playlistCreated(...)
m_source->collection()->addPlaylist( self.staticCast<Tomahawk::playlist_ptr>() );
}
void DynamicPlaylist::reportDeleted( const Tomahawk::dynplaylist_ptr& self )
{
qDebug() << Q_FUNC_INFO;
Q_ASSERT( self.data() == this );
// will emit Collection::playlistCreated(...)
m_source->collection()->deletePlaylist( self.staticCast<Tomahawk::playlist_ptr>() );
}
// static version
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 )
{
// 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< 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( m_generator->type() != type ) { // new generator needed
m_generator = generatorinterface_ptr( GeneratorFactory::create( type ) );
}
m_generator->setControls( controls );
m_generator->setMode( ONDEMAND )
DynamicPlaylistRevision pr = DynamicPlaylistRevision( setNewRevision( rev, neworderedguids, oldorderedguids, is_newest_rev, addedmap ) );
pr.controls = controls;
pr.type = type;
pr.mode = STATIC;
if( applied )
setCurrentRevision( rev );
pr.applied = applied;
emit revisionLoaded( pr );
}
// ondemand version
void DynamicPlaylist::setRevision( const QString& rev,
bool is_newest_rev,
const QString& type,
const QList< dyncontrol_ptr>& controls,
bool applied )
{
if( m_generator->type() != type ) { // new generator needed
m_generator = generatorinterface_ptr( GeneratorFactory::create( type ) );
}
m_generator->setControls( controls );
m_generator->setMode( ONDEMAND )
DynamicPlaylistRevision pr;
pr.oldrevisionguid = m_currentrevision;
pr.revisionguid = rev;
pr.controls = controls;
pr.type = type;
pr.mode = ONDEMAND;
emit revisionLoaded( pr );
}

View File

@ -0,0 +1,120 @@
#ifndef DYNAMIC_PLAYLIST_H
#define DYNAMIC_PLAYLIST_H
#include <QObject>
#include <QList>
#include <QSharedPointer>
#include "tomahawk/playlist.h"
#include "dynamic/generatorinterface.h"
namespace Tomahawk {
/**
* Subclass of playlist that adds the information needed to store a dynamic playlist.
* It uses normal PlaylistEntries but also has a mode, a generator, and a list of controls
*/
class DynamicPlaylist : public Playlist
{
Q_OBJECT
Q_PROPERTY( GeneratorMode mode WRITE setMode READ mode )
Q_PROPERTY( QString type WRITE setType READ type )
public:
enum DynamicPlaylistMode {
OnDemand = 0,
StaticPlaylist
};
/// Generate an empty dynamic playlist with default generator
static Tomahawk::dynplaylist_ptr create( const source_ptr& author,
const QString& guid,
const QString& title,
const QString& info,
const QString& creator,
bool shared
);
static bool remove( const dynplaylist_ptr& playlist );
virtual void loadRevision( const QString& rev = "" );
GeneratorMode mode() const { return m_generator->mode(); }
QString type() const { return m_generator->type(); }
generatorinterface_ptr generator() const { return m_generator; }
// <IGNORE hack="true">
// these need to exist and be public for the json serialization stuff
// you SHOULD NOT call them. They are used for an alternate CTOR method from json.
// maybe friend QObjectHelper and make them private?
explicit DynamicPlaylist( const source_ptr& author )
: Playlist( author )
{
qDebug() << Q_FUNC_INFO << "JSON";
}
void setMode( GeneratorMode mode ) { m_generator->setMode( mode ); }
void setType( const QString& type ) { /** TODO */; }
void setGenerator( const generatorinterface_ptr& gen_ptr ) { m_generator = gen_ptr; }
// </IGNORE>
signals:
/// emitted when the playlist revision changes (whenever the playlist changes)
void revisionLoaded( Tomahawk::DynamicPlaylistRevision );
public slots:
// want to update the playlist from the model?
// generate a newrev using uuid() and call this:
// if this is a static playlist, pass it a new list of entries. implicitly sets mode to static
void createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, const QList< dyncontrol_ptr>& controls, const QList< plentry_ptr >& entries );
// if it is ondemand, no entries are needed implicitly sets mode to ondemand
void createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, const QList< dyncontrol_ptr>& controls );
void reportCreated( const Tomahawk::dynplaylist_ptr& self );
void reportDeleted( const Tomahawk::dynplaylist_ptr& self );
// called from setdynamicplaylistrevision db cmd
// static version
void 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, Tomahawk::plentry_ptr >& addedmap,
bool applied );
// ondemand version
void setRevision( const QString& rev,
bool is_newest_rev,
const QString& type,
const QList< dyncontrol_ptr>& controls,
bool applied );
private:
// called from loadAllPlaylists DB cmd:
explicit DynamicPlaylist( const source_ptr& src,
const QString& currentrevision,
const QString& title,
const QString& info,
const QString& creator,
const QString& type,
bool shared,
int lastmod,
const QString& guid = "" ); // populate db
// called when creating new playlist
explicit DynamicPlaylist( const source_ptr& author,
const QString& guid,
const QString& title,
const QString& info,
const QString& creator,
const QString& type,
bool shared );
private:
generatorinterface_ptr m_generator;
};
}; // namespace
#endif

View File

@ -0,0 +1,23 @@
#include "generatorfactory.h"
Tomahawk::GeneratorFactory::GeneratorFactory()
{
}
Tomahawk::GeneratorFactory::~GeneratorFactory()
{
qDeleteAll( m_factories.values() );
}
generatorinterface_ptr Tomahawk::GeneratorFactory::create ( const QString& type )
{
if( !m_factories.contains( type ) )
return generatorinterface_ptr();
return generatorinterface_ptr( m_factories.value( type )->create() );
}
void Tomahawk::GeneratorFactory::registerFactory ( const QString& type, Tomahawk::GeneratorFactoryInterface* interface )
{
m_factories.insert( type, interface );
}

View File

@ -0,0 +1,39 @@
#ifndef GENERATOR_FACTORY_H
#define GENERATOR_FACTORY_H
#include <QHash>
#include <QString>
namespace Tomahawk {
/**
* Generators should subclass this and have it create the custom Generator
*/
class GeneratorFactoryInterface
{
public:
GeneratorInterface* create() = 0;
};
/**
* Simple factory that generates Generators from string type descriptors
*/
class GeneratorFactory
{
public:
GeneratorFactory();
~GeneratorFactory();
generatorinterface_ptr create( const QString& type );
void registerFactory( const QString& type, GeneratorFactoryInterface* interface );
private:
QHash<QString, GeneratorFactoryInterface*> m_factories;
};
};
#endif

View File

@ -0,0 +1,52 @@
#ifndef GENERATOR_INTERFACE_H
#define GENERATOR_INTERFACE_H
#include <QtCore/QObject>
namespace Tomahawk {
enum GeneratorMode {
OnDemand = 0,
Static
};
/**
* The abstract interface for Dynamic Playlist Generators. Generators have the following features:
* - They create new DynamicControls that are appropriate for the generator
* - They expose a list of controls that this generator currently is operating on
* - They have a state of OnDemand or Static
*/
class GeneratorInterface : public QObject
{
Q_OBJECT
Q_PROPERTY( QString type READ type )
Q_PROPERTY( GeneratorMode mode READ mode WRITE setMode );
Q_ENUMS( GeneratorMode )
public:
explicit GeneratorInterface() {}
virtual ~GeneratorInterface() {}
// Can't make it pure otherwise we can't shove it in QVariants :-/
virtual dyncontrol_ptr createControl() const {}
/// The type of this generator
virtual QString type() const { return m_type; }
virtual GeneratorMode mode() const { return m_mode; }
virtual void setMode( GeneratorMode mode ) { m_mode = mode; }
// control functions
virtual QList< dyncontrol_ptr > controls() const { return m_controls; }
virtual void addControl( const dyncontrol_ptr& control ) { m_controls << control; }
virtual void clearControls() { m_controls.clear(); }
virtual void setControls( const QList< dyncontrol_ptr>& controls ) { m_controls = controls; }
private:
QString m_type;
GeneratorMode m_mode;
QList< dyncontrol_ptr > m_controls;
};
};
#endif

View File

@ -168,21 +168,11 @@ void
Playlist::createNewRevision( const QString& newrev, const QString& oldrev, const QList< plentry_ptr >& entries )
{
// qDebug() << "m_entries guids:";
// foreach( plentry_ptr pp, m_entries ) qDebug() << pp->guid();
QSet<QString> currentguids;
foreach( plentry_ptr p, m_entries )
currentguids.insert( p->guid() ); // could be cached as member?
// calc list of newly added entries:
QList<plentry_ptr> added;
QList<plentry_ptr> added = newEntries( entries );
QStringList orderedguids;
foreach( plentry_ptr p, entries )
{
orderedguids << p->guid();
if( !currentguids.contains(p->guid()) )
added << p;
}
// source making the change (localy user in this case)
source_ptr author = APP->sourcelist().getLocal();
@ -225,87 +215,9 @@ Playlist::setRevision( const QString& rev,
);
return;
}
//qDebug() << Q_FUNC_INFO << (qlonglong)this
// << rev << neworderedguids << oldorderedguids
// << "isnewest:" << is_newest_rev << addedmap << applied << m_entries
// ;
// build up correctly ordered new list of plentry_ptrs from
// existing ones, and the ones that have been added
QMap<QString, plentry_ptr> entriesmap;
foreach( const plentry_ptr& p, m_entries )
entriesmap.insert( p->guid(), p );
//qDebug() << "Entries map:" << entriesmap;
QList<plentry_ptr> entries;
//qDebug() << "m_entries:" << m_entries.count() << m_entries;
//qDebug() << "counters:" << neworderedguids.count() << entriesmap.count() << addedmap.count();
foreach( const QString& id, neworderedguids )
{
//qDebug() << "id:" << id;
//qDebug() << "newordered:" << neworderedguids.count() << neworderedguids;
//qDebug() << "entriesmap:" << entriesmap.count() << entriesmap;
//qDebug() << "addedmap:" << addedmap.count() << addedmap;
//qDebug() << "m_entries" << m_entries;
if( entriesmap.contains( id ) )
{
entries.append( entriesmap.value( id ) );
}
else if( addedmap.contains( id ) )
{
entries.append( addedmap.value( id ) );
if( is_newest_rev ) m_entries.append( addedmap.value( id ) );
}
else
{
Q_ASSERT( false ); // XXX
}
}
//qDebug() << Q_FUNC_INFO << rev << entries.length() << applied;
PlaylistRevision pr;
pr.oldrevisionguid = m_currentrevision;
pr.revisionguid = rev;
// entries that have been removed:
QSet<QString> removedguids = oldorderedguids.toSet().subtract( neworderedguids.toSet() );
//qDebug() << "Removedguids:" << removedguids << "oldorederedguids" << oldorderedguids << "newog" << neworderedguids;
foreach( QString remid, removedguids )
{
// NB: entriesmap will contain old/removed entries only if the removal was done
// in the same session - after a restart, history is not in memory.
if( entriesmap.contains( remid ) )
{
pr.removed << entriesmap.value( remid );
if( is_newest_rev )
{
//qDebug() << "Removing from m_entries" << remid;
for( int k = 0 ; k<m_entries.length(); ++k )
{
if( m_entries.at(k)->guid() == remid )
{
//qDebug() << "removed at " << k;
m_entries.removeAt(k);
break;
}
}
}
}
}
pr.added = addedmap.values();
//qDebug() << "Revision set:" << rev
// << "added" << pr.added.size()
// << "removed" << pr.removed.size()
// << "total entries" << m_entries.size();
pr.newlist = entries;
PlaylistRevision pr = setNewRevision( rev, neworderedguids, oldorderedguids, is_newest_rev, addedmap );
if( applied )
m_currentrevision = rev;
pr.applied = applied;
@ -313,6 +225,94 @@ Playlist::setRevision( const QString& rev,
emit revisionLoaded( pr );
}
PlaylistRevision
Playlist::setNewRevision( const QString& rev,
const QList<QString>& neworderedguids,
const QList<QString>& oldorderedguids,
bool is_newest_rev,
const QMap< QString, Tomahawk::plentry_ptr >& addedmap )
{
//qDebug() << Q_FUNC_INFO << (qlonglong)this
// << rev << neworderedguids << oldorderedguids
// << "isnewest:" << is_newest_rev << addedmap << applied << m_entries
// ;
// build up correctly ordered new list of plentry_ptrs from
// existing ones, and the ones that have been added
QMap<QString, plentry_ptr> entriesmap;
foreach( const plentry_ptr& p, m_entries )
entriesmap.insert( p->guid(), p );
//qDebug() << "Entries map:" << entriesmap;
QList<plentry_ptr> entries;
//qDebug() << "m_entries:" << m_entries.count() << m_entries;
//qDebug() << "counters:" << neworderedguids.count() << entriesmap.count() << addedmap.count();
foreach( const QString& id, neworderedguids )
{
//qDebug() << "id:" << id;
//qDebug() << "newordered:" << neworderedguids.count() << neworderedguids;
//qDebug() << "entriesmap:" << entriesmap.count() << entriesmap;
//qDebug() << "addedmap:" << addedmap.count() << addedmap;
//qDebug() << "m_entries" << m_entries;
if( entriesmap.contains( id ) )
{
entries.append( entriesmap.value( id ) );
}
else if( addedmap.contains( id ) )
{
entries.append( addedmap.value( id ) );
if( is_newest_rev ) m_entries.append( addedmap.value( id ) );
}
else
{
Q_ASSERT( false ); // XXX
}
}
//qDebug() << Q_FUNC_INFO << rev << entries.length() << applied;
PlaylistRevision pr;
pr.oldrevisionguid = m_currentrevision;
pr.revisionguid = rev;
// entries that have been removed:
QSet<QString> removedguids = oldorderedguids.toSet().subtract( neworderedguids.toSet() );
//qDebug() << "Removedguids:" << removedguids << "oldorederedguids" << oldorderedguids << "newog" << neworderedguids;
foreach( QString remid, removedguids )
{
// NB: entriesmap will contain old/removed entries only if the removal was done
// in the same session - after a restart, history is not in memory.
if( entriesmap.contains( remid ) )
{
pr.removed << entriesmap.value( remid );
if( is_newest_rev )
{
//qDebug() << "Removing from m_entries" << remid;
for( int k = 0 ; k<m_entries.length(); ++k )
{
if( m_entries.at(k)->guid() == remid )
{
//qDebug() << "removed at " << k;
m_entries.removeAt(k);
break;
}
}
}
}
}
pr.added = addedmap.values();
//qDebug() << "Revision set:" << rev
// << "added" << pr.added.size()
// << "removed" << pr.removed.size()
// << "total entries" << m_entries.size();
pr.newlist = entries;
}
void Playlist::resolve()
{
@ -361,3 +361,20 @@ Playlist::addEntries( const QList<query_ptr>& queries, const QString& oldrev )
QString newrev = uuid();
createNewRevision( newrev, oldrev, el );
}
QList< plentry_ptr >
Playlist::newEntries( const QList< plentry_ptr > entries )
{
QSet<QString> currentguids;
foreach( plentry_ptr p, m_entries )
currentguids.insert( p->guid() ); // could be cached as member?
// calc list of newly added entries:
QList<plentry_ptr> added;
foreach( plentry_ptr p, entries )
{
if( !currentguids.contains(p->guid()) )
added << p;
}
return added;
}