IMDB info + .torrent from magnet

Show info of movies/series's (rating, poster, actors, ...) from IMDB on almost any torrent domain (thepiratebay, *torrent* , ...) as well as showing .torrent download links from any magnet:?url

  1. // ==UserScript==
  2. // @name IMDB info + .torrent from magnet
  3. // @version 2.20200620
  4. // @description Show info of movies/series's (rating, poster, actors, ...) from IMDB on almost any torrent domain (thepiratebay, *torrent* , ...) as well as showing .torrent download links from any magnet:?url
  5. // @namespace hossam6236
  6. // @author hossam6236
  7. // @license GPL-3.0-or-later
  8. // @include http*://*torrent*.*/*
  9. // @include http*://*pirate*bay*.*/*
  10. // @include http*://*tpb*.*/*
  11. // @include http*://*isohunt*.*/*
  12. // @include http*://*1337x*.*/*
  13. // @include http*://*rarbg*.*/*
  14. // @include http*://*zooqle*.*/*
  15. // @include http*://*bitsnoop*.*/*
  16. // @include http*://*dnoid*.*/*
  17. // @include http*://*torlock*.*/*
  18. // @include http*://*eztv*.*/*
  19. // @include http*://*idope*.*/*
  20. // @include http*://*toorgle*.*/*
  21. // @include http*://*demonoid*.*/*
  22. // @include http*://*kickass*.*/*
  23. // @include http*://*kat*.*/*
  24. // @include http*://*boxofficemojo*.*/*
  25. // @include http*://*.imdb.*/*
  26. // @grant GM_xmlhttpRequest
  27. // @connect imdb.com
  28. // @connect omdbapi.com
  29. // @connect media-amazon.com
  30. // ==/UserScript==
  31.  
  32. // Find at:
  33. // https://greasyfork.org/en/scripts/16946-imdb-info-torrent-from-magnet
  34. // https://openuserjs.org/scripts/hossam6236/IMDB_info_%2B_.torrent_from_magnet
  35.  
  36. // TODO:
  37. // - storage (cleanup)
  38. // - tests
  39.  
  40. /**
  41. * @typedef {(m: ResolvedMovieData) => void} CbMovieMouseEvent
  42. * @typedef {Map<string, { title: string, year: string, hash: string, promise: Promise<ResolvedMovieData> }>} MoviesDataMap
  43. * @typedef {{
  44. Error?: any;
  45. Actors: string;
  46. Awards: string;
  47. BoxOffice: string;
  48. Country: string;
  49. DVD: string;
  50. Director: string;
  51. Genre: string;
  52. Language: string;
  53. Metascore: string;
  54. Plot: string;
  55. Poster: string;
  56. Production: string;
  57. Rated: string;
  58. Ratings: {Source: string;Value: string;}[][];
  59. Released: string;
  60. Response: string;
  61. Runtime: string;
  62. Title: string;
  63. Trailer?: string;
  64. Type: string;
  65. Website: string;
  66. Writer: string;
  67. Year: string;
  68. imdbID: string;
  69. imdbRating: string;
  70. imdbVotes: string;
  71. }} ResolvedMovieData
  72. */
  73.  
  74. /**
  75. * @global
  76. * @type {(a: Partial<XMLHttpRequest> & {url: string; method: string;}) => void} GM_xmlhttpRequest
  77. */
  78. var GM_xmlhttpRequest;
  79.  
  80. (function IIFE(document, hostname) {
  81. const POSTER_PLACEHOLDER =
  82. "http://ia.media-imdb.com/images/G/01/imdb/images/nopicture/large/film-184890147._CB379391879_.png";
  83. const STYLE = `
  84. .imdb-download-link::before {
  85. content: '⇩';
  86. }
  87. .title_wrapper .imdb-download-link {
  88. font-size: .5em;
  89. }
  90. a.movie-preview {
  91. display: inline-block !important;
  92. }
  93. .movie-preview-starter {
  94. display: inline-block;
  95. position: fixed;
  96. opacity: 0.85;
  97. top: 0;
  98. right: 0;
  99. z-index: 10000;
  100. text-align: center;
  101. }
  102. .movie-preview-starter--button {
  103. display: inline-block;
  104. cursor: pointer;
  105. margin: 7px;
  106. padding: 7px;
  107. font-size: 12pt;
  108. font-family: Tahoma, Arial;
  109. border-radius: 5px;
  110. }
  111. .movie-preview-box {
  112. position: fixed;
  113. z-index:9999;
  114. width:475px;
  115. height:283px;
  116. top: calc(50vh - 150px);
  117. left: 50vw;
  118. display: flex;
  119. color: #000;
  120. background-color: white;
  121. border: 3px solid #222;
  122. border-radius: 5px;
  123. overflow: hidden;
  124. opacity: 0;
  125. visibility: hidden;
  126. transition: all 0.5s ease-in-out;
  127. }
  128. .movie-preview-box.visible {
  129. opacity: 1;
  130. visibility: visible;
  131. }
  132. .movie-preview-box *,
  133. .movie-preview-unique-list > * {
  134. font-size: 10pt;
  135. font-family: Tahoma, Arial;
  136. line-height: initial;
  137. }
  138. .movie-preview-box.no-trailer .preview--info--trailer {
  139. display: none;
  140. }
  141. .torrent-download-links {
  142. opacity: 0.8;
  143. font-size: 90%;
  144. position: absolute;
  145. display: none;
  146. }
  147. .assisted-torrent-link:hover .torrent-download-links {
  148. display: inline-block;
  149. }
  150. .movie-preview-unique-list {
  151. width: 50%;
  152. max-width: 400px;
  153. max-height: 200px;
  154. margin: auto;
  155. overflow: auto;
  156. text-align: left;
  157. padding: 5px;
  158. line-height: 15px;
  159. color: #000;
  160. background-color: white;
  161. border: 3px solid #222;
  162. border-radius: 5px;
  163. }
  164. .movie-preview-unique-list > * {
  165. margin: 2px;
  166. }
  167. .movie-preview-unique-list a {
  168. border: 0;
  169. }
  170. .movie-preview-unique-list a:hover {
  171. border: 0;
  172. text-decoration: underline;
  173. }
  174. a.movie-preview {
  175. cursor: pointer;
  176. }
  177. a.movie-preview.highlight {
  178. background-color: rgba(255, 231, 58, 0.59);
  179. }
  180. .movie-preview-enhancement {
  181. display: inline-block !important;
  182. max-width: 30px;
  183. min-width: 30px;
  184. font-size: 85%;
  185. margin:0 4px 0 0;
  186. }
  187. .movie-preview-enhancement.remarkable {
  188. font-weight: bold;
  189. }
  190. .movie-preview-enhancement.starred-1::after {
  191. content: "★";
  192. color: #DD0000;
  193. }
  194. .movie-preview-enhancement.starred-2::after {
  195. content: "★";
  196. color: #660000;
  197. }
  198. .movie-preview-enhancement.starred-3::after {
  199. content: "★";
  200. }
  201. .movie-preview-enhancement.starred-4::after {
  202. content: "☆";
  203. }
  204. .preview--poster {
  205. flex-shrink: 0;
  206. width: 200px;
  207. height: 283px;
  208. }
  209. .preview--poster--img {
  210. cursor: pointer;
  211. width: 100%;
  212. height: 100%;
  213. }
  214. .preview--info {
  215. text-align:left;
  216. padding:3px;
  217. height:277px;
  218. overflow:auto;
  219. display:inline-block;
  220. }
  221. .preview--info--title {
  222. text-align:center;
  223. font-size:125%pt;
  224. font-weight:bold;
  225. }
  226. .preview--info--trailer {
  227. color: #369;
  228. cursor: pointer;
  229. display: inline-block;
  230. }
  231. .preview--info--trailer:hover {
  232. text-decoration: underline;
  233. }
  234. .preview--info--trailer::before {
  235. content: '(';
  236. }
  237. .preview--info--trailer::after {
  238. content: '), ';
  239. }
  240. .preview--info--imdb-rating,
  241. .preview--info--imdb-votes {
  242. font-weight: bold;
  243. }
  244. `;
  245.  
  246. /** @param {string} style */
  247. const appendStyleToDocument = (style) => {
  248. const styleNode = document.createElement("style");
  249. styleNode.type = "text/css";
  250. styleNode.textContent = style;
  251. document.head.append(styleNode);
  252. };
  253.  
  254. /**
  255. * @param {HTMLImageElement} imageNode
  256. * @param {string} src
  257. */
  258. const setImgSrcBypassingAdBlock = (imageNode, src) => {
  259. imageNode.src = src;
  260. imageNode.onerror = (e) => {
  261. if (!imageNode.src.startsWith("http")) return;
  262. GM_xmlhttpRequest({
  263. url: imageNode.src,
  264. method: "GET",
  265. responseType: "blob",
  266. onload: (data) => {
  267. const reader = new FileReader();
  268. reader.onloadend = () => {
  269. // @ts-ignore
  270. imageNode.src = reader.result;
  271. const oldNode = imageNode;
  272. // @ts-ignore
  273. imageNode = oldNode.cloneNode();
  274. // @ts-ignore
  275. imageNode.style = "";
  276. oldNode.replaceWith(imageNode);
  277. };
  278. // @ts-ignore
  279. reader.readAsDataURL(data.response);
  280. },
  281. });
  282. };
  283. };
  284.  
  285. /** @param {string} movieTitle */
  286. const getTorrentSearchURLFromMovieTitle = (movieTitle) =>
  287. `https://thepiratebay.org/search/${encodeURIComponent(movieTitle)}/0/99/0`;
  288.  
  289. const applyImdbDomUpdate = () => {
  290. const movieTitleNodes = document.querySelectorAll(
  291. "div.titleBar > div.title_wrapper > h1, td.titleColumn, div.lister-item-content .lister-item-header, div.title > a.title-grid, td.overview-top > h4 > a"
  292. );
  293. for (let movieTitleNode of movieTitleNodes) {
  294. if (movieTitleNode.hasAttribute("with-download-link")) continue;
  295. movieTitleNode.setAttribute("with-download-link", "true");
  296.  
  297. let movieTitle = movieTitleNode.textContent || "";
  298. if (movieTitleNode.querySelector(".lister-item-index")) {
  299. /** @type {HTMLElement} */
  300. // @ts-ignore
  301. let movieTitleNodeClone = movieTitleNode.cloneNode(true);
  302. movieTitleNodeClone.removeChild(
  303. /** @type {Node} */
  304. (movieTitleNodeClone.querySelector(".lister-item-index"))
  305. ).textContent;
  306. }
  307. movieTitle = movieTitle.replace(/\s+/g, " ").trim();
  308.  
  309. const linkNode = document.createElement("a");
  310. linkNode.classList.add("imdb-download-link");
  311. linkNode.setAttribute(
  312. "href",
  313. getTorrentSearchURLFromMovieTitle(movieTitle)
  314. );
  315.  
  316. movieTitleNode.append(linkNode);
  317. }
  318. };
  319.  
  320. /**
  321. * @param {string} title
  322. * @param {string} year
  323. */
  324. const getMovieHashFromTitleAndYear = (title, year = "") => {
  325. return `${title}_${year}`.trim().replace(/[^a-zA-Z0-9]+/g, "-");
  326. };
  327.  
  328. /** @param {HTMLAnchorElement} linkNode */
  329. const getMovieTitleAndYearFromLinkNode = (linkNode) => {
  330. const linkText = linkNode.textContent || "";
  331. const linkHref = linkNode.getAttribute("href") || "";
  332. const boxofficemojo = /^\/movies\/\?id=(.+)\.htm/i.exec(linkHref);
  333. let title = linkText
  334. .toLowerCase()
  335. .replace(",", " ")
  336. .replace(".", " ")
  337. .replace("(", " ")
  338. .replace("1080p", "")
  339. .replace("720p", "");
  340. /** @type {any} */
  341. let year = "";
  342. if (boxofficemojo) {
  343. title = linkText.toLowerCase();
  344. if (!year) {
  345. year = /\(([0-9]{4}).*\)/.exec(title);
  346. if (year) {
  347. // year from link text (if available)
  348. title = title.replace(year[0], " ").trim();
  349. year = year[1];
  350. } else {
  351. year = /([0-9]{4})\.htm$/.exec(linkHref);
  352. if (year && year[1] && year[1] > 1950) {
  353. // year from link href (if available)
  354. year = year[1];
  355. } else {
  356. // year from GET parameter (if available)
  357. year = new URL(window.location.href).searchParams.get("yr");
  358. if (!year) {
  359. // year is current year
  360. year = new Date().getFullYear();
  361. }
  362. }
  363. }
  364. }
  365. return { title, year };
  366. } else {
  367. const reM = /[0-9]{4}/i;
  368. const reS = /S[0-9]{2}E[0-9]{2}|[0-9]{1}x[0-9]{2}/i;
  369. /** @type {number | RegExpExecArray | null} */
  370. let matchYear = reM.exec(title);
  371. matchYear = matchYear && matchYear.length ? parseInt(matchYear[0]) : null;
  372. const matchSeries = reS.exec(title);
  373. if (matchYear && matchYear > 1900) {
  374. year = matchYear;
  375. title = title.substr(0, title.search(reM)).trim();
  376. return { title, year };
  377. } else if (matchSeries) {
  378. year = "-";
  379. title = title.substr(0, title.search(reS)).trim();
  380. return { title, year };
  381. }
  382. }
  383. return { title: null, year: null };
  384. };
  385.  
  386. // const storage = localStorage.getItem('movie_preview');
  387.  
  388. /** @param {string} url */
  389. const fetchSafe = (url) => {
  390. return new Promise((resolve, reject) => {
  391. GM_xmlhttpRequest({
  392. url,
  393. method: "GET",
  394. onload: (data) => {
  395. // @ts-ignore
  396. resolve(data.responseText);
  397. },
  398. onerror: reject,
  399. });
  400. });
  401. };
  402.  
  403. /**
  404. * @param {string} title
  405. * @param {number|string|null} year
  406. * @returns {Promise<ResolvedMovieData>}
  407. */
  408. const loadMovie = (title, year) => {
  409. const url = `https://www.omdbapi.com/?apikey=c989d08d&t=${title}&y=${year}&plot=full&r=json`;
  410. // const url = 'http://www.imdb.com/xml/find?json=1&nr=1&tt=on&q=' + encodeURIComponent(title + ' (' + year + ')');
  411. return fetchSafe(url).then((responseText) => JSON.parse(responseText)); // resolvedMovieData
  412. };
  413.  
  414. /**
  415. * @param {string} text
  416. * @param {RegExp} regexp
  417. * @param {number} captureGroupIndex
  418. * @returns {string}
  419. */
  420. const _matchOnlyCaptureGroup = (text, regexp, captureGroupIndex = 1) => {
  421. const matchResult = text.match(regexp);
  422. return matchResult !== null ? matchResult[captureGroupIndex] : "";
  423. };
  424.  
  425. /** @param {ResolvedMovieData} resolvedMovieData */
  426. const assessMovieRankings = (resolvedMovieData) => {
  427. const rankingMetrics = extractRankingMetrics(resolvedMovieData);
  428. const { rating, votes, wins_sig, wins, noms_sig, noms } = rankingMetrics;
  429. const isRemarkable = rating >= 7.0 && votes > 50000;
  430.  
  431. let starredDegree;
  432. if ((wins_sig >= 1 || noms_sig >= 2) && (wins >= 5 || noms >= 10)) {
  433. starredDegree = 1; // red filled star
  434. } else if (
  435. wins >= 10 ||
  436. (noms_sig >= 1 && noms >= 5) ||
  437. (rating > 8.0 && votes > 50000)
  438. ) {
  439. starredDegree = 2; // darkred filled star
  440. } else if (wins >= 5 || noms >= 10 || noms_sig >= 1 || votes > 150000) {
  441. starredDegree = 3; // black/blue filled star
  442. } else if (wins + noms > 1) {
  443. starredDegree = 4; // non-filled star
  444. }
  445.  
  446. let significancePercentage = 1.0;
  447. if (rating <= 5.0 || votes <= 1000) {
  448. significancePercentage = Math.max(
  449. 0.15,
  450. Math.min(rating / 10, votes / 1000)
  451. );
  452. } else if (
  453. resolvedMovieData.imdbRating == "N/A" ||
  454. resolvedMovieData.imdbVotes == "N/A"
  455. ) {
  456. significancePercentage = 0.15;
  457. }
  458.  
  459. return {
  460. isRemarkable,
  461. starredDegree,
  462. significancePercentage,
  463. rankingMetrics,
  464. };
  465. };
  466.  
  467. /** @param {ResolvedMovieData} resolvedMovieData */
  468. const extractRankingMetrics = (resolvedMovieData) => {
  469. const awards = resolvedMovieData.Awards;
  470. const reg_wins = /([0-9]+) win(s|)/;
  471. const reg_noms = /([0-9]+) nomination(s|)/;
  472. const reg_wins_sig = /Won ([0-9]+) Oscar(s|)/;
  473. const reg_noms_sig = /Nominated for ([0-9]+) Oscar(s|)/;
  474.  
  475. return {
  476. rating: parseFloat(resolvedMovieData.imdbRating),
  477. votes: parseFloat(resolvedMovieData.imdbVotes.replace(/,/g, "")),
  478. wins: parseInt(_matchOnlyCaptureGroup(awards, reg_wins, 1)) || 0,
  479. noms: parseInt(_matchOnlyCaptureGroup(awards, reg_noms, 1)) || 0,
  480. wins_sig: parseInt(_matchOnlyCaptureGroup(awards, reg_wins_sig, 1)) || 0,
  481. noms_sig: parseInt(_matchOnlyCaptureGroup(awards, reg_noms_sig, 1)) || 0,
  482. awards_text: awards.toLowerCase(),
  483. };
  484. };
  485.  
  486. /**
  487. * @param {HTMLElement[] | NodeListOf<Element>} nodes
  488. * @param {ResolvedMovieData} resolvedMovieData
  489. * @param {CbMovieMouseEvent} cbOnMouseOver
  490. * @param {CbMovieMouseEvent} cbOnMouseOut
  491. */
  492. const updateLinkNodesWithMovieData = (
  493. nodes,
  494. resolvedMovieData,
  495. cbOnMouseOver,
  496. cbOnMouseOut
  497. ) => {
  498. for (let linkNode of nodes) {
  499. if (resolvedMovieData.Error) continue;
  500. /** @type {HTMLElement} */
  501. (linkNode).onmouseover = () => cbOnMouseOver(resolvedMovieData);
  502. /** @type {HTMLElement} */
  503. (linkNode).onmouseout = () => cbOnMouseOut(resolvedMovieData);
  504.  
  505. const {
  506. isRemarkable,
  507. starredDegree,
  508. significancePercentage,
  509. rankingMetrics: { awards_text },
  510. } = assessMovieRankings(resolvedMovieData);
  511.  
  512. const enhancementNode = document.createElement("a");
  513. enhancementNode.classList.add("movie-preview-enhancement");
  514. linkNode.parentNode &&
  515. linkNode.parentNode.insertBefore(enhancementNode, linkNode);
  516. if (isRemarkable) {
  517. enhancementNode.classList.add("remarkable");
  518. }
  519. if (starredDegree) {
  520. enhancementNode.classList.add(`starred-${starredDegree}`);
  521. }
  522.  
  523. enhancementNode.setAttribute(
  524. "href",
  525. `http://www.imdb.com/title/${resolvedMovieData.imdbID}`
  526. );
  527. enhancementNode.setAttribute("target", "_blank");
  528. enhancementNode.setAttribute(
  529. "title",
  530. `${resolvedMovieData.imdbVotes} votes - ${resolvedMovieData.Runtime} - Rated ${resolvedMovieData.Rated} - Awards: ${awards_text}`
  531. );
  532. enhancementNode.innerHTML = resolvedMovieData.imdbRating;
  533. enhancementNode.style.opacity = significancePercentage.toString();
  534. }
  535. };
  536.  
  537. /** @param {MoviesDataMap} moviesDataMap */
  538. const createUniqueMovieList = (moviesDataMap) => {
  539. let wrapperNode = document.createElement("div");
  540. wrapperNode.classList.add("movie-preview-unique-list");
  541. for (let movieData of moviesDataMap.values()) {
  542. const parentNode = document.createElement("div");
  543. const linkNode = document.createElement("a");
  544. linkNode.textContent = movieData.hash;
  545. linkNode.classList.add("movie-preview");
  546. linkNode.dataset.movieHash = movieData.hash;
  547. linkNode.onclick = () => {
  548. for (let movPreview of document.querySelectorAll(".movie-preview")) {
  549. movPreview.classList.remove("highlight");
  550. }
  551. for (let movPreview of document.querySelectorAll(
  552. `.movie-preview[data-movie-hash="${movieData.hash}"]`
  553. )) {
  554. movPreview.classList.add("highlight");
  555. }
  556. };
  557. movieData.promise.then((resolvedMovieData) => {
  558. if (resolvedMovieData.Title) {
  559. linkNode.textContent = `${resolvedMovieData.Title} (${resolvedMovieData.Year})`;
  560. }
  561. });
  562. wrapperNode.appendChild(parentNode);
  563. parentNode.appendChild(linkNode);
  564. }
  565. return wrapperNode;
  566. };
  567.  
  568. /** @param {HTMLElement} node */
  569. const cleanupPorn = (node) => {
  570. const innerHTML = node.innerHTML.toLowerCase();
  571. if (innerHTML.includes("xxx") || innerHTML.includes("porn"))
  572. node.outerHTML = "";
  573. };
  574.  
  575. /** @param {string} hostname */
  576. const isHostnamePirateBay = (hostname) =>
  577. /.*(pirate.*bay|tpb).*/.test(hostname);
  578. /** @param {string} hostname */
  579. const isHostnameIMDB = (hostname) => hostname.endsWith("imdb.com");
  580.  
  581. /**
  582. * @param {HTMLElement} parentNode
  583. * @param {string} selector
  584. * @returns {HTMLElement}
  585. */
  586. const _querySelector = (parentNode, selector) =>
  587. // @ts-ignore
  588. parentNode.querySelector(selector);
  589.  
  590. const initPreviewNode = () => {
  591. /** @type {HTMLDivElement & {show: ()=>void; hide: ()=>void; hiding: number; setMovie: CbMovieMouseEvent}} */
  592. // @ts-ignore
  593. const previewNode = document.createElement("div");
  594. previewNode.classList.add("movie-preview-box");
  595. previewNode.insertAdjacentHTML(
  596. "beforeend",
  597. `
  598. <div class="preview--poster">
  599. <img class="preview--poster--img" src="${POSTER_PLACEHOLDER}">
  600. </div>
  601. <div class="preview--info">
  602. <div class="preview--info--title">
  603. <a href="" target="_blank">
  604. <span class="title">The Martian</span> (<span class="year">2015</span>)
  605. </a>
  606. </div>
  607. <div class="preview--info--trailer" title="Play trailer" data-trailer-url="${POSTER_PLACEHOLDER}">▶</div>
  608. <span class="preview--info--imdb-rating">8.1</span><span style="color:grey;">/10</span> (<span class="preview--info--imdb-votes">275,300</span> votes), <span class="preview--info--imdb-metascore">-</span> Metascore
  609. <br /><u>Awards</u>: <span class="preview--info--awards"></span>
  610. <br /><u>Genre</u>: <span class="preview--info--genre">Adventure, Comedy, Drama</span>
  611. <br /><u>Released</u>: <span class="preview--info--released">17 Oct 2017</span>
  612. <br /><u>Box Office</u>: <span class="preview--info--boxofficegross">$123,456,789</span>
  613. <br /><u>Rated</u>: <span class="preview--info--mpaa-rating">PG-13</span>, <u>Runtime</u>: <span class="preview--info--runtime">144 min</span>
  614. <br /><u>Actors</u>: <span class="preview--info--actors">Matt Damon, Jessica Chastain, Kristen Wiig, Jeff Daniels</span>
  615. <br /><u>Director</u>: <span class="preview--info--director">Ridley Scott</span>
  616. <br /><u>Plot</u>: <span class="preview--info--plot">During a manned mission to Mars, Astronaut Mark Watney is presumed dead after a fierce storm and left behind by his crew. But Watney has survived and finds himself stranded and alone on the hostile planet. With only meager supplies, he must draw upon his ingenuity, wit and spirit to subsist and find a way to signal to Earth that he is alive.</span>
  617. </div>
  618. `
  619. );
  620.  
  621. // @ts-ignore
  622. previewNode.querySelector(".preview--poster--img").onclick = (e) => {
  623. e.preventDefault();
  624. const poster = e.currentTarget.src;
  625. if (poster === POSTER_PLACEHOLDER || !poster.startsWidth("http")) return;
  626. window.open(poster, "", "width=600, height=600");
  627. };
  628.  
  629. // @ts-ignore
  630. previewNode.querySelector(".preview--info--trailer").onclick = (e) => {
  631. e.preventDefault();
  632. window.open(
  633. e.currentTarget.getAttribute("data-trailer-url"),
  634. "",
  635. "width=900, height=500"
  636. );
  637. };
  638.  
  639. previewNode.show = () => {
  640. previewNode.classList.add("visible");
  641. clearTimeout(previewNode.hiding);
  642. };
  643.  
  644. previewNode.hide = () => {
  645. previewNode.hiding = setTimeout(
  646. () => previewNode.classList.remove("visible"),
  647. 1500
  648. );
  649. };
  650.  
  651. previewNode.setMovie = (movie) => {
  652. _querySelector(previewNode, ".preview--info--title > a").setAttribute(
  653. "href",
  654. `http://www.imdb.com/title/${movie.imdbID}`
  655. );
  656. _querySelector(previewNode, ".preview--info--title .title").textContent =
  657. movie.Title;
  658. _querySelector(previewNode, ".preview--info--title .year").textContent =
  659. movie.Year;
  660.  
  661. if (!movie.Poster) previewNode.classList.add("no-poster");
  662. else previewNode.classList.remove("no-poster");
  663. setImgSrcBypassingAdBlock(
  664. /** @type {HTMLImageElement} */
  665. (_querySelector(previewNode, ".preview--poster--img")),
  666. movie.Poster || POSTER_PLACEHOLDER
  667. );
  668.  
  669. if (!movie.Trailer) previewNode.classList.add("no-trailer");
  670. else previewNode.classList.remove("no-trailer");
  671. _querySelector(
  672. previewNode,
  673. ".preview--info--trailer"
  674. ).dataset.trailerUrl = movie.Trailer || "";
  675.  
  676. _querySelector(previewNode, ".preview--info--imdb-rating").textContent =
  677. movie.imdbRating;
  678. _querySelector(previewNode, ".preview--info--imdb-votes").textContent =
  679. movie.imdbVotes;
  680. _querySelector(
  681. previewNode,
  682. ".preview--info--imdb-metascore"
  683. ).textContent = movie.Metascore;
  684. _querySelector(previewNode, ".preview--info--released").innerHTML =
  685. movie.Released;
  686. _querySelector(previewNode, ".preview--info--boxofficegross").innerHTML =
  687. movie.BoxOffice || "N/A";
  688. _querySelector(previewNode, ".preview--info--genre").textContent =
  689. movie.Genre;
  690. _querySelector(previewNode, ".preview--info--mpaa-rating").textContent =
  691. movie.Rated;
  692. _querySelector(previewNode, ".preview--info--runtime").textContent =
  693. movie.Runtime;
  694. _querySelector(
  695. previewNode,
  696. ".preview--info--awards"
  697. ).innerHTML = movie.Awards.replace("Oscars.", "<b>Oscars</b>.")
  698. .replace("Oscar.", "<b>Oscar</b>.")
  699. .replace("Another ", "<br />Another ");
  700. _querySelector(previewNode, ".preview--info--actors").textContent =
  701. movie.Actors;
  702. _querySelector(previewNode, ".preview--info--director").textContent =
  703. movie.Director;
  704. _querySelector(previewNode, ".preview--info--plot").textContent =
  705. movie.Plot;
  706. };
  707.  
  708. previewNode.onmouseover = previewNode.show;
  709. previewNode.onmouseout = previewNode.hide;
  710.  
  711. return previewNode;
  712. };
  713.  
  714. appendStyleToDocument(STYLE);
  715.  
  716. if (isHostnameIMDB(hostname)) {
  717. applyImdbDomUpdate();
  718. } else {
  719. const starterNode = document.createElement("form");
  720. starterNode.classList.add("movie-preview-starter");
  721. starterNode.insertAdjacentHTML(
  722. "beforeend",
  723. `<button class="movie-preview-starter--button"> load IMDb info </button>`
  724. );
  725. starterNode.onsubmit = (e) => {
  726. e.preventDefault();
  727.  
  728. const previewNode = initPreviewNode();
  729. /** @type {MoviesDataMap} */
  730. const moviesDataMap = new Map();
  731.  
  732. for (let linkNode of document.querySelectorAll("a")) {
  733. const href = linkNode.getAttribute("href") || "";
  734. // const torrentz = /^\/([a-zA-Z0-9]{40})/i.exec(href);
  735. // const piratebay = /^\/torrent\/([0-9])/i.exec(href);
  736. const hashMatch = /(^\/|^magnet\:\?xt\=urn\:btih\:)([a-zA-Z0-9]{40})/i.exec(
  737. href
  738. );
  739.  
  740. cleanupPorn(linkNode);
  741.  
  742. if (hashMatch) {
  743. const hash = hashMatch[2].toUpperCase();
  744. /////////////////////////////////////////////////////// Loading Magnet from piratebay
  745. const assistingNode = document.createElement("div");
  746. assistingNode.classList.add("torrent-download-links");
  747. assistingNode.insertAdjacentHTML(
  748. "beforeend",
  749. `
  750. <a target="_blank" href="http://torrage.info/torrent.php?h=${hash}" style="display: inline-block; padding:0 5px 0 5px; background-color:#748DAB; text-align:center;">t1</a>
  751. <a target="_blank" href="http://www.btcache.me/torrent/${hash}" style="display: inline-block; padding:0 5px 0 5px; background-color:#748DAB; text-align:center;">t1</a>
  752. <a target="_blank" href="http://torrentproject.se/torrent/${hash}.torrent" style="display: inline-block; padding:0 5px 0 5px; background-color:#748DAB; text-align:center;">m</a>
  753. `
  754. );
  755. /** @type {HTMLElement | null} */
  756. // @ts-ignore
  757. const parentNode = linkNode.parentNode;
  758. if (parentNode) {
  759. parentNode.classList.add("assisted-torrent-link");
  760. parentNode.append(assistingNode);
  761. }
  762. }
  763.  
  764. let { title, year } = getMovieTitleAndYearFromLinkNode(linkNode);
  765.  
  766. if (title && year) {
  767. const movieHash = getMovieHashFromTitleAndYear(title, year);
  768. linkNode.classList.add("movie-preview");
  769. linkNode.dataset.movieHash = movieHash;
  770. linkNode.style.display =
  771. linkNode.style.display === "block"
  772. ? "inline-block"
  773. : linkNode.style.display;
  774. if (!moviesDataMap.has(movieHash)) {
  775. moviesDataMap.set(movieHash, {
  776. title,
  777. year,
  778. hash: movieHash,
  779. promise: loadMovie(title, year),
  780. });
  781. }
  782. }
  783. }
  784.  
  785. console.log(`IMDB info + .torrent from magnet`, moviesDataMap);
  786.  
  787. /** @param {ResolvedMovieData} resolvedMovieData */
  788. const cbOnMouseOver = (resolvedMovieData) => {
  789. previewNode.setMovie(resolvedMovieData);
  790. previewNode.show();
  791. };
  792. const cbOnMouseOut = previewNode.hide;
  793.  
  794. for (let movieData of moviesDataMap.values()) {
  795. movieData.promise.then((resolvedMovieData) => {
  796. updateLinkNodesWithMovieData(
  797. document.querySelectorAll(
  798. `.movie-preview[data-movie-hash="${movieData.hash}"]`
  799. ),
  800. resolvedMovieData,
  801. cbOnMouseOver,
  802. cbOnMouseOut
  803. );
  804. });
  805. }
  806.  
  807. starterNode.remove();
  808. document.body.prepend(createUniqueMovieList(moviesDataMap));
  809. document.body.append(previewNode);
  810. };
  811.  
  812. document.body.prepend(starterNode);
  813. }
  814.  
  815. if (isHostnamePirateBay(hostname)) {
  816. /** @type {HTMLElement | null} */
  817. const mainContent = document.querySelector("#main-content");
  818. if (!mainContent) return;
  819. mainContent.style.marginLeft = "0";
  820. mainContent.style.marginRight = "0";
  821. }
  822. })(document, window.location.hostname);