// ==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 // @downloadURL none // ==/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+)/, '$1'); if (link.nextElementSibling && link.nextElementSibling.matches('.number-of-works')) { link.nextElementSibling.innerHTML = num; } else { link.insertAdjacentHTML('afterend', '' + num + ''); } } 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); }