// ==UserScript== // @name MB Auto Track Lengths // @version 1.01 // @match https://musicbrainz.org/release/*/discids // @match https://beta.musicbrainz.org/release/*/discids // @match https://musicbrainz.org/cdtoc/* // @match https://beta.musicbrainz.org/cdtoc/* // @run-at document-end // @author Anakunda // @namespace https://greasyfork.org/users/321857 // @copyright 2024, Anakunda (https://greasyfork.org/users/321857) // @license GPL-3.0-or-later // @description Auto sets track lengths for media by unique attached disc id. // @require https://openuserjs.org/src/libs/Anakunda/xhrLib.min.js // @downloadURL none // ==/UserScript== 'use strict'; const manualMode = false; const isDiscIdRow = tr => ['odd', 'even'].some(cls => tr.classList.contains(cls)); const getTrackLengths = tr => Array.prototype.find.call(tr.querySelectorAll('td a'), function(a) { //if (a.textContent.trim() == 'Set track lengths') return true; if (!a.pathname.startsWith('/cdtoc/') || !a.pathname.endsWith('/set-durations')) return false; const query = new URLSearchParams(a.search); return parseInt(query.get('medium')) > 0; }) || null; const sections = document.location.pathname.startsWith('/release/') ? Array.from(document.body.querySelectorAll('table.tbl > tbody > tr.subh'), function(subh) { const discIds = [ ]; for (let tr = subh.nextElementSibling; tr != null && isDiscIdRow(tr); tr = tr.nextElementSibling) discIds.push(getTrackLengths(tr)); return discIds; }) : document.location.pathname.startsWith('/cdtoc/') ? [Array.prototype.filter.call(document.body.querySelectorAll('table.tbl > tbody > tr'), isDiscIdRow) .map(getTrackLengths)] : null; if (sections != null) sections.forEach(function(section) { function setTrackLengths(link, autoRun, makeVotable = false) { console.assert(link instanceof HTMLAnchorElement); if (!(link instanceof HTMLAnchorElement)) throw 'Invalid argument'; if (link.disabled) return; else link.disabled = true; const animation = link.animate([ { offset: 0.0, opacity: 1 }, { offset: 0.4, opacity: 1 }, { offset: 0.5, opacity: 0.1 }, { offset: 0.9, opacity: 0.1 }, ], { duration: 600, iterations: Infinity }); localXHR(link.href).then(function(document) { const diffs = Array.from(document.body.querySelectorAll('div#page > table.wrap-block.details'), function(track) { const times = ['old', 'new'].map(function(cls) { if ((cls = track.querySelector('td.' + cls)) != null) cls = cls.textContent.trim(); else return NaN; return cls.split(':').reverse().reduce((total, time, index) => total + parseInt(time) * Math.pow(60, index), 0); }); return Math.abs(times[1] - times[0]); }); return Promise[diffs.some(diff => diff > 5) ? 'reject' : 'resolve'](diffs); }).then(function(diffs) { const postData = new URLSearchParams({ 'confirm.edit_note': '' }); if (makeVotable) postData.set('confirm.make_votable', 1); localXHR(link.href, { responseType: null }, postData).then(statusCode => ({ textContent: 'Track lengths successfully set', style: 'color: green;', title: 'Status code: ' + statusCode, }), reason => ({ textContent: 'Error setting track lengths', style: 'color: red;', title: reason, })).then(attributes => { link.replaceWith(Object.assign(document.createElement('span'), attributes)) }); }, function(diffs) { animation.cancel(); if (!autoRun) return document.location.assign(link.href); link.style.color = 'red'; link.disabled = false; link.title = '[ ' + diffs.join(', ') + ' ]'; }); } if (section.length > 1 || manualMode) return section.filter(Boolean).forEach(function(link) { link.onclick = function(evt) { setTrackLengths(evt.currentTarget, false); return false; }; }); if (section.length > 0) section = section.filter(Boolean); else return; if (section.length == 1) setTrackLengths(section[0], true); });