mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-11-04 03:27:14 -05:00 
			
		
		
		
	Add subtitles to the player
This commit is contained in:
		
							parent
							
								
									9b62cb8a93
								
							
						
					
					
						commit
						6eccb2cede
					
				@ -20,7 +20,7 @@ services:
 | 
				
			|||||||
      - postgres
 | 
					      - postgres
 | 
				
			||||||
    volumes:
 | 
					    volumes:
 | 
				
			||||||
      - ./back:/app
 | 
					      - ./back:/app
 | 
				
			||||||
      - /app/out
 | 
					      - /app/out/
 | 
				
			||||||
      - kyoo:/var/lib/kyoo
 | 
					      - kyoo:/var/lib/kyoo
 | 
				
			||||||
      - ./video:/video
 | 
					      - ./video:/video
 | 
				
			||||||
  front:
 | 
					  front:
 | 
				
			||||||
@ -29,7 +29,8 @@ services:
 | 
				
			|||||||
      dockerfile: Dockerfile.dev
 | 
					      dockerfile: Dockerfile.dev
 | 
				
			||||||
    volumes:
 | 
					    volumes:
 | 
				
			||||||
      - ./front:/app
 | 
					      - ./front:/app
 | 
				
			||||||
      - /app/nodes_modules
 | 
					      - /app/node_modules/
 | 
				
			||||||
 | 
					      - /app/.next/
 | 
				
			||||||
    ports:
 | 
					    ports:
 | 
				
			||||||
      - "3000:3000"
 | 
					      - "3000:3000"
 | 
				
			||||||
    restart: on-failure
 | 
					    restart: on-failure
 | 
				
			||||||
