File indexing completed on 2023-05-30 11:30:46

0001 /**
0002  * Copyright (C) 2018 Michael Pyne <mpyne@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 #include "directoryloader.h"
0018 
0019 #include <QFileInfo>
0020 #include <QMutex>
0021 #include <QMutexLocker>
0022 
0023 #include "mediafiles.h"
0024 #include "collectionlist.h"
0025 
0026 // Classifies files into types for potential loading purposes.
0027 enum class MediaFileType {
0028     UnusableFile,
0029     MediaFile,
0030     Playlist,
0031     Directory
0032 };
0033 
0034 static MediaFileType classifyFile(const QFileInfo &fileInfo);
0035 static FileHandle loadMediaFile(const QString &fileName);
0036 
0037 DirectoryLoader::DirectoryLoader(const QString &dir, QObject *parent)
0038     : QObject(parent)
0039     , m_dirIterator(
0040         dir,
0041         QDir::AllEntries | QDir::NoDotAndDotDot,
0042         QDirIterator::Subdirectories | QDirIterator::FollowSymlinks)
0043 {
0044 }
0045 
0046 void DirectoryLoader::startLoading()
0047 {
0048     static const int BATCH_SIZE = 256;
0049     FileHandleList files;
0050 
0051     while(m_dirIterator.hasNext()) {
0052         const auto fileName = m_dirIterator.next();
0053         const QFileInfo fileInfo(fileName);
0054         const auto type = classifyFile(fileInfo);
0055 
0056         switch(type) {
0057             case MediaFileType::Playlist:
0058                 emit loadedPlaylist(fileName);
0059                 break;
0060 
0061             case MediaFileType::MediaFile:
0062                 {
0063                     const auto loadedMetadata = loadMediaFile(fileInfo.canonicalFilePath());
0064                     files << loadedMetadata;
0065 
0066                     if(files.count() >= BATCH_SIZE) {
0067                         emit loadedFiles(files);
0068                         files.clear();
0069                     }
0070                 }
0071                 break;
0072 
0073             case MediaFileType::Directory:
0074                 // this should be impossible based on the
0075                 // QDirIterator settings
0076                 continue;
0077             case MediaFileType::UnusableFile:
0078                 continue;
0079             default:
0080                 continue;
0081         }
0082     }
0083 
0084     if(!files.isEmpty()) {
0085         emit loadedFiles(files);
0086     }
0087 }
0088 
0089 MediaFileType classifyFile(const QFileInfo &fileInfo)
0090 {
0091     const QString path = fileInfo.canonicalFilePath();
0092 
0093     if(fileInfo.isFile() && fileInfo.isReadable() &&
0094         MediaFiles::isMediaFile(path))
0095     {
0096         return MediaFileType::MediaFile;
0097     }
0098 
0099     // These are all the files we care about, anything remaining needs to be a
0100     // directory or playlist or it must be unusable
0101 
0102     if(MediaFiles::isPlaylistFile(path)) {
0103         return MediaFileType::Playlist;
0104     }
0105 
0106     if(fileInfo.isDir()) {
0107         return MediaFileType::Directory;
0108     }
0109 
0110     return MediaFileType::UnusableFile;
0111 }
0112 
0113 FileHandle loadMediaFile(const QString &fileName)
0114 {
0115     // Just because our GUI thread accesses are serialized by signal/slot magic
0116     // doesn't mean that our non-GUI threads don't require deconfliction
0117     // Neither FileHandle nor TagLib are not thread-safe so synchronize access
0118     // to this function.
0119     static QMutex fhLock;
0120     QMutexLocker locker(&fhLock);
0121 
0122     const auto item = CollectionList::instance()->lookup(fileName);
0123     if(item) {
0124         // Don't re-load a file if it's already loaded once
0125         return FileHandle(item->file());
0126     }
0127     else {
0128         FileHandle loadedMetadata(fileName);
0129         (void) loadedMetadata.tag(); // Ensure tag is read
0130 
0131         return loadedMetadata;
0132     }
0133 }