1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-04-22 00:42:04 +02:00

Create and load database-backed special dynamic playlists, and fix some debugs and stuff

This commit is contained in:
Leo Franchi 2011-07-30 15:35:32 -04:00
parent ca467fb5bc
commit c76fabbc2b
15 changed files with 169 additions and 50 deletions

@ -31,7 +31,6 @@ DatabaseCommand_CreatePlaylist::DatabaseCommand_CreatePlaylist( QObject* parent
: DatabaseCommandLoggable( parent )
, m_report( true )
{
qDebug() << Q_FUNC_INFO << "def";
}
@ -41,7 +40,6 @@ DatabaseCommand_CreatePlaylist::DatabaseCommand_CreatePlaylist( const source_ptr
, m_playlist( playlist )
, m_report( false ) //this ctor used when creating locally, reporting done elsewhere
{
qDebug() << Q_FUNC_INFO;
}
@ -55,7 +53,6 @@ DatabaseCommand_CreatePlaylist::exec( DatabaseImpl* lib )
void
DatabaseCommand_CreatePlaylist::postCommitHook()
{
qDebug() << Q_FUNC_INFO;
if ( m_report == false )
return;
@ -122,7 +119,6 @@ DatabaseCommand_CreatePlaylist::createPlaylist( DatabaseImpl* lib, bool dynamic)
cre.bindValue( ":creator", m.value( "creator" ) );
cre.bindValue( ":lastmodified", m.value( "lastmodified", 0 ) );
}
tDebug() << "CREATE PLAYLIST:" << cre.boundValues();
cre.exec();
}

@ -37,14 +37,13 @@ DatabaseCommand_GenericSelect::DatabaseCommand_GenericSelect( const QString& sql
void
DatabaseCommand_GenericSelect::exec( DatabaseImpl* dbi )
{
Q_ASSERT( source()->isLocal() || source()->id() >= 1 );
TomahawkSqlQuery query = dbi->newquery();
query.exec( m_sqlSelect );
query.prepare( m_sqlSelect );
query.exec();
QList< query_ptr > queries;
// Expecting
while ( query.next() )
{
@ -54,9 +53,20 @@ DatabaseCommand_GenericSelect::exec( DatabaseImpl* dbi )
QString artist, track, album;
track = query.value( 0 ).toString();
artist = query.value( 1 ).toString();
album = query.value( 2 ).toString();
Tomahawk::query_ptr qry = Tomahawk::Query::get( artist, track, album, uuid(), true ); // Only auto-resolve non-local results
Tomahawk::query_ptr qry = Tomahawk::Query::get( artist, track, QString(), uuid(), true ); // Only auto-resolve non-local results
QVariantList extraData;
int count = 2;
while ( query.value( count ).isValid() )
{
extraData << query.value( count );
count++;
}
if( !extraData.isEmpty() )
qry->setProperty( "data", extraData );
queries << qry;
}

@ -32,7 +32,9 @@
* that match.
*
* In order for the conversion to query_ptr to work, the SELECT command should select the following items:
* track.name, artist.name, album.name
* track.name, artist.name [, optional extra values ]
*
* Any extra values in the resultset will be returned as a QVariantList attached to the "data" property of each query_ptr
*
*/
class DLLEXPORT DatabaseCommand_GenericSelect : public DatabaseCommand

@ -40,6 +40,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::DatabaseCommand_SetDynamicPlaylistRe
, m_type( type )
, m_mode( mode )
, m_controls( controls )
, m_playlist( 0 )
{
}
@ -56,6 +57,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::DatabaseCommand_SetDynamicPlaylistRe
, m_type( type )
, m_mode( mode )
, m_controls( controls )
, m_playlist( 0 )
{
}
@ -100,21 +102,28 @@ DatabaseCommand_SetDynamicPlaylistRevision::postCommitHook()
dynplaylist_ptr playlist = source()->collection()->autoPlaylist( playlistguid() );
if ( playlist.isNull() )
playlist = source()->collection()->station( playlistguid() );
// UGH we don't have a sharedptr from DynamicPlaylist+
DynamicPlaylist* rawPl = playlist.data();
if( playlist.isNull() ) // if it's neither an auto or station, it must not be auto-loaded, so we MUST have been told about it directly
rawPl = m_playlist;
// workaround a bug in pre-0.1.0 tomahawks. they created dynamic playlists in OnDemand mode *always*, and then set the mode to the real one.
// now that we separate them, if we get them as one and then get a changed mode, the playlist ends up in the wrong bucket in Collection.
// so here we fix it if we have to.
// HACK
tDebug() << "Does this need FIXING?" << playlist->mode() << source()->collection()->autoPlaylist( playlistguid() ).isNull() << source()->collection()->station( playlistguid() ).isNull();
if( playlist->mode() == Static && source()->collection()->autoPlaylist( playlistguid() ).isNull() ) // should be here
tDebug() << "Does this need the 0.3->0.1 playlist category hack fix?" << ( rawPl->mode() == Static && source()->collection()->autoPlaylist( playlistguid() ).isNull() )
<< ( rawPl->mode() == OnDemand && source()->collection()->station( playlistguid() ).isNull() )
<< rawPl->mode() << source()->collection()->autoPlaylist( playlistguid() ).isNull() << source()->collection()->station( playlistguid() ).isNull();
if( rawPl->mode() == Static && source()->collection()->autoPlaylist( playlistguid() ).isNull() ) // should be here
source()->collection()->moveStationToAuto( playlistguid() );
else if ( playlist->mode() == OnDemand && source()->collection()->station( playlistguid() ).isNull() ) // should be here
else if ( rawPl->mode() == OnDemand && source()->collection()->station( playlistguid() ).isNull() ) // should be here
source()->collection()->moveAutoToStation( playlistguid() );
if ( playlist.isNull() )
if ( rawPl == 0 )
{
tLog() <<"Got null playlist with guid:" << playlistguid() << "from source and collection:" << source()->friendlyName() << source()->collection()->name() << "and mode is static?:" << (m_mode == Static);
Q_ASSERT( !playlist.isNull() );
Q_ASSERT( false );
return;
}
if ( !m_controlsV.isEmpty() && m_controls.isEmpty() )
@ -124,13 +133,13 @@ DatabaseCommand_SetDynamicPlaylistRevision::postCommitHook()
controlMap << v.toMap();
if ( m_mode == OnDemand )
playlist->setRevision( newrev(),
rawPl->setRevision( newrev(),
true, // this *is* the newest revision so far
m_type,
controlMap,
m_applied );
else
playlist->setRevision( newrev(),
rawPl->setRevision( newrev(),
orderedentriesguids,
m_previous_rev_orderedguids,
m_type,
@ -142,13 +151,13 @@ DatabaseCommand_SetDynamicPlaylistRevision::postCommitHook()
else
{
if ( m_mode == OnDemand )
playlist->setRevision( newrev(),
rawPl->setRevision( newrev(),
true, // this *is* the newest revision so far
m_type,
m_controls,
m_applied );
else
playlist->setRevision( newrev(),
rawPl->setRevision( newrev(),
orderedentriesguids,
m_previous_rev_orderedguids,
m_type,
@ -251,3 +260,9 @@ DatabaseCommand_SetDynamicPlaylistRevision::exec( DatabaseImpl* lib )
query2.exec();
}
}
void
DatabaseCommand_SetDynamicPlaylistRevision::setPlaylist( DynamicPlaylist* pl )
{
m_playlist = pl;
}

@ -78,11 +78,16 @@ public:
void setType( const QString& type ) { m_type = type; }
void setMode( int mode ) { m_mode = (GeneratorMode)mode; }
void setPlaylist( DynamicPlaylist* pl ); // raw pointer b/c we don't have the shared pointer from inside the shared pointer
private:
QString m_type;
GeneratorMode m_mode;
QList< dyncontrol_ptr > m_controls;
QList< QVariant > m_controlsV;
// ARG i hate sharedpointers sometimes
DynamicPlaylist* m_playlist; // Only used if setting revision of a non-autoloaded playlist, as those aren't able to be looked up by guid
};
#endif // DATABASECOMMAND_SETDYNAMICPLAYLISTREVISION_H

@ -59,7 +59,7 @@ public:
int e = t.elapsed();
if ( e >= TOMAHAWK_QUERY_THRESHOLD )
qDebug() << "TomahawkSqlQuery (" << lastQuery() << ") finished in" << t.elapsed() << "ms";
tLog() << "TomahawkSqlQuery (" << lastQuery() << ") finished in" << t.elapsed() << "ms";
return ret;
}
@ -67,11 +67,10 @@ public:
private:
void showError()
{
qDebug()
<< endl << "*** DATABASE ERROR ***" << endl
<< this->lastQuery() << endl
<< "boundValues:" << this->boundValues() << endl
<< this->lastError().text() << endl
tLog() << "\n" << "*** DATABASE ERROR ***" << "\n"
<< this->lastQuery() << "\n"
<< "boundValues:" << this->boundValues() << "\n"
<< this->lastError().text() << "\n"
;
Q_ASSERT( false );
}

@ -59,6 +59,7 @@ DynamicPlaylist::DynamicPlaylist ( const Tomahawk::source_ptr& src,
int lastmod,
const QString& guid )
: Playlist( src, currentrevision, title, info, creator, createdOn, shared, lastmod, guid )
, m_autoLoad( false )
{
// qDebug() << "Creating Dynamic Playlist 1";
// TODO instantiate generator
@ -75,8 +76,10 @@ DynamicPlaylist::DynamicPlaylist ( const Tomahawk::source_ptr& author,
const QString& creator,
const QString& type,
GeneratorMode mode,
bool shared )
bool shared,
bool autoLoad )
: Playlist ( author, guid, title, info, creator, shared )
, m_autoLoad( autoLoad )
{
// qDebug() << "Creating Dynamic Playlist 2";
m_generator = geninterface_ptr( GeneratorFactory::create( type ) );
@ -127,14 +130,17 @@ DynamicPlaylist::create( const Tomahawk::source_ptr& author,
const QString& creator,
GeneratorMode mode,
bool shared,
const QString& type )
const QString& type,
bool autoLoad
)
{
dynplaylist_ptr dynplaylist = dynplaylist_ptr( new DynamicPlaylist( author, guid, title, info, creator, type, mode, shared ) );
dynplaylist_ptr dynplaylist = dynplaylist_ptr( new DynamicPlaylist( author, guid, title, info, creator, type, mode, shared, autoLoad ) );
DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( author, dynplaylist );
DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( author, dynplaylist, autoLoad );
connect( cmd, SIGNAL(finished()), dynplaylist.data(), SIGNAL(created()) );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
dynplaylist->reportCreated( dynplaylist );
if( autoLoad )
dynplaylist->reportCreated( dynplaylist );
return dynplaylist;
}
@ -187,6 +193,9 @@ DynamicPlaylist::createNewRevision( const QString& newrev,
type,
Static,
controls );
if( !m_autoLoad )
cmd->setPlaylist( this );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
}
@ -211,6 +220,9 @@ DynamicPlaylist::createNewRevision( const QString& newrev,
type,
OnDemand,
controls );
if( !m_autoLoad )
cmd->setPlaylist( this );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
}

@ -69,6 +69,7 @@ class DLLEXPORT DynamicPlaylist : public Playlist
// :-( int becuase qjson chokes on my enums
Q_PROPERTY( int mode WRITE setMode READ mode )
Q_PROPERTY( QString type WRITE setType READ type )
Q_PROPERTY( bool autoLoad READ autoLoad )
friend class ::DatabaseCommand_SetDynamicPlaylistRevision;
friend class ::DatabaseCommand_CreateDynamicPlaylist;
@ -86,7 +87,9 @@ public:
const QString& creator,
GeneratorMode mode,
bool shared,
const QString& type = QString() );
const QString& type = QString(),
bool autoLoad = true
);
static bool remove( const dynplaylist_ptr& playlist );
virtual void loadRevision( const QString& rev = "" );
@ -95,6 +98,7 @@ public:
int mode() const;
QString type() const;
geninterface_ptr generator() const;
bool autoLoad() const { return m_autoLoad; }
// Creates a new revision from the playlist in memory. Use this is you change the controls or
// mode of a playlist and want to save it to db/others.
@ -186,10 +190,13 @@ private:
const QString& creator,
const QString& type,
GeneratorMode mode,
bool shared );
bool shared,
bool autoLoad = true );
QList< dyncontrol_ptr > variantsToControl( const QList< QVariantMap >& controlsV );
geninterface_ptr m_generator;
bool m_autoLoad;
};
}; // namespace

