Greasy Fork is available in English.
Auto sets track lengths for media by unique attached disc id.
当前为
// ==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 http://greasyfork.icu/users/321857
// @copyright 2024, Anakunda (http://greasyfork.icu/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
// ==/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);
});