IMDB works#

Shows number of credited works after names on IMDB site

目前为 2017-01-24 提交的版本。查看 最新版本

  1. // ==UserScript==//
  2. // @name IMDB works#
  3. // @description Shows number of credited works after names on IMDB site
  4. // @match *://*.imdb.com/*
  5. // @version 1.0.6
  6. // @author wOxxOm
  7. // @namespace wOxxOm.scripts
  8. // @license MIT License
  9. // @grant GM_addStyle
  10. // @run-at document-start
  11. // @require https://gf.qytechs.cn/scripts/12228/code/setMutationHandler.js
  12. // ==/UserScript==
  13.  
  14. var SELECTOR = 'a[href^="/name/nm"]';
  15. var CACHE_FRESH_DURATION = 30 * 24 * 3600 * 1000; // 1 month
  16. var CACHE_STALE_DURATION = CACHE_FRESH_DURATION * 2; // keep stale data another 2 months, then kill
  17.  
  18. GM_addStyle(
  19. '.number-of-works, .number-of-works span { opacity: 0.5; transition: opacity .25s ease-in-out .25s; }' +
  20. '.number-of-works span:before { content: "/"; }' +
  21. '.number-of-works:hover { opacity: 1.0; }' +
  22. '.number-of-works:hover span { opacity: 1.0; }' +
  23. '.number-of-works:before { content: " ["; opacity: 0.5; }' +
  24. '.number-of-works:after { content: "]"; opacity: 0.5; }'
  25. );
  26.  
  27. process(document.querySelectorAll(SELECTOR));
  28. setMutationHandler(document, SELECTOR, process);
  29.  
  30. function process(links) {
  31. var now = Date.now();
  32. for (var link, i = 0; (link = links[i++]); ) {
  33. if (link.querySelector('img') ||
  34. !link.textContent.trim() ||
  35. link.children[0] && link.textContent.trim() != link.children[0].textContent.trim() ||
  36. link.nextElementSibling && link.nextElementSibling.matches('.number-of-works'))
  37. continue;
  38. var id = (link.pathname.match(/\/name\/nm(\d+)\/?$/) || [])[1];
  39. if (!id)
  40. continue;
  41. var cacheKey = 'works#' + id;
  42. var cache = (localStorage[cacheKey] || '').split('\t');
  43. if (cache.length == 2 && +cache[0] && cache[1]) {
  44. showWorksNum(link, cache[1]);
  45. var isFresh = +cache[0] > now;
  46. if (isFresh)
  47. continue;
  48. }
  49. doXHR({
  50. url: link.pathname,
  51. link: link,
  52. cacheKey: cacheKey,
  53. onload: parseNamePage,
  54. });
  55. }
  56. }
  57.  
  58. function showWorksNum(link, num) {
  59. num = num.toString().replace(/\/(\d+)/, '<span>$1</span>');
  60. if (link.nextElementSibling && link.nextElementSibling.matches('.number-of-works')) {
  61. link.nextElementSibling.innerHTML = num;
  62. } else {
  63. link.insertAdjacentHTML('afterend', '<span class="number-of-works">' + num + '</span>');
  64. }
  65. }
  66.  
  67. function parseNamePage(doc, options) {
  68. var credits = [].map.call(doc.querySelectorAll('#filmography .head'), function(e) {
  69. return +(e.textContent.match(/\((\d+) credits?\)/i) || [])[1];
  70. });
  71. if (!credits.length)
  72. return;
  73. var creditsSum = credits.reduce(function(sum, e) { return sum + e; }, 0);
  74. var worksNum = credits[0] + (credits.length > 1 ? '/' + creditsSum : '');
  75. showWorksNum(options.link, worksNum);
  76. localStorage[options.cacheKey] = '' + (Date.now() + CACHE_FRESH_DURATION) + '\t' + worksNum;
  77. }
  78.  
  79. function doXHR(options) {
  80. if (document.readyState == 'complete') {
  81. sendRequest(options);
  82. return;
  83. }
  84. if (!doXHR.queue)
  85. initQueue();
  86. if (!isDupe()) {
  87. doXHR.queue.push(options);
  88. doXHR.queuedUrl[options.url] = options;
  89. }
  90.  
  91. function sendRequest(options) {
  92. var xhr = new XMLHttpRequest();
  93. xhr.open('GET', options.url);
  94. xhr.responseType = 'document';
  95. xhr.onload = function(e) {
  96. options.onload(xhr.response, options);
  97. doXHR.activeRequests--;
  98. poolQueue();
  99. };
  100. doXHR.activeRequests++;
  101. xhr.send();
  102. }
  103.  
  104. function initQueue() {
  105. doXHR.queue = [];
  106. doXHR.queuedUrl = {};
  107. doXHR.activeRequests = 0;
  108. document.addEventListener('DOMContentLoaded', function docLoaded() {
  109. document.removeEventListener('DOMContentLoaded', docLoaded);
  110. cleanupStorage();
  111. poolQueue();
  112. });
  113. }
  114.  
  115. function isDupe() {
  116. var dupe = doXHR.queuedUrl[options.url];
  117. if (!dupe)
  118. return false;
  119. if (dupe.link == options.link)
  120. return true;
  121. // this request's link element will use the will-be-cached data from the earlier queued request
  122. options.url = '';
  123. var _onload = dupe.onload;
  124. dupe.onload = function() {
  125. _onload.apply(null, arguments);
  126. showWorksNum(options.link, localStorage[options.cacheKey].split('\t')[1]);
  127. };
  128. return true;
  129. }
  130.  
  131. function poolQueue() {
  132. while (doXHR.queue.length && doXHR.activeRequests < 16) {
  133. sendRequest(doXHR.queue.shift());
  134. }
  135. }
  136. }
  137.  
  138. function cleanupStorage() {
  139. setTimeout(function doCleanup() {
  140. var now = Date.now(), i = 0;
  141. for (var k in localStorage) {
  142. if (k.lastIndexOf('works#', 0) === 0 && (+localStorage[k].split('\t')[0]) + CACHE_STALE_DURATION < now) {
  143. delete localStorage[k];
  144. }
  145. if (++i % 100 === 0 && Date.now() - now > 10) {
  146. setTimeout(doCleanup, 1000);
  147. return;
  148. }
  149. }
  150. }, 1000);
  151. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址