Skip to content

refactor(select all): migrate to TypeScript, drop jQuery #590

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 40 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# MB Userscripts

![GitHub Test Workflow Status](https://img.shields.io/github/workflow/status/ROpdebee/mb-userscripts/nightly%20tests?label=tests)
![GitHub Deployment Workflow Status](https://img.shields.io/github/workflow/status/ROpdebee/mb-userscripts/deploy?label=deployment)
![Codecov](https://img.shields.io/codecov/c/gh/ROpdebee/mb-userscripts)
Expand All @@ -22,19 +23,23 @@ Blinds editor and voter details before your votes are cast.
[![Install](https://img.shields.io/badge/install-latest-informational?style=for-the-badge&logo=tampermonkey)](mb_blind_votes.user.js?raw=1)
[![Source](https://img.shields.io/badge/source-grey?style=for-the-badge&logo=github)](mb_blind_votes.user.js)


## MB: Bulk copy-paste work codes

Quickly copy-paste work identifiers (ISWC, agency work codes) from [CISAC's ISWCNet](https://iswcnet.cisac.org/search) or [GEMA repertoire search](https://online.gema.de/werke/search.faces?lang=en) into a MusicBrainz work.

[![Install](https://img.shields.io/badge/install-latest-informational?style=for-the-badge&logo=tampermonkey)](mb_bulk_copy_work_codes.user.js?raw=1)
[![Source](https://img.shields.io/badge/source-grey?style=for-the-badge&logo=github)](mb_bulk_copy_work_codes.user.js)


## MB: Display CAA image dimensions

Loads and displays the image dimensions of images in the cover art archive.

[![Install](https://img.shields.io/badge/dynamic/json?label=install&query=%24.version&url=https%3A%2F%2Fraw.githubusercontent.com%2FROpdebee%2Fmb-userscripts%2Fdist%2Fmb_caa_dimensions.metadata.json&logo=tampermonkey&style=for-the-badge&color=informational)](https://raw.github.com/ROpdebee/mb-userscripts/dist/mb_caa_dimensions.user.js)
[![Install](https://img.shields.io/badge/dynamic/json?label=install&query=%24.version&url=https%3A%2F%2Fraw.github.com%2FROpdebee%2Fmb-userscripts%2Fdist%2Fmb_caa_dimensions.metadata.json&logo=tampermonkey&style=for-the-badge&color=informational)](https://raw.github.com/ROpdebee/mb-userscripts/dist/mb_caa_dimensions.user.js)
[![Source](https://img.shields.io/badge/source-grey?style=for-the-badge&logo=github)](src/mb_caa_dimensions)
[![Changelog](https://img.shields.io/badge/changelog-grey?style=for-the-badge)](https://github.com/ROpdebee/mb-userscripts/blob/dist/mb_caa_dimensions.changelog.md)


## MB: Enhanced Cover Art Uploads

Expand All @@ -49,48 +54,56 @@ In a nutshell:

Full list of supported artwork providers [here](src/mb_enhanced_cover_art_uploads/docs/supported_providers.md).

[![Install](https://img.shields.io/badge/dynamic/json?label=install&query=%24.version&url=https%3A%2F%2Fraw.githubusercontent.com%2FROpdebee%2Fmb-userscripts%2Fdist%2Fmb_enhanced_cover_art_uploads.metadata.json&logo=tampermonkey&style=for-the-badge&color=informational)](https://raw.github.com/ROpdebee/mb-userscripts/dist/mb_enhanced_cover_art_uploads.user.js)
[![Install](https://img.shields.io/badge/dynamic/json?label=install&query=%24.version&url=https%3A%2F%2Fraw.github.com%2FROpdebee%2Fmb-userscripts%2Fdist%2Fmb_enhanced_cover_art_uploads.metadata.json&logo=tampermonkey&style=for-the-badge&color=informational)](https://raw.github.com/ROpdebee/mb-userscripts/dist/mb_enhanced_cover_art_uploads.user.js)
[![Source](https://img.shields.io/badge/source-grey?style=for-the-badge&logo=github)](src/mb_enhanced_cover_art_uploads)
[![Changelog](https://img.shields.io/badge/changelog-grey?style=for-the-badge)](https://github.com/ROpdebee/mb-userscripts/blob/dist/mb_enhanced_cover_art_uploads.changelog.md)

## MB: Supercharged Cover Art Edits

Supercharges reviewing cover art edits. Displays release information on CAA edits. Enables image comparisons on removed and added images.

[![Install](https://img.shields.io/badge/install-latest-informational?style=for-the-badge&logo=tampermonkey)](mb_supercharged_caa_edits.user.js?raw=1)
[![Source](https://img.shields.io/badge/source-grey?style=for-the-badge&logo=github)](mb_supercharged_caa_edits.user.js)
## MB: QoL: Inline all recording's tracks on releases

## MB: Validate Work Codes
Display all tracks and releases on which a recording appears from the release page. Makes it easier to check whether live or DJ-mix recordings are wrongly linked to other tracks.

Validate work attributes on various MB pages. Highlights invalid (red) or ill-formatted (yellow) work codes.
[![Install](https://img.shields.io/badge/install-latest-informational?style=for-the-badge&logo=tampermonkey)](mb_qol_inline_recording_tracks.user.js?raw=1)
[![Source](https://img.shields.io/badge/source-grey?style=for-the-badge&logo=github)](mb_qol_inline_recording_tracks.user.js)

[![Install](https://img.shields.io/badge/install-latest-informational?style=for-the-badge&logo=tampermonkey)](mb_validate_work_codes.user.js?raw=1)
[![Source](https://img.shields.io/badge/source-grey?style=for-the-badge&logo=github)](mb_validate_work_codes.user.js)

## Smaller Quality of Life Scripts
Smaller scripts that offer minor QoL improvements.
## MB: QoL: Paste multiple external links at once

### MB: QoL: Select All Update Recordings
Add buttons to release editor to select all "Update recordings" checkboxes. Differs from the built-in "Select All" checkboxes in that it doesn't lock the checkboxes to a given state, enabling you to deselect some checkboxes.
Paste multiple external links at once into the external link editor. Input is split on whitespace (newlines, tabs, spaces, etc.) and fed into the link editor separately.

[![Install](https://img.shields.io/badge/install-latest-informational?style=for-the-badge&logo=tampermonkey)](mb_qol_select_all_update_recordings.user.js?raw=1)
[![Source](https://img.shields.io/badge/source-grey?style=for-the-badge&logo=github)](mb_qol_select_all_update_recordings.user.js)
[![Install](https://img.shields.io/badge/dynamic/json?label=install&query=%24.version&url=https%3A%2F%2Fraw.github.com%2FROpdebee%2Fmb-userscripts%2Fdist%2Fmb_multi_external_links.metadata.json&logo=tampermonkey&style=for-the-badge&color=informational)](https://raw.github.com/ROpdebee/mb-userscripts/dist/mb_multi_external_links.user.js)
[![Source](https://img.shields.io/badge/source-grey?style=for-the-badge&logo=github)](src/mb_multi_external_links)
[![Changelog](https://img.shields.io/badge/changelog-grey?style=for-the-badge)](https://github.com/ROpdebee/mb-userscripts/blob/dist/mb_multi_external_links.changelog.md)

### MB: QoL: Inline all recording's tracks on releases
Display all tracks and releases on which a recording appears from the release page. Makes it easier to check whether live or DJ-mix recordings are wrongly linked to other tracks.

[![Install](https://img.shields.io/badge/install-latest-informational?style=for-the-badge&logo=tampermonkey)](mb_qol_inline_recording_tracks.user.js?raw=1)
[![Source](https://img.shields.io/badge/source-grey?style=for-the-badge&logo=github)](mb_qol_inline_recording_tracks.user.js)
## MB: QoL: Seed the batch recording comments script

### MB: QoL: Seed the batch recording comments script
Seed the recording comments for the batch recording comments userscripts with live and DJ-mix data. Can save a bunch of keystrokes when setting live or DJ-mix disambiguation comments. DJ-mix comments are derived from the release title. Live comments are derived from "recorded at place", "recorded in area", and "recording of work" advanced relationships.

[![Install](https://img.shields.io/badge/install-latest-informational?style=for-the-badge&logo=tampermonkey)](mb_qol_seed_recording_disambiguation.user.js?raw=1)
[![Source](https://img.shields.io/badge/source-grey?style=for-the-badge&logo=github)](mb_qol_seed_recording_disambiguation.user.js)

### MB: QoL: Paste multiple external links at once
Paste multiple external links at once into the external link editor. Input is split on whitespace (newlines, tabs, spaces, etc.) and fed into the link editor separately.

[![Install](https://img.shields.io/badge/dynamic/json?label=install&query=%24.version&url=https%3A%2F%2Fraw.githubusercontent.com%2FROpdebee%2Fmb-userscripts%2Fdist%2Fmb_multi_external_links.metadata.json&logo=tampermonkey&style=for-the-badge&color=informational)](https://raw.github.com/ROpdebee/mb-userscripts/dist/mb_multi_external_links.user.js)
[![Source](https://img.shields.io/badge/source-grey?style=for-the-badge&logo=github)](src/mb_multi_external_links)
[![Changelog](https://img.shields.io/badge/changelog-grey?style=for-the-badge)](https://github.com/ROpdebee/mb-userscripts/blob/dist/mb_multi_external_links.changelog.md)
## MB: QoL: Select All Update Recordings

Add buttons to release editor to select all "Update recordings" checkboxes. Differs from the built-in "Select All" checkboxes in that it doesn't lock the checkboxes to a given state, enabling you to deselect some checkboxes.

[![Install](https://img.shields.io/badge/dynamic/json?label=install&query=%24.version&url=https%3A%2F%2Fraw.github.com%2FROpdebee%2Fmb-userscripts%2Fdist%2Fmb_qol_select_all_update_recordings.metadata.json&logo=tampermonkey&style=for-the-badge&color=informational)](https://raw.github.com/ROpdebee/mb-userscripts/dist/mb_qol_select_all_update_recordings.user.js)
[![Source](https://img.shields.io/badge/source-grey?style=for-the-badge&logo=github)](src/mb_qol_select_all_update_recordings)
[![Changelog](https://img.shields.io/badge/changelog-grey?style=for-the-badge)](https://github.com/ROpdebee/mb-userscripts/blob/dist/mb_qol_select_all_update_recordings.changelog.md)


## MB: Supercharged Cover Art Edits

Supercharges reviewing cover art edits. Displays release information on CAA edits. Enables image comparisons on removed and added images.

[![Install](https://img.shields.io/badge/install-latest-informational?style=for-the-badge&logo=tampermonkey)](mb_supercharged_caa_edits.user.js?raw=1)
[![Source](https://img.shields.io/badge/source-grey?style=for-the-badge&logo=github)](mb_supercharged_caa_edits.user.js)


## MB: Validate Work Codes

Validate work attributes on various MB pages. Highlights invalid (red) or ill-formatted (yellow) work codes.

[![Install](https://img.shields.io/badge/install-latest-informational?style=for-the-badge&logo=tampermonkey)](mb_validate_work_codes.user.js?raw=1)
[![Source](https://img.shields.io/badge/source-grey?style=for-the-badge&logo=github)](mb_validate_work_codes.user.js)
20 changes: 18 additions & 2 deletions build/build.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
import fs from 'node:fs/promises';

import { generateReadmeContent } from './generate-readme';
import { buildUserscripts } from './rollup';
import { getVersionForToday } from './versions';

async function checkReadmeContent(): Promise<void> {
const actualContent = await fs.readFile('README.md', 'utf8');
const expectedContent = await generateReadmeContent();

if (actualContent !== expectedContent) {
throw new Error('README content has changed. Please regenerate (npm run generate-readme).');
}
}

// Don't use await at the top level, this is incompatible with node and
// CommonJS modules.
buildUserscripts(getVersionForToday()).
catch(console.error);
buildUserscripts(getVersionForToday())
.then(checkReadmeContent)
.catch((err) => {
console.error(err);
process.exit(1);
});
147 changes: 147 additions & 0 deletions build/generate-readme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import fs from 'node:fs/promises';

import dedent from 'ts-dedent';

import { MetadataGenerator } from './plugin-userscript';

const PREAMBLE = dedent`
# MB Userscripts

![GitHub Test Workflow Status](https://img.shields.io/github/workflow/status/ROpdebee/mb-userscripts/nightly%20tests?label=tests)
![GitHub Deployment Workflow Status](https://img.shields.io/github/workflow/status/ROpdebee/mb-userscripts/deploy?label=deployment)
![Codecov](https://img.shields.io/codecov/c/gh/ROpdebee/mb-userscripts)
[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org)
[![GitHub license](https://img.shields.io/github/license/ROpdebee/mb-userscripts)](https://github.com/ROpdebee/mb-userscripts/blob/main/LICENSE)

Collection of userscripts for MusicBrainz.

[Dedicated support thread](https://community.metabrainz.org/t/ropdebees-userscripts-support-thread/551947)

## Installing

To use these userscripts, you need a userscript add-on or extension such as [Tampermonkey](https://www.tampermonkey.net/), [Violentmonkey](https://violentmonkey.github.io/), or [Greasemonkey](https://addons.mozilla.org/en-GB/firefox/addon/greasemonkey/) installed in your browser. More information can be found [here](https://stackapps.com/tags/script/info), [here](https://openuserjs.org/about/Userscript-Beginners-HOWTO), or [here](https://userscripts-mirror.org/about/installing.html).

_Note: Although we aim to support all browsers and userscript add-ons, we currently cannot guarantee universal compatibility. If you discover a compatibility problem, please [submit an issue](https://github.com/ROpdebee/mb-userscripts/issues/new) and state your browser and userscript engine versions._
`;

interface UserscriptData {
id: string;
name: string;
blurb: string;
urls?: {
metadata: string;
install: string;
source: string;
changelog: string;
};
}

const LEGACY_SCRIPT_DATA: UserscriptData[] = [
{
id: 'mb_blind_votes',
name: 'MB: Blind Votes',
blurb: 'Blinds editor and voter details before your votes are cast.',
},
{
id: 'mb_bulk_copy_work_codes',
name: 'MB: Bulk copy-paste work codes',
blurb: 'Quickly copy-paste work identifiers (ISWC, agency work codes) from [CISAC\'s ISWCNet](https://iswcnet.cisac.org/search) or [GEMA repertoire search](https://online.gema.de/werke/search.faces?lang=en) into a MusicBrainz work.',
},
{
id: 'mb_supercharged_caa_edits',
name: 'MB: Supercharged Cover Art Edits',
blurb: 'Supercharges reviewing cover art edits. Displays release information on CAA edits. Enables image comparisons on removed and added images.',
},
{
id: 'mb_validate_work_codes',
name: 'MB: Validate Work Codes',
blurb: 'Validate work attributes on various MB pages. Highlights invalid (red) or ill-formatted (yellow) work codes.',
},
{
id: 'mb_qol_inline_recording_tracks',
name: "MB: QoL: Inline all recording's tracks on releases",
blurb: 'Display all tracks and releases on which a recording appears from the release page. Makes it easier to check whether live or DJ-mix recordings are wrongly linked to other tracks.',
},
{
id: 'mb_qol_seed_recording_disambiguation',
name: 'MB: QoL: Seed the batch recording comments script',
blurb: 'Seed the recording comments for the batch recording comments userscripts with live and DJ-mix data. Can save a bunch of keystrokes when setting live or DJ-mix disambiguation comments. DJ-mix comments are derived from the release title. Live comments are derived from "recorded at place", "recorded in area", and "recording of work" advanced relationships.',
},
];

async function getUserscriptData(): Promise<UserscriptData[]> {
const srcDirs = await fs.readdir('./src');
const userscriptIds = srcDirs
.filter((name) => name.startsWith('mb'));

return Promise.all(userscriptIds
.map(async (userscriptId) => {
const metaGen = await MetadataGenerator.create({
userscriptName: userscriptId,
branchName: 'dist',
version: '',
});
const userscriptMeta = await metaGen.loadMetadata();

return {
id: userscriptId,
name: userscriptMeta.name,
blurb: userscriptMeta.blurb,
urls: {
metadata: metaGen.gitURLs.constructRawURL('dist', `${userscriptId}.metadata.json`),
install: metaGen.gitURLs.constructRawURL('dist', `${userscriptId}.user.js`),
source: `src/${userscriptId}`,
changelog: metaGen.gitURLs.constructBlobURL('dist', `${userscriptId}.changelog.md`),
},
};
}));
}

function generateSection(data: UserscriptData): string {
const badgeBase = 'https://img.shields.io/badge';
const installBadge = data.urls
? `${badgeBase}/dynamic/json?label=install&query=%24.version&url=${encodeURIComponent(data.urls.metadata)}&logo=tampermonkey&style=for-the-badge&color=informational`
: `${badgeBase}/install-latest-informational?style=for-the-badge&logo=tampermonkey`;
const installUrl = data.urls?.install ?? `${data.id}.user.js?raw=1`;
const sourceBadge = `${badgeBase}/source-grey?style=for-the-badge&logo=github`;
const sourceUrl = data.urls?.source ?? `${data.id}.user.js`;

let links = dedent`
[![Install](${installBadge})](${installUrl})
[![Source](${sourceBadge})](${sourceUrl})
`;
if (data.urls?.changelog) {
links += `\n[![Changelog](${badgeBase}/changelog-grey?style=for-the-badge)](${data.urls.changelog})`;
}

return dedent`
## ${data.name}

${data.blurb}

${links}

`;
}

export async function generateReadmeContent(): Promise<string> {
const userscriptData = await getUserscriptData();
userscriptData.push(...LEGACY_SCRIPT_DATA);
userscriptData.sort((a, b) => a.name < b.name ? -1 : 1);

return dedent`
${PREAMBLE}

${userscriptData.map((data) => generateSection(data)).join('\n\n')}
`;
}

async function generateReadme(): Promise<void> {
await fs.writeFile('README.md', await generateReadmeContent());
}

// eslint-disable-next-line unicorn/prefer-module
if (require.main === module) {
generateReadme()
.catch(console.error);
}
3 changes: 2 additions & 1 deletion build/plugin-userscript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export class MetadataGenerator {
*
* @return {Promise<UserscriptMetadata>} The userscript's metadata.
*/
private async loadMetadata(): Promise<AllUserscriptMetadata> {
public async loadMetadata(): Promise<AllUserscriptMetadata> {
const metadataFile = path.resolve('./src', this.options.userscriptName, 'meta.ts');
// eslint-disable-next-line no-unsanitized/method -- Fine.
const specificMetadata = (await import(metadataFile) as { default: UserscriptMetadata }).default;
Expand Down Expand Up @@ -205,6 +205,7 @@ export class MetadataGenerator {
*/
private createMetadataBlock(metadata: Readonly<AllUserscriptMetadata>): string {
const metadataLines = Object.entries<string | readonly string[]>(metadata)
.filter((entry) => this.options.metadataOrder.includes(entry[0]))
.sort((a: readonly [string, unknown], b: readonly [string, unknown]) =>
this.options.metadataOrder.indexOf(a[0]) - this.options.metadataOrder.indexOf(b[0]))
.flatMap(this.createMetadataLines.bind(this));
Expand Down
6 changes: 3 additions & 3 deletions mb_qol_select_all_update_recordings.user.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// ==UserScript==
// @name MB: QoL: Select All Update Recordings
// @version 2021.10.24
// @version 2022.10.14
// @description Add buttons to release editor to select all "Update recordings" checkboxes.
// @author ROpdebee
// @license MIT; https://opensource.org/licenses/MIT
// @namespace https://github.com/ROpdebee/mb-userscripts
// @downloadURL https://raw.github.com/ROpdebee/mb-userscripts/main/mb_qol_select_all_update_recordings.user.js
// @updateURL https://raw.github.com/ROpdebee/mb-userscripts/main/mb_qol_select_all_update_recordings.user.js
// @downloadURL https://raw.github.com/ROpdebee/mb-userscripts/dist/mb_qol_select_all_update_recordings.user.js
// @updateURL https://raw.github.com/ROpdebee/mb-userscripts/dist/mb_qol_select_all_update_recordings.meta.js
// @match *://*.musicbrainz.org/release/*/edit
// @match *://*.musicbrainz.org/release/add
// @require https://code.jquery.com/jquery-3.6.0.min.js
Expand Down
Loading