您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Highlights matching ISRCs in green and non-matches red.
// ==UserScript== // @name ISRC Hunt: Highlight ISRC matches and differences // @namespace https://musicbrainz.org/user/chaban // @version 1.2.1 // @description Highlights matching ISRCs in green and non-matches red. // @tag ai-created // @author chaban // @license MIT // @match *://isrchunt.com/spotify/importisrc* // @match *://isrchunt.com/deezer/importisrc* // @grant none // ==/UserScript== (function() { 'use strict'; /** * Injects a CSS string into the document head if it hasn't been injected already. * @param {string} id - A unique ID for the style element. * @param {string} css - The CSS string to inject. */ function addGlobalStyle(id, css) { if (!document.getElementById(id)) { const style = document.createElement('style'); style.id = id; style.textContent = css; document.head.appendChild(style); } } const isrcStyleId = 'isrc-highlight-userscript-style'; const isrcCss = ` .isrc-segment-container .isrc-part:not(.designation)::after { content: '\\2011'; } .isrc-base-style { font-family: monospace !important; font-size: 1.05em !important; white-space: nowrap !important; } .isrc-match-highlight { background-color: lightgreen !important; } .isrc-diff-highlight { background-color: salmon !important; } `; addGlobalStyle(isrcStyleId, isrcCss); /** * Parses a comma-separated string of ISRCs from a cell's text content, * normalizing them to uppercase for consistent display and comparison. * @param {string} textContent - The raw text content from the table cell. * @returns {string[]} An array of normalized (uppercase) ISRC strings. */ function parseIsrcs(textContent) { const trimmedText = textContent.trim(); if (!trimmedText) { return []; } return trimmedText.split(',').map(isrc => isrc.trim().toUpperCase()); } /** * Creates a <code> element containing <span> elements for each ISRC part. * Hyphens are rendered via CSS pseudo-elements for non-selection. * The text color is applied directly to this <code> element to ensure it takes precedence. * @param {string} isrc - The 12-character ISRC string (expected to be uppercase). * @param {string} textColor - The desired text color for this ISRC (always 'black' in this version). * @returns {HTMLElement} A <code> element with nested spans for ISRC segments. */ function formatIsrcForDisplay(isrc, textColor) { const codeContainer = document.createElement('code'); codeContainer.style.setProperty('color', textColor, 'important'); if (typeof isrc !== 'string' || isrc.length !== 12) { codeContainer.textContent = isrc; return codeContainer; } codeContainer.classList.add('isrc-segment-container'); const partLengths = [2, 3, 2, 5]; const partClasses = ['country', 'registrant', 'year', 'designation']; let currentIndex = 0; for (let i = 0; i < partLengths.length; i++) { const length = partLengths[i]; const part = isrc.substring(currentIndex, currentIndex + length); const span = document.createElement('span'); span.classList.add('isrc-part', partClasses[i]); span.textContent = part; codeContainer.appendChild(span); currentIndex += length; } return codeContainer; } /** * Highlights ISRCs within a given cell based on comparison with a set of other ISRCs, * using direct DOM manipulation for safer rendering and precise control. * @param {HTMLElement} cell - The table cell element to modify. * @param {string[]} isrcsToHighlightNormalized - Array of normalized (uppercase) ISRCs in this cell. * @param {Set<string>} comparisonSetNormalized - A Set of normalized (uppercase) ISRCs from the other cell for comparison. */ function highlightIsrcsInCell(cell, isrcsToHighlightNormalized, comparisonSetNormalized) { cell.innerHTML = ''; isrcsToHighlightNormalized.forEach((isrc, index) => { const isrcDisplayWrapper = document.createElement('span'); const bgColor = comparisonSetNormalized.has(isrc) ? 'lightgreen' : 'salmon'; const textColor = 'black'; isrcDisplayWrapper.classList.add('isrc-base-style'); isrcDisplayWrapper.style.setProperty('background-color', bgColor, 'important'); isrcDisplayWrapper.appendChild(formatIsrcForDisplay(isrc, textColor)); cell.appendChild(isrcDisplayWrapper); if (index < isrcsToHighlightNormalized.length - 1) { cell.appendChild(document.createTextNode(', ')); } }); } /** * Highlights ISRCs in two cells by comparing them against each other. * This function encapsulates the symmetrical calls to highlightIsrcsInCell. * @param {HTMLElement} cell1 - The first table cell. * @param {string[]} isrcs1Normalized - Normalized ISRCs for the first cell. * @param {HTMLElement} cell2 - The second table cell. * @param {string[]} isrcs2Normalized - Normalized ISRCs for the second cell. */ function crossHighlightCells(cell1, isrcs1Normalized, cell2, isrcs2Normalized) { const set1 = new Set(isrcs1Normalized); const set2 = new Set(isrcs2Normalized); highlightIsrcsInCell(cell1, isrcs1Normalized, set2); highlightIsrcsInCell(cell2, isrcs2Normalized, set1); } /** * Processes a single table row to highlight ISRCs in the Spotify and MusicBrainz cells. * @param {HTMLElement} row - The table row element to process. */ function processRowIsrcs(row) { const spotifyIsrcCell = row.querySelector('td:nth-child(4)'); const mbIsrcCell = row.querySelector('td:nth-child(7)'); if (spotifyIsrcCell && mbIsrcCell) { const spotifyIsrcsNormalized = parseIsrcs(spotifyIsrcCell.textContent); const mbIsrcsNormalized = parseIsrcs(mbIsrcCell.textContent); crossHighlightCells( spotifyIsrcCell, spotifyIsrcsNormalized, mbIsrcCell, mbIsrcsNormalized ); } } const table = document.querySelector('.table'); if (!table) { return; } const rows = table.querySelectorAll('tr'); for (let i = 1; i < rows.length; i++) { processRowIsrcs(rows[i]); } })();