@ -55,6 +56,7 @@ services:
 | 
				
			|||||||
    environment:
 | 
					    environment:
 | 
				
			||||||
      - POSTGRES_USER=${POSTGRES_USER}
 | 
					      - POSTGRES_USER=${POSTGRES_USER}
 | 
				
			||||||
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
 | 
					      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
 | 
				
			||||||
 | 
					      - POSTGRES_DB=${POSTGRES_DB}
 | 
				
			||||||
    volumes:
 | 
					    volumes:
 | 
				
			||||||
      - db:/var/lib/postgresql/data
 | 
					      - db:/var/lib/postgresql/data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -7,5 +7,6 @@
 | 
				
			|||||||
	"mute": "Toggle mute",
 | 
						"mute": "Toggle mute",
 | 
				
			||||||
	"volume": "Volume",
 | 
						"volume": "Volume",
 | 
				
			||||||
	"subtitles": "Subtitles",
 | 
						"subtitles": "Subtitles",
 | 
				
			||||||
 | 
						"subtitle-none": "None",
 | 
				
			||||||
	"fullscreen": "Fullscreen"
 | 
						"fullscreen": "Fullscreen"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -7,5 +7,6 @@
 | 
				
			|||||||
	"mute": "Muet",
 | 
						"mute": "Muet",
 | 
				
			||||||
	"volume": "Volume",
 | 
						"volume": "Volume",
 | 
				
			||||||
	"subtitles": "Sous titres",
 | 
						"subtitles": "Sous titres",
 | 
				
			||||||
 | 
						"subtitle-none": "Aucun",
 | 
				
			||||||
	"fullscreen": "Plein-écran"
 | 
						"fullscreen": "Plein-écran"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -18,6 +18,8 @@
 | 
				
			|||||||
 * along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
					 * along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const CopyPlugin = require("copy-webpack-plugin");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @type {import("next").NextConfig}
 | 
					 * @type {import("next").NextConfig}
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@ -25,6 +27,21 @@ const nextConfig = {
 | 
				
			|||||||
	reactStrictMode: true,
 | 
						reactStrictMode: true,
 | 
				
			||||||
	swcMinify: true,
 | 
						swcMinify: true,
 | 
				
			||||||
	output: "standalone",
 | 
						output: "standalone",
 | 
				
			||||||
 | 
						webpack: (config) => {
 | 
				
			||||||
 | 
							config.plugins = [
 | 
				
			||||||
 | 
								...config.plugins,
 | 
				
			||||||
 | 
								new CopyPlugin({
 | 
				
			||||||
 | 
									patterns: [
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											context: "node_modules/@jellyfin/libass-wasm/dist/js/",
 | 
				
			||||||
 | 
											from: "*",
 | 
				
			||||||
 | 
											to: "static/chunks/",
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									],
 | 
				
			||||||
 | 
								}),
 | 
				
			||||||
 | 
							];
 | 
				
			||||||
 | 
							return config;
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
	async redirects() {
 | 
						async redirects() {
 | 
				
			||||||
		return [
 | 
							return [
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
 | 
				
			|||||||
@ -23,6 +23,7 @@
 | 
				
			|||||||
	"dependencies": {
 | 
						"dependencies": {
 | 
				
			||||||
		"@emotion/react": "^11.9.3",
 | 
							"@emotion/react": "^11.9.3",
 | 
				
			||||||
		"@emotion/styled": "^11.9.3",
 | 
							"@emotion/styled": "^11.9.3",
 | 
				
			||||||
 | 
							"@jellyfin/libass-wasm": "^4.1.1",
 | 
				
			||||||
		"@mui/icons-material": "^5.8.4",
 | 
							"@mui/icons-material": "^5.8.4",
 | 
				
			||||||
		"@mui/material": "^5.8.7",
 | 
							"@mui/material": "^5.8.7",
 | 
				
			||||||
		"next": "12.2.2",
 | 
							"next": "12.2.2",
 | 
				
			||||||
@ -38,6 +39,7 @@
 | 
				
			|||||||
		"@types/node": "18.0.3",
 | 
							"@types/node": "18.0.3",
 | 
				
			||||||
		"@types/react": "18.0.15",
 | 
							"@types/react": "18.0.15",
 | 
				
			||||||
		"@types/react-dom": "18.0.6",
 | 
							"@types/react-dom": "18.0.6",
 | 
				
			||||||
 | 
							"copy-webpack-plugin": "^11.0.0",
 | 
				
			||||||
		"eslint": "8.19.0",
 | 
							"eslint": "8.19.0",
 | 
				
			||||||
		"eslint-config-next": "12.2.2",
 | 
							"eslint-config-next": "12.2.2",
 | 
				
			||||||
		"eslint-config-prettier": "^8.5.0",
 | 
							"eslint-config-prettier": "^8.5.0",
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,11 @@ import { Link } from "~/utils/link";
 | 
				
			|||||||
import { Image } from "./poster";
 | 
					import { Image } from "./poster";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const episodeDisplayNumber = (
 | 
					export const episodeDisplayNumber = (
 | 
				
			||||||
	episode: { seasonNumber?: number; episodeNumber?: number; absoluteNumber?: number },
 | 
						episode: {
 | 
				
			||||||
 | 
							seasonNumber?: number | null;
 | 
				
			||||||
 | 
							episodeNumber?: number | null;
 | 
				
			||||||
 | 
							absoluteNumber?: number | null;
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
	def?: string,
 | 
						def?: string,
 | 
				
			||||||
) => {
 | 
					) => {
 | 
				
			||||||
	if (typeof episode.seasonNumber === "number" && typeof episode.episodeNumber === "number")
 | 
						if (typeof episode.seasonNumber === "number" && typeof episode.episodeNumber === "number")
 | 
				
			||||||
 | 
				
			|||||||
@ -60,6 +60,7 @@ export const TrackP = ResourceP.extend({
 | 
				
			|||||||
	 */
 | 
						 */
 | 
				
			||||||
	displayName: z.string(),
 | 
						displayName: z.string(),
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					export type Track = z.infer<typeof TrackP>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ChapterP = z.object({
 | 
					export const ChapterP = z.object({
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
 | 
				
			|||||||
@ -228,68 +228,33 @@ const Item = ({ item, layout }: { item?: LibraryItem; layout: Layout }) => {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const BrowseSettings = ({
 | 
					const SortByMenu = ({
 | 
				
			||||||
	sortKey,
 | 
						sortKey,
 | 
				
			||||||
	setSort,
 | 
						setSort,
 | 
				
			||||||
	sortOrd,
 | 
						sortOrd,
 | 
				
			||||||
	setSortOrd,
 | 
						setSortOrd,
 | 
				
			||||||
	layout,
 | 
						anchor,
 | 
				
			||||||
	setLayout,
 | 
						onClose,
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
	sortKey: SortBy;
 | 
						sortKey: SortBy;
 | 
				
			||||||
	setSort: (sort: SortBy) => void;
 | 
						setSort: (sort: SortBy) => void;
 | 
				
			||||||
	sortOrd: SortOrd;
 | 
						sortOrd: SortOrd;
 | 
				
			||||||
	setSortOrd: (sort: SortOrd) => void;
 | 
						setSortOrd: (sort: SortOrd) => void;
 | 
				
			||||||
	layout: Layout;
 | 
						anchor: HTMLElement;
 | 
				
			||||||
	setLayout: (layout: Layout) => void;
 | 
						onClose: () => void;
 | 
				
			||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
	const [sortAnchor, setSortAnchor] = useState<HTMLElement | null>(null);
 | 
					 | 
				
			||||||
	const router = useRouter();
 | 
						const router = useRouter();
 | 
				
			||||||
	const { t } = useTranslation("browse");
 | 
						const { t } = useTranslation("browse");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const switchViewTitle = layout === Layout.Grid 
 | 
					 | 
				
			||||||
		? t("browse.switchToList")
 | 
					 | 
				
			||||||
		: t("browse.switchToGrid");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<>
 | 
					 | 
				
			||||||
			<Box sx={{ display: "flex", justifyContent: "space-around" }}>
 | 
					 | 
				
			||||||
				<ButtonGroup sx={{ m: 1 }}>
 | 
					 | 
				
			||||||
					<Button disabled>
 | 
					 | 
				
			||||||
						<FilterList />
 | 
					 | 
				
			||||||
					</Button>
 | 
					 | 
				
			||||||
					<Tooltip title={t("browse.sortby-tt")}>
 | 
					 | 
				
			||||||
						<Button
 | 
					 | 
				
			||||||
							id="sortby"
 | 
					 | 
				
			||||||
							aria-label={t("browse.sortby-tt")}
 | 
					 | 
				
			||||||
							aria-controls={sortAnchor ? "sorby-menu" : undefined}
 | 
					 | 
				
			||||||
							aria-haspopup="true"
 | 
					 | 
				
			||||||
							aria-expanded={sortAnchor ? "true" : undefined}
 | 
					 | 
				
			||||||
							onClick={(event: MouseEvent<HTMLElement>) => setSortAnchor(event.currentTarget)}
 | 
					 | 
				
			||||||
						>
 | 
					 | 
				
			||||||
							<Sort />
 | 
					 | 
				
			||||||
							{t("browse.sortby", { key: t(`browse.sortkey.${sortKey}`) })}
 | 
					 | 
				
			||||||
							{sortOrd === SortOrd.Asc ? <South fontSize="small" /> : <North fontSize="small" />}
 | 
					 | 
				
			||||||
						</Button>
 | 
					 | 
				
			||||||
					</Tooltip>
 | 
					 | 
				
			||||||
					<Tooltip title={switchViewTitle}>
 | 
					 | 
				
			||||||
						<Button
 | 
					 | 
				
			||||||
							onClick={() => setLayout(layout === Layout.List ? Layout.Grid : Layout.List)}
 | 
					 | 
				
			||||||
							aria-label={switchViewTitle}
 | 
					 | 
				
			||||||
						>
 | 
					 | 
				
			||||||
							{layout === Layout.List ? <GridView /> : <ViewList />}
 | 
					 | 
				
			||||||
						</Button>
 | 
					 | 
				
			||||||
					</Tooltip>
 | 
					 | 
				
			||||||
				</ButtonGroup>
 | 
					 | 
				
			||||||
			</Box>
 | 
					 | 
				
			||||||
		<Menu
 | 
							<Menu
 | 
				
			||||||
			id="sortby-menu"
 | 
								id="sortby-menu"
 | 
				
			||||||
			MenuListProps={{
 | 
								MenuListProps={{
 | 
				
			||||||
				"aria-labelledby": "sortby",
 | 
									"aria-labelledby": "sortby",
 | 
				
			||||||
			}}
 | 
								}}
 | 
				
			||||||
				anchorEl={sortAnchor}
 | 
								anchorEl={anchor}
 | 
				
			||||||
				open={!!sortAnchor}
 | 
								open={!!anchor}
 | 
				
			||||||
				onClose={() => setSortAnchor(null)}
 | 
								onClose={onClose}
 | 
				
			||||||
		>
 | 
							>
 | 
				
			||||||
			{Object.values(SortBy).map((x) => (
 | 
								{Object.values(SortBy).map((x) => (
 | 
				
			||||||
				<MenuItem
 | 
									<MenuItem
 | 
				
			||||||
@ -332,6 +297,72 @@ const BrowseSettings = ({
 | 
				
			|||||||
				<ListItemText>{t("browse.sortord.desc")}</ListItemText>
 | 
									<ListItemText>{t("browse.sortord.desc")}</ListItemText>
 | 
				
			||||||
			</MenuItem>
 | 
								</MenuItem>
 | 
				
			||||||
		</Menu>
 | 
							</Menu>
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const BrowseSettings = ({
 | 
				
			||||||
 | 
						sortKey,
 | 
				
			||||||
 | 
						setSort,
 | 
				
			||||||
 | 
						sortOrd,
 | 
				
			||||||
 | 
						setSortOrd,
 | 
				
			||||||
 | 
						layout,
 | 
				
			||||||
 | 
						setLayout,
 | 
				
			||||||
 | 
					}: {
 | 
				
			||||||
 | 
						sortKey: SortBy;
 | 
				
			||||||
 | 
						setSort: (sort: SortBy) => void;
 | 
				
			||||||
 | 
						sortOrd: SortOrd;
 | 
				
			||||||
 | 
						setSortOrd: (sort: SortOrd) => void;
 | 
				
			||||||
 | 
						layout: Layout;
 | 
				
			||||||
 | 
						setLayout: (layout: Layout) => void;
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
 | 
						const [sortAnchor, setSortAnchor] = useState<HTMLElement | null>(null);
 | 
				
			||||||
 | 
						const { t } = useTranslation("browse");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const switchViewTitle = layout === Layout.Grid 
 | 
				
			||||||
 | 
							? t("browse.switchToList")
 | 
				
			||||||
 | 
							: t("browse.switchToGrid");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (
 | 
				
			||||||
 | 
							<>
 | 
				
			||||||
 | 
								<Box sx={{ display: "flex", justifyContent: "space-around" }}>
 | 
				
			||||||
 | 
									<ButtonGroup sx={{ m: 1 }}>
 | 
				
			||||||
 | 
										<Button disabled>
 | 
				
			||||||
 | 
											<FilterList />
 | 
				
			||||||
 | 
										</Button>
 | 
				
			||||||
 | 
										<Tooltip title={t("browse.sortby-tt")}>
 | 
				
			||||||
 | 
											<Button
 | 
				
			||||||
 | 
												id="sortby"
 | 
				
			||||||
 | 
												aria-label={t("browse.sortby-tt")}
 | 
				
			||||||
 | 
												aria-controls={sortAnchor ? "sorby-menu" : undefined}
 | 
				
			||||||
 | 
												aria-haspopup="true"
 | 
				
			||||||
 | 
												aria-expanded={sortAnchor ? "true" : undefined}
 | 
				
			||||||
 | 
												onClick={(event) => setSortAnchor(event.currentTarget)}
 | 
				
			||||||
 | 
											>
 | 
				
			||||||
 | 
												<Sort />
 | 
				
			||||||
 | 
												{t("browse.sortby", { key: t(`browse.sortkey.${sortKey}`) })}
 | 
				
			||||||
 | 
												{sortOrd === SortOrd.Asc ? <South fontSize="small" /> : <North fontSize="small" />}
 | 
				
			||||||
 | 
											</Button>
 | 
				
			||||||
 | 
										</Tooltip>
 | 
				
			||||||
 | 
										<Tooltip title={switchViewTitle}>
 | 
				
			||||||
 | 
											<Button
 | 
				
			||||||
 | 
												onClick={() => setLayout(layout === Layout.List ? Layout.Grid : Layout.List)}
 | 
				
			||||||
 | 
												aria-label={switchViewTitle}
 | 
				
			||||||
 | 
											>
 | 
				
			||||||
 | 
												{layout === Layout.List ? <GridView /> : <ViewList />}
 | 
				
			||||||
 | 
											</Button>
 | 
				
			||||||
 | 
										</Tooltip>
 | 
				
			||||||
 | 
									</ButtonGroup>
 | 
				
			||||||
 | 
								</Box>
 | 
				
			||||||
 | 
								{sortAnchor && (
 | 
				
			||||||
 | 
									<SortByMenu
 | 
				
			||||||
 | 
										sortKey={sortKey}
 | 
				
			||||||
 | 
										sortOrd={sortOrd}
 | 
				
			||||||
 | 
										setSort={setSort}
 | 
				
			||||||
 | 
										setSortOrd={setSortOrd}
 | 
				
			||||||
 | 
										anchor={sortAnchor}
 | 
				
			||||||
 | 
										onClose={() => setSortAnchor(null)}
 | 
				
			||||||
 | 
									/>
 | 
				
			||||||
 | 
								)}
 | 
				
			||||||
		</>
 | 
							</>
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -20,10 +20,18 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import { QueryIdentifier, QueryPage } from "~/utils/query";
 | 
					import { QueryIdentifier, QueryPage } from "~/utils/query";
 | 
				
			||||||
import { withRoute } from "~/utils/router";
 | 
					import { withRoute } from "~/utils/router";
 | 
				
			||||||
import { WatchItem, WatchItemP, Chapter } from "~/models/resources/watch-item";
 | 
					import { WatchItem, WatchItemP, Chapter, Track } from "~/models/resources/watch-item";
 | 
				
			||||||
import { useFetch } from "~/utils/query";
 | 
					import { useFetch } from "~/utils/query";
 | 
				
			||||||
import { ErrorPage } from "~/components/errors";
 | 
					import { ErrorPage } from "~/components/errors";
 | 
				
			||||||
import { useState, useRef, useEffect, HTMLProps, memo, useMemo, useCallback } from "react";
 | 
					import {
 | 
				
			||||||
 | 
						useState,
 | 
				
			||||||
 | 
						useRef,
 | 
				
			||||||
 | 
						useEffect,
 | 
				
			||||||
 | 
						memo,
 | 
				
			||||||
 | 
						useMemo,
 | 
				
			||||||
 | 
						useCallback,
 | 
				
			||||||
 | 
						RefObject,
 | 
				
			||||||
 | 
					} from "react";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
	Box,
 | 
						Box,
 | 
				
			||||||
	CircularProgress,
 | 
						CircularProgress,
 | 
				
			||||||
@ -32,6 +40,10 @@ import {
 | 
				
			|||||||
	Typography,
 | 
						Typography,
 | 
				
			||||||
	Skeleton,
 | 
						Skeleton,
 | 
				
			||||||
	Slider,
 | 
						Slider,
 | 
				
			||||||
 | 
						Menu,
 | 
				
			||||||
 | 
						MenuItem,
 | 
				
			||||||
 | 
						ListItemText,
 | 
				
			||||||
 | 
					    BoxProps,
 | 
				
			||||||
} from "@mui/material";
 | 
					} from "@mui/material";
 | 
				
			||||||
import useTranslation from "next-translate/useTranslation";
 | 
					import useTranslation from "next-translate/useTranslation";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
@ -50,7 +62,11 @@ import {
 | 
				
			|||||||
} from "@mui/icons-material";
 | 
					} from "@mui/icons-material";
 | 
				
			||||||
import { Poster } from "~/components/poster";
 | 
					import { Poster } from "~/components/poster";
 | 
				
			||||||
import { episodeDisplayNumber } from "~/components/episode";
 | 
					import { episodeDisplayNumber } from "~/components/episode";
 | 
				
			||||||
 | 
					import { Link } from "~/utils/link";
 | 
				
			||||||
import NextLink from "next/link";
 | 
					import NextLink from "next/link";
 | 
				
			||||||
 | 
					import { useRouter } from "next/router";
 | 
				
			||||||
 | 
					// @ts-ignore
 | 
				
			||||||
 | 
					import SubtitleOctopus from "@jellyfin/libass-wasm"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const toTimerString = (timer: number, duration?: number) => {
 | 
					const toTimerString = (timer: number, duration?: number) => {
 | 
				
			||||||
	if (!duration) duration = timer;
 | 
						if (!duration) duration = timer;
 | 
				
			||||||
@ -58,6 +74,74 @@ const toTimerString = (timer: number, duration?: number) => {
 | 
				
			|||||||
	return new Date(timer * 1000).toISOString().substring(14, 19);
 | 
						return new Date(timer * 1000).toISOString().substring(14, 19);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const SubtitleMenu = ({
 | 
				
			||||||
 | 
						subtitles,
 | 
				
			||||||
 | 
						setSubtitle,
 | 
				
			||||||
 | 
						selectedID,
 | 
				
			||||||
 | 
						anchor,
 | 
				
			||||||
 | 
						onClose,
 | 
				
			||||||
 | 
					}: {
 | 
				
			||||||
 | 
						subtitles: Track[];
 | 
				
			||||||
 | 
						setSubtitle: (subtitle: Track | null) => void;
 | 
				
			||||||
 | 
						selectedID?: number;
 | 
				
			||||||
 | 
						anchor: HTMLElement;
 | 
				
			||||||
 | 
						onClose: () => void;
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
 | 
						const router = useRouter();
 | 
				
			||||||
 | 
						const { t } = useTranslation("player");
 | 
				
			||||||
 | 
						const { subtitle, ...queryWithoutSubs } = router.query;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (
 | 
				
			||||||
 | 
							<Menu
 | 
				
			||||||
 | 
								id="subtitle-menu"
 | 
				
			||||||
 | 
								MenuListProps={{
 | 
				
			||||||
 | 
									"aria-labelledby": "subtitle",
 | 
				
			||||||
 | 
								}}
 | 
				
			||||||
 | 
								anchorEl={anchor}
 | 
				
			||||||
 | 
								open={!!anchor}
 | 
				
			||||||
 | 
								onClose={onClose}
 | 
				
			||||||
 | 
								anchorOrigin={{
 | 
				
			||||||
 | 
									vertical: "top",
 | 
				
			||||||
 | 
									horizontal: "center",
 | 
				
			||||||
 | 
								}}
 | 
				
			||||||
 | 
								transformOrigin={{
 | 
				
			||||||
 | 
									vertical: "bottom",
 | 
				
			||||||
 | 
									horizontal: "center",
 | 
				
			||||||
 | 
								}}
 | 
				
			||||||
 | 
							>
 | 
				
			||||||
 | 
								<MenuItem
 | 
				
			||||||
 | 
									selected={!selectedID}
 | 
				
			||||||
 | 
									onClick={() => {
 | 
				
			||||||
 | 
										setSubtitle(null);
 | 
				
			||||||
 | 
										onClose();
 | 
				
			||||||
 | 
									}}
 | 
				
			||||||
 | 
									component={Link}
 | 
				
			||||||
 | 
									to={{ query: queryWithoutSubs }}
 | 
				
			||||||
 | 
									shallow
 | 
				
			||||||
 | 
									replace
 | 
				
			||||||
 | 
								>
 | 
				
			||||||
 | 
									<ListItemText>{t("subtitle-none")}</ListItemText>
 | 
				
			||||||
 | 
								</MenuItem>
 | 
				
			||||||
 | 
								{subtitles.map((sub) => (
 | 
				
			||||||
 | 
									<MenuItem
 | 
				
			||||||
 | 
										key={sub.id}
 | 
				
			||||||
 | 
										selected={selectedID == sub.id}
 | 
				
			||||||
 | 
										onClick={() => {
 | 
				
			||||||
 | 
											setSubtitle(sub);
 | 
				
			||||||
 | 
											onClose();
 | 
				
			||||||
 | 
										}}
 | 
				
			||||||
 | 
										component={Link}
 | 
				
			||||||
 | 
										to={{ query: { ...router.query, subtitle: sub.language ?? sub.id } }}
 | 
				
			||||||
 | 
										shallow
 | 
				
			||||||
 | 
										replace
 | 
				
			||||||
 | 
									>
 | 
				
			||||||
 | 
										<ListItemText>{sub.displayName}</ListItemText>
 | 
				
			||||||
 | 
									</MenuItem>
 | 
				
			||||||
 | 
								))}
 | 
				
			||||||
 | 
							</Menu>
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const LoadingIndicator = () => {
 | 
					const LoadingIndicator = () => {
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<Box
 | 
							<Box
 | 
				
			||||||
@ -304,24 +388,50 @@ const LeftButtons = memo(function LeftButtons({
 | 
				
			|||||||
const RightButtons = memo(function RightButton({
 | 
					const RightButtons = memo(function RightButton({
 | 
				
			||||||
	isFullscreen,
 | 
						isFullscreen,
 | 
				
			||||||
	toggleFullscreen,
 | 
						toggleFullscreen,
 | 
				
			||||||
 | 
						subtitles,
 | 
				
			||||||
 | 
						selectedSubtitle,
 | 
				
			||||||
 | 
						selectSubtitle,
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
	isFullscreen: boolean;
 | 
						isFullscreen: boolean;
 | 
				
			||||||
	toggleFullscreen: () => void;
 | 
						toggleFullscreen: () => void;
 | 
				
			||||||
 | 
						subtitles?: Track[];
 | 
				
			||||||
 | 
						selectedSubtitle: Track | null;
 | 
				
			||||||
 | 
						selectSubtitle: (track: Track | null) => void;
 | 
				
			||||||
}) {
 | 
					}) {
 | 
				
			||||||
	const { t } = useTranslation("player");
 | 
						const { t } = useTranslation("player");
 | 
				
			||||||
 | 
						const [subtitleAnchor, setSubtitleAnchor] = useState<HTMLButtonElement | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<Box sx={{ "> *": { mx: "8px !important" } }}>
 | 
							<Box sx={{ "> *": { m: "8px !important" } }}>
 | 
				
			||||||
 | 
								{subtitles && (
 | 
				
			||||||
				<Tooltip title={t("subtitles")}>
 | 
									<Tooltip title={t("subtitles")}>
 | 
				
			||||||
				<IconButton aria-label={t("subtitles")} sx={{ color: "white" }}>
 | 
										<IconButton
 | 
				
			||||||
 | 
											id="sortby"
 | 
				
			||||||
 | 
											aria-label={t("subtitles")}
 | 
				
			||||||
 | 
											aria-controls={subtitleAnchor ? "subtitle-menu" : undefined}
 | 
				
			||||||
 | 
											aria-haspopup="true"
 | 
				
			||||||
 | 
											aria-expanded={subtitleAnchor ? "true" : undefined}
 | 
				
			||||||
 | 
											onClick={(event) => setSubtitleAnchor(event.currentTarget)}
 | 
				
			||||||
 | 
											sx={{ color: "white" }}
 | 
				
			||||||
 | 
										>
 | 
				
			||||||
						<ClosedCaption />
 | 
											<ClosedCaption />
 | 
				
			||||||
					</IconButton>
 | 
										</IconButton>
 | 
				
			||||||
				</Tooltip>
 | 
									</Tooltip>
 | 
				
			||||||
 | 
								)}
 | 
				
			||||||
			<Tooltip title={t("fullscreen")}>
 | 
								<Tooltip title={t("fullscreen")}>
 | 
				
			||||||
				<IconButton onClick={toggleFullscreen} aria-label={t("fullscreen")} sx={{ color: "white" }}>
 | 
									<IconButton onClick={toggleFullscreen} aria-label={t("fullscreen")} sx={{ color: "white" }}>
 | 
				
			||||||
					{isFullscreen ? <FullscreenExit /> : <Fullscreen />}
 | 
										{isFullscreen ? <FullscreenExit /> : <Fullscreen />}
 | 
				
			||||||
				</IconButton>
 | 
									</IconButton>
 | 
				
			||||||
			</Tooltip>
 | 
								</Tooltip>
 | 
				
			||||||
 | 
								{subtitleAnchor && (
 | 
				
			||||||
 | 
									<SubtitleMenu
 | 
				
			||||||
 | 
										subtitles={subtitles!}
 | 
				
			||||||
 | 
										anchor={subtitleAnchor}
 | 
				
			||||||
 | 
										setSubtitle={selectSubtitle}
 | 
				
			||||||
 | 
										selectedID={selectedSubtitle?.id}
 | 
				
			||||||
 | 
										onClose={() => setSubtitleAnchor(null)}
 | 
				
			||||||
 | 
									/>
 | 
				
			||||||
 | 
								)}
 | 
				
			||||||
		</Box>
 | 
							</Box>
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
@ -356,6 +466,67 @@ const Back = memo(function Back({ name, href }: { name?: string; href: string })
 | 
				
			|||||||
	);
 | 
						);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const useSubtitleController = (player: RefObject<HTMLVideoElement>): [Track | null, (value: Track | null) => void] => {
 | 
				
			||||||
 | 
						const [selectedSubtitle, setSubtitle] = useState<Track | null>(null);
 | 
				
			||||||
 | 
						const [htmlTrack, setHtmlTrack] = useState<HTMLTrackElement | null>(null);
 | 
				
			||||||
 | 
						const [subocto, setSubOcto] = useState<SubtitleOctopus | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return [
 | 
				
			||||||
 | 
							selectedSubtitle,
 | 
				
			||||||
 | 
							useCallback(
 | 
				
			||||||
 | 
								(value: Track | null) => {
 | 
				
			||||||
 | 
									const removeHtmlSubtitle = () => {
 | 
				
			||||||
 | 
										if (htmlTrack) htmlTrack.remove();
 | 
				
			||||||
 | 
										setHtmlTrack(null);
 | 
				
			||||||
 | 
									};
 | 
				
			||||||
 | 
									const removeOctoSub = () => {
 | 
				
			||||||
 | 
										if (subocto) {
 | 
				
			||||||
 | 
											subocto.freeTrack();
 | 
				
			||||||
 | 
											subocto.dispose();
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										setSubOcto(null);
 | 
				
			||||||
 | 
									};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (!player.current) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									setSubtitle(value);
 | 
				
			||||||
 | 
									if (!value) {
 | 
				
			||||||
 | 
										removeHtmlSubtitle();
 | 
				
			||||||
 | 
										removeOctoSub();
 | 
				
			||||||
 | 
									} else if (value.codec === "vtt" || value.codec === "srt") {
 | 
				
			||||||
 | 
										removeOctoSub();
 | 
				
			||||||
 | 
										const track: HTMLTrackElement = htmlTrack ?? document.createElement("track");
 | 
				
			||||||
 | 
										track.kind = "subtitles";
 | 
				
			||||||
 | 
										track.label = value.displayName;
 | 
				
			||||||
 | 
										if (value.language) track.srclang = value.language;
 | 
				
			||||||
 | 
										track.src = `subtitle/${value.slug}.vtt`;
 | 
				
			||||||
 | 
										track.className = "subtitle_container";
 | 
				
			||||||
 | 
										track.default = true;
 | 
				
			||||||
 | 
										track.onload = () => {
 | 
				
			||||||
 | 
											if (player.current) player.current.textTracks[0].mode = "showing";
 | 
				
			||||||
 | 
										};
 | 
				
			||||||
 | 
										player.current.appendChild(track);
 | 
				
			||||||
 | 
										setHtmlTrack(track);
 | 
				
			||||||
 | 
									} else if (value.codec === "ass") {
 | 
				
			||||||
 | 
										removeHtmlSubtitle();
 | 
				
			||||||
 | 
										removeOctoSub();
 | 
				
			||||||
 | 
										setSubOcto(
 | 
				
			||||||
 | 
											new SubtitleOctopus({
 | 
				
			||||||
 | 
												video: player.current,
 | 
				
			||||||
 | 
												subUrl: `/api/subtitle/${value.slug}`,
 | 
				
			||||||
 | 
												workerUrl: "/_next/static/chunks/subtitles-octopus-worker.js",
 | 
				
			||||||
 | 
												legacyWorkerUrl: "/_next/static/chunks/subtitles-octopus-worker-legacy.js",
 | 
				
			||||||
 | 
												/* fonts:  */
 | 
				
			||||||
 | 
												renderMode: "wasm-blend",
 | 
				
			||||||
 | 
											}),
 | 
				
			||||||
 | 
										);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								[htmlTrack, subocto, player],
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
 | 
						];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const useVideoController = () => {
 | 
					const useVideoController = () => {
 | 
				
			||||||
	const player = useRef<HTMLVideoElement>(null);
 | 
						const player = useRef<HTMLVideoElement>(null);
 | 
				
			||||||
	const [isPlaying, setPlay] = useState(true);
 | 
						const [isPlaying, setPlay] = useState(true);
 | 
				
			||||||
@ -366,6 +537,7 @@ const useVideoController = () => {
 | 
				
			|||||||
	const [volume, setVolume] = useState(100);
 | 
						const [volume, setVolume] = useState(100);
 | 
				
			||||||
	const [isMuted, setMute] = useState(false);
 | 
						const [isMuted, setMute] = useState(false);
 | 
				
			||||||
	const [isFullscreen, setFullscreen] = useState(false);
 | 
						const [isFullscreen, setFullscreen] = useState(false);
 | 
				
			||||||
 | 
						const [selectedSubtitle, selectSubtitle] = useSubtitleController(player);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	useEffect(() => {
 | 
						useEffect(() => {
 | 
				
			||||||
		if (!player?.current?.duration) return;
 | 
							if (!player?.current?.duration) return;
 | 
				
			||||||
@ -373,7 +545,7 @@ const useVideoController = () => {
 | 
				
			|||||||
	}, [player]);
 | 
						}, [player]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const togglePlay = useCallback(() => {
 | 
						const togglePlay = useCallback(() => {
 | 
				
			||||||
		if (!player?.current) return;
 | 
							if (!player.current) return;
 | 
				
			||||||
		if (!isPlaying) {
 | 
							if (!isPlaying) {
 | 
				
			||||||
			player.current.play();
 | 
								player.current.play();
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
@ -390,7 +562,7 @@ const useVideoController = () => {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}, [isFullscreen]);
 | 
						}, [isFullscreen]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const videoProps: HTMLProps<HTMLVideoElement> = useMemo(
 | 
						const videoProps: BoxProps<"video"> = useMemo(
 | 
				
			||||||
		() => ({
 | 
							() => ({
 | 
				
			||||||
			ref: player,
 | 
								ref: player,
 | 
				
			||||||
			onClick: togglePlay,
 | 
								onClick: togglePlay,
 | 
				
			||||||
@ -418,7 +590,17 @@ const useVideoController = () => {
 | 
				
			|||||||
		[player, togglePlay, toggleFullscreen],
 | 
							[player, togglePlay, toggleFullscreen],
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
	return {
 | 
						return {
 | 
				
			||||||
		state: { isPlaying, isLoading, progress, duration, buffered, volume, isMuted, isFullscreen },
 | 
							state: {
 | 
				
			||||||
 | 
								isPlaying,
 | 
				
			||||||
 | 
								isLoading,
 | 
				
			||||||
 | 
								progress,
 | 
				
			||||||
 | 
								duration,
 | 
				
			||||||
 | 
								buffered,
 | 
				
			||||||
 | 
								volume,
 | 
				
			||||||
 | 
								isMuted,
 | 
				
			||||||
 | 
								isFullscreen,
 | 
				
			||||||
 | 
								selectedSubtitle,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		videoProps,
 | 
							videoProps,
 | 
				
			||||||
		togglePlay,
 | 
							togglePlay,
 | 
				
			||||||
		toggleMute: useCallback(() => {
 | 
							toggleMute: useCallback(() => {
 | 
				
			||||||
@ -439,6 +621,7 @@ const useVideoController = () => {
 | 
				
			|||||||
			},
 | 
								},
 | 
				
			||||||
			[player],
 | 
								[player],
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
							selectSubtitle,
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -447,19 +630,31 @@ const query = (slug: string): QueryIdentifier<WatchItem> => ({
 | 
				
			|||||||
	parser: WatchItemP,
 | 
						parser: WatchItemP,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//
 | 
					// Callback used to hide the controls when the mouse goes iddle. This is stored globally to clear the old timeout
 | 
				
			||||||
 | 
					// if the mouse moves again
 | 
				
			||||||
let mouseCallback: NodeJS.Timeout;
 | 
					let mouseCallback: NodeJS.Timeout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Player: QueryPage<{ slug: string }> = ({ slug }) => {
 | 
					const Player: QueryPage<{ slug: string }> = ({ slug }) => {
 | 
				
			||||||
	const { data, error } = useFetch(query(slug));
 | 
						const { data, error } = useFetch(query(slug));
 | 
				
			||||||
	const {
 | 
						const {
 | 
				
			||||||
		state: { isPlaying, isLoading, progress, duration, buffered, volume, isMuted, isFullscreen },
 | 
							state: {
 | 
				
			||||||
 | 
								isPlaying,
 | 
				
			||||||
 | 
								isLoading,
 | 
				
			||||||
 | 
								progress,
 | 
				
			||||||
 | 
								duration,
 | 
				
			||||||
 | 
								buffered,
 | 
				
			||||||
 | 
								volume,
 | 
				
			||||||
 | 
								isMuted,
 | 
				
			||||||
 | 
								isFullscreen,
 | 
				
			||||||
 | 
								selectedSubtitle,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		videoProps,
 | 
							videoProps,
 | 
				
			||||||
		togglePlay,
 | 
							togglePlay,
 | 
				
			||||||
		toggleMute,
 | 
							toggleMute,
 | 
				
			||||||
		toggleFullscreen,
 | 
							toggleFullscreen,
 | 
				
			||||||
		setProgress,
 | 
							setProgress,
 | 
				
			||||||
		setVolume,
 | 
							setVolume,
 | 
				
			||||||
 | 
							selectSubtitle,
 | 
				
			||||||
	} = useVideoController();
 | 
						} = useVideoController();
 | 
				
			||||||
	const [showHover, setHover] = useState(false);
 | 
						const [showHover, setHover] = useState(false);
 | 
				
			||||||
	const [mouseMoved, setMouseMoved] = useState(false);
 | 
						const [mouseMoved, setMouseMoved] = useState(false);
 | 
				
			||||||
@ -494,7 +689,7 @@ const Player: QueryPage<{ slug: string }> = ({ slug }) => {
 | 
				
			|||||||
			<Box
 | 
								<Box
 | 
				
			||||||
				component="video"
 | 
									component="video"
 | 
				
			||||||
				src={data?.link.direct}
 | 
									src={data?.link.direct}
 | 
				
			||||||
				{...(videoProps as any)}
 | 
									{...videoProps}
 | 
				
			||||||
				sx={{
 | 
									sx={{
 | 
				
			||||||
					position: "absolute",
 | 
										position: "absolute",
 | 
				
			||||||
					top: 0,
 | 
										top: 0,
 | 
				
			||||||
@ -511,15 +706,19 @@ const Player: QueryPage<{ slug: string }> = ({ slug }) => {
 | 
				
			|||||||
			<Box
 | 
								<Box
 | 
				
			||||||
				onMouseEnter={() => setHover(true)}
 | 
									onMouseEnter={() => setHover(true)}
 | 
				
			||||||
				onMouseLeave={() => setHover(false)}
 | 
									onMouseLeave={() => setHover(false)}
 | 
				
			||||||
				sx={ displayControls ? {
 | 
									sx={
 | 
				
			||||||
 | 
										displayControls
 | 
				
			||||||
 | 
											? {
 | 
				
			||||||
								visibility: "visible",
 | 
													visibility: "visible",
 | 
				
			||||||
								opacity: 1,
 | 
													opacity: 1,
 | 
				
			||||||
								transition: "opacity .2s ease-in",
 | 
													transition: "opacity .2s ease-in",
 | 
				
			||||||
				} : {
 | 
											  }
 | 
				
			||||||
 | 
											: {
 | 
				
			||||||
								visibility: "hidden",
 | 
													visibility: "hidden",
 | 
				
			||||||
								opacity: 0,
 | 
													opacity: 0,
 | 
				
			||||||
								transition: "opacity .4s ease-out, visibility 0s .4s",
 | 
													transition: "opacity .4s ease-out, visibility 0s .4s",
 | 
				
			||||||
				}}
 | 
											  }
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			>
 | 
								>
 | 
				
			||||||
				<Back
 | 
									<Back
 | 
				
			||||||
					name={data?.name}
 | 
										name={data?.name}
 | 
				
			||||||
@ -566,7 +765,13 @@ const Player: QueryPage<{ slug: string }> = ({ slug }) => {
 | 
				
			|||||||
									{toTimerString(progress, duration)} : {toTimerString(duration)}
 | 
														{toTimerString(progress, duration)} : {toTimerString(duration)}
 | 
				
			||||||
								</Typography>
 | 
													</Typography>
 | 
				
			||||||
							</Box>
 | 
												</Box>
 | 
				
			||||||
							<RightButtons isFullscreen={isFullscreen} toggleFullscreen={toggleFullscreen} />
 | 
												<RightButtons
 | 
				
			||||||
 | 
													isFullscreen={isFullscreen}
 | 
				
			||||||
 | 
													toggleFullscreen={toggleFullscreen}
 | 
				
			||||||
 | 
													subtitles={data?.subtitles}
 | 
				
			||||||
 | 
													selectedSubtitle={selectedSubtitle}
 | 
				
			||||||
 | 
													selectSubtitle={selectSubtitle}
 | 
				
			||||||
 | 
												/>
 | 
				
			||||||
						</Box>
 | 
											</Box>
 | 
				
			||||||
					</Box>
 | 
										</Box>
 | 
				
			||||||
				</Box>
 | 
									</Box>
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										117
									
								
								front/yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										117
									
								
								front/yarn.lock
									
									
									
									
									
								
							@ -195,6 +195,11 @@
 | 
				
			|||||||
  resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
 | 
					  resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
 | 
				
			||||||
  integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
 | 
					  integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"@jellyfin/libass-wasm@^4.1.1":
 | 
				
			||||||
 | 
					  version "4.1.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/@jellyfin/libass-wasm/-/libass-wasm-4.1.1.tgz#d1c0e789844e1ad5d3b36acaeb7351e59f5b7d9a"
 | 
				
			||||||
 | 
					  integrity sha512-xQVJw+lZUg4U1TmLS80reBECfPtpCgRF8hhUSvUUQM9g68OvINyUU3K2yqRH+8tomGpghiRaIcr/bUJ83e0veA==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"@mui/base@5.0.0-alpha.88":
 | 
					"@mui/base@5.0.0-alpha.88":
 | 
				
			||||||
  version "5.0.0-alpha.88"
 | 
					  version "5.0.0-alpha.88"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.88.tgz#0930d1849c74ba62a28ab2d8533de88764173ba4"
 | 
					  resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.88.tgz#0930d1849c74ba62a28ab2d8533de88764173ba4"
 | 
				
			||||||
@ -404,6 +409,11 @@
 | 
				
			|||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    "@types/ms" "*"
 | 
					    "@types/ms" "*"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"@types/json-schema@^7.0.9":
 | 
				
			||||||
 | 
					  version "7.0.11"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
 | 
				
			||||||
 | 
					  integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"@types/json5@^0.0.29":
 | 
					"@types/json5@^0.0.29":
 | 
				
			||||||
  version "0.0.29"
 | 
					  version "0.0.29"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
 | 
					  resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
 | 
				
			||||||
@ -535,6 +545,20 @@ acorn@^8.7.1:
 | 
				
			|||||||
  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
 | 
					  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
 | 
				
			||||||
  integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
 | 
					  integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ajv-formats@^2.1.1:
 | 
				
			||||||
 | 
					  version "2.1.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520"
 | 
				
			||||||
 | 
					  integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    ajv "^8.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ajv-keywords@^5.0.0:
 | 
				
			||||||
 | 
					  version "5.1.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16"
 | 
				
			||||||
 | 
					  integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    fast-deep-equal "^3.1.3"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ajv@^6.10.0, ajv@^6.12.4:
 | 
					ajv@^6.10.0, ajv@^6.12.4:
 | 
				
			||||||
  version "6.12.6"
 | 
					  version "6.12.6"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
 | 
					  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
 | 
				
			||||||
@ -545,6 +569,16 @@ ajv@^6.10.0, ajv@^6.12.4:
 | 
				
			|||||||
    json-schema-traverse "^0.4.1"
 | 
					    json-schema-traverse "^0.4.1"
 | 
				
			||||||
    uri-js "^4.2.2"
 | 
					    uri-js "^4.2.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ajv@^8.0.0, ajv@^8.8.0:
 | 
				
			||||||
 | 
					  version "8.11.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f"
 | 
				
			||||||
 | 
					  integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    fast-deep-equal "^3.1.1"
 | 
				
			||||||
 | 
					    json-schema-traverse "^1.0.0"
 | 
				
			||||||
 | 
					    require-from-string "^2.0.2"
 | 
				
			||||||
 | 
					    uri-js "^4.2.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ansi-regex@^5.0.1:
 | 
					ansi-regex@^5.0.1:
 | 
				
			||||||
  version "5.0.1"
 | 
					  version "5.0.1"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
 | 
					  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
 | 
				
			||||||
@ -774,6 +808,18 @@ copy-anything@^3.0.2:
 | 
				
			|||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    is-what "^4.1.6"
 | 
					    is-what "^4.1.6"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					copy-webpack-plugin@^11.0.0:
 | 
				
			||||||
 | 
					  version "11.0.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz#96d4dbdb5f73d02dd72d0528d1958721ab72e04a"
 | 
				
			||||||
 | 
					  integrity sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    fast-glob "^3.2.11"
 | 
				
			||||||
 | 
					    glob-parent "^6.0.1"
 | 
				
			||||||
 | 
					    globby "^13.1.1"
 | 
				
			||||||
 | 
					    normalize-path "^3.0.0"
 | 
				
			||||||
 | 
					    schema-utils "^4.0.0"
 | 
				
			||||||
 | 
					    serialize-javascript "^6.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
core-js-pure@^3.20.2:
 | 
					core-js-pure@^3.20.2:
 | 
				
			||||||
  version "3.23.4"
 | 
					  version "3.23.4"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.23.4.tgz#aba5c7fb297063444f6bf93afb0362151679a012"
 | 
					  resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.23.4.tgz#aba5c7fb297063444f6bf93afb0362151679a012"
 | 
				
			||||||
@ -1180,6 +1226,17 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
 | 
				
			|||||||
  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
 | 
					  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
 | 
				
			||||||
  integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
 | 
					  integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fast-glob@^3.2.11:
 | 
				
			||||||
 | 
					  version "3.2.12"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80"
 | 
				
			||||||
 | 
					  integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    "@nodelib/fs.stat" "^2.0.2"
 | 
				
			||||||
 | 
					    "@nodelib/fs.walk" "^1.2.3"
 | 
				
			||||||
 | 
					    glob-parent "^5.1.2"
 | 
				
			||||||
 | 
					    merge2 "^1.3.0"
 | 
				
			||||||
 | 
					    micromatch "^4.0.4"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fast-glob@^3.2.9:
 | 
					fast-glob@^3.2.9:
 | 
				
			||||||
  version "3.2.11"
 | 
					  version "3.2.11"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
 | 
					  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
 | 
				
			||||||
@ -1351,6 +1408,17 @@ globby@^11.1.0:
 | 
				
			|||||||
    merge2 "^1.4.1"
 | 
					    merge2 "^1.4.1"
 | 
				
			||||||
    slash "^3.0.0"
 | 
					    slash "^3.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					globby@^13.1.1:
 | 
				
			||||||
 | 
					  version "13.1.2"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.2.tgz#29047105582427ab6eca4f905200667b056da515"
 | 
				
			||||||
 | 
					  integrity sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    dir-glob "^3.0.1"
 | 
				
			||||||
 | 
					    fast-glob "^3.2.11"
 | 
				
			||||||
 | 
					    ignore "^5.2.0"
 | 
				
			||||||
 | 
					    merge2 "^1.4.1"
 | 
				
			||||||
 | 
					    slash "^4.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
has-bigints@^1.0.1, has-bigints@^1.0.2:
 | 
					has-bigints@^1.0.1, has-bigints@^1.0.2:
 | 
				
			||||||
  version "1.0.2"
 | 
					  version "1.0.2"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
 | 
					  resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
 | 
				
			||||||
@ -1580,6 +1648,11 @@ json-schema-traverse@^0.4.1:
 | 
				
			|||||||
  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
 | 
					  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
 | 
				
			||||||
  integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
 | 
					  integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					json-schema-traverse@^1.0.0:
 | 
				
			||||||
 | 
					  version "1.0.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
 | 
				
			||||||
 | 
					  integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
json-stable-stringify-without-jsonify@^1.0.1:
 | 
					json-stable-stringify-without-jsonify@^1.0.1:
 | 
				
			||||||
  version "1.0.1"
 | 
					  version "1.0.1"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
 | 
					  resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
 | 
				
			||||||
@ -1981,6 +2054,11 @@ next@12.2.2:
 | 
				
			|||||||
    "@next/swc-win32-ia32-msvc" "12.2.2"
 | 
					    "@next/swc-win32-ia32-msvc" "12.2.2"
 | 
				
			||||||
    "@next/swc-win32-x64-msvc" "12.2.2"
 | 
					    "@next/swc-win32-x64-msvc" "12.2.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					normalize-path@^3.0.0:
 | 
				
			||||||
 | 
					  version "3.0.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
 | 
				
			||||||
 | 
					  integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
object-assign@^4.1.1:
 | 
					object-assign@^4.1.1:
 | 
				
			||||||
  version "4.1.1"
 | 
					  version "4.1.1"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
 | 
					  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
 | 
				
			||||||
@ -2183,6 +2261,13 @@ queue-microtask@^1.2.2:
 | 
				
			|||||||
  resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
 | 
					  resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
 | 
				
			||||||
  integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
 | 
					  integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					randombytes@^2.1.0:
 | 
				
			||||||
 | 
					  version "2.1.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
 | 
				
			||||||
 | 
					  integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    safe-buffer "^5.1.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
react-dom@18.2.0:
 | 
					react-dom@18.2.0:
 | 
				
			||||||
  version "18.2.0"
 | 
					  version "18.2.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
 | 
					  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
 | 
				
			||||||
@ -2260,6 +2345,11 @@ remove-accents@0.4.2:
 | 
				
			|||||||
  resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5"
 | 
					  resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5"
 | 
				
			||||||
  integrity sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==
 | 
					  integrity sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require-from-string@^2.0.2:
 | 
				
			||||||
 | 
					  version "2.0.2"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
 | 
				
			||||||
 | 
					  integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
resolve-from@^4.0.0:
 | 
					resolve-from@^4.0.0:
 | 
				
			||||||
  version "4.0.0"
 | 
					  version "4.0.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
 | 
					  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
 | 
				
			||||||
@ -2309,6 +2399,11 @@ sade@^1.7.3:
 | 
				
			|||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    mri "^1.1.0"
 | 
					    mri "^1.1.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					safe-buffer@^5.1.0:
 | 
				
			||||||
 | 
					  version "5.2.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
 | 
				
			||||||
 | 
					  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
safe-buffer@~5.1.1:
 | 
					safe-buffer@~5.1.1:
 | 
				
			||||||
  version "5.1.2"
 | 
					  version "5.1.2"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
 | 
					  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
 | 
				
			||||||
@ -2321,6 +2416,16 @@ scheduler@^0.23.0:
 | 
				
			|||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    loose-envify "^1.1.0"
 | 
					    loose-envify "^1.1.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					schema-utils@^4.0.0:
 | 
				
			||||||
 | 
					  version "4.0.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7"
 | 
				
			||||||
 | 
					  integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    "@types/json-schema" "^7.0.9"
 | 
				
			||||||
 | 
					    ajv "^8.8.0"
 | 
				
			||||||
 | 
					    ajv-formats "^2.1.1"
 | 
				
			||||||
 | 
					    ajv-keywords "^5.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
semver@^6.3.0:
 | 
					semver@^6.3.0:
 | 
				
			||||||
  version "6.3.0"
 | 
					  version "6.3.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
 | 
					  resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
 | 
				
			||||||
@ -2333,6 +2438,13 @@ semver@^7.3.7:
 | 
				
			|||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    lru-cache "^6.0.0"
 | 
					    lru-cache "^6.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					serialize-javascript@^6.0.0:
 | 
				
			||||||
 | 
					  version "6.0.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
 | 
				
			||||||
 | 
					  integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    randombytes "^2.1.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
shebang-command@^2.0.0:
 | 
					shebang-command@^2.0.0:
 | 
				
			||||||
  version "2.0.0"
 | 
					  version "2.0.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
 | 
					  resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
 | 
				
			||||||
@ -2359,6 +2471,11 @@ slash@^3.0.0:
 | 
				
			|||||||
  resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
 | 
					  resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
 | 
				
			||||||
  integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
 | 
					  integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					slash@^4.0.0:
 | 
				
			||||||
 | 
					  version "4.0.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7"
 | 
				
			||||||
 | 
					  integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
source-map-js@^1.0.1:
 | 
					source-map-js@^1.0.1:
 | 
				
			||||||
  version "1.0.2"
 | 
					  version "1.0.2"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
 | 
					  resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user