├── LICENSE ├── SubtitleTranslate - baidu.as ├── SubtitleTranslate - baidu.ico ├── readme.md └── 使用方法.txt /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 fjqingyou 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /SubtitleTranslate - baidu.as: -------------------------------------------------------------------------------- 1 | /* 2 | real time subtitle translate for PotPlayer using Bai Du API 3 | */ 4 | 5 | // string GetTitle() -> get title for UI 6 | // string GetVersion -> get version for manage 7 | // string GetDesc() -> get detail information 8 | // string GetLoginTitle() -> get title for login dialog 9 | // string GetLoginDesc() -> get desc for login dialog 10 | // string ServerLogin(string User, string Pass) -> login 11 | // string ServerLogout() -> logout 12 | // array GetSrcLangs() -> get source language 13 | // array GetDstLangs() -> get target language 14 | // string Translate(string Text, string &in SrcLang, string &in DstLang) -> do translate !! 15 | 16 | 17 | //必须配置的部分,不过现在已经移交到“实时字幕翻译”中了 18 | //它的位置是: 打开任意视频或者点击左上角的PolPlayer -> 字幕 -> 实时字幕翻译 -> 实时字幕翻译设置 -> 选中百度翻译 -> 点右边的 “账户设置” 19 | string appId = "";//appid 20 | string toKen = "";//密钥 21 | 22 | //可选配置,一般而言是不用修改的! 23 | int coolTime = 1300;//冷却时间,这里的单位是毫秒,1秒钟=1000毫秒,如果提示 error:54003, 那么就加大这个数字,建议一次加100 24 | string userAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36";//这个是可选配置,一般不用修改! 25 | 26 | //执行环境,请不要修改! 27 | int NULL = 0; 28 | int executeThreadId = NULL;//这个变量的命名是我的目标,不过,暂时没能实现!只是做了个还有小bug的临时替代方案 29 | int nextExecuteTime = 0;//下次执行代码的时间 30 | 31 | 32 | /** 获取当前插件的版本号*/ 33 | string GetVersion(){ 34 | return "1"; 35 | } 36 | 37 | /** 获取当前插件的标题 */ 38 | string GetTitle(){ 39 | return "{$CP950=百度翻譯$}{$CP936=百度翻译$}{$CP0=Bai Du translate$}"; 40 | } 41 | 42 | 43 | /** 获取当前插件的表述信息 */ 44 | string GetDesc(){ 45 | return "https://fanyi.baidu.com/"; 46 | } 47 | 48 | /** 获取登录的标题 */ 49 | string GetLoginTitle(){ 50 | return "请输入配置"; 51 | } 52 | 53 | /** 获取登录的描述信息 */ 54 | string GetLoginDesc(){ 55 | return "请输入AppId和密钥!"; 56 | } 57 | 58 | 59 | /** 获取登录时,用户输入框的标签名称 */ 60 | string GetUserText(){ 61 | return "App ID:"; 62 | } 63 | 64 | /** 获取登录时,密码输入框的标签名称 */ 65 | string GetPasswordText(){ 66 | return "密钥:"; 67 | } 68 | 69 | 70 | /** 获取支持的语言列表 - 源语言 */ 71 | array GetSrcLangs(){ 72 | array ret = GetLangTable(); 73 | 74 | ret.insertAt(0, ""); // empty is auto 75 | return ret; 76 | } 77 | 78 | /** 获取支持的语言列表 - 目标语言 */ 79 | array GetDstLangs(){ 80 | return GetLangTable(); 81 | } 82 | 83 | /** 登录账号入口 84 | * - 这里不做校验了,只要有输入就判定为成功。具体测试由用户的翻译测试按钮去测试 85 | * @param appIdStr appid 字符串 86 | * @param toKenStr 秘钥字符串 87 | */ 88 | string ServerLogin(string appIdStr, string toKenStr){ 89 | //空字符串校验 90 | if(appIdStr.empty() || toKenStr.empty()) return "fail"; 91 | 92 | //记录到全局变量中 93 | appId = appIdStr; 94 | toKen = toKenStr; 95 | return "200 ok"; 96 | } 97 | 98 | 99 | /** 翻译的入口 100 | * @param text 待翻译的原文 101 | * @param srcLang 当前语言 102 | * @param dstLang 目标语言 103 | */ 104 | string Translate(string text, string &in srcLang, string &in dstLang){ 105 | string ret = ""; 106 | if(!text.empty()){//确实有内容需要翻译才有必要继续 107 | //开发文档。需要App id 等信息 108 | //http://api.fanyi.baidu.com/api/trans/product/apidoc 109 | // HostOpenConsole(); // for debug 110 | 111 | //语言选择 112 | srcLang = GetLang(srcLang); 113 | dstLang = GetLang(dstLang); 114 | 115 | //对原文进行 url 编码 116 | string q = HostUrlEncode(text); 117 | 118 | //构建请求的 url 地址 119 | string salt = "" + HostGetTickCount();//随机数 120 | string sign = HostHashMD5(appId + text + salt + toKen);//签名 appid+q+salt+密钥 121 | string parames = "from=" + srcLang + "&to=" + dstLang + "&appid=" + appId + "&sign=" + sign + "&salt=" + salt + "&q=" + q; 122 | string url = "https://fanyi-api.baidu.com/api/trans/vip/translate?" + parames; 123 | 124 | //线程同步 - 独占锁 125 | acquireExclusiveLock(); 126 | 127 | //计算冷却时间,应百度翻译新版API要求,加入频率设定 128 | int tickCount = HostGetTickCount(); 129 | int sleepTime = nextExecuteTime - tickCount; 130 | 131 | //冷却处理 132 | if(sleepTime > 0){//如果冷却时间还没到,有需要休息的部分 133 | HostSleep(sleepTime);//那么就休息这些时间 134 | } 135 | 136 | //请求翻译 137 | string html = HostUrlGetString(url, userAgent); 138 | 139 | //更新下次执行任务的时间 140 | nextExecuteTime = coolTime + HostGetTickCount();//上面 HostUrlGetString 需要时间执行,所以需要重新获取 TickCount 141 | 142 | //线程同步 - 释放独占锁 143 | releaseExclusiveLock(); 144 | 145 | //解析翻译结果 146 | if(!html.empty()){//如果成功取得 Html 内容 147 | ret = JsonParse(html);//那么解析这个 HTML 里面的 json 内容 148 | } 149 | 150 | //翻译结果特殊处理 151 | if(text == ret){//如果翻译后的译文,跟原文一致 152 | if(srcLang == "zh" && dstLang == "cht"){// 简体 转 繁体 153 | //不进行任何处理 154 | }else if(srcLang == "cht" && dstLang == "zh"){// 繁体 转 简体 155 | //不进行任何处理 156 | }else{ 157 | ret = " ";//那么忽略这个字幕 158 | } 159 | } 160 | 161 | if(ret.length() > 0){//如果有翻译结果 162 | srcLang = "UTF8"; 163 | dstLang = "UTF8"; 164 | } 165 | } 166 | return ret; 167 | } 168 | 169 | /** 获取语言 */ 170 | string GetLang(string &in lang){ 171 | string result = lang; 172 | 173 | if(result.empty()){//空字符串 174 | result = "auto"; 175 | } else if(result == "zh-CN"){//简体中文 176 | result = "zh"; 177 | } else if(result == "zh-TW"){//繁体中文 178 | result = "cht"; 179 | } else if(result == "ja"){//日语 180 | result = "jp"; 181 | } else if(result == "ro"){//罗马尼亚语 182 | result = "rom"; 183 | } 184 | 185 | return result; 186 | } 187 | 188 | 189 | /** 支持的语言列表 */ 190 | array langTable = { 191 | "zh-CN",//->zh 192 | "zh-TW",//->cht 193 | "en", 194 | "ja",//->jp 195 | "kor", 196 | "fra", 197 | "spa", 198 | "th", 199 | "ara", 200 | "ru", 201 | "pt", 202 | "de", 203 | "it", 204 | "el", 205 | "nl", 206 | "pl", 207 | "bul", 208 | "est", 209 | "dan", 210 | "fin", 211 | "cs", 212 | "ro",//->rom 213 | "slo", 214 | "swe", 215 | "hu", 216 | "vie", 217 | "yue",//粤语 218 | "wyw",//文言文 219 | }; 220 | 221 | /** 获取支持语言 */ 222 | array GetLangTable(){ 223 | return langTable; 224 | } 225 | 226 | /** 解析Json数据 227 | * @param json 服务器返回的Json字符串 228 | */ 229 | string JsonParse(string json){ 230 | string ret = "";//返回值 231 | JsonReader reader; 232 | JsonValue root; 233 | 234 | if (reader.parse(json, root)){//如果成功解析了json内容 235 | if(root.isObject()){//要求是对象模式 236 | array keys = root.getKeys();//获取json root对象中所有的key 237 | //查找是否存在错误 238 | if(hasErrorInResult(keys)){//如果发生了错误 239 | JsonValue errorCode = root["error_code"];//错误编号 240 | JsonValue errorMsg = root["error_msg"];//错误信息描述 241 | ret = "error: " + errorCode.asString() + ", error_msg=" + errorMsg.asString(); 242 | }else{//如果没发生错误 243 | JsonValue transResult = root["trans_result"];//取得翻译结果 244 | if(transResult.isArray()){//如果有翻译结果-必须是数组形式 245 | for(int i = 0; i < transResult.size(); i++){ 246 | JsonValue item = transResult[i];//取得翻译结果 247 | JsonValue dst = item["dst"];//获取翻译结果的目标 248 | if(i > 0){//如果需要处理多行的情况 249 | ret += "\n";//第二行开始的开头位置,加上换行符 250 | } 251 | ret += dst.asString();//拼接翻译结果,可能存在多行 252 | } 253 | } 254 | } 255 | } 256 | } 257 | return ret; 258 | } 259 | 260 | /** 检查翻译结果返回值中是否存在错误 261 | * @param keys json root 层的 key 列表 262 | */ 263 | bool hasErrorInResult(array keys){ 264 | bool result = false; 265 | for(uint i = 0; i < keys.size(); i++){ 266 | if("error_code" == keys[i]){ 267 | result = true; 268 | break; 269 | } 270 | } 271 | return result; 272 | } 273 | 274 | /** 上独占锁 275 | * - 当前仅仅只是模拟版,还有 bug ,不过暂时可临时使用 276 | */ 277 | void acquireExclusiveLock(){ 278 | int tickCount1 = HostGetTickCount();//取得第一个时刻 279 | HostSleep(1); 280 | int tickCount2 = HostGetTickCount();//取得第二个时刻 281 | /** 282 | 注意: 283 | 1、这是一个临时的方案 284 | 2、因为我本地尝试:HostLoadLibrary("Kernel32.dll") 没能正常工作,所以才采用当前这个临时方案 285 | 3、key 原本应该是唯一的,不然可能存在多个线程得到的是同一个tickCount。会导致多个线程同时执行,意味着这多个线程只能成功一个翻译,虽然已经做了部分防御,但是不能确保万一! 286 | 4、当然,上方的触发的概率不高,不过确实存在这个bug。 287 | 5、所以当前只能作为临时方案,有更好的方案时,必须替换掉 288 | */ 289 | int key = tickCount1 << 16 + (tickCount2 & 0xFFFF);//两个时刻合并,使得多线程重复相同数字的概率下降,但还是有可能重复,当前这个算法,仅仅能作为临时的解决方案而已! 290 | 291 | while(executeThreadId != key){ 292 | if(executeThreadId == NULL){//如果没其他任务在执行了 293 | executeThreadId = key;//尝试注册当前任务为执行任务 294 | } 295 | 296 | HostSleep(1);//休息下,看看有没有抢着注册的其他线程任务,或者等待正在执行的任务解除锁 297 | 298 | if(executeThreadId == key){//如果没被其他线程抢注册了 299 | HostSleep(1);//再次休息下 300 | if(executeThreadId == key){//二次确认,确保原子性 301 | break;//成功抢到执行权限,不必再等待了 302 | } 303 | } 304 | } 305 | } 306 | 307 | /** 释放独占锁 308 | * - 当前仅仅只是模拟版,还有 bug ,不过暂时可临时使用 309 | */ 310 | void releaseExclusiveLock(){ 311 | executeThreadId = NULL;//解除锁 312 | } -------------------------------------------------------------------------------- /SubtitleTranslate - baidu.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fjqingyou/PotPlayer_Subtitle_Translate_Baidu/7b9f9d7a46a4003d4f8df46e30b798340eb699ea/SubtitleTranslate - baidu.ico -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 声明 2 | > 3 | >1、这个是机器翻译。所以翻译效果肯定不是很好,请不要抱太高的期望! 4 | > 5 | >2、我个人使用的环境是:不带字幕的视频+ass外挂字幕+在线翻译这样的结构。推荐也是用这个模式,内嵌字幕可能是硬绘制在视频帧图片里面,那种情况的视频暂时没法翻译。建议先找一个这种格式的,测试集成效果! 6 | > 7 | >3、我只是源码开源者,只为技术交流,拒绝承担任何责任! 8 | ---- 9 | 10 | # 费用方面 - 推荐选择完全免费的方式 11 | > 12 | >1、首先。当前插件是完全免费的! 13 | > 14 | >2、百度翻译默认开通的'标准版'服务当前是免费的,但是 2022年8月1日起将调整为 **免费5万字符/月**,可参考: [【百度翻译开放平台】关于通用文本翻译API版本权益调整的通知](https://github.com/fjqingyou/PotPlayer_Subtitle_Translate_Baidu/issues/24),而具体详情请查阅百度翻译的官方文档和公告信息 15 | ---- 16 | 17 | # 安装使用 18 | 19 | ## 第一步:必须开通百度翻译的开发者,并注册一个应用 20 | 21 | >1、前往网址: 22 | > 23 | >2、登录你自己的百度账号 24 | > 25 | >3、第1的网址打开后点“产品服务”,拉到下面,有个“立即使用” 26 | > 27 | >4、填写百度翻译要求的必要信息,并完成应用注册,不要填写IP地址,其他的填写啥看你个人喜好咯! 28 | ---- 29 | 30 | ## 第二步:安装翻译插件 31 | 32 | >1、将SubtitleTranslate - baidu.as、SubtitleTranslate - baidu.ico这两个文件选中,Ctrl+C复制 33 | > 34 | >2、打开PotPlayer播放器的安装路径(方法不会的请百度、谷歌等等方式搜索) 35 | > 36 | >3、再进入Extention文件夹,接着又进入Subtitle文件夹、最后进入Translate文件夹 37 | > 38 | >4、Ctrl+V,把刚才选择的两个文件粘贴到这个文件夹 39 | ---- 40 | 41 | ## 第三步:获取和配置翻译插件的 appId 与 密钥 42 | 43 | >1、第一步中的1,网址,点击“管理控制台” 44 | > 45 | >2、我的服务下面一点,如果有“此服务已停用 开启(这两个字蓝色)”的提示,点击“开启” 46 | > 47 | >3、拉到底部,会有“申请信息”,里面包含 “APP ID” 和 “密钥” 48 | > 49 | >4、随便打开一个带外挂字幕的视频,例如外挂ass字幕文件的视频! 50 | > 51 | >5、右键点击视频->字幕->在线字幕翻译->实时字幕翻译设置->选中百度翻译-> 点右边的 “账户设置”,会弹出一个对话框。 52 | > 53 | >6、将上面第 3 步得到的“APP ID” 和 “密钥”分别填写进去,然后点确定,会再次弹出对话框,点击关闭就行了。 54 | ---- 55 | 56 | ## 第四步:测试 57 | 58 | >1、随便打开一个带中文字幕的视频(如果上方打开的视频没关闭,不必重新打开) 59 | > 60 | >2、右键点击视频->字幕->在线字幕翻译->Bai Du translate 61 | > 62 | >3、右键点击视频->字幕->在线字幕翻译->总是使用(注:这个看个人需求) 63 | > 64 | >4、右键点击视频->字幕->在线字幕翻译->下面显示翻译(注:这个看个人需求) 65 | > 66 | >5、右键点击视频->字幕->在线字幕翻译->目标语言->之后在语言列表选择你要的,例如英语,日语、汉语等等任意一个,看个人需求。 67 | > 68 | >6、畅玩吧 69 | ---- 70 | 71 | ## 可选的配置 - 翻译请求的频率 72 | 73 | >1、这个配置是可选的,一般不用配置的 74 | > 75 | >2、原因:百度翻译有默认翻译冷却时间,短时间内多次传输翻译请求,那么很可能会被拦截,如果翻译结果会提示 error:54003……那么就是被拦截了。如果出现这个,那么才有必要做这个配置,否则就不需要这个配置的!具体请参考 ‘**费用方面**’ 这个部分的描述 76 | > 77 | >3、用记事本等文本编辑器打开刚才安装后的SubtitleTranslate - baidu.as这个文件(不是当前这个文本的同一个文件夹里的文件哦,是配置到PolPlayer里面的那个文件!) 78 | > 79 | >4、修改大概23行位置的 int coolTime = 1000; 数字1000,将它加大一些,然后保存好文件再试试,如果不够就再加大一些,1000是指1秒钟,1000毫秒=1秒,是指一条字幕翻译后,下一条字幕最少需要等多久才开始翻译的意思。加大数字则代表降低请求翻译的频率,反之减少则提高请求的频率。例如设置为100时就是一秒钟翻译10条的意思,设置10000时则是10秒翻译1条的意思。请注意不要删除掉这行的最后一个英文的分号 80 | > 81 | >5、保存文件 82 | > 83 | >6、如果开着PotPlayer则关闭再重新打开,没开的直接打开就行了,然后就可以试试效果了。 84 | ---- 85 | 86 | # 常见错误 87 | > 88 | >如果集成后,翻译结果出现:error: 数字,error_msg:英语,那么根据数字,在下方表格查找原因: 89 | > 90 | >|错误码|含义|推荐的解决方法|官方的解决方法| 91 | >|----|----|----|----| 92 | >|52000|成功|已经成功,无需处理| | 93 | >|52001|请求超时|应该是网络问题,重试即可|重试| 94 | >|52002|系统错误|服务器错误,重试即可|重试| 95 | >|52003|未授权用户|参考右侧|检查您的 appid 是否正确,或者服务是否开通| 96 | >|54000|必填参数为空|应该是插件问题,建议反馈|检查是否少传参数| 97 | >|54001|签名错误|有可能是Potplay版本过低,在 Potplay(64位版),版本 230405(1.7.21900) 可正常工作。或者插件问题,建议反馈|请检查您的签名生成方法| 98 | >|54003|访问频率受限|提高 coolTime 的间隔时间,进而降低翻译频率|请降低您的调用频率| 99 | >|54004|账户余额不足|当前这个账号的本月翻译内容过多,等下月恢复额度,或者参考右侧|请前往管理控制台为账户充值| 100 | >|54005|长query请求频繁|提高 coolTime 的间隔时间,进而降低翻译频率|请降低长query的发送频率,3s后再试| 101 | >|58000|客户端IP非法|参考右侧,没拥有固定IP时,推荐不填写|检查个人资料里填写的IP地址是否正确,可前往管理控制台平台修改IP限制,IP可留空| 102 | >|58001|译文语言方向不支持|参考右侧|检查译文语言是否在语言列表里| 103 | >|58002|服务当前已关闭|参考右侧|请前往管理控制台开启服务| 104 | >|90107|认证未通过或未生效|参考右侧|请前往我的认证查看认证进度| 105 | > 106 | ---- 107 | 108 | # 最后:参与扩展 109 | 110 | >欢迎广大网友们继续扩展优化,欢迎任何有益的合并请求! 111 | > 112 | >github发布地址: 113 | > 114 | >gitee发布地址: 115 | > 116 | ---- 117 | -------------------------------------------------------------------------------- /使用方法.txt: -------------------------------------------------------------------------------- 1 | 1、首先,请在跟当前文件的同一个文件夹下,找到 readme.md 文件。 2 | 2、用鼠标点着它不放,然后拖动鼠标到现在您看到文字的这里。接着松开鼠标 3 | 3、之后你会发现,当前这里的内容发生了变化。直接看变化后的内容就好了! 4 | 4、如果之后关闭文件提示保存,点否,如果没提示直接无视本行说明! 5 | --------------------------------------------------------------------------------