@ -36,7 +36,7 @@ DatabaseControl::DatabaseControl( const QString& selectedType, const QStringList
}
DatabaseControl::DatabaseControl( const QString& sql, const QString& summary, const QStringList& typeSelectors, QObject* parent )
: DynamicControl ( typeSelectors )
: DynamicControl ( "SQL", typeSelectors )
, m_sql( sql )
, m_sqlSummary( summary )
{

@ -53,7 +53,11 @@ DatabaseFactory::typeSelectors() const
DatabaseGenerator::DatabaseGenerator ( QObject* parent )
: GeneratorInterface ( parent )
, m_curCountRequested( 0 )
{
// defaults
m_type = "database";
m_mode = Static;
// m_logo.load( RESPATH "images )
}
@ -85,41 +89,62 @@ void
DatabaseGenerator::generate( int number )
{
tLog() << "Generating" << number << "tracks for this database dynamic playlist with" << m_controls.size() << "controls:";
if ( m_controls.isEmpty() )
{
qWarning() << "No controls, can't generate...!";
emit error( "Failed to generate tracks", "No controls!" );
return;
}
foreach ( const dyncontrol_ptr& ctrl, m_controls )
qDebug() << ctrl->selectedType() << ctrl->match() << ctrl->input();
// TODO for now, we just support the special "SQL" control, not meant to be shown to the user. Just does a raw query.
bool isSql = false;
bool hasSql = false;
bool hasOther = false;
foreach ( const dyncontrol_ptr& ctrl, m_controls )
{
if( ctrl->selectedType() == "SQL" )
isSql = true;
else if( !isSql )
{
qWarning() << "Cannot mix sql and non-sql controls!";
emit error( "Failed to generate tracks", "Cannot mix sql and non-sql controls" );
}
if ( ctrl->selectedType() == "SQL" )
hasSql = true;
else
hasOther = true;
}
if ( hasSql == hasOther )
{
qWarning() << "Cannot mix sql and non-sql controls!";
emit error( "Failed to generate tracks", "Cannot mix sql and non-sql controls" );
return;
}
// TODO for now we just support 1 sql query if we're a sql type
if ( isSql )
if ( hasSql )
{
dyncontrol_ptr control = m_controls.first();
DatabaseCommand_GenericSelect* cmd = new DatabaseCommand_GenericSelect( control.dynamicCast< DatabaseControl >()->sql(), this );
connect( cmd, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SIGNAL( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
tDebug() << "Generated sql query:" << control.dynamicCast< DatabaseControl >()->sql();
DatabaseCommand_GenericSelect* cmd = new DatabaseCommand_GenericSelect( control.dynamicCast< DatabaseControl >()->sql() );
m_curCountRequested = number; // Can't set count on dbcmd itself as sender() in slot is 0
connect( cmd, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) );
return;
}
}
void
DatabaseGenerator::tracksGenerated ( const QList< query_ptr >& tracks )
{
emit generated( tracks );
if( m_curCountRequested == 0 )
return;
if ( m_curCountRequested < tracks.size() )
emit generated( tracks.mid( 0, m_curCountRequested ) );
else
emit generated( tracks );
m_curCountRequested = 0;
}

@ -73,6 +73,7 @@ namespace Tomahawk
private:
QPixmap m_logo;
int m_curCountRequested;
};
};

