Adds an icon with a link to Stremio on every film on Letterboxd
// ==UserScript==
// @name Letterboxd Stremio Icon and link
// @namespace http://tampermonkey.net/
// @version 2024-01-12
// @description Adds an icon with a link to Stremio on every film on Letterboxd
// @author CarnivalHipster
// @match https://letterboxd.com/film/*
// @match https://letterboxd.com/*/film/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=stremio.com
// @license MIT
// ==/UserScript==
const tmdbApiKey = ''; //Put Your TMDB Key here
var StremioImg = 'https://www.google.com/s2/favicons?sz=64&domain=stremio.com';
var filmTitle = filmData['name']
filmTitle = filmTitle.replace(/[\/\\#,+()$~%.":*?<>{}!]/g, '');
filmTitle = filmTitle.replace(/&/g, '%26')
var filmYear = filmData['releaseYear']
var tmdbId = getMovieId("TMDb");
var imdbId = getMovieId("IMDb");
var pixelSize = 30;
function getMovieId(idType) {
var element = document.querySelector(`[data-track-action="${idType}"]`); // Gets Tmdb and Imdb Ids
if (typeof element !== 'undefined' && element !== null) {
var buttons = document.getElementsByClassName('micro-button track-event');
if (idType == "TMDb"){
for (var i = 0; i < buttons.length; i++) {
var tmdbUrl = buttons[i].getAttribute('href');
if (tmdbUrl.includes('themoviedb.org')) {
var tmdbId = tmdbUrl.split('/').filter(str => str)[3];
console.log(`${idType} ID: ${tmdbId}`);
return tmdbId;
}
}
} else {
var idBtn = buttons[0].href.match(/(tt\d+)/); // Capture 'tt' followed by one or more digits
var imdbId = idBtn ? idBtn[1] : null; // Check if the match was successful
if (imdbId) {
console.log(`IMDb ID: ${imdbId}`);
} else {
console.error('IMDb ID not found');
}
return imdbId;
}
}
return null;
}
if (document.readyState == "complete" || document.readyState == "loaded" || document.readyState == "interactive") {
main();
} else {
document.addEventListener('DOMContentLoaded', main);
}
function makeApiCall(url) {
return fetch(url)
.then(response => response.json())
.catch(error => {
throw error;
});
}
function getImdbIdFromTitleAndYear(filmTitle, filmYear) {
const api_key = tmdbApiKey;
const tmdbUrl = `https://api.themoviedb.org/3/search/movie?api_key=${api_key}&query=${filmTitle}&year=${filmYear}`;
return makeApiCall(tmdbUrl)
.then(data => {
if (data.total_results > 0) {
const tmdbId = data.results[0].id;
const tmdbMovieUrl = `https://api.themoviedb.org/3/movie/${tmdbId}?api_key=${api_key}`;
return makeApiCall(tmdbMovieUrl);
} else {
throw new Error(`No movie was found with the title "${filmTitle}" and release year "${filmYear}".`);
}
})
.then(movieData => {
if (movieData && movieData.imdb_id) {
return movieData.imdb_id;
} else {
throw new Error(`IMDb ID not found for the movie "${filmTitle}" (${filmYear})`);
}
});
}
function createIcon(cont, title, href, icon, eventListener) {
var a = document.createElement('a');
a.href = href;
a.title = title;
a.setAttribute('target', '_blank');
var img = document.createElement('img');
img.src = icon;
img.style.padding = '3px';
img.style.borderRadius = '3px';
img.setAttribute('height', pixelSize);
img.setAttribute('width', pixelSize);
a.appendChild(img);
var cell = cont.insertCell(-1);
cell.appendChild(a);
if (eventListener) {
img.addEventListener('click', async (event) => {
eventListener();
img.style.opacity = '0.6';
setTimeout(() => {
img.style.opacity = '1';
}, 100);
});
}
console.log(title, 'icon built successfully.');
return a;
}
function applyCSS() {
const iconElt = document.querySelectorAll('#media-icon');
iconElt.forEach(el => {
el.style.display = 'table';
el.style.margin = '0 auto';
el.childNodes.forEach(child => {
child.style.padding = '2px 4px 0px 4px';
child.id = 'tor-icon';
});
});
console.log('CSS applied.');
}
function createIconStremio(tr, title, href, icon) {
var eventListener = async function () {
if (imdbId == undefined) {
imdbId = await getImdbIdFromTitleAndYear(filmTitle, filmYear);
console.log(`The IMDB ID grabbed from their API for ${filmTitle} (${filmYear}) is ${imdbId}.`);
} else {
console.log(`The IMDB ID for ${filmTitle} (${filmYear}) is ${imdbId}.`);
}
};
var stremioIconLink = createIcon(tr, title, href, icon, eventListener);
var stremioIconImg = stremioIconLink.querySelector('img');
}
async function setupStremioIcon(tr) {
await fetchImdbIdOnLoad();
createIconStremio(tr,'Stremio', `https://web.stremio.com/#/detail/movie/${imdbId}/${imdbId}`, StremioImg);
}
async function fetchImdbIdOnLoad() {
try {
imdbId = await getImdbIdFromTitleAndYear(filmTitle, filmYear);
console.log(`Fetched IMDb ID: ${imdbId}`);
} catch (error) {
console.error(`Error fetching IMDb ID: ${error}`);
}
}
async function main() {
let divContainer = document.querySelector('#media-icon');
if (!divContainer) {
const actionsPanel = document.querySelector('.js-actions-panel');
const listItem = document.createElement('li');
divContainer = document.createElement('div');
divContainer.id = 'media-icon';
listItem.appendChild(divContainer);
actionsPanel.insertBefore(listItem, actionsPanel.lastChild);
}
let tr = divContainer.querySelector('tr');
if (!tr) {
tr = document.createElement('tr');
tr.style.display = 'flex';
divContainer.appendChild(tr);
}
await setupStremioIcon(tr);
applyCSS();
}