File indexing completed on 2024-05-19 04:50:25
0001 /**************************************************************************************** 0002 * Copyright (c) 2007 Nikolaj Hald Nielsen <nhn@kde.org> * 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 #define DEBUG_PREFIX "ScriptableServiceQueryMaker" 0018 0019 #include "ScriptableServiceQueryMaker.h" 0020 0021 #include "core/meta/support/MetaConstants.h" 0022 #include "core/support/Debug.h" 0023 #include "core-impl/collections/support/MemoryMatcher.h" 0024 #include "scripting/scriptmanager/ScriptManager.h" 0025 #include "services/scriptable/ScriptableServiceMeta.h" 0026 0027 #include <QTimer> 0028 0029 using namespace Collections; 0030 0031 struct ScriptableServiceQueryMaker::Private { 0032 //don't change the order of items in this enum 0033 enum QueryType { TRACK=1, ALBUM=2, ARTIST=3, GENRE=4, NONE=5 }; 0034 QueryType type; 0035 QueryType closestParent; 0036 int maxsize; 0037 QString callbackString; 0038 int parentId; 0039 AlbumQueryMode albumMode; 0040 QString filter; 0041 QString lastFilter; 0042 }; 0043 0044 ScriptableServiceQueryMaker::ScriptableServiceQueryMaker( ScriptableServiceCollection * collection, const QString &name ) 0045 : DynamicServiceQueryMaker() 0046 , d( new Private ) 0047 , m_convertToMultiTracks( false ) 0048 { 0049 setParent( collection ); 0050 m_collection = collection; 0051 m_name = name; 0052 0053 connect( collection, &Collections::ScriptableServiceCollection::updateComplete, 0054 this, &ScriptableServiceQueryMaker::slotScriptComplete ); 0055 0056 d->type = Private::NONE; 0057 d->closestParent = Private::NONE; 0058 d->maxsize = -1; 0059 d->parentId = -1; 0060 d->albumMode = AllAlbums; 0061 } 0062 0063 ScriptableServiceQueryMaker::~ScriptableServiceQueryMaker() 0064 { 0065 delete d; 0066 } 0067 0068 0069 void ScriptableServiceQueryMaker::run() 0070 { 0071 if ( d->albumMode == OnlyCompilations ) 0072 return; 0073 0074 if ( d->type == Private::NONE ) 0075 //TODO error handling 0076 return; 0077 0078 if ( d->callbackString.isEmpty() ) 0079 d->callbackString = QStringLiteral("none"); 0080 0081 0082 if ( d->type == Private::GENRE ) { 0083 if ( ( m_collection->levels() == 4 ) && ( m_collection->lastFilter() != d->filter ) ) 0084 { 0085 m_collection->clear(); 0086 } 0087 QTimer::singleShot( 0, this, &ScriptableServiceQueryMaker::fetchGenre ); 0088 } 0089 else if ( d->type == Private::ARTIST ) 0090 { 0091 if ( ( m_collection->levels() == 3 ) && ( m_collection->lastFilter() != d->filter ) ) 0092 { 0093 m_collection->clear(); 0094 } 0095 QTimer::singleShot( 0, this, &ScriptableServiceQueryMaker::fetchArtists ); 0096 } 0097 else if ( d->type == Private::ALBUM ) 0098 { 0099 if ( ( m_collection->levels() == 2 ) && ( m_collection->lastFilter() != d->filter ) ) 0100 { 0101 m_collection->clear(); 0102 } 0103 QTimer::singleShot( 0, this, &ScriptableServiceQueryMaker::fetchAlbums ); 0104 } 0105 else if ( d->type == Private::TRACK ) 0106 { 0107 if ( ( m_collection->levels() == 1 ) && ( m_collection->lastFilter() != d->filter ) ) 0108 { 0109 m_collection->clear(); 0110 } 0111 QTimer::singleShot( 0, this, &ScriptableServiceQueryMaker::fetchTracks ); 0112 } 0113 0114 } 0115 0116 void ScriptableServiceQueryMaker::abortQuery() 0117 { 0118 } 0119 0120 QueryMaker * ScriptableServiceQueryMaker::setQueryType( QueryType type ) 0121 { 0122 switch( type ) { 0123 case QueryMaker::Artist: 0124 case QueryMaker::AlbumArtist: 0125 d->type = Private::ARTIST; 0126 return this; 0127 0128 case QueryMaker::Album: 0129 d->type = Private::ALBUM; 0130 return this; 0131 0132 case QueryMaker::Track: 0133 d->type = Private::TRACK; 0134 return this; 0135 0136 case QueryMaker::Genre: 0137 d->type = Private::GENRE; 0138 return this; 0139 0140 case QueryMaker::Composer: 0141 case QueryMaker::Year: 0142 case QueryMaker::Custom: 0143 case QueryMaker::Label: 0144 case QueryMaker::None: 0145 //TODO: Implement. 0146 return this; 0147 } 0148 0149 return this; 0150 } 0151 0152 QueryMaker * ScriptableServiceQueryMaker::addMatch( const Meta::GenrePtr &genre ) 0153 { 0154 if ( d->closestParent > Private::GENRE ) 0155 { 0156 d->closestParent = Private::GENRE; 0157 const Meta::ScriptableServiceGenre * scriptableGenre = static_cast< const Meta::ScriptableServiceGenre * >( genre.data() ); 0158 d->callbackString = scriptableGenre->callbackString(); 0159 d->parentId = scriptableGenre->id(); 0160 } 0161 return this; 0162 } 0163 0164 QueryMaker * ScriptableServiceQueryMaker::addMatch( const Meta::ArtistPtr & artist, QueryMaker::ArtistMatchBehaviour behaviour ) 0165 { 0166 Q_UNUSED( behaviour ); 0167 const Meta::ScriptableServiceArtist *scriptableArtist = dynamic_cast<const Meta::ScriptableServiceArtist *>( artist.data() ); 0168 if ( scriptableArtist && d->closestParent > Private::ARTIST ) 0169 { 0170 d->closestParent = Private::ARTIST; 0171 d->callbackString = scriptableArtist->callbackString(); 0172 d->parentId = scriptableArtist->id(); 0173 } 0174 return this; 0175 } 0176 0177 QueryMaker * ScriptableServiceQueryMaker::addMatch( const Meta::AlbumPtr & album ) 0178 { 0179 if ( d->closestParent > Private::ALBUM ) 0180 { 0181 d->closestParent = Private::ALBUM; 0182 debug() << "Here!"; 0183 const Meta::ScriptableServiceAlbum * scriptableAlbum = static_cast< const Meta::ScriptableServiceAlbum * >( album.data() ); 0184 d->callbackString = scriptableAlbum->callbackString(); 0185 d->parentId = scriptableAlbum->id(); 0186 } 0187 return this; 0188 } 0189 0190 void 0191 ScriptableServiceQueryMaker::setConvertToMultiTracks( bool convert ) 0192 { 0193 m_convertToMultiTracks = convert; 0194 } 0195 0196 void ScriptableServiceQueryMaker::handleResult( const Meta::GenreList & genres ) 0197 { 0198 if ( d->maxsize >= 0 && genres.count() > d->maxsize ) 0199 Q_EMIT newGenresReady( genres.mid( 0, d->maxsize ) ); 0200 else 0201 Q_EMIT newGenresReady( genres ); 0202 } 0203 0204 void ScriptableServiceQueryMaker::handleResult( const Meta::AlbumList & albums ) 0205 { 0206 if ( d->maxsize >= 0 && albums.count() > d->maxsize ) 0207 Q_EMIT newAlbumsReady( albums.mid( 0, d->maxsize ) ); 0208 else 0209 Q_EMIT newAlbumsReady( albums ); 0210 } 0211 0212 void ScriptableServiceQueryMaker::handleResult( const Meta::ArtistList & artists ) 0213 { 0214 if ( d->maxsize >= 0 && artists.count() > d->maxsize ) 0215 Q_EMIT newArtistsReady( artists.mid( 0, d->maxsize ) ); 0216 else 0217 Q_EMIT newArtistsReady( artists ); 0218 } 0219 0220 void ScriptableServiceQueryMaker::handleResult( const Meta::TrackList &tracks ) 0221 { 0222 Meta::TrackList ret; 0223 if( m_convertToMultiTracks ) 0224 { 0225 foreach( const Meta::TrackPtr &track, tracks ) 0226 { 0227 using namespace Meta; 0228 const ScriptableServiceTrack *serviceTrack = 0229 dynamic_cast<const ScriptableServiceTrack *>( track.data() ); 0230 if( !serviceTrack ) 0231 { 0232 error() << "failed to convert generic track" << track.data() << "to ScriptableServiceTrack"; 0233 continue; 0234 } 0235 ret << serviceTrack->playableTrack(); 0236 } 0237 } 0238 else 0239 ret = tracks; 0240 0241 if ( d->maxsize >= 0 && ret.count() > d->maxsize ) 0242 Q_EMIT newTracksReady( ret.mid( 0, d->maxsize ) ); 0243 else 0244 Q_EMIT newTracksReady( ret ); 0245 } 0246 0247 void ScriptableServiceQueryMaker::fetchGenre() 0248 { 0249 DEBUG_BLOCK 0250 Meta::GenreList genre = m_collection->genreMap().values(); 0251 0252 if ( genre.count() > 0 ) 0253 { 0254 handleResult( genre ); 0255 Q_EMIT( queryDone() ); 0256 } 0257 else 0258 //this is where we call the script to get it to add more stuff! 0259 ScriptManager::instance()->ServiceScriptPopulate( m_name, 3, d->parentId, d->callbackString, d->filter ); 0260 } 0261 0262 void ScriptableServiceQueryMaker::fetchArtists() 0263 { 0264 DEBUG_BLOCK 0265 Meta::ArtistList artists; 0266 0267 if ( d->parentId != -1 ) 0268 { 0269 Meta::GenrePtr genrePtr = m_collection->genreById( d->parentId ); 0270 Meta::ScriptableServiceGenre * scGenre = dynamic_cast<Meta::ScriptableServiceGenre *> ( genrePtr.data() ); 0271 if ( scGenre ) 0272 { 0273 Meta::ArtistList allArtists = m_collection->artistMap().values(); 0274 0275 foreach ( Meta::ArtistPtr artistPtr, allArtists ) 0276 { 0277 Meta::ScriptableServiceArtist *scArtist = dynamic_cast<Meta::ScriptableServiceArtist *> ( artistPtr.data() ); 0278 if ( scArtist && scArtist->genreId() == d->parentId ) 0279 artists.append( artistPtr ); 0280 } 0281 } 0282 } 0283 0284 if ( artists.count() > 0 ) 0285 { 0286 handleResult( artists ); 0287 Q_EMIT( queryDone() ); 0288 } 0289 else 0290 //this is where we call the script to get it to add more stuff! 0291 ScriptManager::instance()->ServiceScriptPopulate( m_name, 2, d->parentId, d->callbackString, d->filter ); 0292 } 0293 0294 void ScriptableServiceQueryMaker::fetchAlbums() 0295 { 0296 DEBUG_BLOCK 0297 debug() << "parent id: " << d->parentId; 0298 0299 if ( d->albumMode == OnlyCompilations) 0300 return; 0301 0302 Meta::AlbumList albums; 0303 0304 if ( d->parentId != -1 ) 0305 { 0306 albums = matchAlbums( m_collection, m_collection->artistById( d->parentId ) ); 0307 } 0308 else 0309 albums = m_collection->albumMap().values(); 0310 if ( albums.count() > 0 ) 0311 { 0312 handleResult( albums ); 0313 Q_EMIT( queryDone() ); 0314 } 0315 else 0316 //this is where we call the script to get it to add more stuff! 0317 ScriptManager::instance()->ServiceScriptPopulate( m_name, 1, d->parentId, d->callbackString, d->filter ); 0318 } 0319 0320 void ScriptableServiceQueryMaker::fetchTracks() 0321 { 0322 DEBUG_BLOCK 0323 0324 Meta::TrackList tracks; 0325 0326 debug() << "parent id: " << d->parentId; 0327 0328 Meta::AlbumPtr album; 0329 if ( d->parentId != -1 && ( album = m_collection->albumById( d->parentId ) ) ) 0330 { 0331 AlbumMatcher albumMatcher( album ); 0332 tracks = albumMatcher.match( m_collection->trackMap().values() ); 0333 } 0334 else 0335 tracks = m_collection->trackMap().values(); 0336 0337 if ( tracks.count() > 0 ) { 0338 handleResult( tracks ); 0339 Q_EMIT( queryDone() ); 0340 } 0341 else 0342 //this is where we call the script to get it to add more stuff! 0343 { 0344 debug() << "i am sending signals!"; 0345 ScriptManager::instance()->ServiceScriptPopulate( m_name, 0, d->parentId, d->callbackString, d->filter ); 0346 } 0347 } 0348 0349 void ScriptableServiceQueryMaker::slotScriptComplete() 0350 { 0351 DEBUG_BLOCK 0352 0353 if ( d->type == Private::GENRE ) 0354 { 0355 Meta::GenreList genre = m_collection->genreMap().values(); 0356 handleResult( genre ); 0357 } 0358 else if ( d->type == Private::ARTIST ) 0359 { 0360 Meta::ArtistList artists; 0361 if ( d->parentId != -1 ) 0362 { 0363 Meta::GenrePtr genrePtr = m_collection->genreById( d->parentId ); 0364 Meta::ScriptableServiceGenre * scGenre = dynamic_cast<Meta::ScriptableServiceGenre *> ( genrePtr.data() ); 0365 if ( scGenre ) 0366 { 0367 Meta::ArtistList allArtists = m_collection->artistMap().values(); 0368 0369 foreach ( Meta::ArtistPtr artistPtr, allArtists ) 0370 { 0371 Meta::ScriptableServiceArtist *scArtist = dynamic_cast<Meta::ScriptableServiceArtist *> ( artistPtr.data() ); 0372 0373 if ( scArtist && scArtist->genreId() == d->parentId ) 0374 artists.append( artistPtr ); 0375 } 0376 } 0377 } 0378 else 0379 artists = m_collection->artistMap().values(); 0380 debug() << "there are " << artists.count() << " artists"; 0381 handleResult( artists ); 0382 } 0383 else if ( d->type == Private::ALBUM ) 0384 { 0385 Meta::AlbumList albums; 0386 if ( d->parentId != -1 ) 0387 { 0388 albums = matchAlbums( m_collection, m_collection->artistById( d->parentId ) ); 0389 } 0390 else 0391 albums = m_collection->albumMap().values(); 0392 0393 debug() << "there are " << albums.count() << " albums"; 0394 handleResult( albums ); 0395 } 0396 else if ( d->type == Private::TRACK ) 0397 { 0398 Meta::TrackList tracks; 0399 if ( d->parentId != -1 ) 0400 { 0401 Meta::AlbumPtr album = m_collection->albumById( d->parentId ); 0402 if( album ) 0403 { 0404 AlbumMatcher albumMatcher( album ); 0405 tracks = albumMatcher.match( m_collection->trackMap().values() ); 0406 } 0407 } 0408 else 0409 tracks = m_collection->trackMap().values(); 0410 debug() << "there are " << tracks.count() << " tracks"; 0411 handleResult( tracks ); 0412 } 0413 Q_EMIT( queryDone() ); 0414 } 0415 0416 QueryMaker * ScriptableServiceQueryMaker::setAlbumQueryMode( AlbumQueryMode mode ) 0417 { 0418 d->albumMode = mode; 0419 return this; 0420 } 0421 0422 QueryMaker * ScriptableServiceQueryMaker::addFilter( qint64 value, const QString & filter, bool matchBegin, bool matchEnd ) 0423 { 0424 Q_UNUSED( matchBegin ) 0425 Q_UNUSED( matchEnd ) 0426 0427 DEBUG_BLOCK 0428 0429 if ( value == Meta::valTitle ) 0430 { 0431 //I am sure there is a really good reason to add this space, as nothing works if it is removed, but WHY?!? 0432 d->filter += filter + ' '; 0433 d->filter = d->filter.replace( ' ', QLatin1String("%20") ); 0434 } 0435 0436 int level = 0; 0437 0438 if ( d->type == Private::GENRE ) 0439 level = 4; 0440 if ( d->type == Private::ARTIST ) 0441 level = 3; 0442 else if ( d->type == Private::ALBUM ) 0443 level = 2; 0444 else if ( d->type == Private::TRACK ) 0445 level = 1; 0446 0447 // should only clear all if we are querying for a top level item 0448 if ( m_collection->levels() == level ) 0449 { 0450 //we need to clear everything as we have no idea what the scripts wants to do... 0451 //TODO: with KSharedPointers in use, does this leak!? 0452 0453 debug() << "clear all!!!!!!!!!!!!!!"; 0454 m_collection->clear(); 0455 } 0456 0457 d->lastFilter = d->filter; 0458 m_collection->setLastFilter( d->filter ); 0459 return this; 0460 0461 } 0462