Clicks specified buttons across tabs using the BroadcastChannel API.
当前为
// ==UserScript==
// @name Click buttons across tabs
// @namespace https://musicbrainz.org/user/chaban
// @version 1.0
// @tag ai-created
// @description Clicks specified buttons across tabs using the BroadcastChannel API.
// @author chaban
// @license MIT
// @match *://*.musicbrainz.org/*
// @match *://magicisrc.kepstin.ca/*
// @match *://magicisrc-beta.kepstin.ca/*
// @grant GM.info
// @grant GM_registerMenuCommand
// ==/UserScript==
(function () {
'use strict';
/**
* @typedef {Object} SiteConfig
* @property {string|string[]} hostnames - A single hostname string or an array of hostname strings.
* @property {string|string[]} paths - A single path string or an array of path strings (can be partial matches).
* @property {string} channelName - The name of the BroadcastChannel to use for this site.
* @property {string} messageTrigger - The message data that triggers the button click.
* @property {string} buttonSelector - The CSS selector for the button to be clicked.
* @property {string} menuCommandName - The name to display in the Tampermonkey/Greasemonkey menu.
*/
/**
* Configuration for different websites and their button click settings.
* @type {SiteConfig[]}
*/
const siteConfigurations = [
{
hostnames: 'musicbrainz.org',
paths: ['/edit', '/add-cover-art'],
channelName: 'mb_edit_channel',
messageTrigger: 'submit-edit',
buttonSelector: 'button.submit.positive[type="submit"]',
menuCommandName: 'MusicBrainz: Submit Edit (All Tabs)'
},
{
hostnames: ['magicisrc.kepstin.ca','magicisrc-beta.kepstin.ca'],
paths: ['/'],
channelName: 'magicisrc_submit_channel',
messageTrigger: 'submit-isrcs',
buttonSelector: '[onclick^="doSubmitISRCs"]',
menuCommandName: 'MagicISRC: Submit ISRCs (All Tabs)'
}
];
const scriptName = GM.info.script.name;
/**
* Sends a message to the specified BroadcastChannel.
* @param {string} channelName
* @param {string} message
*/
function sendMessageToChannel(channelName, message) {
try {
new BroadcastChannel(channelName).postMessage(message);
console.log(`[${scriptName}] Sent message "${message}" to channel "${channelName}".`);
} catch (error) {
console.error(`[${scriptName}] Error sending message to channel "${channelName}":`, error);
}
}
/**
* Initializes the BroadcastChannel listener for the current site if a match is found.
* Also registers a menu command if `GM_registerMenuCommand` is available.
*/
function initializeBroadcastChannelListener() {
const currentHostname = location.hostname;
const currentPathname = location.pathname;
for (const config of siteConfigurations) {
const hostnames = Array.isArray(config.hostnames) ? config.hostnames : [config.hostnames];
const paths = Array.isArray(config.paths) ? config.paths : [config.paths];
const hostnameMatches = hostnames.some(hostname => currentHostname.endsWith(hostname));
const pathMatches = paths.some(path => currentPathname.includes(path));
if (hostnameMatches && pathMatches) {
try {
const channel = new BroadcastChannel(config.channelName);
channel.addEventListener('message', (event) => {
if (event.data === config.messageTrigger) {
const btn = document.querySelector(config.buttonSelector);
if (btn) {
btn.click();
console.log(`[${scriptName}][${scriptName}] Button clicked for selector "${config.buttonSelector}" on channel "${config.channelName}".`);
} else {
console.warn(`[${scriptName}] Button with selector "${config.buttonSelector}" not found.`);
}
}
});
console.log(`[${scriptName}] Listener active for channel "${config.channelName}".`);
} catch (error) {
console.error(`[${scriptName}] Error initializing BroadcastChannel for "${config.channelName}":`, error);
}
if (typeof GM_registerMenuCommand !== 'undefined' && config.menuCommandName) {
GM_registerMenuCommand(config.menuCommandName, () => {
sendMessageToChannel(config.channelName, config.messageTrigger);
});
console.log(`[${scriptName}] Registered menu command "${config.menuCommandName}".`);
} else {
console.warn(`[${scriptName}] Couldn't register menu command "${config.menuCommandName}".`)
}
// Only activate one listener and register one command per tab for the first matching configuration
return;
}
}
}
initializeBroadcastChannelListener();
})();