Add recursive search when opening a document with missing clip, heavily based...

Add recursive search when opening a document with missing clip, heavily based on a patch from Alberto Villa

svn path=/branches/KDE4/; revision=2723
parent 86cb6a21
......@@ -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);
}
......
......@@ -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());
......
......@@ -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);
......
......@@ -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) {
......
......@@ -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);
......
......@@ -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()));
}
......
......@@ -27,6 +27,7 @@
#include <QPainter>
#include <QItemDelegate>
#include <QUndoStack>
#include <QDir>
#include <KTreeWidgetSearchLine>
#include <KUrl>
......
......@@ -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() {
......
......@@ -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... */
......
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