Greasy Fork is available in English.

Easy Offline pikpak addon

Add pikpak support for Easy Offline

  1. // ==UserScript==
  2. // @name Easy Offline pikpak addon
  3. // @namespace hoothin
  4. // @version 2024-08-16
  5. // @description Add pikpak support for Easy Offline
  6. // @author hoothin
  7. // @match *://*/*
  8. // @grant GM_setValue
  9. // @grant GM_getValue
  10. // @grant GM_xmlhttpRequest
  11. // @grant GM_notification
  12. // @grant unsafeWindow
  13. // @run-at document-start
  14. // @require https://unpkg.com/crypto-js@4.2.0/crypto-js.js
  15. // @connect user.mypikpak.com
  16. // @connect api-drive.mypikpak.com
  17. // ==/UserScript==
  18.  
  19. (function() {
  20. 'use strict';
  21. let info = GM_getValue("pikpakUserInfo"), device_id, user_id;
  22. const CLIENT_ID = "YNxT9w7GMdWvEOKa";
  23. const CLIENT_SECRET = "dbw2OtmVEeuUvIptb1Coyg";
  24. const CLIENT_VERSION = "1.47.1";
  25. const PACKAG_ENAME = "com.pikcloud.pikpak";
  26. const SDK_VERSION = "2.0.4.204000";
  27. const APP_NAME = "com.pikcloud.pikpak";
  28. function captchaInit() {
  29. return new Promise(resolve => {
  30. let meta = {};
  31. if (/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(info.userName)) {
  32. meta.email = info.userName;
  33. } else if (/\d{11,18}/.test(info.userName)) {
  34. meta.phone_number = info.userName;
  35. } else {
  36. meta.username = info.userName;
  37. }
  38. let params = {
  39. "client_id": CLIENT_ID,
  40. "action": "POST:https://user.mypikpak.com/v1/auth/signin",
  41. "device_id": device_id,
  42. "meta": meta
  43. }
  44. GM_xmlhttpRequest({
  45. method: 'POST',
  46. url: 'https://user.mypikpak.com/v1/shield/captcha/init',
  47. data: JSON.stringify(params),
  48. onload: (res) => {
  49. if(res.status === 200) {
  50. let data = JSON.parse(res.responseText);
  51. resolve(data);
  52. } else {
  53. info.loginInfo = null;
  54. GM_setValue("pikpakUserInfo", info);
  55. const msg = JSON.parse(res.responseText).error_description;
  56. alert(msg);
  57. }
  58. },
  59. onerror: (e) => {
  60. GM_notification("Error: " + (e.statusText || e.error));
  61. },
  62. ontimeout: (e) => {
  63. GM_notification("Error: " + (e.statusText || e.error));
  64. }
  65. });
  66. });
  67. }
  68. function buildCustomUserAgent() {
  69. let signature_base = `${device_id}${PACKAG_ENAME}1appkey`;
  70. let sha1_result = CryptoJS.SHA1(signature_base).toString(CryptoJS.enc.Hex);
  71. let md5_result = CryptoJS.MD5(sha1_result).toString(CryptoJS.enc.Hex);
  72.  
  73. let device_sign = `div101.${device_id}${md5_result}`;
  74.  
  75. let user_agent_parts = [
  76. `ANDROID-${APP_NAME}/${CLIENT_VERSION}`,
  77. "protocolVersion/200",
  78. "accesstype/",
  79. `clientid/${CLIENT_ID}`,
  80. `clientversion/${CLIENT_VERSION}`,
  81. "action_type/",
  82. "networktype/WIFI",
  83. "sessionid/",
  84. `deviceid/${device_id}`,
  85. "providername/NONE",
  86. `devicesign/${device_sign}`,
  87. "refresh_token/",
  88. `sdkversion/${SDK_VERSION}`,
  89. `datetime/${Date.now()}`,
  90. `usrno/${user_id}`,
  91. `appname/${APP_NAME}`,
  92. "session_origin/",
  93. "grant_type/",
  94. "appid/",
  95. "clientip/",
  96. "devicename/Xiaomi_M2004j7ac",
  97. "osversion/13",
  98. "platformversion/10",
  99. "accessmode/",
  100. "devicemodel/M2004J7AC",
  101. ]
  102.  
  103. return user_agent_parts.join(" ");
  104. }
  105. document.addEventListener("click", function(e) {
  106. if (e.target && e.target.getAttribute && e.target.getAttribute("name") === "pikpak" && e.target.parentNode.id === "icons") {
  107. GM_setValue("pikpakUserInfo", "");
  108. alert("PikPak account has been cleared");
  109. }
  110. });
  111. var _unsafeWindow = (typeof unsafeWindow === 'undefined') ? window : unsafeWindow;
  112. if (!_unsafeWindow.eoAddons) _unsafeWindow.eoAddons = {};
  113. _unsafeWindow.eoAddons.pikpak = {
  114. regex: /mypikpak\.com/,
  115. url: "http://user.mypikpak.com/",
  116. bgColor: "2265ff",
  117. noTxt: true,
  118. linkRegExp: /^magnet:\?xt|^PikPak:\/\/|\.(torrent|mp4|mp3|rar|7z|zip|rmvb|mkv|avi|iso)$/i,
  119. directUrl: function(offUrl) {
  120. if (!info) {
  121. let userName = prompt("userName");
  122. if (!userName) return;
  123. let userPass = prompt("userPass");
  124. if (!userPass) return;
  125. info = {userName: userName, userPass: userPass};
  126. GM_setValue("pikpakUserInfo", info);
  127. }
  128. var postUrl = async () => {
  129. let postData;
  130. if (offUrl.indexOf('PikPak://') === 0) {
  131. const urlData = offUrl.substring(9).split('|')
  132. postData = {
  133. kind: "drive#file",
  134. name: urlData[0],
  135. size: urlData[1],
  136. hash: urlData[2],
  137. upload_type: "UPLOAD_TYPE_RESUMABLE",
  138. objProvider: {
  139. provider: "UPLOAD_TYPE_UNKNOWN"
  140. }
  141. }
  142. } else {
  143. postData = {
  144. kind: "drive#file",
  145. name: "",
  146. upload_type: "UPLOAD_TYPE_URL",
  147. url: {
  148. url: offUrl
  149. },
  150. params: {"from":"file"},
  151. folder_type: "DOWNLOAD"
  152. }
  153. }
  154. GM_xmlhttpRequest({
  155. method: 'POST',
  156. url: 'https://api-drive.mypikpak.com/drive/v1/files',
  157. data: JSON.stringify(postData),
  158. headers: {
  159. "Content-Type": "application/json; charset=utf-8",
  160. authorization: info.loginInfo.token_type + ' ' + info.loginInfo.access_token,
  161. "X-Captcha-Token": info.captchaData.captcha_token
  162. },
  163. onload: (res) => {
  164. if(res.status === 200) {
  165. GM_notification("Task OK");
  166. } else if(res.status === 401) {
  167. info.loginInfo=null;
  168. GM_setValue("pikpakUserInfo", info);
  169. const msg = JSON.parse(res.responseText).error_description;
  170. alert(msg);
  171. } else if(res.status === 400) {
  172. const msg = JSON.parse(res.responseText).error_description;
  173. alert(msg);
  174. } else if(res.status === 403) {
  175. const msg = JSON.parse(res.responseText).error_description;
  176. alert(msg);
  177. }
  178. },
  179. onerror: (e) => {
  180. GM_notification("Error: " + (e.statusText || e.error));
  181. },
  182. ontimeout: (e) => {
  183. GM_notification("Error: " + (e.statusText || e.error));
  184. }
  185. })
  186. };
  187. device_id = CryptoJS.MD5(`${info.userName}${info.userPass}`).toString(CryptoJS.enc.Hex);
  188. (async () => {
  189. if (!info.captchaData || info.captchaData.expires < new Date().getTime()) {
  190. let captchaData = await captchaInit();
  191. info.captchaData = captchaData;
  192. if (!info.captchaData.expires && info.captchaData.expires_in) {
  193. info.captchaData.expires = new Date().getTime() + 1000 * info.captchaData.expires_in;
  194. }
  195. GM_setValue("pikpakUserInfo", info);
  196. }
  197. if (!info.loginInfo || info.loginInfo.expires < new Date().getTime()) {
  198. let data = {
  199. "client_id": CLIENT_ID,
  200. "client_secret": CLIENT_SECRET,
  201. "password": info.userPass,
  202. "username": info.userName,
  203. "captcha_token": info.captchaData.captcha_token,
  204. }
  205. GM_xmlhttpRequest({
  206. method: 'POST',
  207. url: 'https://user.mypikpak.com/v1/auth/signin',
  208. data: JSON.stringify(data),
  209. headers: {
  210. 'user-agent': 'accessmode/ devicename/Netease_Mumu appname/android-com.pikcloud.pikpak cmd/login appid/ action_type/ clientid/YNxT9w7GMdWvEOKa deviceid/56e000d71f4660700ca974f2305171c5 refresh_token/ grant_type/ networktype/WIFI devicemodel/MuMu accesstype/ sessionid/ osversion/6.0.1 datetime/1636364470779 sdkversion/1.0.1.101600 protocolversion/200 clientversion/ providername/NONE clientip/ session_origin/ devicesign/div101.56e000d71f4660700ca974f2305171c5b94c3d4196a9dd74e49d7710a7af873d platformversion/10 usrno/null'
  211. },
  212. onload: (res) => {
  213. if (res.status === 200) {
  214. info.loginInfo = JSON.parse(res.responseText);
  215. if (!info.loginInfo.expires && info.loginInfo.expires_in) {
  216. info.loginInfo.expires = new Date().getTime() + 1000 * info.loginInfo.expires_in;
  217. }
  218. GM_setValue("pikpakUserInfo", info);
  219. postUrl();
  220. } else if (res.status === 401) {
  221. GM_setValue("pikpakUserInfo","");
  222. const msg = JSON.parse(res.responseText).error_description;
  223. alert(msg);
  224. } else if (res.status === 400) {
  225. GM_setValue("pikpakUserInfo","");
  226. const msg = JSON.parse(res.responseText).error_description;
  227. alert(msg);
  228. } else if (res.status === 403) {
  229. GM_setValue("pikpakUserInfo","");
  230. const msg = JSON.parse(res.responseText).error_description;
  231. alert(msg);
  232. }
  233. },
  234. onerror: (e) => {
  235. GM_notification("Error: " + (e.statusText || e.error));
  236. },
  237. ontimeout: (e) => {
  238. GM_notification("Error: " + (e.statusText || e.error));
  239. }
  240. })
  241. } else {
  242. postUrl();
  243. }
  244. })();
  245. return false;
  246. },
  247. bgImg: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAYFBMVEUAAAArbP5MdNFMdNIrbP4sbP1QdctQdcwvbv////8dYf8sbP9smP/09/8qaf/V4f8iZf/u8/+lwP+duv+UtP+PsP9hkP92n/9nlP/K2v/F1v+7z/+Ytv9Wif9Aef91nv9e7pgKAAAACHRSTlMA+puT/PWHhQBB7XEAAACQSURBVBjTTY8JCsQwCEWdrjFm37p37n/LMdAJfSTgf4IoAPQf8fDpgek4N9NxnwQZJx0/w+UMNd/XV6nluqsBQbRp79Xqvd7ICBCuaHuElMJhdXEspMKGko/YA2LYXyKuiGt8Ca3rfwm052nbjILV1IyFBZFZck4xppwXQ8Srk2wQzXzcIBpDB8w0/vM4AfwACl4LKjajMX0AAAAASUVORK5CYII="
  248. };
  249. })();