diff --git a/CMakeLists.txt b/CMakeLists.txt index 0358fe8..47bf63a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,159 +1,159 @@ cmake_minimum_required(VERSION 3.5) set(KF5_VERSION "5.56.0") # handled by release scripts set(KF5_DEP_VERSION "5.56.0") # handled by release scripts project(KFileMetaData VERSION ${KF5_VERSION}) include(FeatureSummary) find_package(ECM 5.56.0 NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://projects.kde.org/projects/kdesupport/extra-cmake-modules") feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${ECM_MODULE_PATH}) include(KDEInstallDirs) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMAddTests) include(GenerateExportHeader) include(ECMSetupVersion) include(ECMGenerateHeaders) include(ECMAddQch) include(ECMQtDeclareLoggingCategory) include(CheckStructHasMember) option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF) add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)") ecm_setup_version(PROJECT VARIABLE_PREFIX KFILEMETADATA PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5FileMetaDataConfigVersion.cmake" SOVERSION 3) # Dependencies set(REQUIRED_QT_VERSION 5.10.0) find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG COMPONENTS Gui) set_package_properties(Qt5Gui PROPERTIES DESCRIPTION "Qt 5: Gui component" TYPE OPTIONAL PURPOSE "Qt5::Gui is needed to build the AppImage extractor") find_package(Qt5 ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE COMPONENTS Xml) find_package(KF5 ${KF5_DEP_VERSION} COMPONENTS Archive) set_package_properties(KF5Archive PROPERTIES DESCRIPTION "KDE Frameworks 5: Archive Framework" URL "https://download.kde.org/stable/frameworks/" TYPE OPTIONAL PURPOSE "Archive is needed to build ODF and OOXML 2007 extractors") find_package(KF5 ${KF5_DEP_VERSION} COMPONENTS CoreAddons) set_package_properties(KF5CoreAddons PROPERTIES DESCRIPTION "KDE Frameworks 5: Core Addons Framework" URL "https://download.kde.org/stable/frameworks/" TYPE REQUIRED PURPOSE "Needed for the formatting of properties for display purposes") find_package(KF5 ${KF5_DEP_VERSION} COMPONENTS Config) set_package_properties(KF5Config PROPERTIES DESCRIPTION "KDE Frameworks 5: Config Framework" URL "https://download.kde.org/stable/frameworks/" TYPE OPTIONAL PURPOSE "Config is needed to build the AppImage extractor") find_package(KF5 ${KF5_DEP_VERSION} REQUIRED COMPONENTS I18n) find_package(Poppler 0.12.1 COMPONENTS Qt5) set_package_properties(Poppler PROPERTIES DESCRIPTION "A PDF rendering library" URL "https://poppler.freedesktop.org/" TYPE OPTIONAL PURPOSE "Support for PDF files") find_package(Taglib 1.11.1) set_package_properties(Taglib PROPERTIES DESCRIPTION "Id3 tag reader" URL "https://taglib.org/" TYPE OPTIONAL PURPOSE "Support for music metadata") find_package(LibExiv2 0.21) set_package_properties(LibExiv2 PROPERTIES TYPE OPTIONAL PURPOSE "Support for image metadata") find_package(FFmpeg 57.48 COMPONENTS AVCODEC) find_package(FFmpeg 57.40 COMPONENTS AVFORMAT) find_package(FFmpeg 55.27 COMPONENTS AVUTIL) set_package_properties(FFmpeg PROPERTIES DESCRIPTION "Video Tag reader" URL "https://ffmpeg.org/" TYPE OPTIONAL PURPOSE "Support for video metadata") if (FFmpeg_AVFORMAT_FOUND) set(CMAKE_REQUIRED_INCLUDES ${AVCODEC_INCLUDE_DIRS}) CHECK_STRUCT_HAS_MEMBER(AVStream codecpar "stdint.h;libavformat/avformat.h" HAVE_AVSTREAM_CODECPAR LANGUAGE CXX) endif() find_package(EPub) set_package_properties(EPub PROPERTIES DESCRIPTION "Ebook epub reader" URL "https://sourceforge.net/projects/ebook-tools/" TYPE OPTIONAL PURPOSE "Support for epub metadata") find_package(CatDoc) set_package_properties(CatDoc PROPERTIES DESCRIPTION "catdoc executable" URL "https://www.wagner.pp.ru/~vitus/software/catdoc/" TYPE RUNTIME PURPOSE "Extract text from office 98 files - RUNTIME dependency") if ( CMAKE_SYSTEM_NAME MATCHES "Linux" ) find_package(Xattr) set_package_properties(Xattr PROPERTIES DESCRIPTION "library libattr " URL "https://savannah.nongnu.org/projects/attr" TYPE REQUIRED PURPOSE "Extended attribute shared library") endif() #find_package(QMobipocket) #set_package_properties(QMobipocket PROPERTIES DESCRIPTION "Mobipocket epub reader" # URL "https://projects.kde.org/projects/kde/kdegraphics/kdegraphics-mobipocket" # TYPE OPTIONAL PURPOSE "Support for mobi metadata") find_package(libappimage 0.1.10 CONFIG) set_package_properties(libappimage PROPERTIES DESCRIPTION "Core library of the AppImage project" URL "https://github.com/AppImage/libappimage" TYPE OPTIONAL PURPOSE "Needed to build the AppImage extractor" ) add_definitions(-DTRANSLATION_DOMAIN=\"kfilemetadata5\") -add_definitions(-DQT_NO_FOREACH) + add_subdirectory(src) if (BUILD_TESTING) add_subdirectory(autotests) add_subdirectory(tests) endif() # Config files set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/KF5FileMetaData") if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ki18n_install(po) endif() if (BUILD_QCH) ecm_install_qch_export( TARGETS KF5FileMetaData_QCH FILE KF5FileMetaDataQchTargets.cmake DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KF5FileMetaDataQchTargets.cmake\")") endif() include(CMakePackageConfigHelpers) configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/KF5FileMetaDataConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5FileMetaDataConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5FileMetaDataConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5FileMetaDataConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT devel ) # contains list of debug categories, for kdebugsettings install(FILES kfilemetadata.categories DESTINATION ${KDE_INSTALL_CONFDIR}) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/externalextractor.cpp b/src/externalextractor.cpp index 781ac5e..553e926 100644 --- a/src/externalextractor.cpp +++ b/src/externalextractor.cpp @@ -1,174 +1,173 @@ /* * This file is part of the KFileMetaData project * Copyright (C) 2016 Varun Joshi * Copyright (C) 2015 Boudhayan Gupta * * This library 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 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 library. If not, see . * */ #include "externalextractor.h" #include "kfilemetadata_debug.h" #include #include #include #include #include #include "properties.h" #include "propertyinfo.h" #include "typeinfo.h" #define EXTRACTOR_TIMEOUT_MS 30000 using namespace KFileMetaData; class Q_DECL_HIDDEN ExternalExtractor::ExternalExtractorPrivate { public: QString path; QStringList writeMimetypes; QString mainPath; }; ExternalExtractor::ExternalExtractor(QObject* parent) : ExtractorPlugin(parent), d_ptr(new ExternalExtractorPrivate) { } ExternalExtractor::ExternalExtractor(const QString& pluginPath) : ExtractorPlugin(nullptr), d_ptr(new ExternalExtractorPrivate) { Q_D(ExternalExtractor); d->path = pluginPath; QDir pluginDir(pluginPath); QStringList pluginDirContents = pluginDir.entryList(QDir::Files | QDir::NoDotAndDotDot); if (!pluginDirContents.contains(QStringLiteral("manifest.json"))) { qCDebug(KFILEMETADATA_LOG) << pluginPath << "does not seem to contain a valid plugin"; return; } QFile manifest(pluginDir.filePath(QStringLiteral("manifest.json"))); manifest.open(QIODevice::ReadOnly); QJsonDocument manifestDoc = QJsonDocument::fromJson(manifest.readAll()); if (!manifestDoc.isObject()) { qCDebug(KFILEMETADATA_LOG) << "Manifest does not seem to be a valid JSON Object"; return; } QJsonObject rootObject = manifestDoc.object(); const QJsonArray mimetypesArray = rootObject.value(QStringLiteral("mimetypes")).toArray(); QStringList mimetypes; mimetypes.reserve(mimetypesArray.count()); for (const QVariant &mimetype : mimetypesArray) { mimetypes << mimetype.toString(); } d->writeMimetypes.append(mimetypes); d->mainPath = pluginDir.filePath(rootObject[QStringLiteral("main")].toString()); } ExternalExtractor::~ExternalExtractor() { delete d_ptr; } QStringList ExternalExtractor::mimetypes() const { Q_D(const ExternalExtractor); return d->writeMimetypes; } void ExternalExtractor::extract(ExtractionResult* result) { Q_D(ExternalExtractor); QJsonDocument writeData; QJsonObject writeRootObject; QByteArray output; QByteArray errorOutput; writeRootObject[QStringLiteral("path")] = QJsonValue(result->inputUrl()); writeRootObject[QStringLiteral("mimetype")] = result->inputMimetype(); writeData.setObject(writeRootObject); QProcess extractorProcess; extractorProcess.start(d->mainPath, QStringList(), QIODevice::ReadWrite); bool started = extractorProcess.waitForStarted(); if (!started) { qCWarning(KFILEMETADATA_LOG) << "External extractor" << d->mainPath << "failed to start:" << extractorProcess.errorString(); return; } extractorProcess.write(writeData.toJson()); extractorProcess.closeWriteChannel(); extractorProcess.waitForFinished(EXTRACTOR_TIMEOUT_MS); output = extractorProcess.readAll(); errorOutput = extractorProcess.readAllStandardError(); if (extractorProcess.exitStatus()) { qCWarning(KFILEMETADATA_LOG) << "External extractor" << d->mainPath << "failed to index" << result->inputUrl() << "-" << errorOutput; return; } // now we read in the output (which is a standard json format) into the // ExtractionResult QJsonDocument extractorData = QJsonDocument::fromJson(output); if (!extractorData.isObject()) { return; } QJsonObject rootObject = extractorData.object(); QJsonObject propertiesObject = rootObject[QStringLiteral("properties")].toObject(); - const auto lstKeys = propertiesObject.keys(); - for(const auto &key : lstKeys) { + Q_FOREACH(const auto &key, propertiesObject.keys()) { if (key == QStringLiteral("typeInfo")) { TypeInfo info = TypeInfo::fromName(propertiesObject.value(key).toString()); result->addType(info.type()); continue; } // for plaintext extraction if (key == QStringLiteral("text")) { result->append(propertiesObject.value(key).toString(QStringLiteral(""))); continue; } PropertyInfo info = PropertyInfo::fromName(key); if (info.name() != key) { continue; } result->add(info.property(), propertiesObject.value(key).toVariant()); } if (rootObject[QStringLiteral("status")].toString() != QStringLiteral("OK")) { qCDebug(KFILEMETADATA_LOG) << rootObject[QStringLiteral("error")].toString(); } } diff --git a/src/externalwriter.cpp b/src/externalwriter.cpp index 6cad374..3b029f0 100644 --- a/src/externalwriter.cpp +++ b/src/externalwriter.cpp @@ -1,149 +1,149 @@ /* * This file is part of the KFileMetaData project * Copyright (C) 2016 Varun Joshi * * This library 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 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 library. If not, see . * */ #include "kfilemetadata_debug.h" #include #include #include #include #include #include #include "externalwriter.h" #include "properties.h" #include "propertyinfo.h" #define WRITER_TIMEOUT_MS 30000 using namespace KFileMetaData; class Q_DECL_HIDDEN ExternalWriter::ExternalWriterPrivate { public: QString path; QStringList writeMimetypes; QString mainPath; }; ExternalWriter::ExternalWriter(QObject* parent) : WriterPlugin(parent), d_ptr(new ExternalWriterPrivate) { } ExternalWriter::~ExternalWriter() { delete d_ptr; } ExternalWriter::ExternalWriter(const QString& pluginPath) : WriterPlugin(new QObject()), d_ptr(new ExternalWriterPrivate) { Q_D(ExternalWriter); d->path = pluginPath; QDir pluginDir(pluginPath); QStringList pluginDirContents = pluginDir.entryList(); if (!pluginDirContents.contains(QStringLiteral("manifest.json"))) { qCDebug(KFILEMETADATA_LOG) << "Path does not seem to contain a valid plugin"; return; } QFile manifest(pluginDir.filePath(QStringLiteral("manifest.json"))); manifest.open(QIODevice::ReadOnly); QJsonDocument manifestDoc = QJsonDocument::fromJson(manifest.readAll()); if (!manifestDoc.isObject()) { qCDebug(KFILEMETADATA_LOG) << "Manifest does not seem to be a valid JSON Object"; return; } QJsonObject rootObject = manifestDoc.object(); - const QJsonArray mimetypesArray = rootObject.value(QStringLiteral("mimetypes")).toArray(); + QJsonArray mimetypesArray = rootObject.value(QStringLiteral("mimetypes")).toArray(); QStringList mimetypes; - for (const QVariant &mimetype : mimetypesArray) { + Q_FOREACH(const QVariant &mimetype, mimetypesArray) { mimetypes << mimetype.toString(); } d->writeMimetypes.append(mimetypes); d->mainPath = pluginDir.filePath(rootObject[QStringLiteral("main")].toString()); } QStringList ExternalWriter::writeMimetypes() const { Q_D(const ExternalWriter); return d->writeMimetypes; } void ExternalWriter::write(const WriteData& data) { Q_D(ExternalWriter); QJsonDocument writeData; QJsonObject rootObject; QJsonObject propertiesObject; QByteArray output; QByteArray errorOutput; QMap properties = data.getAllProperties(); const auto &propertiesKeys = properties.keys(); - for (const Property::Property &property : propertiesKeys) { + Q_FOREACH(const Property::Property &property, propertiesKeys) { PropertyInfo propertyInfo(property); propertiesObject[propertyInfo.name()] = QJsonValue::fromVariant(properties[property]); } rootObject[QStringLiteral("path")] = QJsonValue(data.inputUrl()); rootObject[QStringLiteral("mimetype")] = data.inputMimetype(); rootObject[QStringLiteral("properties")] = propertiesObject; writeData.setObject(rootObject); QProcess writerProcess; writerProcess.start(d->mainPath, QStringList(), QIODevice::ReadWrite); writerProcess.write(writeData.toJson()); writerProcess.closeWriteChannel(); writerProcess.waitForFinished(WRITER_TIMEOUT_MS); errorOutput = writerProcess.readAllStandardError(); if (writerProcess.exitStatus()) { qCDebug(KFILEMETADATA_LOG) << "Something went wrong while trying to write data"; qCDebug(KFILEMETADATA_LOG) << errorOutput; return; } output = writerProcess.readAll(); QJsonDocument writerExitData = QJsonDocument::fromJson(output); if (!writerExitData.isObject()) { return; } QJsonObject outputRootObject = writerExitData.object(); if (outputRootObject[QStringLiteral("status")].toString() != QStringLiteral("OK")) { qCDebug(KFILEMETADATA_LOG) << outputRootObject[QStringLiteral("error")].toString(); qCDebug(KFILEMETADATA_LOG) << errorOutput; } } diff --git a/src/extractorcollection.cpp b/src/extractorcollection.cpp index 6a95446..0bb8d3b 100644 --- a/src/extractorcollection.cpp +++ b/src/extractorcollection.cpp @@ -1,179 +1,178 @@ /* * Copyright (C) 2012 Vishesh Handa * Copyright (C) 2016 Varun Joshi * * This library 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 2.1 of the License, or (at your option) any later version. * * This library 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "extractor.h" #include "extractorplugin.h" #include "extractorcollection.h" #include "externalextractor.h" #include "kfilemetadata_debug.h" #include #include #include #include #include "config-kfilemetadata.h" using namespace KFileMetaData; class Q_DECL_HIDDEN ExtractorCollection::Private { public: QHash m_extractors; QList m_allExtractors; QList allExtractors() const; }; ExtractorCollection::ExtractorCollection() : d(new Private) { d->m_allExtractors = d->allExtractors(); - for (Extractor* ex : qAsConst(d->m_allExtractors)) { - const QStringList lstMimetypes = ex->mimetypes(); - for (const QString& type : lstMimetypes) { + foreach (Extractor* ex, d->m_allExtractors) { + foreach (const QString& type, ex->mimetypes()) { d->m_extractors.insertMulti(type, ex); } } } ExtractorCollection::~ExtractorCollection() { qDeleteAll(d->m_allExtractors.begin(), d->m_allExtractors.end()); delete d; } QList ExtractorCollection::allExtractors() const { return d->m_allExtractors; } QList ExtractorCollection::Private::allExtractors() const { QStringList plugins; QStringList pluginPaths; QStringList externalPlugins; QStringList externalPluginPaths; - const QStringList paths = QCoreApplication::libraryPaths(); - for (const QString& libraryPath : paths) { + QStringList paths = QCoreApplication::libraryPaths(); + Q_FOREACH (const QString& libraryPath, paths) { QString path(libraryPath + QStringLiteral("/kf5/kfilemetadata")); QDir dir(path); if (!dir.exists()) { continue; } const QStringList entryList = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); for (const QString& fileName : entryList) { // Make sure the same plugin is not loaded twice, even if it is // installed in two different locations if (plugins.contains(fileName)) continue; plugins << fileName; pluginPaths << dir.absoluteFilePath(fileName); } } plugins.clear(); QDir externalPluginDir(QStringLiteral(LIBEXEC_INSTALL_DIR) + QStringLiteral("/kfilemetadata/externalextractors")); // For external plugins, we look into the directories const QStringList externalPluginEntryList = externalPluginDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); for (const QString& externalPlugin : externalPluginEntryList) { if (externalPlugins.contains(externalPlugin)) continue; externalPlugins << externalPlugin; externalPluginPaths << externalPluginDir.absoluteFilePath(externalPlugin); } externalPlugins.clear(); QList extractors; - for (const QString& pluginPath : qAsConst(pluginPaths)) { + Q_FOREACH (const QString& pluginPath, pluginPaths) { QPluginLoader loader(pluginPath); if (!loader.load()) { qWarning() << "Could not create Extractor: " << pluginPath; qWarning() << loader.errorString(); continue; } QObject* obj = loader.instance(); if (obj) { ExtractorPlugin* plugin = qobject_cast(obj); if (plugin) { Extractor* ex= new Extractor; ex->setExtractorPlugin(plugin); ex->setAutoDeletePlugin(Extractor::DoNotDeletePlugin); auto metadata = loader.metaData().value(QLatin1String("MetaData")); if (metadata.type() == QJsonValue::Object) { qCDebug(KFILEMETADATA_LOG) << "Created Plugin with metadata:" << metadata; ex->setMetaData(metadata.toObject().toVariantMap()); } extractors << ex; } else { qCDebug(KFILEMETADATA_LOG) << "Plugin could not be converted to an ExtractorPlugin"; qCDebug(KFILEMETADATA_LOG) << pluginPath; } } else { qCDebug(KFILEMETADATA_LOG) << "Plugin could not create instance" << pluginPath; } } - for (const QString& externalPluginPath : qAsConst(externalPluginPaths)) { + Q_FOREACH (const QString& externalPluginPath, externalPluginPaths) { ExternalExtractor *plugin = new ExternalExtractor(externalPluginPath); Extractor* extractor = new Extractor; extractor->setExtractorPlugin(plugin); extractor->setAutoDeletePlugin(Extractor::AutoDeletePlugin); extractors << extractor; } return extractors; } QList ExtractorCollection::fetchExtractors(const QString& mimetype) const { QList plugins = d->m_extractors.values(mimetype); if (!plugins.isEmpty()) { return plugins; } // try to find the best matching more generic extractor by mimetype inheritance QMimeDatabase db; auto type = db.mimeTypeForName(mimetype); const QStringList ancestors = type.allAncestors(); for (auto ancestor : ancestors) { QList plugins = d->m_extractors.values(ancestor); if (!plugins.isEmpty()) { qCDebug(KFILEMETADATA_LOG) << "Using inherited mimetype" << ancestor << "for" << mimetype; return plugins; } } return plugins; } diff --git a/src/extractorplugin.cpp b/src/extractorplugin.cpp index 9e9dc1f..00e4227 100644 --- a/src/extractorplugin.cpp +++ b/src/extractorplugin.cpp @@ -1,145 +1,145 @@ /* Copyright (C) 2012 Vishesh Handa Copyright (C) 2012 Jörg Ehrichs This library 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 2.1 of the License, or (at your option) any later version. This library 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 library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "extractorplugin.h" #include using namespace KFileMetaData; ExtractorPlugin::ExtractorPlugin(QObject* parent): QObject(parent) { } ExtractorPlugin::~ExtractorPlugin() { } // // Helper functions // QDateTime ExtractorPlugin::dateTimeFromString(const QString& dateString) { QDateTime dateTime; if (!dateTime.isValid()) { dateTime = QDateTime::fromString(dateString, QStringLiteral("yyyy-MM-dd")); dateTime.setTimeSpec(Qt::UTC); } if (!dateTime.isValid()) { dateTime = QDateTime::fromString(dateString, QStringLiteral("dd-MM-yyyy")); dateTime.setTimeSpec(Qt::UTC); } if (!dateTime.isValid()) { dateTime = QDateTime::fromString(dateString, QStringLiteral("yyyy-MM")); dateTime.setTimeSpec(Qt::UTC); } if (!dateTime.isValid()) { dateTime = QDateTime::fromString(dateString, QStringLiteral("MM-yyyy")); dateTime.setTimeSpec(Qt::UTC); } if (!dateTime.isValid()) { dateTime = QDateTime::fromString(dateString, QStringLiteral("yyyy.MM.dd")); dateTime.setTimeSpec(Qt::UTC); } if (!dateTime.isValid()) { dateTime = QDateTime::fromString(dateString, QStringLiteral("dd.MM.yyyy")); dateTime.setTimeSpec(Qt::UTC); } if (!dateTime.isValid()) { dateTime = QDateTime::fromString(dateString, QStringLiteral("dd MMMM yyyy")); dateTime.setTimeSpec(Qt::UTC); } if (!dateTime.isValid()) { dateTime = QDateTime::fromString(dateString, QStringLiteral("MM.yyyy")); dateTime.setTimeSpec(Qt::UTC); } if (!dateTime.isValid()) { dateTime = QDateTime::fromString(dateString, QStringLiteral("yyyy.MM")); dateTime.setTimeSpec(Qt::UTC); } if (!dateTime.isValid()) { dateTime = QDateTime::fromString(dateString, QStringLiteral("yyyy")); dateTime.setTimeSpec(Qt::UTC); } if (!dateTime.isValid()) { dateTime = QDateTime::fromString(dateString, QStringLiteral("yy")); dateTime.setTimeSpec(Qt::UTC); } if (!dateTime.isValid()) { dateTime = QDateTime::fromString(dateString, Qt::ISODate); } if (!dateTime.isValid()) { dateTime = QDateTime::fromString(dateString, QStringLiteral("dddd d MMM yyyy h':'mm':'ss AP")); dateTime.setTimeSpec(Qt::LocalTime); } if (!dateTime.isValid()) { dateTime = QDateTime::fromString(dateString, QStringLiteral("yyyy:MM:dd hh:mm:ss")); dateTime.setTimeSpec(Qt::LocalTime); } if (!dateTime.isValid()) { dateTime = QDateTime::fromString(dateString, Qt::SystemLocaleDate); dateTime.setTimeSpec(Qt::UTC); } if (!dateTime.isValid()) { dateTime = QDateTime::fromString(dateString, Qt::SystemLocaleShortDate); dateTime.setTimeSpec(Qt::UTC); } if (!dateTime.isValid()) { dateTime = QDateTime::fromString(dateString, Qt::SystemLocaleLongDate); dateTime.setTimeSpec(Qt::UTC); } if (!dateTime.isValid()) { qWarning() << "Could not determine correct datetime format from:" << dateString; return QDateTime(); } return dateTime; } QStringList ExtractorPlugin::contactsFromString(const QString& string) { QString cleanedString = string; cleanedString = cleanedString.remove(QLatin1Char('{')); cleanedString = cleanedString.remove(QLatin1Char('}')); QStringList contactStrings = string.split(QLatin1Char(','), QString::SkipEmptyParts); if (contactStrings.size() == 1) contactStrings = string.split(QLatin1Char(';'), QString::SkipEmptyParts); if (contactStrings.size() == 1) contactStrings = string.split(QStringLiteral(" ft "), QString::SkipEmptyParts); if (contactStrings.size() == 1) contactStrings = string.split(QStringLiteral(" feat. "), QString::SkipEmptyParts); if (contactStrings.size() == 1) contactStrings = string.split(QStringLiteral(" feat "), QString::SkipEmptyParts); QStringList list; list.reserve(contactStrings.count()); - for(const QString& contactName : qAsConst(contactStrings)) { + foreach(const QString& contactName, contactStrings) { list << contactName.trimmed(); } return list; } diff --git a/src/extractors/office2007extractor.cpp b/src/extractors/office2007extractor.cpp index f777814..10ea544 100644 --- a/src/extractors/office2007extractor.cpp +++ b/src/extractors/office2007extractor.cpp @@ -1,294 +1,294 @@ /* Copyright (C) 2013 Vishesh Handa This library 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 2.1 of the License, or (at your option) any later version. This library 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 library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "office2007extractor.h" #include #include #include #include using namespace KFileMetaData; Office2007Extractor::Office2007Extractor(QObject* parent) : ExtractorPlugin(parent) { } const QStringList supportedMimeTypes = { QStringLiteral("application/vnd.openxmlformats-officedocument.wordprocessingml.document"), QStringLiteral("application/vnd.openxmlformats-officedocument.presentationml.presentation"), QStringLiteral("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"), }; QStringList Office2007Extractor::mimetypes() const { return supportedMimeTypes; } void Office2007Extractor::extract(ExtractionResult* result) { KZip zip(result->inputUrl()); if (!zip.open(QIODevice::ReadOnly)) { qWarning() << "Document is not a valid ZIP archive"; return; } const KArchiveDirectory* rootDir = zip.directory(); if (!rootDir) { qWarning() << "Invalid document structure (main directory is missing)"; return; } const QStringList rootEntries = rootDir->entries(); if (!rootEntries.contains(QStringLiteral("docProps"))) { qWarning() << "Invalid document structure (docProps is missing)"; return; } const KArchiveEntry* docPropEntry = rootDir->entry(QStringLiteral("docProps")); if (!docPropEntry->isDirectory()) { qWarning() << "Invalid document structure (docProps is not a directory)"; return; } const KArchiveDirectory* docPropDirectory = dynamic_cast(docPropEntry); const QStringList docPropsEntries = docPropDirectory->entries(); if (docPropsEntries.contains(QStringLiteral("core.xml"))) { QDomDocument coreDoc(QStringLiteral("core")); const KArchiveFile* file = static_cast(docPropDirectory->entry(QStringLiteral("core.xml"))); coreDoc.setContent(file->data()); QDomElement docElem = coreDoc.documentElement(); QDomElement elem = docElem.firstChildElement(QStringLiteral("dc:description")); if (!elem.isNull()) { QString str = elem.text(); if (!str.isEmpty()) { result->add(Property::Description, str); } } elem = docElem.firstChildElement(QStringLiteral("dc:subject")); if (!elem.isNull()) { QString str = elem.text(); if (!str.isEmpty()) { result->add(Property::Subject, str); } } elem = docElem.firstChildElement(QStringLiteral("dc:title")); if (!elem.isNull()) { QString str = elem.text(); if (!str.isEmpty()) { result->add(Property::Title, str); } } elem = docElem.firstChildElement(QStringLiteral("dc:creator")); if (!elem.isNull()) { QString str = elem.text(); if (!str.isEmpty()) { result->add(Property::Author, str); } } elem = docElem.firstChildElement(QStringLiteral("dc:language")); if (!elem.isNull()) { QString str = elem.text(); if (!str.isEmpty()) { result->add(Property::Language, str); } } elem = docElem.firstChildElement(QStringLiteral("dcterms:created")); if (!elem.isNull()) { QString str = elem.text(); QDateTime dt = dateTimeFromString(str); if (!dt.isNull()) { result->add(Property::CreationDate, dt); } } elem = docElem.firstChildElement(QStringLiteral("cp:keywords")); if (!elem.isNull()) { QString str = elem.text(); if (!str.isEmpty()) { result->add(Property::Keywords, str); } } } if (docPropsEntries.contains(QStringLiteral("app.xml"))) { QDomDocument appDoc(QStringLiteral("app")); const KArchiveFile* file = static_cast(docPropDirectory->entry(QStringLiteral("app.xml"))); appDoc.setContent(file->data()); QDomElement docElem = appDoc.documentElement(); // According to the ontologies only Documents can have a wordCount and pageCount const QString mimeType = result->inputMimetype(); if (mimeType == QLatin1String("application/vnd.openxmlformats-officedocument.wordprocessingml.document")) { QDomElement elem = docElem.firstChildElement(QStringLiteral("Pages")); if (!elem.isNull()) { bool ok = false; int pageCount = elem.text().toInt(&ok); if (ok) { result->add(Property::PageCount, pageCount); } } elem = docElem.firstChildElement(QStringLiteral("Words")); if (!elem.isNull()) { bool ok = false; int wordCount = elem.text().toInt(&ok); if (ok) { result->add(Property::WordCount, wordCount); } } } QDomElement elem = docElem.firstChildElement(QStringLiteral("Application")); if (!elem.isNull()) { QString app = elem.text(); if (!app.isEmpty()) { result->add(Property::Generator, app); } } } // // Plain Text // bool extractPlainText = (result->inputFlags() & ExtractionResult::ExtractPlainText); if (rootEntries.contains(QStringLiteral("word"))) { result->addType(Type::Document); if (!extractPlainText) return; const KArchiveEntry* wordEntry = rootDir->entry(QStringLiteral("word")); if (!wordEntry->isDirectory()) { qWarning() << "Invalid document structure (word is not a directory)"; return; } const KArchiveDirectory* wordDirectory = dynamic_cast(wordEntry); const QStringList wordEntries = wordDirectory->entries(); if (wordEntries.contains(QStringLiteral("document.xml"))) { const KArchiveFile* file = static_cast(wordDirectory->entry(QStringLiteral("document.xml"))); extractTextWithTag(file->createDevice(), QStringLiteral("w:t"), result); } } else if (rootEntries.contains(QStringLiteral("xl"))) { result->addType(Type::Document); result->addType(Type::Spreadsheet); if (!extractPlainText) return; const KArchiveEntry* xlEntry = rootDir->entry(QStringLiteral("xl")); if (!xlEntry->isDirectory()) { qWarning() << "Invalid document structure (xl is not a directory)"; return; } const KArchiveDirectory* xlDirectory = dynamic_cast(xlEntry); extractTextFromFiles(xlDirectory, result); } else if (rootEntries.contains(QStringLiteral("ppt"))) { result->addType(Type::Document); result->addType(Type::Presentation); if (!extractPlainText) return; const KArchiveEntry* pptEntry = rootDir->entry(QStringLiteral("ppt")); if (!pptEntry->isDirectory()) { qWarning() << "Invalid document structure (ppt is not a directory)"; return; } const KArchiveDirectory* pptDirectory = dynamic_cast(pptEntry); extractTextFromFiles(pptDirectory, result); } } void Office2007Extractor::extractAllText(QIODevice* device, ExtractionResult* result) { QXmlStreamReader xml(device); while (!xml.atEnd()) { xml.readNext(); if (xml.isCharacters()) { QString str = xml.text().toString(); result->append(str); } if (xml.isEndDocument() || xml.hasError()) break; } } void Office2007Extractor::extractTextFromFiles(const KArchiveDirectory* archiveDir, ExtractionResult* result) { const QStringList entries = archiveDir->entries(); - for (const QString & entryName : entries) { + foreach(const QString & entryName, entries) { const KArchiveEntry* entry = archiveDir->entry(entryName); if (entry->isDirectory()) { const KArchiveDirectory* subDir = dynamic_cast(entry); extractTextFromFiles(subDir, result); continue; } if (!entryName.endsWith(QLatin1String(".xml"))) continue; const KArchiveFile* file = static_cast(entry); extractAllText(file->createDevice(), result); } } void Office2007Extractor::extractTextWithTag(QIODevice* device, const QString& tag, ExtractionResult* result) { QXmlStreamReader xml(device); while (!xml.atEnd()) { xml.readNext(); if (xml.qualifiedName().startsWith(tag) && xml.isStartElement()) { QString str = xml.readElementText(QXmlStreamReader::IncludeChildElements); if (!str.isEmpty()) { result->append(str); } } if (xml.isEndDocument() || xml.hasError()) break; } } diff --git a/src/extractors/taglibextractor.cpp b/src/extractors/taglibextractor.cpp index 54dca7c..5cf556c 100644 --- a/src/extractors/taglibextractor.cpp +++ b/src/extractors/taglibextractor.cpp @@ -1,1104 +1,1104 @@ /* Copyright (C) 2012 Vishesh Handa This library 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 2.1 of the License, or (at your option) any later version. This library 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 library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "taglibextractor.h" // Taglib includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KFileMetaData; TagLibExtractor::TagLibExtractor(QObject* parent) : ExtractorPlugin(parent) { } const QStringList supportedMimeTypes = { QStringLiteral("audio/flac"), QStringLiteral("audio/mp4"), QStringLiteral("audio/mpeg"), QStringLiteral("audio/mpeg3"), QStringLiteral("audio/ogg"), QStringLiteral("audio/opus"), QStringLiteral("audio/speex"), QStringLiteral("audio/wav"), QStringLiteral("audio/x-aiff"), QStringLiteral("audio/x-ape"), QStringLiteral("audio/x-mpeg"), QStringLiteral("audio/x-ms-wma"), QStringLiteral("audio/x-musepack"), QStringLiteral("audio/x-opus+ogg"), QStringLiteral("audio/x-speex"), QStringLiteral("audio/x-vorbis+ogg"), QStringLiteral("audio/x-wav"), QStringLiteral("audio/x-wavpack"), }; QStringList TagLibExtractor::mimetypes() const { return supportedMimeTypes; } void TagLibExtractor::extractId3Tags(TagLib::ID3v2::Tag* Id3Tags, ExtractedData& data) { if (Id3Tags->isEmpty()) { return; } TagLib::ID3v2::FrameList lstID3v2; // Artist. lstID3v2 = Id3Tags->frameListMap()["TPE1"]; for (const auto& frame : qAsConst(lstID3v2)) { if (!data.artists.isEmpty()) { data.artists += ", "; } data.artists += frame->toString(); } // Album Artist. lstID3v2 = Id3Tags->frameListMap()["TPE2"]; for (const auto& frame : qAsConst(lstID3v2)) { if (!data.albumArtists.isEmpty()) { data.albumArtists += ", "; } data.albumArtists += frame->toString(); } // Composer. lstID3v2 = Id3Tags->frameListMap()["TCOM"]; for (const auto& frame : qAsConst(lstID3v2)) { if (!data.composers.isEmpty()) { data.composers += ", "; } data.composers += frame->toString(); } // Lyricist. lstID3v2 = Id3Tags->frameListMap()["TEXT"]; for (const auto& frame : qAsConst(lstID3v2)) { if (!data.lyricists.isEmpty()) { data.lyricists += ", "; } data.lyricists += frame->toString(); } // Genre. lstID3v2 = Id3Tags->frameListMap()["TCON"]; for (const auto& frame : qAsConst(lstID3v2)) { data.genres.append(frame->toString()); } // Disc number. lstID3v2 = Id3Tags->frameListMap()["TPOS"]; for (const auto& frame : qAsConst(lstID3v2)) { data.discNumber = frame->toString().toInt(); } // Performer. lstID3v2 = Id3Tags->frameListMap()["TMCL"]; for (const auto& frame : qAsConst(lstID3v2)) { if (!data.performer.isEmpty()) { data.performer += ", "; } data.performer += frame->toString(); } // Conductor. lstID3v2 = Id3Tags->frameListMap()["TPE3"]; for (const auto& frame : qAsConst(lstID3v2)) { if (!data.conductor.isEmpty()) { data.conductor += ", "; } data.conductor += frame->toString(); } // Publisher. lstID3v2 = Id3Tags->frameListMap()["TPUB"]; for (const auto& frame : qAsConst(lstID3v2)) { if (!data.publisher.isEmpty()) { data.publisher += ", "; } data.publisher += frame->toString(); } // Copyright. lstID3v2 = Id3Tags->frameListMap()["TCOP"]; for (const auto& frame : qAsConst(lstID3v2)) { if (!data.copyright.isEmpty()) { data.copyright += ", "; } data.copyright += frame->toString(); } // Language. lstID3v2 = Id3Tags->frameListMap()["TLAN"]; for (const auto& frame : qAsConst(lstID3v2)) { if (!data.language.isEmpty()) { data.language += ", "; } data.language += frame->toString(); } // Lyrics. lstID3v2 = Id3Tags->frameListMap()["USLT"]; for (const auto& frame : qAsConst(lstID3v2)) { if (!data.lyrics.isEmpty()) { data.lyrics += ", "; } data.lyrics += frame->toString(); } // Compilation. lstID3v2 = Id3Tags->frameListMap()["TCMP"]; for (const auto& frame : qAsConst(lstID3v2)) { if (!data.compilation.isEmpty()) { data.compilation += ", "; } data.compilation += frame->toString(); } // Rating. /* There is no standard regarding ratings. Most of the implementations match a 5 stars rating to a range of 0-255 for MP3. Match it to baloo rating with a range of 0 - 10 */ lstID3v2 = Id3Tags->frameListMap()["POPM"]; for (const auto& frame : qAsConst(lstID3v2)) { TagLib::ID3v2::PopularimeterFrame *ratingFrame = static_cast(frame); int rating = ratingFrame->rating(); if (rating == 0) { data.rating = 0; } else if (rating == 1) { TagLib::String ratingProvider = ratingFrame->email(); if (ratingProvider == "no@email" || ratingProvider == "org.kde.kfilemetadata") { data.rating = 1; } else { data.rating = 2; } } else if (rating >= 1 && rating <= 255) { data.rating = static_cast(0.032 * rating + 2); } } // User Text Frame. lstID3v2 = Id3Tags->frameListMap()["TXXX"]; if (!lstID3v2.isEmpty()) { // look for ReplayGain tags typedef TagLib::ID3v2::UserTextIdentificationFrame IdFrame; auto trackGainFrame = IdFrame::find(Id3Tags, "replaygain_track_gain"); if (trackGainFrame && !trackGainFrame->fieldList().isEmpty()) { data.replayGainTrackGain = TStringToQString(trackGainFrame->fieldList().back()); } auto trackPeakFrame = IdFrame::find(Id3Tags, "replaygain_track_peak"); if (trackPeakFrame && !trackPeakFrame->fieldList().isEmpty()) { data.replayGainTrackPeak = TStringToQString(trackPeakFrame->fieldList().back()); } auto albumGainFrame = IdFrame::find(Id3Tags, "replaygain_album_gain"); if (albumGainFrame && !albumGainFrame->fieldList().isEmpty()) { data.replayGainAlbumGain = TStringToQString(albumGainFrame->fieldList().back()); } auto albumPeakFrame = IdFrame::find(Id3Tags, "replaygain_album_peak"); if (albumPeakFrame && !albumPeakFrame->fieldList().isEmpty()) { data.replayGainAlbumPeak = TStringToQString(albumPeakFrame->fieldList().back()); } } //TODO handle TIPL tag } void TagLibExtractor::extractMp4Tags(TagLib::MP4::Tag* mp4Tags, ExtractedData& data) { if (mp4Tags->isEmpty()) { return; } TagLib::MP4::ItemListMap allTags = mp4Tags->itemListMap(); TagLib::MP4::ItemListMap::Iterator itAlbumArtists = allTags.find("aART"); if (itAlbumArtists != allTags.end()) { data.albumArtists = itAlbumArtists->second.toStringList().toString(", "); } TagLib::MP4::ItemListMap::Iterator itDiscNumber = allTags.find("disk"); if (itDiscNumber != allTags.end()) { data.discNumber = itDiscNumber->second.toInt(); } TagLib::MP4::ItemListMap::Iterator itCompilation = allTags.find("cpil"); if (itCompilation != allTags.end()) { data.compilation = itCompilation->second.toStringList().toString(", "); } TagLib::MP4::ItemListMap::Iterator itCopyright = allTags.find("cprt"); if (itCopyright != allTags.end()) { data.copyright = itCopyright->second.toStringList().toString(", "); } TagLib::String genreAtomName(TagLib::String("©gen", TagLib::String::UTF8).to8Bit(), TagLib::String::Latin1); TagLib::MP4::ItemListMap::Iterator itGenres = allTags.find(genreAtomName); if (itGenres != allTags.end()) { data.genres = itGenres->second.toStringList().toString(", "); } TagLib::String composerAtomName(TagLib::String("©wrt", TagLib::String::UTF8).to8Bit(), TagLib::String::Latin1); TagLib::MP4::ItemListMap::Iterator itComposers = allTags.find(composerAtomName); if (itComposers != allTags.end()) { data.composers = itComposers->second.toStringList().toString(", "); } /* There is no standard regarding ratings. Mimic MediaMonkey's behavior with a range of 0 to 100 (stored in steps of 10) and make it compatible with baloo rating with a range from 0 to 10 */ TagLib::MP4::ItemListMap::Iterator itRating = allTags.find("rate"); if (itRating != allTags.end()) { data.rating = itRating->second.toStringList().toString().toInt() / 10; } TagLib::String lyricsAtomName(TagLib::String("©lyr", TagLib::String::UTF8).to8Bit(), TagLib::String::Latin1); TagLib::MP4::ItemListMap::Iterator itLyrics = allTags.find(lyricsAtomName); if (itLyrics != allTags.end()) { data.lyrics = itLyrics->second.toStringList().toString(", "); } } void TagLibExtractor::extractApeTags(TagLib::APE::Tag* apeTags, ExtractedData& data) { if (apeTags->isEmpty()) { return; } TagLib::APE::ItemListMap lstApe = apeTags->itemListMap(); TagLib::APE::ItemListMap::ConstIterator itApe; itApe = lstApe.find("ARTIST"); if (itApe != lstApe.end()) { if (!data.artists.isEmpty()) { data.artists += ", "; } data.artists += (*itApe).second.toString(); } itApe = lstApe.find("ALBUMARTIST"); if (itApe == lstApe.end()) { itApe = lstApe.find("ALBUM ARTIST"); } if (itApe != lstApe.end()) { if(!data.albumArtists.isEmpty()) { data.albumArtists += ", "; } data.albumArtists += (*itApe).second.toString(); } itApe = lstApe.find("COMPOSER"); if (itApe != lstApe.end()) { if (!data.composers.isEmpty()) { data.composers += ", "; } data.composers += (*itApe).second.toString(); } itApe = lstApe.find("LYRICIST"); if (itApe != lstApe.end()) { if (!data.lyricists.isEmpty()) { data.lyricists += ", "; } data.lyricists += (*itApe).second.toString(); } itApe = lstApe.find("GENRE"); if (itApe != lstApe.end()) { data.genres.append((*itApe).second.toString()); } itApe = lstApe.find("LOCATION"); if (itApe != lstApe.end()) { if (!data.location.isEmpty()) { data.location += ", "; } data.location += (*itApe).second.toString(); } itApe = lstApe.find("ARRANGER"); if (itApe != lstApe.end()) { if (!data.arranger.isEmpty()) { data.arranger += ", "; } data.arranger += (*itApe).second.toString(); } itApe = lstApe.find("PERFORMER"); if (itApe != lstApe.end()) { if (!data.performer.isEmpty()) { data.performer += ", "; } data.performer += (*itApe).second.toString(); } itApe = lstApe.find("CONDUCTOR"); if (itApe != lstApe.end()) { if (!data.conductor.isEmpty()) { data.conductor += ", "; } data.conductor += (*itApe).second.toString(); } itApe = lstApe.find("ENSEMBLE"); if (itApe != lstApe.end()) { if (!data.ensemble.isEmpty()) { data.ensemble += ", "; } data.ensemble += (*itApe).second.toString(); } itApe = lstApe.find("PUBLISHER"); if (itApe != lstApe.end()) { if (!data.publisher.isEmpty()) { data.publisher += ", "; } data.publisher += (*itApe).second.toString(); } itApe = lstApe.find("COPYRIGHT"); if (itApe != lstApe.end()) { if (!data.copyright.isEmpty()) { data.copyright += ", "; } data.copyright += (*itApe).second.toString(); } itApe = lstApe.find("LABEL"); if (itApe != lstApe.end()) { if (!data.label.isEmpty()) { data.label += ", "; } data.label += (*itApe).second.toString(); } itApe = lstApe.find("AUTHOR"); if (itApe != lstApe.end()) { if (!data.author.isEmpty()) { data.author += ", "; } data.author += (*itApe).second.toString(); } itApe = lstApe.find("LICENSE"); if (itApe != lstApe.end()) { if (!data.license.isEmpty()) { data.license += ", "; } data.license += (*itApe).second.toString(); } itApe = lstApe.find("LYRICS"); if (itApe != lstApe.end()) { if (!data.lyrics.isEmpty()) { data.lyrics += ", "; } data.lyrics += (*itApe).second.toString(); } itApe = lstApe.find("COMPILATION"); if (itApe != lstApe.end()) { if (!data.compilation.isEmpty()) { data.compilation += ", "; } data.compilation += (*itApe).second.toString(); } itApe = lstApe.find("LANGUAGE"); if (itApe != lstApe.end()) { if (!data.language.isEmpty()) { data.language += ", "; } data.language += (*itApe).second.toString(); } itApe = lstApe.find("DISC"); if (itApe == lstApe.end()) { itApe = lstApe.find("DISCNUMBER"); } if (itApe != lstApe.end()) { data.discNumber = (*itApe).second.toString().toInt(); } itApe = lstApe.find("OPUS"); if (itApe != lstApe.end()) { data.opus = (*itApe).second.toString().toInt(); } itApe = lstApe.find("RATING"); if (itApe != lstApe.end()) { /* There is no standard regarding ratings. There is one implementation most seem to follow with a range of 0 to 100 (stored in steps of 10). Make it compatible with baloo rating with a range from 0 to 10 */ data.rating = (*itApe).second.toString().toInt() / 10; } itApe = lstApe.find("REPLAYGAIN_TRACK_GAIN"); if (itApe != lstApe.end()) { data.replayGainTrackGain = TStringToQString((*itApe).second.toString()); } itApe = lstApe.find("REPLAYGAIN_TRACK_PEAK"); if (itApe != lstApe.end()) { data.replayGainTrackPeak = TStringToQString((*itApe).second.toString()); } itApe = lstApe.find("REPLAYGAIN_ALBUM_GAIN"); if (itApe != lstApe.end()) { data.replayGainAlbumGain = TStringToQString((*itApe).second.toString()); } itApe = lstApe.find("REPLAYGAIN_ALBUM_PEAK"); if (itApe != lstApe.end()) { data.replayGainAlbumPeak = TStringToQString((*itApe).second.toString()); } } void TagLibExtractor::extractVorbisTags(TagLib::Ogg::XiphComment* vorbisTags, ExtractedData& data) { if (vorbisTags->isEmpty()) { return; } TagLib::Ogg::FieldListMap lstOgg = vorbisTags->fieldListMap(); TagLib::Ogg::FieldListMap::ConstIterator itOgg; itOgg = lstOgg.find("ARTIST"); if (itOgg != lstOgg.end()) { if (!data.artists.isEmpty()) { data.artists += ", "; } data.artists += (*itOgg).second.toString(", "); } itOgg = lstOgg.find("ALBUMARTIST"); if (itOgg == lstOgg.end()) { itOgg = lstOgg.find("ALBUM ARTIST"); } if (itOgg != lstOgg.end()) { if (!data.albumArtists.isEmpty()) { data.albumArtists += ", "; } data.albumArtists += (*itOgg).second.toString(", "); } itOgg = lstOgg.find("COMPOSER"); if (itOgg != lstOgg.end()) { if (!data.composers.isEmpty()) { data.composers += ", "; } data.composers += (*itOgg).second.toString(", "); } itOgg = lstOgg.find("LYRICIST"); if (itOgg != lstOgg.end()) { if (!data.lyricists.isEmpty()) { data.lyricists += ", "; } data.lyricists += (*itOgg).second.toString(", "); } itOgg = lstOgg.find("LOCATION"); if (itOgg != lstOgg.end()) { if (!data.location.isEmpty()) { data.location += ", "; } data.location += (*itOgg).second.toString(", "); } itOgg = lstOgg.find("ARRANGER"); if (itOgg != lstOgg.end()) { if (!data.arranger.isEmpty()) { data.arranger += ", "; } data.arranger += (*itOgg).second.toString(", "); } itOgg = lstOgg.find("PERFORMER"); if (itOgg != lstOgg.end()) { if (!data.performer.isEmpty()) { data.performer += ", "; } data.performer += (*itOgg).second.toString(", "); } itOgg = lstOgg.find("CONDUCTOR"); if (itOgg != lstOgg.end()) { if (!data.conductor.isEmpty()) { data.conductor += ", "; } data.conductor += (*itOgg).second.toString(", "); } itOgg = lstOgg.find("ENSEMBLE"); if (itOgg != lstOgg.end()) { if (!data.ensemble.isEmpty()) { data.ensemble += ", "; } data.ensemble += (*itOgg).second.toString(", "); } itOgg = lstOgg.find("PUBLISHER"); if (itOgg != lstOgg.end()) { if (!data.publisher.isEmpty()) { data.publisher += ", "; } data.publisher += (*itOgg).second.toString(", "); } itOgg = lstOgg.find("COPYRIGHT"); if (itOgg != lstOgg.end()) { if (!data.copyright.isEmpty()) { data.copyright += ", "; } data.copyright += (*itOgg).second.toString(", "); } itOgg = lstOgg.find("LABEL"); if (itOgg != lstOgg.end()) { if (!data.label.isEmpty()) { data.label += ", "; } data.label += (*itOgg).second.toString(", "); } itOgg = lstOgg.find("AUTHOR"); if (itOgg != lstOgg.end()) { if (!data.author.isEmpty()) { data.author += ", "; } data.author += (*itOgg).second.toString(", "); } itOgg = lstOgg.find("LICENSE"); if (itOgg != lstOgg.end()) { if (!data.license.isEmpty()) { data.license += ", "; } data.license += (*itOgg).second.toString(", "); } itOgg = lstOgg.find("LYRICS"); if (itOgg != lstOgg.end()) { if (!data.lyrics.isEmpty()) { data.lyrics += ", "; } data.lyrics += (*itOgg).second.toString(", "); } itOgg = lstOgg.find("COMPILATION"); if (itOgg != lstOgg.end()) { if (!data.compilation.isEmpty()) { data.compilation += ", "; } data.compilation += (*itOgg).second.toString(", "); } itOgg = lstOgg.find("LANGUAGE"); if (itOgg != lstOgg.end()) { if (!data.language.isEmpty()) { data.language += ", "; } data.language += (*itOgg).second.toString(", "); } itOgg = lstOgg.find("GENRE"); if (itOgg != lstOgg.end()) { data.genres.append((*itOgg).second); } itOgg = lstOgg.find("DISCNUMBER"); if (itOgg != lstOgg.end()) { data.discNumber = (*itOgg).second.toString("").toInt(); } itOgg = lstOgg.find("OPUS"); if (itOgg != lstOgg.end()) { data.opus = (*itOgg).second.toString("").toInt(); } itOgg = lstOgg.find("RATING"); if (itOgg != lstOgg.end()) { //there is no standard regarding ratings. There is one implementation //most seem to follow with a range of 0 to 100 (stored in steps of 10). //make it compatible with baloo rating with a range from 0 to 10 data.rating = (*itOgg).second.toString("").toInt() / 10; } itOgg = lstOgg.find("REPLAYGAIN_TRACK_GAIN"); if (itOgg != lstOgg.end()) { data.replayGainTrackGain = TStringToQString((*itOgg).second.toString("")); } itOgg = lstOgg.find("REPLAYGAIN_TRACK_PEAK"); if (itOgg != lstOgg.end()) { data.replayGainTrackPeak = TStringToQString((*itOgg).second.toString("")); } itOgg = lstOgg.find("REPLAYGAIN_ALBUM_GAIN"); if (itOgg != lstOgg.end()) { data.replayGainAlbumGain = TStringToQString((*itOgg).second.toString("")); } itOgg = lstOgg.find("REPLAYGAIN_ALBUM_PEAK"); if (itOgg != lstOgg.end()) { data.replayGainAlbumPeak = TStringToQString((*itOgg).second.toString("")); } } void TagLibExtractor::extractAsfTags(TagLib::ASF::Tag* asfTags, ExtractedData& data) { if (asfTags->isEmpty()) { return; } if (!asfTags->copyright().isEmpty()) { data.copyright = asfTags->copyright(); } TagLib::ASF::AttributeList lstASF = asfTags->attribute("WM/SharedUserRating"); if (!lstASF.isEmpty()) { int rating = lstASF.front().toString().toInt(); //map the rating values of WMP to Baloo rating //0->0, 1->2, 25->4, 50->6, 75->8, 99->10 if (rating == 0) { data.rating = 0; } else if (rating == 1) { data.rating = 2; } else { data.rating = static_cast(0.09 * rating + 2); } } lstASF = asfTags->attribute("WM/PartOfSet"); if (!lstASF.isEmpty()) { data.discNumber = lstASF.front().toString().toInt(); } lstASF = asfTags->attribute("WM/AlbumArtist"); for (const auto& attribute : qAsConst(lstASF)) { if (!data.albumArtists.isEmpty()) { data.albumArtists += ", "; } data.albumArtists += attribute.toString(); } lstASF = asfTags->attribute("WM/Composer"); for (const auto& attribute : qAsConst(lstASF)) { if (!data.composers.isEmpty()) { data.composers += ", "; } data.composers += attribute.toString(); } lstASF = asfTags->attribute("WM/Conductor"); for (const auto& attribute : qAsConst(lstASF)) { if (!data.conductor.isEmpty()) { data.conductor += ", "; } data.conductor += attribute.toString(); } lstASF = asfTags->attribute("WM/Writer"); for (const auto& attribute : qAsConst(lstASF)) { if (!data.lyricists.isEmpty()) { data.lyricists += ", "; } data.lyricists += attribute.toString(); } lstASF = asfTags->attribute("WM/Publisher"); for (const auto& attribute : qAsConst(lstASF)) { if (!data.publisher.isEmpty()) { data.publisher += ", "; } data.publisher += attribute.toString(); } lstASF = asfTags->attribute("Author"); for (const auto& attribute : qAsConst(lstASF)) { if (!data.author.isEmpty()) { data.author += ", "; } data.author += attribute.toString(); } } void TagLibExtractor::extract(ExtractionResult* result) { const QString fileUrl = result->inputUrl(); const QString mimeType = result->inputMimetype(); // Open the file readonly. Important if we're sandboxed. TagLib::FileStream stream(fileUrl.toUtf8().constData(), true); if (!stream.isOpen()) { qWarning() << "Unable to open file readonly: " << fileUrl; return; } TagLib::FileRef file(&stream, true); if (file.isNull()) { qWarning() << "Unable to open file: " << fileUrl; return; } TagLib::Tag* tags = file.tag(); result->addType(Type::Audio); ExtractedData data; if ((mimeType == QLatin1String("audio/mpeg")) || (mimeType == QLatin1String("audio/mpeg3")) || (mimeType == QLatin1String("audio/x-mpeg"))) { TagLib::MPEG::File mpegFile(&stream, TagLib::ID3v2::FrameFactory::instance(), true); if (mpegFile.hasID3v2Tag()) { extractId3Tags(mpegFile.ID3v2Tag(), data); } } else if ((mimeType == QLatin1String("audio/x-aiff")) || (mimeType == QLatin1String("audio/wav")) || (mimeType == QLatin1String("audio/x-wav"))) { /* For some reason, TagLib::RIFF::AIFF::File and TagLib::RIFF::WAV::File tag() return * only an invalid pointer. Use the dynamic_cast instead. */ TagLib::ID3v2::Tag* ID3v2Tag = dynamic_cast(tags); if (ID3v2Tag) { extractId3Tags(ID3v2Tag, data); } } else if (mimeType == QLatin1String("audio/mp4")) { TagLib::MP4::File mp4File(&stream, true); if (mp4File.hasMP4Tag()) { extractMp4Tags(mp4File.tag(), data); } } else if (mimeType == QLatin1String("audio/x-musepack")) { TagLib::MPC::File mpcFile(&stream, true); if (mpcFile.hasAPETag()) { extractApeTags(mpcFile.APETag(), data); } } else if (mimeType == QLatin1String("audio/x-ape")) { TagLib::APE::File apeFile(&stream, true); if (apeFile.hasAPETag()) { extractApeTags(apeFile.APETag(), data); } } else if (mimeType == QLatin1String("audio/x-wavpack")) { TagLib::WavPack::File wavpackFile(&stream, true); if (wavpackFile.hasAPETag()) { extractApeTags(wavpackFile.APETag(), data); } } else if (mimeType == QLatin1String("audio/flac")) { TagLib::FLAC::File flacFile(&stream, TagLib::ID3v2::FrameFactory::instance(), true); if (flacFile.hasXiphComment()) { extractVorbisTags(flacFile.xiphComment(), data); } } else if (mimeType == QLatin1String("audio/ogg") || mimeType == QLatin1String("audio/x-vorbis+ogg")) { TagLib::Ogg::Vorbis::File oggFile(&stream, true); if (oggFile.tag()) { extractVorbisTags(oggFile.tag(), data); } } else if (mimeType == QLatin1String("audio/opus") || mimeType == QLatin1String("audio/x-opus+ogg")) { TagLib::Ogg::Opus::File opusFile(&stream, true); if (opusFile.tag()) { extractVorbisTags(opusFile.tag(), data); } } else if (mimeType == QLatin1String("audio/speex") || mimeType == QLatin1String("audio/x-speex")) { TagLib::Ogg::Speex::File speexFile(&stream, true); if (speexFile.tag()) { extractVorbisTags(speexFile.tag(), data); } } else if (mimeType == QLatin1String("audio/x-ms-wma")) { /* For some reason, TagLib::ASF::File tag() returns only an invalid pointer. * Use the dynamic_cast instead. */ TagLib::ASF::Tag* asfTags = dynamic_cast(tags); if (asfTags) { extractAsfTags(asfTags, data); } } if (!tags->isEmpty()) { QString title = TStringToQString(tags->title()); if (!title.isEmpty()) { result->add(Property::Title, title); } QString comment = TStringToQString(tags->comment()); if (!comment.isEmpty()) { result->add(Property::Comment, comment); } if (data.genres.isEmpty()) { data.genres.append(tags->genre()); } for (uint i = 0; i < data.genres.size(); i++) { QString genre = TStringToQString(data.genres[i]).trimmed(); if (!genre.isEmpty()) { // Convert from int bool ok = false; int genreNum = genre.toInt(&ok); if (ok) { genre = TStringToQString(TagLib::ID3v1::genre(genreNum)); } result->add(Property::Genre, genre); } } const auto artistString = data.artists.isEmpty() ? TStringToQString(tags->artist()) : TStringToQString(data.artists).trimmed(); const auto artists = contactsFromString(artistString); for (auto& artist : artists) { result->add(Property::Artist, artist); } const auto composersString = TStringToQString(data.composers).trimmed(); const auto composers = contactsFromString(composersString); for (auto& comp : composers) { result->add(Property::Composer, comp); } const auto lyricistsString = TStringToQString(data.lyricists).trimmed(); const auto lyricists = contactsFromString(lyricistsString); for (auto& lyr : lyricists) { result->add(Property::Lyricist, lyr); } const auto album = TStringToQString(tags->album()); if (!album.isEmpty()) { result->add(Property::Album, album); const auto albumArtistsString = TStringToQString(data.albumArtists).trimmed(); const auto albumArtists = contactsFromString(albumArtistsString); for (auto& res : albumArtists) { result->add(Property::AlbumArtist, res); } } if (tags->track()) { result->add(Property::TrackNumber, tags->track()); } if (tags->year()) { result->add(Property::ReleaseYear, tags->year()); } QString locationsString = TStringToQString(data.location).trimmed(); - const QStringList locations = contactsFromString(locationsString); - for (const QString& loc : locations) { + QStringList locations = contactsFromString(locationsString); + foreach(const QString& loc, locations) { result->add(Property::Location, loc); } QString performersString = TStringToQString(data.performer).trimmed(); - const QStringList performers = contactsFromString(performersString); - for (const QString& per : performers) { + QStringList performers = contactsFromString(performersString); + foreach(const QString& per, performers) { result->add(Property::Performer, per); } QString ensembleString = TStringToQString(data.ensemble).trimmed(); - const QStringList ensembles = contactsFromString(ensembleString); - for (const QString& ens : ensembles) { + QStringList ensembles = contactsFromString(ensembleString); + foreach(const QString& ens, ensembles) { result->add(Property::Ensemble, ens); } QString arrangerString = TStringToQString(data.arranger).trimmed(); - const QStringList arrangers = contactsFromString(arrangerString); - for (const QString& arr : arrangers) { + QStringList arrangers = contactsFromString(arrangerString); + foreach(const QString& arr, arrangers) { result->add(Property::Arranger, arr); } QString conductorString = TStringToQString(data.conductor).trimmed(); - const QStringList conductors = contactsFromString(conductorString); - for (const QString& arr : conductors) { + QStringList conductors = contactsFromString(conductorString); + foreach(const QString& arr, conductors) { result->add(Property::Conductor, arr); } QString publisherString = TStringToQString(data.publisher).trimmed(); - const QStringList publishers = contactsFromString(publisherString); - for (const QString& arr : publishers) { + QStringList publishers = contactsFromString(publisherString); + foreach(const QString& arr, publishers) { result->add(Property::Publisher, arr); } QString copyrightString = TStringToQString(data.copyright).trimmed(); - const QStringList copyrights = contactsFromString(copyrightString); - for (const QString& arr : copyrights) { + QStringList copyrights = contactsFromString(copyrightString); + foreach(const QString& arr, copyrights) { result->add(Property::Copyright, arr); } QString labelString = TStringToQString(data.label).trimmed(); - const QStringList labels = contactsFromString(labelString); - for (const QString& arr : labels) { + QStringList labels = contactsFromString(labelString); + foreach(const QString& arr, labels) { result->add(Property::Label, arr); } QString authorString = TStringToQString(data.author).trimmed(); - const QStringList authors = contactsFromString(authorString); - for (const QString& arr : authors) { + QStringList authors = contactsFromString(authorString); + foreach(const QString& arr, authors) { result->add(Property::Author, arr); } QString languageString = TStringToQString(data.language).trimmed(); - const QStringList languages = contactsFromString(languageString); - for (const QString& arr : languages) { + QStringList languages = contactsFromString(languageString); + foreach(const QString& arr, languages) { result->add(Property::Language, arr); } QString licenseString = TStringToQString(data.license).trimmed(); - const QStringList licenses = contactsFromString(licenseString); - for (const QString& arr : licenses) { + QStringList licenses = contactsFromString(licenseString); + foreach(const QString& arr, licenses) { result->add(Property::License, arr); } QString compilationString = TStringToQString(data.compilation).trimmed(); - const QStringList compilations = contactsFromString(compilationString); - for (const QString& arr : compilations) { + QStringList compilations = contactsFromString(compilationString); + foreach(const QString& arr, compilations) { result->add(Property::Compilation, arr); } QString lyricsString = TStringToQString(data.lyrics).trimmed(); if (!lyricsString.isEmpty()) { result->add(Property::Lyrics, lyricsString); } if (data.opus.isValid()) { result->add(Property::Opus, data.opus); } if (data.discNumber.isValid()) { result->add(Property::DiscNumber, data.discNumber); } if (data.rating.isValid()) { result->add(Property::Rating, data.rating); } if (!data.replayGainAlbumGain.isEmpty()) { /* remove " dB" suffix */ if (data.replayGainAlbumGain.endsWith(QStringLiteral(" dB"), Qt::CaseInsensitive)) { data.replayGainAlbumGain.chop(3); } bool success = false; double replayGainAlbumGain = data.replayGainAlbumGain.toDouble(&success); if (success) { result->add(Property::ReplayGainAlbumGain, replayGainAlbumGain); } } if (!data.replayGainAlbumPeak.isEmpty()) { bool success = false; double replayGainAlbumPeak = data.replayGainAlbumPeak.toDouble(&success); if (success) { result->add(Property::ReplayGainAlbumPeak, replayGainAlbumPeak); } } if (!data.replayGainTrackGain.isEmpty()) { /* remove " dB" suffix */ if (data.replayGainTrackGain.endsWith(QStringLiteral(" dB"), Qt::CaseInsensitive)) { data.replayGainTrackGain.chop(3); } bool success = false; double replayGainTrackGain = data.replayGainTrackGain.toDouble(&success); if (success) { result->add(Property::ReplayGainTrackGain, replayGainTrackGain); } } if (!data.replayGainTrackPeak.isEmpty()) { bool success = false; double replayGainTrackPeak = data.replayGainTrackPeak.toDouble(&success); if (success) { result->add(Property::ReplayGainTrackPeak, replayGainTrackPeak); } } } TagLib::AudioProperties* audioProp = file.audioProperties(); if (audioProp) { if (audioProp->length()) { // What about the xml duration? result->add(Property::Duration, audioProp->length()); } if (audioProp->bitrate()) { result->add(Property::BitRate, audioProp->bitrate() * 1000); } if (audioProp->channels()) { result->add(Property::Channels, audioProp->channels()); } if (audioProp->sampleRate()) { result->add(Property::SampleRate, audioProp->sampleRate()); } } } // TAG information (incomplete). // https://xiph.org/vorbis/doc/v-comment.html // https://help.mp3tag.de/main_tags.html // http://id3.org/ // https://www.legroom.net/2009/05/09/ogg-vorbis-and-flac-comment-field-recommendations // https://kodi.wiki/view/Music_tagging#Tags_Kodi_reads // https://wiki.hydrogenaud.io/index.php?title=Tag_Mapping // https://picard.musicbrainz.org/docs/mappings/ // -- FLAC/OGG -- // Artist: ARTIST, PERFORMER // Album artist: ALBUMARTIST // Composer: COMPOSER // Lyricist: LYRICIST // Conductor: CONDUCTOR // Disc number: DISCNUMBER // Total discs: TOTALDISCS, DISCTOTAL // Track number: TRACKNUMBER // Total tracks: TOTALTRACKS, TRACKTOTAL // Genre: GENRE // -- ID3v2 -- // Artist: TPE1 // Album artist: TPE2 // Composer: TCOM // Lyricist: TEXT // Conductor: TPE3 // Disc number[/total dics]: TPOS // Track number[/total tracks]: TRCK // Genre: TCON diff --git a/src/writercollection.cpp b/src/writercollection.cpp index 8ee96fc..163f801 100644 --- a/src/writercollection.cpp +++ b/src/writercollection.cpp @@ -1,168 +1,167 @@ /* * This file is part of the KFileMetaData project * Copyright (C) 2016 Varun Joshi * Copyright (C) 2016 Vishesh Handa * * This library 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 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 library. If not, see . * */ #include "kfilemetadata_debug.h" #include #include #include #include #include "writer.h" #include "writer_p.h" #include "writerplugin.h" #include "writercollection.h" #include "externalwriter.h" #include "config-kfilemetadata.h" using namespace KFileMetaData; class Q_DECL_HIDDEN WriterCollection::WriterCollectionPrivate { public: QHash m_writers; QList allWriters() const; }; WriterCollection::WriterCollection() : d_ptr(new WriterCollectionPrivate) { Q_D(WriterCollection); const QList all = d->allWriters(); - for (Writer* writer : all) { - const QStringList lst = writer->mimetypes(); - for (const QString& type : lst) { + foreach (Writer* writer, all) { + foreach (const QString& type, writer->mimetypes()) { d->m_writers.insertMulti(type, writer); } } } WriterCollection::~WriterCollection() { Q_D(WriterCollection); qDeleteAll(d->m_writers.begin(), d->m_writers.end()); delete d; } QList WriterCollection::WriterCollectionPrivate::allWriters() const { QStringList plugins; QStringList pluginPaths; QStringList externalPlugins; QStringList externalPluginPaths; const QStringList paths = QCoreApplication::libraryPaths(); - for (const QString& libraryPath : paths) { + Q_FOREACH (const QString& libraryPath, paths) { QString path(libraryPath + QStringLiteral("/kf5/kfilemetadata/writers")); QDir dir(path); if (!dir.exists()) { continue; } const QStringList entryList = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); for (const QString& fileName : entryList) { // Make sure the same plugin is not loaded twice, even if it // installed in two different locations if (plugins.contains(fileName)) continue; plugins << fileName; pluginPaths << dir.absoluteFilePath(fileName); } } plugins.clear(); QDir externalPluginDir(QStringLiteral(LIBEXEC_INSTALL_DIR) + QStringLiteral("/kfilemetadata/writers/externalwriters")); // For external plugins, we look into the directories const QStringList externalPluginEntryList = externalPluginDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); for (const QString& externalPlugin : externalPluginEntryList) { if (externalPlugins.contains(externalPlugin)) continue; externalPlugins << externalPlugin; externalPluginPaths << externalPluginDir.absoluteFilePath(externalPlugin); } externalPlugins.clear(); QList writers; - for (const QString& pluginPath : qAsConst(pluginPaths)) { + Q_FOREACH (const QString& pluginPath, pluginPaths) { QPluginLoader loader(pluginPath); if (!loader.load()) { qCWarning(KFILEMETADATA_LOG) << "Could not create Writer: " << pluginPath; qCWarning(KFILEMETADATA_LOG) << loader.errorString(); continue; } QObject* obj = loader.instance(); if (obj) { WriterPlugin* plugin = qobject_cast(obj); if (plugin) { Writer* writer = new Writer; writer->d_ptr->m_plugin = plugin; writers << writer; } else { qCDebug(KFILEMETADATA_LOG) << "Plugin could not be converted to a WriterPlugin"; qCDebug(KFILEMETADATA_LOG) << pluginPath; } } else { qCDebug(KFILEMETADATA_LOG) << "Plugin could not create instance" << pluginPath; } } - for (const QString& externalPluginPath : qAsConst(externalPluginPaths)) { + Q_FOREACH (const QString& externalPluginPath, externalPluginPaths) { ExternalWriter *plugin = new ExternalWriter(externalPluginPath); Writer* writer = new Writer; writer->d_ptr->m_plugin = plugin; writers << writer; } return writers; } QList WriterCollection::fetchWriters(const QString& mimetype) const { Q_D(const WriterCollection); QList plugins = d->m_writers.values(mimetype); if (plugins.isEmpty()) { auto it = d->m_writers.constBegin(); for (; it != d->m_writers.constEnd(); ++it) { if (mimetype.startsWith(it.key())) plugins << it.value(); } } return plugins; } diff --git a/tests/dump.cpp b/tests/dump.cpp index ab58f5d..8600bd2 100644 --- a/tests/dump.cpp +++ b/tests/dump.cpp @@ -1,76 +1,76 @@ /* * Copyright (C) 2014 Vishesh Handa * * This library 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 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 library. If not, see . * */ #include #include #include #include #include #include #include "extractor.h" #include "extractorcollection.h" #include "propertyinfo.h" #include "simpleextractionresult.h" #include int main(int argc, char** argv) { QCoreApplication app(argc, argv); QCommandLineParser parser; parser.addPositionalArgument(QStringLiteral("filename"), QStringLiteral("File to process")); parser.process(app); if (parser.positionalArguments().size() != 1) { qDebug() << "Only one argument is accepted"; parser.showHelp(1); } QString url = QFileInfo(parser.positionalArguments().at(0)).absoluteFilePath(); QMimeDatabase mimeDb; QString mimetype = mimeDb.mimeTypeForFile(url).name(); KFileMetaData::ExtractorCollection extractors; QList exList = extractors.fetchExtractors(mimetype); QTextStream out(stdout); out << url << " " << mimetype << "\n\n"; - for (KFileMetaData::Extractor* ex : qAsConst(exList)) { + Q_FOREACH (KFileMetaData::Extractor* ex, exList) { KFileMetaData::SimpleExtractionResult result(url, mimetype, KFileMetaData::ExtractionResult::ExtractMetaData); ex->extract(&result); out << "Extractor For: " << ex->mimetypes().join(QLatin1Char(' ')) << "\n"; KFileMetaData::PropertyMap properties = result.properties(); KFileMetaData::PropertyMap::const_iterator it = properties.constBegin(); for (; it != properties.constEnd(); it++) { out << KFileMetaData::PropertyInfo(it.key()).displayName() << " --> " << it.value().toString() << " (" << it.value().typeName() << ")\n"; } out << "-------------\n"; } return 0; }