126 lines
4.4 KiB
C#
126 lines
4.4 KiB
C#
using System.Runtime.InteropServices.JavaScript;
|
|
using MetadataExtractor;
|
|
using MetadataExtractor.Formats.Exif;
|
|
using MetadataExtractor.Formats.QuickTime;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Serilog;
|
|
using Spectre.Console;
|
|
using Directory = System.IO.Directory;
|
|
|
|
namespace PhotoRenamer;
|
|
|
|
internal class Renamer
|
|
{
|
|
private readonly string _sourcePath;
|
|
private readonly string _targetPath;
|
|
private int _currentCount;
|
|
|
|
public Renamer(IConfiguration configuration)
|
|
{
|
|
_sourcePath = Path.GetFullPath(configuration["Source"]);
|
|
_targetPath = Path.GetFullPath(configuration["Target"]);
|
|
Log.Information($"Source path: {_sourcePath}");
|
|
Log.Information($"Target path: {_targetPath}");
|
|
}
|
|
|
|
public async Task<int> RunAsync()
|
|
{
|
|
var files = Directory.GetFiles(_sourcePath, "*", SearchOption.AllDirectories);
|
|
_currentCount = 0;
|
|
var po = new ParallelOptions { MaxDegreeOfParallelism = 3 };
|
|
await AnsiConsole
|
|
.Progress()
|
|
.StartAsync(async ctx =>
|
|
{
|
|
await Parallel.ForEachAsync(files, po, (s, token) => Body(s, ctx, token));
|
|
});
|
|
return 0;
|
|
}
|
|
|
|
private async ValueTask Body(string file, ProgressContext progressContext, CancellationToken token)
|
|
{
|
|
var task = progressContext.AddTask($"[green]{file}[/]");
|
|
try
|
|
{
|
|
DateTime dateTime;
|
|
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 progress = new Progress<double>(v => task.Value = v * 100);
|
|
|
|
await CopyFileAsync(folder, file, progress);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, "Error reading file information");
|
|
}
|
|
finally
|
|
{
|
|
Interlocked.Increment(ref _currentCount);
|
|
task.StopTask();
|
|
}
|
|
}
|
|
|
|
private static DateTime GetDateTimeFromLastWrite(string file)
|
|
{
|
|
var creationTime = File.GetLastWriteTime(file);
|
|
return creationTime;
|
|
}
|
|
|
|
private static DateTime? GetDateTimeFromExif(IEnumerable<MetadataExtractor.Directory> directories)
|
|
{
|
|
return directories.OfType<ExifIfd0Directory>().FirstOrDefault()?.TryGetDateTime(ExifDirectoryBase.TagDateTime, out var dateTime) == true ? dateTime : null;
|
|
}
|
|
|
|
private static DateTime? GetDateTimeFromMp4(IEnumerable<MetadataExtractor.Directory> directories)
|
|
{
|
|
return directories.OfType<QuickTimeMovieHeaderDirectory>().FirstOrDefault()?.TryGetDateTime(QuickTimeMovieHeaderDirectory.TagCreated, out var dateTime) == true ? dateTime : null;
|
|
}
|
|
|
|
private static void ResetTimes(FileSystemInfo destination, FileSystemInfo source)
|
|
{
|
|
destination.LastWriteTime = source.LastWriteTime;
|
|
destination.LastWriteTimeUtc = source.LastWriteTimeUtc;
|
|
destination.CreationTime = source.CreationTime;
|
|
destination.CreationTimeUtc = source.CreationTimeUtc;
|
|
}
|
|
|
|
private static async Task CopyFileAsync(string folder, string file, IProgress<double>? progress = null)
|
|
{
|
|
var destination = new FileInfo(Path.Combine(folder, Path.GetFileName(file)));
|
|
var source = new FileInfo(file);
|
|
if (destination.Exists && destination.Length == source.Length)
|
|
{
|
|
progress?.Report(1);
|
|
return;
|
|
}
|
|
|
|
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 sourceStream.CopyToAsync(targetStream, 81920, progress: progress);
|
|
|
|
ResetTimes(destination, source);
|
|
}
|
|
|
|
private string CreateFolder(DateTime dateTime)
|
|
{
|
|
var folder = Path.Combine(_targetPath, dateTime.Year.ToString(), dateTime.Month.ToString("D2"));
|
|
if (!Directory.Exists(folder))
|
|
{
|
|
Directory.CreateDirectory(folder);
|
|
}
|
|
|
|
return folder;
|
|
}
|
|
}
|