File indexing completed on 2024-04-28 04:21:08
0001 // SPDX-FileCopyrightText: 2020-2024 Johannes Zarl-Zierl <johannes@zarl-zierl.at> 0002 // SPDX-FileCopyrightText: 2022 Tobias Leupold <tl@stonemx.de> 0003 // 0004 // SPDX-License-Identifier: GPL-2.0-or-later 0005 0006 #include "Logging.h" 0007 #include "ThumbnailCacheConverter.h" 0008 0009 #include <kpabase/SettingsData.h> 0010 #include <kpabase/version.h> 0011 #include <kpathumbnails/ThumbnailCache.h> 0012 0013 #include <KAboutData> 0014 #include <KConfigGroup> 0015 #include <KLocalizedString> 0016 #include <KSharedConfig> 0017 #include <QCommandLineOption> 0018 #include <QCommandLineParser> 0019 #include <QCoreApplication> 0020 #include <QDebug> 0021 #include <QDir> 0022 #include <QLocale> 0023 #include <QLoggingCategory> 0024 #include <QTextStream> 0025 #include <QTimer> 0026 0027 using namespace KPAThumbnailTool; 0028 0029 void checkConflictingOptions(const QCommandLineParser &parser, const QCommandLineOption &opt1, const QCommandLineOption &opt2, QTextStream &err) 0030 { 0031 if (parser.isSet(opt1) && parser.isSet(opt2)) { 0032 err << i18nc("@info:shell", "Conflicting commandline options: %1 and %2!\n", opt1.names().constFirst(), opt2.names().constFirst()); 0033 exit(1); 0034 } 0035 } 0036 0037 int main(int argc, char **argv) 0038 { 0039 KLocalizedString::setApplicationDomain("kphotoalbum"); 0040 QCoreApplication app(argc, argv); 0041 0042 KAboutData aboutData( 0043 QStringLiteral("kpa-thumbnailtool"), // component name 0044 i18n("KPhotoAlbum Thumbnail Tool"), // display name 0045 QStringLiteral(KPA_VERSION), 0046 i18n("Tool for inspecting and editing the KPhotoAlbum thumbnail cache"), // short description 0047 KAboutLicense::GPL, 0048 i18n("Copyright (C) 2020-2024 The KPhotoAlbum Development Team"), // copyright statement 0049 QString(), // other text 0050 QStringLiteral("https://www.kphotoalbum.org") // homepage 0051 ); 0052 aboutData.setOrganizationDomain("kde.org"); 0053 // maintainer is expected to be the first entry 0054 // Note: I like to sort by name, grouped by active/inactive; 0055 // Jesper gets ranked with the active authors for obvious reasons 0056 aboutData.addAuthor(i18n("Johannes Zarl-Zierl"), i18n("Development, Maintainer"), QStringLiteral("johannes@zarl-zierl.at")); 0057 aboutData.addAuthor(i18n("Robert Krawitz"), i18n("Development, Optimization"), QStringLiteral("rlk@alum.mit.edu")); 0058 aboutData.addAuthor(i18n("Tobias Leupold"), i18n("Development, Releases, Website"), QStringLiteral("tl@stonemx.de")); 0059 aboutData.addAuthor(i18n("Jesper K. Pedersen"), i18n("Former Maintainer, Project Creator"), QStringLiteral("blackie@kde.org")); 0060 // not currently active: 0061 aboutData.addAuthor(i18n("Hassan Ibraheem"), QString(), QStringLiteral("hasan.ibraheem@gmail.com")); 0062 aboutData.addAuthor(i18n("Jan Kundrát"), QString(), QStringLiteral("jkt@gentoo.org")); 0063 aboutData.addAuthor(i18n("Andreas Neustifter"), QString(), QStringLiteral("andreas.neustifter@gmail.com")); 0064 aboutData.addAuthor(i18n("Tuomas Suutari"), QString(), QStringLiteral("thsuut@utu.fi")); 0065 aboutData.addAuthor(i18n("Miika Turkia"), QString(), QStringLiteral("miika.turkia@gmail.com")); 0066 aboutData.addAuthor(i18n("Henner Zeller"), QString(), QStringLiteral("h.zeller@acm.org")); 0067 0068 // initialize the commandline parser 0069 QCommandLineParser parser; 0070 parser.addPositionalArgument(QString::fromUtf8("imageDir"), i18nc("@info:shell", "The folder containing the .thumbnail folder.")); 0071 QCommandLineOption infoOption { QString::fromUtf8("info"), i18nc("@info:shell", "Print information about thumbnail cache.") }; 0072 parser.addOption(infoOption); 0073 QCommandLineOption convertV5ToV4Option { QString::fromUtf8("convertV5ToV4"), i18nc("@info:shell", "Convert thumbnailindex to format suitable for KPhotoAlbum >= 4.3.") }; 0074 parser.addOption(convertV5ToV4Option); 0075 QCommandLineOption verifyOption { QString::fromUtf8("check-thumbnail-dimensions"), i18nc("@info:shell", "Check thumbnail cache for consistency of thumbnail dimensions.") }; 0076 parser.addOption(verifyOption); 0077 QCommandLineOption fixOption { QString::fromUtf8("remove-broken"), i18nc("@info:shell", "Fix inconsistent thumbnails by removing them from the cache (requires --check-thumbnail-dimensions).") }; 0078 parser.addOption(fixOption); 0079 QCommandLineOption vacuumOption { QString::fromUtf8("vacuum"), i18nc("@info:shell", "Remove unreferenced thumbnails from the thumbnail data files.") }; 0080 parser.addOption(vacuumOption); 0081 QCommandLineOption quietOption { QString::fromUtf8("quiet"), i18nc("@info:shell", "Be less verbose.") }; 0082 parser.addOption(quietOption); 0083 0084 KAboutData::setApplicationData(aboutData); 0085 aboutData.setupCommandLine(&parser); 0086 0087 parser.process(app); 0088 aboutData.processCommandLine(&parser); 0089 QTextStream console { stdout }; 0090 QTextStream err { stderr }; 0091 0092 checkConflictingOptions(parser, convertV5ToV4Option, infoOption, err); 0093 checkConflictingOptions(parser, convertV5ToV4Option, verifyOption, err); 0094 0095 const auto args = parser.positionalArguments(); 0096 if (args.empty()) { 0097 err << i18nc("@info:shell", "Missing argument!\n"); 0098 return 1; 0099 } 0100 const auto imageDir = QDir { args.first() }; 0101 if (!imageDir.exists()) { 0102 err << i18nc("@info:shell", "%1 is not a folder!\n", args.first()); 0103 return 1; 0104 } 0105 if (parser.isSet(convertV5ToV4Option)) { 0106 const QString indexFile = imageDir.absoluteFilePath(QString::fromUtf8(".thumbnails/thumbnailindex")); 0107 return convertV5ToV4Cache(indexFile, err); 0108 } 0109 0110 int returnValue = 0; 0111 DB::DummyUIDelegate uiDelegate; 0112 Settings::SettingsData::setup(imageDir.path(), uiDelegate); 0113 const auto thumbnailDir = imageDir.absoluteFilePath(ImageManager::defaultThumbnailDirectory()); 0114 ImageManager::ThumbnailCache cache { thumbnailDir }; 0115 if (parser.isSet(infoOption)) { 0116 console << i18nc("@info:shell", "Thumbnail cache folder: %1\n", thumbnailDir); 0117 console << i18nc("@info:shell", "Thumbnail index file version: %1\n", cache.actualFileVersion()); 0118 console << i18nc("@info:shell", "Maximum supported thumbnailindex file version: %1\n", cache.preferredFileVersion()); 0119 console << i18nc("@info:shell", "Thumbnail storage dimensions: %1 pixels\n", cache.thumbnailSize()); 0120 if (cache.actualFileVersion() < 5 && !parser.isSet(quietOption)) { 0121 console << i18nc("@info:shell", "Note: Thumbnail storage dimensions are defined in the configuration file prior to v5.\n"); 0122 } 0123 console << i18nc("@info:shell", "Number of thumbnails: %1\n", cache.size()); 0124 console.flush(); 0125 } 0126 if (parser.isSet(verifyOption)) { 0127 const auto incorrectDimensions = cache.findIncorrectlySizedThumbnails(); 0128 if (incorrectDimensions.isEmpty()) { 0129 console << i18nc("@info:shell", "No inconsistencies found.\n"); 0130 } else { 0131 returnValue = 1; 0132 if (!parser.isSet(quietOption)) { 0133 console << i18nc("@info:shell This line is printed before a list of file names.", "The following thumbnails appear to have incorrect sizes:\n"); 0134 for (const auto &filename : incorrectDimensions) { 0135 console << filename.absolute() << "\n"; 0136 } 0137 } 0138 if (parser.isSet(fixOption)) { 0139 cache.removeThumbnails(incorrectDimensions); 0140 cache.save(); 0141 console << i18ncp("@info:shell", 0142 "Removed 1 inconsistent thumbnail from the database.\n", 0143 "Removed %1 inconsistent thumbnails from the database.\n", incorrectDimensions.size()); 0144 } 0145 } 0146 console.flush(); 0147 } 0148 if (parser.isSet(vacuumOption)) { 0149 cache.vacuum(); 0150 } 0151 0152 // immediately quit the event loop: 0153 QTimer::singleShot(0, &app, [&app, returnValue]() { app.exit(returnValue); }); 0154 return QCoreApplication::exec(); 0155 } 0156 // vi:expandtab:tabstop=4 shiftwidth=4: