diff --git a/PhotoRenamer.Test/PhotoRenamer.Test.csproj b/PhotoRenamer.Test/PhotoRenamer.Test.csproj index 82a1f42..c2108af 100644 --- a/PhotoRenamer.Test/PhotoRenamer.Test.csproj +++ b/PhotoRenamer.Test/PhotoRenamer.Test.csproj @@ -1,20 +1,15 @@  - net5.0 - + net7.0 false - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/PhotoRenamer/FilesHelper.cs b/PhotoRenamer/FilesHelper.cs index af8f744..aeae8d0 100644 --- a/PhotoRenamer/FilesHelper.cs +++ b/PhotoRenamer/FilesHelper.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; - -namespace PhotoRenamer +namespace PhotoRenamer { public static class FilesHelper { diff --git a/PhotoRenamer/HttpClientExtensions.cs b/PhotoRenamer/HttpClientExtensions.cs new file mode 100644 index 0000000..9742290 --- /dev/null +++ b/PhotoRenamer/HttpClientExtensions.cs @@ -0,0 +1,76 @@ +namespace PhotoRenamer +{ + public static class HttpClientExtensions + { + public static async Task DownloadAsync( + this HttpClient client, + string requestUri, + Stream destination, + IProgress? progress = null, + CancellationToken cancellationToken = default + ) + { + // Get the http headers first to examine the content length + using var response = await client.GetAsync( + requestUri, + HttpCompletionOption.ResponseHeadersRead + ); + var contentLength = response.Content.Headers.ContentLength; + + using var download = await response.Content.ReadAsStreamAsync(cancellationToken); + // Ignore progress reporting when no progress reporter was + // passed or when the content length is unknown + if (progress == null || !contentLength.HasValue) + { + await download.CopyToAsync(destination, cancellationToken); + return; + } + + // Convert absolute progress (bytes downloaded) into relative progress (0% - 100%) + var relativeProgress = new Progress( + totalBytes => progress.Report((float)totalBytes / contentLength.Value) + ); + // Use extension method to report progress while downloading + await download.CopyToAsync(destination, 81920, relativeProgress, cancellationToken); + progress.Report(1); + } + + public static async Task CopyToAsync( + this Stream source, + Stream destination, + int bufferSize, + IProgress? progress = null, + CancellationToken cancellationToken = default + ) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + if (!source.CanRead) + throw new ArgumentException("Has to be readable", nameof(source)); + if (destination == null) + throw new ArgumentNullException(nameof(destination)); + if (!destination.CanWrite) + throw new ArgumentException("Has to be writable", nameof(destination)); + if (bufferSize < 0) + throw new ArgumentOutOfRangeException(nameof(bufferSize)); + + var buffer = new byte[bufferSize]; + long totalBytesRead = 0; + int bytesRead; + while ( + ( + bytesRead = await source + .ReadAsync(buffer, 0, buffer.Length, cancellationToken) + .ConfigureAwait(false) + ) != 0 + ) + { + await destination + .WriteAsync(buffer, 0, bytesRead, cancellationToken) + .ConfigureAwait(false); + totalBytesRead += bytesRead; + progress?.Report(totalBytesRead); + } + } + } +} diff --git a/PhotoRenamer/PhotoRenamer.csproj b/PhotoRenamer/PhotoRenamer.csproj index 800bded..acf406a 100644 --- a/PhotoRenamer/PhotoRenamer.csproj +++ b/PhotoRenamer/PhotoRenamer.csproj @@ -2,20 +2,20 @@ Exe - net5.0 + net7.0 enable + enable - - - - - - - - - + + + + + + + + diff --git a/PhotoRenamer/Program.cs b/PhotoRenamer/Program.cs index fe7d72a..98ae2d0 100644 --- a/PhotoRenamer/Program.cs +++ b/PhotoRenamer/Program.cs @@ -1,6 +1,4 @@ -using System; -using System.IO; -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using Serilog; namespace PhotoRenamer diff --git a/PhotoRenamer/Renamer.cs b/PhotoRenamer/Renamer.cs index 1ebbc2b..44fcd4e 100644 --- a/PhotoRenamer/Renamer.cs +++ b/PhotoRenamer/Renamer.cs @@ -4,13 +4,6 @@ using MetadataExtractor.Formats.QuickTime; using Microsoft.Extensions.Configuration; using Serilog; using ShellProgressBar; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; using Directory = System.IO.Directory; namespace PhotoRenamer @@ -49,27 +42,32 @@ namespace PhotoRenamer var i = 0; using var progressBar = new ProgressBar(files.Length, "Copying files", options); var po = new ParallelOptions { MaxDegreeOfParallelism = 4 }; - Parallel.ForEach(files, po, file => - { - try + Parallel.ForEach( + files, + po, + file => { - var directories = ImageMetadataReader.ReadMetadata(file); - var dateTime = GetDateTimeFromExif(directories) - ?? GetDateTimeFromMp4(directories) - ?? GetDateTimeFromLastWrite(file); - var folder = CreateFolder(dateTime); - CopyFile(folder, file, progressBar); + try + { + var directories = ImageMetadataReader.ReadMetadata(file); + var dateTime = + GetDateTimeFromExif(directories) + ?? GetDateTimeFromMp4(directories) + ?? GetDateTimeFromLastWrite(file); + var folder = CreateFolder(dateTime); + CopyFile(folder, file, progressBar); + } + catch (ImageProcessingException) + { + //silently ignore + } + finally + { + Interlocked.Increment(ref i); + progressBar.Tick(i); + } } - catch (ImageProcessingException) - { - //silently ignore - } - finally - { - Interlocked.Increment(ref i); - progressBar.Tick(i); - } - }); + ); return 0; } @@ -80,24 +78,30 @@ namespace PhotoRenamer return creationTime; } - private static DateTime? GetDateTimeFromExif(IEnumerable directories) + private static DateTime? GetDateTimeFromExif( + IEnumerable directories + ) { DateTime dateTime = default; - return directories - .OfType() - .FirstOrDefault()? - .TryGetDateTime(ExifDirectoryBase.TagDateTime, out dateTime) == true + return + directories + .OfType() + .FirstOrDefault() + ?.TryGetDateTime(ExifDirectoryBase.TagDateTime, out dateTime) == true ? (DateTime?)dateTime : null; } - private static DateTime? GetDateTimeFromMp4(IEnumerable directories) + private static DateTime? GetDateTimeFromMp4( + IEnumerable directories + ) { DateTime dateTime = default; - return directories - .OfType() - .FirstOrDefault()? - .TryGetDateTime(QuickTimeMovieHeaderDirectory.TagCreated, out dateTime) == true + return + directories + .OfType() + .FirstOrDefault() + ?.TryGetDateTime(QuickTimeMovieHeaderDirectory.TagCreated, out dateTime) == true ? (DateTime?)dateTime : null; } @@ -110,26 +114,19 @@ namespace PhotoRenamer destination.CreationTimeUtc = source.CreationTimeUtc; } - private void CopyFile(string folder, string file, ProgressBarBase progressBar) + private async void CopyFile(string folder, string file, ProgressBarBase progressBar) { var destination = new FileInfo(Path.Combine(folder, Path.GetFileName(file))); var source = new FileInfo(file); - if (destination.Exists && destination.Length == source.Length) return; + if (destination.Exists && destination.Length == source.Length) + return; using var child = progressBar.Spawn(100, destination.FullName, _childOptions); - using var client = new WebClient(); + using var client = new HttpClient(); + using var fileStream = File.OpenWrite(destination.FullName); - void OnClientOnDownloadProgressChanged(object o, DownloadProgressChangedEventArgs args) => - child.Tick(args.ProgressPercentage); - - client.DownloadProgressChanged += OnClientOnDownloadProgressChanged; - client.DownloadFileAsync(new Uri(source.FullName), destination.FullName); - while (client.IsBusy) - { - Thread.Sleep(100); - } - - client.DownloadProgressChanged -= OnClientOnDownloadProgressChanged; + var progress = new Progress(o => progressBar.Tick((int)o * 100)); + await client.DownloadAsync(source.FullName, fileStream, progress); ResetTimes(destination, source); child.Tick(100); @@ -137,9 +134,14 @@ namespace PhotoRenamer private string CreateFolder(DateTime dateTime) { - var folder = Path.Combine(_targetPath, dateTime.Year.ToString(), dateTime.Month.ToString("D2")); - if (!Directory.Exists(folder)) Directory.CreateDirectory(folder); + var folder = Path.Combine( + _targetPath, + dateTime.Year.ToString(), + dateTime.Month.ToString("D2") + ); + if (!Directory.Exists(folder)) + Directory.CreateDirectory(folder); return folder; } } -} \ No newline at end of file +} diff --git a/PhotoRenamer/Types/MediaFile.cs b/PhotoRenamer/Types/MediaFile.cs index d6617ff..f3f1a86 100644 --- a/PhotoRenamer/Types/MediaFile.cs +++ b/PhotoRenamer/Types/MediaFile.cs @@ -1,6 +1,4 @@ -using System; - -namespace PhotoRenamer.Types +namespace PhotoRenamer.Types { public class MediaFile {