Greasy Fork is available in English.

pikpak助手

pikpak网盘助手,绕过ip限制,支持aria2下载!

Version vom 05.01.2023. Aktuellste Version

  1. // ==UserScript==
  2. // @name pikpak助手
  3. // @name:zh-CN pikpak助手
  4. // @namespace http://tampermonkey.net/
  5. // @version 0.7
  6. // @author xiaoziguys
  7. // @description pikpak网盘助手,绕过ip限制,支持aria2下载!
  8. // @description:zh-cn pikpak网盘助手,绕过ip限制,支持aria2下载!
  9. // @license MIT
  10. // @icon https://www.google.com/s2/favicons?sz=64&domain=mypikpak.com
  11. // @match https://mypikpak.com/*
  12. // @require https://cdn.jsdelivr.net/npm/vue@3.2.45/dist/vue.global.prod.js
  13. // @grant GM_xmlhttpRequest
  14. // @grant unsafeWindow
  15. // ==/UserScript==
  16.  
  17. (o=>{const a=document.createElement("style");a.dataset.source="vite-plugin-monkey",a.innerText=o,document.head.appendChild(a)})(".dialog[data-v-80dfc4ca]{position:absolute;top:20%;left:50%;transform:translate(-50%);background:#fff;z-index:10000;padding:30px;box-shadow:0 0 50px #000;border-radius:8px}.dialog .close[data-v-80dfc4ca]{position:absolute;right:10px;top:10px;font-size:30px;cursor:pointer;color:#999}.movies[data-v-80dfc4ca],.movies li[data-v-80dfc4ca]{margin-top:10px}.movies li input[data-v-80dfc4ca]{margin-right:10px}.footer[data-v-80dfc4ca]{margin-top:20px;display:flex;flex-direction:row-reverse}.dialog[data-v-07297299]{position:absolute;top:20%;left:50%;transform:translate(-50%);background:#fff;z-index:10000;padding:30px;box-shadow:0 0 50px #000;border-radius:8px}.dialog .close[data-v-07297299]{position:absolute;right:10px;top:10px;font-size:30px;cursor:pointer;color:#999}.footer[data-v-07297299]{margin-top:20px;display:flex;flex-direction:row-reverse}.form[data-v-07297299]{margin-top:20px}.xz-input[data-v-07297299]{border:#d9d9d9 1px solid;margin-bottom:10px;padding:5px;margin-top:5px}.aria2-tip[data-v-6f3840c3]{padding:10px;background:rgba(0,0,0,.8);position:absolute;top:30px;left:50%;transform:translateY(-50%);color:#fff;border-radius:8px}.btns[data-v-c13a86b4]{display:flex;flex-direction:row-reverse;padding-right:10px;padding-top:20px}.btns li[data-v-c13a86b4]{cursor:pointer;margin-right:10px}");
  18.  
  19. (function(vue) {
  20. "use strict";
  21. function getPlatform() {
  22. let u = navigator.userAgent;
  23. let isAndroid = u.indexOf("Android") > -1 || u.indexOf("Adr") > -1;
  24. let isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
  25. if (isAndroid) {
  26. return "Android";
  27. } else if (isIOS) {
  28. return "IOS";
  29. } else {
  30. return "PC";
  31. }
  32. }
  33. var monkeyWindow = window;
  34. var GM_xmlhttpRequest = /* @__PURE__ */ (() => monkeyWindow.GM_xmlhttpRequest)();
  35. function post(url, data, headers, type) {
  36. data = JSON.stringify(data);
  37. return new Promise((resolve, reject) => {
  38. GM_xmlhttpRequest({
  39. method: "POST",
  40. url,
  41. headers,
  42. data,
  43. responseType: type || "json",
  44. onload: (res) => {
  45. type === "blob" ? resolve(res) : res.response ? resolve(res.response || res.responseText) : reject(res);
  46. },
  47. onerror: (err) => {
  48. reject(err);
  49. }
  50. });
  51. });
  52. }
  53. function postData(url = "", data = {}, customHeaders = {}, method = "GET") {
  54. ({
  55. method,
  56. mode: "cors",
  57. cache: "no-cache",
  58. credentials: "same-origin",
  59. headers: {
  60. "Content-Type": "application/json",
  61. ...customHeaders
  62. },
  63. redirect: "follow",
  64. referrerPolicy: "no-referrer"
  65. });
  66. if (method === "GET") {
  67. return fetch(url, {
  68. method: "GET",
  69. mode: "cors",
  70. cache: "no-cache",
  71. credentials: "same-origin",
  72. headers: {
  73. "Content-Type": "application/json",
  74. ...customHeaders
  75. },
  76. redirect: "follow",
  77. referrerPolicy: "no-referrer"
  78. }).then((response) => response.json());
  79. } else {
  80. return new Promise((resolve, reject) => {
  81. let xhr = new XMLHttpRequest();
  82. xhr.onreadystatechange = function() {
  83. if (xhr.readyState === 4) {
  84. if (xhr.status === 200) {
  85. resolve(JSON.parse(xhr.response));
  86. } else {
  87. reject({});
  88. }
  89. }
  90. };
  91. xhr.open(method, url);
  92. xhr.setRequestHeader("content-type", "application/json");
  93. xhr.send(JSON.stringify(data));
  94. });
  95. }
  96. }
  97. function getDownload(id) {
  98. let token = "";
  99. let captcha = "";
  100. for (let i = 0; i < 20; i++) {
  101. let key = window.localStorage.key(i);
  102. if (key === null)
  103. break;
  104. if (key && key.startsWith("credentials")) {
  105. let tokenData = JSON.parse(window.localStorage.getItem(key));
  106. token = tokenData.token_type + " " + tokenData.access_token;
  107. continue;
  108. }
  109. if (key && key.startsWith("captcha")) {
  110. let tokenData = JSON.parse(window.localStorage.getItem(key));
  111. captcha = tokenData.token;
  112. }
  113. }
  114. let header = {
  115. Authorization: token,
  116. "x-device-id": window.localStorage.getItem("deviceid"),
  117. "x-captcha-token": captcha
  118. };
  119. return postData("https://api-drive.mypikpak.com/drive/v1/files/" + id + "?", {}, header);
  120. }
  121. function pushToAria(url, data) {
  122. if (["Android", "IOS"].includes(getPlatform()) && !GM_xmlhttpRequest) {
  123. return postData(url, data, {}, "POST");
  124. } else {
  125. return post(url, data, {}, "");
  126. }
  127. }
  128. const AriaDownloadDialog_vue_vue_type_style_index_0_scoped_80dfc4ca_lang = "";
  129. const _export_sfc = (sfc, props) => {
  130. const target = sfc.__vccOpts || sfc;
  131. for (const [key, val] of props) {
  132. target[key] = val;
  133. }
  134. return target;
  135. };
  136. const _withScopeId$1 = (n) => (vue.pushScopeId("data-v-80dfc4ca"), n = n(), vue.popScopeId(), n);
  137. const _hoisted_1$3 = {
  138. key: 0,
  139. style: { "width": "600px" },
  140. class: "dialog"
  141. };
  142. const _hoisted_2$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ vue.createElementVNode("h2", null, "请勾选你要下载的", -1));
  143. const _hoisted_3$1 = { class: "movies" };
  144. const _hoisted_4$1 = ["id", "value"];
  145. const _sfc_main$3 = {
  146. __name: "AriaDownloadDialog",
  147. props: {
  148. show: Boolean
  149. },
  150. emits: ["update:show", "msg"],
  151. setup(__props, { emit: emits }) {
  152. const props = __props;
  153. const list = vue.ref([]);
  154. const selected = vue.ref([]);
  155. const checkedAll = vue.ref(false);
  156. vue.watch(
  157. () => props.show,
  158. (val) => {
  159. if (val) {
  160. const domItems = document.body.querySelectorAll(".row.grid");
  161. const tempList = [];
  162. for (let i = 0; i < domItems.length; i++) {
  163. const item = domItems[i];
  164. if (!domItems[i].querySelector(".folder-cover")) {
  165. tempList.push({ id: item.id, name: item.querySelector(".name").innerText });
  166. }
  167. }
  168. list.value = tempList;
  169. }
  170. }
  171. );
  172. const close = () => {
  173. selected.value = [];
  174. checkedAll.value = false;
  175. emits("update:show", false);
  176. };
  177. const onCheckAll = () => {
  178. if (checkedAll.value) {
  179. selected.value = list.value.map((item) => item.id);
  180. } else {
  181. selected.value = [];
  182. }
  183. };
  184. const onCheck = () => {
  185. checkedAll.value = selected.value.length === list.value.length;
  186. };
  187. const push = () => {
  188. let total = selected.value.length;
  189. let success = 0;
  190. let fail = 0;
  191. let ariaHost = window.localStorage.getItem("ariaHost") || "";
  192. let ariaPath = window.localStorage.getItem("ariaPath") || "";
  193. let ariaToken = window.localStorage.getItem("ariaToken") || "";
  194. let errorMSG = "";
  195. if (!ariaHost) {
  196. emits("msg", "请先配置aria2");
  197. close();
  198. return;
  199. }
  200. for (let item of selected.value) {
  201. getDownload(item).then((res) => {
  202. if (res.error_description) {
  203. emits("msg", `失败原因: ${res.error_description} 请刷新!`);
  204. return;
  205. }
  206. let ariaData = {
  207. id: new Date().getTime(),
  208. jsonrpc: "2.0",
  209. method: "aria2.addUri",
  210. params: [
  211. [res.web_content_link],
  212. { out: res.name }
  213. ]
  214. };
  215. ariaPath && (ariaData.params[1].dir = ariaPath);
  216. ariaToken && ariaData.params.unshift(`token:${ariaToken}`);
  217. total--;
  218. pushToAria(ariaHost, ariaData).then((ariares) => {
  219. if (ariares.result) {
  220. success++;
  221. } else {
  222. errorMSG = ariares.error.message === "Unauthorized" ? "密钥不对" : "推送失败";
  223. fail++;
  224. }
  225. }).catch((e) => {
  226. errorMSG = `${e.statusText} 请检测配置`;
  227. emits("msg", `失败原因: ${e.statusText}`);
  228. fail++;
  229. }).finally(() => {
  230. if (total === 0) {
  231. emits("msg", `成功:${success} 失败: ${fail} ${fail !== 0 ? "失败原因" + errorMSG : ""}`);
  232. }
  233. });
  234. }).catch((e) => {
  235. fail++;
  236. total--;
  237. });
  238. }
  239. close();
  240. };
  241. return (_ctx, _cache) => {
  242. return __props.show ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$3, [
  243. _hoisted_2$1,
  244. vue.createElementVNode("div", {
  245. class: "close",
  246. onClick: close
  247. }, "×"),
  248. vue.withDirectives(vue.createElementVNode("input", {
  249. onChange: onCheckAll,
  250. style: { "margin": "10px 10px 0 0" },
  251. type: "checkbox",
  252. id: "checkbox",
  253. "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => checkedAll.value = $event)
  254. }, null, 544), [
  255. [vue.vModelCheckbox, checkedAll.value]
  256. ]),
  257. vue.createTextVNode("全选 "),
  258. vue.createElementVNode("ul", _hoisted_3$1, [
  259. (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(list.value, (item) => {
  260. return vue.openBlock(), vue.createElementBlock("li", {
  261. key: item.id
  262. }, [
  263. vue.withDirectives(vue.createElementVNode("input", {
  264. onChange: onCheck,
  265. type: "checkbox",
  266. id: item.id,
  267. value: item.id,
  268. "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => selected.value = $event)
  269. }, null, 40, _hoisted_4$1), [
  270. [vue.vModelCheckbox, selected.value]
  271. ]),
  272. vue.createTextVNode(vue.toDisplayString(item.name), 1)
  273. ]);
  274. }), 128))
  275. ]),
  276. vue.createElementVNode("div", { class: "footer" }, [
  277. vue.createElementVNode("div", {
  278. class: "btn el-button el-button--primary",
  279. onClick: push
  280. }, "推送到aria2")
  281. ])
  282. ])) : vue.createCommentVNode("", true);
  283. };
  284. }
  285. };
  286. const AriaDownloadDialog = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["__scopeId", "data-v-80dfc4ca"]]);
  287. const AriaConfigDialog_vue_vue_type_style_index_0_scoped_07297299_lang = "";
  288. const _withScopeId = (n) => (vue.pushScopeId("data-v-07297299"), n = n(), vue.popScopeId(), n);
  289. const _hoisted_1$2 = {
  290. key: 0,
  291. style: { "width": "400px" },
  292. class: "dialog"
  293. };
  294. const _hoisted_2 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("h2", null, "请配置你的aria2", -1));
  295. const _hoisted_3 = { class: "form" };
  296. const _hoisted_4 = { class: "form-item" };
  297. const _hoisted_5 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("span", null, "服务器:", -1));
  298. const _hoisted_6 = { class: "el-input xz-input" };
  299. const _hoisted_7 = { class: "form-item" };
  300. const _hoisted_8 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("span", null, "路径:", -1));
  301. const _hoisted_9 = { class: "el-input xz-input" };
  302. const _hoisted_10 = { class: "form-item" };
  303. const _hoisted_11 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("span", null, "密钥:", -1));
  304. const _hoisted_12 = { class: "el-input xz-input" };
  305. const _sfc_main$2 = {
  306. __name: "AriaConfigDialog",
  307. props: {
  308. show: Boolean
  309. },
  310. emits: ["update:show", "msg"],
  311. setup(__props, { emit: emits }) {
  312. const close = () => {
  313. emits("update:show", false);
  314. };
  315. let ariaHost = window.localStorage.getItem("ariaHost") || "";
  316. let ariaPath = window.localStorage.getItem("ariaPath") || "";
  317. let ariaToken = window.localStorage.getItem("ariaToken") || "";
  318. const form = vue.reactive({
  319. host: ariaHost,
  320. path: ariaPath,
  321. token: ariaToken
  322. });
  323. const save = () => {
  324. window.localStorage.setItem("ariaHost", form.host);
  325. window.localStorage.setItem("ariaPath", form.path);
  326. window.localStorage.setItem("ariaToken", form.token);
  327. close();
  328. emits("msg", "保存成功!");
  329. };
  330. return (_ctx, _cache) => {
  331. return __props.show ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$2, [
  332. _hoisted_2,
  333. vue.createElementVNode("div", {
  334. class: "close",
  335. onClick: close
  336. }, "×"),
  337. vue.createElementVNode("div", _hoisted_3, [
  338. vue.createElementVNode("div", _hoisted_4, [
  339. _hoisted_5,
  340. vue.createElementVNode("div", _hoisted_6, [
  341. vue.withDirectives(vue.createElementVNode("input", {
  342. "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => form.host = $event),
  343. class: "el-input__inner"
  344. }, null, 512), [
  345. [vue.vModelText, form.host]
  346. ])
  347. ])
  348. ]),
  349. vue.createElementVNode("div", _hoisted_7, [
  350. _hoisted_8,
  351. vue.createElementVNode("div", _hoisted_9, [
  352. vue.withDirectives(vue.createElementVNode("input", {
  353. "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => form.path = $event),
  354. class: "el-input__inner"
  355. }, null, 512), [
  356. [vue.vModelText, form.path]
  357. ])
  358. ])
  359. ]),
  360. vue.createElementVNode("div", _hoisted_10, [
  361. _hoisted_11,
  362. vue.createElementVNode("div", _hoisted_12, [
  363. vue.withDirectives(vue.createElementVNode("input", {
  364. "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => form.token = $event),
  365. class: "el-input__inner"
  366. }, null, 512), [
  367. [vue.vModelText, form.token]
  368. ])
  369. ])
  370. ])
  371. ]),
  372. vue.createElementVNode("div", { class: "footer" }, [
  373. vue.createElementVNode("div", {
  374. class: "btn el-button el-button--primary",
  375. onClick: save
  376. }, "保存")
  377. ])
  378. ])) : vue.createCommentVNode("", true);
  379. };
  380. }
  381. };
  382. const AriaConfigDialog = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-07297299"]]);
  383. const Aria2Toast_vue_vue_type_style_index_0_scoped_6f3840c3_lang = "";
  384. const _hoisted_1$1 = {
  385. key: 0,
  386. class: "aria2-tip"
  387. };
  388. const _sfc_main$1 = {
  389. __name: "Aria2Toast",
  390. setup(__props, { expose }) {
  391. const show = vue.ref(false);
  392. const open = () => {
  393. show.value = true;
  394. setTimeout(() => {
  395. show.value = false;
  396. }, 3e3);
  397. };
  398. expose({ open });
  399. return (_ctx, _cache) => {
  400. return show.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$1, [
  401. vue.renderSlot(_ctx.$slots, "default", {}, void 0, true)
  402. ])) : vue.createCommentVNode("", true);
  403. };
  404. }
  405. };
  406. const Aria2Toast = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-6f3840c3"]]);
  407. const App_vue_vue_type_style_index_0_scoped_c13a86b4_lang = "";
  408. const _hoisted_1 = {
  409. key: 0,
  410. class: "btns"
  411. };
  412. const _sfc_main = {
  413. __name: "App",
  414. setup(__props) {
  415. const downloadShow = vue.ref(false);
  416. const configShow = vue.ref(false);
  417. const tip = vue.ref("");
  418. const toastRef = vue.ref(null);
  419. const showPlugin = vue.ref(false);
  420. const showToast = (val) => {
  421. tip.value = val;
  422. toastRef.value.open();
  423. };
  424. if (location.pathname !== "/") {
  425. showPlugin.value = true;
  426. }
  427. return (_ctx, _cache) => {
  428. return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [
  429. showPlugin.value ? (vue.openBlock(), vue.createElementBlock("ul", _hoisted_1, [
  430. vue.createElementVNode("li", {
  431. class: "btn",
  432. onClick: _cache[0] || (_cache[0] = ($event) => downloadShow.value = true)
  433. }, "aria2下载"),
  434. vue.createElementVNode("li", {
  435. class: "btn",
  436. onClick: _cache[1] || (_cache[1] = ($event) => configShow.value = true)
  437. }, "aria2配置")
  438. ])) : vue.createCommentVNode("", true),
  439. vue.createVNode(AriaDownloadDialog, {
  440. onMsg: showToast,
  441. show: downloadShow.value,
  442. "onUpdate:show": _cache[2] || (_cache[2] = ($event) => downloadShow.value = $event)
  443. }, null, 8, ["show"]),
  444. vue.createVNode(AriaConfigDialog, {
  445. onMsg: showToast,
  446. show: configShow.value,
  447. "onUpdate:show": _cache[3] || (_cache[3] = ($event) => configShow.value = $event)
  448. }, null, 8, ["show"]),
  449. vue.createVNode(Aria2Toast, {
  450. ref_key: "toastRef",
  451. ref: toastRef
  452. }, {
  453. default: vue.withCtx(() => [
  454. vue.createTextVNode(vue.toDisplayString(tip.value), 1)
  455. ]),
  456. _: 1
  457. }, 512)
  458. ], 64);
  459. };
  460. }
  461. };
  462. const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-c13a86b4"]]);
  463. document.cookie = "pp_access_to_visit=true";
  464. setTimeout(() => {
  465. vue.createApp(App).mount(
  466. (() => {
  467. let pikpakContainer = document.getElementById("app");
  468. const app = document.createElement("div");
  469. document.body.insertBefore(app, pikpakContainer);
  470. return app;
  471. })()
  472. );
  473. }, 1e3);
  474. })(Vue);