diff --git a/README.md b/README.md
index 5d92e24..2ae006a 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,13 @@ Add a button on Bandcamp's album pages to open MusicBrainz release editor with p
[](https://github.com/murdos/musicbrainz-userscripts/blob/master/bandcamp_importer.user.js)
[](https://raw.github.com/murdos/musicbrainz-userscripts/master/bandcamp_importer.user.js)
+## Import Ampwall releases to MusicBrainz
+
+Add a button on Ampwall's album pages to open MusicBrainz release editor with pre-filled data for the selected release
+
+[](https://github.com/murdos/musicbrainz-userscripts/blob/master/ampwall_importer.user.js)
+[](https://raw.github.com/murdos/musicbrainz-userscripts/master/ampwall_importer.user.js)
+
##
Import Beatport releases to MusicBrainz
One-click importing of releases from beatport.com/release pages into MusicBrainz
@@ -139,6 +146,13 @@ Add a button on Qobuz's album pages to open MusicBrainz release editor with pre-
[](https://github.com/murdos/musicbrainz-userscripts/blob/master/qobuz_importer.user.js)
[](https://raw.github.com/murdos/musicbrainz-userscripts/master/qobuz_importer.user.js)
+## Import Subvert.fm releases to MusicBrainz
+
+Add a button on Subvert.fm's album pages to open MusicBrainz release editor with pre-filled data for the selected release
+
+[](https://github.com/murdos/musicbrainz-userscripts/blob/master/subvert_importer.user.js)
+[](https://raw.github.com/murdos/musicbrainz-userscripts/master/subvert_importer.user.js)
+
## Import Takealot releases to MusicBrainz
Add a button to import https://www.takealot.com/ releases to MusicBrainz via API
diff --git a/ampwall_importer.user.js b/ampwall_importer.user.js
new file mode 100644
index 0000000..8ba8266
--- /dev/null
+++ b/ampwall_importer.user.js
@@ -0,0 +1,145 @@
+// ==UserScript==
+// @name Import Ampwall releases to Musicbrainz
+// @description Add a button on Ampwall release pages to open MusicBrainz release editor with pre-filled data for the selected release
+// @version 2026.6.20
+// @license X11
+// @downloadURL https://raw.githubusercontent.com/murdos/musicbrainz-userscripts/master/ampwall_importer.user.js
+// @updateURL https://raw.githubusercontent.com/murdos/musicbrainz-userscripts/master/ampwall_importer.user.js
+// @namespace https://github.com/murdos/musicbrainz-userscripts
+// @match https://ampwall.com/a/*/album/*
+// @require lib/mbimport.js
+// @require lib/logger.js
+// @require lib/mbimportstyle.js
+// ==/UserScript==
+
+function onLoad() {
+ MBImportStyle();
+ const release = retrieveReleaseInfo();
+ insertLink(release, window.location);
+}
+
+function getArtistCredit(json) {
+ return MBImport.makeArtistCredits([json['byArtist']['name']]);
+}
+
+function getFormat() {
+ // We only support digital downloads right now.
+ return 'Digital Media';
+}
+
+function getPackaging() {
+ // We only support digital downloads right now.
+ return 'None';
+}
+
+function getReleaseType() {
+ // It appears that Ampwall treats all releases as albums so just
+ // return nothing for now and let the user decide.
+ return '';
+}
+
+function getTitle(json) {
+ return json['albumRelease'][0]['name'];
+}
+
+function getLabels() {
+ // TODO this will probably explode on releases with multiple labels.
+ const labels = document.querySelector('div[data-testid=labels-links] a');
+ if (labels === null) {
+ return [];
+ }
+ return [
+ {
+ name: labels.textContent,
+ catno: '',
+ },
+ ];
+}
+
+function getDiscs(json) {
+ let tracks = [];
+ json['track']['itemListElement'].forEach(function (track) {
+ tracks.push({
+ artist_credit: MBImport.makeArtistCredits([json['byArtist']['name']]),
+ title: track['name'],
+ duration: parseDuration(track['duration']),
+ });
+ });
+ return [
+ {
+ tracks: tracks,
+ format: getFormat(json),
+ },
+ ];
+}
+
+function getURLs(json) {
+ return [
+ {
+ url: json['url'],
+ link_type: MBImport.URL_TYPES.purchase_for_download,
+ },
+ ];
+}
+
+function parseDuration(s) {
+ // Durations are returned in the format "PT0H0M7S". Strip out the
+ // leading/trailing characters and convert the rest to colons.
+ return s.replace(/(^PT)|(S$)/g, '').replace(/[HM]/g, ':');
+}
+
+function retrieveReleaseInfo() {
+ const json = JSON.parse(document.querySelector("script[type='application/ld+json']").textContent);
+ const releaseDate = new Date(json['datePublished']);
+ const release = {
+ artist_credit: getArtistCredit(json),
+ title: getTitle(json),
+ year: releaseDate.getUTCFullYear(),
+ month: releaseDate.getUTCMonth() + 1,
+ day: releaseDate.getUTCDate(),
+ country: 'XW',
+ status: 'official',
+ language: 'eng',
+ script: 'Latn',
+ type: getReleaseType(json),
+ urls: getURLs(json),
+ labels: getLabels(),
+ discs: getDiscs(json),
+ packaging: getPackaging(),
+ format: getFormat(),
+ };
+ LOGGER.info('Parsed release: ', release);
+ return release;
+}
+
+// Insert button into page
+function insertLink(release) {
+ const edit_note = MBImport.makeEditNote(window.location, 'Ampwall');
+ const div = document.createElement('div');
+
+ const formButton = document.createElement('span');
+ formButton.className = 'tab-title musicbrainz-import';
+ formButton.innerHTML = MBImport.buildFormHTML(MBImport.buildFormParameters(release, edit_note));
+ formButton.style.display = 'inline-block';
+
+ const searchButton = document.createElement('span');
+ searchButton.className = 'tab-title musicbrainz-import';
+ searchButton.innerHTML = MBImport.buildSearchButton(release);
+ searchButton.style.display = 'inline-block';
+
+ div.appendChild(formButton);
+ div.appendChild(searchButton);
+
+ // Set a slight delay to stop breaking React/Next.
+ window.setTimeout(function () {
+ const container = document.querySelector('div.d_flex.ai_center.flex-wrap_wrap.gap_space1');
+ container.appendChild(formButton);
+ container.appendChild(searchButton);
+ }, 2000);
+}
+
+if (document.readyState !== 'loading') {
+ onLoad();
+} else {
+ document.addEventListener('DOMContentLoaded', onLoad);
+}
diff --git a/subvert_importer.user.js b/subvert_importer.user.js
new file mode 100644
index 0000000..ac0f997
--- /dev/null
+++ b/subvert_importer.user.js
@@ -0,0 +1,149 @@
+// ==UserScript==
+// @name Import Subvert.fm releases to Musicbrainz
+// @description Add a button on Subvert.fm release pages to open MusicBrainz release editor with pre-filled data for the selected release
+// @version 2026.6.20
+// @license X11
+// @downloadURL https://raw.githubusercontent.com/murdos/musicbrainz-userscripts/master/subvert_importer.user.js
+// @updateURL https://raw.githubusercontent.com/murdos/musicbrainz-userscripts/master/subvert_importer.user.js
+// @namespace https://github.com/murdos/musicbrainz-userscripts
+// @match https://subvert.fm/*/*
+// @match https://www.subvert.fm/*/*
+// @require lib/mbimport.js
+// @require lib/logger.js
+// @require lib/mbimportstyle.js
+// ==/UserScript==
+
+function onLoad() {
+ MBImportStyle();
+ const release = retrieveReleaseInfo();
+ insertLink(release, window.location);
+}
+
+function getArtistCredit(data) {
+ let artists = [];
+ data['release']['artists'].forEach(function (artist) {
+ artists.push(artist['name']);
+ });
+ return MBImport.makeArtistCredits(artists);
+}
+
+function getFormat() {
+ // We only support digital downloads right now.
+ return 'Digital Media';
+}
+
+function getPackaging() {
+ // We only support digital downloads right now.
+ return 'None';
+}
+
+function getReleaseType(data) {
+ return data['release']['releaseType'];
+}
+
+function getTitle(data) {
+ return data['release']['name'];
+}
+
+function getLabels(data) {
+ let labels = [];
+ data['release']['labelsOnReleases'].forEach(function (label) {
+ labels.push({
+ label: label['label']['name'],
+ catno: label['catalogNumber'],
+ });
+ });
+ return labels;
+}
+
+function getDiscs(data) {
+ let tracks = [];
+ data['release']['tracks'].forEach(function (track) {
+ let artists = [];
+ track['track']['artists'].forEach(function (artist) {
+ artists.push(artist['name']);
+ });
+ tracks.push({
+ artist_credit: MBImport.makeArtistCredits(artists),
+ title: track['track']['name'],
+ duration: track['track']['duration'] * 1000,
+ });
+ });
+ return [
+ {
+ tracks: tracks,
+ format: getFormat(data),
+ },
+ ];
+}
+
+function getURLs(data) {
+ let urls = [];
+ if (data['release']['isDownloadAllowed']) {
+ urls.push({
+ url: window.location,
+ link_type: MBImport.URL_TYPES.purchase_for_download,
+ });
+ }
+ if (data['release']['isStreamingAllowed']) {
+ urls.push({
+ url: window.location,
+ link_type: MBImport.URL_TYPES.stream_for_free,
+ });
+ }
+ if (data['release']['priceCents'] > 0) {
+ urls.push({
+ url: window.location,
+ link_type: MBImport.URL_TYPES.download_for_free,
+ });
+ }
+ return urls;
+}
+
+function retrieveReleaseInfo() {
+ let json = JSON.parse(document.querySelector('script#__NEXT_DATA__').textContent);
+
+ // Skip non-release pages
+ if (json['page'] !== '/[artistSlug]/[releaseSlug]') {
+ return;
+ }
+
+ const data = json['props']['pageProps'];
+ const releaseDate = new Date(data['release']['releaseDate']);
+ const release = {
+ artist_credit: getArtistCredit(data),
+ title: getTitle(data),
+ year: releaseDate.getUTCFullYear(),
+ month: releaseDate.getUTCMonth() + 1,
+ day: releaseDate.getUTCDate(),
+ country: 'XW',
+ status: 'official',
+ language: 'eng',
+ script: 'Latn',
+ type: getReleaseType(data),
+ urls: getURLs(data),
+ labels: getLabels(data),
+ discs: getDiscs(data),
+ packaging: getPackaging(data),
+ format: getFormat(data),
+ };
+ LOGGER.info('Parsed release: ', release);
+ return release;
+}
+
+// Insert button into page
+function insertLink(release, url) {
+ const edit_note = MBImport.makeEditNote(url, 'Subvert');
+ const container = document.querySelector('div.releaseRailMetaWrapper.leftRail.hideMobile');
+ const sourceNode = container.children[container.children.length - 3];
+ const importContainer = sourceNode.cloneNode(true);
+ importContainer.children[0].innerHTML = MBImport.buildFormHTML(MBImport.buildFormParameters(release, edit_note));
+ importContainer.children[1].innerHTML = MBImport.buildSearchButton(release);
+ sourceNode.after(importContainer);
+}
+
+if (document.readyState !== 'loading') {
+ onLoad();
+} else {
+ document.addEventListener('DOMContentLoaded', onLoad);
+}