Skip to content

Commit eeafbd6

Browse files
committed
feat(seed): Support updating an existing release with external links
No deduplication of links is performed because Harmony does not cache the necessary data from the MB API. Changing the API include parameters would unfortunately be a breaking change for permalinks. Having link deduplication in MBS would be very useful: https://tickets.metabrainz.org/browse/MBS-13993
1 parent 48384a0 commit eeafbd6

File tree

4 files changed

+61
-20
lines changed

4 files changed

+61
-20
lines changed

musicbrainz/seeding.ts

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export interface ReleaseSeedOptions {
2727
redirectUrl?: URL;
2828
/** Base URL of the Harmony instance which was used to seed the release, for permalinks. */
2929
seederUrl?: URL;
30+
/** Indicates whether the seed will be used to update an existing release. */
31+
isUpdate?: boolean;
3032
/** Options for the annotation builder. */
3133
annotation?: AnnotationIncludes;
3234
}
@@ -53,6 +55,30 @@ export function createReleaseSeed(release: HarmonyRelease, options: ReleaseSeedO
5355
redirectUrl.search = lookupState.toString();
5456
}
5557

58+
const externalLinks: ReleaseUrlSeed[] = release.externalLinks.flatMap((link) =>
59+
link.types?.length
60+
? link.types.map((type) => ({
61+
url: link.url,
62+
link_type: convertLinkType('release', type, new URL(link.url)),
63+
}))
64+
: ({
65+
url: link.url,
66+
})
67+
);
68+
69+
if (options.isUpdate) {
70+
// Only seed external links for now, updating other properties is more controversial.
71+
// For some properties (such as the tracklist) seeding updates is even affected by bugs:
72+
// https://tickets.metabrainz.org/browse/MBS-13688
73+
const seed: Omit<ReleaseSeed, 'name'> = {
74+
urls: externalLinks,
75+
edit_note: buildEditNote(release.info, options),
76+
redirect_uri: redirectUrl?.href,
77+
};
78+
79+
return flatten(seed);
80+
}
81+
5682
const seed: ReleaseSeed = {
5783
name: release.title,
5884
artist_credit: convertArtistCredit(release.artists),
@@ -83,16 +109,7 @@ export function createReleaseSeed(release: HarmonyRelease, options: ReleaseSeedO
83109
})),
84110
language: release.language?.code,
85111
script: release.script?.code,
86-
urls: release.externalLinks.flatMap<ReleaseUrlSeed>((link) =>
87-
link.types?.length
88-
? link.types.map((type) => ({
89-
url: link.url,
90-
link_type: convertLinkType('release', type, new URL(link.url)),
91-
}))
92-
: ({
93-
url: link.url,
94-
})
95-
),
112+
urls: externalLinks,
96113
annotation: buildAnnotation(release, options.annotation),
97114
edit_note: buildEditNote(release.info, options),
98115
redirect_uri: redirectUrl?.href,
@@ -187,7 +204,7 @@ function buildEditNote(info: ReleaseInfo, options: ReleaseSeedOptions): string {
187204

188205
const { projectUrl, seederUrl } = options;
189206
const sourceUrl = seederUrl ? createReleasePermalink(info, seederUrl) : projectUrl;
190-
lines.unshift(`Imported with Harmony (${sourceUrl}), using data from:`);
207+
lines.unshift(`${options.isUpdate ? 'Updated' : 'Imported'} with Harmony (${sourceUrl}), using data from:`);
191208

192209
return lines.join('\n');
193210
}

