PageRenderTime 502ms CodeModel.GetById 52ms app.highlight 361ms RepoModel.GetById 38ms app.codeStats 1ms

/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp

http://github.com/tomahawk-player/tomahawk
C++ | 310 lines | 232 code | 48 blank | 30 comment | 23 complexity | 939fe8d66a14ba968edd5a9c89135269 MD5 | raw file
  1/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  2 *
  3 *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
  4 *
  5 *   Tomahawk is free software: you can redistribute it and/or modify
  6 *   it under the terms of the GNU General Public License as published by
  7 *   the Free Software Foundation, either version 3 of the License, or
  8 *   (at your option) any later version.
  9 *
 10 *   Tomahawk is distributed in the hope that it will be useful,
 11 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 13 *   GNU General Public License for more details.
 14 *
 15 *   You should have received a copy of the GNU General Public License
 16 *   along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
 17 */
 18
 19#include "DatabaseCommand_SetPlaylistRevision.h"
 20
 21#include "collection/Collection.h"
 22#include "network/Servent.h"
 23#include "utils/Json.h"
 24#include "utils/Logger.h"
 25
 26#include "DatabaseImpl.h"
 27#include "PlaylistEntry.h"
 28#include "Source.h"
 29#include "TomahawkSqlQuery.h"
 30#include "Track.h"
 31
 32#include <QSqlQuery>
 33
 34using namespace Tomahawk;
 35
 36
 37DatabaseCommand_SetPlaylistRevision::DatabaseCommand_SetPlaylistRevision(
 38                      const source_ptr& s,
 39                      const QString& playlistguid,
 40                      const QString& newrev,
 41                      const QString& oldrev,
 42                      const QStringList& orderedguids,
 43                      const QList<plentry_ptr>& addedentries,
 44                      const QList<plentry_ptr>& entries )
 45    : DatabaseCommandLoggable( s )
 46    , m_failed( false )
 47    , m_applied( false )
 48    , m_newrev( newrev )
 49    , m_oldrev( oldrev )
 50    , m_addedentries( addedentries )
 51    , m_entries( entries )
 52    , m_metadataUpdate( false )
 53{
 54    Q_ASSERT( !newrev.isEmpty() );
 55    m_localOnly = ( newrev == oldrev );
 56
 57    setPlaylistguid( playlistguid );
 58
 59    QVariantList tmp;
 60    foreach( const QString& s, orderedguids )
 61        tmp << s;
 62
 63    setOrderedguids( tmp );
 64}
 65
 66
 67DatabaseCommand_SetPlaylistRevision::DatabaseCommand_SetPlaylistRevision(
 68                        const source_ptr& s,
 69                        const QString& playlistguid,
 70                        const QString& newrev,
 71                        const QString& oldrev,
 72                        const QStringList& orderedguids,
 73                        const QList<plentry_ptr>& entriesToUpdate )
 74    : DatabaseCommandLoggable( s )
 75    , m_failed( false )
 76    , m_applied( false )
 77    , m_newrev( newrev )
 78    , m_oldrev( oldrev )
 79    , m_entries( entriesToUpdate )
 80    , m_metadataUpdate( true )
 81{
 82    Q_ASSERT( !newrev.isEmpty() );
 83    m_localOnly = false;
 84
 85    QVariantList tmp;
 86    foreach( const QString& s, orderedguids )
 87        tmp << s;
 88
 89    setOrderedguids( tmp );
 90    setPlaylistguid( playlistguid );
 91}
 92
 93
 94void
 95DatabaseCommand_SetPlaylistRevision::postCommitHook()
 96{
 97    tDebug() << Q_FUNC_INFO;
 98    if ( m_localOnly )
 99        return;
100
101    QStringList orderedentriesguids;
102    foreach( const QVariant& v, m_orderedguids )
103        orderedentriesguids << v.toString();
104
105    // private, but we are a friend. will recall itself in its own thread:
106    playlist_ptr playlist = source()->dbCollection()->playlist( m_playlistguid );
107//    Q_ASSERT( !playlist.isNull() );
108    if ( !playlist )
109        return;
110
111    if ( playlist->loaded() )
112    {
113        playlist->setRevision( m_newrev,
114                            orderedentriesguids,
115                            m_previous_rev_orderedguids,
116                            true, // this *is* the newest revision so far
117                            m_addedmap,
118                            m_applied );
119    }
120    else
121        playlist->setCurrentrevision( m_newrev );
122
123    if ( source()->isLocal() )
124        Servent::instance()->triggerDBSync();
125}
126
127
128void
129DatabaseCommand_SetPlaylistRevision::exec( DatabaseImpl* lib )
130{
131    QString currentRevision;
132    // get the current revision for this playlist
133    // this also serves to check the playlist exists.
134    TomahawkSqlQuery chkq = lib->newquery();
135    chkq.prepare( "SELECT currentrevision FROM playlist WHERE guid = ?" );
136    chkq.addBindValue( m_playlistguid );
137    if ( chkq.exec() && chkq.next() )
138    {
139        currentRevision = chkq.value( 0 ).toString();
140        tDebug() << Q_FUNC_INFO << "pl guid" << m_playlistguid << "- curr rev" << currentRevision << source()->friendlyName() << source()->id();
141    }
142    else
143    {
144        tDebug() << "ERROR: No such playlist:" << m_playlistguid << currentRevision << source()->friendlyName() << source()->id();
145//        Q_ASSERT_X( false, "DatabaseCommand_SetPlaylistRevision::exec", "No such playlist, WTF?" );
146        m_failed = true;
147        return;
148    }
149
150    QVariantList vlist = m_orderedguids;
151    const QByteArray entries = TomahawkUtils::toJson( vlist );
152
153    // add any new items:
154    TomahawkSqlQuery adde = lib->newquery();
155    if ( m_localOnly )
156    {
157        QString sql = "UPDATE playlist_item SET result_hint = ? WHERE guid = ?";
158        adde.prepare( sql );
159
160        foreach( const plentry_ptr& e, m_entries )
161        {
162            if ( !e->isValid() )
163                continue;
164            if ( !e->query()->numResults() )
165                continue;
166
167            adde.bindValue( 0, e->resultHint() );
168            adde.bindValue( 1, e->guid() );
169            adde.exec();
170        }
171
172        return;
173    }
174    else if ( m_metadataUpdate )
175    {
176        QString sql = "UPDATE playlist_item SET trackname = ?, artistname = ?, albumname = ?, annotation = ?, duration = ?, addedon = ?, addedby = ? WHERE guid = ?";
177        adde.prepare( sql );
178
179        foreach( const plentry_ptr& e, m_entries )
180        {
181            if ( !e->isValid() )
182                continue;
183
184            adde.bindValue( 0, e->query()->queryTrack()->track() );
185            adde.bindValue( 1, e->query()->queryTrack()->artist() );
186            adde.bindValue( 2, e->query()->queryTrack()->album() );
187            adde.bindValue( 3, e->annotation() );
188            adde.bindValue( 4, (int) e->duration() );
189            adde.bindValue( 5, e->lastmodified() );
190            adde.bindValue( 6, source()->isLocal() ? QVariant(QVariant::Int) : source()->id() );
191            adde.bindValue( 7, e->guid() );
192
193            adde.exec();
194        }
195    }
196    else
197    {
198        QString sql = "REPLACE INTO playlist_item( guid, playlist, trackname, artistname, albumname, "
199                                                 "annotation, duration, addedon, addedby, result_hint ) "
200                      "VALUES( ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )";
201        adde.prepare( sql );
202
203//         qDebug() << "Num new playlist_items to add:" << m_addedentries.length();
204        foreach( const plentry_ptr& e, m_addedentries )
205        {
206            if ( !e->isValid() )
207                continue;
208
209//             qDebug() << "Adding:" << e->guid() << e->query()->track() << e->query()->artist();
210
211            m_addedmap.insert( e->guid(), e ); // needed in postcommithook
212
213            adde.bindValue( 0, e->guid() );
214            adde.bindValue( 1, m_playlistguid );
215            adde.bindValue( 2, e->query()->queryTrack()->track() );
216            adde.bindValue( 3, e->query()->queryTrack()->artist() );
217            adde.bindValue( 4, e->query()->queryTrack()->album() );
218            adde.bindValue( 5, e->annotation() );
219            adde.bindValue( 6, (int) e->duration() );
220            adde.bindValue( 7, e->lastmodified() );
221            adde.bindValue( 8, source()->isLocal() ? QVariant(QVariant::Int) : source()->id() );
222            adde.bindValue( 9, e->resultHint() );
223            adde.exec();
224        }
225    }
226
227    // add / update the revision:
228    TomahawkSqlQuery query = lib->newquery();
229    QString sql = "INSERT INTO playlist_revision(guid, playlist, entries, author, timestamp, previous_revision) "
230                  "VALUES(?, ?, ?, ?, ?, ?)";
231    query.prepare( sql );
232
233    query.addBindValue( m_newrev );
234    query.addBindValue( m_playlistguid );
235    query.addBindValue( entries );
236    query.addBindValue( source()->isLocal() ? QVariant(QVariant::Int) : source()->id() );
237    query.addBindValue( 0 ); //ts
238    query.addBindValue( m_oldrev.isEmpty() ? QVariant(QVariant::String) : m_oldrev );
239    query.exec();
240
241    tDebug() << "Currentrevision:" << currentRevision << "oldrev:" << m_oldrev;
242    // if optimistic locking is ok, update current revision to this new one
243    if ( currentRevision == m_oldrev )
244    {
245        tDebug() << "Updating current revision, optimistic locking ok" << m_newrev;
246
247        TomahawkSqlQuery query2 = lib->newquery();
248        query2.prepare( "UPDATE playlist SET currentrevision = ? WHERE guid = ?" );
249        query2.bindValue( 0, m_newrev );
250        query2.bindValue( 1, m_playlistguid );
251        query2.exec();
252
253        m_applied = true;
254
255        // load previous revision entries, which we need to pass on
256        // so the change can be diffed
257        TomahawkSqlQuery query_entries = lib->newquery();
258        query_entries.prepare( "SELECT entries, playlist, author, timestamp, previous_revision "
259                               "FROM playlist_revision "
260                               "WHERE guid = :guid" );
261        query_entries.bindValue( ":guid", m_oldrev );
262        query_entries.exec();
263        if ( query_entries.next() )
264        {
265            bool ok;
266            QVariant v = TomahawkUtils::parseJson( query_entries.value( 0 ).toByteArray(), &ok );
267            Q_ASSERT( ok && v.type() == QVariant::List ); //TODO
268
269            m_previous_rev_orderedguids = v.toStringList();
270        }
271    }
272    else if ( !m_oldrev.isEmpty() )
273    {
274        tDebug() << "Not updating current revision, optimistic locking fail" << currentRevision << m_oldrev;
275
276        // This will fail if we run two SetPlaylistRevisions commands on the same playlist concurrently
277        Q_ASSERT( !source()->isLocal() );
278    }
279}
280
281
282void
283DatabaseCommand_SetPlaylistRevision::setAddedentriesV( const QVariantList& vlist )
284{
285    m_addedentries.clear();
286    foreach( const QVariant& v, vlist )
287    {
288        PlaylistEntry* pep = new PlaylistEntry;
289        TomahawkUtils::qvariant2qobject( v.toMap(), pep );
290
291        if ( pep->isValid() )
292            m_addedentries << plentry_ptr( pep );
293    }
294}
295
296
297QVariantList
298DatabaseCommand_SetPlaylistRevision::addedentriesV() const
299{
300    QVariantList vlist;
301    foreach( const plentry_ptr& pe, m_addedentries )
302    {
303        if ( !pe->isValid() )
304            continue;
305
306        QVariant v = TomahawkUtils::qobject2qvariant( pe.data() );
307        vlist << v;
308    }
309    return vlist;
310}