// ==UserScript==
// @name Show Letterboxd rating
// @description Show Letterboxd rating on imdb.com, metacritic.com, rottentomatoes.com, BoxOfficeMojo, Amazon, Google Play, allmovie.com, Wikipedia, themoviedb.org, movies.com
// @namespace cuzi
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @grant unsafeWindow
// @grant GM.xmlHttpRequest
// @grant GM.setValue
// @grant GM.getValue
// @require http://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
// @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @license GPL-3.0-or-later; http://www.gnu.org/licenses/gpl-3.0.txt
// @version 4
// @connect letterboxd.com
// @include https://play.google.com/store/movies/details/*
// @include http://www.amazon.com/*
// @include https://www.amazon.com/*
// @include http://www.amazon.co.uk/*
// @include https://www.amazon.co.uk/*
// @include http://www.amazon.fr/*
// @include https://www.amazon.fr/*
// @include http://www.amazon.de/*
// @include https://www.amazon.de/*
// @include http://www.amazon.es/*
// @include https://www.amazon.es/*
// @include http://www.amazon.ca/*
// @include https://www.amazon.ca/*
// @include http://www.amazon.in/*
// @include https://www.amazon.in/*
// @include http://www.amazon.it/*
// @include https://www.amazon.it/*
// @include http://www.amazon.co.jp/*
// @include https://www.amazon.co.jp/*
// @include http://www.amazon.com.mx/*
// @include https://www.amazon.com.mx/*
// @include http://www.amazon.com.au/*
// @include https://www.amazon.com.au/*
// @include http://www.imdb.com/title/*
// @include https://www.imdb.com/title/*
// @include http://www.serienjunkies.de/*
// @include https://www.serienjunkies.de/*
// @include http://www.boxofficemojo.com/movies/*
// @include https://www.boxofficemojo.com/movies/*
// @include http://www.allmovie.com/movie/*
// @include https://www.allmovie.com/movie/*
// @include https://en.wikipedia.org/*
// @include http://www.movies.com/*/m*
// @include https://www.themoviedb.org/movie/*
// @include https://www.rottentomatoes.com/m/*
// @include https://rottentomatoes.com/m/*
// @include http://www.metacritic.com/movie/*
// @include https://www.metacritic.com/movie/*
// @include https://www.nme.com/reviews/movie/*
// @include https://itunes.apple.com/*/movie/*
// @include https://www.tvhoard.com/*
// ==/UserScript==
var baseURL = "https://letterboxd.com"
var baseURL_search = baseURL + "/s/autocompletefilm?q={query}&limit=20×tamp={timestamp}"
var baseURL_openTab = baseURL + "/search/{query}/";
var baseURL_ratingHistogram = baseURL + "/csi{url}rating-histogram/";
const cacheExpireAfterHours = 4;
function minutesSince(time) {
let seconds = ((new Date()).getTime() - time.getTime()) / 1000;
return seconds>60?parseInt(seconds/60)+" min ago":"now";
}
function fixLetterboxdURLs(html) {
return html.replace(/<a /g,'<a target="_blank" ').replace(/href="\//g,'href="'+baseURL+"/").replace(/src="\//g,'src="'+baseURL+"/");
}
function filterUniversalUrl(url) {
try {
url = url.match(/http.+/)[0];
} catch(e) { }
try {
url = url.replace(/https?:\/\/(www.)?/,"");
} catch(e) { }
if(url.startsWith("imdb.com/") && url.match(/(imdb\.com\/\w+\/\w+\/)/)) {
// Remove movie subpage from imdb url
return url.match(/(imdb\.com\/\w+\/\w+\/)/)[1];
} else if(url.startsWith("thetvdb.com/")) {
// Do nothing with thetvdb.com urls
return url;
} else if(url.startsWith("boxofficemojo.com/")) {
// Keep the important id= on
try {
var parts = url.split("?");
var page = url[0] + "?";
var idparam = url[1].match(/(id=.+?)(\.|&)/)[1];
return page+idparam;
} catch(e) {
return url;
}
} else {
// Default: Remove parameters
return url.split("?")[0].split("&")[0];
}
}
var parseLDJSON_cache = {}
function parseLDJSON(keys, condition) {
if(document.querySelector('script[type="application/ld+json"]')) {
var data = [];
var scripts = document.querySelectorAll('script[type="application/ld+json"]');
for(let i = 0; i < scripts.length; i++) {
var jsonld;
if (scripts[i].innerText in parseLDJSON_cache) {
jsonld = parseLDJSON_cache[scripts[i].innerText]
} else {
try {
jsonld = JSON.parse(scripts[i].innerText);
parseLDJSON_cache[scripts[i].innerText] = jsonld
} catch(e) {
parseLDJSON_cache[scripts[i].innerText] = null
continue;
}
}
if(jsonld) {
if(Array.isArray(jsonld)) {
data.push(...jsonld)
} else {
data.push(jsonld);
}
}
}
for(let i = 0; i < data.length; i++) {
try {
if(data[i] && data[i] && (typeof condition != 'function' || condition(data[i]))) {
if(Array.isArray(keys)) {
let r = [];
for(let j = 0; j < keys.length; j++) {
r.push(data[i][keys[j]]);
}
return r;
} else if(keys) {
return data[i][keys];
} else if(typeof condition === 'function') {
return data[i]; // Return whole object
}
}
} catch(e) {
continue;
}
}
return data;
}
return null;
}
async function addToWhiteList(letterboxdUrl) {
let whitelist = JSON.parse(await GM.getValue("whitelist","{}"));
const docUrl = filterUniversalUrl(document.location.href)
whitelist[docUrl] = letterboxdUrl
await GM.setValue("whitelist", JSON.stringify(whitelist));
}
async function removeFromWhiteList() {
let whitelist = JSON.parse(await GM.getValue("whitelist","{}"));
const docUrl = filterUniversalUrl(document.location.href)
if(docUrl in whitelist) {
delete whitelist[docUrl]
await GM.setValue("whitelist", JSON.stringify(whitelist));
}
}
var current = {
type : null,
query : null,
year : null
};
async function searchMovie(query, type, year, forceList) {
// Load data from letterboxd search API or from cache
current.type = type;
current.query = query;
current.year = year;
let whitelist = JSON.parse(await GM.getValue("whitelist","{}"));
if(forceList) {
whitelist = {}
}
const docUrl = filterUniversalUrl(document.location.href)
if(docUrl in whitelist) {
return loadMovieRating({'url' : whitelist[docUrl]})
}
const url = baseURL_search.replace("{query}", encodeURIComponent(query)).replace("{timestamp}", encodeURIComponent(Date.now()));
let cache = JSON.parse(await GM.getValue("cache","{}"));
// Delete cached values, that are expired
for(var prop in cache) {
if((new Date()).getTime() - (new Date(cache[prop].time)).getTime() > cacheExpireAfterHours*60*60*1000) {
delete cache[prop];
}
}
// Check cache or request new content
if(url in cache) {
// Use cached response
handleSearchResponse(cache[url], forceList);
} else {
GM.xmlHttpRequest({
method: "GET",
url: url,
onload: function(response) {
// Save to chache
response.time = (new Date()).toJSON();
// Chrome fix: Otherwise JSON.stringify(cache) omits responseText
var newobj = {};
for(var key in response) {
newobj[key] = response[key];
}
newobj.responseText = response.responseText;
cache[url] = newobj;
GM.setValue("cache",JSON.stringify(cache));
handleSearchResponse(response, forceList);
},
onerror: function(response) {
console.log("Letterboxd GM.xmlHttpRequest Error: "+response.status+"\nURL: "+requestURL+"\nResponse:\n"+response.responseText);
},
});
}
}
function handleSearchResponse(response, forceList) {
// Handle GM.xmlHttpRequest response
let result = JSON.parse(response.responseText);
if(forceList && (result.result == false || !result.data || !result.data.length)) {
alert("Letterboxd userscript\n\nNo results for "+current.query);
} else if(result.result == false || !result.data || !result.data.length) {
console.log("Letterboxd: No results for "+current.query);
} else if(!forceList && result.data.length == 1) {
loadMovieRating(result.data[0]);
} else {
// Sort results by closest match
function matchQuality(title, year, originalTitle) {
if(title == current.query && year == current.year) {
return 105 + year;
}
if(originalTitle && originalTitle == current.query && year == current.year) {
return 104 + year;
}
if(title == current.query && current.year) {
return 103 - Math.abs(year - current.year);
}
if(originalTitle && originalTitle == current.query && current.year) {
return 102 - Math.abs(year - current.year);
}
if(title.replace(/\(.+\)/, "").trim() == current.query && current.year) {
return 101 - Math.abs(year - current.year);
}
if(originalTitle && originalTitle.replace(/\(.+\)/, "").trim() == current.query && current.year) {
return 100 - Math.abs(year - current.year);
}
if(title == current.query) {
return 12;
}
if(originalTitle && originalTitle == current.query) {
return 11;
}
if(title.replace(/\(.+\)/, "").trim() == current.query) {
return 10;
}
if(originalTitle && originalTitle.replace(/\(.+\)/, "").trim() == current.query) {
return 9;
}
if(title.startsWith(current.query)) {
return 8;
}
if(originalTitle && originalTitle.startsWith(current.query)) {
return 7;
}
if(current.query.indexOf(title) != -1) {
return 6;
}
if(originalTitle && current.query.indexOf(originalTitle) != -1) {
return 5;
}
if(title.indexOf(current.query) != -1) {
return 4;
}
if(originalTitle && originalTitle.indexOf(current.query) != -1) {
return 3;
}
if(current.query.toLowerCase().indexOf(title.toLowerCase()) != -1) {
return 2;
}
if(title.toLowerCase().indexOf(current.query.toLowerCase()) != -1) {
return 1;
}
return 0;
}
result.data.sort(function(a,b) {
if(!a.hasOwnProperty('matchQuality')) {
a.matchQuality = matchQuality(a.name, a.releaseYear, a.originalName);
}
if(!b.hasOwnProperty('matchQuality')) {
b.matchQuality = matchQuality(b.name, b.releaseYear, b.originalName);
}
return b.matchQuality - a.matchQuality;
});
if(!forceList && result.data.length > 1 && result.data[0].matchQuality > 100 && result.data[1].matchQuality < result.data[0].matchQuality) {
loadMovieRating(result.data[0]);
} else {
showMovieList(result.data, new Date(response.time));
}
}
}
function showMovieList(arr, time) {
// Show a small box in the right lower corner
$("#mcdiv321letterboxd").remove();
let main,div;
div = main = $('<div id="mcdiv321letterboxd"></div>').appendTo(document.body);
div.css({
position:"fixed",
bottom :0,
right: 0,
minWidth: 100,
maxHeight: "95%",
overflow: "auto",
backgroundColor: "#fff",
border: "2px solid #bbb",
borderRadius:" 6px",
boxShadow: "0 0 3px 3px rgba(100, 100, 100, 0.2)",
color: "#000",
padding:" 3px",
zIndex: "5010001",
fontFamily : "Helvetica,Arial,sans-serif"
});
var imgFrame = function imgFrameFct(image125, scale) {
if(!image125) {
return
}
let html = '<iframe sandbox scrolling="no" src="' + baseURL + image125 + '" marginheight="0" marginwidth="0" style="vertical-align:middle; padding:0px; border:none; display:inline; max-width:125px; margin-top:'+(40.0*scale-40.0)+'%; margin-left:'+(40.0*scale-40.0)+'%; transform:scale('+scale+'); transform-origin:bottom right"></iframe> '
html += '<div style="position:absolute;top:0px;left:0px;width:'+(180.0*scale-45.0)+'px;height:'+(180.0*scale-25)+'px"></div> '
return html
}
// First result
let first = $('<div style="position:relative"><a style="font-size:small; color:#136CB2; " href="' + baseURL + arr[0].url + '">' + imgFrame(arr[0].image125, 0.75) + '<div style="max-width:350px;display:inline-block">' + arr[0].name + (arr[0].originalTitle?' ['+arr[0].originalTitle+']':'') + (arr[0].releaseYear?" (" + arr[0].releaseYear + ")":"") + "</div></a></div>").click(selectMovie).appendTo(main);
first[0].dataset["movie"] = JSON.stringify(arr[0])
// Shall the following results be collapsed by default?
if((arr.length > 1 && arr[0].matchQuality > 10) || arr.length > 10) {
let a = $('<span style="color:gray;font-size: x-small">More results...</span>').appendTo(main).click(function() { more.css("display", "block"); this.parentNode.removeChild(this); });
let more = div = $("<div style=\"display:none\"></div>").appendTo(main);
}
// More results
for(let i = 1; i < arr.length; i++) {
let entry = $('<div style="position:relative"><a style="font-size:small; color:#136CB2; " href="' + baseURL + arr[i].url + '">' + imgFrame(arr[i].image125, 0.5) + '<div style="max-width:350px;display:inline-block">' + arr[i].name + (arr[i].originalTitle?' ['+arr[i].originalTitle+']':'') + (arr[0].releaseYear?" (" + arr[0].releaseYear + ")":"") + "</div></a></div>").click(selectMovie).appendTo(div);
entry[0].dataset["movie"] = JSON.stringify(arr[i])
}
// Footer
let sub = $("<div></div>").appendTo(main);
$('<time style="color:#789; font-size: 11px;" datetime="'+time+'" title="'+time.toLocaleTimeString()+" "+time.toLocaleDateString()+'">'+minutesSince(time)+'</time>').appendTo(sub);
$('<a style="color:#789; font-size: 11px;" target="_blank" href="' + baseURL_openTab.replace("{query}", encodeURIComponent(current.query)) + '" title="Open Letterboxd">@letterboxd.com</a>').appendTo(sub);
$('<span title="Hide me" style="cursor:pointer; float:right; color:#789; font-size: 11px; padding-left:5px;padding-top:3px">❎</span>').appendTo(sub).click(function() {
document.body.removeChild(this.parentNode.parentNode);
});
}
function selectMovie(ev) {
ev.preventDefault()
$("#mcdiv321letterboxd").html("Loading...")
const data = JSON.parse(this.dataset.movie)
loadMovieRating(data)
addToWhiteList(data.url)
}
async function loadMovieRating(data) {
// Load page from letterboxd
if("name" in data) {
current.query = data.name;
}
if("releaseYear" in data) {
current.year = data.releaseYear;
}
const url = baseURL_ratingHistogram.replace("{url}", data.url)
let cache = JSON.parse(await GM.getValue("cache","{}"));
// Delete cached values, that are expired
for(var prop in cache) {
if((new Date()).getTime() - (new Date(cache[prop].time)).getTime() > cacheExpireAfterHours*60*60*1000) {
delete cache[prop];
}
}
// Check cache or request new content
if(url in cache) {
// Use cached response
showMovieRating(cache[url], data.url, data);
} else {
GM.xmlHttpRequest({
method: "GET",
url: url,
onload: function(response) {
// Save to chache
response.time = (new Date()).toJSON();
// Chrome fix: Otherwise JSON.stringify(cache) omits responseText
var newobj = {};
for(var key in response) {
newobj[key] = response[key];
}
newobj.responseText = response.responseText;
cache[url] = newobj;
GM.setValue("cache",JSON.stringify(cache));
showMovieRating(newobj, data.url, data);
},
onerror: function(response) {
console.log("GM.xmlHttpRequest Error: "+response.status+"\nURL: "+requestURL+"\nResponse:\n"+response.responseText);
},
});
}
}
function showMovieRating(response, letterboxdUrl, otherData) {
// Show a small box in the right lower corner
const time = new Date(response.time)
$("#mcdiv321letterboxd").remove();
let main,div;
div = main = $('<div id="mcdiv321letterboxd"></div>').appendTo(document.body);
div.css({
position:"fixed",
bottom :0,
right: 0,
width: 230,
minHeight: 44,
color: "#789",
padding:" 3px",
zIndex: "5010001",
fontFamily : "Helvetica,Arial,sans-serif"
});
const CSS = `<style>
.rating {
display: inline-block;
height: 16px;
background: url(https://s.ltrbxd.com/static/img/sprite.18bffd5e.svg) no-repeat -290px -90px;
background-position-x: -290px;
background-position-y: -90px;
text-indent: 110%;
white-space: nowrap;
overflow: hidden;
}
.rating-green .rating{
background-position:-450px -50px
}
.rating-green .rated-0{
width:0
}
.rating-green .rated-1{
width:13px;
background-position:-515px -50px
}
.rating-green .rated-2{
width:12px
}
.rating-green .rated-3{
width:26px;
background-position:-502px -50px
}
.rating-green .rated-4{
width:25px
}
.rating-green .rated-5{
width:39px;
background-position:-489px -50px
}
.rating-green .rated-6{
width:38px
}
.rating-green .rated-7{
width:52px;
background-position:-476px -50px
}
.rating-green .rated-8{
width:51px
}
.rating-green .rated-9{
width:65px;
background-position:-463px -50px
}
.rating-green .rated-10{
width:64px
}
.rating-green-tiny .rating{
background-position:-350px -380px;
height:9px
}
.rating-green-tiny .rated-2{
width:9px
}
.rating-green-tiny .rated-10{
width:49px
}
.rating-histogram,.rating-histogram ul{
height:44px
}
.rating-histogram .rating-1,.rating-histogram .rating-5{
position:absolute;
bottom:0;
display:block;
height:9px
}
.rating-histogram .rating-1 .rating,.rating-histogram .rating-5 .rating{
display:block
}
.rating-histogram .rating-1{
left:0
}
.rating-histogram .rating-5{
right:0
}
.rating-histogram ul{
width:200px;
left:15px
}
.rating-histogram ul,.rating-histogram ul li{
display:block;
overflow:hidden;
position:absolute;
bottom:0
}
.rating-histogram ul li{
height:1px;
width:30px;
height:100%;
font-size:10px;
line-height:1;
text-indent:110%;
white-space:nowrap;
border-bottom:0px
}
#mcdiv321letterboxd:hover .rating-histogram ul li{
border-bottom:2px solid green
}
.rating-histogram i{
background:#456;
border-top-right-radius:2px;
border-top-left-radius:2px
}
.rating-histogram a,.rating-histogram i{
width:100%;
position:absolute;
bottom:0;
left:0
}
.rating-histogram a{
display:block;
top:0;
right:0;
background:none;
padding:0;
color:#789
}
.rating-histogram a:link,.rating-histogram a:visited{
color:#789;
text-decoration:none
}
.rating-histogram a:hover i{
background-color:#678
}
.ratings-histogram-chart .section-heading{
margin-bottom:15px
}
.ratings-histogram-chart .average-rating{
position:absolute;
top:8px;
left:188px;
z-index:1
}
.ratings-histogram-chart .average-rating .display-rating{
display:block;
font-size:20px;
text-align:center;
color:#789;
margin-left:1px;
line-height:40px;
width:33px;
height:33px;
border-radius:20px;
font-family:Graphik-Light-Web,sans-serif;
font-weight:400
}
.rating-histogram {
overflow:hidden;
color:#9ab;
display:block
width: 230px;
height: 44px;
position: relative
}
.ratings-histogram-chart .all-link.more-link {
font-size:10px;
position:absolute;
top:0;
left:180px;
}
#mcdiv321letterboxd .footer {
display:none;
}
#mcdiv321letterboxd:hover .footer {
display:block;
}
#mcdiv321letterboxd {
border:none;
border-radius: 0px;
background-color:transparent;
transition:bottom 0.7s, background-color 0.5s, height 0.5s;
}
#mcdiv321letterboxd:hover {
border-radius: 4px;
background-color:rgb(44, 52, 64)
}
/* Fixes/Resets for interfering site css */
#mcdiv321letterboxd .tooltip{
border: none;
box-shadow:none;
background-color:transparent;
opacity:1.0;
white-space: nowrap;
}
</style>`
$(CSS).appendTo(main);
let section = $(fixLetterboxdURLs(response.responseText)).appendTo(main)
section.find("h2").remove();
let identName = current.query
let identYear = current.year?' ('+current.year+')':''
let identOriginalName = ''
let identDirector = ''
if(otherData) {
if('name' in otherData && otherData.name) {
identName = otherData.name
}
if('year' in otherData && otherData.year) {
identYear = ' ('+otherData.year+')'
}
if('originalName' in otherData && otherData.originalName) {
identOriginalName = ' "'+otherData.originalName+'"'
}
if('directors' in otherData) {
identDirector = []
for(let i = 0; i < otherData.directors.length; i++) {
if('name' in otherData.directors[i]) {
identDirector.push(otherData.directors[i].name)
}
}
if(identDirector) {
identDirector = '<br><span style="font-size:10px">Dir. ' + identDirector.join(', ') + '</span>'
} else {
identDirector = ''
}
}
}
// Footer
let sub = $('<div class="footer"></div>').appendTo(main);
$('<span style="color:#789; font-size: 11px">' + identName + identOriginalName + identYear + identDirector + '</span>').appendTo(sub)
$('<br>').appendTo(sub)
$('<time style="color:#789; font-size: 11px;" datetime="'+time+'" title="'+time.toLocaleTimeString()+" "+time.toLocaleDateString()+'">'+minutesSince(time)+'</time>').appendTo(sub);
$('<a style="color:#789; font-size: 11px;" target="_blank" href="' + baseURL+letterboxdUrl + '" title="Open Letterboxd">@letterboxd.com</a>').appendTo(sub);
$('<span title="Hide me" style="cursor:pointer; float:right; color:#789; font-size: 11px">❎</span>').appendTo(sub).click(function() {
document.getElementById('mcdiv321letterboxd').remove()
});
$('<span title="Wrong movie!" style="cursor:pointer; float:right; color:#789; font-size: 11px">🙅</span>').appendTo(sub).click(function() {
removeFromWhiteList()
searchMovie(current.query, current.type, current.year, true);
});
$('<span style="clear:right">').appendTo(sub)
}
var Always = () => true;
var sites = {
'googleplay' : {
host : ["play.google.com"],
condition : Always,
products : [
{
condition : () => ~document.location.href.indexOf("/movies/details/"),
type : "movie",
data : () => document.querySelector("*[itemprop=name]").textContent
}
]
},
'imdb' : {
host : ["imdb.com"],
condition : () => !~document.location.pathname.indexOf("/mediaviewer") && !~document.location.pathname.indexOf("/mediaindex") && !~document.location.pathname.indexOf("/videoplayer"),
products : [
{
condition : function() {
let e = document.querySelector("meta[property='og:type']");
if(e) {
return e.content == "video.movie"
}
return false;
},
type : "movie",
data : function() {
var year = null;
var name = null;
var jsonld = null;
if(document.querySelector("#titleYear")) {
year = parseInt(document.querySelector("#titleYear a").firstChild.textContent);
}
if(document.querySelector("meta[property='og:title']") && document.querySelector("meta[property='og:title']").content) { // English title, this is the prefered title for Rottentomatoes' search
name = document.querySelector("meta[property='og:title']").content.trim()
if (name.indexOf('- IMDb') !== -1) {
name = name.replace('- IMDb', '').trim()
}
name = name.replace(/\(\d{4}\)/, '').trim()
}
if(document.querySelector('script[type="application/ld+json"]')) { // Original title and release year
jsonld = parseLDJSON(["name", "datePublished"]);
if (name == null)
name = jsonld[0];
if (year == null)
year = parseInt(jsonld[1].match(/\d{4}/)[0]);
}
if(name != null && year != null) {
return [name, year]; // Use original title
}
if(document.querySelector(".originalTitle") && document.querySelector(".title_wrapper h1")) {
return [document.querySelector(".title_wrapper h1").firstChild.data.trim(), year] // Use localized title
} else if(document.querySelector("h1[itemprop=name]")) { // Movie homepage (New design 2015-12)
return [document.querySelector("h1[itemprop=name]").firstChild.textContent.trim(), year];
} else if(document.querySelector("*[itemprop=name] a") && document.querySelector("*[itemprop=name] a").firstChild.data) { // Subpage of a move
return [document.querySelector("*[itemprop=name] a").firstChild.data.trim(), year];
} else if(document.querySelector(".title-extra[itemprop=name]")) { // Movie homepage: sub-/alternative-/original title
return [document.querySelector(".title-extra[itemprop=name]").firstChild.textContent.replace(/\"/g,"").trim(), year];
} else { // Movie homepage (old design)
return document.querySelector("*[itemprop=name]").firstChild.textContent.trim();
}
}
}
]
},
'metacritic' : {
host : ["www.metacritic.com"],
condition : () => document.querySelector("meta[property='og:type']"),
products : [{
condition : () => document.querySelector("meta[property='og:type']").content == "video.movie",
type : "movie",
data : function() {
var year = null;
if(document.querySelector(".release_year")) {
year = parseInt(document.querySelector(".release_year").firstChild.textContent);
} else if(document.querySelector(".release_data .data")) {
year = document.querySelector(".release_data .data").textContent.match(/(\d{4})/)[1]
}
return [document.querySelector("meta[property='og:title']").content, year]
}
}]
},
'amazon' : {
host : ["amazon."],
condition : Always,
products : [{
condition : () => document.querySelector('[data-automation-id=title]'),
type : "movie",
data : () => document.querySelector('[data-automation-id=title]').textContent.trim()
}]
},
'BoxOfficeMojo' : {
host : ["boxofficemojo.com"],
condition : () => ~document.location.search.indexOf("id="),
products : [{
condition : () => document.querySelector("#body table:nth-child(2) tr:first-child b"),
type : "movie",
data : function() {
var year = null;
try {
var tds = document.querySelectorAll("#body table:nth-child(2) tr:first-child table table table td");
for(var i = 0; i< tds.length; i++) {
if(~tds[i].innerText.indexOf("Release Date")) {
year = parseInt(tds[i].innerText.match(/\d{4}/)[0]);
break;
}
}
} catch(e) { }
return [document.querySelector("#body table:nth-child(2) tr:first-child b").firstChild.data, year];
}
}]
},
'AllMovie' : {
host : ["allmovie.com"],
condition : () => document.querySelector("h2[itemprop=name].movie-title"),
products : [{
condition : () => document.querySelector("h2[itemprop=name].movie-title"),
type : "movie",
data : () => document.querySelector("h2[itemprop=name].movie-title").firstChild.data.trim()
}]
},
'en.wikipedia' : {
host : ["en.wikipedia.org"],
condition : Always,
products : [{
condition : function() {
if(!document.querySelector(".infobox .summary")) {
return false;
}
var r = /\d\d\d\d films/;
return $("#catlinks a").filter((i,e) => e.firstChild.data.match(r)).length;
},
type : "movie",
data : () => document.querySelector(".infobox .summary").firstChild.data
}]
},
'movies.com' : {
host : ["movies.com"],
condition : () => document.querySelector("meta[property='og:title']"),
products : [{
condition : Always,
type : "movie",
data : () => document.querySelector("meta[property='og:title']").content
}]
},
'themoviedb' : {
host : ["themoviedb.org"],
condition : () => document.querySelector("meta[property='og:type']"),
products : [{
condition : () => document.querySelector("meta[property='og:type']").content == "movie",
type : "movie",
data : function() {
var year = null;
try {
year = parseInt(document.querySelector(".release_date").innerText.match(/\d{4}/)[0]);
} catch(e) {}
return [document.querySelector("meta[property='og:title']").content, year]
}
}]
},
'rottentomatoes' : {
host : ["rottentomatoes.com"],
condition : Always,
products : [{
condition : () => document.location.pathname.startsWith("/m/"),
type : "movie",
data : () => document.querySelector("h1").firstChild.textContent
}
]
},
'nme' : {
host : ["nme.com"],
condition : () => document.location.pathname.startsWith("/reviews/"),
products : [ {
condition : () => document.location.pathname.startsWith("/reviews/movie/"),
type : "movie",
data : function() {
var year = null;
try {
year = parseInt(document.querySelector("*[itemprop=datePublished]").content.match(/\d{4}/)[0])
} catch(e) {}
try {
return [ document.querySelector(".title-primary").textContent.match(/‘(.+?)’/)[1] , year ];
} catch(e) {
return [ document.querySelector("h1").textContent.match(/:\s*(.+)/)[1].trim() , year ];
}
}
}]
},
'itunes' : {
host : ["itunes.apple.com"],
condition : Always,
products : [{
condition : () => ~document.location.href.indexOf("/movie/"),
type : "movie",
data : () => parseLDJSON("name", (j) => (j["@type"] == "Movie"))
}]
},
'TVHoard' : {
host : ['tvhoard.com'],
condition : Always,
products : [{
condition : () => document.location.pathname.split("/").length === 3 && document.location.pathname.split('/')[1] === 'titles' && !document.querySelector('app-root title-secondary-details-panel .seasons') && document.querySelector('app-root title-page-container h1.title a'),
type : 'movie',
data : () => [document.querySelector('app-root title-page-container h1.title a').textContent.trim(), document.querySelector('app-root title-page-container title-primary-details-panel h1.title .year').textContent.trim().substring(1,5)]
}]
},
};
function main() {
var dataFound = false;
for(var name in sites) {
var site = sites[name];
if(site.host.some(function(e) {return ~this.indexOf(e)}, document.location.hostname) && site.condition()) {
for(var i = 0; i < site.products.length; i++) {
if(site.products[i].condition()) {
// Try to retrieve item name from page
var data;
try {
data = site.products[i].data();
} catch(e) {
data = false;
console.log("Letterboxd Error:")
console.log(e);
}
if(data) {
if(Array.isArray(data) && data[1]) {
searchMovie(data[0].trim(), site.products[i].type, parseInt(data[1]));
} else {
searchMovie(data.trim(), site.products[i].type);
}
dataFound = true
}
break;
}
}
break;
}
}
return dataFound;
}
async function adaptForRottentomatoesScript() {
if(!document.getElementById('mcdiv321rotten') || !document.getElementById('mcdiv321letterboxd')) {
return
}
const h = parseInt(document.getElementById('mcdiv321rotten').clientHeight) + 5
if(document.getElementById('mcdiv321letterboxd').dataset.adapted && parseInt(document.getElementById('mcdiv321letterboxd').dataset.adapted) == h) {
return
}
const letterboxd = document.getElementById('mcdiv321letterboxd')
letterboxd.style.bottom = h + 'px'
document.getElementById('mcdiv321letterboxd').dataset.adapted = h
}
(function() {
const firstRunResult = main();
var lastLoc = document.location.href;
var lastContent = document.body.innerText;
var lastCounter = 0;
function newpage() {
if(lastContent == document.body.innerText && lastCounter < 15) {
window.setTimeout(newpage, 500);
lastCounter++;
} else {
lastCounter = 0;
let re = main();
if(!re) { // No page matched or no data found
window.setTimeout(newpage, 1000);
}
}
}
window.setInterval(function() {
adaptForRottentomatoesScript()
if(document.location.href != lastLoc) {
lastLoc = document.location.href;
$("#mcdiv321letterboxd").remove();
window.setTimeout(newpage,1000);
}
},500);
if (!firstRunResult) {
// Initial run had no match, let's try again there may be new content
window.setTimeout(main, 2000);
}
})();