Adds hotkeys to perform actions on selected entities. "A" = Artwork, "D" = Delete, "E" = Edit, "W" = Merge
当前为
// ==UserScript==
// @name MusicBrainz: Hotkeys for selected entities
// @namespace https://musicbrainz.org/user/chaban
// @version 1.0.1
// @description Adds hotkeys to perform actions on selected entities. "A" = Artwork, "D" = Delete, "E" = Edit, "W" = Merge
// @tag ai-created
// @author chaban
// @license MIT
// @include https://*musicbrainz.org/artist*
// @include https://*musicbrainz.org/area/*
// @include https://*musicbrainz.org/release-group/*
// @include https://*musicbrainz.org/label/*
// @include https://*musicbrainz.org/place/*
// @include https://*musicbrainz.org/isrc/*
// @include https://*musicbrainz.org/iswc/*
// @include https://*musicbrainz.org/report/*
// @include https://*musicbrainz.org/*/*/artists
// @include https://*musicbrainz.org/*/*/releases
// @include https://*musicbrainz.org/*/*/recordings
// @include https://*musicbrainz.org/*/*/release-groups
// @include https://*musicbrainz.org/*/*/events
// @include https://*musicbrainz.org/*/*/labels
// @include https://*musicbrainz.org/*/*/places
// @grant none
// ==/UserScript==
(function() {
'use strict';
const entityTypes = {
release: { actions: ['delete', 'edit', 'viewArtwork'] },
recording: { actions: ['delete', 'edit'] },
work: { actions: ['edit'] },
area: { actions: ['delete', 'edit'] },
instrument: { actions: ['delete', 'edit'] },
genre: { actions: ['delete', 'edit'] },
'release-group': { actions: ['edit'] },
event: { actions: ['edit', 'viewArtwork'] },
place: { actions: ['edit'] },
label: { actions: ['edit'] },
series: { actions: ['edit'] }
};
/**
* Extracts the entity type from the link.
* @param {HTMLAnchorElement} link The link element.
* @returns {string|null} The entity type or null if not detectable.
*/
function getEntityTypeFromLink(link) {
if (!link || !link.href) {
return null;
}
const href = link.href;
const parts = href.split('/').filter(Boolean);
const type = parts[2];
return entityTypes[type] ? type : null;
}
/**
* Extracts the MBID from a URL.
* @param {string} url The URL from which the MBID should be extracted.
* @returns {string|null} The MBID or null if not found.
*/
function extractMBID(url) {
const mbidRegex = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
const match = url.match(mbidRegex);
return match ? match[0] : null;
}
/**
* Opens pages based on action.
* @param {NodeListOf<HTMLInputElement>} checkboxes - Checkboxes of entities.
* @param {string} action - Type of action (edit, delete, viewArtwork).
*/
function openPages(checkboxes, action) {
checkboxes.forEach((checkbox, index) => {
const row = checkbox.closest('tr');
if (row) {
const entityLink = row.querySelector('a[href]');
const entityType = getEntityTypeFromLink(entityLink);
const mbid = extractMBID(entityLink.href);
if (entityType && entityTypes[entityType].actions.includes(action) && mbid) {
let url = `/${entityType}/${mbid}/${action}`;
if (action === 'viewArtwork') {
url = entityType === 'release' ? `/release/${mbid}/cover-art` : `/event/${mbid}/event-art`;
}
setTimeout(() => {
window.open(url, '_blank');
}, index * 1000);
}
}
});
}
/**
* Handles the keydown event for triggering actions.
* @param {KeyboardEvent} event - The keydown event.
*/
function handleKeyDown(event) {
const checkedSelector = 'input[name="add-to-merge"]:checked';
const checkboxes = document.querySelectorAll(checkedSelector);
switch (event.key) {
case 'w':
if (checkboxes.length > 1) {
const container = document.querySelector('.list-merge-buttons-row-container');
if (container) {
const buttons = container.querySelectorAll('button');
if (buttons.length > 0) {
buttons[buttons.length - 1].click();
}
}
}
break;
case 'd':
if (checkboxes.length > 0) {
openPages(checkboxes, 'delete');
}
break;
case 'e':
if (checkboxes.length > 0) {
openPages(checkboxes, 'edit');
}
break;
case 'a':
if (checkboxes.length > 0) {
openPages(checkboxes, 'viewArtwork');
}
break;
}
}
document.addEventListener('keydown', handleKeyDown);
})();