-
Notifications
You must be signed in to change notification settings - Fork 159
/
SubtitleTranslate - baidu.as
312 lines (263 loc) · 10.5 KB
/
SubtitleTranslate - baidu.as
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
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
/*
real time subtitle translate for PotPlayer using Bai Du API
*/
// string GetTitle() -> get title for UI
// string GetVersion -> get version for manage
// string GetDesc() -> get detail information
// string GetLoginTitle() -> get title for login dialog
// string GetLoginDesc() -> get desc for login dialog
// string ServerLogin(string User, string Pass) -> login
// string ServerLogout() -> logout
// array<string> GetSrcLangs() -> get source language
// array<string> GetDstLangs() -> get target language
// string Translate(string Text, string &in SrcLang, string &in DstLang) -> do translate !!
//必须配置的部分,不过现在已经移交到“实时字幕翻译”中了
//它的位置是: 打开任意视频或者点击左上角的PolPlayer -> 字幕 -> 实时字幕翻译 -> 实时字幕翻译设置 -> 选中百度翻译 -> 点右边的 “账户设置”
string appId = "";//appid
string toKen = "";//密钥
//可选配置,一般而言是不用修改的!
int coolTime = 1300;//冷却时间,这里的单位是毫秒,1秒钟=1000毫秒,如果提示 error:54003, 那么就加大这个数字,建议一次加100
string userAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36";//这个是可选配置,一般不用修改!
//执行环境,请不要修改!
int NULL = 0;
int executeThreadId = NULL;//这个变量的命名是我的目标,不过,暂时没能实现!只是做了个还有小bug的临时替代方案
int nextExecuteTime = 0;//下次执行代码的时间
/** 获取当前插件的版本号*/
string GetVersion(){
return "1";
}
/** 获取当前插件的标题 */
string GetTitle(){
return "{$CP950=百度翻譯$}{$CP936=百度翻译$}{$CP0=Bai Du translate$}";
}
/** 获取当前插件的表述信息 */
string GetDesc(){
return "https://fanyi.baidu.com/";
}
/** 获取登录的标题 */
string GetLoginTitle(){
return "请输入配置";
}
/** 获取登录的描述信息 */
string GetLoginDesc(){
return "请输入AppId和密钥!";
}
/** 获取登录时,用户输入框的标签名称 */
string GetUserText(){
return "App ID:";
}
/** 获取登录时,密码输入框的标签名称 */
string GetPasswordText(){
return "密钥:";
}
/** 获取支持的语言列表 - 源语言 */
array<string> GetSrcLangs(){
array<string> ret = GetLangTable();
ret.insertAt(0, ""); // empty is auto
return ret;
}
/** 获取支持的语言列表 - 目标语言 */
array<string> GetDstLangs(){
return GetLangTable();
}
/** 登录账号入口
* - 这里不做校验了,只要有输入就判定为成功。具体测试由用户的翻译测试按钮去测试
* @param appIdStr appid 字符串
* @param toKenStr 秘钥字符串
*/
string ServerLogin(string appIdStr, string toKenStr){
//空字符串校验
if(appIdStr.empty() || toKenStr.empty()) return "fail";
//记录到全局变量中
appId = appIdStr;
toKen = toKenStr;
return "200 ok";
}
/** 翻译的入口
* @param text 待翻译的原文
* @param srcLang 当前语言
* @param dstLang 目标语言
*/
string Translate(string text, string &in srcLang, string &in dstLang){
string ret = "";
if(!text.empty()){//确实有内容需要翻译才有必要继续
//开发文档。需要App id 等信息
//http://api.fanyi.baidu.com/api/trans/product/apidoc
// HostOpenConsole(); // for debug
//语言选择
srcLang = GetLang(srcLang);
dstLang = GetLang(dstLang);
//对原文进行 url 编码
string q = HostUrlEncode(text);
//构建请求的 url 地址
string salt = "" + HostGetTickCount();//随机数
string sign = HostHashMD5(appId + text + salt + toKen);//签名 appid+q+salt+密钥
string parames = "from=" + srcLang + "&to=" + dstLang + "&appid=" + appId + "&sign=" + sign + "&salt=" + salt + "&q=" + q;
string url = "https://fanyi-api.baidu.com/api/trans/vip/translate?" + parames;
//线程同步 - 独占锁
acquireExclusiveLock();
//计算冷却时间,应百度翻译新版API要求,加入频率设定
int tickCount = HostGetTickCount();
int sleepTime = nextExecuteTime - tickCount;
//冷却处理
if(sleepTime > 0){//如果冷却时间还没到,有需要休息的部分
HostSleep(sleepTime);//那么就休息这些时间
}
//请求翻译
string html = HostUrlGetString(url, userAgent);
//更新下次执行任务的时间
nextExecuteTime = coolTime + HostGetTickCount();//上面 HostUrlGetString 需要时间执行,所以需要重新获取 TickCount
//线程同步 - 释放独占锁
releaseExclusiveLock();
//解析翻译结果
if(!html.empty()){//如果成功取得 Html 内容
ret = JsonParse(html);//那么解析这个 HTML 里面的 json 内容
}
//翻译结果特殊处理
if(text == ret){//如果翻译后的译文,跟原文一致
if(srcLang == "zh" && dstLang == "cht"){// 简体 转 繁体
//不进行任何处理
}else if(srcLang == "cht" && dstLang == "zh"){// 繁体 转 简体
//不进行任何处理
}else{
ret = " ";//那么忽略这个字幕
}
}
if(ret.length() > 0){//如果有翻译结果
srcLang = "UTF8";
dstLang = "UTF8";
}
}
return ret;
}
/** 获取语言 */
string GetLang(string &in lang){
string result = lang;
if(result.empty()){//空字符串
result = "auto";
} else if(result == "zh-CN"){//简体中文
result = "zh";
} else if(result == "zh-TW"){//繁体中文
result = "cht";
} else if(result == "ja"){//日语
result = "jp";
} else if(result == "ro"){//罗马尼亚语
result = "rom";
}
return result;
}
/** 支持的语言列表 */
array<string> langTable = {
"zh-CN",//->zh
"zh-TW",//->cht
"en",
"ja",//->jp
"kor",
"fra",
"spa",
"th",
"ara",
"ru",
"pt",
"de",
"it",
"el",
"nl",
"pl",
"bul",
"est",
"dan",
"fin",
"cs",
"ro",//->rom
"slo",
"swe",
"hu",
"vie",
"yue",//粤语
"wyw",//文言文
};
/** 获取支持语言 */
array<string> GetLangTable(){
return langTable;
}
/** 解析Json数据
* @param json 服务器返回的Json字符串
*/
string JsonParse(string json){
string ret = "";//返回值
JsonReader reader;
JsonValue root;
if (reader.parse(json, root)){//如果成功解析了json内容
if(root.isObject()){//要求是对象模式
array<string> keys = root.getKeys();//获取json root对象中所有的key
//查找是否存在错误
if(hasErrorInResult(keys)){//如果发生了错误
JsonValue errorCode = root["error_code"];//错误编号
JsonValue errorMsg = root["error_msg"];//错误信息描述
ret = "error: " + errorCode.asString() + ", error_msg=" + errorMsg.asString();
}else{//如果没发生错误
JsonValue transResult = root["trans_result"];//取得翻译结果
if(transResult.isArray()){//如果有翻译结果-必须是数组形式
for(int i = 0; i < transResult.size(); i++){
JsonValue item = transResult[i];//取得翻译结果
JsonValue dst = item["dst"];//获取翻译结果的目标
if(i > 0){//如果需要处理多行的情况
ret += "\n";//第二行开始的开头位置,加上换行符
}
ret += dst.asString();//拼接翻译结果,可能存在多行
}
}
}
}
}
return ret;
}
/** 检查翻译结果返回值中是否存在错误
* @param keys json root 层的 key 列表
*/
bool hasErrorInResult(array<string> keys){
bool result = false;
for(uint i = 0; i < keys.size(); i++){
if("error_code" == keys[i]){
result = true;
break;
}
}
return result;
}
/** 上独占锁
* - 当前仅仅只是模拟版,还有 bug ,不过暂时可临时使用
*/
void acquireExclusiveLock(){
int tickCount1 = HostGetTickCount();//取得第一个时刻
HostSleep(1);
int tickCount2 = HostGetTickCount();//取得第二个时刻
/**
注意:
1、这是一个临时的方案
2、因为我本地尝试:HostLoadLibrary("Kernel32.dll") 没能正常工作,所以才采用当前这个临时方案
3、key 原本应该是唯一的,不然可能存在多个线程得到的是同一个tickCount。会导致多个线程同时执行,意味着这多个线程只能成功一个翻译,虽然已经做了部分防御,但是不能确保万一!
4、当然,上方的触发的概率不高,不过确实存在这个bug。
5、所以当前只能作为临时方案,有更好的方案时,必须替换掉
*/
int key = tickCount1 << 16 + (tickCount2 & 0xFFFF);//两个时刻合并,使得多线程重复相同数字的概率下降,但还是有可能重复,当前这个算法,仅仅能作为临时的解决方案而已!
while(executeThreadId != key){
if(executeThreadId == NULL){//如果没其他任务在执行了
executeThreadId = key;//尝试注册当前任务为执行任务
}
HostSleep(1);//休息下,看看有没有抢着注册的其他线程任务,或者等待正在执行的任务解除锁
if(executeThreadId == key){//如果没被其他线程抢注册了
HostSleep(1);//再次休息下
if(executeThreadId == key){//二次确认,确保原子性
break;//成功抢到执行权限,不必再等待了
}
}
}
}
/** 释放独占锁
* - 当前仅仅只是模拟版,还有 bug ,不过暂时可临时使用
*/
void releaseExclusiveLock(){
executeThreadId = NULL;//解除锁
}