diff --git a/ImageManager/ExtractOneVideoFrame.cpp b/ImageManager/ExtractOneVideoFrame.cpp index dd4601a8..87d77d4d 100644 --- a/ImageManager/ExtractOneVideoFrame.cpp +++ b/ImageManager/ExtractOneVideoFrame.cpp @@ -1,171 +1,164 @@ /* Copyright 2012 Jesper K. Pedersen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 14 of version 3 of the license. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "ExtractOneVideoFrame.h" #include "Logging.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace ImageManager { QString ExtractOneVideoFrame::s_tokenForShortVideos; #define STR(x) QString::fromUtf8(x) void ExtractOneVideoFrame::extract(const DB::FileName &fileName, double offset, QObject* receiver, const char* slot) { if ( MainWindow::FeatureDialog::hasVideoThumbnailer()) new ExtractOneVideoFrame(fileName, offset, receiver, slot); } ExtractOneVideoFrame::ExtractOneVideoFrame(const DB::FileName &fileName, double offset, QObject *receiver, const char *slot) { m_fileName = fileName; m_process = new Utilities::Process(this); setupWorkingDirectory(); m_process->setWorkingDirectory(m_workingDirectory); connect( m_process, SIGNAL(finished(int)), this, SLOT(frameFetched())); connect( m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(handleError(QProcess::ProcessError))); connect( this, SIGNAL(result(QImage)), receiver, slot); if (MainWindow::FeatureDialog::ffmpegBinary().isEmpty()) { QStringList arguments; arguments << STR("-nosound") << STR("-ss") << QString::number(offset,'f',4) << STR("-vf") << STR("screenshot") << STR("-frames") << STR("20") << STR("-vo") << STR("png:z=9") << fileName.absolute(); qCDebug(ImageManagerLog, "%s %s", qPrintable(MainWindow::FeatureDialog::mplayerBinary()), qPrintable(arguments.join(QString::fromLatin1(" ")))); m_process->start(MainWindow::FeatureDialog::mplayerBinary(), arguments); } else { QStringList arguments; // analyzeduration is for videos where the videostream starts later than the sound arguments << STR("-ss") << QString::number(offset, 'f', 4) << STR("-analyzeduration") << STR("200M") << STR("-i") << fileName.absolute() << STR("-vf") << STR("thumbnail") << STR("-vframes") << STR("20") << m_workingDirectory + STR("/000000%02d.png"); qCDebug(ImageManagerLog, "%s %s", qPrintable(MainWindow::FeatureDialog::ffmpegBinary()), qPrintable(arguments.join(QString::fromLatin1(" ")))); m_process->start(MainWindow::FeatureDialog::ffmpegBinary(), arguments); } } void ExtractOneVideoFrame::frameFetched() { if (!QFile::exists(m_workingDirectory + STR("/00000020.png"))) markShortVideo(m_fileName); QString name; for (int i = 20; i>0; --i) { name = m_workingDirectory +STR("/000000%1.png").arg(i,2, 10, QChar::fromLatin1('0')); if (QFile::exists(name)) { qCDebug(ImageManagerLog) << "Using video frame " << i; break; } } QImage image(name); emit result(image); deleteWorkingDirectory(); deleteLater(); } void ExtractOneVideoFrame::handleError(QProcess::ProcessError error) { QString message; switch (error) { case QProcess::FailedToStart: message = i18n("Failed to start"); break; case QProcess::Crashed: message = i18n("Crashed"); break; case QProcess::Timedout: message = i18n("Timedout"); break; case QProcess::ReadError: message = i18n("Read error"); break; case QProcess::WriteError: message = i18n("Write error"); break; case QProcess::UnknownError: message = i18n("Unknown error"); break; } KMessageBox::information( MainWindow::Window::theMainWindow(), i18n("

Error when extracting video thumbnails.
Error was: %1

" , message ), QString(), QLatin1String("errorWhenRunningQProcessFromExtractOneVideoFrame")); emit result(QImage()); deleteLater(); } void ExtractOneVideoFrame::setupWorkingDirectory() { const QString tmpPath = STR("%1/KPA-XXXXXX").arg(QDir::tempPath()); m_workingDirectory = QString::fromUtf8(mkdtemp(tmpPath.toUtf8().data())); } void ExtractOneVideoFrame::deleteWorkingDirectory() { QDir dir(m_workingDirectory); QStringList files = dir.entryList(QDir::Files); for ( const QString& file : files ) dir.remove(file); dir.rmdir(m_workingDirectory); } void ExtractOneVideoFrame::markShortVideo(const DB::FileName &fileName) { - if (s_tokenForShortVideos.isNull()) { - Utilities::StringSet usedTokens = MainWindow::TokenEditor::tokensInUse().toSet(); - for ( int ch = 'A'; ch <= 'Z'; ++ch ) { - QString token = QChar::fromLatin1( (char) ch ); - if (!usedTokens.contains(token)) { - s_tokenForShortVideos = token; - break; - } - } + if (s_tokenForShortVideos.isEmpty()) { + s_tokenForShortVideos = MainWindow::TokenEditor::unusedToken(); - if (s_tokenForShortVideos.isNull()) { + if (s_tokenForShortVideos.isEmpty()) { // Hmmm, no free token. OK lets just skip setting tokens. return; } KMessageBox::information(MainWindow::Window::theMainWindow(), i18n("Unable to extract video thumbnails from some files. " "Either the file is damaged in some way, or the video is ultra short. " "For your convenience, the token '%1' " "has been set on those videos.\n\n" "(You might need to wait till the video extraction led in your status bar has stopped blinking, " "to see all affected videos.)", s_tokenForShortVideos)); } DB::ImageInfoPtr info = DB::ImageDB::instance()->info(fileName); DB::CategoryPtr tokensCategory = DB::ImageDB::instance()->categoryCollection()->categoryForSpecial(DB::Category::TokensCategory); info->addCategoryInfo(tokensCategory->name(), s_tokenForShortVideos); MainWindow::DirtyIndicator::markDirty(); } } // namespace ImageManager // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/MainWindow/TokenEditor.cpp b/MainWindow/TokenEditor.cpp index 0ceecd18..d7113882 100644 --- a/MainWindow/TokenEditor.cpp +++ b/MainWindow/TokenEditor.cpp @@ -1,138 +1,150 @@ /* Copyright (C) 2003-2018 Jesper K. Pedersen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "TokenEditor.h" #include #include #include #include #include #include #include #include #include #include "DB/ImageDB.h" #include "DB/CategoryCollection.h" #include "DB/Category.h" #include "DB/ImageSearchInfo.h" #include "Settings/SettingsData.h" using namespace MainWindow; TokenEditor::TokenEditor( QWidget* parent ) :QDialog( parent ) { setWindowTitle( i18nc("@title:window", "Remove Tokens" ) ); QVBoxLayout *dialogLayout = new QVBoxLayout(this); QWidget* mainContents = new QWidget; QVBoxLayout* vlay = new QVBoxLayout( mainContents ); QLabel* label = new QLabel( i18n("Select tokens to remove from all images and videos:") ); vlay->addWidget( label ); QGridLayout* grid = new QGridLayout; vlay->addLayout( grid ); int index = 0; for ( int ch = 'A'; ch <= 'Z'; ch++, index++ ) { QChar token = QChar::fromLatin1( (char) ch ); QCheckBox* box = new QCheckBox( token ); grid->addWidget( box, index/5, index % 5 ); m_checkBoxes.append( box ); } QHBoxLayout* hlay = new QHBoxLayout; vlay->addLayout( hlay ); hlay->addStretch( 1 ); QPushButton* selectAll = new QPushButton( i18n("Select All") ); QPushButton* selectNone = new QPushButton( i18n("Select None") ); hlay->addWidget( selectAll ); hlay->addWidget( selectNone ); connect(selectAll, &QPushButton::clicked, this, &TokenEditor::selectAll); connect(selectNone, &QPushButton::clicked, this, &TokenEditor::selectNone); dialogLayout->addWidget(mainContents); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); buttonBox->button(QDialogButtonBox::Ok)->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &TokenEditor::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &TokenEditor::reject); dialogLayout->addWidget(buttonBox); } void TokenEditor::show() { QStringList tokens = tokensInUse(); Q_FOREACH( QCheckBox *box, m_checkBoxes ) { box->setChecked( false ); QString txt = box->text().remove( QString::fromLatin1("&") ); box->setEnabled( tokens.contains( txt ) ); } QDialog::show(); } void TokenEditor::selectAll() { Q_FOREACH( QCheckBox *box, m_checkBoxes ) { box->setChecked( true ); } } void TokenEditor::selectNone() { Q_FOREACH( QCheckBox *box, m_checkBoxes ) { box->setChecked( false ); } } /** I would love to use Settings::optionValue, but that method does not forget about an item once it has seen it, which is really what it should do anyway, otherwise it would be way to expensive in use. */ QStringList TokenEditor::tokensInUse() { QStringList res; DB::CategoryPtr tokensCategory = DB::ImageDB::instance()->categoryCollection()->categoryForSpecial(DB::Category::TokensCategory); QMap map = DB::ImageDB::instance()->classify( DB::ImageSearchInfo(), tokensCategory->name(), DB::anyMediaType ); for( QMap::Iterator it = map.begin(); it != map.end(); ++it ) { if ( it.value() > 0 ) res.append( it.key() ); } return res; } +QString TokenEditor::unusedToken() +{ + QSet usedTokens = tokensInUse().toSet(); + for ( int ch = 'A'; ch <= 'Z'; ++ch ) { + const QString token = QChar::fromLatin1( (char) ch ); + if (!usedTokens.contains(token)) { + return token; + } + } + return {}; +} + void TokenEditor::accept() { DB::CategoryPtr tokensCategory = DB::ImageDB::instance()->categoryCollection()->categoryForSpecial(DB::Category::TokensCategory); Q_FOREACH( const QCheckBox *box, m_checkBoxes ) { if ( box->isChecked() && box->isEnabled() ) { QString txt = box->text().remove( QString::fromLatin1("&") ); tokensCategory->removeItem( txt ); } } QDialog::accept(); } // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/MainWindow/TokenEditor.h b/MainWindow/TokenEditor.h index 299b3a8a..9b3c41c3 100644 --- a/MainWindow/TokenEditor.h +++ b/MainWindow/TokenEditor.h @@ -1,50 +1,55 @@ /* Copyright (C) 2003-2010 Jesper K. Pedersen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TOKENEDITOR_H #define TOKENEDITOR_H #include #include class QCheckBox; namespace MainWindow { class TokenEditor :public QDialog { Q_OBJECT public: explicit TokenEditor( QWidget* parent ); virtual void show(); static QStringList tokensInUse(); + /** + * @brief unusedToken + * @return a token that is not in use, or an empty QString if all tokens are used. + */ + static QString unusedToken(); protected slots: void selectAll(); void selectNone(); virtual void accept(); private: QList m_checkBoxes; }; } #endif /* TOKENEDITOR_H */ // vi:expandtab:tabstop=4 shiftwidth=4: