From 96bce7c83ab4ddf57fedb8b9b224b188f5b0c3d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20H=C3=A4felfinger?= Date: Sat, 2 Sep 2023 23:44:10 +0200 Subject: [PATCH] makes most classes internal and adds a download command to download original images from piwigo to the local folder (can be used for backup of a gallery) --- PiwigoDirectorySync/AppSettings.cs | 2 +- .../Commands/CommonCommandSettings.cs | 2 +- .../Commands/DownloadImagesCommand.cs | 33 ++++++ .../Commands/DownloadImagesSettings.cs | 5 + PiwigoDirectorySync/Commands/ScanCommand.cs | 2 +- PiwigoDirectorySync/Commands/ScanSettings.cs | 2 +- .../Commands/SyncAlbumsCommand.cs | 5 +- .../Commands/SyncAlbumsSettings.cs | 2 +- .../Commands/SyncFullCommand.cs | 4 +- .../Commands/SyncFullSettings.cs | 2 +- .../Commands/SyncImagesCommand.cs | 6 +- .../Commands/SyncImagesSettings.cs | 2 +- .../DependencyInjectionTypeRegistrar.cs | 2 +- .../Infrastructure/DependencyTypeResolver.cs | 2 +- .../Infrastructure/FilesystemHelpers.cs | 14 +++ .../Infrastructure/IPiwigoClientFactory.cs | 2 +- .../Infrastructure/PiwigoClientFactory.cs | 2 +- .../PiwigoDirectorySync.csproj | 1 + PiwigoDirectorySync/Program.cs | 3 +- .../Properties/launchSettings.json | 6 + .../Services/AlbumSynchronizer.cs | 2 +- PiwigoDirectorySync/Services/FileIndexer.cs | 16 +-- .../Services/FileSystemScanner.cs | 2 +- .../Services/IAlbumSynchronizer.cs | 2 +- PiwigoDirectorySync/Services/IFileIndexer.cs | 2 +- .../Services/IFileSystemScanner.cs | 2 +- .../Services/IImageSynchronizer.cs | 5 +- .../Services/ImageSynchronizer.cs | 104 +++++++++++++++++- PiwigoDirectorySync/Settings.cs | 2 +- 29 files changed, 194 insertions(+), 42 deletions(-) create mode 100644 PiwigoDirectorySync/Commands/DownloadImagesCommand.cs create mode 100644 PiwigoDirectorySync/Commands/DownloadImagesSettings.cs create mode 100644 PiwigoDirectorySync/Infrastructure/FilesystemHelpers.cs diff --git a/PiwigoDirectorySync/AppSettings.cs b/PiwigoDirectorySync/AppSettings.cs index d524c7b..b89f87c 100644 --- a/PiwigoDirectorySync/AppSettings.cs +++ b/PiwigoDirectorySync/AppSettings.cs @@ -2,7 +2,7 @@ using Microsoft.Extensions.Configuration; namespace PiwigoDirectorySync; -public static class AppSettings +internal static class AppSettings { public static readonly IReadOnlySet SupportedExtensions = new HashSet { "jpg", "jpeg", "png" }; diff --git a/PiwigoDirectorySync/Commands/CommonCommandSettings.cs b/PiwigoDirectorySync/Commands/CommonCommandSettings.cs index 71b08f9..f7706cd 100644 --- a/PiwigoDirectorySync/Commands/CommonCommandSettings.cs +++ b/PiwigoDirectorySync/Commands/CommonCommandSettings.cs @@ -6,7 +6,7 @@ using Spectre.Console.Cli; namespace PiwigoDirectorySync.Commands; [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global", Justification = "Done by parser")] -public class CommonCommandSettings : CommandSettings +internal class CommonCommandSettings : CommandSettings { [CommandArgument(0, "[PiwigoServerId]")] [DefaultValue(1)] diff --git a/PiwigoDirectorySync/Commands/DownloadImagesCommand.cs b/PiwigoDirectorySync/Commands/DownloadImagesCommand.cs new file mode 100644 index 0000000..524914c --- /dev/null +++ b/PiwigoDirectorySync/Commands/DownloadImagesCommand.cs @@ -0,0 +1,33 @@ +using System.Diagnostics; +using Microsoft.Extensions.Logging; +using PiwigoDirectorySync.Services; +using Spectre.Console.Cli; + +namespace PiwigoDirectorySync.Commands; + +internal class DownloadImagesCommand : AsyncCommand +{ + private readonly IImageSynchronizer _imageSynchronizer; + private readonly ILogger _logger; + + public DownloadImagesCommand(IImageSynchronizer imageSynchronizer, ILogger logger) + { + _imageSynchronizer = imageSynchronizer; + _logger = logger; + } + + public override async Task ExecuteAsync(CommandContext context, DownloadImagesSettings settings) + { + _logger.LogInformation("Starting image download for piwigo server {SettingsPiwigoServerId}", settings.PiwigoServerId); + var stopWatch = Stopwatch.StartNew(); + + var cancellationTokenSource = new CancellationTokenSource(); + await _imageSynchronizer.DownloadImagesAsync(settings.PiwigoServerId, cancellationTokenSource.Token); + + stopWatch.Stop(); + _logger.LogInformation("Synchronized all images with piwigo server {SettingsPiwigoServerId} in {ElapsedTotalSeconds} seconds", settings.PiwigoServerId, + stopWatch.Elapsed.TotalSeconds); + + return 0; + } +} \ No newline at end of file diff --git a/PiwigoDirectorySync/Commands/DownloadImagesSettings.cs b/PiwigoDirectorySync/Commands/DownloadImagesSettings.cs new file mode 100644 index 0000000..180071a --- /dev/null +++ b/PiwigoDirectorySync/Commands/DownloadImagesSettings.cs @@ -0,0 +1,5 @@ +namespace PiwigoDirectorySync.Commands; + +internal class DownloadImagesSettings : CommonCommandSettings +{ +} \ No newline at end of file diff --git a/PiwigoDirectorySync/Commands/ScanCommand.cs b/PiwigoDirectorySync/Commands/ScanCommand.cs index ada6b84..1db7526 100644 --- a/PiwigoDirectorySync/Commands/ScanCommand.cs +++ b/PiwigoDirectorySync/Commands/ScanCommand.cs @@ -6,7 +6,7 @@ using Spectre.Console.Cli; namespace PiwigoDirectorySync.Commands; -public class ScanCommand : AsyncCommand +internal class ScanCommand : AsyncCommand { private readonly IFileIndexer _fileIndexer; private readonly IFileSystemScanner _fileSystemScanner; diff --git a/PiwigoDirectorySync/Commands/ScanSettings.cs b/PiwigoDirectorySync/Commands/ScanSettings.cs index a3faab2..cd1ff0d 100644 --- a/PiwigoDirectorySync/Commands/ScanSettings.cs +++ b/PiwigoDirectorySync/Commands/ScanSettings.cs @@ -5,7 +5,7 @@ using Spectre.Console.Cli; namespace PiwigoDirectorySync.Commands; [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global")] -public class ScanSettings : CommonCommandSettings +internal class ScanSettings : CommonCommandSettings { [CommandOption("-d|--mark-for-delete")] [DefaultValue(false)] diff --git a/PiwigoDirectorySync/Commands/SyncAlbumsCommand.cs b/PiwigoDirectorySync/Commands/SyncAlbumsCommand.cs index 9a3af91..aba8e01 100644 --- a/PiwigoDirectorySync/Commands/SyncAlbumsCommand.cs +++ b/PiwigoDirectorySync/Commands/SyncAlbumsCommand.cs @@ -5,7 +5,7 @@ using Spectre.Console.Cli; namespace PiwigoDirectorySync.Commands; -public class SyncAlbumsCommand : AsyncCommand +internal class SyncAlbumsCommand : AsyncCommand { private readonly IAlbumSynchronizer _albumSynchronizer; private readonly ILogger _logger; @@ -18,7 +18,8 @@ public class SyncAlbumsCommand : AsyncCommand public override async Task ExecuteAsync(CommandContext context, SyncAlbumsSettings settings) { - _logger.LogInformation("Starting album synchronization"); + _logger.LogInformation("Starting album synchronization for piwigo server {SettingsPiwigoServerId}", settings.PiwigoServerId); + var stopWatch = Stopwatch.StartNew(); var cancellationTokenSource = new CancellationTokenSource(); diff --git a/PiwigoDirectorySync/Commands/SyncAlbumsSettings.cs b/PiwigoDirectorySync/Commands/SyncAlbumsSettings.cs index 6f4c8b6..9fe9b46 100644 --- a/PiwigoDirectorySync/Commands/SyncAlbumsSettings.cs +++ b/PiwigoDirectorySync/Commands/SyncAlbumsSettings.cs @@ -3,6 +3,6 @@ using System.Diagnostics.CodeAnalysis; namespace PiwigoDirectorySync.Commands; [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global")] -public class SyncAlbumsSettings : CommonCommandSettings +internal class SyncAlbumsSettings : CommonCommandSettings { } \ No newline at end of file diff --git a/PiwigoDirectorySync/Commands/SyncFullCommand.cs b/PiwigoDirectorySync/Commands/SyncFullCommand.cs index f34e8d2..869da77 100644 --- a/PiwigoDirectorySync/Commands/SyncFullCommand.cs +++ b/PiwigoDirectorySync/Commands/SyncFullCommand.cs @@ -5,7 +5,7 @@ using Spectre.Console.Cli; namespace PiwigoDirectorySync.Commands; -public class SyncFullCommand : AsyncCommand +internal class SyncFullCommand : AsyncCommand { private readonly IAlbumSynchronizer _albumSynchronizer; private readonly IFileIndexer _fileIndexer; @@ -37,7 +37,7 @@ public class SyncFullCommand : AsyncCommand await _albumSynchronizer.SynchronizeAlbums(settings.PiwigoServerId, cancellationTokenSource.Token); _logger.LogInformation("running image synchronization"); - await _imageSynchronizer.SynchronizeImages(settings.PiwigoServerId, cancellationTokenSource.Token); + await _imageSynchronizer.SynchronizeImagesAsync(settings.PiwigoServerId, cancellationTokenSource.Token); stopWatch.Stop(); _logger.LogInformation("Full synchronization for piwigo server {SettingsPiwigoServerId} finished in {ElapsedTotalSeconds} seconds", settings.PiwigoServerId, diff --git a/PiwigoDirectorySync/Commands/SyncFullSettings.cs b/PiwigoDirectorySync/Commands/SyncFullSettings.cs index 67f573d..ce35e9b 100644 --- a/PiwigoDirectorySync/Commands/SyncFullSettings.cs +++ b/PiwigoDirectorySync/Commands/SyncFullSettings.cs @@ -1,3 +1,3 @@ namespace PiwigoDirectorySync.Commands; -public class SyncFullSettings : CommonCommandSettings {} \ No newline at end of file +internal class SyncFullSettings : CommonCommandSettings {} \ No newline at end of file diff --git a/PiwigoDirectorySync/Commands/SyncImagesCommand.cs b/PiwigoDirectorySync/Commands/SyncImagesCommand.cs index c70dfe7..1bf4a8a 100644 --- a/PiwigoDirectorySync/Commands/SyncImagesCommand.cs +++ b/PiwigoDirectorySync/Commands/SyncImagesCommand.cs @@ -5,7 +5,7 @@ using Spectre.Console.Cli; namespace PiwigoDirectorySync.Commands; -public class SyncImagesCommand : AsyncCommand +internal class SyncImagesCommand : AsyncCommand { private readonly IImageSynchronizer _imageSynchronizer; private readonly ILogger _logger; @@ -18,11 +18,11 @@ public class SyncImagesCommand : AsyncCommand public override async Task ExecuteAsync(CommandContext context, SyncImagesSettings settings) { - _logger.LogInformation("Starting image synchronization"); + _logger.LogInformation("Starting image synchronization of piwigo server {SettingsPiwigoServerId}", settings.PiwigoServerId); var stopWatch = Stopwatch.StartNew(); var cancellationTokenSource = new CancellationTokenSource(); - await _imageSynchronizer.SynchronizeImages(settings.PiwigoServerId, cancellationTokenSource.Token); + await _imageSynchronizer.SynchronizeImagesAsync(settings.PiwigoServerId, cancellationTokenSource.Token); stopWatch.Stop(); _logger.LogInformation("Synchronized all images with piwigo server {SettingsPiwigoServerId} in {ElapsedTotalSeconds} seconds", settings.PiwigoServerId, diff --git a/PiwigoDirectorySync/Commands/SyncImagesSettings.cs b/PiwigoDirectorySync/Commands/SyncImagesSettings.cs index dd2ebac..d84cb6f 100644 --- a/PiwigoDirectorySync/Commands/SyncImagesSettings.cs +++ b/PiwigoDirectorySync/Commands/SyncImagesSettings.cs @@ -1,5 +1,5 @@ namespace PiwigoDirectorySync.Commands; -public class SyncImagesSettings : CommonCommandSettings +internal class SyncImagesSettings : CommonCommandSettings { } \ No newline at end of file diff --git a/PiwigoDirectorySync/Infrastructure/DependencyInjectionTypeRegistrar.cs b/PiwigoDirectorySync/Infrastructure/DependencyInjectionTypeRegistrar.cs index 0bc8418..f251106 100644 --- a/PiwigoDirectorySync/Infrastructure/DependencyInjectionTypeRegistrar.cs +++ b/PiwigoDirectorySync/Infrastructure/DependencyInjectionTypeRegistrar.cs @@ -3,7 +3,7 @@ using Spectre.Console.Cli; namespace PiwigoDirectorySync.Infrastructure; -public sealed class DependencyInjectionTypeRegistrar : ITypeRegistrar +internal sealed class DependencyInjectionTypeRegistrar : ITypeRegistrar { private readonly IServiceCollection _builder; diff --git a/PiwigoDirectorySync/Infrastructure/DependencyTypeResolver.cs b/PiwigoDirectorySync/Infrastructure/DependencyTypeResolver.cs index 95f219b..f424256 100644 --- a/PiwigoDirectorySync/Infrastructure/DependencyTypeResolver.cs +++ b/PiwigoDirectorySync/Infrastructure/DependencyTypeResolver.cs @@ -2,7 +2,7 @@ namespace PiwigoDirectorySync.Infrastructure; -public sealed class DependencyTypeResolver : ITypeResolver, IDisposable +internal sealed class DependencyTypeResolver : ITypeResolver, IDisposable { private readonly IServiceProvider _provider; diff --git a/PiwigoDirectorySync/Infrastructure/FilesystemHelpers.cs b/PiwigoDirectorySync/Infrastructure/FilesystemHelpers.cs new file mode 100644 index 0000000..d477382 --- /dev/null +++ b/PiwigoDirectorySync/Infrastructure/FilesystemHelpers.cs @@ -0,0 +1,14 @@ +using System.Security.Cryptography; + +namespace PiwigoDirectorySync.Infrastructure; + +internal static class FilesystemHelpers +{ + public static async Task CalculateMd5SumAsync(string imageFileFullPath, CancellationToken stoppingToken) + { + using var md5 = MD5.Create(); + await using var stream = File.OpenRead(imageFileFullPath); + var hash = await md5.ComputeHashAsync(stream, stoppingToken); + return BitConverter.ToString(hash).Replace("-", string.Empty).ToLowerInvariant(); + } +} \ No newline at end of file diff --git a/PiwigoDirectorySync/Infrastructure/IPiwigoClientFactory.cs b/PiwigoDirectorySync/Infrastructure/IPiwigoClientFactory.cs index 9b7aeee..f6dee90 100644 --- a/PiwigoDirectorySync/Infrastructure/IPiwigoClientFactory.cs +++ b/PiwigoDirectorySync/Infrastructure/IPiwigoClientFactory.cs @@ -3,7 +3,7 @@ using PiwigoDirectorySync.Persistence; namespace PiwigoDirectorySync.Infrastructure; -public interface IPiwigoClientFactory +internal interface IPiwigoClientFactory { Task GetPiwigoClientAsync(ServerEntity piwigoServer, CancellationToken ct); } \ No newline at end of file diff --git a/PiwigoDirectorySync/Infrastructure/PiwigoClientFactory.cs b/PiwigoDirectorySync/Infrastructure/PiwigoClientFactory.cs index f5b440b..d3628f5 100644 --- a/PiwigoDirectorySync/Infrastructure/PiwigoClientFactory.cs +++ b/PiwigoDirectorySync/Infrastructure/PiwigoClientFactory.cs @@ -4,7 +4,7 @@ using PiwigoDirectorySync.Persistence; namespace PiwigoDirectorySync.Infrastructure; -public class PiwigoClientFactory : IPiwigoClientFactory +internal class PiwigoClientFactory : IPiwigoClientFactory { private readonly ILoggerFactory _loggerFactory; diff --git a/PiwigoDirectorySync/PiwigoDirectorySync.csproj b/PiwigoDirectorySync/PiwigoDirectorySync.csproj index 8c997ec..f866c7c 100644 --- a/PiwigoDirectorySync/PiwigoDirectorySync.csproj +++ b/PiwigoDirectorySync/PiwigoDirectorySync.csproj @@ -12,6 +12,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/PiwigoDirectorySync/Program.cs b/PiwigoDirectorySync/Program.cs index b1ab1a0..3dcf192 100644 --- a/PiwigoDirectorySync/Program.cs +++ b/PiwigoDirectorySync/Program.cs @@ -45,11 +45,12 @@ app.Configure(config => #endif config.AddCommand("scan"); + config.AddCommand("download"); config.AddBranch("sync", c => { + c.AddCommand("full"); c.AddCommand("albums"); c.AddCommand("images"); - c.AddCommand("full"); }); }); diff --git a/PiwigoDirectorySync/Properties/launchSettings.json b/PiwigoDirectorySync/Properties/launchSettings.json index 07f461b..97cb2ef 100644 --- a/PiwigoDirectorySync/Properties/launchSettings.json +++ b/PiwigoDirectorySync/Properties/launchSettings.json @@ -30,6 +30,12 @@ "commandLineArgs": "sync full", "environmentVariables": { } + }, + "Download": { + "commandName": "Project", + "commandLineArgs": "download", + "environmentVariables": { + } } } } diff --git a/PiwigoDirectorySync/Services/AlbumSynchronizer.cs b/PiwigoDirectorySync/Services/AlbumSynchronizer.cs index 03c3a43..92f0412 100644 --- a/PiwigoDirectorySync/Services/AlbumSynchronizer.cs +++ b/PiwigoDirectorySync/Services/AlbumSynchronizer.cs @@ -7,7 +7,7 @@ using PiwigoDirectorySync.Persistence; namespace PiwigoDirectorySync.Services; -public class AlbumSynchronizer : IAlbumSynchronizer +internal class AlbumSynchronizer : IAlbumSynchronizer { private readonly ILogger _logger; private readonly PersistenceContext _persistenceContext; diff --git a/PiwigoDirectorySync/Services/FileIndexer.cs b/PiwigoDirectorySync/Services/FileIndexer.cs index 7b76cd1..26a71c0 100644 --- a/PiwigoDirectorySync/Services/FileIndexer.cs +++ b/PiwigoDirectorySync/Services/FileIndexer.cs @@ -1,12 +1,12 @@ -using System.Security.Cryptography; -using System.Threading.Channels; +using System.Threading.Channels; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using PiwigoDirectorySync.Infrastructure; using PiwigoDirectorySync.Persistence; namespace PiwigoDirectorySync.Services; -public class FileIndexer : IFileIndexer +internal class FileIndexer : IFileIndexer { private readonly IList _failedFiles = new List(); private readonly ILogger _logger; @@ -54,7 +54,7 @@ public class FileIndexer : IFileIndexer if (image.LastChange != fileInfo.LastWriteTimeUtc) { image.UploadRequired = true; - image.Md5Sum = await CalculateMd5SumAsync(fullFilePath, ct); + image.Md5Sum = await FilesystemHelpers.CalculateMd5SumAsync(fullFilePath, ct); } image.DeleteRequired = false; @@ -125,12 +125,4 @@ public class FileIndexer : IFileIndexer return album; } - - private static async Task CalculateMd5SumAsync(string imageFileFullPath, CancellationToken stoppingToken) - { - using var md5 = MD5.Create(); - await using var stream = File.OpenRead(imageFileFullPath); - var hash = await md5.ComputeHashAsync(stream, stoppingToken); - return BitConverter.ToString(hash).Replace("-", string.Empty).ToLowerInvariant(); - } } \ No newline at end of file diff --git a/PiwigoDirectorySync/Services/FileSystemScanner.cs b/PiwigoDirectorySync/Services/FileSystemScanner.cs index 665a232..a7e235b 100644 --- a/PiwigoDirectorySync/Services/FileSystemScanner.cs +++ b/PiwigoDirectorySync/Services/FileSystemScanner.cs @@ -4,7 +4,7 @@ using PiwigoDirectorySync.Persistence; namespace PiwigoDirectorySync.Services; -public class FileSystemScanner : IFileSystemScanner +internal class FileSystemScanner : IFileSystemScanner { private readonly ILogger _logger; private readonly PersistenceContext _persistenceContext; diff --git a/PiwigoDirectorySync/Services/IAlbumSynchronizer.cs b/PiwigoDirectorySync/Services/IAlbumSynchronizer.cs index 83d65e0..89ab6c2 100644 --- a/PiwigoDirectorySync/Services/IAlbumSynchronizer.cs +++ b/PiwigoDirectorySync/Services/IAlbumSynchronizer.cs @@ -1,6 +1,6 @@ namespace PiwigoDirectorySync.Services; -public interface IAlbumSynchronizer +internal interface IAlbumSynchronizer { Task SynchronizeAlbums(int piwigoServerId, CancellationToken ct); } \ No newline at end of file diff --git a/PiwigoDirectorySync/Services/IFileIndexer.cs b/PiwigoDirectorySync/Services/IFileIndexer.cs index 553d1a1..4af97c9 100644 --- a/PiwigoDirectorySync/Services/IFileIndexer.cs +++ b/PiwigoDirectorySync/Services/IFileIndexer.cs @@ -2,7 +2,7 @@ using System.Threading.Channels; namespace PiwigoDirectorySync.Services; -public interface IFileIndexer +internal interface IFileIndexer { int TotalFilesScanned { get; } IReadOnlyCollection FailedFiles { get; } diff --git a/PiwigoDirectorySync/Services/IFileSystemScanner.cs b/PiwigoDirectorySync/Services/IFileSystemScanner.cs index 7d16d1a..b943833 100644 --- a/PiwigoDirectorySync/Services/IFileSystemScanner.cs +++ b/PiwigoDirectorySync/Services/IFileSystemScanner.cs @@ -2,7 +2,7 @@ using System.Threading.Channels; namespace PiwigoDirectorySync.Services; -public interface IFileSystemScanner +internal interface IFileSystemScanner { Task ScanAsync(Channel fileQueue, int piwigoServerId, CancellationToken ct); } \ No newline at end of file diff --git a/PiwigoDirectorySync/Services/IImageSynchronizer.cs b/PiwigoDirectorySync/Services/IImageSynchronizer.cs index 31c63ec..3b8e0b9 100644 --- a/PiwigoDirectorySync/Services/IImageSynchronizer.cs +++ b/PiwigoDirectorySync/Services/IImageSynchronizer.cs @@ -1,6 +1,7 @@ namespace PiwigoDirectorySync.Services; -public interface IImageSynchronizer +internal interface IImageSynchronizer { - Task SynchronizeImages(int piwigoServerId, CancellationToken ct); + Task SynchronizeImagesAsync(int piwigoServerId, CancellationToken ct); + Task DownloadImagesAsync(int piwigoServerId, CancellationToken ct); } \ No newline at end of file diff --git a/PiwigoDirectorySync/Services/ImageSynchronizer.cs b/PiwigoDirectorySync/Services/ImageSynchronizer.cs index 68c5365..046661f 100644 --- a/PiwigoDirectorySync/Services/ImageSynchronizer.cs +++ b/PiwigoDirectorySync/Services/ImageSynchronizer.cs @@ -1,13 +1,15 @@ -using Microsoft.EntityFrameworkCore; +using Flurl.Http; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Piwigo.Client; +using Piwigo.Client.Albums; using Piwigo.Client.Images; using PiwigoDirectorySync.Infrastructure; using PiwigoDirectorySync.Persistence; namespace PiwigoDirectorySync.Services; -public class ImageSynchronizer : IImageSynchronizer +internal class ImageSynchronizer : IImageSynchronizer { private readonly ILogger _logger; private readonly PersistenceContext _persistenceContext; @@ -20,7 +22,34 @@ public class ImageSynchronizer : IImageSynchronizer _persistenceContext = persistenceContext; } - public async Task SynchronizeImages(int piwigoServerId, CancellationToken ct) + public async Task DownloadImagesAsync(int piwigoServerId, CancellationToken ct) + { + var piwigoServer = await _persistenceContext.PiwigoServers.FindAsync(new object?[] { piwigoServerId }, ct); + if (piwigoServer is null) + { + _logger.LogError("Could not sync images with piwigo server {PiwigoServerId}", piwigoServerId); + return; + } + + _logger.LogInformation("Downloading missing images of piwigo server {PiwigoServerName} using base path {PiwigoServerRootDirectory}", piwigoServer.Name, + piwigoServer.RootDirectory); + + var piwigoClient = await _piwigoClientFactory.GetPiwigoClientAsync(piwigoServer, ct); + + var albumIdsToDownload = await _persistenceContext.PiwigoAlbums.Where(a => a.ServerAlbumId.HasValue).Select(a => a.ServerAlbumId!.Value).Distinct().ToListAsync(ct); + + foreach (var albumId in albumIdsToDownload) + { + var albumInfos = await piwigoClient.Album.GetListAsync(albumId, false, false, ThumbnailSize.Thumb, ct); + var albumInfo = albumInfos.First(); + + _logger.LogInformation("Starting downloads for album {AlbumInfoName}", albumInfo.Name); + + await DownloadImagesForAlbumAsync(piwigoClient, piwigoServer, albumId, albumInfo, ct); + } + } + + public async Task SynchronizeImagesAsync(int piwigoServerId, CancellationToken ct) { var piwigoServer = await _persistenceContext.PiwigoServers.FindAsync(new object?[] { piwigoServerId }, ct); if (piwigoServer is null) @@ -39,6 +68,75 @@ public class ImageSynchronizer : IImageSynchronizer await UploadChangedImagesToServerAsync(piwigoClient, piwigoServer, ct); } + private async Task DownloadImagesForAlbumAsync(IPiwigoClient piwigoClient, ServerEntity piwigoServer, int albumId, Album albumInfo, CancellationToken ct) + { + if (albumInfo.NbImages is null or <= 0) + { + _logger.LogInformation("No images to download for empty album {AlbumId} / {AlbumInfoName}", albumId, albumInfo.Name); + return; + } + + const int pageSize = 100; + var numberOfPages = albumInfo.NbImages.Value / pageSize + 1; + var currentPage = 0; + + while (currentPage < numberOfPages) + { + var imagePagingInfo = new ImagePagingInfo(currentPage, pageSize, albumInfo.NbImages.Value); + var images = await piwigoClient.Image.GetImagesAsync(albumId, false, imagePagingInfo, ImageFilter.Empty, ImageOrder.Name, ct); + + foreach (var image in images.Images) + { + await DownloadImageAsync(piwigoServer, albumInfo, image, ct); + } + + currentPage++; + } + } + + private async Task DownloadImageAsync(ServerEntity piwigoServer, Album albumInfo, Image image, CancellationToken ct) + { + var localAlbum = await _persistenceContext.PiwigoAlbums.FindByServerIdAsync(piwigoServer.Id, albumInfo.Id, ct); + if (localAlbum is null) + { + _logger.LogWarning("Could not add image {ImageId} / {ImageFile}: album with server id {AlbumInfoId} / {AlbumInfoName} not found", image.Id, image.File, albumInfo.Id, + albumInfo.Name); + return; + } + + var localImage = await GetOrAddImageFromServerAsync(localAlbum, image, ct); + + var fileInfo = new FileInfo(Path.Combine(piwigoServer.RootDirectory, localImage.FilePath)); + if (fileInfo.Exists) + { + _logger.LogWarning("Tried to download image {ImageFile} but it already exists", image.File); + return; + } + + await image.ElementUrl.DownloadFileAsync(fileInfo.Directory!.FullName, fileInfo.Name, cancellationToken: ct); + localImage.Md5Sum = await FilesystemHelpers.CalculateMd5SumAsync(fileInfo.FullName, ct); + await _persistenceContext.SaveChangesAsync(ct); + } + + private async Task GetOrAddImageFromServerAsync(AlbumEntity album, Image image, CancellationToken ct) + { + var imageEntity = await _persistenceContext.PiwigoImages.Where(i => i.AlbumId == album.Id && i.ServerImageId == image.Id).FirstOrDefaultAsync(ct); + if (imageEntity is null) + { + imageEntity = new ImageEntity + { + AlbumId = album.Id, + Album = album, + FilePath = Path.Combine(album.Path, image.File!), + UploadRequired = false, + DeleteRequired = false + }; + _persistenceContext.PiwigoImages.Add(imageEntity); + } + + return imageEntity; + } + private async Task UploadChangedImagesToServerAsync(IPiwigoClient piwigoClient, ServerEntity piwigoServer, CancellationToken ct) { var imagesToUpload = await _persistenceContext.PiwigoImages.Include(i => i.Album) diff --git a/PiwigoDirectorySync/Settings.cs b/PiwigoDirectorySync/Settings.cs index 2134014..5b2b827 100644 --- a/PiwigoDirectorySync/Settings.cs +++ b/PiwigoDirectorySync/Settings.cs @@ -1,6 +1,6 @@ namespace PiwigoDirectorySync; -public class Settings +internal class Settings { public string DbProvider { get; set; } = null!; public string ImageRootDirectory { get; set; } = null!;