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