Skip to content

Commit e93550b

Browse files
committed
feat: enhance progress bar with detailed status display
- Show spinner, progress bar, file count, elapsed/remaining time, and current file - Update total count dynamically when new files discovered with recursive unpacking - Fix negative remaining time calculation - Pass current filename through progress callback for display
1 parent dabdaf3 commit e93550b

3 files changed

Lines changed: 64 additions & 20 deletions

File tree

ReFrontier/Orchestration/ApplicationOrchestrator.cs

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -153,38 +153,49 @@ private ProcessingStatistics ProcessDirectoryWithProgress(string directoryPath,
153153
.AutoClear(false)
154154
.HideCompleted(false)
155155
.Columns(
156-
new TaskDescriptionColumn(),
156+
new SpinnerColumn(),
157157
new ProgressBarColumn(),
158-
new PercentageColumn(),
159-
new ElapsedTimeColumn(),
160-
new RemainingTimeColumn(),
161-
new SpinnerColumn()
158+
new TaskDescriptionColumn(), // Shows "42/100 00:15/01:30 filename.bin"
159+
new PercentageColumn()
162160
)
163161
.Start(ctx =>
164162
{
165-
var task = ctx.AddTask("[green]Scanning...[/]", maxValue: 100);
163+
var task = ctx.AddTask("[dim]Scanning...[/]", maxValue: 100);
166164
task.IsIndeterminate = true;
165+
var startTime = DateTime.Now;
167166

168167
stats = _program.StartProcessingDirectory(directoryPath, processingArgs, (current, total, currentFile) =>
169168
{
170169
if (total > 0)
171170
{
172171
task.IsIndeterminate = false;
173172
task.MaxValue = total;
174-
task.Value = current;
173+
task.Value = Math.Min(current, total); // Clamp to avoid overflow
175174

176-
// Update description with count and current file (truncate if too long)
175+
// Calculate elapsed and remaining time
176+
var elapsed = DateTime.Now - startTime;
177+
var avgPerFile = current > 0 ? elapsed.TotalSeconds / current : 0;
178+
int remainingFiles = Math.Max(0, total - current); // Ensure non-negative
179+
var remaining = TimeSpan.FromSeconds(avgPerFile * remainingFiles);
180+
181+
// Format times as mm:ss
182+
string elapsedStr = $"{(int)elapsed.TotalMinutes:D2}:{elapsed.Seconds:D2}";
183+
string remainingStr = $"{(int)remaining.TotalMinutes:D2}:{remaining.Seconds:D2}";
184+
185+
// Truncate filename if too long
177186
string fileName = Path.GetFileName(currentFile);
178187
if (fileName.Length > 25)
179188
fileName = fileName[..22] + "...";
180-
task.Description = $"[grey]{current}/{total}[/] [green]{fileName}[/]";
189+
190+
task.Description = $"[blue]{current}/{total}[/] [dim]{elapsedStr}/{remainingStr}[/] [green]{fileName}[/]";
181191
}
182192
});
183193

194+
// Final state
195+
var totalElapsed = DateTime.Now - startTime;
196+
string finalElapsed = $"{(int)totalElapsed.TotalMinutes:D2}:{totalElapsed.Seconds:D2}";
184197
if (stats != null)
185-
task.Description = $"[grey]{stats.HandledFiles}/{stats.TotalFiles}[/] [green]Done[/]";
186-
else
187-
task.Description = "[green]Done[/]";
198+
task.Description = $"[blue]{stats.HandledFiles}/{stats.TotalFiles}[/] [dim]{finalElapsed}[/] [green]Done[/]";
188199
task.Value = task.MaxValue;
189200
});
190201
}

