File indexing completed on 2024-05-05 04:48:30
0001 /**************************************************************************************** 0002 * Copyright (c) 2008 Daniel Caleb Jones <danielcjones@gmail.com> * 0003 * Copyright (c) 2010, 2013 Ralf Engels <ralf-engels@gmx.de> * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify it under * 0006 * the terms of the GNU General Public License as published by the Free Software * 0007 * Foundation; either version 2 of the License, or (at your option) version 3 or * 0008 * any later version accepted by the membership of KDE e.V. (or its successor approved * 0009 * by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of * 0010 * version 3 of the license. * 0011 * * 0012 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0013 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0014 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0015 * * 0016 * You should have received a copy of the GNU General Public License along with * 0017 * this program. If not, see <http://www.gnu.org/licenses/>. * 0018 ****************************************************************************************/ 0019 0020 #define DEBUG_PREFIX "BiasSolver" 0021 0022 #include "BiasSolver.h" 0023 0024 #include "amarokconfig.h" 0025 #include "core/meta/Meta.h" 0026 #include "core/meta/support/MetaConstants.h" 0027 #include "core/support/Debug.h" 0028 #include "core/collections/QueryMaker.h" 0029 #include "core-impl/collections/support/CollectionManager.h" 0030 0031 #include <ThreadWeaver/Thread> 0032 0033 #include <QHash> 0034 #include <QMutexLocker> 0035 0036 #include <cmath> 0037 0038 /* These number are black magic. The best values can only be obtained through 0039 * exhaustive trial and error or writing another optimization program to 0040 * optimize this optimization program. They are very sensitive. Be careful */ 0041 0042 namespace Dynamic 0043 { 0044 0045 class SolverList 0046 { 0047 public: 0048 0049 SolverList( const Meta::TrackList &trackList, 0050 int contextCount, 0051 const BiasPtr &bias ) 0052 : m_trackList(trackList) 0053 , m_contextCount( contextCount ) 0054 , m_bias( bias ) 0055 {} 0056 0057 void appendTrack( const Meta::TrackPtr &track ) 0058 { 0059 m_trackList.append( track ); 0060 } 0061 0062 void removeTrack() 0063 { 0064 m_trackList.removeLast(); 0065 } 0066 0067 SolverList &operator=( const SolverList& x ) 0068 { 0069 m_trackList = x.m_trackList; 0070 m_contextCount = x.m_contextCount; 0071 m_bias = x.m_bias; 0072 0073 return *this; 0074 } 0075 0076 Meta::TrackList m_trackList; 0077 int m_contextCount; // the number of tracks belonging to the context 0078 BiasPtr m_bias; 0079 }; 0080 0081 0082 0083 BiasSolver::BiasSolver( int n, const BiasPtr &bias, const Meta::TrackList &context ) 0084 : m_n( n ) 0085 , m_bias( bias ) 0086 , m_context( context ) 0087 , m_abortRequested( false ) 0088 , m_currentProgress( 0 ) 0089 { 0090 debug() << "CREATING BiasSolver in thread:" << QThread::currentThreadId() << "to get"<<n<<"tracks with"<<context.count()<<"context"; 0091 0092 m_allowDuplicates = AmarokConfig::dynamicDuplicates(); 0093 0094 getTrackCollection(); 0095 0096 connect( m_bias.data(), &Dynamic::AbstractBias::resultReady, 0097 this, &BiasSolver::biasResultReady ); 0098 } 0099 0100 0101 BiasSolver::~BiasSolver() 0102 { 0103 debug() << "DESTROYING BiasSolver in thread:" << QThread::currentThreadId(); 0104 Q_EMIT endProgressOperation( this ); 0105 } 0106 0107 0108 void 0109 BiasSolver::requestAbort() 0110 { 0111 m_abortRequested = true; 0112 Q_EMIT endProgressOperation( this ); 0113 } 0114 0115 bool 0116 BiasSolver::success() const 0117 { 0118 return !m_abortRequested; 0119 } 0120 0121 void 0122 BiasSolver::setAutoDelete( bool autoDelete ) 0123 { 0124 if( autoDelete ) 0125 { 0126 if( isFinished() ) 0127 deleteLater(); 0128 connect( this, &BiasSolver::done, this, &BiasSolver::deleteLater ); 0129 } 0130 else 0131 { 0132 disconnect( this, &BiasSolver::done, this, &BiasSolver::deleteLater ); 0133 } 0134 } 0135 0136 0137 void 0138 BiasSolver::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread ) 0139 { 0140 Q_UNUSED(self); 0141 Q_UNUSED(thread); 0142 DEBUG_BLOCK 0143 0144 debug() << "BiasSolver::run in thread:" << QThread::currentThreadId(); 0145 m_startTime = QDateTime::currentDateTime(); 0146 0147 // wait until we get the track collection 0148 { 0149 QMutexLocker locker( &m_collectionResultsMutex ); 0150 if( !m_trackCollection ) 0151 { 0152 debug() << "waiting for collection results"; 0153 m_collectionResultsReady.wait( &m_collectionResultsMutex ); 0154 } 0155 debug() << "collection has" << m_trackCollection->count()<<"uids"; 0156 } 0157 0158 debug() << "generating playlist"; 0159 SolverList list( m_context, m_context.count(), m_bias ); 0160 addTracks( &list ); 0161 debug() << "found solution"<<list.m_trackList.count()<<"time"<< m_startTime.msecsTo( QDateTime::currentDateTime() ); 0162 0163 m_solution = list.m_trackList.mid( m_context.count() ); 0164 // setFinished( true ); 0165 setStatus(Status_Success); 0166 } 0167 0168 void 0169 BiasSolver::defaultBegin(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) 0170 { 0171 Q_EMIT started(self); 0172 ThreadWeaver::Job::defaultBegin(self, thread); 0173 } 0174 0175 void 0176 BiasSolver::defaultEnd(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) 0177 { 0178 ThreadWeaver::Job::defaultEnd(self, thread); 0179 if (!self->success()) { 0180 Q_EMIT failed(self); 0181 } 0182 Q_EMIT done(self); 0183 } 0184 0185 void 0186 BiasSolver::addTracks( SolverList *list ) 0187 { 0188 bool firstTrack = ( list->m_trackList.count() == list->m_contextCount ); 0189 0190 if( m_abortRequested ) 0191 return; 0192 0193 updateProgress( list ); 0194 0195 if( list->m_trackList.count() >= list->m_contextCount + m_n ) 0196 return; // we have all tracks 0197 0198 TrackSet set = matchingTracks( list->m_trackList ); 0199 if( !m_allowDuplicates ) 0200 set = withoutDuplicate( list->m_trackList.count(), list->m_trackList, set ); 0201 0202 if( set.trackCount() == 0 ) 0203 return; // no candidates 0204 0205 // debug() << "addTracks at"<<list->m_trackList.count()<<"candidates:"<<set.trackCount()<<"time"<< m_startTime.msecsTo( QDateTime::currentDateTime() ); 0206 0207 for( int tries = 0; tries < 5 || firstTrack ; tries++ ) 0208 { 0209 if( m_abortRequested ) 0210 return; 0211 0212 list->appendTrack( getRandomTrack( set ) ); 0213 addTracks( list ); // add another track recursively 0214 if( list->m_trackList.count() >= list->m_contextCount + m_n ) 0215 return; // we have all tracks 0216 0217 // if time is up just try to fill the list as much as possible not cleaning up 0218 if( m_startTime.msecsTo( QDateTime::currentDateTime() ) > MAX_TIME_MS ) 0219 return; 0220 0221 list->removeTrack(); 0222 } 0223 } 0224 0225 0226 Meta::TrackList 0227 BiasSolver::solution() 0228 { 0229 return m_solution; 0230 } 0231 0232 0233 Meta::TrackPtr 0234 BiasSolver::getRandomTrack( const TrackSet& subset ) const 0235 { 0236 if( subset.trackCount() == 0 ) 0237 return Meta::TrackPtr(); 0238 0239 Meta::TrackPtr track; 0240 0241 // this is really dumb, but we sometimes end up with uids that don't point to anything 0242 int giveup = 50; 0243 while( giveup-- && !track ) 0244 track = trackForUid( subset.getRandomTrack() ); 0245 0246 if( !track ) 0247 error() << "track is 0 in BiasSolver::getRandomTrack()"; 0248 0249 return track; 0250 } 0251 0252 Meta::TrackPtr 0253 BiasSolver::trackForUid( const QString& uid ) const 0254 { 0255 const QUrl url( uid ); 0256 Meta::TrackPtr track = CollectionManager::instance()->trackForUrl( url ); 0257 0258 if( !track ) 0259 warning() << "trackForUid returned no track for "<<uid; 0260 return track; 0261 } 0262 0263 0264 // ---- getting the matchingTracks ---- 0265 0266 void 0267 BiasSolver::biasResultReady( const TrackSet &set ) 0268 { 0269 QMutexLocker locker( &m_biasResultsMutex ); 0270 m_tracks = set; 0271 m_biasResultsReady.wakeAll(); 0272 } 0273 0274 TrackSet 0275 BiasSolver::matchingTracks( const Meta::TrackList& playlist ) const 0276 { 0277 QMutexLocker locker( &m_biasResultsMutex ); 0278 m_tracks = m_bias->matchingTracks( playlist, 0279 m_context.count(), m_context.count() + m_n, 0280 m_trackCollection ); 0281 if( m_tracks.isOutstanding() ) 0282 m_biasResultsReady.wait( &m_biasResultsMutex ); 0283 0284 // debug() << "BiasSolver::matchingTracks returns"<<m_tracks.trackCount()<<"of"<<m_trackCollection->count()<<"tracks."; 0285 0286 return m_tracks; 0287 } 0288 0289 Dynamic::TrackSet 0290 BiasSolver::withoutDuplicate( int position, const Meta::TrackList& playlist, 0291 const Dynamic::TrackSet& oldSet ) 0292 { 0293 Dynamic::TrackSet result = Dynamic::TrackSet( oldSet ); 0294 for( int i = 0; i < playlist.count(); i++ ) 0295 if( i != position && playlist[i] ) 0296 result.subtract( playlist[i] ); 0297 0298 return result; 0299 } 0300 0301 0302 // ---- getting the TrackCollection ---- 0303 0304 void 0305 BiasSolver::trackCollectionResultsReady( const QStringList &uids ) 0306 { 0307 m_collectionUids.append( uids ); 0308 } 0309 0310 void 0311 BiasSolver::trackCollectionDone() 0312 { 0313 QMutexLocker locker( &m_collectionResultsMutex ); 0314 0315 m_trackCollection = TrackCollectionPtr( new TrackCollection( m_collectionUids ) ); 0316 m_collectionUids.clear(); 0317 0318 m_collectionResultsReady.wakeAll(); 0319 } 0320 0321 void 0322 BiasSolver::getTrackCollection() 0323 { 0324 // get all the unique ids from the collection manager 0325 Collections::QueryMaker *qm = CollectionManager::instance()->queryMaker(); 0326 qm->setQueryType( Collections::QueryMaker::Custom ); 0327 qm->addReturnValue( Meta::valUniqueId ); 0328 qm->setAutoDelete( true ); 0329 0330 connect( qm, &Collections::QueryMaker::newResultReady, 0331 this, &BiasSolver::trackCollectionResultsReady ); 0332 connect( qm, &Collections::QueryMaker::queryDone, 0333 this, &BiasSolver::trackCollectionDone ); 0334 0335 qm->run(); 0336 } 0337 0338 void 0339 BiasSolver::updateProgress( const SolverList* list ) 0340 { 0341 if( m_n <= 0 ) 0342 return; 0343 0344 int progress = (int)(100.0 * (double)( list->m_trackList.count() - list->m_contextCount ) / m_n ); 0345 0346 while( m_currentProgress < progress ) 0347 { 0348 m_currentProgress++; 0349 Q_EMIT incrementProgress(); 0350 } 0351 } 0352 0353 0354 } 0355