SubtitleTranslate - baidu.as 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. /*
  2. real time subtitle translate for PotPlayer using Bai Du API
  3. */
  4. // string GetTitle() -> get title for UI
  5. // string GetVersion -> get version for manage
  6. // string GetDesc() -> get detail information
  7. // string GetLoginTitle() -> get title for login dialog
  8. // string GetLoginDesc() -> get desc for login dialog
  9. // string ServerLogin(string User, string Pass) -> login
  10. // string ServerLogout() -> logout
  11. // array<string> GetSrcLangs() -> get source language
  12. // array<string> GetDstLangs() -> get target language
  13. // string Translate(string Text, string &in SrcLang, string &in DstLang) -> do translate !!
  14. //必须配置的部分,不过现在已经移交到“实时字幕翻译”中了
  15. //它的位置是: 打开任意视频或者点击左上角的PolPlayer -> 字幕 -> 实时字幕翻译 -> 实时字幕翻译设置 -> 选中百度翻译 -> 点右边的 “账户设置”
  16. string appId = "";//appid
  17. string toKey = "";//密钥
  18. //可选配置,一般而言是不用修改的!
  19. int coolTime = 1000;//冷却时间,这里的单位是毫秒,1秒钟=1000毫秒,如果提示 error:54003, 那么就加大这个数字,建议一次加100
  20. string userAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36";//这个是可选配置,一般不用修改!
  21. //执行环境,请不要修改!
  22. int NULL = 0;
  23. int executeThreadId = NULL;//这个变量的命名是我的目标,不过,暂时没能实现!只是做了个还有小bug的临时替代方案
  24. int nextExecuteTime = 0;//下次执行代码的时间
  25. string GetVersion(){
  26. return "1";
  27. }
  28. string GetTitle(){
  29. return "{$CP950=Bai Du 翻譯$}{$CP0=Bai Du translate$}";
  30. }
  31. string GetDesc(){
  32. return "https://fanyi.baidu.com/";
  33. }
  34. string GetLoginTitle(){
  35. return "请输入配置";
  36. }
  37. string GetLoginDesc(){
  38. return "请输入AppId和密钥!";
  39. }
  40. string GetUserText(){
  41. return "App ID:";
  42. }
  43. string GetPasswordText(){
  44. return "密钥:";
  45. }
  46. array<string> GetSrcLangs(){
  47. array<string> ret = GetLangTable();
  48. ret.insertAt(0, ""); // empty is auto
  49. return ret;
  50. }
  51. array<string> GetDstLangs(){
  52. return GetLangTable();
  53. }
  54. string ServerLogin(string appIdStr, string toKeyStr){
  55. if (appIdStr.empty() || toKeyStr.empty()) return "fail";
  56. appId = appIdStr;
  57. toKey = toKeyStr;
  58. return "200 ok";
  59. }
  60. string Translate(string text, string &in srcLang, string &in dstLang){
  61. string ret = "";
  62. if(!text.empty()){//确实有内容需要翻译才有必要继续
  63. //开发文档。需要App id 等信息
  64. //http://api.fanyi.baidu.com/api/trans/product/apidoc
  65. // HostOpenConsole(); // for debug
  66. //语言选择
  67. srcLang = GetLang(srcLang);
  68. dstLang = GetLang(dstLang);
  69. // API.. Always UTF-8
  70. string q = HostUrlEncode(text);
  71. string salt = "" + HostGetTickCount();//随机数
  72. string sign = HostHashMD5(appId + text + salt + toKey);//签名 appid+q+salt+密钥
  73. string parames = "from=" + srcLang + "&to=" + dstLang + "&appid=" + appId + "&sign=" + sign + "&salt=" + salt + "&q=" + q;
  74. string url = "http://api.fanyi.baidu.com/api/trans/vip/translate?" + parames;
  75. //线程同步 - 独占锁
  76. acquireExclusiveLock();
  77. //计算冷却时间,应百度翻译新版API要求,加入频率设定
  78. int tickCount = HostGetTickCount();
  79. int sleepTime = nextExecuteTime - tickCount;
  80. // HostPrintUTF8("tickCount == " + tickCount + " sleepTime == " + sleepTime);// for debug
  81. if(sleepTime > 0){//如果冷却时间还没到,有需要休息的部分
  82. HostSleep(sleepTime);//那么就休息这些时间
  83. }
  84. // HostPrintUTF8("url == " + url);// for debug
  85. string html = HostUrlGetString(url, userAgent);
  86. //更新下次执行任务的时间
  87. nextExecuteTime = coolTime + HostGetTickCount();//上面 HostUrlGetString 需要时间执行,所以需要重新获取 TickCount
  88. //线程同步 - 释放独占锁
  89. releaseExclusiveLock();
  90. if(!html.empty()){//如果成功取得 Html 内容
  91. ret = JsonParse(html);//那么解析这个 HTML 里面的 json 内容
  92. }
  93. if (ret.length() > 0){//如果有翻译结果
  94. srcLang = "UTF8";
  95. dstLang = "UTF8";
  96. }
  97. if(text == ret){//如果翻译后的译文,跟原文一致
  98. ret = "";//那么忽略这个字幕
  99. }
  100. }
  101. return ret;
  102. }
  103. //获取语言
  104. string GetLang(string &in lang){
  105. string result = lang;
  106. if(result.empty()){//空字符串
  107. result = "auto";
  108. } else if(result == "zh-CN"){//简体中文
  109. result = "zh";
  110. } else if(result == "zh-TW"){//繁体中文
  111. result = "cht";
  112. } else if(result == "ja"){//日语
  113. result = "jp";
  114. } else if(result == "ro"){//罗马尼亚语
  115. result = "rom";
  116. }
  117. return result;
  118. }
  119. array<string> langTable = {
  120. "zh-CN",//->zh
  121. "zh-TW",//->cht
  122. "en",
  123. "ja",//->jp
  124. "kor",
  125. "fra",
  126. "spa",
  127. "th",
  128. "ara",
  129. "ru",
  130. "pt",
  131. "de",
  132. "it",
  133. "el",
  134. "nl",
  135. "pl",
  136. "bul",
  137. "est",
  138. "dan",
  139. "fin",
  140. "cs",
  141. "ro",//->rom
  142. "slo",
  143. "swe",
  144. "hu",
  145. "vie"
  146. "yue",//粤语
  147. "wyw",//文言文
  148. };
  149. //获取支持语言
  150. array<string> GetLangTable(){
  151. return langTable;
  152. }
  153. //解析Json数据
  154. string JsonParse(string json){
  155. string ret = "";//返回值
  156. JsonReader reader;
  157. JsonValue root;
  158. if (reader.parse(json, root)){//如果成功解析了json内容
  159. if(root.isObject()){//要求是对象模式
  160. bool hasError = false;
  161. array<string> keys = root.getKeys();//获取json root对象中所有的key
  162. //查找是否存在错误
  163. for(uint i = 0; i < keys.size(); i++){
  164. if("error_code" == keys[i]){
  165. hasError = true;
  166. break;
  167. }
  168. }
  169. if(hasError){//如果发生了错误
  170. JsonValue errorCode = root["error_code"];//错误编号
  171. JsonValue errorMsg = root["error_msg"];//错误信息描述
  172. ret = "error: " + errorCode.asString() + ", error_msg=" + errorMsg.asString();
  173. }else{//如果没发生错误
  174. JsonValue transResult = root["trans_result"];//取得翻译结果
  175. if(transResult.isArray()){//如果有翻译结果-必须是数组形式
  176. for(int i = 0; i < transResult.size(); i++){
  177. JsonValue item = transResult[i];//取得翻译结果
  178. JsonValue dst = item["dst"];//获取翻译结果的目标
  179. if(i > 0){//如果需要处理多行的情况
  180. ret += "\n";//第二行开始的开头位置,加上换行符
  181. }
  182. ret += dst.asString();//拼接翻译结果,可能存在多行
  183. }
  184. }
  185. }
  186. }
  187. }
  188. return ret;
  189. }
  190. /**
  191. 上独占锁 - 当前仅仅只是模拟版,还有 bug ,不过暂时可临时使用
  192. */
  193. void acquireExclusiveLock(){
  194. int tickCount1 = HostGetTickCount();//取得第一个时刻
  195. HostSleep(1);
  196. int tickCount2 = HostGetTickCount();//取得第二个时刻
  197. /**
  198. 注意:
  199. 1、这是一个临时的方案
  200. 2、因为我本地尝试:HostLoadLibrary("Kernel32.dll") 没能正常工作,所以才采用当前这个临时方案
  201. 3、key 原本应该是唯一的,不然可能存在多个线程得到的是同一个tickCount。会导致多个线程同时执行,意味着这多个线程只能成功一个翻译,虽然已经做了部分防御,但是不能确保万一!
  202. 4、当然,上方的触发的概率不高,不过确实存在这个bug。
  203. 5、所以当前只能作为临时方案,有更好的方案时,必须替换掉
  204. */
  205. int key = tickCount1 << 16 + (tickCount2 & 0xFFFF);//两个时刻合并,使得多线程重复相同数字的概率下降,但还是有可能重复,当前这个算法,仅仅能作为临时的解决方案而已!
  206. while(executeThreadId != key){
  207. if(executeThreadId == NULL){//如果没其他任务在执行了
  208. executeThreadId = key;//尝试注册当前任务为执行任务
  209. }
  210. HostSleep(1);//休息下,看看有没有抢着注册的其他线程任务,或者等待正在执行的任务解除锁
  211. if(executeThreadId == key){//如果没被其他线程抢注册了
  212. HostSleep(1);//再次休息下
  213. if(executeThreadId == key){//二次确认,确保原子性
  214. break;//成功抢到执行权限,不必再等待了
  215. }
  216. }
  217. }
  218. }
  219. /**
  220. 释放独占锁 - 当前仅仅只是模拟版,还有 bug ,不过暂时可临时使用
  221. */
  222. void releaseExclusiveLock(){
  223. executeThreadId = NULL;//解除锁
  224. }