mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-21 14:47:41 +01:00
commit
1c77807987
25 changed files with 322 additions and 72 deletions
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
* text=auto eol=lf
|
3
.github/workflows/deploying.yml
vendored
3
.github/workflows/deploying.yml
vendored
|
@ -18,12 +18,13 @@ jobs:
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
|
cache: 'yarn'
|
||||||
|
|
||||||
- name: Install Yarn packages
|
- name: Install Yarn packages
|
||||||
run: yarn install
|
run: yarn install
|
||||||
|
|
||||||
- name: Build project
|
- name: Build project
|
||||||
run: npm run build
|
run: yarn build
|
||||||
|
|
||||||
- name: Upload production-ready build files
|
- name: Upload production-ready build files
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
|
|
48
.github/workflows/linting_annotate.yml
vendored
Normal file
48
.github/workflows/linting_annotate.yml
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
name: Annotate linting
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
actions: read # download artifact
|
||||||
|
checks: write # annotate
|
||||||
|
|
||||||
|
# this is done as a seperate workflow so
|
||||||
|
# the annotater has access to write to checks (to annotate)
|
||||||
|
on:
|
||||||
|
workflow_run:
|
||||||
|
workflows: ["Linting and Testing"]
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
annotate:
|
||||||
|
name: Annotate linting
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Download linting report
|
||||||
|
uses: actions/github-script@v6
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
run_id: ${{github.event.workflow_run.id }},
|
||||||
|
});
|
||||||
|
const matchArtifact = artifacts.data.artifacts.filter((artifact) => {
|
||||||
|
return artifact.name == "eslint_report.json"
|
||||||
|
})[0];
|
||||||
|
const download = await github.rest.actions.downloadArtifact({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
artifact_id: matchArtifact.id,
|
||||||
|
archive_format: 'zip',
|
||||||
|
});
|
||||||
|
const fs = require('fs');
|
||||||
|
fs.writeFileSync('${{github.workspace}}/eslint_report.zip', Buffer.from(download.data));
|
||||||
|
|
||||||
|
- run: unzip eslint_report.zip
|
||||||
|
|
||||||
|
- name: Annotate linting
|
||||||
|
uses: ataylorme/eslint-annotate-action@v2
|
||||||
|
with:
|
||||||
|
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
report-json: "eslint_report.json"
|
30
.github/workflows/linting_testing.yml
vendored
30
.github/workflows/linting_testing.yml
vendored
|
@ -5,8 +5,7 @@ on:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- dev
|
- dev
|
||||||
pull_request_target:
|
pull_request:
|
||||||
types: [opened, reopened, synchronize]
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
linting:
|
linting:
|
||||||
|
@ -21,6 +20,7 @@ jobs:
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
|
cache: 'yarn'
|
||||||
|
|
||||||
- name: Install Yarn packages
|
- name: Install Yarn packages
|
||||||
run: yarn install
|
run: yarn install
|
||||||
|
@ -30,11 +30,27 @@ jobs:
|
||||||
# continue on error, so it still reports it in the next step
|
# continue on error, so it still reports it in the next step
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
- name: Annotate Code Linting Results
|
- uses: actions/upload-artifact@v3
|
||||||
uses: ataylorme/eslint-annotate-action@v2
|
|
||||||
with:
|
with:
|
||||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
name: eslint_report.json
|
||||||
report-json: "eslint_report.json"
|
path: eslint_report.json
|
||||||
|
|
||||||
|
building:
|
||||||
|
name: Build project
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
cache: 'yarn'
|
||||||
|
|
||||||
|
- name: Install Yarn packages
|
||||||
|
run: yarn install
|
||||||
|
|
||||||
- name: Build Project
|
- name: Build Project
|
||||||
run: npm run build
|
run: yarn build
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "movie-web",
|
"name": "movie-web",
|
||||||
"version": "3.0.4",
|
"version": "3.0.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://movie.squeezebox.dev",
|
"homepage": "https://movie.squeezebox.dev",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
5
public/_headers
Normal file
5
public/_headers
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
/*
|
||||||
|
X-Frame-Options: DENY
|
||||||
|
X-XSS-Protection: 1; mode=block
|
||||||
|
X-Content-Type-Options: nosniff
|
||||||
|
Referrer-Policy: origin-when-cross-origin
|
|
@ -1,7 +1,6 @@
|
||||||
window.__CONFIG__ = {
|
window.__CONFIG__ = {
|
||||||
// url must NOT end with a slash
|
// url must NOT end with a slash
|
||||||
VITE_CORS_PROXY_URL: "",
|
VITE_CORS_PROXY_URL: "",
|
||||||
|
|
||||||
VITE_TMDB_API_KEY: "b030404650f279792a8d3287232358e3",
|
VITE_TMDB_API_KEY: "b030404650f279792a8d3287232358e3",
|
||||||
VITE_OMDB_API_KEY: "aa0937c0",
|
VITE_OMDB_API_KEY: "aa0937c0",
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { mwFetch, proxiedFetch } from "@/backend/helpers/fetch";
|
||||||
import { MWCaption, MWCaptionType } from "@/backend/helpers/streams";
|
import { MWCaption, MWCaptionType } from "@/backend/helpers/streams";
|
||||||
import toWebVTT from "srt-webvtt";
|
import toWebVTT from "srt-webvtt";
|
||||||
|
|
||||||
|
export const CUSTOM_CAPTION_ID = "customCaption";
|
||||||
export async function getCaptionUrl(caption: MWCaption): Promise<string> {
|
export async function getCaptionUrl(caption: MWCaption): Promise<string> {
|
||||||
if (caption.type === MWCaptionType.SRT) {
|
if (caption.type === MWCaptionType.SRT) {
|
||||||
let captionBlob: Blob;
|
let captionBlob: Blob;
|
||||||
|
@ -32,3 +33,18 @@ export async function getCaptionUrl(caption: MWCaption): Promise<string> {
|
||||||
|
|
||||||
throw new Error("invalid type");
|
throw new Error("invalid type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function convertCustomCaptionFileToWebVTT(file: File) {
|
||||||
|
const header = await file.slice(0, 6).text();
|
||||||
|
const isWebVTT = header === "WEBVTT";
|
||||||
|
if (!isWebVTT) {
|
||||||
|
return toWebVTT(file);
|
||||||
|
}
|
||||||
|
return URL.createObjectURL(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function revokeCaptionBlob(url: string | undefined) {
|
||||||
|
if (url && url.startsWith("blob:")) {
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -54,12 +54,17 @@ export async function getMetaFromId(
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
const imdbId = data.external_ids.find(
|
let imdbId = data.external_ids.find(
|
||||||
(v) => v.provider === "imdb_latest"
|
(v) => v.provider === "imdb_latest"
|
||||||
)?.external_id;
|
)?.external_id;
|
||||||
const tmdbId = data.external_ids.find(
|
if (!imdbId)
|
||||||
|
imdbId = data.external_ids.find((v) => v.provider === "imdb")?.external_id;
|
||||||
|
|
||||||
|
let tmdbId = data.external_ids.find(
|
||||||
(v) => v.provider === "tmdb_latest"
|
(v) => v.provider === "tmdb_latest"
|
||||||
)?.external_id;
|
)?.external_id;
|
||||||
|
if (!tmdbId)
|
||||||
|
tmdbId = data.external_ids.find((v) => v.provider === "tmdb")?.external_id;
|
||||||
|
|
||||||
if (!imdbId || !tmdbId) throw new Error("not enough info");
|
if (!imdbId || !tmdbId) throw new Error("not enough info");
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import { proxiedFetch } from "../helpers/fetch";
|
import { proxiedFetch } from "../helpers/fetch";
|
||||||
import { registerProvider } from "../helpers/register";
|
import { registerProvider } from "../helpers/register";
|
||||||
import { MWStreamQuality, MWStreamType } from "../helpers/streams";
|
import {
|
||||||
|
MWCaptionType,
|
||||||
|
MWStreamQuality,
|
||||||
|
MWStreamType,
|
||||||
|
} from "../helpers/streams";
|
||||||
import { MWMediaType } from "../metadata/types";
|
import { MWMediaType } from "../metadata/types";
|
||||||
|
|
||||||
const netfilmBase = "https://net-film.vercel.app";
|
const netfilmBase = "https://net-film.vercel.app";
|
||||||
|
@ -18,7 +22,6 @@ registerProvider({
|
||||||
displayName: "NetFilm",
|
displayName: "NetFilm",
|
||||||
rank: 15,
|
rank: 15,
|
||||||
type: [MWMediaType.MOVIE, MWMediaType.SERIES],
|
type: [MWMediaType.MOVIE, MWMediaType.SERIES],
|
||||||
disabled: true, // https://github.com/lamhoang1256/netfilm/issues/25
|
|
||||||
|
|
||||||
async scrape({ media, episode, progress }) {
|
async scrape({ media, episode, progress }) {
|
||||||
// search for relevant item
|
// search for relevant item
|
||||||
|
@ -48,20 +51,29 @@ registerProvider({
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const { qualities } = watchInfo.data;
|
const data = watchInfo.data;
|
||||||
|
|
||||||
// get best quality source
|
// get best quality source
|
||||||
const source = qualities.reduce((p: any, c: any) =>
|
const source = data.qualities.reduce((p: any, c: any) =>
|
||||||
c.quality > p.quality ? c : p
|
c.quality > p.quality ? c : p
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const mappedCaptions = data.subtitles.map((sub: Record<string, any>) => ({
|
||||||
|
needsProxy: false,
|
||||||
|
url: sub.url.replace("https://convert-srt-to-vtt.vercel.app/?url=", ""),
|
||||||
|
type: MWCaptionType.SRT,
|
||||||
|
langIso: sub.language,
|
||||||
|
}));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
embeds: [],
|
embeds: [],
|
||||||
stream: {
|
stream: {
|
||||||
streamUrl: source.url,
|
streamUrl: source.url
|
||||||
|
.replace("akm-cdn", "aws-cdn")
|
||||||
|
.replace("gg-cdn", "aws-cdn"),
|
||||||
quality: qualityMap[source.quality as QualityInMap],
|
quality: qualityMap[source.quality as QualityInMap],
|
||||||
type: MWStreamType.HLS,
|
type: MWStreamType.HLS,
|
||||||
captions: [],
|
captions: mappedCaptions,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -109,20 +121,29 @@ registerProvider({
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const { qualities } = episodeStream.data;
|
const data = episodeStream.data;
|
||||||
|
|
||||||
// get best quality source
|
// get best quality source
|
||||||
const source = qualities.reduce((p: any, c: any) =>
|
const source = data.qualities.reduce((p: any, c: any) =>
|
||||||
c.quality > p.quality ? c : p
|
c.quality > p.quality ? c : p
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const mappedCaptions = data.subtitles.map((sub: Record<string, any>) => ({
|
||||||
|
needsProxy: false,
|
||||||
|
url: sub.url.replace("https://convert-srt-to-vtt.vercel.app/?url=", ""),
|
||||||
|
type: MWCaptionType.SRT,
|
||||||
|
langIso: sub.language,
|
||||||
|
}));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
embeds: [],
|
embeds: [],
|
||||||
stream: {
|
stream: {
|
||||||
streamUrl: source.url,
|
streamUrl: source.url
|
||||||
|
.replace("akm-cdn", "aws-cdn")
|
||||||
|
.replace("gg-cdn", "aws-cdn"),
|
||||||
quality: qualityMap[source.quality as QualityInMap],
|
quality: qualityMap[source.quality as QualityInMap],
|
||||||
type: MWStreamType.HLS,
|
type: MWStreamType.HLS,
|
||||||
captions: [],
|
captions: mappedCaptions,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -36,6 +36,7 @@ export enum Icons {
|
||||||
CASTING = "casting",
|
CASTING = "casting",
|
||||||
CIRCLE_EXCLAMATION = "circle_exclamation",
|
CIRCLE_EXCLAMATION = "circle_exclamation",
|
||||||
DOWNLOAD = "download",
|
DOWNLOAD = "download",
|
||||||
|
PICTURE_IN_PICTURE = "pictureInPicture",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IconProps {
|
export interface IconProps {
|
||||||
|
@ -79,6 +80,7 @@ const iconList: Record<Icons, string> = {
|
||||||
circle_exclamation: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24V264c0 13.3-10.7 24-24 24s-24-10.7-24-24V152c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>`,
|
circle_exclamation: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24V264c0 13.3-10.7 24-24 24s-24-10.7-24-24V152c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>`,
|
||||||
casting: "",
|
casting: "",
|
||||||
download: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-download"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>`,
|
download: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-download"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>`,
|
||||||
|
pictureInPicture: `<svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" fill="currentColor" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98V5c0-1.1-.9-2-2-2zm0 16.01H3V4.98h18v14.03z"/></svg>`,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ChromeCastButton() {
|
function ChromeCastButton() {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export const DISCORD_LINK = "https://discord.gg/Jhqt4Xzpfb";
|
export const DISCORD_LINK = "https://discord.gg/Jhqt4Xzpfb";
|
||||||
export const GITHUB_LINK = "https://github.com/movie-web/movie-web";
|
export const GITHUB_LINK = "https://github.com/movie-web/movie-web";
|
||||||
export const APP_VERSION = "3.0.4";
|
export const APP_VERSION = "3.0.5";
|
||||||
export const GA_ID = "G-44YVXRL61C";
|
export const GA_ID = "G-44YVXRL61C";
|
||||||
|
|
|
@ -4,12 +4,13 @@
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
@apply bg-denim-100 text-denim-700 font-open-sans overflow-x-hidden;
|
@apply bg-denim-100 font-open-sans text-denim-700 overflow-x-hidden;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
min-height: 100dvh;
|
min-height: 100dvh;
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-full], html[data-full] body {
|
html[data-full],
|
||||||
|
html[data-full] body {
|
||||||
overscroll-behavior-y: none;
|
overscroll-behavior-y: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,8 @@
|
||||||
"episodes": "Episodes",
|
"episodes": "Episodes",
|
||||||
"source": "Source",
|
"source": "Source",
|
||||||
"captions": "Captions",
|
"captions": "Captions",
|
||||||
"download": "Download"
|
"download": "Download",
|
||||||
|
"pictureInPicture": "Picture in Picture"
|
||||||
},
|
},
|
||||||
"popouts": {
|
"popouts": {
|
||||||
"sources": "Sources",
|
"sources": "Sources",
|
||||||
|
@ -70,6 +71,8 @@
|
||||||
"episode": "E{{index}} - {{title}}",
|
"episode": "E{{index}} - {{title}}",
|
||||||
"noCaptions": "No captions",
|
"noCaptions": "No captions",
|
||||||
"linkedCaptions": "Linked captions",
|
"linkedCaptions": "Linked captions",
|
||||||
|
"customCaption": "Custom caption",
|
||||||
|
"uploadCustomCaption": "Upload caption (SRT, VTT)",
|
||||||
"noEmbeds": "No embeds were found for this source",
|
"noEmbeds": "No embeds were found for this source",
|
||||||
"errors": {
|
"errors": {
|
||||||
"loadingWentWong": "Something went wrong loading the episodes for {{seasonTitle}}",
|
"loadingWentWong": "Something went wrong loading the episodes for {{seasonTitle}}",
|
||||||
|
|
|
@ -38,3 +38,11 @@ export function canWebkitFullscreen(): boolean {
|
||||||
export function canFullscreen(): boolean {
|
export function canFullscreen(): boolean {
|
||||||
return canFullscreenAnyElement() || canWebkitFullscreen();
|
return canFullscreenAnyElement() || canWebkitFullscreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function canPictureInPicture(): boolean {
|
||||||
|
return "pictureInPictureEnabled" in document;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canWebkitPictureInPicture(): boolean {
|
||||||
|
return "webkitSupportsPresentationMode" in document.createElement("video");
|
||||||
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ import { PopoutProviderAction } from "@/video/components/popouts/PopoutProviderA
|
||||||
import { ChromecastAction } from "@/video/components/actions/ChromecastAction";
|
import { ChromecastAction } from "@/video/components/actions/ChromecastAction";
|
||||||
import { CastingTextAction } from "@/video/components/actions/CastingTextAction";
|
import { CastingTextAction } from "@/video/components/actions/CastingTextAction";
|
||||||
import { DownloadAction } from "@/video/components/actions/DownloadAction";
|
import { DownloadAction } from "@/video/components/actions/DownloadAction";
|
||||||
|
import { PictureInPictureAction } from "@/video/components/actions/PictureInPictureAction";
|
||||||
|
|
||||||
type Props = VideoPlayerBaseProps;
|
type Props = VideoPlayerBaseProps;
|
||||||
|
|
||||||
|
@ -144,6 +145,7 @@ export function VideoPlayer(props: Props) {
|
||||||
<div />
|
<div />
|
||||||
<div className="flex items-center justify-center">
|
<div className="flex items-center justify-center">
|
||||||
<DownloadAction />
|
<DownloadAction />
|
||||||
|
<PictureInPictureAction />
|
||||||
<CaptionsSelectionAction />
|
<CaptionsSelectionAction />
|
||||||
<SeriesSelectionAction />
|
<SeriesSelectionAction />
|
||||||
<SourceSelectionAction />
|
<SourceSelectionAction />
|
||||||
|
@ -161,6 +163,7 @@ export function VideoPlayer(props: Props) {
|
||||||
<ChromecastAction />
|
<ChromecastAction />
|
||||||
<AirplayAction />
|
<AirplayAction />
|
||||||
<DownloadAction />
|
<DownloadAction />
|
||||||
|
<PictureInPictureAction />
|
||||||
<CaptionsSelectionAction />
|
<CaptionsSelectionAction />
|
||||||
<FullscreenAction />
|
<FullscreenAction />
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -28,7 +28,7 @@ export function DownloadAction(props: Props) {
|
||||||
href={isHLS ? undefined : sourceInterface.source?.url}
|
href={isHLS ? undefined : sourceInterface.source?.url}
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
download={title ? normalizeTitle(title) : undefined}
|
download={title ? `${normalizeTitle(title)}.mp4` : undefined}
|
||||||
>
|
>
|
||||||
<VideoPlayerIconButton
|
<VideoPlayerIconButton
|
||||||
className={props.className}
|
className={props.className}
|
||||||
|
|
40
src/video/components/actions/PictureInPictureAction.tsx
Normal file
40
src/video/components/actions/PictureInPictureAction.tsx
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { Icons } from "@/components/Icon";
|
||||||
|
import { useIsMobile } from "@/hooks/useIsMobile";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useControls } from "@/video/state/logic/controls";
|
||||||
|
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||||
|
import { useCallback } from "react";
|
||||||
|
import {
|
||||||
|
canPictureInPicture,
|
||||||
|
canWebkitPictureInPicture,
|
||||||
|
} from "@/utils/detectFeatures";
|
||||||
|
import { VideoPlayerIconButton } from "../parts/VideoPlayerIconButton";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PictureInPictureAction(props: Props) {
|
||||||
|
const { isMobile } = useIsMobile();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const descriptor = useVideoPlayerDescriptor();
|
||||||
|
const controls = useControls(descriptor);
|
||||||
|
|
||||||
|
const handleClick = useCallback(() => {
|
||||||
|
controls.togglePictureInPicture();
|
||||||
|
}, [controls]);
|
||||||
|
|
||||||
|
if (!canPictureInPicture() && !canWebkitPictureInPicture()) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VideoPlayerIconButton
|
||||||
|
className={props.className}
|
||||||
|
icon={Icons.PICTURE_IN_PICTURE}
|
||||||
|
onClick={handleClick}
|
||||||
|
text={
|
||||||
|
isMobile ? (t("videoPlayer.buttons.pictureInPicture") as string) : ""
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,4 +1,8 @@
|
||||||
import { getCaptionUrl } from "@/backend/helpers/captions";
|
import {
|
||||||
|
getCaptionUrl,
|
||||||
|
convertCustomCaptionFileToWebVTT,
|
||||||
|
CUSTOM_CAPTION_ID,
|
||||||
|
} from "@/backend/helpers/captions";
|
||||||
import { MWCaption } from "@/backend/helpers/streams";
|
import { MWCaption } from "@/backend/helpers/streams";
|
||||||
import { Icon, Icons } from "@/components/Icon";
|
import { Icon, Icons } from "@/components/Icon";
|
||||||
import { useLoading } from "@/hooks/useLoading";
|
import { useLoading } from "@/hooks/useLoading";
|
||||||
|
@ -6,7 +10,7 @@ import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||||
import { useControls } from "@/video/state/logic/controls";
|
import { useControls } from "@/video/state/logic/controls";
|
||||||
import { useMeta } from "@/video/state/logic/meta";
|
import { useMeta } from "@/video/state/logic/meta";
|
||||||
import { useSource } from "@/video/state/logic/source";
|
import { useSource } from "@/video/state/logic/source";
|
||||||
import { useMemo, useRef } from "react";
|
import { ChangeEvent, useMemo, useRef } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { PopoutListEntry, PopoutSection } from "./PopoutUtils";
|
import { PopoutListEntry, PopoutSection } from "./PopoutUtils";
|
||||||
|
|
||||||
|
@ -37,6 +41,29 @@ export function CaptionSelectionPopout() {
|
||||||
);
|
);
|
||||||
|
|
||||||
const currentCaption = source.source?.caption?.id;
|
const currentCaption = source.source?.caption?.id;
|
||||||
|
const customCaptionUploadElement = useRef<HTMLInputElement>(null);
|
||||||
|
const [setCustomCaption, loadingCustomCaption, errorCustomCaption] =
|
||||||
|
useLoading(async (captionFile: File) => {
|
||||||
|
if (
|
||||||
|
!captionFile.name.endsWith(".srt") &&
|
||||||
|
!captionFile.name.endsWith(".vtt")
|
||||||
|
) {
|
||||||
|
throw new Error("Only SRT or VTT files are allowed");
|
||||||
|
}
|
||||||
|
controls.setCaption(
|
||||||
|
CUSTOM_CAPTION_ID,
|
||||||
|
await convertCustomCaptionFileToWebVTT(captionFile)
|
||||||
|
);
|
||||||
|
controls.closePopout();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function handleUploadCaption(e: ChangeEvent<HTMLInputElement>) {
|
||||||
|
if (!e.target.files) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const captionFile = e.target.files[0];
|
||||||
|
setCustomCaption(captionFile);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -54,6 +81,26 @@ export function CaptionSelectionPopout() {
|
||||||
>
|
>
|
||||||
{t("videoPlayer.popouts.noCaptions")}
|
{t("videoPlayer.popouts.noCaptions")}
|
||||||
</PopoutListEntry>
|
</PopoutListEntry>
|
||||||
|
<PopoutListEntry
|
||||||
|
key={CUSTOM_CAPTION_ID}
|
||||||
|
active={currentCaption === CUSTOM_CAPTION_ID}
|
||||||
|
loading={loadingCustomCaption}
|
||||||
|
errored={!!errorCustomCaption}
|
||||||
|
onClick={() => {
|
||||||
|
customCaptionUploadElement.current?.click();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{currentCaption === CUSTOM_CAPTION_ID
|
||||||
|
? t("videoPlayer.popouts.customCaption")
|
||||||
|
: t("videoPlayer.popouts.uploadCustomCaption")}
|
||||||
|
<input
|
||||||
|
ref={customCaptionUploadElement}
|
||||||
|
type="file"
|
||||||
|
onChange={handleUploadCaption}
|
||||||
|
className="hidden"
|
||||||
|
accept=".vtt, .srt"
|
||||||
|
/>
|
||||||
|
</PopoutListEntry>
|
||||||
</PopoutSection>
|
</PopoutSection>
|
||||||
|
|
||||||
<p className="sticky top-0 z-10 flex items-center space-x-1 bg-ash-200 px-5 py-3 text-sm font-bold uppercase">
|
<p className="sticky top-0 z-10 flex items-center space-x-1 bg-ash-200 px-5 py-3 text-sm font-bold uppercase">
|
||||||
|
|
|
@ -96,7 +96,7 @@ export function PopoutListEntry(props: PopoutListEntryTypes) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={[
|
className={[
|
||||||
"group -mx-2 flex cursor-pointer items-center justify-between space-x-1 rounded p-2 font-semibold transition-[background-color,color] duration-150",
|
"group my-2 -mx-2 flex cursor-pointer items-center justify-between space-x-1 rounded p-2 font-semibold transition-[background-color,color] duration-150",
|
||||||
hover,
|
hover,
|
||||||
props.active
|
props.active
|
||||||
? `${bg} active text-white outline-denim-700`
|
? `${bg} active text-white outline-denim-700`
|
||||||
|
|
|
@ -13,6 +13,7 @@ export type ControlMethods = {
|
||||||
setMeta(data?: VideoPlayerMeta): void;
|
setMeta(data?: VideoPlayerMeta): void;
|
||||||
setCurrentEpisode(sId: string, eId: string): void;
|
setCurrentEpisode(sId: string, eId: string): void;
|
||||||
setDraggingTime(num: number): void;
|
setDraggingTime(num: number): void;
|
||||||
|
togglePictureInPicture(): void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useControls(
|
export function useControls(
|
||||||
|
@ -100,5 +101,9 @@ export function useControls(
|
||||||
updateMeta(descriptor, state);
|
updateMeta(descriptor, state);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
togglePictureInPicture() {
|
||||||
|
state.stateProvider?.togglePictureInPicture();
|
||||||
|
updateInterface(descriptor, state);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
} from "@/video/components/hooks/volumeStore";
|
} from "@/video/components/hooks/volumeStore";
|
||||||
import { resetStateForSource } from "@/video/state/providers/helpers";
|
import { resetStateForSource } from "@/video/state/providers/helpers";
|
||||||
import { updateInterface } from "@/video/state/logic/interface";
|
import { updateInterface } from "@/video/state/logic/interface";
|
||||||
|
import { revokeCaptionBlob } from "@/backend/helpers/captions";
|
||||||
import { getPlayerState } from "../cache";
|
import { getPlayerState } from "../cache";
|
||||||
import { updateMediaPlaying } from "../logic/mediaplaying";
|
import { updateMediaPlaying } from "../logic/mediaplaying";
|
||||||
import { VideoPlayerStateProvider } from "./providerTypes";
|
import { VideoPlayerStateProvider } from "./providerTypes";
|
||||||
|
@ -83,6 +84,9 @@ export function createCastingStateProvider(
|
||||||
state.pausedWhenSeeking = state.mediaPlaying.isPaused;
|
state.pausedWhenSeeking = state.mediaPlaying.isPaused;
|
||||||
this.pause();
|
this.pause();
|
||||||
},
|
},
|
||||||
|
togglePictureInPicture() {
|
||||||
|
// no picture in picture while casting
|
||||||
|
},
|
||||||
async setVolume(v) {
|
async setVolume(v) {
|
||||||
// clamp time between 0 and 1
|
// clamp time between 0 and 1
|
||||||
let volume = Math.min(v, 1);
|
let volume = Math.min(v, 1);
|
||||||
|
@ -135,6 +139,7 @@ export function createCastingStateProvider(
|
||||||
},
|
},
|
||||||
setCaption(id, url) {
|
setCaption(id, url) {
|
||||||
if (state.source) {
|
if (state.source) {
|
||||||
|
revokeCaptionBlob(state.source.caption?.url);
|
||||||
state.source.caption = {
|
state.source.caption = {
|
||||||
id,
|
id,
|
||||||
url,
|
url,
|
||||||
|
@ -144,6 +149,7 @@ export function createCastingStateProvider(
|
||||||
},
|
},
|
||||||
clearCaption() {
|
clearCaption() {
|
||||||
if (state.source) {
|
if (state.source) {
|
||||||
|
revokeCaptionBlob(state.source.caption?.url);
|
||||||
state.source.caption = null;
|
state.source.caption = null;
|
||||||
updateSource(descriptor, state);
|
updateSource(descriptor, state);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ export type VideoPlayerStateController = {
|
||||||
setCaption(id: string, url: string): void;
|
setCaption(id: string, url: string): void;
|
||||||
clearCaption(): void;
|
clearCaption(): void;
|
||||||
getId(): string;
|
getId(): string;
|
||||||
|
togglePictureInPicture(): void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type VideoPlayerStateProvider = VideoPlayerStateController & {
|
export type VideoPlayerStateProvider = VideoPlayerStateController & {
|
||||||
|
|
|
@ -5,6 +5,8 @@ import {
|
||||||
canFullscreen,
|
canFullscreen,
|
||||||
canFullscreenAnyElement,
|
canFullscreenAnyElement,
|
||||||
canWebkitFullscreen,
|
canWebkitFullscreen,
|
||||||
|
canPictureInPicture,
|
||||||
|
canWebkitPictureInPicture,
|
||||||
} from "@/utils/detectFeatures";
|
} from "@/utils/detectFeatures";
|
||||||
import { MWStreamType } from "@/backend/helpers/streams";
|
import { MWStreamType } from "@/backend/helpers/streams";
|
||||||
import { updateInterface } from "@/video/state/logic/interface";
|
import { updateInterface } from "@/video/state/logic/interface";
|
||||||
|
@ -16,6 +18,7 @@ import {
|
||||||
import { updateError } from "@/video/state/logic/error";
|
import { updateError } from "@/video/state/logic/error";
|
||||||
import { updateMisc } from "@/video/state/logic/misc";
|
import { updateMisc } from "@/video/state/logic/misc";
|
||||||
import { resetStateForSource } from "@/video/state/providers/helpers";
|
import { resetStateForSource } from "@/video/state/providers/helpers";
|
||||||
|
import { revokeCaptionBlob } from "@/backend/helpers/captions";
|
||||||
import { getPlayerState } from "../cache";
|
import { getPlayerState } from "../cache";
|
||||||
import { updateMediaPlaying } from "../logic/mediaplaying";
|
import { updateMediaPlaying } from "../logic/mediaplaying";
|
||||||
import { VideoPlayerStateProvider } from "./providerTypes";
|
import { VideoPlayerStateProvider } from "./providerTypes";
|
||||||
|
@ -191,6 +194,7 @@ export function createVideoStateProvider(
|
||||||
},
|
},
|
||||||
setCaption(id, url) {
|
setCaption(id, url) {
|
||||||
if (state.source) {
|
if (state.source) {
|
||||||
|
revokeCaptionBlob(state.source.caption?.url);
|
||||||
state.source.caption = {
|
state.source.caption = {
|
||||||
id,
|
id,
|
||||||
url,
|
url,
|
||||||
|
@ -200,10 +204,28 @@ export function createVideoStateProvider(
|
||||||
},
|
},
|
||||||
clearCaption() {
|
clearCaption() {
|
||||||
if (state.source) {
|
if (state.source) {
|
||||||
|
revokeCaptionBlob(state.source.caption?.url);
|
||||||
state.source.caption = null;
|
state.source.caption = null;
|
||||||
updateSource(descriptor, state);
|
updateSource(descriptor, state);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
togglePictureInPicture() {
|
||||||
|
if (canWebkitPictureInPicture()) {
|
||||||
|
const webkitPlayer = player as any;
|
||||||
|
webkitPlayer.webkitSetPresentationMode(
|
||||||
|
webkitPlayer.webkitPresentationMode === "picture-in-picture"
|
||||||
|
? "inline"
|
||||||
|
: "picture-in-picture"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (canPictureInPicture()) {
|
||||||
|
if (player !== document.pictureInPictureElement) {
|
||||||
|
player.requestPictureInPicture();
|
||||||
|
} else {
|
||||||
|
document.exitPictureInPicture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
providerStart() {
|
providerStart() {
|
||||||
this.setVolume(getStoredVolume());
|
this.setVolume(getStoredVolume());
|
||||||
|
|
||||||
|
|
82
yarn.lock
82
yarn.lock
|
@ -927,10 +927,10 @@
|
||||||
"@babel/helper-validator-identifier" "^7.19.1"
|
"@babel/helper-validator-identifier" "^7.19.1"
|
||||||
to-fast-properties "^2.0.0"
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
"@esbuild/linux-x64@0.16.5":
|
"@esbuild/darwin-arm64@0.16.5":
|
||||||
version "0.16.5"
|
version "0.16.5"
|
||||||
resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.5.tgz"
|
resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.5.tgz"
|
||||||
integrity sha512-vsOwzKN+4NenUTyuoWLmg5dAuO8JKuLD9MXSeENA385XucuOZbblmOMwwgPlHsgVRtSjz38riqPJU2ALI/CWYQ==
|
integrity sha512-4HlbUMy50cRaHGVriBjShs46WRPshtnVOqkxEGhEuDuJhgZ3regpWzaQxXOcDXFvVwue8RiqDAAcOi/QlVLE6Q==
|
||||||
|
|
||||||
"@eslint/eslintrc@^1.3.3":
|
"@eslint/eslintrc@^1.3.3":
|
||||||
version "1.3.3"
|
version "1.3.3"
|
||||||
|
@ -948,9 +948,9 @@
|
||||||
strip-json-comments "^3.1.1"
|
strip-json-comments "^3.1.1"
|
||||||
|
|
||||||
"@formkit/auto-animate@^1.0.0-beta.5":
|
"@formkit/auto-animate@^1.0.0-beta.5":
|
||||||
version "1.0.0-beta.5"
|
version "1.0.0-beta.6"
|
||||||
resolved "https://registry.npmjs.org/@formkit/auto-animate/-/auto-animate-1.0.0-beta.5.tgz"
|
resolved "https://registry.npmjs.org/@formkit/auto-animate/-/auto-animate-1.0.0-beta.6.tgz"
|
||||||
integrity sha512-WoSwyhAZPOe6RB/IgicOtCHtrWwEpfKIZ/H/nxpKfnZL9CB6hhhBGU5bCdMRw7YpAUF2CDlQa+WWh+gCqz5lDg==
|
integrity sha512-PVDhLAlr+B4Xb7e+1wozBUWmXa6BFU8xUPR/W/E+TsQhPS1qkAdAsJ25keEnFrcePSnXHrOsh3tiFbEToOzV9w==
|
||||||
|
|
||||||
"@headlessui/react@^1.5.0":
|
"@headlessui/react@^1.5.0":
|
||||||
version "1.7.5"
|
version "1.7.5"
|
||||||
|
@ -1120,15 +1120,10 @@
|
||||||
magic-string "^0.25.0"
|
magic-string "^0.25.0"
|
||||||
string.prototype.matchall "^4.0.6"
|
string.prototype.matchall "^4.0.6"
|
||||||
|
|
||||||
"@swc/core-linux-x64-gnu@1.3.22":
|
"@swc/core-darwin-arm64@1.3.22":
|
||||||
version "1.3.22"
|
version "1.3.22"
|
||||||
resolved "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.22.tgz"
|
resolved "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.22.tgz"
|
||||||
integrity sha512-FLkbiqsdXsVIFZi6iedx4rSBGX8x0vo/5aDlklSxJAAYOcQpO0QADKP5Yr65iMT1d6ABCt2d+/StpGLF7GWOcA==
|
integrity sha512-MMhtPsuXp8gpUgr9bs+RZQ2IyFGiUNDG93usCDAFgAF+6VVp+YaAVjET/3/Bx5Lk2WAt0RxT62C9KTEw1YMo3w==
|
||||||
|
|
||||||
"@swc/core-linux-x64-musl@1.3.22":
|
|
||||||
version "1.3.22"
|
|
||||||
resolved "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.22.tgz"
|
|
||||||
integrity sha512-giBuw+Z0Bq6fpZ0Y5TcfpcQwf9p/cE1fOQyO/K1XSTn/haQOqFi7421Jq/dFThSARZiXw1u9Om9VFbwxr8VI+A==
|
|
||||||
|
|
||||||
"@swc/core@^1.3.21":
|
"@swc/core@^1.3.21":
|
||||||
version "1.3.22"
|
version "1.3.22"
|
||||||
|
@ -1169,9 +1164,9 @@
|
||||||
integrity sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==
|
integrity sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==
|
||||||
|
|
||||||
"@types/chrome@*":
|
"@types/chrome@*":
|
||||||
version "0.0.210"
|
version "0.0.217"
|
||||||
resolved "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.210.tgz"
|
resolved "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.217.tgz"
|
||||||
integrity sha512-VSjQu1k6a/rAfuqR1Gi/oxHZj4+t6+LG+GobNI3ZWI6DQ+fmphNSF6TrLHG6BYK2bXc9Gb4c1uXFKRRVLaGl5Q==
|
integrity sha512-q8fLzCCoHiR9gYRoqvrx12+HaJjRTqUom5Ks/wLSR8Ac83qAqWaA4NgUBUcDjM1O1ACczygxIHCEENXs1zmbqQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/filesystem" "*"
|
"@types/filesystem" "*"
|
||||||
"@types/har-format" "*"
|
"@types/har-format" "*"
|
||||||
|
@ -1803,9 +1798,9 @@ camelcase-css@^2.0.1:
|
||||||
integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
|
integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
|
||||||
|
|
||||||
caniuse-lite@^1.0.30001426, caniuse-lite@^1.0.30001449:
|
caniuse-lite@^1.0.30001426, caniuse-lite@^1.0.30001449:
|
||||||
version "1.0.30001457"
|
version "1.0.30001458"
|
||||||
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz"
|
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001458.tgz"
|
||||||
integrity sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==
|
integrity sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w==
|
||||||
|
|
||||||
chai@^4.3.7:
|
chai@^4.3.7:
|
||||||
version "4.3.7"
|
version "4.3.7"
|
||||||
|
@ -1944,9 +1939,9 @@ copy-to-clipboard@^3.3.1:
|
||||||
toggle-selection "^1.0.6"
|
toggle-selection "^1.0.6"
|
||||||
|
|
||||||
core-js-compat@^3.25.1:
|
core-js-compat@^3.25.1:
|
||||||
version "3.28.0"
|
version "3.29.0"
|
||||||
resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.28.0.tgz"
|
resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.29.0.tgz"
|
||||||
integrity sha512-myzPgE7QodMg4nnd3K1TDoES/nADRStM8Gpz0D6nhkwbmwEnE0ZGJgoWsvQ722FR8D7xS0n0LV556RcEicjTyg==
|
integrity sha512-ScMn3uZNAFhK2DGoEfErguoiAHhV2Ju+oJo/jK08p7B3f3UhocUrCCkTvnZaiS+edl5nlIoiBXKcwMc6elv4KQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist "^4.21.5"
|
browserslist "^4.21.5"
|
||||||
|
|
||||||
|
@ -1956,9 +1951,9 @@ core-js-pure@^3.25.1:
|
||||||
integrity sha512-VVXcDpp/xJ21KdULRq/lXdLzQAtX7+37LzpyfFM973il0tWSsDEoyzG38G14AjTpK9VTfiNM9jnFauq/CpaWGQ==
|
integrity sha512-VVXcDpp/xJ21KdULRq/lXdLzQAtX7+37LzpyfFM973il0tWSsDEoyzG38G14AjTpK9VTfiNM9jnFauq/CpaWGQ==
|
||||||
|
|
||||||
core-js@^3.6.5:
|
core-js@^3.6.5:
|
||||||
version "3.27.1"
|
version "3.28.0"
|
||||||
resolved "https://registry.npmjs.org/core-js/-/core-js-3.27.1.tgz"
|
resolved "https://registry.npmjs.org/core-js/-/core-js-3.28.0.tgz"
|
||||||
integrity sha512-GutwJLBChfGCpwwhbYoqfv03LAfmiz7e7D/BNxzeMxwQf10GRSzqiOjx7AmtEk+heiD/JWmBuyBPgFtx0Sg1ww==
|
integrity sha512-GiZn9D4Z/rSYvTeg1ljAIsEqFm0LaN9gVtwDCrKL80zHtS31p9BAjmTxVqTQDMpwlMolJZOFntUG2uwyj7DAqw==
|
||||||
|
|
||||||
cross-spawn@^7.0.2:
|
cross-spawn@^7.0.2:
|
||||||
version "7.0.3"
|
version "7.0.3"
|
||||||
|
@ -2096,7 +2091,7 @@ delayed-stream@~1.0.0:
|
||||||
resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz"
|
||||||
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
|
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
|
||||||
|
|
||||||
destr@^1.2.1:
|
destr@^1.2.2:
|
||||||
version "1.2.2"
|
version "1.2.2"
|
||||||
resolved "https://registry.npmjs.org/destr/-/destr-1.2.2.tgz"
|
resolved "https://registry.npmjs.org/destr/-/destr-1.2.2.tgz"
|
||||||
integrity sha512-lrbCJwD9saUQrqUfXvl6qoM+QN3W7tLV5pAOs+OqOmopCCz/JkE05MHedJR1xfk4IAnZuJXPVuN5+7jNA2ZCiA==
|
integrity sha512-lrbCJwD9saUQrqUfXvl6qoM+QN3W7tLV5pAOs+OqOmopCCz/JkE05MHedJR1xfk4IAnZuJXPVuN5+7jNA2ZCiA==
|
||||||
|
@ -2691,6 +2686,11 @@ fscreen@^1.2.0:
|
||||||
resolved "https://registry.npmjs.org/fscreen/-/fscreen-1.2.0.tgz"
|
resolved "https://registry.npmjs.org/fscreen/-/fscreen-1.2.0.tgz"
|
||||||
integrity sha512-hlq4+BU0hlPmwsFjwGGzZ+OZ9N/wq9Ljg/sq3pX+2CD7hrJsX9tJgWWK/wiNTFM212CLHWhicOoqwXyZGGetJg==
|
integrity sha512-hlq4+BU0hlPmwsFjwGGzZ+OZ9N/wq9Ljg/sq3pX+2CD7hrJsX9tJgWWK/wiNTFM212CLHWhicOoqwXyZGGetJg==
|
||||||
|
|
||||||
|
fsevents@~2.3.2:
|
||||||
|
version "2.3.2"
|
||||||
|
resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz"
|
||||||
|
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
|
||||||
|
|
||||||
function-bind@^1.1.1:
|
function-bind@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
|
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
|
||||||
|
@ -3252,9 +3252,9 @@ json-stable-stringify-without-jsonify@^1.0.1:
|
||||||
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
|
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
|
||||||
|
|
||||||
json5@^1.0.1:
|
json5@^1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz"
|
||||||
integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==
|
integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
|
||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
|
|
||||||
|
@ -3527,10 +3527,10 @@ natural-compare@^1.4.0:
|
||||||
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
|
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
|
||||||
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
||||||
|
|
||||||
node-fetch-native@^1.0.1:
|
node-fetch-native@^1.0.2:
|
||||||
version "1.0.1"
|
version "1.0.2"
|
||||||
resolved "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.0.1.tgz"
|
resolved "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.0.2.tgz"
|
||||||
integrity sha512-VzW+TAk2wE4X9maiKMlT+GsPU4OMmR1U9CrHSmd3DFLn2IcZ9VJ6M6BBugGfYUnPCLSYxXdZy17M0BEJyhUTwg==
|
integrity sha512-KIkvH1jl6b3O7es/0ShyCgWLcfXxlBrLBbP3rOr23WArC66IMcU4DeZEeYEOwnopYhawLTn7/y+YtmASe8DFVQ==
|
||||||
|
|
||||||
node-releases@^2.0.8:
|
node-releases@^2.0.8:
|
||||||
version "2.0.10"
|
version "2.0.10"
|
||||||
|
@ -3625,13 +3625,13 @@ object.values@^1.1.5:
|
||||||
es-abstract "^1.20.4"
|
es-abstract "^1.20.4"
|
||||||
|
|
||||||
ofetch@^1.0.0:
|
ofetch@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/ofetch/-/ofetch-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/ofetch/-/ofetch-1.0.1.tgz"
|
||||||
integrity sha512-d40aof8czZFSQKJa4+F7Ch3UC5D631cK1TTUoK+iNEut9NoiCL+u0vykl/puYVUS2df4tIQl5upQcolIcEzQjQ==
|
integrity sha512-icBz2JYfEpt+wZz1FRoGcrMigjNKjzvufE26m9+yUiacRQRHwnNlGRPiDnW4op7WX/MR6aniwS8xw8jyVelF2g==
|
||||||
dependencies:
|
dependencies:
|
||||||
destr "^1.2.1"
|
destr "^1.2.2"
|
||||||
node-fetch-native "^1.0.1"
|
node-fetch-native "^1.0.2"
|
||||||
ufo "^1.0.0"
|
ufo "^1.1.0"
|
||||||
|
|
||||||
once@^1.3.0:
|
once@^1.3.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
|
@ -4746,7 +4746,7 @@ typescript@*, typescript@^4.6.4, "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0
|
||||||
resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz"
|
resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz"
|
||||||
integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==
|
integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==
|
||||||
|
|
||||||
ufo@^1.0.0, ufo@^1.1.0:
|
ufo@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.npmjs.org/ufo/-/ufo-1.1.0.tgz"
|
resolved "https://registry.npmjs.org/ufo/-/ufo-1.1.0.tgz"
|
||||||
integrity sha512-LQc2s/ZDMaCN3QLpa+uzHUOQ7SdV0qgv3VBXOolQGXTaaZpIur6PwUclF5nN2hNkiTRcUugXd1zFOW3FLJ135Q==
|
integrity sha512-LQc2s/ZDMaCN3QLpa+uzHUOQ7SdV0qgv3VBXOolQGXTaaZpIur6PwUclF5nN2hNkiTRcUugXd1zFOW3FLJ135Q==
|
||||||
|
|
Loading…
Reference in a new issue