您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds tags count indicator to list of entries
当前为
- // ==UserScript==
- // @name MFC tag counter
- // @namespace https://takkkane.tumblr.com/scripts/mfcTagCounter
- // @version 0.1.3
- // @description Adds tags count indicator to list of entries
- // @author Nefere
- // @supportURL https://twitter.com/TaxDelusion
- // @match https://myfigurecollection.net/entry/*
- // @match https://myfigurecollection.net/browse.v4.php*
- // @match https://myfigurecollection.net/browse/calendar/*
- // @match https://myfigurecollection.net/*
- // @match https://myfigurecollection.net/item/browse/figure/
- // @match https://myfigurecollection.net/item/browse/goods/
- // @match https://myfigurecollection.net/item/browse/media/
- // @match https://myfigurecollection.net/item/browse/calendar/*
- // @icon https://www.google.com/s2/favicons?sz=64&domain=myfigurecollection.net
- // @license MIT
- // @icon https://www.google.com/s2/favicons?sz=64&domain=myfigurecollection.net
- // @license MIT
- // @grant GM.getValue
- // @grant GM.setValue
- // ==/UserScript==
- (async function () {
- 'use strict';
- /**
- * Name of the class used for a tag indicator container.
- * It should be not used on the page it's inserted to.
- **/
- var TAG_CLASSNAME = "us-tag";
- /**
- * Name of the class that does not appear on the page.
- * Used to return empty collections of nodes from functions.
- **/
- var FAKE_CLASS_PLACEHOLDER = "what-i-was-looking-for";
- /**
- * A time in miliseconds to wait between requests for /entry pages.
- * Too short time may results in "429 - Too many requests" error responses.
- * Can be increased with REQUEST_DELAY_MULTIPLIER.
- **/
- var REQUEST_DELAY = 1000;
- /**
- * A multipler that is used on REQUEST_DELAY when 429 response error is obtained.
- * Should be over 1 to properly work.
- **/
- var REQUEST_DELAY_MULTIPLIER = 1.1;
- /**
- * A time in seconds for how long the entry data saved in a cache is considered "fresh" and up to date.
- * After the entry data is "rotten", it is removed from cache and may be replaced with new data.
- **/
- var CACHE_FRESH_SECONDS = 10 * 60;
- /**
- * Map entries for tagCounterCache that are yet to be persisted in the extension storage.
- **/
- var CACHE_SAVE_ENTRIES = [];
- /**
- * How many entries have to be added to the cache so the cache can be persisted in the extension storage.
- * It requires using GM.getValue() and GM.setValue()
- **/
- var CACHE_SAVE_AFTER_SETTING_VALUES_ORDER = 5;
- /**
- * A cache for tag count indicated in the entry page.
- * It's a Map() consisted of:
- * * keys: pathname of an entry page ("/entry/2")
- * * values: object with fields:
- * ** number: integer with number of tags on the entry page (24)
- * ** updatedTime: timestamp of when the map was updated.
- * Map entries may be deleted after time indicated in CACHE_FRESH_SECONDS.
- **/
- var tagCounterCache;
- function sleep(ms) {
- return new Promise(resolve => setTimeout(resolve, ms));
- };
- async function getTagCounterCache() {
- return new Map(Object.entries(
- JSON.parse(await GM.getValue('tagCounterCache', '{}'))));
- };
- async function saveTagCounterCache() {
- var newTagCounterCache = await getTagCounterCache();
- for (var entry of CACHE_SAVE_ENTRIES) {
- newTagCounterCache.set(entry.key, entry.value);
- }
- GM.setValue('tagCounterCache', JSON.stringify(Object.fromEntries(newTagCounterCache)));
- tagCounterCache = newTagCounterCache;
- newTagCounterCache.length = 0; /* clear new data as they are persisted */
- };
- async function pushToTagCounterCache(url, tagCounter) {
- if (tagCounter) {
- var time = Date.now();
- var entry = {
- key: url,
- value: {
- 'number': tagCounter,
- 'updatedTime': time
- }
- };
- tagCounterCache.set(entry.key, entry.value);
- CACHE_SAVE_ENTRIES.push(entry);
- if (CACHE_SAVE_ENTRIES.length % CACHE_SAVE_AFTER_SETTING_VALUES_ORDER == 0) {
- saveTagCounterCache();
- }
- }
- };
- function getTagCounterFromTagCounterCache(url) {
- var tagCounterPair = tagCounterCache.get(url);
- if (tagCounterPair == null) {
- return 0;
- }
- var rottenPairDate = new Date(tagCounterPair.updatedTime);
- rottenPairDate.setSeconds(rottenPairDate.getSeconds() + CACHE_FRESH_SECONDS);
- if (rottenPairDate < Date.now()) {
- tagCounterCache.delete(url);
- return 0;
- }
- return tagCounterPair.number;
- };
- function addStyles() {
- $("<style>")
- .prop("type", "text/css")
- .html("\
- .item-icon ." + TAG_CLASSNAME + " {\
- position: absolute;\
- display: block;\
- right: 1px;\
- bottom: 1px;\
- height: 16px;\
- padding: 0 4px;\
- font-weight: 700;\
- color: gold;\
- background-color: darkgreen\
- }")
- .appendTo("head");
- };
- function getEntryContainers() {
- var pathname = window.location.pathname;
- var search = window.location.search;
- var searchParams = new URLSearchParams(search);
- var tbParam = searchParams.get("_tb");
- if (pathname.includes("/entry/") /* encyclopedia entry */
- || pathname.includes("/browse.v4.php") /* search results with filters */
- || pathname.includes("/browse/calendar/") /* calendar page */
- || pathname.includes("/item/browse/calendar/") /* new calendar page */
- || pathname.includes("/item/browse/figure/") /* new figures page */
- || pathname.includes("/item/browse/goods/") /* new goods page */
- || pathname.includes("/item/browse/media/") /* new media page */
- || tbParam !== null) {
- var result = $("#wide .result:not(.hidden)");
- return result;
- }
- console.log("unsupported getEntryContainers");
- return $(FAKE_CLASS_PLACEHOLDER);
- };
- function isDetailedList() {
- var search = window.location.search;
- var searchParams = new URLSearchParams(search);
- var outputParam = searchParams.get("output"); /* 0 - detailedList, 1,2 - grid, 3 - diaporama */
- return outputParam == 0;
- };
- function getItemsFromContainer(entryContainer) {
- var icons = $(entryContainer).find(".item-icons .item-icon");
- if (icons.length > 0) {
- return icons;
- }
- var pathname = window.location.pathname;
- if (pathname.includes("/browse.v4.php") /* search page, detailed list view */
- && isDetailedList()) {
- return $(FAKE_CLASS_PLACEHOLDER);
- }
- console.log("unsupported getItemsFromContainer");
- return $(FAKE_CLASS_PLACEHOLDER);
- };
- function getTagCounterFromHtml(html) {
- var parser = new DOMParser();
- var doc = parser.parseFromString(html, 'text/html');
- var tagCounterNode = doc.querySelector('.tbx-target-TAGS .count');
- return tagCounterNode.textContent;
- };
- function addTagCounterToSearchResult(itemLinkElement, countOfTags) {
- var tagElement = document.createElement("span");
- tagElement.setAttribute("class", TAG_CLASSNAME);
- tagElement.textContent = countOfTags;
- itemLinkElement.appendChild(tagElement);
- };
- async function fetchAndHandle(queue) {
- var resultQueue = [];
- for (var itemElement of queue) {
- var itemLinkElement = itemElement.firstChild;
- var entryLink = itemLinkElement.getAttribute("href");
- fetch(entryLink, {
- headers: {
- "User-Agent": GM.info.script.name + " " + GM.info.script.version
- }
- }).then(function (response) {
- if (response.ok) {
- return response.text();
- }
- return Promise.reject(response);
- }).then(function (html) {
- var countOfTags = getTagCounterFromHtml(html);
- addTagCounterToSearchResult(itemLinkElement, countOfTags);
- pushToTagCounterCache(entryLink, countOfTags);
- }).catch(function (err) {
- if (err.status == 429) {
- console.warn('Too many requests. Added the request to fetch later', err.url);
- resultQueue.push(itemElement);
- REQUEST_DELAY = REQUEST_DELAY * REQUEST_DELAY_MULTIPLIER;
- console.info('Increased delay to ' + REQUEST_DELAY);
- }
- });
- await sleep(REQUEST_DELAY);
- }
- return resultQueue;
- };
- async function main() {
- var cacheQueue = [];
- var entryContainers = getEntryContainers();
- entryContainers.each(function (i, entryContainer) {
- var itemsElements = getItemsFromContainer(entryContainer);
- itemsElements.each(function (i, itemElement) {
- cacheQueue.push(itemElement);
- });
- });
- var queue = [];
- tagCounterCache = await getTagCounterCache();
- for (var itemElement of cacheQueue) {
- var itemLinkElement = itemElement.firstChild;
- var entryLink = itemLinkElement.getAttribute("href");
- var cache = getTagCounterFromTagCounterCache(entryLink);
- if (cache > 0) {
- addTagCounterToSearchResult(itemLinkElement, cache);
- } else {
- queue.push(itemElement);
- }
- }
- while (queue.length) {
- queue = await fetchAndHandle(queue);
- }
- saveTagCounterCache();
- };
- addStyles();
- main();
- })();