Automatically adds links to your favorite works on a specific artist page(/users/xxxxxxxx).
// ==UserScript==
// @name Pixiv Faview
// @namespace https://solact.blogspot.com/
// @version 0.2
// @description Automatically adds links to your favorite works on a specific artist page(/users/xxxxxxxx).
// @description:ja 作者ごとの作品一覧ページ(/users/xxxxxxxx)で、自分がお気に入りに追加した作品へのリンクを自動で追加します。
// @author Solact
// @include https://www.pixiv.net/*
// @match https://www.pixiv.net/*
// @license GPL-3.0
// @icon https://www.google.com/s2/favicons?domain=pixiv.net
// @grant none
// @noframes
// ==/UserScript==
(async () => {
'use strict';
// definition
const i18nLib = {
'ja': {
works: '作品',
illusts_manga: 'イラスト・マンガ',
favs: 'ブックマーク',
},
'en': {
works: 'Works',
illusts_manga: 'Illustrations and Manga',
favs: 'Bookmarks',
},
'ko': {
works: '작품',
illusts_manga: '일러스트・만화',
favs: '북마크',
},
'zh-cn': {
works: '作品',
illusts_manga: '插画·漫画',
favs: '收藏',
},
'zh-tw': {
works: '作品',
illusts_manga: '插畫·漫畫',
favs: '收藏',
},
};
const i18n = key => i18nLib[lang][key];
const debug = false;
const debugMsg = (...str) => debug ? console.log(str) : '';
// main code
const lang = document.documentElement.getAttribute('lang').toLowerCase();
if (!lang) {
throw new Error('pixiv lang not found');
return;
}
// initialize
let prevHref, prevUrlUserID;
let bookmarksCount, $bookmarksNode;
let isOnUserPage, rewriteDone;
const mainLoop = setInterval(async () => {
if (location.href != prevHref) {
// page changed (SPA)
debugMsg('location.href changed', location.href);
prevHref = location.href;
if (!location.href.match(/users\/(\d+)/) || location.href.match(/users\/(\d+)\/bookmarks/)) {
// not on pixiv.net/users/xxx
// ignore pixiv.net/users/xxx/bookmarks
debugMsg("not on an artist's user page");
isOnUserPage = false;
return;
}
const urlUserID = location.href.match(/users\/(\d+)/)[1];
if (urlUserID != prevUrlUserID) {
let profileAllJson, profileIdsJsonBodyWorks = {};
// user id changed
debugMsg('urlUserID changed', urlUserID);
prevUrlUserID = urlUserID;
// get an artist's all works
profileAllJson = await fetch(`/ajax/user/${urlUserID}/profile/all?lang=${lang}`).then(res => {
if (res.ok) {
return res.json();
} else {
throw new Error('pixiv API [/ajax/user/UID/profile/all] failed');
}
});
let illustIds = Object.keys({ ...profileAllJson.body.illusts, ...profileAllJson.body.manga });
while (illustIds.length) {
const illustIdsRemaining = illustIds.splice(100); // default 48, maximum approved 100
const params = { 'work_category': 'illust', 'is_first_page': 1, 'lang': lang };
const qs = new URLSearchParams(params);
for (const id of illustIds) {
qs.append('ids[]', id);
}
// get details of an artist's all works
const tmpJson = await fetch(`/ajax/user/${urlUserID}/profile/illusts?${qs}`).then(res => {
if (res.ok) {
return res.json();
} else {
throw new Error('pixiv API [/ajax/user/UID/profile/illusts] failed');
}
});
profileIdsJsonBodyWorks = { ...profileIdsJsonBodyWorks, ...tmpJson.body.works };
illustIds = illustIdsRemaining;
}
// bookmarksCount = parseInt(profileAllJson.body.bookmarkCount.public.illust) + parseInt(profileAllJson.body.bookmarkCount.public.novel);
bookmarksCount = 0;
$bookmarksNode = document.createElement('ul');
for (const [id, val] of Object.entries(profileIdsJsonBodyWorks)) {
if (val.bookmarkData) {
// found a work which you liked
const url = `https://www.pixiv.net/artworks/${id}`;
const $a = document.createElement('a');
$a.setAttribute('href', url);
$a.innerHTML = val.title;
$a.style.color = 'red';
const $li = document.createElement('li');
$li.appendChild($a);
$bookmarksNode.appendChild($li);
bookmarksCount++;
}
}
debugMsg('bookmarksCount', bookmarksCount);
debugMsg('$bookmarksNode', $bookmarksNode);
// we have to set this flag here or we'll get (Bookmarks:undefined)
// because the next 1000ms timer will start rewriting before $bookmarksNode is prepared
isOnUserPage = true;
}
else {
debugMsg('urlUserId not changed');
isOnUserPage = true;
}
rewriteDone = false;
}
if (!isOnUserPage || rewriteDone) {
// return if we're not on an artist's user page
// return if we're on an artist's user page and we've finished rewriting
return;
}
// wait for target and rewrite it
debugMsg('wait for target');
for (const $elem of document.querySelectorAll("h2")) {
if ($elem.innerHTML == i18n('illusts_manga') || $elem.innerHTML == i18n('works')) {
// add fav count like Illustrations and Manga(Bookmarks: xxx)
$elem.innerHTML += `(${i18n('favs')}: ${bookmarksCount})`;
// add fav list after fav count with red title
const insertAfter = (referenceNode, newNode) => {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
};
insertAfter($elem.parentNode.parentNode.parentNode, $bookmarksNode);
debugMsg('rewriting');
rewriteDone = true;
}
}
}, 1000);
})();