发送到115

115离线下载功能

  1. // ==UserScript==
  2. // @name 发送到115
  3. // @author zxf10608
  4. // @version 7.5.1
  5. // @icon https://115.com/favicon.ico
  6. // @namespace https://greasyfork.org/zh-CN/scripts/408466
  7. // @description 115离线下载功能
  8. // @require https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js
  9. // @require https://greasyfork.org/scripts/398240-gm-config-zh-cn/code/GM_config_zh-CN.js
  10. // @require https://greasyfork.org/scripts/412267-base64-v1-0/code/base64_v10.js
  11. // @require https://cdn.jsdelivr.net/npm/toastr@2.1.4/toastr.min.js
  12. // @resource toastrCss https://cdn.jsdelivr.net/npm/toastr@2.1.4/build/toastr.min.css
  13. // @match http*://*/*
  14. // @match http*://*.115.com/*
  15. // @exclude http*://*.115.com/bridge*
  16. // @exclude http*://*.115.com/*/static*
  17. // @exclude http*://*.baidu.com/*
  18. // @exclude http*://*.iqiyi.com/*
  19. // @exclude http*://*.qq.com/*
  20. // @exclude http*://*.youku.com/*
  21. // @exclude http*://*.bilibili.com/
  22. // @exclude http*://*.pptv.com/*
  23. // @exclude http*://*.fun.tv/*
  24. // @exclude http*://*.sohu.com/*
  25. // @exclude http*://*.le.com/*
  26. // @exclude http*://*.tudou.com/*
  27. // @exclude http*://*.bilibili.com/*
  28. // @exclude http*://music.163.com/*
  29. // @exclude http*://github.com/*
  30. // @exclude http*://gitee.com/*
  31. // @exclude http*://btcache.me/*
  32. // @exclude http*://*.jd.com/*
  33. // @exclude http*://*.taobao.com/*
  34. // @exclude http*://*.tmall.com/*
  35. // @exclude http*://*.vip.com/*
  36. // @exclude http*://*.pinduoduo.com/*
  37. // @grant GM_setValue
  38. // @grant GM_getValue
  39. // @grant GM_addStyle
  40. // @grant GM_download
  41. // @grant GM_openInTab
  42. // @grant GM_setClipboard
  43. // @grant GM_getResourceText
  44. // @grant GM_xmlhttpRequest
  45. // @grant GM_registerMenuCommand
  46. // @connect 115.com
  47. // @connect *
  48. // @grant unsafeWindow
  49. // @grant window.open
  50. // @grant window.close
  51. // @run-at document-start
  52. // @compatible chrome
  53. // @license GPL License
  54. // ==/UserScript==
  55.  
  56. (function() {
  57. 'use strict';
  58. var newVersion = 'v7.5.1';
  59.  
  60. if (typeof GM_config == 'undefined') {
  61. alert('115优化大师:\n网络异常,相关库文件加载失败,脚本无法使用,请刷新网页重新加载!');
  62. return;
  63. } else {
  64. console.log('115优化大师:相关库文件加载成功!');
  65. }
  66.  
  67. // 配置界面
  68. function config() {
  69. var windowCss = '#Cfg .nav-tabs {margin: 20 2} #Cfg .config_var textarea{width: 310px; height: 50px;} #Cfg .inline {padding-bottom:0px;} #Cfg .config_header a:hover {color:#1e90ff;} #Cfg .config_var {margin-left: 10%;margin-right: 10%;} #Cfg input[type="checkbox"] {margin: 3px 3px 3px 0px;} #Cfg input[type="text"] {width: 53px;} #Cfg {background-color: lightblue;} #Cfg .reset_holder {float: left; position: relative; bottom: -1em;} #Cfg .saveclose_buttons {margin: .7em;} #Cfg .section_desc {font-size: 10pt;}';
  70.  
  71. GM_registerMenuCommand('设置', opencfg);
  72. function opencfg() {
  73. GM_config.open();
  74. }
  75.  
  76. GM_config.init({
  77. id: 'Cfg',
  78. title: GM_config.create('a', {
  79. href: 'https://greasyfork.org/zh-CN/scripts/408466',
  80. target: '_blank',
  81. className: 'setTitle',
  82. textContent: '115优化大师',
  83. title: '作者:zxf10608 版本:' + newVersion + ' 点击访问主页'
  84. }),
  85. isTabs: true,
  86. skin: 'tab',
  87. css: windowCss,
  88. frameStyle: {
  89. height: '550px',
  90. width: '445px',
  91. zIndex: '2147483648',
  92. },
  93. fields: {
  94. // 离线升级
  95. offline_Down: {
  96. section: ['离线升级', '升级离线下载功能'],
  97. label: '启用一键离线下载',
  98. labelPos: 'right',
  99. type: 'checkbox',
  100. default: true,
  101. },
  102. offline_result: {
  103. label: '任务添加后显示离线结果',
  104. labelPos: 'right',
  105. type: 'checkbox',
  106. default: true,
  107. },
  108. open_List: {
  109. label: '离线后自动打开任务列表',
  110. labelPos: 'right',
  111. type: 'checkbox',
  112. default: false,
  113. },
  114. open_search: {
  115. label: '离线成功后开启视频搜索',
  116. labelPos: 'right',
  117. type: 'checkbox',
  118. default: true,
  119. line: 'start',
  120. },
  121. search_result: {
  122. label: '显示视频搜索结果',
  123. labelPos: 'right',
  124. type: 'checkbox',
  125. default: true,
  126. },
  127. open_Popup: {
  128. label: '搜到视频自动播放',
  129. labelPos: 'right',
  130. type: 'checkbox',
  131. default: false,
  132. line: 'end',
  133. },
  134. fuzzy_find: {
  135. label: '启用下载地址模糊匹配',
  136. labelPos: 'right',
  137. type: 'checkbox',
  138. default: false,
  139. },
  140. diy_folder: {
  141. label: '自定义离线下载文件夹',
  142. labelPos: 'right',
  143. type: 'checkbox',
  144. default: false,
  145. line: 'start',
  146. },
  147. save_folder: {
  148. label: '设置文件夹',
  149. labelPos: 'right',
  150. type: 'button',
  151. line: 'end',
  152. click: function() {
  153. setFolder();
  154. }
  155. },
  156. reminder2: {
  157. label: '温馨提示',
  158. labelPos: 'right',
  159. type: 'button',
  160. click: function() {
  161. alert('1、显示离线下载结果有10s延时,用于服务器响应时间。\n2、为避免通知弹窗过多,最多只显示3个视频搜索结果,更多请自行到115查看。\n3、“启用下载地址模糊匹配”后,能根据哈希值或纯文本模糊匹配磁力链接和迅雷专用链,如在磁力搜索引擎、资源网等有奇效,但在某些网页有一定几率误识别,请谨慎开启。');
  162. }
  163. },
  164. },
  165. events: {
  166. save: function() {
  167. GM_config.close();
  168. location.reload();
  169. }
  170. },
  171. });
  172. }
  173. config();
  174.  
  175. var G = GM_config;
  176. var localHref = window.location.href;
  177. var show_result = G.get('offline_result');
  178. var down_reg = /^(magnet|thunder|ftp|ed2k):/i;
  179. var UA = navigator.userAgent; // 使用浏览器默认UA
  180. var sign_url = 'http://115.com/?ct=offline&ac=space';
  181. var task_del = 'https://115.com/web/lixian/?ct=lixian&ac=task_del';
  182. var add_url = 'http://115.com/web/lixian/?ct=lixian&ac=add_task_url';
  183. var add_urls = 'http://115.com/web/lixian/?ct=lixian&ac=add_task_urls';
  184. var lists_url = 'http://115.com/web/lixian/?ct=lixian&ac=task_lists';
  185. var a_list = `<br><a target="_blank" class="openList" href="javascript:void(0);" style="color:blue;" title="点击打开离线链接任务列表">打开任务列表</a>`;
  186. console.log('115脚本UA:' + UA);
  187.  
  188. // 通知样式
  189. function notice() {
  190. GM_addStyle(GM_getResourceText('toastrCss'));
  191. GM_addStyle('.toast{font-size:15px!important;width:360px!important;} .toast-title{font-size:16px!important;text-align:center}');
  192. toastr.options = {
  193. "closeButton": true,
  194. "debug": false,
  195. "progressBar": true,
  196. "timeOut": 8000,
  197. "extendedTimeOut": 8000,
  198. "positionClass": 'toast-top-right', // 固定右上角
  199. "allowHtml": true,
  200. "newestOnTop": false,
  201. };
  202. }
  203. notice();
  204.  
  205. // AJAX请求
  206. function AjaxCall(href, callback) {
  207. GM_xmlhttpRequest({
  208. method: "GET",
  209. url: href,
  210. headers: {
  211. "User-Agent": UA,
  212. Origin: "https://115.com",
  213. },
  214. onload: function(data) {
  215. if (data.readyState == 4 && data.status == 200) {
  216. var htmlTxt = data.responseText;
  217. callback(null, htmlTxt);
  218. }
  219. },
  220. onerror: function(error) {
  221. callback(error);
  222. },
  223. ontimeout: function(error) {
  224. callback(error);
  225. },
  226. });
  227. }
  228.  
  229. // 设置离线下载文件夹
  230. function setFolder() {
  231. var old_cid = GM_getValue('offlineFolder') || '';
  232. var new_cid = prompt('请输入离线下载保存文件夹的cid值:\n ※ 获取cid值方法:打开需要保存到的网盘文件夹,复制地址栏中"cid="后面的一串数字,以"&"截止,如https://115.com/?cid=012345678912345678&...,cid值则为 012345678912345678。该项不填或填无效值则保存至默认文件夹(云下载)。※', old_cid);
  233. if (/^(\d{17,19}|0)$/.test(new_cid)) {
  234. GM_setValue('offlineFolder', new_cid);
  235. alert('设置成功,现cid值为:\n' + new_cid);
  236. } else if (new_cid == '') {
  237. GM_setValue('offlineFolder', '');
  238. alert('未输入cid值,保存至默认文件夹(云下载)。');
  239. } else if (new_cid == null) {
  240. console.log('已点击取消');
  241. } else {
  242. alert('设置失败,cid值无效,请重新输入!\n(该值除根目录为 0 外,其他文件夹均为17至19位纯数字)');
  243. setFolder();
  244. }
  245. }
  246.  
  247. // 处理下载链接
  248. function getRightUrl(url) {
  249. var newUrl = url;
  250. if (/^thunder/i.test(url)) {
  251. var key = url.replace(/thunder:\/\//i, '');
  252. var temp = decode64(key);
  253. newUrl = decodeURIComponent(temp.slice(2, -2));
  254. }
  255. if (/^magnet/i.test(newUrl)) {
  256. var hash = newUrl.split('&')[0].substring(20) || newUrl.substring(20);
  257. if (hash.length == 32) {
  258. hash = base32To16(hash);
  259. }
  260. newUrl = 'magnet:?xt=urn:btih:' + hash;
  261. } else if (/^\/\//.test(url)) {
  262. newUrl = location.protocol + url;
  263. } else if (/^\/(?!\/)/.test(url)) {
  264. newUrl = location.protocol + '//' + location.host + url;
  265. }
  266. return newUrl;
  267. }
  268.  
  269. // Base32转Base16
  270. function base32To16(str) {
  271. if (str.length % 8 !== 0 || /[0189]/.test(str)) {
  272. return str;
  273. }
  274. str = str.toUpperCase();
  275. var bin = "", newStr = "", i;
  276. for (i = 0; i < str.length; i++) {
  277. var charCode = str.charCodeAt(i);
  278. if (charCode < 65) charCode -= 24;
  279. else charCode -= 65;
  280. charCode = '0000' + charCode.toString(2);
  281. charCode = charCode.substr(charCode.length - 5);
  282. bin += charCode;
  283. }
  284. for (i = 0; i < bin.length; i += 4) {
  285. newStr += parseInt(bin.substring(i, i + 4), 2).toString(16);
  286. }
  287. return newStr;
  288. }
  289.  
  290. // 检查重复链接
  291. function repeat(link) {
  292. var result = false;
  293. var A = link.slice(0, 60);
  294. if ($('.115offline').length == 0) return result;
  295. $('.115offline').each(function() {
  296. var B = $(this).data('href').slice(0, 60);
  297. if (A.toLowerCase() == B.toLowerCase()) {
  298. result = true;
  299. return false;
  300. }
  301. });
  302. return result;
  303. }
  304.  
  305. // 搜索离线任务
  306. function searchTask(json, link) {
  307. var dataEl = false;
  308. for (var i = 0; i < json.tasks.length; i++) {
  309. if (json.tasks[i].url == link || json.tasks[i].url == decodeURIComponent(link)) {
  310. dataEl = json.tasks[i];
  311. break;
  312. }
  313. }
  314. return dataEl;
  315. }
  316.  
  317. // 账号验证弹窗
  318. function verify() {
  319. var time = new Date().getTime();
  320. var w = 335;
  321. var h = 500;
  322. var t = (window.screen.availHeight - h) / 2;
  323. var l = (window.screen.availWidth - w) / 2;
  324. var link = 'https://captchaapi.115.com/?ac=security_code&type=web&cb=Close911_' + time;
  325. var a = confirm('立即打开验证账号弹窗?');
  326. if (a) {
  327. var blocked = false;
  328. try {
  329. var wroxWin = window.open(link, '请验证账号', 'height=' + h + ',width=' + w + ',top=' + t + ',left=' + l + ',toolbar=no,menubar=no,scrollbars=no,resizable=no,location=no,status=no');
  330. if (wroxWin == null) {
  331. blocked = true;
  332. }
  333. } catch (e) {
  334. blocked = true;
  335. }
  336. if (blocked) {
  337. alert('验证弹窗已被拦截,请允许本页面弹出式窗口!');
  338. }
  339. }
  340. }
  341.  
  342. // 离线下载核心逻辑
  343. var offline = function() {
  344. return {
  345. getSign: function(key, save_name) {
  346. return new Promise(function(resolve, reject) {
  347. if (/^\w+=/.test(key)) {
  348. resolve(key);
  349. return;
  350. }
  351.  
  352. var UserID = GM_getValue('115ID') || '';
  353. var cid = G.get('diy_folder') ? GM_getValue('offlineFolder') : '';
  354. var title = save_name ? save_name : '';
  355. GM_xmlhttpRequest({
  356. method: 'GET',
  357. url: sign_url,
  358. responseType: 'json',
  359. headers: {
  360. "User-Agent": UA,
  361. Origin: "https://115.com",
  362. },
  363. onload: function(result) {
  364. if (result.responseText.indexOf('html') != -1) {
  365. toastr.error('请先登录115网盘账号!', '离线任务添加失败。');
  366. setTimeout(function() {
  367. var a = confirm('立即打开115网盘登录页面?');
  368. if (a) {
  369. GM_openInTab('https://115.com/?mode=login', false);
  370. }
  371. }, 3000);
  372. return;
  373. }
  374. var data = {
  375. uid: UserID,
  376. sign: result.response.sign,
  377. time: result.response.time,
  378. wp_path_id: cid,
  379. savepath: title
  380. };
  381.  
  382. if ($.isPlainObject(key)) {
  383. var value = $.param($.extend(data, key));
  384. } else {
  385. var value = $.param(data) + `&url=${key}`;
  386. }
  387. resolve(value);
  388. },
  389. onerror: function(error) {
  390. reject(error);
  391. },
  392. });
  393. });
  394. },
  395.  
  396. getData: function(herf, key, save_name) {
  397. return offline.getSign(key, save_name).then(function(value) {
  398. return new Promise(function(resolve, reject) {
  399. GM_xmlhttpRequest({
  400. method: 'POST',
  401. data: value,
  402. url: herf,
  403. responseType: 'json',
  404. headers: {
  405. "User-Agent": UA,
  406. "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
  407. "Accept": "application/json, text/javascript, */*; q=0.01",
  408. Origin: "https://115.com",
  409. "X-Requested-With": "XMLHttpRequest"
  410. },
  411. onload: function(result) {
  412. resolve(result.response);
  413. },
  414. onerror: function(error) {
  415. reject(error);
  416. },
  417. });
  418. });
  419. });
  420. },
  421.  
  422. check: function(link, link2, one) {
  423. if (document.hidden) {
  424. GM_setValue('noTimeOut', true);
  425. toastr.options.timeOut = 0;
  426. } else {
  427. GM_setValue('noTimeOut', false);
  428. toastr.options.timeOut = 12000;
  429. }
  430.  
  431. var c = 1;
  432. var retry = false;
  433. var txt2 = '5秒后自动重试,请稍等。';
  434. function add(retry, txt2) {
  435. if (c == 6) {
  436. console.log('离线结果查询异常。离线任务数量过多,请清空后再试。');
  437. toastr.warning('离线任务数量过多,请清空后再试。', '离线结果查询异常!');
  438. return;
  439. }
  440.  
  441. var key = '';
  442. var lists_url2 = lists_url + '&page=' + c;
  443. console.log('离线任务数据地址:' + lists_url2);
  444. offline.getData(lists_url2, key).then(function(json) {
  445. console.log('离线任务列表第' + c + '页:');
  446. console.log(json);
  447. if (json.state) {
  448. var dataEl = searchTask(json, link);
  449. if (dataEl) {
  450. var name = dataEl.del_path == '' ? dataEl.name : dataEl.del_path.slice(0, -1);
  451. var a_del = ` <a target="_blank" class="delTask" data=${dataEl.info_hash} href="javascript:void(0);" style="color:blue;" title="删除该离线任务">删除任务</a>`;
  452. if (dataEl.status != -1) {
  453. if (dataEl.move == -1) {
  454. toastr.warning('空间不足,请到115扩容', '离线下载异常!');
  455. return;
  456. }
  457. var down_result = dataEl.percentDone.toFixed(0);
  458. var cid = dataEl.file_id || 0;
  459. if (down_result >= 99 && cid != 0) {
  460. var txt = `文件(夹)名:${name},大小:${change(dataEl.size)}。`;
  461. resultMark(link2, 3);
  462. if (one) {
  463. console.log(txt + '离线下载已完成。');
  464. return;
  465. }
  466.  
  467. if (show_result && !retry) {
  468. toastr.success(txt + a_list + a_del, '离线下载已完成', { timeOut: 5000 });
  469. }
  470.  
  471. if (G.get('open_search')) {
  472. offline.search(dataEl.name, cid, function(search_result, video, j, num) {
  473. if (search_result) {
  474. if (G.get('search_result')) {
  475. var videoTxt = JSON.stringify(video);
  476. var txt = `文件名:${video.name},大小:${video.size},时长:${tranTime(video.time)}。`;
  477. var h1 = `<br><a target="_blank" class="115play" data=${videoTxt} href="javascript:void(0);" style="color:blue;" title="播放该视频">播放</a>`;
  478. var h2 = ` <a target="_blank" class="115down" data=${videoTxt} one="1" href="javascript:void(0);" style="color:blue;" title="下载该视频">下载</a>`;
  479. var h3 = ` <a target="_blank" class="115del" data=${videoTxt} href="javascript:void(0);" style="color:blue;" title="删除该视频文件夹">删除</a>`;
  480. var h4 = ` <a target="_blank" class="115newName" data=${videoTxt} href="javascript:void(0);" style="color:blue;" title="重命名该视频">重命名</a>`;
  481. var h5 = ` <a target="_blank" class="openFolder" data=${cid} href="javascript:void(0);" style="color:blue;" title="查看所属文件列表">查看</a>`;
  482. toastr.success(txt + h1 + h2 + h3 + h4 + h5, `发现第 ${j} 个视频(共 ${num} 个)`);
  483. }
  484.  
  485. if (G.get('open_Popup') && j == 1) {
  486. setTimeout(function() {
  487. var type = '115play';
  488. palyData(video, type);
  489. }, 500);
  490. }
  491. } else {
  492. if (dataEl.move == 2 || dataEl.move == 0 || dataEl.status == 0) {
  493. var txt = '离线数据取回网盘中。';
  494. } else {
  495. var txt = '未发现任何视频文件。';
  496. }
  497. var h1 = `<br><a target="_blank" class="openFolder" data=${cid} href="javascript:void(0);" style="color:blue;" title="点击打开所属文件列表">打开文件列表</a>`;
  498. toastr.warning(txt + txt2 + h1, '视频搜索无结果!');
  499.  
  500. if (!retry) {
  501. setTimeout(function() {
  502. retry = true;
  503. txt2 = '';
  504. toastr.clear();
  505. console.log('重试搜索结果:');
  506. add(retry, txt2);
  507. }, 5000);
  508. }
  509. }
  510. });
  511. }
  512. } else if (show_result) {
  513. resultMark(link2, 4);
  514. if (one) {
  515. console.log(`文件(夹)名:${name},已离线下载 ${down_result}%。`);
  516. return;
  517. }
  518. var txt = `文件(夹)名:${name},下载进度为:<span style="color:purple;">${down_result}%</span>。`;
  519. toastr.warning(txt + a_list + a_del, '离线下载中...');
  520. }
  521. } else if (show_result) {
  522. resultMark(link2, 4);
  523. if (one) {
  524. console.log(`文件(夹)名:${name},离线下载失败。`);
  525. return;
  526. } else if (dataEl.err == 10016) {
  527. var txt = '文件含违规内容,已自动拦截。';
  528. } else {
  529. var txt = '未知原因,请到115网盘查看。';
  530. var a_del = '';
  531. }
  532. toastr.error(txt + a_list + a_del, '离线下载失败!', { timeOut: 8000 });
  533. return;
  534. }
  535. } else {
  536. console.log('第' + c + '页查询失败,无匹配数据');
  537. if (c == json.page_count) {
  538. console.log('离线链接对比异常,已搜索所有离线列表页面,无返回结果。');
  539. toastr.warning('搜索参数错误。', '离线结果查询异常!', { timeOut: 5000 });
  540. return;
  541. }
  542. c++;
  543. add();
  544. }
  545. } else {
  546. toastr.error('查询离线结果失败。', '服务器错误!');
  547. return;
  548. }
  549. });
  550. }
  551. add(retry, txt2);
  552. },
  553.  
  554. addButton: function() {
  555. $('[href]').each(function() {
  556. var url = $(this).attr('href');
  557. var reg1 = /\.(torrent|rar|zip|7z|mp4|rmvb|mkv|avi)$/i;
  558. var $El = $(this).parent().filter('li,td,th,:header').find('[Searched]');
  559.  
  560. if ((!down_reg.test(url) && !reg1.test(url)) || $(this).is('[Searched]') || $El.length > 1 || ($El.length = 1 && url.indexOf($El.attr('Searched')) != -1)) {
  561. return;
  562. }
  563.  
  564. if (down_reg.test(url)) {
  565. $(this).attr('Searched', url.split(':')[0]);
  566. } else if (/torrent$/i.test(url)) {
  567. $(this).attr('Searched', 'torrent');
  568. } else {
  569. $(this).attr('Searched', 'other');
  570. }
  571.  
  572. var link = getRightUrl(url);
  573. if (repeat(link)) {
  574. return;
  575. }
  576.  
  577. $(this).css('display', 'inline-block');
  578. $(this).after('<img src="https://cdn.jsdelivr.net/gh/zxf10608/JavaScript/icon/115logo.ico" class="115offline" data-href=' + link + ' style="z-index:9123456789;display:inline-block;cursor:pointer;margin:0px 5px 2px;border-radius:50%;border:0px;vertical-align:middle;outline:none!important;padding:0px!important;height:20px!important;width:20px!important;left:0px!important;top:0px!important;" title="使用115网盘离线下载,右键复制地址\n' + link + '">');
  579. });
  580. },
  581.  
  582. addLink: function() {
  583. $('a,button,span,li').each(function() {
  584. var reg1 = /(^|\/|&|-|\.|\?|=|:|#|_|@)([a-f0-9]{40}|[a-z2-7]{32})(?!\w)/i;
  585. var reg2 = /[a-z]{40}|[a-z]{32}/i;
  586.  
  587. if ($(this).next().addBack().is('[Searched],[href*="google"],[href*="motelppp.com"],[href*="bvmqkla.de"]') || $(this).find('img').length > 0) {
  588. return;
  589. }
  590.  
  591. var url = getAttribute(this);
  592. if (url.length > 0) {
  593. for (var i = 0; i < url.length; i++) {
  594. if (down_reg.test(url[i]) || (reg1.test(url[i]) && !reg2.test(url[i].match(reg1)[2]))) {
  595. if (down_reg.test(url[i])) {
  596. var value = url[i].split(':')[0];
  597. var templink = url[i];
  598. } else {
  599. var value = 'magnet';
  600. var templink = 'magnet:?xt=urn:btih:' + url[i].match(reg1)[2];
  601. }
  602. var link = getRightUrl(templink);
  603. if (repeat(link)) {
  604. return;
  605. }
  606. $(this).attr('Searched', value);
  607. $(this).after('<img src="https://cdn.jsdelivr.net/gh/zxf10608/JavaScript/icon/115logo.ico" class="115offline" data-href=' + link + ' style="z-index:9123456789;display:inline-block;cursor:pointer;margin:0px 5px 2px;border-radius:50%;border:0px;vertical-align:middle;outline:none!important;padding:0px!important;height:20px!important;width:20px!important;left:0px!important;top:0px!important;" title="使用115网盘离线下载2,右键复制地址\n' + link + '">');
  608. return;
  609. }
  610. }
  611. }
  612. });
  613. },
  614.  
  615. addSelect: function() {
  616. if ($('.115offline').length < 3) return;
  617.  
  618. $('.115offline:not([Sed])').each(function() {
  619. $(this).attr('Sed', 1);
  620. var url = $(this).data('href');
  621. $(this).after('<input type="checkbox" class="115select" value=' + url + ' title="长按shift键,连续选择" style="z-index:9123456789;display:inline-block;cursor:pointer;height:16px!important;width:16px!important;margin:0px 2px 1px;border-radius:50%;border:0px;vertical-align:middle;outline:none!important;padding:0px!important;left:0px!important;top:0px!important;" />');
  622. });
  623.  
  624. var sel = $('.115offline').length > 10 ? $('.115select:eq(-1),.115select:eq(0)') : $('.115select:eq(-1)');
  625. sel.each(function() {
  626. if ($(this).is('[batched]')) return;
  627. $(this).attr('batched', 1);
  628. $(this).after('<img src="https://cdn.jsdelivr.net/gh/zxf10608/JavaScript/icon/batch_down00.png" class="115offline_batch" style="z-index:9123456789;display:inline-block;cursor:pointer;margin:0px 1px 2px;border:0px;vertical-align:middle;outline:none!important;padding:0px!important;height:23px!important;width:23px!important;left:0px!important;top:0px!important;" title="使用115网盘批量离线下载所选地址,右键可全选等">');
  629. $(this).parent().css('overflow', 'visible');
  630. });
  631. },
  632.  
  633. search: function(name, cid, callback) {
  634. var title = new Array();
  635. title[0] = name.replace(/(\.|-|_)?(f?hd|sd|720p|1080p|full|mp4|avi|mkv|wmv|rmvb|rm|flv|f4v)/gi, ' ');
  636. title[1] = title[0].replace(/\/|&|-|\.|\?|=|:|#|_|@/g, ' ');
  637. title[2] = '.';
  638.  
  639. var a = 0;
  640. function add2() {
  641. if (a == 3) {
  642. console.log('该文件夹无视频文件。');
  643. callback(false);
  644. return;
  645. }
  646. var searchLink = 'https://webapi.115.com/files/search?cid=' + cid + '&search_value=' + encodeURIComponent(title[a]) + '&type=4';
  647. AjaxCall(searchLink, function(error, htmlTxt) {
  648. if (typeof htmlTxt == 'undefined') {
  649. a++;
  650. add2();
  651. } else {
  652. var json = JSON.parse(htmlTxt);
  653. if (a == 2 && json.folder.name == '云下载') {
  654. callback(false);
  655. return;
  656. }
  657.  
  658. if (json.count > 0) {
  659. var num = json.count;
  660. for (var i = 0; i < num; i++) {
  661. var $dataEh = json.data[i];
  662. var video = {};
  663. video['name'] = name2($dataEh.n);
  664. video['pid'] = $dataEh.pc;
  665. video['cid'] = $dataEh.cid;
  666. video['fid'] = $dataEh.fid;
  667. video['size'] = change($dataEh.s);
  668. video['sha'] = $dataEh.sha;
  669. video['time'] = $dataEh.play_long;
  670.  
  671. callback(true, video, i + 1, num);
  672. console.log('第' + a + '次搜索结果' + i + ':' + $dataEh.n + ' ' + video.size);
  673. if (i == 2) {
  674. return;
  675. }
  676. }
  677. } else {
  678. a++;
  679. add2();
  680. }
  681. }
  682. });
  683. }
  684. add2();
  685. }
  686. };
  687. }();
  688.  
  689. // 文件大小转换
  690. function change(number) {
  691. var size = "";
  692. if (number < 1024 * 1024 * 1024) {
  693. size = (number / (1024 * 1024)).toFixed(2) + "MB";
  694. } else {
  695. size = (number / (1024 * 1024 * 1024)).toFixed(2) + "GB";
  696. }
  697. var sizeStr = size + "";
  698. var index = sizeStr.indexOf(".");
  699. var dou = sizeStr.substr(index + 1, 2);
  700. if (dou == "00") {
  701. return sizeStr.substring(0, index) + sizeStr.substr(index + 3, 2);
  702. }
  703. return size;
  704. }
  705.  
  706. // 文件名处理
  707. function name2(txt) {
  708. var newName = txt.replace(/\.(?!\w{2,4}$)/g, '_').replace(/\s/g, ' ');
  709. return newName;
  710. }
  711.  
  712. // 时间转换
  713. function tranTime(num) {
  714. var showTime = '';
  715. if (num > 3600) { showTime += ' ' + parseInt(num / 3600) + ' 小时'; num = num % 3600; }
  716. if (num > 60) { showTime += ' ' + parseInt(num / 60) + ' 分'; num = num % 60; }
  717. return showTime += ' ' + parseInt(num) + ' 秒';
  718. }
  719.  
  720. // 点击限制
  721. function clickOne(el, t) {
  722. var time = t ? t : 5000;
  723. if (el.attr('clicked') == 1) {
  724. console.log('5s内不可点击该按钮');
  725. return false;
  726. } else {
  727. el.attr('clicked', 1);
  728. el.css('opacity', '0.2');
  729. setTimeout(function() {
  730. el.attr('clicked', 0);
  731. el.css('opacity', '0.7');
  732. }, time);
  733. return true;
  734. }
  735. }
  736.  
  737. // 标记结果
  738. function resultMark(el, type) {
  739. if (el.length == 0 || !show_result) return;
  740.  
  741. var urls = [];
  742. var color = { 1: '#00CCFF', 2: '#DA70D6', 3: '#AEDD81', 4: '#EB7347' }[type];
  743. for (var i = 0; i < el.length; i++) {
  744. urls.push(el[i].url);
  745. $('.115offline').each(function() {
  746. var link = $(this).data('href');
  747. var $al = $(this).prev();
  748. var m = $al.attr('marked');
  749. if ((el[i].url == link || el[i].url == decodeURIComponent(link)) && m != 3) {
  750. $al.attr('marked', type).css('background-color', color);
  751. $al.find('[style]').removeAttr('style');
  752. return false;
  753. }
  754. });
  755. }
  756. return urls;
  757. }
  758.  
  759. // 获取属性
  760. function getAttribute(e) {
  761. var data = [];
  762. $.each(e.attributes, function() {
  763. if (this.specified && this.value.length > 30) {
  764. data.push(this.value);
  765. }
  766. });
  767. if ($(e).text().length > 25) data.push($(e).text());
  768. return data;
  769. }
  770.  
  771. // 右键菜单
  772. function right_menu() {
  773. $('body').append(`
  774. <div class="115menu" style="width:97px;height:85px;z-index:9123456789;overflow:hidden;position:absolute;display:none;background-color:#D0D0D0">
  775. <ul style="padding:5px 7px;margin:0px;list-style:none;">
  776. <li><a href="javascript:;" class="right_menu00">全选</a></li>
  777. <li><a href="javascript:;" class="right_menu01">反选</a></li>
  778. <li><a href="javascript:;" class="right_menu02">复制所选</a></li>
  779. <li><a href="javascript:;" class="right_menu11">复制地址</a></li>
  780. <li><a href="javascript:;" class="right_menu12">种子下载1</a></li>
  781. <li><a href="javascript:;" class="right_menu13">种子下载2</a></li>
  782. </ul>
  783. </div>`);
  784. $('.115menu a').css({ 'line-height': '25px', 'text-decoration': 'none', 'color': '#2C3E50', 'padding': '1px 5px', 'font-size': '16px', 'font-family': 'arial' });
  785. $('.115menu a').hover(function() {
  786. $(this).css({ 'background-color': '#2777F8', 'color': '#FFF' });
  787. }, function() {
  788. $(this).css({ 'background-color': '', 'color': '#2C3E50' });
  789. });
  790. }
  791.  
  792. // 播放视频
  793. function palyData(video, type) {
  794. var link = 'https://115.com/?ct=play&ac=location&pickcode=' + video.pid + '&hls=1';
  795. GM_openInTab(link, false);
  796. }
  797.  
  798. // 文档加载事件
  799. $(document).ready(function() {
  800. if (localHref.indexOf('https://115.com/') != -1) {
  801. if (typeof (unsafeWindow.USER_ID) != 'undefined') {
  802. GM_setValue('115ID', unsafeWindow.USER_ID);
  803. console.log('115账号已登录,账号ID获取成功!');
  804. } else {
  805. console.log('115账号未登录,账号ID获取失败!');
  806. }
  807. }
  808.  
  809. if (localHref.indexOf('https://captchaapi.115.com') != -1) {
  810. var t_close;
  811. $('body').on('click', '.vcode-hint', function() {
  812. t_close = setTimeout(function() {
  813. console.log('验证正确');
  814. window.open('', '_self');
  815. window.close();
  816. }, 200);
  817. return false;
  818. });
  819. $('.vcode-hint').on('DOMNodeInserted', function(e) {
  820. console.log('验证错误!');
  821. clearTimeout(t_close);
  822. });
  823. }
  824. });
  825.  
  826. // 主逻辑
  827. $(document).ready(function() {
  828. $(document).on('click', function(e) {
  829. $('.115menu').hide();
  830. });
  831.  
  832. $('body').on('click', '.115offline', function(e) {
  833. if (!clickOne($(this))) return;
  834. var link = $(this).data('href');
  835. var save_name = '';
  836. offline.getData(add_url, link, save_name).then(function(json) {
  837. console.log('离线任务添加结果:');
  838. console.log(json);
  839. var errNum = json.errcode || json.error_code || '';
  840. var link2 = [{'url': link}];
  841. if (json.state) {
  842. if (show_result) {
  843. var txt = '10秒后显示离线结果。';
  844. } else {
  845. var txt = link;
  846. if (G.get('open_List')) {
  847. setTimeout(function() {
  848. GM_openInTab('https://115.com/?tab=offline&mode=wangpan', false);
  849. }, 2000);
  850. }
  851. }
  852. resultMark(link2, 1);
  853. toastr.info(txt, '离线任务添加成功。', { timeOut: 10000 });
  854. setTimeout(function() {
  855. offline.check(link, link2);
  856. }, 10000);
  857. } else if (errNum == 10008) {
  858. toastr.warning('任务已存在,无需重复添加。', '离线任务添加无效!', { timeOut: 5000 });
  859. if (G.get('open_List')) {
  860. setTimeout(function() {
  861. GM_openInTab('https://115.com/?tab=offline&mode=wangpan', false);
  862. }, 2000);
  863. }
  864. resultMark(link2, 1);
  865. offline.check(link, link2);
  866. } else if (errNum == 911) {
  867. toastr.warning('账号异常,请验证账号。', '离线下载失败!', { timeOut: 5000 });
  868. setTimeout(function() {
  869. verify();
  870. }, 1000);
  871. } else {
  872. resultMark(link2, 2);
  873. toastr.warning(json.error_msg, '离线任务添加失败!', { timeOut: 12000 });
  874. }
  875. console.log('离线链接:' + link + ' 添加结果:' + json.state + ' 原因:' + json.error_msg);
  876. }, function(error) {
  877. toastr.error('服务器繁忙,请稍后再试。', '离线任务添加异常!');
  878. console.log(error);
  879. });
  880. return false;
  881. });
  882.  
  883. $('body').on('click', '.115offline_batch', function() {
  884. var l = $('.115select:checked').length;
  885. if (l < 10 && !clickOne($('.115offline_batch'))) {
  886. return;
  887. } else if (l < 2 || l > 100) {
  888. toastr.warning('单次选中数量限 <span style="color:red;">2-99</span> 个。', '批量离线操作无效!', { timeOut: 6000 });
  889. return;
  890. } else if (l > 10) {
  891. toastr.info('所选中地址较多,服务器需要较长时间响应,请稍等10s以上,未弹出结果前勿重复点击。', '温馨提示。', { timeOut: 10000 });
  892. if (!clickOne($('.115offline_batch'), 10000)) return;
  893. }
  894.  
  895. var links = {};
  896. $('.115select:checked').each(function(e) {
  897. links['url[' + e + ']'] = $(this).attr('value');
  898. });
  899.  
  900. offline.getData(add_urls, links).then(function(json) {
  901. console.log('批量离线任务添加结果:');
  902. console.log(json);
  903. var errNum = json.errcode || json.error_code || '';
  904.  
  905. if (json.state) {
  906. var s = 0, e = 0, f = 0;
  907. var success_result = [], exist_result = [], all_result = [], fail_result = [];
  908. for (var n = 0; n < json.result.length; n++) {
  909. var dataEl = json.result[n];
  910. if (dataEl.state) {
  911. s++;
  912. success_result.push(dataEl);
  913. all_result.push(dataEl);
  914. } else if (dataEl.errcode == 10008) {
  915. e++;
  916. exist_result.push(dataEl);
  917. all_result.push(dataEl);
  918. } else {
  919. f++;
  920. fail_result.push(dataEl);
  921. }
  922. }
  923.  
  924. var txt2 = '10秒后显示离线结果。';
  925. var error = fail_result.length > 0 ? fail_result[0].error_msg : '任务已存在';
  926. if (f + e == json.result.length) {
  927. var txt1 = `有 <span style="color:red;">${f + e}</span> 个任务创建失败,原因:${error}。`;
  928. toastr.warning(txt1 + a_list, '批量离线任务添加失败。', { timeOut: 10000 });
  929. } else if (f + e > 0) {
  930. if (e > 0) txt2 = '新建任务' + txt2;
  931. var txt1 = `有 <span style="color:purple;">${s}</span> 个任务创建成功。有 <span style="color:red;">${f + e}</span> 个任务创建失败,原因:${error}。`;
  932. toastr.info(txt1 + txt2 + a_list, '批量离线任务添加成功。', { timeOut: 10000 });
  933. } else {
  934. var txt1 = `有 <span style="color:purple;">${s}</span> 个任务创建成功。`;
  935. toastr.info(txt1 + txt2 + a_list, '批量离线任务添加成功。', { timeOut: 10000 });
  936. }
  937.  
  938. var success_links = resultMark(success_result, 1);
  939. var exist_links = resultMark(exist_result, 1);
  940. resultMark(fail_result, 2);
  941.  
  942. if (s + e > 20) {
  943. toastr.warning('离线数量大于20,请自行到115查看。' + a_list, '未查询离线结果!', { timeOut: 6000 });
  944. return;
  945. }
  946.  
  947. if (s > 0) {
  948. setTimeout(function() {
  949. for (let h = 0; h < s; h++) {
  950. var one = (all_result[0].url == success_links[h]) ? false : true;
  951. var url2 = [{'url': success_links[h]}];
  952. offline.check(success_links[h], url2, one);
  953. }
  954. }, 10000);
  955. }
  956. if (e > 0) {
  957. for (let i = 0; i < e; i++) {
  958. var one = (all_result[0].url == exist_links[i]) ? false : true;
  959. var url2 = [{'url': exist_links[i]}];
  960. offline.check(exist_links[i], url2, one);
  961. }
  962. }
  963.  
  964. if (f != json.result.length && G.get('open_List')) {
  965. setTimeout(function() {
  966. GM_openInTab('https://115.com/?tab=offline&mode=wangpan', false);
  967. }, 2000);
  968. }
  969. } else if (errNum == 911) {
  970. toastr.warning('账号异常,请验证账号。', '批量离线下载失败!', { timeOut: 5000 });
  971. setTimeout(function() {
  972. verify();
  973. }, 1000);
  974. } else {
  975. toastr.warning(json.error_msg + a_list, '批量离线任务添加失败!', { timeOut: 12000 });
  976. }
  977. }, function(error) {
  978. toastr.error('服务器繁忙,请稍后再试。', '批量离线任务添加异常!');
  979. console.log(error);
  980. });
  981. return false;
  982. });
  983.  
  984. $('body').on('click', '.115select', function(e) {
  985. if (e.shiftKey) {
  986. var first = $('.115select').index($('.115select:checked').first());
  987. var me = $('.115select').index($(this));
  988. var last = $('.115select').index($('.115select:checked').last());
  989. var Min = Math.min(first, me, last);
  990. var Max = Math.max(first, me, last);
  991.  
  992. for (var i = Min; i <= Max; i++) {
  993. $('.115select').eq(i).prop('checked', true);
  994. }
  995. }
  996. });
  997.  
  998. $('body').on('click', '.openList:not([click="1"]), .openFolder:not([click="1"])', function() {
  999. $(this).attr('click', '1');
  1000. if ($(this).is('.openList')) {
  1001. var txt = 'tab=offline';
  1002. } else {
  1003. var fID = $(this).attr('data');
  1004. var txt = 'cid=' + fID + '&offset=0';
  1005. }
  1006. GM_openInTab('https://115.com/?' + txt + '&mode=wangpan', false);
  1007. return false;
  1008. });
  1009.  
  1010. $('body').on('contextmenu', '.115offline, .115offline_batch', function(e) {
  1011. $('.115menu').css({ left: e.pageX + 'px', top: e.pageY + 'px' });
  1012. $('.115menu').show();
  1013. var link = $(this).data('href') || '';
  1014. if ($(this).is('.115offline_batch')) {
  1015. $('.115menu [class^="right_menu0"]').show();
  1016. $('.115menu [class^="right_menu1"]').hide();
  1017. } else if (/^magnet/i.test(link)) {
  1018. $('.115menu').attr('data-href', link);
  1019. $('.115menu [class^="right_menu1"]').show();
  1020. $('.115menu [class^="right_menu0"]').hide();
  1021. } else {
  1022. $('.115menu').hide();
  1023. GM_setClipboard(link);
  1024. toastr.success('下载地址复制成功!');
  1025. }
  1026. return false;
  1027. });
  1028.  
  1029. $('body').on('click', '[class^="right_menu0"]', function() {
  1030. if ($(this).is('.right_menu00')) {
  1031. $('.115select').prop('checked', true);
  1032. } else if ($(this).is('.right_menu01')) {
  1033. $('.115select').each(function() {
  1034. if ($(this).prop('checked')) {
  1035. $(this).prop('checked', false);
  1036. } else {
  1037. $(this).prop('checked', true);
  1038. }
  1039. });
  1040. } else {
  1041. if ($('.115select:checked').length == 0) {
  1042. toastr.warning('复制失败,未选中任何链接!');
  1043. return;
  1044. }
  1045. var urls = [];
  1046. $('.115select:checked').each(function() {
  1047. urls.push($(this).attr('value'));
  1048. });
  1049. GM_setClipboard(urls.join('\r\n'));
  1050. toastr.success(urls.length + ' 个下载地址复制成功!');
  1051. }
  1052. return false;
  1053. });
  1054.  
  1055. $('body').on('click', '[class^="right_menu1"]', function() {
  1056. var link = $(this).parents('.115menu').data('href');
  1057. var hash = link.match(/[a-f0-9]{40}/i)[0].toUpperCase();
  1058. if ($(this).is('.right_menu11')) {
  1059. GM_setClipboard(link);
  1060. toastr.success('下载地址复制成功!');
  1061. } else if ($(this).is('.right_menu12')) {
  1062. GM_openInTab(`https://itorrents.org/torrent/${hash}.torrent`, false);
  1063. } else if ($(this).is('.right_menu13')) {
  1064. GM_openInTab(`https://btcache.me/torrent/${hash}`, false);
  1065. }
  1066. return false;
  1067. });
  1068.  
  1069. if (G.get('offline_Down') && localHref.indexOf('115.com') == -1) {
  1070. if (localHref.match(/[0-9]mag\.net|yuhuage/) != null) {
  1071. right_menu();
  1072. var time1 = setInterval(function() {
  1073. offline.addButton();
  1074. if ($('.115offline').length >= 20) {
  1075. clearInterval(time1);
  1076. }
  1077. }, 500);
  1078. setTimeout(function() {
  1079. offline.addSelect();
  1080. $('.mag1').remove();
  1081. clearInterval(time1);
  1082. }, 5000);
  1083. } else if (localHref.match(/www\.gying\./) != null) {
  1084. right_menu();
  1085. if (localHref.match(/\/bt\//) != null) {
  1086. setTimeout(function() {
  1087. offline.addLink();
  1088. offline.addSelect();
  1089. }, 500);
  1090. } else {
  1091. var time2 = setInterval(function() {
  1092. if ($('.torrent').length > 0) {
  1093. offline.addButton();
  1094. offline.addSelect();
  1095. clearInterval(time2);
  1096. }
  1097. if ($('.torrent').length == 0) {
  1098. clearInterval(time2);
  1099. }
  1100. }, 1500);
  1101. }
  1102. } else {
  1103. var time3 = 300;
  1104. if (localHref.match(/av/) != null) {
  1105. time3 = 3000;
  1106. }
  1107. setTimeout(function() {
  1108. offline.addButton();
  1109. if (localHref.match(/av/) != null) {
  1110. $('.movie td').removeAttr('onclick');
  1111. }
  1112. }, time3);
  1113. if (G.get('fuzzy_find')) {
  1114. setTimeout(function() {
  1115. offline.addLink();
  1116. }, time3 + 10);
  1117. }
  1118. setTimeout(function() {
  1119. right_menu();
  1120. offline.addSelect();
  1121. $('.mag1').remove();
  1122. }, time3 + 20);
  1123. }
  1124. }
  1125. });
  1126. })();