[feat]: actually include version checks

This commit is contained in:
Kendall Garner 2024-02-17 00:57:10 -08:00
commit dae2f9bd0a
No known key found for this signature in database
GPG key ID: 18D2767419676C87
36 changed files with 224 additions and 222 deletions

View file

@ -52,8 +52,8 @@ import type {
ServerInfoArgs,
StructuredLyricsArgs,
StructuredLyric,
ServerType,
} from '/@/renderer/api/types';
import { ServerType } from '/@/renderer/types';
import { DeletePlaylistResponse, RandomSongListArgs } from './types';
import { ndController } from '/@/renderer/api/navidrome/navidrome-controller';
import { ssController } from '/@/renderer/api/subsonic/subsonic-controller';
@ -173,7 +173,7 @@ const endpoints: ApiController = {
getPlaylistList: ndController.getPlaylistList,
getPlaylistSongList: ndController.getPlaylistSongList,
getRandomSongList: ssController.getRandomSongList,
getServerInfo: ssController.getServerInfo,
getServerInfo: ndController.getServerInfo,
getSongDetail: ndController.getSongDetail,
getSongList: ndController.getSongList,
getStructuredLyrics: ssController.getStructuredLyrics,

View file

@ -3,7 +3,7 @@ import { jfType } from '/@/renderer/api/jellyfin/jellyfin-types';
import { initClient, initContract } from '@ts-rest/core';
import axios, { AxiosError, AxiosResponse, isAxiosError, Method } from 'axios';
import qs from 'qs';
import { ServerListItem } from '/@/renderer/types';
import { ServerListItem } from '/@/renderer/api/types';
import omitBy from 'lodash/omitBy';
import { z } from 'zod';
import { authenticationFailure } from '/@/renderer/api/utils';

View file

@ -10,8 +10,9 @@ import {
Playlist,
MusicFolder,
Genre,
ServerListItem,
ServerType,
} from '/@/renderer/api/types';
import { ServerListItem, ServerType } from '/@/renderer/types';
const getStreamUrl = (args: {
container?: string;

View file

@ -7,7 +7,7 @@ import qs from 'qs';
import { ndType } from './navidrome-types';
import { authenticationFailure, resultWithHeaders } from '/@/renderer/api/utils';
import { useAuthStore } from '/@/renderer/store';
import { ServerListItem } from '/@/renderer/types';
import { ServerListItem } from '/@/renderer/api/types';
import { toast } from '/@/renderer/components';
import i18n from '/@/i18n/i18n';

View file

@ -1,3 +1,9 @@
import { ndApiClient } from '/@/renderer/api/navidrome/navidrome-api';
import { ndNormalize } from '/@/renderer/api/navidrome/navidrome-normalize';
import { NavidromeExtensions, ndType } from '/@/renderer/api/navidrome/navidrome-types';
import { ssApiClient } from '/@/renderer/api/subsonic/subsonic-api';
import semverCoerce from 'semver/functions/coerce';
import semverGte from 'semver/functions/gte';
import {
AlbumArtistDetailArgs,
AlbumArtistDetailResponse,
@ -39,11 +45,10 @@ import {
RemoveFromPlaylistResponse,
RemoveFromPlaylistArgs,
genreListSortMap,
ServerInfo,
ServerInfoArgs,
} from '../types';
import { ndApiClient } from '/@/renderer/api/navidrome/navidrome-api';
import { ndNormalize } from '/@/renderer/api/navidrome/navidrome-normalize';
import { ndType } from '/@/renderer/api/navidrome/navidrome-types';
import { ssApiClient } from '/@/renderer/api/subsonic/subsonic-api';
import { hasFeature } from '/@/renderer/api/utils';
const authenticate = async (
url: string,
@ -355,6 +360,16 @@ const deletePlaylist = async (args: DeletePlaylistArgs): Promise<DeletePlaylistR
const getPlaylistList = async (args: PlaylistListArgs): Promise<PlaylistListResponse> => {
const { query, apiClientProps } = args;
const customQuery = query._custom?.navidrome;
// Smart playlists only became available in 0.48.0. Do not filter for previous versions
if (
customQuery &&
customQuery.smart !== undefined &&
!hasFeature(apiClientProps.server, NavidromeExtensions.SMART_PLAYLISTS)
) {
customQuery.smart = undefined;
}
const res = await ndApiClient(apiClientProps).getPlaylistList({
query: {
@ -363,7 +378,7 @@ const getPlaylistList = async (args: PlaylistListArgs): Promise<PlaylistListResp
_sort: query.sortBy ? playlistListSortMap.navidrome[query.sortBy] : undefined,
_start: query.startIndex,
q: query.searchTerm,
...query._custom?.navidrome,
...customQuery,
},
});
@ -465,6 +480,61 @@ const removeFromPlaylist = async (
return null;
};
const VERSION_INFO: Array<[string, Record<string, number[]>]> = [
['0.48.0', { [NavidromeExtensions.SMART_PLAYLISTS]: [1] }],
];
const getFeatures = (version: string): Record<string, number[]> => {
const cleanVersion = semverCoerce(version);
const features: Record<string, number[]> = {};
let matched = cleanVersion === null;
for (const [version, supportedFeatures] of VERSION_INFO) {
if (!matched) {
matched = semverGte(cleanVersion!, version);
}
if (matched) {
for (const [feature, feat] of Object.entries(supportedFeatures)) {
if (feature in features) {
features[feature].push(...feat);
} else {
features[feature] = feat;
}
}
}
}
return features;
};
const getServerInfo = async (args: ServerInfoArgs): Promise<ServerInfo> => {
const { apiClientProps } = args;
// Navidrome will always populate serverVersion
const ping = await ssApiClient(apiClientProps).ping();
if (ping.status !== 200) {
throw new Error('Failed to ping server');
}
const features: Record<string, number[]> = getFeatures(ping.body.serverVersion!);
if (ping.body.openSubsonic) {
const res = await ssApiClient(apiClientProps).getServerInfo();
if (res.status !== 200) {
throw new Error('Failed to get server extensions');
}
for (const extension of res.body.openSubsonicExtensions) {
features[extension.name] = extension.versions;
}
}
return { features, id: apiClientProps.server?.id, version: ping.body.serverVersion! };
};
export const ndController = {
addToPlaylist,
authenticate,
@ -478,6 +548,7 @@ export const ndController = {
getPlaylistDetail,
getPlaylistList,
getPlaylistSongList,
getServerInfo,
getSongDetail,
getSongList,
getUserList,

View file

@ -7,8 +7,9 @@ import {
User,
AlbumArtist,
Genre,
ServerListItem,
ServerType,
} from '/@/renderer/api/types';
import { ServerListItem, ServerType } from '/@/renderer/types';
import z from 'zod';
import { ndType } from './navidrome-types';
import { ssType } from '/@/renderer/api/subsonic/subsonic-types';

View file

@ -343,6 +343,10 @@ const removeFromPlaylistParameters = z.object({
id: z.array(z.string()),
});
export enum NavidromeExtensions {
SMART_PLAYLISTS = 'smartPlaylists',
}
export const ndType = {
_enum: {
albumArtistList: ndAlbumArtistListSort,

View file

@ -1,8 +1,14 @@
import { nanoid } from 'nanoid';
import { z } from 'zod';
import { ssType } from '/@/renderer/api/subsonic/subsonic-types';
import { QueueSong, LibraryItem, AlbumArtist, Album } from '/@/renderer/api/types';
import { ServerListItem, ServerType } from '/@/renderer/types';
import {
QueueSong,
LibraryItem,
AlbumArtist,
Album,
ServerListItem,
ServerType,
} from '/@/renderer/api/types';
const getCoverArtUrl = (args: {
baseUrl: string | undefined;

View file

@ -57,13 +57,16 @@ export type User = {
export type ServerListItem = {
credential: string;
features?: Record<string, number[]>;
id: string;
name: string;
ndCredential?: string;
savePassword?: boolean;
type: ServerType;
url: string;
userId: string | null;
username: string;
version?: string;
};
export enum ServerType {

View file

@ -2,7 +2,7 @@ import { AxiosHeaders } from 'axios';
import { z } from 'zod';
import { toast } from '/@/renderer/components';
import { useAuthStore } from '/@/renderer/store';
import { ServerListItem } from '/@/renderer/types';
import { ServerListItem } from '/@/renderer/api/types';
// Since ts-rest client returns a strict response type, we need to add the headers to the body object
export const resultWithHeaders = <ItemType extends z.ZodTypeAny>(itemSchema: ItemType) => {
@ -38,3 +38,20 @@ export const authenticationFailure = (currentServer: ServerListItem | null) => {
useAuthStore.getState().actions.setCurrentServer(null);
}
};
export const hasFeature = (
server: ServerListItem | null,
feature: string,
version = 1,
): boolean => {
if (!server || !server.features) {
return false;
}
const versions = server.features[feature];
if (!versions || versions.length === 0) {
return false;
}
return versions.includes(version);
};

View file

@ -10,7 +10,7 @@ import styled from 'styled-components';
import type { AlbumArtist, Artist } from '/@/renderer/api/types';
import { Text } from '/@/renderer/components/text';
import { AppRoute } from '/@/renderer/router/routes';
import { ServerType } from '/@/renderer/types';
import { ServerType } from '/@/renderer/api/types';
import { Skeleton } from '/@/renderer/components/skeleton';
const CellContainer = styled(motion.div)<{ height: number }>`

View file

@ -11,9 +11,9 @@ import {
LibraryItem,
AnyLibraryItems,
RatingResponse,
ServerType,
} from '/@/renderer/api/types';
import { useSetAlbumListItemDataById, useSetQueueRating, getServerById } from '/@/renderer/store';
import { ServerType } from '/@/renderer/types';
export const useUpdateRating = () => {
const queryClient = useQueryClient();

View file

@ -16,12 +16,12 @@ import orderBy from 'lodash/orderBy';
import { generatePath, useNavigate } from 'react-router';
import { api } from '/@/renderer/api';
import { QueryPagination, queryKeys } from '/@/renderer/api/query-keys';
import { BasePaginatedResponse, LibraryItem } from '/@/renderer/api/types';
import { BasePaginatedResponse, LibraryItem, ServerListItem } from '/@/renderer/api/types';
import { getColumnDefs, VirtualTableProps } from '/@/renderer/components/virtual-table';
import { SetContextMenuItems, useHandleTableContextMenu } from '/@/renderer/features/context-menu';
import { AppRoute } from '/@/renderer/router/routes';
import { useListStoreActions } from '/@/renderer/store';
import { ListDisplayType, ServerListItem, TablePagination } from '/@/renderer/types';
import { ListDisplayType, TablePagination } from '/@/renderer/types';
import { useSearchParams } from 'react-router-dom';
import { ListKey, useListStoreByKey } from '../../../store/list.store';

View file

@ -15,7 +15,7 @@ import {
RiSettings3Fill,
} from 'react-icons/ri';
import { queryKeys } from '/@/renderer/api/query-keys';
import { AlbumListSort, LibraryItem, SortOrder } from '/@/renderer/api/types';
import { AlbumListSort, LibraryItem, ServerType, SortOrder } from '/@/renderer/api/types';
import { Button, DropdownMenu, MultiSelect, Slider, Switch, Text } from '/@/renderer/components';
import { VirtualInfiniteGridRef } from '/@/renderer/components/virtual-grid';
import { ALBUM_TABLE_COLUMNS } from '/@/renderer/components/virtual-table';
@ -31,7 +31,7 @@ import {
useListStoreActions,
useListStoreByKey,
} from '/@/renderer/store';
import { ListDisplayType, Play, ServerType, TableColumn } from '/@/renderer/types';
import { ListDisplayType, Play, TableColumn } from '/@/renderer/types';
import i18n from '/@/i18n/i18n';
const FILTERS = {

View file

@ -9,7 +9,7 @@ import { RiFolder2Line, RiMoreFill, RiRefreshLine, RiSettings3Fill } from 'react
import { useListContext } from '../../../context/list-context';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { AlbumArtistListSort, LibraryItem, SortOrder } from '/@/renderer/api/types';
import { AlbumArtistListSort, LibraryItem, ServerType, SortOrder } from '/@/renderer/api/types';
import { Button, DropdownMenu, MultiSelect, Slider, Switch, Text } from '/@/renderer/components';
import { VirtualInfiniteGridRef } from '/@/renderer/components/virtual-grid';
import { ALBUMARTIST_TABLE_COLUMNS } from '/@/renderer/components/virtual-table';
@ -21,7 +21,7 @@ import {
useListStoreActions,
useListStoreByKey,
} from '/@/renderer/store';
import { ListDisplayType, ServerType, TableColumn } from '/@/renderer/types';
import { ListDisplayType, TableColumn } from '/@/renderer/types';
import i18n from '/@/i18n/i18n';
const FILTERS = {

View file

@ -8,7 +8,8 @@ import {
usePlayerStore,
} from '/@/renderer/store';
import { SetActivity } from '@xhayper/discord-rpc';
import { PlayerStatus, ServerType } from '/@/renderer/types';
import { PlayerStatus } from '/@/renderer/types';
import { ServerType } from '/@/renderer/api/types';
const discordRpc = isElectron() ? window.electron.discordRpc : null;
@ -40,7 +41,7 @@ export const useDiscordRpc = () => {
largeImageText: currentSong?.album || 'Unknown album',
smallImageKey: undefined,
smallImageText: currentStatus,
state: artists && `By ${artists}` || "Unknown artist",
state: (artists && `By ${artists}`) || 'Unknown artist',
};
if (currentStatus === PlayerStatus.PLAYING) {

View file

@ -5,7 +5,7 @@ import { useQueryClient } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { RiFolder2Fill, RiMoreFill, RiRefreshLine, RiSettings3Fill } from 'react-icons/ri';
import { queryKeys } from '/@/renderer/api/query-keys';
import { GenreListSort, LibraryItem, SortOrder } from '/@/renderer/api/types';
import { GenreListSort, LibraryItem, ServerType, SortOrder } from '/@/renderer/api/types';
import { Button, DropdownMenu, MultiSelect, Slider, Switch, Text } from '/@/renderer/components';
import { VirtualInfiniteGridRef } from '/@/renderer/components/virtual-grid';
import { GENRE_TABLE_COLUMNS } from '/@/renderer/components/virtual-table';
@ -19,7 +19,7 @@ import {
useListStoreActions,
useListStoreByKey,
} from '/@/renderer/store';
import { ListDisplayType, ServerType, TableColumn } from '/@/renderer/types';
import { ListDisplayType, TableColumn } from '/@/renderer/types';
import i18n from '/@/i18n/i18n';
const FILTERS = {

View file

@ -8,13 +8,14 @@ import {
LyricGetQuery,
SubsonicExtensions,
StructuredLyric,
ServerType,
} from '/@/renderer/api/types';
import { QueryHookArgs } from '/@/renderer/lib/react-query';
import { getServerById, useLyricsSettings } from '/@/renderer/store';
import { queryKeys } from '/@/renderer/api/query-keys';
import { ServerType } from '/@/renderer/types';
import { api } from '/@/renderer/api';
import isElectron from 'is-electron';
import { hasFeature } from '/@/renderer/api/utils';
const lyricsIpc = isElectron() ? window.electron.lyrics : null;
@ -112,7 +113,7 @@ export const useSongLyricsBySong = (
source: server?.name ?? 'music server',
};
}
} else if (server.features && SubsonicExtensions.SONG_LYRICS in server.features) {
} else if (hasFeature(server, SubsonicExtensions.SONG_LYRICS)) {
const subsonicLyrics = await api.controller
.getStructuredLyrics({
apiClientProps: { server, signal },

View file

@ -15,11 +15,12 @@ import {
ServerType,
GenreListSort,
SortOrder,
ServerListItem,
} from '/@/renderer/api/types';
import { api } from '/@/renderer/api';
import { useAuthStore } from '/@/renderer/store';
import { queryKeys } from '/@/renderer/api/query-keys';
import { Play, PlayQueueAddOptions, ServerListItem } from '/@/renderer/types';
import { Play, PlayQueueAddOptions } from '/@/renderer/types';
import i18n from '/@/i18n/i18n';
interface ShuffleAllSlice extends RandomSongListQuery {

View file

@ -8,8 +8,9 @@ import {
SongListResponse,
SongListSort,
SortOrder,
ServerListItem,
ServerType,
} from '/@/renderer/api/types';
import { ServerListItem, ServerType } from '/@/renderer/types';
export const getPlaylistSongsById = async (args: {
id: string;

View file

@ -11,6 +11,8 @@ import { useCreatePlaylist } from '/@/renderer/features/playlists/mutations/crea
import { convertQueryGroupToNDQuery } from '/@/renderer/features/playlists/utils';
import { useCurrentServer } from '/@/renderer/store';
import { useTranslation } from 'react-i18next';
import { hasFeature } from '/@/renderer/api/utils';
import { NavidromeExtensions } from '/@/renderer/api/navidrome/navidrome-types';
interface CreatePlaylistFormProps {
onCancel: () => void;
@ -120,12 +122,13 @@ export const CreatePlaylistForm = ({ onCancel }: CreatePlaylistFormProps) => {
})}
/>
)}
{server?.type === ServerType.NAVIDROME && (
<Switch
label="Is smart playlist?"
onChange={(e) => setIsSmartPlaylist(e.currentTarget.checked)}
/>
)}
{server?.type === ServerType.NAVIDROME &&
hasFeature(server, NavidromeExtensions.SMART_PLAYLISTS) && (
<Switch
label="Is smart playlist?"
onChange={(e) => setIsSmartPlaylist(e.currentTarget.checked)}
/>
)}
</Group>
{server?.type === ServerType.NAVIDROME && isSmartPlaylist && (
<Stack pt="1rem">

View file

@ -17,7 +17,13 @@ import {
} from 'react-icons/ri';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { LibraryItem, PlaylistSongListQuery, SongListSort, SortOrder } from '/@/renderer/api/types';
import {
LibraryItem,
PlaylistSongListQuery,
ServerType,
SongListSort,
SortOrder,
} from '/@/renderer/api/types';
import {
DropdownMenu,
Button,
@ -39,7 +45,7 @@ import {
useSetPlaylistStore,
useSetPlaylistTablePagination,
} from '/@/renderer/store';
import { ListDisplayType, ServerType, Play, TableColumn } from '/@/renderer/types';
import { ListDisplayType, Play, TableColumn } from '/@/renderer/types';
import { usePlaylistDetail } from '/@/renderer/features/playlists/queries/playlist-detail-query';
import { useParams, useNavigate } from 'react-router';
import { SONG_TABLE_COLUMNS } from '/@/renderer/components/virtual-table';

View file

@ -9,11 +9,11 @@ import { PlaylistListHeaderFilters } from '/@/renderer/features/playlists/compon
import { LibraryHeaderBar } from '/@/renderer/features/shared';
import { useContainerQuery } from '/@/renderer/hooks';
import { PlaylistListFilter, useCurrentServer, useListStoreActions } from '/@/renderer/store';
import { ListDisplayType, ServerType } from '/@/renderer/types';
import { ListDisplayType } from '/@/renderer/types';
import debounce from 'lodash/debounce';
import { useTranslation } from 'react-i18next';
import { RiFileAddFill } from 'react-icons/ri';
import { LibraryItem } from '/@/renderer/api/types';
import { LibraryItem, ServerType } from '/@/renderer/api/types';
import { useListFilterRefresh } from '../../../hooks/use-list-filter-refresh';
import { useListContext } from '/@/renderer/context/list-context';
import { useListStoreByKey } from '../../../store/list.store';

View file

@ -4,12 +4,11 @@ import { nanoid } from 'nanoid/non-secure';
import { useTranslation } from 'react-i18next';
import { generatePath, useNavigate } from 'react-router';
import { createSearchParams } from 'react-router-dom';
import { LibraryItem } from '/@/renderer/api/types';
import { LibraryItem, ServerType } from '/@/renderer/api/types';
import { CreatePlaylistForm } from '/@/renderer/features/playlists';
import { Command, CommandPalettePages } from '/@/renderer/features/search/components/command';
import { AppRoute } from '/@/renderer/router/routes';
import { useCurrentServer } from '/@/renderer/store';
import { ServerType } from '/@/renderer/types';
interface HomeCommandsProps {
handleClose: () => void;

View file

@ -3,7 +3,7 @@ import { openModal } from '@mantine/modals';
import { Command, CommandPalettePages } from '/@/renderer/features/search/components/command';
import { ServerList } from '/@/renderer/features/servers';
import { useAuthStoreActions, useServerList } from '/@/renderer/store';
import { ServerListItem } from '/@/renderer/types';
import { ServerListItem } from '/@/renderer/api/types';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router';
import { AppRoute } from '/@/renderer/router/routes';

View file

@ -6,9 +6,8 @@ import { useFocusTrap } from '@mantine/hooks';
import { closeAllModals } from '@mantine/modals';
import isElectron from 'is-electron';
import { nanoid } from 'nanoid/non-secure';
import { AuthenticationResponse } from '/@/renderer/api/types';
import { AuthenticationResponse, ServerType } from '/@/renderer/api/types';
import { useAuthStoreActions } from '/@/renderer/store';
import { ServerType } from '/@/renderer/types';
import { api } from '/@/renderer/api';
import { useTranslation } from 'react-i18next';

View file

@ -7,9 +7,8 @@ import { closeAllModals } from '@mantine/modals';
import isElectron from 'is-electron';
import { useTranslation } from 'react-i18next';
import { RiInformationLine } from 'react-icons/ri';
import { AuthenticationResponse } from '/@/renderer/api/types';
import { AuthenticationResponse, ServerListItem, ServerType } from '/@/renderer/api/types';
import { useAuthStoreActions } from '/@/renderer/store';
import { ServerListItem, ServerType } from '/@/renderer/types';
import { api } from '/@/renderer/api';
import i18n from '/@/i18n/i18n';
import { queryClient } from '/@/renderer/lib/react-query';

View file

@ -7,7 +7,7 @@ import { RiDeleteBin2Line, RiEdit2Fill } from 'react-icons/ri';
import { EditServerForm } from '/@/renderer/features/servers/components/edit-server-form';
import { ServerSection } from '/@/renderer/features/servers/components/server-section';
import { useAuthStoreActions } from '/@/renderer/store';
import { ServerListItem as ServerItem } from '/@/renderer/types';
import { ServerListItem as ServerItem } from '/@/renderer/api/types';
const localSettings = isElectron() ? window.electron.localSettings : null;

View file

@ -15,7 +15,7 @@ import {
} from 'react-icons/ri';
import { useListStoreByKey } from '../../../store/list.store';
import { queryKeys } from '/@/renderer/api/query-keys';
import { LibraryItem, SongListSort, SortOrder } from '/@/renderer/api/types';
import { LibraryItem, ServerType, SongListSort, SortOrder } from '/@/renderer/api/types';
import { Button, DropdownMenu, MultiSelect, Slider, Switch, Text } from '/@/renderer/components';
import { VirtualInfiniteGridRef } from '/@/renderer/components/virtual-grid';
import { SONG_TABLE_COLUMNS } from '/@/renderer/components/virtual-table';
@ -27,7 +27,7 @@ import { useContainerQuery } from '/@/renderer/hooks';
import { useListFilterRefresh } from '/@/renderer/hooks/use-list-filter-refresh';
import { queryClient } from '/@/renderer/lib/react-query';
import { SongListFilter, useCurrentServer, useListStoreActions } from '/@/renderer/store';
import { ListDisplayType, Play, ServerType, TableColumn } from '/@/renderer/types';
import { ListDisplayType, Play, TableColumn } from '/@/renderer/types';
import i18n from '/@/i18n/i18n';
const FILTERS = {

View file

@ -29,7 +29,7 @@ import {
useSidebarStore,
useAppStoreActions,
} from '/@/renderer/store';
import { ServerListItem, ServerType } from '/@/renderer/types';
import { ServerListItem, ServerType } from '/@/renderer/api/types';
import packageJson from '../../../../../package.json';
const browser = isElectron() ? window.electron.browser : null;

View file

@ -3,6 +3,7 @@ import { useAuthStoreActions, useCurrentServer } from '/@/renderer/store';
import { useQuery } from '@tanstack/react-query';
import { queryKeys } from '/@/renderer/api/query-keys';
import { controller } from '/@/renderer/api/controller';
import isEqual from 'lodash/isEqual';
export const useServerVersion = () => {
const { updateServer } = useAuthStoreActions();
@ -24,7 +25,7 @@ export const useServerVersion = () => {
useEffect(() => {
if (server && server.id === serverInfo.data?.id) {
const { version, features } = serverInfo.data;
if (version !== server.version) {
if (version !== server.version || !isEqual(features, server.features)) {
updateServer(server.id, {
features,
version,

View file

@ -6,7 +6,7 @@ import { immer } from 'zustand/middleware/immer';
import { useAlbumArtistListDataStore } from '/@/renderer/store/album-artist-list-data.store';
import { useAlbumListDataStore } from '/@/renderer/store/album-list-data.store';
import { useListStore } from '/@/renderer/store/list.store';
import { ServerListItem } from '/@/renderer/types';
import { ServerListItem } from '/@/renderer/api/types';
export interface AuthState {
currentServer: ServerListItem | null;

View file

@ -54,26 +54,6 @@ export enum Platform {
WINDOWS = 'windows',
}
export enum ServerType {
JELLYFIN = 'jellyfin',
NAVIDROME = 'navidrome',
SUBSONIC = 'subsonic',
}
export type ServerListItem = {
credential: string;
features?: Record<string, number[]>;
id: string;
name: string;
ndCredential?: string;
savePassword?: boolean;
type: ServerType;
url: string;
userId: string | null;
username: string;
version?: string;
};
export enum PlayerStatus {
PAUSED = 'paused',
PLAYING = 'playing',