// ==UserScript== // @name Netflix Ratings // @namespace lecapitan // @description View various website ratings while in netflix // @include http://*.netflix.com/* // @version 1.0.0.0 // @downloadURL none // ==/UserScript== document.netflixRatingsObject = { load: function() { // If the page is a movie page var mdpOverview = document.getElementById("mdp-overview"); if (mdpOverview) { var titleElement = this.getTitleElement(mdpOverview); // Get Rotten Tomatoes ratings var dataElement = this.getRTMainDataElement(mdpOverview); if (titleElement && dataElement) { // Load ratings if (!this.hasClass(dataElement, "nrLoading") && !this.hasClass(dataElement, "nrLoaded")) { // Start the search this.getRatings("RottenTomatoes", this.getTitle(titleElement), this.getYear(mdpOverview), dataElement); } } // Get IMDb ratings dataElement = this.getIMDbMainDataElement(mdpOverview); if (titleElement && dataElement) { // Load ratings if (!this.hasClass(dataElement, "nrLoading") && !this.hasClass(dataElement, "nrLoaded")) { // Start the search this.getRatings("IMDb", this.getTitle(titleElement), this.getYear(mdpOverview), dataElement); } } // Get metacritic ratings dataElement = this.getmetacriticMainDataElement(mdpOverview); if (titleElement && dataElement) { // Load ratings if (!this.hasClass(dataElement, "nrLoading") && !this.hasClass(dataElement, "nrLoaded")) { // Start the search this.getRatings("metacritic", this.getTitle(titleElement), this.getYear(mdpOverview), dataElement); } } } }, getTitleElement: function(element) { if (element.id && element.id == "mdp-overview") { if (element.getElementsByClassName("title").length > 0) { return element.getElementsByClassName("title")[0]; } } return undefined; }, getRTMainDataElement: function(element) { if (element.getElementsByClassName("nrRTMainData").length > 0) { return element.getElementsByClassName("nrRTMainData")[0]; } else { if (document.getElementsByTagName("dl").length > 0) { var dlElement = document.getElementsByTagName("dl")[0]; var dtElement = document.createElement("dt"); dtElement.innerHTML = "Rotten Tomatoes: "; dlElement.appendChild(dtElement); var ddElement = document.createElement("dd"); var linkElement = document.createElement("a"); linkElement.className = "nrRTMainData"; ddElement.appendChild(linkElement); dlElement.appendChild(ddElement); return linkElement; } } return undefined; }, getIMDbMainDataElement: function(element) { if (element.id && element.id == "mdp-overview") { if (element.getElementsByClassName("nrIMDbMainData").length > 0) { return element.getElementsByClassName("nrIMDbMainData")[0]; } else { if (document.getElementsByTagName("dl").length > 0) { var dlElement = document.getElementsByTagName("dl")[0]; var dtElement = document.createElement("dt"); dtElement.innerHTML = "IMDb: "; dlElement.appendChild(dtElement); var ddElement = document.createElement("dd"); var linkElement = document.createElement("a"); linkElement.className = "nrIMDbMainData"; ddElement.appendChild(linkElement); dlElement.appendChild(ddElement); return linkElement; } } } return undefined; }, getmetacriticMainDataElement: function(element) { if (element.id && element.id == "mdp-overview") { if (element.getElementsByClassName("nrmetacriticMainData").length > 0) { return element.getElementsByClassName("nrmetacriticMainData")[0]; } else { if (document.getElementsByTagName("dl").length > 0) { var dlElement = document.getElementsByTagName("dl")[0]; var dtElement = document.createElement("dt"); dtElement.innerHTML = "metacritic: "; dlElement.appendChild(dtElement); var ddElement = document.createElement("dd"); var linkElement = document.createElement("a"); linkElement.className = "nrmetacriticMainData"; ddElement.appendChild(linkElement); dlElement.appendChild(ddElement); return linkElement; } } } return undefined; }, getRatings: function(type, title, year, dataElement) { dataElement.innerHTML = "..."; // Get the rotten tomatoes rating if (type == "RottenTomatoes") { dataElement.className = "nrRTMainData nrLoading"; this.fetchRating(type, this.getRTUrl(title), title, year, dataElement); } else if (type == "IMDb") { dataElement.className = "nrIMDbMainData nrLoading"; this.fetchRating(type, this.getIMDbUrl(title), title, year, dataElement); } else if (type == "metacritic") { dataElement.className = "nrmetacriticMainData nrLoading"; this.fetchRating(type, this.getmetacriticUrl(title), title, year, dataElement); } }, getRTUrl: function(title) { return "http://www.rottentomatoes.com/search/?search=" + title.replace(/-/g, " ").replace(/ /g, "+"); }, getIMDbUrl: function(title) { return "http://www.imdb.com/find?q=" + title.replace(/-/g, " ").replace(/ /g, "+").replace(/&/g, "%26") + "&s=all"; }, getmetacriticUrl: function(title) { return "http://www.metacritic.com/search/movie/" + title.replace(/-/g, " ").replace(/ /g, "+") + "/results"; }, fetchRating: function(type, url, title, year, dataElement) { // Send the request if (url && url != "") { // Set the link dataElement.href = url; // Make a cross-domain request for this url if (type == "RottenTomatoes") { GM_xmlhttpRequest({ method: "GET", url: url, onload: function(responseDetails) { // Get the HTML document that we received var responseDoc = document.netflixRatingsObject.getDom(responseDetails.responseText); // Parse the response document.netflixRatingsObject.parseRTResponse(responseDoc, title, year, dataElement); }, onerror: function(responseDetails) { console.log("failure"); } }); } else if (type == "IMDb") { GM_xmlhttpRequest({ method: "GET", url: url, onload: function(responseDetails) { // Get the HTML document that we received var responseDoc = document.netflixRatingsObject.getDom(responseDetails.responseText); // Parse the response document.netflixRatingsObject.parseIMDbResponse(responseDoc, title, year, dataElement); }, onerror: function(responseDetails) { console.log("failure"); } }); } else if (type == "metacritic") { GM_xmlhttpRequest({ method: "GET", url: url, onload: function(responseDetails) { // Get the HTML document that we received var responseDoc = document.netflixRatingsObject.getDom(responseDetails.responseText); // Parse the response document.netflixRatingsObject.parsemetacriticResponse(responseDoc, title, year, dataElement); }, onerror: function(responseDetails) { console.log("failure"); } }); } } }, // ----------------------------------- // Parsing functions // ----------------------------------- parseRTResponse: function(responseDoc, title, year, dataElement) { // Check for no results if (responseDoc.getElementsByClassName("noresults").length > 0) { dataElement.innerHTML = "no results"; // Set the loaded flag dataElement.className = "nrRTMainData nrLoaded"; } // Check for search results list else if (responseDoc.getElementById("movie_results_ul")) { // Get all list items var movieItems = responseDoc.getElementsByClassName("media_block_content"); // Check search results for a full match (title and year) for (var i = 0; i < movieItems.length; i++) { if (movieItems[i].getElementsByTagName("h3").length <= 0) { continue; // This is some menu element on the page } var parsedTitle; var parsedYear; var newUrl; // Get the title and url var linkList = movieItems[i].getElementsByTagName("a"); for (var ii = 0; ii < linkList.length; ii++) { if (linkList[ii].href.indexOf("/m/") >= 0) { parsedTitle = linkList[ii].innerHTML; newUrl = "http://www.rottentomatoes.com" + linkList[ii].href; } } // Get the year var yearList = movieItems[i].getElementsByClassName("movie_year"); if (yearList.length > 0) { parsedYear = yearList[0].innerHTML.replace(/ /g, "").replace(/\(/, "").replace(/\)/, ""); } // Check the title and year if (parsedTitle.toLowerCase().replace(/[^a-z0-9]/g, "") == title.toLowerCase().replace(/[^a-z0-9]/g, "") && parsedYear == year) { document.netflixRatingsObject.fetchRating("RottenTomatoes", newUrl, title, year, dataElement); return; } } // Did not find a full match, check search results for just a title match for (var i = 0; i < movieItems.length; i++) { if (movieItems[i].getElementsByTagName("h3").length <= 0) { continue; // This is some menu element on the page } var parsedTitle; var newUrl; // Get the title and url var linkList = movieItems[i].getElementsByTagName("a"); for (var ii = 0; ii < linkList.length; ii++) { if (linkList[ii].href.indexOf("m/") >= 0) { parsedTitle = linkList[ii].innerHTML; newUrl = "http://www.rottentomatoes.com" + linkList[ii].href; } } // Similar title if (parsedTitle.toLowerCase().replace(/[^a-z0-9]/g, "") == title.toLowerCase().replace(/[^a-z0-9]/g, "")) { document.netflixRatingsObject.fetchRating("RottenTomatoes", newUrl, title, year, dataElement); return; } } // Did not find a title match, check search results for just a year match for (var i = 0; i < movieItems.length; i++) { if (movieItems[i].getElementsByTagName("h3").length <= 0) { continue; // This is some menu element on the page } var parsedYear; var newUrl; // Get the title and url var linkList = movieItems[i].getElementsByTagName("a"); for (var ii = 0; ii < linkList.length; ii++) { if (linkList[ii].href.indexOf("/m/") >= 0) { newUrl = "http://www.rottentomatoes.com" + linkList[ii].href; } } // Get the year var yearList = movieItems[i].getElementsByClassName("movie_year"); if (yearList.length > 0) { parsedYear = yearList[0].innerHTML.replace(/ /g, "").replace(/\(/, "").replace(/\)/, ""); } // Same year if (parsedYear == year) { document.netflixRatingsObject.fetchRating("RottenTomatoes", newUrl, title, year, dataElement); return; } } // Did not find any matches dataElement.innerHTML = "..?"; // Set the loaded flag dataElement.className = "nrRTMainData nrLoaded"; } // Got a movie's page else { // Set the element text, if we cannot parse critic or user ratings dataElement.innerHTML = "..?"; // Set the loaded flag dataElement.className = "nrRTMainData nrLoaded"; // Get the critics rating var criticsRating = responseDoc.getElementById('all-critics-meter'); if (criticsRating) { dataElement.innerHTML = criticsRating.innerHTML; } // Get the users rating var usersRating = responseDoc.getElementsByClassName('meter popcorn numeric'); if (usersRating && usersRating.length > 0) { dataElement.innerHTML += " | " + usersRating[0].innerHTML; } else { usersRating = responseDoc.getElementsByClassName('meter spilled numeric'); if (usersRating && usersRating.length > 0) { dataElement.innerHTML += " | " + usersRating[0].innerHTML; } } } }, parseIMDbResponse: function(responseDoc, title, year, dataElement) { // Check for no results - IMDb seems to always provide some results... if (responseDoc.getElementsByClassName("noresults").length > 0) { dataElement.innerHTML = "no results"; // Set the loaded flag dataElement.className = "nrIMDbMainData nrLoaded"; } // Check for search results list else if (responseDoc.getElementsByClassName("findList").length > 0) { // Get all list items var movieItems = responseDoc.getElementsByClassName("result_text"); // Check search results for a full match (title and year) for (var i = 0; i < movieItems.length; i++) { var parsedTitle; var parsedYear; var newUrl; // Get the title and url var linkList = movieItems[i].getElementsByTagName("a"); for (var ii = 0; ii < linkList.length; ii++) { if (linkList[ii].href.indexOf("/title/") >= 0) { parsedTitle = linkList[ii].innerHTML; newUrl = "http://www.imdb.com" + linkList[ii].href; } } // Get the year var yearMatch = movieItems[i].innerHTML.match(/\(([0-9]*)\)/); if (yearMatch.length > 0) { parsedYear = yearMatch[1]; } // Same title and year if (parsedTitle.toLowerCase().replace(/[^a-z0-9]/g, "") == title.toLowerCase().replace(/[^a-z0-9]/g, "") && parsedYear == year) { document.netflixRatingsObject.fetchRating("IMDb", newUrl, title, year, dataElement); return; } } // Did not find a full match, check search results for just a title match for (var i = 0; i < movieItems.length; i++) { var parsedTitle; var newUrl; // Get the title and url var linkList = movieItems[i].getElementsByTagName("a"); for (var ii = 0; ii < linkList.length; ii++) { if (linkList[ii].href.indexOf("/title/") >= 0) { parsedTitle = linkList[ii].innerHTML; newUrl = "http://www.imdb.com" + linkList[ii].href; } } // Same title if (parsedTitle.toLowerCase().replace(/[^a-z0-9]/g, "") == title.toLowerCase().replace(/[^a-z0-9]/g, "")) { document.netflixRatingsObject.fetchRating("IMDb", newUrl, title, year, dataElement); return; } } // Did not find a title match, check search results for just a year match for (var i = 0; i < movieItems.length; i++) { var parsedYear; var newUrl; // Get the title and url var linkList = movieItems[i].getElementsByTagName("a"); for (var ii = 0; ii < linkList.length; ii++) { if (linkList[ii].href.indexOf("/title/") >= 0) { newUrl = "http://www.imdb.com" + linkList[ii].href; } } // Get the year var yearMatch = movieItems[i].innerHTML.match(/\(([0-9]*)\)/); if (yearMatch.length > 0) { parsedYear = yearMatch[1]; } // Same year if (parsedYear == year) { document.netflixRatingsObject.fetchRating("IMDb", newUrl, title, year, dataElement); return; } } // Did not find a match dataElement.innerHTML = "..?"; // Set the loaded flag dataElement.className = "nrIMDbMainData nrLoaded"; } else { // Set the element text, if we cannot parse ratings dataElement.innerHTML = "..?"; // Set the loaded flag dataElement.className = "nrIMDbMainData nrLoaded"; // Get the IMDb rating var elementList = responseDoc.getElementsByClassName("star-box-giga-star"); if (elementList.length >= 0) { dataElement.innerHTML = elementList[0].innerHTML; } } }, parsemetacriticResponse: function(responseDoc, title, year, dataElement) { // Check for no results - IMDb seems to always provide some results... if (responseDoc.getElementsByClassName("noresults").length > 0) { dataElement.innerHTML = "no results"; // Set the loaded flag dataElement.className = "nrmetacriticMainData nrLoaded"; } // Check for search results list else if (responseDoc.getElementsByClassName("search_results").length > 0) { // Get all list items var movieItems = responseDoc.getElementsByClassName("result"); // Check search results for a full match (title and year) for (var i = 0; i < movieItems.length; i++) { // Check the result type var resultTypeElements = movieItems[0].getElementsByClassName("result_type"); if (resultTypeElements.length <= 0 || resultTypeElements[0].childNodes[1].innerHTML != "Movie") { continue; // Not a movie result type } var parsedTitle; var parsedYear; var newUrl; // Get the title and url var linkList = movieItems[i].getElementsByTagName("a"); for (var ii = 0; ii < linkList.length; ii++) { if (linkList[ii].href.indexOf("/movie/") >= 0) { parsedTitle = linkList[ii].innerHTML; newUrl = "http://www.metacritic.com" + linkList[ii].href; } } // Get the year var releaseDateElements = movieItems[i].getElementsByClassName("release_date"); if (releaseDateElements.length > 0) { var yearMatches = releaseDateElements[0].childNodes[3].innerHTML.match(/[A-Za-z0-9 ]*[,][ ]([0-9][0-9][0-9][0-9])/); if (yearMatches) { parsedYear = yearMatches[1]; } } // Same title and year if (parsedTitle.toLowerCase().replace(/[^a-z0-9]/g, "") == title.toLowerCase().replace(/[^a-z0-9]/g, "") && parsedYear == year) { document.netflixRatingsObject.fetchRating("metacritic", newUrl, title, year, dataElement); return; } } // Did not find a full match, check search results for just a title match for (var i = 0; i < movieItems.length; i++) { // Check the result type var resultTypeElements = movieItems[0].getElementsByClassName("result_type"); if (resultTypeElements.length <= 0 || resultTypeElements[0].childNodes[1].innerHTML != "Movie") { continue; // Not a movie result type } var parsedTitle; var newUrl; // Get the title and url var linkList = movieItems[i].getElementsByTagName("a"); for (var ii = 0; ii < linkList.length; ii++) { if (linkList[ii].href.indexOf("/movie/") >= 0) { parsedTitle = linkList[ii].innerHTML; newUrl = "http://www.metacritic.com" + linkList[ii].href; } } // Same title if (parsedTitle.toLowerCase().replace(/[^a-z0-9]/g, "") == title.toLowerCase().replace(/[^a-z0-9]/g, "")) { document.netflixRatingsObject.fetchRating("metacritic", newUrl, title, year, dataElement); return; } } // Did not find a title match, check search results for just a year match for (var i = 0; i < movieItems.length; i++) { // Check the result type var resultTypeElements = movieItems[0].getElementsByClassName("result_type"); if (resultTypeElements.length <= 0 || resultTypeElements[0].childNodes[1].innerHTML != "Movie") { continue; // Not a movie result type } var parsedYear; var newUrl; // Get the title and url var linkList = movieItems[i].getElementsByTagName("a"); for (var ii = 0; ii < linkList.length; ii++) { if (linkList[ii].href.indexOf("/movie/") >= 0) { newUrl = "http://www.metacritic.com" + linkList[ii].href; } } // Get the year var releaseDateElements = movieItems[i].getElementsByClassName("release_date"); if (releaseDateElements.length > 0) { var yearMatches = releaseDateElements[0].childNodes[3].innerHTML.match(/[A-Za-z0-9 ]*[,][ ]([0-9][0-9][0-9][0-9])/); if (yearMatches) { parsedYear = yearMatches[1]; } } // Same title and year if (parsedYear == year) { document.netflixRatingsObject.fetchRating("metacritic", newUrl, title, year, dataElement); return; } } // Did not find a match dataElement.innerHTML = "..?"; // Set the loaded flag dataElement.className = "nrmetacriticMainData nrLoaded"; } else { // Set the element text, if we cannot parse ratings dataElement.innerHTML = "..?"; // Set the loaded flag dataElement.className = "nrmetacriticMainData nrLoaded"; // Get the metacritic rating var criticsRating, usersRating; var elementList = responseDoc.getElementsByClassName("score_value"); if (elementList.length >= 0) { for (var i = 0; i < elementList.length; i++) { if (elementList[i].parentNode.tagName.toLowerCase() == "a" && elementList[i].parentNode.href.indexOf("critic-reviews") >= 0) { criticsRating = elementList[i].innerHTML; } if (elementList[i].parentNode.tagName.toLowerCase() == "a" && elementList[i].parentNode.href.indexOf("user-reviews") >= 0) { usersRating = elementList[i].innerHTML; } } if (criticsRating) { dataElement.innerHTML = criticsRating; } if (usersRating) { dataElement.innerHTML += " | " + usersRating; } } } }, // ----------------------------------- // Helper functions // ----------------------------------- hasClass: function(element, cls) { return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1; }, insertAfter: function(referenceNode, newNode) { referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); }, getTitle: function(element) { return element.innerHTML; }, getYear: function(element) { var year; if (element.getElementsByClassName("year")) { year = element.getElementsByClassName("year")[0].innerHTML; } return year; }, getDom: function(responseHTML) { var dt = document.implementation.createDocumentType("html", "-//W3C//DTD HTML 4.01 Transitional//EN", "http://www.w3.org/TR/html4/loose.dtd"); var doc = document.implementation.createDocument('', '', dt); var html = doc.createElement('html'); html.innerHTML = responseHTML; doc.appendChild(html); return doc; } }; // Inject CSS var styleElement = document.createElement('style'); styleElement.type = 'text/css'; styleElement.innerHTML = '#mdp-metadata dl dt { width:25% !important; }'; styleElement.innerHTML += '#mdp-metadata dl dd { width:70% !important; }'; document.head.appendChild(styleElement); // Load ratings document.netflixRatingsObject.load();