Pixiv Infinite Scroll

为 Pixiv 添加无限滚动功能。

目前为 2023-08-29 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Pixiv Infinite Scroll
  3. // @name:ja Pixiv Infinite Scroll
  4. // @name:zh-CN Pixiv Infinite Scroll
  5. // @name:zh-TW Pixiv Infinite Scroll
  6. // @namespace https://github.com/chimaha/Pixiv-Infinite-Scroll
  7. // @match https://www.pixiv.net/*
  8. // @grant none
  9. // @version 1.3
  10. // @author chimaha
  11. // @description Add infinite scroll feature to Pixiv.
  12. // @description:ja Pixivに無限スクロール機能を追加します。
  13. // @description:zh-CN 为 Pixiv 添加无限滚动功能。
  14. // @description:zh-TW 因為Pixiv有無限移動功能。
  15. // @license MIT license
  16. // @icon https://www.pixiv.net/favicon.ico
  17. // @supportURL https://github.com/chimaha/Pixiv-Infinite-Scroll/issues
  18. // ==/UserScript==
  19.  
  20. /*! Pixiv Infinite Scroll | MIT license | https://github.com/chimaha/Pixiv-Infinite-Scroll/blob/main/LICENSE */
  21.  
  22. // フォロー中の無限スクロール-----------------------------------------------------------------
  23. function following_process() {
  24.  
  25. // langの値によって言語を変更する
  26. const setFollowLanguage = [];
  27. const currentLanguage = document.querySelector("html").getAttribute("lang");
  28. switch (currentLanguage) {
  29. case "ja":
  30. setFollowLanguage.push("フォロー中", "フォローする");
  31. break;
  32. case "ko":
  33. setFollowLanguage.push("팔로우 중", "팔로우하기");
  34. break;
  35. case "zh-CN":
  36. setFollowLanguage.push("已关注", "加关注");
  37. break;
  38. case "zh-TW":
  39. setFollowLanguage.push("關注中", "加關注");
  40. break;
  41. default:
  42. setFollowLanguage.push("Following", "Follow");
  43. }
  44.  
  45. function createElement(userId, userName, userProfileImage, userComment, userFollowing, illustId, illustTitle, illustUrl, illustBookmarkData, illustAlt, illustR18, illustPageCount) {
  46.  
  47. // フォロー中・フォローするを切り替え
  48. let changeFollowLanguage;
  49. let followClass;
  50. let followStyle = "";
  51. if (userFollowing) {
  52. changeFollowLanguage = setFollowLanguage[0];
  53. followClass = "cnpwVx";
  54. followStyle = 'style="background-color: var(--charcoal-surface3); color: var(--charcoal-text2); font-weight: bold; padding-right: 24px; padding-left: 24px; border-radius: 999999px; height: 40px;"';
  55. } else {
  56. changeFollowLanguage = setFollowLanguage[1];
  57. followClass = "fOWAlD";
  58. }
  59. // ブックマークを切り替え
  60. let bookmarkClass = [];
  61. let bookmarkStyle = [];
  62. for (const checkBookmark of illustBookmarkData) {
  63. if (checkBookmark) {
  64. bookmarkClass.push("bXjFLc");
  65. bookmarkStyle.push('style="color: rgb(255, 64, 96); fill: currentcolor;"');
  66. } else {
  67. bookmarkClass.push("dxYRhf");
  68. }
  69. }
  70. // コメントに特定の記号が入っていた場合にエスケープ
  71. function escapleText(userComment) {
  72. return userComment
  73. .replace(/&/g, "&")
  74. .replace(/</g, "&lt;")
  75. .replace(/>/g, "&gt;")
  76. .replace(/"/g, "&quot;")
  77. .replace(/'/g, "&#039;");
  78. }
  79. const escapedComment = escapleText(userComment);
  80.  
  81. // R18マーク
  82. let r18Element = [];
  83. for (const checkR18 of illustR18) {
  84. if (checkR18 == "R-18") {
  85. r18Element.push('<div class="sc-rp5asc-15 cIllir"><div class="sc-1ovn4zb-0 bfWaOT">R-18</div></div>');
  86. } else {
  87. r18Element.push("");
  88. }
  89. }
  90.  
  91. // うごくイラスト再生マーク。イラスト枚数表示
  92. let ugoiraElement = [];
  93. let pageCountElement = [];
  94. illustPageCount.forEach((pageCount, i) => {
  95. if (illustAlt[i].slice(-4) == "うごイラ") {
  96. ugoiraElement.push('<svg viewBox="0 0 24 24" style="width: 48px; height: 48px; position: absolute;" class="sc-192k5ld-0 etaMpt sc-rp5asc-8 kSDUsv"><circle cx="12" cy="12" r="10" class="sc-192k5ld-1 lajlxF" style="fill: rgba(0, 0, 0, 0.32);"></circle><path d="M9,8.74841664 L9,15.2515834 C9,15.8038681 9.44771525,16.2515834 10,16.2515834 C10.1782928,16.2515834 10.3533435,16.2039156 10.5070201,16.1135176 L16.0347118,12.8619342 C16.510745,12.5819147 16.6696454,11.969013 16.3896259,11.4929799 C16.3034179,11.3464262 16.1812655,11.2242738 16.0347118,11.1380658 L10.5070201,7.88648243 C10.030987,7.60646294 9.41808527,7.76536339 9.13806578,8.24139652 C9.04766776,8.39507316 9,8.57012386 9,8.74841664 Z" class="sc-192k5ld-2 jwyUTl" style="fill: rgb(255, 255, 255);"></path></svg>');
  97. pageCountElement.push("");
  98. } else {
  99. ugoiraElement.push("");
  100. if (pageCount >= 2) {
  101. pageCountElement.push(`
  102. <div class="sc-rp5asc-5 hHNegy">
  103. <div class="sc-1mr081w-0 kZlOCw">
  104. <span class="sc-1mr081w-1 gODLwk">
  105. <span class="sc-14heosd-0 gbNjFx">
  106. <svg viewBox="0 0 9 10" size="9" class="sc-14heosd-1 fArvVr">
  107. <path d="M8,3 C8.55228475,3 9,3.44771525 9,4 L9,9 C9,9.55228475 8.55228475,10 8,10 L3,10 C2.44771525,10 2,9.55228475 2,9 L6,9 C7.1045695,9 8,8.1045695 8,7 L8,3 Z M1,1 L6,1 C6.55228475,1 7,1.44771525 7,2 L7,7 C7,7.55228475 6.55228475,8 6,8 L1,8 C0.44771525,8 0,7.55228475 0,7 L0,2 C0,1.44771525 0.44771525,1 1,1 Z" transform=""></path>
  108. </svg>
  109. </span>
  110. </span>
  111. <span>${pageCount}</span>
  112. </div>
  113. </div>`);
  114. } else {
  115. pageCountElement.push("");
  116. }
  117. }
  118. });
  119.  
  120. // イラストがない場合は表示しないようにするため、分けて作成する
  121. let illustGroup = "";
  122. for (let i = 0; i < illustId.length; i++) {
  123. illustGroup += `
  124. <div class="sc-w2rqc8-2 bgeGyS">
  125. <div class="sc-fgp4rp-1 cQUnPX">
  126. <div class="sc-iasfms-5 liyNwX">
  127. <div type="illust" size="184" class="sc-iasfms-3 iIcDMF">
  128. <div width="184" height="184" class="sc-rp5asc-0 fxGVAF addBookmark">
  129. <a class="sc-d98f2c-0 sc-rp5asc-16 iUsZyY sc-eWnToP khjDVZ" data-gtm-value="${illustId[i]}" data-gtm-user-id="${userId}" href="/artworks/${illustId[i]}">
  130. <div radius="4" class="sc-rp5asc-9 cYUezH">
  131. <img src="${illustUrl[i]}" style="object-fit: cover; object-position: center center;" alt="${illustAlt[i]}" class="sc-rp5asc-10 erYaF">
  132. ${ugoiraElement[i]}
  133. </div>
  134. <div class="sc-rp5asc-12 Sxcoo">
  135. <div class="sc-rp5asc-13 liXhix">${r18Element[i]}</div>
  136. ${pageCountElement[i]}
  137. </div>
  138. </a>
  139. <div class="sc-iasfms-4 iHfghO">
  140. <div class="">
  141. <button type="button" class="sc-kgq5hw-0 fgVkZi">
  142. <svg viewBox="0 0 32 32" width="32" height="32" class="sc-j89e3c-1 ${bookmarkClass[i]}" ${bookmarkStyle[i]}>
  143. <path d="M21,5.5 C24.8659932,5.5 28,8.63400675 28,12.5 C28,18.2694439 24.2975093,23.1517313 17.2206059,27.1100183 C16.4622493,27.5342993 15.5379984,27.5343235 14.779626,27.110148 C7.70250208,23.1517462 4,18.2694529 4,12.5 C4,8.63400691 7.13400681,5.5 11,5.5 C12.829814,5.5 14.6210123,6.4144028 16,7.8282366 C17.3789877,6.4144028 19.170186,5.5 21,5.5 Z"></path>
  144. <path d="M16,11.3317089 C15.0857201,9.28334665 13.0491506,7.5 11,7.5 C8.23857625,7.5 6,9.73857647 6,12.5 C6,17.4386065 9.2519779,21.7268174 15.7559337,25.3646328 C15.9076021,25.4494645 16.092439,25.4494644 16.2441073,25.3646326 C22.7480325,21.7268037 26,17.4385986 26,12.5 C26,9.73857625 23.7614237,7.5 21,7.5 C18.9508494,7.5 16.9142799,9.28334665 16,11.3317089 Z" class="sc-j89e3c-0 dUurgf"></path>
  145. </svg>
  146. </button>
  147. </div>
  148. </div>
  149. </div>
  150. </div>
  151. <div class="sc-iasfms-0 jtpclu">
  152. <a class="sc-d98f2c-0 sc-iasfms-6 gqlfsh" href="/artworks/${illustId[i]}">${illustTitle[i]}</a>
  153. </div>
  154. </div>
  155. </div>
  156. </div>`;
  157. }
  158.  
  159. let illustContainer = "";
  160. if (illustId[0]) {
  161. illustContainer = `
  162. <div class="sc-11m5zdr-2 gClOXE">
  163. <div>
  164. <div class="sc-1kr69jw-2 hYTIUt">
  165. <div class="sc-1kr69jw-4 hOZSpq">
  166. <div class="sc-1kr69jw-5 Dzlsu">
  167. <div class="sc-1kr69jw-6 dhGRLC">
  168. <div class="sc-1kr69jw-3 wJpxo" data-add-scroll="true">
  169. <ul class="sc-1kr69jw-0 hkzusx">
  170. ${illustGroup}
  171. </ul>
  172. </div>
  173. </div>
  174. </div>
  175. </div>
  176. <div class="sc-1kr69jw-1 ioeugW">
  177. <button type="button" style="left: 0px; margin-bottom: 0px; padding-left: 16px; padding-bottom: 0px;" class="sc-lsvxoe-1 sc-lsvxoe-2 bpAOQo fXtZss">
  178. <div class="sc-lsvxoe-0 kYzpDP">
  179. <svg viewBox="0 0 24 24" size="24" class="sc-11csm01-0 fivNSm">
  180. <path d="M8.08579 16.5858C7.30474 17.3668 7.30474 18.6332 8.08579 19.4142C8.86684 20.1953 10.1332 20.1953 10.9142 19.4142L18.3284 12L10.9142 4.58579C10.1332 3.80474 8.86684 3.80474 8.08579 4.58579C7.30474 5.36684 7.30474 6.63317 8.08579 7.41421L12.6716 12L8.08579 16.5858Z" transform="rotate(180 12 12)"></path>
  181. </svg>
  182. </div>
  183. </button>
  184. <button type="button" style="right: -72px; margin-bottom: 0px; padding-right: 16px; padding-bottom: 0px;" class="sc-lsvxoe-1 sc-lsvxoe-2 bpAOQo fXtZss">
  185. <div class="sc-lsvxoe-0 kYzpDP">
  186. <svg viewBox="0 0 24 24" size="24" class="sc-11csm01-0 fivNSm">
  187. <path d="M8.08579 16.5858C7.30474 17.3668 7.30474 18.6332 8.08579 19.4142C8.86684 20.1953 10.1332 20.1953 10.9142 19.4142L18.3284 12L10.9142 4.58579C10.1332 3.80474 8.86684 3.80474 8.08579 4.58579C7.30474 5.36684 7.30474 6.63317 8.08579 7.41421L12.6716 12L8.08579 16.5858Z"></path>
  188. </svg>
  189. </div>
  190. </button>
  191. </div>
  192. </div>
  193. </div>
  194. </div>`;
  195. }
  196.  
  197. // "appendElements+="で一括追加にすると、なぜかundefinedが追加され続けるので一つずつ追加
  198. const appendElements = `
  199. <div class="sc-1y4z60g-5 cPVjJh addElement page${scrollPageCount + 1}">
  200. <div class="sc-11m5zdr-0 bbJBkV">
  201. <div class="sc-11m5zdr-1 clrYBQ">
  202. <div class="sc-19z9m4s-0 fbLOpg">
  203. <a class="sc-d98f2c-0" data-gtm-value="${userId}" href="/users/${userId}">
  204. <div size="80" title="${userName}" role="img" class="sc-1asno00-0 deMagM">
  205. <img src="${userProfileImage}" style="object-fit: cover; object-position: center top;" width="80" height="80" alt="${userName}">
  206. </div>
  207. </a>
  208. <div class="sc-19z9m4s-4 fYGGbS">
  209. <div class="sc-19z9m4s-5 iqZEnZ">
  210. <a class="sc-d98f2c-0 sc-19z9m4s-2 QHGGh" data-gtm-value="${userId}" href="/users/${userId}">${userName}</a>
  211. </div>
  212. <div class="sc-19z9m4s-3 isEYuz">${escapedComment}</div>
  213. <div class="sc-19z9m4s-1 qjElz">
  214. <button class="sc-bdnxRM jvCTkj sc-dlnjwi ${followClass} sc-1obql3d-0 Rlftz gtm-undefined sc-1obql3d-0 Rlftz gtm-undefined follow" data-gtm-user-id="${userId}" data-click-action="click" data-click-label="follow" height="40" ${followStyle}>${changeFollowLanguage}</button>
  215. <div aria-current="false" class="sc-125tkm8-0 sc-125tkm8-3 ka-dhPl eZXKAK">
  216. <div class="sc-1ij5ui8-0 QihHO sc-125tkm8-2 gUcOiA" role="button">
  217. <pixiv-icon name="24/Dot"></pixiv-icon>
  218. </div>
  219. </div>
  220. </div>
  221. </div>
  222. </div>
  223. </div>
  224. ${illustContainer}
  225. </div>
  226. </div>`;
  227.  
  228. document.querySelector(".sc-1y4z60g-4.cqwgCG").insertAdjacentHTML("beforeend", appendElements);
  229. }
  230.  
  231. // https://www.pixiv.net/ajax/user/*/following?offset=24&limit=24&rest=show
  232. // https://www.pixiv.net/users/*/following?p=2
  233.  
  234. if (document.querySelectorAll(".sc-1y4z60g-5.cPVjJh").length < 23) { return; }
  235.  
  236. // URL作成
  237. const matches = window.location.href.match(followingRegex);
  238. let offset;
  239. if (matches[2]) {
  240. offset = (matches[2] * 24) + (scrollPageCount * 24);
  241. } else {
  242. offset = 24 + (scrollPageCount * 24);
  243. }
  244. scrollPageCount++;
  245. const url = `https://www.pixiv.net/ajax/user/${matches[1]}/following?offset=${offset}&limit=24&rest=show`;
  246.  
  247. const fetchData = async () => {
  248. const response = await fetch(url);
  249. const json = await response.json();
  250. for (let i = 0; i < Object.keys(json.body.users).length; i++) {
  251. const users = json.body.users[i];
  252. const userId = users.userId;
  253. const userName = users.userName;
  254. const userProfileImage = users.profileImageUrl;
  255. const userComment = users.userComment.slice(0, 98);
  256. const userFollowing = users.following;
  257. const illustId = [];
  258. const illustTitle = [];
  259. const illustUrl = [];
  260. const illustBookmarkData = [];
  261. const illustAlt = [];
  262. const illustR18 = [];
  263. const illustPageCount = [];
  264. for (let j = 0; j < Object.keys(json.body.users[i].illusts).length; j++) {
  265. const illusts = json.body.users[i].illusts[j];
  266. illustId.push(illusts.id);
  267. illustTitle.push(illusts.title);
  268. illustUrl.push(illusts.url);
  269. illustBookmarkData.push(illusts.bookmarkData);
  270. illustAlt.push(illusts.alt);
  271. illustR18.push(illusts.tags[0]);
  272. illustPageCount.push(illusts.pageCount);
  273. }
  274. createElement(userId, userName, userProfileImage, userComment, userFollowing, illustId, illustTitle, illustUrl, illustBookmarkData, illustAlt, illustR18, illustPageCount);
  275. }
  276. };
  277. (async () => {
  278. await fetchData();
  279. bookmarkAddDelete();
  280. followAndUnfollow(setFollowLanguage);
  281. })();
  282. }
  283. // -----------------------------------------------------------------------------------------
  284.  
  285.  
  286.  
  287. // ブックマーク・フォローユーザーの作品・タグ検索の無限スクロール--------------------------------
  288. function bookmarkAndTag_process(checkType) {
  289. function createElement(illustId, illustTitle, illustUrl, userId, userName, illustPageCount, illustBookmarkData, illustAlt, userProfileImage, typeElement, typeClass, illustR18, illustMaskReason) {
  290.  
  291. // langの値によって言語を変更する
  292. const setDeletedLanguage = [];
  293. const currentLanguage = document.querySelector("html").getAttribute("lang");
  294. switch (currentLanguage) {
  295. case "ja":
  296. setDeletedLanguage.push("R18 / R18G", "作品", "閲覧制限中", "削除済み", "もしくは非公開");
  297. break;
  298. case "ko":
  299. setDeletedLanguage.push("R-18 / R-18G", "작품", "열람 제한 중", "삭제됨", "혹은 비공개");
  300. break;
  301. case "zh-CN":
  302. setDeletedLanguage.push("R-18 / R-18G", "作品", "浏览受限(含成人内容)", "已删除", "或不公开");
  303. break;
  304. case "zh-TW":
  305. setDeletedLanguage.push("R-18 / R-18G", "作品", "瀏覽受限(含成人內容)", "已刪除", "或非公開");
  306. break;
  307. default:
  308. setDeletedLanguage.push("R-18/R-18G", "works", "Restricted (Adult Content)", "Deleted", "or private");
  309. }
  310.  
  311. // ブックマークを切り替え
  312. let bookmarkClass = "";
  313. let bookmarkStyle = "";
  314. if (illustBookmarkData) {
  315. bookmarkClass = "bXjFLc";
  316. bookmarkStyle = 'style="color: rgb(255, 64, 96); fill: currentcolor;"';
  317. } else {
  318. bookmarkClass = "dxYRhf";
  319. }
  320.  
  321. // R18マーク
  322. let r18Element = "";
  323. if (illustR18 == "R-18") {
  324. r18Element = `
  325. <div class="sc-rp5asc-15 cIllir">
  326. <div class="sc-1ovn4zb-0 bfWaOT">R-18</div>
  327. </div>`;
  328. }
  329.  
  330. // うごくイラスト再生マーク・イラスト数表示
  331. let ugoiraElement = "";
  332. let pageCountElement = "";
  333. if (illustAlt.slice(-4) == "うごイラ") {
  334. ugoiraElement = '<svg viewBox="0 0 24 24" style="width: 48px; height: 48px;" class="sc-192k5ld-0 etaMpt sc-rp5asc-8 kSDUsv"><circle cx="12" cy="12" r="10" class="sc-192k5ld-1 lajlxF"></circle><path d="M9,8.74841664 L9,15.2515834 C9,15.8038681 9.44771525,16.2515834 10,16.2515834 C10.1782928,16.2515834 10.3533435,16.2039156 10.5070201,16.1135176 L16.0347118,12.8619342 C16.510745,12.5819147 16.6696454,11.969013 16.3896259,11.4929799 C16.3034179,11.3464262 16.1812655,11.2242738 16.0347118,11.1380658 L10.5070201,7.88648243 C10.030987,7.60646294 9.41808527,7.76536339 9.13806578,8.24139652 C9.04766776,8.39507316 9,8.57012386 9,8.74841664 Z" class="sc-192k5ld-2 jwyUTl"></path></svg>';
  335. } else {
  336. if (illustPageCount > 2) {
  337. pageCountElement = `
  338. <div class="sc-rp5asc-5 hHNegy">
  339. <div class="sc-1mr081w-0 kZlOCw">
  340. <span class="sc-1mr081w-1 gODLwk">
  341. <span class="sc-14heosd-0 gbNjFx">
  342. <svg viewBox="0 0 9 10" size="9" class="sc-14heosd-1 fArvVr">
  343. <path d="M8,3 C8.55228475,3 9,3.44771525 9,4 L9,9 C9,9.55228475 8.55228475,10 8,10 L3,10 C2.44771525,10 2,9.55228475 2,9 L6,9 C7.1045695,9 8,8.1045695 8,7 L8,3 Z M1,1 L6,1 C6.55228475,1 7,1.44771525 7,2 L7,7 C7,7.55228475 6.55228475,8 6,8 L1,8 C0.44771525,8 0,7.55228475 0,7 L0,2 C0,1.44771525 0.44771525,1 1,1 Z" transform=""></path>
  344. </svg>
  345. </span>
  346. </span>
  347. <span>${illustPageCount}</span>
  348. </div>
  349. </div>`;
  350. }
  351. }
  352.  
  353.  
  354. let illustContainer = "";
  355. let userNameContainer = "";
  356. let addBookmarkClass ="";
  357. let illustTitleElement;
  358. if (illustTitle == "-----") {
  359. if (illustMaskReason == "r18" || illustMaskReason == "r18g") {
  360. // R18・R18G
  361. illustContainer = `
  362. <span to="/artworks/${illustId}" class="sc-rp5asc-16 iUsZyY sc-eWnToP khjDVZ" data-gtm-value="${illustId}" data-gtm-user-id="${userId}">
  363. <div class="sc-7i69t-2 wDRGm">
  364. <div class="sc-7i69t-0 lmTlVI">
  365. <div class="sc-7i69t-6 jjBwSZ">
  366. <svg viewBox="0 0 24 24" style="width: 72px; height: 72px;" class="sc-11k840d-0 hgKsyL">
  367. <path d="M5.26763775,4 L9.38623853,11.4134814 L5,14.3684211 L5,18 L13.0454155,18 L14.1565266,20 L5,20 C3.8954305,20 3,19.1045695 3,18 L3,6 C3,4.8954305 3.8954305,4 5,4 L5.26763775,4 Z M9.84347336,4 L19,4 C20.1045695,4 21,4.8954305 21,6 L21,18 C21,19.1045695 20.1045695,20 19,20 L18.7323623,20 L17.6212511,18 L19,18 L19,13 L16,15 L15.9278695,14.951913 L9.84347336,4 Z M16,7 C14.8954305,7 14,7.8954305 14,9 C14,10.1045695 14.8954305,11 16,11 C17.1045695,11 18,10.1045695 18,9 C18,7.8954305 17.1045695,7 16,7 Z M7.38851434,1.64019979 L18.3598002,21.3885143 L16.6114857,22.3598002 L5.64019979,2.61148566 L7.38851434,1.64019979 Z"></path>
  368. </svg>
  369. </div>
  370. <div class="sc-7i69t-4 hEKLCY">${setDeletedLanguage[0]}<br>${setDeletedLanguage[1]}</div>
  371. </div>
  372. </div>
  373. </span>
  374. <div class="sc-iasfms-4 iHfghO">
  375. <div class=""><button type="button" class="sc-kgq5hw-0 fgVkZi" disabled="">
  376. <svg viewBox="0 0 32 32" width="32" height="32" class="sc-j89e3c-1 dxYRhf">
  377. <path d="M21,5.5 C24.8659932,5.5 28,8.63400675 28,12.5 C28,18.2694439 24.2975093,23.1517313 17.2206059,27.1100183 C16.4622493,27.5342993 15.5379984,27.5343235 14.779626,27.110148 C7.70250208,23.1517462 4,18.2694529 4,12.5 C4,8.63400691 7.13400681,5.5 11,5.5 C12.829814,5.5 14.6210123,6.4144028 16,7.8282366 C17.3789877,6.4144028 19.170186,5.5 21,5.5 Z"></path>
  378. <path d="M16,11.3317089 C15.0857201,9.28334665 13.0491506,7.5 11,7.5 C8.23857625,7.5 6,9.73857647 6,12.5 C6,17.4386065 9.2519779,21.7268174 15.7559337,25.3646328 C15.9076021,25.4494645 16.092439,25.4494644 16.2441073,25.3646326 C22.7480325,21.7268037 26,17.4385986 26,12.5 C26,9.73857625 23.7614237,7.5 21,7.5 C18.9508494,7.5 16.9142799,9.28334665 16,11.3317089 Z" class="sc-j89e3c-0 dUurgf"></path>
  379. </svg>
  380. </button>
  381. </div>
  382. </div>`;
  383. illustTitleElement = `<a class="sc-iasfms-6 hvLYiR" to="/artworks/${illustId}">${setDeletedLanguage[2]}</a>`
  384. } else {
  385. // 削除・非公開
  386. illustContainer = `
  387. <span to="/artworks/${illustId}" class="sc-rp5asc-16 iUsZyY sc-eWnToP khjDVZ" data-gtm-value="${illustId}" data-gtm-user-id="0">
  388. <div class="sc-7i69t-2 wDRGm" style="display: flex; -moz-box-pack: center; justify-content: center; -moz-box-align: center; align-items: center; user-select: none; background-color: var(--charcoal-background2); width: 184px; height: 184px;">
  389. <div class="sc-7i69t-0 lmTlVI" style="display: grid; -moz-box-pack: center; place-content: space-between center; margin-bottom: -1px; height: 122px; width: 122px;">
  390. <div class="sc-7i69t-6 tGGpA" style="margin-right: -2px; justify-self: center; color: var(--charcoal-text4);">
  391. <svg viewBox="0 0 24 24" size="72" class="sc-11csm01-0 fieitW" style="stroke: none; fill: currentcolor; width: 72px; height: 72px; line-height: 0; font-size: 0px; vertical-align: middle;">
  392. <path d="M5 22C3.34315 22 2 20.6569 2 19V5C2 3.34315 3.34315 2 5 2H15C16.6569 2 18 3.34315 18 5V15H22V19C22 20.6569 20.6569 22 19 22H5ZM18 19C18 19.2652 18.1054 19.5196 18.2929 19.7071C18.4804 19.8946 18.7348 20 19 20C19.2652 20 19.5196 19.8946 19.7071 19.7071C19.8946 19.5196 20 19.2652 20 19V17H18V19ZM11.0819 16.8469C11.0819 17.4837 10.5656 18 9.92874 18C9.29189 18 8.77562 17.4837 8.77562 16.8469C8.77562 16.21 9.29189 15.6938 9.92874 15.6938C10.5656 15.6938 11.0819 16.21 11.0819 16.8469ZM11.0063 13.3967C11.0432 13.3229 11.117 13.203 11.1631 13.1292C11.4142 12.761 11.764 12.4544 12.1185 12.1435C12.9365 11.4263 13.7802 10.6866 13.4971 9.11635C13.2295 7.55733 11.9842 6.27505 10.4251 6.04443C8.5248 5.76768 6.84585 6.93925 6.33848 8.6182C6.18165 9.15325 6.58755 9.69753 7.14105 9.69753H7.32555C7.70378 9.69753 8.0082 9.43 8.13735 9.0979C8.43255 8.27688 9.2997 7.71415 10.2591 7.9171C11.1447 8.1016 11.7904 8.97798 11.7074 9.88203C11.6449 10.5864 11.1417 10.976 10.5834 11.4083C10.2349 11.678 9.86497 11.9644 9.56723 12.3543C9.54814 12.3734 9.4852 12.4627 9.43889 12.5283C9.41803 12.5579 9.40054 12.5827 9.39195 12.5942C9.30893 12.7233 9.19823 12.9447 9.14288 13.0923C9.13365 13.1015 9.13365 13.1108 9.13365 13.12C9.02295 13.4521 8.94915 13.8488 8.94915 14.3192H10.8034C10.8034 14.0886 10.831 13.8857 10.8956 13.6919C10.9049 13.655 10.9879 13.4244 11.0063 13.3967Z" fill-rule="evenodd" clip-rule="evenodd" style="stroke: none; fill: currentcolor; line-height: 0; font-size: 0px;"></path>
  393. </svg>
  394. </div>
  395. <div class="sc-7i69t-4 hEKLCY" style="text-align: center; color: var(--charcoal-text4); font-size: 16px; line-height: 24px; font-weight: bold; display: flow-root;">${setDeletedLanguage[3]}<br>${setDeletedLanguage[4]}</div>
  396. </div>
  397. </div>
  398. </span>
  399. <div class="sc-iasfms-4 iHfghO">
  400. <div class="">
  401. <button type="button" class="sc-kgq5hw-0 fgVkZi" disabled="">
  402. <svg viewBox="0 0 32 32" width="32" height="32" class="sc-j89e3c-1 dxYRhf">
  403. <path d="M21,5.5 C24.8659932,5.5 28,8.63400675 28,12.5 C28,18.2694439 24.2975093,23.1517313 17.2206059,27.1100183 C16.4622493,27.5342993 15.5379984,27.5343235 14.779626,27.110148 C7.70250208,23.1517462 4,18.2694529 4,12.5 C4,8.63400691 7.13400681,5.5 11,5.5 C12.829814,5.5 14.6210123,6.4144028 16,7.8282366 C17.3789877,6.4144028 19.170186,5.5 21,5.5 Z"></path>
  404. <path d="M16,11.3317089 C15.0857201,9.28334665 13.0491506,7.5 11,7.5 C8.23857625,7.5 6,9.73857647 6,12.5 C6,17.4386065 9.2519779,21.7268174 15.7559337,25.3646328 C15.9076021,25.4494645 16.092439,25.4494644 16.2441073,25.3646326 C22.7480325,21.7268037 26,17.4385986 26,12.5 C26,9.73857625 23.7614237,7.5 21,7.5 C18.9508494,7.5 16.9142799,9.28334665 16,11.3317089 Z" class="sc-j89e3c-0 dUurgf"></path>
  405. </svg>
  406. </button>
  407. </div>
  408. </div>`;
  409. illustTitleElement = `<a class="sc-iasfms-6 gqlfsh" to="/artworks/${illustId}">${illustTitle}</a>`
  410. }
  411.  
  412. } else {
  413. // ノーマル
  414. illustContainer = `
  415. <a class="sc-d98f2c-0 sc-rp5asc-16 iUsZyY ${typeClass} sc-eWnToP khjDVZ" data-gtm-value="${illustId}" data-gtm-user-id="${userId}" href="/artworks/${illustId}">
  416. <div radius="4" class="sc-rp5asc-9 cYUezH">
  417. <img src="${illustUrl}" style="object-fit: cover; object-position: center center;" alt="${illustAlt}" class="sc-rp5asc-10 erYaF">
  418. ${ugoiraElement}
  419. </div>
  420. <div class="sc-rp5asc-12 Sxcoo">
  421. <div class="sc-rp5asc-13 liXhix">
  422. ${r18Element}
  423. </div>
  424. ${pageCountElement}
  425. </div>
  426. </a>
  427. <div class="sc-iasfms-4 iHfghO">
  428. <div class="">
  429. <button type="button" class="sc-kgq5hw-0 fgVkZi">
  430. <svg viewBox="0 0 32 32" width="32" height="32" class="sc-j89e3c-1 ${bookmarkClass}" ${bookmarkStyle}>
  431. <path d="M21,5.5 C24.8659932,5.5 28,8.63400675 28,12.5 C28,18.2694439 24.2975093,23.1517313 17.2206059,27.1100183 C16.4622493,27.5342993 15.5379984,27.5343235 14.779626,27.110148 C7.70250208,23.1517462 4,18.2694529 4,12.5 C4,8.63400691 7.13400681,5.5 11,5.5 C12.829814,5.5 14.6210123,6.4144028 16,7.8282366 C17.3789877,6.4144028 19.170186,5.5 21,5.5 Z"></path>
  432. <path d="M16,11.3317089 C15.0857201,9.28334665 13.0491506,7.5 11,7.5 C8.23857625,7.5 6,9.73857647 6,12.5 C6,17.4386065 9.2519779,21.7268174 15.7559337,25.3646328 C15.9076021,25.4494645 16.092439,25.4494644 16.2441073,25.3646326 C22.7480325,21.7268037 26,17.4385986 26,12.5 C26,9.73857625 23.7614237,7.5 21,7.5 C18.9508494,7.5 16.9142799,9.28334665 16,11.3317089 Z" class="sc-j89e3c-0 dUurgf"></path>
  433. </svg>
  434. </button>
  435. </div>
  436. </div>`;
  437.  
  438. userNameContainer = `
  439. <div aria-haspopup="true" class="sc-1rx6dmq-0 icsUdQ">
  440. <div class="sc-1rx6dmq-1 eMfHJB">
  441. <a class="sc-d98f2c-0" data-gtm-value="${userId}" href="/users/${userId}">
  442. <div size="24" title="${userName}" role="img" class="sc-1asno00-0 hMqBzA">
  443. <img src="${userProfileImage}" style="object-fit: cover; object-position: center top;" width="24" height="24" alt="${userName}">
  444. </div>
  445. </a>
  446. </div>
  447. <a class="sc-d98f2c-0 sc-1rx6dmq-2 kghgsn" data-gtm-value="${userId}" href="/users/${userId}">${userName}</a>
  448. </div>`;
  449. illustTitleElement = `<a class="sc-d98f2c-0 sc-iasfms-6 gqlfsh" href="/artworks/${illustId}">${illustTitle}</a>`
  450. addBookmarkClass = " addBookmark"
  451. }
  452.  
  453. appendElements += `
  454. ${typeElement}
  455. <div class="sc-iasfms-5 liyNwX">
  456. <div type="illust" size="184" class="sc-iasfms-3 iIcDMF">
  457. <div width="184" height="184" class="sc-rp5asc-0 fxGVAF${addBookmarkClass}">
  458. ${illustContainer}
  459. </div>
  460. </div>
  461. <div class="sc-iasfms-0 jtpclu">
  462. ${illustTitleElement}
  463. </div>
  464. <div class="sc-iasfms-0 jtpclu">${userNameContainer}</div>
  465. </div>
  466. </li>`;
  467. }
  468.  
  469. let appendElements = "";
  470. if (checkType == "bookmark") {
  471. // ブックマーク
  472. // https://www.pixiv.net/ajax/user/*/illusts/bookmarks?tag=&offset=0&limit=48&rest=show
  473. // https://www.pixiv.net/users/*/bookmarks/artworks?p=2
  474.  
  475. if (document.querySelectorAll(".sc-9y4be5-2.kFAPOq").length < 48) { return; }
  476.  
  477. // URL作成
  478. const matches = window.location.href.match(bookmarkRegex);
  479. let offset;
  480. if (matches[2]) {
  481. offset = (matches[2] * 48) + (scrollPageCount * 48);
  482. } else {
  483. offset = 48 + (scrollPageCount * 48);
  484. }
  485. scrollPageCount++;
  486. const url = `https://www.pixiv.net/ajax/user/${matches[1]}/illusts/bookmarks?tag=&offset=${offset}&limit=48&rest=show`;
  487.  
  488. const fetchData = async () => {
  489. const response = await fetch(url);
  490. const json = await response.json();
  491. for (let i = 0; i < Object.keys(json.body.works).length; i++) {
  492. const illust = json.body.works[i];
  493. const illustId = illust.id;
  494. const illustTitle = illust.title;
  495. const illustUrl = illust.url;
  496. const userId = illust.userId;
  497. const userName = illust.userName;
  498. const illustPageCount = illust.pageCount;
  499. const illustBookmarkData = illust.bookmarkData;
  500. const illustAlt = illust.alt;
  501. const userProfileImage = illust.profileImageUrl;
  502. const typeElement = `<li size="1" offset="0" class="sc-9y4be5-2 sc-9y4be5-3 sc-1wcj34s-1 kFAPOq CgxkO addElement page${scrollPageCount + 1}" style="display: block">`;
  503. const typeClass = "";
  504. const illustR18 = illust.tags[0];
  505. const illustMaskReason = illust.maskReason;
  506. createElement(illustId, illustTitle, illustUrl, userId, userName, illustPageCount, illustBookmarkData, illustAlt, userProfileImage, typeElement, typeClass, illustR18, illustMaskReason);
  507. }
  508. document.querySelector(".sc-9y4be5-1.jtUPOE").insertAdjacentHTML("beforeend", appendElements);
  509. };
  510. (async () => {
  511. await fetchData();
  512. bookmarkAddDelete();
  513. })();
  514. } else if (checkType == "follow") {
  515. // フォローユーザーの作品
  516. // https://www.pixiv.net/ajax/follow_latest/illust?p=2&mode=all
  517. // https://www.pixiv.net/bookmark_new_illust.php?p=2
  518.  
  519. if (document.querySelectorAll(".sc-9y4be5-2.kFAPOq").length < 60) { return; }
  520.  
  521. // URL作成
  522. const matches = window.location.href.match(followUserWorkRegex);
  523. let offset;
  524. scrollPageCount++;
  525. if (matches[2]) {
  526. offset = matches[2] + scrollPageCount;
  527. } else {
  528. offset = 1 + scrollPageCount;
  529. }
  530. let setMode = "";
  531. if (matches[1]) {
  532. setMode = "r18";
  533. } else {
  534. setMode = "all";
  535. }
  536. const url = `https://www.pixiv.net/ajax/follow_latest/illust?p=${offset}&mode=${setMode}`;
  537.  
  538. const fetchData = async () => {
  539. const response = await fetch(url);
  540. const json = await response.json();
  541. for (let i = 0; i < Object.keys(json.body.thumbnails.illust).length; i++) {
  542. const illust = json.body.thumbnails.illust[i];
  543. const illustId = illust.id;
  544. const illustTitle = illust.title;
  545. const illustUrl = illust.url;
  546. const userId = illust.userId;
  547. const userName = illust.userName;
  548. const illustPageCount = illust.pageCount;
  549. const illustBookmarkData = illust.bookmarkData;
  550. const illustAlt = illust.alt;
  551. const userProfileImage = illust.profileImageUrl;
  552. const typeElement = `<li size="1" offset="0" class="sc-9y4be5-2 sc-9y4be5-3 sc-1wcj34s-1 kFAPOq wHEbW addElement page${scrollPageCount + 1}" style="display: block">`;
  553. const typeClass = "gtm-followlatestpage-thumbnail-link";
  554. const illustR18 = illust.tags[0];
  555. const illustMaskReason = illust.maskReason;
  556. createElement(illustId, illustTitle, illustUrl, userId, userName, illustPageCount, illustBookmarkData, illustAlt, userProfileImage, typeElement, typeClass, illustR18, illustMaskReason);
  557. }
  558. document.querySelector(".sc-9y4be5-1.jtUPOE").insertAdjacentHTML("beforeend", appendElements);
  559. };
  560. (async () => {
  561. await fetchData();
  562. bookmarkAddDelete();
  563. })();
  564. } else if (checkType == "tag") {
  565. // タグ検索
  566. // https://www.pixiv.net/ajax/search/artworks/*?word=*&order=date_d&mode=all&p=1&s_mode=s_tag_full&type=all
  567. // https://www.pixiv.net/tags/*/artworks?p=2
  568.  
  569. if (document.querySelectorAll(".sc-l7cibp-2.gpVAva").length < 60) { return; }
  570.  
  571. // URL作成
  572. const matches = window.location.href.match(tagRegex);
  573. let offset;
  574. ++scrollPageCount;
  575. if (matches[7]) {
  576. offset = matches[7] + scrollPageCount;
  577. } else {
  578. offset = 1 + scrollPageCount;
  579. }
  580.  
  581. let setIllustType = "";
  582. let insertIllustType;
  583. if (matches[2] == "manga") {
  584. setIllustType = "type=manga";
  585. insertIllustType = "manga";
  586. } else if (matches[2] == "artworks") {
  587. setIllustType = "type=all";
  588. insertIllustType = "illustManga";
  589. } else if (matches[9] == "illust") {
  590. setIllustType = "type=illust";
  591. insertIllustType = "illust";
  592. } else if (matches[9] == "ugoira") {
  593. setIllustType = "type=ugoira";
  594. insertIllustType = "illust";
  595. } else if (matches[2] == "illustrations") {
  596. setIllustType = "type=illust_and_ugoira";
  597. insertIllustType = "illust";
  598. }
  599.  
  600. let sinceDate = "";
  601. if (matches[5]) {
  602. sinceDate = `&${matches[5]}`;
  603. }
  604. let untilDate = "";
  605. if (matches[6]) {
  606. untilDate = `&${matches[6]}`;
  607. }
  608. let otherTag = "";
  609. if (matches[10]) {
  610. otherTag = `&${matches[10]}`;
  611. }
  612.  
  613. let setMode = "";
  614. if (matches[4] == "mode=safe" || matches[4] == "mode=r18") {
  615. setMode = matches[4];
  616. } else {
  617. setMode = "mode=all";
  618. }
  619.  
  620. let orderDate = "";
  621. if (matches[3] == "order=date") {
  622. orderDate = matches[3];
  623. } else {
  624. orderDate = "order=date_d";
  625. }
  626.  
  627. let tagMatchMode = "";
  628. if (matches[8] == "s_mode=s_tag" || matches[8] == "s_mode=s_tc") {
  629. tagMatchMode = matches[8];
  630. } else {
  631. tagMatchMode = "s_mode=s_tag_full";
  632. }
  633. const url = `https://www.pixiv.net/ajax/search/${matches[2]}/${matches[1]}?word=${matches[1]}&${orderDate}&${setMode}&p=${offset}&${tagMatchMode}&${setIllustType}${sinceDate}${untilDate}${otherTag}`;
  634.  
  635. const fetchData = async () => {
  636. const response = await fetch(url);
  637. const json = await response.json();
  638. for (let i = 0; i < Object.keys(json.body[insertIllustType].data).length; i++) {
  639. // jsonファイルに、なぜか必ず1つだけ欠けている部分があるのでスキップする
  640. if (!json.body[insertIllustType].data[i].id) { continue; }
  641. const illust = json.body[insertIllustType].data[i];
  642. const illustId = illust.id;
  643. const illustTitle = illust.title;
  644. const illustUrl = illust.url;
  645. const userId = illust.userId;
  646. const userName = illust.userName;
  647. const illustPageCount = illust.pageCount;
  648. const illustBookmarkData = illust.bookmarkData;
  649. const illustAlt = illust.alt;
  650. const userProfileImage = illust.profileImageUrl;
  651. const typeElement = '<li class="sc-l7cibp-2 gpVAva addElement page${scrollPageCount + 1}" style="display: block">';
  652. const typeClass = "";
  653. const illustR18 = illust.tags[0];
  654. const illustMaskReason = illust.maskReason;
  655. createElement(illustId, illustTitle, illustUrl, userId, userName, illustPageCount, illustBookmarkData, illustAlt, userProfileImage, typeElement, typeClass, illustR18, illustMaskReason);
  656. }
  657. document.querySelector(".sc-l7cibp-1.krFoBL").insertAdjacentHTML("beforeend", appendElements);
  658. };
  659. (async () => {
  660. await fetchData();
  661. bookmarkAddDelete();
  662. })();
  663. }
  664. }
  665. // -----------------------------------------------------------------------------------------
  666.  
  667.  
  668. // 新たに追加した要素でのブックマーク・フォロー機能---------------------------------------------
  669. // x-csrf-tokenを取得
  670. const getCsrfToken = async () => {
  671. const response = await fetch(location.origin);
  672. const data = await response.text();
  673. const matchToken = data.match(/"token":"([a-z0-9]+)"/);
  674. return matchToken[1];
  675. };
  676.  
  677. // ブックマーク追加・削除
  678. function bookmarkAddDelete() {
  679. const buttonGrandElements = document.querySelectorAll(".addBookmark");
  680. for (let i = 0; i < buttonGrandElements.length; i++) {
  681.  
  682. const buttonElement = buttonGrandElements[i].querySelector(".sc-kgq5hw-0.fgVkZi");
  683. const svgElement = buttonGrandElements[i].querySelector("svg.sc-j89e3c-1");
  684. const getIdElement = buttonGrandElements[i].querySelector("a.sc-d98f2c-0.khjDVZ");
  685. const userId = getIdElement.getAttribute("data-gtm-user-id");
  686. const illustId = getIdElement.getAttribute("data-gtm-value");
  687. buttonGrandElements[i].classList.remove("addBookmark");
  688.  
  689. buttonElement.addEventListener("click", () => {
  690. if (svgElement.classList.contains("dxYRhf")) {
  691. // ブックマーク追加
  692. (async () => {
  693. try {
  694. buttonElement.setAttribute("disabled", true);
  695. const addBookmarkBody = {
  696. illust_id: illustId,
  697. restrict: 0,
  698. comment: "",
  699. tags: []
  700. };
  701. const url = "https://www.pixiv.net/ajax/illusts/bookmarks/add";
  702.  
  703. const setCsrfToken = await getCsrfToken();
  704. const response = await fetch(url, {
  705. method: "post",
  706. headers: {
  707. "Accept": "application/json",
  708. "Content-Type": "application/json; charset=utf-8",
  709. "x-csrf-token": setCsrfToken,
  710. },
  711. body: JSON.stringify(addBookmarkBody),
  712. credentials: "same-origin"
  713. });
  714. const json = await response.json();
  715. // ストレージにBookmarkIDを保存
  716. sessionStorage.setItem(illustId, json.body.last_bookmark_id);
  717.  
  718. if (!response.ok) { throw new Error(); }
  719.  
  720. buttonElement.removeAttribute("disabled");
  721. svgElement.style.color = "rgb(255, 64, 96)";
  722. svgElement.style.fill = "currentcolor";
  723. svgElement.classList.remove("dxYRhf");
  724. svgElement.classList.add("bXjFLc");
  725.  
  726. } catch (error) {
  727. console.error(error);
  728. buttonElement.removeAttribute("disabled");
  729. }
  730. })();
  731. } else if (svgElement.classList.contains("bXjFLc")) {
  732. // ブックマーク削除
  733. (async () => {
  734. try {
  735. buttonElement.setAttribute("disabled", true);
  736.  
  737. // BookmarkIDを取得する
  738. const getStorageItem = sessionStorage.getItem(illustId);
  739. sessionStorage.removeItem(illustId);
  740. let bookmarkId;
  741. if (getStorageItem) {
  742. bookmarkId = getStorageItem;
  743. } else {
  744. // https://www.pixiv.net/ajax/user/*/illusts?ids[]=*
  745. const illustInfoUrl = `https://www.pixiv.net/ajax/user/${userId}/illusts?ids[]=${illustId}`;
  746. const illustInfoResponse = await fetch(illustInfoUrl);
  747. const illustInfoJson = await illustInfoResponse.json();
  748. bookmarkId = illustInfoJson.body[illustId].bookmarkData.id;
  749. }
  750.  
  751. const deleteBookmarkBody = new URLSearchParams({
  752. bookmark_id: bookmarkId
  753. });
  754. const url = "https://www.pixiv.net/ajax/illusts/bookmarks/delete"
  755.  
  756. const setCsrfToken = await getCsrfToken();
  757. const response = await fetch(url, {
  758. method: "post",
  759. headers: {
  760. "Accept": "application/json",
  761. "x-csrf-token": setCsrfToken,
  762. },
  763. body: deleteBookmarkBody,
  764. credentials: "same-origin"
  765. });
  766.  
  767. if (!response.ok) { throw new Error(); }
  768.  
  769. buttonElement.removeAttribute("disabled");
  770. svgElement.removeAttribute("style");
  771. svgElement.classList.remove("bXjFLc");
  772. svgElement.classList.add("dxYRhf");
  773.  
  774. } catch (error) {
  775. console.error(error);
  776. buttonElement.removeAttribute("disabled");
  777. }
  778. })();
  779. }
  780. })
  781. }
  782. }
  783.  
  784. // フォロー・フォロー解除
  785. function followAndUnfollow(setFollowLanguage) {
  786. const buttonElements = document.querySelectorAll(".follow");
  787. for (let i = 0; i < buttonElements.length; i++) {
  788.  
  789. const buttonElement = buttonElements[i];
  790. const userId = buttonElement.getAttribute("data-gtm-user-id");
  791. buttonElement.classList.remove("follow");
  792.  
  793. buttonElement.addEventListener("click", () => {
  794. if (buttonElement.classList.contains("fOWAlD")) {
  795. // フォローする
  796. (async () => {
  797. try {
  798. buttonElement.setAttribute("disabled", true);
  799.  
  800. const followBody = new URLSearchParams({
  801. mode: "add",
  802. type: "user",
  803. user_id: userId,
  804. tag: "",
  805. restrict: 0,
  806. format: "json"
  807. });
  808. const url = "https://www.pixiv.net/bookmark_add.php";
  809.  
  810. const setCsrfToken = await getCsrfToken();
  811. const response = await fetch(url, {
  812. method: "post",
  813. headers: {
  814. "Accept": "application/json",
  815. "x-csrf-token": setCsrfToken,
  816. },
  817. body: followBody,
  818. credentials: "same-origin"
  819. });
  820.  
  821. if (!response.ok) { throw new Error(); }
  822.  
  823. buttonElement.removeAttribute("disabled");
  824. buttonElement.classList.remove("fOWAlD");
  825. buttonElement.classList.add("cnpwVx");
  826. buttonElement.style.backgroundColor = "var(--charcoal-surface3)";
  827. buttonElement.style.color = "var(--charcoal-text2)";
  828. buttonElement.style.fontWeight = "bold";
  829. buttonElement.style.padding = "0 24px";
  830. buttonElement.style.borderRadius = "999999px";
  831. buttonElement.style.height = "40px";
  832. buttonElement.textContent = setFollowLanguage[0];
  833.  
  834. } catch (error) {
  835. console.error(error);
  836. buttonElement.removeAttribute("disabled");
  837. }
  838. })();
  839. } else if (buttonElement.classList.contains("cnpwVx")) {
  840. // フォロー解除
  841. (async () => {
  842. try {
  843. buttonElement.setAttribute("disabled", true);
  844.  
  845. const unfollowBody = new URLSearchParams({
  846. mode: "del",
  847. type: "bookuser",
  848. id: userId
  849. });
  850. const url = "https://www.pixiv.net/rpc_group_setting.php"
  851.  
  852. const setCsrfToken = await getCsrfToken();
  853. const response = await fetch(url, {
  854. method: "post",
  855. headers: {
  856. "Accept": "application/json",
  857. "x-csrf-token": setCsrfToken,
  858. },
  859. body: unfollowBody,
  860. credentials: "same-origin"
  861. });
  862.  
  863. if (!response.ok) { throw new Error(); }
  864.  
  865. buttonElement.removeAttribute("disabled");
  866. buttonElement.classList.remove("cnpwVx");
  867. buttonElement.classList.add("fOWAlD");
  868. buttonElement.removeAttribute("style");
  869. buttonElement.textContent = setFollowLanguage[1];
  870.  
  871. } catch (error) {
  872. console.error(error);
  873. buttonElement.removeAttribute("disabled");
  874. }
  875. })();
  876. }
  877. })
  878. }
  879. }
  880. // -----------------------------------------------------------------------------------------
  881.  
  882.  
  883.  
  884. let isProcessed = false;
  885. let currentUrl;
  886. let scrollPageCount = 0;
  887. const followingRegex = /https:\/\/www\.pixiv\.net(?:\/en)?\/users\/(\d+)\/following(?:\?p=(\d+))?/;
  888. const bookmarkRegex = /https:\/\/www\.pixiv\.net(?:\/en)?\/users\/(\d+)\/bookmarks\/artworks(?:\?p=(\d+))?/;
  889. const followUserWorkRegex = /https:\/\/www\.pixiv\.net\/bookmark_new_illust(_r18)?\.php(?:\?p=(\d+))?/;
  890. const tagRegex = /https:\/\/www\.pixiv\.net(?:\/en)?\/tags\/(.+)\/(artworks|illustrations|manga)(?:\?(order=date))?(?:(?:&|\?)(mode=(?:r18|safe)))?(?:(?:&|\?)(scd=\d{4}\-\d{2}-\d{2}))?(?:(?:&|\?)(ecd=\d{4}\-\d{2}-\d{2}))?(?:(?:&|\?)p=(\d+))?(?:(?:&|\?)(s_mode=(?:s_tag|s_tc)))?(?:(?:&|\?)type=([^&]+))?(?:(?:&|\?)(.+))?/;
  891.  
  892. const observer = new MutationObserver(mutationsList => {
  893. // URLが変更された際の処理
  894. if (window.location.href != currentUrl) {
  895. isProcessed = false;
  896. scrollPageCount = 0;
  897. // タグページで条件を変更した際に、追加した要素を削除する
  898. if (tagRegex.test(window.location.href)) {
  899. const removeElements = document.querySelectorAll(".addElement");
  900. for (const removeElement of removeElements) {
  901. removeElement.remove();
  902. }
  903. }
  904. }
  905.  
  906. if (followingRegex.test(window.location.href)) {
  907. // フォロー
  908. currentUrl = window.location.href;
  909. const intersectionTarget = document.querySelector(".sc-1y4z60g-4.cqwgCG");
  910.  
  911. if (intersectionTarget && !isProcessed) {
  912. isProcessed = true;
  913. const scrollObserver = new IntersectionObserver(entries => {
  914. entries.forEach((entry) => {
  915. if (entry.isIntersecting) {
  916. following_process();
  917. isProcessed = false;
  918. scrollObserver.unobserve(entry.target);
  919. }
  920. })
  921. });
  922. scrollObserver.observe(document.querySelector(".sc-1y4z60g-5.cPVjJh:last-child").previousElementSibling);
  923. }
  924. } else if (bookmarkRegex.test(window.location.href) || followUserWorkRegex.test(window.location.href) || tagRegex.test(window.location.href)) {
  925. // ブックマーク・フォローユーザーの作品・タグ検索
  926. currentUrl = window.location.href;
  927.  
  928. let checkType;
  929. if (bookmarkRegex.test(window.location.href)) {
  930. checkType = "bookmark";
  931. } else if (followUserWorkRegex.test(window.location.href)) {
  932. checkType = "follow";
  933. } else {
  934. checkType = "tag";
  935. }
  936.  
  937. let intersectionTarget;
  938. if (bookmarkRegex.test(window.location.href) || followUserWorkRegex.test(window.location.href)) {
  939. intersectionTarget = document.querySelector(".sc-9y4be5-1.jtUPOE");
  940. } else {
  941. intersectionTarget = document.querySelector(".sc-l7cibp-1.krFoBL img");
  942. }
  943.  
  944. if (intersectionTarget && !isProcessed) {
  945. isProcessed = true;
  946.  
  947. const options = { rootMargin: "0px 0px 300px 0px" };
  948. const scrollObserver = new IntersectionObserver(entries => {
  949. entries.forEach((entry) => {
  950. if (entry.isIntersecting) {
  951. bookmarkAndTag_process(checkType);
  952. isProcessed = false;
  953. scrollObserver.unobserve(entry.target);
  954. }
  955. })
  956. }, options);
  957.  
  958. if (bookmarkRegex.test(window.location.href) || followUserWorkRegex.test(window.location.href)) {
  959. scrollObserver.observe(document.querySelector(".sc-9y4be5-2.kFAPOq:last-child"));
  960. } else {
  961. // タグページで条件を切り替えた際に、要素を取得するタイミングを遅らせるためにsetTimeoutを使用
  962. // 2ページ目と3ページ目が同時に読み込まれてしまうので、2ページ目もsetTimeoutを使用
  963. if (scrollPageCount == 0 || scrollPageCount == 1) {
  964. setTimeout(() => {
  965. scrollObserver.observe(document.querySelector(".sc-l7cibp-2.gpVAva:last-child"));
  966. }, 400);
  967. } else {
  968. scrollObserver.observe(document.querySelector(".sc-l7cibp-2.gpVAva:last-child"));
  969. }
  970. }
  971. }
  972. }
  973. });
  974. const config = { childList: true, subtree: true };
  975. observer.observe(document.querySelector("#root"), config);

QingJ © 2025

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