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-30 提交的版本,查看 最新版本

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