6#include <QtCore/QFileInfo>
18 _fileWatcher->setDebounceDelay(500);
21 this, &QGCArchiveWatcher::_onDirectoryChanged);
24QGCArchiveWatcher::~QGCArchiveWatcher()
26 if (_extractionJob !=
nullptr && _extractionJob->isRunning()) {
27 _cancelPending =
true;
36void QGCArchiveWatcher::setFilterMode(FilterMode mode)
41void QGCArchiveWatcher::setAutoDecompress(
bool enable)
43 if (_autoDecompress != enable) {
44 _autoDecompress = enable;
49void QGCArchiveWatcher::setOutputDirectory(
const QString &directory)
51 if (_outputDirectory != directory) {
52 _outputDirectory = directory;
57void QGCArchiveWatcher::setRemoveAfterExtraction(
bool remove)
59 _removeAfterExtraction = remove;
62void QGCArchiveWatcher::setDebounceDelay(
int milliseconds)
67int QGCArchiveWatcher::debounceDelay()
const
76bool QGCArchiveWatcher::watchDirectory(
const QString &directoryPath)
78 if (directoryPath.isEmpty()) {
79 qCWarning(QGCArchiveWatcherLog) <<
"watchDirectory: empty path";
83 const QString canonicalPath = QFileInfo(directoryPath).absoluteFilePath();
85 if (!QFileInfo(canonicalPath).isDir()) {
86 qCWarning(QGCArchiveWatcherLog) <<
"watchDirectory: not a directory:" << directoryPath;
91 if (!_knownFiles.contains(canonicalPath)) {
92 QDir dir(canonicalPath);
93 const QStringList entries = dir.entryList(QDir::Files);
94 QSet<QString> fileSet;
95 for (
const QString &entry : entries) {
96 fileSet.insert(dir.absoluteFilePath(entry));
98 _knownFiles[canonicalPath] = fileSet;
99 qCDebug(QGCArchiveWatcherLog) <<
"Initialized" << fileSet.size() <<
"known files in" << canonicalPath;
103 qCDebug(QGCArchiveWatcherLog) <<
"Watching directory for archives:" << canonicalPath;
110bool QGCArchiveWatcher::unwatchDirectory(
const QString &directoryPath)
112 const QString canonicalPath = QFileInfo(directoryPath).absoluteFilePath();
113 _knownFiles.remove(canonicalPath);
117QStringList QGCArchiveWatcher::watchedDirectories()
const
122void QGCArchiveWatcher::clear()
124 _fileWatcher->
clear();
126 _pendingExtractions.clear();
127 _currentArchive.clear();
128 _setExtracting(
false);
131 if (_extractionJob) {
132 if (_extractionJob->isRunning()) {
133 _cancelPending =
true;
143QStringList QGCArchiveWatcher::scanDirectory(
const QString &directoryPath)
const
145 QStringList archives;
147 QDir dir(directoryPath);
152 const QStringList entries = dir.entryList(QDir::Files);
153 for (
const QString &entry : entries) {
154 const QString fullPath = dir.absoluteFilePath(entry);
155 if (_isWatchedFormat(fullPath)) {
156 archives.append(fullPath);
163void QGCArchiveWatcher::cancelExtraction()
165 if (_extractionJob !=
nullptr && _extractionJob->isRunning()) {
166 qCDebug(QGCArchiveWatcherLog) <<
"Cancelling extraction";
167 _cancelPending =
true;
170 _pendingExtractions.clear();
171 _currentArchive.clear();
172 _setExtracting(
false);
180void QGCArchiveWatcher::_onDirectoryChanged(
const QString &path)
182 qCDebug(QGCArchiveWatcherLog) <<
"Directory changed:" << path;
190 const QStringList currentEntries = dir.entryList(QDir::Files);
191 QSet<QString> currentFiles;
192 for (
const QString &entry : currentEntries) {
193 currentFiles.insert(dir.absoluteFilePath(entry));
197 QSet<QString> &knownFiles = _knownFiles[path];
198 const QSet<QString> newFiles = currentFiles - knownFiles;
201 knownFiles = currentFiles;
204 for (
const QString &filePath : newFiles) {
205 _processNewFile(filePath);
209void QGCArchiveWatcher::_onExtractionProgress(qreal progress)
211 _setProgress(progress);
214void QGCArchiveWatcher::_onExtractionFinished(
bool success)
216 if (_cancelPending) {
217 _cancelPending =
false;
218 _currentArchive.clear();
219 _setExtracting(
false);
224 qCDebug(QGCArchiveWatcherLog) <<
"Extraction finished:" << _currentArchive
225 <<
"success:" << success;
227 QString outputPath = _extractionJob->outputPath();
231 if (success && _removeAfterExtraction) {
232 if (QFile::remove(_currentArchive)) {
233 qCDebug(QGCArchiveWatcherLog) <<
"Removed source archive:" << _currentArchive;
235 qCWarning(QGCArchiveWatcherLog) <<
"Failed to remove source archive:" << _currentArchive;
241 _currentArchive.clear();
242 _setExtracting(
false);
246 if (!_pendingExtractions.isEmpty()) {
247 const QString next = _pendingExtractions.takeFirst();
248 _startExtraction(next);
256bool QGCArchiveWatcher::_isWatchedFormat(
const QString &filePath)
const
260 switch (_filterMode) {
261 case FilterMode::Archives:
263 case FilterMode::Compressed:
265 case FilterMode::Both:
273void QGCArchiveWatcher::_processNewFile(
const QString &filePath)
275 if (!_isWatchedFormat(filePath)) {
281 qCDebug(QGCArchiveWatcherLog) <<
"Detected archive:" << filePath
286 if (_autoDecompress) {
289 if (!_pendingExtractions.contains(filePath)) {
290 _pendingExtractions.append(filePath);
291 qCDebug(QGCArchiveWatcherLog) <<
"Queued for extraction:" << filePath;
294 _startExtraction(filePath);
299void QGCArchiveWatcher::_startExtraction(
const QString &archivePath)
301 if (!QFileInfo::exists(archivePath)) {
302 qCWarning(QGCArchiveWatcherLog) <<
"Archive no longer exists:" << archivePath;
307 QString outputPath = _outputDirectory;
308 if (outputPath.isEmpty()) {
309 outputPath = QFileInfo(archivePath).absolutePath();
317 qCDebug(QGCArchiveWatcherLog) <<
"Starting extraction:" << archivePath
318 <<
"to" << outputPath
319 << (isArchive ?
"(archive)" :
"(compressed file)");
321 _currentArchive = archivePath;
322 _cancelPending =
false;
323 _setExtracting(
true);
327 if (_extractionJob ==
nullptr) {
330 this, &QGCArchiveWatcher::_onExtractionProgress);
332 this, &QGCArchiveWatcher::_onExtractionFinished);
340 const QString decompressedPath = outputPath +
"/" + strippedName;
345void QGCArchiveWatcher::_setExtracting(
bool extracting)
347 if (_extracting != extracting) {
348 _extracting = extracting;
353void QGCArchiveWatcher::_setProgress(qreal progress)
355 if (!qFuzzyCompare(_progress, progress)) {
356 _progress = progress;
Watches directories for archive files with optional auto-decompression.
QObject wrapper for async compression operations using QtConcurrent/QPromise.
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void autoDecompressChanged(bool autoDecompress)
Property change signals.
void progressChanged(qreal progress)
void extractionComplete(const QString &archivePath, const QString &outputPath, bool success, const QString &errorString)
void outputDirectoryChanged(const QString &directory)
void extractingChanged(bool extracting)
void archiveDetected(const QString &archivePath, QGCCompression::Format format)
void progressChanged(qreal progress)
Emitted when progress changes (0.0 to 1.0)
void cancel()
Cancel current operation.
void finished(bool success)
void decompressFile(const QString &inputPath, const QString &outputPath=QString(), qint64 maxBytes=0)
void extractArchive(const QString &archivePath, const QString &outputDirectoryPath, qint64 maxBytes=0)
bool unwatchDirectory(const QString &directoryPath)
void clear()
Stop watching all files and directories.
void setDebounceDelay(int milliseconds)
void directoryChanged(const QString &path)
int debounceDelay() const
QStringList watchedDirectories() const
bool watchDirectory(const QString &directoryPath, ChangeCallback callback)
bool isCompressionFormat(Format format)
Check if format is a compression format (single stream)
QString formatName(Format format)
Get human-readable name for a format.
bool isArchiveFormat(Format format)
Check if format is an archive (contains multiple files)
QString strippedPath(const QString &filePath)
Format
Archive and compression format types (for decompression)
Format detectFormat(const QString &filePath, bool useContentFallback)