// ==UserScript== // @name IMDb TMDB Letterboxd Linker // @description Opens the corresponding IMDb, TMDB, or Letterboxd page for movies, TV shows and people with a single click. Additionally, it also displays IMDb ratings on both TMDB and Letterboxd pages. // @author Tetrax-10 // @namespace https://github.com/Tetrax-10/imdb-tmdb-letterboxd-linker // @version 2.0 // @license MIT // @match *://*.imdb.com/title/tt* // @match *://*.imdb.com/name/nm* // @match *://*.themoviedb.org/movie/* // @match *://*.themoviedb.org/tv/* // @match *://*.themoviedb.org/person/* // @match *://*.letterboxd.com/film/* // @connect imdb.com // @connect themoviedb.org // @homepageURL https://github.com/Tetrax-10/imdb-tmdb-letterboxd-linker // @supportURL https://github.com/Tetrax-10/imdb-tmdb-letterboxd-linker/issues // @icon https://tetrax-10.github.io/imdb-tmdb-letterboxd-linker/assets/icon.png // @run-at document-start // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @downloadURL none // ==/UserScript== ;(() => { const TMDB_API_KEY = GM_getValue("TMDB_API_KEY", null) GM_registerMenuCommand("Settings", showPopup) function showPopup() { GM_addStyle(` #linker-settings-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); display: flex; justify-content: center; align-items: center; z-index: 10000; } #linker-settings-popup { background-color: rgb(32, 36, 44); padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); z-index: 10001; font-family: Source Sans Pro, Arial, sans-serif; font-feature-settings: normal; font-variation-settings: normal; font-size: 100%; font-weight: inherit; line-height: 1.5; letter-spacing: normal; width: 60%; max-height: 80vh; overflow-y: auto; display: flex; flex-direction: column; -webkit-overflow-scrolling: touch; } #linker-settings-popup input { color: #cfcfcf; } #linker-settings-popup label { color: rgb(207, 207, 207); font-weight: bold; font-size: 1.2em; margin-bottom: 10px; } #linker-settings-popup input { background-color: rgb(32, 36, 44); border: 1px solid rgb(207, 207, 207); color: rgb(207, 207, 207); padding: 10px; border-radius: 8px; margin-bottom: 10px; } `) // Create overlay const overlay = document.createElement("div") overlay.id = "linker-settings-overlay" overlay.onclick = (e) => { if (e.target === overlay) closePopup(overlay) } // popup element const popup = document.createElement("div") popup.id = "linker-settings-popup" // popup content const label = document.createElement("label") label.textContent = "Enter your TMDB API key:" // input element const input = document.createElement("input") input.type = "text" input.value = GM_getValue("TMDB_API_KEY", "") input.oninput = (e) => GM_setValue("TMDB_API_KEY", e.target.value) // inject popup popup.appendChild(label) popup.appendChild(input) overlay.appendChild(popup) document.body.appendChild(overlay) input.focus() } function closePopup(overlay) { document.body.removeChild(overlay) } const imdbPageCss = ` #linker-parent { display: flex; align-self: center; } #linker-letterboxd-link { align-self: center; } #linker-letterboxd { height: 27px; width: 53px; margin-top: 7px; } #linker-divider { border-left: 3px solid rgba(232, 230, 227, 0.5) !important; height: 25px; border-radius: 10px; margin-left: 10px; align-self: center; } #linker-loading { height: 20px; align-self: center; text-align: center; margin-left: 10px; margin-right: 40px; } #linker-tmdb-link { height: 27px; width: 60px; background: #022036 !important; color: #51b4ad !important; border: solid #51b4ad 2px !important; border-radius: 6px; align-self: center; margin-left: 10px; margin-right: 20px; font-weight: bold; text-align: center; align-content: center; } @media only screen and (max-width: 767px) { #linker-loading { margin-right: 6px; } #linker-tmdb-link { width: 48px; margin-left: 10px; margin-right: 10px; font-size: smaller; } } ` const tmdbTitlePageCss = ` #linker-parent { margin-top: 20px; display: flex; align-items: flex-start; } #linker-imdb-svg-bg { fill: #c59f00 !important; } #linker-divider { border-left: 2px solid rgba(232, 230, 227, 0.5) !important; height: 23px; border-radius: 10px; margin-left: 10px; } #linker-loading { height: 20px; margin-left: 10px; } #linker-imdb-container { display: flex; align-items: center; margin-left: 10px; } #linker-imdb-rating { margin-left: 10px; } html.k-mobile #linker-parent { margin-top: unset; margin-left: auto; margin-right: auto; } ` const tmdbPersonPageCss = ` #linker-imdb-svg, #linker-letterboxd-svg path { --darkreader-inline-fill: #d0d0d0 !important; } ` const letterboxdTitlePageCss = ` #linker-loading { border: 2px solid rgba(255, 255, 255, 0.3) !important; border-top: 2px solid #cfcfcf !important; height: 8px !important; width: 8px !important; margin-left: 4px; } ` const commonUtils = (() => { const ImdbSvg = `` const ImdbSvgWithoutBg = `` const letterboxdSvg = `` const LetterboxdSvgWithoutBg = `` function isMobile() { const data = navigator.userAgent || navigator.vendor || window.opera // prettier-ignore if (navigator.userAgentData?.mobile || /Mobi/i.test(navigator.userAgent) || 'ontouchstart' in document.documentElement || /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(data) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(data.substr(0, 4))) { return true } else { return false } } async function waitForElement(selector, timeout = null, nthElement = 1) { // wait till document body loads while (!document.body) { await new Promise((resolve) => setTimeout(resolve, 10)) } nthElement -= 1 return new Promise((resolve) => { if (document.querySelectorAll(selector)?.[nthElement]) { return resolve(document.querySelectorAll(selector)?.[nthElement]) } const observer = new MutationObserver(async () => { if (document.querySelectorAll(selector)?.[nthElement]) { resolve(document.querySelectorAll(selector)?.[nthElement]) observer.disconnect() } else { if (timeout) { async function timeOver() { return new Promise((resolve) => { setTimeout(() => { observer.disconnect() resolve(false) }, timeout) }) } resolve(await timeOver()) } } }) observer.observe(document.body, { childList: true, subtree: true, }) }) } async function getImdbRating(imdbId) { if (!imdbId) return [undefined, undefined] return new Promise((resolve) => { GM_xmlhttpRequest({ method: "GET", url: `https://www.imdb.com/title/${imdbId}/ratings`, onload: function (response) { const parser = new DOMParser() const dom = parser.parseFromString(response.responseText, "text/html") const rating = dom.querySelector(`div[data-testid="rating-button__aggregate-rating__score"] > span`)?.innerText const numRating = dom.querySelector(`div[data-testid="rating-button__aggregate-rating__score"] + div`)?.innerText resolve([rating, numRating]) }, onerror: function (error) { console.error(`Can't scrape IMDb: ${imdbId}`, error) }, }) }) } function createDividerElement() { const divider = document.createElement("div") divider.id = "linker-divider" return divider } function createParentContainer() { const parentContainer = document.createElement("div") parentContainer.id = "linker-parent" return parentContainer } function createLoadingElement() { const loadingElement = document.createElement("div") loadingElement.id = "linker-loading" GM_addStyle(` #linker-loading { border: 4px solid rgba(255, 255, 255, 0.3); border-radius: 50%; border-top: 4px solid #cfcfcf; width: 22px; height: 22px; animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } `) return loadingElement } return { isMobile: isMobile, waitForElement: waitForElement, getImdbRating: getImdbRating, svg: { ImdbSvg: ImdbSvg, ImdbSvgWithoutBg: ImdbSvgWithoutBg, letterboxdSvg: letterboxdSvg, LetterboxdSvgWithoutBg: LetterboxdSvgWithoutBg, }, element: { createDividerElement: createDividerElement, createParentContainer: createParentContainer, createLoadingElement: createLoadingElement, }, } })() const imdbPageUtils = (() => { function createLetterboxdElement(imdbId) { const linkElement = document.createElement("a") linkElement.id = "linker-letterboxd-link" linkElement.href = imdbId.startsWith("https") ? imdbId : `https://letterboxd.com/imdb/${imdbId}/` linkElement.target = "_blank" linkElement.innerHTML = commonUtils.svg.letterboxdSvg return linkElement } function createTmdbElement(tmdbData) { const linkElement = document.createElement("a") linkElement.id = "linker-tmdb-link" linkElement.target = "_blank" linkElement.innerText = "TMDB" if (tmdbData["media_type"] === "tv_episode") { linkElement.href = `https://www.themoviedb.org/tv/${tmdbData["show_id"]}/season/${tmdbData["season_number"]}/episode/${tmdbData["episode_number"]}` } else if (typeof tmdbData === "object") { linkElement.href = `https://www.themoviedb.org/${tmdbData["media_type"]}/${tmdbData.id}` } else if (typeof tmdbData === "string") { linkElement.href = tmdbData } return linkElement } return { element: { createLetterboxdElement: createLetterboxdElement, createTmdbElement: createTmdbElement, }, } })() async function imdbTitlePageInjector() { // check is mobile const isMobile = location.host.includes("m.imdb") // extract imdb id from url const path = location.pathname.split("/") const imdbId = path[2] || null // create elements that doesn't require any API calls const parentContainer = commonUtils.element.createParentContainer() const letterboxdElement = imdbPageUtils.element.createLetterboxdElement(imdbId) const dividerElement = commonUtils.element.createDividerElement() const loadingElement = commonUtils.element.createLoadingElement() // inject elements that doesn't require any API calls window.addEventListener("load", () => { commonUtils.waitForElement("div:has( > div[data-testid='hero-rating-bar__user-rating'])", 10000, isMobile ? 2 : 1).then((element) => { element.insertBefore(parentContainer, element.firstChild) parentContainer.appendChild(letterboxdElement) parentContainer.appendChild(dividerElement) if (!TMDB_API_KEY) return parentContainer.appendChild(loadingElement) }) }) // inject parent element if not present function injectParentElement() { if (document.querySelector("#linker-parent")) return commonUtils.waitForElement("div:has( > div[data-testid='hero-rating-bar__user-rating'])", 10000, isMobile ? 2 : 1).then((element) => { element.insertBefore(parentContainer, element.firstChild) }) } // inject the parent element every 100ms. Since IMDb sometimes re-renders its components, the parent element may occasionally be removed. const intervalId = setInterval(injectParentElement, 100) // Stop the interval after 5 seconds setTimeout(() => { clearInterval(intervalId) }, 5000) if (!TMDB_API_KEY) { await commonUtils.waitForElement("#linker-divider") const tmdbElement = imdbPageUtils.element.createTmdbElement(`https://www.themoviedb.org/redirect?external_source=imdb_id&external_id=${imdbId}`) parentContainer.appendChild(tmdbElement) return } // fetch tmdb id const tmdbRawRes = await fetch(`https://api.themoviedb.org/3/find/${imdbId}?api_key=${TMDB_API_KEY}&external_source=imdb_id`) const tmdbRes = await tmdbRawRes.json() const tmdbData = tmdbRes["movie_results"]?.[0] || tmdbRes["tv_results"]?.[0] || tmdbRes["tv_episode_results"]?.[0] if (tmdbData && (await commonUtils.waitForElement("#linker-loading", 10000))) { // inject tmdb element and remove loading element const tmdbElement = imdbPageUtils.element.createTmdbElement(tmdbData) parentContainer.removeChild(loadingElement) parentContainer.appendChild(tmdbElement) } else { // if no tmdb id then remove divider and loading element parentContainer.removeChild(dividerElement) parentContainer.removeChild(loadingElement) } } async function imdbPersonPageInjector() { // check is mobile const isMobile = location.host.includes("m.imdb") // extract imdb id from url const path = location.pathname.split("/") const imdbId = path[2] || null // create parent and loading element const parentContainer = commonUtils.element.createParentContainer() const loadingElement = commonUtils.element.createLoadingElement() // inject parent and loading element window.addEventListener("load", () => { commonUtils.waitForElement("div:has( > .starmeter-logo)", 10000, isMobile ? 2 : 1).then((element) => { element.insertBefore(parentContainer, element.firstChild) parentContainer.appendChild(loadingElement) }) }) // inject parent element if not present function injectParentElement() { if (document.querySelector("#linker-parent")) return commonUtils.waitForElement("div:has( > .starmeter-logo)", 10000, isMobile ? 2 : 1).then((element) => { element.insertBefore(parentContainer, element.firstChild) }) } // inject the parent element every 100ms. Since IMDb sometimes re-renders its components, the parent element may occasionally be removed. const intervalId = setInterval(injectParentElement, 100) // Stop the interval after 5 seconds setTimeout(() => { clearInterval(intervalId) }, 5000) if (!TMDB_API_KEY) { await commonUtils.waitForElement("#linker-loading") const tmdbElement = imdbPageUtils.element.createTmdbElement(`https://www.themoviedb.org/redirect?external_source=imdb_id&external_id=${imdbId}`) parentContainer.removeChild(loadingElement) parentContainer.appendChild(tmdbElement) return } // fetch tmdb id const tmdbRawRes = await fetch(`https://api.themoviedb.org/3/find/${imdbId}?api_key=${TMDB_API_KEY}&external_source=imdb_id`) const tmdbRes = await tmdbRawRes.json() const tmdbData = tmdbRes["movie_results"]?.[0] || tmdbRes["tv_results"]?.[0] || tmdbRes["tv_episode_results"]?.[0] || tmdbRes["person_results"]?.[0] if (tmdbData && (await commonUtils.waitForElement("#linker-loading", 10000))) { // inject tmdb element and remove loading element const tmdbElement = imdbPageUtils.element.createTmdbElement(tmdbData) const letterboxdElement = imdbPageUtils.element.createLetterboxdElement(`https://letterboxd.com/tmdb/${tmdbData.id}/person`) const dividerElement = commonUtils.element.createDividerElement() parentContainer.removeChild(loadingElement) parentContainer.appendChild(letterboxdElement) parentContainer.appendChild(dividerElement) parentContainer.appendChild(tmdbElement) } else { // if no tmdb id then remove loading element parentContainer.removeChild(loadingElement) } } const tmdbTitlePageUtils = (() => { function createLetterboxdElement(tmdbId, type) { const linkElement = document.createElement("a") linkElement.href = `https://letterboxd.com/tmdb/${tmdbId}/${type === "movie" ? "" : type}` linkElement.target = "_blank" linkElement.innerHTML = commonUtils.svg.letterboxdSvg return linkElement } function createImdbContainer() { const imdbContainer = document.createElement("div") imdbContainer.id = "linker-imdb-container" return imdbContainer } function createImdbElement(imdbId) { const linkElement = document.createElement("a") linkElement.href = `https://imdb.com/title/${imdbId}` linkElement.target = "_blank" linkElement.innerHTML = commonUtils.svg.ImdbSvg return linkElement } function createImdbRatingElement(rating, numRatings) { const text = rating !== undefined ? `${rating}${numRatings !== undefined ? ` ( ${numRatings} )` : ""}` : null const ratingElement = document.createElement("div") ratingElement.id = "linker-imdb-rating" ratingElement.innerText = text if (text) { return ratingElement } else { return null } } return { element: { createLetterboxdElement: createLetterboxdElement, createImdbContainer: createImdbContainer, createImdbElement: createImdbElement, createImdbRatingElement: createImdbRatingElement, }, } })() async function tmdbTitlePageInjector() { // check is mobile const isMobile = commonUtils.isMobile() // extract tmdb id from url const path = location.pathname.split("/") const tmdbId = path[2].match(/\d+/)?.[0] || null // inject elements that doesn't require any API calls const parentContainer = commonUtils.element.createParentContainer() const letterboxdElement = tmdbTitlePageUtils.element.createLetterboxdElement(tmdbId, path[1]) const dividerElement = commonUtils.element.createDividerElement() const imdbContainer = tmdbTitlePageUtils.element.createImdbContainer() const loadingElement = commonUtils.element.createLoadingElement() commonUtils.waitForElement(`.header.poster${isMobile ? " > .title" : ""}`, 10000).then((element) => { if (isMobile) { element.insertBefore(parentContainer, element?.firstChild?.nextSibling?.nextSibling) } else { element.appendChild(parentContainer) } parentContainer.appendChild(letterboxdElement) if (!TMDB_API_KEY) return parentContainer.appendChild(dividerElement) parentContainer.appendChild(imdbContainer) imdbContainer.appendChild(loadingElement) }) if (!TMDB_API_KEY) return // fetch imdb id const tmdbRawRes = await fetch(`https://api.themoviedb.org/3/${path[1]}/${tmdbId}/external_ids?api_key=${TMDB_API_KEY}`) const tmdbRes = await tmdbRawRes.json() const imdbId = tmdbRes["imdb_id"] || null // exit if no IMDb Id found if (!imdbId) { parentContainer.removeChild(dividerElement) parentContainer.removeChild(imdbContainer) return } // inject imdb element const imdbElement = tmdbTitlePageUtils.element.createImdbElement(imdbId) commonUtils.waitForElement(`.header.poster${isMobile ? " > .title" : ""}`, 10000).then(async () => { await commonUtils.waitForElement("#linker-imdb-container", 5000) imdbContainer.insertBefore(imdbElement, loadingElement) }) // scrape IMDb ratings const [imdbRating, imdbNumRating] = await commonUtils.getImdbRating(imdbId) // inject imdb rating element const imdbRatingElement = tmdbTitlePageUtils.element.createImdbRatingElement(imdbRating, imdbNumRating) await commonUtils.waitForElement("#linker-loading", 10000) imdbContainer.removeChild(loadingElement) if (imdbRatingElement) imdbContainer.appendChild(imdbRatingElement) } const tmdbPersonPageUtils = (() => { function createLogoElement(id, type = "imdb") { const linkContainer = document.createElement("div") const linkElement = document.createElement("a") linkElement.className = "social_link" linkElement.href = type === "imdb" ? `https://www.imdb.com/name/${id}` : `https://letterboxd.com/tmdb/${id}/person` linkElement.target = "_blank" linkElement.title = `Visit ${type === "imdb" ? "IMDb" : "Letterboxd"}` linkElement.rel = "noopener" if (type !== "imdb") linkElement.style.width = "38px" const svgContainer = document.createElement("div") svgContainer.className = "glyphicons_v2" svgContainer.style.width = "50px" svgContainer.innerHTML = type === "imdb" ? commonUtils.svg.ImdbSvgWithoutBg : commonUtils.svg.LetterboxdSvgWithoutBg linkElement.appendChild(svgContainer) linkContainer.appendChild(linkElement) return linkContainer } return { element: { createLogoElement: createLogoElement, }, } })() async function tmdbPersonPageInjector() { // extract tmdb id from url const path = location.pathname.split("/") const tmdbId = path[2].match(/\d+/)?.[0] || null // inject elements that doesn't require any API calls const letterboxdElement = tmdbPersonPageUtils.element.createLogoElement(tmdbId, "letterboxd") commonUtils.waitForElement(".social_links", 10000).then((element) => { element.insertBefore(letterboxdElement, element.firstChild) }) if (!TMDB_API_KEY) return // fetch imdb id const tmdbRawRes = await fetch(`https://api.themoviedb.org/3/${path[1]}/${tmdbId}/external_ids?api_key=${TMDB_API_KEY}`) const tmdbRes = await tmdbRawRes.json() const imdbId = tmdbRes["imdb_id"] || null // inject imdb element if (imdbId) { const imdbElement = tmdbPersonPageUtils.element.createLogoElement(imdbId) commonUtils.waitForElement(`.social_links`, 10000).then(async (element) => { await commonUtils.waitForElement("#linker-letterboxd-svg") element.insertBefore(imdbElement, letterboxdElement.nextElementSibling) }) } } function letterboxdTitlePageInjector() { commonUtils.waitForElement(`.micro-button.track-event[data-track-action="IMDb"]`, 10000).then(async (element) => { // preserve original display style const originalDisplayStyle = element.style.display // inject loading element const loadingElement = commonUtils.element.createLoadingElement() element.style.display = "inline-flex" element.appendChild(loadingElement) // fetch imdb id and get ratings const imdbId = element.href?.match(/\/title\/(tt\d+)\/?/)?.[1] ?? null const [imdbRating, imdbNumRating] = await commonUtils.getImdbRating(imdbId) // remove loading element await commonUtils.waitForElement("#linker-loading", 10000) element.removeChild(loadingElement) element.style.display = originalDisplayStyle // update imdb button element.innerText = `IMDB${imdbRating ? ` | ${imdbRating}` : ""}${imdbNumRating !== undefined ? ` (${imdbNumRating})` : ""}` }) } const currentURL = location.protocol + "//" + location.hostname + location.pathname if (/^(https?:\/\/[^.]+\.imdb\.com\/title\/tt[^\/]+(?:\/\?.*)?\/?)$/.test(currentURL)) { // IMDb title page GM_addStyle(imdbPageCss) imdbTitlePageInjector() } else if (/^(https?:\/\/[^.]+\.imdb\.com\/name\/nm[^\/]+(?:\/\?.*)?\/?)$/.test(currentURL)) { // IMDb person page GM_addStyle(imdbPageCss) imdbPersonPageInjector() } else if (/^(https?:\/\/[^.]+\.themoviedb\.org\/(movie|tv)\/\d[^\/]+(?:\/\?.*)?\/?)$/.test(currentURL)) { // TMDB title page GM_addStyle(tmdbTitlePageCss) tmdbTitlePageInjector() } else if (/^(https?:\/\/[^.]+\.themoviedb\.org\/person\/\d[^\/]+(?:\/\?.*)?\/?)$/.test(currentURL)) { // TMDB person page GM_addStyle(tmdbPersonPageCss) tmdbPersonPageInjector() } else if (/^(https?:\/\/letterboxd\.com\/film\/[^\/]+(?:\/\?.*)?\/?(crew|details|genres)?)$/.test(currentURL)) { // Letterboxd title page GM_addStyle(letterboxdTitlePageCss) letterboxdTitlePageInjector() } })()