Last active
April 2, 2024 04:19
-
-
Save ToadKing/923288ca6e62cf9130828638ab6e0423 to your computer and use it in GitHub Desktop.
Musicbrainz - Combine Catalog Numbers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Musicbrainz - Combine Catalog Numbers | |
// @namespace com.toadking.mb.catalog | |
// @match https://musicbrainz.org/* | |
// @grant none | |
// @version 1.4 | |
// @author Toad King | |
// ==/UserScript== | |
const catNumRegExp = /^(.*?)(\d+)([^\d]*)$/ | |
function parseCatNums(catalogNumbers) { | |
let splitCatNums = [] | |
for (const catNum of catalogNumbers) { | |
const catnumSearch = catNum.match(catNumRegExp) | |
if (!catnumSearch) { | |
splitCatNums.push({ prefix: catNum, suffix: '' }) | |
} else { | |
const [, prefix, num, suffix] = catnumSearch | |
splitCatNums.push({ prefix, num, suffix }) | |
} | |
} | |
let parsedCatNums = [] | |
while (splitCatNums.length > 0) { | |
const { prefix, suffix, num } = splitCatNums[0] | |
// skip non-matching catalog numbers or number-only ones | |
if ((prefix == '' && suffix === '') || num === undefined) { | |
parsedCatNums.push(prefix + (num !== undefined ? num : '') + suffix) | |
splitCatNums.shift() | |
} else { | |
const matchedPrefixes = splitCatNums.filter(c => c.num !== undefined && c.prefix === prefix && c.suffix === suffix).sort((a, b) => Number(a.num) - Number(b.num)) | |
splitCatNums = splitCatNums.filter(c => c.num === undefined || c.prefix !== prefix) | |
const padSize = matchedPrefixes[0].num.length | |
const ranges = [] | |
let currentRange = { start: Number(matchedPrefixes[0].num), end: Number(matchedPrefixes[0].num) } | |
for (const catNum of matchedPrefixes.slice(1)) { | |
const num = Number(catNum.num) | |
if (num === currentRange.end + 1) { | |
currentRange.end = num | |
} else { | |
ranges.push(currentRange) | |
currentRange = { start: num, end: num } | |
} | |
} | |
ranges.push(currentRange) | |
for (const { start, end } of ranges) { | |
let endString = String(end).padStart(padSize, '0') | |
if (start === end) { | |
parsedCatNums.push(prefix + endString) | |
} else { | |
// use endString.length to handle cases like 99~100 | |
let startString = String(start).padStart(endString.length, '0') | |
while (startString.length > 0) { | |
if (startString[0] !== endString[0]) { | |
break | |
} | |
startString = startString.substring(1) | |
endString = endString.substring(1) | |
} | |
parsedCatNums.push(prefix + String(start).padStart(padSize, '0') + '~' + endString + suffix) | |
} | |
} | |
} | |
} | |
return parsedCatNums | |
} | |
const catalogLists = new Set(Array.from(document.querySelectorAll('.catalog-number')).map(e => e.parentElement)) | |
for (const listElem of catalogLists) { | |
const catalogNumbers = new Set(Array.from(listElem.querySelectorAll('.catalog-number')).map(e => e.textContent)) | |
// skip elements that contain only one catalog number (also includes label lists on release pages) | |
if (catalogNumbers.size <= 1) { | |
continue | |
} | |
const parsedCatNums = parseCatNums(catalogNumbers) | |
const newElems = parsedCatNums.map(c => { | |
const span = document.createElement('span') | |
span.className = 'catalog-number' | |
span.textContent = c | |
return span | |
}) | |
listElem.replaceChildren(newElems.shift()) | |
for (const elem of newElems) { | |
listElem.appendChild(document.createTextNode(', ')) | |
listElem.appendChild(elem) | |
} | |
} | |
// Tests | |
// const tests = [ | |
// // Long list | |
// { | |
// input: 'SBPS-0076, SBPS-0077, SBPS-0078, SBPS-0079, SBPS-0080, SBPS-0081, SBPS-0082, SBPS-0083, SBPS-0084, SBPS-0085, SBPS-0086, SBPS-0087, SBPS-0088, SBPS-0089, SBPS-0090, SBPS-0091, SBPS-0092, SBPS-0093, SBPS-0094, SBPS-0095'.split(', '), | |
// expected: ['SBPS-0076~95'], | |
// }, | |
// // Reverse order | |
// { | |
// input: 'VPCG-84243, VPCG-84242, VPCG-84241, VPCG-84240, VPCG-84239, VPCG-84238, VPCG-84237, VPCG-84236, VPCG-84235, VPCG-84234'.split(', '), | |
// expected: ['VPCG-84234~43'], | |
// }, | |
// // No dashes | |
// { | |
// input: 'LC2493, LC2494, LC2495, LC2496, LC2497, LC2498, LC2499, LC2500, LC2501, LC2502, LC2503, LC2504, LC2505'.split(', '), | |
// expected: ['LC2493~505'], | |
// }, | |
// // Dashes | |
// { | |
// input: 'GFCA-525, GFCA-526, GFCA-527, GFCA-528, GFCA-529, GFCA-530, GFCA-531, GFCA-532, GFCA-533, GFCA-534, GFCA-535, GFCA-536, GFCA-537'.split(', '), | |
// expected: ['GFCA-525~37'], | |
// }, | |
// // Split in ranges | |
// { | |
// input: 'TEST-01, TEST-02, TEST-04, TEST-05'.split(', '), | |
// expected: ['TEST-01~2', 'TEST-04~5'], | |
// }, | |
// // Padding increases | |
// { | |
// input: 'VTJL-10, VTJL-9'.split(', '), | |
// expected: ['VTJL-9~10'], | |
// }, | |
// // Numbers only | |
// { | |
// input: '1, 2, 3'.split(', '), | |
// expected: ['1', '2', '3'], | |
// }, | |
// // no catalog numbers | |
// { | |
// input: [], | |
// expected: [], | |
// }, | |
// // no numbers | |
// { | |
// input: 'foo, bar'.split(', '), | |
// expected: ['foo', 'bar'], | |
// }, | |
// // Prefix and Suffix | |
// { | |
// input: 'KBCI-00008A, KBCI-00009A, KBCI-00010A, KBCI-00011A, KBCI-00012A, KBCI-00013A'.split(', '), | |
// expected: ['KBCI-00008~13A'], | |
// }, | |
// // Suffix only | |
// { | |
// input: '00008A, 00009A, 00010A'.split(', '), | |
// expected: ['00008~10A'], | |
// }, | |
// // Two number areas | |
// { | |
// input: '1-00008, 1-00009, 1-00010'.split(', '), | |
// expected: ['1-00008~10'], | |
// }, | |
// // Two number areas with suffix | |
// { | |
// input: '1-00008A, 1-00009A, 1-00010A'.split(', '), | |
// expected: ['1-00008~10A'], | |
// }, | |
// ] | |
// for (const { input, expected } of tests) { | |
// const output = parseCatNums(input) | |
// if (JSON.stringify(output) !== JSON.stringify(expected)) { | |
// throw new Error(`test failed (expected ${expected}, got ${output})`) | |
// } | |
// } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment