// ==UserScript==
// @name Pixiv Infinite Scroll
// @name:ja Pixiv Infinite Scroll
// @name:zh-CN Pixiv Infinite Scroll
// @name:zh-TW Pixiv Infinite Scroll
// @namespace https://github.com/chimaha/Pixiv-Infinite-Scroll
// @match https://www.pixiv.net/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @version 1.4.4.2
// @author chimaha
// @description Add infinite scroll feature to Pixiv.
// @description:ja Pixivに無限スクロール機能を追加します。
// @description:zh-CN 为 Pixiv 添加无限滚动功能。
// @description:zh-TW 因為Pixiv有無限移動功能。
// @license MIT license
// @compatible firefox
// @compatible chrome
// @icon https://raw.githubusercontent.com/chimaha/Pixiv-Infinite-Scroll/main/icon.png
// @supportURL https://github.com/chimaha/Pixiv-Infinite-Scroll/issues
// @downloadURL none
// ==/UserScript==
/*! Pixiv Infinite Scroll | MIT license | https://github.com/chimaha/Pixiv-Infinite-Scroll/blob/main/LICENSE */
"use strict";
let showDividingLine = GM_getValue("dividingLine", true);
// エスケープHTML
function escapeText(str) {
if (typeof str != "string") {
return str;
}
return str
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
const fetchResponse = async (url) => {
let response;
let json;
try {
response = await fetch(url);
if (!response.ok) { throw new Error(`Response Error:${response.status}`); }
json = await response.json();
return json;
} catch (error) {
console.error(error);
}
}
// イラストをマウスオーバーでopacity変更
function mouseover() {
for (const element of document.querySelectorAll(`[data-page="${scrollPageCount + 1}"] a.khjDVZ`)) {
element.addEventListener("mouseover", () => {
element.style.opacity = "0.8";
});
element.addEventListener("mouseleave", () => {
element.style.opacity = "1";
});
}
}
// フォロー中の無限スクロール-----------------------------------------------------------------
function following_process() {
// langの値によって言語を変更する
const setFollowLanguage = [];
const currentLanguage = document.querySelector("html").getAttribute("lang");
switch (currentLanguage) {
case "ja":
setFollowLanguage.push("フォロー中", "フォローする");
break;
case "ko":
setFollowLanguage.push("팔로우 중", "팔로우하기");
break;
case "zh-CN":
setFollowLanguage.push("已关注", "加关注");
break;
case "zh-TW":
setFollowLanguage.push("關注中", "加關注");
break;
default:
setFollowLanguage.push("Following", "Follow");
}
let borderCount = 0;
function createElement(userId, userName, userProfileImage, userComment, userFollowing, illustId, illustTitle, illustUrl, illustBookmarkData, illustAlt, illustR18, illustPageCount) {
// ページ区切り線
borderCount++;
if (borderCount == 1 && showDividingLine) {
const borderElement = `
${borderOffset}
`;
document.querySelector(".sc-1y4z60g-4.cqwgCG").insertAdjacentHTML("beforeend", borderElement);
}
// フォロー中・フォローするを切り替え
let changeFollowLanguage;
let followClass;
let followStyle = "";
if (userFollowing) {
changeFollowLanguage = setFollowLanguage[0];
followClass = "cnpwVx";
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;"';
} else {
changeFollowLanguage = setFollowLanguage[1];
followClass = "fOWAlD";
}
// ブックマークを切り替え
let bookmarkClass = [];
let bookmarkStyle = [];
for (const checkBookmark of illustBookmarkData) {
if (checkBookmark) {
bookmarkClass.push("bXjFLc");
bookmarkStyle.push('style="color: rgb(255, 64, 96); fill: currentcolor;"');
} else {
bookmarkClass.push("dxYRhf");
}
}
// R18マーク
let r18Element = [];
for (const checkR18 of illustR18) {
if (checkR18 == "R-18") {
r18Element.push('');
} else {
r18Element.push("");
}
}
// うごくイラスト再生マーク。イラスト枚数表示
let ugoiraElement = [];
let pageCountElement = [];
illustPageCount.forEach((pageCount, i) => {
if (illustAlt[i].slice(-4) == "うごイラ") {
ugoiraElement.push('');
pageCountElement.push("");
} else {
ugoiraElement.push("");
if (pageCount >= 2) {
pageCountElement.push(`
`);
} else {
pageCountElement.push("");
}
}
});
// イラストがない場合は表示しないようにするため、分けて作成する
let illustGroup = "";
for (let i = 0; i < illustId.length; i++) {
illustGroup += `
`;
}
let illustContainer = "";
if (illustId[0]) {
illustContainer = `
`;
}
// "appendElements+="で一括追加にすると、なぜかundefinedが追加され続けるので一つずつ追加
const appendElements = `
${escapeText(userComment)}
${illustContainer}
`;
document.querySelector(".sc-1y4z60g-4").insertAdjacentHTML("beforeend", appendElements);
}
// https://www.pixiv.net/ajax/user/*/following?offset=24&limit=24&rest=show
// https://www.pixiv.net/users/*/following?p=2
const illustItems = document.querySelectorAll(".sc-1y4z60g-5");
if (illustItems.length < 24 && scrollPageCount == 1) { return; }
// URL作成
const matches = location.href.match(followingRegex);
let offset;
let borderOffset;
if (matches[2] && isValid) {
scrollPageCount == 0 ? saveScrollPageCount = Number(matches[2]) : "";
offset = (saveScrollPageCount * 24) + (scrollPageCount * 24);
borderOffset = scrollPageCount + saveScrollPageCount + 1;
} else {
scrollPageCount == 0 ? saveScrollPageCount = 0 : "";
isValid = false;
offset = 24 + (scrollPageCount * 24);
borderOffset = scrollPageCount + 2;
}
scrollPageCount++;
if (scrollPageCount == 1) {
revertURL(illustItems, 23, 30);
}
const url = `https://www.pixiv.net/ajax/user/${matches[1]}/following?offset=${offset}&limit=24&rest=show`;
const fetchData = async () => {
const json = await fetchResponse(url);
for (let i = 0; i < Object.keys(json.body.users).length; i++) {
const users = json.body.users[i];
const userId = users.userId;
const userName = users.userName;
const userProfileImage = users.profileImageUrl;
const userComment = users.userComment.slice(0, 98);
const userFollowing = users.following;
const illustId = [];
const illustTitle = [];
const illustUrl = [];
const illustBookmarkData = [];
const illustAlt = [];
const illustR18 = [];
const illustPageCount = [];
for (let j = 0; j < Object.keys(json.body.users[i].illusts).length; j++) {
const illusts = json.body.users[i].illusts[j];
illustId.push(illusts.id);
illustTitle.push(illusts.title);
illustUrl.push(illusts.url);
illustBookmarkData.push(illusts.bookmarkData);
illustAlt.push(illusts.alt);
illustR18.push(illusts.tags[0]);
illustPageCount.push(illusts.pageCount);
}
createElement(userId, userName, userProfileImage, userComment, userFollowing, illustId, illustTitle, illustUrl, illustBookmarkData, illustAlt, illustR18, illustPageCount);
}
mouseover();
};
(async () => {
await fetchData();
bookmarkAddDelete();
followAndUnfollow(setFollowLanguage);
changeURL(matches[2], false, 23, 30);
})();
}
// -----------------------------------------------------------------------------------------
// ブックマーク・フォローユーザーの作品・タグ検索・プロフィールページの無限スクロール--------------
function bookmarkAndTag_process(checkType, matches) {
function createElement(illustId, illustTitle, illustUrl, userId, userName, illustPageCount, illustBookmarkData, illustAlt, userProfileImage, typeElement, typeClass, illustR18, illustMaskReason) {
// langの値によって言語を変更する
const setDeletedLanguage = [];
const currentLanguage = document.querySelector("html").getAttribute("lang");
switch (currentLanguage) {
case "ja":
setDeletedLanguage.push("R18 / R18G", "作品", "閲覧制限中", "削除済み", "もしくは非公開");
break;
case "ko":
setDeletedLanguage.push("R-18 / R-18G", "작품", "열람 제한 중", "삭제됨", "혹은 비공개");
break;
case "zh-CN":
setDeletedLanguage.push("R-18 / R-18G", "作品", "浏览受限(含成人内容)", "已删除", "或不公开");
break;
case "zh-TW":
setDeletedLanguage.push("R-18 / R-18G", "作品", "瀏覽受限(含成人內容)", "已刪除", "或非公開");
break;
default:
setDeletedLanguage.push("R-18/R-18G", "works", "Restricted (Adult Content)", "Deleted", "or private");
}
// ブックマークを切り替え
let bookmarkClass = "";
let bookmarkStyle = "";
if (illustBookmarkData) {
bookmarkClass = "bXjFLc";
bookmarkStyle = 'style="color: rgb(255, 64, 96); fill: currentcolor;"';
} else {
bookmarkClass = "dxYRhf";
}
// R18マーク
let r18Element = "";
if (illustR18 == "R-18") {
r18Element = `
`;
}
// うごくイラスト再生マーク・イラスト数表示
let ugoiraElement = "";
let pageCountElement = "";
if (illustAlt.slice(-4) == "うごイラ") {
ugoiraElement = '