]> git.sesse.net Git - kdenlive/blob - src/documentchecker.cpp
Start of the new document checker (check for missing clips,...) not fully functionnal yet
[kdenlive] / src / documentchecker.cpp
1 /***************************************************************************
2  *   Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
18  ***************************************************************************/
19
20
21 #include "documentchecker.h"
22 #include "kthumb.h"
23 #include "definitions.h"
24
25 #include <KDebug>
26 #include <KGlobalSettings>
27 #include <KFileItem>
28 #include <KIO/NetAccess>
29 #include <KFileDialog>
30 #include <KApplication>
31 #include <KUrlRequesterDialog>
32
33 #include <QTreeWidgetItem>
34 #include <QFile>
35 #include <QHeaderView>
36 #include <QIcon>
37 #include <QPixmap>
38 #include <QTimer>
39 #include <QCryptographicHash>
40
41 const int hashRole = Qt::UserRole;
42 const int sizeRole = Qt::UserRole + 1;
43 const int idRole = Qt::UserRole + 2;
44 const int statusRole = Qt::UserRole + 3;
45
46 DocumentChecker::DocumentChecker(QDomDocument doc, QWidget * parent) :
47         QDialog(parent), m_doc(doc)
48 {
49     setFont(KGlobalSettings::toolBarFont());
50     m_view.setupUi(this);
51
52     QDomNodeList producers = m_doc.elementsByTagName("producer");
53     QDomNodeList infoproducers = m_doc.elementsByTagName("kdenlive_producer");
54
55     int clipType;
56     QDomElement e;
57     QString id;
58     QString resource;
59     QList <QDomElement> missingClips;
60     for (int i = 0; i < infoproducers.count(); i++) {
61         e = infoproducers.item(i).toElement();
62         clipType = e.attribute("type").toInt();
63         if (clipType == TEXT) continue;
64         id = e.attribute("id");
65         resource = e.attribute("resource");
66         if (clipType == SLIDESHOW) resource = KUrl(resource).directory();
67         if (!KIO::NetAccess::exists(KUrl(resource), KIO::NetAccess::SourceSide, 0)) {
68             // Missing clip found
69             missingClips.append(e);
70         }
71     }
72
73     if (missingClips.isEmpty()) QTimer::singleShot(0, this, SLOT(reject()));
74
75     for (int i = 0; i < missingClips.count(); i++) {
76         e = missingClips.at(i).toElement();
77         QString clipType;
78         switch (e.attribute("type").toInt()) {
79         case AV:
80             clipType = i18n("Video clip");
81             break;
82         case VIDEO:
83             clipType = i18n("Mute video clip");
84             break;
85         case AUDIO:
86             clipType = i18n("Audio clip");
87             break;
88         case PLAYLIST:
89             clipType = i18n("Playlist clip");
90             break;
91         case IMAGE:
92             clipType = i18n("Image clip");
93             break;
94         case SLIDESHOW:
95             clipType = i18n("Slideshow clip");
96             break;
97         default:
98             clipType = i18n("Video clip");
99         }
100         QTreeWidgetItem *item = new QTreeWidgetItem(m_view.treeWidget, QStringList() << clipType << e.attribute("resource"));
101         item->setIcon(0, KIcon("dialog-close"));
102         item->setData(0, hashRole, e.attribute("file_hash"));
103         item->setData(0, sizeRole, e.attribute("file_size"));
104         item->setData(0, idRole, e.attribute("id"));
105         item->setData(0, statusRole, '1');
106     }
107     connect(m_view.recursiveSearch, SIGNAL(pressed()), this, SLOT(slotSearchClips()));
108     connect(m_view.treeWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(slotEditItem(QTreeWidgetItem *, int)));
109     //adjustSize();
110 }
111
112 DocumentChecker::~DocumentChecker() {}
113
114 void DocumentChecker::slotSearchClips()
115 {
116     QString newpath = KFileDialog::getExistingDirectory(KUrl("kfiledialog:///clipfolder"), kapp->activeWindow(), i18n("Clips folder"));
117     if (newpath.isEmpty()) return;
118     int ix = 0;
119     QTreeWidgetItem *child = m_view.treeWidget->topLevelItem(ix);
120     while (child && !child->data(0, statusRole).toString().isEmpty()) {
121         QString clipPath = searchFileRecursively(QDir(newpath), child->data(0, sizeRole).toString(), child->data(0, hashRole).toString());
122         if (!clipPath.isEmpty()) {
123             child->setText(1, clipPath);
124             child->setIcon(0, KIcon("dialog-ok"));
125             child->setData(0, statusRole, QString());
126         }
127         ix++;
128         child = m_view.treeWidget->topLevelItem(ix);
129     }
130 }
131
132 QString DocumentChecker::searchFileRecursively(const QDir &dir, const QString &matchSize, const QString &matchHash) const
133 {
134     QString foundFileName;
135     QByteArray fileData;
136     QByteArray fileHash;
137     QStringList filesAndDirs = dir.entryList(QDir::Files | QDir::Readable);
138     for (int i = 0; i < filesAndDirs.size() && foundFileName.isEmpty(); i++) {
139         QFile file(dir.absoluteFilePath(filesAndDirs.at(i)));
140         if (file.open(QIODevice::ReadOnly)) {
141             if (QString::number(file.size()) == matchSize) {
142                 /*
143                 * 1 MB = 1 second per 450 files (or faster)
144                 * 10 MB = 9 seconds per 450 files (or faster)
145                 */
146                 if (file.size() > 1000000*2) {
147                     fileData = file.read(1000000);
148                     if (file.seek(file.size() - 1000000))
149                         fileData.append(file.readAll());
150                 } else
151                     fileData = file.readAll();
152                 file.close();
153                 fileHash = QCryptographicHash::hash(fileData, QCryptographicHash::Md5);
154                 if (QString(fileHash.toHex()) == matchHash)
155                     return file.fileName();
156             }
157         }
158         kDebug() << filesAndDirs.at(i) << file.size() << fileHash.toHex();
159     }
160     filesAndDirs = dir.entryList(QDir::Dirs | QDir::Readable | QDir::Executable | QDir::NoDotAndDotDot);
161     for (int i = 0; i < filesAndDirs.size() && foundFileName.isEmpty(); i++) {
162         foundFileName = searchFileRecursively(dir.absoluteFilePath(filesAndDirs.at(i)), matchSize, matchHash);
163         if (!foundFileName.isEmpty())
164             break;
165     }
166     return foundFileName;
167 }
168
169 void DocumentChecker::slotEditItem(QTreeWidgetItem *item, int)
170 {
171     KUrl url = KUrlRequesterDialog::getUrl(item->text(1), this, i18n("Enter new location for file"));
172     if (url.isEmpty()) return;
173     item->setText(1, url.path());
174     if (KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0)) {
175         item->setIcon(0, KIcon("dialog-ok"));
176         item->setData(0, statusRole, QString());
177     }
178 }
179
180 // virtual
181 void DocumentChecker::accept()
182 {
183     QDomElement e;
184     QDomNodeList producers = m_doc.elementsByTagName("producer");
185     QDomNodeList infoproducers = m_doc.elementsByTagName("kdenlive_producer");
186     int ix = 0;
187     QTreeWidgetItem *child = m_view.treeWidget->topLevelItem(ix);
188     while (child && child->data(0, statusRole).toString().isEmpty()) {
189         QString id = child->data(0, idRole).toString();
190         for (int i = 0; i < infoproducers.count(); i++) {
191             e = infoproducers.item(i).toElement();
192             if (e.attribute("id") == id) {
193                 // Fix clip
194                 e.setAttribute("resource", child->text(1));
195                 break;
196             }
197         }
198         for (int i = 0; i < producers.count(); i++) {
199             e = producers.item(i).toElement();
200             if (e.attribute("id") == id) {
201                 // Fix clip
202                 e.setAttribute("resource", child->text(1));
203                 break;
204             }
205         }
206
207         ix++;
208         child = m_view.treeWidget->topLevelItem(ix);
209     }
210     QDialog::accept();
211 }
212
213 #include "documentchecker.moc"
214
215