Greasy Fork 还支持 简体中文。

IMDB List Importer

Import list of titles or people in the imdb list

目前為 2019-08-07 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name IMDB List Importer
  3. // @namespace Neinei0k_imdb
  4. // @include https://www.imdb.com/list/*
  5. // @version 7.0
  6. // @grant GM.xmlHttpRequest
  7. // @description Import list of titles or people in the imdb list
  8. // ==/UserScript==
  9.  
  10. var o = {
  11.  
  12. init: function(e) {
  13. this.etext = e.querySelector('textarea');
  14. this.efile = e.querySelector('input[type="file"]');
  15. this.eready = e.children[10]; // DOM element with messages for the user.
  16. var checkboxes = e.querySelectorAll('input[type="checkbox"]');
  17. this.source = checkboxes[0];
  18. this.csv = checkboxes[1];
  19. this.unique = checkboxes[2];
  20. var hidden_element = document.querySelector('#main > input'); // Unknown hidden element. Data needs to be send with all requests.
  21. if (hidden_element == null) {
  22. this.log('e','Hidden element not found');
  23. } else {
  24. this.hidden_data = hidden_element.id + "=" + hidden_element.value;
  25. }
  26. },
  27.  
  28. run: function(event) {
  29. this.text = this.etext.value;
  30. if (this.source.checked) { // read data from file
  31. var file = this.efile.files[0];
  32. if (file !== undefined) {
  33. this.log("i","Reading file " + file.name);
  34. var r = new FileReader();
  35. r.onload = this.file_onload.bind(this);
  36. r.readAsText(file);
  37. } else {
  38. this.log("e","File is undefined");
  39. }
  40. } else { // read data from input element
  41. this.add_list(this.create_list());
  42. }
  43. },
  44.  
  45. file_onload: function(e) {
  46. if (e.target.error === null) {
  47. this.text = e.target.result;
  48. this.add_list(this.create_list());
  49. } else {
  50. this.log("e","File reading error: " + e.target.error);
  51. }
  52. },
  53.  
  54. log: function(level,msg) {
  55. var l = "";
  56. switch (level) {
  57. case 'i': l = "Info: "; break;
  58. case 'w': l = "Warning: "; break;
  59. case 'e': l = "Error: "; break;
  60. }
  61. if (l.length !== 0)
  62. console.log("IMDB List Importer: " + l + msg);
  63. if (level == "n" || level == "e")
  64. this.eready.innerText = msg;
  65. },
  66.  
  67. create_list: function() {
  68. var re;
  69. // Find type of the list
  70. /*if (document.querySelector('[data-type="Characters"]') !== null) {
  71. this.log("i", "List type: characters");
  72. re = "ch";
  73. } else*/
  74. if (document.querySelector('[data-type="People"]') !== null) {
  75. this.log("i", "List type: people");
  76. re = "nm";
  77. } else if (document.querySelector('[data-type="Titles"]') !== null) {
  78. this.log("i", "List type: titles");
  79. re = "tt";
  80. } else {
  81. this.log("e","Could not determine type of the list");
  82. return [];
  83. }
  84. re += "[0-9]{7}";
  85.  
  86. if (this.csv.checked) {
  87. return this.read_csv(re);
  88. } else {
  89. re = new RegExp(re);
  90. var list = [];
  91. var e;
  92. var text = this.text;
  93. while ((e = re.exec(text)) !== null) {
  94. var flag = '';
  95. if (this.unique.checked) flag = 'g';
  96. text = text.replace(new RegExp(e[0], flag), '');
  97. list.push({const: e[0], description: ""});
  98. }
  99. return list;
  100. }
  101. },
  102.  
  103. read_csv: function(re) {
  104. re = new RegExp("^" + re + "$");
  105. var list = [];
  106. // Parse csv
  107. var text = this.text.split('\n'); // Separate by lines
  108. this.text = null; // Variable may have lots of data which is no longer needed
  109. var parsed_text = [];
  110. for (var i in text) { // For each line
  111. if (text[i].trim().length === 0) { // Ignore empty lines including lines with only white space characters
  112. continue;
  113. }
  114. var state = 0; // 0 - outside of double quotes (comma character is the separator), 1 - inside double quotes (comma character is part of a field)
  115. var parsed_line = [""];
  116. for (var j in text[i]) {
  117. if (state == 0 && text[i][j] == ',') {
  118. parsed_line.push("");
  119. } else if (text[i][j] == '"') {
  120. state = (state + 1) % 2;
  121. } else {
  122. parsed_line[parsed_line.length-1] += text[i][j];
  123. }
  124. }
  125. parsed_text.push(parsed_line);
  126. }
  127. text = parsed_text;
  128. // console.log(text); // print parsed data
  129.  
  130. // Find const and description field numbers.
  131. try {
  132. if (text.length < 2) { // There must be at least 2 rows in the data
  133. throw "No data";
  134. }
  135. var fl = text[0];
  136. var fll = fl.length;
  137. var const_field = fl.indexOf('const');
  138. if (const_field === -1) {
  139. const_field = fl.indexOf('Const');
  140. if (const_field === -1) {
  141. throw "Field 'const' not found.";
  142. }
  143. }
  144. } catch (err) {
  145. this.log("e","Input line 1: " + err);
  146. return [];
  147. }
  148. var desc_field = fl.indexOf('description');
  149. if (desc_field === -1) {
  150. desc_field = fl.indexOf('Description');
  151. }
  152. this.log("i","Found csv file fields const(" + const_field +
  153. ") and description(" + desc_field + ")");
  154. text.shift();
  155.  
  156. // Add elements to the list
  157. for (var i = 0; i < text.length; i++) {
  158. if (text[i].length === 0)
  159. continue;
  160. try {
  161. fl = text[i];
  162. if (fll !== fl.length) throw "Invalid number of fields.";
  163. if (re.exec(fl[const_field]) === null) throw "Invalid 'const' field.";
  164. } catch (err) {
  165. this.log("e","Input line " + (i+2) + ": " + err);
  166. return [];
  167. }
  168. if (this.unique.checked) {
  169. var exists = list.findIndex(function(v){
  170. return v.const === fl[const_field];
  171. });
  172. if (exists !== -1) continue;
  173. }
  174. list.push({const: fl[const_field],description: (desc_field == -1 ? "" : fl[desc_field])});
  175. }
  176. // console.log(list); // Print final list
  177. return list;
  178. },
  179. add_list: function(list) {
  180. if (list.length === 0)
  181. return;
  182.  
  183. var msg = "Elements to add: ";
  184. for (var i in list)
  185. msg += list[i].const + ",";
  186. this.log("i",msg);
  187.  
  188. var l = {};
  189. l.list = list;
  190. l.ready = 0;
  191. l.list_id = /ls[0-9]{1,}/.exec(location.href)[0];
  192. this.sendNext(l);
  193. },
  194.  
  195. sendNext: function(l) {
  196. this.log("i",'Add element ' + l.ready + ': ' + l.list[l.ready].const);
  197. this.send_request(this.check_item, l, 'https://www.imdb.com/list/' + l.list_id + '/' + l.list[l.ready].const + '/add', this.hidden_data);
  198. },
  199.  
  200. send_request: function(f,l,u,d) {
  201. GM.xmlHttpRequest({
  202. method: "POST",
  203. url: u,
  204. data: d,
  205. headers: {
  206. "Content-Type": "application/x-www-form-urlencoded"
  207. },
  208. onreadystatechange: f.bind(this,l)
  209. });
  210. /*var x = new XMLHttpRequest();
  211. x.onreadystatechange = f.bind(this,l);
  212. x.open('POST', u, true);
  213. x.setRequestHeader('Content-Type',
  214. 'application/x-www-form-urlencoded');
  215. x.send(d);*/
  216. },
  217.  
  218. check_item: function(l, e) {
  219. this.log("i","Add element(" + l.list[l.ready].const +
  220. ") request: readyState(" + e.readyState +
  221. "), status(" + e.status + ")");
  222. if (e.readyState == 4 && e.status == 200) {
  223. if (l.list[l.ready].description.length !== 0) {
  224. this.send_request(this.check_item_desc, l, 'https://www.imdb.com/list/' + l.list_id + '/edit/itemdescription',
  225. 'newDescription=' + l.list[l.ready].description +
  226. '&listItem=' + JSON.parse(e.responseText).list_item_id + '&' + this.hidden_data);
  227. } else {
  228. this.showReady(l);
  229. }
  230. }
  231. },
  232.  
  233. check_item_desc: function(l,e) {
  234. this.log("i","Add element(" + l.list[l.ready].const +
  235. ") description request: readyState(" + e.readyState +
  236. "), status(" + e.status + ")");
  237. if (e.readyState == 4 && e.status == 200) {
  238. this.showReady(l);
  239. }
  240. },
  241.  
  242. showReady: function(l) {
  243. l.ready += 1;
  244. this.log("n",'Ready ' + l.ready + ' of ' + l.list.length + '.');
  245. if (l.ready == l.list.length) {
  246. location.reload();
  247. } else {
  248. this.sendNext(l);
  249. }
  250. },
  251.  
  252. change: function(e) {
  253. var s = e.target.checked;
  254. this.etext.disabled = s;
  255. this.efile.disabled = !s;
  256. },
  257.  
  258. };
  259.  
  260. var c = window.File && window.FileReader && window.FileList && window.Blob; // Check support of File API
  261. var div = document.createElement('div');
  262. div.setAttribute('class', 'search-bar');
  263. div.style.height = "initial"
  264. var s = '<textarea style="background-color: white; width: 100%; height: 100px; overflow: initial"></textarea><br>';
  265. if (c) {
  266. s += '<input type="file" disabled><br>';
  267. s += '<label>';
  268. s += '<input type="checkbox" style="width: initial;">';
  269. s += '<span style="font-weight: normal;">';
  270. s += 'Import from file (otherwise import from text)';
  271. s += '</span>';
  272. s += '</label><br>';
  273. } else {
  274. s += '<span style="font-weight: normal;">';
  275. s += 'Looks like your browser does not support File API for reading local files.';
  276. s += '</span><br>';
  277. }
  278. s += '<label>';
  279. s += '<input type="checkbox" checked style="width: initial;">';
  280. s += '<span style="font-weight: normal;">Data from .csv file</span>';
  281. s += '</label><br>';
  282. s += '<label>';
  283. s += '<input type="checkbox" style="width: initial;">';
  284. s += '<span style="font-weight: normal;">Add only unique elements</span>';
  285. s += '</label><br>';
  286. s += '<div>Set-up parameters. Insert text or choose file. Press \'Import List\' button.</div>';
  287. s += '<button class="btn">Import List</button>';
  288. div.innerHTML = s;
  289.  
  290. o.init(div);
  291. div.querySelector('button').addEventListener('click',o.run.bind(o),false);
  292. if (c) {
  293. o.source.addEventListener('change',o.change.bind(o),false);
  294. }
  295.  
  296. var list_edit = document.querySelector('.lister-search');
  297. list_edit.appendChild(div);