File indexing completed on 2024-05-05 04:48:50

0001 /****************************************************************************************
0002  * Copyright (c) 2007 Maximilian Kossick <maximilian.kossick@googlemail.com>            *
0003  * Copyright (c) 2008 Seb Ruiz <ruiz@kde.org>                                           *
0004  * Copyright (c) 2009-2010 Jeff Mitchell <mitchell@kde.org>                             *
0005  * Copyright (c) 2013 Ralf Engels <ralf-engels@gmx.de>                                  *
0006  *                                                                                      *
0007  * This program is free software; you can redistribute it and/or modify it under        *
0008  * the terms of the GNU General Public License as published by the Free Software        *
0009  * Foundation; either version 2 of the License, or (at your option) any later           *
0010  * version.                                                                             *
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 "AbstractScanResultProcessor"
0021 
0022 #include "AbstractScanResultProcessor.h"
0023 
0024 #include "collectionscanner/Album.h"
0025 #include "collectionscanner/Directory.h"
0026 #include "collectionscanner/Playlist.h"
0027 #include "collectionscanner/Track.h"
0028 #include "core/logger/Logger.h"
0029 #include "core/support/Debug.h"
0030 #include "core/support/Components.h"
0031 #include "core-impl/collections/support/ArtistHelper.h"
0032 #include "playlistmanager/PlaylistManager.h"
0033 #include "scanner/GenericScanManager.h"
0034 
0035 AbstractScanResultProcessor::AbstractScanResultProcessor( GenericScanManager* manager, QObject* parent )
0036     : QObject( parent )
0037     , m_manager( manager )
0038     , m_type( GenericScanManager::PartialUpdateScan )
0039 {
0040     connect( manager, &GenericScanManager::started,
0041              this, &AbstractScanResultProcessor::scanStarted );
0042     connect( manager, &GenericScanManager::directoryCount,
0043              this, &AbstractScanResultProcessor::scanDirectoryCount );
0044     connect( manager, &GenericScanManager::directoryScanned,
0045              this, &AbstractScanResultProcessor::scanDirectoryScanned );
0046     connect( manager, &GenericScanManager::succeeded,
0047              this, &AbstractScanResultProcessor::scanSucceeded );
0048     connect( manager, &GenericScanManager::failed,
0049              this, &AbstractScanResultProcessor::scanFailed );
0050 }
0051 
0052 AbstractScanResultProcessor::~AbstractScanResultProcessor()
0053 {
0054     cleanupMembers();
0055 }
0056 
0057 void
0058 AbstractScanResultProcessor::scanStarted( GenericScanManager::ScanType type )
0059 {
0060     DEBUG_BLOCK;
0061 
0062     m_type = type;
0063 
0064     // -- show the progress operation for the job
0065     Amarok::Logger::newProgressOperation( this,
0066                                           i18n( "Scanning music" ),
0067                                           100,
0068                                           this,
0069                                           &AbstractScanResultProcessor::abort );
0070 
0071 }
0072 
0073 void
0074 AbstractScanResultProcessor::scanDirectoryCount( int count )
0075 {
0076     // message( i18np("Found one directory", "Found %1 directories", count ) );
0077     debug() << "got" << count << "directories";
0078     Q_EMIT totalSteps( count * 2 );
0079 }
0080 
0081 void
0082 AbstractScanResultProcessor::scanDirectoryScanned( QSharedPointer<CollectionScanner::Directory> dir )
0083 {
0084     m_directories.append( dir );
0085     Q_EMIT incrementProgress();
0086 }
0087 
0088 void
0089 AbstractScanResultProcessor::scanSucceeded()
0090 {
0091     DEBUG_BLOCK;
0092 
0093     // the default for albums with several artists is that it's a compilation
0094     // however, some album names are unlikely to be a compilation
0095     static QStringList nonCompilationAlbumNames;
0096     if( nonCompilationAlbumNames.isEmpty() )
0097     {
0098         nonCompilationAlbumNames
0099             << QLatin1String("") // don't throw together albums without name. At least not here
0100             << QStringLiteral("Best Of")
0101             << QStringLiteral("Anthology")
0102             << QStringLiteral("Hit collection")
0103             << QStringLiteral("Greatest Hits")
0104             << QStringLiteral("All Time Greatest Hits")
0105             << QStringLiteral("Live");
0106     }
0107 
0108     // -- commit the directories
0109     foreach( QSharedPointer<CollectionScanner::Directory> dir, m_directories )
0110     {
0111         commitDirectory( dir );
0112 
0113         // -- sort the tracks into albums
0114         QSet<CollectionScanner::Album*> dirAlbums;
0115         QSet<QString> dirAlbumNames;
0116         QList<CollectionScanner::Track*> tracks = dir->tracks();
0117 
0118         // check what album names we have
0119         foreach( CollectionScanner::Track* track, dir->tracks() )
0120         {
0121             if( !track->album().isEmpty() )
0122                 dirAlbumNames.insert( track->album() );
0123         }
0124 
0125         // use the directory name as album name
0126         QString fallbackAlbumName = ( dirAlbumNames.isEmpty() ?
0127                                       QDir( dir->path() ).dirName() :
0128                                       QString() );
0129 
0130         foreach( CollectionScanner::Track* track, dir->tracks() )
0131         {
0132             CollectionScanner::Album *album = sortTrack( track, fallbackAlbumName );
0133 
0134             dirAlbums.insert( album );
0135             dirAlbumNames.insert( track->album() );
0136         }
0137 
0138         // if all the tracks from this directory end up in one album
0139         // (or they have at least the same name) then it's likely that an image
0140         // from this directory could be a cover
0141         if( dirAlbumNames.count() == 1 )
0142             (*dirAlbums.begin())->setCovers( dir->covers() );
0143     }
0144 
0145     // --- add all albums
0146     QList<QString> keys = m_albumNames.uniqueKeys();
0147     Q_EMIT totalSteps( m_directories.count() + keys.count() ); // update progress bar
0148     foreach( const QString &key, keys )
0149     {
0150         // --- commit the albums as compilation or normal album
0151 
0152         QList<CollectionScanner::Album*> albums = m_albumNames.values( key );
0153 
0154         // if we have multiple albums with the same name, check if it
0155         // might be a compilation
0156         for( int i = albums.count() - 1; i >= 0; --i )
0157         {
0158             CollectionScanner::Album *album = albums.at( i );
0159             // commit all albums with a track with the noCompilation flag
0160             if( album->isNoCompilation() ||
0161                 nonCompilationAlbumNames.contains( album->name(), Qt::CaseInsensitive ) )
0162                 commitAlbum( albums.takeAt( i ) );
0163         }
0164 
0165         // only one album left. (that either means all have the same album artist tag
0166         // or none has one. In the last case we try to guess an album artist)
0167         if( albums.count() == 1 )
0168         {
0169             CollectionScanner::Album *album = albums.takeFirst();
0170 
0171             // look if all the tracks have the same (guessed) artist.
0172             // It's no compilation.
0173             bool isCompilation = false;
0174             bool firstTrack = true;
0175             QString albumArtist;
0176             foreach( CollectionScanner::Track *track, album->tracks() )
0177             {
0178                 QString trackAlbumArtist =
0179                     ArtistHelper::bestGuessAlbumArtist( track->albumArtist(),
0180                                                         track->artist(),
0181                                                         track->genre(),
0182                                                         track->composer() );
0183                 if( firstTrack )
0184                     albumArtist = trackAlbumArtist;
0185                 firstTrack = false;
0186 
0187                 if( trackAlbumArtist.isEmpty() || track->isCompilation() ||
0188                     albumArtist != trackAlbumArtist )
0189                     isCompilation = true;
0190             }
0191 
0192             if( !isCompilation )
0193                 album->setArtist( albumArtist );
0194             commitAlbum( album );
0195         }
0196 
0197         // several albums with different album artist.
0198         else if( albums.count() > 1 )
0199         {
0200             while( !albums.isEmpty() )
0201                 commitAlbum( albums.takeFirst() );
0202         }
0203 
0204         Q_EMIT incrementProgress();
0205     }
0206 
0207     // -- now check if some of the tracks are not longer used and also not moved to another directory
0208     foreach( QSharedPointer<CollectionScanner::Directory> dir, m_directories )
0209         if( !dir->isSkipped() )
0210             deleteDeletedTracksAndSubdirs( dir );
0211 
0212     // -- delete all not-found directories (special handling for PartialUpdateScan):
0213     deleteDeletedDirectories();
0214 
0215     cleanupMembers();
0216     Q_EMIT endProgressOperation( this );
0217 }
0218 
0219 void
0220 AbstractScanResultProcessor::scanFailed( const QString& text )
0221 {
0222     message( text );
0223 
0224     cleanupMembers();
0225     Q_EMIT endProgressOperation( this );
0226 }
0227 
0228 void
0229 AbstractScanResultProcessor::abort()
0230 {
0231     m_manager->abort();
0232 }
0233 
0234 void
0235 AbstractScanResultProcessor::commitDirectory( QSharedPointer<CollectionScanner::Directory> dir )
0236 {
0237     if( dir->path().isEmpty() )
0238     {
0239         warning() << "got directory with no path from the scanner, not adding";
0240         return;
0241     }
0242 
0243     // --- add all playlists
0244     foreach( const CollectionScanner::Playlist &playlist, dir->playlists() )
0245         commitPlaylist( playlist );
0246 }
0247 
0248 void
0249 AbstractScanResultProcessor::commitPlaylist( const CollectionScanner::Playlist &playlist )
0250 {
0251     // debug() << "commitPlaylist on " << playlist->path();
0252 
0253     if( The::playlistManager() )
0254         The::playlistManager()->import( QUrl::fromLocalFile( playlist.path() ) );
0255 }
0256 
0257 CollectionScanner::Album*
0258 AbstractScanResultProcessor::sortTrack( CollectionScanner::Track *track, const QString &dirName )
0259 {
0260     QString albumName = track->album();
0261 
0262     // we allow albums with empty name and nonempty artist, see bug 272471
0263     QString albumArtist = track->albumArtist();
0264     if( track->isCompilation() )
0265         albumArtist.clear();  // no album artist denotes a compilation
0266     if( track->isNoCompilation() && albumArtist.isEmpty() )
0267         albumArtist = ArtistHelper::bestGuessAlbumArtist( track->albumArtist(),
0268                                                           track->artist(),
0269                                                           track->genre(),
0270                                                           track->composer() );
0271 
0272     if( albumName.isEmpty() && albumArtist.isEmpty() ) // try harder to set at least one
0273         albumName = dirName;
0274 
0275     AlbumKey key( albumName, albumArtist );
0276 
0277     CollectionScanner::Album *album;
0278     if( m_albums.contains( key ) )
0279         album = m_albums.value( key );
0280     else
0281     {
0282         album = new CollectionScanner::Album( albumName, albumArtist );
0283         m_albums.insert( key, album );
0284         m_albumNames.insert( albumName, album );
0285     }
0286 
0287     album->addTrack( track );
0288     return album;
0289 }
0290 
0291 void
0292 AbstractScanResultProcessor::cleanupMembers()
0293 {
0294     // note: qt had problems with CLEAN_ALL macro
0295     QHash<QString, CollectionScanner::Album*>::const_iterator i = m_albumNames.constBegin();
0296     while (i != m_albumNames.constEnd()) {
0297         delete i.value();
0298         ++i;
0299     }
0300     m_albumNames.clear();
0301     m_albums.clear();
0302     m_directories.clear();
0303 }
0304 
0305