├── README.md ├── api.txt ├── src ├── MediaPlayParse - PanVideo.as ├── MediaPlayParse - PanVideo.ico └── PotplayerPanVideoSV.js ├── 字幕api填写.png ├── 清晰度选择.png └── 迅雷captcha_sign.txt /README.md: -------------------------------------------------------------------------------- 1 | # PotplayerPanVideoSV 2 | 3 | ***如果发现不能使用,请按照步骤更新两个脚本。*** 4 | 5 | **没有网盘,或是不想用网盘,只想在线看视频,跳转到[Github_PotplayerM3U8](https://github.com/Bleu404/PotplayerM3U8)** 6 | 7 | ## 步骤 8 | * 0.安装[蓝奏云Potpalyer](https://bleu.lanzoue.com/iFZgu07w69vg),密码:3mk7 9 | 10 | * 1.打开PotPlayer安装路径,选择目录`Extension\Media\PlayParse`,将复制的`MediaPlayParse - PanVideo.as`和`MediaPlayParse - PanVideo.ico`文件粘贴; 11 | 12 | >MediaPlayParse - PanVideo.as[下载地址](https://github.com/Bleu404/PotplayerPanVideoSV/raw/main/src/MediaPlayParse%20-%20PanVideo.as) 13 | 14 | * 2.脚本管理器安装[`PotplayerPanVideoSV.js`](https://greasyfork.org/scripts/442448-potplayer%E4%BA%91%E7%9B%98-%E4%B8%93%E4%BE%9B%E7%89%88/code/PotPlayer%E4%BA%91%E7%9B%98-%E4%B8%93%E4%BE%9B%E7%89%88.user.js); 15 | 16 | * 3.在脚本管理器中配置WEBDAV`主机`、`用户`、`密码`; 17 | 18 | >3.1 注册附带webdav的网盘(坚果云、TeraCLOUD等)、TeraCLOUD用我的推荐码:***DDGYA***,可额外获得5g 19 | 3.2 密码不是登陆密码,是授权第三方应用的密码 20 | 21 | * 4.选择好需要播放的视频,`右键`=>`PotPlayer打开`; 22 | 23 | * 5.~~为了再次打开potplayer,也能播放。在`Extension\Media\PlayParse`文件夹下新建`panvideo.txt`,文件内容为`主机`、`用户`、`密码`,格式如下~~ 24 | 25 | >~~dav.jianguoyun.com/dav~~ 26 | ~~12345678@qq.com~~ 27 | ~~123456789~~ 28 | 29 | ## 字幕 30 | 31 | * 1.从[opensubtitles.com](https://www.opensubtitles.com/)获取英文字幕,因为每人网络状况不同,可能出现开启字幕无法加载视频的状况; 32 | 33 | * 2.注册账号并开启API之后每个api每天100(或更多)个字幕; 34 | 35 | * 3.修改`MediaPlayParse - PanVideo.as`中的代码,开启字幕; 36 | 37 | >3.1 添加一个api:`array apikey ={"api"};`添加两个api:`array apikey ={"api_1","api_2"};` 38 | >>注意代码其中`,` `"` `;` 均为英文字符 39 | 40 | >3.2 `int subcount = 0;`//每个视频获取字幕个数,设置过多,可能无法加载视频。 41 | * 4.字幕api填写示例如下 42 | ![字幕api填写](https://fastly.jsdelivr.net/gh/Bleu404/PotplayerPanVideoSV@main/字幕api填写.png) 43 | ## 其他 44 | 45 | * 1.可以在PotPlayer中切换画质; 46 | 47 | * 2.迅雷云盘,支持选择原画(非会员只有300KB/s); 48 | 49 | * 3.阿里云盘,支持选择原画,链接的有效期变长了。 50 | 51 | ![画质选择](https://fastly.jsdelivr.net/gh/Bleu404/PotplayerPanVideoSV@main/清晰度选择.png) 52 | -------------------------------------------------------------------------------- /api.txt: -------------------------------------------------------------------------------- 1 | Script engine is AngelScript(http://www.angelcode.com/angelscript/) 2 | 3 | ------------------------PotPlayer's builtin type--------------------------------- 4 | intptr 5 | int pointer 6 | 7 | uintptr 8 | unsigned pointer 9 | 10 | ------------------------PotPlayer's builtin API---------------------------------- 11 | 12 | int HostGetAPIVersion() 13 | Get API version 14 | 15 | void HostOpenConsole() 16 | Open debug console 17 | 18 | void HostPrintUTF8(const string &in) 19 | Print UTF8 string 20 | 21 | void HostPrintUTF16(const string &in) 22 | Print UTF16 string 23 | 24 | string HostGetVersion() 25 | Get PotPlayer's version 26 | 27 | bool HostCheckVersion(int major, int minor, int build) 28 | Check PotPlayer's version 29 | 30 | bool HostIsWin64() 31 | Check PotPlayer is 64bit build or not 32 | 33 | void HostSleep(int ms) 34 | win32 API Sleep() 35 | 36 | uint HostGetTickCount() 37 | win32 API GetTickCount() 38 | 39 | void HostIncTimeOut(int ms) 40 | Increase function timeout time. 41 | 42 | bool HostCheckMediaFile(const string &in filename, bool Video, bool Audio, bool Playlist) 43 | Check filename is Video or Audio or Playlist 44 | 45 | string HostGetTempFolder() 46 | Get temp folder 47 | 48 | string HostGetExtension(const string &in) 49 | Get file extension 50 | 51 | string HostMBCSToUTF8(const string &in) 52 | Convert multibyte to UTF8 53 | 54 | string HostMBCSToUTF16(const string &in) 55 | Convert multibyte to UTF16 56 | 57 | string HostUTF8ToMBCS(const string &in) 58 | Convert UTF8 to multibyte 59 | 60 | string HostUTF16ToMBCS(const string &in) 61 | Convert UTF16 to multibyte 62 | 63 | string HostUTF8ToUTF16(const string &in) 64 | Convert UTF8 to UTF16 65 | 66 | string HostUTF16ToUTF8(const string &in) 67 | Convert UTF16 to UTF8 68 | 69 | string HostUrlGetString(const string &in url, const string &in UserAgent = "", const string &in Header = "", const string &in PostData = "", bool NoCookie = false) 70 | Get or Post HTTP(S) resource 71 | 72 | string HostUrlGetStringWithAPI(const string &in url, const string &in UserAgent = "", const string &in Header = "", const string &in PostData = "", bool NoCookie = false) 73 | Get or Post HTTP(S) resource with API key 74 | 75 | uintptr HostOpenHTTP(const string &in url, const string &in UserAgent = "", const string &in Header = "", const string &in PostData = "", bool NoCookie = false) 76 | Open Get or Post HTTP(S) resource 77 | 78 | uintptr HostOpenHTTPWithAPI(const string &in url, const string &in UserAgent = "", const string &in Header = "", const string &in PostData = "", bool NoCookie = false) 79 | Open Get or Post HTTP(S) resource with API key 80 | 81 | string HostGetContentHTTP(uintptr http) 82 | Get Content data 83 | 84 | string HostGetHeaderHTTP(uintptr http) 85 | Get Header data 86 | 87 | int HostGetStatusHTTP(uintptr http) 88 | Get HTTP status code 89 | 90 | void HostCloseHTTP(uintptr http) 91 | Close http 92 | 93 | uintptr HostFileOpen(const string &in filename) 94 | Open local file 95 | 96 | void HostFileClose(uintptr fp) 97 | Close local file 98 | 99 | int64 HostFileSeek(uintptr fp, int64 offset, int from = 0) 100 | Seek file's position 101 | 102 | int64 HostFileLength(uintptr fp) 103 | Get file length 104 | 105 | uint8 HostFileReadBYTE(uintptr fp) 106 | Read uint8 107 | 108 | uint16 HostFileReadWORD(uintptr fp) 109 | Read uint16 110 | 111 | uint32 HostFileReadDWORD(uintptr fp) 112 | Read uint32 113 | 114 | uint64 HostFileReadQWORD(uintptr fp) 115 | Read uint64 116 | 117 | string HostFileRead(uintptr fp, int len) 118 | Read string by len 119 | 120 | string HostUrlEncode(const string &in url) 121 | Encode Url escape 122 | 123 | string HostUrlDecode(const string &in url) 124 | Decode Url escape 125 | 126 | string HostBase64Enc(const string &in data) 127 | Encode BASE 64 128 | 129 | string HostBase64Dec(const string &in data) 130 | Decode BASE 64 131 | 132 | string HostDecompress(const string &in data) 133 | Decompress data(zip or etc...) 134 | 135 | string HostGzipCompress(const string &in data) 136 | Compress Gzip 137 | 138 | string HostGzipDeflate(const string &in data) 139 | Compress Gzip Deflate 140 | 141 | string HostHashMD5(const string &in data) 142 | Get MD5 143 | 144 | string HostHashSHA(const string &in data) 145 | Get SHA 146 | 147 | string HostHashSHA1(const string &in data) 148 | Get SHA1 149 | 150 | string HostHashSHA256(const string &in data) 151 | Get SHA 256 152 | 153 | string HostHashSHA384(const string &in data) 154 | Get SHA 384 155 | 156 | string HostHashSHA512(const string &in data) 157 | Get SHA 512 158 | 159 | bool HostCompareMovieName(const string &in filename1, const string &in filename2) 160 | Compare filename1 with filename2 161 | 162 | bool HostSaveString(const string &in key, const string &in val) 163 | Save key, value string to temp storage 164 | 165 | string HostLoadString(const string &in key, const string &in def = "") 166 | Load temp key, value string from temp storage 167 | 168 | bool HostSaveInteger(const string &in key, int value) 169 | Save key, value integer to temp storage 170 | 171 | string int HostLoadInteger(const string &in, int def = 0) 172 | Load temp key, value integer from temp storage 173 | 174 | string HostIso639LangName() 175 | Get current ISO639 Language name 176 | 177 | string HostIso3166CtryName() 178 | Get current ISO 3166 country name 179 | 180 | string HostRegExpParse(const string &in str, const string &in pattern) 181 | const std::regex regex(pattern); 182 | std::cmatch match; 183 | 184 | if (std::regex_search(str, match, regex) && match.size() == 2) 185 | { 186 | return std::string(match[1].first, match[1].length()); 187 | } 188 | 189 | return ""; 190 | 191 | bool HostRegExpParse(const string &in str, const string &in pattern, array &dic) 192 | const std::regex regex(pattern); 193 | std::cmatch match; 194 | bool ret = std::regex_search(str, match, regex); 195 | 196 | if (ret) 197 | { 198 | assign match -> dic ... "first" and "second" 199 | } 200 | return ret; 201 | 202 | string HostFixFileName(const string &in filename) 203 | Fix file for create 204 | 205 | uintptr HostLoadLibrary(const string &in filename) 206 | win32 API LoadLibrary() 207 | 208 | uintptr HostGetProcAddress(uintptr dll, const string &in symbol) 209 | win32 API GetProcAddress() 210 | 211 | void HostFreeLibrary(uintptr dll) 212 | win32 API FreeLibrary() 213 | 214 | void HostCallProcAsync(uintptr proc, const string &in paramSig, uint64 param1 = 0, uint64 param2 = 0, uint64 param3 = 0, uint64 param4 = 0, uint64 param5 = 0, uint64 param6 = 0) 215 | int HostCallProcInt(uintptr proc, const string &in paramSig, uint64 param1 = 0, uint64 param2 = 0, uint64 param3 = 0, uint64 param4 = 0, uint64 param5 = 0, uint64 param6 = 0) 216 | uintptr HostCallProcUIntPtr(uintptr proc, const string &in paramSig, uint64 param1 = 0, uint64 param2 = 0, uint64 param3 = 0, uint64 param4 = 0, uint64 param5 = 0, uint64 param6 = 0) 217 | uint64 HostCallProcUInt64(uintptr proc, const string &in paramSig, uint64 param1 = 0, uint64 param2 = 0, uint64 param3 = 0, uint64 param4 = 0, uint64 param5 = 0, uint64 param6 = 0) 218 | call dll's function 219 | paramSig -> P: Pointer, Q: int64, W: int16, B: int8, else int32 etc) "PQWI" -> Function(Pointer, int64, int16, int32); 220 | HostCallProcAsync -> Async call 221 | 222 | uintptr HostInt2UIntPtr(const int &var) 223 | uintptr HostUInt2UIntPtr(const int &var) 224 | uintptr HostString2UIntPtr(const string &in var) 225 | Get var's pointer 226 | 227 | string HostUIntPtr2String(uintptr ptr) 228 | C string(char *) to script string 229 | 230 | void HostSetUrlUserAgentHTTP(const string &url, const string &userAgent) 231 | Set useragent for HTTP url 232 | 233 | void HostSetUrlHeaderHTTP(const string &url, const string &header) 234 | Set http header for HTTP url 235 | 236 | 237 | ------------------------PotPlayer's builtin class---------------------------------- 238 | ------------------------string 239 | int replace(const string &in from, const string &in to) const 240 | string Right(int Count) const 241 | string Left(int Count) const 242 | string TrimRight(const string &in str = "") const 243 | string TrimLeft(const string &in = "") const 244 | string Trim(const string &in = "") const 245 | string MakeLower() const 246 | string MakeUpper() const 247 | 248 | 249 | ------------------------jsoncpp 250 | ------JsonValue 251 | bool isNull() 252 | bool isBool() 253 | bool isInt() 254 | bool isUInt() 255 | bool isInt64() 256 | bool isUInt64() 257 | bool isFloat() 258 | bool isDouble() 259 | bool isNumeric() 260 | bool isString() 261 | bool isArray() 262 | bool isObject() 263 | bool canString() 264 | int asInt() 265 | uint asUInt() 266 | int64 asInt64() 267 | uint64 asUInt64() 268 | float asFloat() 269 | double asDouble() 270 | bool asBool() 271 | string asString() 272 | int size() 273 | array @getKeys() const 274 | [int] 275 | [string] 276 | 277 | ------JsonReader 278 | bool parse(string &in json, JsonValue &out root) 279 | 280 | ------------------------tinyxml2 281 | ------XMLAttribute 282 | bool isValid() 283 | bool isBool() 284 | bool isInt() 285 | bool isUInt() 286 | bool isInt64() 287 | bool isDouble() 288 | bool isFloat() 289 | int asInt(int defaultValue = 0) 290 | uint asUInt(uint defaultValue = 0) 291 | int64 asInt64(int64 defaultValue = 0) 292 | float asFloat(float defaultValue = 0) 293 | double asDouble(double defaultValue = 0) 294 | bool asBool(bool defaultValue = false) 295 | string asString() 296 | string Name() 297 | string Value() 298 | XMLAttribute Next() 299 | 300 | ------XMLElement 301 | bool isValid(const string &in name = "") 302 | bool isBool(const string &in name = "") 303 | bool isInt(const string &in name = "") 304 | bool isUInt(const string &in name = "") 305 | bool isInt64(const string &in name = "") 306 | bool isDouble(const string &in name = "") 307 | bool isFloat(const string &in name = "") 308 | int asInt(const string &in name = "", int defaultValue = 0) 309 | uint asUInt(const string &in name = "", uint defaultValue = 0) 310 | int64 asInt64(const string &in name = "", int64 defaultValue = 0) 311 | float asFloat(const string &in name = "", float defaultValue = 0) 312 | double asDouble(const string &in name = "", double defaultValue = 0) 313 | bool asBool(const string &in name = "", bool defaultValue = false) 314 | string asString(const string &in name = "") 315 | string Name() 316 | string Value() 317 | string Attribute(const string &in name, const string &in value = "") 318 | XMLAttribute FindAttribute(const string &in name) 319 | XMLAttribute FirstAttribute() 320 | XMLElement FirstChildElement(const string &in name = "") 321 | XMLElement NextSiblingElement() 322 | 323 | ------XMLDocument 324 | bool Parse(string &in xml) 325 | XMLElement FirstChildElement(string &in name = "") 326 | XMLElement RootElement() 327 | 328 | ------------------------TimXmlRpc 329 | ------XmlRpcValue 330 | bool isNull() 331 | bool isBool() 332 | bool isInt() 333 | bool isDouble() 334 | bool isString() 335 | bool isArray() 336 | bool isObject() 337 | bool canString() 338 | int asInt() 339 | double asDouble() 340 | bool asBool() 341 | string asString() 342 | int size() 343 | [int] 344 | [string] 345 | 346 | ------XmlRpcClient 347 | bool execute(const string &in cmd, const XmlRpcValue &in param, XmlRpcValue &out result) 348 | 349 | -------------------------------------------------------------------------------- /src/MediaPlayParse - PanVideo.as: -------------------------------------------------------------------------------- 1 | /* 2 | PotPlayer云盘-专供版 bleu 3 | */ 4 | 5 | // void OnInitialize() 6 | // void OnFinalize() 7 | // string GetTitle() -> get title for UI 8 | // string GetVersion -> get version for manage 9 | // string GetDesc() -> get detail information 10 | // string GetLoginTitle() -> get title for login dialog 11 | // string GetLoginDesc() -> get desc for login dialog 12 | // string GetUserText() -> get user text for login dialog 13 | // string GetPasswordText() -> get password text for login dialog 14 | // string ServerCheck(string User, string Pass) -> server check 15 | // string ServerLogin(string User, string Pass) -> login 16 | // void ServerLogout() -> logout 17 | //------------------------------------------------------------------------------------------------ 18 | // bool PlayitemCheck(const string &in) -> check playitem 19 | // string PlayitemParse(const string &in path,dictionary &MetaData, array &QualityList) -> parse playitem 20 | // bool PlaylistCheck(const string &in) -> check playlist 21 | // array PlaylistParse(const string &in) -> parse playlist 22 | 23 | string GetTitle() 24 | { 25 | return "PanVideo"; 26 | } 27 | 28 | string GetVersion() 29 | { 30 | return "1.2"; 31 | } 32 | 33 | string GetDesc() 34 | { 35 | return "https://github.com/Bleu404/PotplayerPanVideoSV"; 36 | } 37 | 38 | string USERAGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.82 Safari/537.36"; 39 | 40 | JsonReader JSON; 41 | JsonValue HEADERS; 42 | int quIndex; 43 | string getTimeStamp() 44 | { 45 | datetime a =datetime(); 46 | datetime b =datetime(1970,01,02,00,00,00);//不能设置为1970-01-01 47 | return formatInt((a-b+86400)*1000); 48 | } 49 | string getCaptchasign(){ 50 | string srcStr = HEADERS['clientid'].asString() 51 | + "1.79.8" 52 | + "pan.xunlei.com" 53 | + HEADERS['x-device-id'].asString() 54 | + getTimeStamp(); 55 | 56 | array random = { 57 | "Slyf1918drRdfvVF" 58 | ,"8aMrXysG81/lSUK5JjFxGCYCuFrlBL" 59 | ,"cKt0D7jwHGIn" 60 | ,"AMJGU1uXzhe" 61 | ,"opAwUQEXUBQ5XvX955RqIysmA/gMuK" 62 | ,"zc78nJHR6TQQVyZ0S0kbpc7++527LCm" 63 | ,"PSCqhcu7OtJ77s1YoC" 64 | ,"5wVeQ5M8DIURYeHycRKQ6Yr+W" 65 | ,"o8" 66 | ,"+HclH3koSIHBuX008jfMNOd94Ygx3" 67 | ,"J" 68 | ,"WrB0fGeCp+dIj+R" 69 | ,"N+wTICzuycJyYRw" 70 | ,"VCuydo5Y0F3AE"}; 71 | 72 | for(int i = 0;i & QualityList) 95 | { 96 | array qualities = {"搁置","360", "480","540","720", "1080"}; 97 | dictionary item; 98 | if(name == "FHD")name = "超清 1080p"; 99 | if(name == "HD")name = "高清 720p"; 100 | if(name == "SD")name = "标清 540p"; 101 | if(name == "LD")name = "流畅 360p"; 102 | string temp = HostRegExpParse(name,"([0-9]+)"); 103 | HostPrintUTF8(temp); 104 | if (temp!="" && qualities.find(temp) > 0) { 105 | quIndex = qualities.find(temp); 106 | } else { 107 | quIndex++; 108 | } 109 | item["itag"] = quIndex; 110 | item["url"] = url; 111 | item["quality"] = name; 112 | QualityList.insertLast(item); 113 | return true; 114 | } 115 | 116 | bool PlayitemCheck(const string & in path) 117 | { 118 | if(path.find("panvideo") == 0) 119 | { 120 | if(HEADERS.isNull()){ 121 | HostPrintUTF8("重新获取头信息"); 122 | uintptr hfo = HostFileOpen("Extension\\Media\\PlayParse\\panvideo.txt"); 123 | string tempstr = HostFileRead(hfo, 500); 124 | tempstr.replace(" ",""); 125 | array < string > temp = tempstr.split("\r\n"); 126 | tempstr = HostUrlGetStringWithAPI("https://"+temp[0]+"/PanPlaylist/panvideo.txt", USERAGENT, "authorization: Basic "+HostBase64Enc(temp[1]+":"+temp[2]), getData(""), false); 127 | JSON.parse(tempstr, HEADERS); 128 | HEADERS = HEADERS["header"]; 129 | HostFileClose(hfo); 130 | } 131 | return true; 132 | } 133 | return false; 134 | } 135 | 136 | //从opensubtitles.com获取字幕,注册账号并开启API之后每个api每天100(或更多)个字幕 137 | array searchSubtitle(string subname) 138 | { 139 | array apikey ={};//api用户信息必填 140 | int subcount = 0;//每个视频获取字幕个数,设置过多,可能无法加载视频 141 | dictionary subitem; 142 | array sub; 143 | JsonValue userjv,fileinfo; 144 | string subURL,retString; 145 | if(subcount == 0||apikey.size() == 0)return sub; 146 | subURL = "https://api.opensubtitles.com/api/v1/subtitles/?languages=en&query="+subname; 147 | retString = HostUrlGetString(subURL, "PotplayerPanVideoSV", "Content-Type: application/json\r\nApi-Key: "+apikey[0],getData(""), false); 148 | if(retString == "")return sub; 149 | JSON.parse(retString,userjv); 150 | HostPrintUTF8("搜索字幕完毕"); 151 | for(int i = 0;i & QualityList) 169 | { 170 | string ret; 171 | JsonValue jsonVal; 172 | array < string > temp = path.split("##"); 173 | if (!PlayitemCheck(path)) return ret; 174 | string tempstr; 175 | quIndex = 5; 176 | 177 | //HostOpenConsole(); 178 | if(temp[1]=="xunlei") 179 | { 180 | ret = HostUrlGetString(temp[2], USERAGENT, getHeaders("xl_1"), getData(""), false); 181 | if(ret==""||ret.find("error")>=0) 182 | { 183 | if(ret.find("token is expired")>0) 184 | { 185 | tempstr = HostUrlGetString("https://xluser-ssl.xunlei.com/v1/auth/token", USERAGENT, getHeaders("xl_2"),getData("xl_token"), false); 186 | JSON.parse(tempstr,jsonVal); 187 | HEADERS['refresh_token'] = jsonVal["refresh_token"]; 188 | HEADERS['token_type'] = jsonVal["token_type"]; 189 | HEADERS['access_token'] = jsonVal["access_token"]; 190 | } 191 | string initUrl = "https://xluser-ssl.xunlei.com/v1/shield/captcha/init"; 192 | tempstr = HostUrlGetString(initUrl, USERAGENT, getHeaders("xl_0"),getData("xl_init"), false); 193 | JSON.parse(tempstr,jsonVal); 194 | if(jsonVal["captcha_token"].asString()!="")HEADERS['x-captcha-token'] = jsonVal["captcha_token"]; 195 | ret = HostUrlGetString(temp[2], USERAGENT, getHeaders("xl_1"), getData(""), false); 196 | } 197 | JSON.parse(ret, jsonVal); 198 | 199 | setQuality("原画 300KB/s",jsonVal["web_content_link"].asString(),QualityList); 200 | for (int i = 0; i < jsonVal["medias"].size(); i++) 201 | { 202 | JsonValue mary = jsonVal["medias"][i]; 203 | if (!mary["link"].canString()) 204 | { 205 | setQuality(mary["media_name"].asString(),mary["link"]["url"].asString(),QualityList); 206 | if(i == jsonVal["medias"].size()-1) 207 | { 208 | ret = mary["link"]["url"].asString(); 209 | } 210 | } 211 | } 212 | } 213 | if(temp[1]=="aliyun") 214 | { 215 | temp[2].replace("https://",""); 216 | 217 | string url = "https://api.aliyundrive.com/v2/file/get_download_url"; 218 | tempstr = HostUrlGetString(url, USERAGENT, getHeaders("al_0"),getData("al_item")+temp[2]+"\"}", false); 219 | JSON.parse(tempstr,jsonVal); 220 | //HostPrintUTF8(jsonVal["url"].asString()); 221 | HostSetUrlHeaderHTTP(jsonVal["url"].asString(), "referer: https://www.aliyundrive.com/"); 222 | setQuality("原画",jsonVal["url"].asString(),QualityList); 223 | 224 | url = "https://api.aliyundrive.com/v2/file/get_video_preview_play_info"; 225 | tempstr = HostUrlGetString(url, USERAGENT, getHeaders("al_0"),getData("al_item")+temp[2]+"\"}", false); 226 | JSON.parse(tempstr,jsonVal); 227 | JsonValue templist = jsonVal["video_preview_play_info"]["live_transcoding_task_list"]; 228 | for (int i = 0; i < templist.size(); i++) 229 | { 230 | HostSetUrlHeaderHTTP(templist[i]["url"].asString(), "referer: https://www.aliyundrive.com/"); 231 | setQuality(templist[i]["template_id"].asString(),templist[i]["url"].asString(),QualityList); 232 | if(i == templist.size()-1) 233 | { 234 | ret = templist[i]["url"].asString(); 235 | } 236 | } 237 | } 238 | MetaData["subtitle"] = searchSubtitle(temp[3]); 239 | return ret; 240 | } 241 | 242 | bool PlaylistCheck(const string & in path) 243 | { 244 | array < string > temp = path.split("##"); 245 | if (path.find("panvideo") == 0) 246 | { 247 | //JSON.parse(HostUrlDecode(temp[2]), HEADERS); 248 | return true; 249 | } 250 | return false; 251 | } 252 | 253 | array < dictionary > PlaylistParse(const string & in path) 254 | { 255 | //HostOpenConsole(); 256 | array < dictionary > ret; 257 | array < string > temp = path.split("##"); 258 | JsonValue Itemlist; 259 | string tempstr; 260 | //HostPrintUTF8(temp[3]); 261 | tempstr = HostUrlGetStringWithAPI(temp[2], USERAGENT, "authorization: Basic "+HostBase64Enc(temp[3]+":"+temp[4]), getData(""), false); 262 | JSON.parse(tempstr, Itemlist); 263 | HEADERS = Itemlist["header"]; 264 | Itemlist = Itemlist["list"]; 265 | //HostPrintUTF8(tempstr); 266 | for (int i = 0; i < Itemlist.size(); i++) 267 | { 268 | dictionary item,subitem; 269 | item["url"] = "panvideo##"+temp[1]+"##"+Itemlist[i]["url"].asString()+"##"+Itemlist[i]["title"].asString(); 270 | item["title"] = Itemlist[i]["title"].asString(); 271 | ret.insertLast(item); 272 | } 273 | return ret; 274 | } 275 | -------------------------------------------------------------------------------- /src/MediaPlayParse - PanVideo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bleu404/PotplayerPanVideoSV/6e53ba77a578457ef76eaa8ab7df97f26f4706d8/src/MediaPlayParse - PanVideo.ico -------------------------------------------------------------------------------- /src/PotplayerPanVideoSV.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name PotPlayer云盘-专供版 3 | // @namespace https://github.com/Bleu404/PotplayerPanVideoSV 4 | // @version 1.1.0 5 | // @description 此脚本为《PotPlayer播放云盘视频》姊妹篇,需配合MediaPlayParse - PanVideo.as脚本使用。在potplayer中选择画质、字幕,迅雷云盘增加原画,阿里云盘增加时长。 6 | // @author bleu 7 | // @compatible edge Tampermonkey 8 | // @compatible chrome Tampermonkey 9 | // @compatible firefox Tampermonkey 10 | // @license MIT 11 | // @match https://pan.xunlei.com/* 12 | // @match https://www.aliyundrive.com/* 13 | // @icon https://fastly.jsdelivr.net/gh/Bleu404/PRPO@latest/png/ppvsv.png 14 | // @grant GM_setValue 15 | // @grant GM_getValue 16 | // @grant GM_xmlhttpRequest 17 | // @grant GM_registerMenuCommand 18 | // @grant unsafeWindow 19 | // @connect * 20 | // @connect xunlei.com 21 | // @connect aliyundrive.com 22 | // @connect jianguoyun.com 23 | // @connect teracloud.jp 24 | // @require https://fastly.jsdelivr.net/npm/sweetalert2@11.1.0/dist/sweetalert2.all.min.js 25 | // @require https://fastly.jsdelivr.net/npm/bleutools@1.0.2/bleutools.min.js 26 | // ==/UserScript== 27 | 28 | (function () { 29 | 'use strict'; 30 | const ORGXHRSRH = XMLHttpRequest.prototype.setRequestHeader; 31 | let bleuc,contextMenu, itemsInfo, arryIndex, Option, observer,cloud; 32 | const flieTypeStr = ".wmv,.rmvb,.avi,.mp4,.mkv,.flv,.swf.mpeg4,.mpeg2,.3gp,.mpga,.qt,.rm,.wmz,.wmd,.wvx,.wmx,.wm,.mpg,.mpeg,mov,.asf,.m4v,"; 33 | const tools = { 34 | getCloudName() { 35 | switch (document.domain) { 36 | case 'xunlei.com': 37 | cloud = xunlei; 38 | break; 39 | case 'pan.xunlei.com': 40 | cloud = xunlei; 41 | break; 42 | case 'www.aliyundrive.com': 43 | cloud = aliyun; 44 | this.hookXHRHeader(); 45 | break; 46 | } 47 | }, 48 | checkFileType(name) { 49 | let type = name.toLowerCase().substring(name.lastIndexOf('.')) || "bleu" 50 | return flieTypeStr.indexOf(`${type},`) >= 0 ? true : false 51 | }, 52 | async putFileInWebdav(name, info) { 53 | let header = { 54 | "authorization": `Basic ${btoa(`${bleuc.cun}:${bleuc.cpw}`)}` 55 | } 56 | let url = `https://${bleuc.cip}/PanPlaylist`; 57 | let method = bleuc.cip.indexOf('teracloud')>0?'GET':'PROPFIND'; 58 | await bleu.XHR(method, url, undefined, header, undefined).then( () => {} 59 | ,async()=>{await bleu.XHR('MKCOL', url, undefined, header, undefined)}) 60 | url = `https://${bleuc.cip}/PanPlaylist/${name}`; 61 | await bleu.XHR('PUT',url , info, header, 'xml').then(() => { 62 | bleu.swalInfo(`✅${name}`, 3000, 'center'); 63 | }, () => bleu.swalInfo(`❌${name}`, 3000, 'center')) 64 | }, 65 | checkConfig() { 66 | bleuc = JSON.parse(GM_getValue('bleuc') || null) || {cip: '',cun: '',cpw: ''}; 67 | if (!(bleuc.cip != '' && bleuc.cun != '' && bleuc.cpw != '')) { 68 | bleu.swalInfo(`❗请先设置WEBDAV`, '', 'center') 69 | return false 70 | } 71 | return true 72 | }, 73 | saveConfig() { 74 | let temp = document.querySelector('#cip').value.trim() 75 | temp = temp.charAt(temp.length - 1) === '/' ? temp.substring(0, temp.length - 1) : temp 76 | temp = temp.indexOf('https://') < 0 ? temp : temp.replace('https://', '') 77 | GM_setValue("bleuc", JSON.stringify({ 78 | 'cip': temp, 79 | 'cun': document.querySelector('#cun').value.trim(), 80 | 'cpw': document.querySelector('#cpw').value.trim(), 81 | })); 82 | }, 83 | configHtml() { 84 | bleuc = JSON.parse(GM_getValue('bleuc') || null) || {cip: '',cun: '',cpw: ''}; 85 | let html = ` 86 |

