]> git.sesse.net Git - kdenlive/commitdiff
Add recursive search when opening a document with missing clip, heavily based on...
authorJean-Baptiste Mardelle <jb@kdenlive.org>
Sun, 23 Nov 2008 22:35:59 +0000 (22:35 +0000)
committerJean-Baptiste Mardelle <jb@kdenlive.org>
Sun, 23 Nov 2008 22:35:59 +0000 (22:35 +0000)
svn path=/branches/KDE4/; revision=2723

src/customtrackview.cpp
src/docclipbase.cpp
src/docclipbase.h
src/kdenlivedoc.cpp
src/kdenlivedoc.h
src/projectlist.cpp
src/projectlist.h
src/renderer.cpp
src/renderer.h

index 1cfc05fd4cb456c05b31939a3b478fff017d6e3a..97f32b704727549b385c7af35c8e28da508c9271 100644 (file)
@@ -1090,20 +1090,20 @@ void CustomTrackView::slotAddTransitionToSelectedClips(QDomElement transition) {
             const int transitiontrack = getPreviousVideoTrack(info.track);
             GenTime pos = GenTime((int)(mapToScene(m_menuPosition).x()), m_document->fps());
             if (pos < item->startPos() + item->duration() / 2) {
-               // add transition to clip start
+                // add transition to clip start
                 info.startPos = item->startPos();
                 if (transitiontrack != 0) transitionClip = getClipItemAt((int) info.startPos.frames(m_document->fps()), m_scene->m_tracksList.count() - transitiontrack);
                 if (transitionClip && transitionClip->endPos() < item->endPos()) {
                     info.endPos = transitionClip->endPos();
                 } else info.endPos = info.startPos + GenTime(65, m_document->fps());
             } else {
-               // add transition to clip  end
+                // add transition to clip  end
                 info.endPos = item->endPos();
                 if (transitiontrack != 0) transitionClip = getClipItemAt((int) info.endPos.frames(m_document->fps()), m_scene->m_tracksList.count() - transitiontrack);
                 if (transitionClip && transitionClip->startPos() > item->startPos()) {
                     info.startPos = transitionClip->startPos();
                 } else info.startPos = info.endPos - GenTime(65, m_document->fps());
-               if (transition.attribute("tag") == "luma") EffectsList::setParameter(transition, "reverse", "1");
+                if (transition.attribute("tag") == "luma") EffectsList::setParameter(transition, "reverse", "1");
             }
             slotAddTransition(item, info, transitiontrack, transition);
         }
index bf26e183182901bf4cf9c6fd22b0092d980036d7..c2eae7c753b092fc58bae59588177ebf19bb0b01 100644 (file)
@@ -15,6 +15,8 @@
  *                                                                         *
  ***************************************************************************/
 
+#include <QCryptographicHash>
+
 #include <KDebug>
 
 #include "kdenlivesettings.h"
@@ -34,6 +36,7 @@ DocClipBase::DocClipBase(ClipManager *clipManager, QDomElement xml, const QStrin
     }
 
     KUrl url = KUrl(xml.attribute("resource"));
