File indexing completed on 2024-05-05 16:28:10
0001 // SPDX-FileCopyrightText: 2012-2020 The KPhotoAlbum Development Team 0002 // SPDX-FileCopyrightText: 2022 Johannes Zarl-Zierl <johannes@zarl-zierl.at> 0003 // 0004 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 0006 #include "ExtractOneVideoFrame.h" 0007 0008 #include <DB/CategoryCollection.h> 0009 #include <DB/ImageDB.h> 0010 #include <MainWindow/DirtyIndicator.h> 0011 #include <MainWindow/FeatureDialog.h> 0012 #include <MainWindow/TokenEditor.h> 0013 #include <MainWindow/Window.h> 0014 #include <Utilities/Process.h> 0015 #include <kpabase/Logging.h> 0016 #include <kpabase/StringSet.h> 0017 0018 #include <KLocalizedString> 0019 #include <KMessageBox> 0020 #include <QDir> 0021 #include <QTemporaryDir> 0022 0023 namespace ImageManager 0024 { 0025 QString ExtractOneVideoFrame::s_tokenForShortVideos; 0026 0027 #define STR(x) QString::fromUtf8(x) 0028 void ExtractOneVideoFrame::extract(const DB::FileName &fileName, double offset, QObject *receiver, const char *slot) 0029 { 0030 if (MainWindow::FeatureDialog::hasVideoThumbnailer()) 0031 new ExtractOneVideoFrame(fileName, offset, receiver, slot); 0032 } 0033 0034 void ExtractOneVideoFrame::processFinished(int exitCode, QProcess::ExitStatus status) 0035 { 0036 if (status == QProcess::ExitStatus::NormalExit && exitCode == 0) { 0037 frameFetched(); 0038 } else { 0039 handleError(m_process->error()); 0040 } 0041 } 0042 0043 ExtractOneVideoFrame::ExtractOneVideoFrame(const DB::FileName &fileName, double offset, QObject *receiver, const char *slot) 0044 { 0045 m_fileName = fileName; 0046 const QString tmpPath = STR("%1/KPA-XXXXXX").arg(QDir::tempPath()); 0047 m_workingDirectory = new QTemporaryDir(tmpPath); 0048 if (!m_workingDirectory->isValid()) 0049 qCWarning(ImageManagerLog) << "Failed to create temporary directory!"; 0050 m_process = new Utilities::Process(this); 0051 m_process->setWorkingDirectory(m_workingDirectory->path()); 0052 0053 connect(m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &ExtractOneVideoFrame::processFinished); 0054 connect(this, SIGNAL(result(QImage)), receiver, slot); 0055 0056 Q_ASSERT(MainWindow::FeatureDialog::hasVideoThumbnailer()); 0057 QStringList arguments; 0058 // analyzeduration is for videos where the videostream starts later than the sound 0059 arguments << STR("-ss") << QString::number(offset, 'f', 4) << STR("-analyzeduration") 0060 << STR("200M") << STR("-i") << fileName.absolute() << STR("-vf") << STR("thumbnail") 0061 << STR("-vframes") << STR("20") << m_workingDirectory->filePath(STR("000000%02d.png")); 0062 0063 qCDebug(ImageManagerLog, "%s %s", qPrintable(MainWindow::FeatureDialog::ffmpegBinary()), qPrintable(arguments.join(QString::fromLatin1(" ")))); 0064 0065 m_process->start(MainWindow::FeatureDialog::ffmpegBinary(), arguments); 0066 } 0067 0068 void ExtractOneVideoFrame::frameFetched() 0069 { 0070 if (!QFile::exists(m_workingDirectory->filePath(STR("00000020.png")))) 0071 markShortVideo(m_fileName); 0072 0073 QString name; 0074 for (int i = 20; i > 0; --i) { 0075 name = m_workingDirectory->filePath(STR("000000%1.png").arg(i, 2, 10, QChar::fromLatin1('0'))); 0076 if (QFile::exists(name)) { 0077 qCDebug(ImageManagerLog) << "Using video frame " << i; 0078 break; 0079 } 0080 } 0081 0082 QImage image(name); 0083 Q_EMIT result(image); 0084 delete m_workingDirectory; 0085 deleteLater(); 0086 } 0087 0088 void ExtractOneVideoFrame::handleError(QProcess::ProcessError error) 0089 { 0090 QString message; 0091 switch (error) { 0092 case QProcess::FailedToStart: 0093 message = i18n("Failed to start"); 0094 break; 0095 case QProcess::Crashed: 0096 message = i18n("Crashed"); 0097 break; 0098 case QProcess::Timedout: 0099 message = i18n("Timedout"); 0100 break; 0101 case QProcess::ReadError: 0102 message = i18n("Read error"); 0103 break; 0104 case QProcess::WriteError: 0105 message = i18n("Write error"); 0106 break; 0107 case QProcess::UnknownError: 0108 message = i18n("Unknown error"); 0109 break; 0110 } 0111 0112 KMessageBox::information(MainWindow::Window::theMainWindow(), 0113 i18n("<p>Error when extracting video thumbnails.<br/>Error was: %1</p>", message), 0114 QString(), QLatin1String("errorWhenRunningQProcessFromExtractOneVideoFrame")); 0115 Q_EMIT result(QImage()); 0116 deleteLater(); 0117 } 0118 0119 void ExtractOneVideoFrame::markShortVideo(const DB::FileName &fileName) 0120 { 0121 if (s_tokenForShortVideos.isNull()) { 0122 const auto tokensInUse = MainWindow::TokenEditor::tokensInUse(); 0123 Utilities::StringSet usedTokens(tokensInUse.begin(), tokensInUse.end()); 0124 for (int ch = 'A'; ch <= 'Z'; ++ch) { 0125 QString token = QChar::fromLatin1((char)ch); 0126 if (!usedTokens.contains(token)) { 0127 s_tokenForShortVideos = token; 0128 break; 0129 } 0130 } 0131 0132 if (s_tokenForShortVideos.isNull()) { 0133 // Hmmm, no free token. OK lets just skip setting tokens. 0134 return; 0135 } 0136 KMessageBox::information(MainWindow::Window::theMainWindow(), 0137 i18n("Unable to extract video thumbnails from some files. " 0138 "Either the file is damaged in some way, or the video is ultra short. " 0139 "For your convenience, the token '%1' " 0140 "has been set on those videos.\n\n" 0141 "(You might need to wait till the video extraction led in your status bar has stopped blinking, " 0142 "to see all affected videos.)", 0143 s_tokenForShortVideos)); 0144 } 0145 0146 DB::ImageInfoPtr info = DB::ImageDB::instance()->info(fileName); 0147 DB::CategoryPtr tokensCategory = DB::ImageDB::instance()->categoryCollection()->categoryForSpecial(DB::Category::TokensCategory); 0148 info->addCategoryInfo(tokensCategory->name(), s_tokenForShortVideos); 0149 MainWindow::DirtyIndicator::markDirty(); 0150 } 0151 0152 } // namespace ImageManager 0153 // vi:expandtab:tabstop=4 shiftwidth=4: 0154 0155 #include "moc_ExtractOneVideoFrame.cpp"