您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Shows number of credited works after names on IMDB site
- // ==UserScript==//
- // @name IMDB works#
- // @description Shows number of credited works after names on IMDB site
- // @match *://*.imdb.com/*
- // @version 1.0.7
- // @author wOxxOm
- // @namespace wOxxOm.scripts
- // @license MIT License
- // @grant GM_addStyle
- // @run-at document-start
- // @require https://greasyfork.org/scripts/12228/code/setMutationHandler.js
- // ==/UserScript==
- var SELECTOR = 'a[href^="/name/nm"]';
- var CACHE_FRESH_DURATION = 30 * 24 * 3600 * 1000; // 1 month
- var CACHE_STALE_DURATION = CACHE_FRESH_DURATION * 2; // keep stale data another 2 months, then kill
- GM_addStyle(
- '.number-of-works, .number-of-works span { opacity: 0.5; transition: opacity .25s ease-in-out .25s; }' +
- '.number-of-works span:before { content: "/"; }' +
- '.number-of-works:hover { opacity: 1.0; }' +
- '.number-of-works:hover span { opacity: 1.0; }' +
- '.number-of-works:before { content: " ["; opacity: 0.5; }' +
- '.number-of-works:after { content: "]"; opacity: 0.5; }'
- );
- process(document.querySelectorAll(SELECTOR));
- setMutationHandler(document, SELECTOR, process);
- if (location.pathname.match(/\/name\/nm\d+\/?$/)) {
- document.addEventListener('DOMContentLoaded', function() {
- parseNamePage(document, {cacheKey: 'works#' + location.pathname.match(/\/name\/nm(\d+)\/?$/)[1]});
- });
- }
- function process(links) {
- var now = Date.now();
- for (var link, i = 0; (link = links[i++]); ) {
- if (link.querySelector('img') ||
- !link.textContent.trim() ||
- link.children[0] && link.textContent.trim() != link.children[0].textContent.trim() ||
- link.nextElementSibling && link.nextElementSibling.matches('.number-of-works'))
- continue;
- var id = (link.pathname.match(/\/name\/nm(\d+)\/?$/) || [])[1];
- if (!id)
- continue;
- var cacheKey = 'works#' + id;
- var cache = (localStorage[cacheKey] || '').split('\t');
- if (cache.length == 2 && +cache[0] && cache[1]) {
- showWorksNum(link, cache[1]);
- var isFresh = +cache[0] > now;
- if (isFresh)
- continue;
- }
- doXHR({
- url: link.pathname,
- link: link,
- cacheKey: cacheKey,
- onload: parseNamePage,
- });
- }
- }
- function showWorksNum(link, num) {
- num = num.toString().replace(/\/(\d+)/, '<span>$1</span>');
- if (link.nextElementSibling && link.nextElementSibling.matches('.number-of-works')) {
- link.nextElementSibling.innerHTML = num;
- } else {
- link.insertAdjacentHTML('afterend', '<span class="number-of-works">' + num + '</span>');
- }
- }
- function parseNamePage(doc, options) {
- var credits = [].map.call(doc.querySelectorAll('#filmography .head'), function(e) {
- return {
- num: +(e.textContent.match(/\((\d+) credits?\)/i) || [])[1],
- type: (e.querySelector('a[name]') || {name: ''}).name,
- };
- });
- if (!credits.length)
- return;
- var creditsSum = credits.reduce(function(sum, e) { return sum + e.num; }, 0);
- var rxIgnore = /^(self|archive_footage|thanks)$/;
- var creditsSorted = credits.sort(function(a, b) {
- if (rxIgnore.test(a.type))
- return 1;
- else if (rxIgnore.test(b.type))
- return -1;
- else
- return b.num - a.num || (a.type == 'actor' ? -1 : b.type == 'actor' ? 1 : 0);
- });
- var worksNum = rxIgnore.test(creditsSorted[0].type) ? creditsSum : creditsSorted[0].num + (credits.length > 1 ? '/' + creditsSum : '');
- localStorage[options.cacheKey] = '' + (Date.now() + CACHE_FRESH_DURATION) + '\t' + worksNum;
- if (options.link)
- showWorksNum(options.link, worksNum);
- }
- function doXHR(options) {
- if (document.readyState == 'complete') {
- sendRequest(options);
- return;
- }
- if (!doXHR.queue)
- initQueue();
- if (!isDupe()) {
- doXHR.queue.push(options);
- doXHR.queuedUrl[options.url] = options;
- }
- function sendRequest(options) {
- var xhr = new XMLHttpRequest();
- xhr.open('GET', options.url);
- xhr.responseType = 'document';
- xhr.onload = function(e) {
- options.onload(xhr.response, options);
- doXHR.activeRequests--;
- poolQueue();
- };
- doXHR.activeRequests++;
- xhr.send();
- }
- function initQueue() {
- doXHR.queue = [];
- doXHR.queuedUrl = {};
- doXHR.activeRequests = 0;
- document.addEventListener('DOMContentLoaded', function() {
- cleanupStorage();
- poolQueue();
- });
- }
- function isDupe() {
- var dupe = doXHR.queuedUrl[options.url];
- if (!dupe)
- return false;
- if (dupe.link == options.link)
- return true;
- // this request's link element will use the will-be-cached data from the earlier queued request
- options.url = '';
- var _onload = dupe.onload;
- dupe.onload = function() {
- _onload.apply(null, arguments);
- showWorksNum(options.link, localStorage[options.cacheKey].split('\t')[1]);
- };
- return true;
- }
- function poolQueue() {
- while (doXHR.queue.length && doXHR.activeRequests < 16) {
- sendRequest(doXHR.queue.shift());
- }
- }
- }
- function cleanupStorage() {
- setTimeout(function doCleanup() {
- var now = Date.now(), i = 0;
- for (var k in localStorage) {
- if (k.lastIndexOf('works#', 0) === 0 && (+localStorage[k].split('\t')[0]) + CACHE_STALE_DURATION < now) {
- delete localStorage[k];
- }
- if (++i % 100 === 0 && Date.now() - now > 10) {
- setTimeout(doCleanup, 1000);
- return;
- }
- }
- }, 1000);
- }