+    if (!m_properties.contains("file_hash") && !url.isEmpty()) getFileHash(url.path());
     int out = xml.attribute("out").toInt();
     if (out != 0) {
         setDuration(GenTime(out, KdenliveSettings::project_fps()));
@@ -517,10 +520,36 @@ void DocClipBase::clearProperty(const QString &key) {
     m_properties.remove(key);
 }
 
+void DocClipBase::getFileHash(const QString &url) {
+    QFile file(url);
+    if (file.open(QIODevice::ReadOnly)) { // write size and hash only if resource points to a file
+        QByteArray fileData;
+        QByteArray fileHash;
+        //kDebug() << "SETTING HASH of" << value;
+        m_properties.insert("file_size", QString::number(file.size()));
+        /*
+               * 1 MB = 1 second per 450 files (or faster)
+               * 10 MB = 9 seconds per 450 files (or faster)
+               */
+        if (file.size() > 1000000*2) {
+            fileData = file.read(1000000);
+            if (file.seek(file.size() - 1000000))
+                fileData.append(file.readAll());
+        } else
+            fileData = file.readAll();
+        file.close();
+        fileHash = QCryptographicHash::hash(fileData, QCryptographicHash::Md5);
+        m_properties.insert("file_hash", QString(fileHash.toHex()));
+        //kDebug() << file.fileName() << file.size() << fileHash.toHex();
+    }
+}
+
 void DocClipBase::setProperty(const QString &key, const QString &value) {
     m_properties.insert(key, value);
-    if (key == "resource") m_thumbProd->updateClipUrl(KUrl(value));
-    else if (key == "out") setDuration(GenTime(value.toInt(), KdenliveSettings::project_fps()));
+    if (key == "resource") {
+        m_thumbProd->updateClipUrl(KUrl(value));
+        getFileHash(value);
+    } else if (key == "out") setDuration(GenTime(value.toInt(), KdenliveSettings::project_fps()));
     //else if (key == "transparency") m_clipProducer->set("transparency", value.toInt());
     else if (key == "colour") {
         char *tmp = (char *) qstrdup(value.toUtf8().data());
index cc427b0d96076ae230b92454fce6d55568516b5d..bc850231e23cf2ccb81f41ac633996c2bf147cd9 100644 (file)
@@ -221,6 +221,7 @@ private:   // Private attributes
     void slotRefreshProducer();
     void setProducerProperty(const char *name, const char *data);
     void setProducerProperty(const char *name, int data);
+    void getFileHash(const QString &url);
 
 public slots:
     void updateAudioThumbnail(QMap<int, QMap<int, QByteArray> > data);
index 5d7c6077d172ad583a3a26c0c5aeaa48f0cce0b0..b07a4b9094942d99dee277c0cece8225fda391e2 100644 (file)
@@ -1003,12 +1003,55 @@ void KdenliveDoc::addClip(QDomElement elem, QString clipId, bool createClipItem)
     DocClipBase *clip = m_clipManager->getClipById(producerId);
     if (clip == NULL) {
         elem.setAttribute("id", producerId);
+        QString path = elem.attribute("resource");
+        if (!path.isEmpty() && !QFile::exists(path)) {
+            const QString size = elem.attribute("file_size");
+            const QString hash = elem.attribute("file_hash");
+            QString newpath;
+            KMessageBox::ButtonCode action = KMessageBox::No;
+            if (!size.isEmpty() && !hash.isEmpty()) {
+                if (!m_searchFolder.isEmpty()) newpath = Render::searchFileRecursively(m_searchFolder, size, hash);
+                else action = (KMessageBox::ButtonCode)KMessageBox::messageBox(kapp->activeWindow(), KMessageBox::WarningYesNoCancel, i18n("<qt>Clip <b>%1</b><br>is invalid, what do you want to do?", path), i18n("File not found"), KGuiItem(i18n("Search automatically")), KGuiItem(i18n("Remove from project")), KGuiItem(i18n("Keep as placeholder")));
+            } else {
+                newpath = KFileDialog::getOpenFileName(KUrl("kfiledialog:///clipfolder"), QString(), kapp->activeWindow(), i18n("Looking for %1", path));
+            }
+            if (action == KMessageBox::Yes) {
+                kDebug() << "// ASKED FOR SRCH CLIP: " << clipId;
+                m_searchFolder = KFileDialog::getExistingDirectory(KUrl("kfiledialog:///clipfolder"), kapp->activeWindow());
+                if (!m_searchFolder.isEmpty()) {
+                    newpath = Render::searchFileRecursively(QDir(m_searchFolder), size, hash);
+                }
+            }
+            if (!newpath.isEmpty()) {
+                elem.setAttribute("resource", newpath);
+                setNewClipResource(clipId, newpath);
+            }
+        }
         clip = new DocClipBase(m_clipManager, elem, producerId);
         m_clipManager->addClip(clip);
     }
     if (createClipItem) emit addProjectClip(clip);
 }
 
+void KdenliveDoc::setNewClipResource(const QString &id, const QString &path) {
+    QDomNodeList prods = m_document.elementsByTagName("producer");
+    int maxprod = prods.count();
+    for (int i = 0; i < maxprod; i++) {
+        QDomNode m = prods.at(i);
+        QString prodId = m.toElement().attribute("id");
+        if (prodId == id || prodId.startsWith(id + "_")) {
+            QDomNodeList params = m.childNodes();
+            for (int j = 0; j < params.count(); j++) {
+                QDomElement e = params.item(j).toElement();
+                if (e.attribute("name") == "resource") {
+                    e.firstChild().setNodeValue(path);
+                    break;
+                }
+            }
+        }
+    }
+}
+
 void KdenliveDoc::addClipInfo(QDomElement elem, QString clipId) {
     DocClipBase *clip = m_clipManager->getClipById(clipId);
     if (clip == NULL) {
index cae8843905b2345d27f30813815a5544fd9bcefc..ef918b48fb3d6a9e9399ab6157c391aad6bbd4ee 100644 (file)
@@ -123,6 +123,7 @@ private:
     MltVideoProfile m_profile;
     QString m_scenelist;
     QTimer *m_autoSaveTimer;
+    QString m_searchFolder;
     /** tells whether current doc has been changed since last save event */
     bool m_modified;
     /** Project folder, used to store project files (titles, effects,...) */
@@ -133,6 +134,7 @@ private:
     QDomDocument createEmptyDocument(const int videotracks, const int audiotracks);
     QString colorToString(const QColor& c);
     void checkProjectClips();
+    void setNewClipResource(const QString &id, const QString &path);
 
 public slots:
     void slotCreateTextClip(QString group, const QString &groupId);
index ffd93a24f90dbb5ff44edf85792df16e98fefe96..eafc67de68048b1e55aba9c2b35eb9f14c08ce14 100644 (file)
@@ -442,13 +442,35 @@ void ProjectList::slotAddClip(QUrl givenUrl, QString group) {
 void ProjectList::slotRemoveInvalidClip(const QString &id) {
     ProjectItem *item = getItemById(id);
     if (item) {
-        QString path = item->referencedClip()->fileURL().path();
-        if (!path.isEmpty()) KMessageBox::sorry(this, i18n("<qt>Clip <b>%1</b><br>is invalid, will be removed from project.", path));
-
+        const QString path = item->referencedClip()->fileURL().path();
+        //if (!path.isEmpty()) KMessageBox::sorry(this, i18n("<qt>Clip <b>%1</b><br>is invalid, will be removed from project.", path));
+        KMessageBox::ButtonCode action;
+        if (!path.isEmpty()) {
+            action = (KMessageBox::ButtonCode)KMessageBox::messageBox(this, KMessageBox::WarningYesNoCancel, i18n("<qt>Clip <b>%1</b><br>is invalid, what do you want to do?", path), i18n("File not found"), KGuiItem(i18n("Search automatically")), KGuiItem(i18n("Remove from project")), KGuiItem(i18n("Keep as placeholder")));
+        } else
+            action = KMessageBox::No; // then remove
+        if (action == KMessageBox::Yes) { // search
+            QString foundFileName;
+            if (!item->referencedClip()->getProperty("file_size").isEmpty() && !item->referencedClip()->getProperty("file_hash").isEmpty()) { // both hash and file size were registered
+                QString rootDir = KFileDialog::getExistingDirectory(KUrl("kfiledialog:///clipfolder"), this);
+                if (!rootDir.isEmpty()) {
+                    foundFileName = Render::searchFileRecursively(QDir(rootDir), item->referencedClip()->getProperty("file_size"), item->referencedClip()->getProperty("file_hash"));
+                }
+            }
+            if (foundFileName.isEmpty())
+                KMessageBox::sorry(this, i18n("<qt>Cannot find a match for clip<br><b>%1</b>,<br>leaving in project as a placeholder.", path));
+            else {
+                QMap <QString, QString> properties;
+                properties["resource"] = foundFileName;
+                kDebug() << "CLIP ID:" << item->referencedClip()->getId() << "--- setting 'resource' to" << foundFileName;
+                slotUpdateClipProperties(item->referencedClip()->getId(), properties);
+            }
+        } else if (action == KMessageBox::No) { // remove
+            QList <QString> ids;
+            ids << id;
+            m_doc->deleteProjectClip(ids);
+        } // else keep it (last choice to be automatically bound to ESC)
     }
-    QList <QString> ids;
-    ids << id;
-    m_doc->deleteProjectClip(ids);
     if (!m_infoQueue.isEmpty()) QTimer::singleShot(300, this, SLOT(slotProcessNextClipInQueue()));
 }
 
index 8130148f03cf740d2e858316e294dab76a135fcb..b6115798f544146f92b4d62f2bc145b290d0a423 100644 (file)
@@ -27,6 +27,7 @@
 #include <QPainter>
 #include <QItemDelegate>
 #include <QUndoStack>
+#include <QDir>
 
 #include <KTreeWidgetSearchLine>
 #include <KUrl>
index 4a913797a92fc9f6a858e946b735f22a385cfcee..c217bd125d12018e2871b22e3bc727fb7d203a09 100644 (file)
@@ -33,7 +33,8 @@ extern "C" {
 #include <QTimer>
 #include <QDir>
 #include <QApplication>
-#include <QPainter>
+//#include <QPainter>
+#include <QCryptographicHash>
 
 #include <KDebug>
 #include <KStandardDirs>
@@ -520,7 +521,7 @@ void Render::getFileProperties(const QDomElement &xml, const QString &clipId) {
     }
     if (xml.hasAttribute("out")) producer->set_in_and_out(xml.attribute("in").toInt(), xml.attribute("out").toInt());
 
-    if (producer->is_blank()) {
+    if (producer->is_blank() || !producer->is_valid()) {
         kDebug() << " / / / / / / / /ERRROR / / / / // CANNOT LOAD PRODUCER: ";
         emit removeInvalidClip(clipId);
         return;
@@ -691,6 +692,44 @@ void Render::getFileProperties(const QDomElement &xml, const QString &clipId) {
 }
 
 
+//static
+
+QString Render::searchFileRecursively(const QDir &dir, const QString &matchSize, const QString &matchHash) {
+    QString foundFileName;
+    QByteArray fileData;
+    QByteArray fileHash;
+    QStringList filesAndDirs = dir.entryList(QDir::Files | QDir::Readable);
+    for (int i = 0; i < filesAndDirs.size() && foundFileName.isEmpty(); i++) {
+        QFile file(dir.absoluteFilePath(filesAndDirs.at(i)));
+        if (file.open(QIODevice::ReadOnly)) {
+            if (QString::number(file.size()) == matchSize) {
+                /*
+                * 1 MB = 1 second per 450 files (or faster)
+                * 10 MB = 9 seconds per 450 files (or faster)
+                */
+                if (file.size() > 1000000*2) {
+                    fileData = file.read(1000000);
+                    if (file.seek(file.size() - 1000000))
+                        fileData.append(file.readAll());
+                } else
+                    fileData = file.readAll();
+                file.close();
+                fileHash = QCryptographicHash::hash(fileData, QCryptographicHash::Md5);
+                if (QString(fileHash.toHex()) == matchHash)
+                    return file.fileName();
+            }
+        }
+        kDebug() << filesAndDirs.at(i) << file.size() << fileHash.toHex();
+    }
+    filesAndDirs = dir.entryList(QDir::Dirs | QDir::Readable | QDir::Executable | QDir::NoDotAndDotDot);
+    for (int i = 0; i < filesAndDirs.size() && foundFileName.isEmpty(); i++) {
+        foundFileName = searchFileRecursively(dir.absoluteFilePath(filesAndDirs.at(i)), matchSize, matchHash);
+        if (!foundFileName.isEmpty())
+            break;
+    }
+    return foundFileName;
+}
+
 /** Create the producer from the Westley QDomDocument */
 #if 0
 void Render::initSceneList() {
index cc8cbde259e7b2c26c198a59abbe0952f6588fd6..7eb6c5deb422f4f2a499a7c956de8074dbef4571 100644 (file)
@@ -22,7 +22,8 @@
 #include <qstring.h>
 #include <qmap.h>
 #include <QList>
-#include <QWidget>
+#include <QDir>
+//#include <QWidget>
 
 #include <kurl.h>
 
@@ -179,6 +180,7 @@ Q_OBJECT public:
     int mltChangeClipSpeed(ItemInfo info, double speed, double oldspeed, Mlt::Producer *prod);
 
     QList <Mlt::Producer *> producersList();
+    static QString searchFileRecursively(const QDir &dir, const QString &matchSize, const QString &matchHash);
 
 private:   // Private attributes & methods
     /** The name of this renderer - useful to identify the renderes by what they do - e.g. background rendering, workspace monitor, etc... */