diff --git a/.yarnrc b/.yarnrc deleted file mode 100644 index 5ef1bed4b..000000000 --- a/.yarnrc +++ /dev/null @@ -1,5 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -yarn-path ".yarn/releases/yarn-1.22.19.cjs" diff --git a/package.json b/package.json index a101594b1..62871e8f6 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@ctrl/transmission": "^4.1.1", "@emotion/react": "^11.10.5", "@emotion/server": "^11.10.0", + "@jellyfin/sdk": "^0.7.0", "@mantine/core": "^5.9.3", "@mantine/dates": "^5.9.3", "@mantine/dropzone": "^5.9.3", @@ -62,6 +63,7 @@ "rss-parser": "^3.12.0", "sabnzbd-api": "^1.5.0", "uuid": "^8.3.2", + "xml-js": "^1.6.11", "yarn": "^1.22.19", "zustand": "^4.1.4" }, diff --git a/public/locales/en/modules/media-server.json b/public/locales/en/modules/media-server.json new file mode 100644 index 000000000..1b93eb4c2 --- /dev/null +++ b/public/locales/en/modules/media-server.json @@ -0,0 +1,24 @@ +{ + "descriptor": { + "name": "Media Server", + "description": "Interact with your Jellyfin or Plex media server", + "settings": { + "title": "Settings for media server widget" + } + }, + "card": { + "table": { + "header": { + "session": "Session", + "user": "User", + "currentlyPlaying": "Currently playing" + } + }, + "errors": { + "general": { + "title": "Unable to load content", + "text": "Unable to retrieve information from the server. Please check the logs for more details" + } + } + } +} \ No newline at end of file diff --git a/src/components/AppAvatar.tsx b/src/components/AppAvatar.tsx new file mode 100644 index 000000000..08ca5c7b4 --- /dev/null +++ b/src/components/AppAvatar.tsx @@ -0,0 +1,26 @@ +import { Avatar, DefaultMantineColor, useMantineTheme } from '@mantine/core'; + +export const AppAvatar = ({ + iconUrl, + color, +}: { + iconUrl: string; + color?: DefaultMantineColor | undefined; +}) => { + const { colors, colorScheme } = useMantineTheme(); + + return ( + + ); +}; diff --git a/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/IntegrationSelector.tsx b/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/IntegrationSelector.tsx index 7ba366339..cbb74405f 100644 --- a/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/IntegrationSelector.tsx +++ b/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/IntegrationSelector.tsx @@ -75,6 +75,16 @@ export const IntegrationSelector = ({ form }: IntegrationSelectorProps) => { image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/readarr.png', label: 'Readarr', }, + { + value: 'jellyfin', + image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/jellyfin.png', + label: 'Jellyfin', + }, + { + value: 'plex', + image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/plex.png', + label: 'Plex', + }, ].filter((x) => Object.keys(integrationFieldProperties).includes(x.value)); const getNewProperties = (value: string | null): AppIntegrationPropertyType[] => { diff --git a/src/hooks/widgets/media-servers/useGetMediaServers.tsx b/src/hooks/widgets/media-servers/useGetMediaServers.tsx new file mode 100644 index 000000000..7b3d10164 --- /dev/null +++ b/src/hooks/widgets/media-servers/useGetMediaServers.tsx @@ -0,0 +1,17 @@ +import { useQuery } from '@tanstack/react-query'; +import { MediaServersResponseType } from '../../../types/api/media-server/response'; + +interface GetMediaServersParams { + enabled: boolean; +} + +export const useGetMediaServers = ({ enabled }: GetMediaServersParams) => + useQuery({ + queryKey: ['media-servers'], + queryFn: async (): Promise => { + const response = await fetch('/api/modules/media-server'); + return response.json(); + }, + enabled, + refetchInterval: 10 * 1000, + }); diff --git a/src/pages/api/modules/media-server/index.ts b/src/pages/api/modules/media-server/index.ts new file mode 100644 index 000000000..3bbc82fb2 --- /dev/null +++ b/src/pages/api/modules/media-server/index.ts @@ -0,0 +1,227 @@ +import { Jellyfin } from '@jellyfin/sdk'; +import { getSessionApi } from '@jellyfin/sdk/lib/utils/api/session-api'; +import { getSystemApi } from '@jellyfin/sdk/lib/utils/api/system-api'; + +import Consola from 'consola'; + +import { getCookie } from 'cookies-next'; + +import { NextApiRequest, NextApiResponse } from 'next'; + +import { BaseItemKind, ProgramAudio } from '@jellyfin/sdk/lib/generated-client/models'; +import { getConfig } from '../../../../tools/config/getConfig'; +import { PlexClient } from '../../../../tools/server/sdk/plex/plexClient'; +import { GenericMediaServer } from '../../../../types/api/media-server/media-server'; +import { MediaServersResponseType } from '../../../../types/api/media-server/response'; +import { + GenericCurrentlyPlaying, + GenericSessionInfo, +} from '../../../../types/api/media-server/session-info'; +import { ConfigAppType } from '../../../../types/app'; + +const jellyfin = new Jellyfin({ + clientInfo: { + name: 'Homarr', + version: '0.0.1', + }, + deviceInfo: { + name: 'Homarr Jellyfin Widget', + id: 'homarr-jellyfin-widget', + }, +}); + +const Get = async (request: NextApiRequest, response: NextApiResponse) => { + const configName = getCookie('config-name', { req: request }); + const config = getConfig(configName?.toString() ?? 'default'); + + const apps = config.apps.filter((app) => + ['jellyfin', 'plex'].includes(app.integration?.type ?? '') + ); + + const servers = await Promise.all( + apps.map(async (app): Promise => { + try { + return await handleServer(app); + } catch (error) { + Consola.error( + `failed to communicate with media server '${app.name}' (${app.id}): ${error}` + ); + return { + serverAddress: app.url, + sessions: [], + success: false, + version: undefined, + type: undefined, + appId: app.id, + }; + } + }) + ); + + return response.status(200).json({ + servers: servers.filter((server) => server !== undefined), + } as MediaServersResponseType); +}; + +const handleServer = async (app: ConfigAppType): Promise => { + switch (app.integration?.type) { + case 'jellyfin': { + const username = app.integration.properties.find((x) => x.field === 'username'); + + if (!username || !username.value) { + return { + appId: app.id, + serverAddress: app.url, + sessions: [], + type: 'jellyfin', + version: undefined, + success: false, + }; + } + + const password = app.integration.properties.find((x) => x.field === 'password'); + + if (!password || !password.value) { + return { + appId: app.id, + serverAddress: app.url, + sessions: [], + type: 'jellyfin', + version: undefined, + success: false, + }; + } + + const api = jellyfin.createApi(app.url); + const infoApi = await getSystemApi(api).getPublicSystemInfo(); + await api.authenticateUserByName(username.value, password.value); + const sessionApi = await getSessionApi(api); + const sessions = await sessionApi.getSessions(); + return { + type: 'jellyfin', + appId: app.id, + serverAddress: app.url, + version: infoApi.data.Version ?? undefined, + sessions: sessions.data.map( + (session): GenericSessionInfo => ({ + id: session.Id ?? '?', + username: session.UserName ?? undefined, + sessionName: `${session.Client} (${session.DeviceName})`, + supportsMediaControl: session.SupportsMediaControl ?? false, + currentlyPlaying: session.NowPlayingItem + ? { + name: session.NowPlayingItem.Name as string, + seasonName: session.NowPlayingItem.SeasonName as string, + albumName: session.NowPlayingItem.Album as string, + episodeCount: session.NowPlayingItem.EpisodeCount ?? undefined, + metadata: { + video: + session.NowPlayingItem && + session.NowPlayingItem.Width && + session.NowPlayingItem.Height + ? { + videoCodec: undefined, + width: session.NowPlayingItem.Width ?? undefined, + height: session.NowPlayingItem.Height ?? undefined, + bitrate: undefined, + videoFrameRate: session.TranscodingInfo?.Framerate + ? String(session.TranscodingInfo?.Framerate) + : undefined, + } + : undefined, + audio: session.TranscodingInfo + ? { + audioChannels: session.TranscodingInfo.AudioChannels ?? undefined, + audioCodec: session.TranscodingInfo.AudioCodec ?? undefined, + } + : undefined, + transcoding: session.TranscodingInfo + ? { + audioChannels: session.TranscodingInfo.AudioChannels ?? -1, + audioCodec: session.TranscodingInfo.AudioCodec ?? undefined, + container: session.TranscodingInfo.Container ?? undefined, + width: session.TranscodingInfo.Width ?? undefined, + height: session.TranscodingInfo.Height ?? undefined, + videoCodec: session.TranscodingInfo?.VideoCodec ?? undefined, + audioDecision: undefined, + context: undefined, + duration: undefined, + error: undefined, + sourceAudioCodec: undefined, + sourceVideoCodec: undefined, + timeStamp: undefined, + transcodeHwRequested: undefined, + videoDecision: undefined, + } + : undefined, + }, + type: convertJellyfinType(session.NowPlayingItem.Type), + } + : undefined, + userProfilePicture: undefined, + }) + ), + success: true, + }; + } + case 'plex': { + const apiKey = app.integration.properties.find((x) => x.field === 'apiKey'); + + if (!apiKey || !apiKey.value) { + return { + serverAddress: app.url, + sessions: [], + type: 'plex', + appId: app.id, + version: undefined, + success: false, + }; + } + + const plexClient = new PlexClient(app.url, apiKey.value); + const sessions = await plexClient.getSessions(); + return { + serverAddress: app.url, + sessions, + type: 'plex', + version: undefined, + appId: app.id, + success: true, + }; + } + default: { + Consola.warn( + `media-server api entered a fallback case. This should normally not happen and must be reported. Cause: '${app.name}' (${app.id})` + ); + return undefined; + } + } +}; + +const convertJellyfinType = (kind: BaseItemKind | undefined): GenericCurrentlyPlaying['type'] => { + switch (kind) { + case BaseItemKind.Audio: + case BaseItemKind.MusicVideo: + return 'audio'; + case BaseItemKind.Episode: + case BaseItemKind.Video: + return 'video'; + case BaseItemKind.Movie: + return 'movie'; + case BaseItemKind.TvChannel: + case BaseItemKind.TvProgram: + case BaseItemKind.LiveTvChannel: + case BaseItemKind.LiveTvProgram: + return 'tv'; + default: + return undefined; + } +}; + +export default async (request: NextApiRequest, response: NextApiResponse) => { + if (request.method === 'GET') { + return Get(request, response); + } + + return response.status(405); +}; diff --git a/src/tools/server/sdk/plex/plexClient.ts b/src/tools/server/sdk/plex/plexClient.ts new file mode 100644 index 000000000..6bc6707ce --- /dev/null +++ b/src/tools/server/sdk/plex/plexClient.ts @@ -0,0 +1,108 @@ +import { Element, xml2js } from 'xml-js'; + +import { + GenericCurrentlyPlaying, + GenericSessionInfo, +} from '../../../../types/api/media-server/session-info'; + +export class PlexClient { + constructor(private readonly apiAddress: string, private readonly token: string) {} + + async getSessions(): Promise { + const response = await fetch(`${this.apiAddress}/status/sessions?X-Plex-Token=${this.token}`); + const body = await response.text(); + + // convert xml response to objects, as there is no JSON api + const data = xml2js(body); + + // TODO: Investigate when there are no media containers + const mediaContainer = data.elements[0] as Element; + + // no sessions are open or available + if (!mediaContainer.elements?.some((_) => true)) { + return []; + } + + const videoElements = mediaContainer.elements as Element[]; + + const videos = videoElements + .map((videoElement): GenericSessionInfo | undefined => { + // extract the elements from the children + const userElement = this.findElement('User', videoElement.elements); + const playerElement = this.findElement('Player', videoElement.elements); + const mediaElement = this.findElement('Media', videoElement.elements); + const sessionElement = this.findElement('Session', videoElement.elements); + + if (!userElement || !playerElement || !mediaElement || !sessionElement) { + return undefined; + } + + const { videoCodec, videoFrameRate, audioCodec, audioChannels, height, width, bitrate } = + mediaElement; + + const transcodingElement = this.findElement('TranscodeSession', videoElement.elements); + + return { + id: sessionElement.id as string, + username: userElement.title as string, + userProfilePicture: userElement.thumb as string, + sessionName: `${playerElement.product} (${playerElement.title})`, + currentlyPlaying: { + name: videoElement.attributes?.title as string, + type: this.getCurrentlyPlayingType(videoElement.attributes?.type as string), + metadata: { + video: { + bitrate, + height, + videoCodec, + videoFrameRate, + width, + }, + audio: { + audioChannels, + audioCodec, + }, + transcoding: + transcodingElement === undefined + ? undefined + : { + audioChannels: transcodingElement.audioChannels, + audioCodec: transcodingElement.audioCodec, + audioDecision: transcodingElement.audioDecision, + container: transcodingElement.container, + context: transcodingElement.context, + duration: transcodingElement.duration, + error: transcodingElement.error === 1, + height: transcodingElement.height, + sourceAudioCodec: transcodingElement.sourceAudioCodec, + sourceVideoCodec: transcodingElement.sourceVideoCodec, + timeStamp: transcodingElement.timeStamp, + transcodeHwRequested: transcodingElement.transcodeHwRequested === 1, + videoCodec: transcodingElement.videoCodec, + videoDecision: transcodingElement.videoDecision, + width: transcodingElement.width, + }, + }, + }, + } as GenericSessionInfo; + }) + .filter((x) => x !== undefined) as GenericSessionInfo[]; + + return videos; + } + + private findElement(name: string, elements: Element[] | undefined) { + return elements?.find((x) => x.name === name)?.attributes; + } + + private getCurrentlyPlayingType(type: string): GenericCurrentlyPlaying['type'] { + switch (type) { + case 'movie': + return 'movie'; + case 'episode': + return 'video'; + default: + return undefined; + } + } +} diff --git a/src/tools/server/translation-namespaces.ts b/src/tools/server/translation-namespaces.ts index ad11ffe33..ec2b9b044 100644 --- a/src/tools/server/translation-namespaces.ts +++ b/src/tools/server/translation-namespaces.ts @@ -33,6 +33,7 @@ export const dashboardNamespaces = [ 'modules/docker', 'modules/dashdot', 'modules/overseerr', + 'modules/media-server', 'modules/common-media-cards', 'modules/video-stream', ]; diff --git a/src/types/api/media-server/media-server.ts b/src/types/api/media-server/media-server.ts new file mode 100644 index 000000000..1b6301caf --- /dev/null +++ b/src/types/api/media-server/media-server.ts @@ -0,0 +1,34 @@ +import { GenericSessionInfo } from './session-info'; + +export type GenericMediaServer = { + /** + * The type of the media server. + * Undefined indicates, that the type is either unsupported or recognizing went wrong + */ + type: 'jellyfin' | 'plex' | undefined; + + /** + * The address of the server + */ + serverAddress: string; + + /** + * The current version of the server + */ + version: string | undefined; + + /** + * The active sessions on the server + */ + sessions: GenericSessionInfo[]; + + /** + * The app id of the used app + */ + appId: string; + + /** + * Indicates, wether the communication was successfull or not + */ + success: boolean; +}; diff --git a/src/types/api/media-server/response.ts b/src/types/api/media-server/response.ts new file mode 100644 index 000000000..f4f89d726 --- /dev/null +++ b/src/types/api/media-server/response.ts @@ -0,0 +1,5 @@ +import { GenericMediaServer } from './media-server'; + +export type MediaServersResponseType = { + servers: GenericMediaServer[]; +}; diff --git a/src/types/api/media-server/session-info.ts b/src/types/api/media-server/session-info.ts new file mode 100644 index 000000000..5b8c25ab7 --- /dev/null +++ b/src/types/api/media-server/session-info.ts @@ -0,0 +1,46 @@ +export type GenericSessionInfo = { + supportsMediaControl: boolean; + username: string | undefined; + id: string; + sessionName: string; + userProfilePicture: string | undefined; + currentlyPlaying: GenericCurrentlyPlaying | undefined; +}; + +export type GenericCurrentlyPlaying = { + name: string; + seasonName: string | undefined; + albumName: string | undefined; + episodeCount: number | undefined; + type: 'audio' | 'video' | 'tv' | 'movie' | undefined; + metadata: { + video: { + videoCodec: string | undefined; + videoFrameRate: string | undefined; + height: number | undefined; + width: number | undefined; + bitrate: number | undefined; + } | undefined; + audio: { + audioCodec: string | undefined; + audioChannels: number | undefined; + } | undefined; + transcoding: { + context: string | undefined; + sourceVideoCodec: string | undefined; + sourceAudioCodec: string | undefined; + videoDecision: string | undefined; + audioDecision: string | undefined; + container: string | undefined; + videoCodec: string | undefined; + audioCodec: string | undefined; + error: boolean | undefined; + duration: number | undefined; + audioChannels: number | undefined; + width: number | undefined; + height: number | undefined; + transcodeHwRequested: boolean | undefined; + timeStamp: number | undefined; + } | undefined; + }; +}; diff --git a/src/types/app.ts b/src/types/app.ts index 0f6284628..2e611c031 100644 --- a/src/types/app.ts +++ b/src/types/app.ts @@ -41,6 +41,8 @@ export type IntegrationType = | 'deluge' | 'qBittorrent' | 'transmission' + | 'plex' + | 'jellyfin' | 'nzbGet'; export type AppIntegrationType = { @@ -79,6 +81,8 @@ export const integrationFieldProperties: { nzbGet: ['username', 'password'], qBittorrent: ['username', 'password'], transmission: ['username', 'password'], + jellyfin: ['username', 'password'], + plex: ['apiKey'], }; export type IntegrationFieldDefinitionType = { diff --git a/src/widgets/download-speed/Tile.tsx b/src/widgets/download-speed/Tile.tsx index 1926b1c8d..48b3117fb 100644 --- a/src/widgets/download-speed/Tile.tsx +++ b/src/widgets/download-speed/Tile.tsx @@ -15,6 +15,7 @@ import { Serie, Datum, ResponsiveLine } from '@nivo/line'; import { IconDownload, IconUpload } from '@tabler/icons'; import { useTranslation } from 'next-i18next'; import { useEffect } from 'react'; +import { AppAvatar } from '../../components/AppAvatar'; import { useConfigContext } from '../../config/provider'; import { useGetDownloadClientsQueue } from '../../hooks/widgets/download-speed/useGetNetworkSpeed'; import { useColorTheme } from '../../tools/color'; @@ -258,17 +259,3 @@ export default function TorrentNetworkTrafficTile({ widget }: TorrentNetworkTraf ); } - -const AppAvatar = ({ iconUrl }: { iconUrl: string }) => { - const { colors, colorScheme } = useMantineTheme(); - - return ( - - ); -}; diff --git a/src/widgets/index.ts b/src/widgets/index.ts index d431590ba..c568c2ddf 100644 --- a/src/widgets/index.ts +++ b/src/widgets/index.ts @@ -2,6 +2,7 @@ import calendar from './calendar/CalendarTile'; import dashdot from './dashDot/DashDotTile'; import date from './date/DateTile'; import torrentNetworkTraffic from './download-speed/TorrentNetworkTrafficTile'; +import mediaServer from './media-server/MediaServerTile'; import rss from './rss/RssWidgetTile'; import torrent from './torrent/TorrentTile'; import usenet from './useNet/UseNetTile'; @@ -18,4 +19,5 @@ export default { date, rss, 'video-stream': videoStream, + 'media-server': mediaServer, }; diff --git a/src/widgets/media-server/DetailCollapseable.tsx b/src/widgets/media-server/DetailCollapseable.tsx new file mode 100644 index 000000000..536e3d0e7 --- /dev/null +++ b/src/widgets/media-server/DetailCollapseable.tsx @@ -0,0 +1,128 @@ +import { Card, Divider, Flex, Grid, Group, Text } from '@mantine/core'; +import { IconDeviceMobile, IconId } from '@tabler/icons'; +import { GenericSessionInfo } from '../../types/api/media-server/session-info'; + +export const DetailCollapseable = ({ session }: { session: GenericSessionInfo }) => { + let details: { title: string; metrics: { name: string; value: string | undefined }[] }[] = []; + + if (session.currentlyPlaying) { + if (session.currentlyPlaying.metadata.video) { + details = [ + ...details, + { + title: 'Video', + metrics: [ + { + name: 'Resolution', + value: `${session.currentlyPlaying.metadata.video.width}x${session.currentlyPlaying.metadata.video.height}`, + }, + { + name: 'Framerate', + value: session.currentlyPlaying.metadata.video.videoFrameRate, + }, + { + name: 'Codec', + value: session.currentlyPlaying.metadata.video.videoCodec, + }, + { + name: 'Bitrate', + value: session.currentlyPlaying.metadata.video.bitrate + ? String(session.currentlyPlaying.metadata.video.bitrate) + : undefined, + }, + ], + }, + ]; + } + if (session.currentlyPlaying.metadata.audio) { + details = [ + ...details, + { + title: 'Audio', + metrics: [ + { + name: 'Audio channels', + value: `${session.currentlyPlaying.metadata.audio.audioChannels}`, + }, + { + name: 'Audio codec', + value: session.currentlyPlaying.metadata.audio.audioCodec, + }, + ], + }, + ]; + } + + if (session.currentlyPlaying.metadata.transcoding) { + details = [ + ...details, + { + title: 'Transcoding', + metrics: [ + { + name: 'Resolution', + value: `${session.currentlyPlaying.metadata.transcoding.width}x${session.currentlyPlaying.metadata.transcoding.height}`, + }, + { + name: 'Context', + value: session.currentlyPlaying.metadata.transcoding.context, + }, + { + name: 'Hardware encoding requested', + value: session.currentlyPlaying.metadata.transcoding.transcodeHwRequested + ? 'yes' + : 'no', + }, + { + name: 'Source codec', + value: + session.currentlyPlaying.metadata.transcoding.sourceAudioCodec || + session.currentlyPlaying.metadata.transcoding.sourceVideoCodec + ? `${session.currentlyPlaying.metadata.transcoding.sourceVideoCodec} ${session.currentlyPlaying.metadata.transcoding.sourceAudioCodec}` + : undefined, + }, + { + name: 'Target codec', + value: `${session.currentlyPlaying.metadata.transcoding.videoCodec} ${session.currentlyPlaying.metadata.transcoding.audioCodec}`, + }, + ], + }, + ]; + } + } + + return ( + + + + + ID + + {session.id} + + + + + Device + + {session.sessionName} + + {details.length > 0 && } + + {details.map((detail, index) => ( + + {detail.title} + {detail.metrics + .filter((x) => x.value !== undefined) + .map((metric, index2) => ( + + {metric.name} + {metric.value} + + ))} + + ))} + + + ); +}; diff --git a/src/widgets/media-server/MediaServerTile.tsx b/src/widgets/media-server/MediaServerTile.tsx new file mode 100644 index 000000000..c1c40064d --- /dev/null +++ b/src/widgets/media-server/MediaServerTile.tsx @@ -0,0 +1,110 @@ +import { + Avatar, + Center, + Group, + Loader, + ScrollArea, + Stack, + Table, + Text, + Title, +} from '@mantine/core'; +import { IconAlertTriangle, IconMovie } from '@tabler/icons'; +import { useTranslation } from 'next-i18next'; +import { AppAvatar } from '../../components/AppAvatar'; +import { useConfigContext } from '../../config/provider'; +import { useGetMediaServers } from '../../hooks/widgets/media-servers/useGetMediaServers'; +import { defineWidget } from '../helper'; +import { IWidget } from '../widgets'; +import { TableRow } from './TableRow'; + +const definition = defineWidget({ + id: 'media-server', + icon: IconMovie, + options: {}, + component: MediaServerTile, + gridstack: { + minWidth: 3, + minHeight: 2, + maxWidth: 12, + maxHeight: 12, + }, +}); + +export type MediaServerWidget = IWidget<(typeof definition)['id'], typeof definition>; + +interface MediaServerWidgetProps { + widget: MediaServerWidget; +} + +function MediaServerTile({ widget }: MediaServerWidgetProps) { + const { t } = useTranslation('modules/media-server'); + const { config } = useConfigContext(); + + const { data, isError } = useGetMediaServers({ + enabled: config !== undefined, + }); + + if (isError) { + return ( +
+ + + {t('card.errors.general.title')} + {t('card.errors.general.text')} + +
+ ); + } + + if (!data) { +
+ +
; + } + + return ( + + + + + + + + + + + + {data?.servers.map((server) => { + const app = config?.apps.find((x) => x.id === server.appId); + return server.sessions.map((session, index) => ( + + )); + })} + +
{t('card.table.header.session')}{t('card.table.header.user')}{t('card.table.header.currentlyPlaying')}
+
+ + + + {data?.servers.map((server) => { + const app = config?.apps.find((x) => x.id === server.appId); + + if (!app) { + return null; + } + + return ( + + ); + })} + + +
+ ); +} + +export default definition; diff --git a/src/widgets/media-server/NowPlayingDisplay.tsx b/src/widgets/media-server/NowPlayingDisplay.tsx new file mode 100644 index 000000000..c3bdc40d1 --- /dev/null +++ b/src/widgets/media-server/NowPlayingDisplay.tsx @@ -0,0 +1,50 @@ +import { Flex, Group, Stack, Text } from '@mantine/core'; +import { + IconDeviceTv, + IconHeadphones, + IconQuestionMark, + IconVideo, + TablerIcon, +} from '@tabler/icons'; +import { useTranslation } from 'next-i18next'; +import { GenericSessionInfo } from '../../types/api/media-server/session-info'; + +export const NowPlayingDisplay = ({ session }: { session: GenericSessionInfo }) => { + const { t } = useTranslation(); + + if (!session.currentlyPlaying) { + return null; + } + + const Icon = (): TablerIcon => { + switch (session.currentlyPlaying?.type) { + case 'audio': + return IconHeadphones; + case 'tv': + return IconDeviceTv; + case 'video': + return IconVideo; + default: + return IconQuestionMark; + } + }; + + const Test = Icon(); + + return ( + + + + {session.currentlyPlaying.name} + + {session.currentlyPlaying.albumName ? ( + {session.currentlyPlaying.albumName} + ) : ( + session.currentlyPlaying.seasonName && ( + {session.currentlyPlaying.seasonName} + ) + )} + + + ); +}; diff --git a/src/widgets/media-server/TableRow.tsx b/src/widgets/media-server/TableRow.tsx new file mode 100644 index 000000000..b7f4e966a --- /dev/null +++ b/src/widgets/media-server/TableRow.tsx @@ -0,0 +1,73 @@ +import { + Avatar, + Card, + Collapse, + createStyles, + Flex, + Grid, + Group, + Stack, + Text, + Title, +} from '@mantine/core'; +import { useState } from 'react'; +import { AppAvatar } from '../../components/AppAvatar'; +import { GenericSessionInfo } from '../../types/api/media-server/session-info'; +import { AppType } from '../../types/app'; +import { DetailCollapseable } from './DetailCollapseable'; +import { NowPlayingDisplay } from './NowPlayingDisplay'; + +interface TableRowProps { + session: GenericSessionInfo; + app: AppType | undefined; +} + +export const TableRow = ({ session, app }: TableRowProps) => { + const [collapseOpen, setCollapseOpen] = useState(false); + const hasUserThumb = session.userProfilePicture !== undefined; + const { classes } = useStyles(); + return ( + <> + setCollapseOpen(!collapseOpen)}> + + + {app?.appearance.iconUrl && } + {session.sessionName} + + + + + {hasUserThumb ? ( + + ) : ( + + {session.username?.at(0)?.toUpperCase()} + + )} + {session.username} + + + + + + + + + + + + + + + ); +}; + +const useStyles = createStyles(() => ({ + dataRow: { + cursor: 'pointer', + }, + collapseTableDataCell: { + border: 'none !important', + padding: '0 !important', + }, +})); diff --git a/yarn.lock b/yarn.lock index 07f8b81f0..efb2bc550 100644 --- a/yarn.lock +++ b/yarn.lock @@ -680,45 +680,45 @@ __metadata: languageName: node linkType: hard -"@floating-ui/core@npm:^1.1.0": - version: 1.1.1 - resolution: "@floating-ui/core@npm:1.1.1" - checksum: a8497bf981eedf652d275f7207342a9b24c1a67909dca9e042e2379b24321e6688c7774415f3ea607c466adf82b02d8779513f449376c46b2982f2380c80cd35 +"@floating-ui/core@npm:^1.2.1": + version: 1.2.1 + resolution: "@floating-ui/core@npm:1.2.1" + checksum: 7a263e15abad4cd98ce3938075c90f76bafef71c67964c83dd7cf1e1916c92ccfa46a4f2b95b403bb180f70b3e7ded118db1cbc3426269ec70e948edf0d19ae2 languageName: node linkType: hard -"@floating-ui/dom@npm:^1.1.1": - version: 1.1.1 - resolution: "@floating-ui/dom@npm:1.1.1" +"@floating-ui/dom@npm:^1.2.1": + version: 1.2.1 + resolution: "@floating-ui/dom@npm:1.2.1" dependencies: - "@floating-ui/core": ^1.1.0 - checksum: 8b7f3b98ed7ec0b634e4a0b735253b0442358c5cea8302935fc185b2bd882202a053622abe9248c76d0908645dd35f93adeaed2d64371b2ab76b36725ce3f7d3 + "@floating-ui/core": ^1.2.1 + checksum: ad6928d5dd934ca8b3dcffdfcacde9d147afc0db4db0c0294b23d5f897224d73c7f0ae578b46d813f0d7ae22a86e7a3f8914fdbcdf4a8d42a1b430d0518f45be languageName: node linkType: hard -"@floating-ui/react-dom@npm:^1.2.2": - version: 1.2.2 - resolution: "@floating-ui/react-dom@npm:1.2.2" +"@floating-ui/react-dom@npm:^1.3.0": + version: 1.3.0 + resolution: "@floating-ui/react-dom@npm:1.3.0" dependencies: - "@floating-ui/dom": ^1.1.1 + "@floating-ui/dom": ^1.2.1 peerDependencies: react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 5cb7879a8fe53ce4ca8361473d93e18806402d6bcde4e935d5aafa2b72032019e62f7d019d7d7145ead1ed829569ac14d6d8c50d7beedcea04cb3d02af661597 + checksum: ce0ad3e3bbe43cfd15a6a0d5cccede02175c845862bfab52027995ab99c6b29630180dc7d146f76ebb34730f90a6ab9bf193c8984fe8d7f56062308e4ca98f77 languageName: node linkType: hard "@floating-ui/react@npm:^0.19.1": - version: 0.19.1 - resolution: "@floating-ui/react@npm:0.19.1" + version: 0.19.2 + resolution: "@floating-ui/react@npm:0.19.2" dependencies: - "@floating-ui/react-dom": ^1.2.2 + "@floating-ui/react-dom": ^1.3.0 aria-hidden: ^1.1.3 tabbable: ^6.0.1 peerDependencies: react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: b36000fbb7148d8403072ad0408d6b9463bfa9cdc7e8f91cf741dee159b2ca9f73461f3ae56e4c94def67c16db75337d5ea90624e0a5c0dbb064cfa86ea025e2 + checksum: 00fd827c2dcf879fec221d89ef5b90836bbecacc236ce2acc787db32ae7311d490cd136b13a8d0b6ab12842554a2ee1110605aa832af71a45c0a7297e342072c languageName: node linkType: hard @@ -774,6 +774,16 @@ __metadata: languageName: node linkType: hard +"@jellyfin/sdk@npm:^0.7.0": + version: 0.7.0 + resolution: "@jellyfin/sdk@npm:0.7.0" + dependencies: + axios: 0.27.2 + compare-versions: 5.0.1 + checksum: d7ddae88464e0f2276ec7a793f7e525098a56d96491a450d5e859bf46d74e473a4b4bd5dce2a64ef80484e12e23c14934a4cb20429e89a3c41e52e817e3f31d4 + languageName: node + linkType: hard + "@jest/console@npm:^28.1.3": version: 28.1.3 resolution: "@jest/console@npm:28.1.3" @@ -1058,133 +1068,133 @@ __metadata: linkType: hard "@mantine/core@npm:^5.9.3": - version: 5.10.3 - resolution: "@mantine/core@npm:5.10.3" + version: 5.10.4 + resolution: "@mantine/core@npm:5.10.4" dependencies: "@floating-ui/react": ^0.19.1 - "@mantine/styles": 5.10.3 - "@mantine/utils": 5.10.3 + "@mantine/styles": 5.10.4 + "@mantine/utils": 5.10.4 "@radix-ui/react-scroll-area": 1.0.2 react-textarea-autosize: 8.3.4 peerDependencies: - "@mantine/hooks": 5.10.3 + "@mantine/hooks": 5.10.4 react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 460e613e90ecd94a9414bb93a98046e076256fa54ec5cd0b43d20815755e7862826d912a516591d2a2e0356647d72b293564d2ccb9ca1fb8999da0e963d701bd + checksum: 98cba720fa9764a7a45b13d167ad2dce1af1b639c202a25cf60c8ca439e386dfddefae262e770df62348af90647ba6cbc095c074a8f0582968059450fbbc2ff0 languageName: node linkType: hard "@mantine/dates@npm:^5.9.3": - version: 5.10.3 - resolution: "@mantine/dates@npm:5.10.3" + version: 5.10.4 + resolution: "@mantine/dates@npm:5.10.4" dependencies: - "@mantine/utils": 5.10.3 + "@mantine/utils": 5.10.4 peerDependencies: - "@mantine/core": 5.10.3 - "@mantine/hooks": 5.10.3 + "@mantine/core": 5.10.4 + "@mantine/hooks": 5.10.4 dayjs: ">=1.0.0" react: ">=16.8.0" - checksum: 80e22b0eb2235a373f0d35979bea7aeea671515962f4d119811145da7086f2258663ceb2d19a6ad23a02b6f08e1c53047778ec37cf559d8e81c9061d9f83a159 + checksum: 14a32aa4c16e030266629dfc5171e930d271682de742541b2298d448df11c9fab40e1d8003eabccfd5449a65ef14681993af6426197da4dcad1f509fb9fff932 languageName: node linkType: hard "@mantine/dropzone@npm:^5.9.3": - version: 5.10.3 - resolution: "@mantine/dropzone@npm:5.10.3" + version: 5.10.4 + resolution: "@mantine/dropzone@npm:5.10.4" dependencies: - "@mantine/utils": 5.10.3 + "@mantine/utils": 5.10.4 react-dropzone: 14.2.3 peerDependencies: - "@mantine/core": 5.10.3 - "@mantine/hooks": 5.10.3 + "@mantine/core": 5.10.4 + "@mantine/hooks": 5.10.4 react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 550bf8a282220adb3f7564e2ffa7c617480a5ab9d2556c50072b614450ca1d7fc526e8755a635936c4cb76f9a090f91686f690b7e7704320972dd282e00ab1d6 + checksum: 0b837b5bc7c982ad9832142c880c1b0f2827de5c2163b76895cac2dbe6204f7c67e466468a3fb00f72482bad957c4b1209820025c4dfc2c25650734df79e208b languageName: node linkType: hard "@mantine/form@npm:^5.9.3": - version: 5.10.3 - resolution: "@mantine/form@npm:5.10.3" + version: 5.10.4 + resolution: "@mantine/form@npm:5.10.4" dependencies: fast-deep-equal: ^3.1.3 klona: ^2.0.5 peerDependencies: react: ">=16.8.0" - checksum: 88bcf7b37d19e0648500e9da7111d961a3b130ef1164f4c4fc15f606cececdddad614887ff6659e0a0ce3896a4e52e62c2f72758f857e932e903cf412a69ca54 + checksum: 00ebc0011981f8dae5e96e833f7e0102067a7bc38b1f6208b377f029fdc8bb703a492f40a688eb015dea3147c3b6e5a7cd26c584b93cc8d2567f0dcb10319759 languageName: node linkType: hard "@mantine/hooks@npm:^5.9.3": - version: 5.10.3 - resolution: "@mantine/hooks@npm:5.10.3" + version: 5.10.4 + resolution: "@mantine/hooks@npm:5.10.4" peerDependencies: react: ">=16.8.0" - checksum: 629554658e910dec1c14ecbdaf8e48c1ce2af022044269e73ff069d719a4f9b68428bb75ade5108ace40bd29253658e663c594a237e080226de28705c10c871e + checksum: 41ededb62ea9311303e4b8d577ec21c12ddb339e60e70c3a1f561cb1b2c66fb6e6f29a7a23a89322748779cc06797de749203afd546f2b13180781c0e2873fa6 languageName: node linkType: hard "@mantine/modals@npm:^5.9.3": - version: 5.10.3 - resolution: "@mantine/modals@npm:5.10.3" + version: 5.10.4 + resolution: "@mantine/modals@npm:5.10.4" dependencies: - "@mantine/utils": 5.10.3 + "@mantine/utils": 5.10.4 peerDependencies: - "@mantine/core": 5.10.3 - "@mantine/hooks": 5.10.3 + "@mantine/core": 5.10.4 + "@mantine/hooks": 5.10.4 react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: b013eb3c68ee1c0f163c1bd4b786c59e4a7f77d596f175ef2a88b1922153c24a154f12a36b65138af778cdd47944869056df5beb921e5d07801f5246b19b8da9 + checksum: 82fce48fffbbce11526212b994e2763f0b36a276a86385f5b9ac1b5f1226f39f3f12aa171fe8b9181b755f95dbb869ef88f339e620bfd0ec28b7b0017f6b7fa7 languageName: node linkType: hard "@mantine/next@npm:^5.9.3": - version: 5.10.3 - resolution: "@mantine/next@npm:5.10.3" + version: 5.10.4 + resolution: "@mantine/next@npm:5.10.4" dependencies: - "@mantine/ssr": 5.10.3 - "@mantine/styles": 5.10.3 + "@mantine/ssr": 5.10.4 + "@mantine/styles": 5.10.4 peerDependencies: next: "*" react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 78ee3e7f7565318f40f367c8d53edfb164ae10c4fcc788b8792b27ae3f10fad81be8dbfaecd171fd66c43eafc50cc30ea5ab7f3da5421642b3a8c96d5a03d618 + checksum: 344590d09cfef4194187f7ea430082d9f22379e264b04fa3a9261a137abf9b1e4681eb7c610b678822b2ad3001f5611e201a73a4612ee8b13d93460d68a28b0c languageName: node linkType: hard "@mantine/notifications@npm:^5.9.3": - version: 5.10.3 - resolution: "@mantine/notifications@npm:5.10.3" + version: 5.10.4 + resolution: "@mantine/notifications@npm:5.10.4" dependencies: - "@mantine/utils": 5.10.3 + "@mantine/utils": 5.10.4 react-transition-group: 4.4.2 peerDependencies: - "@mantine/core": 5.10.3 - "@mantine/hooks": 5.10.3 + "@mantine/core": 5.10.4 + "@mantine/hooks": 5.10.4 react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: e4b735725fc7c0558cf0b084f677d432d6bd49220621ff20e32371621ba454a9d8b8eba0a36db7e99a0951cc174cb51b7ec91a9e127cba13330c93f3bcaecb10 + checksum: da439698331f09dd1f3efaa470df1f9717f7309c2a61b0bfaa14bd832185fbd0711fbaffe867b0d6978108db30d059909b59c2b7c0ed83d1bd55bc8b7a6d3e7f languageName: node linkType: hard -"@mantine/ssr@npm:5.10.3": - version: 5.10.3 - resolution: "@mantine/ssr@npm:5.10.3" +"@mantine/ssr@npm:5.10.4": + version: 5.10.4 + resolution: "@mantine/ssr@npm:5.10.4" dependencies: - "@mantine/styles": 5.10.3 + "@mantine/styles": 5.10.4 html-react-parser: 1.4.12 peerDependencies: "@emotion/react": ">=11.9.0" "@emotion/server": ">=11.4.0" react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 8a6f0140f8fc853e3be8b0ec78908f71378ab76f36efe58ea2b1818139db60658bd12d3b1a56b9e61218262e85e2fbf9488393c3f0a64cb2d24125a27230397f + checksum: 74e5c56b85ea731597aba876941086306abb5ae24faa86d37a5210cfd743aa164e97b29e547563f810ef92b51b3c90bd02ce1a39fbee84cbec2abd2e6ad45ca7 languageName: node linkType: hard -"@mantine/styles@npm:5.10.3": - version: 5.10.3 - resolution: "@mantine/styles@npm:5.10.3" +"@mantine/styles@npm:5.10.4": + version: 5.10.4 + resolution: "@mantine/styles@npm:5.10.4" dependencies: clsx: 1.1.1 csstype: 3.0.9 @@ -1192,16 +1202,16 @@ __metadata: "@emotion/react": ">=11.9.0" react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 60fcfd5ccbfd7e1106a24a13c3f705ac4b995214ae038c2d8e7bf5e795cb7c1f5da9ee5e336caedf2a537e77272a05eb315e76df3edb4a466ec158ae6aeaf8b5 + checksum: 54af835dca68a457be758570c82eab2d602da19a24da599b2f2c02f451be136a400b2f8efbbdc7d1a677188b57515d3ace23df0b8aa8e37c4ddf3a2fdbce1630 languageName: node linkType: hard -"@mantine/utils@npm:5.10.3": - version: 5.10.3 - resolution: "@mantine/utils@npm:5.10.3" +"@mantine/utils@npm:5.10.4": + version: 5.10.4 + resolution: "@mantine/utils@npm:5.10.4" peerDependencies: react: ">=16.8.0" - checksum: 063237e49f3c52e3bbcd99e7a8383ac881718d9e1e039d32249231370a463a19e070cea7f90c3c0fea2933c3c162f5509a0f2add015db2aeb90f7945cd954a12 + checksum: 96e2602f8500c29b5979d4fe0b3456c8de911ff1bd2ef216d960b23a5370ff6828871aa859538a4004ad095fb63d7e0e76cdfb365bdb930f70f8076d730302c1 languageName: node linkType: hard @@ -1877,33 +1887,33 @@ __metadata: languageName: node linkType: hard -"@tanstack/query-core@npm:4.24.4": - version: 4.24.4 - resolution: "@tanstack/query-core@npm:4.24.4" - checksum: b357e950a41e5769878a3bfe9dd56a66828ccebb2f9396e568e8835bed71eb37926770a461e3aa5bafb7e1121c5141cee86aedf3ced88a2eacf754d545b107cd +"@tanstack/query-core@npm:4.24.6": + version: 4.24.6 + resolution: "@tanstack/query-core@npm:4.24.6" + checksum: 495dff04f27d72174051f97676fce0fbe322bca966edb7f174f0005a3363ac0300c31eb06255dafa7ee519a430840542379f5ec9f6846903b6bb878e46c33656 languageName: node linkType: hard "@tanstack/react-query-devtools@npm:^4.24.4": - version: 4.24.4 - resolution: "@tanstack/react-query-devtools@npm:4.24.4" + version: 4.24.6 + resolution: "@tanstack/react-query-devtools@npm:4.24.6" dependencies: "@tanstack/match-sorter-utils": ^8.7.0 superjson: ^1.10.0 use-sync-external-store: ^1.2.0 peerDependencies: - "@tanstack/react-query": 4.24.4 + "@tanstack/react-query": 4.24.6 react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 8b8f1ae8e55f016f25b383baae0000f0b608ec0327ee4eccb0a7b3b1c596b12f68c848e429be84c8a6039bd0a7d5bd36a7232fd7818868f1a3ae3d0462898e26 + checksum: 65722242dc040f74fe70748000cc3f24c2cdd45fa2cfc1ee9d846c37c7d3e55438426a4a4973e6eb2a55efb280c0f5ea2ae091bfa493c1f3ca4edac2aba757af languageName: node linkType: hard "@tanstack/react-query@npm:^4.2.1": - version: 4.24.4 - resolution: "@tanstack/react-query@npm:4.24.4" + version: 4.24.6 + resolution: "@tanstack/react-query@npm:4.24.6" dependencies: - "@tanstack/query-core": 4.24.4 + "@tanstack/query-core": 4.24.6 use-sync-external-store: ^1.2.0 peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -1914,7 +1924,7 @@ __metadata: optional: true react-native: optional: true - checksum: 8cf008d690fc5d336f6471ea2bc0751189f03874dcc7930cf8944fb57fa34e3b5347823775dbd36629d53a7aacb9ce87482b1adb3ad80d277a3a6c73b797f6a2 + checksum: 250bc2cba8dd7da61680a8e360fac87ee057d079ee3be950c76dbc063f941119817634857c1cb0d9b12042dd5d4fbe847c02af5dfb89ae8daa0f9339f87ad605 languageName: node linkType: hard @@ -2080,9 +2090,9 @@ __metadata: linkType: hard "@types/node@npm:*, @types/node@npm:^18.11.18": - version: 18.11.18 - resolution: "@types/node@npm:18.11.18" - checksum: 03f17f9480f8d775c8a72da5ea7e9383db5f6d85aa5fefde90dd953a1449bd5e4ffde376f139da4f3744b4c83942166d2a7603969a6f8ea826edfb16e6e3b49d + version: 18.13.0 + resolution: "@types/node@npm:18.13.0" + checksum: 4ea10f8802848b01672bce938f678b6774ca2cee0c9774f12275ab064ae07818419c3e2e41d6257ce7ba846d1ea26c63214aa1dfa4166fa3746291752b8c6416 languageName: node linkType: hard @@ -2094,9 +2104,9 @@ __metadata: linkType: hard "@types/node@npm:^16.10.2": - version: 16.18.11 - resolution: "@types/node@npm:16.18.11" - checksum: 2a3b1da13063debe6e26f732defb5f03ef4ef732c3e08daba838d8850433bd00e537ce1a97ce9bcfc4b15db5218d701d1265fae94e0d6926906bec157e6b46e0 + version: 16.18.12 + resolution: "@types/node@npm:16.18.12" + checksum: fc3271182414f8593018ef8f00b4718116a92f463f619081bd399d9460e7861e1dd7eebc7cf94c23567e418ff397babed077011711aae8d47171b5a81c5bd71d languageName: node linkType: hard @@ -2201,12 +2211,12 @@ __metadata: linkType: hard "@typescript-eslint/eslint-plugin@npm:^5.30.7": - version: 5.50.0 - resolution: "@typescript-eslint/eslint-plugin@npm:5.50.0" + version: 5.52.0 + resolution: "@typescript-eslint/eslint-plugin@npm:5.52.0" dependencies: - "@typescript-eslint/scope-manager": 5.50.0 - "@typescript-eslint/type-utils": 5.50.0 - "@typescript-eslint/utils": 5.50.0 + "@typescript-eslint/scope-manager": 5.52.0 + "@typescript-eslint/type-utils": 5.52.0 + "@typescript-eslint/utils": 5.52.0 debug: ^4.3.4 grapheme-splitter: ^1.0.4 ignore: ^5.2.0 @@ -2220,43 +2230,43 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 351c4a157a7d717cc3835bdc09324b20d649463738a029c5701e5a38cdb162305ff7d56adff196a0c3245c24ea3167bbdac7f1c30399b8c1d495abbdbc1c53d6 + checksum: cff07ee94d8ab2a1b6c33b5c5bf641eff2bf2bebc0f35a9d8b3f128fd610e27a4aaf620bc2ad23608ad161b1810b7e32e5a2e0f746cc5094c3f506f7a14daa34 languageName: node linkType: hard "@typescript-eslint/parser@npm:^5.30.7": - version: 5.50.0 - resolution: "@typescript-eslint/parser@npm:5.50.0" + version: 5.52.0 + resolution: "@typescript-eslint/parser@npm:5.52.0" dependencies: - "@typescript-eslint/scope-manager": 5.50.0 - "@typescript-eslint/types": 5.50.0 - "@typescript-eslint/typescript-estree": 5.50.0 + "@typescript-eslint/scope-manager": 5.52.0 + "@typescript-eslint/types": 5.52.0 + "@typescript-eslint/typescript-estree": 5.52.0 debug: ^4.3.4 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 816a421ce9a5c61a2e92499d6d400aed4211ca5b685e0212844b6659f7acfeba1cca0418b462236c44eea6e8a2574cd51ccb7abc2bf4a8cad5b7a275d71ae9bf + checksum: 1d8ff6e932f9c9db8d24b16ce89fd963f0982c38559e500aa1f8dc5cd66abd02f1659dd1a1361ce550def05331803caa69a69a039b54c94fc0f22919a2305c12 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:5.50.0": - version: 5.50.0 - resolution: "@typescript-eslint/scope-manager@npm:5.50.0" +"@typescript-eslint/scope-manager@npm:5.52.0": + version: 5.52.0 + resolution: "@typescript-eslint/scope-manager@npm:5.52.0" dependencies: - "@typescript-eslint/types": 5.50.0 - "@typescript-eslint/visitor-keys": 5.50.0 - checksum: bd49447a834c82cb130e6900644042c3a84195bf7a63483385e90b6454c65856d6f276c997cad6bf9c36c9d0cb168fdde625ce4c78c3b8bcce42da782270794b + "@typescript-eslint/types": 5.52.0 + "@typescript-eslint/visitor-keys": 5.52.0 + checksum: 9a03fe30f8e90a5106c482478f213eefdd09f2f74e24d9dc59b453885466a758fe6d1cd24d706aed6188fb03c84b16ca6491cf20da6b16b8fc53cad8b8c327f2 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:5.50.0": - version: 5.50.0 - resolution: "@typescript-eslint/type-utils@npm:5.50.0" +"@typescript-eslint/type-utils@npm:5.52.0": + version: 5.52.0 + resolution: "@typescript-eslint/type-utils@npm:5.52.0" dependencies: - "@typescript-eslint/typescript-estree": 5.50.0 - "@typescript-eslint/utils": 5.50.0 + "@typescript-eslint/typescript-estree": 5.52.0 + "@typescript-eslint/utils": 5.52.0 debug: ^4.3.4 tsutils: ^3.21.0 peerDependencies: @@ -2264,23 +2274,23 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: d2fc2fd10ef300865fd6a902ae92aef6c45cddc4359445f1e5c6dc9511063b52d2170cc6b525763395d4171c177b3d0fffd77cf9a2ab7e01fcd7109bd1a5a585 + checksum: ac5422040461febab8a2eeec76d969024ccff76203dec357f7220c9b5e0dde96e3e3a76fd4118d42b50bd5bfb3a194aaceeb63417a2ac4e1ebf5e687558a9a10 languageName: node linkType: hard -"@typescript-eslint/types@npm:5.50.0": - version: 5.50.0 - resolution: "@typescript-eslint/types@npm:5.50.0" - checksum: 1189c63d35abeec685dd519fd923926b884e63d5e10e4a9fe995aebfde59b8a2e10773090ec3ba32a0ec408746b18f6a454d9bedb0b6c7ce8b6066547144fb4d +"@typescript-eslint/types@npm:5.52.0": + version: 5.52.0 + resolution: "@typescript-eslint/types@npm:5.52.0" + checksum: 018940d61aebf7cf3f7de1b9957446e2ea01f08fe950bef4788c716a3a88f7c42765fe7d80152b0d0428fcd4bd3ace2dfa8c459ba1c59d9a84e951642180f869 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.50.0": - version: 5.50.0 - resolution: "@typescript-eslint/typescript-estree@npm:5.50.0" +"@typescript-eslint/typescript-estree@npm:5.52.0": + version: 5.52.0 + resolution: "@typescript-eslint/typescript-estree@npm:5.52.0" dependencies: - "@typescript-eslint/types": 5.50.0 - "@typescript-eslint/visitor-keys": 5.50.0 + "@typescript-eslint/types": 5.52.0 + "@typescript-eslint/visitor-keys": 5.52.0 debug: ^4.3.4 globby: ^11.1.0 is-glob: ^4.0.3 @@ -2289,35 +2299,35 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: cb1ac8d39647da6d52750c713d9635750ed41245ec82f937a159a71ad3bf490ebabfad3b43eeca07bca39d60df30d3a2f31f8bed0061381731d92a62e284b867 + checksum: 67d396907fee3d6894e26411a5098a37f07e5d50343189e6361ff7db91c74a7ffe2abd630d11f14c2bda1f4af13edf52b80b11cbccb55b44079c7cec14c9e108 languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.50.0, @typescript-eslint/utils@npm:^5.10.0, @typescript-eslint/utils@npm:^5.43.0": - version: 5.50.0 - resolution: "@typescript-eslint/utils@npm:5.50.0" +"@typescript-eslint/utils@npm:5.52.0, @typescript-eslint/utils@npm:^5.10.0, @typescript-eslint/utils@npm:^5.43.0": + version: 5.52.0 + resolution: "@typescript-eslint/utils@npm:5.52.0" dependencies: "@types/json-schema": ^7.0.9 "@types/semver": ^7.3.12 - "@typescript-eslint/scope-manager": 5.50.0 - "@typescript-eslint/types": 5.50.0 - "@typescript-eslint/typescript-estree": 5.50.0 + "@typescript-eslint/scope-manager": 5.52.0 + "@typescript-eslint/types": 5.52.0 + "@typescript-eslint/typescript-estree": 5.52.0 eslint-scope: ^5.1.1 eslint-utils: ^3.0.0 semver: ^7.3.7 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 4471ae8b24449300e009f1cc09ee0d38cce20ae9171e8fbf4ef752ce4eb87104cc0d813d8f7051b619fa05e1e7c12b748dad49832911685297b1bbfef3c01f0b + checksum: 01906be5262ece36537e9d586e4d2d4791e05752a9354bcb42b1f5bf965f53daa13309c61c3dff5e201ea28c298e4e01cf0c93738afa0099fea0da3b1d8cb3a5 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:5.50.0": - version: 5.50.0 - resolution: "@typescript-eslint/visitor-keys@npm:5.50.0" +"@typescript-eslint/visitor-keys@npm:5.52.0": + version: 5.52.0 + resolution: "@typescript-eslint/visitor-keys@npm:5.52.0" dependencies: - "@typescript-eslint/types": 5.50.0 + "@typescript-eslint/types": 5.52.0 eslint-visitor-keys: ^3.3.0 - checksum: 55319cb7ee7b78d07d9dc67a388d69fe0b7f11cbc79190e17e7f87a39c9992d08dab3b5872d5a7f01094dda28ad6ac61d3573e59015ef70bf138d4c4f8c45b88 + checksum: 33b44f0cd35b7b47f34e89d52e47b8d8200f55af306b22db4de104d79f65907458ea022e548f50d966e32fea150432ac9c1ae65b3001b0ad2ac8a17c0211f370 languageName: node linkType: hard @@ -2674,7 +2684,7 @@ __metadata: languageName: node linkType: hard -"axios@npm:^0.27.2": +"axios@npm:0.27.2, axios@npm:^0.27.2": version: 0.27.2 resolution: "axios@npm:0.27.2" dependencies: @@ -2944,17 +2954,17 @@ __metadata: linkType: hard "cacheable-request@npm:^10.2.1": - version: 10.2.6 - resolution: "cacheable-request@npm:10.2.6" + version: 10.2.7 + resolution: "cacheable-request@npm:10.2.7" dependencies: "@types/http-cache-semantics": ^4.0.1 get-stream: ^6.0.1 - http-cache-semantics: ^4.1.0 + http-cache-semantics: ^4.1.1 keyv: ^4.5.2 mimic-response: ^4.0.0 normalize-url: ^8.0.0 responselike: ^3.0.0 - checksum: 273d6cbcec025d2922a641c72eeb7e566963cc68da99248ff83053c9b140fe46ae1a856e7e8855378112d8d342ff2ec9398d1552a3a8ce4af8fbc21c606bda1f + checksum: 25cfbe0cab755bcee3bf6610b0253414e583289f795a28035616bf3186ecb303e8caf1943cbf42d92410b3f24124a1cdd663dd8ff52ac2c997a5129de5e9557a languageName: node linkType: hard @@ -3005,9 +3015,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30001406, caniuse-lite@npm:^1.0.30001449": - version: 1.0.30001450 - resolution: "caniuse-lite@npm:1.0.30001450" - checksum: 511b360bfc907b2e437699364cf96b83507bc45043926450056642332bcd6f65a1e72540c828534ae15e0ac906e3e9af46cb2bb84458dd580bc31478e9dce282 + version: 1.0.30001452 + resolution: "caniuse-lite@npm:1.0.30001452" + checksum: de02aad7b71112409f30de53e8080bef0fe612ed95bba8b14fb830f59683e8caabc27bdd520563686965be77f2cb56e239e44b920144630b91d7fe9911ba8ad5 languageName: node linkType: hard @@ -3073,9 +3083,9 @@ __metadata: linkType: hard "ci-info@npm:^3.2.0": - version: 3.7.1 - resolution: "ci-info@npm:3.7.1" - checksum: 72d93d5101ea1c186511277fbd8d06ae8a6e028cc2fb94361e92bf735b39c5ebd192e8d15a66ff8c4e3ed569f87c2f844e96f90e141b2de5c649f77ec34ff601 + version: 3.8.0 + resolution: "ci-info@npm:3.8.0" + checksum: d0a4d3160497cae54294974a7246202244fff031b0a6ea20dd57b10ec510aa17399c41a1b0982142c105f3255aff2173e5c0dd7302ee1b2f28ba3debda375098 languageName: node linkType: hard @@ -3198,6 +3208,13 @@ __metadata: languageName: node linkType: hard +"compare-versions@npm:5.0.1": + version: 5.0.1 + resolution: "compare-versions@npm:5.0.1" + checksum: 302a4e46224b47b9280cf894c6c87d8df912671fa391dcdbf0e63438d9b0a69fe20dd747fb439e8d54c43af016ff4eaaf0a4c9d8e7ca358bcd12dadf4ad2935e + languageName: node + linkType: hard + "concat-map@npm:0.0.1": version: 0.0.1 resolution: "concat-map@npm:0.0.1" @@ -3261,9 +3278,9 @@ __metadata: linkType: hard "core-js@npm:^3": - version: 3.27.2 - resolution: "core-js@npm:3.27.2" - checksum: 718debd426f55a6b97cf9b757c936be258afd6d4f7052f89d0f96c982d7013e9000b0b006df42831a0cf32adad298e34d6a19052dce9ae1c7ab87162c0c665e0 + version: 3.28.0 + resolution: "core-js@npm:3.28.0" + checksum: 3155fd0ec16d0089106b145e9595280a4ea4bde0d7ff26aa14364cd4f1c203baf6620c3025acd284f363d08b9f21104101692766ca9a36ffeee7307bdf3e1881 languageName: node linkType: hard @@ -3355,7 +3372,14 @@ __metadata: languageName: node linkType: hard -"d3-format@npm:1 - 2, d3-format@npm:^1.4.4": +"d3-format@npm:1 - 2": + version: 2.0.0 + resolution: "d3-format@npm:2.0.0" + checksum: c4d3c8f9941d097d514d3986f54f21434e08e5876dc08d1d65226447e8e167600d5b9210235bb03fd45327225f04f32d6e365f08f76d2f4b8bff81594851aaf7 + languageName: node + linkType: hard + +"d3-format@npm:^1.4.4": version: 1.4.5 resolution: "d3-format@npm:1.4.5" checksum: 1b8b2c0bca182173bccd290a43e8b635a83fc8cfe52ec878c7bdabb997d47daac11f2b175cebbe73f807f782ad655f542bdfe18180ca5eb3498a3a82da1e06ab @@ -3419,14 +3443,7 @@ __metadata: languageName: node linkType: hard -"d3-time@npm:1 - 2, d3-time@npm:^1.0.11": - version: 1.1.0 - resolution: "d3-time@npm:1.1.0" - checksum: 33fcfff94ff093dde2048c190ecca8b39fe0ec8b3c61e9fc39c5f6072ce5b86dd2b91823f086366995422bbbac7f74fd9abdb7efe4f292a73b1c6197c699cc78 - languageName: node - linkType: hard - -"d3-time@npm:^2.1.1": +"d3-time@npm:1 - 2, d3-time@npm:^2.1.1": version: 2.1.1 resolution: "d3-time@npm:2.1.1" dependencies: @@ -3435,6 +3452,13 @@ __metadata: languageName: node linkType: hard +"d3-time@npm:^1.0.11": + version: 1.1.0 + resolution: "d3-time@npm:1.1.0" + checksum: 33fcfff94ff093dde2048c190ecca8b39fe0ec8b3c61e9fc39c5f6072ce5b86dd2b91823f086366995422bbbac7f74fd9abdb7efe4f292a73b1c6197c699cc78 + languageName: node + linkType: hard + "damerau-levenshtein@npm:^1.0.8": version: 1.0.8 resolution: "damerau-levenshtein@npm:1.0.8" @@ -3533,12 +3557,12 @@ __metadata: linkType: hard "define-properties@npm:^1.1.3, define-properties@npm:^1.1.4": - version: 1.1.4 - resolution: "define-properties@npm:1.1.4" + version: 1.2.0 + resolution: "define-properties@npm:1.2.0" dependencies: has-property-descriptors: ^1.0.0 object-keys: ^1.1.1 - checksum: ce0aef3f9eb193562b5cfb79b2d2c86b6a109dfc9fdcb5f45d680631a1a908c06824ddcdb72b7573b54e26ace07f0a23420aaba0d5c627b34d2c1de8ef527e2b + checksum: e60aee6a19b102df4e2b1f301816804e81ab48bb91f00d0d935f269bf4b3f79c88b39e4f89eaa132890d23267335fd1140dfcd8d5ccd61031a0a2c41a54e33a6 languageName: node linkType: hard @@ -3706,9 +3730,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.4.284": - version: 1.4.286 - resolution: "electron-to-chromium@npm:1.4.286" - checksum: 6b53e2aea63892cb4af85ea4ee5ed2b6d848713519987efcf4c1177a32e2fe6d04a7f591f5bcd1feab0b3c88890c6eaf65b6feb16c0e0319bf07e31de31930af + version: 1.4.297 + resolution: "electron-to-chromium@npm:1.4.297" + checksum: d5b83384c378cb22e023abd9ad566abfd573ea7a24109b992f034752bcf77602b67b5a20064a8810ee6bd4db17547d80fa7e30c3602f57a503950e8063491eb2 languageName: node linkType: hard @@ -4094,13 +4118,13 @@ __metadata: linkType: hard "eslint-plugin-testing-library@npm:^5.5.1": - version: 5.10.0 - resolution: "eslint-plugin-testing-library@npm:5.10.0" + version: 5.10.2 + resolution: "eslint-plugin-testing-library@npm:5.10.2" dependencies: "@typescript-eslint/utils": ^5.43.0 peerDependencies: eslint: ^7.5.0 || ^8.0.0 - checksum: 3278fc4683a99d24ac2b6d2ed0359db1b0509674350e4b9a958a226f57b4b90e070c02e1f4c2806da885d8025c1e8c952cb9a5e9751e69baac3d12cfe6804000 + checksum: 3b2b330e62f4a6dc438050006f0d0c97605f6861828b153271dc6d2fafb1e60f4e86fbaa8166c7afd452e3b6cad39413738fd4c8e2eb2def1915c678154676da languageName: node linkType: hard @@ -4172,8 +4196,8 @@ __metadata: linkType: hard "eslint@npm:^8.20.0": - version: 8.33.0 - resolution: "eslint@npm:8.33.0" + version: 8.34.0 + resolution: "eslint@npm:8.34.0" dependencies: "@eslint/eslintrc": ^1.4.1 "@humanwhocodes/config-array": ^0.11.8 @@ -4216,7 +4240,7 @@ __metadata: text-table: ^0.2.0 bin: eslint: bin/eslint.js - checksum: 727e63ab8b7acf281442323c5971f6afdd5b656fbcebc4476cf54e35af51b2f180617433fc5e1952f0449ca3f43a905527f9407ea4b8a7ea7562fc9c3f278d4c + checksum: 4e13e9eb05ac2248efbb6acae0b2325091235d5c47ba91a4775c7d6760778cbcd358a773ebd42f4629d2ad89e27c02f5d66eb1f737d75d9f5fc411454f83b2e5 languageName: node linkType: hard @@ -4632,7 +4656,7 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3": +"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0": version: 1.2.0 resolution: "get-intrinsic@npm:1.2.0" dependencies: @@ -4694,7 +4718,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:7.1.7, glob@npm:^7.1.3, glob@npm:^7.1.4": +"glob@npm:7.1.7": version: 7.1.7 resolution: "glob@npm:7.1.7" dependencies: @@ -4708,6 +4732,20 @@ __metadata: languageName: node linkType: hard +"glob@npm:^7.1.3, glob@npm:^7.1.4": + version: 7.2.3 + resolution: "glob@npm:7.2.3" + dependencies: + fs.realpath: ^1.0.0 + inflight: ^1.0.4 + inherits: 2 + minimatch: ^3.1.1 + once: ^1.3.0 + path-is-absolute: ^1.0.0 + checksum: 29452e97b38fa704dabb1d1045350fb2467cf0277e155aa9ff7077e90ad81d1ea9d53d3ee63bd37c05b09a065e90f16aec4a65f5b8de401d1dac40bc5605d133 + languageName: node + linkType: hard + "glob@npm:^8.0.1": version: 8.1.0 resolution: "glob@npm:8.1.0" @@ -4935,6 +4973,7 @@ __metadata: "@ctrl/transmission": ^4.1.1 "@emotion/react": ^11.10.5 "@emotion/server": ^11.10.0 + "@jellyfin/sdk": ^0.7.0 "@mantine/core": ^5.9.3 "@mantine/dates": ^5.9.3 "@mantine/dropzone": ^5.9.3 @@ -4995,6 +5034,7 @@ __metadata: typescript: ^4.7.4 uuid: ^8.3.2 video.js: ^8.0.3 + xml-js: ^1.6.11 yarn: ^1.22.19 zustand: ^4.1.4 languageName: unknown @@ -5074,7 +5114,7 @@ __metadata: languageName: node linkType: hard -"http-cache-semantics@npm:^4.0.0, http-cache-semantics@npm:^4.1.0": +"http-cache-semantics@npm:^4.0.0, http-cache-semantics@npm:^4.1.0, http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" checksum: 83ac0bc60b17a3a36f9953e7be55e5c8f41acc61b22583060e8dedc9dd5e3607c823a88d0926f9150e571f90946835c7fe150732801010845c72cd8bbff1a236 @@ -5178,9 +5218,9 @@ __metadata: linkType: hard "immutable@npm:^4.0.0": - version: 4.2.3 - resolution: "immutable@npm:4.2.3" - checksum: 11be9a328d5e3a35de47b7b7835e7d89f790bf19595a8de11e1ae19b2bb3be5def00c69928969f1607aa66fe4a3f48e95460b2b58894a83ce70b65c872466940 + version: 4.2.4 + resolution: "immutable@npm:4.2.4" + checksum: 3be84eded37b05e65cad57bfba630bc1bf170c498b7472144bc02d2650cc9baef79daf03574a9c2e41d195ebb55a1c12c9b312f41ee324b653927b24ad8bcaa7 languageName: node linkType: hard @@ -5259,13 +5299,13 @@ __metadata: linkType: hard "internal-slot@npm:^1.0.3, internal-slot@npm:^1.0.4": - version: 1.0.4 - resolution: "internal-slot@npm:1.0.4" + version: 1.0.5 + resolution: "internal-slot@npm:1.0.5" dependencies: - get-intrinsic: ^1.1.3 + get-intrinsic: ^1.2.0 has: ^1.0.3 side-channel: ^1.0.4 - checksum: 8974588d06bab4f675573a3b52975370facf6486df51bc0567a982c7024fa29495f10b76c0d4dc742dd951d1b72024fdc1e31bb0bedf1678dc7aacacaf5a4f73 + checksum: 97e84046bf9e7574d0956bd98d7162313ce7057883b6db6c5c7b5e5f05688864b0978ba07610c726d15d66544ffe4b1050107d93f8a39ebc59b15d8b429b497a languageName: node linkType: hard @@ -6454,7 +6494,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.2": +"minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" dependencies: @@ -6472,14 +6512,7 @@ __metadata: languageName: node linkType: hard -"minimist@npm:^1.2.0, minimist@npm:^1.2.6": - version: 1.2.7 - resolution: "minimist@npm:1.2.7" - checksum: 7346574a1038ca23c32e02252f603801f09384dd1d78b69a943a4e8c2c28730b80e96193882d3d3b22a063445f460e48316b29b8a25addca2d7e5e8f75478bec - languageName: node - linkType: hard - -"minimist@npm:~1.2.5": +"minimist@npm:^1.2.0, minimist@npm:^1.2.6, minimist@npm:~1.2.5": version: 1.2.8 resolution: "minimist@npm:1.2.8" checksum: 75a6d645fb122dad29c06a7597bddea977258957ed88d7a6df59b5cd3fe4a527e253e9bbf2e783e4b73657f9098b96a5fe96ab8a113655d4109108577ecf85b0 @@ -6547,9 +6580,9 @@ __metadata: linkType: hard "minipass@npm:^4.0.0": - version: 4.0.1 - resolution: "minipass@npm:4.0.1" - checksum: 48eb3141cc247b44f738944cbd789aedeed9288ebdb64c7de9b3bf23e9e71d611381bfecf643d877d25f7ca9f3d5ab7b6757ef6f46282086812ac5372b7cd291 + version: 4.0.3 + resolution: "minipass@npm:4.0.3" + checksum: a09f405e2f380ae7f6ee0cbb53b45c1fcc1b6c70fc3896f4d20649d92a10e61892c57bd9960a64cedf6c90b50022cb6c195905b515039c335b423202f99e6f18 languageName: node linkType: hard @@ -6600,14 +6633,14 @@ __metadata: languageName: node linkType: hard -"ms@npm:2.1.2, ms@npm:^2.1.1": +"ms@npm:2.1.2": version: 2.1.2 resolution: "ms@npm:2.1.2" checksum: 673cdb2c3133eb050c745908d8ce632ed2c02d85640e2edb3ace856a2266a813b30c613569bf3354fdf4ea7d1a1494add3bfa95e2713baa27d0c2c71fc44f58f languageName: node linkType: hard -"ms@npm:^2.0.0": +"ms@npm:^2.0.0, ms@npm:^2.1.1": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d @@ -6803,9 +6836,9 @@ __metadata: linkType: hard "node-releases@npm:^2.0.8": - version: 2.0.9 - resolution: "node-releases@npm:2.0.9" - checksum: 3ae6b1b300dc72c1a628861093d339a01aa017d3ad9017b0478384be29d6f9c93b9e26c91fce79728cecaadc04d0f16834b7ae1a018730e3e54962ec8c6aa86f + version: 2.0.10 + resolution: "node-releases@npm:2.0.10" + checksum: d784ecde25696a15d449c4433077f5cce620ed30a1656c4abf31282bfc691a70d9618bae6868d247a67914d1be5cc4fde22f65a05f4398cdfb92e0fc83cadfbc languageName: node linkType: hard @@ -7201,11 +7234,11 @@ __metadata: linkType: hard "prettier@npm:^2.7.1": - version: 2.8.3 - resolution: "prettier@npm:2.8.3" + version: 2.8.4 + resolution: "prettier@npm:2.8.4" bin: prettier: bin-prettier.js - checksum: 92f2ceb522d454370e02082aa74ad27388672f7cee8975028b59517c069fe643bdc73e322675c8faf2ff173d7a626d1a6389f26b474000308e793aa25fff46e5 + checksum: c173064bf3df57b6d93d19aa98753b9b9dd7657212e33b41ada8e2e9f9884066bb9ca0b4005b89b3ab137efffdf8fbe0b462785aba20364798ff4303aadda57e languageName: node linkType: hard @@ -7755,19 +7788,19 @@ __metadata: linkType: hard "sass@npm:^1.56.1": - version: 1.58.0 - resolution: "sass@npm:1.58.0" + version: 1.58.1 + resolution: "sass@npm:1.58.1" dependencies: chokidar: ">=3.0.0 <4.0.0" immutable: ^4.0.0 source-map-js: ">=0.6.2 <2.0.0" bin: sass: sass.js - checksum: a7219634881d2de6441fb619787fb1a02e3fa0333fb715be26aa335ba49d6bdb4f1105d9df70a80a67200893022b08346745783dc49046095d94fc6e044492d6 + checksum: ff079887d906b5c0dde99084d14ac36336d238c0c07935ff6381bad68f05de212c1ff12657ac1e8a0533523cd7a393126facdc2508d758e7d5700344a0e6ea51 languageName: node linkType: hard -"sax@npm:>=0.6.0": +"sax@npm:>=0.6.0, sax@npm:^1.2.4": version: 1.2.4 resolution: "sax@npm:1.2.4" checksum: d3df7d32b897a2c2f28e941f732c71ba90e27c24f62ee918bd4d9a8cfb3553f2f81e5493c7f0be94a11c1911b643a9108f231dd6f60df3fa9586b5d2e3e9e1fe @@ -8212,9 +8245,9 @@ __metadata: linkType: hard "tabbable@npm:^6.0.1": - version: 6.0.1 - resolution: "tabbable@npm:6.0.1" - checksum: 65e378ad69a97416f2fdce34ade11b8ff68b33d9b2d978920a9d285c77e1bb88cb35113a8f00af8c4f0163d788d451a48840a216fa918d6a3f0c554951deb984 + version: 6.1.0 + resolution: "tabbable@npm:6.1.0" + checksum: 4ca476f10b031f213187078fcbe4b04884b6cf6f3a5f31175ce79ac03fc47ad11b74a8137922e78ac6bb007b50386140e2f9911182885ebc79d0cb7996697985 languageName: node linkType: hard @@ -8658,19 +8691,19 @@ __metadata: linkType: hard "v8-to-istanbul@npm:^9.0.1": - version: 9.0.1 - resolution: "v8-to-istanbul@npm:9.0.1" + version: 9.1.0 + resolution: "v8-to-istanbul@npm:9.1.0" dependencies: "@jridgewell/trace-mapping": ^0.3.12 "@types/istanbul-lib-coverage": ^2.0.1 convert-source-map: ^1.6.0 - checksum: a49c34bf0a3af0c11041a3952a2600913904a983bd1bc87148b5c033bc5c1d02d5a13620fcdbfa2c60bc582a2e2970185780f0c844b4c3a220abf405f8af6311 + checksum: 2069d59ee46cf8d83b4adfd8a5c1a90834caffa9f675e4360f1157ffc8578ef0f763c8f32d128334424159bb6b01f3876acd39cd13297b2769405a9da241f8d1 languageName: node linkType: hard "video.js@npm:^7 || ^8, video.js@npm:^8.0.3": - version: 8.0.3 - resolution: "video.js@npm:8.0.3" + version: 8.0.4 + resolution: "video.js@npm:8.0.4" dependencies: "@babel/runtime": ^7.12.5 "@videojs/http-streaming": 3.0.0 @@ -8686,7 +8719,7 @@ __metadata: videojs-contrib-quality-levels: 3.0.0 videojs-font: 3.2.0 videojs-vtt.js: 0.15.4 - checksum: 3bb4b334192116c8bcbe254a67fee1655cb829155845afaf274efe5bf8019f9a32081156f697b68b322af91853f0b843ffbbed572cefb307d5f47ea61c6c78b4 + checksum: e106e7985355c8039a8534b9e506b7d3dd4685d247777e0a86af1a7396c0c11048efb3d1a954e759250c2576516c8c4099d9039c16857ed4d21f182328a71fa2 languageName: node linkType: hard @@ -8868,6 +8901,17 @@ __metadata: languageName: node linkType: hard +"xml-js@npm:^1.6.11": + version: 1.6.11 + resolution: "xml-js@npm:1.6.11" + dependencies: + sax: ^1.2.4 + bin: + xml-js: ./bin/cli.js + checksum: 24a55479919413687105fc2d8ab05e613ebedb1c1bc12258a108e07cff5ef793779297db854800a4edf0281303ebd1f177bc4a588442f5344e62b3dddda26c2b + languageName: node + linkType: hard + "xml2js@npm:^0.4.19": version: 0.4.23 resolution: "xml2js@npm:0.4.23" @@ -8969,8 +9013,8 @@ __metadata: linkType: hard "zustand@npm:^4.1.4": - version: 4.3.2 - resolution: "zustand@npm:4.3.2" + version: 4.3.3 + resolution: "zustand@npm:4.3.3" dependencies: use-sync-external-store: 1.2.0 peerDependencies: @@ -8981,6 +9025,6 @@ __metadata: optional: true react: optional: true - checksum: fc443abf5bc9deac0d4e375847e7914e44c7ffc9f7f09b60e466cb9bbbcf5a46706bf2f9c8b9e6e6c9a1c5aea0bd6123cbf9fbcd39788ae27d8494d505969ae8 + checksum: fe0277ab0ce14849dea254cbdcfe83d4614364d38e616875f75ef2419f29085f5b03605efabeb33189a4557e647efaa294e0984c4f642e0566f445a7b207cdd6 languageName: node linkType: hard