The remote synchronization runner now reports its progress to the UI
This commit is contained in:
@@ -375,7 +375,7 @@ public class RemoteSynchronizationModule : IGenericCallbackModuleWithProgress
|
||||
progressUpdater?.UpdatePhase(OperationPhase.Backup_RemoteSynchronization);
|
||||
|
||||
var config = dest.Config with { Src = m_source! };
|
||||
var exitCode = RemoteSynchronizationRunner.Run(config, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
var exitCode = RemoteSynchronizationRunner.Run(config, CancellationToken.None, progressUpdater).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
|
||||
if (exitCode == 0)
|
||||
RecordSyncOperation(i);
|
||||
|
||||
@@ -23,6 +23,7 @@ using Duplicati.Library.Backend;
|
||||
using Duplicati.Library.Common.IO;
|
||||
using Duplicati.Library.Interface;
|
||||
using Duplicati.Library.Localization.Short;
|
||||
using Duplicati.Library.Main;
|
||||
using Duplicati.Library.Main.Backend;
|
||||
using Duplicati.Library.Logging;
|
||||
using Duplicati.Library.Utility;
|
||||
@@ -135,8 +136,9 @@ namespace RemoteSynchronization
|
||||
/// </summary>
|
||||
/// <param name="config">The parsed configuration for the tool.</param>
|
||||
/// <param name="token">The cancellation token to use for the asynchronous operations.</param>
|
||||
/// <param name="progressUpdater">Optional progress updater for reporting file count and transfer progress to the UI.</param>
|
||||
/// <returns>The return code for the main entry; 0 on success.</returns>
|
||||
public static async Task<int> Run(Config config, CancellationToken token)
|
||||
public static async Task<int> Run(Config config, CancellationToken token, IOperationProgressUpdater? progressUpdater = null)
|
||||
{
|
||||
// Parse the log level
|
||||
var log_level_parsed = Enum.TryParse<Duplicati.Library.Logging.LogMessageType>(config.LogLevel, true, out var log_level_enum);
|
||||
@@ -160,11 +162,11 @@ namespace RemoteSynchronization
|
||||
// Start the logging scope
|
||||
using var _ = Duplicati.Library.Logging.Log.StartScope(multi_sink, log_level_enum);
|
||||
|
||||
return await RunCore(config, token).ConfigureAwait(false);
|
||||
return await RunCore(config, token, progressUpdater).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return await RunCore(config, token).ConfigureAwait(false);
|
||||
return await RunCore(config, token, progressUpdater).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,8 +175,9 @@ namespace RemoteSynchronization
|
||||
/// </summary>
|
||||
/// <param name="config">The parsed configuration for the tool.</param>
|
||||
/// <param name="token">The cancellation token to use for the asynchronous operations.</param>
|
||||
/// <param name="progressUpdater">Optional progress updater for reporting file count and transfer progress to the UI.</param>
|
||||
/// <returns>The return code for the main entry; 0 on success.</returns>
|
||||
private static async Task<int> RunCore(Config config, CancellationToken token)
|
||||
private static async Task<int> RunCore(Config config, CancellationToken token, IOperationProgressUpdater? progressUpdater = null)
|
||||
{
|
||||
// Unpack and parse the multi token options
|
||||
var global_options = ParseOptions(config.GlobalOptions);
|
||||
@@ -257,6 +260,15 @@ namespace RemoteSynchronization
|
||||
}
|
||||
}
|
||||
|
||||
// Report total file count to the progress updater (delete + copy operations)
|
||||
var totalFileCount = to_delete.Count() + to_copy.Count();
|
||||
var totalFileSize = to_copy.Sum(f => f.Size);
|
||||
if (progressUpdater != null)
|
||||
{
|
||||
progressUpdater.UpdatefileCount(totalFileCount, totalFileSize, true);
|
||||
progressUpdater.UpdatefilesProcessed(0, 0);
|
||||
}
|
||||
|
||||
// Delete or rename the files that are not needed
|
||||
long renamed = 0, deleted = 0;
|
||||
if (config.Retention)
|
||||
@@ -272,8 +284,12 @@ namespace RemoteSynchronization
|
||||
"Deleted {0} files from {1}", deleted, b2m.DisplayName);
|
||||
}
|
||||
|
||||
// Update progress after delete/rename phase
|
||||
var deletedOrRenamed = Math.Max(deleted, renamed);
|
||||
progressUpdater?.UpdatefilesProcessed(deletedOrRenamed, 0);
|
||||
|
||||
// Copy the files
|
||||
var (copied, copy_errors) = await CopyAsync(b1m, b2m, to_copy, config, token).ConfigureAwait(false);
|
||||
var (copied, copy_errors) = await CopyAsync(b1m, b2m, to_copy, config, deletedOrRenamed, token, progressUpdater).ConfigureAwait(false);
|
||||
Duplicati.Library.Logging.Log.WriteInformationMessage(LOGTAG, "rsync",
|
||||
"Copied {0} files from {1} to {2}", copied, b1m.DisplayName, b2m.DisplayName);
|
||||
|
||||
@@ -290,7 +306,7 @@ namespace RemoteSynchronization
|
||||
for (int i = 0; i < config.Retry; i++)
|
||||
{
|
||||
await Task.Delay(5000).ConfigureAwait(false); // Wait 5 seconds before retrying
|
||||
(copied, copy_errors) = await CopyAsync(b1m, b2m, copy_errors, config, token).ConfigureAwait(false);
|
||||
(copied, copy_errors) = await CopyAsync(b1m, b2m, copy_errors, config, deletedOrRenamed, token, progressUpdater).ConfigureAwait(false);
|
||||
Duplicati.Library.Logging.Log.WriteInformationMessage(LOGTAG, "rsync",
|
||||
"Copied {0} files from {1} to {2}", copied, b1m.DisplayName, b2m.DisplayName);
|
||||
if (!copy_errors.Any())
|
||||
@@ -345,11 +361,14 @@ namespace RemoteSynchronization
|
||||
/// <param name="b_dst">The destination backend.</param>
|
||||
/// <param name="files">The files that will be copied.</param>
|
||||
/// <param name="config">The parsed configuration for the tool.</param>
|
||||
/// <param name="filesProcessedOffset">The number of files already processed before this copy phase (e.g., from delete/rename), used for progress reporting.</param>
|
||||
/// <param name="token">The cancellation token to use for the asynchronous operations.</param>
|
||||
/// <param name="progressUpdater">Optional progress updater for reporting file-level progress to the UI.</param>
|
||||
/// <returns>A tuple holding the number of succesful copies and a List of the files that failed.</returns>
|
||||
private static async Task<(long, IEnumerable<IFileEntry>)> CopyAsync(LightWeightBackendManager b_src, LightWeightBackendManager b_dst, IEnumerable<IFileEntry> files, Config config, CancellationToken token)
|
||||
private static async Task<(long, IEnumerable<IFileEntry>)> CopyAsync(LightWeightBackendManager b_src, LightWeightBackendManager b_dst, IEnumerable<IFileEntry> files, Config config, long filesProcessedOffset, CancellationToken token, IOperationProgressUpdater? progressUpdater = null)
|
||||
{
|
||||
long successful_copies = 0;
|
||||
long sizeProcessed = 0;
|
||||
List<IFileEntry> errors = [];
|
||||
long i = 0, n = files.Count();
|
||||
var sw_get_src = new System.Diagnostics.Stopwatch();
|
||||
@@ -362,6 +381,9 @@ namespace RemoteSynchronization
|
||||
if (config.Progress)
|
||||
Console.Write($"\rCopying: {i}/{n}");
|
||||
|
||||
// Report current file to the progress updater
|
||||
progressUpdater?.StartFile(f.Name, f.Size);
|
||||
|
||||
Duplicati.Library.Logging.Log.WriteVerboseMessage(LOGTAG, "rsync",
|
||||
"Copying {0} from {1} to {2}", f.Name, b_src.DisplayName, b_dst.DisplayName);
|
||||
|
||||
@@ -373,6 +395,10 @@ namespace RemoteSynchronization
|
||||
await b_src.GetAsync(f.Name, s_src, token).ConfigureAwait(false);
|
||||
s_src.Position = 0;
|
||||
sw_get_src.Stop();
|
||||
|
||||
// Report download progress (half of the file transfer)
|
||||
progressUpdater?.UpdateFileProgress(f.Size / 2);
|
||||
|
||||
if (config.DryRun)
|
||||
{
|
||||
Duplicati.Library.Logging.Log.WriteDryrunMessage(LOGTAG, "rsync",
|
||||
@@ -394,6 +420,9 @@ namespace RemoteSynchronization
|
||||
await b_dst.PutAsync(f.Name, s_src, token).ConfigureAwait(false);
|
||||
sw_put_dst.Stop();
|
||||
|
||||
// Report upload complete (full file transfer done)
|
||||
progressUpdater?.UpdateFileProgress(f.Size);
|
||||
|
||||
if (config.VerifyGetAfterPut)
|
||||
{
|
||||
using var s_dst = Duplicati.Library.Utility.TempFileStream.Create();
|
||||
@@ -427,6 +456,7 @@ namespace RemoteSynchronization
|
||||
}
|
||||
|
||||
successful_copies++;
|
||||
sizeProcessed += f.Size;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -438,6 +468,10 @@ namespace RemoteSynchronization
|
||||
{
|
||||
i++;
|
||||
|
||||
// Report overall progress: files processed so far (offset + current index)
|
||||
progressUpdater?.UpdatefilesProcessed(filesProcessedOffset + i, sizeProcessed);
|
||||
progressUpdater?.UpdateProgress(n > 0 ? (float)i / n : 1.0f);
|
||||
|
||||
// Stop any running timers
|
||||
sw_get_src.Stop();
|
||||
sw_put_dst.Stop();
|
||||
|
||||
Reference in New Issue
Block a user