115批量文件迅雷下载(暂不支持文件夹类型下载)

迅雷党的福音来啦~\(≧▽≦)/~,免VIP,免115浏览器,对了还能在线播放(使用前务必阅读“使用步骤”)

  1. // ==UserScript==
  2. // @name 115批量文件迅雷下载(暂不支持文件夹类型下载)
  3. // @namespace sscozak
  4. // @include *://115.com/?ct=file*
  5. // @version 1.3.8.3
  6. // @run-at document-end
  7. // @grant none
  8. // @description 迅雷党的福音来啦~\(≧▽≦)/~,免VIP,免115浏览器,对了还能在线播放(使用前务必阅读“使用步骤”)
  9. // @compatible firefox
  10. // @compatible chrome
  11. // ==/UserScript==
  12.  
  13.  
  14. // ======================================= Monkey插件兼容处置 =============================================
  15. const RWD = window.parent;
  16. const RWDs = "window.parent";
  17. const RDM = RWD.document;
  18. const RDMs = RWDs+".document";
  19.  
  20. // ======================================= XunLei下载 =============================================
  21. RWD.Core.FileAPI.Thunder = {
  22. // 存放URL列表及相关维护操作
  23. thunder_urls : [],
  24. thunder_table : null,
  25. thunder_counter : null,
  26. thunder_total : 0,
  27. thunderClear : function() {
  28. var len = this.thunder_urls.length;
  29. this.thunder_urls.splice(0,len);
  30. this.thunder_table = null;
  31. this.thunder_counter = null;
  32. this.thunder_total = 0;
  33. },
  34. // 更新窗口URL列表
  35. thunderUpdateTable : function(r) {
  36. if (this.thunder_table) {
  37. var row = this.thunder_table.insertRow();
  38. row.style["white-space"]="nowrap"; // 内容水平展开,不自动换行
  39. var cell = row.insertCell(); cell.align = "left"; cell.innerHTML = r.file_name;
  40. var cell = row.insertCell(); cell.align = "left"; cell.innerHTML = r.file_url;
  41. }
  42. if (this.thunder_counter) {
  43. this.thunder_counter.innerHTML = '['+this.thunder_urls.length+'/'+this.thunder_total+']';
  44. }
  45. },
  46. thunderPushURL : function(url) {
  47. this.thunder_urls.push(url);
  48. },
  49. thunderHelperBeacon : "",
  50. thunderCheckHelper : function() {
  51. this.thunderHelperBeacon = "";
  52. var helper = RDM.querySelectorAll("thunderapihelper");
  53. if (0 === helper.length) {
  54. return false;
  55. }
  56. this.thunderHelperBeacon = helper[0].innerHTML;
  57. return true;
  58. },
  59. thunderBuildTaskWithCookie : function() {
  60. var len = this.thunder_urls.length;
  61. if (0 >= len) {
  62. alert("URL列表为空。。");
  63. return;
  64. }
  65. // 携带Cookie下载(请确保已安装addon:ThunderAPIHelper_WebExt)
  66. var urls = this.thunder_urls;
  67. var links = [];
  68. for (var i = 0; i < len; i++) {
  69. links.push(urls[i].file_url);
  70. }
  71. var link_container = RDM.getElementById("ID_link_container");
  72. link_container = link_container ? link_container
  73. : RDM.createElement("ThunderLinksContainer");
  74. link_container.setAttribute("id", "ID_link_container");
  75. link_container.setAttribute("hrefs", JSON.stringify(links));
  76. RDM.documentElement.appendChild(link_container);
  77.  
  78. var evt = RDM.createEvent("Events");
  79. evt.initEvent(this.thunderHelperBeacon, true, false);
  80. link_container.dispatchEvent(evt);
  81. },
  82. thunderBuildTaskWithoutCookie : function() {
  83. var len = this.thunder_urls.length;
  84. if (0 >= len) {
  85. alert("URL列表为空。。");
  86. return;
  87. }
  88. // 裸链接下载(无cookie)
  89. var urls = this.thunder_urls;
  90. var pid = 66666; // 迅雷合作ID(随意)
  91. BatchTasker.BeginBatch(4,pid); //开始批量添加
  92. for (var i = 0; i < len; i++) {
  93. BatchTasker.AddTask(ThunderEncode(urls[i].file_url), urls[i].file_name); //添加下载任务
  94. }
  95. BatchTasker.EndBatch(pid);
  96. },
  97. // 启动下载
  98. thunderLaunch : function() {
  99. // 判断迅雷NPAPI插件是否存在,若存在则调用原JS方法,否则调用ThunderAPIHelper扩展提供的JS接口
  100. if (navigator.mimeTypes['application/np_xunlei_plugin']
  101. || navigator.mimeTypes['application/np_xunlei_plugin.2']) {
  102. this.thunderBuildTaskWithoutCookie();
  103. } else if (this.thunderCheckHelper()) {
  104. this.thunderBuildTaskWithCookie();
  105. } else if (confirm("当前浏览器不支持迅雷NPAPI插件,"
  106. +"请安装ThunderAPIHelper扩展,"
  107. +"详情留意Greasyfork上的脚本说明")) {
  108. var greasyfork = "https://greasyfork.org/zh-CN/scripts/28050-115%E6%89%B9%E9%87%8F%E6%96%87%E4%BB%B6%E8%BF%85%E9%9B%B7%E4%B8%8B%E8%BD%BD-%E6%9A%82%E4%B8%8D%E6%94%AF%E6%8C%81%E6%96%87%E4%BB%B6%E5%A4%B9%E7%B1%BB%E5%9E%8B%E4%B8%8B%E8%BD%BD";
  109. //document.location = greasyfork;
  110. RWD.open(greasyfork,"_blank");
  111. }
  112. },
  113. // 批量复制链接
  114. thunderCopyToClipboard : function() {
  115. var len = this.thunder_urls.length;
  116. if (0 >= len) {
  117. alert("URL列表为空。。");
  118. return;
  119. }
  120. var urls = this.thunder_urls;
  121. var urls_content = '';
  122. for (var i = 0; i < urls.length; i++) {
  123. urls_content += urls[i].file_url+'\r\n';
  124. }
  125. var url_clipboard = RDM.getElementById('ID_url_clip');
  126. url_clipboard.innerHTML = urls_content;
  127. // 选取并复制URLs
  128. RWD.$(url_clipboard).focus();
  129. RWD.$(url_clipboard).select();
  130. if (RDM.execCommand('copy', false, "")) {
  131. alert("已复制 "+urls.length+" 条链接至剪切板");
  132. } else {
  133. alert("复制失败,请留言至GreasyFork反馈,谢谢");
  134. }
  135. },
  136. // 预加载子窗口框架页面
  137. thunderPreloadFramePage : function() {
  138. var stylePatcher = function(obj, attr_nm, attr_vl) {
  139. for (var i = 0; i < attr_nm.length; i++) {
  140. obj.style[attr_nm[i]] = attr_vl[i];
  141. }
  142. };
  143. // 子窗口
  144. var url_wdw = RDM.createElement('div'); url_wdw.id = "ID_url_wdw";
  145. url_wdw.className = 'dialog-box dialog-mini easy-download window-current';
  146. stylePatcher(url_wdw,
  147. ['z-index', 'width', 'position', 'top', 'left', 'display'],
  148. ['1000000002', '50%', 'fixed', '15%', '25%', 'none']);
  149. RDM.body.appendChild(url_wdw);
  150. // 构建列表页面
  151. var html = [];
  152. html.push('<div class="dialog-box dialog-mini easy-download window-current" style="z-index: 1000000002; '
  153. +'width: 50%; height:60%; position: fixed; top: 15%; left: 25%;">');
  154. html.push('<div class="dialog-header" rel="title_box" ws_property="1">'
  155. +'<div style="float:left"><h3 rel="base_title">115迅雷小工具</h3></div>'
  156. +'<div id="ID_thunder_counter"></div>'
  157. +'</div>');
  158. html.push('<div class="dialog-handle"><a href="javascript:;" class="close" id="ID_thunder_close">关闭</a></div>');
  159. html.push('<div id="ID_thunder_context" rel="base_content" style="height:85%"></div></div>');
  160. // 神隐的复制板
  161. html.push('<textarea id="ID_url_clip" style="width:1px;height:1px;border-style:none;"></textarea>');
  162. // 插入HTML文本
  163. url_wdw.innerHTML = (function(){
  164. var res = '';
  165. for (var i = 0; i < html.length; i++) {
  166. res += html[i];
  167. }
  168. return res;
  169. })();
  170. // 背景蒙版
  171. var bk_mask = RDM.createElement('div'); bk_mask.id = "ID_bk_mask";
  172. stylePatcher(bk_mask,
  173. ['z-index', 'background', 'height', 'left', 'position', 'top', 'width', 'opacity', 'display'],
  174. ['1000000001', 'rgb(0, 0, 0)', '100%', '0px', 'fixed', '0px', '100%', '0.4', 'none']);
  175. RDM.body.appendChild(bk_mask);
  176. var bk_mask_inner = RDM.createElement('div');
  177. stylePatcher(bk_mask_inner,
  178. ['height', 'width'],
  179. ['100%', '100%']);
  180. bk_mask.appendChild(bk_mask_inner);
  181. // 设置关闭窗口按钮事件
  182. var btn_close = RDM.getElementById('ID_thunder_close');
  183. btn_close.onclick = Function('(function(){'+RDMs+'.getElementById("ID_bk_mask").style["display"]="none";'
  184. +''+RDMs+'.getElementById("ID_url_wdw").style["display"]="none";'
  185. +''+RDMs+'.getElementById("ID_thunder_context").innerHTML="";'
  186. +'})()');
  187. return url_wdw;
  188. },
  189. // 装填子窗口内容
  190. thunderShowPageWithContext : function(context, callback) {
  191. var url_wdw = RDM.getElementById('ID_url_wdw');
  192. url_wdw = url_wdw?url_wdw:this.thunderPreloadFramePage();
  193. var bk_mask = RDM.getElementById('ID_bk_mask');
  194.  
  195. url_wdw.style["display"] = "";
  196. bk_mask.style["display"] = "";
  197.  
  198. var ctx = RDM.getElementById('ID_thunder_context');
  199. ctx.innerHTML = context, callback&&callback();
  200. },
  201. // 显示资源链接列表
  202. thunderShowLinks : function() {
  203. // 文件链接显示列表
  204. var html = [];
  205. html.push('<div id="ID_thunder_links" style="height:100%"><div class="dialog-frame" style="height:100%; overflow:scroll;">');
  206. html.push('<table id="ID_thunder_table" style="height:auto" border="1" cellspacing="20"></table></div>');
  207. // 按钮
  208. html.push('<div style="height:20%">'
  209. // 开始下载
  210. +'<a href="javascript:;" class="button btn-green" style="width:50%; padding:unset" onclick="Core.FileAPI.Thunder.thunderLaunch()">'
  211. +'<i class="icon ico-normal"></i><em>开始下载</em></a>'
  212. // 复制链接
  213. +'<a href="javascript:;" class="button btn-blue" style="width:50%; padding:unset" onclick="Core.FileAPI.Thunder.thunderCopyToClipboard()">'
  214. +'<i class="icon ico-normal"></i><em>复制链接</em></a>'
  215. +'</div></div>');
  216. var context;
  217. context = (function(){
  218. var res = '';
  219. for (var i = 0; i < html.length; i++) {
  220. res += html[i];
  221. }
  222. return res;
  223. })();
  224. this.thunderShowPageWithContext(context, ()=>{
  225. // 获取列表Table
  226. RWD.Core.FileAPI.Thunder.thunder_table = RDM.getElementById('ID_thunder_table');
  227. RWD.Core.FileAPI.Thunder.thunder_counter = RDM.getElementById('ID_thunder_counter');
  228. });
  229. },
  230. // 在线播放视频(网页播放器,目前兼容性不好,暂搁置)
  231. thunderPlayVideoOnWeb : function(url) {
  232. var html = [];
  233. //html.push('<video id="ID_thunder_player" style="height:100%;width:100%" controls="controls" preload="auto" src=""></video>');
  234. html.push('<embed id="ID_thunder_player" src="" type="audio/x-pn-realaudio-plugin" height="100%" width="100%"/>');
  235. var context;
  236. context = (function(){
  237. var res = '';
  238. for (var i = 0; i < html.length; i++) {
  239. res += html[i];
  240. }
  241. return res;
  242. })();
  243. this.thunderShowPageWithContext(context, ()=>{
  244. RDM.getElementById('ID_thunder_player').src = url;
  245. });
  246. },
  247. };
  248.  
  249.  
  250. // ======================================= 在线点播 =============================================
  251. RWD.Core.FileAPI.Player = {
  252. VideoList : [],
  253. VideoTypes : ['mp4','avi','rmvb','rm','3gp','wmv','mov','mp3','flv','mpeg','mkv','f4v','mpg','dat'],
  254. GetFileType : function(file_name) {
  255. return file_name.split('.').pop();
  256. },
  257. CheckVideoType : function(type_name) {
  258. var video_types = this.VideoTypes;
  259. for (var idx = 0; idx < video_types.length; ++idx) {
  260. if (type_name === video_types[idx]) {
  261. return true;
  262. }
  263. }
  264. return false;
  265. },
  266. AddVideoButtonForItem : function() {
  267. const DM = RWD.frames["wangpan"].document;
  268. var items = DM.querySelectorAll("li[rel='item'][file_type='1']");
  269. for (var idx = 0; idx < items.length; ++idx) {
  270. if (this.CheckVideoType(this.GetFileType(items[idx].attributes.title.value))) {
  271. var opdv = items[idx].querySelector("div.file-opr")||items[idx].querySelector("span.file-name");
  272. var aplay = '<a href="javascript:;" menu="play_one" onclick="window.parent.Core.FileAPI.Player.PlayOne(this)">'
  273. +'<i class=""></i>'
  274. +'<span>播放</span>'
  275. +'</a>';
  276. opdv.innerHTML += aplay;
  277. }
  278. }
  279. },
  280. AddVideoButtonForBar : function() {
  281. const DM = RWD.frames["wangpan"].document;
  282. // 获取当前被选中的文件
  283. var items = DM.querySelectorAll('li[rel=\'list\'], li[rel=\'item\'][file_type=\'1\'].' + "selected");
  284. // 筛选其中可以播放的文件
  285. this.VideoList.splice(0, this.VideoList.length); // 清空上次的信息
  286. for (var idx = 0; idx < items.length; ++idx) {
  287. if (this.CheckVideoType(this.GetFileType(items[idx].attributes.title.value))) {
  288. this.VideoList.push(items[idx]);
  289. }
  290. }
  291. if (0 === this.VideoList.length) {
  292. return;
  293. }
  294.  
  295. var bar = DM.querySelector("ul");
  296. var aplay = '<li menu="play_all" onclick="window.parent.Core.FileAPI.Player.PlayAll()">'
  297. +'<span>播放全部</span>'
  298. +'</li>';
  299. bar.innerHTML += aplay;
  300. },
  301. // 播放单个文件
  302. PlayOne : function(item_btn_elem) {
  303. var item_info_elem = item_btn_elem.parentElement.parentElement;
  304. var pick_code = item_info_elem.attributes.pick_code.value;
  305. RWD.Core.FileAPI.RequestFileURL(pick_code, ((r)=>{
  306. RWD.Core.FileAPI.Player.PlayVideo([{title:r.file_name,url:r.file_url}]);
  307. }));
  308. },
  309. // 批量播放
  310. PlayAll : function() {
  311. if (0 === this.VideoList.length) {
  312. return;
  313. }
  314.  
  315. const video_count = this.VideoList.length;
  316. var remain = video_count;
  317. var play_list = new Array(video_count);
  318. var unplayed = true;
  319. var timer_key = 0;
  320. var delay_time = 5000;
  321. // 设置超时行为
  322. var time_to_play = function() {
  323. if (!unplayed) {
  324. return;
  325. }
  326. unplayed = false;
  327. var ready_list = [];
  328. for (var idx = 0; idx < video_count; ++idx) {
  329. if (play_list[idx]) {
  330. ready_list.push(play_list[idx]);
  331. }
  332. }
  333. RWD.Core.FileAPI.Player.PlayVideo(ready_list);
  334. };
  335. // 异步请求播放文件URL
  336. for (var idx = 0; idx < video_count; ++idx) {
  337. const idc = idx; // 保存当前文件任务的ID,必须使用Const类型
  338. RWD.Core.FileAPI.RequestFileURL(this.VideoList[idx].attributes.pick_code.value,(
  339. (r)=>{
  340. play_list[idc] = {title:r.file_name,url:r.file_url};
  341. // 检查是否已获取全部url
  342. RWD.clearTimeout(timer_key); // 取消当前延时
  343. if (!unplayed) {
  344. return;
  345. } else if (--remain === 0) {
  346. unplayed = false;
  347. RWD.Core.FileAPI.Player.PlayVideo(play_list);
  348. } else {
  349. // 重设延时器
  350. timer_key = RWD.setTimeout(time_to_play,delay_time);
  351. }
  352. }));
  353. }
  354. // 设置超时
  355. timer_key = RWD.setTimeout(time_to_play,delay_time);
  356. },
  357. // 生成ASX文件调用本地PotPlayer
  358. PlayVideo : function(video_infos) {
  359. if (0 === video_infos.length) {
  360. return;
  361. }
  362. console.log("Ready to play: "+video_infos.length);
  363. console.log(video_infos);
  364. var hrefs = '<ASX Version="3.0">';
  365. for (var i = 0; i < video_infos.length; ++i) {
  366. hrefs += '<Entry>';
  367. if (video_infos[i].title) {
  368. hrefs += '<Title>' + video_infos[i].title + '</Title>';
  369. }
  370. hrefs += '<Ref href ="' + video_infos[i].url + '" />';
  371. hrefs += '</Entry>';
  372. }
  373. hrefs += '</ASX>';
  374. var asx_content = [hrefs];
  375. var asx_blob = new Blob(asx_content,{'type':'video/x-ms-asf-plugin'}); // 由关联ASX文件的程序调用
  376. var url = URL.createObjectURL(asx_blob); // 提供blob对象的url地址
  377. location.href = url; // 等效于将url直接输入于地址栏,播放视频
  378. },
  379. };
  380.  
  381. // ====================================== 工具函数 ==============================================
  382. // 异步请求文件URL
  383. RWD.Core.FileAPI.RequestFileURL = function(pick_code, callback) {
  384. var _ = function () {
  385. RWD.UA$.ajax({
  386. url: 'files/download?pickcode=' + pick_code,
  387. type: 'GET',
  388. dataType: 'json',
  389. cache: false,
  390. success: function (r) {
  391. callback&&callback(r);
  392. }
  393. })
  394. };
  395. _();
  396. return;
  397. }
  398.  
  399. // ===================================== 注入 ===============================================
  400.  
  401. // 替换原函数,直接根据pickcode请求资源URL
  402. RWD.Core.FileAPI.Download = function (pick_code, win) {
  403. this.RequestFileURL(pick_code, ((r)=>{
  404. //alert("URL: "+r.file_url);
  405. RWD.Core.FileAPI.Thunder.thunderPushURL(r);
  406. RWD.Core.FileAPI.Thunder.thunderUpdateTable(r);
  407. }));
  408. };
  409.  
  410. // 替换原函数,对所有选中的文件(不含文件夹)进行URL获取
  411. RWD.Core.FileAPI.DownloadSomeFile = function (list) {
  412. if (!list.length) {
  413. RWD.Core.MinMessage.Show({
  414. text: '请选择文件',
  415. type: 'war',
  416. timeout: 2000
  417. });
  418. return;
  419. }
  420. var TypeFilter = function(list) {
  421. var file_list = [];
  422. for (var i = 0; i < list.length; i++) {
  423. if (list[i].attr('file_type') == '1') {
  424. file_list.push(list[i]);
  425. }
  426. }
  427. return file_list;
  428. }
  429. list = TypeFilter(list);
  430. if (0 === list.length) {
  431. alert("当前无法下载文件夹类型...");
  432. return;
  433. }
  434. RWD.Core.FileAPI.Thunder.thunderClear();
  435. RWD.Core.FileAPI.Thunder.thunderShowLinks();
  436. RWD.Core.FileAPI.Thunder.thunder_total = list.length;
  437. for (var i = 0; i < list.length; i++) {
  438. RWD.Core.FileAPI.Download(list[i].attr('pick_code'));
  439. }
  440. };
  441.  
  442. // ======================================= 初始化 =============================================
  443. // 加载迅雷JS库
  444. (function(){
  445. var getScriptSync = function(url) {
  446. RWD.$.ajax({
  447. url: url,
  448. async: false,
  449. dataType: "script"
  450. });
  451. };
  452. getScriptSync('http://pstatic.xunlei.com/js/webThunderDetect.js');
  453. getScriptSync('http://pstatic.xunlei.com/js/base64.js');
  454. getScriptSync('http://pstatic.xunlei.com/js/thunderBatch.js');
  455. })();
  456.  
  457. // 监听iframe载入
  458. (function(){
  459. var sub_wind = RDM.querySelector("iframe[rel='wangpan']");
  460. sub_wind.onload = (function(){
  461. // 设置观察器
  462. var DM = RWD.frames["wangpan"].document;
  463. DM.item_bar_observer = new MutationObserver((function(e){RWD.Core.FileAPI.Player.AddVideoButtonForBar();}));
  464. DM.item_bar_observer.observe(DM.querySelector('#js_operate_box'),{'childList':true});
  465. DM.item_list_observer = new MutationObserver((function(e){RWD.Core.FileAPI.Player.AddVideoButtonForItem();}));
  466. DM.item_list_observer.observe(DM.querySelector('#js_data_list'),{'childList':true});
  467. });
  468. })();