wnacg-Viewer

wnacg-Viewer 功能:1. 書架管理—快速加入/移除書架;2. 幻燈片模式—自動切換,優化圖片載入;3. 專輯鏈接自動更新—連結至下拉閱讀;4. 關鍵字搜尋—輕鬆查找相關作品。

  1. // ==UserScript==
  2. // @name wnacg-Viewer
  3. // @description wnacg-Viewer 功能:1. 書架管理—快速加入/移除書架;2. 幻燈片模式—自動切換,優化圖片載入;3. 專輯鏈接自動更新—連結至下拉閱讀;4. 關鍵字搜尋—輕鬆查找相關作品。
  4. // @version 2.6.2
  5. // @author MrDaDaDo
  6. // @match https://wnacg.com/*
  7. // @match https://www.wnacg.com/*
  8. // @run-at document-end
  9. // @require https://code.jquery.com/jquery-3.3.1.min.js
  10. // @namespace https://github.com/MrDaDaDo/wnacg-Viewer
  11. // @license GPL-3.0-or-later
  12. // ==/UserScript==
  13.  
  14. (() => {
  15. const $ = window.jQuery;
  16.  
  17. const genFavBtnId = favId => `add-to-fav-btn-${favId}`;
  18.  
  19. const getAid = () => window.location.href.match(/photos-slide-aid-(\d+)\.html/)[1];
  20.  
  21. const addToFav = favId => {
  22. const aid = getAid();
  23. $.post(`https://www.wnacg.com/users-save_fav-id-${aid}.html`, { favc_id: favId })
  24. .done(() => {
  25. const $btn = $(`#${genFavBtnId(favId)}`);
  26. $btn.addClass('cur');
  27. alert('加入書架成功');
  28. setIsInFav(aid, favId);
  29. })
  30. .fail(() => {
  31. alert('加入書架失敗');
  32. });
  33. };
  34.  
  35. const deleteFav = (favId, link) => {
  36. const postData = {
  37. ajax: true,
  38. _t: Math.random(),
  39. };
  40. $.post(link, postData)
  41. .done(() => {
  42. alert('移出書架成功');
  43. const $btn = $(`#${genFavBtnId(favId)}`);
  44. $btn.removeClass('cur');
  45. $btn.off('click').on('click', () => addToFav(favId));
  46. })
  47. .fail(() => {
  48. alert('移出書架失敗');
  49. });
  50. };
  51.  
  52. const setIsInFav = async (aid, favId, page = 1) => {
  53. const data = await $.get(`https://www.wnacg.com/users-users_fav-page-${page}-c-${favId}.html`);
  54. if (data.indexOf(`photos-index-aid-${aid}.html`) >= 0) {
  55. const $btn = $(`#${genFavBtnId(favId)}`);
  56. $btn.addClass('cur');
  57. $btn.off('click');
  58. const $data = $(data);
  59. $data.find('.box_cel.u_listcon').each(function () {
  60. const $div = $(this);
  61. const $targetLink = $div.find(`a[href*='photos-index-aid-${aid}.html']`);
  62. if ($targetLink.length > 0) {
  63. const onclickValue = $div.find("a[onclick*='Mui.box.show']").attr('onclick');
  64. const deleteLinkMatch = onclickValue.match(/Mui\.box\.show\('(.+?)'\)/);
  65. if (deleteLinkMatch && deleteLinkMatch[1]) {
  66. const deleteLink = deleteLinkMatch[1];
  67. $btn.on('click', () => deleteFav(favId, deleteLink));
  68. }
  69. }
  70. });
  71. } else if (data.indexOf('>後頁') >= 0) {
  72. setIsInFav(aid, favId, page + 1);
  73. } else {
  74. const $btn = $(`#${genFavBtnId(favId)}`);
  75. $btn.off('click').on('click', () => addToFav(favId));
  76. }
  77. };
  78.  
  79. const initAddToFavBtn = async ($parent, aid) => {
  80. const data = await $.get('https://www.wnacg.com/users-users_fav.html');
  81. const regex = /<label class="nav_label">書架分類:<\/label>([\s\S]*?)<a class="btn_blue" href="\/\?ctl=users&act=favclass">管理分類<\/a>/;
  82. const match = data.match(regex);
  83. const favHtml = match[1];
  84. const favRegex = /users-users_fav-c-(\d+).html ">(.*?)</g;
  85. let favMatch;
  86. while ((favMatch = favRegex.exec(favHtml)) !== null) {
  87. const [favId, favName] = [favMatch[1], favMatch[2]];
  88. const btnId = genFavBtnId(favId);
  89. const $btn = $(`<a id="${btnId}">${favName}</a>`);
  90. $parent.append($btn);
  91. $btn.css('cursor', 'pointer');
  92. $(`#${btnId}`).click(() => addToFav(favId));
  93. setIsInFav(getAid(), favId, aid);
  94. }
  95. };
  96.  
  97. const genImageDivHtml = (imageSrc, index, total) => `
  98. <div style="text-align:center;color:#999;padding-bottom:10px;font-size:13px;">
  99. <img src="${imageSrc}" width="960px"><br>
  100. <span>${index}/${total}</span>
  101. </div>
  102. `;
  103.  
  104. const viewSlide = imageSrcList => {
  105. $('#shareBox, #control_block, #mask_panel, #cite_vote, #page_scale, .header, .footer').remove();
  106. const $parent = $('#img_list').parent();
  107. $('#img_list, #img_load').remove();
  108. const $favLabel = $('<label class="nav_list" style="display: block; text-align: center; margin: 0 auto;"></label>');
  109. $parent.append($favLabel);
  110. initAddToFavBtn($favLabel);
  111. const $imgDiv = $('<div id="wnacg-viewer-img-list"></div>');
  112. $parent.append($imgDiv);
  113. const title = document.title.replace(' - 列表 - 紳士漫畫-專註分享漢化本子|邪惡漫畫', '');
  114. const $titleDiv = $('<div id="wnacg-viewer-title" style="text-align:center;color:#999;padding-bottom:10px;font-size:26px;"></div>');
  115. const regex = /\[([^\[\]]+?)\]/g;
  116. let lastIndex = 0;
  117. let match;
  118. while ((match = regex.exec(title)) !== null) {
  119. if (match.index > lastIndex) {
  120. $titleDiv.append(title.slice(lastIndex, match.index));
  121. }
  122. const searchKeyword = match[1];
  123. const $link = $(`<a href="https://www.wnacg.com/search/?q=${searchKeyword}&f=_all&s=create_time_DESC&syn=yes" target="_blank" style="color: #4A90E2;">${searchKeyword}</a>`);
  124. $titleDiv.append('[').append($link).append(']');
  125. lastIndex = regex.lastIndex;
  126. }
  127. if (lastIndex < title.length) {
  128. $titleDiv.append(title.slice(lastIndex));
  129. }
  130. $imgDiv.append($titleDiv);
  131.  
  132. const batchSize = 10;
  133. let currentBatch = 0;
  134. const loadNextBatch = () => {
  135. const startIndex = currentBatch * batchSize;
  136. const endIndex = Math.min(startIndex + batchSize, imageSrcList.length);
  137. for (let i = startIndex; i < endIndex; i++) {
  138. $imgDiv.append(genImageDivHtml(imageSrcList[i], i + 1, imageSrcList.length));
  139. }
  140. currentBatch++;
  141. if (startIndex < imageSrcList.length) {
  142. setTimeout(loadNextBatch, 2000);
  143. }
  144. };
  145. loadNextBatch();
  146. };
  147.  
  148. const goSlide = () => {
  149. const photosGalleryScriptUrl = location.href.replace('photos-slide-aid', 'photos-gallery-aid');
  150. $.get(photosGalleryScriptUrl, scripts => {
  151. const imageSrcList = scripts.split('\n')
  152. .filter(script => script.includes('var imglist'))
  153. .flatMap(script => script.match(/\/\/[^"]+/gm).map(urlString => urlString.replace('\\', '')));
  154. viewSlide(imageSrcList);
  155. });
  156. };
  157.  
  158. const goAlbums = () => {
  159. $('.pic_box a').each((index, element) => {
  160. const $linkElement = $(element);
  161. const href = $linkElement.attr('href');
  162. $linkElement.attr('href', href.replace('index', 'slide'));
  163. });
  164. };
  165.  
  166. const url = window.location.href;
  167. if (url === 'https://www.wnacg.com/' || url.startsWith('https://www.wnacg.com/albums') || url.startsWith('https://www.wnacg.com/search')) {
  168. goAlbums();
  169. } else if (url.startsWith('https://www.wnacg.com/photos-slide-aid')) {
  170. goSlide();
  171. }
  172. })();