RED (+ NWCD, Orpheus) Upload Assistant

Script fills in as much accurately the upload and group edit forms based on foobar2000's playlist selection via pasted output of copy command, release consistency check, two tracklist layouts, basic colours customization, featured artists extraction, image URl fetching from store and more...

当前为 2019-08-31 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name RED (+ NWCD, Orpheus) Upload Assistant
  3. // @namespace https://greasyfork.org/cs/users/321857-anakunda
  4. // @version 1.722
  5. // @description Script fills in as much accurately the upload and group edit forms based on foobar2000's playlist selection via pasted output of copy command, release consistency check, two tracklist layouts, basic colours customization, featured artists extraction, image URl fetching from store and more...
  6. // @author Anakunda
  7. // @iconURL https://redacted.ch/favicon.ico
  8. // @match https://redacted.ch/upload.php*
  9. // @match https://redacted.ch/torrents.php?action=editgroup*
  10. // @match https://notwhat.cd/upload.php*
  11. // @match https://notwhat.cd/torrents.php?action=editgroup*
  12. // @match https://orpheus.network/upload.php*
  13. // @match https://orpheus.network/torrents.php?action=editgroup*
  14. // @connect file://*
  15. // @connect *
  16. // @grant GM_xmlhttpRequest
  17. // @grant GM_getValue
  18. // @grant GM_setValue
  19. // @grant GM_deleteValue
  20. // @grant GM_log
  21. // ==/UserScript==
  22.  
  23. // The pattern for built-in copy command or custom Text Tools quick copy command, which is handled by this helper is:
  24. // $fix_eol(%album artist%,)$char(30)$fix_eol(%album%,)$char(30)[$if3(%date%,%ORIGINAL RELEASE DATE%,%year%)]$char(30)[$if3(%releasedate%,%retail date%,%date%,%year%)]$char(30)[$fix_eol($if2(%label%,%publisher%),)]$char(30)[$fix_eol($if3(%catalog%,%CATALOGNUMBER%,%catalog #%,%barcode%,%UPC%,%EAN%),)]$char(30)%__encoding%$char(30)%__codec%$char(30)[%__codec_profile%]$char(30)[%__bitrate%]$char(30)[%__bitspersample%]$char(30)[%__samplerate%]$char(30)[%__channels%]$char(30)[$if3(%media%,%discogs_format%,%source%)]$char(30)[$fix_eol(%genre%,)]['; '$fix_eol(%style%,)]$char(30)[$num(%discnumber%,0)]$char(30)[$num(%totaldiscs%,0)]$char(30)[$fix_eol(%discsubtitle%,)]$char(30)[%track number%]$char(30)[$num(%totaltracks%,0)]$char(30)$fix_eol(%title%,)$char(30)[$fix_eol(%track artist%,)]$char(30)[$if($strcmp($fix_eol(%performer%,),$fix_eol(%artist%,)),,$fix_eol(%performer%,))]$char(30)[$fix_eol(%composer%,)]$char(30)[$fix_eol(%conductor%,)]$char(30)[$fix_eol(%remixer%,)]$char(30)[$fix_eol(%compiler%,)]$char(30)[$fix_eol($if2(%producer%,%producedby%),)]$char(30)%length_seconds_fp%$char(30)[%replaygain_album_gain%]$char(30)[%album dynamic range%]$char(30)[%__tool%][ | %ENCODER%][ | %ENCODER_OPTIONS%]$char(30)[$fix_eol($if2(%url%,'https://www.discogs.com/release/'%discogs_release_id%),)]$char(30)$directory_path(%path%)$char(30)[$replace($replace(%comment%,$char(13),$char(29)),$char(10),$char(28))]
  25.  
  26. 'use strict';
  27.  
  28. var prefs = {
  29. set: function(prop, def) { this[prop] = GM_getValue(prop, def) },
  30. save: function() {
  31. for (var iter in this) { if (typeof this[iter] != 'function') GM_setValue(iter, this[iter]) }
  32. },
  33. };
  34. prefs.set('remap_texttools_newlines', 0); // convert underscores to linebreaks (ambiguous)
  35. prefs.set('clean_on_apply', 0); // clean the input box on successfull fill
  36. prefs.set('keep_meaningles_composers', 0); // keep composers from file tags also for non-composer emphasis works
  37. prefs.set('always_hide_dnu_list', 0); // risky!
  38. prefs.set('single_threshold', 8 * 60); // Max length of single in s
  39. prefs.set('EP_threshold', 28 * 60); // Max time of EP in s
  40. // tracklist specific
  41. prefs.set('tracklist_style', 1); // 1: classical, 2: propertional right aligned
  42. prefs.set('max_tracklist_width', 80); // right margin of the right aligned tracklist. should not exceed the group description width on any device
  43. prefs.set('title_separator', '. '); // divisor of track# and title
  44. prefs.set('tracklist_head_color', '#4682B4');
  45. prefs.set('tracklist_single_color', '#708080');
  46. // classical tracklist only components colouring
  47. prefs.set('tracklist_discsubtitle_color', '#008B8B');
  48. prefs.set('tracklist_classicalblock_color', 'Olive');
  49. prefs.set('tracklist_tracknumber_color', '#8899AA');
  50. prefs.set('tracklist_artist_color', '#889B2F');
  51. prefs.set('tracklist_composer_color', '#556B2F');
  52. prefs.set('tracklist_duration_color', '#4682B4');
  53.  
  54. var iter, rows = [], ref, tbl, elem, child, tb, warnings;
  55. if (document.URL.search(/\/upload\.php\b/) >= 0) {
  56. ref = document.querySelector('form#upload_table > div#dynamic_form');
  57. if (ref == null) return;
  58. common1();
  59. let x = [];
  60. x.push(document.createElement('tr'));
  61. x[0].style.verticalAlign = 'middle';
  62. child = document.createElement('input');
  63. child.id = 'fill-from-text';
  64. child.value = 'Fill from text (overwrite)';
  65. child.type = 'button';
  66. child.style.width = '13em';
  67. child.addEventListener("click", fill_from_text, false);
  68. x[0].append(child);
  69. elem.append(x[0]);
  70. x.push(document.createElement('tr'));
  71. x[1].style.verticalAlign = 'middle';
  72. child = document.createElement('input');
  73. child.id = 'fill-from-text-weak';
  74. child.value = 'Fill from text (keep values)';
  75. child.type = 'button';
  76. child.style.width = '13em';
  77. child.addEventListener("click", fill_from_text, false);
  78. x[1].append(child);
  79. elem.append(x[1]);
  80. common2();
  81. } else if (document.URL.indexOf('/torrents.php?action=editgroup') >= 0) {
  82. ref = document.querySelector('form.edit_form > div > div > input[type="submit"]');
  83. if (ref == null) return;
  84. ref.parentNode.insertBefore(document.createElement('br'), ref);
  85. common1();
  86. child = document.createElement('input');
  87. child.id = 'append-from-text';
  88. child.value = 'Fill from text (append)';
  89. child.type = 'button';
  90. child.addEventListener("click", fill_from_text, false);
  91. elem.append(child);
  92. common2();
  93. }
  94.  
  95. function common1() {
  96. tbl = document.createElement('tr');
  97. elem = document.createElement('td');
  98. child = document.createElement('textarea');
  99. child.id = 'import_data';
  100. child.name = 'import_data';
  101. child.cols = 50;
  102. child.rows = 3;
  103. child.style.width = '610px';
  104. child.style.height = '3em';
  105. child.className = ' wbbarea';
  106. child.setAttribute('data-wbb', '');
  107. elem.append(child);
  108. tbl.append(elem);
  109. elem = document.createElement('td');
  110. elem.align = 'right';
  111. }
  112. function common2() {
  113. tbl.append(elem);
  114. tb = document.createElement('tbody');
  115. tb.append(tbl);
  116. tbl = document.createElement('table');
  117. tbl.id = 'upload assistant';
  118. tbl.cellPadding = 3;
  119. tbl.cellSpacing = '';
  120. tbl.className = 'layout border';
  121. tbl.border = 0;
  122. tbl.width = '100%';
  123. tbl.append(tb);
  124. ref.parentNode.insertBefore(tbl, ref);
  125. }
  126.  
  127. // Hide DNU list (warning - risky!)
  128. //if ((ref = document.querySelector('div#content > div:first-of-type')) != null) ref.style.display = 'none';
  129.  
  130. class TagManager extends Array {
  131. constructor() {
  132. super();
  133. this.substitutions = [
  134. [/^Alternative\s*&\s*Indie$/i, 'alternative', 'indie'],
  135. [/^Pop and Rock$/i, 'pop', 'rock'],
  136. [/^Rock and Pop$/i, 'pop', 'rock'],
  137. [/^World\s*&\s*Country$/i, 'world.music', 'country'],
  138. [/^Jazz Fusion\s*&\s*Jazz Rock$/i, 'jazz.fusion', 'jazz rock'],
  139. [/^(?:Singer\s*&\s*)?Songwriter$/i, 'singer.songwriter'],
  140. [/^Singer and Songwriter$/i, 'singer.songwriter'],
  141. [/^R\s*&\s*B$/i, 'rhytm.and.blues'],
  142. [/^Rock\s*(?:[\-\/]\s*)?Pop$/i, 'pop.rock'],
  143. [/^(?:film )?soundtracks?$/i, 'score'],
  144. [/^electro$/i, 'electronic'],
  145. [/^metal$/i, 'heavy.metal'],
  146. [/^nonfiction$/i, 'non.fiction'],
  147. ];
  148. }
  149.  
  150. add(...tags) {
  151. var added = 0;
  152. for (var tag of tags) {
  153. if (typeof tag != 'string') continue;
  154. tag.split(/\s*[\,\/\;\>\|]+\s*/).forEach(function(tag) {
  155. tag = tag.normalize("NFD").
  156. replace(/[\u0300-\u036f]/g, '').
  157. replace(/\(.*?\)|\[.*?\]|\{.*?\}/g, '').
  158. trim();
  159. if (tag.length <= 0 || tag == '?') return null;
  160. for (var k of this.substitutions) {
  161. if (k[0].test(tag)) { added += this.add(...k.slice(1)); return; }
  162. }
  163. tag = tag.
  164. replace(/\s*[\'\’\`][Nn](?:\s+|[\'\’\`]\s*)/, ' and ').
  165. replace(/\s*[&\+]\s*/g, ' and ').
  166. replace(/[\!\@\#\$\%\^\*\?\<\"\[\{\]\}\=]+/g, '').
  167. replace(/[\s\-\_\.\'\`\~]+/g, '.').
  168. toLowerCase();
  169. if (tag.length >= 2 && !this.includes(tag)) {
  170. this.push(tag);
  171. ++added;
  172. }
  173. }.bind(this));
  174. }
  175. return added;
  176. }
  177. toString() {
  178. return this.length > 0 ? this.sort().join(', ') : '';
  179. }
  180. };
  181.  
  182. function fill_from_text() {
  183. var overwrite = this.id != 'fill-from-text-weak';
  184. var clipBoard = document.getElementById('import_data');
  185. if (clipBoard == null) return false;
  186. //let promise = clientInformation.clipboard.readText().then(text => clipBoard = text);
  187. //if (typeof clipBoard != 'string') return false;
  188. var category = document.getElementById('categories');
  189. var ref, iter, i, matches, rx;
  190. if ((warnings = document.getElementById('UA_warnings')) != null) warnings.parentNode.removeChild(warnings);
  191. if (category == null && document.getElementById('releasetype') != null
  192. || category != null && category.value == 0) return fill_from_text_music();
  193. if (category != null && category.value == 1) return fill_from_text_apps();
  194. if (category != null && (category.value == 2 || category.value == 3)) return fill_from_text_books();
  195. return category == null ? fill_from_text_apps() || fill_from_text_books() : false;
  196.  
  197. function fill_from_text_music() {
  198. const div = ['—', '⸺', '⸻'];
  199. var lines = clipBoard.value.split(/[\r\n]+/);
  200. var track, tracks = [];
  201.  
  202. for (iter of lines) {
  203. let metaData = iter.split('\x1E');
  204. track = {
  205. artist: metaData.shift().trim() || null,
  206. album: metaData.shift().trim() || null,
  207. album_year: extract_year(metaData.shift().trim()),
  208. release_year: extract_year(metaData.shift().trim()),
  209. label: metaData.shift().trim() || null,
  210. catalog: metaData.shift().trim() || null,
  211. encoding: metaData.shift().trim() || null,
  212. codec: metaData.shift().trim() || null,
  213. codec_profile: metaData.shift().trim() || null,
  214. bitrate: parseFloat(metaData.shift().trim()) || null,
  215. bd: parseInt(metaData.shift().trim()) || null,
  216. sr: parseInt(metaData.shift().trim()) || null,
  217. channels: parseInt(metaData.shift().trim()) || null,
  218. media: metaData.shift().trim() || null,
  219. genre: metaData.shift().trim() || null,
  220. discnumber: parseInt(metaData.shift().trim()) || null,
  221. totaldiscs: parseInt(metaData.shift().trim()) || null,
  222. discsubtitle: metaData.shift().trim() || null,
  223. tracknumber: metaData.shift().trim() || null,
  224. totaltracks: parseInt(metaData.shift().trim()) || null,
  225. title: metaData.shift().trim() || null,
  226. track_artist: metaData.shift().trim() || null,
  227. performer: metaData.shift().trim() || null,
  228. composer: metaData.shift().trim() || null,
  229. conductor: metaData.shift().trim() || null,
  230. remixer: metaData.shift().trim() || null,
  231. compiler: metaData.shift().trim() || null,
  232. producer: metaData.shift().trim() || null,
  233. duration: parseFloat(metaData.shift().trim()) || null,
  234. rg: metaData.shift().trim() || null,
  235. dr: metaData.shift().trim() || null,
  236. vendor: metaData.shift().trim() || null,
  237. url: metaData.shift().trim() || null,
  238. dirpath: metaData.shift() || null,
  239. comment: metaData.shift().trim() || null,
  240. };
  241. if (track.comment == '.') track.comment = undefined;
  242. if (track.comment) {
  243. track.comment = track.comment.replace(/\x1D/g, '\r').replace(/\x1C/g, '\n');
  244. if (prefs.remap_texttools_newlines) track.comment = track.comment.replace(/__/g, '\r\n').replace(/_/g, '\n') // ambiguous
  245. }
  246. if (track.dr != null) track.dr = parseInt(track.dr); // DR0
  247. tracks.push(track);
  248. }
  249. var album_artists = [], albums = [], album_years = [], release_years = [], labels = [], catalogs = [];
  250. var codecs = [], bds = [], medias = [], genres = [], srs = {}, urls = [], comments = [], track_artists = [];
  251. var encodings = [], bitrates = [], codec_profiles = [], drs = [], channels = [], rgs = [], dirpaths = [];
  252. var vendors = [];
  253. let is_va = false, composer_significant = false, is_from_dsd = false, is_classical = false;
  254. var total_time = 0, release_type = 1, album_bitrate = 0, totaldiscs = 1, artist_counter = 0;
  255. var edition_title, media, yadg_prefil = '';
  256. const featParser1 = /\(feat(?:\.|uring)\s+([^\(\)]+?)\s*\)$/i;
  257. const featParser2 = /\[feat(?:\.|uring)\s+([^\[\]]+?)\s*\]$/i;
  258. for (iter of tracks) {
  259. push_unique(album_artists, 'artist');
  260. push_unique(track_artists, 'track_artist');
  261. push_unique(albums, 'album');
  262. push_unique(album_years, 'album_year');
  263. push_unique(release_years, 'release_year');
  264. push_unique(labels, 'label');
  265. push_unique(catalogs, 'catalog');
  266. push_unique(encodings, 'encoding');
  267. push_unique(codecs, 'codec');
  268. push_unique(codec_profiles, 'codec_profile');
  269. push_unique(bitrates, 'bitrate');
  270. push_unique(bds, 'bd');
  271. push_unique(channels, 'channels');
  272. push_unique(medias, 'media');
  273. if (iter.sr) {
  274. if (typeof srs[iter.sr] != 'number') {
  275. srs[iter.sr] = iter.duration;
  276. } else {
  277. srs[iter.sr] += iter.duration;
  278. }
  279. }
  280. push_unique(genres, 'genre');
  281. push_unique(urls, 'url');
  282. push_unique(comments, 'comment');
  283. push_unique(rgs, 'rg');
  284. push_unique(drs, 'dr');
  285. push_unique(vendors, 'vendor');
  286. push_unique(dirpaths, 'dirpath');
  287.  
  288. if (iter.discnumber > totaldiscs) totaldiscs = iter.discnumber;
  289. total_time += iter.duration;
  290. album_bitrate += iter.duration * iter.bitrate;
  291. }
  292. function push_unique(array, prop) {
  293. if (iter[prop] !== undefined && iter[prop] !== null && (typeof iter[prop] != 'string' || iter[prop].length > 0)
  294. && !array.includes(iter[prop])) array.push(iter[prop]);
  295. }
  296. // inconsistent releases not allowed - die
  297. if (encodings.length > 1) { addWarning('FATAL: fuzzy releases aren\'t allowed (encoding): ' + encodings); return false; }
  298. if (codecs.length > 1) { addWarning('FATAL: fuzzy releases aren\'t allowed (codec): ' + codecs); return false; }
  299. if (codec_profiles.length > 1) { addWarning('FATAL: fuzzy releases aren\'t allowed (codec profile): ' + codec_profiles); return false; }
  300. if (vendors.length > 1) { addWarning('FATAL: fuzzy releases aren\'t allowed (vendor): ' + vendors); return false; }
  301. if (medias.length > 1) { addWarning('FATAL: fuzzy releases aren\'t allowed (media): ' + medias); return false; }
  302. if (channels.length > 1) { addWarning('FATAL: fuzzy releases aren\'t allowed (channel): ' + channels); return false; }
  303. if (album_artists.length > 1) { addWarning('FATAL: fuzzy releases aren\'t allowed (album artists): ' + album_artists); return false; }
  304. if (albums.length > 1) { addWarning('FATAL: fuzzy releases aren\'t allowed (album): ' + albums); return false; }
  305. var tags = new TagManager();
  306. album_bitrate /= total_time;
  307. if (total_time <= prefs.single_threshold) {
  308. release_type = 9; // single
  309. } else if (total_time <= prefs.EP_threshold) {
  310. release_type = 5; // EP
  311. }
  312. if (album_artists.length == 1 && (ref = document.getElementById('artist')) != null) {
  313. const guest_parser = /^(.*?)(?:\s+(?:feat(?:\.|uring)|with)\s+(.*))?$/;
  314. if (matches = album_artists[0].match(guest_parser)) {
  315. let main_artists = [], guests = [], composers = [], conductors = [];
  316. let remixers = [], performers = [], compilers = [], producers = [];
  317. const artist_parser = /\s*(?:[\,\;\/\|]|(?:&)\s+(?!(?:The|His|Friends)\b))+\s*/i;
  318. const weak_artist_parser = /\s*[\,\;\/\|]+\s*/;
  319. const other_artists_parsers = [
  320. [/^(.*?)\s+(?:under|(?:conducted) by)\s+(.*)$/, conductors],
  321. ];
  322. const invalid_artist = /^#?N\/?A$/i;
  323. const noakas = /\s+aka\s+(.*)/;
  324. let j;
  325. if (/^(?:Various(?: Artists?)?|VA)$/.test(matches[1])) {
  326. is_va = true;
  327. } else {
  328. j = matches[1].split(artist_parser);
  329. (j.every(twoOrMore) ? j : [ matches[0] ]).forEach(function(i) {
  330. i = guess_other_artists(i);
  331. if (!main_artists.includesCaseless(i)) main_artists.push(i);
  332. });
  333. yadg_prefil = matches[1];
  334. }
  335. if (!is_va && matches[2]) {
  336. guests = matches[2].split(weak_artist_parser);
  337. if (!guests.every(twoOrMore)) guests = matches[2];
  338. }
  339. for (iter of tracks) {
  340. add_track_artists('track_artist');
  341. add_track_artists('performer');
  342. add_other_artists(remixers, 'remixer');
  343. add_other_artists(composers, 'composer');
  344. add_other_artists(conductors, 'conductor');
  345. add_other_artists(compilers, 'compiler');
  346. add_other_artists(producers, 'producer');
  347.  
  348. if (iter.title
  349. && ((matches = iter.title.match(/\(remix(?:ed)? by ([^\(\)]+)\)/i))
  350. || (matches = iter.title.match(/\(([^\(\)]+?)(?:[\'\’\`]s)? remix\)/i))
  351. || (matches = iter.title.match(/\[remix(?:ed)? by ([^\[\]]+)\]/i))
  352. || (matches = iter.title.match(/\[([^\[\]]+?)(?:[\'\’\`]s)? remix\]/i)))) {
  353. j = matches[1].split(weak_artist_parser);
  354. for (i of j.every(twoOrMore) ? j : [ matches[0] ]) { if (!remixers.includesCaseless(i)) remixers.push(i); }
  355. }
  356. if (iter.title && ((matches = iter.title.match(featParser1)) || (matches = iter.title.match(featParser2)))) {
  357. j = matches[1].split(weak_artist_parser);
  358. (j.every(twoOrMore) ? j : [ matches[1] ]).forEach(k => { if (!notInGuestSMain(k)) guests.push(k) });
  359. addWarning('Warning: featured artist(s) in track name (#' +
  360. iter.tracknumber + ': ' + iter.title + ')', false, '#C00000');
  361. }
  362. } // iterate tracks
  363. // split ampersands
  364. for (i = main_artists.length; i > 0; --i) {
  365. j = main_artists[i - 1].split(' & ');
  366. if (j.length >= 2 && j.every(twoOrMore) && !j.every(notInGuestSMain)) {
  367. main_artists.splice(i - 1, 1, ...j.filter(k => !main_artists.includesCaseless(k)));
  368. }
  369. }
  370. for (i = guests.length; i > 0; --i) {
  371. j = guests[i - 1].split(' & ');
  372. if (j.length >= 2 && j.every(twoOrMore) && !j.every(notInGuestSMain)) {
  373. guests.splice(i - 1, 1, ...j.filter(notInGuestSMain));
  374. }
  375. }
  376.  
  377. function notInGuestSMain(k) { return !main_artists.includesCaseless(k) && !guests.includesCaseless(k) }
  378. function twoOrMore(k) { return k.length >= 2 };
  379. function add_track_artists(prop) {
  380. if (iter[prop] && (matches = iter[prop].match(guest_parser))) {
  381. j = matches[1].split(weak_artist_parser);
  382. for (i of j.every(twoOrMore) ? j : [ matches[1] ]) {
  383. i = guess_other_artists(i);
  384. if (!invalid_artist.test(i) && !main_artists.includesCaseless(i)
  385. && (is_va || !guests.includesCaseless(i))) (is_va ? main_artists : guests).push(i);
  386. }
  387. if (matches[2]) {
  388. j = matches[2].split(weak_artist_parser);
  389. for (i of j.every(twoOrMore) ? j : [ matches[2] ]) {
  390. i = i.replace(noakas, '');
  391. if (!invalid_artist.test(i) && notInGuestSMain(i)) guests.push(i);
  392. }
  393. }
  394. }
  395. }
  396. function add_other_artists(list, prop) {
  397. if (!iter[prop]) return;
  398. j = iter[prop].split(weak_artist_parser);
  399. for (i of j.every(twoOrMore) ? j : [ iter[prop] ]) {
  400. i = i.replace(noakas, '');
  401. if (!invalid_artist.test(i) && !list.includesCaseless(i)) list.push(i);
  402. }
  403. }
  404. function guess_other_artists(i) {
  405. other_artists_parsers.forEach(function(iter) {
  406. if (iter[0].exec(i) == null) return;
  407. i = RegExp.$2.replace(noakas, '');
  408. if (!invalid_artist.test(i) && !iter[1].includesCaseless(i)) iter[1].push(RegExp.$2);
  409. i = RegExp.$1;
  410. });
  411. return i.replace(noakas, '');
  412. }
  413.  
  414. if (!ref.disabled) {
  415. let artist_index = 0;
  416. feed_artist_category(main_artists.filter(k => !conductors.includesCaseless(k)), 1);
  417. feed_artist_category(guests.filter(k => !main_artists.includesCaseless(k) && !conductors.includesCaseless(k)), 2);
  418. feed_artist_category(remixers, 3);
  419. feed_artist_category(composers, 4);
  420. feed_artist_category(conductors, 5);
  421. feed_artist_category(compilers, 6);
  422. feed_artist_category(producers, 7);
  423.  
  424. function feed_artist_category(list, type) {
  425. for (iter of list.sort()) {
  426. let id = 'artist';
  427. if (artist_index > 0) {
  428. id += '_' + artist_index;
  429. if (document.getElementById(id) == null) add_artist();
  430. }
  431. ref = document.getElementById(id);
  432. if (ref != null && (overwrite || !ref.value)) {
  433. ref.value = iter;
  434. ref.nextElementSibling.value = type;
  435. }
  436. ++artist_index;
  437. }
  438. }
  439. }
  440. }
  441. }
  442. if (is_va && release_type == 1) release_type = 7; // compilation
  443. if (albums.length == 1) {
  444. let album = albums[0];
  445. rx = /\s+(?:-\s+Single|\[Single\]|\(Single\))$/i;
  446. if (rx.test(album)) {
  447. album = album.replace(rx, '');
  448. release_type = 9; // single
  449. }
  450. rx = /\s+(?:(?:-\s+)?EP|\[EP\]|\(EP\))$/;
  451. if (rx.test(album)) {
  452. album = album.replace(rx, '')
  453. release_type = 5; // EP
  454. }
  455. rx = /\s+\((?:Live|En directo?|Ao Vivo)\b[^\(\)]*\)$/i;
  456. if (rx.test(album)) {
  457. //album = album.replace(rx, '')
  458. if (release_type == 1 || release_type == 7) release_type = 11; // live album
  459. }
  460. rx = /\s+\[(?:Live|En directo?|Ao Vivo)\b[^\[\]]*\]$/i;
  461. if (rx.test(album)) {
  462. //album = album.replace(rx, '')
  463. if (release_type == 1 || release_type == 7) release_type = 11; // live album
  464. }
  465. if (album.search(/(?:^Live [aA]t\b|^Directo? [Ee]n\b|\bUnplugged\b|\bAcoustic Stage\b|\s+Live$)/) >= 0
  466. && (release_type == 1 || release_type == 7)) release_type = 11; // live album
  467. rx = /\b(?:Best [Oo]f|Greatest Hits)\b/;
  468. if (rx.test(album) && release_type == 1) release_type = 6; // Anthology
  469. rx = '\\b(?:Soundtrack|Score|Motion Picture|Series|Television|Original(?: \w+)? Cast|Music from|Musique originale|Bande originale)\\b';
  470. if (reInParenthesis(rx).test(album) || reInBrackets(rx).test(album)) {
  471. //album = album.replace(rx, '')
  472. release_type = 3; // soundtrack
  473. tags.add('score');
  474. composer_significant = true;
  475. }
  476. rx = /\s+(?:\([^\(\)]*\bRemix(?:e[ds])?\b[^\(\)]*\)|Remix(?:e[ds])?)$/i;
  477. if (rx.test(album)) {
  478. //album = album.replace(rx, '')
  479. if (release_type == 1) release_type = 13; // remix
  480. }
  481. rx = /\s+\[[^\[\]]*\bRemix(?:e[ds])?\b[^\[\]]*\]$/i;
  482. if (rx.test(album)) {
  483. //album = album.replace(rx, '')
  484. if (release_type == 1) release_type = 13; // remix
  485. }
  486. rx = /\s+\(([^\(\)]*\b(?:Remaster(?:ed)?\b[^\(\)]*|Reissue|Edition|Version))\)$/i;
  487. if (matches = rx.exec(album)) {
  488. album = album.replace(rx, '');
  489. edition_title = matches[1];
  490. }
  491. rx = /\s+\[([^\[\]]*\b(?:Remaster(?:ed)?\b[^\[\]]*|Reissue|Edition|Version))\]$/i;
  492. if (matches = rx.exec(album)) {
  493. album = album.replace(rx, '');
  494. edition_title = matches[1];
  495. }
  496. rx = /\s+-\s+([^\[\]\(\)\-]*\b(?:(?:Remaster(?:ed)?|Bonus Track)\b[^\[\]\(\)\-]*|Reissue|Edition|Version))$/i;
  497. if (matches = rx.exec(album)) {
  498. album = album.replace(rx, '');
  499. edition_title = matches[1];
  500. }
  501. if (featParser1.test(album)) album = album.replace(featParser1, '');
  502. if (featParser2.test(album)) album = album.replace(featParser1, '');
  503. rx = /\s+(?:\[LP\]|\(LP\))$/;
  504. if (matches = rx.exec(album)) { album = album.replace(rx, ''); media = 'Vinyl'; }
  505. rx = /\s+(?:\[SACD\]|\(SACD\))$/;
  506. if (matches = rx.exec(album)) { album = album.replace(rx, ''); media = 'SACD'; }
  507. rx = /\s+(?:\[(?:Blu[\s\-]?Ray|B[DR])\]|\((?:Blu[\s\-]?Ray|B[DR])\))$/;
  508. if (matches = rx.exec(album)) { album = album.replace(rx, ''); media = 'Blu-Ray'; }
  509. rx = /\s+(?:\[DVD(?:-?A)?\]|\(DVD(?:-?A)?\))$/;
  510. if (matches = rx.exec(album)) { album = album.replace(rx, ''); media = 'DVD'; }
  511. if (element_writable(ref = document.getElementById('title'))) ref.value = album;
  512. if (yadg_prefil) { yadg_prefil += ' - ' }
  513. yadg_prefil += album;
  514. }
  515. if (yadg_prefil && (ref = document.getElementById('yadg_input')) != null) {
  516. ref.value = yadg_prefil;
  517. ref = document.getElementById('yadg_submit');
  518. if (ref != null && !ref.disabled) ref.click();
  519. }
  520. if (album_years.length == 1) {
  521. if (element_writable(ref = document.getElementById('year'))) ref.value = album_years[0];
  522. } else if (album_years.length > 1) {
  523. addWarning('Warning: inconsistent album year accross album: ' + album_years, false, '#C00000');
  524. }
  525. if (release_years.length == 1) {
  526. if (element_writable(ref = document.getElementById('remaster_year'))) ref.value = release_years[0];
  527. } else if (release_years.length > 1) {
  528. addWarning('Warning: inconsistent release year accross album: ' + release_years, false, '#C00000');
  529. }
  530. if (edition_title) {
  531. if (element_writable(ref = document.getElementById('remaster_title'))) ref.value = edition_title;
  532. }
  533. rx = /\s*[\,\;]\s*/g;
  534. if (labels.length == 1 && element_writable(ref = document.getElementById('remaster_record_label'))) {
  535. ref.value = labels[0].replace(rx, ' / ');
  536. } else if (labels.length > 1) {
  537. addWarning('Warning: inconsistent label accross album: ' + labels, false, '#C00000');
  538. }
  539. if (catalogs.length > 0 && element_writable(ref = document.getElementById('remaster_catalogue_number'))) {
  540. ref.value = catalogs.map(k => k.replace(rx, ' / ')).join(' / ');
  541. }
  542. var br_isSet = (ref = document.getElementById('bitrate')) != null && ref.value;
  543. if (codecs.length > 0 && element_writable(ref = document.getElementById('format'))) {
  544. ref.value = codecs[0];
  545. exec(function() { Format() });
  546. }
  547. let sel;
  548. if (encodings[0] == 'lossless') {
  549. if (!bds.every(k => [16, 24].includes(k))) {
  550. sel = null; // album containing disallowed bit depth
  551. } else {
  552. sel = bds.includes(24) ? '24bit Lossless' : 'Lossless';
  553. }
  554. } else if (bitrates.length > 0) {
  555. let lame_version = vendors.length > 0 && (matches = vendors[0].match(/^LAME(\d+)\.(\d+)/i)) ?
  556. parseInt(matches[1]) * 1000 + parseInt(matches[2]) : undefined;
  557. if (codec_profiles.length == 1 && codec_profiles[0] == 'VBR V0') {
  558. sel = lame_version >= 3094 ? 'V0 (VBR)' : 'APX (VBR)'
  559. } else if (codec_profiles.length == 1 && codec_profiles[0] == 'VBR V1') {
  560. sel = 'V1 (VBR)'
  561. } else if (codec_profiles.length == 1 && codec_profiles[0] == 'VBR V2') {
  562. sel = lame_version >= 3094 ? sel = 'V2 (VBR)' : 'APS (VBR)'
  563. } else if (bitrates.length == 1 && [192, 256, 320].includes(Math.round(bitrates[0]))) {
  564. sel = Math.round(bitrates[0]);
  565. } else if (bitrates.length >= 1) {
  566. if (element_writable(ref = document.getElementById('bitrate')) && ref.value != 'Other') {
  567. ref.value = 'Other';
  568. exec(function() { Bitrate() });
  569. }
  570. if (element_writable(ref = document.getElementById('other_bitrate'))) {
  571. ref.value = Math.round(bitrates.length == 1 ? bitrates[0] : album_bitrate);
  572. if ((ref = document.getElementById('vbr')) != null) ref.checked = bitrates.length > 1;
  573. }
  574. }
  575. }
  576. if (sel && (ref = document.getElementById('bitrate')) != null && !elem.disabled && (overwrite || !br_isSet)) {
  577. ref.value = sel;
  578. }
  579. if (medias.length > 0) {
  580. sel = undefined;
  581. if (medias[0].search(/\b(?:WEB|File|Digital Download)\b/i) >= 0) sel = 'WEB';
  582. if (medias[0].search(/\bCD\b/) >= 0) sel = 'CD';
  583. if (medias[0].search(/\b(?:SACD|Hybrid)\b/) >= 0) sel = 'SACD';
  584. if (medias[0].search(/\bBlu[-\s]?Ray\b/i) >= 0) sel = 'Blu-Ray';
  585. if (medias[0].search(/\bDVD(?:-?A)?\b/) >= 0) sel = 'DVD';
  586. if (medias[0].search(/\b(?:Vinyl\b|LP\b|12"|7")/) >= 0) sel = 'Vinyl';
  587. if (sel) media = sel;
  588. if (media && element_writable(ref = document.getElementById('media'))) ref.value = sel;
  589. }
  590. if (genres.length >= 1) {
  591. genres.forEach(function(genre) {
  592. if (/\b(?:Classical|Symphony|Symphonic(?:al)?$|Chamber|Choral|Opera|Klassik|Duets)\b/i.test(genre)) {
  593. composer_significant = true;
  594. is_classical = true
  595. }
  596. if (/\b(?:Jazz|Vocal)\b/i.test(genre)) {
  597. composer_significant = true;
  598. }
  599. if (/\b(?:Soundtracks?|Score|Films?|Games?|Video|Series?|Theatre|Musical)\b/i.test(genre)) {
  600. composer_significant = true;
  601. if (release_type == 1) release_type = 3;
  602. }
  603. tags.add(genre);
  604. });
  605. if (genres.length > 1) addWarning('Warning: inconsistent genre accross album: ' + genres, false, '#C00000');
  606. }
  607. if (tags.length > 0 && element_writable(ref = document.getElementById('tags'))) ref.value = tags.toString();
  608. if (element_writable(ref = document.getElementById('releasetype'))) ref.value = release_type;
  609.  
  610. if (!composer_significant && !prefs.keep_meaningles_composers) {
  611. ref = document.querySelectorAll('input[name="artists[]"]');
  612. if (ref != null) ref.forEach(i => { if (['4', '5'].includes(i.nextElementSibling.value)) i.value = null });
  613. }
  614.  
  615. var description, ripinfo, dur, vinyl_test = /^(Vinyl rip by\s+)(.*)$/im;
  616. if (tracks.length > 1) {
  617. gen_full_tracklist();
  618. } else { // single
  619. description = '[align=center]';
  620. description += isRED() ? '[pad=20|20|20|20]' : '';
  621. description += '[size=4][b][color=' + prefs.tracklist_artist_color + ']' + album_artists[0] + '[/color][hr]';
  622. //description += '[color=' + prefs.tracklist_single_color + ']';
  623. description += tracks[0].title;
  624. //description += '[/color]'
  625. description += '[/b]';
  626. if (tracks[0].composer) {
  627. description += '\n[i][color=' + prefs.tracklist_composer_color + '](' + tracks[0].composer + ')[/color][/i]';
  628. }
  629. description += '\n\n[color=' + prefs.tracklist_duration_color +'][' +
  630. make_time_string(tracks[0].duration) + '][/color][/size]';
  631. if (isRED()) description += '[/pad]';
  632. description += '[/align]';
  633. }
  634.  
  635. if (comments.length == 1 && comments[0]) {
  636. let cmt = comments[0];
  637. if (matches = cmt.match(vinyl_test)) {
  638. ripinfo = cmt.slice(matches.index).trim().split(/[\r\n]+/);
  639. description = description.concat('\n\n', cmt.slice(0, matches.index).trim());
  640. } else {
  641. description = description.concat('\n\n', cmt);
  642. }
  643. }
  644. if (element_writable(ref = document.getElementById('album_desc'))) ref.value = description;
  645. if ((ref = document.getElementById('body')) != null && !ref.disabled) {
  646. let editioninfo;
  647. if (edition_title) {
  648. editioninfo = '[size=5][b]' + edition_title;
  649. if (release_years.length >= 1) { editioninfo = editioninfo.concat(' (', release_years[0] + ')') }
  650. editioninfo = editioninfo.concat('[/b][/size]\n\n');
  651. } else { editioninfo = '' }
  652. if (ref.textLength > 0) {
  653. ref.value = ref.value.concat('\n\n', editioninfo, description);
  654. } else {
  655. ref.value = editioninfo + description;
  656. }
  657. }
  658. let lineage = '', comment = '', drinfo, srcinfo;
  659. if (Object.keys(srs).length > 0) {
  660. let kHz = Object.keys(srs).sort((a, b) => srs[b] - srs[a]).map(f => f / 1000).join('/').concat('kHz');
  661. if (element_writable(ref = document.getElementById('release_samplerate'))) {
  662. ref.value = Object.keys(srs).length > 1 ? '999' : Math.floor(Object.keys(srs)[0] / 1000);
  663. }
  664. if (bds.includes(24)) {
  665. if (drs.length >= 1) drinfo = '[hide=DR' + (drs.length == 1 ? drs[0] : '') + '][pre][/pre]';
  666. if (media == 'Vinyl') {
  667. let hassr = ref == null || Object.keys(srs).length > 1;
  668. lineage = hassr ? kHz + ' ' : '';
  669. if (ripinfo) {
  670. ripinfo[0] = ripinfo[0].replace(vinyl_test, '$1[color=blue]$2[/color]');
  671. if (hassr) { ripinfo[0] = ripinfo[0].replace(/^Vinyl\b/, 'vinyl') }
  672. lineage += ripinfo[0] + '\n\n[u]Lineage:[/u]' + ripinfo.slice(1).map(k => '\n' + k).join('');
  673. } else {
  674. lineage += (hassr ? 'Vinyl' : ' vinyl') + ' rip by [color=blue][/color]\n\n[u]Lineage:[/u]';
  675. }
  676. if (drs.length >= 1) drinfo += '\n\n[img][/img]\n[img][/img]\n[img][/img][/hide]';
  677. } else if (['Blu-Ray', 'DVD', 'SACD'].includes(media)) {
  678. lineage = ref ? '' : kHz;
  679. if (channels.length == 1) add_channel_info();
  680. if (media == 'SACD' || is_from_dsd) {
  681. lineage += ' from DSD64 using foobar2000\'s SACD decoder (direct-fp64)';
  682. lineage += '\nOutput gain +0dB';
  683. }
  684. drinfo += '[/hide]';
  685. //add_rg_info();
  686. } else { // WEB Hi-Res
  687. if (ref == null || Object.keys(srs).length > 1) lineage = kHz;
  688. if (channels.length == 1 && channels[0] != 2) add_channel_info();
  689. add_dr_info();
  690. //if (lineage.length > 0) add_rg_info();
  691. if (bds.length >= 2) {
  692. let hybrid_tracks = tracks.filter(k => k.bd < 24).map(k => k.tracknumber);
  693. if (hybrid_tracks) {
  694. if (lineage) lineage += '\n';
  695. lineage += 'Note: track';
  696. if (hybrid_tracks.length > 1) lineage += 's';
  697. lineage += ' #' + hybrid_tracks.sort().join(', ') +
  698. (hybrid_tracks.length > 1 ? ' are' : ' is') + ' 16bit lossless';
  699. }
  700. }
  701. drinfo = Object.keys(srs).includes(88200) ? drinfo.concat('[/hide]') : null;
  702. }
  703. } else { // 16bit or lossy
  704. if (Object.keys(srs).some(f => f != 44100)) lineage = kHz;
  705. if (channels.length == 1 && channels[0] != 2) add_channel_info();
  706. //add_dr_info();
  707. //if (lineage.length > 0) add_rg_info();
  708. if (['AAC', 'Opus', 'Vorbis'].includes(codecs[0]) && vendors[0]) {
  709. let _encoder_settings = vendors[0];
  710. if (codecs[0] == 'AAC' && vendors[0].search(/^qaac\s+[\d\.]+/i) >= 0) {
  711. let enc = [];
  712. if (matches = vendors[0].match(/\bqaac\s+([\d\.]+)\b/i)) enc[0] = matches[1];
  713. if (matches = vendors[0].match(/\bCoreAudioToolbox\s+([\d\.]+)\b/i)) enc[1] = matches[1];
  714. if (matches = vendors[0].match(/\b(AAC-\S+)\s+Encoder\b/i)) enc[2] = matches[1];
  715. if (matches = vendors[0].match(/\b([TC]VBR|ABR|CBR)\s+(\S+)\b/)) { enc[3] = matches[1]; enc[4] = matches[2]; }
  716. if (matches = vendors[0].match(/\bQuality\s+(\d+)\b/i)) enc[5] = matches[1];
  717. _encoder_settings = 'Converted by Apple\'s ' + enc[2] + ' encoder (' + enc[3] + '-' + enc[4] + ')';
  718. }
  719. if (lineage) lineage += '\n\n';
  720. lineage += _encoder_settings;
  721. }
  722. }
  723. }
  724. function add_dr_info() {
  725. if (drs.length != 1 || document.getElementById('release_dynamicrange') != null) return false;
  726. if (lineage.length > 0) lineage += ' | ';
  727. if (drs[0] < 4) lineage += '[color=red]';
  728. lineage += 'DR' + drs[0];
  729. if (drs[0] < 4) lineage += '[/color]';
  730. return true;
  731. }
  732. function add_rg_info() {
  733. if (rgs.length != 1) return false;
  734. if (lineage.length > 0) lineage += ' | ';
  735. lineage += 'RG'; //lineage += 'RG ' + rgs[0];
  736. return true;
  737. }
  738. function add_channel_info() {
  739. if (channels.length != 1) return false;
  740. let chi = getChanString(channels[0]);
  741. if (lineage.length > 0 && chi.length > 0) lineage += ', ';
  742. lineage += chi;
  743. return chi.length > 0;
  744. }
  745. if (urls.length == 1 && urls[0]) {
  746. srcinfo = '[url]' + urls[0] + '[/url]';
  747. if (element_writable(document.getElementById('image'))) {
  748. let u = urls[0];
  749. if (u.search(/^https?:\/\/(\w+\.)?discogs\.com\/release\/[\w\-]+\/?$/i) >= 0) u += '/images';
  750. GM_xmlhttpRequest({ method: 'GET', url: u, onload: fetch_image_from_store });
  751. }
  752. // } else if (element_writable(document.getElementById('image'))
  753. // && ((ref = document.getElementById('album_desc')) != null || (ref = document.getElementById('body')) != null)
  754. // && ref.textLength > 0 && (matches = ref.value.matchAll(/\b(https?\/\/[\w\-\&\_\?\=]+)/i)) != null) {
  755. }
  756. ref = document.getElementById('release_lineage');
  757. if (ref != null) {
  758. if (element_writable(ref)) {
  759. if (drinfo) comment = drinfo;
  760. if (lineage && srcinfo) lineage += '\n\n';
  761. if (srcinfo) lineage += srcinfo;
  762. ref.value = lineage;
  763. }
  764. } else {
  765. comment = lineage;
  766. if (comment && drinfo) comment += '\n\n';
  767. if (drinfo) comment += drinfo;
  768. if (comment && srcinfo) comment += '\n\n';
  769. if (srcinfo) comment += srcinfo;
  770. }
  771. if (comment.length > 0) {
  772. if (element_writable(ref = document.getElementById('release_desc'))) ref.value = comment;
  773. }
  774. if (encodings[0] == 'lossless' && codecs[0] == 'FLAC' && bds.includes(24) && dirpaths.length == 1) {
  775. var uri = new URL(dirpaths[0] + '\\foo_dr.txt');
  776. GM_xmlhttpRequest({
  777. method: 'GET',
  778. url: uri.href,
  779. onload: function(response) {
  780. if (response.readyState != 4 || !response.responseText) return;
  781. var rlsDesc = document.getElementById('release_lineage') || document.getElementById('release_desc');
  782. if (rlsDesc == null) return;
  783. var value = rlsDesc.value;
  784. matches = value.match(/(^\[hide=DR\d*\]\[pre\])\[\/pre\]/im);
  785. if (matches == null) return;
  786. var index = matches.index + matches[1].length;
  787. rlsDesc.value = value.slice(0, index).concat(response.responseText, value.slice(index));
  788. }
  789. });
  790. }
  791. if (drs.length == 1) {
  792. if (element_writable(ref = document.getElementById('release_dynamicrange'))) ref.value = drs[0];
  793. }
  794. if (prefs.clean_on_apply) document.getElementById('import_data').value = null;
  795. prefs.save();
  796. return true;
  797.  
  798. function gen_full_tracklist() { // ========================= TACKLIST =========================
  799. description = isRED() ? '[pad=5|0|0|0]' : '';
  800. description += '[size=4][color=' + prefs.tracklist_head_color + '][b]Tracklisting[/b][/color][/size]';
  801. if (isRED()) '[/pad]';
  802. let classical_units = new Set();
  803. if (is_classical) {
  804. for (track of tracks) {
  805. if (matches = track.title.match(/^(.+?)\s*:\s+(.*)$/)) {
  806. classical_units.add(track.classical_unit_title = matches[1]);
  807. track.classical_title = matches[2];
  808. } else {
  809. track.classical_unit_title = null;
  810. }
  811. }
  812. for (let unit of classical_units.keys()) {
  813. let group_performer = array_homogenous(tracks.filter(k => k.classical_unit_title === unit).map(k => k.track_artist));
  814. let group_composer = array_homogenous(tracks.filter(k => k.classical_unit_title === unit).map(k => k.composer));
  815. for (track of tracks) {
  816. if (track.classical_unit_title !== unit) continue;
  817. if (group_composer) track.classical_unit_composer = track.composer;
  818. if (group_performer) track.classical_unit_performer = track.track_artist;
  819. }
  820. }
  821. }
  822. let block = 1, lastdisc, lastsubtitle, lastside, vinyl_trackwidth;
  823. let lastwork = classical_units.size > 0 ? null : undefined;
  824. description += '\n';
  825. let volumes = new Map(tracks.map(k => [k.discnumber, undefined]));
  826. volumes.forEach(function(val, key) {
  827. volumes.set(key, array_homogenous(tracks.filter(k => k.discnumber == key).map(k => k.discsubtitle)));
  828. });
  829. if (media == 'Vinyl') {
  830. let max_side_track = undefined;
  831. rx = /^([A-Z])(\d+)?(\.(\d+))?/i;
  832. for (iter of tracks) {
  833. if (matches = iter.tracknumber.match(rx)) {
  834. max_side_track = Math.max(parseInt(matches[2]) || 1, max_side_track || 0);
  835. }
  836. }
  837. if (typeof max_side_track == 'number') {
  838. max_side_track = max_side_track.toString().length;
  839. vinyl_trackwidth = 1 + max_side_track;
  840. for (iter of tracks) {
  841. if (matches = iter.tracknumber.match(rx)) {
  842. iter.tracknumber = matches[1].toUpperCase();
  843. if (matches[2]) iter.tracknumber += matches[2].padStart(max_side_track, '0');
  844. }
  845. }
  846. }
  847. }
  848. function prologue(prefix, postfix) {
  849. function block1() {
  850. if (block == 3) description += postfix;
  851. description += '\n';
  852. block = 1;
  853. }
  854. function block2() {
  855. if (block == 3) description += postfix;
  856. description += '\n';
  857. block = 2;
  858. }
  859. function block3() {
  860. if (block == 2) { description += '[hr]' } else { description += '\n' }
  861. if (block != 3) description += prefix;
  862. block = 3;
  863. }
  864. if (totaldiscs > 1 && iter.discnumber != lastdisc) {
  865. block1();
  866. description += '[size=3][color=' + prefs.tracklist_discsubtitle_color + '][b]Disc ' + iter.discnumber;
  867. if (iter.discsubtitle && (!volumes.has(iter.discnumber) || volumes.get(iter.discnumber))) {
  868. description += ' - ' + iter.discsubtitle;
  869. lastsubtitle = iter.discsubtitle;
  870. }
  871. description += '[/b][/color][/size]';
  872. lastdisc = iter.discnumber;
  873. }
  874. if (iter.discsubtitle != lastsubtitle) {
  875. block1();
  876. if (iter.discsubtitle) {
  877. description += '[size=2][color=' + prefs.tracklist_discsubtitle_color + '][b]' +
  878. iter.discsubtitle + '[/b][/color][/size]';
  879. }
  880. lastsubtitle = iter.discsubtitle;
  881. }
  882. if (iter.classical_unit_title !== lastwork) {
  883. if (iter.classical_unit_composer || iter.classical_unit_title || iter.classical_unit_performer) {
  884. block2();
  885. description += '[size=2][color=' + prefs.tracklist_classicalblock_color + '][b]';
  886. if (iter.classical_unit_composer) description += iter.classical_unit_composer + ': ';
  887. if (iter.classical_unit_title) description += iter.classical_unit_title;
  888. description += '[/b]';
  889. if (iter.classical_unit_performer) description += ' (' + iter.classical_unit_performer + ')';
  890. description += '[/color][/size]';
  891. } else {
  892. if (block != 2) block1();
  893. }
  894. lastwork = iter.classical_unit_title;
  895. }
  896. block3();
  897. if (media == 'Vinyl') {
  898. let c = iter.tracknumber[0].toUpperCase();
  899. if (lastside != undefined && c != lastside) description += '\n';
  900. lastside = c;
  901. }
  902. }
  903. for (iter of tracks.sort(function(a, b) {
  904. var d = a.discnumber - b.discnumber;
  905. var t = a.tracknumber - b.tracknumber;
  906. return isNaN(d) || d == 0 ? isNaN(t) ? a.tracknumber.localeCompare(b.tracknumber) : t : d;
  907. })) {
  908. let title = '';
  909. let ttwidth = vinyl_trackwidth || Math.max((iter.totaltracks || tracks.length).toString().length, 2);
  910. if (prefs.tracklist_style == 1) {
  911. // STYLE 1 ----------------------------------------
  912. prologue('[size=2]', '[/size]\n');
  913. track = '[b][color=' + prefs.tracklist_tracknumber_color + ']';
  914. track += isNaN(parseInt(iter.tracknumber)) ? iter.tracknumber : iter.tracknumber.padStart(ttwidth, '0');
  915. track += '[/color][/b]' + prefs.title_separator;
  916. if (iter.track_artist && !iter.classical_unit_performer) {
  917. title = '[color=' + prefs.tracklist_artist_color + ']' + iter.track_artist + '[/color] - ';
  918. }
  919. title += iter.classical_title || iter.title;
  920. if (iter.composer && composer_significant && !iter.classical_unit_composer) {
  921. title = title.concat(' [color=', prefs.tracklist_composer_color, '](', iter.composer, ')[/color]');
  922. }
  923. description += track + title + ' [i][color=' + prefs.tracklist_duration_color +'][' +
  924. make_time_string(iter.duration) + '][/color][/i]';
  925. } else if (prefs.tracklist_style == 2) {
  926. // STYLE 2 ----------------------------------------
  927. prologue('[size=2][pre]', '[/pre][/size]');
  928. track = isNaN(parseInt(iter.tracknumber)) ? iter.tracknumber : iter.tracknumber.padStart(ttwidth, '0');
  929. track += prefs.title_separator;
  930. if (iter.track_artist && !iter.classical_unit_performer) title = iter.track_artist + ' - ';
  931. title += iter.classical_title || iter.title;
  932. if (iter.composer && composer_significant && !iter.classical_unit_composer) {
  933. title = title.concat(' (', iter.composer, ')');
  934. }
  935. dur = '[' + make_time_string(iter.duration) + ']';
  936. let l = 0, width = prefs.max_tracklist_width - track.length - dur.length - 1;
  937. while (title.length > 0) {
  938. let j = width;
  939. if (title.length > width) {
  940. while (j > 0 && title[j] != ' ') { --j }
  941. if (j <= 0) { j = width }
  942. }
  943. let left = title.slice(0, j).trim();
  944. if (++l <= 1) {
  945. description = description.concat(track, left.padEnd(width, ' '), ' ', dur);
  946. width = prefs.max_tracklist_width - track.length - 2;
  947. } else {
  948. description = description.concat('\n', ' '.repeat(track.length), left);
  949. }
  950. title = title.slice(j).trim();
  951. }
  952. }
  953. }
  954. if (prefs.tracklist_style == 1) {
  955. description += '\n\n' + div[0].repeat(10) + '\n[color=' + prefs.tracklist_duration_color +
  956. ']Total time: [i]' + make_time_string(total_time) + '[/i][/color][/size]';
  957. } else if (prefs.tracklist_style == 2) {
  958. dur = '[' + make_time_string(total_time) + ']';
  959. description = description.concat('\n\n', div[0].repeat(32).padStart(prefs.max_tracklist_width));
  960. description = description.concat('\n', 'Total time:'.padEnd(prefs.max_tracklist_width - dur.length), dur);
  961. description = description.concat('[/pre][/size]');
  962. }
  963. }
  964.  
  965. function getChanString(n) {
  966. const chanmap = [
  967. 'mono',
  968. 'stereo',
  969. '2.1',
  970. '4.0 surround sound',
  971. '5.0 surround sound',
  972. '5.1 surround sound',
  973. '7.0 surround sound',
  974. '7.1 surround sound',
  975. ];
  976. return n >= 1 && n <= 8 ? chanmap[n - 1] : n + 'chn surround sound';
  977. }
  978.  
  979. function fetch_image_from_store(response) {
  980. if (response.readyState != 4 || !response.responseText) return;
  981. ref = document.getElementById('image');
  982. var parser = new DOMParser();
  983. var html = parser.parseFromString(response.responseText, "text/html");
  984. if (response.finalUrl.toLowerCase().indexOf('qobuz.com') >= 0
  985. && (ref = html.querySelector('div.album-cover > img')) != null) set_image(ref.src);
  986. if (response.finalUrl.toLowerCase().indexOf('7digital.com') >= 0
  987. && (ref = html.querySelector('span.release-packshot-image > img[itemprop="image"]')) != null) {
  988. set_image(ref.src);
  989. }
  990. if (response.finalUrl.toLowerCase().indexOf('highresaudio.com') >= 0
  991. && (ref = html.querySelector('div.albumbody > img.cover[data-pin-media]')) != null) {
  992. set_image(ref.dataset.pinMedia);
  993. }
  994. if (response.finalUrl.toLowerCase().indexOf('hdtracks.com') >= 0
  995. && (ref = html.querySelector('p.product-image > img')) != null) set_image(ref.src);
  996. if (response.finalUrl.toLowerCase().indexOf('bandcamp.com') >= 0
  997. && (ref = html.querySelector('div#tralbumArt > a.popupImage')) != null) set_image(ref.href);
  998. if (response.finalUrl.toLowerCase().indexOf('discogs.com') >= 0
  999. && (ref = html.querySelector('div#view_images > p:first-of-type > span > img')) != null) set_image(ref.src);
  1000. if (response.finalUrl.toLowerCase().indexOf('junodownload.com') >= 0
  1001. && (ref = html.querySelector('a.productimage')) != null) set_image(ref.href);
  1002. if (response.finalUrl.toLowerCase().indexOf('supraphonline.cz') >= 0
  1003. && (ref = html.querySelector('div.sexycover > img')) != null) set_image(ref.src.replace(/\?\d+$/, ''));
  1004. }
  1005. }
  1006.  
  1007. function fill_from_text_apps() {
  1008. if (clipBoard.value.search(/^https?:\/\//i) < 0) return false;
  1009. var parser, html, description, tags = new TagManager();
  1010. if (clipBoard.value.toLowerCase().indexOf('//sanet') >= 0) {
  1011. GM_xmlhttpRequest({ method: 'GET', url: clipBoard.value, onload: function(response) {
  1012. if (response.readyState != 4 || response.status != 200) return;
  1013. parser = new DOMParser();
  1014. html = parser.parseFromString(response.responseText, "text/html");
  1015.  
  1016. i = html.querySelector('h1.item_title > span');
  1017. if (i != null && element_writable(ref = document.getElementById('title'))) {
  1018. ref.value = i.textContent.replace(/\(x64\)$/i, '(64-bit)').replace(/\bBuild\s+(\d+)/, 'build $1').
  1019. replace(/\bMultilingual\b/, 'multilingual').replace(/\bMultilanguage\b/, 'multilanguage');
  1020. }
  1021.  
  1022. i = html.querySelector('section.descr');
  1023. if (i != null) {
  1024. description = '';
  1025. ref = html.querySelector('section.descr > div.release-info');
  1026. if (ref != null) var releaseInfo = ref.textContent.trim();
  1027. desc_extract(i);
  1028. ref = html.querySelector('div.txtleft > a');
  1029. if (ref != null) description += '\n\n[b]Product page:[/b]\n[url]' + de_anonymize(ref.href) + '[/url]';
  1030. write_description(description);
  1031.  
  1032. function desc_extract(node) {
  1033. for (var i of node.childNodes) {
  1034. if (i.nodeType == 3) {
  1035. if (i.length < 5) continue;
  1036. description += i.textContent.trim();
  1037. } else if (i.nodeName == 'BR' || i.nodeName == 'HR') {
  1038. description += '\n';
  1039. } else if (i.nodeName == 'LABEL') {
  1040. description += '\n\n[b]' + i.textContent.trim() + '[/b]\n';
  1041. } else if (i.nodeName == 'A') {
  1042. if (i.classList.contains('mfp-image')) {
  1043. //rehost_imgs([de_anonymize(i.href)]).then(new_url => {
  1044. // description += '\n\n[img]' + new_url + '[/img]'
  1045. //}).catch(function() {
  1046. // description += '\n\n[img]' + de_anonymize(i.href) + '[/img]'
  1047. //});
  1048. description += '\n\n[img]' + de_anonymize(i.href) + '[/img]'
  1049. } else {
  1050. description += '[url=' + de_anonymize(i.href) + ']' + i.textContent.trim() + '[/url]';
  1051. }
  1052. } else if (i.nodeName == 'B' || i.nodeName == 'STRONG') {
  1053. description += '[b]' + i.textContent + '[/b]';
  1054. } else if (i.nodeName == 'I') {
  1055. description += '[i]' + i.textContent + '[/i]';
  1056. } else if (i.nodeName == 'DIV') {
  1057. if (i.classList.contains('scrpad') || i.classList.contains('aleft')) {
  1058. desc_extract(i);
  1059. description += '\n';
  1060. }
  1061. }
  1062. }
  1063. }
  1064. }
  1065.  
  1066. i = html.querySelector('section.descr > div.center > a.mfp-image');
  1067. if (i != null) {
  1068. set_image(i.href);
  1069. } else {
  1070. i = html.querySelector('section.descr > div.center > img[data-src]');
  1071. if (i != null) set_image(i.dataset.src);
  1072. }
  1073.  
  1074. var cat = html.querySelector('a.cat:last-of-type > span');
  1075. if (cat != null) {
  1076. if (cat.textContent.toLowerCase() == 'windows') {
  1077. tags.add('apps.windows');
  1078. if (releaseInfo && releaseInfo.search(/\bx64\b/i) >= 0) tags.add('win64');
  1079. if (releaseInfo && releaseInfo.search(/\bx86\b/i) >= 0) tags.add('win32');
  1080. }
  1081. if (cat.textContent.toLowerCase() == 'macos') tags.add('apps.mac');
  1082. if (cat.textContent.toLowerCase() == 'linux' || cat.textContent.toLowerCase() == 'unix') tags.add('apps.linux');
  1083. if (cat.textContent.toLowerCase() == 'android') tags.add('apps.android');
  1084. if (cat.textContent.toLowerCase() == 'ios') tags.add('apps.ios');
  1085. }
  1086. if (tags.length > 0 && element_writable(ref = document.getElementById('tags'))) {
  1087. ref.value = tags.toString();
  1088. }
  1089. }, });
  1090. return true;
  1091. }
  1092. return false;
  1093.  
  1094. function de_anonymize(uri) {
  1095. return uri ? uri.replace('http://anonymz.com/?', '').replace('https://anonymz.com/?', '') : null;
  1096. }
  1097.  
  1098. function html2php(str) {
  1099. return str ? str.replace(/\<b\>/ig, '[b]').replace(/\<\/b\>/ig, '[/b]').
  1100. replace(/\<i\>/ig, '[i]').replace(/\<\/i\>/ig, '[/i]') : null;
  1101. }
  1102. }
  1103.  
  1104. function fill_from_text_books() {
  1105. if (clipBoard.value.search(/^https?:\/\//i) < 0) return false;
  1106. var parser, html, description, tags = new TagManager();
  1107. if (clipBoard.value.toLowerCase().indexOf('martinus.cz') >= 0 || clipBoard.value.toLowerCase().indexOf('martinus.sk') >= 0) {
  1108. GM_xmlhttpRequest({ method: 'GET', url: clipBoard.value, onload: function(response) {
  1109. if (response.readyState != 4 || response.status != 200) return;
  1110. parser = new DOMParser();
  1111. html = parser.parseFromString(response.responseText, "text/html");
  1112.  
  1113. function get_detail(x, y) {
  1114. var ref = html.querySelector('section#details > div > div > div:first-of-type > div:nth-child(' +
  1115. x + ') > dl:nth-child(' + y + ') > dd');
  1116. return ref != null ? ref.textContent.trim() : null;
  1117. }
  1118.  
  1119. i = html.querySelectorAll('article > ul > li > a');
  1120. if (i != null && element_writable(ref = document.getElementById('title'))) {
  1121. description = join_authors(i);
  1122. i = html.querySelector('article > h1');
  1123. if (i != null) description += ' - ' + i.textContent.trim();
  1124. i = html.querySelector('div.bar.mb-medium > div:nth-child(1) > dl > dd > span');
  1125. if (i != null && (matches = i.textContent.match(/\b(\d{4})\b/)) != null) description += ' (' + matches[1] + ')';
  1126. ref.value = description;
  1127. }
  1128.  
  1129. description = '[quote]';
  1130. i = html.querySelector('section#description > div');
  1131. if (i != null) {
  1132. desc_extract(i);
  1133.  
  1134. function desc_extract(node) {
  1135. for (var i of node.childNodes) {
  1136. if (i.nodeType == 3 || i.nodeName == 'P') {
  1137. //if (i.length < 10) continue;
  1138. description += i.textContent;
  1139. } else if (i.nodeName == 'BR' || i.nodeName == 'HR') {
  1140. description += '\n';
  1141. } else if (i.nodeName == 'B' || i.nodeName == 'STRONG') {
  1142. description += '[b]' + i.textContent + '[/b]';
  1143. } else if (i.nodeName == 'I') {
  1144. description += '[i]' + i.textContent + '[/i]';
  1145. } else if (i.nodeName == 'DIV') {
  1146. //desc_extract(i);
  1147. //description += '\n';
  1148. }
  1149. }
  1150. }
  1151. }
  1152.  
  1153. description += '[/quote]';
  1154. let details = html.querySelectorAll('section#details > div > div > div:first-of-type > div > dl');
  1155. for (var detail of details) {
  1156. var lbl = detail.children[0].textContent.trim();
  1157. var val = detail.children[1].textContent.trim();
  1158. if (lbl.search(/\b(?:originál)/i) >= 0) lbl = 'Original title'
  1159. else if (lbl.search(/\b(?:rozm)/i) >= 0) continue
  1160. else if (lbl.search(/\b(?:datum|dátum|rok)\b/i) >= 0) lbl = 'Release date'
  1161. else if (lbl.search(/\b(?:katalog|katalóg)/i) >= 0) lbl = 'Catalogue #'
  1162. else if (lbl.search(/\b(?:stran|strán)\b/i) >= 0) lbl = 'Page count'
  1163. else if (lbl.search(/\bjazyk/i) >= 0) lbl = 'Language'
  1164. else if (lbl.search(/\b(?:nakladatel|vydavatel)/i) >= 0) lbl = 'Publisher'
  1165. else if (lbl.search(/\b(?:vazba|vázba)\b/i) >= 0) continue
  1166. else if (lbl.search(/\b(?:doporuč|ODPORÚČ)/i) >= 0) lbl = 'Age rating'
  1167. else if (lbl.search(/\b(?:ISBN)\b/i) >= 0) {
  1168. val = '[url=https://www.worldcat.org/isbn/' + detail.children[1].textContent.trim() +
  1169. ']' + detail.children[1].textContent.trim() + '[/url]';
  1170. // } else if (lbl.search(/\b(?:ISBN)\b/i) >= 0) {
  1171. // val = '[url=https://www.goodreads.com/search/search?q=' + detail.children[1].textContent.trim() +
  1172. // '&search_type=books]' + detail.children[1].textContent.trim() + '[/url]';
  1173. }
  1174. description += '\n[b]' + lbl + ':[/b] ' + val;
  1175. }
  1176. description += '\n[b]More info:[/b] ' + response.finalUrl;
  1177. write_description(description);
  1178.  
  1179. if ((i = html.querySelector('a.mj-product-preview > img')) != null) {
  1180. set_image(i.src.replace(/\?.*/, ''));
  1181. } else if ((i = html.querySelector('head > meta[property="og:image"]')) != null) {
  1182. set_image(i.content.replace(/\?.*/, ''));
  1183. }
  1184.  
  1185. var cat = html.querySelectorAll('dd > ul > li > a');
  1186. if (cat != null) cat.forEach(x => { tags.add(x.textContent) });
  1187. if (tags.length > 0 && element_writable(ref = document.getElementById('tags'))) {
  1188. ref.value = tags.toString();
  1189. }
  1190. }, });
  1191. return true;
  1192. } else if (clipBoard.value.toLowerCase().indexOf('goodreads.com') >= 0) {
  1193. GM_xmlhttpRequest({ method: 'GET', url: clipBoard.value, onload: function(response) {
  1194. if (response.readyState != 4 || response.status != 200) return;
  1195. parser = new DOMParser();
  1196. html = parser.parseFromString(response.responseText, "text/html");
  1197.  
  1198. i = html.querySelectorAll('a.authorName > span');
  1199. if (i != null && element_writable(ref = document.getElementById('title'))) {
  1200. description = join_authors(i);
  1201. i = html.querySelector('h1#bookTitle');
  1202. if (i != null) description += ' - ' + i.textContent.trim();
  1203. i = html.querySelector('div#details > div#row:nth-child(2)');
  1204. if (i != null && (matches = i.textContent.match(/\b(\d{4})\b/)) != null) description += ' (' + matches[1] + ')';
  1205. ref.value = description;
  1206. }
  1207.  
  1208. description = '[quote]';
  1209. i = html.querySelector('div#description > span:last-of-type');
  1210. if (i != null) {
  1211. desc_extract(i);
  1212.  
  1213. function desc_extract(node) {
  1214. for (var i of node.childNodes) {
  1215. if (i.nodeType == 3 || i.nodeName == 'P') {
  1216. //if (i.length < 10) continue;
  1217. description += i.textContent;
  1218. } else if (i.nodeName == 'BR' || i.nodeName == 'HR') {
  1219. description += '\n';
  1220. } else if (i.nodeName == 'B' || i.nodeName == 'STRONG') {
  1221. description += '[b]' + i.textContent + '[/b]';
  1222. } else if (i.nodeName == 'I') {
  1223. description += '[i]' + i.textContent + '[/i]';
  1224. } else if (i.nodeName == 'DIV') {
  1225. //desc_extract(i);
  1226. //description += '\n';
  1227. }
  1228. }
  1229. }
  1230. }
  1231. description += '[/quote]';
  1232.  
  1233. function strip(str) {
  1234. return typeof str == 'string' ?
  1235. str.replace(/\s{2,}/g, ' ').replace(/[\n\r]+/, '').replace(/\s*\.{3}(?:less|more)\b/g, '').trim() : null;
  1236. }
  1237.  
  1238. i = html.querySelectorAll('div#details > div.row');
  1239. if (i != null) i.forEach(k => { description += '\n' + strip(k.innerText) });
  1240. description += '\n';
  1241.  
  1242. let details = html.querySelectorAll('div#bookDataBox > div.clearFloats');
  1243. for (var detail of details) {
  1244. var lbl = detail.children[0].textContent.trim();
  1245. var val = strip(detail.children[1].textContent);
  1246. if (lbl.search(/\b(?:ISBN)\b/i) >= 0 && ((matches = val.match(/\b(\d{13})\b/)) != null
  1247. || (matches = val.match(/\b(\d{10})\b/)) != null)) {
  1248. val = '[url=https://www.worldcat.org/isbn/' + matches[1] + ']' + strip(detail.children[1].textContent) + '[/url]';
  1249. }
  1250. description += '\n[b]' + lbl + ':[/b] ' + val;
  1251. }
  1252. description += '\n[b]More info:[/b] ' + response.finalUrl;
  1253. write_description(description);
  1254.  
  1255. if ((i = html.querySelector('div.editionCover > img')) != null) {
  1256. set_image(i.src.replace(/\?.*/, ''));
  1257. }
  1258.  
  1259. var cat = html.querySelectorAll('div.elementList > div.left');
  1260. if (cat != null) cat.forEach(x => { tags.add(x.textContent.trim()) });
  1261. if (tags.length > 0 && element_writable(ref = document.getElementById('tags'))) {
  1262. ref.value = tags.toString();
  1263. }
  1264. }, });
  1265. return true;
  1266. } else if (clipBoard.value.toLowerCase().indexOf('databazeknih.cz') >= 0) {
  1267. let url = clipBoard.value;
  1268. if (url.toLowerCase().indexOf('show=alldesc') < 0) {
  1269. if (!url.includes('?')) { url += '?show=alldesc' } else { url += '&show=alldesc' }
  1270. }
  1271. GM_xmlhttpRequest({ method: 'GET', url: url, onload: function(response) {
  1272. if (response.readyState != 4 || response.status != 200) return;
  1273. parser = new DOMParser();
  1274. html = parser.parseFromString(response.responseText, "text/html");
  1275.  
  1276. i = html.querySelectorAll('span[itemprop="author"] > a');
  1277. if (i != null && element_writable(ref = document.getElementById('title'))) {
  1278. description = join_authors(i);
  1279. i = html.querySelector('h1[itemprop="name"]');
  1280. if (i != null) description += ' - ' + i.textContent.trim();
  1281. i = html.querySelector('span[itemprop="datePublished"]');
  1282. if (i != null && (matches = i.textContent.match(/\b(\d{4})\b/)) != null) description += ' (' + matches[1] + ')';
  1283. ref.value = description;
  1284. }
  1285.  
  1286. description = '[quote]';
  1287. i = html.querySelector('p[itemprop="description"]');
  1288. if (i != null) desc_extract(i);
  1289.  
  1290. function desc_extract(node) {
  1291. for (var i of node.childNodes) {
  1292. if (i.nodeType == 3 || i.nodeName == 'P') {
  1293. //if (i.length < 10) continue;
  1294. description += i.textContent.trim();
  1295. } else if (i.nodeName == 'BR' || i.nodeName == 'HR') {
  1296. description += '\n';
  1297. } else if (i.nodeName == 'B' || i.nodeName == 'STRONG') {
  1298. description += '[b]' + i.textContent + '[/b]';
  1299. } else if (i.nodeName == 'I') {
  1300. description += '[i]' + i.textContent + '[/i]';
  1301. } else if (i.nodeName == 'DIV') {
  1302. //desc_extract(i);
  1303. //description += '\n';
  1304. }
  1305. }
  1306. }
  1307. description += '[/quote]';
  1308.  
  1309. let details = html.querySelectorAll('table.bdetail tr');
  1310. if (details != null) details.forEach(function(detail) {
  1311. var lbl = detail.children[0].textContent.trim();
  1312. var val = detail.children[1].textContent.trim();
  1313. if (lbl.search(/(?:žánr|\bvazba)\b/i) >= 0) return;
  1314. else if (lbl.search(/\b(?:orig)/i) >= 0) lbl = 'Original title';
  1315. else if (lbl.search(/\b(?:série)\b/i) >= 0) lbl = 'Series';
  1316. else if (lbl.search(/\b(?:vydáno)\b/i) >= 0) lbl = 'Released';
  1317. else if (lbl.search(/\b(?:stran)\b/i) >= 0) lbl = 'Page count';
  1318. else if (lbl.search(/\b(?:jazyk)\b/i) >= 0) lbl = 'Language';
  1319. else if (lbl.search(/\b(?:překlad)/i) >= 0) lbl = 'Translation';
  1320. else if (lbl.search(/\b(?:autor obálky)\b/i) >= 0) lbl = 'Cover author';
  1321. else if (lbl.search(/\b(?:ISBN)\b/i) >= 0 && (matches = val.match(/\b(\d+(?:-\d+)*)\b/)) != null) {
  1322. val = '[url=https://www.worldcat.org/isbn/' + matches[1].replace(/-/g, '') +
  1323. ']' + detail.children[1].textContent.trim() + '[/url]';
  1324. }
  1325. description += '\n[b]' + lbl + '[/b] ' + val;
  1326. });
  1327. description += '\n[b]More info:[/b] ' + response.finalUrl.replace(/\?.*/, '');
  1328. write_description(description);
  1329.  
  1330. if ((i = html.querySelector('div#icover_mid > a')) != null) set_image(i.href.replace(/\?.*/, ''));
  1331. if ((i = html.querySelector('div#lbImage')) != null
  1332. && (matches = i.style.backgroundImage.match(/\burl\("(.*)"\)/i)) != null) {
  1333. set_image(matches[1].replace(/\?.*/, ''));
  1334. }
  1335.  
  1336. var cat = html.querySelectorAll('h5[itemprop="genre"] > a');
  1337. if (cat != null) cat.forEach(x => { tags.add(x.textContent.trim()) });
  1338. cat = html.querySelectorAll('a.tag');
  1339. if (cat != null) cat.forEach(x => { tags.add(x.textContent.trim()) });
  1340. if (tags.length > 0 && element_writable(ref = document.getElementById('tags'))) {
  1341. ref.value = tags.toString();
  1342. }
  1343. }, });
  1344. return true;
  1345. }
  1346. return false;
  1347.  
  1348. function join_authors(nodeList) {
  1349. if (typeof nodeList != 'object') return null;
  1350. var authors = [];
  1351. nodeList.forEach(k => { authors.push(k.textContent.trim()) });
  1352. return authors.join(' & ');
  1353. }
  1354. }
  1355.  
  1356. function write_description(desc) {
  1357. if (typeof desc != 'string') return;
  1358. if (element_writable(ref = document.getElementById('desc'))) ref.value = desc;
  1359. if ((ref = document.getElementById('body')) != null && !ref.disabled) {
  1360. if (ref.textLength > 0) ref.value += '\n\n';
  1361. ref.value += desc;
  1362. }
  1363. }
  1364.  
  1365. function set_image(url) {
  1366. var image = document.getElementById('image');
  1367. if (!element_writable(image)) return false;
  1368. image.value = url;
  1369. var rehost_btn = document.querySelector('input.rehost_it_cover[type="button"]');
  1370. if (rehost_btn != null) {
  1371. rehost_btn.click();
  1372. } else {
  1373. var pr = rehost_imgs([url]);
  1374. if (pr != null) pr.then(new_urls => { image.value = new_urls[0] });
  1375. }
  1376. }
  1377.  
  1378. // PTPIMG rehoster taken from `PTH PTPImg It`
  1379. function rehost_imgs(urls) {
  1380. if (!Array.isArray(urls)) return null;;
  1381. var config = JSON.parse(window.localStorage.ptpimg_it);
  1382. return config.api_key ? new Promise(ptpimg_upload_urls).catch(m => { alert(m) }) : null;
  1383.  
  1384. function ptpimg_upload_urls(resolve, reject) {
  1385. const boundary = 'NN-GGn-PTPIMG';
  1386. var data = '--' + boundary + "\n";
  1387. data += 'Content-Disposition: form-data; name="link-upload"\n\n';
  1388. data += urls.map(function(url) {
  1389. return url.toLowerCase().indexOf('://reho.st/') < 0 && url.toLowerCase().indexOf('discogs.com') >= 0 ?
  1390. 'https://reho.st/' + url : url;
  1391. }).join('\n') + '\n';
  1392. data += '--' + boundary + '\n';
  1393. data += 'Content-Disposition: form-data; name="api_key"\n\n';
  1394. data += config.api_key + '\n';
  1395. data += '--' + boundary + '--';
  1396. GM_xmlhttpRequest({
  1397. method: 'POST',
  1398. url: 'https://ptpimg.me/upload.php',
  1399. responseType: 'json',
  1400. headers: {
  1401. 'Content-type': 'multipart/form-data; boundary=' + boundary,
  1402. },
  1403. data: data,
  1404. onload: response => {
  1405. if (response.status != 200) reject('Response error ' + response.status);
  1406. resolve(response.response.map(item => 'https://ptpimg.me/' + item.code + '.' + item.ext));
  1407. },
  1408. });
  1409. }
  1410. }
  1411.  
  1412. function element_writable(elem) { return elem != null && !elem.disabled && (overwrite || !elem.value) }
  1413. }
  1414.  
  1415. function add_artist() { exec(function() { AddArtistField() }) }
  1416.  
  1417. function array_homogenous(arr) { return arr.every(k => k === arr[0]) }
  1418.  
  1419. function exec(fn) {
  1420. let script = document.createElement('script');
  1421. script.type = 'application/javascript';
  1422. script.textContent = '(' + fn + ')();';
  1423. document.body.appendChild(script); // run the script
  1424. document.body.removeChild(script); // clean up
  1425. }
  1426.  
  1427. function make_time_string(duration) {
  1428. let t = Math.round(duration);
  1429. t = Math.abs(t);
  1430. let x = Math.floor(t / 60 ** 2);
  1431. let res;
  1432. if (x > 0) {
  1433. res = x + ':' + Math.floor(t / 60 % 60).toString().padStart(2, '0');
  1434. } else {
  1435. res = Math.floor(t / 60 % 60).toString();
  1436. }
  1437. return res + ':' + (t % 60).toString().padStart(2, '0');
  1438. }
  1439.  
  1440. function extract_year(expr) {
  1441. if (typeof expr != 'string') return null;
  1442. var year = parseInt(expr);
  1443. if (year > 0) return year;
  1444. var m = expr.match(/\b(\d{4})\b/);
  1445. return m && (year = parseInt(m[1])) > 0 ? year : null;
  1446. }
  1447.  
  1448. function isRED() { return document.domain.toLowerCase().endsWith('redacted.ch') }
  1449. function isNWCD() { return document.domain.toLowerCase().endsWith('notwhat.cd') }
  1450. function isOrpheus() { return document.domain.toLowerCase().endsWith('orpheus.network') }
  1451.  
  1452. function reInParenthesis(expr) { return new RegExp('\\s+\\([^\\(\\)]*'.concat(expr, '[^\\(\\)]*\\)$'), 'i') }
  1453. function reInBrackets(expr) { return new RegExp('\\s+\\[[^\\[\\]]*'.concat(expr, '[^\\[\\]]*\\]$'), 'i') }
  1454.  
  1455. function matchCaseless(str) { return str.toLowerCase() == this.toLowerCase() }
  1456.  
  1457. Array.prototype.includesCaseless = function(str) { return this.find(matchCaseless, str) != undefined };
  1458.  
  1459. function addWarning(text, bold = true, color = 'red') {
  1460. warnings = document.getElementById('UA_warnings');
  1461. if (warnings == null) {
  1462. var ua = document.getElementById('upload assistant');
  1463. if (ua == null) return null;
  1464. warnings = document.createElement('TR');
  1465. if (warnings == null) return null;
  1466. warnings.id = 'UA_warnings';
  1467. ua.children[0].append(warnings);
  1468.  
  1469. elem = document.createElement('TD');
  1470. if (elem == null) return null;
  1471. elem.colSpan = 2;
  1472. elem.style.paddingLeft = '15px';
  1473. elem.style.paddingRight = '15px';
  1474. elem.style.textAlign = 'left';
  1475. warnings.append(elem);
  1476. } else {
  1477. elem = warnings.children[0];
  1478. if (elem == null) return null;
  1479. }
  1480. var div = document.createElement('DIV');
  1481. if (color) div.style.color = color;
  1482. if (bold) {
  1483. div.appendChild(document.createElement('B')).textContent = text;
  1484. } else {
  1485. div.textContent = text;
  1486. }
  1487. return elem.appendChild(div);
  1488. }