server/islands/ReleaseSeeder.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ export function ReleaseSeeder({ release, sourceUrl, targetUrl, projectUrl }: {
1414
targetUrl: string;
1515
}) {
1616
const seederSourceUrl = sourceUrl ? new URL(sourceUrl) : undefined;
17+
const isUpdate = targetUrl.endsWith('/edit');
1718
const seed = createReleaseSeed(release, {
1819
projectUrl: new URL(projectUrl),
1920
redirectUrl: (seederSourceUrl && checkSetting('seeder.redirect', true))
2021
? new URL('release/actions', seederSourceUrl)
2122
: undefined,
2223
seederUrl: seederSourceUrl,
24+
isUpdate,
2325
annotation: {
2426
availability: checkSetting('annotation.availability', false),
2527
copyright: checkSetting('annotation.copyright', true),
@@ -28,12 +30,20 @@ export function ReleaseSeeder({ release, sourceUrl, targetUrl, projectUrl }: {
2830
});
2931

3032
return (
31-
<form action={targetUrl} method='post' target={getSetting('seeder.target', '_blank')} name='release-seeder'>
33+
<form
34+
action={targetUrl}
35+
method='post'
36+
target={getSetting('seeder.target', '_blank')}
37+
name={isUpdate ? 'release-update-seeder' : 'release-seeder'}
38+
>
3239
{Object.entries(seed).flatMap(([key, valueOrValues]) => {
3340
return preferArray(valueOrValues).map((value) => <input type='hidden' name={key} value={value} />);
3441
})}
35-
<InputWithOverlay type='submit' value='Import into MusicBrainz'>
36-
<SpriteIcon name='database-import' />
42+
<InputWithOverlay
43+
type='submit'
44+
value={isUpdate ? 'Update external links in MusicBrainz' : 'Import into MusicBrainz'}
45+
>
46+
<SpriteIcon name={isUpdate ? 'database-edit' : 'database-import'} />
3747
</InputWithOverlay>
3848
</form>
3949
);

server/routes/icon-sprite.svg.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import IconBrandTidal from 'tabler-icons/brand-tidal.tsx';
1010
import IconAlertTriangle from 'tabler-icons/alert-triangle.tsx';
1111
import IconBarcode from 'tabler-icons/barcode.tsx';
1212
import IconBug from 'tabler-icons/bug.tsx';
13+
import IconDatabaseEdit from 'tabler-icons/database-edit.tsx';
1314
import IconDatabaseImport from 'tabler-icons/database-import.tsx';
1415
import IconDisc from 'tabler-icons/disc.tsx';
1516
import IconHelp from 'tabler-icons/help.tsx';
@@ -32,6 +33,7 @@ const icons: Icon[] = [
3233
IconAlertTriangle,
3334
IconBarcode,
3435
IconBug,
36+
IconDatabaseEdit,
3537
IconDatabaseImport,
3638
IconDisc,
3739
IconHelp,

server/routes/release.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export default defineRoute(async (req, ctx) => {
2828
let releaseMap: ProviderReleaseMap | undefined;
2929
let enabledProviders: Set<string> | undefined = undefined;
3030
let gtinInput: GTIN = '', urlInput = '', regionsInput: string[] = [];
31+
let existingMBID: string | undefined;
3132

3233
try {
3334
const {
@@ -63,6 +64,7 @@ export default defineRoute(async (req, ctx) => {
6364

6465
const mbInfo = release.info.providers.find((provider) => provider.name === 'MusicBrainz');
6566
if (mbInfo) {
67+
existingMBID = mbInfo.id;
6668
release.info.messages.push({
6769
text:
6870
`Release with GTIN ${release.gtin} already exists on MusicBrainz ([show actions](release/actions?release_mbid=${mbInfo.id}))`,
@@ -120,12 +122,22 @@ export default defineRoute(async (req, ctx) => {
120122
))}
121123
{release && <Release release={release} releaseMap={releaseMap} />}
122124
{release && (
123-
<ReleaseSeeder
124-
release={release}
125-
projectUrl={codeUrl.href}
126-
sourceUrl={seederSourceUrl.href}
127-
targetUrl={seederTargetUrl.href}
128-
/>
125+
<div class='row'>
126+
<ReleaseSeeder
127+
release={release}
128+
projectUrl={codeUrl.href}
129+
sourceUrl={seederSourceUrl.href}
130+
targetUrl={seederTargetUrl.href}
131+
/>
132+
{existingMBID && (
133+
<ReleaseSeeder
134+
release={release}
135+
projectUrl={codeUrl.href}
136+
sourceUrl={seederSourceUrl.href}
137+
targetUrl={join(musicbrainzTargetServer, 'release', existingMBID, 'edit').href}
138+
/>
139+
)}
140+
</div>
129141
)}
130142
</main>
131143
</>

0 commit comments

Comments
 (0)