mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-12 16:35:49 +01:00
🔀 Merge branch 'dev' into feature/dashdot-consistency-changes
This commit is contained in:
5
.yarnrc
5
.yarnrc
@@ -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"
|
|
||||||
@@ -29,6 +29,7 @@
|
|||||||
"@ctrl/transmission": "^4.1.1",
|
"@ctrl/transmission": "^4.1.1",
|
||||||
"@emotion/react": "^11.10.5",
|
"@emotion/react": "^11.10.5",
|
||||||
"@emotion/server": "^11.10.0",
|
"@emotion/server": "^11.10.0",
|
||||||
|
"@jellyfin/sdk": "^0.7.0",
|
||||||
"@mantine/core": "^5.9.3",
|
"@mantine/core": "^5.9.3",
|
||||||
"@mantine/dates": "^5.9.3",
|
"@mantine/dates": "^5.9.3",
|
||||||
"@mantine/dropzone": "^5.9.3",
|
"@mantine/dropzone": "^5.9.3",
|
||||||
@@ -49,6 +50,7 @@
|
|||||||
"dockerode": "^3.3.2",
|
"dockerode": "^3.3.2",
|
||||||
"fily-publish-gridstack": "^0.0.13",
|
"fily-publish-gridstack": "^0.0.13",
|
||||||
"framer-motion": "^9.0.2",
|
"framer-motion": "^9.0.2",
|
||||||
|
"html-entities": "^2.3.3",
|
||||||
"i18next": "^21.9.1",
|
"i18next": "^21.9.1",
|
||||||
"js-file-download": "^0.4.12",
|
"js-file-download": "^0.4.12",
|
||||||
"next": "^13.1.6",
|
"next": "^13.1.6",
|
||||||
@@ -58,8 +60,10 @@
|
|||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-simple-code-editor": "^0.13.1",
|
"react-simple-code-editor": "^0.13.1",
|
||||||
|
"rss-parser": "^3.12.0",
|
||||||
"sabnzbd-api": "^1.5.0",
|
"sabnzbd-api": "^1.5.0",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
|
"xml-js": "^1.6.11",
|
||||||
"yarn": "^1.22.19",
|
"yarn": "^1.22.19",
|
||||||
"zustand": "^4.1.4"
|
"zustand": "^4.1.4"
|
||||||
},
|
},
|
||||||
|
|||||||
23
public/locales/en/modules/iframe.json
Normal file
23
public/locales/en/modules/iframe.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"descriptor": {
|
||||||
|
"name": "IFrame",
|
||||||
|
"description": "Embed any content from the internet. Some websites may restrict access.",
|
||||||
|
"settings": {
|
||||||
|
"title": "IFrame settings",
|
||||||
|
"embedUrl": {
|
||||||
|
"label": "Embed URL"
|
||||||
|
},
|
||||||
|
"allowFullScreen": {
|
||||||
|
"label": "Allow full screen"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"card": {
|
||||||
|
"errors": {
|
||||||
|
"noUrl": {
|
||||||
|
"title": "Enter an URL",
|
||||||
|
"text": "Ensure that you've entered a valid address in the configuration of your widget"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
public/locales/en/modules/media-server.json
Normal file
24
public/locales/en/modules/media-server.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
public/locales/en/modules/rss.json
Normal file
20
public/locales/en/modules/rss.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"descriptor": {
|
||||||
|
"name": "RSS Widget",
|
||||||
|
"description": "Grabs the items from a RSS feed and displays them. Commonly used for online news",
|
||||||
|
"settings": {
|
||||||
|
"title": "Settings for RSS widget",
|
||||||
|
"rssFeedUrl": {
|
||||||
|
"label": "RSS feed url"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"card": {
|
||||||
|
"errors": {
|
||||||
|
"general": {
|
||||||
|
"title": "Unable to retrieve RSS feed",
|
||||||
|
"text": "There was a problem reaching out the the RSS feed. Make sure that you've configured the feed correctly and use a valid RSS url, that matches the official standard specification. After updating the feed, you may need to save your dashboard and refresh the page."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/components/AppAvatar.tsx
Normal file
26
src/components/AppAvatar.tsx
Normal file
@@ -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 (
|
||||||
|
<Avatar
|
||||||
|
src={iconUrl}
|
||||||
|
bg={colorScheme === 'dark' ? colors.gray[8] : colors.gray[2]}
|
||||||
|
size="sm"
|
||||||
|
radius="xl"
|
||||||
|
p={4}
|
||||||
|
styles={{
|
||||||
|
root: {
|
||||||
|
borderColor: color !== undefined ? colors[color] : undefined,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -75,6 +75,16 @@ export const IntegrationSelector = ({ form }: IntegrationSelectorProps) => {
|
|||||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/readarr.png',
|
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/readarr.png',
|
||||||
label: 'Readarr',
|
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));
|
].filter((x) => Object.keys(integrationFieldProperties).includes(x.value));
|
||||||
|
|
||||||
const getNewProperties = (value: string | null): AppIntegrationPropertyType[] => {
|
const getNewProperties = (value: string | null): AppIntegrationPropertyType[] => {
|
||||||
|
|||||||
17
src/hooks/widgets/media-servers/useGetMediaServers.tsx
Normal file
17
src/hooks/widgets/media-servers/useGetMediaServers.tsx
Normal file
@@ -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<MediaServersResponseType> => {
|
||||||
|
const response = await fetch('/api/modules/media-server');
|
||||||
|
return response.json();
|
||||||
|
},
|
||||||
|
enabled,
|
||||||
|
refetchInterval: 10 * 1000,
|
||||||
|
});
|
||||||
10
src/hooks/widgets/rss/useGetRssFeed.tsx
Normal file
10
src/hooks/widgets/rss/useGetRssFeed.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
export const useGetRssFeed = (feedUrl: string) =>
|
||||||
|
useQuery({
|
||||||
|
queryKey: ['rss-feed', feedUrl],
|
||||||
|
queryFn: async () => {
|
||||||
|
const response = await fetch('/api/modules/rss');
|
||||||
|
return response.json();
|
||||||
|
},
|
||||||
|
});
|
||||||
227
src/pages/api/modules/media-server/index.ts
Normal file
227
src/pages/api/modules/media-server/index.ts
Normal file
@@ -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<GenericMediaServer | undefined> => {
|
||||||
|
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<GenericMediaServer | undefined> => {
|
||||||
|
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);
|
||||||
|
};
|
||||||
93
src/pages/api/modules/rss/index.ts
Normal file
93
src/pages/api/modules/rss/index.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import Consola from 'consola';
|
||||||
|
|
||||||
|
import { getCookie } from 'cookies-next';
|
||||||
|
|
||||||
|
import { decode } from 'html-entities';
|
||||||
|
|
||||||
|
import { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
|
||||||
|
import Parser from 'rss-parser';
|
||||||
|
|
||||||
|
import { getConfig } from '../../../../tools/config/getConfig';
|
||||||
|
import { Stopwatch } from '../../../../tools/shared/stopwatch';
|
||||||
|
import { IRssWidget } from '../../../../widgets/rss/RssWidgetTile';
|
||||||
|
|
||||||
|
type CustomItem = {
|
||||||
|
'media:content': string;
|
||||||
|
enclosure: {
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const parser: Parser<any, CustomItem> = new Parser({
|
||||||
|
customFields: {
|
||||||
|
item: ['media:content', 'enclosure'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Get = async (request: NextApiRequest, response: NextApiResponse) => {
|
||||||
|
const configName = getCookie('config-name', { req: request });
|
||||||
|
const config = getConfig(configName?.toString() ?? 'default');
|
||||||
|
|
||||||
|
const rssWidget = config.widgets.find((x) => x.id === 'rss') as IRssWidget | undefined;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!rssWidget ||
|
||||||
|
!rssWidget.properties.rssFeedUrl ||
|
||||||
|
rssWidget.properties.rssFeedUrl.length < 1
|
||||||
|
) {
|
||||||
|
response.status(400).json({ message: 'required widget does not exist' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Consola.info('Requesting RSS feed...');
|
||||||
|
const stopWatch = new Stopwatch();
|
||||||
|
const feed = await parser.parseURL(rssWidget.properties.rssFeedUrl);
|
||||||
|
Consola.info(`Retrieved RSS feed after ${stopWatch.getEllapsedMilliseconds()} milliseconds`);
|
||||||
|
|
||||||
|
const orderedFeed = {
|
||||||
|
...feed,
|
||||||
|
items: feed.items
|
||||||
|
.map((item: { title: any; content: any }) => ({
|
||||||
|
...item,
|
||||||
|
title: item.title ? decode(item.title) : undefined,
|
||||||
|
content: decode(item.content),
|
||||||
|
enclosure: createEnclosure(item),
|
||||||
|
}))
|
||||||
|
.sort((a: { pubDate: number }, b: { pubDate: number }) => {
|
||||||
|
if (!a.pubDate || !b.pubDate) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.pubDate - b.pubDate;
|
||||||
|
})
|
||||||
|
.slice(0, 20),
|
||||||
|
};
|
||||||
|
|
||||||
|
response.status(200).json({
|
||||||
|
feed: orderedFeed,
|
||||||
|
success: orderedFeed?.items !== undefined,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const createEnclosure = (item: any) => {
|
||||||
|
if (item.enclosure) {
|
||||||
|
return item.enclosure;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item['media:content']) {
|
||||||
|
return {
|
||||||
|
url: item['media:content'].$.url,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async (request: NextApiRequest, response: NextApiResponse) => {
|
||||||
|
if (request.method === 'GET') {
|
||||||
|
return Get(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.status(405);
|
||||||
|
};
|
||||||
@@ -92,3 +92,8 @@
|
|||||||
height: 0px;
|
height: 0px;
|
||||||
min-height: 0px !important;
|
min-height: 0px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scroll-area-w100 .mantine-ScrollArea-viewport > div:nth-of-type(1) {
|
||||||
|
width: 100%;
|
||||||
|
display: inherit !important;
|
||||||
|
}
|
||||||
108
src/tools/server/sdk/plex/plexClient.ts
Normal file
108
src/tools/server/sdk/plex/plexClient.ts
Normal file
@@ -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<GenericSessionInfo[]> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,9 +29,12 @@ export const dashboardNamespaces = [
|
|||||||
'modules/torrents-status',
|
'modules/torrents-status',
|
||||||
'modules/weather',
|
'modules/weather',
|
||||||
'modules/ping',
|
'modules/ping',
|
||||||
|
'modules/iframe',
|
||||||
|
'modules/rss',
|
||||||
'modules/docker',
|
'modules/docker',
|
||||||
'modules/dashdot',
|
'modules/dashdot',
|
||||||
'modules/overseerr',
|
'modules/overseerr',
|
||||||
|
'modules/media-server',
|
||||||
'modules/common-media-cards',
|
'modules/common-media-cards',
|
||||||
'modules/video-stream',
|
'modules/video-stream',
|
||||||
];
|
];
|
||||||
|
|||||||
11
src/tools/shared/stopwatch.ts
Normal file
11
src/tools/shared/stopwatch.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export class Stopwatch {
|
||||||
|
private startTime: Date;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.startTime = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
getEllapsedMilliseconds() {
|
||||||
|
return new Date().getTime() - this.startTime.getTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/types/api/media-server/media-server.ts
Normal file
34
src/types/api/media-server/media-server.ts
Normal file
@@ -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;
|
||||||
|
};
|
||||||
5
src/types/api/media-server/response.ts
Normal file
5
src/types/api/media-server/response.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { GenericMediaServer } from './media-server';
|
||||||
|
|
||||||
|
export type MediaServersResponseType = {
|
||||||
|
servers: GenericMediaServer[];
|
||||||
|
};
|
||||||
46
src/types/api/media-server/session-info.ts
Normal file
46
src/types/api/media-server/session-info.ts
Normal file
@@ -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;
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -41,6 +41,8 @@ export type IntegrationType =
|
|||||||
| 'deluge'
|
| 'deluge'
|
||||||
| 'qBittorrent'
|
| 'qBittorrent'
|
||||||
| 'transmission'
|
| 'transmission'
|
||||||
|
| 'plex'
|
||||||
|
| 'jellyfin'
|
||||||
| 'nzbGet';
|
| 'nzbGet';
|
||||||
|
|
||||||
export type AppIntegrationType = {
|
export type AppIntegrationType = {
|
||||||
@@ -79,6 +81,8 @@ export const integrationFieldProperties: {
|
|||||||
nzbGet: ['username', 'password'],
|
nzbGet: ['username', 'password'],
|
||||||
qBittorrent: ['username', 'password'],
|
qBittorrent: ['username', 'password'],
|
||||||
transmission: ['username', 'password'],
|
transmission: ['username', 'password'],
|
||||||
|
jellyfin: ['username', 'password'],
|
||||||
|
plex: ['apiKey'],
|
||||||
};
|
};
|
||||||
|
|
||||||
export type IntegrationFieldDefinitionType = {
|
export type IntegrationFieldDefinitionType = {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { Serie, Datum, ResponsiveLine } from '@nivo/line';
|
|||||||
import { IconDownload, IconUpload } from '@tabler/icons';
|
import { IconDownload, IconUpload } from '@tabler/icons';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
import { AppAvatar } from '../../components/AppAvatar';
|
||||||
import { useConfigContext } from '../../config/provider';
|
import { useConfigContext } from '../../config/provider';
|
||||||
import { useGetDownloadClientsQueue } from '../../hooks/widgets/download-speed/useGetNetworkSpeed';
|
import { useGetDownloadClientsQueue } from '../../hooks/widgets/download-speed/useGetNetworkSpeed';
|
||||||
import { useColorTheme } from '../../tools/color';
|
import { useColorTheme } from '../../tools/color';
|
||||||
@@ -258,17 +259,3 @@ export default function TorrentNetworkTrafficTile({ widget }: TorrentNetworkTraf
|
|||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const AppAvatar = ({ iconUrl }: { iconUrl: string }) => {
|
|
||||||
const { colors, colorScheme } = useMantineTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Avatar
|
|
||||||
src={iconUrl}
|
|
||||||
bg={colorScheme === 'dark' ? colors.gray[8] : colors.gray[2]}
|
|
||||||
size="sm"
|
|
||||||
radius="xl"
|
|
||||||
p={4}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
82
src/widgets/iframe/IFrameTile.tsx
Normal file
82
src/widgets/iframe/IFrameTile.tsx
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import { Center, createStyles, Stack, Title, Text, Container } from '@mantine/core';
|
||||||
|
import { IconBrowser, IconUnlink } from '@tabler/icons';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
|
import { defineWidget } from '../helper';
|
||||||
|
import { IWidget } from '../widgets';
|
||||||
|
|
||||||
|
const definition = defineWidget({
|
||||||
|
id: 'iframe',
|
||||||
|
icon: IconBrowser,
|
||||||
|
gridstack: {
|
||||||
|
maxHeight: 12,
|
||||||
|
maxWidth: 12,
|
||||||
|
minHeight: 1,
|
||||||
|
minWidth: 1,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
embedUrl: {
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
},
|
||||||
|
allowFullScreen: {
|
||||||
|
type: 'switch',
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
component: IFrameTile,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type IIFrameWidget = IWidget<(typeof definition)['id'], typeof definition>;
|
||||||
|
|
||||||
|
interface IFrameTileProps {
|
||||||
|
widget: IIFrameWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
function IFrameTile({ widget }: IFrameTileProps) {
|
||||||
|
const { t } = useTranslation('modules/iframe');
|
||||||
|
const { classes } = useStyles();
|
||||||
|
|
||||||
|
if (!widget.properties.embedUrl) {
|
||||||
|
return (
|
||||||
|
<Center h="100%">
|
||||||
|
<Stack align="center">
|
||||||
|
<IconUnlink size={36} strokeWidth={1.2} />
|
||||||
|
<Stack align="center" spacing={0}>
|
||||||
|
<Title order={6} align="center">
|
||||||
|
{t('card.errors.noUrl.title')}
|
||||||
|
</Title>
|
||||||
|
<Text align="center" maw={200}>
|
||||||
|
{t('card.errors.noUrl.text')}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Center>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container h="100%" w="100%" p={0}>
|
||||||
|
<iframe
|
||||||
|
className={classes.iframe}
|
||||||
|
src={widget.properties.embedUrl}
|
||||||
|
title="widget iframe"
|
||||||
|
allowFullScreen={widget.properties.allowFullScreen}
|
||||||
|
>
|
||||||
|
<Text>Your Browser does not support iframes. Please update your browser.</Text>
|
||||||
|
</iframe>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = createStyles(({ radius }) => ({
|
||||||
|
iframe: {
|
||||||
|
borderRadius: radius.sm,
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
border: 'none',
|
||||||
|
background: 'none',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default definition;
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
import date from './date/DateTile';
|
|
||||||
import calendar from './calendar/CalendarTile';
|
import calendar from './calendar/CalendarTile';
|
||||||
import dashdot from './dashDot/DashDotTile';
|
import dashdot from './dashDot/DashDotTile';
|
||||||
import usenet from './useNet/UseNetTile';
|
import date from './date/DateTile';
|
||||||
import weather from './weather/WeatherTile';
|
|
||||||
import torrent from './torrent/TorrentTile';
|
|
||||||
import torrentNetworkTraffic from './download-speed/TorrentNetworkTrafficTile';
|
import torrentNetworkTraffic from './download-speed/TorrentNetworkTrafficTile';
|
||||||
|
import iframe from './iframe/IFrameTile';
|
||||||
|
import mediaServer from './media-server/MediaServerTile';
|
||||||
|
import rss from './rss/RssWidgetTile';
|
||||||
|
import torrent from './torrent/TorrentTile';
|
||||||
|
import usenet from './useNet/UseNetTile';
|
||||||
import videoStream from './video/VideoStreamTile';
|
import videoStream from './video/VideoStreamTile';
|
||||||
|
import weather from './weather/WeatherTile';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
calendar,
|
calendar,
|
||||||
@@ -15,5 +18,8 @@ export default {
|
|||||||
'torrents-status': torrent,
|
'torrents-status': torrent,
|
||||||
dlspeed: torrentNetworkTraffic,
|
dlspeed: torrentNetworkTraffic,
|
||||||
date,
|
date,
|
||||||
|
rss,
|
||||||
'video-stream': videoStream,
|
'video-stream': videoStream,
|
||||||
|
iframe,
|
||||||
|
'media-server': mediaServer,
|
||||||
};
|
};
|
||||||
|
|||||||
128
src/widgets/media-server/DetailCollapseable.tsx
Normal file
128
src/widgets/media-server/DetailCollapseable.tsx
Normal file
@@ -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 (
|
||||||
|
<Card>
|
||||||
|
<Flex justify="space-between" mb="xs">
|
||||||
|
<Group>
|
||||||
|
<IconId size={16} />
|
||||||
|
<Text>ID</Text>
|
||||||
|
</Group>
|
||||||
|
<Text>{session.id}</Text>
|
||||||
|
</Flex>
|
||||||
|
<Flex justify="space-between" mb="md">
|
||||||
|
<Group>
|
||||||
|
<IconDeviceMobile size={16} />
|
||||||
|
<Text>Device</Text>
|
||||||
|
</Group>
|
||||||
|
<Text>{session.sessionName}</Text>
|
||||||
|
</Flex>
|
||||||
|
{details.length > 0 && <Divider label="Stats for nerds" labelPosition="center" mt="lg" mb="sm" />}
|
||||||
|
<Grid>
|
||||||
|
{details.map((detail, index) => (
|
||||||
|
<Grid.Col xs={12} sm={6} key={index}>
|
||||||
|
<Text weight="bold">{detail.title}</Text>
|
||||||
|
{detail.metrics
|
||||||
|
.filter((x) => x.value !== undefined)
|
||||||
|
.map((metric, index2) => (
|
||||||
|
<Group position="apart" key={index2}>
|
||||||
|
<Text>{metric.name}</Text>
|
||||||
|
<Text>{metric.value}</Text>
|
||||||
|
</Group>
|
||||||
|
))}
|
||||||
|
</Grid.Col>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
110
src/widgets/media-server/MediaServerTile.tsx
Normal file
110
src/widgets/media-server/MediaServerTile.tsx
Normal file
@@ -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 (
|
||||||
|
<Center>
|
||||||
|
<Stack align="center">
|
||||||
|
<IconAlertTriangle />
|
||||||
|
<Title order={6}>{t('card.errors.general.title')}</Title>
|
||||||
|
<Text>{t('card.errors.general.text')}</Text>
|
||||||
|
</Stack>
|
||||||
|
</Center>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
<Center h="100%">
|
||||||
|
<Loader />
|
||||||
|
</Center>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack h="100%">
|
||||||
|
<ScrollArea offsetScrollbars>
|
||||||
|
<Table highlightOnHover striped>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{t('card.table.header.session')}</th>
|
||||||
|
<th>{t('card.table.header.user')}</th>
|
||||||
|
<th>{t('card.table.header.currentlyPlaying')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{data?.servers.map((server) => {
|
||||||
|
const app = config?.apps.find((x) => x.id === server.appId);
|
||||||
|
return server.sessions.map((session, index) => (
|
||||||
|
<TableRow session={session} app={app} key={index} />
|
||||||
|
));
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</Table>
|
||||||
|
</ScrollArea>
|
||||||
|
|
||||||
|
<Group position="right" mt="auto">
|
||||||
|
<Avatar.Group>
|
||||||
|
{data?.servers.map((server) => {
|
||||||
|
const app = config?.apps.find((x) => x.id === server.appId);
|
||||||
|
|
||||||
|
if (!app) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppAvatar
|
||||||
|
iconUrl={app.appearance.iconUrl}
|
||||||
|
color={server.success === true ? undefined : 'red'}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Avatar.Group>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default definition;
|
||||||
50
src/widgets/media-server/NowPlayingDisplay.tsx
Normal file
50
src/widgets/media-server/NowPlayingDisplay.tsx
Normal file
@@ -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 (
|
||||||
|
<Flex wrap="nowrap" gap="sm" align="center">
|
||||||
|
<Test size={16} />
|
||||||
|
<Stack spacing={0}>
|
||||||
|
<Text lineClamp={1}>{session.currentlyPlaying.name}</Text>
|
||||||
|
|
||||||
|
{session.currentlyPlaying.albumName ? (
|
||||||
|
<Text lineClamp={1} color="dimmed" size="xs">{session.currentlyPlaying.albumName}</Text>
|
||||||
|
) : (
|
||||||
|
session.currentlyPlaying.seasonName && (
|
||||||
|
<Text lineClamp={1} color="dimmed" size="xs">{session.currentlyPlaying.seasonName}</Text>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
73
src/widgets/media-server/TableRow.tsx
Normal file
73
src/widgets/media-server/TableRow.tsx
Normal file
@@ -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 (
|
||||||
|
<>
|
||||||
|
<tr className={classes.dataRow} onClick={() => setCollapseOpen(!collapseOpen)}>
|
||||||
|
<td>
|
||||||
|
<Flex wrap="nowrap" gap="xs">
|
||||||
|
{app?.appearance.iconUrl && <AppAvatar iconUrl={app.appearance.iconUrl} />}
|
||||||
|
<Text lineClamp={1}>{session.sessionName}</Text>
|
||||||
|
</Flex>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Flex wrap="nowrap" gap="sm">
|
||||||
|
{hasUserThumb ? (
|
||||||
|
<Avatar src={session.userProfilePicture} size="sm" />
|
||||||
|
) : (
|
||||||
|
<Avatar src={null} alt={session.username} size="sm">
|
||||||
|
{session.username?.at(0)?.toUpperCase()}
|
||||||
|
</Avatar>
|
||||||
|
)}
|
||||||
|
<Text>{session.username}</Text>
|
||||||
|
</Flex>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<NowPlayingDisplay session={session} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className={classes.collapseTableDataCell} colSpan={3}>
|
||||||
|
<Collapse in={collapseOpen} w="100%">
|
||||||
|
<DetailCollapseable session={session} />
|
||||||
|
</Collapse>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const useStyles = createStyles(() => ({
|
||||||
|
dataRow: {
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
collapseTableDataCell: {
|
||||||
|
border: 'none !important',
|
||||||
|
padding: '0 !important',
|
||||||
|
},
|
||||||
|
}));
|
||||||
236
src/widgets/rss/RssWidgetTile.tsx
Normal file
236
src/widgets/rss/RssWidgetTile.tsx
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
import {
|
||||||
|
ActionIcon,
|
||||||
|
Badge,
|
||||||
|
Card,
|
||||||
|
Center,
|
||||||
|
createStyles,
|
||||||
|
Flex,
|
||||||
|
Group,
|
||||||
|
Image,
|
||||||
|
Loader,
|
||||||
|
LoadingOverlay,
|
||||||
|
MediaQuery,
|
||||||
|
ScrollArea,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
Title,
|
||||||
|
UnstyledButton,
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { useElementSize } from '@mantine/hooks';
|
||||||
|
import {
|
||||||
|
IconBulldozer,
|
||||||
|
IconCalendarTime,
|
||||||
|
IconClock,
|
||||||
|
IconCopyright,
|
||||||
|
IconRefresh,
|
||||||
|
IconRss,
|
||||||
|
IconSpeakerphone,
|
||||||
|
} from '@tabler/icons';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useGetRssFeed } from '../../hooks/widgets/rss/useGetRssFeed';
|
||||||
|
import { sleep } from '../../tools/client/time';
|
||||||
|
import { defineWidget } from '../helper';
|
||||||
|
import { IWidget } from '../widgets';
|
||||||
|
|
||||||
|
const definition = defineWidget({
|
||||||
|
id: 'rss',
|
||||||
|
icon: IconRss,
|
||||||
|
options: {
|
||||||
|
rssFeedUrl: {
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gridstack: {
|
||||||
|
minWidth: 2,
|
||||||
|
minHeight: 2,
|
||||||
|
maxWidth: 12,
|
||||||
|
maxHeight: 12,
|
||||||
|
},
|
||||||
|
component: RssTile,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type IRssWidget = IWidget<(typeof definition)['id'], typeof definition>;
|
||||||
|
|
||||||
|
interface RssTileProps {
|
||||||
|
widget: IRssWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
function RssTile({ widget }: RssTileProps) {
|
||||||
|
const { t } = useTranslation('modules/rss');
|
||||||
|
const { data, isLoading, isFetching, isError, refetch } = useGetRssFeed(
|
||||||
|
widget.properties.rssFeedUrl
|
||||||
|
);
|
||||||
|
const { classes } = useStyles();
|
||||||
|
const [loadingOverlayVisible, setLoadingOverlayVisible] = useState(false);
|
||||||
|
const { ref, height } = useElementSize();
|
||||||
|
|
||||||
|
if (!data || isLoading) {
|
||||||
|
return (
|
||||||
|
<Center>
|
||||||
|
<Loader />
|
||||||
|
</Center>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.success || isError) {
|
||||||
|
return (
|
||||||
|
<Center h="100%">
|
||||||
|
<Stack align="center">
|
||||||
|
<IconRss size={40} strokeWidth={1} />
|
||||||
|
<Title order={6}>{t('card.errors.general.title')}</Title>
|
||||||
|
<Text align="center">{t('card.errors.general.text')}</Text>
|
||||||
|
</Stack>
|
||||||
|
</Center>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack ref={ref} h="100%">
|
||||||
|
<LoadingOverlay visible={loadingOverlayVisible} />
|
||||||
|
<Flex gap="md">
|
||||||
|
{data.feed.image ? (
|
||||||
|
<Image
|
||||||
|
src={data.feed.image.url}
|
||||||
|
alt={data.feed.image.title}
|
||||||
|
width="auto"
|
||||||
|
height={40}
|
||||||
|
mx="auto"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Title order={6}>{data.feed.title}</Title>
|
||||||
|
)}
|
||||||
|
<UnstyledButton
|
||||||
|
onClick={async () => {
|
||||||
|
setLoadingOverlayVisible(true);
|
||||||
|
await Promise.all([sleep(1500), refetch()]);
|
||||||
|
setLoadingOverlayVisible(false);
|
||||||
|
}}
|
||||||
|
disabled={isFetching || isLoading}
|
||||||
|
>
|
||||||
|
<ActionIcon>
|
||||||
|
<IconRefresh />
|
||||||
|
</ActionIcon>
|
||||||
|
</UnstyledButton>
|
||||||
|
</Flex>
|
||||||
|
<ScrollArea className="scroll-area-w100" w="100%">
|
||||||
|
<Stack w="100%" spacing="xs">
|
||||||
|
{data.feed.items.map((item: any, index: number) => (
|
||||||
|
<Card
|
||||||
|
key={index}
|
||||||
|
withBorder
|
||||||
|
component={Link}
|
||||||
|
href={item.link}
|
||||||
|
radius="md"
|
||||||
|
target="_blank"
|
||||||
|
w="100%"
|
||||||
|
>
|
||||||
|
{item.enclosure && (
|
||||||
|
// eslint-disable-next-line @next/next/no-img-element
|
||||||
|
<img
|
||||||
|
className={classes.backgroundImage}
|
||||||
|
src={item.enclosure.url ?? undefined}
|
||||||
|
alt="backdrop"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Flex gap="xs">
|
||||||
|
<MediaQuery query="(max-width: 1200px)" styles={{ display: 'none' }}>
|
||||||
|
<Image
|
||||||
|
src={item.enclosure?.url ?? undefined}
|
||||||
|
width={140}
|
||||||
|
height={140}
|
||||||
|
radius="md"
|
||||||
|
withPlaceholder
|
||||||
|
/>
|
||||||
|
</MediaQuery>
|
||||||
|
<Flex gap={2} direction="column">
|
||||||
|
{item.categories && (
|
||||||
|
<Flex gap="xs" wrap="wrap" h={20} style={{ overflow: 'hidden' }}>
|
||||||
|
{item.categories.map((category: any, categoryIndex: number) => (
|
||||||
|
<Badge key={categoryIndex}>{category._}</Badge>
|
||||||
|
))}
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Text lineClamp={2}>{item.title}</Text>
|
||||||
|
<Text color="dimmed" size="xs" lineClamp={3}>
|
||||||
|
{item.content}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{item.pubDate && <TimeDisplay date={item.pubDate} />}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</ScrollArea>
|
||||||
|
|
||||||
|
<Flex wrap="wrap" columnGap="md">
|
||||||
|
<Group spacing="sm">
|
||||||
|
<IconCopyright size={14} />
|
||||||
|
<Text color="dimmed" size="sm">
|
||||||
|
{data.feed.copyright}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
<Group>
|
||||||
|
<IconCalendarTime size={14} />
|
||||||
|
<Text color="dimmed" size="sm">
|
||||||
|
{data.feed.pubDate}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
<Group>
|
||||||
|
<IconBulldozer size={14} />
|
||||||
|
<Text color="dimmed" size="sm">
|
||||||
|
{data.feed.lastBuildDate}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
{data.feed.feedUrl && (
|
||||||
|
<Group spacing="sm">
|
||||||
|
<IconSpeakerphone size={14} />
|
||||||
|
<Text
|
||||||
|
color="dimmed"
|
||||||
|
size="sm"
|
||||||
|
variant="link"
|
||||||
|
target="_blank"
|
||||||
|
component={Link}
|
||||||
|
href={data.feed.feedUrl}
|
||||||
|
>
|
||||||
|
Feed URL
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TimeDisplay = ({ date }: { date: string }) => (
|
||||||
|
<Group mt="auto" spacing="xs">
|
||||||
|
<IconClock size={14} />
|
||||||
|
<Text size="xs" color="dimmed">
|
||||||
|
{date}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
|
||||||
|
const useStyles = createStyles(({ colorScheme }) => ({
|
||||||
|
backgroundImage: {
|
||||||
|
position: 'absolute',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
filter: colorScheme === 'dark' ? 'blur(30px)' : 'blur(15px)',
|
||||||
|
transform: 'scaleX(-1)',
|
||||||
|
opacity: colorScheme === 'dark' ? 0.3 : 0.2,
|
||||||
|
transition: 'ease-in-out 0.2s',
|
||||||
|
|
||||||
|
'&:hover': {
|
||||||
|
opacity: colorScheme === 'dark' ? 0.4 : 0.3,
|
||||||
|
filter: 'blur(40px) brightness(0.7)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default definition;
|
||||||
340
yarn.lock
340
yarn.lock
@@ -131,7 +131,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.19.0, @babel/helper-plugin-utils@npm:^7.8.0":
|
"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.19.0, @babel/helper-plugin-utils@npm:^7.8.0":
|
||||||
version: 7.20.2
|
version: 7.20.2
|
||||||
resolution: "@babel/helper-plugin-utils@npm:7.20.2"
|
resolution: "@babel/helper-plugin-utils@npm:7.20.2"
|
||||||
checksum: f6cae53b7fdb1bf3abd50fa61b10b4470985b400cc794d92635da1e7077bb19729f626adc0741b69403d9b6e411cddddb9c0157a709cc7c4eeb41e663be5d74b
|
checksum: f6cae53b7fdb1bf3abd50fa61b10b4470985b400cc794d92635da1e7077bb19729f626adc0741b69403d9b6e411cddddb9c0157a709cc7c4eeb41e663be5d74b
|
||||||
@@ -263,17 +263,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@babel/plugin-syntax-jsx@npm:^7.17.12":
|
|
||||||
version: 7.18.6
|
|
||||||
resolution: "@babel/plugin-syntax-jsx@npm:7.18.6"
|
|
||||||
dependencies:
|
|
||||||
"@babel/helper-plugin-utils": ^7.18.6
|
|
||||||
peerDependencies:
|
|
||||||
"@babel/core": ^7.0.0-0
|
|
||||||
checksum: 6d37ea972970195f1ffe1a54745ce2ae456e0ac6145fae9aa1480f297248b262ea6ebb93010eddb86ebfacb94f57c05a1fc5d232b9a67325b09060299d515c67
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@babel/plugin-syntax-logical-assignment-operators@npm:^7.8.3":
|
"@babel/plugin-syntax-logical-assignment-operators@npm:^7.8.3":
|
||||||
version: 7.10.4
|
version: 7.10.4
|
||||||
resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4"
|
resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4"
|
||||||
@@ -507,12 +496,11 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@emotion/babel-plugin@npm:^11.10.5":
|
"@emotion/babel-plugin@npm:^11.10.6":
|
||||||
version: 11.10.5
|
version: 11.10.6
|
||||||
resolution: "@emotion/babel-plugin@npm:11.10.5"
|
resolution: "@emotion/babel-plugin@npm:11.10.6"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-module-imports": ^7.16.7
|
"@babel/helper-module-imports": ^7.16.7
|
||||||
"@babel/plugin-syntax-jsx": ^7.17.12
|
|
||||||
"@babel/runtime": ^7.18.3
|
"@babel/runtime": ^7.18.3
|
||||||
"@emotion/hash": ^0.9.0
|
"@emotion/hash": ^0.9.0
|
||||||
"@emotion/memoize": ^0.8.0
|
"@emotion/memoize": ^0.8.0
|
||||||
@@ -523,9 +511,7 @@ __metadata:
|
|||||||
find-root: ^1.1.0
|
find-root: ^1.1.0
|
||||||
source-map: ^0.5.7
|
source-map: ^0.5.7
|
||||||
stylis: 4.1.3
|
stylis: 4.1.3
|
||||||
peerDependencies:
|
checksum: 3eed138932e8edf2598352e69ad949b9db3051a4d6fcff190dacbac9aa838d7ef708b9f3e6c48660625d9311dae82d73477ae4e7a31139feef5eb001a5528421
|
||||||
"@babel/core": ^7.0.0
|
|
||||||
checksum: e3353499c76c4422d6e900c0dfab73607056d9da86161a3f27c3459c193c4908050c5d252c68fcde231e13f02a9d8e0dc07d260317ae0e5206841e331cc4caae
|
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -573,11 +559,11 @@ __metadata:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@emotion/react@npm:^11.10.5":
|
"@emotion/react@npm:^11.10.5":
|
||||||
version: 11.10.5
|
version: 11.10.6
|
||||||
resolution: "@emotion/react@npm:11.10.5"
|
resolution: "@emotion/react@npm:11.10.6"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime": ^7.18.3
|
"@babel/runtime": ^7.18.3
|
||||||
"@emotion/babel-plugin": ^11.10.5
|
"@emotion/babel-plugin": ^11.10.6
|
||||||
"@emotion/cache": ^11.10.5
|
"@emotion/cache": ^11.10.5
|
||||||
"@emotion/serialize": ^1.1.1
|
"@emotion/serialize": ^1.1.1
|
||||||
"@emotion/use-insertion-effect-with-fallbacks": ^1.0.0
|
"@emotion/use-insertion-effect-with-fallbacks": ^1.0.0
|
||||||
@@ -585,14 +571,11 @@ __metadata:
|
|||||||
"@emotion/weak-memoize": ^0.3.0
|
"@emotion/weak-memoize": ^0.3.0
|
||||||
hoist-non-react-statics: ^3.3.1
|
hoist-non-react-statics: ^3.3.1
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@babel/core": ^7.0.0
|
|
||||||
react: ">=16.8.0"
|
react: ">=16.8.0"
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
"@babel/core":
|
|
||||||
optional: true
|
|
||||||
"@types/react":
|
"@types/react":
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 32b67b28e9b6d6c53b970072680697f04c2521441050bdeb19a1a7f0164af549b4dad39ff375eda1b6a3cf1cc86ba2c6fa55460ec040e6ebbca3e9ec58353cf7
|
checksum: 4762042e39126ffaffe76052dc65c9bb0ba6b8893013687ba3cc13ed4dd834c31597f1230684c3c078e90aecc13ab6cd0e3cde0dec8b7761affd2571f4d80019
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -774,6 +757,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"@jest/console@npm:^28.1.3":
|
||||||
version: 28.1.3
|
version: 28.1.3
|
||||||
resolution: "@jest/console@npm:28.1.3"
|
resolution: "@jest/console@npm:28.1.3"
|
||||||
@@ -1058,133 +1051,133 @@ __metadata:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mantine/core@npm:^5.9.3":
|
"@mantine/core@npm:^5.9.3":
|
||||||
version: 5.10.3
|
version: 5.10.4
|
||||||
resolution: "@mantine/core@npm:5.10.3"
|
resolution: "@mantine/core@npm:5.10.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@floating-ui/react": ^0.19.1
|
"@floating-ui/react": ^0.19.1
|
||||||
"@mantine/styles": 5.10.3
|
"@mantine/styles": 5.10.4
|
||||||
"@mantine/utils": 5.10.3
|
"@mantine/utils": 5.10.4
|
||||||
"@radix-ui/react-scroll-area": 1.0.2
|
"@radix-ui/react-scroll-area": 1.0.2
|
||||||
react-textarea-autosize: 8.3.4
|
react-textarea-autosize: 8.3.4
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@mantine/hooks": 5.10.3
|
"@mantine/hooks": 5.10.4
|
||||||
react: ">=16.8.0"
|
react: ">=16.8.0"
|
||||||
react-dom: ">=16.8.0"
|
react-dom: ">=16.8.0"
|
||||||
checksum: 460e613e90ecd94a9414bb93a98046e076256fa54ec5cd0b43d20815755e7862826d912a516591d2a2e0356647d72b293564d2ccb9ca1fb8999da0e963d701bd
|
checksum: 98cba720fa9764a7a45b13d167ad2dce1af1b639c202a25cf60c8ca439e386dfddefae262e770df62348af90647ba6cbc095c074a8f0582968059450fbbc2ff0
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mantine/dates@npm:^5.9.3":
|
"@mantine/dates@npm:^5.9.3":
|
||||||
version: 5.10.3
|
version: 5.10.4
|
||||||
resolution: "@mantine/dates@npm:5.10.3"
|
resolution: "@mantine/dates@npm:5.10.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@mantine/utils": 5.10.3
|
"@mantine/utils": 5.10.4
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@mantine/core": 5.10.3
|
"@mantine/core": 5.10.4
|
||||||
"@mantine/hooks": 5.10.3
|
"@mantine/hooks": 5.10.4
|
||||||
dayjs: ">=1.0.0"
|
dayjs: ">=1.0.0"
|
||||||
react: ">=16.8.0"
|
react: ">=16.8.0"
|
||||||
checksum: 80e22b0eb2235a373f0d35979bea7aeea671515962f4d119811145da7086f2258663ceb2d19a6ad23a02b6f08e1c53047778ec37cf559d8e81c9061d9f83a159
|
checksum: 14a32aa4c16e030266629dfc5171e930d271682de742541b2298d448df11c9fab40e1d8003eabccfd5449a65ef14681993af6426197da4dcad1f509fb9fff932
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mantine/dropzone@npm:^5.9.3":
|
"@mantine/dropzone@npm:^5.9.3":
|
||||||
version: 5.10.3
|
version: 5.10.4
|
||||||
resolution: "@mantine/dropzone@npm:5.10.3"
|
resolution: "@mantine/dropzone@npm:5.10.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@mantine/utils": 5.10.3
|
"@mantine/utils": 5.10.4
|
||||||
react-dropzone: 14.2.3
|
react-dropzone: 14.2.3
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@mantine/core": 5.10.3
|
"@mantine/core": 5.10.4
|
||||||
"@mantine/hooks": 5.10.3
|
"@mantine/hooks": 5.10.4
|
||||||
react: ">=16.8.0"
|
react: ">=16.8.0"
|
||||||
react-dom: ">=16.8.0"
|
react-dom: ">=16.8.0"
|
||||||
checksum: 550bf8a282220adb3f7564e2ffa7c617480a5ab9d2556c50072b614450ca1d7fc526e8755a635936c4cb76f9a090f91686f690b7e7704320972dd282e00ab1d6
|
checksum: 0b837b5bc7c982ad9832142c880c1b0f2827de5c2163b76895cac2dbe6204f7c67e466468a3fb00f72482bad957c4b1209820025c4dfc2c25650734df79e208b
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mantine/form@npm:^5.9.3":
|
"@mantine/form@npm:^5.9.3":
|
||||||
version: 5.10.3
|
version: 5.10.4
|
||||||
resolution: "@mantine/form@npm:5.10.3"
|
resolution: "@mantine/form@npm:5.10.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-deep-equal: ^3.1.3
|
fast-deep-equal: ^3.1.3
|
||||||
klona: ^2.0.5
|
klona: ^2.0.5
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ">=16.8.0"
|
react: ">=16.8.0"
|
||||||
checksum: 88bcf7b37d19e0648500e9da7111d961a3b130ef1164f4c4fc15f606cececdddad614887ff6659e0a0ce3896a4e52e62c2f72758f857e932e903cf412a69ca54
|
checksum: 00ebc0011981f8dae5e96e833f7e0102067a7bc38b1f6208b377f029fdc8bb703a492f40a688eb015dea3147c3b6e5a7cd26c584b93cc8d2567f0dcb10319759
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mantine/hooks@npm:^5.9.3":
|
"@mantine/hooks@npm:^5.9.3":
|
||||||
version: 5.10.3
|
version: 5.10.4
|
||||||
resolution: "@mantine/hooks@npm:5.10.3"
|
resolution: "@mantine/hooks@npm:5.10.4"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ">=16.8.0"
|
react: ">=16.8.0"
|
||||||
checksum: 629554658e910dec1c14ecbdaf8e48c1ce2af022044269e73ff069d719a4f9b68428bb75ade5108ace40bd29253658e663c594a237e080226de28705c10c871e
|
checksum: 41ededb62ea9311303e4b8d577ec21c12ddb339e60e70c3a1f561cb1b2c66fb6e6f29a7a23a89322748779cc06797de749203afd546f2b13180781c0e2873fa6
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mantine/modals@npm:^5.9.3":
|
"@mantine/modals@npm:^5.9.3":
|
||||||
version: 5.10.3
|
version: 5.10.4
|
||||||
resolution: "@mantine/modals@npm:5.10.3"
|
resolution: "@mantine/modals@npm:5.10.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@mantine/utils": 5.10.3
|
"@mantine/utils": 5.10.4
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@mantine/core": 5.10.3
|
"@mantine/core": 5.10.4
|
||||||
"@mantine/hooks": 5.10.3
|
"@mantine/hooks": 5.10.4
|
||||||
react: ">=16.8.0"
|
react: ">=16.8.0"
|
||||||
react-dom: ">=16.8.0"
|
react-dom: ">=16.8.0"
|
||||||
checksum: b013eb3c68ee1c0f163c1bd4b786c59e4a7f77d596f175ef2a88b1922153c24a154f12a36b65138af778cdd47944869056df5beb921e5d07801f5246b19b8da9
|
checksum: 82fce48fffbbce11526212b994e2763f0b36a276a86385f5b9ac1b5f1226f39f3f12aa171fe8b9181b755f95dbb869ef88f339e620bfd0ec28b7b0017f6b7fa7
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mantine/next@npm:^5.9.3":
|
"@mantine/next@npm:^5.9.3":
|
||||||
version: 5.10.3
|
version: 5.10.4
|
||||||
resolution: "@mantine/next@npm:5.10.3"
|
resolution: "@mantine/next@npm:5.10.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@mantine/ssr": 5.10.3
|
"@mantine/ssr": 5.10.4
|
||||||
"@mantine/styles": 5.10.3
|
"@mantine/styles": 5.10.4
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
next: "*"
|
next: "*"
|
||||||
react: ">=16.8.0"
|
react: ">=16.8.0"
|
||||||
react-dom: ">=16.8.0"
|
react-dom: ">=16.8.0"
|
||||||
checksum: 78ee3e7f7565318f40f367c8d53edfb164ae10c4fcc788b8792b27ae3f10fad81be8dbfaecd171fd66c43eafc50cc30ea5ab7f3da5421642b3a8c96d5a03d618
|
checksum: 344590d09cfef4194187f7ea430082d9f22379e264b04fa3a9261a137abf9b1e4681eb7c610b678822b2ad3001f5611e201a73a4612ee8b13d93460d68a28b0c
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mantine/notifications@npm:^5.9.3":
|
"@mantine/notifications@npm:^5.9.3":
|
||||||
version: 5.10.3
|
version: 5.10.4
|
||||||
resolution: "@mantine/notifications@npm:5.10.3"
|
resolution: "@mantine/notifications@npm:5.10.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@mantine/utils": 5.10.3
|
"@mantine/utils": 5.10.4
|
||||||
react-transition-group: 4.4.2
|
react-transition-group: 4.4.2
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@mantine/core": 5.10.3
|
"@mantine/core": 5.10.4
|
||||||
"@mantine/hooks": 5.10.3
|
"@mantine/hooks": 5.10.4
|
||||||
react: ">=16.8.0"
|
react: ">=16.8.0"
|
||||||
react-dom: ">=16.8.0"
|
react-dom: ">=16.8.0"
|
||||||
checksum: e4b735725fc7c0558cf0b084f677d432d6bd49220621ff20e32371621ba454a9d8b8eba0a36db7e99a0951cc174cb51b7ec91a9e127cba13330c93f3bcaecb10
|
checksum: da439698331f09dd1f3efaa470df1f9717f7309c2a61b0bfaa14bd832185fbd0711fbaffe867b0d6978108db30d059909b59c2b7c0ed83d1bd55bc8b7a6d3e7f
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mantine/ssr@npm:5.10.3":
|
"@mantine/ssr@npm:5.10.4":
|
||||||
version: 5.10.3
|
version: 5.10.4
|
||||||
resolution: "@mantine/ssr@npm:5.10.3"
|
resolution: "@mantine/ssr@npm:5.10.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@mantine/styles": 5.10.3
|
"@mantine/styles": 5.10.4
|
||||||
html-react-parser: 1.4.12
|
html-react-parser: 1.4.12
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@emotion/react": ">=11.9.0"
|
"@emotion/react": ">=11.9.0"
|
||||||
"@emotion/server": ">=11.4.0"
|
"@emotion/server": ">=11.4.0"
|
||||||
react: ">=16.8.0"
|
react: ">=16.8.0"
|
||||||
react-dom: ">=16.8.0"
|
react-dom: ">=16.8.0"
|
||||||
checksum: 8a6f0140f8fc853e3be8b0ec78908f71378ab76f36efe58ea2b1818139db60658bd12d3b1a56b9e61218262e85e2fbf9488393c3f0a64cb2d24125a27230397f
|
checksum: 74e5c56b85ea731597aba876941086306abb5ae24faa86d37a5210cfd743aa164e97b29e547563f810ef92b51b3c90bd02ce1a39fbee84cbec2abd2e6ad45ca7
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mantine/styles@npm:5.10.3":
|
"@mantine/styles@npm:5.10.4":
|
||||||
version: 5.10.3
|
version: 5.10.4
|
||||||
resolution: "@mantine/styles@npm:5.10.3"
|
resolution: "@mantine/styles@npm:5.10.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
clsx: 1.1.1
|
clsx: 1.1.1
|
||||||
csstype: 3.0.9
|
csstype: 3.0.9
|
||||||
@@ -1192,16 +1185,16 @@ __metadata:
|
|||||||
"@emotion/react": ">=11.9.0"
|
"@emotion/react": ">=11.9.0"
|
||||||
react: ">=16.8.0"
|
react: ">=16.8.0"
|
||||||
react-dom: ">=16.8.0"
|
react-dom: ">=16.8.0"
|
||||||
checksum: 60fcfd5ccbfd7e1106a24a13c3f705ac4b995214ae038c2d8e7bf5e795cb7c1f5da9ee5e336caedf2a537e77272a05eb315e76df3edb4a466ec158ae6aeaf8b5
|
checksum: 54af835dca68a457be758570c82eab2d602da19a24da599b2f2c02f451be136a400b2f8efbbdc7d1a677188b57515d3ace23df0b8aa8e37c4ddf3a2fdbce1630
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mantine/utils@npm:5.10.3":
|
"@mantine/utils@npm:5.10.4":
|
||||||
version: 5.10.3
|
version: 5.10.4
|
||||||
resolution: "@mantine/utils@npm:5.10.3"
|
resolution: "@mantine/utils@npm:5.10.4"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ">=16.8.0"
|
react: ">=16.8.0"
|
||||||
checksum: 063237e49f3c52e3bbcd99e7a8383ac881718d9e1e039d32249231370a463a19e070cea7f90c3c0fea2933c3c162f5509a0f2add015db2aeb90f7945cd954a12
|
checksum: 96e2602f8500c29b5979d4fe0b3456c8de911ff1bd2ef216d960b23a5370ff6828871aa859538a4004ad095fb63d7e0e76cdfb365bdb930f70f8076d730302c1
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -2080,9 +2073,9 @@ __metadata:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/node@npm:*, @types/node@npm:^18.11.18":
|
"@types/node@npm:*, @types/node@npm:^18.11.18":
|
||||||
version: 18.13.0
|
version: 18.14.0
|
||||||
resolution: "@types/node@npm:18.13.0"
|
resolution: "@types/node@npm:18.14.0"
|
||||||
checksum: 4ea10f8802848b01672bce938f678b6774ca2cee0c9774f12275ab064ae07818419c3e2e41d6257ce7ba846d1ea26c63214aa1dfa4166fa3746291752b8c6416
|
checksum: d83fcf5e4ed544755dd9028f5cbb6b9d46235043159111bb2ad62223729aee581c0144a9f6df8ba73d74011db9ed4ebd7af2fd5e0996714e3beb508a5da8ac5c
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -2674,7 +2667,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"axios@npm:^0.27.2":
|
"axios@npm:0.27.2, axios@npm:^0.27.2":
|
||||||
version: 0.27.2
|
version: 0.27.2
|
||||||
resolution: "axios@npm:0.27.2"
|
resolution: "axios@npm:0.27.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3005,9 +2998,9 @@ __metadata:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"caniuse-lite@npm:^1.0.30001406, caniuse-lite@npm:^1.0.30001449":
|
"caniuse-lite@npm:^1.0.30001406, caniuse-lite@npm:^1.0.30001449":
|
||||||
version: 1.0.30001451
|
version: 1.0.30001456
|
||||||
resolution: "caniuse-lite@npm:1.0.30001451"
|
resolution: "caniuse-lite@npm:1.0.30001456"
|
||||||
checksum: 48a06a7881093bb4d8a08ed5428f24a1cbdaa544b0a6f0c3614287d4f34b6c853e79a0f608a5bd901c27995f5e951825606fba11e7930251cc422bd61de9d849
|
checksum: c2cc479962149abd09a25b64699ee7484d9c433db2bad0a489f7b51b09a463c991f6efd7b8e201bc1a1ccf3294263f88503a3adf0a57db9046939ee7e58b76a6
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -3198,6 +3191,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"concat-map@npm:0.0.1":
|
||||||
version: 0.0.1
|
version: 0.0.1
|
||||||
resolution: "concat-map@npm:0.0.1"
|
resolution: "concat-map@npm:0.0.1"
|
||||||
@@ -3713,9 +3713,9 @@ __metadata:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"electron-to-chromium@npm:^1.4.284":
|
"electron-to-chromium@npm:^1.4.284":
|
||||||
version: 1.4.295
|
version: 1.4.302
|
||||||
resolution: "electron-to-chromium@npm:1.4.295"
|
resolution: "electron-to-chromium@npm:1.4.302"
|
||||||
checksum: 66fff1341d3c94c2ccd1f2a39cffdb92118304f4b949d3194427e7022d6a6bd8c482b5c4afd9dce210117ba20cac01c1a1466089f5a862fe9c563113b86ff829
|
checksum: aa764494f9a5b6916ba9f311c0204b2c73449addba18cc55d43e84e8c4465732af9cd6560a8efeb32f3c5a928299030e41352e5b3a081e9e56b086d5be618f45
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -3758,7 +3758,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"entities@npm:^2.0.0":
|
"entities@npm:^2.0.0, entities@npm:^2.0.3":
|
||||||
version: 2.2.0
|
version: 2.2.0
|
||||||
resolution: "entities@npm:2.2.0"
|
resolution: "entities@npm:2.2.0"
|
||||||
checksum: 19010dacaf0912c895ea262b4f6128574f9ccf8d4b3b65c7e8334ad0079b3706376360e28d8843ff50a78aabcb8f08f0a32dbfacdc77e47ed77ca08b713669b3
|
checksum: 19010dacaf0912c895ea262b4f6128574f9ccf8d4b3b65c7e8334ad0079b3706376360e28d8843ff50a78aabcb8f08f0a32dbfacdc77e47ed77ca08b713669b3
|
||||||
@@ -4101,13 +4101,13 @@ __metadata:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"eslint-plugin-testing-library@npm:^5.5.1":
|
"eslint-plugin-testing-library@npm:^5.5.1":
|
||||||
version: 5.10.1
|
version: 5.10.2
|
||||||
resolution: "eslint-plugin-testing-library@npm:5.10.1"
|
resolution: "eslint-plugin-testing-library@npm:5.10.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/utils": ^5.43.0
|
"@typescript-eslint/utils": ^5.43.0
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^7.5.0 || ^8.0.0
|
eslint: ^7.5.0 || ^8.0.0
|
||||||
checksum: fbc24ce8cbd03bee283ae520f023bd52f10d21fb25bd66c5a09a0e0ad17997bae559450438ce0b4fcc38b4e14b6802f18482c41d2bc72bf1a8e1a12a91766572
|
checksum: 3b2b330e62f4a6dc438050006f0d0c97605f6861828b153271dc6d2fafb1e60f4e86fbaa8166c7afd452e3b6cad39413738fd4c8e2eb2def1915c678154676da
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -4249,11 +4249,11 @@ __metadata:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"esquery@npm:^1.4.0":
|
"esquery@npm:^1.4.0":
|
||||||
version: 1.4.0
|
version: 1.4.2
|
||||||
resolution: "esquery@npm:1.4.0"
|
resolution: "esquery@npm:1.4.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
estraverse: ^5.1.0
|
estraverse: ^5.1.0
|
||||||
checksum: a0807e17abd7fbe5fbd4fab673038d6d8a50675cdae6b04fbaa520c34581be0c5fa24582990e8acd8854f671dd291c78bb2efb9e0ed5b62f33bac4f9cf820210
|
checksum: 2f4ad89c5aafaca61cc2c15e256190f0d6deb4791cae6552d3cb4b1eb8867958cdf27a56aaa3272ff17435e3eaa19ee0d4129fac336ca6373d7354d7b5da7966
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -4512,8 +4512,8 @@ __metadata:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"framer-motion@npm:^9.0.2":
|
"framer-motion@npm:^9.0.2":
|
||||||
version: 9.0.2
|
version: 9.0.4
|
||||||
resolution: "framer-motion@npm:9.0.2"
|
resolution: "framer-motion@npm:9.0.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@emotion/is-prop-valid": ^0.8.2
|
"@emotion/is-prop-valid": ^0.8.2
|
||||||
"@motionone/dom": ^10.15.3
|
"@motionone/dom": ^10.15.3
|
||||||
@@ -4525,7 +4525,7 @@ __metadata:
|
|||||||
dependenciesMeta:
|
dependenciesMeta:
|
||||||
"@emotion/is-prop-valid":
|
"@emotion/is-prop-valid":
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 5694d3a49acb9f753f0eed4a946c2b33efc237e0863b882cc4bed9a6022447ce2c9024134b5e9fd7b6f73602d9af046fa633c0f7235e8cbb24702b403299b55e
|
checksum: 9ef23d81c78785dd2f7f278bad133d96c22fd3a9dcc310521ea220873b0a923ca927dc2d3e8000e221edfcc5bfd6272f752435472e3a8f9b1ef476202ede1ce2
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -4944,6 +4944,7 @@ __metadata:
|
|||||||
"@ctrl/transmission": ^4.1.1
|
"@ctrl/transmission": ^4.1.1
|
||||||
"@emotion/react": ^11.10.5
|
"@emotion/react": ^11.10.5
|
||||||
"@emotion/server": ^11.10.0
|
"@emotion/server": ^11.10.0
|
||||||
|
"@jellyfin/sdk": ^0.7.0
|
||||||
"@mantine/core": ^5.9.3
|
"@mantine/core": ^5.9.3
|
||||||
"@mantine/dates": ^5.9.3
|
"@mantine/dates": ^5.9.3
|
||||||
"@mantine/dropzone": ^5.9.3
|
"@mantine/dropzone": ^5.9.3
|
||||||
@@ -4985,6 +4986,7 @@ __metadata:
|
|||||||
eslint-plugin-unused-imports: ^2.0.0
|
eslint-plugin-unused-imports: ^2.0.0
|
||||||
fily-publish-gridstack: ^0.0.13
|
fily-publish-gridstack: ^0.0.13
|
||||||
framer-motion: ^9.0.2
|
framer-motion: ^9.0.2
|
||||||
|
html-entities: ^2.3.3
|
||||||
i18next: ^21.9.1
|
i18next: ^21.9.1
|
||||||
jest: ^28.1.3
|
jest: ^28.1.3
|
||||||
js-file-download: ^0.4.12
|
js-file-download: ^0.4.12
|
||||||
@@ -4996,12 +4998,14 @@ __metadata:
|
|||||||
react: ^18.2.0
|
react: ^18.2.0
|
||||||
react-dom: ^18.2.0
|
react-dom: ^18.2.0
|
||||||
react-simple-code-editor: ^0.13.1
|
react-simple-code-editor: ^0.13.1
|
||||||
|
rss-parser: ^3.12.0
|
||||||
sabnzbd-api: ^1.5.0
|
sabnzbd-api: ^1.5.0
|
||||||
sass: ^1.56.1
|
sass: ^1.56.1
|
||||||
turbo: ^1.7.4
|
turbo: ^1.7.4
|
||||||
typescript: ^4.7.4
|
typescript: ^4.7.4
|
||||||
uuid: ^8.3.2
|
uuid: ^8.3.2
|
||||||
video.js: ^8.0.3
|
video.js: ^8.0.3
|
||||||
|
xml-js: ^1.6.11
|
||||||
yarn: ^1.22.19
|
yarn: ^1.22.19
|
||||||
zustand: ^4.1.4
|
zustand: ^4.1.4
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
@@ -5017,6 +5021,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"html-entities@npm:^2.3.3":
|
||||||
|
version: 2.3.3
|
||||||
|
resolution: "html-entities@npm:2.3.3"
|
||||||
|
checksum: 92521501da8aa5f66fee27f0f022d6e9ceae62667dae93aa6a2f636afa71ad530b7fb24a18d4d6c124c9885970cac5f8a52dbf1731741161002816ae43f98196
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"html-escaper@npm:^2.0.0":
|
"html-escaper@npm:^2.0.0":
|
||||||
version: 2.0.2
|
version: 2.0.2
|
||||||
resolution: "html-escaper@npm:2.0.2"
|
resolution: "html-escaper@npm:2.0.2"
|
||||||
@@ -6318,9 +6329,9 @@ __metadata:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"lru-cache@npm:^7.7.1":
|
"lru-cache@npm:^7.7.1":
|
||||||
version: 7.14.1
|
version: 7.16.1
|
||||||
resolution: "lru-cache@npm:7.14.1"
|
resolution: "lru-cache@npm:7.16.1"
|
||||||
checksum: d72c6713c6a6d86836a7a6523b3f1ac6764768cca47ec99341c3e76db06aacd4764620e5e2cda719a36848785a52a70e531822dc2b33fb071fa709683746c104
|
checksum: 64618e3ed4fd1203afedd9bbf5247921b1419f8e3100f20e58e5f04e741f8287bd7d04fefaad332411bb53b3a73445714b235de750cf5d310cba1fa23bd82795
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -7656,6 +7667,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"rss-parser@npm:^3.12.0":
|
||||||
|
version: 3.12.0
|
||||||
|
resolution: "rss-parser@npm:3.12.0"
|
||||||
|
dependencies:
|
||||||
|
entities: ^2.0.3
|
||||||
|
xml2js: ^0.4.19
|
||||||
|
checksum: aa0f0eb2e3a5c70677a1c7cb6c2e96420f12c8963a8bed922ec2ff1bb9dbbb725fc5783be31ca8140154c3d5589ccd31580ced7d32ebd0dda7572f78ce242a41
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"run-parallel@npm:^1.1.9":
|
"run-parallel@npm:^1.1.9":
|
||||||
version: 1.2.0
|
version: 1.2.0
|
||||||
resolution: "run-parallel@npm:1.2.0"
|
resolution: "run-parallel@npm:1.2.0"
|
||||||
@@ -7726,15 +7747,22 @@ __metadata:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"sass@npm:^1.56.1":
|
"sass@npm:^1.56.1":
|
||||||
version: 1.58.1
|
version: 1.58.2
|
||||||
resolution: "sass@npm:1.58.1"
|
resolution: "sass@npm:1.58.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
chokidar: ">=3.0.0 <4.0.0"
|
chokidar: ">=3.0.0 <4.0.0"
|
||||||
immutable: ^4.0.0
|
immutable: ^4.0.0
|
||||||
source-map-js: ">=0.6.2 <2.0.0"
|
source-map-js: ">=0.6.2 <2.0.0"
|
||||||
bin:
|
bin:
|
||||||
sass: sass.js
|
sass: sass.js
|
||||||
checksum: ff079887d906b5c0dde99084d14ac36336d238c0c07935ff6381bad68f05de212c1ff12657ac1e8a0533523cd7a393126facdc2508d758e7d5700344a0e6ea51
|
checksum: e0febe4d274af7b9490b9207ff7f05762d60df6b2ad307f7a823432cb4e1604eced6784ae635a6b80e4a6177c047f5a9623c53a15aaec3b9bf981ea86e8937a9
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"sax@npm:>=0.6.0, sax@npm:^1.2.4":
|
||||||
|
version: 1.2.4
|
||||||
|
resolution: "sax@npm:1.2.4"
|
||||||
|
checksum: d3df7d32b897a2c2f28e941f732c71ba90e27c24f62ee918bd4d9a8cfb3553f2f81e5493c7f0be94a11c1911b643a9108f231dd6f60df3fa9586b5d2e3e9e1fe
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -8166,9 +8194,9 @@ __metadata:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"tabbable@npm:^6.0.1":
|
"tabbable@npm:^6.0.1":
|
||||||
version: 6.0.1
|
version: 6.1.1
|
||||||
resolution: "tabbable@npm:6.0.1"
|
resolution: "tabbable@npm:6.1.1"
|
||||||
checksum: 65e378ad69a97416f2fdce34ade11b8ff68b33d9b2d978920a9d285c77e1bb88cb35113a8f00af8c4f0163d788d451a48840a216fa918d6a3f0c554951deb984
|
checksum: 348639497262241ce8e0ccb0664ea582a386183107299ee8f27cf7b56bc84f36e09eaf667d3cb4201e789634012a91f7129bcbd49760abe874fbace35b4cf429
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -8335,58 +8363,58 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"turbo-darwin-64@npm:1.7.4":
|
"turbo-darwin-64@npm:1.8.0":
|
||||||
version: 1.7.4
|
version: 1.8.0
|
||||||
resolution: "turbo-darwin-64@npm:1.7.4"
|
resolution: "turbo-darwin-64@npm:1.8.0"
|
||||||
conditions: os=darwin & cpu=x64
|
conditions: os=darwin & cpu=x64
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"turbo-darwin-arm64@npm:1.7.4":
|
"turbo-darwin-arm64@npm:1.8.0":
|
||||||
version: 1.7.4
|
version: 1.8.0
|
||||||
resolution: "turbo-darwin-arm64@npm:1.7.4"
|
resolution: "turbo-darwin-arm64@npm:1.8.0"
|
||||||
conditions: os=darwin & cpu=arm64
|
conditions: os=darwin & cpu=arm64
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"turbo-linux-64@npm:1.7.4":
|
"turbo-linux-64@npm:1.8.0":
|
||||||
version: 1.7.4
|
version: 1.8.0
|
||||||
resolution: "turbo-linux-64@npm:1.7.4"
|
resolution: "turbo-linux-64@npm:1.8.0"
|
||||||
conditions: os=linux & cpu=x64
|
conditions: os=linux & cpu=x64
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"turbo-linux-arm64@npm:1.7.4":
|
"turbo-linux-arm64@npm:1.8.0":
|
||||||
version: 1.7.4
|
version: 1.8.0
|
||||||
resolution: "turbo-linux-arm64@npm:1.7.4"
|
resolution: "turbo-linux-arm64@npm:1.8.0"
|
||||||
conditions: os=linux & cpu=arm64
|
conditions: os=linux & cpu=arm64
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"turbo-windows-64@npm:1.7.4":
|
"turbo-windows-64@npm:1.8.0":
|
||||||
version: 1.7.4
|
version: 1.8.0
|
||||||
resolution: "turbo-windows-64@npm:1.7.4"
|
resolution: "turbo-windows-64@npm:1.8.0"
|
||||||
conditions: os=win32 & cpu=x64
|
conditions: os=win32 & cpu=x64
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"turbo-windows-arm64@npm:1.7.4":
|
"turbo-windows-arm64@npm:1.8.0":
|
||||||
version: 1.7.4
|
version: 1.8.0
|
||||||
resolution: "turbo-windows-arm64@npm:1.7.4"
|
resolution: "turbo-windows-arm64@npm:1.8.0"
|
||||||
conditions: os=win32 & cpu=arm64
|
conditions: os=win32 & cpu=arm64
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"turbo@npm:^1.7.4":
|
"turbo@npm:^1.7.4":
|
||||||
version: 1.7.4
|
version: 1.8.0
|
||||||
resolution: "turbo@npm:1.7.4"
|
resolution: "turbo@npm:1.8.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
turbo-darwin-64: 1.7.4
|
turbo-darwin-64: 1.8.0
|
||||||
turbo-darwin-arm64: 1.7.4
|
turbo-darwin-arm64: 1.8.0
|
||||||
turbo-linux-64: 1.7.4
|
turbo-linux-64: 1.8.0
|
||||||
turbo-linux-arm64: 1.7.4
|
turbo-linux-arm64: 1.8.0
|
||||||
turbo-windows-64: 1.7.4
|
turbo-windows-64: 1.8.0
|
||||||
turbo-windows-arm64: 1.7.4
|
turbo-windows-arm64: 1.8.0
|
||||||
dependenciesMeta:
|
dependenciesMeta:
|
||||||
turbo-darwin-64:
|
turbo-darwin-64:
|
||||||
optional: true
|
optional: true
|
||||||
@@ -8402,7 +8430,7 @@ __metadata:
|
|||||||
optional: true
|
optional: true
|
||||||
bin:
|
bin:
|
||||||
turbo: bin/turbo
|
turbo: bin/turbo
|
||||||
checksum: c4387cfee36c57dd1490e9b2452888d8450faa390aec5bc148389b426bc079d03044891d61ad84a6a3a7454e66dfbe9e9cfba05f636b36fef7ece7af275ef9ce
|
checksum: 7f97068d7f9a155e088d3575b1f9922e68fa3015aae0c92625238d44b4e6c275bec2a281907702dedb402fca29a6cd4690499e916cb334d7c24c98099bc3d8b0
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -8612,13 +8640,13 @@ __metadata:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"v8-to-istanbul@npm:^9.0.1":
|
"v8-to-istanbul@npm:^9.0.1":
|
||||||
version: 9.0.1
|
version: 9.1.0
|
||||||
resolution: "v8-to-istanbul@npm:9.0.1"
|
resolution: "v8-to-istanbul@npm:9.1.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@jridgewell/trace-mapping": ^0.3.12
|
"@jridgewell/trace-mapping": ^0.3.12
|
||||||
"@types/istanbul-lib-coverage": ^2.0.1
|
"@types/istanbul-lib-coverage": ^2.0.1
|
||||||
convert-source-map: ^1.6.0
|
convert-source-map: ^1.6.0
|
||||||
checksum: a49c34bf0a3af0c11041a3952a2600913904a983bd1bc87148b5c033bc5c1d02d5a13620fcdbfa2c60bc582a2e2970185780f0c844b4c3a220abf405f8af6311
|
checksum: 2069d59ee46cf8d83b4adfd8a5c1a90834caffa9f675e4360f1157ffc8578ef0f763c8f32d128334424159bb6b01f3876acd39cd13297b2769405a9da241f8d1
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -8822,6 +8850,34 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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"
|
||||||
|
dependencies:
|
||||||
|
sax: ">=0.6.0"
|
||||||
|
xmlbuilder: ~11.0.0
|
||||||
|
checksum: ca0cf2dfbf6deeaae878a891c8fbc0db6fd04398087084edf143cdc83d0509ad0fe199b890f62f39c4415cf60268a27a6aed0d343f0658f8779bd7add690fa98
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"xmlbuilder@npm:~11.0.0":
|
||||||
|
version: 11.0.1
|
||||||
|
resolution: "xmlbuilder@npm:11.0.1"
|
||||||
|
checksum: 7152695e16f1a9976658215abab27e55d08b1b97bca901d58b048d2b6e106b5af31efccbdecf9b07af37c8377d8e7e821b494af10b3a68b0ff4ae60331b415b0
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"xtend@npm:^4.0.0":
|
"xtend@npm:^4.0.0":
|
||||||
version: 4.0.2
|
version: 4.0.2
|
||||||
resolution: "xtend@npm:4.0.2"
|
resolution: "xtend@npm:4.0.2"
|
||||||
@@ -8874,8 +8930,8 @@ __metadata:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"yargs@npm:^17.3.1":
|
"yargs@npm:^17.3.1":
|
||||||
version: 17.6.2
|
version: 17.7.0
|
||||||
resolution: "yargs@npm:17.6.2"
|
resolution: "yargs@npm:17.7.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
cliui: ^8.0.1
|
cliui: ^8.0.1
|
||||||
escalade: ^3.1.1
|
escalade: ^3.1.1
|
||||||
@@ -8884,7 +8940,7 @@ __metadata:
|
|||||||
string-width: ^4.2.3
|
string-width: ^4.2.3
|
||||||
y18n: ^5.0.5
|
y18n: ^5.0.5
|
||||||
yargs-parser: ^21.1.1
|
yargs-parser: ^21.1.1
|
||||||
checksum: 47da1b0d854fa16d45a3ded57b716b013b2179022352a5f7467409da5a04a1eef5b3b3d97a2dfc13e8bbe5f2ffc0afe3bc6a4a72f8254e60f5a4bd7947138643
|
checksum: e7d5f5b60e63b04ded7c27c3d4b194565565cac3ea19fffcdbb183bed973a83106822a04dda28ebba4811ce92949a9d9858d3935186ff8f343548bf98aab2120
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user