IMDb List Helper

Makes creating IMDb lists more efficient and convenient

当前为 2016-12-11 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name IMDb List Helper
  3. // @namespace imdb
  4. // @description Makes creating IMDb lists more efficient and convenient
  5. // @version 2.3.0
  6. // @include http://*imdb.com/list/edit*
  7. // @require https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.71/jquery.csv-0.71.min.js
  8. // ==/UserScript==
  9.  
  10. var jQuery = unsafeWindow.jQuery;
  11. var $ = jQuery;
  12.  
  13. //
  14. // CHANGELOG
  15. //
  16. // 2.3.0
  17. // bugfix: importing ratings works again
  18. //
  19. // 2.2.0
  20. // added: support for people
  21. //
  22. // 2.1.1
  23. // added: only show import form if ratings is selected
  24. //
  25. // 2.1
  26. // added: importers for imdb, rateyourmusic, criticker
  27. //
  28. // 2.0
  29. // added: import ratings
  30. // added: if regex doesn't match, skip entry
  31. //
  32. // 1.6.1.2
  33. // added: input text suggestion as a placeholder
  34. //
  35. // 1.6.1.1
  36. // fixed: some entries are skipped when adding imdb ids/urls
  37. //
  38.  
  39. //
  40. // milliseconds between each request
  41. //
  42. var REQUEST_DELAY = 1000;
  43.  
  44. function processImdb(file) {
  45. var csv_lines = file.split("\n");
  46. for(var i = 1; i < csv_lines.length; ++i) {
  47. try {
  48. var data = jQuery.csv.toArray(csv_lines[i]);
  49. var rating = data[8];
  50. if(rating === "") {
  51. continue;
  52. }
  53. $("#filmList").append(rating + "," + data[1] + "\n");
  54. }
  55. catch(e) {
  56. console.log("Exception: " + e);
  57. console.log("Bad line: " + csv_lines[i]);
  58. }
  59. }
  60. }
  61.  
  62. function processRym(file) {
  63. var csv_lines = file.split("\n");
  64. for(var i = 1; i < csv_lines.length; ++i) {
  65. try {
  66. var data = jQuery.csv.toArray(csv_lines[i]);
  67. if(data.length < 6) {
  68. continue;
  69. }
  70. $("#filmList").append(data[4] + "," + data[1] + "\n");
  71. }
  72. catch(e) {
  73. console.log("Exception: " + e);
  74. console.log("Bad line: " + csv_lines[i]);
  75. }
  76. }
  77. }
  78.  
  79. function processCriticker(file) {
  80. var lines = file.split("\n");
  81. for(var i = 1; i < lines.length; ++i) {
  82. var line = lines[i];
  83. var m = line.match(/([1-9]{1}|10)([0-9])?\t(.+)/);
  84. if(m === null) {
  85. continue;
  86. }
  87. var rating = m[1];
  88. var second = m[2];
  89. if(second === undefined) {
  90. // If second value is undefined the rating is < 10 (on criticker)
  91. // and will be changed to 1 because you can't rate 0 on IMDb
  92. rating = "1";
  93. }
  94. $("#filmList").append(rating + "," + m[3] + "\n");
  95. }
  96. }
  97.  
  98. function handleImport(evt) {
  99. var files = evt.target.files;
  100. var file = files[0];
  101. var reader = new FileReader();
  102. reader.onload = function(event){
  103. var file = event.target.result;
  104. var format = $("select[name=import]").val();
  105. if(format === "none") {
  106. alert("Select importer and try again.");
  107. return;
  108. }
  109. else if(format === "imdb") {
  110. processImdb(file);
  111. }
  112. else if(format === "rym") {
  113. processRym(file);
  114. }
  115. else if(format === "criticker") {
  116. processCriticker(file);
  117. }
  118. }
  119.  
  120. reader.readAsText(file);
  121. }
  122.  
  123. var ListManager = {
  124. regex: "^(.*)$",
  125. processRegex: function(rx, cb) {
  126. var filmTitle = rx[1];
  127. cb(filmTitle);
  128. },
  129. handleSelection: function(imdbId, cb) {
  130. cb();
  131. }
  132. };
  133.  
  134. var RatingManager = {
  135. rating: 0,
  136. regex: "^([1-9]{1}|10),(.*)$",
  137. processRegex: function(rx, cb) {
  138. RatingManager.rating = rx[1];
  139. var filmTitle = rx[2];
  140. cb(filmTitle);
  141. },
  142. handleSelection: function(imdbId, cb) {
  143. console.log("RatingManager::handleSelection: Rating " + imdbId);
  144. $.get("http://www.imdb.com/title/" + imdbId, function(data) {
  145. var authHash = $(data).find("#star-rating-widget").data("auth");
  146. var params = {tconst: imdbId, rating: RatingManager.rating,
  147. auth: authHash, tracking_tag: "list",
  148. pageId: imdbId, pageType: "title", subPageType: "main"};
  149. $.post("http://www.imdb.com/ratings/_ajax/title", params, function(data) {
  150. if(data.status !== 200) {
  151. alert("Rating failed. Status code " + data.status);
  152. }
  153. else {
  154. cb();
  155. }
  156. }, "json");
  157. });
  158. }
  159. };
  160.  
  161. var App = {
  162. manager: ListManager,
  163. films: [],
  164. regexObj: null,
  165. isEmpty: function() {
  166. return App.films.length === 0;
  167. },
  168. next: function() {
  169. return App.films.shift();
  170. },
  171. run: function() {
  172. var textEdit = '<div id="imdbListTextEdit" style="'
  173. + 'padding: 10px; border: 1px solid #e8e8e8">'
  174. + '<p><b>Import mode:</b><input type="radio" name="importmode" value="list" checked="checked">List</input>'
  175. + '<input type="radio" name="importmode" value="ratings">Ratings</input></p>'
  176. + '<textarea id="filmList" rows="7" cols="60" placeholder="Input titles, IMDb URLs, and IDs here and click Start"></textarea><br />'
  177. + '<input type="button" id="doList" value="Start" /> '
  178. + '<input type="button" id="skipFilm" value="Skip" /> '
  179. + '<input type="button" id="retryPost" value="Retry" /> '
  180. + '<span style="font-weight: bold">Remaining: <span id="filmsRemaining">0</span></span><br /><br />'
  181. + '<span style="font-weight: bold;">Current: <input type="text" id="filmCurrent" size="65" style="font-family: monospace" /></span><br />'
  182. + '<span style="font-weight: bold;">Regexp: <input type="text" value="" id="filmRegexp" size="65" style="font-family: monospace; margin-top: 4px; margin-left: 1px" /></span><br />'
  183. + '<p id="importform" style="display: none"><b>Import from:</b> <select name="import"><option value="none">Select</option><option value="imdb">IMDb</option>'
  184. + '<option value="rym">RateYourMusic</option><option value="criticker">Criticker</option></select><b> File: </b><input type="file" id="fileimport"></p>'
  185. + '</div>';
  186. $("div#main > div.list_edit > div.add").after(textEdit);
  187. $("#filmRegexp").val(App.manager.regex);
  188. $("#fileimport").on("change", handleImport);
  189. $("#imdbListTextEdit").on("change", "input[name=importmode]", function() {
  190. var value = $(this).val();
  191. if(value === "list") {
  192. App.manager = ListManager;
  193. $("#importform").hide();
  194. }
  195. else {
  196. App.manager = RatingManager;
  197. $("#importform").show();
  198. }
  199. $("#filmRegexp").val(App.manager.regex);
  200. });
  201. // When start button is clicked
  202. $("#imdbListTextEdit").on("click", "#doList", function(e) {
  203. $regexBox = $("#filmRegexp");
  204. if($regexBox.val()) {
  205. App.regexObj = RegExp($regexBox.val());
  206. }
  207. else {
  208. App.regexObj = RegExp(App.manager.regex);
  209. }
  210. // Disable the text area and the button and the regexp box
  211. // as well as the import mode
  212. $filmList = $("#filmList");
  213. $filmList.attr("disabled", "disabled");
  214. $regexBox.attr("disabled", "disabled");
  215. $("#doList").attr("disabled", "disabled");
  216. $("input[name=importmode]").attr("disabled", "disabled");
  217. App.films = $filmList.val().split("\n");
  218. App.handleNext();
  219. });
  220. // when skip button is clicked
  221. $("#imdbListTextEdit").on("click", "#skipFilm", function(e) {
  222. App.handleNext();
  223. });
  224. // Sometimes the request fails forcing the user to skip an entry to continue
  225. $("#imdbListTextEdit").on("click", "#retryPost", function(e) {
  226. $("button.search").trigger("click");
  227. });
  228. },
  229. reset: function() {
  230. App.films = [];
  231. App.regexObj = null;
  232. $("#filmList").removeAttr("disabled");
  233. $("#filmRegexp").removeAttr("disabled"); // leave regex
  234. $("#doList").removeAttr("disabled");
  235. $("input[name=importmode]").removeAttr("disabled");
  236. $("#filmCurrent").val("");
  237. $("input[name=add]", "div.add").val("");
  238. $("div.results", "div.add").html("");
  239. },
  240. search: function(filmTitle) {
  241. // remove unnecessary whitespace
  242. filmTitle = $.trim(filmTitle);
  243. // set current text to what we're searching
  244. $("#filmCurrent").val(filmTitle);
  245. // remove the first title from the text box and set the remaining number
  246. $filmList = $("#filmList");
  247. var newList = $filmList.val().split("\n");
  248. $("#filmsRemaining").text(newList.length-1);
  249. $filmList.val(newList.slice(1).join("\n"));
  250. // Run regex if it matches and let the manager process the result
  251. var result = App.regexObj.exec(filmTitle);
  252. if(result !== null) {
  253. App.manager.processRegex(result, function(filmTitle) {
  254. // Set imdb search input field to film title
  255. $("input[name=add]", "div.add").val(filmTitle);
  256. // Trigger search button click
  257. $("button.search").trigger("click");
  258. });
  259. }
  260. else {
  261. App.handleNext();
  262. }
  263. },
  264. handleNext: function() {
  265. if(!App.isEmpty()) {
  266. // if there's more items, search next...
  267. App.search(App.next());
  268. }
  269. else {
  270. // if last film
  271. App.reset();
  272. }
  273. }
  274. };
  275.  
  276. $(document).ready(App.run);
  277.  
  278. // When a search result item is clicked by user or script
  279. $("div.results").on("click", "li", function(e) {
  280. App.manager.handleSelection($(this).attr("id"), function() {
  281. App.handleNext();
  282. });
  283. });
  284.  
  285. // Monitors for changes to the search result box
  286. // If it recognizes an IMDBb URL/ID, it's clicked automatically
  287. // since there's only one result
  288. $("div.results").bind("DOMNodeInserted", function(e) {
  289. if($("#filmCurrent").val().match(/([CHMNTchmnt]{2}[0-9]{7})/) !== null
  290. && $("div.results").find("li").length) {
  291. setTimeout(function() {
  292. $("li", "div.results").first()[0].click();
  293. }, REQUEST_DELAY);
  294. }
  295. });