Torrenter

Adds links to torrent sites on popular movie websites.

当前为 2022-04-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Torrenter
  3. // @namespace http://www.google.com/search?q=mabakay
  4. // @version 2.2.0
  5. // @description Adds links to torrent sites on popular movie websites.
  6. // @description:pl Dodaje linki do stron z torrentami na popularnych stronach o filmach.
  7. // @author mabakay
  8. // @copyright 2010 - 2022, mabakay
  9. // @date 17 April 2022
  10. // @license MIT
  11. // @run-at document-end
  12. // @icon64URL https://raw.githubusercontent.com/mabakay/torrenter/master/torrenter_64.png
  13. // @supportURL https://github.com/mabakay/torrenter
  14. // @match http://www.filmweb.pl/*
  15. // @match https://www.filmweb.pl/*
  16. // @match http://release24.pl/*
  17. // @match https://release24.pl/*
  18. // @match http://www.imdb.com/*
  19. // @match https://www.imdb.com/*
  20. // @match http://www.rottentomatoes.com/*
  21. // @match https://www.rottentomatoes.com/*
  22. // @require https://openuserjs.org/src/libs/sizzle/GM_config.min.js
  23. // @grant GM_getValue
  24. // @grant GM_setValue
  25. // @grant GM_registerMenuCommand
  26. // ==/UserScript==
  27. "use strict";
  28. class TorrenterConfigurator {
  29. constructor(language, changeCallback) {
  30. this._language = language;
  31. if (!GM_config || !GM_registerMenuCommand) {
  32. return;
  33. }
  34. let gmConfiguration = {
  35. "id": "mabakay_Torrenter",
  36. "title": this.localization.settingsTitle,
  37. "fields": {
  38. "showEngines": {
  39. "label": this.localization.showBuildInEngines,
  40. "type": "checkbox",
  41. "default": true
  42. },
  43. "showUserEngines": {
  44. "label": this.localization.showUserEngines,
  45. "type": "checkbox",
  46. "default": false
  47. },
  48. "showUserEnginesFirst": {
  49. "label": this.localization.showUserEnginesFirst,
  50. "type": "checkbox",
  51. "default": false
  52. },
  53. "userEngines": {
  54. "label": this.localization.userEngines,
  55. "type": "textarea",
  56. "default": "",
  57. }
  58. },
  59. "events": {
  60. "open": (document, window, frame) => {
  61. let userEnginesFiled = document.getElementById("mabakay_Torrenter_field_userEngines");
  62. userEnginesFiled.setAttribute("cols", "80");
  63. userEnginesFiled.setAttribute("rows", "10");
  64. userEnginesFiled.setAttribute("placeholder", this.localization.eg + " https://search-site.com/?title={title}&year={year}&orderby=seeds[&imdbTag={imdb}]");
  65. let enginesFieldDescription = document.createElement("div");
  66. enginesFieldDescription.setAttribute("style", "font-size: 12px;margin: 5px 6px;color: gray;");
  67. enginesFieldDescription.innerHTML = this.localization.addEngineDescription;
  68. let enginesLabelField = document.getElementById("mabakay_Torrenter_userEngines_field_label");
  69. enginesLabelField.after(enginesFieldDescription);
  70. let saveButton = document.getElementById("mabakay_Torrenter_saveBtn");
  71. saveButton.textContent = this.localization.saveButtonCaption;
  72. let closeButton = document.getElementById("mabakay_Torrenter_closeBtn");
  73. closeButton.textContent = this.localization.closeCaptionButton;
  74. let restToDefaultsLink = document.getElementById("mabakay_Torrenter_resetLink");
  75. restToDefaultsLink.textContent = this.localization.resetLinkCaption;
  76. GM_config.frame.setAttribute("style", "inset: 166px auto auto 326px;border: 1px solid rgb(0, 0, 0);height: 440px;margin: 0px;opacity: 1;overflow: auto;padding: 0px;position: fixed;width: 650px;z-index: 9999;display: block;");
  77. },
  78. "save": () => {
  79. GM_config.close();
  80. if (changeCallback) {
  81. changeCallback();
  82. }
  83. }
  84. }
  85. };
  86. GM_config.init(gmConfiguration);
  87. GM_registerMenuCommand(this.localization.configureMenuItem, () => { GM_config.open(); });
  88. }
  89. get localization() {
  90. if (!TorrenterConfigurator._localization.hasOwnProperty(this._language)) {
  91. return TorrenterConfigurator._localization["en"];
  92. }
  93. return TorrenterConfigurator._localization[this._language];
  94. }
  95. getConfigurationProperty(name, defaultValue) {
  96. var _a;
  97. try {
  98. return (_a = GM_config.get(name)) !== null && _a !== void 0 ? _a : defaultValue;
  99. }
  100. catch (_b) {
  101. return defaultValue;
  102. }
  103. }
  104. static getLanguage() {
  105. let lang = (window.navigator.languages ? window.navigator.languages[0] : window.navigator.language).toLowerCase();
  106. if (lang.indexOf("-") !== -1)
  107. lang = lang.split("-")[0];
  108. if (lang.indexOf("_") !== -1)
  109. lang = lang.split("_")[0];
  110. return lang;
  111. }
  112. getConfiguration() {
  113. return {
  114. engines: [
  115. "https://thepiratebay10.org/search/{title}[ {year}]/0/7/0",
  116. "https://rarbg.to/torrents.php?search={title}[ {year}]&order=seeders&by=DESC[&imdb={imdb}]",
  117. "https://1337x.to/sort-search/{title}[ {year}]/seeders/desc/1/",
  118. "https://torrentz2eu.org/index.html?q={title}[ {year}]",
  119. "https://yts.mx/browse-movies/{title}[/all/all/0/seeds/{year}/all]",
  120. "https://eztv.re/search/{title}[ {year}]",
  121. "https://www.torlock.com/?q={title}[ {year}]&sort=seeds&order=desc",
  122. "https://www.torrentdownloads.me/search/?new=1&s_cat=0&search={title}[ {year}]",
  123. "https://www.limetorrents.pro/search/all/{title}[ {year}]/seeds/1/"
  124. ],
  125. showEngines: this.getConfigurationProperty("showEngines", true),
  126. showUserEngines: this.getConfigurationProperty("showUserEngines", false),
  127. showUserEnginesFirst: this.getConfigurationProperty("showUserEnginesFirst", false),
  128. userEngines: this.getConfigurationProperty("userEngines", "").split(/\r?\n/).filter((item) => { return !!item; }),
  129. };
  130. }
  131. ;
  132. }
  133. TorrenterConfigurator._localization = {
  134. en: {
  135. settingsTitle: "Torrenter Script Settings",
  136. showBuildInEngines: "Show Build-in Engines",
  137. showUserEngines: "Show User Definied Engines",
  138. showUserEnginesFirst: "Show User Definied Engines First",
  139. userEngines: "User Engines",
  140. eg: "e.g.",
  141. addEngineDescription: "Type by separating with an enter. Available variables are:</br>&emsp;{title} - movie title</br>&emsp;{year} - movie release year</br>&emsp;{imdb} - position ID in www.imdb.com</br>&emsp;[] - optional fragment, removed if the internal tag is not found by the site processor",
  142. saveButtonCaption: "Save",
  143. closeCaptionButton: "Close",
  144. resetLinkCaption: "Reset to defaults",
  145. configureMenuItem: "Configure"
  146. },
  147. pl: {
  148. settingsTitle: "Ustawienia skryptu Torrenter",
  149. showBuildInEngines: "Pokaż wyszukiwarki wbudowane",
  150. showUserEngines: "Pokaż wyszukiwarki użytkownika",
  151. showUserEnginesFirst: "Pokaż wyszukiwarki użytkownika jako pierwsze",
  152. userEngines: "Wyszukiwarki użytkownika",
  153. eg: "np.",
  154. addEngineDescription: "Podaj rozdzielając enterem. Dostępne zmienne to:</br>&emsp;{title} - tytuł filmu</br>&emsp;{year} - rok wydania filmu</br>&emsp;{imdb} - ID pozycji w serwisie www.imdb.com</br>&emsp;[] - fragment opcjonalny usuwany jeżeli wewnętrzny tag nie zostanie odnaleziony przez parser strony",
  155. saveButtonCaption: "Zapisz",
  156. closeCaptionButton: "Anuluj",
  157. resetLinkCaption: "Przywróć ustawienia domyślne",
  158. configureMenuItem: "Skonfiguruj"
  159. }
  160. };
  161. class Torrenter {
  162. apply(config, siteProcessor) {
  163. let torrenterElements = document.getElementsByClassName("torrenter");
  164. if (torrenterElements && torrenterElements.length > 0) {
  165. for (let i = torrenterElements.length - 1; i >= 0; i--) {
  166. torrenterElements[i].remove();
  167. }
  168. }
  169. setTimeout(() => { siteProcessor((tag, style, itemStyle, args) => { return this.createLinkSpan(config, tag, style, itemStyle, args); }); }, 250);
  170. }
  171. static getSiteProcessor(hostName) {
  172. switch (hostName) {
  173. case "release24.pl":
  174. return Torrenter.processRelease24;
  175. case "www.filmweb.pl":
  176. return Torrenter.processFilmweb;
  177. case "www.imdb.com":
  178. return Torrenter.processImdb;
  179. case "www.rottentomatoes.com":
  180. return Torrenter.processRottenTomatoes;
  181. }
  182. }
  183. createLinkSpan(config, tag, style, itemStyle, args) {
  184. let span = document.createElement(tag);
  185. span.setAttribute("style", style);
  186. span.classList.add("torrenter");
  187. let engines = [];
  188. if (config.showEngines && config.showUserEngines) {
  189. if (config.showUserEnginesFirst) {
  190. engines = config.userEngines.concat(config.engines);
  191. }
  192. else {
  193. engines = config.engines.concat(config.userEngines);
  194. }
  195. }
  196. else if (config.showEngines) {
  197. engines = config.engines;
  198. }
  199. else if (config.showUserEngines) {
  200. engines = config.userEngines;
  201. }
  202. for (let i = 0; i < engines.length; i++) {
  203. let link = document.createElement("a");
  204. link.setAttribute("href", Torrenter.format(engines[i], args));
  205. if (itemStyle) {
  206. link.setAttribute("style", itemStyle);
  207. }
  208. let urlRegex = /(https?:\/\/)(.+?)\//;
  209. let regexResult = engines[i].match(urlRegex);
  210. link.innerHTML = Torrenter.getFavIconImg(regexResult[2]);
  211. link.setAttribute("title", regexResult[2]);
  212. if (i > 0) {
  213. let separator = document.createElement("span");
  214. separator.innerHTML = "&nbsp;|&nbsp;";
  215. span.appendChild(separator);
  216. }
  217. span.appendChild(link);
  218. }
  219. return span;
  220. }
  221. static getFavIconImg(url) {
  222. return '<img src="' + window.location.protocol + '//www.google.com/s2/favicons?domain=' + url + '" width="16px" height="16px">';
  223. }
  224. static format(str, args) {
  225. return str.replace(/(?:\[[^{}]*?)?{(\w+)}(?:[^{}]*?\])?/g, (text, placeholder) => {
  226. if (text[0] == "[" && text[text.length - 1] == "]") {
  227. return args.hasOwnProperty(placeholder) && args[placeholder] != null ? text.substring(1, text.length - 1).replace("{" + placeholder + "}", encodeURIComponent(args[placeholder])) : "";
  228. }
  229. else {
  230. return args.hasOwnProperty(placeholder) ? encodeURIComponent(args[placeholder]) : text;
  231. }
  232. });
  233. }
  234. static processRelease24(createLinkSpan) {
  235. let titleElement = document.getElementById("mainwindow");
  236. let loopCount = titleElement.childElementCount;
  237. for (let i = 1; i < loopCount; i++) {
  238. let elem = titleElement.children[i];
  239. if (elem.className === "wpis") {
  240. let title_regex = /\"(.*)\"\s*(\(([0-9]{4})\))?/;
  241. let match = elem.children[0].children[0].innerHTML.match(title_regex);
  242. if (match != null) {
  243. let title = match[1];
  244. let year = match.length === 4 && match[3] ? match[3] : null;
  245. let span = createLinkSpan("span", "margin-left: 1em; font-weight: normal;", "position: relative; top: 5px;", { title, year });
  246. elem.children[2].children[0].children[0].children[0].children[0].children[1].children[0].children[0].children[0].appendChild(span);
  247. }
  248. }
  249. }
  250. }
  251. static processFilmweb(createLinkSpan) {
  252. let titleElement = document.querySelector(".fP__title");
  253. let title;
  254. let year;
  255. if (titleElement) {
  256. let smallTitleElement = document.querySelector(".fP__originalTitle");
  257. if (smallTitleElement) {
  258. title = smallTitleElement.textContent;
  259. }
  260. else {
  261. title = titleElement.textContent;
  262. }
  263. let yearRegexp = /([0-9]{4})/;
  264. let match = document.querySelector(".fP__year").textContent.match(yearRegexp);
  265. if (match != null) {
  266. year = match[1];
  267. }
  268. }
  269. let headerElement = document.querySelector(".fP__titleDetails");
  270. if (headerElement && title) {
  271. headerElement.appendChild(createLinkSpan("span", "display: inline-flex;", "position: relative; top: 2px; z-index: 1;", { title, year }));
  272. }
  273. }
  274. static processImdb(createLinkSpan) {
  275. let titleElement = document.querySelector('[data-testid*="block__title"]');
  276. let title;
  277. let year;
  278. if (titleElement) {
  279. let smallTitleElement = document.querySelector('[data-testid*="original-title"]');
  280. if (smallTitleElement) {
  281. title = smallTitleElement.childNodes[0].nodeValue;
  282. // Remove "Original title" prefix
  283. let titleRegexp = /Original title: (.*)|.*/;
  284. let titleMatch = title.match(titleRegexp);
  285. if (titleMatch != null) {
  286. title = titleMatch[1];
  287. }
  288. }
  289. else {
  290. title = titleElement.childNodes[0].nodeValue;
  291. }
  292. let yearElement = document.querySelector('[class*="ipc-inline-list__item"] span');
  293. if (yearElement) {
  294. let yearRegexp = /([0-9]{4})/;
  295. let match = yearElement.textContent.match(yearRegexp);
  296. if (match != null) {
  297. year = match[1];
  298. }
  299. }
  300. }
  301. let headerElement = document.querySelector('[data-testid*="block__metadata"]');
  302. if (headerElement && title) {
  303. let match = window.location.pathname.match(/\/(tt.*?)(?:\/|\?|$)/i);
  304. let imdb = match != null ? match[1] : null;
  305. headerElement.appendChild(createLinkSpan("span", "margin-left: 1em; display: inline-block;", null, { title, year, imdb }));
  306. }
  307. }
  308. static processRottenTomatoes(createLinkSpan) {
  309. let titleElement = document.querySelector(".scoreboard__title");
  310. let title;
  311. let year;
  312. if (titleElement) {
  313. title = titleElement.textContent;
  314. let yearRegexp = /([0-9]{4})/;
  315. let match = document.querySelector(".scoreboard__info").textContent.match(yearRegexp);
  316. if (match != null) {
  317. year = match[1];
  318. }
  319. }
  320. let headerElement = document.querySelector(".scoreboard__title");
  321. if (headerElement && title) {
  322. headerElement.appendChild(createLinkSpan("span", "margin-left: 1em;font-size: 0.5em;position: relative;top: -7px;", "position: relative; top: 2px;", { title, year }));
  323. }
  324. }
  325. }
  326. let configurator = new TorrenterConfigurator(TorrenterConfigurator.getLanguage(), () => { applyFunction(configurator.getConfiguration()); });
  327. let config = configurator.getConfiguration();
  328. let hostName = window.location.hostname;
  329. let siteProcessor = Torrenter.getSiteProcessor(hostName);
  330. let applyFunction = (config) => {
  331. if (siteProcessor) {
  332. let torrenter = new Torrenter();
  333. torrenter.apply(config, siteProcessor);
  334. }
  335. };
  336. applyFunction(config);