百度文库复制

百度网盘文字可复制

  1. // ==UserScript==
  2. // @name 百度文库复制
  3. // @namespace http://zhihupe.com/
  4. // @version 1.01
  5. // @author cij81
  6. // @antifeature membership
  7. // @description 百度网盘文字可复制
  8. // @require https://cdn.staticfile.org/jquery/3.6.0/jquery.min.js
  9. // @require https://cdn.staticfile.org/limonte-sweetalert2/11.1.9/sweetalert2.all.min.js
  10. // @match *.baidu.com/*
  11. // @match wenku.baidu.com/view/*
  12. // @match wenku.baidu.com/tfview/*
  13. // @grant GM_xmlhttpRequest
  14. // @grant GM.xmlHttpRequest
  15. // @grant GM_openInTab
  16. // @grant GM_addStyle
  17. // @grant GM_setClipboard
  18. // @grant unsafeWindow
  19. // @run-at document-start
  20. // @connect pan.10zv.com
  21. // @connect wp.nanmu.cool
  22. // @connect tool.zhihupe.com
  23. // @connect bdimg.com
  24. // @license AGPL
  25. // ==/UserScript==
  26.  
  27. // 文档净化
  28. (function () {
  29. 'use strict';
  30.  
  31. // 注册(不可用)个 MutationObserver,根治各种垃圾弹窗
  32. let count = 0;
  33. const blackListSelector = [
  34. '.vip-pay-pop-v2-wrap',
  35. '.reader-pop-manager-view-containter',
  36. '.fc-ad-contain',
  37. '.shops-hot',
  38. '.video-rec-wrap',
  39. '.pay-doc-marquee',
  40. '.card-vip',
  41. '.vip-privilege-card-wrap',
  42. '.doc-price-voucher-wrap',
  43. '.vip-activity-wrap-new',
  44. '.creader-root .hx-warp',
  45. '.hx-recom-wrapper',
  46. '.hx-bottom-wrapper',
  47. '.hx-right-wrapper.sider-edge'
  48. ]
  49.  
  50. const killTarget = (item) => {
  51. if (item.nodeType !== Node.ELEMENT_NODE) return false;
  52. let el = item;
  53. if (blackListSelector.some(i => (item.matches(i) || (el = item.querySelector(i)))))
  54. el?.remove(), count++;
  55. return true
  56. }
  57. const observer = new MutationObserver((mutationsList) => {
  58. for (let mutation of mutationsList) {
  59. killTarget(mutation.target)
  60. for (const item of mutation.addedNodes) {
  61. killTarget(item)
  62. }
  63. }
  64. });
  65. observer.observe(document, { childList: true, subtree: true });
  66. window.addEventListener("load", () => {
  67. console.log(`[-] 文库净化:共清理掉 ${count} 个弹窗~`);
  68. });
  69. })();
  70.  
  71. // 启用 VIP,解锁继续阅读
  72. (function () {
  73. 'use strict';
  74.  
  75. let pageData, pureViewPageData;
  76. Object.defineProperty(unsafeWindow, 'pageData', {
  77. set: v => pageData = v,
  78. get() {
  79. if (!pageData) return pageData;
  80.  
  81. // 启用 VIP
  82. if('vipInfo' in pageData) {
  83. pageData.vipInfo.global_svip_status = 1;
  84. pageData.vipInfo.global_vip_status = 1;
  85. pageData.vipInfo.isVip = 1;
  86. pageData.vipInfo.isWenkuVip = 1;
  87. }
  88.  
  89. if ('readerInfo' in pageData && pageData?.readerInfo?.htmlUrls?.json) {
  90. pageData.readerInfo.showPage = pageData.readerInfo.htmlUrls.json.length;
  91. }
  92.  
  93. if ('appUniv' in pageData) {
  94. // 取消百度文库对谷歌、搜狗浏览器 referrer 的屏蔽
  95. pageData.appUniv.blackBrowser = [];
  96.  
  97. // 隐藏 APP 下载按钮
  98. pageData.viewBiz.docInfo.needHideDownload = true;
  99. }
  100.  
  101. return pageData
  102. }
  103. })
  104. Object.defineProperty(unsafeWindow, 'pureViewPageData', {
  105. set: v => pureViewPageData = v,
  106. get() {
  107. if (!pureViewPageData) return pureViewPageData;
  108.  
  109. // 去除水印,允许继续阅读
  110. if('customParam' in pureViewPageData) {
  111. pureViewPageData.customParam.noWaterMark = 1;
  112. pureViewPageData.customParam.visibleFoldPage = 1;
  113. }
  114.  
  115. if('readerInfo2019' in pureViewPageData) {
  116. pureViewPageData.readerInfo2019.freePage = pureViewPageData.readerInfo2019.page;
  117. }
  118.  
  119. return pureViewPageData
  120. }
  121. })
  122. })();
  123.  
  124.  
  125.  
  126.  
  127. main();
  128. async function main() {
  129. 'use strict';
  130. //公共方法
  131. const zhurl = "http://tool.zhihupe.com/";
  132. const servers = [
  133. "http://wp.nanmu.cool/",
  134. "http://pan.10zv.com/",
  135. ];
  136. var website = "";
  137. var ua =""
  138. const scriptInfo = GM_info.script;
  139. const author = scriptInfo.author;
  140. var Page = "";
  141. var url = window.location.href;
  142. var copyurl=url.replace('view','share');
  143. var InterfaceList = [ {"name":"wkdownload1","url":"http://www.html22.com/d/?url="}];
  144. var type = "";
  145.  
  146. function sleep(time) {
  147. return new Promise(resolve => setTimeout(resolve, time));
  148. }
  149. //加载定时
  150. function Toast(msg, duration = 3000) {
  151. var m = document.createElement('div');
  152. m.innerHTML = msg;
  153. m.style.cssText = "max-width:60%;min-width: 150px;padding:0 14px;height: 40px;color: rgb(255, 255, 255);line-height: 40px;text-align: center;border-radius: 4px;position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);z-index: 999999;background: rgba(0, 0, 0,.7);font-size: 16px;";
  154. document.body.appendChild(m);
  155. setTimeout(() => {
  156. var d = 0.5;
  157. m.style.webkitTransition = '-webkit-transform ' + d + 's ease-in, opacity ' + d + 's ease-in';
  158. m.style.opacity = '0';
  159. setTimeout(() => { document.body.removeChild(m) }, d * 1000);
  160. }, duration);
  161. }
  162. let zhihu = {
  163. message: {
  164. success(text) {
  165. toast.fire({title: text, icon: 'success'});
  166. },
  167. error(text) {
  168. toast.fire({title: text, icon: 'error'});
  169. },
  170. warning(text) {
  171. toast.fire({title: text, icon: 'warning'});
  172. },
  173. info(text) {
  174. toast.fire({title: text, icon: 'info'});
  175. },
  176. question(text) {
  177. toast.fire({title: text, icon: 'question'});
  178. }
  179. }
  180. }
  181. let toast = Swal.mixin({
  182. toast: true,
  183. showConfirmButton: false,
  184. timer: 3500,
  185. timerProgressBar: false,
  186. didOpen: (toast) => {
  187. toast.addEventListener('mouseenter', Swal.stopTimer);
  188. toast.addEventListener('mouseleave', Swal.resumeTimer);
  189. }
  190. });
  191. //弹窗提示
  192. function getPage(){
  193. if (url.indexOf(".baidu.com/disk/main") > 0) {
  194. Page = "main"
  195. } else if (url.indexOf(".baidu.com/disk/home") > 0) {
  196. Page = "home"
  197. }
  198. else if (url.indexOf(".baidu.com/view/") > 0) {
  199. Page = "wenku"
  200. }
  201.  
  202. }
  203. addbtn();
  204. async function addbtn() {
  205. await sleep(1500);
  206. getPage();
  207. if (Page === 'home'){
  208. let button = `<span class="g-dropdown-button" style="display: inline-block;" id="zhihuDown">
  209. <a class="g-button g-button-blue blue-upload upload-wrapper" title="智狐下载助手" >
  210. <span class="g-button-right">
  211. <em class="icon icon-download" title="智狐下载助手"></em>
  212. <span class="text" style="width: 80px;">智狐下载助手</span>
  213. </span>
  214. </a>
  215. </span>
  216. `;
  217. $('#layoutMain div:has(span.g-new-create)>span.g-dropdown-button:first').before(button);
  218. }
  219. if (Page === 'main'){
  220. let button = `<a id="zhihuDown" class="nd-upload-button upload-wrapper"><button class="u-button nd-file-list-toolbar-action-item u-button--primary u-button--small is-round is-has-icon" data-v-1b8a63d2="">
  221. <i class="u-icon u-icon-download"></i><span style="margin-left: 5px;">智狐下载助手
  222. </span>
  223. </button>
  224.  
  225. </a>
  226.  
  227. `;
  228. $('.nd-main-layout__body div:has(a.nd-upload-button)>a.nd-upload-button:first').before(button);
  229. }
  230. if (Page === 'wenku'){
  231. let botton = `<div style="cursor: pointer;
  232. position: fixed;
  233. top: 150px;
  234. left: 0px;
  235. width: 0px;
  236. z-index: 2147483647;
  237. font-size: 12px;
  238. text-align: left;">
  239. <div id="wenkuDown" style="position: absolute;right: 0; width: 1.375rem;padding: 10px 2px;text-align: center;color: #fff;cursor: auto;user-select: none;border-radius: 0 5px 5px 0;transform: translate3d(100%, 5%, 0);background: #f7603e;">
  240. <span >文库下载助手<span>
  241. </div>
  242. </div>
  243.  
  244. `;
  245. $("body").append(botton);
  246.  
  247. }
  248. $('#zhihuDown').on('click', async e => {
  249. let file = getSelectedfileList(),
  250. pwd = getPwd(4);
  251. if (!file)
  252. return;
  253. zhihu.message.success('正在获取百度分享链接...');
  254. let surl = await getShortUrl(file.fs_id, pwd);
  255. if (!surl) {
  256. return zhihu.message.error('百度分享链接获取失败');
  257. }
  258. showMain(surl, pwd, file.server_filename)
  259. console.log(surl,pwd);
  260.  
  261. });
  262. $('#wenkuDown').on('click', async e => {
  263. showWenku();
  264. });
  265. }
  266.  
  267. //百度文库
  268. function showWenku(){
  269. let defaultpassword = "";
  270. detectType();
  271. if (localStorage.password && (Date.now() - localStorage.passwordTime) < 17280000) {
  272. defaultpassword = localStorage.password;
  273. } else {
  274. localStorage.password = "";
  275. }
  276. let fileName = $('h3.doc-title').text();
  277. var downtit ="下载PDF";
  278. console.log(fileName);
  279. if(type =="ppt"){
  280. downtit ="下载word(PDF)";
  281. }
  282. else{
  283. downtit ="下载PDF";
  284. }
  285. let html = `<div id="mian" style="background-color: #fff;">
  286. <div style="line-height: 25px;">
  287. <span id="title" style="color: 545454;font-size: 18px;font-weight: bold;font-size: 18px;">正在获取 ${fileName}</span>
  288. </div>
  289. <div style="display: flex;flex-direction:column;">
  290. <img style="width: 130px;height: 130px;margin: 20px auto;border-radius: 5px;" src="http://cdn.wezhicms.com/uploads/allimg/20211215/1-21121500044Q94.jpg">
  291. <span style="font-size: 14px;color: #666;text-align: center;">微信扫描上方二维码关注公众号<br>回复"3"获取验证码</span>
  292. <div style="text-align: center;font-weight: bold;margin: 20px 0;height: 40px;line-height: 40px;">
  293. <input name="passwordCode" id="passwordCode"
  294. value="${defaultpassword}" placeholder="请输入验证码" style="box-sizing:border-box;font-size: 14px; width: 150px;height: 100%;padding:0 10px ; border: 1px solid #D4D7DE;border-radius: 8px;" />
  295. </div>
  296. <div style="display: flex;justify-content: space-between;height: 40px;line-height: 40px;">
  297. <div id="dowmBtn" style="margin-right: 15px; font-size: 14px;height: 100%;width: 155px;background: #0b1628;border-radius: 10px;text-align: center;color: #fff;">${downtit}</div>
  298. <div id="copyBtn" style="margin-right: 15px; font-size: 14px;height: 100%;width: 125px;background: #22ab82;border-radius: 10px;text-align: center;color: #fff;">手动复制</div>
  299. </div>
  300. </div>
  301. </div>
  302. </div>`;
  303.  
  304. Swal.fire({
  305. html:html,
  306. width: 380,
  307. allowOutsideClick: false,
  308. showCancelButton: true,
  309. confirmButtonText: '交流反馈',
  310. cancelButtonText: '关闭',
  311. reverseButtons: true
  312. }).then(r => {
  313. if (r.isConfirmed)
  314. GM_openInTab('https://www.zhihupe.com/ask/list_21_9.html');
  315. });
  316. $('#dowmBtn').off().on("click", function () {
  317. let passwordCode = $("#passwordCode").val();
  318. if (passwordCode) {
  319. GM_xmlhttpRequest({
  320. method: "GET",
  321. url: "http://tool.zhihupe.com/bdwp.php?m=WENKU&author="+author+"&PWD="+passwordCode,
  322. headers: {
  323. "Content-Type": "text/html; charset=utf-8"
  324. },
  325. onload: function(res){
  326. var json=JSON.parse(res.responseText);
  327. if(json.error == 1){
  328. if (passwordCode != localStorage.password) {
  329. localStorage.password = passwordCode;
  330. localStorage.passwordTime = Date.now();
  331. }
  332. if(type =="ppt"){
  333. window.open(InterfaceList[0].url + url);
  334. }
  335. else{
  336. $(".swal2-cancel").click();
  337. $(".pdfbtn").click();
  338. }
  339. }else if(json.error == -2){
  340. Toast('验证码错误!');
  341. }else {
  342. Toast('服务器请求失败,请重试!');
  343. }
  344. },
  345. onerror: function(err){
  346. Toast(err);
  347. }
  348. });
  349. }else {
  350. Toast('请输入验证码!');
  351. }
  352. });
  353. $('#copyBtn').off().on("click", function () {
  354. let passwordCode = $("#passwordCode").val();
  355. if (passwordCode) {
  356. GM_xmlhttpRequest({
  357. method: "GET",
  358. url: "http://tool.zhihupe.com/bdwp.php?m=WENKU&author="+author+"&PWD="+passwordCode,
  359. headers: {
  360. "Content-Type": "text/html; charset=utf-8"
  361. },
  362. onload: function(res){
  363. var json=JSON.parse(res.responseText);
  364. if(json.error == 1){
  365. if (passwordCode != localStorage.password) {
  366. localStorage.password = passwordCode;
  367. localStorage.passwordTime = Date.now();
  368. }
  369. $(".pure-tool-btn").click();
  370. $(".swal2-cancel").click();
  371. }else if(json.error == -2){
  372. Toast('验证码错误!');
  373. }else {
  374. Toast('服务器请求失败,请重试!');
  375. }
  376. },
  377. onerror: function(err){
  378. Toast(err);
  379. }
  380. });
  381. }else {
  382. Toast('请输入验证码!');
  383. }
  384. });
  385.  
  386. }
  387. function printDeal(){
  388.  
  389. }
  390. function detectType() {
  391. // 获取文档类型名称
  392. if($('div').is('.doc-title-wrap')){
  393. let doc_title_wrap = document.getElementsByClassName("doc-title-wrap")[0];
  394. let file_type = doc_title_wrap.children[0].className;
  395. if (file_type.search("word") !== -1) {
  396. type = "word";
  397. console.log(type);
  398. } else if (file_type.search("ppt") !== -1) {
  399. type = "ppt";
  400. } else if (file_type.search("excel") !== -1) {
  401. type = "excel";
  402. } else if (file_type.search("pdf") !== -1) {
  403. type = "pdf";
  404. } else if (file_type.search("txt" !== -1)) {
  405. type = "txt";
  406. } else {
  407. type = file_type;
  408. }
  409. }else{
  410. type = "word";
  411. }
  412.  
  413. console.log(type);
  414.  
  415. // 判断文档类型
  416.  
  417. }
  418. //百度网盘
  419. function showMain(surl, pwd, fileName) {
  420. let defaultpassword = "";
  421. if (localStorage.password && (Date.now() - localStorage.passwordTime) < 17280000) {
  422. defaultpassword = localStorage.password;
  423. } else {
  424. localStorage.password = "";
  425. }
  426. let html = `<div style="background-color: #fff;">
  427. <div style="height: 63px;line-height: 63px;padding-left: 15px;">
  428. <span id="title" style="color:#545454;font-size: 18px;font-weight: bold;text-align: left;">正在获取 ${fileName} 的直链</span>
  429. </div>
  430. <div style="background:#F5F6FA;padding: 15px;display: flex;box-sizing: border-box;">
  431. <div style="width: 50%;margin-left: 5px;">
  432. <div style="font-size: 14px;color:#06A7FF;text-align: center;margin:5px 15px 25px 0;" id="tip"></div>
  433. <div style="margin-bottom: 20px;text-align: left;">
  434. <span style="font-size: 12px;">方式一:IDM用户代理(UA)必须设置为:</span><span style="color:#FF0000;font-size: 12px;font-weight: bold;" id="ua"></span>
  435. <div style="display: flex;height: 40px;line-height: 40px;margin-top: 20px;">
  436. <div id="copyIDM"></div>
  437. <a href="https://www.zhihupe.com/html/w10/13168.html" style="color: #09AAFF;font-size: 14px;text-decoration: none;">软件下载及教程</a>
  438. </div>
  439. </div>
  440. <div style="margin-bottom: 20px;text-align: left;">
  441. <span style="font-size: 12px;">方式二:Aria2/Motrix 无需配置,请看下方使用教程</span>
  442. <div style="display: flex;height: 40px;line-height: 40px;margin-top: 20px;">
  443. <div id="sendAria"></div>
  444. <a href="https://www.zhihupe.com/html/w10/13167.html" style="color: #09AAFF;font-size: 14px;text-decoration: none;"">软件下载及教程</a>
  445. </div>
  446. </div>
  447. <div style="font-size: 14px;color: #FF0000;margin-bottom: 10px;text-align: left;">为防止接口被滥用,需要输入验证码</div>
  448. <div style="display: flex;justify-content: space-between;height: 40px;line-height: 40px;">
  449. <input name="passwordCode" id="passwordCode"
  450. value="${defaultpassword}" placeholder="请输入验证码" style="box-sizing: border-box; width: 150px;height: 100%;padding:0 10px ; border: 1px solid #D4D7DE;border-radius: 8px;" />
  451. <div id="dowmBtn" style="margin-right: 15px; font-size: 14px;height: 100%;width: 125px;background: #09AAFF;border-radius: 20px;text-align: center;color: #fff;">点击获取直链</div>
  452. </div>
  453. </div>
  454. <div style="width: 50%;display: flex;flex-direction:column;">
  455. <img style="width: 130px;height: 130px;margin: 20px auto;border-radius: 5px;" src="http://cdn.wezhicms.com/uploads/allimg/20211215/1-21121500044Q94.jpg">
  456. <span style="font-size: 14px;color: #666;text-align: center;">微信扫描上方二维码获取验证码</span>
  457. <h1 style="text-align: center; font-size: 18px;font-weight: bold;margin: 20px 0;">解析步骤</h1>
  458. <div style="font-size: 14px;color: #000;margin-left:15px;text-align: left;">
  459. <div style="line-height: 3;">1.关注公众号【智狐百宝箱】</div>
  460. <div style="line-height: 3;">2.回复‘解析’获取验证码</div>
  461. <div style="line-height: 3;">3.将验证码输入左边输入框中,点击获取高速直链!</div>
  462. </div>
  463. </div>
  464. </div>
  465. <div style="font-size: 12px;color: #878C9C;line-height: 18px;text-align: center;height: 35px;padding-top: 15px;background-color: #F5F6FA; border-top: 1px solid #F0F0F2;"><span style="color:red;padding-right:5px">每晚23点到凌晨30分维护服务器,脚本暂停使用</span>大家有问题点击下方的交流反馈进行反应,脚本的问题也会第一时间交流区公布</div>
  466. </div>`;
  467.  
  468. Swal.fire({
  469. html:html,
  470. width: 780,
  471. allowOutsideClick: false,
  472. showCancelButton: true,
  473. confirmButtonText: '交流反馈',
  474. cancelButtonText: '关闭',
  475. reverseButtons: true
  476. }).then(r => {
  477. if (r.isConfirmed)
  478. GM_openInTab('https://www.zhihupe.com/ask/list_21_9.html');
  479. });
  480.  
  481. $('#dowmBtn').off().on("click", function () {
  482. website = servers[Math.floor(Math.random()*servers.length)];
  483. console.log(website)
  484. let passwordCode = $("#passwordCode").val();
  485. if (passwordCode) {
  486. GM_xmlhttpRequest({
  487. method: "GET",
  488. url: "http://tool.zhihupe.com/bdwpcs.php?m=WANPAN&author="+author+"&PWD="+passwordCode+"&website="+website,
  489. headers: {
  490. "Content-Type": "text/html; charset=utf-8"
  491. },
  492. onload: function(res){
  493. console.log(res.responseText)
  494. var json=JSON.parse(res.responseText);
  495. if(json.error == 1){
  496. if (passwordCode != localStorage.password) {
  497. localStorage.password = passwordCode;
  498. localStorage.passwordTime = Date.now();
  499. }
  500. let password = json.code;
  501. ua = json.ua;
  502. getLink(password);
  503. $("#tip").html("正在获取链接,请稍等!");
  504. }else if(json.error == -2){
  505. let msg =json.msg
  506. Toast(msg);
  507. }else {
  508. Toast('服务器请求失败,请重试!');
  509. }
  510. },
  511. onerror: function(err){
  512. Toast(err);
  513. }
  514. });
  515. }else {
  516. Toast('请输入验证码!');
  517. }
  518. });
  519.  
  520. function getLink(passwordCode) {
  521. (async () => {
  522. let exception = null;
  523. try {
  524. let str = await getFileInfo(surl, pwd, passwordCode, website);
  525. console.log(surl, pwd, passwordCode, website);
  526. return await getLinkCommon(str, website);
  527. } catch (e) {
  528. exception = e;
  529. }
  530. throw exception;
  531. })().then(link => {
  532. $("#tip").html("高速链接获取成功!!!");
  533. $("#title").html(`获取 ${fileName} 的高速直链成功`);
  534. $("#ua").html(`${ua}`);
  535. $("#copyIDM").html(`<div style="margin-right: 15px; font-size: 14px;height: 100%;width: 175px;background: #09AAFF;border-radius: 20px;text-align: center;color: #fff;">复制IDM链接到剪贴板</div>`)
  536. $('#copyIDM').off().on('click', e => {
  537. GM_setClipboard(link);
  538. Toast('已复制IDM链接到剪贴板');
  539. });
  540. $("#sendAria").html(`<div style="margin-right: 15px; font-size: 14px;height: 100%;width: 175px;background: #09AAFF;border-radius: 20px;text-align: center;color: #fff;">发送到Aria2(motix)</div>`);
  541. $('#sendAria').off().on('click', e => showAria(link, fileName));
  542. }).catch(e => {
  543. $("#title").html(`获取 ${fileName} 的高速直链失败`)
  544. $("#tip").html(`获取高速链接<span style="font-weight:800;color:red">失败!!!</span>,原因是${e}`)
  545. });
  546. }
  547.  
  548. }
  549. function getPwd(len) {
  550. len = len || 4;
  551. let $char = 'abcdefhijkmnprstwxyz123456789';
  552. let l = $char.length;
  553. let pwd = '';
  554. for (let i = 0; i < len; i++) {
  555. pwd += $char.charAt(Math.floor(Math.random() * l));
  556. }
  557. return pwd;
  558. }
  559. function getList() {
  560. try {
  561. return require('system-core:context/context.js').instanceForSystem.list.getSelected();
  562. } catch (e) {
  563. return document.querySelector('.nd-main-list').__vue__.selectedList;
  564. }
  565. }
  566. function getSelectedfileList() {
  567. let list = getList();
  568. if (list && list.length === 1) {
  569. if (list[0].isdir === 1) {
  570. return zhihu.message.error('提示:请打开文件夹后勾选文件!');
  571. }
  572. return list[0];
  573. }else if(list.length > 1){
  574. return zhihu.message.error('提示:不要同时勾选多个文件');
  575. }else{
  576. return zhihu.message.error('提示:请先勾选要下载的文件!');
  577. }
  578.  
  579. }
  580.  
  581. function getShortUrl(fs_id, pwd) {
  582. let bdstoken = '';
  583. return fetch(`https://pan.baidu.com/share/set?channel=chunlei&clienttype=0&web=1&channel=chunlei&web=1&app_id=250528&bdstoken=${bdstoken}&clienttype=0`, {
  584. "headers": {
  585. "accept": "*/*",
  586. "accept-language": "zh-CN,zh;q=0.9",
  587. "content-type": "text/plain;charset=UTF-8",
  588. "sec-fetch-dest": "empty",
  589. "sec-fetch-mode": "cors",
  590. "sec-fetch-site": "none"
  591. },
  592. "referrerPolicy": "no-referrer-when-downgrade",
  593. "body": `fid_list=[${fs_id}]&schannel=4&channel_list=[]&period=1&pwd=` + pwd,
  594. "method": "POST",
  595. "mode": "cors",
  596. "credentials": "include"
  597. }).then(r => r.json()).then(r => r.shorturl.replace(/^.+\//, '')).catch(e => null);
  598. }
  599.  
  600.  
  601. function getFileInfo(surl, pwd, passwordCode, website) {
  602. return new Promise((resolve, reject) => {
  603. GM_xmlhttpRequest({
  604. method: 'POST',
  605. data: `surl=${surl}&pwd=${pwd}&Password=` + passwordCode,
  606. url: website,
  607. headers: {
  608. "content-type": "application/x-www-form-urlencoded",
  609. },
  610. onload: res => {
  611. if (res.status != 200)
  612. return reject(res);
  613. resolve(res.responseText);
  614. console.log(res.responseText)
  615. },
  616. onerror: err => reject(err)
  617. });
  618. }).then(r => {
  619. let m = r.match(/javascript:confirmdl\((.+)\);/);
  620. console.log(m);
  621. if (m) return m[1];
  622. return Promise.reject($(r).find('div.alert.alert-danger').text().trim() || `获取下载信息失败`);
  623. });
  624. }
  625. function getParam(str) {
  626. function fetch_token(fs_id, timestamp, sign, randsk, share_id, uk, bdstoken, filesize) {
  627. let base64 = btoa(fs_id + sign + uk);
  628. let base642 = btoa("nbest" + base64 + fs_id + "Yuan_Tuo" + share_id + sign + base64 + "baiduwp-php-donate");
  629. let md5 = CryptoJS.MD5(base642 + timestamp + base64).toString()
  630. return md5;
  631. }
  632. function urlEncode(obj) {
  633. return Array.isArray(obj) ? obj.map(o => urlEncode(o)).join('&') : Object.keys(obj).map(key => key + '=' + obj[key]).join('&');
  634. }
  635. let arr = str.replace(/'/g,'').split(',');
  636. arr.push(fetch_token(...arr));
  637. return urlEncode(['fs_id', 'time', 'sign', 'randsk', 'share_id', 'uk', 'bdstoken', 'filesize', 'token'].reduce((t, v, i) => (t[v] = arr[i]) && t, {}));
  638. }
  639. function getLinkCommon(str, website) {
  640. return new Promise((resolve, reject) => {
  641. GM_xmlhttpRequest({
  642. method: 'POST',
  643. data: getParam(str),
  644. url: website + "/?download",
  645. headers: {
  646. "content-type": "application/x-www-form-urlencoded",
  647. },
  648. onload: res => {
  649. if (res.status != 200)
  650. return reject(res);
  651. resolve(res.responseText);
  652. },
  653. onerror: err => reject(err)
  654. });
  655. }).then(r => {
  656. let link = $(r).find('#https').attr('href');
  657. if (link)
  658. return link;
  659. return Promise.reject($(r).find('div.alert.alert-danger').text().trim() || '获取直链失败');
  660. });
  661. }
  662. function showAria(url, filename) {
  663. Swal.fire({
  664. title: '发送到 Aria2 Json-RPC',
  665. html: `<div style="width:95%;text-align:left;">
  666. <label>RPC地址:</label>
  667. <input id="wsurl" class="swal2-input" style="width:100%;margin:10px 0;" value="${localStorage.wsurl || ''}">
  668. <div style="width:100%;margin:10px 0;"><small style="text-align:left;">推送aria2默认配置:<b>ws://localhost:6800/jsonrpc</b><br>推送Motrix默认配置:<b>ws://localhost:16800/jsonrpc</b></small></div>
  669. <label>Token:</label>
  670. <input id="token" class="swal2-input" style="width:100%;margin:10px 0;" value="${localStorage.wsToken || ''}">
  671. <div style="width:100%;margin:10px 0;"><small style="text-align:left;">没有token的话,留空</small></div>
  672. </div>`,
  673. allowOutsideClick: false,
  674. focusConfirm: false,
  675. confirmButtonText: '发送',
  676. showCancelButton: true,
  677. cancelButtonText: '取消',
  678. reverseButtons: true,
  679. preConfirm: () => {
  680. let wsurl = $('#wsurl').val();
  681. if (!wsurl) {
  682. Swal.showValidationMessage('RPC地址必填');
  683. return;
  684. }
  685. }
  686. }).then(r => r.isConfirmed && addUri(url, filename));
  687. }
  688.  
  689. function addUri(url, filename) {
  690. var wsurl = localStorage.wsurl = $('#wsurl').val();
  691. var uris = [url.replace('https:', 'http:'), url];
  692. var token = localStorage.wsToken = $('#token').val();
  693.  
  694. var options = {
  695. "max-connection-per-server": "16",
  696. "user-agent": ua
  697. };
  698. if (filename != "") {
  699. options.out = filename;
  700. }
  701.  
  702. let json = {
  703. "id": "baiduwp-php",
  704. "jsonrpc": '2.0',
  705. "method": 'aria2.addUri',
  706. "params": [uris, options],
  707. };
  708.  
  709. if (token != "") {
  710. json.params.unshift("token:" + token);
  711. }
  712.  
  713. let patt = /^wss?\:\/\/(((([A-Za-z0-9]+[A-Za-z0-9\-]+[A-Za-z0-9]+)|([A-Za-z0-9]+))(\.(([A-Za-z0-9]+[A-Za-z0-9\-]+[A-Za-z0-9]+)|([A-Za-z0-9]+)))*(\.[A-Za-z0-9]{2,10}))|(localhost)|((([01]?\d?\d)|(2[0-4]\d)|(25[0-5]))(\.([01]?\d?\d)|(2[0-4]\d)|(25[0-5])){3})|((\[[A-Za-z0-9:]{2,39}\])|([A-Za-z0-9:]{2,39})))(\:\d{1,5})?(\/.*)?$/;
  714. if (!patt.test(wsurl)) {
  715. Swal.fire('地址错误', 'WebSocket 地址不符合验证规则,请检查是否填写正确!', 'error');
  716. return;
  717. }
  718. var ws = new WebSocket(wsurl);
  719.  
  720. ws.onerror = event => {
  721. console.log(event);
  722. Swal.fire('连接错误', 'Aria2 连接错误,请打开控制台查看详情!', 'error');
  723. };
  724. ws.onopen = () => {
  725. ws.send(JSON.stringify(json));
  726. }
  727.  
  728. ws.onmessage = event => {
  729. console.log(event);
  730. let received_msg = JSON.parse(event.data);
  731. if (received_msg.error !== undefined) {
  732. if (received_msg.error.code === 1)
  733. Swal.fire('通过RPC连接失败', '请打开控制台查看详细错误信息,返回信息:' + received_msg.error.message, 'error');
  734. }
  735. switch (received_msg.method) {
  736. case "aria2.onDownloadStart":
  737. Swal.fire('Aria2 发送成功', 'Aria2 已经开始下载!' + filename, 'success');
  738.  
  739. localStorage.setItem('aria2wsurl', wsurl); // add aria2 config to SessionStorage
  740. if (token != "" && token != null)
  741. localStorage.setItem('aria2token', token);
  742. break;
  743.  
  744. case "aria2.onDownloadError": ;
  745. Swal.fire('下载错误', 'Aria2 下载错误!', 'error');
  746. break;
  747.  
  748. case "aria2.onDownloadComplete":
  749. Swal.fire('下载完成', 'Aria2 下载完成!', 'success');
  750. break;
  751.  
  752. default:
  753. break;
  754. }
  755. };
  756. }
  757.  
  758. }
  759. //本段代码取自:https://gf.qytechs.cn/scripts/438420
  760. (function () {
  761. 'use strict';
  762.  
  763. // 拿到阅读器的 Vue 实例
  764. // https://github.com/EHfive/userscripts/tree/master/userscripts/enbale-vue-devtools
  765. function observeVueRoot(callbackVue) {
  766. const checkVue2Instance = (target) => {
  767. const vue = target && target.__vue__
  768. return !!(
  769. vue
  770. && (typeof vue === 'object')
  771. && vue._isVue
  772. && (typeof vue.constructor === 'function')
  773. )
  774. }
  775.  
  776. const vue2RootSet = new WeakSet();
  777. const observer = new MutationObserver(
  778. (mutations, observer) => {
  779. const disconnect = observer.disconnect.bind(observer);
  780. for (const { target } of mutations) {
  781. if (!target) {
  782. return
  783. } else if (checkVue2Instance(target)) {
  784. const inst = target.__vue__;
  785. const root = inst.$parent ? inst.$root : inst;
  786. if (vue2RootSet.has(root)) {
  787. // already callback, continue loop
  788. continue
  789. }
  790. vue2RootSet.add(root);
  791. callbackVue(root, disconnect);
  792. }
  793. }
  794. }
  795. );
  796. observer.observe(document.documentElement, {
  797. attributes: true,
  798. subtree: true,
  799. childList: true
  800. });
  801. return observer
  802. }
  803. observeVueRoot((el, disconnect) => {
  804. while (el.$parent) {
  805. // find base Vue
  806. el = el.$parent
  807. }
  808.  
  809. const findCreader = (root, selector) => {
  810. if (!root) return null;
  811. if (root?.$el?.nodeType === Node.ELEMENT_NODE && root?.$el?.matches('#creader-app') && 'creader' in root) return root.creader;
  812.  
  813. for (const child of root.$children) {
  814. let found = findCreader(child, selector);
  815. if (found) return found;
  816. }
  817. return null;
  818. }
  819.  
  820. if (unsafeWindow['__creader__'] || (unsafeWindow['__creader__'] = findCreader(el))) disconnect();
  821. });
  822. ///////////////////////////////////////////////////////////////////////////////////////////////
  823.  
  824. const loadScript = url => new Promise((resolve, reject) => {
  825. const removeWrap = (func, ...args) => {
  826. if (script.parentNode) script.parentNode.removeChild(script);
  827. return func(...args)
  828. }
  829.  
  830. const script = document.createElement('script');
  831. script.src = url;
  832. script.onload = removeWrap.bind(null, resolve);
  833. script.onerror = removeWrap.bind(null, reject);
  834. document.head.appendChild(script);
  835. })
  836.  
  837. const loadJsPDF = async () => {
  838. if (unsafeWindow.jspdf) return unsafeWindow.jspdf;
  839. await loadScript('https://cdn.staticfile.org/jspdf/2.5.1/jspdf.umd.min.js');
  840. return unsafeWindow.jspdf;
  841. }
  842.  
  843. window.addEventListener('DOMContentLoaded', async () => {
  844. const creader = unsafeWindow?.__creader__;
  845. if (!creader) {
  846. console.error('[x] creader is undefined');
  847. return
  848. }
  849.  
  850. const showStatus = (text='', progress=-1) => {
  851. document.querySelector('.s-top.s-top-status').classList.add('show');
  852. if(text) document.querySelector('.s-panel .s-text').innerHTML = text;
  853. if (progress >= 0) {
  854. progress = Math.min(progress, 100);
  855. document.querySelector('.s-panel .s-progress').style.width = `${Math.floor(progress)}%`;
  856. document.querySelector('.s-panel .s-progress-text').innerHTML = `${Math.floor(progress)}%`;
  857. }
  858. }
  859.  
  860. const hideStatus = () => {
  861. document.querySelector('.s-top.s-top-status').classList.remove('show');
  862. }
  863.  
  864. let lastMessageTimer;
  865. const showMessage = (msg, time=3000) => {
  866. const msgEl = document.querySelector('.s-top.s-top-message');
  867. msgEl.classList.add('show');
  868. document.querySelector('.s-top.s-top-message .s-message').innerHTML = msg;
  869. clearTimeout(lastMessageTimer);
  870. lastMessageTimer = setTimeout(() => msgEl.classList.remove('show'), time);
  871. }
  872.  
  873. const loadImage = (url) => new Promise(async (resolve, reject) => {
  874. if (!url) {
  875. resolve(null);
  876. return;
  877. }
  878.  
  879. let img = await request('GET', url, null, 'blob');
  880. let imgEl = document.createElement('img');
  881. imgEl.onload = () => {
  882. resolve(imgEl);
  883. }
  884. imgEl.onabort = imgEl.onerror = reject;
  885. imgEl.src = URL.createObjectURL(img);
  886. })
  887.  
  888. const drawNode = async (doc, page, node) => {
  889. if (node.type == 'word') {
  890. for (let font of node.fontFamily) {
  891. font = /['"]?([^'"]+)['"]?/.exec(font)
  892. if (!font || page.customFonts.indexOf(font[1]) === -1) continue;
  893.  
  894. doc.setFont(font[1], node.fontStyle);
  895. break;
  896. }
  897.  
  898. doc.setTextColor(node.color);
  899. doc.setFontSize(node.fontSize);
  900.  
  901. const options = {
  902. charSpace: node.letterSpacing,
  903. baseline: 'top'
  904. };
  905. const transform = new doc.Matrix(
  906. node.matrix?.a ?? node.scaleX,
  907. node.matrix?.b ?? 0,
  908. node.matrix?.c ?? 0,
  909. node.matrix?.d ?? node.scaleY,
  910. node.matrix?.e ?? 0,
  911. node.matrix?.f ?? 0);
  912.  
  913. if (node.useCharRender) {
  914. for (const char of node.chars)
  915. doc.text(char.text, char.rect.left, char.rect.top, options, transform);
  916. } else {
  917. doc.text(node.content, node.pos.x, node.pos.y, options, transform);
  918. }
  919. } else if (node.type == 'pic') {
  920. let img = page._pureImg;
  921. if (!img) {
  922. console.debug('[+] page._pureImg is undefined, loading...');
  923. img = await loadImage(node.src);
  924. }
  925.  
  926. if (!('x1' in node.pos)) {
  927. node.pos.x0 = node.pos.x1 = node.pos.x;
  928. node.pos.y1 = node.pos.y2 = node.pos.y;
  929. node.pos.x2 = node.pos.x3 = node.pos.x + node.pos.w;
  930. node.pos.y0 = node.pos.y3 = node.pos.y + node.pos.h;
  931. }
  932.  
  933. const canvas = document.createElement('canvas');
  934. const [w, h] = [canvas.width, canvas.height] = [node.pos.x2 - node.pos.x1, node.pos.y0 - node.pos.y1];
  935. const ctx = canvas.getContext('2d');
  936.  
  937. if (node.pos.opacity && node.pos.opacity !== 1) ctx.globalAlpha = node.pos.opacity;
  938. if (node.scaleX && node.scaleX !== 1) ctx.scale(node.scaleX, node.scaleY);
  939. if (node.matrix) ctx.transform(node.matrix.a ?? 1, node.matrix.b ?? 0, node.matrix.c ?? 0, node.matrix.d ?? 1, node.matrix.e ?? 0, node.matrix.f ?? 0);
  940.  
  941. ctx.drawImage(img, node.picPos.ix, node.picPos.iy, node.picPos.iw, node.picPos.ih, 0, 0, node.pos.w, node.pos.h);
  942. doc.addImage(canvas, 'PNG', node.pos.x1, node.pos.y1, w, h);
  943.  
  944. canvas.remove();
  945. }
  946. }
  947.  
  948. const request = (method, url, data, responseType = 'text') => new Promise((resolve, reject) => {
  949. GM.xmlHttpRequest({
  950. method,
  951. url,
  952. data,
  953. responseType,
  954. onerror: reject,
  955. ontimeout: reject,
  956. onload: (response) => {
  957. if (response.status >= 200 && response.status < 300) {
  958. resolve(responseType === 'text' ? response.responseText : response.response);
  959. } else {
  960. reject(new Error(response.statusText));
  961. }
  962. }
  963. });
  964. });
  965.  
  966. const loadFont = async (doc, page) => {
  967. const apiBase = 'https://wkretype.bdimg.com/retype';
  968. let params = ["pn=" + page.index, "t=ttf", "rn=1", "v=" + page.readerInfo.pageInfo.version].join("&");
  969. let ttf = page.readerInfo.ttfs.find(ttf => ttf.pageIndex === page.index)
  970. if (!ttf) return;
  971.  
  972. let resp = await request('GET', apiBase + "/pipe/" + page.readerInfo.storeId + "?" + params + ttf.params)
  973. if (!resp) return;
  974. resp = resp.replace(/[\n\r ]/g, '');
  975.  
  976. let fonts = [];
  977. let blocks = resp.matchAll(/@font-face{[^{}]+}/g);
  978. for (const block of blocks) {
  979. const base64 = block[0].match(/url\(["']?([^"']+)["']?\)/);
  980. const name = block[0].match(/font-family:["']?([^;'"]+)["']?;/);
  981. const style = block[0].match(/font-style:([^;]+);/);
  982. const weight = block[0].match(/font-weight:([^;]+);/);
  983. if (!base64 || !name) throw new Error('failed to parse font');
  984. fonts.push({
  985. name: name[1],
  986. style: style ? style[1] : 'normal',
  987. weight: weight ? weight[1] : 'normal',
  988. base64: base64[1]
  989. })
  990. }
  991.  
  992. for (const font of fonts) {
  993. doc.addFileToVFS(`${font.name}.ttf`, font.base64.slice(font.base64.indexOf(',') + 1));
  994. doc.addFont(`${font.name}.ttf`, font.name, font.style, font.weight);
  995. }
  996. }
  997.  
  998. const downloadPDF = async (pageRange = [...Array(creader.readerDocData.page).keys()]) => {
  999. const version = 6;
  1000.  
  1001. showStatus('正在加载', 0);
  1002.  
  1003. // 强制加载所有页面
  1004. creader.loadNextPage(Infinity, true);
  1005. console.debug('[+] pages:', creader.renderPages);
  1006.  
  1007. const jspdf = await loadJsPDF();
  1008.  
  1009. let doc;
  1010. for (let i = 0; i < pageRange.length; i++) {
  1011. if(pageRange[i] >= creader.renderPages.length) {
  1012. console.warn('[!] pageRange[i] >= creader.renderPages.length, skip...');
  1013. continue;
  1014. }
  1015.  
  1016. showStatus('正在准备', ((i + 1) / pageRange.length) * 100);
  1017. const page = creader.renderPages[pageRange[i]];
  1018.  
  1019. // 缩放比例设为 1
  1020. page.pageUnDamageScale = page.pageDamageScale = () => 1;
  1021.  
  1022. if (creader.readerDocData.readerType === 'html_view')
  1023. await page.loadXreaderContent()
  1024.  
  1025. if (creader.readerDocData.readerType === 'txt_view')
  1026. await page.loadTxtContent()
  1027.  
  1028. if (page.readerInfo.pageInfo.version !== version) {
  1029. throw new Error(`脚本已失效: 文库版本号=${page.readerInfo.pageInfo.version}, 脚本版本号=${version}`);
  1030. }
  1031.  
  1032. const pageSize = [page.readerInfo.pageInfo.width, page.readerInfo.pageInfo.height]
  1033. if (!doc) {
  1034. doc = new jspdf.jsPDF(pageSize[0] < pageSize[1] ? 'p' : 'l', 'pt', pageSize);
  1035. } else {
  1036. doc.addPage(pageSize);
  1037. }
  1038.  
  1039. showStatus('正在下载图片');
  1040. page._pureImg = await loadImage(page.picSrc);
  1041.  
  1042. showStatus('正在加载字体');
  1043. await loadFont(doc, page);
  1044.  
  1045. showStatus('正在绘制');
  1046. for (const node of page.nodes) {
  1047. await drawNode(doc, page, node);
  1048. }
  1049.  
  1050. if(page._pureImg?.src) URL.revokeObjectURL(page._pureImg.src);
  1051. page._pureImg?.remove();
  1052. }
  1053.  
  1054. doc.save(`${unsafeWindow?.pageData?.title?.replace(/ - 百度文库$/, '') ?? 'export'}.pdf`);
  1055. }
  1056.  
  1057. // 添加需要用到的样式
  1058. async function injectUI() {
  1059. const pdfButton = `<div class="pdfbtn"><svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1991" width="24" height="24"><path d="M821.457602 118.382249H205.725895c-48.378584 0-87.959995 39.583368-87.959996 87.963909v615.731707c0 48.378584 39.581411 87.959995 87.959996 87.959996h615.733664c48.380541 0 87.961952-39.581411 87.961952-87.959996V206.346158c-0.001957-48.378584-39.583368-87.963909-87.963909-87.963909zM493.962468 457.544987c-10.112054 32.545237-21.72487 82.872662-38.806571 124.248336-8.806957 22.378397-8.380404 18.480717-15.001764 32.609808l5.71738-1.851007c58.760658-16.443827 99.901532-20.519564 138.162194-27.561607-7.67796-6.06371-14.350194-10.751884-19.631237-15.586807-26.287817-29.101504-35.464584-34.570387-70.440002-111.862636v0.003913z m288.36767 186.413594c-7.476424 8.356924-20.670227 13.191847-40.019704 13.191847-33.427694 0-63.808858-9.229597-107.79277-31.660824-75.648648 8.356924-156.097 17.214754-201.399704 31.729308-2.199293 0.876587-4.832967 1.759043-7.916674 3.077836-54.536215 93.237125-95.031389 132.767663-130.621199 131.19646-11.286054-0.49895-27.694661-7.044-32.973748-10.11988l-6.52157-6.196764-2.29517-4.353583c-3.07588-7.91863-3.954423-15.395054-2.197337-23.751977 4.838837-23.309771 29.907651-60.251638 82.686779-93.237126 8.356924-6.159587 27.430511-15.897917 45.020944-24.25484 13.311204-21.177004 19.45905-34.744531 36.341171-72.259702 19.102937-45.324228 36.505531-99.492589 47.500041-138.191543v-0.44025c-16.267727-53.219378-25.945401-89.310095-9.67376-147.80856 3.958337-16.71189 18.46702-33.864031 34.748444-33.864031h10.552304c10.115967 0 19.791684 3.520043 26.829814 10.552304 29.029107 29.031064 15.39114 103.824649 0.8805 162.323113-0.8805 2.63563-1.322707 4.832967-1.761 6.153717 17.59239 49.697378 45.400538 98.774492 73.108895 121.647926 11.436717 8.791304 22.638634 18.899444 36.71098 26.814161 19.791684-2.20125 37.517128-4.11487 55.547812-4.11487 54.540128 0 87.525615 9.67963 100.279169 30.351814 4.400543 7.034217 6.595923 15.389184 5.281043 24.1844-0.44025 10.996467-4.39663 21.112434-12.31526 29.031064z m-27.796407-36.748157c-4.394673-4.398587-17.024957-16.936907-78.601259-16.936907-3.073923 0-10.622744-0.784623-14.57521 3.612007 32.104987 14.072347 62.830525 24.757704 83.058545 24.757703 3.083707 0 5.72325-0.442207 8.356923-0.876586h1.759044c2.20125-0.8805 3.520043-1.324663 3.960293-5.71738-0.87463-1.324663-1.757087-3.083707-3.958336-4.838837z m-387.124553 63.041845c-9.237424 5.27713-16.71189 10.112054-21.112433 13.634053-31.226444 28.586901-51.018128 57.616008-53.217422 74.331812 19.789727-6.59788 45.737084-35.626987 74.329855-87.961952v-0.003913z m125.574957-297.822284l2.197336-1.761c3.079793-14.072347 5.232127-29.189554 7.87167-38.869184l1.318794-7.036174c4.39663-25.070771 2.71781-39.720334-4.76057-50.272637l-6.59788-2.20125a57.381208 57.381208 0 0 0-3.079794 5.27713c-7.474467 18.47289-7.063567 55.283661 3.0524 94.865072l-0.001956-0.001957z" fill="currentColor" p-id="1992"></path></svg></div>`
  1060. const statusOverlay = `<div class="s-top s-top-status"><div class="s-panel"><div class="s-progress-wrapper"><div class="s-progress"></div></div><div class="s-status" style=""><div class="s-text" style="">正在加载...</div><div class="s-progress-text">0%<div></div></div></div></div></div>`;
  1061. const messageOverlay = `<div class="s-top s-top-message"><div class="s-message">testtest</div></div>`;
  1062.  
  1063. document.body.insertAdjacentHTML('afterbegin', statusOverlay);
  1064. document.body.insertAdjacentHTML('afterbegin', messageOverlay);
  1065. document.querySelector('.tool-bar-wrapper')?.insertAdjacentHTML('afterbegin', pdfButton);
  1066. document.head.appendChild(document.createElement('style')).innerHTML = `
  1067. .pdfbtn {
  1068. display:none
  1069. }
  1070.  
  1071. .s-btn-pdf:hover {
  1072. background-color: #6c32bc;
  1073. cursor: pointer;
  1074. }
  1075.  
  1076. .s-top {
  1077. position: fixed;
  1078. top: 0;
  1079. left: 0;
  1080. bottom: 0;
  1081. right: 0;
  1082. z-index: 2000;
  1083. padding-top: 40vh;
  1084. display: none;
  1085. }
  1086.  
  1087. .s-top.s-top-message {
  1088. text-align: center;
  1089. }
  1090.  
  1091. .s-message {
  1092. background-color: #000000aa;
  1093. color: white;
  1094. padding: 8px 14px;
  1095. text-align: center;
  1096. font-size: 18px;
  1097. border-radius: 6px;
  1098. display: inline-block;
  1099. }
  1100.  
  1101. .s-top.s-top-status {
  1102. z-index: 1000;
  1103. cursor: wait;
  1104. background-color: rgba(0, 0, 0, 0.4);
  1105. backdrop-filter: blur(10px) saturate(1.8);
  1106. }
  1107.  
  1108. .s-top.show {
  1109. display: block;
  1110. }
  1111.  
  1112. .s-panel {
  1113. background: white;
  1114. width: 90%;
  1115. max-width: 480px;
  1116. border-radius: 12px;
  1117. padding: 14px 24px;
  1118. margin: 0 auto;
  1119. }
  1120.  
  1121. .s-progress-wrapper {
  1122. height: 24px;
  1123. border-radius: 12px;
  1124. width: 100%;
  1125. background-color: #eeeff3;
  1126. overflow: hidden;
  1127. margin-bottom: 12px;
  1128. }
  1129.  
  1130. .s-progress {
  1131. background-color: #f7603e;
  1132. height: 24px;
  1133. width: 0;
  1134. transition: width 0.2s ease;
  1135. }
  1136.  
  1137. .s-status {
  1138. display: flex;
  1139. font-size: 14px;
  1140. }
  1141.  
  1142. .s-text {
  1143. flex-grow: 1;
  1144. color: #5f5f5f;
  1145. }
  1146.  
  1147. .s-progress-text {
  1148. color: #f7603e;
  1149. font-weight: bold;
  1150. }
  1151.  
  1152. .s-message {
  1153.  
  1154. }
  1155. `;
  1156. }
  1157.  
  1158. injectUI();
  1159.  
  1160. const exportPDF = async (...args) => {
  1161. try {
  1162. await downloadPDF(...args);
  1163. showMessage(`已成功导出,共计 ${creader.readerDocData.page} 页~`);
  1164. } catch (error) {
  1165. console.error('[x] failed to export:', error);
  1166. showMessage('导出失败:'+error?.message ?? error);
  1167. } finally {
  1168. hideStatus();
  1169. }
  1170. }
  1171.  
  1172. document.querySelector('.pdfbtn').onclick = ()=>exportPDF();
  1173. unsafeWindow['downloadPDF'] = exportPDF;
  1174. });
  1175. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址