File indexing completed on 2025-02-09 05:44:36
0001 /* 0002 SPDX-FileCopyrightText: 2016 Jean-Baptiste Mardelle <jb@kdenlive.org> 0003 This file is part of Kdenlive. See www.kdenlive.org. 0004 0005 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #include "temporarydata.h" 0009 #include "bin/bin.h" 0010 #include "core.h" 0011 #include "doc/kdenlivedoc.h" 0012 #include "kdenlivesettings.h" 0013 0014 #include <KLocalizedString> 0015 #include <KMessageBox> 0016 #include <QDesktopServices> 0017 #include <QFontMetrics> 0018 #include <QGridLayout> 0019 #include <QLabel> 0020 #include <QPaintEvent> 0021 #include <QPainter> 0022 #include <QProgressBar> 0023 #include <QPushButton> 0024 #include <QSpinBox> 0025 #include <QStandardPaths> 0026 #include <QTabWidget> 0027 #include <QToolButton> 0028 #include <QToolTip> 0029 #include <QTreeWidget> 0030 #include <QVBoxLayout> 0031 0032 ChartWidget::ChartWidget(QWidget *parent) 0033 : QWidget(parent) 0034 { 0035 QFontMetrics ft(font()); 0036 int minHeight = ft.height() * 6; 0037 setMinimumSize(minHeight, minHeight); 0038 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); 0039 m_segments = QList<int>(); 0040 } 0041 0042 void ChartWidget::setSegments(const QList<int> &segments) 0043 { 0044 m_segments = segments; 0045 update(); 0046 } 0047 0048 void ChartWidget::paintEvent(QPaintEvent *event) 0049 { 0050 QPainter painter(this); 0051 painter.setRenderHints(QPainter::Antialiasing); 0052 const QRectF clipRect = event->rect(); 0053 painter.setClipRect(clipRect); 0054 painter.setPen(palette().midlight().color()); 0055 int pieWidth = qMin(width(), height()) - 10; 0056 const QRectF pieRect(5, 5, pieWidth, pieWidth); 0057 int ix = 0; 0058 int previous = 0; 0059 for (int val : qAsConst(m_segments)) { 0060 if (val == 0) { 0061 ix++; 0062 continue; 0063 } 0064 painter.setBrush(colorAt(ix)); 0065 painter.drawPie(pieRect, previous, val); 0066 previous += val; 0067 ix++; 0068 } 0069 } 0070 0071 TemporaryData::TemporaryData(KdenliveDoc *doc, bool currentProjectOnly, QWidget *parent) 0072 : QDialog(parent) 0073 , m_doc(doc) 0074 , m_currentProjectOnly(currentProjectOnly) 0075 { 0076 setAttribute(Qt::WA_DeleteOnClose); 0077 setupUi(this); 0078 m_currentSizes = {0, 0, 0, 0, 0}; 0079 0080 // Setup page for current project 0081 m_currentPie = new ChartWidget(this); 0082 currentChartBox->addWidget(m_currentPie); 0083 0084 QPalette pal(palette()); 0085 QFontMetrics ft(font()); 0086 int minHeight = ft.height() / 2; 0087 0088 // Timeline preview data 0089 previewColor->setFixedSize(minHeight, minHeight); 0090 pal.setColor(QPalette::Window, m_currentPie->colorAt(0)); 0091 previewColor->setPalette(pal); 0092 connect(delPreview, &QToolButton::clicked, this, &TemporaryData::deletePreview); 0093 0094 // Proxy clips 0095 proxyColor->setFixedSize(minHeight, minHeight); 0096 pal.setColor(QPalette::Window, m_currentPie->colorAt(1)); 0097 proxyColor->setPalette(pal); 0098 connect(delProxy, &QToolButton::clicked, this, &TemporaryData::deleteProjectProxy); 0099 0100 // Proxy clips 0101 sequenceColor->setFixedSize(minHeight, minHeight); 0102 pal.setColor(QPalette::Window, m_currentPie->colorAt(4)); 0103 sequenceColor->setPalette(pal); 0104 0105 // Audio Thumbs 0106 audioColor->setFixedSize(minHeight, minHeight); 0107 pal.setColor(QPalette::Window, m_currentPie->colorAt(2)); 0108 audioColor->setPalette(pal); 0109 connect(delAudio, &QToolButton::clicked, this, &TemporaryData::deleteAudio); 0110 0111 // Video Thumbs 0112 thumbColor->setFixedSize(minHeight, minHeight); 0113 pal.setColor(QPalette::Window, m_currentPie->colorAt(3)); 0114 thumbColor->setPalette(pal); 0115 connect(delThumb, &QToolButton::clicked, this, &TemporaryData::deleteThumbs); 0116 0117 // Current total 0118 bool ok; 0119 QDir dir = m_doc->getCacheDir(CacheBase, &ok); 0120 currentPath->setText(QStringLiteral("<a href='#'>") + dir.absolutePath() + QStringLiteral("</a>")); 0121 connect(currentPath, &QLabel::linkActivated, this, &TemporaryData::openCacheFolder); 0122 connect(delCurrent, &QToolButton::clicked, this, &TemporaryData::deleteCurrentCacheData); 0123 0124 m_proxies = m_doc->getProxyHashList(); 0125 for (int i = 0; i < m_proxies.count(); i++) { 0126 m_proxies[i].append(QLatin1Char('*')); 0127 } 0128 0129 // Setup global page 0130 m_globalPie = new ChartWidget(this); 0131 gChartLayout->addWidget(m_globalPie); 0132 gChartLayout->setStretch(0, 15); 0133 gChartLayout->setStretch(1, 5); 0134 0135 connect(listWidget, &QTreeWidget::itemDoubleClicked, this, [&](QTreeWidgetItem *item, int) { 0136 QDesktopServices::openUrl(QUrl::fromLocalFile(m_globalDir.absoluteFilePath(item->data(0, Qt::UserRole).toString()))); 0137 }); 0138 0139 // Total Cache data 0140 pal = palette(); 0141 gTotalColor->setFixedSize(minHeight, minHeight); 0142 pal.setColor(QPalette::Window, m_currentPie->colorAt(0)); 0143 gTotalColor->setPalette(pal); 0144 connect(gClean, &QToolButton::clicked, this, &TemporaryData::cleanCache); 0145 0146 // Selection 0147 gSelectedColor->setFixedSize(minHeight, minHeight); 0148 pal.setColor(QPalette::Window, m_currentPie->colorAt(1)); 0149 gSelectedColor->setPalette(pal); 0150 connect(gDelete, &QToolButton::clicked, this, &TemporaryData::deleteSelected); 0151 0152 // Proxy data 0153 connect(gProxyClean, &QToolButton::clicked, this, &TemporaryData::cleanProxy); 0154 connect(gProxyDelete, &QToolButton::clicked, this, &TemporaryData::deleteProxy); 0155 ok = false; 0156 QDir global = m_doc->getCacheDir(SystemCacheRoot, &ok); 0157 QDir proxyFolder(global.absoluteFilePath(QStringLiteral("proxy"))); 0158 gProxyPath->setText(QString("<a href='#'>%1</a>").arg(proxyFolder.absolutePath())); 0159 connect(gProxyPath, &QLabel::linkActivated, [proxyFolder]() { QDesktopServices::openUrl(QUrl::fromLocalFile(proxyFolder.absolutePath())); }); 0160 0161 // Backup data 0162 connect(gBackupClean, &QToolButton::clicked, this, &TemporaryData::cleanBackup); 0163 connect(gBackupDelete, &QToolButton::clicked, this, &TemporaryData::deleteBackup); 0164 QDir backupFolder(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/.backup")); 0165 gBackupPath->setText(QString("<a href='#'>%1</a>").arg(backupFolder.absolutePath())); 0166 connect(gBackupPath, &QLabel::linkActivated, [backupFolder]() { QDesktopServices::openUrl(QUrl::fromLocalFile(backupFolder.absolutePath())); }); 0167 0168 // Config cleanup age 0169 gCleanupSpin->setSuffix(i18np(" month", " months", KdenliveSettings::cleanCacheMonths())); 0170 gCleanupSpin->setValue(KdenliveSettings::cleanCacheMonths()); 0171 connect(gCleanupSpin, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [&](int value) { 0172 KdenliveSettings::setCleanCacheMonths(value); 0173 gCleanupSpin->setSuffix(i18np(" month", " months", KdenliveSettings::cleanCacheMonths())); 0174 }); 0175 0176 // Setup help text 0177 help_cached->setToolTip(i18n("<b>Cached data</b> is composed of clip thumbnails and timeline preview videos. Deleting is safe, all data can be recreated " 0178 "on project opening.<br/><b>Backup data</b> is an archive of previous versions of your project files. Useful if you need to " 0179 "recover a previous version of a project. This data cannot be recovered.<br/><b>Proxy clips</b> are lower resolution video " 0180 "files used for faster editing. Deleting is safe, proxy clips can be recreated if you have the original source clips.")); 0181 connect(help_cached, &QToolButton::clicked, this, [this]() { QToolTip::showText(QCursor::pos(), help_cached->toolTip()); }); 0182 0183 // Cache info message 0184 cache_info->hide(); 0185 QAction *a = new QAction(QIcon::fromTheme(QStringLiteral("edit-clear-history")), i18n("Cleanup"), this); 0186 cache_info->setText(i18n("Your cache and backup data exceeds %1, cleanup is recommended.", KIO::convertSize(1048576 * KdenliveSettings::maxcachesize()))); 0187 cache_info->addAction(a); 0188 connect(a, &QAction::triggered, this, &TemporaryData::slotCleanUp); 0189 0190 processBackupDirectories(); 0191 0192 connect(listWidget, &QTreeWidget::itemSelectionChanged, this, &TemporaryData::refreshGlobalPie); 0193 0194 bool globalOnly = !pCore->bin()->hasUserClip(); 0195 if (currentProjectOnly) { 0196 tabWidget->removeTab(1); 0197 } else if (globalOnly) { 0198 tabWidget->removeTab(0); 0199 } 0200 0201 if (globalOnly && !currentProjectOnly) { 0202 updateGlobalInfo(); 0203 } else { 0204 updateDataInfo(); 0205 } 0206 } 0207 0208 void TemporaryData::updateDataInfo() 0209 { 0210 m_totalCurrent = 0; 0211 bool ok = false; 0212 QDir preview = m_doc->getCacheDir(CacheBase, &ok); 0213 if (!ok) { 0214 projectPage->setEnabled(false); 0215 return; 0216 } 0217 preview = m_doc->getCacheDir(CachePreview, &ok); 0218 if (ok) { 0219 KIO::DirectorySizeJob *job = KIO::directorySize(QUrl::fromLocalFile(preview.absolutePath())); 0220 connect(job, &KIO::DirectorySizeJob::result, this, &TemporaryData::gotPreviewSize); 0221 } 0222 0223 preview = m_doc->getCacheDir(CacheProxy, &ok); 0224 if (ok) { 0225 if (m_proxies.isEmpty()) { 0226 // No proxies for this project 0227 gotProxySize(0); 0228 } else { 0229 preview.setNameFilters(m_proxies); 0230 const QFileInfoList fList = preview.entryInfoList(); 0231 size_t size = 0; 0232 for (const QFileInfo &info : fList) { 0233 size += size_t(info.size()); 0234 } 0235 gotProxySize(size); 0236 } 0237 } 0238 0239 preview = m_doc->getCacheDir(CacheAudio, &ok); 0240 if (ok) { 0241 KIO::DirectorySizeJob *job = KIO::directorySize(QUrl::fromLocalFile(preview.absolutePath())); 0242 connect(job, &KIO::DirectorySizeJob::result, this, &TemporaryData::gotAudioSize); 0243 } 0244 preview = m_doc->getCacheDir(CacheSequence, &ok); 0245 if (ok) { 0246 KIO::DirectorySizeJob *job = KIO::directorySize(QUrl::fromLocalFile(preview.absolutePath())); 0247 connect(job, &KIO::DirectorySizeJob::result, this, &TemporaryData::gotSequenceSize); 0248 } 0249 preview = m_doc->getCacheDir(CacheTmpWorkFiles, &ok); 0250 if (ok) { 0251 KIO::DirectorySizeJob *job = KIO::directorySize(QUrl::fromLocalFile(preview.absolutePath())); 0252 connect(job, &KIO::DirectorySizeJob::result, this, &TemporaryData::gotSequenceSize); 0253 } 0254 preview = m_doc->getCacheDir(CacheThumbs, &ok); 0255 if (ok) { 0256 KIO::DirectorySizeJob *job = KIO::directorySize(QUrl::fromLocalFile(preview.absolutePath())); 0257 connect(job, &KIO::DirectorySizeJob::result, this, &TemporaryData::gotThumbSize); 0258 } 0259 if (!m_currentProjectOnly) { 0260 updateGlobalInfo(); 0261 } 0262 } 0263 0264 void TemporaryData::gotPreviewSize(KJob *job) 0265 { 0266 auto *sourceJob = static_cast<KIO::DirectorySizeJob *>(job); 0267 KIO::filesize_t total = sourceJob->totalSize(); 0268 if (sourceJob->totalFiles() == 0) { 0269 total = 0; 0270 } 0271 delPreview->setEnabled(total > 0); 0272 m_totalCurrent += total; 0273 m_currentSizes[0] = total; 0274 previewSize->setText(KIO::convertSize(total)); 0275 updateTotal(); 0276 } 0277 0278 void TemporaryData::gotProxySize(KIO::filesize_t total) 0279 { 0280 delProxy->setEnabled(total > 0); 0281 m_totalCurrent += total; 0282 m_currentSizes[1] = total; 0283 proxySize->setText(KIO::convertSize(total)); 0284 updateTotal(); 0285 } 0286 0287 void TemporaryData::gotAudioSize(KJob *job) 0288 { 0289 auto *sourceJob = static_cast<KIO::DirectorySizeJob *>(job); 0290 KIO::filesize_t total = sourceJob->totalSize(); 0291 if (sourceJob->totalFiles() == 0) { 0292 total = 0; 0293 } 0294 delAudio->setEnabled(total > 0); 0295 m_totalCurrent += total; 0296 m_currentSizes[2] = total; 0297 audioSize->setText(KIO::convertSize(total)); 0298 updateTotal(); 0299 } 0300 0301 void TemporaryData::gotThumbSize(KJob *job) 0302 { 0303 auto *sourceJob = static_cast<KIO::DirectorySizeJob *>(job); 0304 KIO::filesize_t total = sourceJob->totalSize(); 0305 if (sourceJob->totalFiles() == 0) { 0306 total = 0; 0307 } 0308 delThumb->setEnabled(total > 0); 0309 m_totalCurrent += total; 0310 m_currentSizes[3] = total; 0311 thumbSize->setText(KIO::convertSize(total)); 0312 updateTotal(); 0313 } 0314 0315 void TemporaryData::gotSequenceSize(KJob *job) 0316 { 0317 auto *sourceJob = static_cast<KIO::DirectorySizeJob *>(job); 0318 KIO::filesize_t total = sourceJob->totalSize(); 0319 if (sourceJob->totalFiles() == 0) { 0320 total = 0; 0321 } 0322 m_totalCurrent += total; 0323 m_currentSizes[4] += total; 0324 sequenceSize->setText(KIO::convertSize(m_currentSizes.at(4))); 0325 updateTotal(); 0326 } 0327 0328 void TemporaryData::updateTotal() 0329 { 0330 currentSize->setText(KIO::convertSize(m_totalCurrent)); 0331 delCurrent->setEnabled(m_totalCurrent > 0); 0332 QList<int> segments; 0333 for (KIO::filesize_t size : qAsConst(m_currentSizes)) { 0334 if (m_totalCurrent == 0) { 0335 segments << 0; 0336 } else { 0337 segments << static_cast<int>(16 * size * 360 / m_totalCurrent); 0338 } 0339 } 0340 m_currentPie->setSegments(segments); 0341 } 0342 0343 void TemporaryData::deletePreview() 0344 { 0345 bool ok = false; 0346 QDir dir = m_doc->getCacheDir(CachePreview, &ok); 0347 if (!ok) { 0348 return; 0349 } 0350 if (KMessageBox::warningContinueCancel( 0351 this, 0352 i18n("Delete all data in the preview folder:\n%1\nPreview folder contains the timeline previews, and can be recreated with the source project.", 0353 dir.absolutePath())) != KMessageBox::Continue) { 0354 return; 0355 } 0356 if (dir.dirName() == QLatin1String("preview")) { 0357 dir.removeRecursively(); 0358 dir.mkpath(QStringLiteral(".")); 0359 Q_EMIT disablePreview(); 0360 updateDataInfo(); 0361 } 0362 } 0363 0364 void TemporaryData::deleteBackup() 0365 { 0366 QDir backupFolder(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/.backup")); 0367 if (KMessageBox::warningContinueCancel( 0368 this, i18n("Delete all data in the backup folder:\n%1\nA copy of all your project files is kept in this folder for recovery in case of corruption.", 0369 backupFolder.absolutePath())) != KMessageBox::Continue) { 0370 return; 0371 } 0372 if (backupFolder.dirName() == QLatin1String(".backup")) { 0373 backupFolder.removeRecursively(); 0374 backupFolder.mkpath(QStringLiteral(".")); 0375 processBackupDirectories(); 0376 } 0377 } 0378 0379 void TemporaryData::cleanBackup() 0380 { 0381 QDir backupFolder(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/.backup")); 0382 QFileInfoList files = backupFolder.entryInfoList(QDir::Files, QDir::Time); 0383 QStringList oldFiles; 0384 QDateTime current = QDateTime::currentDateTime(); 0385 KIO::filesize_t totalSize = 0; 0386 for (const QFileInfo &f : qAsConst(files)) { 0387 if (f.lastModified().addMonths(KdenliveSettings::cleanCacheMonths()) < current) { 0388 oldFiles << f.fileName(); 0389 totalSize += f.size(); 0390 } 0391 } 0392 if (oldFiles.isEmpty()) { 0393 KMessageBox::information(this, i18n("No backup data older than %1 months was found.", KdenliveSettings::cleanCacheMonths())); 0394 return; 0395 } 0396 if (KMessageBox::warningContinueCancelList( 0397 this, 0398 i18n("This will delete backup data (%1) for projects older than %2 months.", KIO::convertSize(totalSize), KdenliveSettings::cleanCacheMonths()), 0399 oldFiles) != KMessageBox::Continue) { 0400 return; 0401 } 0402 if (backupFolder.dirName() == QLatin1String(".backup")) { 0403 for (const QString &f : qAsConst(oldFiles)) { 0404 backupFolder.remove(f); 0405 } 0406 processBackupDirectories(); 0407 } 0408 } 0409 0410 void TemporaryData::cleanCache() 0411 { 0412 // Find empty dirs 0413 QList<QTreeWidgetItem *> emptyDirs = listWidget->findItems(KIO::convertSize(0), Qt::MatchExactly, 2); 0414 // Find old dirs 0415 QTreeWidgetItem *root = listWidget->invisibleRootItem(); 0416 if (!root) { 0417 return; 0418 } 0419 // Find old backup data ( older than x months ), or very small (that can be quickly recreated) 0420 size_t total = 0; 0421 QDateTime current = QDateTime::currentDateTime(); 0422 int max = root->childCount(); 0423 for (int i = 0; i < max; i++) { 0424 QTreeWidgetItem *child = root->child(i); 0425 if (emptyDirs.contains(child)) { 0426 continue; 0427 } 0428 size_t childSize = size_t(child->data(1, Qt::UserRole).toLongLong()); 0429 // Check temporary folders with content less than 200kB or old data 0430 if (childSize < 200000 || child->data(2, Qt::UserRole).toDateTime().addMonths(KdenliveSettings::cleanCacheMonths()) < current) { 0431 emptyDirs << child; 0432 total += childSize; 0433 } 0434 } 0435 QStringList folders; 0436 for (auto *item : qAsConst(emptyDirs)) { 0437 folders << item->data(0, Qt::UserRole).toString(); 0438 } 0439 if (folders.isEmpty()) { 0440 KMessageBox::information(this, i18n("No cache data older than %1 months was found.", KdenliveSettings::cleanCacheMonths())); 0441 return; 0442 } 0443 0444 if (KMessageBox::warningContinueCancelList(this, 0445 i18n("This will delete cache data (%1) for projects that were deleted, have very few cached data or older than " 0446 "%2 months. All cached data can be recreated from the source files on project opening.", 0447 KIO::convertSize(total), KdenliveSettings::cleanCacheMonths()), 0448 folders) != KMessageBox::Continue) { 0449 return; 0450 } 0451 deleteCache(folders); 0452 } 0453 0454 void TemporaryData::deleteProjectProxy() 0455 { 0456 if (m_proxies.isEmpty()) { 0457 KMessageBox::information(this, i18n("No proxies found in the current project.")); 0458 return; 0459 } 0460 bool ok = false; 0461 QDir dir = m_doc->getCacheDir(CacheProxy, &ok); 0462 if (!ok || dir.dirName() != QLatin1String("proxy")) { 0463 return; 0464 } 0465 dir.setNameFilters(m_proxies); 0466 QStringList files = dir.entryList(QDir::Files); 0467 if (KMessageBox::warningContinueCancelList(this, 0468 i18n("Delete all project data in the proxy folder:\n%1\nProxy folder contains the proxy clips for all your " 0469 "projects. This proxies can be recreated from the source clips.", 0470 dir.absolutePath()), 0471 files) != KMessageBox::Continue) { 0472 return; 0473 } 0474 for (const QString &file : qAsConst(files)) { 0475 dir.remove(file); 0476 } 0477 Q_EMIT disableProxies(); 0478 updateDataInfo(); 0479 } 0480 0481 void TemporaryData::deleteAudio() 0482 { 0483 bool ok = false; 0484 QDir dir = m_doc->getCacheDir(CacheAudio, &ok); 0485 if (!ok) { 0486 return; 0487 } 0488 if (KMessageBox::warningContinueCancel( 0489 this, i18n("Delete all data in the cache audio folder:\n%1\nThis folder contains the data for audio thumbnails in this project.", 0490 dir.absolutePath())) != KMessageBox::Continue) { 0491 return; 0492 } 0493 if (dir.dirName() == QLatin1String("audiothumbs")) { 0494 dir.removeRecursively(); 0495 dir.mkpath(QStringLiteral(".")); 0496 updateDataInfo(); 0497 } 0498 } 0499 0500 void TemporaryData::deleteThumbs() 0501 { 0502 bool ok = false; 0503 QDir dir = m_doc->getCacheDir(CacheThumbs, &ok); 0504 if (!ok) { 0505 return; 0506 } 0507 if (KMessageBox::warningContinueCancel( 0508 this, i18n("Delete all data in the cache thumbnail folder:\n%1\nThis folder contains the data for video thumbnails in this project.", 0509 dir.absolutePath())) != KMessageBox::Continue) { 0510 return; 0511 } 0512 if (dir.dirName() == QLatin1String("videothumbs")) { 0513 dir.removeRecursively(); 0514 dir.mkpath(QStringLiteral(".")); 0515 updateDataInfo(); 0516 } 0517 } 0518 0519 void TemporaryData::deleteCurrentCacheData(bool warn) 0520 { 0521 bool ok = false; 0522 QDir dir = m_doc->getCacheDir(CacheBase, &ok); 0523 if (!ok) { 0524 return; 0525 } 0526 if (warn && KMessageBox::warningContinueCancel(this, i18n("Delete all data in the cache folder:\n%1\nCache folder contains the audio and video thumbnails, " 0527 "as well as timeline previews. All this data will be recreated on project opening.", 0528 dir.absolutePath())) != KMessageBox::Continue) { 0529 return; 0530 } 0531 if (dir.dirName() == m_doc->getDocumentProperty(QStringLiteral("documentid"))) { 0532 Q_EMIT disablePreview(); 0533 Q_EMIT disableProxies(); 0534 dir.removeRecursively(); 0535 m_doc->initCacheDirs(); 0536 if (warn) { 0537 updateDataInfo(); 0538 } 0539 } 0540 } 0541 0542 void TemporaryData::openCacheFolder() 0543 { 0544 bool ok = false; 0545 QDir dir = m_doc->getCacheDir(CacheBase, &ok); 0546 if (!ok) { 0547 return; 0548 } 0549 QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); 0550 } 0551 0552 void TemporaryData::processBackupDirectories() 0553 { 0554 QDir backupFolder(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/.backup")); 0555 KIO::DirectorySizeJob *job = KIO::directorySize(QUrl::fromLocalFile(backupFolder.absolutePath())); 0556 connect(job, &KIO::DirectorySizeJob::result, this, &TemporaryData::gotBackupSize); 0557 } 0558 0559 void TemporaryData::processProxyDirectory() 0560 { 0561 KIO::DirectorySizeJob *job = KIO::directorySize(QUrl::fromLocalFile(m_globalDir.absoluteFilePath(QStringLiteral("proxy")))); 0562 connect(job, &KIO::DirectorySizeJob::result, this, &TemporaryData::gotProjectProxySize); 0563 } 0564 0565 void TemporaryData::gotProjectProxySize(KJob *job) 0566 { 0567 auto *sourceJob = static_cast<KIO::DirectorySizeJob *>(job); 0568 KIO::filesize_t total = sourceJob->totalSize(); 0569 m_totalProxy = total; 0570 refreshWarningMessage(); 0571 gProxySize->setText(KIO::convertSize(total)); 0572 } 0573 0574 void TemporaryData::gotBackupSize(KJob *job) 0575 { 0576 auto *sourceJob = static_cast<KIO::DirectorySizeJob *>(job); 0577 KIO::filesize_t total = sourceJob->totalSize(); 0578 if (sourceJob->totalFiles() == 0) { 0579 total = 0; 0580 } 0581 m_totalBackup = total; 0582 refreshWarningMessage(); 0583 gBackupSize->setText(KIO::convertSize(total)); 0584 } 0585 0586 void TemporaryData::updateGlobalInfo() 0587 { 0588 listWidget->blockSignals(true); 0589 bool ok = false; 0590 QDir preview = m_doc->getCacheDir(SystemCacheRoot, &ok); 0591 if (!ok) { 0592 globalPage->setEnabled(false); 0593 return; 0594 } 0595 m_globalDir = preview; 0596 m_globalDirectories.clear(); 0597 m_processingDirectory.clear(); 0598 m_totalGlobal = 0; 0599 m_totalProxy = 0; 0600 m_totalBackup = 0; 0601 listWidget->clear(); 0602 m_globalDirectories = m_globalDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); 0603 // These are some KDE cache dirs related to Kdenlive that don't manage ourselves 0604 m_globalDirectories.removeAll(QStringLiteral("knewstuff")); 0605 m_globalDirectories.removeAll(QStringLiteral("attica")); 0606 m_globalDirectories.removeAll(QStringLiteral("proxy")); 0607 gDelete->setEnabled(!m_globalDirectories.isEmpty()); 0608 processProxyDirectory(); 0609 processglobalDirectories(); 0610 listWidget->blockSignals(false); 0611 } 0612 0613 void TemporaryData::processglobalDirectories() 0614 { 0615 if (m_globalDirectories.isEmpty()) { 0616 return; 0617 } 0618 m_processingDirectory = m_globalDirectories.takeFirst(); 0619 KIO::DirectorySizeJob *job = KIO::directorySize(QUrl::fromLocalFile(m_globalDir.absoluteFilePath(m_processingDirectory))); 0620 connect(job, &KIO::DirectorySizeJob::result, this, &TemporaryData::gotFolderSize); 0621 } 0622 0623 void TemporaryData::gotFolderSize(KJob *job) 0624 { 0625 if (m_processingDirectory.isEmpty()) { 0626 return; 0627 } 0628 auto *sourceJob = static_cast<KIO::DirectorySizeJob *>(job); 0629 KIO::filesize_t total = sourceJob->totalSize(); 0630 if (sourceJob->totalFiles() == 0) { 0631 total = 0; 0632 } 0633 m_totalGlobal += total; 0634 auto *item = new TreeWidgetItem(listWidget); 0635 // Check last save path for this cache folder 0636 QDir dir(m_globalDir.absoluteFilePath(m_processingDirectory)); 0637 QStringList filters; 0638 filters << QStringLiteral("*.kdenlive"); 0639 QStringList str = dir.entryList(filters, QDir::Files | QDir::Hidden, QDir::Time); 0640 if (!str.isEmpty()) { 0641 QString path = QUrl::fromPercentEncoding(str.at(0).toUtf8()); 0642 // Remove leading dot 0643 path.remove(0, 1); 0644 item->setText(0, m_processingDirectory + QStringLiteral(" (%1)").arg(QUrl::fromLocalFile(path).fileName())); 0645 if (QFile::exists(path)) { 0646 item->setIcon(0, QIcon::fromTheme(QStringLiteral("kdenlive"))); 0647 } else { 0648 item->setIcon(0, QIcon::fromTheme(QStringLiteral("dialog-close"))); 0649 } 0650 } else { 0651 item->setText(0, m_processingDirectory); 0652 if (m_processingDirectory == QLatin1String("proxy")) { 0653 item->setIcon(0, QIcon::fromTheme(QStringLiteral("kdenlive-show-video"))); 0654 } 0655 } 0656 item->setData(0, Qt::UserRole, m_processingDirectory); 0657 item->setText(1, KIO::convertSize(total)); 0658 QDateTime date = QFileInfo(dir.absolutePath()).lastModified(); 0659 QLocale locale; 0660 item->setText(2, locale.toString(date, QLocale::ShortFormat)); 0661 item->setData(1, Qt::UserRole, total); 0662 item->setData(2, Qt::UserRole, date); 0663 listWidget->addTopLevelItem(item); 0664 listWidget->resizeColumnToContents(0); 0665 listWidget->resizeColumnToContents(1); 0666 if (m_globalDirectories.isEmpty()) { 0667 // Processing done, check total size 0668 refreshWarningMessage(); 0669 gTotalSize->setText(KIO::convertSize(m_totalGlobal)); 0670 listWidget->setCurrentItem(listWidget->topLevelItem(0)); 0671 } else { 0672 processglobalDirectories(); 0673 } 0674 } 0675 0676 void TemporaryData::refreshWarningMessage() 0677 { 0678 if (KdenliveSettings::maxcachesize() > 0 && 0679 ((m_totalGlobal + m_totalBackup + m_totalProxy) > KIO::filesize_t(1048576) * KdenliveSettings::maxcachesize())) { 0680 // Cache larger than x MB, warn 0681 cache_info->animatedShow(); 0682 } else { 0683 cache_info->animatedHide(); 0684 } 0685 } 0686 0687 void TemporaryData::refreshGlobalPie() 0688 { 0689 QList<QTreeWidgetItem *> list = listWidget->selectedItems(); 0690 KIO::filesize_t currentSize = 0; 0691 for (QTreeWidgetItem *current : qAsConst(list)) { 0692 if (current) { 0693 currentSize += current->data(1, Qt::UserRole).toULongLong(); 0694 } 0695 } 0696 gSelectedSize->setText(KIO::convertSize(currentSize)); 0697 int percent = m_totalGlobal <= 0 ? 0 : int(16 * currentSize * 360 / m_totalGlobal); 0698 m_globalPie->setSegments(QList<int>() << 16 * 360 << percent); 0699 if (list.size() == 1 && list.at(0)->text(0) == m_doc->getDocumentProperty(QStringLiteral("documentid"))) { 0700 gDelete->setToolTip(i18n("Clear current cache")); 0701 } else { 0702 gDelete->setToolTip(i18n("Delete selected cache")); 0703 } 0704 } 0705 0706 void TemporaryData::deleteSelected() 0707 { 0708 QList<QTreeWidgetItem *> list = listWidget->selectedItems(); 0709 QStringList folders; 0710 for (QTreeWidgetItem *current : qAsConst(list)) { 0711 if (current) { 0712 folders << current->data(0, Qt::UserRole).toString(); 0713 } 0714 } 0715 if (KMessageBox::warningContinueCancelList(this, 0716 i18n("Delete the following cache folders from\n%1\nCache folders contains the audio and video thumbnails, as " 0717 "well as timeline previews. All this data will be recreated on project opening.", 0718 m_globalDir.absolutePath()), 0719 folders) != KMessageBox::Continue) { 0720 return; 0721 } 0722 deleteCache(folders); 0723 } 0724 0725 void TemporaryData::deleteCache(QStringList &folders) 0726 { 0727 const QString currentId = m_doc->getDocumentProperty(QStringLiteral("documentid")); 0728 for (const QString &folder : qAsConst(folders)) { 0729 if (folder == currentId) { 0730 // Trying to delete current project's tmp folder. Do not delete, but clear it 0731 deleteCurrentCacheData(false); 0732 continue; 0733 } 0734 QDir toRemove(m_globalDir.filePath(folder)); 0735 toRemove.removeRecursively(); 0736 } 0737 updateGlobalInfo(); 0738 } 0739 0740 void TemporaryData::deleteProxy() 0741 { 0742 QDir proxies(m_globalDir.absoluteFilePath(QStringLiteral("proxy"))); 0743 if (proxies.dirName() != QLatin1String("proxy")) { 0744 return; 0745 } 0746 if (KMessageBox::warningContinueCancel(this, i18n("Delete the proxy folder\n%1\nContains proxy clips for all your projects.", proxies.absolutePath())) != 0747 KMessageBox::Continue) { 0748 return; 0749 } 0750 QDir toRemove(m_globalDir.filePath(QStringLiteral("proxy"))); 0751 toRemove.removeRecursively(); 0752 // We deleted proxy folder, recreate it 0753 toRemove.mkpath(QStringLiteral(".")); 0754 processProxyDirectory(); 0755 } 0756 0757 void TemporaryData::cleanProxy() 0758 { 0759 QDir proxies(m_globalDir.absoluteFilePath(QStringLiteral("proxy"))); 0760 if (proxies.dirName() != QLatin1String("proxy")) { 0761 return; 0762 } 0763 QFileInfoList files = proxies.entryInfoList(QDir::Files, QDir::Time); 0764 QStringList oldFiles; 0765 QDateTime current = QDateTime::currentDateTime(); 0766 size_t size = 0; 0767 for (const QFileInfo &f : qAsConst(files)) { 0768 if (f.lastModified().addMonths(KdenliveSettings::cleanCacheMonths()) < current) { 0769 oldFiles << f.fileName(); 0770 size += size_t(f.size()); 0771 } 0772 } 0773 if (oldFiles.isEmpty()) { 0774 KMessageBox::information(this, i18n("No proxy clip older than %1 months found.", KdenliveSettings::cleanCacheMonths())); 0775 return; 0776 } 0777 if (KMessageBox::warningContinueCancelList( 0778 this, i18n("Delete the following proxy clips (%1)\nProxy clips can be recreated on project opening.", KIO::convertSize(size)), oldFiles) != 0779 KMessageBox::Continue) { 0780 return; 0781 } 0782 for (const QString &f : qAsConst(oldFiles)) { 0783 proxies.remove(f); 0784 } 0785 processProxyDirectory(); 0786 } 0787 0788 void TemporaryData::slotCleanUp() 0789 { 0790 cleanCache(); 0791 cleanBackup(); 0792 }