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