ReFrontier/Program.cs

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -397,8 +397,9 @@ public ProcessingStatistics ProcessMultipleLevels(
397397
// Use a concurrent queue to manage files/directories to process
398398
ConcurrentQueue<string> filesToProcess = new(filePathes);
399399

400-
// Track files already processed or queued to prevent duplicates
401-
HashSet<string> processedFiles = new(filePathes, StringComparer.OrdinalIgnoreCase);
400+
// Track files already processed, queued, or created as output to prevent duplicates
401+
// This includes both input files and output files generated during processing
402+
HashSet<string> processedOrOutputFiles = new(filePathes, StringComparer.OrdinalIgnoreCase);
402403
object processedFilesLock = new();
403404

404405
// Consume (process) input files
@@ -417,6 +418,9 @@ public ProcessingStatistics ProcessMultipleLevels(
417418
// to avoid race conditions where files are scanned while still being written
418419
ConcurrentBag<string> outputDirectories = new();
419420

421+
// Collect output files to mark them as processed (prevents re-processing .jkr.bin files)
422+
ConcurrentBag<string> outputFiles = new();
423+
420424
// Use Interlocked to safely disable stage processing after first file
421425
int stageContainerFlag = inputArguments.stageContainer ? 1 : 0;
422426
Parallel.ForEach(currentBatch, parallelOptions, inputFile =>
@@ -439,11 +443,20 @@ public ProcessingStatistics ProcessMultipleLevels(
439443
// Report progress
440444
progressCallback?.Invoke(stats.HandledFiles, stats.TotalFiles, inputFile);
441445

442-
// Collect output directories for deferred processing
443-
// This prevents race conditions where files are scanned while being written
444-
if (inputArguments.recursive && result.OutputPath != null && _fileSystem.DirectoryExists(result.OutputPath))
446+
// Track output for deferred processing
447+
if (result.OutputPath != null)
445448
{
446-
outputDirectories.Add(result.OutputPath);
449+
if (inputArguments.recursive && _fileSystem.DirectoryExists(result.OutputPath))
450+
{
451+
// Output is a directory - scan it later for new files
452+
outputDirectories.Add(result.OutputPath);
453+
}
454+
else if (_fileSystem.FileExists(result.OutputPath))
455+
{
456+
// Output is a file - mark it as processed to prevent re-queuing
457+
// This prevents .jkr.bin files from being picked up by *.bin pattern
458+
outputFiles.Add(result.OutputPath);
459+
}
447460
}
448461
}
449462
catch (ReFrontierException ex)
@@ -455,13 +468,32 @@ public ProcessingStatistics ProcessMultipleLevels(
455468
if (inputArguments.verbose)
456469
_logger.WriteLine($"Skipping {inputFile}: {ex.Message}");
457470
}
471+
catch (IOException ex)
472+
{
473+
stats.IncrementError();
474+
progressCallback?.Invoke(stats.HandledFiles, stats.TotalFiles, inputFile);
475+
476+
// Log file access errors (e.g., file locked by another process)
477+
if (inputArguments.verbose)
478+
_logger.WriteLine($"Skipping {inputFile}: {ex.Message}");
479+
}
458480
});
459481

482+
// Mark all output files as processed to prevent them from being re-queued
483+
// This is critical for preventing .jkr.bin files from being picked up by *.bin pattern
484+
lock (processedFilesLock)
485+
{
486+
foreach (var outputFile in outputFiles)
487+
{
488+
processedOrOutputFiles.Add(outputFile);
489+
}
490+
}
491+
460492
// After all files in batch are processed, scan output directories for new files
461493
// This ensures all files are fully written before being added to the queue
462494
foreach (var outputDir in outputDirectories)
463495
{
464-
int newFileCount = AddNewFilesWithDedup(outputDir, filesToProcess, processedFiles, processedFilesLock);
496+
int newFileCount = AddNewFilesWithDedup(outputDir, filesToProcess, processedOrOutputFiles, processedFilesLock);
465497
stats.AddGeneratedFiles(newFileCount);
466498
}
467499
}

ReFrontier/Services/ProcessingStatistics.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,12 @@ public void IncrementError()
7676
}
7777

7878
/// <summary>
79-
/// Add to generated files count.
79+
/// Add to generated files count and update total.
8080
/// </summary>
8181
public void AddGeneratedFiles(int count)
8282
{
8383
Interlocked.Add(ref _generatedFiles, count);
84+
Interlocked.Add(ref _totalFiles, count);
8485
}
8586
}
8687
}

0 commit comments

Comments
 (0)