From 2e6f3f9f0d2c365168f933b32a517694c62b8c1f Mon Sep 17 00:00:00 2001
From: YoGo9 <162524691+YoGo9@users.noreply.github.com>
Date: Tue, 15 Sep 2026 14:03:43 -0400
Subject: [PATCH 1/2] Update user script for batch adding recording aliases
---
BatchAddRecordingAliases.user.js | 641 +++++++++++++++++++++++++------
1 file changed, 520 insertions(+), 121 deletions(-)
diff --git a/BatchAddRecordingAliases.user.js b/BatchAddRecordingAliases.user.js
index b353a52..9102aba 100644
--- a/BatchAddRecordingAliases.user.js
+++ b/BatchAddRecordingAliases.user.js
@@ -4,15 +4,15 @@
* MIT License
*/
-/* global $ helper edits sidebar requests GM_info aliases */
+/* global $ helper edits requests aliases */
'use strict';
// ==UserScript==
// @name Batch Add Recording Aliases from another Release
// @namespace YoGo9
// @author YoGo9
-// @version 12/26/25
-// @description Paste a source release URL/MBID; copy its track titles as recording aliases on the current (target) release's recordings.
+// @version 2026.09.15
+// @description Copy track titles from another MusicBrainz release to recording aliases on the current release.
// @homepage https://github.com/YoGo9/Scripts
// @updateURL https://raw.githubusercontent.com/YoGo9/Scripts/main/BatchAddRecordingAliases.user.js
// @downloadURL https://raw.githubusercontent.com/YoGo9/Scripts/main/BatchAddRecordingAliases.user.js
@@ -23,13 +23,11 @@
// @grant GM_info
// @run-at document-end
// ==/UserScript==
-'use strict';
if (!/^\/release\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(location.pathname)) {
return;
}
-
(function () {
const HOST = location.origin;
const WS_HOST = 'https://musicbrainz.org';
@@ -51,18 +49,26 @@ if (!/^\/release\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
async function wsRelease(releaseMbid) {
const url = `${WS_HOST}/ws/2/release/${releaseMbid}?inc=recordings+media&fmt=json`;
- const r = await fetch(url, { headers: { Accept: 'application/json' } });
- if (!r.ok) throw new Error(`WS fetch failed ${r.status} for release ${releaseMbid}`);
+ const r = await fetch(url, {
+ headers: { Accept: 'application/json' },
+ });
+
+ if (!r.ok) {
+ throw new Error(`MusicBrainz returned HTTP ${r.status}`);
+ }
+
return r.json();
}
function flattenTracks(releaseJson) {
const out = [];
+
for (const medium of (releaseJson.media || [])) {
- const mPos = medium.position;
+ const mediumPosition = medium.position;
+
for (const track of (medium.tracks || [])) {
out.push({
- mediumPosition: mPos,
+ mediumPosition,
trackPosition: track.position,
trackTitle: track.title,
recordingMbid: track.recording?.id || null,
@@ -70,130 +76,423 @@ if (!/^\/release\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
});
}
}
+
return out;
}
function mapByRecording(tracks) {
- const m = new Map();
- for (const t of tracks) {
- if (t.recordingMbid) m.set(t.recordingMbid, t.trackTitle);
+ const map = new Map();
+
+ for (const track of tracks) {
+ if (track.recordingMbid) {
+ map.set(track.recordingMbid, track.trackTitle);
+ }
}
- return m;
+
+ return map;
}
function mapByPosition(tracks) {
- const m = new Map();
- for (const t of tracks) {
- m.set(`${t.mediumPosition}-${t.trackPosition}`, t.trackTitle);
+ const map = new Map();
+
+ for (const track of tracks) {
+ map.set(
+ `${track.mediumPosition}-${track.trackPosition}`,
+ track.trackTitle
+ );
}
- return m;
+
+ return map;
}
- function esc(s) {
- return String(s ?? '').replace(/[&<>"']/g, c => (
- { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]
- ));
+ function esc(value) {
+ return String(value ?? '').replace(/[&<>"']/g, char => ({
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": ''',
+ })[char]);
}
function editNote(sourceReleaseUrl) {
return `Batch Add Recording Alias from release ${sourceReleaseUrl}`;
}
- function injectUI() {
- const box = document.createElement('div');
- box.style.border = '1px solid #ccc';
- box.style.padding = '10px';
- box.style.margin = '10px 0';
- box.style.background = '#fff';
+ function injectStyles() {
+ const style = document.createElement('style');
+
+ style.textContent = `
+ #yomo-alias-launch {
+ margin-left: .5em;
+ white-space: nowrap;
+ }
+
+ #yomo-alias-panel {
+ display: none;
+ margin: 8px 0 12px;
+ padding: 8px 10px;
+ border: 1px solid #d8d8d8;
+ background: #fafafa;
+ font-size: 13px;
+ }
+
+ #yomo-alias-panel.yomo-open {
+ display: block;
+ }
+
+ #yomo-alias-panel .yomo-line {
+ display: flex;
+ align-items: center;
+ gap: 7px;
+ flex-wrap: wrap;
+ }
+
+ #yomo-alias-panel .yomo-line + .yomo-line {
+ margin-top: 6px;
+ }
+
+ #yomo-src {
+ flex: 1 1 420px;
+ min-width: 220px;
+ max-width: 720px;
+ }
+
+ #yomo-locale {
+ width: 54px;
+ }
+
+ #yomo-summary {
+ min-height: 18px;
+ margin-top: 6px;
+ }
+
+ #yomo-summary.yomo-error,
+ .yomo-row-error {
+ color: #a40000;
+ }
+
+ #yomo-summary.yomo-warning {
+ color: #9a6700;
+ }
+
+ #yomo-actions {
+ margin-left: auto;
+ }
+
+ #yomo-table-wrap {
+ display: none;
+ margin-top: 6px;
+ max-height: 330px;
+ overflow: auto;
+ border-top: 1px solid #ddd;
+ }
+
+ #yomo-table-wrap.yomo-visible {
+ display: block;
+ }
+
+ #yomo-table {
+ width: 100%;
+ margin-top: 5px;
+ }
+
+ #yomo-table th,
+ #yomo-table td {
+ padding: 3px 5px;
+ vertical-align: top;
+ }
+
+ #yomo-table th:first-child,
+ #yomo-table td:first-child {
+ width: 24px;
+ text-align: center;
+ }
+
+ #yomo-table .yomo-disc,
+ #yomo-table .yomo-track {
+ width: 44px;
+ white-space: nowrap;
+ }
+
+ #yomo-table .yomo-recording {
+ width: 43%;
+ }
+
+ #yomo-table .yomo-alias {
+ width: 43%;
+ }
+
+ #yomo-table tr.yomo-done {
+ opacity: .55;
+ }
+
+ #yomo-table tr.yomo-failed {
+ background: #fff2f2;
+ }
+
+ .yomo-row-error {
+ margin-top: 2px;
+ font-size: 11px;
+ }
+
+ #yomo-type-wrap select {
+ max-width: 145px;
+ }
+
+ @media (max-width: 700px) {
+ #yomo-actions {
+ margin-left: 0;
+ }
+
+ #yomo-alias-panel {
+ padding: 7px;
+ }
- box.innerHTML = `
-
-
Copy track titles → recording aliases
+ #yomo-table .yomo-recording,
+ #yomo-table .yomo-alias {
+ width: auto;
+ }
+ }
+ `;
-
+ document.head.appendChild(style);
+ }
-