87 |

88 |
89 |

90 | `; 91 | return html; 92 | }, 93 | hookXHRHeader() { 94 | XMLHttpRequest.prototype.setRequestHeader = function(header, value) { 95 | if(header == "x-signature"){ 96 | aliyun._signature=value; 97 | } 98 | return ORGXHRSRH.apply(this, arguments); 99 | } 100 | }, 101 | cssStyle: ` 102 | .bleuc_config_item{border-radius: 10px;font-size: 20px;margin: 12px 50px;color: #fff;background: linear-gradient(45deg,#12c2e9, #c471ed, #f64f59);box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);} 103 | .bleuc_config_item label{font-size: 15px} 104 | .bleuc_config_item input.bleuc_inp{margin: 0px 10px;font-size: 15px;background: linear-gradient(45deg,#12c2e9, #c471ed, #f64f59);border-style:none;color:black;width:200px} 105 | .bleuc_config_item p{text-align: left;margin: 0px 20px;}`, 106 | 107 | } 108 | const xunlei = { 109 | addTag() { 110 | if (contextMenu.innerText.match(/PotPlayer/)) return 111 | let ul = document.createElement('ul'); 112 | ul.innerHTML = `PotPlayer打开`; 113 | contextMenu.firstChild.prepend(ul.firstChild); 114 | main.addClickEvent(); 115 | }, 116 | getselectFilesInfo() { 117 | let temp = document.querySelectorAll('li.SourceListItem__item--XxpOC.SourceListItem__active--4U0f4'); 118 | temp.forEach((item) => { 119 | this._pushItem(item.__vue__.info); 120 | }) 121 | }, 122 | async updateFile(item) { 123 | Option["list"].push({ 124 | "title": item.name, 125 | "url": `https://api-pan.xunlei.com/drive/v1/files/${item.id}` 126 | }); 127 | }, 128 | async openNextDir(item) { 129 | let url = `https://api-pan.xunlei.com/drive/v1/files?limit=100&parent_id=${item.id}&filters={"phase":{"eq":"PHASE_TYPE_COMPLETE"},"trashed":{"eq":false}}&with_audit=true`; 130 | await bleu.XHR('GET', url, undefined, Option.header).then((res) => { 131 | arryIndex++; 132 | res.files.forEach((item) => { 133 | xunlei._pushItem(item); 134 | }) 135 | },()=>{ 136 | bleu.swalInfo("❗进出目录之后重新转存", '', 'center'); 137 | throw "PPVSV:迅雷-超时,重新转存"; 138 | }) 139 | }, 140 | findContext(node) { 141 | if (node.className === 'pan-content') { 142 | node = node.querySelector('div.pan-dropdown-menu.context-menu'); 143 | if (!node) return; 144 | contextMenu = node; 145 | xunlei.addTag(); 146 | } 147 | }, 148 | closeMenu() {}, 149 | _pushItem(temp) { 150 | if (!itemsInfo[arryIndex]) itemsInfo[arryIndex] = []; 151 | if (temp.kind === 'drive#file' && !tools.checkFileType(temp.name)) return 152 | let itemInfo = { 153 | 'id': temp.id, 154 | 'isdir': temp.kind === 'drive#file' ? false : true, 155 | 'name': temp.name, 156 | }; 157 | itemsInfo[arryIndex].push(itemInfo); 158 | }, 159 | getHeaderInfo() { 160 | Option.header = {}; 161 | Option.header.withCredentials = false; 162 | Option.header['content-type'] = 'application/json'; 163 | for (let key in localStorage) { 164 | let temp = localStorage.getItem(key) 165 | if (key.indexOf('credentials') === 0) { 166 | Option.header["Authorization"] = JSON.parse(temp).token_type + ' ' + JSON.parse(temp).access_token; 167 | Option.header["token_type"] = JSON.parse(temp).token_type; 168 | Option.header["access_token"] = JSON.parse(temp).access_token; 169 | Option.header["refresh_token"] = JSON.parse(temp).refresh_token; 170 | Option["clientid"] = key.substring(key.indexOf('_') + 1); 171 | } 172 | if (key.indexOf('captcha') === 0) 173 | Option.header['x-captcha-token'] = JSON.parse(temp).token 174 | if (key === 'deviceid') 175 | Option.header['x-device-id'] = temp.substring(temp.indexOf('.') + 1, 32 + temp.indexOf('.') + 1) 176 | } 177 | }, 178 | async finallyFunc() { 179 | Option.header["clientid"] = Option["clientid"]; 180 | await tools.putFileInWebdav('panvideo.txt', JSON.stringify(Option)); 181 | unsafeWindow.location.href = `potplayer://panvideo##xunlei##https://${bleuc.cip}/PanPlaylist/panvideo.txt##${bleuc.cun}##${bleuc.cpw}`; 182 | } 183 | } 184 | const aliyun = { 185 | addTag() { 186 | if (contextMenu.innerText.match(/PotPlayer|新建/)) return 187 | let ul = document.createElement('ul'); 188 | ul.innerHTML = ``; 189 | contextMenu.prepend(ul.firstChild); 190 | main.addClickEvent(); 191 | }, 192 | getselectFilesInfo() { 193 | let temp = document.querySelectorAll('div[data-index]') 194 | let attrName; 195 | for(let attr in temp[0]){ 196 | if(attr.indexOf('__reactFiber')==0){ 197 | attrName = attr; 198 | break; 199 | } 200 | } 201 | temp.forEach((item)=>{ 202 | if(item.querySelector('input')&&item.querySelector('input').checked){ 203 | let value = item[attrName].return.pendingProps; 204 | aliyun._pushItem(value.data[value.index]||value.data[value.rowIndex][value.columnIndex]); 205 | } 206 | }) 207 | 208 | }, 209 | async updateFile(item) { 210 | Option["list"].push({ 211 | "title": item.name, 212 | "url": "https://"+item.id 213 | }); 214 | }, 215 | async openNextDir(item) { 216 | let url = `https://api.aliyundrive.com/adrive/v3/file/list?jsonmask=next_marker%2Citems(name%2Cfile_id%2Cdrive_id%2Ctype%2Csize%2Ccreated_at%2Cupdated_at%2Ccategory%2Cfile_extension%2Cparent_file_id%2Cmime_type%2Cstarred%2Cthumbnail%2Curl%2Cstreams_info%2Ccontent_hash%2Cuser_tags%2Ctrashed%2Cvideo_media_metadata%2Cvideo_preview_metadata)`, 217 | token = JSON.parse(localStorage.getItem('token')), 218 | data = { 219 | 'drive_id': token.default_drive_id, 220 | 'parent_file_id': item.id, 221 | 'limit': 100, 222 | }, 223 | header = { 224 | 'x-canary': 'client=web,app=adrive,version=v2.4.0', 225 | 'x-device-id': document.cookie.match(/cna=([^;]*)/)[1], 226 | authorization: `${token.token_type} ${token.access_token}`, 227 | 'x-signature':this._signature 228 | }; 229 | await bleu.XHR('POST', url, JSON.stringify(data),header).then((res) => { 230 | arryIndex++; 231 | res.items.forEach((item)=>{ 232 | aliyun._pushItem(item); 233 | },()=>{ 234 | bleu.swalInfo("🔴💬刷新页面,重新获取", '', 'center'); 235 | throw "PPVSV:阿里-超时,重新转存"; 236 | }) 237 | }) 238 | }, 239 | _signature:'', 240 | findContext(node) { 241 | node = document.querySelector('ul.ant-dropdown-menu'); 242 | if (!node) return; 243 | //observer.disconnect(); 244 | contextMenu = node; 245 | aliyun.addTag(); 246 | }, 247 | closeMenu(){ 248 | contextMenu.parentNode.className='ant-dropdown dropdown-menu--1KRbu ant-dropdown-placement-bottomLeft ant-dropdown-hidden'; 249 | contextMenu.parentNode.style.left='-578px'; 250 | contextMenu.parentNode.style.top='-646px'; 251 | }, 252 | _pushItem(temp) { 253 | if(!itemsInfo[arryIndex]) itemsInfo[arryIndex]= []; 254 | if (temp.type==='file'&&temp.category!="video") return 255 | let itemInfo = { 256 | 'id': temp.fileId||temp.file_id, 257 | 'isdir': temp.type === 'file' ? false : true, 258 | 'name': temp.name, 259 | }; 260 | itemsInfo[arryIndex].push(itemInfo); 261 | }, 262 | getHeaderInfo() { 263 | Option.header ={}; 264 | let token = JSON.parse(localStorage.getItem('token')); 265 | Option.header["authorization"] =`${token.token_type} ${token.access_token}`; 266 | Option.header["drive_id"] =token.default_drive_id; 267 | Option.header["x-signature"]=this._signature; 268 | Option.header["x-device-id"]=document.cookie.match(/cna=([^;]*)/)[1];//decodeURIComponent(localStorage.getItem('APLUS_CNA').substring(9)) 269 | }, 270 | async finallyFunc(){ 271 | await tools.putFileInWebdav('panvideo.txt', JSON.stringify(Option)); 272 | unsafeWindow.location.href = `potplayer://panvideo##aliyun##https://${bleuc.cip}/PanPlaylist/panvideo.txt##${bleuc.cun}##${bleuc.cpw}`; 273 | } 274 | } 275 | const main = { 276 | init() { 277 | observer = new MutationObserver(function (mutations) { 278 | for (let mutation of mutations) { 279 | if (mutation.type === 'childList') { 280 | cloud.findContext(mutation.target); 281 | } 282 | } 283 | }); 284 | observer.observe(document, { 285 | 'childList': true, 286 | 'subtree': true 287 | }); 288 | }, 289 | addClickEvent() { 290 | let bleuButton = document.getElementById('bleuReSave'); 291 | bleuButton.addEventListener('click', async function () { 292 | itemsInfo = []; 293 | arryIndex = 0; 294 | Option = {}, Option["list"] = []; 295 | if(!tools.checkConfig())return; 296 | cloud.closeMenu(); 297 | cloud.getselectFilesInfo(); 298 | cloud.getHeaderInfo(); 299 | if (!itemsInfo[arryIndex]||itemsInfo[arryIndex].length === 0) { 300 | bleu.swalInfo(`❌未选择文件转存!`, 3000, 'center') 301 | return; 302 | } 303 | try { 304 | await main.updateAllFiles(itemsInfo[arryIndex]); 305 | } catch (e) { 306 | console.log(e); 307 | return; 308 | } 309 | Option["list"].length!=0&&cloud.finallyFunc(); 310 | }) 311 | }, 312 | async updateAllFiles(loopArry) { 313 | for (let index = 0; index < loopArry.length; index++) { 314 | if (!loopArry[index].isdir) { 315 | await cloud.updateFile(loopArry[index]); 316 | } else { 317 | await cloud.openNextDir(loopArry[index]); 318 | await main.updateAllFiles(itemsInfo[arryIndex]); 319 | } 320 | bleu.sleep(800); 321 | } 322 | }, 323 | }; 324 | tools.getCloudName(); 325 | //tools.checkConfig(); 326 | bleu.addCssStyle(tools.cssStyle); 327 | GM_registerMenuCommand('配置WEBDAV', () => { 328 | bleu.swalUI('WEBDAV', tools.configHtml(), '400px').then(tools.saveConfig) 329 | }, 'w') 330 | main.init(); 331 | })(); -------------------------------------------------------------------------------- /字幕api填写.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bleu404/PotplayerPanVideoSV/6e53ba77a578457ef76eaa8ab7df97f26f4706d8/字幕api填写.png -------------------------------------------------------------------------------- /清晰度选择.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bleu404/PotplayerPanVideoSV/6e53ba77a578457ef76eaa8ab7df97f26f4706d8/清晰度选择.png -------------------------------------------------------------------------------- /迅雷captcha_sign.txt: -------------------------------------------------------------------------------- 1 | 时间戳 :1680320717027 2 | captcha_sign :d1e30ec0aeb3e827618b3153acf805bc 3 | 4 | 5 | 以下都是两行加一起进行md5计算 6 | 【client_id】 + 【client_version】 + 【package_name】 + 【device_id】 + 【timestamp】: 7 | Xqp0kJBXWhwaTpB61.79.8pan.xunlei.com459896355a5274d41363ccbb2b2365cd1680320717027 8 | 9 | Slyf1918drRdfvVF 10 | 5b8a63a81714271ebe28e546f76c963e 11 | 12 | 8aMrXysG81/lSUK5JjFxGCYCuFrlBL 13 | e4a137a122e7c43f8f6c1425f5e88a3e 14 | 15 | cKt0D7jwHGIn 16 | a10c45c088e9e3f28b06345d90701c54 17 | 18 | AMJGU1uXzhe 19 | e31be7b25da18e184df261c9283afe44 20 | 21 | opAwUQEXUBQ5XvX955RqIysmA/gMuK 22 | 8cedb5bdb71cc59a15b48f707157c488 23 | 24 | zc78nJHR6TQQVyZ0S0kbpc7++527LCm 25 | 3ef8ba2a7795e6ab47cf0a01335bad4e 26 | 27 | PSCqhcu7OtJ77s1YoC 28 | 1898539d23d365035237769c07e935a5 29 | 30 | 5wVeQ5M8DIURYeHycRKQ6Yr+W 31 | e59f75b6a4ab598bfab590cddae73b1e 32 | 33 | o8 34 | 6282720b5323e278282203c46ae1c3bc 35 | 36 | +HclH3koSIHBuX008jfMNOd94Ygx3 37 | 843501cce70d93456b0bb867148ab45d 38 | 39 | J 40 | 3b9e09ec0a81b42865ae546952282176 41 | 42 | WrB0fGeCp+dIj+R 43 | 47cbc3b72ffdec33bd28bd58b3795be4 44 | 45 | N+wTICzuycJyYRw 46 | 5bdfeb28cd2d795c05f210c26ca32117 47 | 48 | VCuydo5Y0F3AE 49 | d1e30ec0aeb3e827618b3153acf805bc 50 | 51 | 52 | d1e30ec0aeb3e827618b3153acf805bc --------------------------------------------------------------------------------