-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathJianyingController.cs
More file actions
270 lines (239 loc) · 11 KB
/
JianyingController.cs
File metadata and controls
270 lines (239 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace JyDraft
{
public enum ExportResolution
{
RES_8K,
RES_4K,
RES_2K,
RES_1080P,
RES_720P,
RES_480P
}
public static class ExportResolutionExtensions
{
public static string GetValue(this ExportResolution res)
{
return res switch
{
ExportResolution.RES_8K => "8K",
ExportResolution.RES_4K => "4K",
ExportResolution.RES_2K => "2K",
ExportResolution.RES_1080P => "1080P",
ExportResolution.RES_720P => "720P",
ExportResolution.RES_480P => "480P",
_ => throw new ArgumentOutOfRangeException()
};
}
}
public enum ExportFramerate
{
FR_24,
FR_25,
FR_30,
FR_50,
FR_60
}
public static class ExportFramerateExtensions
{
public static string GetValue(this ExportFramerate fr)
{
return fr switch
{
ExportFramerate.FR_24 => "24fps",
ExportFramerate.FR_25 => "25fps",
ExportFramerate.FR_30 => "30fps",
ExportFramerate.FR_50 => "50fps",
ExportFramerate.FR_60 => "60fps",
_ => throw new ArgumentOutOfRangeException()
};
}
}
public static class ControlFinder
{
// 委托表示:传入 Control 和深度 返回bool
public static Func<Control, int, bool> DescMatcher(string targetDesc, int depth = 2, bool exact = false)
{
targetDesc = targetDesc.ToLowerInvariant();
return (control, currentDepth) =>
{
if (currentDepth != depth) return false;
string fullDesc = control.GetPropertyValue(30159).ToLowerInvariant();
return exact ? targetDesc == fullDesc : fullDesc.Contains(targetDesc);
};
}
public static Func<Control, int, bool> ClassNameMatcher(string className, int depth = 1, bool exact = false)
{
className = className.ToLowerInvariant();
return (control, currentDepth) =>
{
if (currentDepth != depth) return false;
string currClassName = control.ClassName.ToLowerInvariant();
return exact ? className == currClassName : currClassName.Contains(className);
};
}
}
// 假设 Control 是 UI 自动化控件基类,需根据你实际UI框架调整
public class Control
{
public string ClassName { get; set; }
public string Name { get; set; }
public bool Exists(double timeoutSeconds) => throw new NotImplementedException();
public void Click(bool simulateMove = false) => throw new NotImplementedException();
public string GetPropertyValue(int propId) => throw new NotImplementedException();
public Control GetParentControl() => throw new NotImplementedException();
public Control GetSiblingControl(Func<Control, bool> predicate) => throw new NotImplementedException();
public Control TextControl(int searchDepth = 1, Func<Control, int, bool> compare = null) => throw new NotImplementedException();
public Control GroupControl(int searchDepth = 1, Func<Control, int, bool> compare = null) => throw new NotImplementedException();
public Control WindowControl(int searchDepth = 1, string name = null) => throw new NotImplementedException();
public void SetTopmost(bool topmost) => throw new NotImplementedException();
public void SetActive() => throw new NotImplementedException();
}
public class JianyingController
{
private Control app;
private string appStatus; // "home", "edit", "pre_export"
public JianyingController()
{
GetWindow();
}
public void ExportDraft(string draftName, string outputPath = null,
ExportResolution? resolution = null, ExportFramerate? framerate = null, double timeout = 1200)
{
Console.WriteLine($"开始导出 {draftName} 至 {outputPath}");
GetWindow();
SwitchToHome();
var draftNameText = app.TextControl(searchDepth: 2, compare: ControlFinder.DescMatcher($"HomePageDraftTitle:{draftName}", exact: true));
if (!draftNameText.Exists(0))
throw new Exception($"未找到名为{draftName}的剪映草稿"); // DraftNotFound 可自定义异常
var draftBtn = draftNameText.GetParentControl();
if (draftBtn == null) throw new Exception("草稿按钮不存在");
draftBtn.Click(false);
Thread.Sleep(10000);
GetWindow();
var exportBtn = app.TextControl(searchDepth: 2, compare: ControlFinder.DescMatcher("MainWindowTitleBarExportBtn"));
if (!exportBtn.Exists(0))
throw new Exception("未在编辑窗口中找到导出按钮"); // AutomationError
exportBtn.Click(false);
Thread.Sleep(10000);
GetWindow();
var exportPathSib = app.TextControl(searchDepth: 2, compare: ControlFinder.DescMatcher("ExportPath"));
if (!exportPathSib.Exists(0))
throw new Exception("未找到导出路径框");
var exportPathText = exportPathSib.GetSiblingControl(ctrl => true);
if (exportPathText == null)
throw new Exception("导出路径文本控件未找到");
string exportPath = exportPathText.GetPropertyValue(30159);
if (resolution.HasValue)
{
var settingGroup = app.GroupControl(searchDepth: 1, compare: ControlFinder.ClassNameMatcher("PanelSettingsGroup_QMLTYPE"));
if (!settingGroup.Exists(0))
throw new Exception("未找到导出设置组");
var resolutionBtn = settingGroup.TextControl(searchDepth: 2, compare: ControlFinder.DescMatcher("ExportSharpnessInput"));
if (!resolutionBtn.Exists(0.5))
throw new Exception("未找到导出分辨率下拉框");
resolutionBtn.Click(false);
Thread.Sleep(500);
var resolutionItem = app.TextControl(searchDepth: 2, compare: ControlFinder.DescMatcher(resolution.Value.GetValue()));
if (!resolutionItem.Exists(0.5))
throw new Exception($"未找到{resolution.Value.GetValue()}分辨率选项");
resolutionItem.Click(false);
Thread.Sleep(500);
}
if (framerate.HasValue)
{
var settingGroup = app.GroupControl(searchDepth: 1, compare: ControlFinder.ClassNameMatcher("PanelSettingsGroup_QMLTYPE"));
if (!settingGroup.Exists(0))
throw new Exception("未找到导出设置组");
var framerateBtn = settingGroup.TextControl(searchDepth: 2, compare: ControlFinder.DescMatcher("FrameRateInput"));
if (!framerateBtn.Exists(0.5))
throw new Exception("未找到导出帧率下拉框");
framerateBtn.Click(false);
Thread.Sleep(500);
var framerateItem = app.TextControl(searchDepth: 2, compare: ControlFinder.DescMatcher(framerate.Value.GetValue()));
if (!framerateItem.Exists(0.5))
throw new Exception($"未找到{framerate.Value.GetValue()}帧率选项");
framerateItem.Click(false);
Thread.Sleep(500);
}
var exportOkBtn = app.TextControl(searchDepth: 2, compare: ControlFinder.DescMatcher("ExportOkBtn", exact: true));
if (!exportOkBtn.Exists(0))
throw new Exception("未在导出窗口中找到导出按钮");
exportOkBtn.Click(false);
Thread.Sleep(5000);
var startTime = DateTime.Now;
while (true)
{
GetWindow();
if (appStatus != "pre_export")
continue;
var succeedCloseBtn = app.TextControl(searchDepth: 2, compare: ControlFinder.DescMatcher("ExportSucceedCloseBtn"));
if (succeedCloseBtn.Exists(0))
{
succeedCloseBtn.Click(false);
break;
}
if ((DateTime.Now - startTime).TotalSeconds > timeout)
throw new Exception($"导出超时, 时限为{timeout}秒");
Thread.Sleep(1000);
}
Thread.Sleep(2000);
GetWindow();
SwitchToHome();
Thread.Sleep(2000);
if (!string.IsNullOrEmpty(outputPath))
{
File.Move(exportPath, outputPath);
}
Console.WriteLine($"导出 {draftName} 至 {outputPath} 完成");
}
public void SwitchToHome()
{
if (appStatus == "home")
return;
if (appStatus != "edit")
throw new Exception("仅支持从编辑模式切换到主页");
var closeBtn = app.GroupControl(searchDepth: 1, compare: (c, depth) => c.ClassName == "TitleBarButton").GetSiblingControl(c => true); // foundIndex=3的概念根据实际UI实现调整
closeBtn.Click(false);
Thread.Sleep(2000);
GetWindow();
}
public void GetWindow()
{
if (app != null && app.Exists(0))
app.SetTopmost(false);
// 假设这里实现查找主窗口
app = new Control(); // 你需要替换成真正的窗口查找代码,带上 Compare 比较器
if (!app.Exists(0))
throw new Exception("剪映窗口未找到");
var exportWindow = app.WindowControl(searchDepth: 1, name: "导出");
if (exportWindow.Exists(0))
{
app = exportWindow;
appStatus = "pre_export";
}
else
{
// 这里根据窗体ClassName判断状态,伪代码如下
if (app.Name != "剪映专业版")
{
throw new Exception("不是剪映专业版窗口");
}
if (app.ClassName.ToLower().Contains("homepage"))
appStatus = "home";
else if (app.ClassName.ToLower().Contains("mainwindow"))
appStatus = "edit";
else
appStatus = "";
}
app.SetActive();
app.SetTopmost(true);
}
}
}