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);
}
* *
***************************************************************************/
+#include <QCryptographicHash>
+
#include <KDebug>
#include "kdenlivesettings.h"
}
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()));
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());
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);
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) {
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,...) */
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);
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()));
}
#include <QPainter>
#include <QItemDelegate>
#include <QUndoStack>
+#include <QDir>
#include <KTreeWidgetSearchLine>
#include <KUrl>
#include <QTimer>
#include <QDir>
#include <QApplication>
-#include <QPainter>
+//#include <QPainter>
+#include <QCryptographicHash>
#include <KDebug>
#include <KStandardDirs>
}
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;
}
+//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() {
#include <qstring.h>
#include <qmap.h>
#include <QList>
-#include <QWidget>
+#include <QDir>
+//#include <QWidget>
#include <kurl.h>
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... */