File indexing completed on 2024-05-05 17:56:56

0001 /*
0002     SPDX-FileCopyrightText: 2017-2022 Krusader Krew <https://krusader.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "sizecalculator.h"
0008 
0009 // QtCore
0010 #include <QDebug>
0011 #include <QTimer>
0012 
0013 #include <KIO/StatJob>
0014 
0015 #include "fileitem.h"
0016 #include "virtualfilesystem.h"
0017 
0018 SizeCalculator::SizeCalculator(const QList<QUrl> &urls)
0019     : QObject(nullptr)
0020     , m_urls(urls)
0021     , m_nextUrls(urls)
0022     , m_totalSize(0)
0023     , m_totalFiles(0)
0024     , m_totalDirs(0)
0025     , m_canceled(false)
0026     , m_directorySizeJob(nullptr)
0027 {
0028     QTimer::singleShot(0, this, &SizeCalculator::start);
0029 }
0030 
0031 SizeCalculator::~SizeCalculator()
0032 {
0033     if (m_directorySizeJob)
0034         m_directorySizeJob->kill();
0035 }
0036 
0037 void SizeCalculator::start()
0038 {
0039     emit started();
0040     nextUrl();
0041 }
0042 
0043 void SizeCalculator::add(const QUrl &url)
0044 {
0045     m_urls.append(url);
0046     m_nextUrls.append(url);
0047     emitProgress();
0048 }
0049 
0050 KIO::filesize_t SizeCalculator::totalSize() const
0051 {
0052     return m_totalSize + (m_directorySizeJob ? m_directorySizeJob->totalSize() : 0);
0053 }
0054 
0055 unsigned long SizeCalculator::totalFiles() const
0056 {
0057     return m_totalFiles + (m_directorySizeJob ? m_directorySizeJob->totalFiles() : 0);
0058 }
0059 
0060 unsigned long SizeCalculator::totalDirs() const
0061 {
0062     return m_totalDirs + (m_directorySizeJob ? m_directorySizeJob->totalSubdirs() : 0);
0063 }
0064 
0065 void SizeCalculator::cancel()
0066 {
0067     m_canceled = true;
0068     if (m_directorySizeJob)
0069         m_directorySizeJob->kill();
0070 
0071     done();
0072 }
0073 
0074 void SizeCalculator::nextUrl()
0075 {
0076     if (m_canceled)
0077         return;
0078 
0079     emitProgress();
0080 
0081     if (!m_currentUrl.isEmpty())
0082         emit calculated(m_currentUrl, m_currentUrlSize);
0083     m_currentUrlSize = 0;
0084 
0085     if (m_nextUrls.isEmpty()) {
0086         done();
0087         return;
0088     }
0089     m_currentUrl = m_nextUrls.takeFirst();
0090 
0091     if (m_currentUrl.scheme() == "virt") {
0092         // calculate size of all files/directories in this virtual directory
0093         auto *fs = new VirtualFileSystem();
0094         if (!fs->scanDir(m_currentUrl)) {
0095             qWarning() << "cannot scan virtual FS, URL=" << m_currentUrl.toDisplayString();
0096             nextUrl();
0097             return;
0098         }
0099         const QList<FileItem *> fileItems = fs->fileItems();
0100         for (FileItem *file : fileItems)
0101             m_nextSubUrls << file->getUrl();
0102         delete fs;
0103     } else {
0104         m_nextSubUrls << m_currentUrl;
0105     }
0106 
0107     nextSubUrl();
0108 }
0109 
0110 void SizeCalculator::nextSubUrl()
0111 {
0112     if (m_canceled)
0113         return;
0114 
0115     if (m_nextSubUrls.isEmpty()) {
0116         nextUrl();
0117         return;
0118     }
0119 
0120     const QUrl url = m_nextSubUrls.takeFirst();
0121     KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo);
0122     connect(statJob, &KIO::Job::result, this, &SizeCalculator::slotStatResult);
0123 }
0124 
0125 void SizeCalculator::slotStatResult(KJob *job)
0126 {
0127     if (m_canceled)
0128         return;
0129 
0130     if (job->error()) {
0131         qWarning() << "stat job failed, error=" << job->error() << "; error string=" << job->errorString();
0132         nextSubUrl();
0133         return;
0134     }
0135 
0136     const KIO::StatJob *statJob = dynamic_cast<KIO::StatJob *>(job);
0137     const QUrl &url = statJob->url();
0138 
0139     const KFileItem kfi(statJob->statResult(), url, true);
0140     if (kfi.isFile() || kfi.isLink()) {
0141         m_totalFiles++;
0142         m_totalSize += kfi.size();
0143         m_currentUrlSize += kfi.size();
0144         nextSubUrl();
0145         return;
0146     }
0147 
0148     // URL should be a directory, we are always counting the directory itself
0149     m_totalDirs++;
0150 
0151     m_directorySizeJob = KIO::directorySize(url);
0152     connect(m_directorySizeJob.data(), &KIO::Job::result, this, &SizeCalculator::slotDirectorySizeResult);
0153 }
0154 
0155 void SizeCalculator::slotDirectorySizeResult(KJob *)
0156 {
0157     if (!m_directorySizeJob->error()) {
0158         m_totalSize += m_directorySizeJob->totalSize();
0159         // do not count filesystem size of empty directories for this current directory
0160         m_currentUrlSize += m_directorySizeJob->totalFiles() == 0 ? 0 : m_directorySizeJob->totalSize();
0161         m_totalFiles += m_directorySizeJob->totalFiles();
0162         m_totalDirs += m_directorySizeJob->totalSubdirs();
0163     }
0164     m_directorySizeJob = nullptr;
0165     nextSubUrl();
0166 }
0167 
0168 void SizeCalculator::done()
0169 {
0170     emit finished(m_canceled);
0171     deleteLater();
0172 }
0173 
0174 void SizeCalculator::emitProgress()
0175 {
0176     emit progressChanged(((m_urls.length() - m_nextUrls.length()) * 100) / m_urls.length());
0177 }