/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. #include "DatabaseCommand_SetPlaylistRevision.h"
  19. #include "collection/Collection.h"
  20. #include "network/Servent.h"
  21. #include "utils/Json.h"
  22. #include "utils/Logger.h"
  23. #include "DatabaseImpl.h"
  24. #include "PlaylistEntry.h"
  25. #include "Source.h"
  26. #include "TomahawkSqlQuery.h"
  27. #include "Track.h"
  28. #include <QSqlQuery>
  29. using namespace Tomahawk;
  30. DatabaseCommand_SetPlaylistRevision::DatabaseCommand_SetPlaylistRevision(
  31. const source_ptr& s,
  32. const QString& playlistguid,
  33. const QString& newrev,
  34. const QString& oldrev,
  35. const QStringList& orderedguids,
  36. const QList<plentry_ptr>& addedentries,
  37. const QList<plentry_ptr>& entries )
  38. : DatabaseCommandLoggable( s )
  39. , m_failed( false )
  40. , m_applied( false )
  41. , m_newrev( newrev )
  42. , m_oldrev( oldrev )
  43. , m_addedentries( addedentries )
  44. , m_entries( entries )
  45. , m_metadataUpdate( false )
  46. {
  47. Q_ASSERT( !newrev.isEmpty() );
  48. m_localOnly = ( newrev == oldrev );
  49. setPlaylistguid( playlistguid );
  50. QVariantList tmp;
  51. foreach( const QString& s, orderedguids )
  52. tmp << s;
  53. setOrderedguids( tmp );
  54. }
  55. DatabaseCommand_SetPlaylistRevision::DatabaseCommand_SetPlaylistRevision(
  56. const source_ptr& s,
  57. const QString& playlistguid,
  58. const QString& newrev,
  59. const QString& oldrev,
  60. const QStringList& orderedguids,
  61. const QList<plentry_ptr>& entriesToUpdate )
  62. : DatabaseCommandLoggable( s )
  63. , m_failed( false )
  64. , m_applied( false )
  65. , m_newrev( newrev )
  66. , m_oldrev( oldrev )
  67. , m_entries( entriesToUpdate )
  68. , m_metadataUpdate( true )
  69. {
  70. Q_ASSERT( !newrev.isEmpty() );
  71. m_localOnly = false;
  72. QVariantList tmp;
  73. foreach( const QString& s, orderedguids )
  74. tmp << s;
  75. setOrderedguids( tmp );
  76. setPlaylistguid( playlistguid );
  77. }
  78. void
  79. DatabaseCommand_SetPlaylistRevision::postCommitHook()
  80. {
  81. tDebug() << Q_FUNC_INFO;
  82. if ( m_localOnly )
  83. return;
  84. QStringList orderedentriesguids;
  85. foreach( const QVariant& v, m_orderedguids )
  86. orderedentriesguids << v.toString();
  87. // private, but we are a friend. will recall itself in its own thread:
  88. playlist_ptr playlist = source()->dbCollection()->playlist( m_playlistguid );
  89. // Q_ASSERT( !playlist.isNull() );
  90. if ( !playlist )
  91. return;
  92. if ( playlist->loaded() )
  93. {
  94. playlist->setRevision( m_newrev,
  95. orderedentriesguids,
  96. m_previous_rev_orderedguids,
  97. true, // this *is* the newest revision so far
  98. m_addedmap,
  99. m_applied );
  100. }
  101. else
  102. playlist->setCurrentrevision( m_newrev );
  103. if ( source()->isLocal() )
  104. Servent::instance()->triggerDBSync();
  105. }
  106. void
  107. DatabaseCommand_SetPlaylistRevision::exec( DatabaseImpl* lib )
  108. {
  109. QString currentRevision;
  110. // get the current revision for this playlist
  111. // this also serves to check the playlist exists.
  112. TomahawkSqlQuery chkq = lib->newquery();
  113. chkq.prepare( "SELECT currentrevision FROM playlist WHERE guid = ?" );
  114. chkq.addBindValue( m_playlistguid );
  115. if ( chkq.exec() && chkq.next() )
  116. {
  117. currentRevision = chkq.value( 0 ).toString();
  118. tDebug() << Q_FUNC_INFO << "pl guid" << m_playlistguid << "- curr rev" << currentRevision << source()->friendlyName() << source()->id();
  119. }
  120. else
  121. {
  122. tDebug() << "ERROR: No such playlist:" << m_playlistguid << currentRevision << source()->friendlyName() << source()->id();
  123. // Q_ASSERT_X( false, "DatabaseCommand_SetPlaylistRevision::exec", "No such playlist, WTF?" );
  124. m_failed = true;
  125. return;
  126. }
  127. QVariantList vlist = m_orderedguids;
  128. const QByteArray entries = TomahawkUtils::toJson( vlist );
  129. // add any new items:
  130. TomahawkSqlQuery adde = lib->newquery();
  131. if ( m_localOnly )
  132. {
  133. QString sql = "UPDATE playlist_item SET result_hint = ? WHERE guid = ?";
  134. adde.prepare( sql );
  135. foreach( const plentry_ptr& e, m_entries )
  136. {
  137. if ( !e->isValid() )
  138. continue;
  139. if ( !e->query()->numResults() )
  140. continue;
  141. adde.bindValue( 0, e->resultHint() );
  142. adde.bindValue( 1, e->guid() );
  143. adde.exec();
  144. }
  145. return;
  146. }
  147. else if ( m_metadataUpdate )
  148. {
  149. QString sql = "UPDATE playlist_item SET trackname = ?, artistname = ?, albumname = ?, annotation = ?, duration = ?, addedon = ?, addedby = ? WHERE guid = ?";
  150. adde.prepare( sql );
  151. foreach( const plentry_ptr& e, m_entries )
  152. {
  153. if ( !e->isValid() )
  154. continue;
  155. adde.bindValue( 0, e->query()->queryTrack()->track() );
  156. adde.bindValue( 1, e->query()->queryTrack()->artist() );
  157. adde.bindValue( 2, e->query()->queryTrack()->album() );
  158. adde.bindValue( 3, e->annotation() );
  159. adde.bindValue( 4, (int) e->duration() );
  160. adde.bindValue( 5, e->lastmodified() );
  161. adde.bindValue( 6, source()->isLocal() ? QVariant(QVariant::Int) : source()->id() );
  162. adde.bindValue( 7, e->guid() );
  163. adde.exec();
  164. }
  165. }
  166. else
  167. {
  168. QString sql = "REPLACE INTO playlist_item( guid, playlist, trackname, artistname, albumname, "
  169. "annotation, duration, addedon, addedby, result_hint ) "
  170. "VALUES( ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )";
  171. adde.prepare( sql );
  172. // qDebug() << "Num new playlist_items to add:" << m_addedentries.length();
  173. foreach( const plentry_ptr& e, m_addedentries )
  174. {
  175. if ( !e->isValid() )
  176. continue;
  177. // qDebug() << "Adding:" << e->guid() << e->query()->track() << e->query()->artist();
  178. m_addedmap.insert( e->guid(), e ); // needed in postcommithook
  179. adde.bindValue( 0, e->guid() );
  180. adde.bindValue( 1, m_playlistguid );
  181. adde.bindValue( 2, e->query()->queryTrack()->track() );
  182. adde.bindValue( 3, e->query()->queryTrack()->artist() );
  183. adde.bindValue( 4, e->query()->queryTrack()->album() );
  184. adde.bindValue( 5, e->annotation() );
  185. adde.bindValue( 6, (int) e->duration() );
  186. adde.bindValue( 7, e->lastmodified() );
  187. adde.bindValue( 8, source()->isLocal() ? QVariant(QVariant::Int) : source()->id() );
  188. adde.bindValue( 9, e->resultHint() );
  189. adde.exec();
  190. }
  191. }
  192. // add / update the revision:
  193. TomahawkSqlQuery query = lib->newquery();
  194. QString sql = "INSERT INTO playlist_revision(guid, playlist, entries, author, timestamp, previous_revision) "
  195. "VALUES(?, ?, ?, ?, ?, ?)";
  196. query.prepare( sql );
  197. query.addBindValue( m_newrev );
  198. query.addBindValue( m_playlistguid );
  199. query.addBindValue( entries );
  200. query.addBindValue( source()->isLocal() ? QVariant(QVariant::Int) : source()->id() );
  201. query.addBindValue( 0 ); //ts
  202. query.addBindValue( m_oldrev.isEmpty() ? QVariant(QVariant::String) : m_oldrev );
  203. query.exec();
  204. tDebug() << "Currentrevision:" << currentRevision << "oldrev:" << m_oldrev;
  205. // if optimistic locking is ok, update current revision to this new one
  206. if ( currentRevision == m_oldrev )
  207. {
  208. tDebug() << "Updating current revision, optimistic locking ok" << m_newrev;
  209. TomahawkSqlQuery query2 = lib->newquery();
  210. query2.prepare( "UPDATE playlist SET currentrevision = ? WHERE guid = ?" );
  211. query2.bindValue( 0, m_newrev );
  212. query2.bindValue( 1, m_playlistguid );
  213. query2.exec();
  214. m_applied = true;
  215. // load previous revision entries, which we need to pass on
  216. // so the change can be diffed
  217. TomahawkSqlQuery query_entries = lib->newquery();
  218. query_entries.prepare( "SELECT entries, playlist, author, timestamp, previous_revision "
  219. "FROM playlist_revision "
  220. "WHERE guid = :guid" );
  221. query_entries.bindValue( ":guid", m_oldrev );
  222. query_entries.exec();
  223. if ( query_entries.next() )
  224. {
  225. bool ok;
  226. QVariant v = TomahawkUtils::parseJson( query_entries.value( 0 ).toByteArray(), &ok );
  227. Q_ASSERT( ok && v.type() == QVariant::List ); //TODO
  228. m_previous_rev_orderedguids = v.toStringList();
  229. }
  230. }
  231. else if ( !m_oldrev.isEmpty() )
  232. {
  233. tDebug() << "Not updating current revision, optimistic locking fail" << currentRevision << m_oldrev;
  234. // This will fail if we run two SetPlaylistRevisions commands on the same playlist concurrently
  235. Q_ASSERT( !source()->isLocal() );
  236. }
  237. }
  238. void
  239. DatabaseCommand_SetPlaylistRevision::setAddedentriesV( const QVariantList& vlist )
  240. {
  241. m_addedentries.clear();
  242. foreach( const QVariant& v, vlist )
  243. {
  244. PlaylistEntry* pep = new PlaylistEntry;
  245. TomahawkUtils::qvariant2qobject( v.toMap(), pep );
  246. if ( pep->isValid() )
  247. m_addedentries << plentry_ptr( pep );
  248. }
  249. }
  250. QVariantList
  251. DatabaseCommand_SetPlaylistRevision::addedentriesV() const
  252. {
  253. QVariantList vlist;
  254. foreach( const plentry_ptr& pe, m_addedentries )
  255. {
  256. if ( !pe->isValid() )
  257. continue;
  258. QVariant v = TomahawkUtils::qobject2qvariant( pe.data() );
  259. vlist << v;
  260. }
  261. return vlist;
  262. }