Some progress on archiving feature

svn path=/trunk/kdenlive/; revision=5572
parent aa49bce6
......@@ -25,29 +25,68 @@
#include <KDiskFreeSpaceInfo>
#include <KUrlRequester>
#include <KFileDialog>
#include <KMessageBox>
#include <KApplication>
#include <KIO/NetAccess>
#include <KDebug>
#include <QListWidget>
#include <QTreeWidget>
#include "projectsettings.h"
ArchiveWidget::ArchiveWidget(QList <DocClipBase*> list, QStringList lumas, QWidget * parent) :
ArchiveWidget::ArchiveWidget(QDomDocument doc, QList <DocClipBase*> list, QStringList luma_list, QWidget * parent) :
QDialog(parent),
m_requestedSize(0),
m_copyJob(NULL)
m_copyJob(NULL),
m_doc(doc)
{
setupUi(this);
setWindowTitle(i18n("Archive Project"));
archive_url->setUrl(KUrl(QDir::homePath()));
connect(archive_url, SIGNAL(textChanged (const QString &)), this, SLOT(slotCheckSpace()));
// Setup categories
QTreeWidgetItem *videos = new QTreeWidgetItem(files_list, QStringList() << i18n("Video clips"));
videos->setIcon(0, KIcon("video-x-generic"));
videos->setData(0, Qt::UserRole, i18n("videos"));
videos->setExpanded(false);
QTreeWidgetItem *sounds = new QTreeWidgetItem(files_list, QStringList() << i18n("Audio clips"));
sounds->setIcon(0, KIcon("audio-x-generic"));
sounds->setData(0, Qt::UserRole, i18n("sounds"));
sounds->setExpanded(false);
QTreeWidgetItem *images = new QTreeWidgetItem(files_list, QStringList() << i18n("Image clips"));
images->setIcon(0, KIcon("image-x-generic"));
images->setData(0, Qt::UserRole, i18n("images"));
images->setExpanded(false);
QTreeWidgetItem *slideshows = new QTreeWidgetItem(files_list, QStringList() << i18n("Slideshow clips"));
slideshows->setIcon(0, KIcon("image-x-generic"));
slideshows->setData(0, Qt::UserRole, i18n("slideshows"));
slideshows->setExpanded(false);
QTreeWidgetItem *texts = new QTreeWidgetItem(files_list, QStringList() << i18n("Text clips"));
texts->setIcon(0, KIcon("text-plain"));
texts->setData(0, Qt::UserRole, i18n("texts"));
texts->setExpanded(false);
QTreeWidgetItem *others = new QTreeWidgetItem(files_list, QStringList() << i18n("Other clips"));
others->setIcon(0, KIcon("unknown"));
others->setData(0, Qt::UserRole, i18n("others"));
others->setExpanded(false);
QTreeWidgetItem *lumas = new QTreeWidgetItem(files_list, QStringList() << i18n("Luma files"));
lumas->setIcon(0, KIcon("image-x-generic"));
lumas->setData(0, Qt::UserRole, i18n("lumas"));
lumas->setExpanded(false);
// process all files
QStringList allFonts;
foreach(const QString & file, lumas) {
kDebug()<<"LUMA: "<<file;
files_list->addItem(file);
m_requestedSize += QFileInfo(file).size();
}
KUrl::List fileUrls;
QStringList fileNames;
generateItems(lumas, luma_list);
QStringList slideUrls;
QStringList audioUrls;
QStringList videoUrls;
QStringList imageUrls;
QStringList otherUrls;
for (int i = 0; i < list.count(); i++) {
DocClipBase *clip = list.at(i);
......@@ -55,35 +94,50 @@ ArchiveWidget::ArchiveWidget(QList <DocClipBase*> list, QStringList lumas, QWidg
QStringList subfiles = ProjectSettings::extractSlideshowUrls(clip->fileURL());
foreach(const QString & file, subfiles) {
kDebug()<<"SLIDE: "<<file;
files_list->addItem(file);
new QTreeWidgetItem(slideshows, QStringList() << file);
m_requestedSize += QFileInfo(file).size();
}
} else if (!clip->fileURL().isEmpty()) {
files_list->addItem(clip->fileURL().path());
m_requestedSize += QFileInfo(clip->fileURL().path()).size();
if (clip->clipType() == AUDIO) audioUrls << clip->fileURL().path();
else videoUrls << clip->fileURL().path();
}
if (clip->clipType() == TEXT) {
QStringList imagefiles = TitleWidget::extractImageList(clip->getProperty("xmldata"));
QStringList fonts = TitleWidget::extractFontList(clip->getProperty("xmldata"));
foreach(const QString & file, imagefiles) {
kDebug()<<"TXT IMAGE: "<<file;
files_list->addItem(file);
m_requestedSize += QFileInfo(file).size();
}
imageUrls << imagefiles;
allFonts << fonts;
} else if (clip->clipType() == PLAYLIST) {
QStringList files = ProjectSettings::extractPlaylistUrls(clip->fileURL().path());
foreach(const QString & file, files) {
kDebug()<<"PLAYLIST: "<<file;
files_list->addItem(file);
m_requestedSize += QFileInfo(file).size();
}
otherUrls << files;
}
}
generateItems(sounds, audioUrls);
generateItems(videos, videoUrls);
generateItems(images, imageUrls);
//generateItems(slideshows, slideUrls);
generateItems(others, otherUrls);
#if QT_VERSION >= 0x040500
allFonts.removeDuplicates();
#endif
project_files->setText(i18n("%1 files to archive, requires %2", files_list->count(), KIO::convertSize(m_requestedSize)));
//TODO: fonts
// Hide unused categories, add item count
int total = 0;
for (int i = 0; i < files_list->topLevelItemCount(); i++) {
int items = files_list->topLevelItem(i)->childCount();
if (items == 0) {
files_list->topLevelItem(i)->setHidden(true);
}
else {
total += items;
files_list->topLevelItem(i)->setText(0, files_list->topLevelItem(i)->text(0) + " " + i18n("(%1 items)", items));
}
}
project_files->setText(i18n("%1 files to archive, requires %2", total, KIO::convertSize(m_requestedSize)));
buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Archive"));
connect(buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(slotStartArchiving()));
buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
......@@ -95,6 +149,29 @@ ArchiveWidget::~ArchiveWidget()
{
}
void ArchiveWidget::generateItems(QTreeWidgetItem *parentItem, QStringList items)
{
QStringList filesList;
QString fileName;
foreach(const QString & file, items) {
QTreeWidgetItem *item = new QTreeWidgetItem(parentItem, QStringList() << file);
fileName = KUrl(file).fileName();
if (filesList.contains(fileName)) {
// we have 2 files with same name
int ix = 0;
QString newFileName = fileName.section('.', 0, -2) + "_" + QString::number(ix) + "." + fileName.section('.', -1);
while (filesList.contains(newFileName)) {
ix ++;
newFileName = fileName.section('.', 0, -2) + "_" + QString::number(ix) + "." + fileName.section('.', -1);
}
fileName = newFileName;
item->setData(0, Qt::UserRole, fileName);
}
filesList << fileName;
m_requestedSize += QFileInfo(file).size();
}
}
void ArchiveWidget::slotCheckSpace()
{
KDiskFreeSpaceInfo inf = KDiskFreeSpaceInfo::freeSpaceInfo( archive_url->url().path());
......@@ -112,36 +189,98 @@ void ArchiveWidget::slotCheckSpace()
}
}
void ArchiveWidget::slotStartArchiving()
bool ArchiveWidget::slotStartArchiving(bool firstPass)
{
if (m_copyJob) {
if (firstPass && m_copyJob) {
// archiving in progress, abort
m_copyJob->kill(KJob::EmitResult);
return;
return true;
}
if (!firstPass) m_copyJob = NULL;
else {
//starting archiving
m_duplicateFiles.clear();
m_replacementList.clear();
}
KUrl::List files;
for (int i = 0; i < files_list->count(); i++) {
if (files_list->item(i))
files << KUrl(files_list->item(i)->text());
KUrl destUrl;
for (int i = 0; i < files_list->topLevelItemCount(); i++) {
if (files_list->topLevelItem(i)->childCount() > 0 && !files_list->topLevelItem(i)->isDisabled()) {
files_list->setCurrentItem(files_list->topLevelItem(i));
files_list->topLevelItem(i)->setDisabled(true);
destUrl = KUrl(archive_url->url().path(KUrl::AddTrailingSlash) + files_list->topLevelItem(i)->data(0, Qt::UserRole).toString());
KIO::NetAccess::mkdir(destUrl, this);
QTreeWidgetItem *item;
for (int j = 0; j < files_list->topLevelItem(i)->childCount(); j++) {
item = files_list->topLevelItem(i)->child(j);
if (item->data(0, Qt::UserRole).isNull()) {
files << KUrl(item->text(0));
}
else {
// We must rename the destination file, since another file with same name exists
//TODO: monitor progress
m_duplicateFiles.insert(KUrl(item->text(0)), KUrl(destUrl.path(KUrl::AddTrailingSlash) + item->data(0, Qt::UserRole).toString()));
}
}
break;
}
}
progressBar->setValue(0);
buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Abort"));
m_copyJob = KIO::copy (files, archive_url->url(), KIO::HideProgressInfo);
if (destUrl.isEmpty()) {
if (m_duplicateFiles.isEmpty()) return false;
QMapIterator<KUrl, KUrl> i(m_duplicateFiles);
KUrl startJob;
if (i.hasNext()) {
i.next();
startJob = i.key();
KIO::CopyJob *job = KIO::copyAs(startJob, i.value(), KIO::HideProgressInfo);
connect(job, SIGNAL(result(KJob *)), this, SLOT(slotArchivingFinished(KJob *)));
connect(job, SIGNAL(processedSize(KJob *, qulonglong)), this, SLOT(slotArchivingProgress(KJob *, qulonglong)));
m_duplicateFiles.remove(startJob);
}
return true;
}
m_copyJob = KIO::copy (files, destUrl, KIO::HideProgressInfo);
connect(m_copyJob, SIGNAL(result(KJob *)), this, SLOT(slotArchivingFinished(KJob *)));
connect(m_copyJob, SIGNAL(processedSize(KJob *, qulonglong)), this, SLOT(slotArchivingProgress(KJob *, qulonglong)));
if (firstPass) {
progressBar->setValue(0);
buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Abort"));
}
return true;
}
void ArchiveWidget::slotArchivingFinished(KJob *job)
{
progressBar->setValue(100);
buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Archive"));
if (job->error() == 0) text_info->setText(i18n("Project was successfully archived."));
if (job->error() == 0) {
if (slotStartArchiving(false)) {
// We still have files to archive
return;
}
else {
// Archiving finished
progressBar->setValue(100);
if (processProjectFile()) {
icon_info->setPixmap(KIcon("dialog-ok").pixmap(16, 16));
text_info->setText(i18n("Project was successfully archived."));
}
else {
icon_info->setPixmap(KIcon("dialog-close").pixmap(16, 16));
text_info->setText(i18n("There was an error processing project file"));
}
}
}
else {
m_copyJob = NULL;
icon_info->setPixmap(KIcon("dialog-close").pixmap(16, 16));
text_info->setText(i18n("There was an error while copying the files: %1", job->errorString()));
}
m_copyJob = NULL;
buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Archive"));
for (int i = 0; i < files_list->topLevelItemCount(); i++) {
files_list->topLevelItem(i)->setDisabled(false);
}
}
void ArchiveWidget::slotArchivingProgress(KJob *, qulonglong size)
......@@ -149,3 +288,57 @@ void ArchiveWidget::slotArchivingProgress(KJob *, qulonglong size)
progressBar->setValue((int) 100 * size / m_requestedSize);
}
bool ArchiveWidget::processProjectFile()
{
KUrl destUrl;
QTreeWidgetItem *item;
for (int i = 0; i < files_list->topLevelItemCount(); i++) {
if (files_list->topLevelItem(i)->childCount() > 0) {
destUrl = KUrl(archive_url->url().path(KUrl::AddTrailingSlash) + files_list->topLevelItem(i)->data(0, Qt::UserRole).toString());
for (int j = 0; j < files_list->topLevelItem(i)->childCount(); j++) {
item = files_list->topLevelItem(i)->child(j);
KUrl src(item->text(0));
KUrl dest = destUrl;
if (item->data(0, Qt::UserRole).isNull()) {
dest.addPath(src.fileName());
}
else {
dest.addPath(item->data(0, Qt::UserRole).toString());
}
m_replacementList.insert(src, dest);
}
}
}
// process kdenlive producers
QDomElement mlt = m_doc.documentElement();
QDomNodeList prods = mlt.elementsByTagName("kdenlive_producer");
for (int i = 0; i < prods.count(); i++) {
QDomElement e = prods.item(i).toElement();
if (e.isNull()) continue;
if (e.hasAttribute("resource")) {
KUrl src(e.attribute("resource"));
KUrl dest = m_replacementList.value(src);
if (!dest.isEmpty()) e.setAttribute("resource", dest.path());
}
}
QString path = archive_url->url().path(KUrl::AddTrailingSlash) + "project.kdenlive";
QFile file(path);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
kWarning() << "////// ERROR writing to file: " << path;
KMessageBox::error(kapp->activeWindow(), i18n("Cannot write to file %1", path));
return false;
}
file.write(m_doc.toString().toUtf8());
if (file.error() != QFile::NoError) {
KMessageBox::error(kapp->activeWindow(), i18n("Cannot write to file %1", path));
file.close();
return false;
}
file.close();
return true;
}
......@@ -45,18 +45,26 @@ class ArchiveWidget : public QDialog, public Ui::ArchiveWidget_UI
Q_OBJECT
public:
ArchiveWidget(QList <DocClipBase*> list, QStringList lumas, QWidget * parent = 0);
ArchiveWidget(QDomDocument doc, QList <DocClipBase*> list, QStringList luma_list, QWidget * parent = 0);
~ArchiveWidget();
private slots:
void slotCheckSpace();
void slotStartArchiving();
bool slotStartArchiving(bool firstPass = true);
void slotArchivingFinished(KJob *job);
void slotArchivingProgress(KJob *, qulonglong);
private:
KIO::filesize_t m_requestedSize;
KIO::CopyJob *m_copyJob;
QMap <KUrl, KUrl> m_duplicateFiles;
QMap <KUrl, KUrl> m_replacementList;
QDomDocument m_doc;
/** @brief Generate tree widget subitems from a string list of urls. */
void generateItems(QTreeWidgetItem *parentItem, QStringList items);
/** @brief Replace urls in project file. */
bool processProjectFile();
signals:
......
......@@ -535,15 +535,14 @@ QPoint KdenliveDoc::zone() const
return QPoint(m_documentProperties.value("zonein").toInt(), m_documentProperties.value("zoneout").toInt());
}
bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene, const QStringList expandedFolders)
QDomDocument KdenliveDoc::xmlSceneList(const QString &scene, const QStringList expandedFolders)
{
QDomDocument sceneList;
sceneList.setContent(scene, true);
QDomElement mlt = sceneList.firstChildElement("mlt");
if (mlt.isNull() || !mlt.hasChildNodes()) {
//Make sure we don't save if scenelist is corrupted
KMessageBox::error(kapp->activeWindow(), i18n("Cannot write to file %1, scene list is corrupted.", path));
return false;
//scenelist is corrupted
return sceneList;
}
QDomElement addedXml = sceneList.createElement("kdenlivedoc");
......@@ -658,6 +657,17 @@ bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene, const
addedXml.appendChild(sceneList.importNode(m_clipManager->groupsXml(), true));
//wes.appendChild(doc.importNode(kdenliveData, true));
return sceneList;
}
bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene, const QStringList expandedFolders)
{
QDomDocument sceneList = xmlSceneList(scene, expandedFolders);
if (sceneList.isNull()) {
//Make sure we don't save if scenelist is corrupted
KMessageBox::error(kapp->activeWindow(), i18n("Cannot write to file %1, scene list is corrupted.", path));
return false;
}
QFile file(path);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
......
......@@ -112,6 +112,9 @@ Q_OBJECT public:
QPoint zoom() const;
double dar() const;
double projectDuration() const;
/** @brief Returns the project file xml. */
QDomDocument xmlSceneList(const QString &scene, const QStringList expandedFolders);
/** @brief Saves the project file xml to a file. */
bool saveSceneList(const QString &path, const QString &scene, const QStringList expandedFolders);
int tracksCount() const;
TrackInfo trackInfoAt(int ix) const;
......
......@@ -4249,7 +4249,8 @@ void MainWindow::slotInsertNotesTimecode()
void MainWindow::slotArchiveProject()
{
QList <DocClipBase*> list = m_projectList->documentClipList();
ArchiveWidget *d = new ArchiveWidget(list, m_activeTimeline->projectView()->extractTransitionsLumas(), this);
QDomDocument doc = m_activeDocument->xmlSceneList(m_projectMonitor->sceneList(), m_projectList->expandedFolders());
ArchiveWidget *d = new ArchiveWidget(doc, list, m_activeTimeline->projectView()->extractTransitionsLumas(), this);
d->exec();
}
......
......@@ -63,9 +63,6 @@
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QListWidget" name="files_list"/>
</item>
<item row="4" column="0">
<widget class="QProgressBar" name="progressBar">
<property name="value">
......@@ -83,6 +80,24 @@
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QTreeWidget" name="files_list">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectItems</enum>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<customwidgets>
......
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