@ -22,6 +22,9 @@
#include "database/databasecommand_loaddynamicplaylist.h"
#include "database/database.h"
#include "sourcelist.h"
#include "dynamic/GeneratorInterface.h"
#include "dynamic/database/DatabaseGenerator.h"
#include "utils/logger.h"
#define COOLPLAYLIST_GUID "TOMAHAWK_COOLPLAYLISTOHAI_GUID"
@ -68,6 +71,9 @@ SocialPlaylistWidget::dynamicPlaylistLoaded ( const dynplaylist_ptr& ptr )
m_coolQuery1 = ptr;
tLog() << "SocialPlaylistWidget got dynplaylist loaded with currev: " << m_coolQuery1->currentrevision();
connect( m_coolQuery1.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( playlistRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ) );
connect( m_coolQuery1->generator().data(), SIGNAL(generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
connect( m_coolQuery1->generator().data(), SIGNAL(generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
m_coolQuery1->loadRevision( m_coolQuery1->currentrevision() );
}
@ -75,6 +81,11 @@ void
SocialPlaylistWidget::playlistRevisionLoaded( Tomahawk::DynamicPlaylistRevision )
{
m_coolQuery1Model->loadPlaylist( m_coolQuery1 );
m_coolQuery1->resolve();
if ( m_coolQuery1->entries().isEmpty() ) // Generate some
m_coolQuery1->generator()->generate( 30 );
}
void
@ -90,5 +101,38 @@ SocialPlaylistWidget::dynamicPlaylistLoadDone()
void
SocialPlaylistWidget::createPlaylist()
{
// TODO
// Ok, lets create our playlist
/**
* select count(*) as counter, track.name, artist.name from (select track from playback_log group by track, source), track, artist where track.id = track and artist.id = track.artist group by track order by counter desc limit 0,20;
s elect count(*) as counter, playback_log.track, track.name, artist.name from playback_log, track, artist where track.id = playback_log.track and artist.id = track.artist group by playback_log.track order by counter desc limit 0,10; *
select count(*) as counter, track.name, artist.name from (select track from playback_log group by track, source), track, artist where track not in (select track from playback_log where source is null group by track) and track.id = track and artist.id = track.artist group by track order by counter desc limit 0,20;
select count(*) as counter, track.name, artist.name from (select track from playback_log where source > 0 group by track, source), track, artist where track.id = track and artist.id = track.artist group by track order by counter desc limit 0,20;
*/
m_coolQuery1 = DynamicPlaylist::create( SourceList::instance()->getLocal(), COOLPLAYLIST_GUID, "Cool Playlist!", QString(), QString(), Static, false, "database", false );
connect( m_coolQuery1.data(), SIGNAL( created() ), this, SLOT( playlist1Created() ) );
}
void
SocialPlaylistWidget::playlist1Created()
{
Q_ASSERT( m_coolQuery1->generator().dynamicCast< DatabaseGenerator >() );
QString sql = "select track.name, artist.name, count(*) as counter from (select track from playback_log group by track, source), track, artist where track.id = track and artist.id = track.artist group by track order by counter desc limit 0,100;";
dyncontrol_ptr control = m_coolQuery1->generator().dynamicCast< DatabaseGenerator >()->createControl( sql, "This is a cool playlist!" );
m_coolQuery1->createNewRevision( uuid() );
connect( m_coolQuery1.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( playlistRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ) );
connect( m_coolQuery1->generator().data(), SIGNAL(generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
}
void
SocialPlaylistWidget::tracksGenerated( QList< query_ptr > queries )
{
if ( sender() == m_coolQuery1->generator().data() )
{
tDebug() << "Got generated tracks from playlist, adding" << queries.size() << "tracks";
m_coolQuery1Model->clear();
m_coolQuery1->addEntries( queries, m_coolQuery1->currentrevision() );
}
}

@ -74,8 +74,11 @@ signals:
private slots:
void dynamicPlaylistLoaded( const Tomahawk::dynplaylist_ptr& ptr );
void playlistRevisionLoaded ( Tomahawk::DynamicPlaylistRevision );
void tracksGenerated ( QList<Tomahawk::query_ptr> );
void dynamicPlaylistLoadDone();
void playlist1Created();
private:
void load();
void createPlaylist();

@ -72,9 +72,6 @@ public:
virtual QIcon icon() const;
virtual QWidget* configWidget();
signals:
void avatarReceived( QString, QPixmap );
public slots:
virtual bool connectPlugin( bool startup );
void disconnectPlugin();

@ -40,6 +40,7 @@
#include "sip/SipHandler.h"
#include "playlist/dynamic/GeneratorFactory.h"
#include "playlist/dynamic/echonest/EchonestGenerator.h"
#include "playlist/dynamic/database/DatabaseGenerator.h"
#include "web/api_v1.h"
#include "resolvers/scriptresolver.h"
#include "resolvers/qtscriptresolver.h"
@ -175,6 +176,8 @@ TomahawkApp::init()
tDebug() << "Init Echonest Factory.";
GeneratorFactory::registerFactory( "echonest", new EchonestFactory );
tDebug() << "Init Database Factory.";
GeneratorFactory::registerFactory( "database", new DatabaseFactory );
// Register shortcut handler for this platform
#ifdef Q_WS_MAC