// ==UserScript== // @name IMDb Info On Netflix // @license GPLv3 // @description Detailed IMDB info to Netflix titles // @namespace http://tampermonkey.net/ // @version 1.4.2 // @author Hyftar // @homepageURL https://github.com/Hyftar/IMDb-Info-on-Netflix // @match https://www.netflix.com/browse // @match https://www.netflix.com/title/* // @require https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js // @grant GM_xmlhttpRequest // @license GPLv3 // @downloadURL https://update.greasyfork.icu/scripts/455169/IMDb%20Info%20On%20Netflix.user.js // @updateURL https://update.greasyfork.icu/scripts/455169/IMDb%20Info%20On%20Netflix.meta.js // ==/UserScript== function storageAvailable(type) { let storage; try { storage = window[type]; const x = '__storage_test__'; storage.setItem(x, x); storage.removeItem(x); return true; } catch (e) { return e instanceof DOMException && ( // everything except Firefox e.code === 22 || // Firefox e.code === 1014 || // test name field too, because code might not be present // everything except Firefox e.name === 'QuotaExceededError' || // Firefox e.name === 'NS_ERROR_DOM_QUOTA_REACHED') && // acknowledge QuotaExceededError only if there's something already stored (storage && storage.length !== 0); } } (function () { Storage.prototype.setObject = function(key, value) { this.setItem(key, JSON.stringify(value)); } Storage.prototype.getObject = function(key) { var value = this.getItem(key); if (value === null) { return {} } return JSON.parse(value); } const useLocalStorage = storageAvailable('localStorage'); window.titlesIdCache = useLocalStorage ? localStorage.getObject('TitlesIdCache') : {} window.titlesRatingCache = useLocalStorage ? localStorage.getObject('TitlesRatingCache') : {} $('head') .append( `` ); let isInfoLoaded = false; //main loop const intervalId = setInterval( function () { if ($('.imdbRating').length === 0 && $('.imdbInfoLoading').length === 0) { isInfoLoaded = false; } if ($('.PlayerControlsNeo__button-control-row').length !== 0) { clearInterval(intervalId); return; } if (isInfoLoaded) { return; } if ($('.previewModal--detailsMetadata-right').length !== 0) { isInfoLoaded = true; loadImdbScorePreviewModal(); return; } if ($('.previewModal--info').length !== 0) { isInfoLoaded = true; loadImdbScoreInfoModal(); return; } }, 500 ); function loadImdbScoreInfoModal() { const title = $('.previewModal--boxart')[0].alt; if (title === undefined) { return; } loadRatingAsync(title); } function loadImdbScorePreviewModal() { const title = $('.playerModel--player__storyArt')[0].alt; if (title === undefined) { return; } loadRatingAsync(title); } async function loadRatingAsync(title) { const loadingElem = $('') .attr('src', "https://i.imgur.com/1Aatim3.gif") .attr('width', 20) .attr('class', "imdbInfoLoading") loadingElem.appendTo('.videoMetadata--container:first'); loadingElem.clone().appendTo('.previewModal-episodeDetail-and-badge') const imdbInfo = await getImdbInfoFromTitle(title); $('.imdbInfoLoading').remove() $('.imdbLogo').remove() $('.imdbRating').remove() const color = getRatingColor(imdbInfo); const imdbUrl = 'https://www.imdb.com' + imdbInfo.url; const imdbLogoElem = $('') .attr('class', 'imdbLogo') .attr('href', imdbUrl) .attr('target', '_blank') .addClass('slnk') .html(''); imdbLogoElem.appendTo('.videoMetadata--container:first'); imdbLogoElem.clone().appendTo('.previewModal-episodeDetail-and-badge'); const imdbInfoElem = $('') .attr('class', 'imdbRating') .attr('span', imdbUrl) .attr('style', "line-height:25px; margin-left: 5px; color:" + color) .html(imdbInfo.rating); imdbInfoElem.appendTo('.videoMetadata--container:first'); imdbInfoElem.clone().appendTo('.previewModal-episodeDetail-and-badge'); } async function getImdbInfoFromTitle(title) { const imdbId = await getImdbIdFromTitle(title); if (imdbId === undefined) { return; } return getImdbInfoFromId(imdbId); } async function getImdbIdFromTitle(title) { if (window.titlesIdCache[title] !== undefined) { return Promise.resolve(window.titlesIdCache[title]); } const encodedUrl = encodeURIComponent(title) const url = `https://www.imdb.com/find?s=tt&q=${encodedUrl}` return new Promise( function (resolve, reject) { GM_xmlhttpRequest({ url, method: 'GET', responseType: 'document', synchronous: false, onload: (resp) => { if (resp.status !== 200) { reject(`Error retrieving the IMDb id at url : '${url}'`) } const doc = document.implementation.createHTMLDocument().documentElement; doc.innerHTML = resp.responseText; const link = Array.from(doc.querySelectorAll('.find-result-item:first-child a')) .find((el) => !el.parentNode.textContent.trim().match(/\((?:TV Episode|Short|Video Game|Video)\)/)); const id = link?.href.match(/title\/(tt\d+)/)[1]; if (id === undefined) { return reject(`Error getting IMDb id for ${title}`); } window.titlesIdCache[title] = id; localStorage.setObject('TitlesIdCache', window.titlesIdCache) return resolve(id); } }); } ); } function getImdbInfoFromId(id) { const previousImdbInfo = window.titlesRatingCache[id]; if (previousImdbInfo !== undefined && isIMDbInfoFresh(previousImdbInfo)) { return Promise.resolve(window.titlesRatingCache[id]); } const url = `https://www.imdb.com/title/${id}/`; return new Promise(function (resolve, reject) { GM_xmlhttpRequest({ url, method: 'GET', responseType: 'document', synchronous: false, onload: (resp) => { if (resp.status !== 200) { return reject(`Error retrieving the IMDb rating at url : '${url}'`); } const doc = document.implementation.createHTMLDocument().documentElement; doc.innerHTML = resp.responseText; const jsonDataElement = doc.querySelector('script[type="application/ld+json"]'); if (jsonDataElement === null) { return reject(`The JSON data at url '${url}' was not found`); } const jsonData = JSON.parse(jsonDataElement.textContent); const data = { id, url: jsonData.url, title: jsonData.name, datePublished: jsonData.datePublished, description: jsonData.description, rating: jsonData.aggregateRating.ratingValue, votes: jsonData.aggregateRating.ratingCount, dateFetched: new Date() }; window.titlesRatingCache[id] = data; localStorage.setObject('TitlesRatingCache', window.titlesRatingCache); return resolve(data); } }); }); } function getRatingColor(imdbInfo) { if (imdbInfo.rating < 5) { return "#a10d0d"; } if (imdbInfo.rating < 6) { return "#b33c2d"; } if (imdbInfo.rating < 7) { return "#d98730"; } if (imdbInfo.rating < 8) { return "#8bd925" } return "#2bff00" } function isIMDbInfoFresh(imdbInfo) { const daysSinceRelease = daysSince(imdbInfo.datePublished); const daysSinceFetched = daysSince(imdbInfo.dateFetched); if (daysSinceRelease < 120 && daysSinceFetched > 2) { return false; } if (daysSinceFetched > 21) { return false; } return true; } function daysSince(date) { const now = new Date() const publishDate = new Date(date) return Math.floor((now.getTime()-publishDate.getTime())/(24*3600*1000)) } })();