IMDb display RottenTomatoes info: redux

Display RottenTomatoes Tomatometer rating info on IMDb pages

  1. // ==UserScript==
  2. // @name IMDb display RottenTomatoes info: redux
  3. // @namespace driver8.net
  4. // @version 0.2.1.2
  5. // @description Display RottenTomatoes Tomatometer rating info on IMDb pages
  6. // @author driver8
  7. // @license GNU AGPLv3
  8. // @match *://*.imdb.com/*
  9. // @grant GM_xmlhttpRequest
  10. // @grant GM_getValue
  11. // @grant GM_setValue
  12. // @grant GM_addStyle
  13. // @connect rottentomatoes.com
  14. // @require https://greasyfork.org/scripts/389810-rottentomatoes-utility-library-custom-api/code/RottenTomatoes%20Utility%20Library%20(custom%20API).js?version=959077
  15. // ==/UserScript==
  16.  
  17. (function() {
  18. 'use strict';
  19.  
  20. console.log('hi imdb rt');
  21.  
  22. const MAX_RESULT_AGE = 1; //in days
  23.  
  24. function parse(query, regex, doc) {
  25. doc = doc || document;
  26. try {
  27. let text = doc.querySelector(query).textContent.trim();
  28. if (regex) {
  29. text = text.match(regex)[1];
  30. }
  31. return text.trim();
  32. } catch (e) {
  33. console.log('error', e);
  34. return '';
  35. }
  36. };
  37.  
  38. function start() {
  39. let m = document.title.match(/\((?:TV\s+(?:Series|Mini.?Series|Episode|Movie)\s*)?(\d{4})\s*(?:–|-)?\s*(?:\d{4})?\s*\)/);
  40. if (m) {
  41. let year = parseInt(m[1]);
  42. // Skip TV episodes
  43. if (!document.title.match(/\(TV\s+Episode/)) {
  44. let title = parse('.TitleHeader__TitleText-sc-1wu6n3d-0, .title_wrapper > h1, .sc-b73cd867-0, .eKrKux', /([^()]+)/);
  45. let tv = document.title.match(/\(TV\s+(?:Mini.?)?Series/) ? true : false;
  46. console.log(title, year, tv);
  47. let id = title + year + (tv ? 'tv' : 'movie');
  48. let data = GM_getValue(id, false);
  49. let age_check = true;
  50. try {
  51. age_check = new Date().getFullYear() - parseInt(data.year) > 2; // If movie is older than 2 years, we prob don't need to update the data
  52. age_check = age_check || (Date.now() - new Date(data.fetched).getTime() < MAX_RESULT_AGE * 24 * 60 * 60 * 1000); // Update data for results older than X days
  53. } catch (e) {
  54. age_check = false;
  55. }
  56. if (data instanceof Object && age_check) {
  57. console.log('Data EXISTS', data);
  58. displayData(data);
  59. } else {
  60. // Create spinner gif
  61. let spinnerDiv = document.createElement('div');
  62. spinnerDiv.innerHTML = `<img src="${spinnerGif}">`;
  63. spinnerDiv.id = 'spinner-div';
  64. spinnerDiv.classList.add('imdbRating');
  65. insertRating(spinnerDiv);
  66.  
  67. getRtInfoFromTitle(title, tv, year).then((freshData) => {
  68. GM_setValue(id, freshData);
  69. spinnerDiv.parentNode.removeChild(spinnerDiv);
  70. displayData(freshData);
  71. }).catch((error) => {
  72. console.log('Error getting data:', error);
  73. spinnerDiv.parentNode.removeChild(spinnerDiv);
  74. });
  75. }
  76. }
  77. }
  78. }
  79.  
  80. function displayData(data) {
  81. let score = data.score > -1 ? data.score : 'TBD',
  82. scoreColor = data?.state?.match(/fresh/) ? '#BBFF88' : 'gray',
  83. rating = data.rating > -1 ? data.rating : 'N/A',
  84. votes = data.votes || 0,
  85. consensus = data.consensus || '',
  86. bg = '',
  87. icons = {
  88. 'certified': 'url(https://www.rottentomatoes.com/assets/pizza-pie/images/icons/global/cf-lg.3c29eff04f2.png) no-repeat',
  89. 'fresh': 'url(https://www.rottentomatoes.com/assets/pizza-pie/images/icons/global/new-fresh-lg.12e316e31d2.png) no-repeat',
  90. 'rotten': 'url(https://www.rottentomatoes.com/assets/pizza-pie/images/icons/global/new-rotten-lg.ecdfcf9596f.png) no-repeat',
  91. 'question': 'url(https://www.rottentomatoes.com/assets/pizza-pie/images/poster_default.c8c896e70c3.gif) no-repeat' // not used
  92. };
  93. for (let status in icons) {
  94. let regex = new RegExp(status);
  95. if (regex.test(data.state)) {
  96. bg = icons[status];
  97. break;
  98. }
  99. }
  100. let rtDiv = document.createElement('div');
  101. if (document.querySelector('.RatingBar__RatingContainer-sc-85l9wd-0, .sc-f6306ea-0, .cNGXvE, .rating-bar__base-button')) {
  102. rtDiv.innerHTML = `<div class="RatingBarButtonBase__ContentWrap-sc-15v8ssr-0 jQXoLQ rating-bar__base-button">
  103. <div class="RatingBarButtonBase__Header-sc-15v8ssr-1 bufoWn">RT Rating</div>
  104. <a
  105. class="ipc-button ipc-button--single-padding ipc-button--center-align-content ipc-button--default-height ipc-button--core-baseAlt ipc-button--theme-baseAlt ipc-button--on-textPrimary ipc-text-button RatingBarButtonBase__Button-sc-15v8ssr-2 jjcqHZ"
  106. role="button"
  107. tabindex="0"
  108. aria-label="${consensus.replace(/"/g, "'")}"
  109. aria-disabled="false"
  110. href="https://www.rottentomatoes.com${data.id}"
  111. >
  112. <div class="ipc-button__text">
  113. <div class="RatingBarButtonBase__ButtonContentWrap-sc-15v8ssr-3 jodtvN">
  114. <div class="RatingBarButtonBase__IconWrapper-sc-15v8ssr-4 dwhzFZ" style="background: rgba(0, 0, 0, 0) ${bg} scroll 0% 0% / 28px;">
  115. <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="ipc-icon ipc-icon--star AggregateRatingButton__RatingIcon-sc-1ll29m0-4 iAOIoP" viewBox="0 0 24 24" fill="currentColor" role="presentation">
  116. </svg>
  117. </div>
  118. <div class="AggregateRatingButton__ContentWrap-sc-1ll29m0-0 hmJkIS">
  119. <div class="AggregateRatingButton__Rating-sc-1ll29m0-2 bmbYRW">
  120. <span class="AggregateRatingButton__RatingScore-sc-1ll29m0-1 iTLWoV gfstID" itemprop="ratingValue" style="padding-right: 0;">${score}</span><span style="color: white; padding-right: 0.15em;">%</span><span class="grey jkCVKJ" itemprop="bestRating"><!-- --> (${rating})</span>
  121. </div>
  122. <div class="AggregateRatingButton__TotalRatingAmount-sc-1ll29m0-3 jkCVKJ">${votes}</div>
  123. </div>
  124. </div>
  125. </div>
  126. </a>
  127. </div>
  128. `;
  129. rtDiv.innerHTML = `<div data-testid="hero-rating-bar__aggregate-rating" class="sc-f6306ea-0 cNGXvE rating-bar__base-button">
  130. <div class="sc-f6306ea-1 kWSNWJ">RT RATING</div><a class="ipc-button ipc-button--single-padding ipc-button--center-align-content ipc-button--default-height ipc-button--core-baseAlt ipc-button--theme-baseAlt ipc-button--on-textPrimary ipc-text-button sc-f6306ea-2 dfHGIi" role="button" tabindex="0" aria-label="${consensus.replace(/"/g, "'")}" aria-disabled="false" href="https://www.rottentomatoes.com${data.id}">
  131. <div class="ipc-button__text">
  132. <div class="sc-f6306ea-3 loTxjn">
  133. <div class="sc-f6306ea-4 bhunpA" style="background: rgba(0, 0, 0, 0) ${bg} scroll 0% 0% / 28px;">
  134. </div>
  135. <div class="sc-7ab21ed2-0 fAePGh">
  136. <div data-testid="hero-rating-bar__aggregate-rating__score" class="sc-7ab21ed2-2 kYEdvH"><span class="sc-7ab21ed2-1 jGRxWM">${score}</span><span>%
  137. <!-- -->(${rating})
  138. </span></div>
  139. <div class="sc-7ab21ed2-3 dPVcnq">${votes}</div>
  140. </div>
  141. </div>
  142. </div>
  143. </a>
  144. </div>`
  145. } else {
  146. rtDiv.innerHTML = `<div class="imdbRating" itemtype="http://schema.org/AggregateRating" itemscope="" itemprop="aggregateRating" style="background: rgba(0, 0, 0, 0) ${bg} scroll 0% 0% / 28px;" title="${consensus.replace(/"/g, "'")}">
  147. <div class="ratingValue">
  148. <strong><span itemprop="ratingValue" style="font-size: 21px; line-height: 21px; color: ${scoreColor};">${score}</span></strong><span class="grey" style="color: ${scoreColor};">%</span><span class="grey" itemprop="bestRating">(${votes})</span> </div>
  149. <a href="https://www.rottentomatoes.com${data.id}"><span class="small" itemprop="ratingCount">${rating}</span></a>
  150. </div>`;
  151. }
  152. rtDiv = rtDiv.firstElementChild;
  153.  
  154. insertRating(rtDiv);
  155.  
  156. if (data.consensus) {
  157. let titleBlock = document.querySelector('.sc-94726ce4-0, .cMYixt, .sc-80d4314-0, .fjPRnj');
  158. let consDiv = document.createElement('div');
  159. consDiv.classList.add('consensus-div');
  160. consDiv.innerHTML = '<p>' + data.consensus + '</p>';
  161. titleBlock.after(consDiv);
  162. }
  163. }
  164.  
  165. function insertRating(node) {
  166. let wrapper = document.querySelector('.ratings_wrapper');
  167. if (wrapper) {
  168. wrapper.appendChild(node);
  169. } else {
  170. wrapper = document.createElement('div');
  171. wrapper.classList.add('ratings_wrapper');
  172. wrapper.appendChild(node);
  173. let insAt = document.querySelector('.title_bar_wrapper');
  174. if (insAt) {
  175. insAt.insertBefore(wrapper, insAt.firstElementChild);
  176. } else {
  177. insAt = document.querySelector('.sc-db8c1937-1, .kVSEMR');
  178. insAt && insAt.appendChild(wrapper);
  179. }
  180. }
  181. }
  182.  
  183. function getTitleBlock() {
  184. return document.querySelector('.TitleBlock__Container-sc-1nlhx7j-0, .title_block, .sc-94726ce4-1, .iNShGo, h1[data-testid="hero-title-block__title"]');
  185. };
  186.  
  187. GM_addStyle(`
  188. .consensus-div {
  189. margin: 0px 24px 10px 24px;
  190. }
  191. .consensus-div p {
  192. background-color: #444;
  193. color: #FFF;
  194. border-radius: 5px;
  195. padding: 3px 8px 3px 8px;
  196. font-size: 1em;
  197. }
  198. #spinner-div {
  199. background: none;
  200. }
  201. .heroic-overview .title_block {
  202. padding-bottom: 0px;
  203. }
  204. .jkCVKJ {
  205. font-family: var(--ipt-font-family);
  206. font-size: var(--ipt-type-copyright-size,.75rem);
  207. font-weight: var(--ipt-type-copyright-weight,400);
  208. letter-spacing: var(--ipt-type-copyright-letterSpacing,.03333em);
  209. line-height: var(--ipt-type-copyright-lineHeight,1rem);
  210. text-transform: var(--ipt-type-copyright-textTransform,none);
  211. color: var(--ipt-on-baseAlt-textSecondary-color,rgba(255,255,255,0.7));
  212. }
  213. .bmbYRW {
  214. display: flex;
  215. -moz-box-align: center;
  216. align-items: center;
  217. font-family: var(--ipt-font-family);
  218. font-size: var(--ipt-type-body-size,1rem);
  219. font-weight: var(--ipt-type-body-weight,400);
  220. letter-spacing: var(--ipt-type-body-letterSpacing,.03125em);
  221. text-transform: var(--ipt-type-body-textTransform,none);
  222. color: var(--ipt-on-baseAlt-textSecondary-color,rgba(255,255,255,0.7));
  223. line-height: 1.5rem;
  224. margin-bottom: -0.125rem;
  225. }
  226. `);
  227.  
  228. var spinnerGif = `data:image/gif;base64,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`;
  229. (function check() {
  230. if (getTitleBlock()) {
  231. start();
  232. } else{
  233. window.setTimeout(check, 50);
  234. }
  235. })();
  236. })();