diff --git a/.gitignore b/.gitignore index 3493cd5..fa06a6a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,2 @@ src/bin/ -src/obj/ - -.vs/ -bin/ -obj/ -*.user -*.suo \ No newline at end of file +src/obj/ \ No newline at end of file diff --git a/UniversalTagEditor.Core/UniversalTagEditor.Core.csproj b/UniversalTagEditor.Core/UniversalTagEditor.Core.csproj deleted file mode 100644 index bad939f..0000000 --- a/UniversalTagEditor.Core/UniversalTagEditor.Core.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - net8.0 - enable - enable - - - - - - - - - diff --git a/UniversalTagEditor.Core/runner.cs b/UniversalTagEditor.Core/runner.cs deleted file mode 100644 index b4a3c14..0000000 --- a/UniversalTagEditor.Core/runner.cs +++ /dev/null @@ -1,104 +0,0 @@ -using MetaBrainz.MusicBrainz; -using UniversalTagEditor.Helpers; -using UniversalTagEditor.Types; - -namespace UniversalTagEditor.Core; - -public sealed class UteOptions -{ - public required string WorkingDirectory { get; init; } - public string? AlbumInfoCsv { get; init; } // имя файла или путь - public string? AlbumCover { get; init; } // имя файла или путь - public string? Format { get; init; } // flac/mp3/... - public bool FixTags { get; init; } - public bool EnhanceStructure { get; init; } - public int MaxDegreeOfParallelism { get; init; } = 4; -} - -public sealed record ProgressInfo(int Done, int Total, string? CurrentFile); - -public static class Runner -{ - public static async Task RunAsync( - UteOptions opt, - IProgress? log = null, - IProgress? progress = null, - CancellationToken ct = default) - { - if (string.IsNullOrWhiteSpace(opt.WorkingDirectory)) - throw new ArgumentException("WorkingDirectory is empty."); - - if (!Directory.Exists(opt.WorkingDirectory)) - throw new DirectoryNotFoundException(opt.WorkingDirectory); - - var format = (opt.Format ?? "").Trim(); - if (string.IsNullOrWhiteSpace(format)) - throw new ArgumentException("Format (-f) is required."); - - AlbumInfo album = new(); - - if (!string.IsNullOrWhiteSpace(opt.AlbumInfoCsv)) - { - var csvPath = MakePath(opt.WorkingDirectory, opt.AlbumInfoCsv!); - var records = CsvParser.ParseCsvTable(csvPath); - album = new AlbumInfo { Tracks = records }; - } - - if (!string.IsNullOrWhiteSpace(opt.AlbumCover)) - album.Cover = MakePath(opt.WorkingDirectory, opt.AlbumCover!); - - - var files = new List(); - files.AddRange(Directory.GetFiles(opt.WorkingDirectory, $"*.{format}")); - - if (!opt.EnhanceStructure) - { - foreach (var dir in Directory.GetDirectories(opt.WorkingDirectory)) - files.AddRange(Directory.GetFiles(dir, $"*.{format}")); - } - - if (files.Count == 0) - { - log?.Report("Файлы не найдены."); - return; - } - - var queries = new ThreadLocal(() => - new Query("NavidromeMetadataRecovery", "1.0")); - - int done = 0; - int total = files.Count; - - await Parallel.ForEachAsync( - files.Select((path, index) => (path, index)), - new ParallelOptions - { - MaxDegreeOfParallelism = Math.Max(1, opt.MaxDegreeOfParallelism), - CancellationToken = ct - }, - async (item, token) => - { - var (file, index) = item; - - if (opt.FixTags) - { - await TagEditor.Fix(file, queries.Value!, opt.EnhanceStructure, format); - } - else - { - TagEditor.Edit(file, album, index); - } - - var nowDone = Interlocked.Increment(ref done); - progress?.Report(new ProgressInfo(nowDone, total, file)); - log?.Report($"{nowDone}/{total}: {file} успешно обработан"); - }); - - log?.Report("Готово!"); - } - - private static string MakePath(string workDir, string maybeFileOrPath) - => Path.IsPathRooted(maybeFileOrPath) - ? maybeFileOrPath - : Path.Combine(workDir, maybeFileOrPath); -} \ No newline at end of file diff --git a/UniversalTagEditor.sln b/UniversalTagEditor.sln index b34d5eb..29b63f4 100644 --- a/UniversalTagEditor.sln +++ b/UniversalTagEditor.sln @@ -6,8 +6,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversalTagEditor", "src\UniversalTagEditor.csproj", "{F12A64BA-EF0D-DBA6-7B72-63FC4220F8EC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversalTagEditor.Core", "UniversalTagEditor.Core\UniversalTagEditor.Core.csproj", "{A0248697-C889-481E-89C2-FB3C4DF6A3ED}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,17 +16,12 @@ Global {F12A64BA-EF0D-DBA6-7B72-63FC4220F8EC}.Debug|Any CPU.Build.0 = Debug|Any CPU {F12A64BA-EF0D-DBA6-7B72-63FC4220F8EC}.Release|Any CPU.ActiveCfg = Release|Any CPU {F12A64BA-EF0D-DBA6-7B72-63FC4220F8EC}.Release|Any CPU.Build.0 = Release|Any CPU - {A0248697-C889-481E-89C2-FB3C4DF6A3ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A0248697-C889-481E-89C2-FB3C4DF6A3ED}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A0248697-C889-481E-89C2-FB3C4DF6A3ED}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A0248697-C889-481E-89C2-FB3C4DF6A3ED}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {F12A64BA-EF0D-DBA6-7B72-63FC4220F8EC} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} - {A0248697-C889-481E-89C2-FB3C4DF6A3ED} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D0E6B6A0-0573-41C6-88A8-2678B8799BA7} diff --git a/UniversalTagEditor.Core/Helpers/CsvParser.cs b/src/Helpers/CsvParser.cs similarity index 100% rename from UniversalTagEditor.Core/Helpers/CsvParser.cs rename to src/Helpers/CsvParser.cs diff --git a/UniversalTagEditor.Core/Helpers/DiscogsService.cs b/src/Helpers/DiscogsService.cs similarity index 100% rename from UniversalTagEditor.Core/Helpers/DiscogsService.cs rename to src/Helpers/DiscogsService.cs diff --git a/UniversalTagEditor.Core/Helpers/TagEditor.cs b/src/Helpers/TagEditor.cs similarity index 100% rename from UniversalTagEditor.Core/Helpers/TagEditor.cs rename to src/Helpers/TagEditor.cs diff --git a/src/Program.cs b/src/Program.cs index f5099c6..18aa4c3 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -1,59 +1,121 @@ -using System.Text; -using UniversalTagEditor.Core; +using UniversalTagEditor.Helpers; +using UniversalTagEditor.Types; + +using MetaBrainz.MusicBrainz; + +namespace UniversalTagEditor; class Program { static async Task Main(string[] args) { - var opt = ParseArgs(args); - - var log = new Progress(Console.WriteLine); - var prog = new Progress(p => - Console.Title = $"UniversalTagEditor {p.Done}/{p.Total}"); - - Console.OutputEncoding = Encoding.UTF8; - Console.InputEncoding = Encoding.UTF8; - - Console.WriteLine("WORKDIR RAW : " + opt.WorkingDirectory); - Console.WriteLine("WORKDIR FULL: " + Path.GetFullPath(opt.WorkingDirectory)); - Console.WriteLine("EXISTS : " + Directory.Exists(opt.WorkingDirectory)); - await Runner.RunAsync(opt, log, prog); - } - - static UteOptions ParseArgs(string[] args) - { - string? albumInfo = null; - string? albumCover = null; - string? format = null; - string? workingDirectory = null; + string albumInfo = ""; + string albumCover = ""; + string format = ""; + string workingDirectory = ""; bool fixTags = false; bool enhanceStructure = false; + List threads = new(); + + var query = new Query("NavidromeMetadataRecovery1", "1.0"); + var query2 = new Query("NavidromeMetadataRecovery2", "1.0"); + var query3 = new Query("NavidromeMetadataRecovery3", "1.0"); + var query4 = new Query("NavidromeMetadataRecovery4", "1.0"); + for (int i = 0; i < args.Length; i++) { switch (args[i]) { - case "-w": workingDirectory = args[++i]; break; - case "-a": albumInfo = args[++i]; break; - case "-f": format = args[++i]; break; - case "-c": albumCover = args[++i]; break; - case "--fix-tags": fixTags = true; break; - case "--enhance-structure": enhanceStructure = true; break; + case "-w": + if (i + 1 < args.Length) workingDirectory = args[i + 1]; + i++; + break; + + case "-a": + if (i + 1 < args.Length) albumInfo = args[i + 1]; + i++; + break; + + case "-f": + if (i + 1 < args.Length) format = args[i + 1]; + i++; + break; + + case "-c": + if (i + 1 < args.Length) albumCover = args[i + 1]; + i++; + break; + + case "--fix-tags": + fixTags = true; + break; + case "--enhance-structure": + enhanceStructure = true; + break; } } - if (string.IsNullOrWhiteSpace(workingDirectory)) - throw new ArgumentException("Нужен -w "); - - return new UteOptions + TrackInfo[] records; + AlbumInfo album = new(); + if (albumInfo != "") { - WorkingDirectory = workingDirectory, - AlbumInfoCsv = albumInfo, - AlbumCover = albumCover, - Format = format, - FixTags = fixTags, - EnhanceStructure = enhanceStructure, - MaxDegreeOfParallelism = 4 + records = CsvParser.ParseCsvTable(Path.Combine(workingDirectory, albumInfo)); + album = new() + { + Tracks = records + }; + } + if (albumCover != "") + album.Cover = Path.Combine(workingDirectory, albumCover); + + List files = new(); + files.AddRange(Directory.GetFiles(workingDirectory, $"*.{format}")); + + if (!enhanceStructure) { + string[] directories = Directory.GetDirectories(workingDirectory); + foreach(string directory in directories) + files.AddRange(Directory.GetFiles(directory, $"*.{format}")); + } + + Func, Query, int, Task> cycle = async (List chunk, Query query, int thread) => + { + threads.Add(thread); + for (int i = 0; i < chunk.Count; i++) + { + if (fixTags) + await TagEditor.Fix(chunk[i], query, enhanceStructure, format); + else + TagEditor.Edit(chunk[i], album, i); + Console.WriteLine($"{i+1}. {chunk[i]} successfully edited"); + } + threads.Remove(thread); }; + + if ((int)(files.Count / 4) > 1) + { + Thread thread = new Thread(async () => await cycle(files.GetRange(0, (int)(files.Count / 4)), query, 1)) { IsBackground = true }; + thread.Start(); + + Thread thread2 = new Thread(async () => await cycle(files.GetRange((int)(files.Count / 4), (int)(files.Count / 4)), query2, 2)) { IsBackground = true }; + thread2.Start(); + + if ((int)(files.Count / 4) >= 3) { + Thread thread3 = new Thread(async () => await cycle(files.GetRange(((int)(files.Count / 4) * 2), (int)(files.Count / 4)), query3, 3)) { IsBackground = true }; + thread3.Start(); + } + if ((int)(files.Count / 4) >= 4) { + Thread thread4 = new Thread(async () => await cycle(files.GetRange(((int)(files.Count / 4) * 3), files.Count - ((int)(files.Count / 4) * 3)), query4, 4)) { IsBackground = true }; + thread4.Start(); + } + + Console.WriteLine("Waiting for threads complete"); + while(threads.Count > 0) { } + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine("\n\nTag fixing completed!"); + Console.ResetColor(); + } + else + await cycle(files, query, 1); } } \ No newline at end of file diff --git a/UniversalTagEditor.Core/Types/AlbumInfo.cs b/src/Types/AlbumInfo.cs similarity index 100% rename from UniversalTagEditor.Core/Types/AlbumInfo.cs rename to src/Types/AlbumInfo.cs diff --git a/UniversalTagEditor.Core/Types/TrackInfo.cs b/src/Types/TrackInfo.cs similarity index 100% rename from UniversalTagEditor.Core/Types/TrackInfo.cs rename to src/Types/TrackInfo.cs diff --git a/src/UniversalTagEditor.csproj b/src/UniversalTagEditor.csproj index 9d1ca48..2b01bac 100644 --- a/src/UniversalTagEditor.csproj +++ b/src/UniversalTagEditor.csproj @@ -2,13 +2,15 @@ Exe - net8.0 + net10.0 enable enable - + + +