IMDb List Helper

Makes creating IMDb lists more efficient and convenient

当前为 2015-11-13 提交的版本,查看 最新版本

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