IMDb List Helper

Makes creating IMDb lists more efficient and convenient

当前为 2016-01-17 提交的版本,查看 最新版本

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