File indexing completed on 2024-05-19 04:50:28
0001 /**************************************************************************************** 0002 * Copyright (c) 2012 Matěj Laitl <matej@laitl.cz> * 0003 * * 0004 * This program is free software; you can redistribute it and/or modify it under * 0005 * the terms of the GNU General Public License as published by the Free Software * 0006 * Foundation; either version 2 of the License, or (at your option) any later * 0007 * version. * 0008 * * 0009 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0010 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0011 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0012 * * 0013 * You should have received a copy of the GNU General Public License along with * 0014 * this program. If not, see <http://www.gnu.org/licenses/>. * 0015 ****************************************************************************************/ 0016 0017 #include "SynchronizeTracksJob.h" 0018 0019 #include "core/meta/Meta.h" 0020 #include "core/meta/Statistics.h" 0021 #include "core/support/Components.h" 0022 #include "core/support/Debug.h" 0023 #include "statsyncing/Controller.h" 0024 #include "statsyncing/TrackTuple.h" 0025 0026 #include <ThreadWeaver/Thread> 0027 0028 using namespace StatSyncing; 0029 0030 static const int denom = 20; // Q_EMIT incementProgress() signal each N tracks 0031 static const int fuzz = denom / 2; 0032 0033 SynchronizeTracksJob::SynchronizeTracksJob( const QList<TrackTuple> &tuples, 0034 const TrackList &tracksToScrobble, 0035 const Options &options, QObject *parent ) 0036 : QObject( parent ) 0037 , ThreadWeaver::Job() 0038 , m_abort( false ) 0039 , m_tuples( tuples ) 0040 , m_tracksToScrobble( tracksToScrobble ) 0041 , m_updatedTracksCount( 0 ) 0042 , m_options( options ) 0043 { 0044 } 0045 0046 void 0047 SynchronizeTracksJob::abort() 0048 { 0049 m_abort = true; 0050 } 0051 0052 void 0053 SynchronizeTracksJob::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread) 0054 { 0055 Q_UNUSED(self); 0056 Q_UNUSED(thread); 0057 Q_EMIT totalSteps( ( m_tuples.size() + fuzz ) / denom ); 0058 0059 Controller *controller = Amarok::Components::statSyncingController(); 0060 if( controller ) 0061 { 0062 connect( this, &SynchronizeTracksJob::scrobble, 0063 controller, &StatSyncing::Controller::scrobble ); 0064 // we don't run an event loop, we must use direct connection for controller to talk to us 0065 connect( controller, &StatSyncing::Controller::trackScrobbled, 0066 this, &SynchronizeTracksJob::slotTrackScrobbled, Qt::DirectConnection ); 0067 connect( controller, &StatSyncing::Controller::scrobbleFailed, 0068 this, &SynchronizeTracksJob::slotScrobbleFailed, Qt::DirectConnection ); 0069 } 0070 else 0071 warning() << __PRETTY_FUNCTION__ << "StatSyncing::Controller not available!"; 0072 0073 // first, queue tracks for scrobbling, because after syncing their recent playcount is 0074 // reset 0075 foreach( const TrackPtr &track, m_tracksToScrobble ) 0076 { 0077 Meta::TrackPtr metaTrack = track->metaTrack(); 0078 int playcount = track->recentPlayCount(); 0079 if( metaTrack && playcount > 0 ) 0080 { 0081 m_scrobbledTracks << metaTrack; 0082 Q_EMIT scrobble( metaTrack, playcount, track->lastPlayed() ); 0083 } 0084 } 0085 0086 ProviderPtrSet updatedProviders; 0087 int i = 0; 0088 foreach( const TrackTuple &tuple, m_tuples ) 0089 { 0090 if( m_abort ) 0091 break; 0092 0093 // no point in checking for hasUpdate() here, synchronize() is witty enough 0094 const ProviderPtrSet tupleUpdatedProviders = tuple.synchronize( m_options ); 0095 updatedProviders |= tupleUpdatedProviders; 0096 m_updatedTracksCount += tupleUpdatedProviders.count(); 0097 if( ( i + fuzz ) % denom == 0 ) 0098 Q_EMIT incrementProgress(); 0099 i++; 0100 } 0101 0102 foreach( ProviderPtr provider, updatedProviders ) 0103 provider->commitTracks(); 0104 0105 0106 // we need to reset playCount of scrobbled tracks to reset their recent play count 0107 foreach( Meta::TrackPtr track, m_scrobbledTracks ) 0108 { 0109 Meta::StatisticsPtr statistics = track->statistics(); 0110 statistics->setPlayCount( statistics->playCount() ); 0111 } 0112 0113 if( !m_tracksToScrobble.isEmpty() ) 0114 // wait 3 seconds so that we have chance to catch slotTrackScrobbled().. 0115 QObject::thread()->msleep( 3000 ); 0116 if( controller ) 0117 disconnect( controller, &StatSyncing::Controller::trackScrobbled, this, nullptr ); 0118 disconnect( controller, &StatSyncing::Controller::scrobbleFailed, this, nullptr ); 0119 0120 Q_EMIT endProgressOperation( this ); 0121 } 0122 0123 void SynchronizeTracksJob::defaultBegin(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) 0124 { 0125 Q_EMIT started(self); 0126 ThreadWeaver::Job::defaultBegin(self, thread); 0127 } 0128 0129 void SynchronizeTracksJob::defaultEnd(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) 0130 { 0131 ThreadWeaver::Job::defaultEnd(self, thread); 0132 if (!self->success()) { 0133 Q_EMIT failed(self); 0134 } 0135 Q_EMIT done(self); 0136 } 0137 0138 void 0139 SynchronizeTracksJob::slotTrackScrobbled( const ScrobblingServicePtr &service, 0140 const Meta::TrackPtr &track ) 0141 { 0142 slotScrobbleFailed( service, track, ScrobblingService::NoError ); 0143 } 0144 0145 void 0146 SynchronizeTracksJob::slotScrobbleFailed( const ScrobblingServicePtr &service, 0147 const Meta::TrackPtr &track, int error ) 0148 { 0149 // only count tracks scrobbled by us. Still chance for false-positives, though 0150 if( m_scrobbledTracks.contains( track ) ) 0151 { 0152 ScrobblingService::ScrobbleError errorEnum = ScrobblingService::ScrobbleError( error ); 0153 m_scrobbles[ service ][ errorEnum ]++; 0154 } 0155 } 0156 0157 int 0158 SynchronizeTracksJob::updatedTracksCount() const 0159 { 0160 return m_updatedTracksCount; 0161 } 0162 0163 QMap<ScrobblingServicePtr, QMap<ScrobblingService::ScrobbleError, int> > 0164 SynchronizeTracksJob::scrobbles() 0165 { 0166 return m_scrobbles; 0167 }