|
| 1 | +using System.ComponentModel; |
| 2 | +using System.Text.RegularExpressions; |
| 3 | +using MaiChartManager.CLI.Utils; |
| 4 | +using MaiChartManager.Utils; |
| 5 | +using Spectre.Console; |
| 6 | +using Spectre.Console.Cli; |
| 7 | + |
| 8 | +namespace MaiChartManager.CLI.Commands; |
| 9 | + |
| 10 | +public partial class MakeAbCommand : AsyncCommand<MakeAbCommand.Settings> |
| 11 | +{ |
| 12 | + [GeneratedRegex(@"^(?<id>\d+)\.(png|jpg|jpeg)$", RegexOptions.IgnoreCase)] |
| 13 | + private static partial Regex NumericFileRegex(); |
| 14 | + |
| 15 | + [GeneratedRegex(@"^ui_jacket_(?<id>\d+)\.(png|jpg|jpeg)$", RegexOptions.IgnoreCase)] |
| 16 | + private static partial Regex UiJacketFileRegex(); |
| 17 | + |
| 18 | + public class Settings : CommandSettings |
| 19 | + { |
| 20 | + [CommandArgument(0, "<folder>")] |
| 21 | + [Description("包含封面图片的文件夹路径")] |
| 22 | + public string Folder { get; set; } = ""; |
| 23 | + |
| 24 | + public override ValidationResult Validate() |
| 25 | + { |
| 26 | + if (!Directory.Exists(Folder)) |
| 27 | + return ValidationResult.Error($"文件夹不存在: {Folder}"); |
| 28 | + |
| 29 | + return ValidationResult.Success(); |
| 30 | + } |
| 31 | + } |
| 32 | + |
| 33 | + public override async Task<int> ExecuteAsync(CommandContext context, Settings settings, CancellationToken cancellationToken) |
| 34 | + { |
| 35 | + var candidates = Directory.EnumerateFiles(settings.Folder) |
| 36 | + .Select(path => (Path: path, Name: Path.GetFileName(path))) |
| 37 | + .Select(x => |
| 38 | + { |
| 39 | + var m = NumericFileRegex().Match(x.Name); |
| 40 | + if (m.Success) return (x.Path, Id: m.Groups["id"].Value); |
| 41 | + |
| 42 | + m = UiJacketFileRegex().Match(x.Name); |
| 43 | + if (m.Success) return (x.Path, Id: m.Groups["id"].Value); |
| 44 | + |
| 45 | + return (Path: (string?)null, Id: (string?)null); |
| 46 | + }) |
| 47 | + .Where(x => x.Path is not null) |
| 48 | + .ToList(); |
| 49 | + |
| 50 | + if (candidates.Count == 0) |
| 51 | + { |
| 52 | + AnsiConsole.MarkupLine("[red]✗ 未找到有效的图片文件(需要:纯6位数字文件名或 ui_jacket_xxxxxx 格式,PNG/JPG)[/]"); |
| 53 | + return 1; |
| 54 | + } |
| 55 | + |
| 56 | + var jacketDir = Path.Combine(settings.Folder, "jacket"); |
| 57 | + var jacketSmallDir = Path.Combine(settings.Folder, "jacket_s"); |
| 58 | + Directory.CreateDirectory(jacketDir); |
| 59 | + Directory.CreateDirectory(jacketSmallDir); |
| 60 | + |
| 61 | + AnsiConsole.MarkupLine($"[yellow]找到 {candidates.Count} 张图片,开始转换...[/]"); |
| 62 | + |
| 63 | + var errors = new List<string>(); |
| 64 | + |
| 65 | + await AnsiConsole.Progress() |
| 66 | + .AutoClear(false) |
| 67 | + .Columns( |
| 68 | + new TaskDescriptionColumn(), |
| 69 | + new ProgressBarColumn(), |
| 70 | + new PercentageColumn(), |
| 71 | + new SpinnerColumn()) |
| 72 | + .StartAsync(async ctx => |
| 73 | + { |
| 74 | + var task = ctx.AddTask("转换中", maxValue: candidates.Count); |
| 75 | + |
| 76 | + foreach (var (path, id) in candidates) |
| 77 | + { |
| 78 | + task.Description = $"[green]ui_jacket_{id}[/]"; |
| 79 | + |
| 80 | + try |
| 81 | + { |
| 82 | + await Task.Run(() => |
| 83 | + { |
| 84 | + AssetBundleCreator.CreateTextureAssetBundle( |
| 85 | + path!, |
| 86 | + Path.Combine(jacketDir, $"ui_jacket_{id}.ab"), |
| 87 | + $"UI_Jacket_{id}", |
| 88 | + $"assets/assetbundle/jacket/ui_jacket_{id}.png", |
| 89 | + $"jacket/ui_jacket_{id}.ab"); |
| 90 | + |
| 91 | + AssetBundleCreator.CreateTextureAssetBundle( |
| 92 | + path!, |
| 93 | + Path.Combine(jacketSmallDir, $"ui_jacket_{id}_s.ab"), |
| 94 | + $"UI_Jacket_{id}_s", |
| 95 | + $"assets/assetbundle/jacket_s/ui_jacket_{id}_s.png", |
| 96 | + $"jacket_s/ui_jacket_{id}_s.ab", |
| 97 | + resizeWidth: 200, |
| 98 | + resizeHeight: 200); |
| 99 | + }, cancellationToken); |
| 100 | + } |
| 101 | + catch (Exception ex) |
| 102 | + { |
| 103 | + SentrySdk.CaptureException(ex); |
| 104 | + errors.Add($"{Path.GetFileName(path!)}: {ex.Message}"); |
| 105 | + } |
| 106 | + |
| 107 | + task.Increment(1); |
| 108 | + TerminalProgress.Set((int)(task.Value * 100 / task.MaxValue)); |
| 109 | + } |
| 110 | + |
| 111 | + TerminalProgress.Clear(); |
| 112 | + }); |
| 113 | + |
| 114 | + if (errors.Count > 0) |
| 115 | + { |
| 116 | + AnsiConsole.MarkupLine($"[red]✗ {errors.Count}/{candidates.Count} 个文件转换失败:[/]"); |
| 117 | + foreach (var e in errors) |
| 118 | + AnsiConsole.MarkupLine($"[red] {e}[/]"); |
| 119 | + return 1; |
| 120 | + } |
| 121 | + |
| 122 | + AnsiConsole.MarkupLine($"[green]✓ {candidates.Count} 张图片全部转换成功![/]"); |
| 123 | + AnsiConsole.MarkupLine($"[green] jacket → {jacketDir}[/]"); |
| 124 | + AnsiConsole.MarkupLine($"[green] jacket_s → {jacketSmallDir}[/]"); |
| 125 | + return 0; |
| 126 | + } |
| 127 | +} |
0 commit comments