diff --git a/autotests/filescannertest.cpp b/autotests/filescannertest.cpp index f666909a..df4aabfa 100644 --- a/autotests/filescannertest.cpp +++ b/autotests/filescannertest.cpp @@ -1,147 +1,144 @@ /* * Copyright 2018 Alexander Stippich * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "musicaudiotrack.h" #include "filescanner.h" #include "config-upnp-qt.h" #include #include #include #include #include #include #include #include class FileScannerTest: public QObject { Q_OBJECT public: QString createTrackUrl(QString subpath) { return QStringLiteral(LOCAL_FILE_TESTS_SAMPLE_FILES_PATH) + QStringLiteral("/cover_art") + subpath; } QList mTestTracksForDirectory = { createTrackUrl(QStringLiteral("/artist1/album1/not_existing.ogg")), createTrackUrl(QStringLiteral("/artist1/album2/not_existing.ogg")), createTrackUrl(QStringLiteral("/artist1/album3/not_existing.ogg")), createTrackUrl(QStringLiteral("/artist2/album1/not_existing.ogg")), createTrackUrl(QStringLiteral("/artist2/album2/not_existing.ogg")), createTrackUrl(QStringLiteral("/artist2/album3/not_existing.ogg")), createTrackUrl(QStringLiteral("/artist3/album1/not_existing.ogg")), createTrackUrl(QStringLiteral("/artist3/album2/not_existing.ogg")), createTrackUrl(QStringLiteral("/artist3/album3/not_existing.ogg")), }; QList mTestTracksForMetaData = { createTrackUrl(QStringLiteral("/artist4/test.ogg")), createTrackUrl(QStringLiteral("/artist4/test.flac")), createTrackUrl(QStringLiteral("/artist4/test.mp3")), }; private Q_SLOTS: void initTestCase() { } void testFileMetaDataScan() { QMimeDatabase mimeDb; FileScanner fileScanner; auto scannedTrack = fileScanner.scanOneFile(QUrl::fromLocalFile(QStringLiteral(LOCAL_FILE_TESTS_SAMPLE_FILES_PATH) + QStringLiteral("/music/test.ogg")), mimeDb); QCOMPARE(scannedTrack.title(), QStringLiteral("Title")); QCOMPARE(scannedTrack.genre(), QStringLiteral("Genre")); QCOMPARE(scannedTrack.albumName(), QStringLiteral("Test")); QCOMPARE(scannedTrack.artist(), QStringLiteral("Artist")); } void testFindCoverInDirectory() { FileScanner fileScanner; QVERIFY(!fileScanner.searchForCoverFile(mTestTracksForDirectory.at(0)).isEmpty()); QVERIFY(!fileScanner.searchForCoverFile(mTestTracksForDirectory.at(1)).isEmpty()); QVERIFY(!fileScanner.searchForCoverFile(mTestTracksForDirectory.at(2)).isEmpty()); QVERIFY(!fileScanner.searchForCoverFile(mTestTracksForDirectory.at(3)).isEmpty()); QVERIFY(!fileScanner.searchForCoverFile(mTestTracksForDirectory.at(4)).isEmpty()); QVERIFY(!fileScanner.searchForCoverFile(mTestTracksForDirectory.at(5)).isEmpty()); QVERIFY(!fileScanner.searchForCoverFile(mTestTracksForDirectory.at(6)).isEmpty()); QVERIFY(!fileScanner.searchForCoverFile(mTestTracksForDirectory.at(7)).isEmpty()); QVERIFY(!fileScanner.searchForCoverFile(mTestTracksForDirectory.at(8)).isEmpty()); } void loadCoverFromMetaData() { FileScanner fileScanner; - auto imageData = fileScanner.findEmbeddedCoverImage(mTestTracksForMetaData.at(0)); - QVERIFY(imageData.contains(KFileMetaData::EmbeddedImageData::FrontCover)); - imageData = fileScanner.findEmbeddedCoverImage(mTestTracksForMetaData.at(1)); - QVERIFY(imageData.contains(KFileMetaData::EmbeddedImageData::FrontCover)); - imageData = fileScanner.findEmbeddedCoverImage(mTestTracksForMetaData.at(2)); - QVERIFY(imageData.contains(KFileMetaData::EmbeddedImageData::FrontCover)); + QVERIFY(fileScanner.checkEmbeddedCoverImage(mTestTracksForMetaData.at(0))); + QVERIFY(fileScanner.checkEmbeddedCoverImage(mTestTracksForMetaData.at(1))); + QVERIFY(fileScanner.checkEmbeddedCoverImage(mTestTracksForMetaData.at(2))); } void benchmarkFileScan() { QMimeDatabase mimeDb; FileScanner fileScanner; QBENCHMARK { for (int i = 0; i < 100; i++) { auto scannedTrack = fileScanner.scanOneFile(QUrl::fromLocalFile(QStringLiteral(LOCAL_FILE_TESTS_SAMPLE_FILES_PATH) + QStringLiteral("/music/test.ogg")), mimeDb); auto scannedTrack2 = fileScanner.scanOneFile(QUrl::fromLocalFile(QStringLiteral(LOCAL_FILE_TESTS_SAMPLE_FILES_PATH) + QStringLiteral("/music/testMultiple.ogg")), mimeDb); auto scannedTrack3 = fileScanner.scanOneFile(QUrl::fromLocalFile(QStringLiteral(LOCAL_FILE_TESTS_SAMPLE_FILES_PATH) + QStringLiteral("/music/testMany.ogg")), mimeDb); } } } void benchmarkCoverInDirectory() { FileScanner fileScanner; QBENCHMARK { fileScanner.searchForCoverFile(mTestTracksForDirectory.at(0)); fileScanner.searchForCoverFile(mTestTracksForDirectory.at(1)); fileScanner.searchForCoverFile(mTestTracksForDirectory.at(2)); fileScanner.searchForCoverFile(mTestTracksForDirectory.at(3)); fileScanner.searchForCoverFile(mTestTracksForDirectory.at(4)); fileScanner.searchForCoverFile(mTestTracksForDirectory.at(5)); fileScanner.searchForCoverFile(mTestTracksForDirectory.at(6)); fileScanner.searchForCoverFile(mTestTracksForDirectory.at(7)); fileScanner.searchForCoverFile(mTestTracksForDirectory.at(8)); } } void benchmarkCoverFromMetadata() { FileScanner fileScanner; QBENCHMARK { - fileScanner.findEmbeddedCoverImage(mTestTracksForMetaData.at(0)); - fileScanner.findEmbeddedCoverImage(mTestTracksForMetaData.at(1)); - fileScanner.findEmbeddedCoverImage(mTestTracksForMetaData.at(2)); + fileScanner.checkEmbeddedCoverImage(mTestTracksForMetaData.at(0)); + fileScanner.checkEmbeddedCoverImage(mTestTracksForMetaData.at(1)); + fileScanner.checkEmbeddedCoverImage(mTestTracksForMetaData.at(2)); } } }; QTEST_GUILESS_MAIN(FileScannerTest) #include "filescannertest.moc" diff --git a/src/abstractfile/abstractfilelisting.cpp b/src/abstractfile/abstractfilelisting.cpp index bd75a5d1..9619ef49 100644 --- a/src/abstractfile/abstractfilelisting.cpp +++ b/src/abstractfile/abstractfilelisting.cpp @@ -1,481 +1,475 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "abstractfilelisting.h" #include "config-upnp-qt.h" #include "abstractfile/indexercommon.h" #include "musicaudiotrack.h" #include "notificationitem.h" #include "filescanner.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include class AbstractFileListingPrivate { public: QStringList mAllRootPaths; QFileSystemWatcher mFileSystemWatcher; QHash mAllAlbumCover; QHash>> mDiscoveredFiles; FileScanner mFileScanner; QMimeDatabase mMimeDb; QHash mAllFiles; QAtomicInt mStopRequest = 0; int mImportedTracksCount = 0; int mNotificationUpdateInterval = 1; int mNewFilesEmitInterval = 1; bool mHandleNewFiles = true; bool mWaitEndTrackRemoval = false; bool mErrorWatchingFileSystemChanges = false; }; AbstractFileListing::AbstractFileListing(QObject *parent) : QObject(parent), d(std::make_unique()) { connect(&d->mFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &AbstractFileListing::directoryChanged); connect(&d->mFileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &AbstractFileListing::fileChanged); } AbstractFileListing::~AbstractFileListing() = default; void AbstractFileListing::init() { Q_EMIT askRestoredTracks(); } void AbstractFileListing::newTrackFile(const MusicAudioTrack &partialTrack) { auto scanFileInfo = QFileInfo(partialTrack.resourceURI().toLocalFile()); const auto &newTrack = scanOneFile(partialTrack.resourceURI(), scanFileInfo); if (newTrack.isValid() && newTrack != partialTrack) { Q_EMIT modifyTracksList({newTrack}, d->mAllAlbumCover); } } void AbstractFileListing::restoredTracks(QHash allFiles) { executeInit(std::move(allFiles)); refreshContent(); } void AbstractFileListing::setAllRootPaths(const QStringList &allRootPaths) { if (d->mAllRootPaths == allRootPaths) { return; } d->mAllRootPaths = allRootPaths; } void AbstractFileListing::databaseFinishedInsertingTracksList() { } void AbstractFileListing::databaseFinishedRemovingTracksList() { if (waitEndTrackRemoval()) { Q_EMIT indexingFinished(); setWaitEndTrackRemoval(false); } } void AbstractFileListing::applicationAboutToQuit() { d->mStopRequest = 1; } const QStringList &AbstractFileListing::allRootPaths() const { return d->mAllRootPaths; } void AbstractFileListing::scanDirectory(QList &newFiles, const QUrl &path) { if (d->mStopRequest == 1) { return; } QDir rootDirectory(path.toLocalFile()); rootDirectory.refresh(); if (rootDirectory.exists()) { watchPath(path.toLocalFile()); } auto ¤tDirectoryListingFiles = d->mDiscoveredFiles[path]; auto currentFilesList = QSet(); rootDirectory.refresh(); const auto entryList = rootDirectory.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); for (const auto &oneEntry : entryList) { auto newFilePath = QUrl::fromLocalFile(oneEntry.canonicalFilePath()); if (oneEntry.isDir() || oneEntry.isFile()) { currentFilesList.insert(newFilePath); } } auto removedTracks = QVector>(); for (const auto &removedFilePath : currentDirectoryListingFiles) { auto itFilePath = std::find(currentFilesList.begin(), currentFilesList.end(), removedFilePath.first); if (itFilePath != currentFilesList.end()) { continue; } removedTracks.push_back(removedFilePath); } auto allRemovedTracks = QList(); for (const auto &oneRemovedTrack : removedTracks) { if (oneRemovedTrack.second) { allRemovedTracks.push_back(oneRemovedTrack.first); } else { removeFile(oneRemovedTrack.first, allRemovedTracks); } } for (const auto &oneRemovedTrack : removedTracks) { currentDirectoryListingFiles.remove(oneRemovedTrack); currentDirectoryListingFiles.remove(oneRemovedTrack); } if (!allRemovedTracks.isEmpty()) { Q_EMIT removedTracksList(allRemovedTracks); } if (!d->mHandleNewFiles) { return; } for (const auto &newFilePath : currentFilesList) { QFileInfo oneEntry(newFilePath.toLocalFile()); auto itFilePath = std::find(currentDirectoryListingFiles.begin(), currentDirectoryListingFiles.end(), QPair{newFilePath, oneEntry.isFile()}); if (itFilePath != currentDirectoryListingFiles.end()) { continue; } if (oneEntry.isDir()) { addFileInDirectory(newFilePath, path); scanDirectory(newFiles, newFilePath); if (d->mStopRequest == 1) { break; } continue; } if (!oneEntry.isFile()) { continue; } auto newTrack = scanOneFile(newFilePath, oneEntry); if (newTrack.isValid() && d->mStopRequest == 0) { addCover(newTrack); addFileInDirectory(newTrack.resourceURI(), path); newFiles.push_back(newTrack); ++d->mImportedTracksCount; if (d->mImportedTracksCount % d->mNotificationUpdateInterval == 0) { d->mNotificationUpdateInterval = std::min(50, 1 + d->mNotificationUpdateInterval * 2); } if (newFiles.size() > d->mNewFilesEmitInterval && d->mStopRequest == 0) { d->mNewFilesEmitInterval = std::min(50, 1 + d->mNewFilesEmitInterval * d->mNewFilesEmitInterval); emitNewFiles(newFiles); newFiles.clear(); } } if (d->mStopRequest == 1) { break; } } } void AbstractFileListing::directoryChanged(const QString &path) { const auto directoryEntry = d->mDiscoveredFiles.find(QUrl::fromLocalFile(path)); if (directoryEntry == d->mDiscoveredFiles.end()) { return; } Q_EMIT indexingStarted(); scanDirectoryTree(path); Q_EMIT indexingFinished(); } void AbstractFileListing::fileChanged(const QString &modifiedFileName) { QFileInfo modifiedFileInfo(modifiedFileName); auto modifiedFile = QUrl::fromLocalFile(modifiedFileName); auto modifiedTrack = scanOneFile(modifiedFile, modifiedFileInfo); if (modifiedTrack.isValid()) { Q_EMIT modifyTracksList({modifiedTrack}, d->mAllAlbumCover); } } void AbstractFileListing::executeInit(QHash allFiles) { d->mAllFiles = std::move(allFiles); } void AbstractFileListing::triggerRefreshOfContent() { d->mImportedTracksCount = 0; } void AbstractFileListing::refreshContent() { triggerRefreshOfContent(); } MusicAudioTrack AbstractFileListing::scanOneFile(const QUrl &scanFile, const QFileInfo &scanFileInfo) { MusicAudioTrack newTrack; qCDebug(orgKdeElisaIndexer) << "AbstractFileListing::scanOneFile" << scanFile; auto localFileName = scanFile.toLocalFile(); const auto &fileMimeType = d->mMimeDb.mimeTypeForFile(localFileName); if (!fileMimeType.name().startsWith(QStringLiteral("audio/"))) { return newTrack; } if (scanFileInfo.exists()) { auto itExistingFile = d->mAllFiles.find(scanFile); if (itExistingFile != d->mAllFiles.end()) { if (*itExistingFile >= scanFileInfo.fileTime(QFile::FileModificationTime)) { d->mAllFiles.erase(itExistingFile); return newTrack; } } } newTrack = d->mFileScanner.scanOneFile(scanFile, d->mMimeDb); if (newTrack.isValid()) { newTrack.setHasEmbeddedCover(checkEmbeddedCoverImage(localFileName)); newTrack.setFileModificationTime(scanFileInfo.fileTime(QFile::FileModificationTime)); if (scanFileInfo.exists()) { watchPath(scanFile.toLocalFile()); } } return newTrack; } void AbstractFileListing::watchPath(const QString &pathName) { if (!d->mFileSystemWatcher.addPath(pathName)) { qCDebug(orgKdeElisaIndexer) << "AbstractFileListing::watchPath" << "fail for" << pathName; if (!d->mErrorWatchingFileSystemChanges) { d->mErrorWatchingFileSystemChanges = true; Q_EMIT errorWatchingFileSystemChanges(); } } } void AbstractFileListing::addFileInDirectory(const QUrl &newFile, const QUrl &directoryName) { const auto directoryEntry = d->mDiscoveredFiles.find(directoryName); if (directoryEntry == d->mDiscoveredFiles.end()) { watchPath(directoryName.toLocalFile()); QDir currentDirectory(directoryName.toLocalFile()); if (currentDirectory.cdUp()) { const auto parentDirectoryName = currentDirectory.absolutePath(); const auto parentDirectory = QUrl::fromLocalFile(parentDirectoryName); const auto parentDirectoryEntry = d->mDiscoveredFiles.find(parentDirectory); if (parentDirectoryEntry == d->mDiscoveredFiles.end()) { watchPath(parentDirectoryName); } auto &parentCurrentDirectoryListingFiles = d->mDiscoveredFiles[parentDirectory]; parentCurrentDirectoryListingFiles.insert({directoryName, false}); } } auto ¤tDirectoryListingFiles = d->mDiscoveredFiles[directoryName]; QFileInfo isAFile(newFile.toLocalFile()); currentDirectoryListingFiles.insert({newFile, isAFile.isFile()}); } void AbstractFileListing::scanDirectoryTree(const QString &path) { auto newFiles = QList(); scanDirectory(newFiles, QUrl::fromLocalFile(path)); if (!newFiles.isEmpty() && d->mStopRequest == 0) { emitNewFiles(newFiles); } } void AbstractFileListing::setHandleNewFiles(bool handleThem) { d->mHandleNewFiles = handleThem; } void AbstractFileListing::emitNewFiles(const QList &tracks) { Q_EMIT tracksList(tracks, d->mAllAlbumCover); } void AbstractFileListing::addCover(const MusicAudioTrack &newTrack) { auto itCover = d->mAllAlbumCover.find(newTrack.albumName()); if (itCover != d->mAllAlbumCover.end()) { return; } auto coverUrl = d->mFileScanner.searchForCoverFile(newTrack.resourceURI().toLocalFile()); if (!coverUrl.isEmpty()) { d->mAllAlbumCover[newTrack.resourceURI().toString()] = coverUrl; } } void AbstractFileListing::removeDirectory(const QUrl &removedDirectory, QList &allRemovedFiles) { const auto itRemovedDirectory = d->mDiscoveredFiles.find(removedDirectory); if (itRemovedDirectory == d->mDiscoveredFiles.end()) { return; } const auto ¤tRemovedDirectory = *itRemovedDirectory; for (const auto &itFile : currentRemovedDirectory) { if (itFile.first.isValid() && !itFile.first.isEmpty()) { removeFile(itFile.first, allRemovedFiles); if (itFile.second) { allRemovedFiles.push_back(itFile.first); } } } d->mDiscoveredFiles.erase(itRemovedDirectory); } void AbstractFileListing::removeFile(const QUrl &oneRemovedTrack, QList &allRemovedFiles) { auto itRemovedDirectory = d->mDiscoveredFiles.find(oneRemovedTrack); if (itRemovedDirectory != d->mDiscoveredFiles.end()) { removeDirectory(oneRemovedTrack, allRemovedFiles); } } QHash &AbstractFileListing::allFiles() { return d->mAllFiles; } void AbstractFileListing::checkFilesToRemove() { QList allRemovedFiles; for (auto itFile = d->mAllFiles.begin(); itFile != d->mAllFiles.end(); ++itFile) { allRemovedFiles.push_back(itFile.key()); } if (!allRemovedFiles.isEmpty()) { setWaitEndTrackRemoval(true); Q_EMIT removedTracksList(allRemovedFiles); } } FileScanner &AbstractFileListing::fileScanner() { return d->mFileScanner; } bool AbstractFileListing::checkEmbeddedCoverImage(const QString &localFileName) { - auto images = d->mFileScanner.findEmbeddedCoverImage(localFileName); - if (images.contains(KFileMetaData::EmbeddedImageData::FrontCover)) { - if (!images[KFileMetaData::EmbeddedImageData::FrontCover].isEmpty()) { - return true; - } - } - return false; + return d->mFileScanner.checkEmbeddedCoverImage(localFileName); } bool AbstractFileListing::waitEndTrackRemoval() const { return d->mWaitEndTrackRemoval; } void AbstractFileListing::setWaitEndTrackRemoval(bool wait) { d->mWaitEndTrackRemoval = wait; } const QMimeDatabase &AbstractFileListing::mimeDatabase() const { return d->mMimeDb; } #include "moc_abstractfilelisting.cpp" diff --git a/src/filescanner.cpp b/src/filescanner.cpp index 87e1f2d4..ea781abe 100644 --- a/src/filescanner.cpp +++ b/src/filescanner.cpp @@ -1,266 +1,273 @@ /* * Copyright 2018 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "filescanner.h" #include "config-upnp-qt.h" #if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND #include #include #include #include #include #include #if defined KF5Baloo_FOUND && KF5Baloo_FOUND #include #endif #endif #include #include #include class FileScannerPrivate { public: static const QStringList constSearchStrings; #if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND KFileMetaData::ExtractorCollection mAllExtractors; KFileMetaData::PropertyMap mAllProperties; KFileMetaData::EmbeddedImageData mImageScanner; #endif }; const QStringList FileScannerPrivate::constSearchStrings = { QStringLiteral("*[Cc]over*.jpg"), QStringLiteral("*[Cc]over*.png"), QStringLiteral("*[Ff]older*.jpg"), QStringLiteral("*[Ff]older*.png"), QStringLiteral("*[Ff]ront*.jpg"), QStringLiteral("*[Ff]ront*.png") }; FileScanner::FileScanner() : d(std::make_unique()) { } FileScanner::~FileScanner() = default; MusicAudioTrack FileScanner::scanOneFile(const QUrl &scanFile, const QMimeDatabase &mimeDatabase) { #if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND MusicAudioTrack newTrack; auto localFileName = scanFile.toLocalFile(); QFileInfo scanFileInfo(localFileName); newTrack.setFileModificationTime(scanFileInfo.fileTime(QFile::FileModificationTime)); newTrack.setResourceURI(scanFile); const auto &fileMimeType = mimeDatabase.mimeTypeForFile(localFileName); if (!fileMimeType.name().startsWith(QStringLiteral("audio/"))) { return newTrack; } QString mimetype = fileMimeType.name(); QList exList = d->mAllExtractors.fetchExtractors(mimetype); if (exList.isEmpty()) { return newTrack; } KFileMetaData::Extractor* ex = exList.first(); KFileMetaData::SimpleExtractionResult result(localFileName, mimetype, KFileMetaData::ExtractionResult::ExtractMetaData); ex->extract(&result); d->mAllProperties = result.properties(); scanProperties(localFileName, newTrack); return newTrack; #else Q_UNUSED(scanFile) Q_UNUSED(mimeDatabase) return {}; #endif } void FileScanner::scanProperties(const Baloo::File &match, MusicAudioTrack &trackData) { #if defined KF5Baloo_FOUND && KF5Baloo_FOUND d->mAllProperties = match.properties(); scanProperties(match.path(), trackData); #else Q_UNUSED(match) Q_UNUSED(trackData) #endif } void FileScanner::scanProperties(const QString &localFileName, MusicAudioTrack &trackData) { #if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND if (d->mAllProperties.isEmpty()) { return; } using entry = std::pair; auto rangeBegin = d->mAllProperties.constKeyValueBegin(); QVariant value; while (rangeBegin != d->mAllProperties.constKeyValueEnd()) { auto key = (*rangeBegin).first; auto rangeEnd = std::find_if(rangeBegin, d->mAllProperties.constKeyValueEnd(), [key](const entry& e) { return e.first != key; }); auto distance = std::distance(rangeBegin, rangeEnd); if (distance > 1) { QStringList list; list.reserve(static_cast(distance)); std::for_each(rangeBegin, rangeEnd, [&list](const entry& s) { list.append(s.second.toString()); }); value = QLocale().createSeparatedList(list); } else { value = (*rangeBegin).second; } switch (key) { case KFileMetaData::Property::Artist: trackData.setArtist(value.toString()); break; case KFileMetaData::Property::AlbumArtist: trackData.setAlbumArtist(value.toString()); break; case KFileMetaData::Property::Genre: trackData.setGenre(value.toString()); break; case KFileMetaData::Property::Composer: trackData.setComposer(value.toString()); break; case KFileMetaData::Property::Lyricist: trackData.setLyricist(value.toString()); break; case KFileMetaData::Property::Title: trackData.setTitle(value.toString()); break; case KFileMetaData::Property::Duration: trackData.setDuration(QTime::fromMSecsSinceStartOfDay(int(1000 * value.toDouble()))); break; case KFileMetaData::Property::Album: trackData.setAlbumName(value.toString()); break; case KFileMetaData::Property::TrackNumber: trackData.setTrackNumber(value.toInt()); break; case KFileMetaData::Property::DiscNumber: trackData.setDiscNumber(value.toInt()); break; case KFileMetaData::Property::ReleaseYear: trackData.setYear(value.toInt()); break; case KFileMetaData::Property::Lyrics: trackData.setLyrics(value.toString()); break; case KFileMetaData::Property::Channels: trackData.setChannels(value.toInt()); break; case KFileMetaData::Property::BitRate: trackData.setBitRate(value.toInt()); break; case KFileMetaData::Property::SampleRate: trackData.setSampleRate(value.toInt()); break; case KFileMetaData::Property::Comment: trackData.setComment(value.toString()); break; case KFileMetaData::Property::Rating: trackData.setRating(value.toInt()); break; default: break; } rangeBegin = rangeEnd; } #if !defined Q_OS_ANDROID && !defined Q_OS_WIN auto fileData = KFileMetaData::UserMetaData(localFileName); QString comment = fileData.userComment(); if (!comment.isEmpty()) { trackData.setComment(comment); } int rating = fileData.rating(); if (rating > 0) { trackData.setRating(rating); } #endif if (!trackData.duration().isValid()) { return; } trackData.setValid(true); #else Q_UNUSED(localFileName) Q_UNUSED(trackData) #endif } QUrl FileScanner::searchForCoverFile(const QString &localFileName) { QFileInfo trackFilePath(localFileName); QDir trackFileDir = trackFilePath.absoluteDir(); trackFileDir.setFilter(QDir::Files); trackFileDir.setNameFilters(d->constSearchStrings); QFileInfoList coverFiles = trackFileDir.entryInfoList(); if (coverFiles.isEmpty()) { QString dirNamePattern = QStringLiteral("*") + trackFileDir.dirName() + QStringLiteral("*"); QString dirNameNoSpaces = dirNamePattern.remove(QLatin1Char(' ')); QStringList filters = { dirNamePattern + QStringLiteral(".jpg"), dirNamePattern + QStringLiteral(".png"), dirNameNoSpaces + QStringLiteral(".jpg"), dirNameNoSpaces + QStringLiteral(".png") }; trackFileDir.setNameFilters(filters); coverFiles = trackFileDir.entryInfoList(); } if (coverFiles.isEmpty()) { return QUrl(); } return QUrl::fromLocalFile(coverFiles.first().absoluteFilePath()); } -QMap FileScanner::findEmbeddedCoverImage(const QString &localFileName) +bool FileScanner::checkEmbeddedCoverImage(const QString &localFileName) { #if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND - return d->mImageScanner.imageData(localFileName); + auto imageData = d->mImageScanner.imageData(localFileName); + + if (imageData.contains(KFileMetaData::EmbeddedImageData::FrontCover)) { + if (!imageData[KFileMetaData::EmbeddedImageData::FrontCover].isEmpty()) { + return true; + } + } #endif - return QMap(); + + return false; } diff --git a/src/filescanner.h b/src/filescanner.h index cba6ec6c..766ba520 100644 --- a/src/filescanner.h +++ b/src/filescanner.h @@ -1,64 +1,63 @@ /* * Copyright 2018 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef FILESCANNER_H #define FILESCANNER_H #include "elisaLib_export.h" #include "musicaudiotrack.h" -#include #include #include #include #include namespace Baloo { class File; } class FileScannerPrivate; class ELISALIB_EXPORT FileScanner { public: FileScanner(); virtual ~FileScanner(); MusicAudioTrack scanOneFile(const QUrl &scanFile, const QMimeDatabase &mimeDatabase); void scanProperties(const Baloo::File &match, MusicAudioTrack &trackData); void scanProperties(const QString &localFileName, MusicAudioTrack &trackData); QUrl searchForCoverFile(const QString &localFileName); - QMap findEmbeddedCoverImage(const QString &localFileName); + bool checkEmbeddedCoverImage(const QString &localFileName); private: std::unique_ptr d; }; #endif // FILESCANNER_H