From cc7ecc679522987ac69f9d4854f07e0f1f5c1e3c Mon Sep 17 00:00:00 2001
From: Leo Franchi <lfranchi@kde.org>
Date: Mon, 2 Apr 2012 09:41:35 -0400
Subject: [PATCH] allow updating playlist metadata

---
 .../spotify/SpotifyPlaylistUpdater.cpp        |  8 +--
 .../databasecommand_setplaylistrevision.cpp   | 64 ++++++++++++++----
 .../databasecommand_setplaylistrevision.h     | 18 ++++-
 src/libtomahawk/playlist.cpp                  | 65 +++++++++++++++----
 src/libtomahawk/playlist.h                    |  7 ++
 5 files changed, 129 insertions(+), 33 deletions(-)

diff --git a/src/accounts/spotify/SpotifyPlaylistUpdater.cpp b/src/accounts/spotify/SpotifyPlaylistUpdater.cpp
index b4c84f31a..5a3a6e7d2 100644
--- a/src/accounts/spotify/SpotifyPlaylistUpdater.cpp
+++ b/src/accounts/spotify/SpotifyPlaylistUpdater.cpp
@@ -295,8 +295,8 @@ SpotifyPlaylistUpdater::onTracksInsertedReturn( const QString& msgType, const QV
     Q_ASSERT( trackPositionsInserted.size() == trackIdsInserted.size() );
 
     const QList< plentry_ptr > curEntries = playlist()->entries();
+    QList< plentry_ptr > changed;
 
-    int changed = 0;
     for ( int i = 0; i < trackPositionsInserted.size(); i++ )
     {
         const QVariant posV = trackPositionsInserted[ i ];
@@ -326,13 +326,13 @@ SpotifyPlaylistUpdater::onTracksInsertedReturn( const QString& msgType, const QV
 
         qDebug() << "Setting annotation for track:" << m_waitingForIds[ pos ]->query()->track() << m_waitingForIds[ pos ]->query()->artist() << trackIdsInserted.at( i ).toString();
         m_waitingForIds[ pos ]->setAnnotation( trackIdsInserted.at( i ).toString() );
-        changed++;
+        changed << m_waitingForIds[ pos ];
     }
 
     m_waitingForIds.clear();
     // Save our changes if we added some IDs
-    if ( changed > 0 )
-        playlist()->createNewRevision( uuid(), playlist()->currentrevision(), playlist()->entries() );
+    if ( changed.size() > 0 )
+        playlist()->updateEntries( uuid(), playlist()->currentrevision(), changed );
 
 }
 
diff --git a/src/libtomahawk/database/databasecommand_setplaylistrevision.cpp b/src/libtomahawk/database/databasecommand_setplaylistrevision.cpp
index 689bbb316..b6d431881 100644
--- a/src/libtomahawk/database/databasecommand_setplaylistrevision.cpp
+++ b/src/libtomahawk/database/databasecommand_setplaylistrevision.cpp
@@ -43,6 +43,7 @@ DatabaseCommand_SetPlaylistRevision::DatabaseCommand_SetPlaylistRevision(
     , m_oldrev( oldrev )
     , m_addedentries( addedentries )
     , m_entries( entries )
+    , m_metadataUpdate( false )
 {
     Q_ASSERT( !newrev.isEmpty() );
     m_localOnly = ( newrev == oldrev );
@@ -57,6 +58,33 @@ DatabaseCommand_SetPlaylistRevision::DatabaseCommand_SetPlaylistRevision(
 }
 
 
+DatabaseCommand_SetPlaylistRevision::DatabaseCommand_SetPlaylistRevision(
+                        const source_ptr& s,
+                        const QString& playlistguid,
+                        const QString& newrev,
+                        const QString& oldrev,
+                        const QStringList& orderedguids,
+                        const QList<plentry_ptr>& entriesToUpdate )
+    : DatabaseCommandLoggable( s )
+    , m_applied( false )
+    , m_newrev( newrev )
+    , m_oldrev( oldrev )
+    , m_entries( entriesToUpdate )
+    , m_metadataUpdate( true )
+{
+    Q_ASSERT( !newrev.isEmpty() );
+    m_localOnly = false;
+
+    QVariantList tmp;
+    foreach( const QString& s, orderedguids )
+        tmp << s;
+
+    setOrderedguids( tmp );
+
+    setPlaylistguid( playlistguid );
+}
+
+
 void
 DatabaseCommand_SetPlaylistRevision::postCommitHook()
 {
@@ -105,7 +133,7 @@ DatabaseCommand_SetPlaylistRevision::exec( DatabaseImpl* lib )
     else
     {
         tDebug() << "Playlist:" << m_playlistguid << m_currentRevision << source()->friendlyName() << source()->id();
-        throw "No such playlist, WTF?";
+        Q_ASSERT_X( false, "DatabaseCommand_SetPlaylistRevision::exec", "No such playlist, WTF?" );
         return;
     }
 
@@ -132,20 +160,28 @@ DatabaseCommand_SetPlaylistRevision::exec( DatabaseImpl* lib )
 
         return;
     }
+    else if ( m_metadataUpdate )
+    {
+        QString sql = "UPDATE playlist_item SET trackname = ?, artistname = ?, albumname = ?, annotation = ?, duration = ?, addedon = ?, addedby = ? WHERE guid = ?";
+        adde.prepare( sql );
+
+        foreach( const plentry_ptr& e, m_entries )
+        {
+
+            adde.bindValue( 0, e->query()->track() );
+            adde.bindValue( 1, e->query()->artist() );
+            adde.bindValue( 2, e->query()->album() );
+            adde.bindValue( 3, e->annotation() );
+            adde.bindValue( 4, (int) e->duration() );
+            adde.bindValue( 5, e->lastmodified() );
+            adde.bindValue( 6, source()->isLocal() ? QVariant(QVariant::Int) : source()->id() );
+            adde.bindValue( 7, e->guid() );
+
+            adde.exec();
+        }
+    }
     else
     {
-        // DEBUG only
-        qDebug() << "Current entries in the playlist_item table for this playlist:" << m_playlistguid;
-        TomahawkSqlQuery q = lib->newquery();
-        q.prepare( "SELECT guid, playlist, trackname, artistname, annotation FROM playlist_item WHERE playlist = ?" );
-        q.addBindValue( m_playlistguid );
-        if ( q.exec() )
-        {
-            while ( q.next() )
-            {
-                qDebug() << "====" << q.value( 0 ).toString() << q.value( 1 ).toString()  << q.value( 2 ).toString()  << q.value( 3 ).toString()  << q.value( 4 ).toString() << "====";
-            }
-        }
         QString sql = "INSERT INTO playlist_item( guid, playlist, trackname, artistname, albumname, "
                                                  "annotation, duration, addedon, addedby, result_hint ) "
                       "VALUES( ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )";
@@ -154,7 +190,7 @@ DatabaseCommand_SetPlaylistRevision::exec( DatabaseImpl* lib )
         qDebug() << "Num new playlist_items to add:" << m_addedentries.length();
         foreach( const plentry_ptr& e, m_addedentries )
         {
-            qDebug() << "Adding:" << e->guid() << e->query()->track() << e->query()->artist();
+//             qDebug() << "Adding:" << e->guid() << e->query()->track() << e->query()->artist();
 
             m_addedmap.insert( e->guid(), e ); // needed in postcommithook
 
diff --git a/src/libtomahawk/database/databasecommand_setplaylistrevision.h b/src/libtomahawk/database/databasecommand_setplaylistrevision.h
index 1033f7bfb..6ef20c945 100644
--- a/src/libtomahawk/database/databasecommand_setplaylistrevision.h
+++ b/src/libtomahawk/database/databasecommand_setplaylistrevision.h
@@ -35,15 +35,18 @@ Q_PROPERTY( QString newrev            READ newrev        WRITE setNewrev )
 Q_PROPERTY( QString oldrev            READ oldrev        WRITE setOldrev )
 Q_PROPERTY( QVariantList orderedguids READ orderedguids  WRITE setOrderedguids )
 Q_PROPERTY( QVariantList addedentries READ addedentriesV WRITE setAddedentriesV )
+Q_PROPERTY( bool metadataUpdate       READ metadataUpdate WRITE setMetadataUpdate )
 
 public:
     explicit DatabaseCommand_SetPlaylistRevision( QObject* parent = 0 )
         : DatabaseCommandLoggable( parent )
         , m_applied( false )
         , m_localOnly( false )
+        , m_metadataUpdate( false )
     {}
 
-    explicit DatabaseCommand_SetPlaylistRevision( const source_ptr& s,
+    // Constructor for inserting or removing entries
+    DatabaseCommand_SetPlaylistRevision( const source_ptr& s,
                                                   const QString& playlistguid,
                                                   const QString& newrev,
                                                   const QString& oldrev,
@@ -51,6 +54,15 @@ public:
                                                   const QList<Tomahawk::plentry_ptr>& addedentries,
                                                   const QList<Tomahawk::plentry_ptr>& entries );
 
+    // constructor for updating metadata only
+    DatabaseCommand_SetPlaylistRevision( const source_ptr& s,
+                                                  const QString& playlistguid,
+                                                  const QString& newrev,
+                                                  const QString& oldrev,
+                                                  const QStringList& orderedguids,
+                                                  const QList<Tomahawk::plentry_ptr>& entriesToUpdate );
+
+
     QString commandname() const { return "setplaylistrevision"; }
 
     virtual void exec( DatabaseImpl* lib );
@@ -89,6 +101,8 @@ public:
     QString newrev() const { return m_newrev; }
     QString oldrev() const { return m_oldrev; }
     QString playlistguid() const { return m_playlistguid; }
+    bool metadataUpdate() const { return m_metadataUpdate; }
+    void setMetadataUpdate( bool metadataUpdate ) { m_metadataUpdate = metadataUpdate; }
 
     void setOrderedguids( const QVariantList& l ) { m_orderedguids = l; }
     QVariantList orderedguids() const { return m_orderedguids; }
@@ -106,7 +120,7 @@ private:
     QVariantList m_orderedguids;
     QList<Tomahawk::plentry_ptr> m_addedentries, m_entries;
 
-    bool m_localOnly;
+    bool m_localOnly, m_metadataUpdate;
 };
 
 #endif // DATABASECOMMAND_SETPLAYLISTREVISION_H
diff --git a/src/libtomahawk/playlist.cpp b/src/libtomahawk/playlist.cpp
index c75b8cd77..cfa869300 100644
--- a/src/libtomahawk/playlist.cpp
+++ b/src/libtomahawk/playlist.cpp
@@ -319,9 +319,9 @@ Playlist::createNewRevision( const QString& newrev, const QString& oldrev, const
     foreach( const plentry_ptr& p, entries )
         orderedguids << p->guid();
 
-    qDebug() << "INSERTING ORDERED GUIDS:" << orderedguids << "and with new entries:";
-    foreach( const plentry_ptr& p, added )
-        qDebug() << p->guid();
+//     qDebug() << "INSERTING ORDERED GUIDS:" << orderedguids << "and with new entries:";
+//     foreach( const plentry_ptr& p, added )
+//         qDebug() << p->guid();
 
     // source making the change (local user in this case)
     source_ptr author = SourceList::instance()->getLocal();
@@ -339,6 +339,38 @@ Playlist::createNewRevision( const QString& newrev, const QString& oldrev, const
 }
 
 
+void
+Playlist::updateEntries( const QString& newrev, const QString& oldrev, const QList< plentry_ptr >& entries )
+{
+    tDebug() << Q_FUNC_INFO << newrev << oldrev << entries.count();
+    Q_ASSERT( m_source->isLocal() || newrev == oldrev );
+
+    if ( busy() )
+    {
+        m_updateQueue.enqueue( RevisionQueueItem( newrev, oldrev, entries, oldrev == currentrevision() ) );
+        return;
+    }
+
+    if ( newrev != oldrev )
+        setBusy( true );
+
+    QStringList orderedguids;
+    foreach( const plentry_ptr& p, m_entries )
+        orderedguids << p->guid();
+
+    qDebug() << "Updating playlist metadata:" << entries;
+    DatabaseCommand_SetPlaylistRevision* cmd =
+    new DatabaseCommand_SetPlaylistRevision( SourceList::instance()->getLocal(),
+                                             guid(),
+                                             newrev,
+                                             oldrev,
+                                             orderedguids,
+                                             entries );
+
+    Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
+}
+
+
 // private, called when we loadRevision, or by our friend class DatabaseCommand_SetPlaylistRevision
 // used to save new revision data (either originating locally, or via network msg for syncing)
 void
@@ -569,19 +601,9 @@ Playlist::insertEntries( const QList< query_ptr >& queries, const int position,
         return;
     }
 
-    qDebug() << "inserting entries. GUIDs before:";
-    foreach( const plentry_ptr& p, entries )
-        qDebug() << p->guid();
-
     for ( int i = toInsert.size()-1; i >= 0; --i )
         entries.insert( position, toInsert.at(i) );
 
-    qDebug() << "inserting entries. GUIDs AFTER:";
-    foreach( const plentry_ptr& p, entries )
-        qDebug() << p->guid();
-
-    const int prevSize = m_entries.size();
-    qDebug() << "Done inserting playlist entries in the middle of the playlist! Committing...";
     createNewRevision( uuid(), oldrev, entries );
 
     // We are appending at end, so notify listeners.
@@ -668,6 +690,23 @@ Playlist::checkRevisionQueue()
         }
         createNewRevision( item.newRev, item.oldRev, item.entries );
     }
+    if ( !m_updateQueue.isEmpty() )
+    {
+        RevisionQueueItem item = m_updateQueue.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();
+        }
+        updateEntries( item.newRev, item.oldRev, item.entries );
+    }
 }
 
 
diff --git a/src/libtomahawk/playlist.h b/src/libtomahawk/playlist.h
index 78f3e3745..fb9cddd8f 100644
--- a/src/libtomahawk/playlist.h
+++ b/src/libtomahawk/playlist.h
@@ -223,6 +223,12 @@ public slots:
     // want to update the playlist from the model?
     // generate a newrev using uuid() and call this:
     void createNewRevision( const QString& newrev, const QString& oldrev, const QList< plentry_ptr >& entries );
+
+    // Want to update some metadata of a plentry_ptr? this gets you a new revision too.
+    // entries should be <= entries(), with changed metadata.
+    void updateEntries( const QString& newrev, const QString& oldrev, const QList< plentry_ptr >& entries );
+
+
     void reportCreated( const Tomahawk::playlist_ptr& self );
     void reportDeleted( const Tomahawk::playlist_ptr& self );
 
@@ -286,6 +292,7 @@ private:
     QList< plentry_ptr > m_entries;
 
     QQueue<RevisionQueueItem> m_revisionQueue;
+    QQueue<RevisionQueueItem> m_updateQueue;
 
     PlaylistUpdaterInterface* m_updater;