98T Picture Preview

[NOW]Click/Copy all magnet/torrent links that have not been viewed.

  1. // ==UserScript==
  2. // @name 98T Picture Preview
  3. // @description [NOW]Click/Copy all magnet/torrent links that have not been viewed.
  4. // @version 1.9.0
  5. // @icon data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABMLAAATCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtLvcALW92gawt9w0u73lZb2+62vKzu8ryNDpC87S9gDI1dgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArLPYAKuy1g6ts92Esbbj5rq66/u3tuv5x8jy4r3A56e7wOIhur/jAAAAAAAAAAAAAAAAAK6z3QCxt90FqKvbQ52f1XybotWZp67g+Ker5v+ipej/pKbr/7K17/+3uun/vcPpi////wDP1egAAAAAAAAAAAC4uucWuLjvfqmp6O2Xl97/kZfa/4uR3P96gNX/gYnb/46W5v+SmOv/o6js/7i97srDxvUiyc30CMOf/wDH19YAsrDpjraw8/ynovL/jY3l/3l/3P9eYtD/bXTT/5Sc5f+krO7/r7X0/5qg8P+ssvT5w8X818PF+LG0s+9cp6bnF6Ga3t6mnej/lZDo/3Z42/9vd9f/iI3g/6Op7/+ZoPX/k5z3/6mu+v+8wvr/tbz6/7i9+f+1tfL/sa7s+7y676qZktn/l4/h/4mI4P99gd7/q7Dt/5ed8P9weOz/S0/s/zg96/8+R+r/p6/8/7rC+P+nq/X/qafy/7Gu7v+0s+v3pJ/j/5eV4/+doOv/oKXx/6Op+P9OVub/T1nv/1Ra8P9ASOz/W2jz/4+Z/f+wtvj/oafv/6Cj8v+oqO7/ra7q/ael5MmjpOj/qq7x/6Cn9/+Yovv/dID3/1ll9/9kavj/XWj0/36L+P+NmP3/p6v8/7e69v+tr/T/sLPz/73C8r7AwPFBsrXr2auv7/+Ynfj/nqj8/4uW/f9sefz/XGT9/2Jp/f91gP3/eYX//6Om/P/BxPr+wcT45MXJ+Z7V3Psn////ALm+7Ty6u/Xnq6z9/6eu/P+ZoP7/bHb+/3yH/v+Sm/7/kZf+/6Wr/v+vs/v/ur7648rQ+z70/P8F2+L+AM7R+QDO0PgQy8v7w7+//f+ytv3/srf9/6Km/v+ipf//q6z//7a4//+1uv7/srL+/726/dTX2fob1df6AAAAAADCv/gAwLv4CNPQ/bfJyvv/wcP8/77C/f+5vPz/urr8/72//f+7vv7/sLD+/7Cr+//Bvfju1tf4NdTV+QAAAAAAxsj2AMfL9QnZ1fy30cz8/7689v+qpvb/tbT9/8DC/v+6u/7/t7b8/7Sx+P+3sPX/xMD2vNnc9xDV1vcAAAAAANDX9gDt4v8A3Nz8SczK98+wq+3fmZLwq6uo+47Fx/q/vb33+r279v/IxPn/xsH49MfF9Va3r/IA19z3AAAAAAAAAAAAwMz0AMbY/gG2vOsfr7PsKqus+gawtPAAzNHzF8PH8XDR0vi129v9qtPT+3DT1PgW09T4AAAAAAAAAAAA+B8AAPAPAACABwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAACAAwAAgAMAAIADAADABwAAwgcAAA==
  6. // @author UnforgetMemory
  7. // @namespace https://www.sehuatang.net/*
  8. // @namespace https://www.sehuatang.org/*
  9. // @match https://www.sehuatang.net/forum*
  10. // @match https://www.sehuatang.org/forum*
  11. // @match https://www.sehuatang.net/forum.php?mod=forumdisplay&fid=103&page=*
  12. // @match https://www.sehuatang.org/forum.php?mod=forumdisplay&fid=103&page=*
  13. // @require https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js
  14. // @require https://cdn.jsdelivr.net/npm/sweetalert2@11
  15. // @require https://cdn.jsdelivr.net/npm/i18next@23.10.1/i18next.min.js
  16. // @grant GM_xmlhttpRequest
  17. // @grant GM_addStyle
  18. // @grant GM_registerMenuCommand
  19. // @grant GM_getValue
  20. // @grant GM_setValue
  21. // @grant GM_setClipboard
  22. // @license GNU GPLv3
  23. // ==/UserScript==
  24.  
  25. (function () {
  26. 'use strict';
  27. // Default Config >> Start
  28. const REFERER = document.location.href;
  29. const USER_AGENT = window.navigator.userAgent;
  30. const ACCEPT =
  31. 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7';
  32. const COOKIE = document.cookie;
  33. const JAVDB_COM = 'javdb.com';
  34. const VIEWED_AVIDS_LOCAL_STORAGE_KEY = '68905cf391b2428572e6446042ab1029';
  35. const VIEWED_US_TITLES_LOCAL_STORAGE_KEY = 'abba24c58fc69bf0955bddc7a0eadee1';
  36. const HIDE_VIEWED_MODE_KEY = '780fbed5c332f7f96ca73e19e94a9749';
  37. const VIEWED_COLOR = 'gray';
  38. const JIANGUOYUN_KEY_MD5 = '97f6483755d45ad927caf3108b61be91';
  39. const JIANGUOYUN_DAV_URL_EL_ID = `${JIANGUOYUN_KEY_MD5}_dav_url`;
  40. const JIANGUOYUN_ACCOUNT_EL_ID = `${JIANGUOYUN_KEY_MD5}_account`;
  41. const JIANGUOYUN_PASSWORD_EL_ID = `${JIANGUOYUN_KEY_MD5}_password`;
  42. const AVIDS_CLOUD_FILE_NAME = '95551967d3da7c5af36b141f630683c4';
  43. const US_CLOUD_FILE_NAME = '53648e7622cfa657f1f6de856efd67c9';
  44. const LOCALE_KEY = 'ee2757153264e82a1c8f64db8ddcb3e2';
  45. const LOCALE_LIST = {
  46. enUS: 'en-US',
  47. zhCN: 'zh-CN',
  48. zhHK: 'zh-HK',
  49. zhTW: 'zh-TW',
  50. };
  51. // Default Config >> End
  52.  
  53. // REGEX >> Start
  54. const AVID_REGEX = /[a-zA-Z]{2,6}[-\s]?\d{2,5}/gi;
  55. // REGEX >> End
  56.  
  57. // Gobal Var >> Start
  58. var elArrayLength = 0;
  59. // Gobal Var >> End
  60.  
  61. // i18nnext
  62.  
  63. i18next.init({
  64. lng: currentLocale(),
  65. fallbackLng: LOCALE_LIST.enUS,
  66. resources: {
  67. 'en-US': {
  68. translation: {
  69. Language: '🕮 Language',
  70. 'Hide Viewed': 'Hide Viewed',
  71. Jianguoyun: '☁️ Jianguoyun',
  72. 'Jianguoyun Config': '☁️ Jianguoyun Config',
  73. 'Upload To Jianguoyun': '↑ Upload To ☁️ Jianguoyun',
  74. 'Download from Jianguoyun': '↓ Download from ☁️ Jianguoyun',
  75. 'Local and Jianguoyun merge': '🔄 Local and ☁️ Jianguoyun merge',
  76. 'DAV URL': '☁️ DAV URL',
  77. Account: '👤 Account',
  78. Password: '🔑 Password',
  79. 'Show Password': 'Show Password',
  80. Save: 'Save',
  81. 'Save Successful!': 'Save Successful!',
  82. 'Update Successful!': 'Update Successful!',
  83. 'Update Bad': 'Update Bad',
  84. 'Upload successful!': 'Upload successful!',
  85. 'Sync successful!': 'Sync successful!',
  86. 'Download successful!': 'Download successful!',
  87. 'Hidden Password': 'Hidden Password',
  88. 'Status ERROR': 'Webdav Status Error',
  89. 'Check Config!': 'Check Config!',
  90. 'No Data': 'Cloud No historical data exists!',
  91. 'Bad Download': 'Bad Download Data',
  92. 'Page to Refresh':
  93. 'The page is about to refresh due to new data updates. Please wait...',
  94. 'Viewed Total': 'Viewed Total',
  95. click_all_magnet: 'Click All Magnet',
  96. click_all_torrent: 'Click All Torrent',
  97. },
  98. },
  99. 'zh-CN': {
  100. translation: {
  101. Language: '🕮 语言(简体)',
  102. 'Hide Viewed': '隐藏已阅',
  103. Jianguoyun: '☁️ 坚果云',
  104. 'Jianguoyun Config': '☁️ 坚果云配置',
  105. 'Upload To Jianguoyun': '↑ 上传至 ☁️ 坚果云',
  106. 'Download from Jianguoyun': '从 ☁️ 坚果云 ↓ 下载',
  107. 'Local and Jianguoyun merge': '🔄 双端同步 ☁️ 坚果云 ',
  108. 'DAV URL': '☁️ DAV URL',
  109. Account: '👤 账号',
  110. Password: '🔑 密码',
  111. 'Show Password': '显示密码',
  112. Save: '保存',
  113. 'Save Successful!': '保存成功!',
  114. 'Update Successful!': '更新成功!',
  115. 'Update Bad': '更新失败',
  116. 'Upload successful!': '上传成功!',
  117. 'Sync successful!': '同步成功!',
  118. 'Download successful!': '下载完成!',
  119. 'Hidden Password': '隐藏密码',
  120. 'Status ERROR': 'Webdav 状态异常',
  121. 'Check Config!': '检查配置!',
  122. 'No Data': '云端没有历史数据!',
  123. 'Bad Download': '下载数据出错',
  124. 'Page to Refresh': '数据更新,页面即将刷新,请稍候...',
  125. 'Viewed Total': '浏览量',
  126. click_all_magnet: '点击所有磁力链接',
  127. click_all_torrent: '点击所有种子链接',
  128. },
  129. },
  130. 'zh-TW': {
  131. translation: {
  132. Language: '🕮 語言(台)',
  133. 'Hide Viewed': '隱藏已讀',
  134. Jianguoyun: '☁️ 堅果雲',
  135. 'Jianguoyun Config': '☁️ 堅果雲配置',
  136. 'Upload To Jianguoyun': '⬆️ 上傳至 ☁️ 堅果雲',
  137. 'Download from Jianguoyun': '從 ☁️ 堅果雲 ⬇️ 下載',
  138. 'Local and Jianguoyun merge': '本地與 ☁️ 堅果雲同步',
  139. 'DAV URL': '☁️ DAV 網址',
  140. Account: ' 帳戶',
  141. Password: ' 密碼',
  142. 'Show Password': '顯示密碼',
  143. Save: '儲存',
  144. 'Save Successful!': '儲存成功!',
  145. 'Hidden Password': '隱藏密碼',
  146. 'Update Bad': '更新失敗',
  147. 'Upload successful!': '上傳成功!',
  148. 'Sync successful!': '同步成功!',
  149. 'Download successful!': '下載成功!',
  150. 'Status ERROR': 'Webdav 狀態錯誤',
  151. 'Check Config!': '檢查設定!',
  152. 'No Data': '雲端沒有歷史資料!',
  153. 'Bad Download': '下載數據錯誤',
  154. 'Page to Refresh': '頁面即將重新整理,以更新資料。請稍候...',
  155. 'Viewed Total': '瀏覽量',
  156. click_all_magnet: '點擊所有磁力連結',
  157. click_all_torrent: '點擊所有種子連結',
  158. },
  159. },
  160. 'zh-HK': {
  161. translation: {
  162. Language: '🕮 語言(港)',
  163. 'Hide Viewed': '收埋睇過',
  164. 'Jianguoyun Config': '☁️ 堅果雲',
  165. 'Jianguoyun Config': '☁️ 堅果雲設定',
  166. 'Upload To Jianguoyun': '⬆️ 上載到 ☁️ 堅果雲',
  167. 'Download from Jianguoyun': '由 ☁️ 堅果雲 ⬇️ 下載',
  168. 'Local and Jianguoyun merge': '本地同 ☁️ 堅果雲同步',
  169. 'DAV URL': '☁️ DAV 網址',
  170. Account: '帳戶',
  171. Password: '密碼',
  172. 'Show Password': '睇密碼',
  173. Save: '儲存',
  174. 'Save Successful!': '儲存成功喇!',
  175. 'Hidden Password': '收埋密碼',
  176. 'Update Bad': '更新搞唔掂',
  177. 'Upload successful!': '上傳成功喇!',
  178. 'Sync successful!': '同步成功喇!',
  179. 'Download successful!': '下載掂咗喇!',
  180. 'Status ERROR': 'Webdav 狀態搞唔掂',
  181. 'Check Config!': '睇吓設定啱唔啱!',
  182. 'No Data': '雲端咩資料都冇呀!',
  183. 'Bad Download': '下載嘅資料壞咗',
  184. 'Page to Refresh': '资料更新紧系,页面要更新喇!等阵先!',
  185. 'Viewed Total': '瀏覽量',
  186. click_all_magnet: '點擊所有磁力連結',
  187. click_all_torrent: '點擊所有種子連結',
  188. },
  189. },
  190. },
  191. });
  192.  
  193. // i18nnext
  194.  
  195. // Style >> Start
  196. GM_addStyle(`
  197. .nep_um_main{
  198. display: flex;
  199. flex-flow: wrap;
  200. width: 2501px;
  201. }
  202.  
  203. .nep_um_title {
  204. width: 100%;
  205. max-width: 100%;
  206. margin: 1rem;
  207. font-size: 1.7rem;
  208. font-weight: bold;
  209. white-space: nowrap;
  210. overflow: hidden;
  211. text-overflow: ellipsis;
  212. flex-basis: 100%;
  213. }
  214.  
  215. .nep_um_release_date {
  216. width: 100%;
  217. max-width: 100%;
  218. margin: 1rem;
  219. flex-basis: 100%;
  220. }
  221.  
  222. .nep_um_cover {
  223. height: auto;
  224. margin: 1rem;
  225. flex-basis: 100%;
  226. aspect-ratio: 3 / 2;
  227. }
  228.  
  229. .nep_um_cover > img {
  230. width: 100%;
  231. height: auto;
  232. max-width: 938.667px;
  233. }
  234.  
  235. .nep_um_magent {
  236. flex-basis: 100%;
  237. width: 100%;
  238. margin: 1rem;
  239. }
  240.  
  241. .nep_um_torrent {
  242. flex-basis: 100%;
  243. width: 100%;
  244. margin: 1rem;
  245. font-size: 1.2rem;
  246. }
  247.  
  248. .nep_um_divider {
  249. flex-basis: 100%;
  250. width: 100%;
  251. margin: 1rem;
  252. font-size: 1.2rem;
  253. height: 0.4rem;
  254. background-color: black;
  255. filter: grayscale(0.5);
  256. }
  257. `);
  258. // Style >> End
  259.  
  260. // Custom Config >> Start
  261. GM_registerMenuCommand(`${i18next.t('Language')}`, languageConfigDialog);
  262. GM_registerMenuCommand(hideViewedTitle(), viewedModeSwitch);
  263. GM_registerMenuCommand(
  264. `${i18next.t('Jianguoyun Config')}`,
  265. jianguoyunConfigDialog
  266. );
  267. GM_registerMenuCommand(
  268. `${i18next.t('Upload To Jianguoyun')}`,
  269. uploadToJianguoyun
  270. );
  271. GM_registerMenuCommand(
  272. `${i18next.t('Download from Jianguoyun')}`,
  273. downloadFromJianguoyun
  274. );
  275. GM_registerMenuCommand(
  276. `${i18next.t('Local and Jianguoyun merge')}`,
  277. mergeWithJianguoyun
  278. );
  279. GM_registerMenuCommand(
  280. `${i18next.t('Viewed Total')} ${getCurrentViewedAVIDList().length}`
  281. );
  282. GM_registerMenuCommand(`${i18next.t('click_all_magnet')}`, clickAllMagent);
  283. GM_registerMenuCommand(`${i18next.t('click_all_torrent')}`, clickAllTorrent);
  284. // Custom Config >> End
  285. function currentLocale() {
  286. let locale = GM_getValue(LOCALE_KEY);
  287. if (typeof locale == 'string') {
  288. return locale;
  289. }
  290. return LOCALE_LIST.enUS;
  291. }
  292.  
  293. function languageConfigDialog() {
  294. Swal.fire({
  295. title: '🕮 Language',
  296. input: 'select',
  297. inputOptions: {
  298. 'en-US': '🕮 Language',
  299. 'zh-CN': '🕮 语言(简体)',
  300. 'zh-HK': '🕮 語言(港)',
  301. 'zh-TW': '🕮 語言(台)',
  302. },
  303. inputValue: currentLocale(),
  304. showCancelButton: true,
  305. confirmButtonText: `${i18next.t('Save')}`,
  306. }).then((result) => {
  307. if (result.isConfirmed) {
  308. console.log(result);
  309. GM_setValue(LOCALE_KEY, result.value);
  310. i18next.changeLanguage(result.value);
  311. Swal.fire({
  312. icon: 'success',
  313. title: `${i18next.t('Save Successful!')}`,
  314. text: `${i18next.t('Page to Refresh')}`,
  315. });
  316. setTimeout(() => {
  317. document.location.reload();
  318. }, 300);
  319. }
  320. });
  321. }
  322. function hideViewedTitle() {
  323. let enabled = GM_getValue(HIDE_VIEWED_MODE_KEY) ? '✅' : '❌';
  324. return `${enabled} ${i18next.t('Hide Viewed')}`;
  325. }
  326. function viewedModeSwitch() {
  327. var enabled = GM_getValue(HIDE_VIEWED_MODE_KEY);
  328. enabled = !enabled;
  329. GM_setValue(HIDE_VIEWED_MODE_KEY, enabled);
  330. location.reload();
  331. }
  332.  
  333. class JianguoyunClient {
  334. generateConfig(davURL, account, password) {
  335. if (
  336. account == null ||
  337. typeof account != 'string' ||
  338. account.trim().length < 1
  339. ) {
  340. return null;
  341. }
  342. if (
  343. password == null ||
  344. typeof password != 'string' ||
  345. password.trim().length < 1
  346. ) {
  347. return null;
  348. }
  349. if (
  350. davURL == null ||
  351. typeof davURL != 'string' ||
  352. account.trim().length < 1
  353. ) {
  354. return null;
  355. }
  356. return {
  357. auth: `Basic ${btoa(`${account}:${password}`)}`,
  358. davURL: davURL,
  359. };
  360. }
  361.  
  362. getConfig() {
  363. let jianguoyunConfig = getJianguoyunConfig();
  364. if (jianguoyunConfig == null) {
  365. return null;
  366. }
  367. let account = jianguoyunConfig.account;
  368. let password = jianguoyunConfig.password;
  369. let davURL = jianguoyunConfig.url;
  370. return this.generateConfig(davURL, account, password);
  371. }
  372.  
  373. download(fileName) {
  374. let config = this.getConfig();
  375. if (config == null) {
  376. return Promise.reject('Bad Config!');
  377. }
  378. let url = `${config.davURL}/${fileName}.json`.replace(/\/+/g, '/');
  379. return new Promise((res, error) => {
  380. GM_xmlhttpRequest({
  381. method: 'GET',
  382. timeout: 3000,
  383. headers: { Authorization: config.auth },
  384. url: url,
  385. onload: res,
  386. onerror: error,
  387. ontimeout: error,
  388. });
  389. });
  390. }
  391.  
  392. upload(fileName, data) {
  393. let config = this.getConfig();
  394. if (config == null) {
  395. return Promise.reject('Bad Config!');
  396. }
  397. let url = `${config.davURL}/${fileName}.json`.replace(/\/+/g, '/');
  398. return new Promise((res, error) => {
  399. GM_xmlhttpRequest({
  400. method: 'PUT',
  401. timeout: 3000,
  402. data: data,
  403. headers: { Authorization: config.auth },
  404. url: url,
  405. dataType: 'json',
  406. onload: res,
  407. onerror: error,
  408. ontimeout: error,
  409. });
  410. });
  411. }
  412. }
  413. function saveJianguoyunConfig(url, account, password) {
  414. GM_setValue(
  415. JIANGUOYUN_KEY_MD5,
  416. JSON.stringify({ url: url, account: account, password: password })
  417. );
  418. }
  419. function getJianguoyunConfig() {
  420. let jsonStr = GM_getValue(JIANGUOYUN_KEY_MD5);
  421. if (jsonStr == null) {
  422. return null;
  423. }
  424. try {
  425. return JSON.parse(jsonStr);
  426. } catch (_) {
  427. return null;
  428. }
  429. }
  430. function jianguoyunConfigDialog() {
  431. let oldData = getJianguoyunConfig();
  432. let formHTML = `
  433. <label for="${JIANGUOYUN_DAV_URL_EL_ID}">${i18next.t(
  434. 'DAV URL'
  435. )}</label>
  436. <input type="text" id="${JIANGUOYUN_DAV_URL_EL_ID}" name="${JIANGUOYUN_DAV_URL_EL_ID}" class="swal2-input"/>
  437. <br />
  438. <label for="${JIANGUOYUN_ACCOUNT_EL_ID}">${i18next.t(
  439. 'Account'
  440. )}</label>
  441. <input type="text" id="${JIANGUOYUN_ACCOUNT_EL_ID}" name="${JIANGUOYUN_ACCOUNT_EL_ID}" class="swal2-input"/>
  442. <br />
  443. <label for="${JIANGUOYUN_PASSWORD_EL_ID}">${i18next.t(
  444. 'Password'
  445. )}</label>
  446. <input type="password" id="${JIANGUOYUN_PASSWORD_EL_ID}" name="${JIANGUOYUN_PASSWORD_EL_ID}" class="swal2-input"/>
  447. <button id="${JIANGUOYUN_PASSWORD_EL_ID}_button" class="swal2-cancel">${i18next.t(
  448. 'Show Password'
  449. )}</button>
  450. <br />
  451. `;
  452. Swal.fire({
  453. title: `${i18next.t('Jianguoyun Config')}`,
  454. html: formHTML,
  455. showCancelButton: true,
  456. confirmButtonText: `${i18next.t('Save')}`,
  457. preConfirm: () => {
  458. let url = document.getElementById(JIANGUOYUN_DAV_URL_EL_ID).value;
  459. let account = document.getElementById(JIANGUOYUN_ACCOUNT_EL_ID).value;
  460. let password = document.getElementById(JIANGUOYUN_PASSWORD_EL_ID).value;
  461. saveJianguoyunConfig(url, account, password);
  462. },
  463. }).then((result) => {
  464. if (result.isConfirmed) {
  465. Swal.fire({
  466. icon: 'success',
  467. title: `${i18next.t('Save Successful!')}`,
  468. });
  469. }
  470. });
  471. document.getElementById(JIANGUOYUN_DAV_URL_EL_ID).value = oldData.url;
  472. document.getElementById(JIANGUOYUN_ACCOUNT_EL_ID).value = oldData.account;
  473. document.getElementById(JIANGUOYUN_PASSWORD_EL_ID).value = oldData.password;
  474. const passwordInput = document.getElementById(JIANGUOYUN_PASSWORD_EL_ID);
  475. const togglePasswordButton = document.getElementById(
  476. `${JIANGUOYUN_PASSWORD_EL_ID}_button`
  477. );
  478. togglePasswordButton.addEventListener('click', () => {
  479. if (passwordInput.type === 'password') {
  480. passwordInput.type = 'text';
  481. togglePasswordButton.innerText = `${i18next.t('Hidden Password')}`;
  482. } else {
  483. passwordInput.type = 'password';
  484. togglePasswordButton.innerText = `${i18next.t('Show Password')}`;
  485. }
  486. });
  487. }
  488. function uploadToJianguoyun() {
  489. let avIds = getCurrentViewedAVIDList();
  490. let usTitles = getCurrentViewedUSTitleList();
  491. let jianguoyunClient = new JianguoyunClient();
  492. jianguoyunClient
  493. .upload(US_CLOUD_FILE_NAME, JSON.stringify(usTitles))
  494. .then(() => {})
  495. .catch(() => {});
  496. jianguoyunClient
  497. .upload(AVIDS_CLOUD_FILE_NAME, JSON.stringify(avIds))
  498. .then((res) => {
  499. if (res.status >= 200 && res.status < 300) {
  500. Swal.fire({
  501. icon: 'success',
  502. title: `${i18next.t('Upload successful!')}`,
  503. });
  504. } else {
  505. Swal.fire({
  506. icon: 'error',
  507. title: `${i18next.t('Jianguoyun')}`,
  508. text: `${i18next.t('Status Error')} >> ${res.status} >> ${i18next.t(
  509. 'Check Config!'
  510. )}`,
  511. });
  512. }
  513. })
  514. .catch((error) => {
  515. Swal.fire({
  516. icon: 'error',
  517. title: `${i18next.t('Update Bad')}`,
  518. text: JSON.stringify(error),
  519. });
  520. });
  521. }
  522. function downloadFromJianguoyun() {
  523. let jianguoyunClient = new JianguoyunClient();
  524. jianguoyunClient
  525. .download(US_CLOUD_FILE_NAME)
  526. .then((res) => {
  527. if (res === 200) {
  528. if (res.responseText != null && res.responseText.length > 2) {
  529. let cloudUSTitles = JSON.parse(res.responseText);
  530. addListToViewedUSTitle(cloudUSTitles);
  531. }
  532. }
  533. })
  534. .catch(() => {});
  535. jianguoyunClient
  536. .download(AVIDS_CLOUD_FILE_NAME)
  537. .then((res) => {
  538. if (res.status === 200) {
  539. if (res.responseText != null && res.responseText.length > 2) {
  540. let cloudAvIds = JSON.parse(res.responseText);
  541. addListToViewedAVID(cloudAvIds);
  542. }
  543. Swal.fire({
  544. icon: 'success',
  545. title: `${i18next.t('Download successful!')}`,
  546. });
  547. } else if (res.status == 404) {
  548. Swal.fire({
  549. icon: 'error',
  550. title: `${i18next.t('Jianguoyun')} >> ${i18next.t('No Data')}`,
  551. });
  552. } else {
  553. Swal.fire({
  554. icon: 'error',
  555. title: `${i18next.t('Jianguoyun')}`,
  556. text: `${i18next.t('Status Error')} >> ${res.status} >> ${i18next.t(
  557. 'Check Config!'
  558. )}`,
  559. });
  560. }
  561. })
  562. .catch((error) => {
  563. Swal.fire({
  564. icon: 'error',
  565. title: `${i18next.t('Upload Bad')}`,
  566. text: JSON.stringify(error),
  567. });
  568. });
  569. }
  570. async function mergeWithJianguoyunUSTitle() {
  571. let jianguoyunClient = new JianguoyunClient();
  572. let cloudUSTitles = [];
  573. let usBreakFnc = false;
  574. await jianguoyunClient
  575. .download(US_CLOUD_FILE_NAME)
  576. .then((res) => {
  577. if (res.status >= 200 && res.status < 300) {
  578. if (res.responseText != null && res.responseText.length > 2) {
  579. cloudUSTitles = JSON.parse(res.responseText);
  580. }
  581. } else if (res.status == 404) {
  582. } else {
  583. usBreakFnc = true;
  584. }
  585. })
  586. .catch(() => {
  587. usBreakFnc = false;
  588. });
  589. if (usBreakFnc) {
  590. return;
  591. }
  592. let localUSTitles = getCurrentViewedUSTitleList();
  593. let mergeUSTitles = _.union(cloudUSTitles, localUSTitles);
  594. resetViewedUSTitle(mergeUSTitles);
  595. jianguoyunClient
  596. .upload(US_CLOUD_FILE_NAME, JSON.stringify(mergeUSTitles))
  597. .then(() => {})
  598. .catch(() => {});
  599. }
  600. async function mergeWithJianguoyun() {
  601. mergeWithJianguoyunUSTitle();
  602. let jianguoyunClient = new JianguoyunClient();
  603. let cloudAvIds = [];
  604. let breakFnc = false;
  605. await jianguoyunClient
  606. .download(AVIDS_CLOUD_FILE_NAME)
  607. .then((res) => {
  608. if (res.status >= 200 && res.status < 300) {
  609. if (res.responseText != null && res.responseText.length > 2) {
  610. cloudAvIds = JSON.parse(res.responseText);
  611. }
  612. } else if (res.status == 404) {
  613. } else {
  614. Swal.fire({
  615. icon: 'error',
  616. title: `${i18next.t('Jianguoyun')} >> ${i18next.t(
  617. 'Status Error'
  618. )} >> ${res.status} >> ${i18next.t('Check Config!')}`,
  619. });
  620. breakFnc = true;
  621. }
  622. })
  623. .catch(() => {
  624. breakFnc = false;
  625. });
  626. if (breakFnc) {
  627. Swal.fire({
  628. icon: 'error',
  629. title: `${i18next.t('Jianguoyun')} >> ${i18next.t('Bad Download')}`,
  630. });
  631. return;
  632. }
  633. let localAvIds = getCurrentViewedAVIDList();
  634. let mergeAvIds = _.union(cloudAvIds, localAvIds);
  635. resetViewedAVID(mergeAvIds);
  636. jianguoyunClient
  637. .upload(AVIDS_CLOUD_FILE_NAME, JSON.stringify(mergeAvIds))
  638. .then((res) => {
  639. if (res.status >= 200 && res.status < 300) {
  640. Swal.fire({
  641. icon: 'success',
  642. title: `${i18next.t('Sync successful!')}`,
  643. });
  644. } else {
  645. Swal.fire({
  646. icon: 'error',
  647. title: `${i18next.t('Jianguoyun')} >> ${i18next.t(
  648. 'Status Error'
  649. )} >> ${res.status} >> ${i18next.t('Check Config!')}`,
  650. });
  651. }
  652. })
  653. .catch((error) => {
  654. Swal.fire({
  655. icon: 'error',
  656. title: `${i18next.t('Upload Bad')}`,
  657. text: JSON.stringify(error),
  658. });
  659. });
  660. }
  661.  
  662. function getCurrentViewedAVIDList() {
  663. let localList = GM_getValue(VIEWED_AVIDS_LOCAL_STORAGE_KEY) || '[]';
  664. return JSON.parse(localList);
  665. }
  666.  
  667. function getCurrentViewedUSTitleList() {
  668. let localList = GM_getValue(VIEWED_US_TITLES_LOCAL_STORAGE_KEY) || '[]';
  669. return JSON.parse(localList);
  670. }
  671.  
  672. function exitsViewedAVID(avId) {
  673. return getCurrentViewedAVIDList().includes(avId);
  674. }
  675.  
  676. function exitsViewedUSTitle(title) {
  677. return getCurrentViewedUSTitleList().includes(title);
  678. }
  679.  
  680. function addToViewedAVID(avId) {
  681. let oldIdList = getCurrentViewedAVIDList();
  682. if (oldIdList.includes(avId)) {
  683. return;
  684. }
  685. oldIdList.push(avId);
  686. let newValue = JSON.stringify(oldIdList);
  687. GM_setValue(VIEWED_AVIDS_LOCAL_STORAGE_KEY, newValue);
  688. }
  689.  
  690. function addToViewedUSTitle(title) {
  691. let oldIdList = getCurrentViewedUSTitleList();
  692. if (oldIdList.includes(title)) {
  693. return;
  694. }
  695. oldIdList.push(title);
  696. let newValue = JSON.stringify(oldIdList);
  697. GM_setValue(VIEWED_US_TITLES_LOCAL_STORAGE_KEY, newValue);
  698. }
  699.  
  700. function addListToViewedAVID(avIds) {
  701. let oldList = getCurrentViewedAVIDList();
  702. let newList = _.union(oldList, avIds);
  703. let newValue = JSON.stringify(newList);
  704. GM_setValue(VIEWED_AVIDS_LOCAL_STORAGE_KEY, newValue);
  705. }
  706.  
  707. function addListToViewedUSTitle(titles) {
  708. let oldList = getCurrentViewedUSTitleList();
  709. let newList = _.union(oldList, titles);
  710. let newValue = JSON.stringify(newList);
  711. GM_setValue(VIEWED_US_TITLES_LOCAL_STORAGE_KEY, newValue);
  712. }
  713.  
  714. function resetViewedAVID(avIds) {
  715. if (avIds == null || avIds.length < 1) {
  716. return;
  717. }
  718. let newValue = JSON.stringify(avIds);
  719. GM_setValue(VIEWED_AVIDS_LOCAL_STORAGE_KEY, newValue);
  720. }
  721.  
  722. function resetViewedUSTitle(titles) {
  723. if (titles == null || titles.length < 1) {
  724. return;
  725. }
  726. let newValue = JSON.stringify(titles);
  727. GM_setValue(VIEWED_US_TITLES_LOCAL_STORAGE_KEY, newValue);
  728. }
  729.  
  730. function elOpenUrl(elBody) {
  731. let elUrl = null;
  732. if (elBody) {
  733. let subUrl = null;
  734. let subUrlEl = elBody.querySelector('td > td.icon > a');
  735. if (!subUrlEl) {
  736. subUrlEl = elBody.querySelector('tr > th.common > a.s.xst');
  737. }
  738. if (!subUrlEl) {
  739. subUrlEl = elBody.querySelector('tr > th.new > a.s.xst');
  740. }
  741. if (subUrlEl) {
  742. subUrl = subUrlEl.href;
  743. }
  744. if (subUrl) {
  745. elUrl = subUrl;
  746. }
  747. }
  748. return elUrl;
  749. }
  750. function elAllArray() {
  751. return Array.from(
  752. document.querySelectorAll('[id^="normalthread_"]')
  753. ).filter(function (element) {
  754. if (/^normalthread_\d+$/.test(element.id)) {
  755. return element;
  756. }
  757. });
  758. }
  759. function elCutLastArray() {
  760. let allArray = elAllArray();
  761. let lastArray = null;
  762. if (allArray && allArray.length > 0) {
  763. let nowArrayLength = allArray.length;
  764. if (elArrayLength <= 0) {
  765. lastArray = allArray;
  766. } else {
  767. lastArray = allArray.slice(elArrayLength);
  768. }
  769. elArrayLength = nowArrayLength;
  770. }
  771. return lastArray;
  772. }
  773. function elSubPageImageEl(subPageHTML) {
  774. if (subPageHTML) {
  775. let _el = subPageHTML.querySelector('ignore_js_op > img');
  776. if (_el) {
  777. return `<img src="${_el.getAttribute(
  778. 'zoomFile'
  779. )}" style="width:100%; max-with:300px; height: auto;"/>`;
  780. }
  781. }
  782. return null;
  783. }
  784.  
  785. function elSubPageMagentEl(subPageHTML) {
  786. if (subPageHTML) {
  787. let magentEl = subPageHTML.querySelector('.blockcode > div > ol > li');
  788. if (magentEl) {
  789. return `<a href="${magentEl.innerText}" style="text-decoration:underline;">${magentEl.innerText}</a>`;
  790. }
  791. }
  792. return null;
  793. }
  794.  
  795. function elSubPageTorrentEl(subPageHTML) {
  796. if (subPageHTML) {
  797. return subPageHTML.querySelector(
  798. 'div.pattl > ignore_js_op > dl > dd > p.attnm '
  799. );
  800. }
  801. return null;
  802. }
  803.  
  804. function elPostReleaseDate(elBody) {
  805. let releaseDate = 'Unknown';
  806. if (elBody) {
  807. let _el = elBody.querySelector('tr > td:nth-child(3) > em > span > span');
  808. if (_el) {
  809. releaseDate = _el.innerText;
  810. }
  811. if (_el == null || _el.innerText == '') {
  812. _el = elBody.querySelector('tr > td:nth-child(3) > em > span');
  813. releaseDate = _el.innerText;
  814. }
  815. }
  816. return releaseDate;
  817. }
  818.  
  819. function generateJAVDBURLEl(avId, title) {
  820. return `<a href='https://${JAVDB_COM}/search?q=${avId}&f=all' target='_blank' referrerpolicy='same-origin'>${avId} ${title}</a>`;
  821. }
  822. function elTitleDiv(elBody) {
  823. let title = 'Unknown';
  824. if (elBody) {
  825. let titleEl = elBody.querySelector('tr > th.common > a.s.xst');
  826. if (!titleEl) {
  827. titleEl = elBody.querySelector('tr > th.new > a.s.xst');
  828. }
  829. if (titleEl) {
  830. title = titleEl.innerText.replace(/<savdiv[^>]*>.*?<\/savdiv>/g, '');
  831. }
  832. }
  833. let avIdArray = title.match(AVID_REGEX);
  834. let avId = null;
  835. if (avIdArray && avIdArray.length > 0) {
  836. avId = avIdArray[0];
  837. let _title = title.replace(AVID_REGEX, '');
  838. title = generateJAVDBURLEl(avId, _title);
  839. }
  840. let titleDiv = document.createElement('div');
  841. titleDiv.classList.add('nep_um_title');
  842. titleDiv.innerHTML = title;
  843. let isUS = false;
  844. let isUSViewed = false;
  845. if (avId) {
  846. if (exitsViewedAVID(avId)) {
  847. titleDiv.querySelector('a').style.color = VIEWED_COLOR;
  848. if (GM_getValue(HIDE_VIEWED_MODE_KEY)) {
  849. return avId;
  850. }
  851. } else {
  852. titleDiv.addEventListener('click', function () {
  853. addToViewedAVID(avId);
  854. titleDiv.querySelector('a').style.color = VIEWED_COLOR;
  855. });
  856. }
  857. } else {
  858. isUS = true;
  859. titleDiv.innerHTML = `<a>${title}</a> `;
  860. if (exitsViewedUSTitle(title)) {
  861. isUSViewed = true;
  862. titleDiv.querySelector('a').style.color = VIEWED_COLOR;
  863. if (GM_getValue(HIDE_VIEWED_MODE_KEY)) {
  864. titleDiv.innerHTML = `<a>${title} >> ${i18next.t(
  865. 'Hide Viewed'
  866. )}</a> `;
  867. }
  868. }
  869. }
  870.  
  871. return {
  872. div: titleDiv,
  873. id: avId,
  874. isUS: isUS,
  875. isUSViewed: isUSViewed,
  876. title: title,
  877. };
  878. }
  879.  
  880. function elTranTitleDivStyle(div, avId) {
  881. if (avId) {
  882. addToViewedAVID(avId);
  883. div.querySelector('a').style.color = VIEWED_COLOR;
  884. }
  885. }
  886. function elTranTitleDivStyleForUSTitle(div, title) {
  887. if (title) {
  888. addToViewedUSTitle(title);
  889. div.querySelector('a').style.color = VIEWED_COLOR;
  890. }
  891. }
  892.  
  893. /* toRequestSubURL */
  894. function elConvertElBody(elBody) {
  895. let openPageUrl = elOpenUrl(elBody);
  896.  
  897. if (!openPageUrl) {
  898. return null;
  899. }
  900.  
  901. // main
  902. let mainDiv = document.createElement('div');
  903. mainDiv.classList.add('nep_um_main');
  904. mainDiv.id = 'nep_um_main';
  905.  
  906. // title
  907. let titleDivAndId = elTitleDiv(elBody);
  908. if (typeof titleDivAndId === 'string') {
  909. let hiddenDiv = createHiddenDiv(
  910. titleDivAndId,
  911. `>> ${i18next.t('Hide Viewed')}`
  912. );
  913. mainDiv.append(hiddenDiv);
  914. elBody.innerHTML = '';
  915. elBody.appendChild(mainDiv);
  916. return;
  917. }
  918.  
  919. let isUS = titleDivAndId.isUS;
  920. let titleDiv = titleDivAndId.div;
  921. let avId = titleDivAndId.id;
  922. let title = titleDivAndId.title;
  923. let isUSViewed = titleDivAndId.isUSViewed;
  924.  
  925. if (isUS && isUSViewed) {
  926. let hiddenDiv = createHiddenDiv(
  927. openPageUrl,
  928. title,
  929. `>> ${i18next.t('Hide Viewed')}`
  930. );
  931. mainDiv.append(hiddenDiv);
  932. elBody.innerHTML = '';
  933. elBody.appendChild(mainDiv);
  934. return;
  935. }
  936.  
  937. if (isUS) {
  938. titleDiv.onclick = function () {
  939. elTranTitleDivStyleForUSTitle(titleDiv, title);
  940. window.open(openPageUrl, '_blank');
  941. };
  942. }
  943.  
  944. mainDiv.appendChild(titleDiv);
  945.  
  946. // releaseDate
  947. let releaseDate = elPostReleaseDate(elBody);
  948. let releaseDateDiv = document.createElement('div');
  949. releaseDateDiv.classList.add('nep_um_release_date');
  950. releaseDateDiv.innerText = releaseDate;
  951. mainDiv.appendChild(releaseDateDiv);
  952.  
  953. GM_xmlhttpRequest({
  954. method: 'GET',
  955. url: openPageUrl,
  956. headers: {
  957. 'User-agent': USER_AGENT,
  958. Accept: ACCEPT,
  959. cookie: COOKIE,
  960. referer: REFERER,
  961. },
  962. onerror: function (error) {
  963. console.error('Error fetching data:', error);
  964. elBody.innerHTML = '';
  965. elBody.appendChild(mainDiv);
  966. },
  967. onload: function (res) {
  968. let parser = new DOMParser();
  969. let subPageHTML = parser.parseFromString(res.responseText, 'text/html');
  970. let imgEl = elSubPageImageEl(subPageHTML);
  971. let magentEl = elSubPageMagentEl(subPageHTML);
  972. let torrentEl = elSubPageTorrentEl(subPageHTML);
  973. if (imgEl && (magentEl || torrentEl)) {
  974. // cover
  975. let imgDiv = document.createElement('div');
  976. imgDiv.onclick = function () {
  977. window.open(openPageUrl, '_blank');
  978. if (isUS) {
  979. elTranTitleDivStyleForUSTitle(titleDiv, title);
  980. } else {
  981. elTranTitleDivStyle(titleDiv, avId);
  982. }
  983. };
  984. imgDiv.classList.add('nep_um_cover');
  985. imgDiv.innerHTML = imgEl;
  986. mainDiv.appendChild(imgDiv);
  987.  
  988. // magent
  989. let magentDiv = document.createElement('div');
  990. magentDiv.onclick = function () {
  991. if (isUS) {
  992. elTranTitleDivStyleForUSTitle(titleDiv, title);
  993. } else {
  994. elTranTitleDivStyle(titleDiv, avId);
  995. }
  996. };
  997. magentDiv.classList.add('nep_um_magent');
  998. if (magentEl != null) {
  999. magentDiv.innerHTML = magentEl;
  1000. mainDiv.appendChild(magentDiv);
  1001. }
  1002.  
  1003. // torrent
  1004. let torrentDiv = document.createElement('div');
  1005. torrentDiv.onclick = function () {
  1006. if (isUS) {
  1007. elTranTitleDivStyleForUSTitle(titleDiv, title);
  1008. } else {
  1009. elTranTitleDivStyle(titleDiv, avId);
  1010. }
  1011. };
  1012. torrentDiv.classList.add('nep_um_torrent');
  1013. if (torrentEl?.outerHTML != null) {
  1014. torrentDiv.innerHTML = torrentEl.outerHTML;
  1015. mainDiv.appendChild(torrentDiv);
  1016. }
  1017.  
  1018. // divider
  1019. let dividerDiv = document.createElement('div');
  1020. dividerDiv.classList.add('nep_um_divider');
  1021. mainDiv.appendChild(dividerDiv);
  1022.  
  1023. elBody.innerHTML = '';
  1024. elBody.appendChild(mainDiv);
  1025. }
  1026. },
  1027. });
  1028. }
  1029.  
  1030. function createHiddenDiv(url, innerText, additionalText = '') {
  1031. let hiddenDiv = document.createElement('div');
  1032. hiddenDiv.innerHTML = `<a href='${url}'>${innerText} ${additionalText}</a>`;
  1033. hiddenDiv.style.color = VIEWED_COLOR;
  1034. hiddenDiv.style.fontSize = '0.5rem';
  1035. hiddenDiv.style.textAlign = 'center';
  1036. hiddenDiv.style.width = '100%';
  1037. hiddenDiv.style.paddingBottom = '0.5rem';
  1038. return hiddenDiv;
  1039. }
  1040.  
  1041. function clickAllMagent() {
  1042. let allMagentDiv = document.querySelectorAll('.nep_um_magent');
  1043. let allMagentText = '';
  1044. for (let i = 0; i < allMagentDiv.length; i++) {
  1045. allMagentText += `\r\n${allMagentDiv[i].innerText}`;
  1046. allMagentDiv[i].click();
  1047. }
  1048. GM_setClipboard(allMagentText);
  1049. }
  1050.  
  1051. function clickAllTorrent() {
  1052. let allTorrentDiv = document.querySelectorAll('.nep_um_torrent');
  1053. let allTorrentText = '';
  1054. for (let i = 0; i < allTorrentDiv.length; i++) {
  1055. allTorrentText += `\r\n${allTorrentDiv[i].innerText}`;
  1056. allTorrentDiv[i].click();
  1057. }
  1058. GM_setClipboard(allTorrentText);
  1059. }
  1060.  
  1061. function run() {
  1062. let modifyElArray = elCutLastArray();
  1063. if (modifyElArray && modifyElArray.length > 0) {
  1064. modifyElArray.forEach((_el) => {
  1065. try {
  1066. elConvertElBody(_el);
  1067. } catch (error) {
  1068. console.error('Error in elConvertElBody:', error);
  1069. }
  1070. });
  1071. }
  1072. }
  1073.  
  1074. const runDebounce = _.debounce(run, 3000, { maxWait: 8000 });
  1075.  
  1076. const observer_list = new MutationObserver((mutations) => {
  1077. mutations.forEach((mutation) => {
  1078. if (mutation.type === 'childList') {
  1079. runDebounce();
  1080. }
  1081. });
  1082. });
  1083.  
  1084. const observerConfig_list = { childList: true, subtree: true };
  1085. observer_list.observe(document.body, observerConfig_list);
  1086.  
  1087. const wpDiv = document.querySelector('#wp');
  1088.  
  1089. const observer_width = new ResizeObserver((entries) => {
  1090. entries.forEach((entry) => {
  1091. if (entry.target === wpDiv) {
  1092. const newWidth = wpDiv.clientWidth;
  1093. const childDivs = wpDiv.querySelectorAll('#nep_um_main');
  1094. childDivs.forEach((childDiv) => {
  1095. childDiv.style.width = `${newWidth}px`;
  1096. });
  1097. }
  1098. });
  1099. });
  1100. observer_width.observe(wpDiv);
  1101.  
  1102. // init
  1103. run();
  1104. document.querySelector('#threadlisttableid').style.width = '100%';
  1105. })();