The userscript discussion thread

[mod edit: moved from the new userscript thread, was in response to @dvirtz Event Seeder update post here]

Nice, for the sub event part: Can you also seed the name with “pattern” appended?
“Parent Event Name” → "Main Event Name, "
“Parent Event Name, [WeekdayName/Day X]” → "Main Event Name, [Weekdayname/Day X]: "

Examples:
Vainstream Rockfest, Saturday: Main Stage
Vainstream Rockfest, Day 2: Main Stage

And also the type of the parent event.

PS: Since I have seen this to late, I had vibe coded a version (But I think yours is better and maintained):

// ==UserScript==
// @name         MusicBrainz - Copy Event
// @namespace    https://musicbrainz.org/
// @version      0.1
// @description  Create a new event from an existing event using seeding
// @match        https://musicbrainz.org/event/*
// @grant        GM_openInTab
// ==/UserScript==

(function () {
    'use strict';

    if (!location.pathname.match(/^\/event\/[a-f0-9-]+/)) {
        return;
    }

    function addSidebarButton() {
    // Find sidebar section titled "Editing"
    const headings = [...document.querySelectorAll('h2')];

    const editingHeading = headings.find(
        h => h.textContent.trim() === 'Editing'
    );

    if (!editingHeading) {
        console.log('Copy Event: Editing section not found');
        return;
    }

    // MusicBrainz sidebar layout:
    // <h2>Editing</h2>
    // <ul>...</ul>
    const list = editingHeading.nextElementSibling;

    if (!list || list.tagName !== 'UL') {
        console.log('Copy Event: Editing list not found');
        return;
    }

    const li = document.createElement('li');

    const link = document.createElement('a');
    link.href = '#';
    link.textContent = 'Copy Event';

    link.addEventListener('click', async (e) => {
        e.preventDefault();

        const opts = await showDialog();
        if (!opts) return;

        try {
            const mbid =
                location.pathname.match(
                    /\/event\/([a-f0-9-]+)/
                )[1];

            const data = await fetch(
                `/ws/2/event/${mbid}?fmt=json&inc=artist-rels+event-rels+place-rels+series-rels`
            ).then(r => r.json());

            const seed = buildSeed(data, opts);

            GM_openInTab(seed, {
                active: true
            });

        } catch (err) {
            console.error(err);
            alert(err.message);
        }
    });

    li.appendChild(link);

    list.appendChild(li);
    }

    function showDialog() {
        const overlay = document.createElement('div');

        overlay.style = `
            position:fixed;
            inset:0;
            background:rgba(0,0,0,.35);
            z-index:9999;
        `;

        const box = document.createElement('div');

        box.style = `
            background:white;
            width:320px;
            padding:16px;
            border-radius:6px;
            margin:100px auto;
        `;

        box.innerHTML = `
            <h3>Copy Event</h3>

            <label>
                <input type="checkbox" id="series" checked>
                Series relations
            </label><br>

            <label>
                <input type="checkbox" id="place" checked>
                Place relations
            </label><br>

            <label>
                <input type="checkbox" id="artist">
                Artist relations
            </label><br>

            <label>
                <input type="checkbox" id="event">
                Event relations
            </label>

            <br><br>

            <button id="copy">Create</button>
            <button id="cancel">Cancel</button>
        `;

        overlay.appendChild(box);
        document.body.appendChild(overlay);

        return new Promise(resolve => {

            box.querySelector('#cancel').onclick = () => {
                overlay.remove();
                resolve(null);
            };

            box.querySelector('#copy').onclick = () => {

                const result = {
                    series: box.querySelector('#series').checked,
                    place: box.querySelector('#place').checked,
                    artist: box.querySelector('#artist').checked,
                    event: box.querySelector('#event').checked
                };

                overlay.remove();
                resolve(result);
            };
        });
    }

const EVENT_TYPE_IDS = {
    'Concert': 1,
    'Festival': 2,
    'Launch event': 3,
    'Convention/Expo': 4,
    'Masterclass/Clinic': 5,
    'Stage performance': 6,
    'Award ceremony': 7,
    'Competition': 40
};

function buildSeed(event, opts) {

    const params = new URLSearchParams();

    // Basic fields
    params.set(
        'edit-event.name',
        event.name || ''
    );

    if (event.disambiguation) {
        params.set(
            'edit-event.comment',
            event.disambiguation
        );
    }

    // Event type (MusicBrainz seed expects integer enum)
    if (event.type) {

        const typeId =
            EVENT_TYPE_IDS[event.type];

        if (typeId) {
            params.set(
                'edit-event.type_id',
                String(typeId)
            );
        } else {
            console.warn(
                'Unknown event type:',
                event.type
            );
        }
    }

    // Dates
    const span = event['life-span'];

    if (span?.begin) {
        copyDate(
            params,
            'edit-event.period.begin_date',
            span.begin
        );
    }

    if (span?.end) {
        copyDate(
            params,
            'edit-event.period.end_date',
            span.end
        );
    }

    // Optional edit note
    params.set(
        'edit-event.edit_note',
        'Seed was automatically created'
    );

    // Relations
    let relIndex = 0;

    for (const rel of event.relations || []) {

        if (
            (rel.target_type === 'series' && !opts.series) ||
            (rel.target_type === 'place' && !opts.place) ||
            (rel.target_type === 'artist' && !opts.artist) ||
            (rel.target_type === 'event' && !opts.event)
        ) {
            continue;
        }

        addRelationship(
            params,
            rel,
            relIndex++
        );
    }

    return (
        'https://musicbrainz.org/event/create?' +
        params.toString()
    );
}

function copyDate(
    params,
    prefix,
    date
) {

    const parts = date.split('-');

    params.set(
        `${prefix}.year`,
        parts[0] || ''
    );

    params.set(
        `${prefix}.month`,
        parts[1] || ''
    );

    params.set(
        `${prefix}.day`,
        parts[2] || ''
    );
}

function addRelationship(
    params,
    rel,
    index
) {

    const base =
        `rels.${index}`;

    // Relation type MBID
    if (rel['type-id']) {
        params.set(
            `${base}.type`,
            rel['type-id']
        );
    }

    // Target entity lookup
    const targetType =
        rel['target-type'];

    const target =
        targetType
            ? rel[targetType]
            : null;

    if (!target?.id) {
        console.warn(
            'Missing target id',
            rel
        );
        return;
    }

    params.set(
        `${base}.target`,
        target.id
    );

    // backward relations
    if (rel.direction === 'backward') {
        params.set(
            `${base}.backward`,
            '1'
        );
    }

    // optional relation dates
    if (rel.begin) {
        copyDate(
            params,
            `${base}.period.begin_date`,
            rel.begin
        );
    }

    if (rel.end) {
        copyDate(
            params,
            `${base}.period.end_date`,
            rel.end
        );
    }

    if (rel.ended) {
        params.set(
            `${base}.period.ended`,
            '1'
        );
    }
}

    addSidebarButton();

})();