Works better now
This commit is contained in:
parent
e705d0d286
commit
182813bd21
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
public static class FilesHelper
|
public static class FilesHelper
|
||||||
{
|
{
|
||||||
private static readonly string[] SupportedFileExtensions = { ".jpg", ".cr2", ".mp4" };
|
private static readonly string[] SupportedFileExtensions = { ".jpg", ".cr2", ".mp4", ".mov" };
|
||||||
|
|
||||||
public static IEnumerable<string> FindSupportedFilesRecursively(string path)
|
public static IEnumerable<string> FindSupportedFilesRecursively(string path)
|
||||||
{
|
{
|
||||||
@ -10,8 +10,6 @@ public static class FilesHelper
|
|||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
var fileExt = Path.GetExtension(file);
|
var fileExt = Path.GetExtension(file);
|
||||||
if (fileExt is null)
|
|
||||||
continue;
|
|
||||||
foreach (var supportedFileExtension in SupportedFileExtensions)
|
foreach (var supportedFileExtension in SupportedFileExtensions)
|
||||||
{
|
{
|
||||||
if (fileExt.Equals(supportedFileExtension, StringComparison.InvariantCultureIgnoreCase))
|
if (fileExt.Equals(supportedFileExtension, StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
|
||||||
<PackageReference Include="Serilog" Version="2.12.0" />
|
<PackageReference Include="Serilog" Version="2.12.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
|
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
|
||||||
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
|
<PackageReference Include="Spectre.Console" Version="0.45.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -16,7 +16,7 @@ internal static class Program
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var renamer = new Renamer(configuration);
|
var renamer = new Renamer(configuration);
|
||||||
return renamer.Run();
|
return renamer.RunAsync().GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
using MetadataExtractor;
|
using System.Runtime.InteropServices.JavaScript;
|
||||||
|
using MetadataExtractor;
|
||||||
using MetadataExtractor.Formats.Exif;
|
using MetadataExtractor.Formats.Exif;
|
||||||
using MetadataExtractor.Formats.QuickTime;
|
using MetadataExtractor.Formats.QuickTime;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using ShellProgressBar;
|
using Spectre.Console;
|
||||||
using Directory = System.IO.Directory;
|
using Directory = System.IO.Directory;
|
||||||
|
|
||||||
namespace PhotoRenamer;
|
namespace PhotoRenamer;
|
||||||
|
|
||||||
internal class Renamer
|
internal class Renamer
|
||||||
{
|
{
|
||||||
private readonly ProgressBarOptions _childOptions;
|
|
||||||
private readonly string _sourcePath;
|
private readonly string _sourcePath;
|
||||||
private readonly string _targetPath;
|
private readonly string _targetPath;
|
||||||
private int _currentCount;
|
private int _currentCount;
|
||||||
@ -21,51 +21,52 @@ internal class Renamer
|
|||||||
_targetPath = Path.GetFullPath(configuration["Target"]);
|
_targetPath = Path.GetFullPath(configuration["Target"]);
|
||||||
Log.Information($"Source path: {_sourcePath}");
|
Log.Information($"Source path: {_sourcePath}");
|
||||||
Log.Information($"Target path: {_targetPath}");
|
Log.Information($"Target path: {_targetPath}");
|
||||||
_childOptions = new ProgressBarOptions
|
|
||||||
{
|
|
||||||
ForegroundColor = ConsoleColor.Yellow,
|
|
||||||
BackgroundColor = ConsoleColor.DarkGreen,
|
|
||||||
ProgressCharacter = '─',
|
|
||||||
CollapseWhenFinished = true
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Run()
|
public async Task<int> RunAsync()
|
||||||
{
|
{
|
||||||
var files = Directory.GetFiles(_sourcePath, "*", SearchOption.AllDirectories);
|
var files = Directory.GetFiles(_sourcePath, "*", SearchOption.AllDirectories);
|
||||||
var options = new ProgressBarOptions
|
|
||||||
{
|
|
||||||
ForegroundColor = ConsoleColor.Yellow,
|
|
||||||
ForegroundColorDone = ConsoleColor.DarkGreen,
|
|
||||||
BackgroundColor = ConsoleColor.DarkGray,
|
|
||||||
BackgroundCharacter = '\u2593'
|
|
||||||
};
|
|
||||||
_currentCount = 0;
|
_currentCount = 0;
|
||||||
using var progressBar = new ProgressBar(files.Length, "Copying files", options);
|
var po = new ParallelOptions { MaxDegreeOfParallelism = 3 };
|
||||||
var po = new ParallelOptions { MaxDegreeOfParallelism = 1 };
|
await AnsiConsole
|
||||||
|
.Progress()
|
||||||
Parallel.ForEach(files, po, file => Body(file, progressBar));
|
.StartAsync(async ctx =>
|
||||||
|
{
|
||||||
|
await Parallel.ForEachAsync(files, po, (s, token) => Body(s, ctx, token));
|
||||||
|
});
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Body(string file, ProgressBar progressBar)
|
private async ValueTask Body(string file, ProgressContext progressContext, CancellationToken token)
|
||||||
{
|
{
|
||||||
|
var task = progressContext.AddTask($"[green]{file}[/]");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var directories = ImageMetadataReader.ReadMetadata(file);
|
DateTime dateTime;
|
||||||
var dateTime = GetDateTimeFromExif(directories) ?? GetDateTimeFromMp4(directories) ?? GetDateTimeFromLastWrite(file);
|
try
|
||||||
|
{
|
||||||
|
var directories = ImageMetadataReader.ReadMetadata(file);
|
||||||
|
dateTime = GetDateTimeFromExif(directories) ?? GetDateTimeFromMp4(directories) ?? GetDateTimeFromLastWrite(file);
|
||||||
|
}
|
||||||
|
catch (ImageProcessingException e)
|
||||||
|
{
|
||||||
|
Log.Error(e, $"Error reading file information from {file}");
|
||||||
|
dateTime = GetDateTimeFromLastWrite(file);
|
||||||
|
}
|
||||||
|
|
||||||
var folder = CreateFolder(dateTime);
|
var folder = CreateFolder(dateTime);
|
||||||
CopyFile(folder, file, progressBar);
|
var progress = new Progress<double>(v => task.Value = v * 100);
|
||||||
|
|
||||||
|
await CopyFileAsync(folder, file, progress);
|
||||||
}
|
}
|
||||||
catch (ImageProcessingException)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
//silently ignore
|
Log.Error(ex, "Error reading file information");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref _currentCount);
|
Interlocked.Increment(ref _currentCount);
|
||||||
progressBar.Tick(_currentCount);
|
task.StopTask();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,24 +94,22 @@ internal class Renamer
|
|||||||
destination.CreationTimeUtc = source.CreationTimeUtc;
|
destination.CreationTimeUtc = source.CreationTimeUtc;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void CopyFile(string folder, string file, ProgressBarBase progressBar)
|
private static async Task CopyFileAsync(string folder, string file, IProgress<double>? progress = null)
|
||||||
{
|
{
|
||||||
var destination = new FileInfo(Path.Combine(folder, Path.GetFileName(file)));
|
var destination = new FileInfo(Path.Combine(folder, Path.GetFileName(file)));
|
||||||
var source = new FileInfo(file);
|
var source = new FileInfo(file);
|
||||||
if (destination.Exists && destination.Length == source.Length)
|
if (destination.Exists && destination.Length == source.Length)
|
||||||
|
{
|
||||||
|
progress?.Report(1);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
using var child = progressBar.Spawn(100, destination.FullName, _childOptions);
|
|
||||||
|
|
||||||
await using var sourceStream = File.Open(source.FullName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
await using var sourceStream = File.Open(source.FullName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
await using var targetStream = File.Open(destination.FullName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
|
await using var targetStream = File.Open(destination.FullName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||||
|
|
||||||
var progress = new Progress<long>(o => progressBar.Tick((int)o * 100));
|
await sourceStream.CopyToAsync(targetStream, 81920, progress: progress);
|
||||||
await sourceStream.CopyToAsync(targetStream, 81920, progress);
|
|
||||||
|
|
||||||
ResetTimes(destination, source);
|
ResetTimes(destination, source);
|
||||||
|
|
||||||
child.Tick(100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string CreateFolder(DateTime dateTime)
|
private string CreateFolder(DateTime dateTime)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
public static class StreamExtensions
|
public static class StreamExtensions
|
||||||
{
|
{
|
||||||
public static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress<long>? progress = null, CancellationToken cancellationToken = default)
|
public static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress<double>? progress = null, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (source == null)
|
if (source == null)
|
||||||
throw new ArgumentNullException(nameof(source));
|
throw new ArgumentNullException(nameof(source));
|
||||||
@ -22,7 +22,7 @@ public static class StreamExtensions
|
|||||||
{
|
{
|
||||||
await destination.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false);
|
await destination.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false);
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
progress?.Report(totalBytesRead);
|
progress?.Report(totalBytesRead / (double)source.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"Source": "D:\\Nextcloud2\\Upload",
|
"Source": "C:\\Users\\Holger\\Nextcloud\\DCIM",
|
||||||
"Target": "D:\\UploadX"
|
"Target": "C:\\Users\\Holger\\Nextcloud\\DCIM"
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user