mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-08 04:50:12 +02:00
add settings configuration for integrations
This commit is contained in:
@@ -583,6 +583,7 @@
|
||||
"analytics": "analytics",
|
||||
"generalTab": "general",
|
||||
"hotkeysTab": "hotkeys",
|
||||
"integrationsTab": "integrations",
|
||||
"playbackTab": "playback",
|
||||
"windowTab": "window",
|
||||
"updates": "update",
|
||||
@@ -893,6 +894,14 @@
|
||||
"mpvExtraParameters_help": "one per line",
|
||||
"musicbrainz_description": "show links to MusicBrainz on artist/album pages, where MusicBrainz ID exists",
|
||||
"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_description": "release types to exclude when loading MusicBrainz artist releases (e.g. album, single, ep)",
|
||||
"musicbrainzPrioritizeCountries": "prioritize MusicBrainz countries",
|
||||
"musicbrainzPrioritizeCountries_description": "country codes to prioritize when ordering MusicBrainz releases, comma separated and non case-sensitive (e.g. us, gb, de)",
|
||||
"youtube": "enable youtube integration",
|
||||
"youtube_description": "external songs will attempt to use YouTube to resolve stream URLs for playback",
|
||||
"neteaseTranslation_description": "When enabled, fetches and displays translated lyrics from NetEase if available",
|
||||
"neteaseTranslation": "Enable NetEase translations",
|
||||
"notify": "enable song notifications",
|
||||
|
||||
@@ -1379,10 +1379,10 @@ const ArtistAlbums = ({ albumsQuery }: ArtistAlbumsProps) => {
|
||||
const { t } = useTranslation();
|
||||
const artistReleaseTypeItems = useArtistReleaseTypeItems();
|
||||
const musicBrainzExcludeReleaseTypes = useSettingsStore(
|
||||
(state) => state.general.musicBrainzExcludeReleaseTypes,
|
||||
(state) => state.integrations.musicBrainzExcludeReleaseTypes,
|
||||
);
|
||||
const musicBrainzPrioritizeCountries = useSettingsStore(
|
||||
(state) => state.general.musicBrainzPrioritizeCountries,
|
||||
(state) => state.integrations.musicBrainzPrioritizeCountries,
|
||||
);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [debouncedSearchTerm] = useDebouncedValue(searchTerm, 300);
|
||||
@@ -1407,12 +1407,14 @@ const ArtistAlbums = ({ albumsQuery }: ArtistAlbumsProps) => {
|
||||
}),
|
||||
);
|
||||
|
||||
const musicBrainzEnabled = useSettingsStore((state) => state.integrations.musicBrainz);
|
||||
const musicbrainzArtistQuery = useQuery({
|
||||
...musicbrainzQueries.artist({
|
||||
excludeReleaseTypes: musicBrainzExcludeReleaseTypes,
|
||||
mbzArtistId: detailQuery.data?.mbz as string,
|
||||
prioritizeCountries: musicBrainzPrioritizeCountries,
|
||||
}),
|
||||
enabled: Boolean(musicBrainzEnabled && detailQuery.data?.mbz),
|
||||
meta: {
|
||||
albumArtist: detailQuery.data,
|
||||
albums: albumsQuery.data?.items || [],
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
getImageUrl,
|
||||
normalizeReleaseToAlbum,
|
||||
} from '/@/renderer/features/musicbrainz/utils';
|
||||
import { logFn } from '/@/renderer/utils/logger';
|
||||
import {
|
||||
Album,
|
||||
AlbumArtist,
|
||||
@@ -283,6 +284,12 @@ const RELEASE_INCLUDES: Array<
|
||||
| 'release-groups'
|
||||
> = ['artist-credits', 'artists', 'media', 'recording-level-rels', 'recordings', 'release-groups'];
|
||||
|
||||
const EMPTY_BROWSE_RELEASES: IBrowseReleasesResult = {
|
||||
'release-count': 0,
|
||||
'release-offset': 0,
|
||||
releases: [],
|
||||
};
|
||||
|
||||
export const musicbrainzQueries = {
|
||||
artist: (args: {
|
||||
excludeReleaseTypes?: string[];
|
||||
@@ -297,13 +304,30 @@ export const musicbrainzQueries = {
|
||||
return queryOptions({
|
||||
gcTime: CACHE_TIME,
|
||||
queryFn: async ({ meta }) => {
|
||||
const artist = await musicbrainzApi.lookup('artist', args.mbzArtistId);
|
||||
const releases = await fetchMbzReleasesByArtistId(args.mbzArtistId);
|
||||
try {
|
||||
const artist = await musicbrainzApi.lookup('artist', args.mbzArtistId);
|
||||
const releases = await fetchMbzReleasesByArtistId(args.mbzArtistId);
|
||||
|
||||
return {
|
||||
data: { artist, releases },
|
||||
meta: meta as MusicBrainzArtistSelectMeta,
|
||||
};
|
||||
logFn.debug('MusicBrainz artist lookup API queried', {
|
||||
meta: { artistId: args.mbzArtistId, releases },
|
||||
});
|
||||
|
||||
return {
|
||||
data: { artist, releases },
|
||||
meta: meta as MusicBrainzArtistSelectMeta,
|
||||
};
|
||||
} catch (error) {
|
||||
logFn.warn('MusicBrainz artist lookup failed', {
|
||||
meta: { artistId: args.mbzArtistId, error },
|
||||
});
|
||||
return {
|
||||
data: {
|
||||
artist: {} as IArtist,
|
||||
releases: EMPTY_BROWSE_RELEASES,
|
||||
},
|
||||
meta: meta as MusicBrainzArtistSelectMeta,
|
||||
};
|
||||
}
|
||||
},
|
||||
queryKey: queryKeys.musicbrainz.artist(undefined, args.mbzArtistId, config),
|
||||
select: artistSelect,
|
||||
@@ -314,14 +338,26 @@ export const musicbrainzQueries = {
|
||||
queryOptions({
|
||||
gcTime: CACHE_TIME,
|
||||
queryFn: async () => {
|
||||
const mbzRelease = await musicbrainzApi.lookup(
|
||||
'release',
|
||||
args.releaseId,
|
||||
RELEASE_INCLUDES,
|
||||
);
|
||||
const release = normalizeReleaseToAlbum(mbzRelease);
|
||||
const works = collectWorksFromRelease(mbzRelease);
|
||||
return { release, works };
|
||||
try {
|
||||
const mbzRelease = await musicbrainzApi.lookup(
|
||||
'release',
|
||||
args.releaseId,
|
||||
RELEASE_INCLUDES,
|
||||
);
|
||||
const release = normalizeReleaseToAlbum(mbzRelease);
|
||||
const works = collectWorksFromRelease(mbzRelease);
|
||||
|
||||
logFn.debug('MusicBrainz release lookup API queried', {
|
||||
meta: { release, releaseId: args.releaseId },
|
||||
});
|
||||
|
||||
return { release, works };
|
||||
} catch (error) {
|
||||
logFn.warn('MusicBrainz release lookup failed', {
|
||||
meta: { error, releaseId: args.releaseId },
|
||||
});
|
||||
return { release: null, works: [] };
|
||||
}
|
||||
},
|
||||
queryKey: queryKeys.musicbrainz.release(args.releaseId),
|
||||
staleTime: CACHE_TIME,
|
||||
@@ -331,6 +367,52 @@ export const musicbrainzQueries = {
|
||||
export const MUSICBRAINZ_ID_PREFIX = 'musicbrainz-';
|
||||
|
||||
export async function fetchMbzReleaseAsAlbum(releaseId: string): Promise<Album> {
|
||||
const mbzRelease = await musicbrainzApi.lookup('release', releaseId, RELEASE_INCLUDES);
|
||||
return normalizeReleaseToAlbum(mbzRelease);
|
||||
try {
|
||||
const mbzRelease = await musicbrainzApi.lookup('release', releaseId, RELEASE_INCLUDES);
|
||||
return normalizeReleaseToAlbum(mbzRelease);
|
||||
} catch (error) {
|
||||
logFn.warn('MusicBrainz release fetch failed', { meta: { error, releaseId } });
|
||||
return createEmptyMbzAlbum(releaseId);
|
||||
}
|
||||
}
|
||||
|
||||
function createEmptyMbzAlbum(releaseId: string): Album {
|
||||
return {
|
||||
_itemType: LibraryItem.ALBUM,
|
||||
_serverId: 'musicbrainz',
|
||||
_serverType: ServerType.EXTERNAL,
|
||||
albumArtistName: '',
|
||||
albumArtists: [],
|
||||
artists: [],
|
||||
comment: null,
|
||||
createdAt: '',
|
||||
duration: null,
|
||||
explicitStatus: null,
|
||||
genres: [],
|
||||
id: `musicbrainz-${releaseId}`,
|
||||
imageId: null,
|
||||
imageUrl: null,
|
||||
isCompilation: null,
|
||||
lastPlayedAt: null,
|
||||
mbzId: releaseId,
|
||||
mbzReleaseGroupId: null,
|
||||
name: '',
|
||||
originalDate: null,
|
||||
originalYear: null,
|
||||
participants: {},
|
||||
playCount: null,
|
||||
recordLabels: [],
|
||||
releaseDate: null,
|
||||
releaseType: null,
|
||||
releaseTypes: [],
|
||||
releaseYear: null,
|
||||
size: null,
|
||||
songCount: null,
|
||||
sortName: '',
|
||||
tags: {},
|
||||
updatedAt: '',
|
||||
userFavorite: false,
|
||||
userRating: null,
|
||||
version: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { queryOptions } from '@tanstack/react-query';
|
||||
|
||||
import { logFn } from '/@/renderer/utils/logger';
|
||||
|
||||
async function searchYoutube(query: string): Promise<Array<{ type: string; videoId?: string }>> {
|
||||
if (typeof window !== 'undefined' && window.api?.youtube) {
|
||||
return window.api.youtube.search(query);
|
||||
@@ -11,7 +13,11 @@ export const youtubeQueries = {
|
||||
search: (args: { query: string }) => {
|
||||
return queryOptions({
|
||||
gcTime: 1000 * 60 * 1,
|
||||
queryFn: () => searchYoutube(args.query),
|
||||
queryFn: async () => {
|
||||
const results = await searchYoutube(args.query);
|
||||
logFn.debug('Youtube API queried', { meta: { query: args.query, results } });
|
||||
return results;
|
||||
},
|
||||
queryKey: ['youtube', 'search', args.query],
|
||||
staleTime: 1000 * 60 * 1,
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useMemo, useRef } from 'react';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { youtubeQueries } from '/@/renderer/features/musicbrainz/api/youtube-api';
|
||||
import { TranscodingConfig } from '/@/renderer/store';
|
||||
import { TranscodingConfig, useSettingsStore } from '/@/renderer/store';
|
||||
import { QueueSong, ServerType } from '/@/shared/types/domain-types';
|
||||
|
||||
const YOUTUBE_WATCH_BASE = 'https://www.youtube.com/watch?v=';
|
||||
@@ -16,12 +16,13 @@ export function useSongUrl(
|
||||
const prior = useRef(['', '']);
|
||||
|
||||
const isExternal = song?._serverType === ServerType.EXTERNAL;
|
||||
const youtubeEnabled = useSettingsStore((state) => state.integrations.youtube);
|
||||
const searchQuery =
|
||||
song && isExternal ? `${song.artistName ?? ''} ${song.name ?? ''}`.trim() : '';
|
||||
song && isExternal ? buildYoutubeSearchQuery(song.name, song.artistName) : '';
|
||||
|
||||
const youtubeSearch = useQuery({
|
||||
...youtubeQueries.search({ query: searchQuery }),
|
||||
enabled: Boolean(song && isExternal && searchQuery),
|
||||
enabled: Boolean(song && isExternal && searchQuery && youtubeEnabled),
|
||||
});
|
||||
|
||||
const externalUrl = useMemo(() => {
|
||||
@@ -77,6 +78,16 @@ export function useSongUrl(
|
||||
]);
|
||||
}
|
||||
|
||||
function buildYoutubeSearchQuery(
|
||||
title: string | undefined,
|
||||
artistName: string | undefined,
|
||||
): string {
|
||||
const t = (title ?? '').trim();
|
||||
const a = (artistName ?? '').trim();
|
||||
if (t && a) return `${t} by ${a}`;
|
||||
return t || a || '';
|
||||
}
|
||||
|
||||
function getYoutubeUrlFromSearchResults(
|
||||
results: Array<{ type: string; videoId?: string }> | undefined,
|
||||
): string | undefined {
|
||||
@@ -112,10 +123,11 @@ export async function getSongUrlAsync(
|
||||
}
|
||||
|
||||
if (song._serverType === ServerType.EXTERNAL) {
|
||||
if (typeof window === 'undefined' || !window.api?.youtube) {
|
||||
const youtubeEnabled = useSettingsStore.getState().integrations?.youtube ?? true;
|
||||
if (!youtubeEnabled || typeof window === 'undefined' || !window.api?.youtube) {
|
||||
return undefined;
|
||||
}
|
||||
const searchQuery = `${song.artistName ?? ''} ${song.name ?? ''}`.trim();
|
||||
const searchQuery = buildYoutubeSearchQuery(song.name, song.artistName);
|
||||
if (!searchQuery) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import {
|
||||
SettingOption,
|
||||
SettingsSection,
|
||||
} from '/@/renderer/features/settings/components/settings-section';
|
||||
import {
|
||||
useGeneralSettings,
|
||||
useIntegrationsSettings,
|
||||
useSettingsStoreActions,
|
||||
} from '/@/renderer/store';
|
||||
import { MultiSelect } from '/@/shared/components/multi-select/multi-select';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
import { Switch } from '/@/shared/components/switch/switch';
|
||||
import { TextInput } from '/@/shared/components/text-input/text-input';
|
||||
|
||||
const MUSICBRAINZ_RELEASE_TYPES = [
|
||||
'album',
|
||||
'single',
|
||||
'ep',
|
||||
'broadcast',
|
||||
'compilation',
|
||||
'live',
|
||||
'remix',
|
||||
'appears-on',
|
||||
'audiobook',
|
||||
'audio drama',
|
||||
'demo',
|
||||
'dj-mix',
|
||||
'field recording',
|
||||
'interview',
|
||||
'mixtape/street',
|
||||
'other',
|
||||
'soundtrack',
|
||||
'spokenword',
|
||||
];
|
||||
|
||||
export const IntegrationsTab = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const { musicBrainz } = useGeneralSettings();
|
||||
const settings = useIntegrationsSettings();
|
||||
const { setSettings } = useSettingsStoreActions();
|
||||
|
||||
const updateIntegrations = (updates: Partial<typeof settings>) => {
|
||||
setSettings({
|
||||
integrations: {
|
||||
...settings,
|
||||
...updates,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const options: SettingOption[] = [
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
aria-label={t('setting.musicBrainzQueries', { postProcess: 'sentenceCase' })}
|
||||
defaultChecked={settings.musicBrainz}
|
||||
onChange={(e) => updateIntegrations({ musicBrainz: e.currentTarget.checked })}
|
||||
/>
|
||||
),
|
||||
description: t('setting.musicBrainzQueries', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
title: t('setting.musicBrainzQueries', { postProcess: 'sentenceCase' }),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<MultiSelect
|
||||
aria-label={t('setting.musicbrainzExcludeReleaseTypes', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
clearable
|
||||
data={MUSICBRAINZ_RELEASE_TYPES}
|
||||
defaultValue={settings.musicBrainzExcludeReleaseTypes}
|
||||
onChange={(value) =>
|
||||
updateIntegrations({ musicBrainzExcludeReleaseTypes: value })
|
||||
}
|
||||
width={300}
|
||||
/>
|
||||
),
|
||||
description: t('setting.musicbrainzExcludeReleaseTypes', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
isHidden: !musicBrainz || !settings.musicBrainz,
|
||||
title: t('setting.musicbrainzExcludeReleaseTypes', { postProcess: 'sentenceCase' }),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<TextInput
|
||||
defaultValue={settings.musicBrainzPrioritizeCountries.join(', ')}
|
||||
key={settings.musicBrainzPrioritizeCountries.join(',')}
|
||||
onBlur={(e) => {
|
||||
const value = e.currentTarget.value
|
||||
.split(/[,;\s]+/)
|
||||
.map((s) => s.trim().toUpperCase())
|
||||
.filter(Boolean);
|
||||
updateIntegrations({ musicBrainzPrioritizeCountries: value });
|
||||
}}
|
||||
placeholder="e.g. US, GB, DE"
|
||||
width={300}
|
||||
/>
|
||||
),
|
||||
description: t('setting.musicbrainzPrioritizeCountries', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
isHidden: !musicBrainz || !settings.musicBrainz,
|
||||
title: t('setting.musicbrainzPrioritizeCountries', { postProcess: 'sentenceCase' }),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
aria-label={t('setting.youtube', { postProcess: 'sentenceCase' })}
|
||||
defaultChecked={settings.youtube}
|
||||
onChange={(e) => updateIntegrations({ youtube: e.currentTarget.checked })}
|
||||
/>
|
||||
),
|
||||
description: t('setting.youtube', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
title: t('setting.youtube', { postProcess: 'sentenceCase' }),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
<SettingsSection
|
||||
options={options}
|
||||
title={t('page.setting.integrationsTab', { postProcess: 'sentenceCase' })}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
});
|
||||
@@ -24,6 +24,14 @@ const HotkeysTab = lazy(() =>
|
||||
})),
|
||||
);
|
||||
|
||||
const IntegrationsTab = lazy(() =>
|
||||
import('/@/renderer/features/settings/components/integrations/integrations-tab').then(
|
||||
(module) => ({
|
||||
default: module.IntegrationsTab,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const WindowTab = lazy(() =>
|
||||
import('/@/renderer/features/settings/components/window/window-tab').then((module) => ({
|
||||
default: module.WindowTab,
|
||||
@@ -61,6 +69,9 @@ export const SettingsContent = () => {
|
||||
<Tabs.Tab value="hotkeys">
|
||||
{t('page.setting.hotkeysTab', { postProcess: 'sentenceCase' })}
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab value="integrations">
|
||||
{t('page.setting.integrationsTab', { postProcess: 'sentenceCase' })}
|
||||
</Tabs.Tab>
|
||||
{isElectron() && (
|
||||
<Tabs.Tab value="window">
|
||||
{t('page.setting.windowTab', { postProcess: 'sentenceCase' })}
|
||||
@@ -85,6 +96,11 @@ export const SettingsContent = () => {
|
||||
<HotkeysTab />
|
||||
</Suspense>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value="integrations">
|
||||
<Suspense fallback={null}>
|
||||
<IntegrationsTab />
|
||||
</Suspense>
|
||||
</Tabs.Panel>
|
||||
{isElectron() && (
|
||||
<Tabs.Panel value="window">
|
||||
<Suspense fallback={null}>
|
||||
|
||||
@@ -440,8 +440,6 @@ export const GeneralSettingsSchema = z.object({
|
||||
lastFM: z.boolean(),
|
||||
lastfmApiKey: z.string(),
|
||||
musicBrainz: z.boolean(),
|
||||
musicBrainzExcludeReleaseTypes: z.array(z.string()),
|
||||
musicBrainzPrioritizeCountries: z.array(z.string()),
|
||||
nativeAspectRatio: z.boolean(),
|
||||
passwordStore: z.string().optional(),
|
||||
pathReplace: z.string(),
|
||||
@@ -615,6 +613,13 @@ const QueryBuilderSettingsSchema = z.object({
|
||||
tag: z.array(QueryBuilderCustomFieldSchema),
|
||||
});
|
||||
|
||||
const IntegrationsSettingsSchema = z.object({
|
||||
musicBrainz: z.boolean(),
|
||||
musicBrainzExcludeReleaseTypes: z.array(z.string()),
|
||||
musicBrainzPrioritizeCountries: z.array(z.string()),
|
||||
youtube: z.boolean(),
|
||||
});
|
||||
|
||||
const AutoDJSettingsSchema = z.object({
|
||||
enabled: z.boolean(),
|
||||
itemCount: z.number(),
|
||||
@@ -631,6 +636,7 @@ export const ValidationSettingsStateSchema = z.object({
|
||||
font: FontSettingsSchema,
|
||||
general: GeneralSettingsSchema,
|
||||
hotkeys: HotkeysSettingsSchema,
|
||||
integrations: IntegrationsSettingsSchema,
|
||||
lists: z.record(z.nativeEnum(ItemListKey), ItemListConfigSchema),
|
||||
lyrics: LyricsSettingsSchema,
|
||||
lyricsDisplay: z.record(z.string(), LyricsDisplaySettingsSchema),
|
||||
@@ -640,6 +646,7 @@ export const ValidationSettingsStateSchema = z.object({
|
||||
tab: z.union([
|
||||
z.literal('general'),
|
||||
z.literal('hotkeys'),
|
||||
z.literal('integrations'),
|
||||
z.literal('playback'),
|
||||
z.literal('window'),
|
||||
z.string(),
|
||||
@@ -1015,8 +1022,6 @@ const initialState: SettingsState = {
|
||||
lastFM: true,
|
||||
lastfmApiKey: '',
|
||||
musicBrainz: true,
|
||||
musicBrainzExcludeReleaseTypes: [],
|
||||
musicBrainzPrioritizeCountries: [],
|
||||
nativeAspectRatio: false,
|
||||
passwordStore: undefined,
|
||||
pathReplace: '',
|
||||
@@ -1099,6 +1104,12 @@ const initialState: SettingsState = {
|
||||
},
|
||||
globalMediaHotkeys: true,
|
||||
},
|
||||
integrations: {
|
||||
musicBrainz: true,
|
||||
musicBrainzExcludeReleaseTypes: [],
|
||||
musicBrainzPrioritizeCountries: [],
|
||||
youtube: true,
|
||||
},
|
||||
lists: {
|
||||
['albumDetail']: {
|
||||
display: ListDisplayType.TABLE,
|
||||
@@ -2093,10 +2104,36 @@ export const useSettingsStore = createWithEqualityFn<SettingsSlice>()(
|
||||
}
|
||||
}
|
||||
|
||||
if (version <= 24) {
|
||||
// Move MusicBrainz release/country options to Integrations; keep musicBrainz in general
|
||||
const general = state.general as Record<string, unknown>;
|
||||
state.integrations = {
|
||||
musicBrainz: initialState.integrations.musicBrainz,
|
||||
musicBrainzExcludeReleaseTypes:
|
||||
(general?.musicBrainzExcludeReleaseTypes as string[]) ??
|
||||
initialState.integrations.musicBrainzExcludeReleaseTypes,
|
||||
musicBrainzPrioritizeCountries:
|
||||
(general?.musicBrainzPrioritizeCountries as string[]) ??
|
||||
initialState.integrations.musicBrainzPrioritizeCountries,
|
||||
youtube: initialState.integrations.youtube,
|
||||
};
|
||||
delete general.musicBrainzExcludeReleaseTypes;
|
||||
delete general.musicBrainzPrioritizeCountries;
|
||||
}
|
||||
|
||||
if (version <= 25) {
|
||||
// Add integrations.musicBrainz to enable/disable MusicBrainz API queries
|
||||
state.integrations = {
|
||||
...state.integrations,
|
||||
musicBrainz:
|
||||
(state.integrations as { musicBrainz?: boolean })?.musicBrainz ?? true,
|
||||
};
|
||||
}
|
||||
|
||||
return persistedState;
|
||||
},
|
||||
name: 'store_settings',
|
||||
version: 24,
|
||||
version: 26,
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -2252,6 +2289,9 @@ export const useAlbumBackground = () =>
|
||||
shallow,
|
||||
);
|
||||
|
||||
export const useIntegrationsSettings = () =>
|
||||
useSettingsStore((state) => state.integrations, shallow);
|
||||
|
||||
export const useExternalLinks = () =>
|
||||
useSettingsStore(
|
||||
(state) => ({
|
||||
|
||||
@@ -34,17 +34,18 @@ export const idbStateStorage: StateStorage = {
|
||||
const settingsKeys = [
|
||||
'store_settings_autoDJ',
|
||||
'store_settings_general',
|
||||
'store_settings_lists',
|
||||
'store_settings_hotkeys',
|
||||
'store_settings_playback',
|
||||
'store_settings_integrations',
|
||||
'store_settings_lists',
|
||||
'store_settings_lyrics',
|
||||
'store_settings_playback',
|
||||
'store_settings_queryBuilder',
|
||||
'store_settings_remote',
|
||||
'store_settings_tab',
|
||||
'store_settings_window',
|
||||
'store_settings_discord',
|
||||
'store_settings_font',
|
||||
'store_settings_css',
|
||||
'store_settings_remote',
|
||||
'store_settings_queryBuilder',
|
||||
'store_settings_tab',
|
||||
];
|
||||
|
||||
export const splitSettingsStorage: StateStorage = {
|
||||
|
||||
Reference in New Issue
Block a user