Cleanup & rewrite file watcher,

update title clips whenever template changes
CCBUG: 384340
parent 5138310f
......@@ -5,6 +5,7 @@ set(kdenlive_SRCS
bin/bincommands.cpp
bin/binplaylist.cpp
bin/clipcreator.cpp
bin/filewatcher.cpp
bin/generators/generators.cpp
bin/model/markerlistmodel.cpp
bin/projectclip.cpp
......
......@@ -994,6 +994,7 @@ void Bin::deleteClip(const QString &id)
m_jobManager->discardJobs(id);
ClipType type = clip->clipType();
QString url = clip->url();
m_fileWatcher.removeFile(id, url);
clip->setClipStatus(AbstractProjectItem::StatusDeleting);
if (!m_processingAudioThumb.isEmpty()) {
clip->abortAudioThumbs();
......@@ -1201,6 +1202,7 @@ void Bin::setDocument(KdenliveDoc *project)
// Cleanup previous project
m_itemModel->clean();
m_fileWatcher.clear();
delete m_itemView;
m_itemView = nullptr;
delete m_jobManager;
......@@ -1971,8 +1973,8 @@ void Bin::slotProducerReady(const requestClipInfo &info, std::shared_ptr<Mlt::Pr
emit producerReady(info.clipId);
// Check for file modifications
ClipType t = clip->clipType();
if (t == AV || t == Audio || t == Image || t == Video || t == Playlist) {
m_doc->watchFile(clip->url());
if (t == AV || t == Audio || t == Image || t == Video || t == Playlist || t == TextTemplate) {
m_fileWatcher.addFile(info.clipId, clip->url());
}
if (m_doc->useProxy()) {
if (t == AV || t == Video) {
......@@ -2037,8 +2039,8 @@ void Bin::slotProducerReady(const requestClipInfo &info, std::shared_ptr<Mlt::Pr
newClip->createAudioThumbs();
ClipType t = newClip->clipType();
if (t == AV || t == Audio || t == Image || t == Video || t == Playlist) {
m_doc->watchFile(newClip->url());
if (t == AV || t == Audio || t == Image || t == Video || t == Playlist || t == TextTemplate) {
m_fileWatcher.addFile(info.clipId, newClip->url());
}
if (info.clipId.toInt() >= m_clipCounter) {
m_clipCounter = info.clipId.toInt() + 1;
......
......@@ -25,7 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "abstractprojectitem.h"
#include "timecode.h"
#include "filewatcher.hpp"
#include "effects/effectstack/model/effectstackmodel.hpp"
#include <KMessageWidget>
......@@ -450,6 +450,7 @@ private:
JobManager *m_jobManager;
QToolBar *m_toolbar;
KdenliveDoc *m_doc;
FileWatcher m_fileWatcher;
QLineEdit *m_searchLine;
QToolButton *m_addButton;
QMenu *m_extractAudioAction;
......
/***************************************************************************
* Copyright (C) 2017 by Jean-Baptiste Mardelle *
* This file is part of Kdenlive. See www.kdenlive.org. *
* *
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include "filewatcher.hpp"
#include "core.h"
#include "bin/bin.h"
FileWatcher::FileWatcher(QObject *parent) : QObject(parent)
{
m_fileWatcher = KDirWatch::self();
// Init clip modification tracker
m_modifiedTimer.setInterval(1500);
connect(m_fileWatcher, &KDirWatch::dirty, this, &FileWatcher::slotUrlModified);
connect(m_fileWatcher, &KDirWatch::deleted, this, &FileWatcher::slotUrlMissing);
connect(&m_modifiedTimer, &QTimer::timeout, this, &FileWatcher::slotProcessModifiedUrls);
}
void FileWatcher::addFile(const QString &binId, const QString &url)
{
if (m_occurences.contains(url)) {
QStringList currentIds = m_occurences.value(url);
if (!currentIds.contains(binId)) {
currentIds << binId;
m_occurences[url] = currentIds;
}
} else {
// Unknown file, add to list
m_occurences.insert(url, QStringList() << binId);
m_fileWatcher->addFile(url);
}
}
void FileWatcher::removeFile(const QString &binId, const QString &url)
{
if (!m_occurences.contains(url)) {
return;
}
QStringList currentIds = m_occurences.value(url);
currentIds.removeAll(binId);
if (currentIds.isEmpty()) {
m_fileWatcher->removeFile(url);
m_occurences.remove(url);
} else {
m_occurences[url]=currentIds;
}
}
void FileWatcher::slotUrlModified(const QString &path)
{
if (!m_modifiedUrls.contains(path)) {
m_modifiedUrls << path;
const QStringList ids = m_occurences.value(path);
for (const QString &id : ids) {
pCore->bin()->setWaitingStatus(id);
}
}
if (!m_modifiedTimer.isActive()) {
m_modifiedTimer.start();
}
}
void FileWatcher::slotUrlMissing(const QString &path)
{
// TODO handle missing clips by replacing producer with an invalid producer
const QStringList ids = m_occurences.value(path);
/*for (const QString &id : ids) {
emit missingClip(id);
}*/
}
void FileWatcher::slotProcessModifiedUrls()
{
QStringList checkList = m_modifiedUrls;
for (const QString &path : checkList) {
if (m_fileWatcher->ctime(path).msecsTo(QDateTime::currentDateTime()) > 1000) {
const QStringList ids = m_occurences.value(path);
for (const QString &id : ids) {
pCore->bin()->reloadClip(id);
}
m_modifiedUrls.removeAll(path);
}
}
if (m_modifiedUrls.isEmpty()) {
m_modifiedTimer.stop();
}
}
void FileWatcher::clear()
{
m_fileWatcher->stopScan();
const QList<QString> files = m_occurences.keys();
for (const QString &url : files) {
m_fileWatcher->removeFile(url);
}
m_occurences.clear();
m_fileWatcher->startScan();
}
/***************************************************************************
* Copyright (C) 2017 by Jean-Baptiste Mardelle *
* This file is part of Kdenlive. See www.kdenlive.org. *
* *
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
#ifndef FILEWATCHER_H
#define FILEWATCHER_H
#include <KDirWatch>
#include <QMap>
#include <QTimer>
/** @brief This class is responsible for watching all files used in the project
and triggers a reload notification when a file changes.
*/
class FileWatcher : public QObject
{
public:
// Constructor
explicit FileWatcher(QObject *parent = nullptr);
// Add a file to the list of watched items
void addFile(const QString &binId, const QString &url);
// Remove a file from the list of watched items
void removeFile(const QString &binId, const QString &url);
// Reset all watched files
void clear();
private slots:
void slotUrlModified(const QString &path);
void slotUrlMissing(const QString &path);
void slotProcessModifiedUrls();
private:
KDirWatch *m_fileWatcher;
// A list with urls as keys, and the corresponding clip ids as value
QMap <QString, QStringList> m_occurences;
QStringList m_modifiedUrls;
QTimer m_modifiedTimer;
};
#endif
......@@ -324,7 +324,6 @@ QPixmap ProjectClip::thumbnail(int width, int height)
bool ProjectClip::setProducer(std::shared_ptr<Mlt::Producer> producer, bool replaceProducer)
{
Q_UNUSED(replaceProducer);
updateProducer(std::move(producer));
// Update info
......@@ -348,6 +347,10 @@ bool ProjectClip::setProducer(std::shared_ptr<Mlt::Producer> producer, bool repl
// Make sure we have a hash for this clip
getFileHash();
createAudioThumbs();
if (replaceProducer) {
// Recreate thumbnail
}
return true;
}
......
......@@ -95,12 +95,6 @@ KdenliveDoc::KdenliveDoc(const QUrl &url, const QString &projectFolder, QUndoGro
connect(pCore->producerQueue(), &ProducerQueue::switchProfile, this, &KdenliveDoc::switchProfile);
// connect(m_commandStack, SIGNAL(cleanChanged(bool)), this, SLOT(setModified(bool)));
// Init clip modification tracker
m_modifiedTimer.setInterval(1500);
connect(&m_fileWatcher, &KDirWatch::dirty, this, &KdenliveDoc::slotClipModified);
connect(&m_fileWatcher, &KDirWatch::deleted, this, &KdenliveDoc::slotClipMissing);
connect(&m_modifiedTimer, &QTimer::timeout, this, &KdenliveDoc::slotProcessModifiedClips);
// init default document properties
m_documentProperties[QStringLiteral("zoom")] = QLatin1Char('7');
m_documentProperties[QStringLiteral("verticalzoom")] = QLatin1Char('1');
......@@ -824,10 +818,6 @@ QString KdenliveDoc::searchFileRecursively(const QDir &dir, const QString &match
void KdenliveDoc::deleteClip(const QString &clipId, ClipType type, const QString &url)
{
pCore->binController()->removeBinClip(clipId);
// Remove from file watch
if (type != Color && type != SlideShow && type != QText && !url.isEmpty()) {
m_fileWatcher.removeFile(url);
}
}
std::shared_ptr<ProjectClip> KdenliveDoc::getBinClip(const QString &clipId)
......@@ -1233,55 +1223,6 @@ void KdenliveDoc::slotProxyCurrentItem(bool doProxy, QList<std::shared_ptr<Proje
}
}
// TODO put all file watching stuff in own class
void KdenliveDoc::watchFile(const QString &url)
{
m_fileWatcher.addFile(url);
}
void KdenliveDoc::slotClipModified(const QString &path)
{
const QStringList ids = pCore->binController()->getBinIdsByResource(QFileInfo(path));
for (const QString &id : ids) {
if (!m_modifiedClips.contains(id)) {
pCore->bin()->setWaitingStatus(id);
}
m_modifiedClips[id] = QTime::currentTime();
}
if (!m_modifiedTimer.isActive()) {
m_modifiedTimer.start();
}
}
void KdenliveDoc::slotClipMissing(const QString &path)
{
qCDebug(KDENLIVE_LOG) << "// CLIP: " << path << " WAS MISSING";
QStringList ids = pCore->binController()->getBinIdsByResource(QFileInfo(path));
// TODO handle missing clips by replacing producer with an invalid producer
/*for (const QString &id : ids) {
emit missingClip(id);
}*/
}
void KdenliveDoc::slotProcessModifiedClips()
{
if (!m_modifiedClips.isEmpty()) {
QMapIterator<QString, QTime> i(m_modifiedClips);
while (i.hasNext()) {
i.next();
if (QTime::currentTime().msecsTo(i.value()) <= -1500) {
pCore->bin()->reloadClip(i.key());
m_modifiedClips.remove(i.key());
break;
}
}
setModified(true);
}
if (m_modifiedClips.isEmpty()) {
m_modifiedTimer.stop();
}
}
QMap<QString, QString> KdenliveDoc::documentProperties()
{
m_documentProperties.insert(QStringLiteral("version"), QString::number(DOCUMENTVERSION));
......
......@@ -32,7 +32,6 @@
#include <QTimer>
#include <memory>
#include <KDirWatch>
#include <kautosavefile.h>
#include "definitions.h"
......@@ -122,8 +121,6 @@ public:
const QMap<QString, QString> metadata() const;
/** @brief Set the document metadata (author, copyright, ...) */
void setMetadata(const QMap<QString, QString> &meta);
/** @brief Add url to the file watcher so that we monitor changes */
void watchFile(const QString &url);
/** @brief Get all document properties that need to be saved */
QMap<QString, QString> documentProperties();
bool useProxy() const;
......@@ -171,11 +168,6 @@ private:
QDomDocument m_document;
/** @brief MLT's root (base path) that is stripped from urls in saved xml */
QString m_documentRoot;
KDirWatch m_fileWatcher;
/** Timer used to reload clips when they have been externally modified */
QTimer m_modifiedTimer;
/** List of the clip IDs that need to be reloaded after being externally modified */
QMap<QString, QTime> m_modifiedClips;
Timecode m_timecode;
std::shared_ptr<DocUndoStack> m_commandStack;
ClipManager *m_clipManager;
......@@ -227,9 +219,6 @@ public slots:
void groupsChanged(const QString &groups);
private slots:
void slotClipModified(const QString &path);
void slotClipMissing(const QString &path);
void slotProcessModifiedClips();
void slotModified();
void switchProfile(std::unique_ptr<ProfileParam> &profile, const QString &id, const QDomElement &xml);
void slotSwitchProfile(const QString &profile_path);
......
......@@ -458,42 +458,41 @@ void ProducerQueue::processFileProperties()
int frameNumber = ProjectClip::getXmlProperty(info.xml, QStringLiteral("kdenlive:thumbnailFrame"), QStringLiteral("-1")).toInt();
producer->set("kdenlive:id", info.clipId.toUtf8().constData());
qDebug()<<" * * * ** * * *REQUEST CLIP RELOAD: "<<info.clipId<<" = "<<info.replaceProducer;
if ((info.replaceProducer && !EffectsList::property(info.xml, QStringLiteral("kdenlive:file_hash")).isEmpty()) || proxyProducer) {
qDebug()<<" * * * ** * * *REQUEST CLIP RELOAD NAD REPLACVE!!: "<<info.clipId;
// Clip already has all properties
// We want to replace an existing producer.
if (proxyProducer) {
// Recreate clip thumb
Mlt::Frame *frame = nullptr;
QImage img;
if (KdenliveSettings::gpu_accel()) {
Clip clp(*producer);
Mlt::Producer *glProd = clp.softClone(ClipController::getPassPropertiesList());
if (frameNumber > 0) {
glProd->seek(frameNumber);
}
Mlt::Filter scaler(pCore->getCurrentProfile()->profile(), "swscale");
Mlt::Filter converter(pCore->getCurrentProfile()->profile(), "avcolor_space");
glProd->attach(scaler);
glProd->attach(converter);
frame = glProd->get_frame();
if ((frame != nullptr) && frame->is_valid()) {
img = KThumb::getFrame(frame, fullWidth, info.imageHeight);
emit replyGetImage(info.clipId, img);
}
delete glProd;
} else {
if (frameNumber > 0) {
producer->seek(frameNumber);
}
frame = producer->get_frame();
if ((frame != nullptr) && frame->is_valid()) {
img = KThumb::getFrame(frame, fullWidth, info.imageHeight, forceThumbScale);
emit replyGetImage(info.clipId, img);
}
// Recreate clip thumb
Mlt::Frame *frame = nullptr;
QImage img;
if (KdenliveSettings::gpu_accel()) {
Clip clp(*producer);
QScopedPointer<Mlt::Producer> glProd( clp.softClone(ClipController::getPassPropertiesList()));
if (frameNumber > 0) {
glProd->seek(frameNumber);
}
if (frame) {
delete frame;
Mlt::Filter scaler(pCore->getCurrentProfile()->profile(), "swscale");
Mlt::Filter converter(pCore->getCurrentProfile()->profile(), "avcolor_space");
glProd->attach(scaler);
glProd->attach(converter);
frame = glProd->get_frame();
if ((frame != nullptr) && frame->is_valid()) {
img = KThumb::getFrame(frame, fullWidth, info.imageHeight);
emit replyGetImage(info.clipId, img);
}
} else {
if (frameNumber > 0) {
producer->seek(frameNumber);
}
frame = producer->get_frame();
if ((frame != nullptr) && frame->is_valid()) {
img = KThumb::getFrame(frame, fullWidth, info.imageHeight, forceThumbScale);
emit replyGetImage(info.clipId, img);
}
}
if (frame) {
delete frame;
}
// replace clip
m_processingClipId.removeAll(info.clipId);
......
......@@ -704,10 +704,8 @@ QString ProjectManager::projectSceneList(const QString &outputFolder)
{
// TODO: re-implement overlay and all
// TODO refac: repair this
return QString();
/*
return pCore->monitorManager()->projectMonitor()->sceneList(outputFolder);
bool multitrackEnabled = m_trackView->multitrackView;
/*bool multitrackEnabled = m_trackView->multitrackView;
if (multitrackEnabled) {
// Multitrack view was enabled, disable for auto save
m_trackView->slotMultitrackView(false);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment