add automatic country prioritization based on existing releases

This commit is contained in:
jeffvli
2026-02-07 13:36:17 -08:00
parent f43655ed5a
commit c4ecfeedec
6 changed files with 67 additions and 10 deletions
+6 -4
View File
@@ -897,12 +897,14 @@
"musicbrainz": "show MusicBrainz links",
"musicBrainzQueries": "enable MusicBrainz integration",
"musicBrainzQueries_description": "the integration will query MusicBrainz for missing artist releases and other miscellaneous data",
"musicbrainzExcludeReleaseTypes": "exclude MusicBrainz release types",
"musicbrainzExcludeReleaseTypes": "MusicBrainz release type exclusion",
"musicbrainzExcludeReleaseTypes_description": "release types to exclude when loading MusicBrainz artist releases",
"musicbrainzPrioritizeCountries": "prioritize MusicBrainz countries",
"musicbrainzPrioritizeCountries": "MusicBrainz country priority",
"musicbrainzPrioritizeCountries_description": "countries to prioritize when ordering MusicBrainz releases (first in list has highest priority)",
"youtube": "enable youtube integration",
"youtube_description": "external songs will attempt to use YouTube to resolve stream URLs for playback (desktop only)",
"musicbrainzAutoCountryPriority": "automatic country priority",
"musicbrainzAutoCountryPriority_description": "derive country priority from the artist's MusicBrainz releases (countries with more releases are ranked higher)",
"youtube": "enable YouTube playback",
"youtube_description": "external songs will attempt to use YouTube to resolve stream URLs (desktop only)",
"neteaseTranslation_description": "When enabled, fetches and displays translated lyrics from NetEase if available",
"neteaseTranslation": "Enable NetEase translations",
"notify": "enable song notifications",
+2
View File
@@ -275,6 +275,7 @@ export const queryKeys: Record<
limit: number | undefined,
mbzArtistId: string,
config?: {
autoCountryPriority: boolean;
excludeReleaseTypes: string[];
prioritizeCountries: string[];
},
@@ -286,6 +287,7 @@ export const queryKeys: Record<
limit,
config
? [
String(config.autoCountryPriority),
[...config.excludeReleaseTypes].sort().join(','),
[...config.prioritizeCountries].sort().join(','),
]
@@ -1381,6 +1381,9 @@ const ArtistAlbums = ({ albumsQuery }: ArtistAlbumsProps) => {
const musicBrainzExcludeReleaseTypes = useSettingsStore(
(state) => state.integrations.musicBrainzExcludeReleaseTypes,
);
const musicbrainzAutoCountryPriority = useSettingsStore(
(state) => state.integrations.musicbrainzAutoCountryPriority,
);
const musicBrainzPrioritizeCountries = useSettingsStore(
(state) => state.integrations.musicBrainzPrioritizeCountries,
);
@@ -1410,6 +1413,7 @@ const ArtistAlbums = ({ albumsQuery }: ArtistAlbumsProps) => {
const musicBrainzEnabled = useSettingsStore((state) => state.integrations.musicBrainz);
const musicbrainzArtistQuery = useQuery({
...musicbrainzQueries.artist({
autoCountryPriority: musicbrainzAutoCountryPriority,
excludeReleaseTypes: musicBrainzExcludeReleaseTypes,
mbzArtistId: detailQuery.data?.mbz as string,
prioritizeCountries: musicBrainzPrioritizeCountries,
@@ -1418,6 +1422,7 @@ const ArtistAlbums = ({ albumsQuery }: ArtistAlbumsProps) => {
meta: {
albumArtist: detailQuery.data,
albums: albumsQuery.data?.items || [],
autoCountryPriority: musicbrainzAutoCountryPriority,
excludeReleaseTypes: musicBrainzExcludeReleaseTypes,
prioritizeCountries: musicBrainzPrioritizeCountries,
},
@@ -40,6 +40,7 @@ export type IRelationWithWork = IRelation & { work?: IWork };
export type MusicBrainzArtistSelectMeta = {
albumArtist: AlbumArtist;
albums?: Album[];
autoCountryPriority?: boolean;
excludeReleaseTypes?: string[];
prioritizeCountries?: string[];
};
@@ -123,7 +124,22 @@ const artistSelect = memoize(
const excludeReleaseTypes = (meta.excludeReleaseTypes ?? []).map((t) => t.toLowerCase());
const excludeSet = new Set(excludeReleaseTypes);
const prioritizeCountries = (meta.prioritizeCountries ?? []).map((c) => c.toUpperCase());
let prioritizeCountries: string[];
if (meta.autoCountryPriority) {
const countryCounts = new Map<string, number>();
for (const release of data.releases.releases) {
const country = release.country?.toUpperCase();
if (country) {
countryCounts.set(country, (countryCounts.get(country) ?? 0) + 1);
}
}
prioritizeCountries = [...countryCounts.entries()]
.sort(([, a], [, b]) => b - a)
.map(([code]) => code);
} else {
prioritizeCountries = (meta.prioritizeCountries ?? []).map((c) => c.toUpperCase());
}
const releaseEntries = Array.from(unownedReleases.entries())
.filter(([, release]) => {
@@ -290,11 +306,13 @@ const EMPTY_BROWSE_RELEASES: IBrowseReleasesResult = {
export const musicbrainzQueries = {
artist: (args: {
autoCountryPriority?: boolean;
excludeReleaseTypes?: string[];
mbzArtistId: string;
prioritizeCountries?: string[];
}) => {
const config = {
autoCountryPriority: args.autoCountryPriority ?? false,
excludeReleaseTypes: args.excludeReleaseTypes ?? [],
prioritizeCountries: args.prioritizeCountries ?? [],
};
@@ -378,6 +378,27 @@ export const IntegrationsTab = memo(() => {
isHidden: !musicBrainz || !settings.musicBrainz,
title: t('setting.musicbrainzPrioritizeCountries', { postProcess: 'sentenceCase' }),
},
{
control: (
<Switch
aria-label={t('setting.musicbrainzAutoCountryPriority', {
postProcess: 'sentenceCase',
})}
defaultChecked={settings.musicbrainzAutoCountryPriority}
onChange={(e) =>
updateIntegrations({
musicbrainzAutoCountryPriority: e.currentTarget.checked,
})
}
/>
),
description: t('setting.musicbrainzAutoCountryPriority', {
context: 'description',
postProcess: 'sentenceCase',
}),
isHidden: !musicBrainz || !settings.musicBrainz,
title: t('setting.musicbrainzAutoCountryPriority', { postProcess: 'sentenceCase' }),
},
{
control: (
<Switch
@@ -397,10 +418,7 @@ export const IntegrationsTab = memo(() => {
return (
<Stack gap="md">
<SettingsSection
options={options}
title={t('page.setting.integrationsTab', { postProcess: 'sentenceCase' })}
/>
<SettingsSection options={options} title={'MusicBrainz'} />
</Stack>
);
});
+13 -1
View File
@@ -614,6 +614,7 @@ const QueryBuilderSettingsSchema = z.object({
});
const IntegrationsSettingsSchema = z.object({
musicbrainzAutoCountryPriority: z.boolean(),
musicBrainz: z.boolean(),
musicBrainzExcludeReleaseTypes: z.array(z.string()),
musicBrainzPrioritizeCountries: z.array(z.string()),
@@ -1105,6 +1106,7 @@ const initialState: SettingsState = {
globalMediaHotkeys: true,
},
integrations: {
musicbrainzAutoCountryPriority: false,
musicBrainz: true,
musicBrainzExcludeReleaseTypes: [],
musicBrainzPrioritizeCountries: [],
@@ -2108,6 +2110,7 @@ export const useSettingsStore = createWithEqualityFn<SettingsSlice>()(
// Move MusicBrainz release/country options to Integrations; keep musicBrainz in general
const general = state.general as Record<string, unknown>;
state.integrations = {
musicbrainzAutoCountryPriority: initialState.integrations.musicbrainzAutoCountryPriority,
musicBrainz: initialState.integrations.musicBrainz,
musicBrainzExcludeReleaseTypes:
(general?.musicBrainzExcludeReleaseTypes as string[]) ??
@@ -2130,10 +2133,19 @@ export const useSettingsStore = createWithEqualityFn<SettingsSlice>()(
};
}
if (version <= 26) {
state.integrations = {
...state.integrations,
musicbrainzAutoCountryPriority:
(state.integrations as { musicbrainzAutoCountryPriority?: boolean })
?.musicbrainzAutoCountryPriority ?? false,
};
}
return persistedState;
},
name: 'store_settings',
version: 26,
version: 27,
},
),
);