Seriesfeed++

A fork of Bierdopje AddOn Plus for Seriesfeed

当前为 2016-03-22 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Seriesfeed++
  3. // @namespace https://greasyfork.org/en/users/22592
  4. // @description A fork of Bierdopje AddOn Plus for Seriesfeed
  5. // @include http://seriesfeed.com/*
  6. // @include http://*.seriesfeed.com/*
  7. // @version 1.09
  8. // @grant GM_registerMenuCommand
  9. // @grant GM_setValue
  10. // @grant GM_getValue
  11. // @grant GM_addStyle
  12. // @require http://code.jquery.com/jquery-1.10.2.js
  13. // @require http://code.jquery.com/ui/1.11.4/jquery-ui.js
  14. // @author Mr. Invisible
  15. // @run-at document-end
  16. // ==/UserScript==
  17.  
  18. /*global GM_getValue,GM_registerMenuCommand,GM_addStyle,GM_info,GM_setValue,$ */
  19.  
  20. /**
  21. Changelog:
  22. 1.09: Added an exception
  23. 1.08: Multi-domain
  24. 1.07: Forgot to turn of debug once again
  25. 1.06:
  26. - updated for SeriesFeed 2.0
  27. - added new provider
  28. - dialog also closes when middle-clicking
  29. - re-added functionality to the episode and season pages.
  30. - added exception for legends of tomorrow
  31. - added email address for easier communication
  32. 1.05: Updated for SeriesFeed 1.3
  33. 1.04: Fixed problem with the visual watchlist & dialog for download now closes after clicking a link.
  34. 1.03: Updated for SeriesFeed 1.2
  35. 1.02: Fixed small bug with Chrome-derived browsers
  36. 1.01: Rewrote script in order to accommodate the SeriesFeed pages
  37. 1.00: Cloned from the Bierdopje AddOn Plus version 1.101
  38. **/
  39.  
  40. // Create one accessible object. The remainder is hidden for external use.
  41. var seriesFeedPlusPlus = (function () {
  42. 'use strict';
  43.  
  44. var seriesFeedPlusPlus, configDialog, // Objects
  45. debug, pageRegexes, currentPage, flags, subProviders, dlProviders, dlFormats, dlSites, languageMap, // Variables
  46. main, checkPage, injectMenuItem, modifyPage, handleStartPage, injectDefaultTable, createFunctionality,
  47. createLanguageFlag, parseEpisode, showSubSelectionDialog, handleBroadcastPage, handleWatchlistPage,
  48. injectTableHeader, showDlSelectionDialog, createDownloadLink, formatToConvention, handleSeasonPage,
  49. handleEpisodePage; // Methods
  50.  
  51. // Initialize objects
  52. seriesFeedPlusPlus = {};
  53. configDialog = (function () {
  54. var instance, configElementName, preferences, mapping, show, close, closeOtherSubConfigs, closeSubConfig,
  55. openSubConfig, changeConfiguration, saveConfiguration, loadPreferences, getEnabledSubtitleLanguages,
  56. getConfigValue, getEnabledSubtitleSources, getEnabledDownloadProviders, getEnabledDownloadTypes;
  57.  
  58. // Init vars
  59. configElementName = "configFrame";
  60. // Preferences with their default values
  61. preferences = {
  62. sub_lang_nl: true,
  63. sub_lang_en: true,
  64. sub_source_addic7ed: true,
  65. sub_source_podnapisi: true,
  66. sub_source_opensubtitles: false,
  67. sub_source_subtitleseeker: false,
  68. dl_format_webdl: true,
  69. dl_format_hdtv: true,
  70. dl_format_hdtvx264: true,
  71. dl_source_torrent: false,
  72. dl_source_nzb: true
  73. };
  74. mapping = {
  75. sub_lang_nl: "Ext.SF.SubLanguage_NL",
  76. sub_lang_en: "Ext.SF.SubLanguage_US",
  77. sub_source_addic7ed: "Ext.SF.SubProvider_Addic7ed",
  78. sub_source_podnapisi: "Ext.SF.SubProvider_PodNapisi",
  79. sub_source_opensubtitles: "Ext.SF.SubProvider_OpenSubTitles",
  80. sub_source_subtitleseeker: "Ext.SF.SubProvider_SubtitleSeeker",
  81. dl_format_webdl: "Ext.SF.MediaFormat_WEB-DL",
  82. dl_format_hdtv: "Ext.SF.MediaFormat_HDTV",
  83. dl_format_hdtvx264: "Ext.SF.MediaFormat_HTDV.x264",
  84. dl_source_torrent: "Ext.SF.DownloadFormat_Torrent",
  85. dl_source_nzb: "Ext.SF.DownloadFormat_NZB"
  86. };
  87. // Initialize functions
  88. show = function () {
  89. var css, html, div, subFrames, idx, inputs;
  90. if (document.getElementById(configElementName)) {
  91. close();
  92. return;
  93. }
  94. css = ' ' +
  95. '.h3subframe { margin: 1px 0 0px; padding: 1px 10px; border-bottom: 1px solid #bbb; font-size: 1.5em; font-weight: normal; cursor:pointer; background:#DDDDDD none repeat scroll 0 0; } ' +
  96. '.h3subframe:hover { background:#C0BEBE none repeat scroll 0 0; } ' +
  97. '#h3subframetitle { margin: 2px 0 0px; padding: 7px 10px; border-bottom: 1px solid #bbb; font-size: 2.0em; font-weight: normal; } ' +
  98. '.popup a { color: darkblue; text-decoration: none; } ' +
  99. '.popup p { padding: 1px 10px; margin: 0px 0; font-family:verdana,geneva,lucida,"lucida grande",arial,helvetica,sans-serif; font-size:10pt; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:normal; line-height:normal; } ' +
  100. '.sidebyside { padding: 1px 10px; margin: 0px 0;display:inline-block;width:17em; } ' +
  101. '.h3subframecontent { max-height:294px; overflow:auto; display: none; padding: 10px 10px; } ' +
  102. '#showinfo { font-size:14px; } ';
  103. GM_addStyle(css);
  104.  
  105. html =
  106. '<div id="fade" style="background: #000;height: 100%;opacity: .80;"></div>' +
  107. '<div style="font-family: verdana; color: black; background: #ddd; padding: 10px 20px; border: 10px solid #fff; float: left; width: 731px; position: absolute; top: 2%; left: 40%; margin: 0 0 0 -292px; border-radius: 10px; z-index: 100;">' +
  108. ' <div class="popup" style="float: left; width: 100%; background: #fff; margin: 10px 0; padding: 0px 0 0px; border-left: 1px solid #bbb; border-top: 1px solid #bbb; border-right: 1px solid #bbb;">' +
  109. ' <a href="#" onclick="javascript:return false;">' +
  110. ' <img id="' + configElementName + '_close" style="border:none; position: absolute; right: -20px; top: -20px;" title="Close" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAfCAYAAAD0ma06AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAY1SURBVHjapFZbbFRVFN0zd6Yz08dMoUNf9EGxUItJK62I4AOJEYiQoqE+0OgHCiqG+PgQozH6ofyIJiYEMRqNJpggHySlrRM+hCAtajAUaGgEi9BBSilMO0PnfWeOa597bjt9AEVvsubOPWefs/br7H0sQgj6P4/FYrk9+WkSuoAHgCrgLvV9DLgMdID02rQZmfAmaAJaxS2edDr9s67rL7EB/9XCUuALoEl+pZJEvTAo8A9s6iVKxojKYWheAWxuIMr2GGKp1KHh4eF3vF4vW59me6ZD2Ajsle6LXify7SI68iNROIgtIKtpBvQEB5DI7iC6Zw3Rmi1EM0vlBsFg8OX8/PxvWQdFKm5E2KhiQ9R9iOjL17E6QFRUhAGQpFNjklYrhhT6YbndTtT8LtGjG+T0lStXNhcVFTGpnkE8jpAT4hdgNvm+Ivr+AyIHtM+Fu3Ss0RUZO8pqqos/NiDLblgcQO48/CzRpk/l9KlTp56oq6s7gL8JkzST0AespN9/Itq2Hu7xQnsbRFOcWSBKT50FVpMUHrBD/iKsXb+V6KmtFI/H/3Q6nZzdEZPU1PVFSXbtEoltz0Nzm2HRqleIvjsLa/9CoiSnBs99cwaym4lCYSRSHr4/REg64SBHTX9//2fqGNmVevJ5jn/0Xe+Rhd2SBVdGkInr3hizZI8fOibGg8fM5/EthgIJwxPJ7a/Jd05Ozn14uQEHGRGXsVtOIwHS2nbDlTOIYlHoMoUL9w0Q/GSA/0/KeXglFmEWsp/uIjp9FAbnzWttbV3H3ECWFWdnubTuSBulQ9AwDs2jcSPGby6evGn7sIGJzwuzDUViMekdAZ0jrXvlVGVl5RK8ctlKq6ZpHFSKdBzCwSVjQRILAzh3508TPe29dbl6ZibiB/lrQeWBGFmykGe/dcjpwsLCeuVWpw1ZWskFWO/rM45ZNGWkPXt0ZIR/iJbigHfeoOYuU9UsbmbtWI2x+i+acWSt8yShCiaJVFwq50zeZrsYmapAgz/KFCmzo2gqhk7WJ8SDCY+bomF2qdI2E3/cpKPwXKYs1qdAlozwnjlSJBaLcbVxyqRBlT8rB+fUkJuzGotEXB1TRvc02hfLKHk9btT6BCyPzJ0rpwcGBoLqHGpWVIMjsmLVPkTZhXgbMacUW3pGTB2z+4HA5fHjkE3EDELeYyaSJjx/qZzq6uq6pKJrsR4/flwSeh98mIbmVpET7khBU20qw+4GEbda1ndZyaTpLDLWOtnSchdZVj4pxw8fPuzPLOD2SCSylxvpr9u3C1GDylkClAM73xrrsnfiu4JErMCAqAIW0Nj8DsiWktBnGXJdr24QiURCTuXm5n4MnmZWmQm1EydOPMITg4ODom/VEiHKsGgOyQ14sSQvJhF2j8eoYhXGvPzGmqF7K0V3d7ckQ5XhHHkbeAyoNU9ODpqmvEp0dHSIQEOVsRhWjGSTuOq4OQJOMpQEWXS+RxzYs0cgGSUhCvgO7L+Jg6DKqLyHOGpra0tYgAV9Pp/oX1wnBLunXlnrgVXYfEAzEMzCmFsRLSIpG6opFa27d4twOCzJWlpa2Lr3lTsXAiUmIRcAN1z6Awuy7zs7O8WxjRtFvDDH2JhJG4ClCo1AtUGq59tEz9q1UlGTrK2t7QL2/ATYKJsDUTUwQzZgVAKrSrI89K+dxcXFzbiJUR/K3cmTJ2nWwYNUcfQoeS+cJcdwQGZeIjuHAmV30KWGBjq/YgUtWLiQqquryWazUXt7u3/16tX7IIYbF50D+vjWwUXGJLQYlxZZDdx+v//zsrKyZtnX0ONwcAnWUygUQhtMSELeGK2HCgoKqKSkhNDZ5fj+/fvPNTU1teDvBQW/IuMWEx29g6rkYSv5zlfu8Xgae3p6fGKaD1z4N0i/xtqPALR/WgssAuawK1XNto7eaZSVVhVPl6ruM9Baiuvr6+fBzRUul2sWxPKQWA5Yqg0NDekIwfXe3t4h3EfZ10PAVWXRIMBj16VlRvFLj7smTiB1qArPxPnKcrdqpE5VG0lVEC6EYdUIgsp9ITXGc0mzaU26CGeQampTp7I4W8GlXK/R2MUxoTaOZMAk0jNv4VNe9RXpRGK7IrIrD2QS6mrzpCKfSDRK8q8AAwCF/L1ktjcKFAAAAABJRU5ErkJggg%3D%3D"/>' +
  111. ' </a>' +
  112. ' <div id="h3subframetitle"><b>SeriesFeed AddOn - Preferences</b></div>' +
  113. ' <div id="h3subframe1" class="h3subframe">Media</div>' +
  114. ' <div class="h3subframecontent">' +
  115. ' <p id="showinfo">Choose the <b>media formats</b> you need</p><br>' +
  116. ' <div class="sidebyside"><input type="checkbox" id="dl_format_webdl" /> WEB-DL <font color="gray">(720p &amp; 1080p)</font></div>' +
  117. ' <div class="sidebyside"><input type="checkbox" id="dl_format_hdtv" /> HDTV <font color="gray">(720p &amp; 1080p)</font></div>' +
  118. ' <div class="sidebyside"><input type="checkbox" id="dl_format_hdtvx264" /> HDTV_x264 <font color="gray">(MP4)</font></div>' +
  119. ' <hr/>' +
  120. ' <p id="showinfo">Choose the <b>type of files</b> you want to download</p><br>' +
  121. ' <div class="sidebyside"><input type="checkbox" id="dl_source_nzb" /> NZB</div>' +
  122. ' <div class="sidebyside"><input type="checkbox" id="dl_source_torrent" /> Torrent</div><br>' +
  123. ' </div>' +
  124. ' <div id="h3subframe2" class="h3subframe">Subtitles</div>' +
  125. ' <div class="h3subframecontent">' +
  126. ' <p id="showinfo">Choose the <b>subtitle sites</b> you want as option</p><br>' +
  127. ' <p><input type="checkbox" id="sub_source_addic7ed" /> Addic7eD <font color="gray">(preferred)</font></p>' +
  128. ' <p><input type="checkbox" id="sub_source_podnapisi" /> PodNapisi</p>' +
  129. ' <p><input type="checkbox" id="sub_source_opensubtitles" /> OpenSubtitles</p>' +
  130. ' <p><input type="checkbox" id="sub_source_subtitleseeker" /> SubTitleSeeker <font color="gray">(can be unsafe)</font></p>' +
  131. ' </div>' +
  132. ' <div id="h3subframe3" class="h3subframe">Languages</div>' +
  133. ' <div class="h3subframecontent">' +
  134. ' <p id="showinfo">Choose the <b>subtitle languages</b> you want to find</p><br>' +
  135. ' <p><input type="checkbox" id="sub_lang_nl" /> Nederlands <img src="' + flags.nl + '"/></p>' +
  136. ' <p><input type="checkbox" id="sub_lang_en" /> English <img src="' + flags.en + '"/></p>' +
  137. ' </div>' +
  138. ' <div id="h3subframe4" class="h3subframe">About</div>' +
  139. ' <div class="h3subframecontent">' +
  140. ' <p><b>' + GM_info.script.name + '</b> - version: ' + GM_info.script.version + '</p>' +
  141. ' <br />' +
  142. ' <p>' + GM_info.script.description + '</p>' +
  143. ' <p>Author: Mr. Invisible (mrinvisible@cryptolab.net) - original author: XppX</p>' +
  144. ' <p>License: GPL</p>' +
  145. ' </div>' +
  146. ' </div>' +
  147. '</div>';
  148.  
  149. div = document.createElement("div");
  150. div.id = configElementName;
  151. div.setAttribute('style',
  152. 'visibility: visible;position: fixed;width: 100%;height: 100%;top: 0;left: 0;font-size:12px;' +
  153. 'z-index:1001;text-align:left;');
  154. div.innerHTML = html;
  155. document.body.appendChild(div);
  156. document.getElementById(configElementName + "_close").addEventListener("click", close, false);
  157.  
  158. // Loop through checkboxes to populate them
  159. inputs = div.getElementsByTagName("input");
  160. for (idx = 0; idx < inputs.length; idx++) {
  161. if (inputs[idx].type === "checkbox") {
  162. if (preferences.hasOwnProperty(inputs[idx].id) && preferences[inputs[idx].id]) {
  163. inputs[idx].setAttribute("checked", "checked");
  164. }
  165. // Add a listener to each checkbox
  166. inputs[idx].addEventListener("click", changeConfiguration, false);
  167. }
  168. }
  169.  
  170. // Add event listeners for opening when a click on the head is performed
  171. subFrames = document.getElementsByClassName("h3subframe");
  172. for (idx = 0; idx < subFrames.length; idx++) {
  173. subFrames[idx].addEventListener("click", openSubConfig, false);
  174. }
  175. // Unfold the first one
  176. openSubConfig({
  177. target: document.getElementById('h3subframe1')
  178. });
  179. };
  180. close = function () {
  181. var box = document.getElementById(configElementName);
  182. box.parentNode.removeChild(box);
  183. window.location.reload(false);
  184. };
  185. closeOtherSubConfigs = function (evt) {
  186. var ignore, subFrames, idx;
  187. ignore = evt.target || evt.srcElement;
  188. subFrames = document.getElementsByClassName("h3subframe");
  189. for (idx = 0; idx < subFrames.length; idx++) {
  190. if (ignore !== subFrames[idx]) {
  191. subFrames[idx].nextElementSibling.style.display = "none";
  192. subFrames[idx].addEventListener("click", openSubConfig, false);
  193. }
  194. }
  195. };
  196. closeSubConfig = function (e) {
  197. var evt, target;
  198.  
  199. evt = e || window.event;
  200. target = evt.target || evt.srcElement;
  201. target.removeEventListener("click", closeSubConfig, false);
  202. target.nextElementSibling.style.display = "none";
  203. target.addEventListener("click", openSubConfig, false);
  204. };
  205. openSubConfig = function (e) {
  206. var evt, target;
  207.  
  208. evt = e || window.event;
  209. target = evt.target || evt.srcElement;
  210. target.removeEventListener("click", openSubConfig, false);
  211. target.nextElementSibling.style.display = "block";
  212. closeOtherSubConfigs(evt);
  213. target.addEventListener("click", closeSubConfig, false);
  214. };
  215. changeConfiguration = function (e) {
  216. if (e.target.tagName.toLowerCase() === 'input') {
  217. saveConfiguration(e.target.id, e.target.checked);
  218. }
  219. };
  220. saveConfiguration = function (id, value) {
  221. if (preferences.hasOwnProperty(id)) {
  222. preferences[id] = value;
  223. GM_setValue(mapping[id], value);
  224. }
  225. };
  226. loadPreferences = function () {
  227. var key;
  228. if (debug) {
  229. window.console.log("Entering load preferences function");
  230. }
  231. if (debug) {
  232. window.console.log("Preferences (default):");
  233. window.console.log(preferences);
  234. }
  235.  
  236. for (key in mapping) {
  237. if (mapping.hasOwnProperty(key) && preferences.hasOwnProperty(key)) {
  238. preferences[key] = GM_getValue(mapping[key], preferences[key]);
  239. }
  240. }
  241.  
  242. if (debug) {
  243. window.console.log("Preferences (loaded):");
  244. window.console.log(preferences);
  245. }
  246. };
  247. getConfigValue = function (name) {
  248. if (preferences.hasOwnProperty(name)) {
  249. return preferences[name];
  250. }
  251. return null;
  252. };
  253. getEnabledSubtitleLanguages = function () {
  254. var result = [];
  255.  
  256. if (preferences.sub_lang_en) {
  257. result.push("en");
  258. }
  259. if (preferences.sub_lang_nl) {
  260. result.push("nl");
  261. }
  262.  
  263. return result;
  264. };
  265. getEnabledSubtitleSources = function () {
  266. var result = [];
  267.  
  268. if (preferences.sub_source_addic7ed) {
  269. result.push(subProviders.sub_source_addic7ed);
  270. }
  271. if (preferences.sub_source_podnapisi) {
  272. result.push(subProviders.sub_source_podnapisi);
  273. }
  274. if (preferences.sub_source_opensubtitles) {
  275. result.push(subProviders.sub_source_opensubtitles);
  276. }
  277. if (preferences.sub_source_subtitleseeker) {
  278. result.push(subProviders.sub_source_subtitleseeker);
  279. }
  280.  
  281. return result;
  282. };
  283. getEnabledDownloadProviders = function () {
  284. var result = [];
  285.  
  286. if (preferences.dl_source_torrent) {
  287. result.push(dlProviders.dl_source_torrent);
  288. }
  289. if (preferences.dl_source_nzb) {
  290. result.push(dlProviders.dl_source_nzb);
  291. }
  292.  
  293. return result;
  294. };
  295. getEnabledDownloadTypes = function () {
  296. var result = [];
  297.  
  298. if (preferences.dl_format_webdl) {
  299. result.push(dlFormats.dl_format_webdl);
  300. }
  301. if (preferences.dl_format_hdtv) {
  302. result.push(dlFormats.dl_format_hdtv);
  303. }
  304. if (preferences.dl_format_hdtvx264) {
  305. result.push(dlFormats.dl_format_hdtvx264);
  306. }
  307.  
  308. return result;
  309. };
  310. // Initialize object to return and expose appropriate methods
  311. instance = {};
  312. instance.show = show;
  313. instance.loadPreferences = loadPreferences;
  314. instance.getConfigValue = getConfigValue;
  315. instance.getEnabledSubtitleLanguages = getEnabledSubtitleLanguages;
  316. instance.getEnabledSubtitleSources = getEnabledSubtitleSources;
  317. instance.getEnabledDownloadProviders = getEnabledDownloadProviders;
  318. instance.getEnabledDownloadTypes = getEnabledDownloadTypes;
  319.  
  320. return instance;
  321. }());
  322. // Initialize variables
  323. debug = false;
  324. // Maps short language keywords to the full English language
  325. languageMap = {
  326. "en": "English",
  327. "nl": "Dutch"
  328. };
  329. // Providers, keys of this MUST be equal to the ones in the configDialog.preferences variable
  330. subProviders = {
  331. sub_source_addic7ed: {
  332. title: "Addic7eD",
  333. createLink: function (showName, showEpisode, language) {
  334. var showNameConverted, showEpisodeConverted, languageConverted;
  335. // Convert show name & show episode to appropriate formats
  336. showNameConverted = this.showConversion(showName);
  337. showEpisodeConverted = this.episodeConversion(showEpisode);
  338. languageConverted = this.languageConversion(language);
  339.  
  340. return "http://www.addic7ed.com/serie/" + showNameConverted + "/" + showEpisodeConverted.season + "/" +
  341. showEpisodeConverted.episode + "/" + languageConverted;
  342. },
  343. showConversion: function (show) {
  344. var exceptions;
  345.  
  346. show = show.replace(/ /g, "_");
  347. // Exception map for shows
  348. exceptions = {
  349. "The_Flash": "The_Flash_(2014)",
  350. "Legends_of_Tomorrow": "DC's_Legends_of_Tomorrow",
  351. "Marvel's_Daredevil": "Daredevil"
  352. };
  353. if (exceptions.hasOwnProperty(show)) {
  354. show = exceptions[show];
  355. }
  356. return show;
  357. },
  358. episodeConversion: function (episode) { return parseEpisode(episode); },
  359. languageConversion: function (language) {
  360. switch (language) {
  361. case "nl":
  362. return "17";
  363. case "en":
  364. return "1";
  365. }
  366. return language;
  367. }
  368. },
  369. sub_source_podnapisi: {
  370. title: "PodNapisi",
  371. createLink: function (showName, showEpisode, language) {
  372. var showNameConverted, showEpisodeConverted, languageConverted;
  373. // Convert show name & show episode to appropriate formats
  374. showNameConverted = this.showConversion(showName);
  375. showEpisodeConverted = this.episodeConversion(showEpisode);
  376. languageConverted = this.languageConversion(language);
  377.  
  378. return "http://www.podnapisi.net/subtitles/search/advanced?keywords=" + showNameConverted + "&seasons="
  379. + showEpisodeConverted.season + "&episodes=" + showEpisodeConverted.episode + "&language=" +
  380. languageConverted;
  381. },
  382. showConversion: function (show) {
  383. var exceptions;
  384.  
  385. show = show.replace(/ /g, "+");
  386. // Exception map for shows
  387. exceptions = {};
  388. if (exceptions.hasOwnProperty(show)) {
  389. show = exceptions[show];
  390. }
  391. return show;
  392. },
  393. episodeConversion: function (episode) { return parseEpisode(episode); },
  394. languageConversion: function (language) { return language; }
  395. },
  396. sub_source_opensubtitles: {
  397. title: "OpenSubtitles",
  398. createLink: function (showName, showEpisode, language) {
  399. var showNameConverted, showEpisodeConverted, languageConverted;
  400. // Convert show name & show episode to appropriate formats
  401. showNameConverted = this.showConversion(showName);
  402. showEpisodeConverted = this.episodeConversion(showEpisode);
  403. languageConverted = this.languageConversion(language);
  404.  
  405. return "http://www.openSubtitles.org/nl/search/searchonlytvseries-on/subformat-srt/sublanguageid-" +
  406. languageConverted + "/season-" + showEpisodeConverted.season + "/episode-" +
  407. showEpisodeConverted.episode + "/moviename-" + showNameConverted;
  408. },
  409. showConversion: function (show) {
  410. var exceptions;
  411.  
  412. show = show.replace(/ /g, "+");
  413. // Exception map for shows
  414. exceptions = {};
  415. if (exceptions.hasOwnProperty(show)) {
  416. show = exceptions[show];
  417. }
  418. return show;
  419. },
  420. episodeConversion: function (episode) { return parseEpisode(episode); },
  421. languageConversion: function (language) {
  422. switch (language) {
  423. case "nl":
  424. return "dut";
  425. case "en":
  426. return "eng";
  427. }
  428. }
  429. },
  430. sub_source_subtitleseeker: {
  431. title: "SubTitleSeeker",
  432. createLink: function (showName, showEpisode, language) {
  433. var showNameConverted, showEpisodeConverted, convertedLanguage;
  434. // Convert show name & show episode to appropriate formats
  435. showNameConverted = this.showConversion(showName);
  436. showEpisodeConverted = this.episodeConversion(showEpisode);
  437. convertedLanguage = this.languageConversion(language);
  438. if (debug) {
  439. window.console.log("Language is not used for subTitleSeeker: " + convertedLanguage);
  440. }
  441.  
  442. return "http://www.subtitleseeker.com/search/TV_EPISODES/" + showNameConverted + "+S" +
  443. showEpisodeConverted.season + "E" + showEpisodeConverted.episode;
  444. },
  445. showConversion: function (show) {
  446. var exceptions;
  447.  
  448. show = show.replace(/ /g, "+");
  449. // Exception map for shows
  450. exceptions = {};
  451. if (exceptions.hasOwnProperty(show)) {
  452. show = exceptions[show];
  453. }
  454. return show;
  455. },
  456. episodeConversion: function (episode) { return parseEpisode(episode); },
  457. languageConversion: function (language) { return language; }
  458. }
  459. };
  460. dlFormats = {
  461. dl_format_webdl: "WEB-DL 1080/720p",
  462. dl_format_hdtv: "HDTV 1080/720p",
  463. dl_format_hdtvx264: "HDTV-x264 (MP4)"
  464. };
  465. dlSites = {
  466. torrentz: {
  467. title: "Torrentz",
  468. createLink: function (showName, showEpisode, quality, dialog) {
  469. var url, dlAddition;
  470.  
  471. // Adjust name & episode for search
  472. showName = showName.replace(/ /g, "+");
  473. showEpisode = parseEpisode(showEpisode);
  474. // Determine addition based on quality
  475. dlAddition = "";
  476. switch (quality) {
  477. case dlFormats.dl_format_webdl:
  478. dlAddition = "+WEB%20DL";
  479. break;
  480. case dlFormats.dl_format_hdtv:
  481. dlAddition = "+720p+x264";
  482. break;
  483. case dlFormats.dl_format_hdtvx264:
  484. dlAddition = "+HDTV+x264";
  485. break;
  486. default:
  487. window.console.warn("Got an unknown quality type: " + quality);
  488. }
  489.  
  490. url = "https://torrentz.eu/search?f=" + showName + "+" + formatToConvention(showEpisode) + dlAddition;
  491. return createDownloadLink(this.title, url, dialog);
  492. }
  493. },
  494. kat: {
  495. title: "KAT",
  496. createLink: function (showName, showEpisode, quality, dialog) {
  497. var url, dlAddition;
  498.  
  499. // Adjust name & episode for search
  500. showName = showName.replace(/ /g, "%2B");
  501. showName = showName.replace(/\(/g, "");
  502. showEpisode = parseEpisode(showEpisode);
  503. // Determine addition based on quality
  504. dlAddition = "";
  505. switch (quality) {
  506. case dlFormats.dl_format_webdl:
  507. dlAddition = "%2BWEB-DL";
  508. break;
  509. case dlFormats.dl_format_hdtv:
  510. dlAddition = "%2B720p%2Bx264";
  511. break;
  512. case dlFormats.dl_format_hdtvx264:
  513. dlAddition = "%2BHDTV.x264";
  514. break;
  515. default:
  516. window.console.warn("Got an unknown quality type: " + quality);
  517. }
  518.  
  519. url = "https://kat.cr/usearch/" + showName + "%2B" + formatToConvention(showEpisode) + dlAddition;
  520. return createDownloadLink(this.title, url, dialog);
  521. }
  522. },
  523. tpb: {
  524. title: "TPB",
  525. createLink: function (showName, showEpisode, quality, dialog) {
  526. var url, dlAddition;
  527.  
  528. // Adjust name & episode for search
  529. showEpisode = parseEpisode(showEpisode);
  530. // Determine addition based on quality
  531. dlAddition = "";
  532. switch (quality) {
  533. case dlFormats.dl_format_webdl:
  534. dlAddition = " WEB-DL";
  535. break;
  536. case dlFormats.dl_format_hdtv:
  537. dlAddition = " 720p x264";
  538. break;
  539. case dlFormats.dl_format_hdtvx264:
  540. dlAddition = " HDTV.x264";
  541. break;
  542. default:
  543. window.console.warn("Got an unknown quality type: " + quality);
  544. }
  545.  
  546. url = "https://thepiratebay.se/search/" + showName + " " + formatToConvention(showEpisode) + dlAddition;
  547. return createDownloadLink(this.title, url, dialog);
  548. }
  549. },
  550. nzbindex: {
  551. title: "NZBIndex",
  552. createLink: function (showName, showEpisode, quality, dialog) {
  553. var url, dlAddition;
  554.  
  555. // Adjust name & episode for search
  556. showName = showName.replace(/ /g, "+");
  557. showEpisode = parseEpisode(showEpisode);
  558. // Determine addition based on quality
  559. dlAddition = "";
  560. switch (quality) {
  561. case dlFormats.dl_format_webdl:
  562. dlAddition = "+0p++WEB-DL&age=&max=25&sort=agedesc&minsize=600&maxsize=5120&poster=&nfo=&hidespam=1&more=0";
  563. break;
  564. case dlFormats.dl_format_hdtv:
  565. dlAddition = "+0p++x264&age=&max=25&sort=agedesc&minsize=600&maxsize=5120&poster=&nfo=&hidespam=1&more=0";
  566. break;
  567. case dlFormats.dl_format_hdtvx264:
  568. dlAddition = "+HDTV.x264&age=&max=25&sort=agedesc&minsize=150&maxsize=1536&poster=&nfo=&hidespam=1&more=0";
  569. break;
  570. default:
  571. window.console.warn("Got an unknown quality type: " + quality);
  572. }
  573.  
  574. url = "https://www.nzbindex.com/search/?q=" + showName + '+"' + formatToConvention(showEpisode) +
  575. '"|"' + formatToConvention(showEpisode, "x") + dlAddition;
  576. return createDownloadLink(this.title, url, dialog);
  577. }
  578. },
  579. nzbclub: {
  580. title: "NZBClub",
  581. createLink: function (showName, showEpisode, quality, dialog) {
  582. var url, dlAddition;
  583.  
  584. // Adjust name & episode for search
  585. showName = showName.replace(/ /g, "+");
  586. showEpisode = parseEpisode(showEpisode);
  587. // Determine addition based on quality
  588. dlAddition = "";
  589. switch (quality) {
  590. case dlFormats.dl_format_webdl:
  591. dlAddition = "+WEB-DL&szs=20&sze=24&st=1&sp=1&sn=1";
  592. break;
  593. case dlFormats.dl_format_hdtv:
  594. dlAddition = "+0p+x264&szs=20&sze=24&st=1&sp=1&sn=1";
  595. break;
  596. case dlFormats.dl_format_hdtvx264:
  597. dlAddition = "+HDTV.x264&sz=16&ez=22&rpp=25&st=1&sp=1&sn=1";
  598. break;
  599. default:
  600. window.console.warn("Got an unknown quality type: " + quality);
  601. }
  602.  
  603. url = "https://www.nzbclub.com/search.aspx?q=" + showName + '+' + formatToConvention(showEpisode) +
  604. dlAddition;
  605. return createDownloadLink(this.title, url, dialog);
  606. }
  607. },
  608. binsearch: {
  609. title: "BinSearch",
  610. createLink: function (showName, showEpisode, quality, dialog) {
  611. var url, dlAddition;
  612.  
  613. // Adjust name & episode for search
  614. showName = showName.replace(/ /g, "+");
  615. showEpisode = parseEpisode(showEpisode);
  616. // Determine addition based on quality
  617. dlAddition = "";
  618. switch (quality) {
  619. case dlFormats.dl_format_webdl:
  620. dlAddition = "+WEB-DL&m=&max=25&adv_g=&adv_age=999&adv_sort=date&adv_col=on&minsize=600&maxsize=5120&font=small&postdate=";
  621. break;
  622. case dlFormats.dl_format_hdtv:
  623. dlAddition = "+0p+x264&m=&max=25&adv_g=&adv_age=999&adv_sort=date&adv_col=on&minsize=600&maxsize=5120&font=small&postdate=";
  624. break;
  625. case dlFormats.dl_format_hdtvx264:
  626. dlAddition = "+HDTV.x264&m=&max=25&adv_g=&adv_age=999&adv_sort=date&adv_col=on&minsize=150&maxsize=1024&font=small&postdate=";
  627. break;
  628. default:
  629. window.console.warn("Got an unknown quality type: " + quality);
  630. }
  631.  
  632. url = "https://binsearch.info/index.php?q=" + showName + '+' + formatToConvention(showEpisode) +
  633. dlAddition;
  634. return createDownloadLink(this.title, url, dialog);
  635. }
  636. }
  637. };
  638. dlProviders = {
  639. dl_source_torrent: {
  640. title: "Torrent",
  641. sites: [
  642. dlSites.torrentz,
  643. dlSites.kat,
  644. dlSites.tpb
  645. ]
  646. },
  647. dl_source_nzb: {
  648. title: "NZB",
  649. sites: [
  650. dlSites.nzbindex,
  651. dlSites.nzbclub,
  652. dlSites.binsearch
  653. ]
  654. }
  655. };
  656. // Flags
  657. flags = {
  658. "nl": "data:image/gif;base64,R0lGODlhEAALANUAAAABdP7+/jRusgAhj/picxZYp/UAAPLy8vr6+vj4+Pb29v56iVqKw/1da0V7ujpytSpmrusAAGSSx/pCUftUZP11gwAAR/96hgAAWe7v7yFgq0J3tw1Rokt/vAAAPFCCv1SFwS9qrtHR0fk8Tf6CkdXV1ftqevcxQvg3Rz52uPxMWvVWavZabv8VMflXaP5ZZ3eezv6XpC5qsvYsPPv7+/pQX/5/j/htfvlIV/T19fxwgPX19fz8/P0AAPT09P8AACH5BAAAAAAALAAAAAAQAAsAAAaFwJ/w1ysWDUhkJPJbLC6Vis5kIhAaLkqNeWk0XloVbjJCnWa4SCtGsjmltyqBtbpFDoE8j0dDIBIJCjs+OyUZenx+gII+Bzkih3t9f4GDBwc+IgMwDCAfHQ4bDwIhEBAaGxYAEg4pozKnGgUFHBwhHqsMnZ+hrgKkELgAwxjFxRbIHsoeQQA7",
  659. "en": "data:image/gif;base64,R0lGODlhEAALANUAAGNjtvr6+vb29fHx8fxGRvkQEP15eatjSuNjSjx6+TV0+f2Kd+np6dvb2PxUVPx4YttjSvsrK5q7/PxlZrpjSvw6Oubm5e7u7sljSgR09Z1jSvT080iB+gBMtkF9+vofHwBpu/f39uLi3/syMtVjSqG+/C9y+Ozs7KLA/GNlwPyBbO3t6gNx5QArs/2XhpGRgKysn+z+/rS0qPR0XuXl/P7KwcnJwf39Wzh3+f7+/fv/+vHx7wNv4df+/f3h3GNnxCH5BAAAAAAALAAAAAAQAAsAAAaMwJ/wl0oBjqoHYgmB/HIoXSkgoQYCoawA9kNxOJ5EAmcyTByEiouUmnoEiYspMxhcTreNrR1OKOYgaBUjEQsYAAEJAwonLCIddwwMFj0yABI4fzwgHS2DER8fCwciAQIbG3aRkxYNPi8qNWZoaRGgBbgqGg0BMVqoqTsrNBYiM0sITU0kGBQUBwca0kEAOw=="
  660. };
  661. // Page regexes
  662. pageRegexes = {
  663. // SeriesFeed homepage
  664. start: new RegExp("^/$"),
  665. // Broadcast schedule (format: series/uitzendlijst/[{month}/]* )
  666. broadcast: new RegExp("^.*/series/uitzendlijst(/[a-z]+)*/$"),
  667. // Watchlist (format: series/kijklijst/[topshows/|favorieten/]*
  668. watch: new RegExp("^.*/series/kijklijst/([topshows|favorieten]+/)*$"),
  669. // Episodes/Seasons (format: series/{name}/afleveringen/[seizoen/{nr}/]* )
  670. season: new RegExp("^.*/series/(.+)/afleveringen/(seizoen/[0-9]+/)*$"),
  671. // Episode (format: series/aflevering/{nr}/ )
  672. episode: new RegExp("^.*/series/aflevering/[0-9]+/?$")
  673. };
  674. // Current page
  675. currentPage = null;
  676.  
  677. // Initialize functions
  678. main = function () {
  679. if (debug) {
  680. window.console.log("Entering main function");
  681. }
  682. // Load preferences
  683. configDialog.loadPreferences();
  684. // Register GreaseMonkey menu entry
  685. try {
  686. GM_registerMenuCommand("[SeriesFeed AddOn] Configuratie", configDialog.show, "C");
  687. } catch (e) {
  688. window.console.warn("Could not register GM menu handler. This is normal if run outside of GreaseMonkey.", e);
  689. }
  690. // Inject config menu item
  691. injectMenuItem();
  692. // Check page we're on
  693. checkPage();
  694. // If the page is still null at this point, we didn't identify the page.
  695. if (currentPage === null) {
  696. window.console.warn("Did not identify a page to run on. Not executing any more page alterations.");
  697. return;
  698. }
  699. // Inject some css
  700. GM_addStyle('.ui-front { z-index: 1000 !important; }');
  701. // Modify the page
  702. modifyPage();
  703. };
  704. checkPage = function () {
  705. var key, found;
  706. if (debug) {
  707. window.console.log("Entering checkPage function");
  708. }
  709.  
  710. found = null;
  711. for (key in pageRegexes) {
  712. if (pageRegexes.hasOwnProperty(key)) {
  713. if (debug) {
  714. window.console.log("Trying to match " + pageRegexes[key] + " to " + window.location.pathname);
  715. }
  716. if (pageRegexes[key].exec(window.location.pathname)) {
  717. if (debug) {
  718. window.console.log("Match found for " + key);
  719. }
  720. found = key;
  721. break;
  722. }
  723. }
  724. }
  725. currentPage = found;
  726. };
  727. injectMenuItem = function () {
  728. var idx, links, li, menu, inject, injectLink;
  729. if (debug) {
  730. window.console.log("Entering injectMenuItem function");
  731. }
  732. // There are no id's used, so we'll hook on to some text contents in the page
  733. links = document.getElementsByTagName("a");
  734. for (idx = 0; idx < links.length; idx++) {
  735. if (links[idx].innerHTML === "Profiel wijzigen") {
  736. // Might have a match, verify "menu" element above
  737. li = links[idx].parentNode;
  738. menu = li.parentNode;
  739. if (menu.classList.contains("dropdown-menu")) {
  740. // We can assume safely that we're in a menu. Inject menu item
  741. inject = document.createElement("li");
  742. injectLink = document.createElement("a");
  743. injectLink.innerHTML = "SeriesFeed++ configureren";
  744. injectLink.addEventListener("click", configDialog.show, false);
  745. inject.appendChild(injectLink);
  746. menu.appendChild(inject);
  747. }
  748. }
  749. }
  750. };
  751. modifyPage = function () {
  752. if (debug) {
  753. window.console.log("Entering modifyPage function");
  754. }
  755. // Depending on the type of the page, we need to render differently
  756. switch (currentPage) {
  757. case "start":
  758. handleStartPage();
  759. break;
  760. case "broadcast":
  761. handleBroadcastPage();
  762. break;
  763. case "watch":
  764. handleWatchlistPage();
  765. break;
  766. case "season":
  767. handleSeasonPage();
  768. break;
  769. case "episode":
  770. handleEpisodePage();
  771. break;
  772. default:
  773. window.console.warn("Did not identify a page to run on. Not executing any more page alterations.");
  774. }
  775. // Append css for jquery UI
  776. $("head").append('<link href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css"' +
  777. 'rel="stylesheet" type="text/css">');
  778. };
  779. // Page specific modifications
  780. handleStartPage = function () {
  781. if (debug) {
  782. window.console.log("Entering handleStartPage function");
  783. }
  784. // There is one table of interest: latest favourites. As of 1.3 it can be missing if there's no episodes
  785. injectDefaultTable("favourite_episodes");
  786. };
  787. handleBroadcastPage = function () {
  788. if (debug) {
  789. window.console.log("Entering handleBroadcastPage function");
  790. }
  791. // Single table: broadcasted episodes
  792. injectDefaultTable("afleveringen");
  793. };
  794. handleWatchlistPage = function () {
  795. if (debug) {
  796. window.console.log("Entering handleWatchlistPage function");
  797. }
  798. // Single table: favourites/popular episodes
  799. injectDefaultTable("afleveringen");
  800. };
  801. handleSeasonPage = function () {
  802. var table, showName;
  803. if (debug) {
  804. window.console.log("Entering handleSeasonPage function");
  805. }
  806. // Get show name
  807. showName = document.getElementById('seriesName').value;
  808.  
  809. // Single table: show episodes
  810. table = $("#afleveringen");
  811. // Inject element for header
  812. injectTableHeader("afleveringen");
  813. // Inject icons in rows
  814. table.find("tbody tr.light").each(function (idx, elm) {
  815. var td, cells, showEpisode;
  816. if (debug) {
  817. window.console.log("Processing row " + idx);
  818. }
  819. td = document.createElement("td");
  820. cells = elm.getElementsByTagName("td");
  821. showEpisode = cells[0].firstElementChild.innerHTML;
  822. td.appendChild(createFunctionality(showName, showEpisode));
  823. elm.appendChild(td);
  824. });
  825. };
  826. handleEpisodePage = function () {
  827. var table, row, cell, data, showName, showEpisode;
  828.  
  829. if (debug) {
  830. window.console.log("Entering handleEpisodePage function");
  831. }
  832. // Need to inject new row instead of cell
  833. table = $("#episodeInfo");
  834. data = table.find("thead th").first().html().trim();
  835. data = data.match(/([A-Za-z ]*) \([0-9]{4}\) : (.*)/);
  836. showName = data[1];
  837. showEpisode = data[2];
  838. // Inject
  839. row = document.createElement('tr');
  840. cell = document.createElement('td');
  841. cell.innerHTML = 'SeriesFeed++';
  842. row.appendChild(cell);
  843. cell = document.createElement('td');
  844. cell.appendChild(createFunctionality(showName, showEpisode));
  845. row.appendChild(cell);
  846. table.find("tbody").append(row);
  847. };
  848. // General modification methods
  849. injectTableHeader = function (tableId) {
  850. var table, th;
  851. if (debug) {
  852. window.console.log("Entering injectTableHeader function");
  853. }
  854.  
  855. table = $("#" + tableId);
  856. // Inject element for header
  857. th = document.createElement("th");
  858. th.innerHTML = "SeriesFeed++";
  859. table.find("thead tr")[0].appendChild(th);
  860. };
  861. injectDefaultTable = function (tableId) {
  862. var table, colspan, readMore;
  863. if (debug) {
  864. window.console.log("Entering injectDefaultTable function");
  865. }
  866.  
  867. table = $("#" + tableId);
  868. // Check if element actually exists
  869. if (table.length === 0) {
  870. if (debug) {
  871. window.console.log("Did not find a table with the id: " + tableId);
  872. }
  873. return;
  874. }
  875. // Inject element for header
  876. injectTableHeader(tableId);
  877. // Inject icons in rows
  878. table.find("tbody tr").not('.readMore').each(function (idx, elm) {
  879. var td, cells, showName, showEpisode;
  880. if (debug) {
  881. window.console.log("Processing row" + idx);
  882. }
  883. td = document.createElement("td");
  884. cells = elm.getElementsByTagName("td");
  885. showName = cells[0].firstElementChild.innerHTML;
  886. showEpisode = cells[1].firstElementChild.innerHTML;
  887. td.appendChild(createFunctionality(showName, showEpisode));
  888. elm.appendChild(td);
  889. });
  890. readMore = table.find("tbody tr.readMore td");
  891. colspan = parseInt(readMore.attr("colspan"), 10);
  892. readMore.attr('colspan', colspan + 1);
  893. };
  894. createFunctionality = function (showName, showEpisode) {
  895. var span, languages, idx, downloadProviders, downloadTypes, downloadIcon;
  896. if(debug){
  897. console.log("Entering createFunctionality with parameters: showName: "+showName+", showEpisode: "+showEpisode)
  898. }
  899.  
  900. span = document.createElement("span");
  901. // Add language flags
  902. languages = configDialog.getEnabledSubtitleLanguages();
  903. for (idx = 0; idx < languages.length; idx++) {
  904. span.appendChild(createLanguageFlag(languages[idx], showName, showEpisode));
  905. span.appendChild(document.createTextNode(" "));
  906. }
  907. downloadProviders = configDialog.getEnabledDownloadProviders();
  908. downloadTypes = configDialog.getEnabledDownloadTypes();
  909. if (downloadProviders.length > 0 && downloadTypes.length > 0) {
  910. downloadIcon = document.createElement("i");
  911. downloadIcon.setAttribute('class','fa fa-download');
  912. downloadIcon.setAttribute('style', 'display:inline-block; font-size: 19px; cursor: pointer;');
  913. downloadIcon.title = "download episode";
  914. downloadIcon.addEventListener("click", showDlSelectionDialog);
  915.  
  916. span.appendChild(downloadIcon);
  917. }
  918. return span;
  919. };
  920. createLanguageFlag = function (lang, showName, showEpisode) {
  921. var result, img, subSources;
  922. if(debug){
  923. console.log("Entering createLanguageFlag with parameters: lang: "+lang+", showName: "+showName+", showEpisode: "+showEpisode)
  924. }
  925.  
  926. if (!flags.hasOwnProperty(lang)) {
  927. throw new Error(lang + "is not a recognized language flag!");
  928. }
  929.  
  930. img = document.createElement("img");
  931. img.src = flags[lang];
  932. img.alt = lang + " flag";
  933. img.title = languageMap[lang] + " subtitles";
  934. img.setAttribute("data-language", lang);
  935. img.setAttribute('style', 'height: 16px; vertical-align:top;');
  936. // If there's just one subtitle source, make it a link, otherwise make it a pop-up menu
  937. subSources = configDialog.getEnabledSubtitleSources();
  938. if (subSources.length > 1) {
  939. img.addEventListener("click", showSubSelectionDialog, false);
  940. result = img;
  941. } else {
  942. result = document.createElement("a");
  943. result.href = subSources[0].createLink(showName, showEpisode, lang);
  944. result.target = "_blank";
  945. result.appendChild(img);
  946. }
  947.  
  948. return result;
  949. };
  950. showSubSelectionDialog = function (e) {
  951. var evt, target, dialog, subSources, row, showName, showEpisode, lang, cells, idx, link, p, thead, data;
  952.  
  953. evt = e || window.event;
  954. target = evt.target || evt.srcElement;
  955.  
  956. // Get language
  957. lang = target.getAttribute("data-language");
  958. // Get row, so we can extract show name & episode
  959. row = target.parentNode.parentNode.parentNode;
  960. cells = row.getElementsByTagName("td");
  961. if(currentPage === "season"){
  962. showName = document.getElementById('seriesName').value;
  963. showEpisode = cells[0].firstElementChild.innerHTML;
  964. } else if(currentPage === "episode") {
  965. thead = row.parentNode.previousElementSibling;
  966. data = thead.firstElementChild.firstElementChild.innerHTML.trim();
  967. data = data.match(/([A-Za-z ]*) \([0-9]{4}\) : (.*)/);
  968. showName = data[1];
  969. showEpisode = data[2];
  970. } else {
  971. showName = cells[0].firstElementChild.innerHTML;
  972. showEpisode = cells[1].firstElementChild.innerHTML;
  973. }
  974.  
  975. // Build dialog
  976. dialog = document.createElement("div");
  977. p = document.createElement("p");
  978. p.innerHTML = "Show: " + showName + "<br/>Episode: " + showEpisode;
  979. dialog.appendChild(p);
  980. p = document.createElement("p");
  981. // Get sub source sites
  982. subSources = configDialog.getEnabledSubtitleSources();
  983. for (idx = 0; idx < subSources.length; idx++) {
  984. link = document.createElement("a");
  985. link.target = "_blank";
  986. link.href = subSources[idx].createLink(showName, showEpisode, lang);
  987. link.innerHTML = subSources[idx].title;
  988. link.setAttribute("style","text-decoration: underline;");
  989. link.addEventListener("click", function () {
  990. $(dialog).dialog("close");
  991. }, false);
  992. p.appendChild(link);
  993. p.appendChild(document.createTextNode(" "));
  994. }
  995. dialog.appendChild(p);
  996. $(dialog).dialog({
  997. title: "Download " + languageMap[lang] + " subtitles",
  998. position: { my: "right bottom", at: "top left", of: target }
  999. });
  1000. };
  1001. showDlSelectionDialog = function (e) {
  1002. var evt, target, dialog, row, cells, showName, showEpisode, p, downloadTypes, downloadProviders, idx, ul, li,
  1003. jdx, kdx, thead, data;
  1004.  
  1005. evt = e || window.event;
  1006. target = evt.target || evt.srcElement;
  1007.  
  1008. // Get row, so we can extract show name & episode
  1009. row = target.parentNode.parentNode.parentNode;
  1010. cells = row.getElementsByTagName("td");
  1011. if(currentPage === "season") {
  1012. showName = document.getElementById('seriesName').value;
  1013. showEpisode = cells[0].firstElementChild.innerHTML;
  1014. } else if(currentPage === "episode") {
  1015. thead = row.parentNode.previousElementSibling;
  1016. data = thead.firstElementChild.firstElementChild.innerHTML.trim();
  1017. data = data.match(/([A-Za-z ]*) \([0-9]{4}\) : (.*)/);
  1018. showName = data[1];
  1019. showEpisode = data[2];
  1020. } else {
  1021. showName = cells[0].firstElementChild.innerHTML;
  1022. showEpisode = cells[1].firstElementChild.innerHTML;
  1023. }
  1024.  
  1025. dialog = document.createElement("div");
  1026. p = document.createElement("p");
  1027. p.innerHTML = "Show: " + showName + "<br/>Episode: " + showEpisode;
  1028. dialog.appendChild(p);
  1029. p = document.createElement("p");
  1030. // Get types & sites
  1031. downloadProviders = configDialog.getEnabledDownloadProviders();
  1032. downloadTypes = configDialog.getEnabledDownloadTypes();
  1033. for (idx = 0; idx < downloadTypes.length; idx++) {
  1034. p = document.createElement("p");
  1035. p.appendChild(document.createTextNode(downloadTypes[idx]));
  1036. dialog.appendChild(p);
  1037. ul = document.createElement("ul");
  1038. for (jdx = 0; jdx < downloadProviders.length; jdx++) {
  1039. li = document.createElement("li");
  1040. li.appendChild(document.createTextNode(downloadProviders[jdx].title + ": "));
  1041. for (kdx = 0; kdx < downloadProviders[jdx].sites.length; kdx++) {
  1042. li.appendChild(downloadProviders[jdx].sites[kdx].createLink(showName, showEpisode, downloadTypes[idx], dialog));
  1043. li.appendChild(document.createTextNode(" "));
  1044. }
  1045. ul.appendChild(li);
  1046. }
  1047. dialog.appendChild(ul);
  1048. }
  1049.  
  1050. $(dialog).dialog({
  1051. title: "Download episode",
  1052. position: { my: "right bottom", at: "top left", of: target }
  1053. });
  1054. };
  1055. parseEpisode = function (showEpisode) {
  1056. var result, regex, match;
  1057. if (debug) {
  1058. window.console.log("Entering parseEpisode function");
  1059. }
  1060.  
  1061. result = {
  1062. season: 0,
  1063. episode: 0,
  1064. title: ""
  1065. };
  1066. regex = new RegExp("S([0-9]+)E([0-9]+) - (.+)");
  1067. // Epected format: SxEy - episode title
  1068. match = regex.exec(showEpisode);
  1069. if (match !== null) {
  1070. result.season = parseInt(match[1], 10);
  1071. result.episode = parseInt(match[2], 10);
  1072. result.title = match[3];
  1073. } else {
  1074. window.console.warn("Could not parse " + showEpisode + " correctly!");
  1075. }
  1076. return result;
  1077. };
  1078. createDownloadLink = function (name, href, dialog) {
  1079. var a, closeDialog;
  1080.  
  1081. closeDialog = function () {
  1082. $(dialog).dialog("close");
  1083. };
  1084. a = document.createElement("a");
  1085. a.href = href;
  1086. a.target = "_blank";
  1087. a.innerHTML = name;
  1088. a.setAttribute("style","text-decoration: underline;");
  1089. //a.addEventListener("click", closeDialog, false);
  1090. a.addEventListener("mouseup", closeDialog, false);
  1091.  
  1092. return a;
  1093. };
  1094. // Helper functions
  1095. formatToConvention = function (episodeData, episodeCharacter) {
  1096. episodeCharacter = episodeCharacter || "E";
  1097. return "S" + ((episodeData.season < 10) ? "0" : "") + episodeData.season + episodeCharacter +
  1098. ((episodeData.episode < 10) ? "0" : "") + episodeData.episode;
  1099. };
  1100.  
  1101. // Expose methods to the outside world
  1102. seriesFeedPlusPlus.main = main;
  1103.  
  1104. return seriesFeedPlusPlus;
  1105. }());
  1106.  
  1107. // Execute main
  1108. try {
  1109. seriesFeedPlusPlus.main();
  1110. } catch (e) {
  1111. console.log(e);
  1112. console.log(e.stack);
  1113. // Display error
  1114. var txt = "An error occurred while executing this script.\n\n";
  1115. txt += "Issue: <<<" + e.message + ">>>\n\n";
  1116. txt += "\nPlease report this back to the author (on the greasyfork website, or by sending me an email at mrinvisible@cryptolab.net) so it can be corrected.\n\n";
  1117. txt += "Click 'OK' to continue.\n\n";
  1118. window.alert(txt);
  1119. }