1+ using MaiChartManager . Utils ;
2+ using Spectre . Console ;
3+ using Spectre . Console . Cli ;
4+ using System . ComponentModel ;
5+
6+ namespace MaiChartManager . CLI . Commands ;
7+
8+ public class MakeUsmCommand : AsyncCommand < MakeUsmCommand . Settings >
9+ {
10+ public class Settings : CommandSettings
11+ {
12+ [ CommandArgument ( 0 , "<sources>" ) ]
13+ [ Description ( "要转换的源视频文件" ) ]
14+ public string [ ] Sources { get ; set ; } = [ ] ;
15+
16+ [ CommandOption ( "-O|--output" ) ]
17+ [ Description ( "输出文件路径(仅单文件时可用)" ) ]
18+ public string ? Output { get ; set ; }
19+
20+ [ CommandOption ( "--no-scale" ) ]
21+ [ Description ( "禁用视频缩放" ) ]
22+ [ DefaultValue ( false ) ]
23+ public bool NoScale { get ; set ; }
24+
25+ [ CommandOption ( "--yuv420p" ) ]
26+ [ Description ( "使用 YUV420p 色彩空间" ) ]
27+ [ DefaultValue ( false ) ]
28+ public bool UseYuv420p { get ; set ; }
29+
30+ public override ValidationResult Validate ( )
31+ {
32+ if ( Sources . Length == 0 )
33+ {
34+ return ValidationResult . Error ( "至少需要一个源文件" ) ;
35+ }
36+
37+ if ( Sources . Length > 1 && ! string . IsNullOrEmpty ( Output ) )
38+ {
39+ return ValidationResult . Error ( "多文件转换时不能使用 -O 选项" ) ;
40+ }
41+
42+ foreach ( var source in Sources )
43+ {
44+ if ( ! File . Exists ( source ) )
45+ {
46+ return ValidationResult . Error ( $ "源文件不存在: { source } ") ;
47+ }
48+ }
49+
50+ return ValidationResult . Success ( ) ;
51+ }
52+ }
53+
54+ public override async Task < int > ExecuteAsync ( CommandContext context , Settings settings , CancellationToken cancellationToken )
55+ {
56+ try
57+ {
58+ await AnsiConsole . Status ( )
59+ . Spinner ( Spinner . Known . Dots )
60+ . StartAsync ( "正在检测硬件加速..." , async ctx =>
61+ {
62+ await VideoConvert . CheckHardwareAcceleration ( ) ;
63+ } ) ;
64+
65+ AnsiConsole . MarkupLine ( $ "[green]硬件加速: { VideoConvert . HardwareAcceleration } [/]") ;
66+ AnsiConsole . MarkupLine ( $ "[green]H264 编码器: { VideoConvert . H264Encoder } [/]") ;
67+
68+ if ( settings . Sources . Length == 1 )
69+ {
70+ var source = settings . Sources [ 0 ] ;
71+ var output = settings . Output ?? Path . ChangeExtension ( source , ".dat" ) ;
72+ await ConvertSingleFile ( source , output , settings ) ;
73+ }
74+ else
75+ {
76+ await ConvertMultipleFiles ( settings ) ;
77+ }
78+
79+ AnsiConsole . MarkupLine ( "[green]✓ 所有转换已成功完成![/]" ) ;
80+ return 0 ;
81+ }
82+ catch ( Exception ex )
83+ {
84+ AnsiConsole . MarkupLine ( $ "[red]✗ 错误: { ex . Message } [/]") ;
85+ return 1 ;
86+ }
87+ }
88+
89+ private async Task ConvertSingleFile ( string source , string output , Settings settings )
90+ {
91+ AnsiConsole . MarkupLine ( $ "[yellow]正在转换:[/] { Path . GetFileName ( source ) } → { Path . GetFileName ( output ) } ") ;
92+
93+ await AnsiConsole . Progress ( )
94+ . AutoClear ( false )
95+ . Columns (
96+ new TaskDescriptionColumn ( ) ,
97+ new ProgressBarColumn ( ) ,
98+ new PercentageColumn ( ) ,
99+ new SpinnerColumn ( ) )
100+ . StartAsync ( async ctx =>
101+ {
102+ var task = ctx . AddTask ( $ "[green]转换 { Path . GetFileName ( source ) } [/]") ;
103+ task . MaxValue = 100 ;
104+
105+ await VideoConvert . ConvertVideoToUsm (
106+ source ,
107+ output ,
108+ noScale : settings . NoScale ,
109+ yuv420p : settings . UseYuv420p ,
110+ onProgress : percent => task . Value = percent
111+ ) ;
112+
113+ task . Value = 100 ;
114+ } ) ;
115+
116+ AnsiConsole . MarkupLine ( $ "[green]✓ 已保存到: { output } [/]") ;
117+ }
118+
119+ private async Task ConvertMultipleFiles ( Settings settings )
120+ {
121+ AnsiConsole . MarkupLine ( $ "[yellow]正在转换 { settings . Sources . Length } 个文件...[/]") ;
122+
123+ await AnsiConsole . Progress ( )
124+ . AutoClear ( false )
125+ . Columns (
126+ new TaskDescriptionColumn ( ) ,
127+ new ProgressBarColumn ( ) ,
128+ new PercentageColumn ( ) ,
129+ new SpinnerColumn ( ) )
130+ . StartAsync ( async ctx =>
131+ {
132+ foreach ( var source in settings . Sources )
133+ {
134+ var output = Path . ChangeExtension ( source , ".dat" ) ;
135+ var task = ctx . AddTask ( $ "[green]{ Path . GetFileName ( source ) } [/]") ;
136+ task . MaxValue = 100 ;
137+
138+ try
139+ {
140+ await VideoConvert . ConvertVideoToUsm (
141+ source ,
142+ output ,
143+ noScale : settings . NoScale ,
144+ yuv420p : settings . UseYuv420p ,
145+ onProgress : percent => task . Value = percent
146+ ) ;
147+
148+ task . Value = 100 ;
149+ AnsiConsole . MarkupLine ( $ "[green]✓ { Path . GetFileName ( source ) } → { Path . GetFileName ( output ) } [/]") ;
150+ }
151+ catch ( Exception ex )
152+ {
153+ task . StopTask ( ) ;
154+ AnsiConsole . MarkupLine ( $ "[red]✗ 转换失败 { Path . GetFileName ( source ) } : { ex . Message } [/]") ;
155+ }
156+ }
157+ } ) ;
158+ }
159+ }
0 commit comments