// ==UserScript==
// @name Debrid Media Manager
// @namespace https://debridmediamanager.com
// @version 1.6.2
// @description Add accessible DMM buttons to IMDB, MDBList, AniDB, TraktTV, and Bittorrent sites with magnet links
// @author Ben Adrian Sarmiento <[email protected]>
// @license MIT
// @match *://*/*
// @grant none
// ==/UserScript==
(function () {
"use strict";
const DMM_HOST = "https://debridmediamanager.com";
const X_DMM_HOST = "https://x.debridmediamanager.com";
const SEARCH_BTN_LABEL = "DMM🔎";
function createButton(text, url) {
const button = document.createElement("button");
// button.tabIndex = 0;
// button.disabled = false;
button.textContent = text;
button.style.fontFamily = "'Roboto', sans-serif";
button.style.marginLeft = "5px";
button.style.padding = "3px 5px";
button.style.border = "none";
button.style.borderRadius = "3px";
button.style.background = "#00A0B0"; // Teal color
button.style.color = "#ECF0F1"; // Off-white color
button.style.cursor = "pointer";
button.style.transition = "background-color 0.3s, transform 0.1s";
// Hover effect
button.onmouseover = function () {
this.style.background = "#EDC951"; // Yellow color
};
// Revert hover effect
button.onmouseout = function () {
this.style.background = "#00A0B0"; // Teal color
};
// Click effect
button.onmousedown = function () {
this.style.transform = "scale(0.95)";
this.style.background = "#CC333F"; // Dark red color
};
// Revert click effect
button.onmouseup = function () {
this.style.transform = "scale(1)";
this.style.background = "#EDC951"; // Yellow color (same as hover effect)
};
// Open new tab on click
button.onclick = (event) => {
event.preventDefault();
window.open(url, "_blank");
};
return button;
}
function createLink(text, url) {
const link = document.createElement("a");
link.textContent = text;
link.href = url;
link.target = "_blank"; // Opens the link in a new tab
link.style.display = "inline-block"; // Display as inline-block to style like a button
link.style.fontFamily = "'Roboto', sans-serif";
link.style.marginLeft = "5px";
link.style.padding = "3px 5px";
link.style.border = "none";
link.style.borderRadius = "3px";
link.style.background = "#00A0B0"; // Teal color
link.style.color = "#ECF0F1"; // Off-white color
link.style.textDecoration = "none"; // Remove underline from the link
link.style.cursor = "pointer";
return link;
}
function addButtonToElement(element, text, url) {
const button = createButton(text, url);
element.appendChild(button);
}
function addLinkToElement(element, text, url) {
const button = createLink(text, url);
element.appendChild(button);
}
function getInfoHashFromMagnetLink(magnetLink) {
const urlParams = new URLSearchParams(magnetLink.substring(magnetLink.indexOf('?')));
const xt = urlParams.get('xt');
if (xt && xt.startsWith('urn:btih:')) {
return xt.substring(9); // Remove 'urn:btih:'
}
return null;
}
// IMDB functions
function addButtonsToIMDBSingleTitle() {
const targetElement = document.querySelector(
"section.ipc-page-background h1 > span"
);
if (targetElement && targetElement.hasAttribute("data-dmm-btn-added"))
return;
targetElement.setAttribute("data-dmm-btn-added", "true");
const searchUrl = `${X_DMM_HOST}/${window.location.pathname
.replaceAll("/", "")
.substring(5)}`;
addButtonToElement(targetElement, SEARCH_BTN_LABEL, searchUrl);
}
function addButtonsToIMDBList() {
const items = Array.from(
document.querySelectorAll(
".lister-item .lister-item-header, .lister-item .media"
)
).filter((item) => !item.hasAttribute("data-dmm-btn-added"));
items.forEach((item) => {
item.setAttribute("data-dmm-btn-added", "true");
let link = item.querySelector('a[href^="/title/"]').href;
let imdbId = link.match(/tt\d+/)?.[0];
if (!imdbId) return;
const searchUrl = `${X_DMM_HOST}/${imdbId}`;
addButtonToElement(item, SEARCH_BTN_LABEL, searchUrl);
});
changeObserver("ul.ipc-metadata-list", addButtonsToIMDBList);
}
function addButtonsToIMDBChart() {
const items = Array.from(document.querySelectorAll(".cli-title")).filter(
(item) =>
item.innerText.match(/\d+\./) &&
!item.hasAttribute("data-dmm-btn-added")
);
items.forEach((item) => {
item.setAttribute("data-dmm-btn-added", "true");
let link = item.querySelector('a[href^="/title/"]').href;
let imdbId = link.match(/tt\d+/)?.[0];
if (!imdbId) return;
const searchUrl = `${X_DMM_HOST}/${imdbId}`;
addButtonToElement(item, SEARCH_BTN_LABEL, searchUrl);
});
changeObserver("ul.ipc-metadata-list", addButtonsToIMDBChart);
}
// MDBList functions
function addButtonsToMDBListSingleTitle() {
const targetElement = document.querySelector(
"#content-desktop-2 > div > div:nth-child(1) > h3"
);
if (targetElement && targetElement.hasAttribute("data-dmm-btn-added"))
return;
targetElement.setAttribute("data-dmm-btn-added", "true");
const searchUrl = `${DMM_HOST}${window.location.pathname}`;
addButtonToElement(targetElement, SEARCH_BTN_LABEL, searchUrl);
}
function addButtonsToMDBListSearchResults() {
const items = Array.from(
document.querySelectorAll("div.ui.centered.cards > div")
).filter((item) => !item.hasAttribute("data-dmm-btn-added"));
items.forEach((item) => {
item.setAttribute("data-dmm-btn-added", "true");
const targetElement = item.querySelector("div.header");
if (targetElement) {
const url = targetElement.parentElement
.querySelector("a")
.href.replace("https://mdblist.com/", "");
const searchUrl = `${DMM_HOST}/${url}`;
addButtonToElement(targetElement, SEARCH_BTN_LABEL, searchUrl);
}
});
changeObserver("div.ui.centered.cards", addButtonsToMDBListSearchResults);
}
// AniDB functions
function addButtonsToAniDBSingleTitle() {
const targetElement = document.querySelector("#layout-main > h1.anime");
if (targetElement && targetElement.hasAttribute("data-dmm-btn-added"))
return;
targetElement.setAttribute("data-dmm-btn-added", "true");
const searchUrl = `${DMM_HOST}/${window.location.pathname
.replaceAll("/", "")
.replace("anime", "anime/anidb-")}`;
addButtonToElement(targetElement, SEARCH_BTN_LABEL, searchUrl);
}
function addButtonsToAniDBAnyPage() {
const items = Array.from(document.querySelectorAll("a")).filter(
(item) => item.innerText.trim() && /\/anime\/\d+$/.test(item.href) && !item.hasAttribute("data-dmm-btn-added")
);
items.forEach((item) => {
item.setAttribute("data-dmm-btn-added", "true");
const searchUrl = `${DMM_HOST}/${item.href
.replace("https://anidb.net/", "")
.replaceAll("/", "")
.replace("anime", "anime/anidb-")}`;
addButtonToElement(item, SEARCH_BTN_LABEL, searchUrl);
});
}
// TraktTV functions
function addButtonsToTraktTVSingleTitle() {
const targetElement = document.querySelector("#summary-wrapper div > h1");
if (targetElement && targetElement.hasAttribute("data-dmm-btn-added"))
return;
// find imdb id in page, <a data-type="imdb">
const imdbId = document
.querySelector("a#external-link-imdb")
?.href?.match(/tt\d+/)?.[0];
if (!imdbId) return;
targetElement.setAttribute("data-dmm-btn-added", "true");
const searchUrl = `${X_DMM_HOST}/${imdbId}`;
addButtonToElement(targetElement, SEARCH_BTN_LABEL, searchUrl);
}
// iCheckMovies functions
function addButtonsToiCheckMoviesSingleTitle() {
const imdbId = document
.querySelector("a.optionIMDB")
?.href?.match(/tt\d+/)?.[0];
if (!imdbId) return;
const targetElement = document.querySelector("#movie > h1");
if (targetElement && targetElement.hasAttribute("data-dmm-btn-added"))
return;
targetElement.setAttribute("data-dmm-btn-added", "true");
const searchUrl = `${X_DMM_HOST}/${imdbId}`;
addButtonToElement(targetElement, SEARCH_BTN_LABEL, searchUrl);
}
function addButtonsToiCheckMoviesBetaSingleTitle() {
const imdbId = document
.querySelector("a.stat-imdb")
?.href?.match(/tt\d+/)?.[0];
if (!imdbId) return;
const targetElement = document.querySelector("h1.title");
if (targetElement && targetElement.hasAttribute("data-dmm-btn-added"))
return;
targetElement.setAttribute("data-dmm-btn-added", "true");
const searchUrl = `${X_DMM_HOST}/${imdbId}`;
addLinkToElement(targetElement, SEARCH_BTN_LABEL, searchUrl);
}
function addButtonsToiCheckMoviesList() {
const items = Array.from(
document.querySelectorAll("ol#itemListMovies > li")
).filter((item) => !item.hasAttribute("data-dmm-btn-added"));
items.forEach((item) => {
const imdbId = item
.querySelector("a.optionIMDB")
?.href?.match(/tt\d+/)?.[0];
if (!imdbId) return;
const targetElement = item.querySelector("h2 a");
if (!targetElement) return;
item.setAttribute("data-dmm-btn-added", "true");
const searchUrl = `${X_DMM_HOST}/${imdbId}`;
addButtonToElement(targetElement, SEARCH_BTN_LABEL, searchUrl);
});
}
function addButtonsToiCheckMoviesBetaList() {
const items = Array.from(
document.querySelectorAll("div.media-content")
).filter((item) => !item.hasAttribute("data-dmm-btn-added"));
items.forEach((item) => {
const imdbId = item
.querySelector("a.stat-imdb")
?.href?.match(/tt\d+/)?.[0];
if (!imdbId) return;
const targetElement = item.querySelector("h3.title");
if (!targetElement) return;
item.setAttribute("data-dmm-btn-added", "true");
const searchUrl = `${X_DMM_HOST}/${imdbId}`;
addButtonToElement(targetElement, SEARCH_BTN_LABEL, searchUrl);
});
changeObserver(
"#app section.section div.columns",
addButtonsToiCheckMoviesBetaList
);
}
// letterboxd functions
function addButtonsToLetterboxdSingleTitle() {
const imdbId = document
.querySelector("a[data-track-action='IMDb']")
?.href?.match(/tt\d+/)?.[0];
if (!imdbId) return;
const targetElement = document.querySelector("h1.filmtitle");
if (targetElement && targetElement.hasAttribute("data-dmm-btn-added"))
return;
targetElement.setAttribute("data-dmm-btn-added", "true");
const searchUrl = `${X_DMM_HOST}/${imdbId}`;
addButtonToElement(targetElement, SEARCH_BTN_LABEL, searchUrl);
}
// observer utility function
function changeObserver(cssSelector, addBtnFn) {
const targetNode = document.querySelector(cssSelector);
if (!targetNode) return;
const config = { childList: true, subtree: true };
let debounceTimer;
const callback = function (mutationsList, observer) {
if (debounceTimer) {
clearTimeout(debounceTimer);
}
debounceTimer = setTimeout(() => {
// if (!targetNode) return;
observer.disconnect();
addBtnFn();
observer.observe(targetNode, config);
}, 250);
};
const observer = new MutationObserver(callback);
observer.observe(targetNode, config);
}
function addMagnetLinkButtonToElements(elements) {
elements.forEach(function(link) {
const magnetURL = link.href;
const infoHash = getInfoHashFromMagnetLink(magnetURL);
if (infoHash) {
const buttonURL = `https://debridmediamanager.com/library?addMagnet=${infoHash}`;
const button = createButton("DMM🧲", buttonURL);
link.parentNode.insertBefore(button, link.nextSibling);
}
});
}
// Main function
const magnetLinks = document.querySelectorAll('a[href^="magnet:?"]');
addMagnetLinkButtonToElements(magnetLinks);
const hostname = window.location.hostname;
///// IMDB /////
if (hostname === "www.imdb.com") {
const isIMDBSingleTitlePage = /^\/title\//.test(location.pathname);
const isIMDBListPage =
/^\/search\//.test(location.pathname) ||
/^\/list\/ls/.test(location.pathname);
const isIMDBChartPage = /^\/chart\//.test(location.pathname);
if (isIMDBSingleTitlePage) {
addButtonsToIMDBSingleTitle();
} else if (isIMDBListPage) {
addButtonsToIMDBList();
} else if (isIMDBChartPage) {
addButtonsToIMDBChart();
}
///// IMDB MOBILE /////
} else if (hostname === "m.imdb.com") {
const isIMDBSingleTitlePage = /^\/title\//.test(location.pathname);
const isIMDBListPage =
/^\/search\//.test(location.pathname) ||
/^\/list\/ls/.test(location.pathname);
const isIMDBChartPage = /^\/chart\//.test(location.pathname);
if (isIMDBSingleTitlePage) {
addButtonsToIMDBSingleTitle();
} else if (isIMDBListPage) {
addButtonsToIMDBList();
} else if (isIMDBChartPage) {
addButtonsToIMDBChart();
}
///// MDBLIST /////
} else if (hostname === "mdblist.com") {
const isMDBListSingleTitlePage = /^\/(movie|show)\//.test(
location.pathname
);
if (isMDBListSingleTitlePage) {
addButtonsToMDBListSingleTitle();
} else {
addButtonsToMDBListSearchResults();
}
///// ANIDB /////
} else if (hostname === "anidb.net") {
const isAniDBSingleTitlePage = /^\/anime\/\d+/.test(location.pathname);
if (isAniDBSingleTitlePage) {
addButtonsToAniDBSingleTitle();
}
addButtonsToAniDBAnyPage();
///// TRAKT TV /////
} else if (hostname === "trakt.tv") {
const isTraktTVEpisodePage = /\/episodes\/\d/.test(location.pathname);
if (isTraktTVEpisodePage) return;
const isTraktTVSinglePage = /^\/(shows|movies)\/.+/.test(location.pathname);
if (isTraktTVSinglePage) {
addButtonsToTraktTVSingleTitle();
}
///// ICHECKMOVIES /////
} else if (hostname === "www.icheckmovies.com") {
const isiCheckMoviesListPage = /^\/lists\//.test(location.pathname);
if (isiCheckMoviesListPage) {
addButtonsToiCheckMoviesList();
}
const isiCheckMoviesSingleTitlePage = /^\/movies\//.test(location.pathname);
if (isiCheckMoviesSingleTitlePage) {
addButtonsToiCheckMoviesSingleTitle();
}
} else if (hostname === "beta.icheckmovies.com") {
const isiCheckMoviesListPage = /^\/lists\//.test(location.pathname);
if (isiCheckMoviesListPage) {
addButtonsToiCheckMoviesBetaList();
}
const isiCheckMoviesSingleTitlePage = /^\/movies\//.test(location.pathname);
if (isiCheckMoviesSingleTitlePage) {
addButtonsToiCheckMoviesBetaSingleTitle();
}
///// MYANIMELIST /////
} else if (hostname === "myanimelist.net") {
///// KITSU /////
} else if (hostname === "kitsu.io") {
///// LETTERBOXD /////
} else if (hostname === "letterboxd.com") {
const isLetterboxdSingleTitlePage = /^\/film\//.test(location.pathname);
if (isLetterboxdSingleTitlePage) {
addButtonsToLetterboxdSingleTitle();
}
///// JUSTWATCH /////
} else if (hostname === "www.justwatch.com") {
///// TVDB /////
} else if (hostname === "www.thetvdb.com") {
///// TMDB /////
} else if (hostname === "www.themoviedb.org") {
}
})();