diff --git a/.env.local b/.env.local index df88547..c674fa0 100644 --- a/.env.local +++ b/.env.local @@ -1,9 +1,11 @@ -REACT_APP_CORE_URL=http://192.168.1.15:8080 +# Local overrides (gitignored). Put real secrets here on your development machine. +REACT_APP_YOUTUBE_API_KEY="AIzaSyABiXKk-1tcoR0wQnccZfutBDi0ijTr0Ko" +REACT_APP_CORE_URL=https://restreamer.nextream.sytes.net REACT_APP_WHIP_BASE_URL=http://192.168.1.15:8555 -REACT_APP_YTDLP_URL=http://192.168.1.20:8282 +REACT_APP_YTDLP_URL=http://144.217.82.82:8282 +REACT_APP_YTDLP_URL_TITLES=http://100.73.244.28:8080 REACT_APP_FB_SERVER_URL=http://localhost:3002 REACT_APP_LIVEKIT_API_KEY=APIBTqTGxf9htMK REACT_APP_LIVEKIT_API_SECRET=0dOHWPffwneaPg7OYpe4PeAes21zLJfeYJB9cKzSTtXW REACT_APP_LIVEKIT_WS_URL=wss://livekit-server.nextream.sytes.net REACT_APP_WHIP_SERVER_URL=https://djmaster.nextream.sytes.net -C \ No newline at end of file diff --git a/.gitignore b/.gitignore index 37254f2..aa3c152 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,7 @@ NONPUBLIC .VSCodeCounter .env .env.development.local -.env.test.local +.env.local .env.production.local npm-debug.log* diff --git a/Caddyfile b/Caddyfile index 610bc00..478a107 100644 --- a/Caddyfile +++ b/Caddyfile @@ -5,85 +5,89 @@ } } -:3000 +djmaster.nextream.sytes.net encode zstd gzip # ── Facebook OAuth2 microserver (Node.js en puerto 3002) ────────────────────── handle /fb-server/* { - uri strip_prefix /fb-server - reverse_proxy 127.0.0.1:3002 + uri strip_prefix /fb-server + reverse_proxy restreamer-ui:3002 } # ── LiveKit token endpoint (Node.js en puerto 3002) ─────────────────────────── # POST /livekit/token → genera AccessToken JWT firmado # GET /livekit/config → devuelve wsUrl público (sin secretos) handle /livekit/* { - reverse_proxy 127.0.0.1:3002 + reverse_proxy restreamer-ui:3002 } # ── WebRTC relay WebSocket + status (Node.js en puerto 3002) ───────────────── # 127.0.0.1 evita problema de resolución IPv6 en Alpine ("localhost" → ::1) # HTTP/1.1 necesario para WebSocket upgrade (Caddy requiere versión explícita) handle /webrtc-relay/* { - reverse_proxy 127.0.0.1:3002 { - transport http { - versions 1.1 - } - } + reverse_proxy restreamer-ui:3002 { + transport http { + versions 1.1 + } + } } # ── WebRTC Room HTML (sala para el presentador) ─────────────────────────────── # Sirve la página estática sin fallback al index.html de la SPA handle /webrtc-room/* { - root * /ui/build - file_server + reverse_proxy restreamer-ui:3000 } handle /webrtc-room { - redir /webrtc-room/ 302 + redir /webrtc-room/ 302 } # ── yt-dlp stream extractor (servicio externo configurable via env) ─────────── # /yt-stream/{VIDEO_ID} → http://YTDLP_HOST/stream/{VIDEO_ID} # yt-dlp puede tardar 20-30s — timeouts extendidos a 120s handle_path /yt-stream/* { - rewrite * /stream{path} - reverse_proxy {env.YTDLP_HOST} { - transport http { - dial_timeout 10s - response_header_timeout 120s - read_timeout 120s - } - } + rewrite * /stream{path} + reverse_proxy {env.YTDLP_HOST} { + transport http { + dial_timeout 10s + response_header_timeout 120s + read_timeout 120s + } + } +} + +# ── yt-dlp titles proxy (map /yt-titles/{id} → internal /stream/{id}) ─────── +# Some deployments expose metadata at /stream; proxy to the internal titles service. +handle_path /yt-titles/* { + rewrite * /stream{path} + reverse_proxy {env.YTDLP_TITLES_HOST} { + transport http { + versions 1.1 + dial_timeout 10s + response_header_timeout 15s + read_timeout 15s + } + } } # OAuth2 callback page — must be served as a static HTML (not the SPA index) handle /oauth2callback { - rewrite * /oauth2callback.html - file_server { - root /ui/build - } + rewrite * /oauth2callback.html + reverse_proxy restreamer-ui:3000 } # Facebook OAuth2 callback popup — soporta tanto .html como .htm -# .html → servir directamente handle /oauth/facebook/callback.html { - file_server { - root /ui/build - } + reverse_proxy restreamer-ui:3000 } -# .htm → reescribir internamente a .html (misma página, misma URL visible para Facebook) handle /oauth/facebook/callback.htm { - rewrite * /oauth/facebook/callback.html - file_server { - root /ui/build - } + rewrite * /oauth/facebook/callback.html + reverse_proxy restreamer-ui:3000 } -# Sin extensión → redirigir a .html handle /oauth/facebook/callback { - redir /oauth/facebook/callback.html{query} 302 + redir /oauth/facebook/callback.html{query} 302 } # ── LiveKit Ingress WHIP proxy: OBS publica vía WHIP al mismo dominio ───────── @@ -91,26 +95,24 @@ handle /oauth/facebook/callback { # Caddy lo reenvía al servicio livekit-ingress interno (solo accesible localmente). # LIVEKIT_INGRESS_HOST se configura en docker-compose (p.ej. 192.168.1.20:8088). handle /w/* { - reverse_proxy {env.LIVEKIT_INGRESS_HOST} { - header_up Host {upstream_hostport} - } + reverse_proxy {env.LIVEKIT_INGRESS_HOST} { + header_up Host {upstream_hostport} + } } # ── WHIP info API: genera sesión Ingress (Node en :3002) ───────────────────── handle /api/whip/* { - reverse_proxy 127.0.0.1:3002 + reverse_proxy restreamer-ui:3002 } # ── WHEP relay proxy: Core hace pull aquí → egress server ─────────────────── # Core input: https://djmaster.nextream.sytes.net/whep/rooms/ # EGRESS_HOST se configura en docker-compose (URL del servidor egress). handle /whep/* { - reverse_proxy {env.EGRESS_HOST} + reverse_proxy {env.EGRESS_HOST} } -# SPA — serve static files, fallback to index.html for client-side routing +# SPA — proxy al servidor interno de la aplicación (serve -s build) handle { - root * /ui/build - try_files {path} /index.html - file_server + reverse_proxy restreamer-ui:3000 } diff --git a/docker-compose.yml b/docker-compose.yml index d0ca24d..dc483b5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,13 +1,15 @@ services: restreamer-ui: - # NOTA: Primero compila con: yarn build - # Luego construye la imagen con: docker build --tag restreamer-ui-v2:latest . - # O usa el script: build-docker.bat + # Build image from repository so `docker compose up --build` works on another host + build: + context: . + dockerfile: Dockerfile image: restreamer-ui-v2:latest container_name: restreamer-ui restart: unless-stopped ports: - "3000:3000" + - "3002:3002" environment: # ── Restreamer Core ──────────────────────────────────────────────────── # URL del Core al que se conecta la UI. Dejar vacío para auto-detectar @@ -22,6 +24,11 @@ services: # YTDLP_URL: URL completa del servicio yt-dlp vista desde el NAVEGADOR. # Dejar vacío → la UI usará /yt-stream/ (Caddy proxy, mismo origen = sin CORS). YTDLP_URL: "" + # Host:puerto del servicio titles (usado por Caddy para /yt-titles proxy) + YTDLP_TITLES_HOST: "100.73.244.28:8080" + + # YouTube Data API key (used by the UI to fetch title/description) + YOUTUBE_API_KEY: "AIzaSyABiXKk-1tcoR0wQnccZfutBDi0ijTr0Ko" # ── Facebook OAuth2 microserver ──────────────────────────────────────── # Dejar vacío → Caddy proxy /fb-server → localhost:3002 (sin CORS) @@ -74,6 +81,9 @@ services: # devices: # - "/dev/video1:/dev/video1" # Descomentar si hay cámara USB disponible + # Nginx service removed — using external reverse proxy instead + volumes: restreamer-ui-fb-data: driver: local + # If you previously used caddy, you can remove caddy_data/caddy_config volumes. diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 0a2e51c..bf26cbb 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -11,9 +11,11 @@ cat > "$CONFIG_FILE" < /etc/nginx/conf.d/djmaster.conf + +exec nginx -g 'daemon off;' diff --git a/nginx-examples/README.md b/nginx-examples/README.md new file mode 100644 index 0000000..9fc335b --- /dev/null +++ b/nginx-examples/README.md @@ -0,0 +1,28 @@ +# Nginx examples for ReStreamer UI + +This folder contains minimal example Nginx configuration files to run a reverse proxy for the ReStreamer UI and its microserver. Use them as templates — replace variables and paths to match your server. + +Quick steps + +1. Copy `djmaster.conf.template` to the target server (e.g. `/tmp`). +2. Set variables and generate the real config with `envsubst` (or edit manually): + + ```bash + export UI_HOST=djmaster.nextream.sytes.net + export YTDLP_HOST=144.217.82.82:8282 + export YTDLP_TITLES_HOST=100.73.244.28:8080 + export LIVEKIT_INGRESS_HOST=192.168.1.20:8088 + export LETSENCRYPT_PATH=/etc/letsencrypt + envsubst < djmaster.conf.template > /etc/nginx/sites-available/djmaster.conf + ln -s /etc/nginx/sites-available/djmaster.conf /etc/nginx/sites-enabled/ + nginx -t && systemctl reload nginx + ``` + +Notes + +- The template proxies the UI to `127.0.0.1:3000` and the microserver (OAuth/persistence) to `127.0.0.1:3002` by default. +- `/yt-stream/` and `/yt-titles/` are proxied to external YTDLP hosts to avoid CORS from the browser. +- If you want automatic HTTPS, use `certbot --nginx -d $UI_HOST` (adjust paths to cert files as needed). +- Ensure `client_max_body_size` and timeouts fit your needs (examples set moderately large values). + +If you want me to adapt these to your exact hostnames/paths, tell me the values and I can render the final `djmaster.conf` for you. diff --git a/nginx-examples/djmaster.conf.template b/nginx-examples/djmaster.conf.template new file mode 100644 index 0000000..53d69a0 --- /dev/null +++ b/nginx-examples/djmaster.conf.template @@ -0,0 +1,94 @@ +# djmaster Nginx template +# Replace variables (or use `envsubst`) then install as an Nginx site. + +server { + listen 80; + server_name ${UI_HOST}; + + # ACME challenge served from this location (used by certbot) + location /.well-known/acme-challenge/ { + root /var/www/letsencrypt; + } + + # Redirect all other traffic to HTTPS + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl http2; + server_name ${UI_HOST}; + + ssl_certificate ${LETSENCRYPT_PATH}/live/${UI_HOST}/fullchain.pem; + ssl_certificate_key ${LETSENCRYPT_PATH}/live/${UI_HOST}/privkey.pem; + include ${LETSENCRYPT_PATH}/options-ssl-nginx.conf; + ssl_dhparam ${LETSENCRYPT_PATH}/ssl-dhparams.pem; + + # Serve the frontend (CRA dev/build) proxied to local UI server + location / { + proxy_pass http://127.0.0.1:3000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + proxy_buffering off; + proxy_read_timeout 120s; + } + + # Microserver for OAuth and config persistence + location /fb-server/ { + proxy_pass http://127.0.0.1:3002/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + proxy_buffering off; + proxy_read_timeout 120s; + } + + # YT-DLP stream proxy (avoid CORS in browser) + location /yt-stream/ { + proxy_pass http://${YTDLP_HOST}/; + proxy_set_header Host ${YTDLP_HOST}; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_http_version 1.1; + proxy_buffering off; + proxy_read_timeout 300s; + } + + # YT-DLP titles/metadata proxy + location /yt-titles/ { + proxy_pass http://${YTDLP_TITLES_HOST}/; + proxy_set_header Host ${YTDLP_TITLES_HOST}; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_http_version 1.1; + proxy_buffering off; + proxy_read_timeout 120s; + } + + # LiveKit ingress (if used) + location /livekit-ingress/ { + proxy_pass http://${LIVEKIT_INGRESS_HOST}/; + proxy_set_header Host ${LIVEKIT_INGRESS_HOST}; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_http_version 1.1; + proxy_buffering off; + proxy_read_timeout 60s; + } + + # Tuning + client_max_body_size 200M; + keepalive_timeout 65; + + # Basic security headers + add_header X-Frame-Options "SAMEORIGIN"; + add_header X-Content-Type-Options "nosniff"; + add_header Referrer-Policy "no-referrer-when-downgrade"; + add_header X-XSS-Protection "1; mode=block"; +} diff --git a/public/config.js b/public/config.js index a4d3d90..fcdf68e 100644 --- a/public/config.js +++ b/public/config.js @@ -23,6 +23,7 @@ window.__RESTREAMER_CONFIG__ = { CORE_ADDRESS: '', YTDLP_URL: '', + YTDLP_TITLES_URL: '', FB_SERVER_URL: '', // URL pública del servidor egress (WHIP ingest + WHEP relay). // Ej: 'https://llmchats-whep.zuqtxy.easypanel.host' diff --git a/server/index.js b/server/index.js index 27d7cd4..283eb5e 100644 --- a/server/index.js +++ b/server/index.js @@ -368,6 +368,7 @@ app.get('/health', (_, res) => { res.json({ ok: true, config: CFG_PATH, port: PORT, ts: new Date().toISOString() }); }); + // ═══════════════════════════════════════════════════════════════════════════════ // WHIP INGRESS // Genera una sesión LiveKit Ingress (WHIP_INPUT) y devuelve al browser diff --git a/src/misc/controls/Metadata.js b/src/misc/controls/Metadata.js index 6813a65..161b3d8 100644 --- a/src/misc/controls/Metadata.js +++ b/src/misc/controls/Metadata.js @@ -5,6 +5,15 @@ import makeStyles from '@mui/styles/makeStyles'; import Grid from '@mui/material/Grid'; import Tab from '@mui/material/Tab'; import TextField from '@mui/material/TextField'; +import InputAdornment from '@mui/material/InputAdornment'; +import IconButton from '@mui/material/IconButton'; +import Button from '@mui/material/Button'; +import Tooltip from '@mui/material/Tooltip'; +import CircularProgress from '@mui/material/CircularProgress'; +import YouTubeIcon from '@mui/icons-material/YouTube'; + +import { fetchYtTitles } from '../../utils/ytdlp'; +import { fetchYoutubeSnippet } from '../../utils/youtube'; import TabPanel from '../TabPanel'; import TabsHorizontal from '../TabsHorizontal'; @@ -21,6 +30,8 @@ function init(settings) { const initSettings = { name: 'Livestream', description: 'Live from earth. Powered by datarhei Restreamer.', + youtube_id: '', + youtube_url: '', author: {}, ...settings, }; @@ -34,10 +45,75 @@ function init(settings) { return initSettings; } +const extractYouTubeVideoId = (url) => { + if (!url) return ''; + const trimmed = url.trim(); + const patterns = [ + /[?&]v=([a-zA-Z0-9_-]{11})(?:[&?/]|$)/, + /youtu\.be\/([a-zA-Z0-9_-]{11})(?:[?&\/]|$)/, + /\/(?:live|embed|shorts|v)\/([a-zA-Z0-9_-]{11})(?:[?&\/]|$)/, + ]; + for (const pattern of patterns) { + const match = trimmed.match(pattern); + if (match) return match[1]; + } + if (/^[a-zA-Z0-9_-]{11}$/.test(trimmed)) return trimmed; + return ''; +}; + export default function Control(props) { const classes = useStyles(); const [$tab, setTab] = React.useState('content'); const settings = init(props.settings); + const [fetching, setFetching] = React.useState(false); + const [fetchError, setFetchError] = React.useState(''); + const youtubeUrlRef = React.useRef(null); + + const handleFetchTitles = async () => { + setFetchError(''); + let videoId = settings.youtube_id || (props.settings && props.settings.youtube_id) || ''; + if (!videoId) { + const url = settings.youtube_url || (props.settings && props.settings.youtube_url) || ''; + if (url) { + videoId = extractYouTubeVideoId(url); + if (videoId) settings.youtube_id = videoId; + } + } + if (!videoId) { + setFetchError('No YouTube ID found in metadata — introduce la URL en el campo de YouTube arriba.'); + try { youtubeUrlRef.current && youtubeUrlRef.current.focus(); } catch (e) {} + return; + } + setFetching(true); + try { + // Prefer YouTube Data API on the client (runtime config or REACT_APP var) + const _rt = (typeof window !== 'undefined' && window.__RESTREAMER_CONFIG__) || {}; + const ytApiKey = _rt.YOUTUBE_API_KEY || _rt.YT_API_KEY || process.env.REACT_APP_YOUTUBE_API_KEY || ''; + let t = null; + if (ytApiKey) { + try { + t = await fetchYoutubeSnippet(videoId, ytApiKey, 10000); + } catch (err) { + console.warn('[youtube api] failed:', err && err.message ? err.message : err); + } + } + + // Fallback to yt-dlp titles/stream extractor if YouTube API is not available or fails + if (!t) { + const d = await fetchYtTitles(videoId); + if (d && (d.title || d.description)) t = d; + } + + if (t && t.title) settings.name = t.title; + if (t && t.description) settings.description = t.description; + settings.youtube_id = videoId; + props.onChange(settings, false); + } catch (e) { + setFetchError(e && e.message ? e.message : 'Failed to fetch titles'); + } finally { + setFetching(false); + } + }; // Set the defaults React.useEffect(() => { @@ -73,7 +149,52 @@ export default function Control(props) { - Name} value={settings.name} onChange={handleChange('name')} /> + YouTube URL} + placeholder="https://www.youtube.com/watch?v=VIDEO_ID" + inputRef={youtubeUrlRef} + value={settings.youtube_url || ''} + onChange={handleChange('youtube_url')} + /> + + + Name} + value={settings.name} + onChange={handleChange('name')} + InputProps={{ + endAdornment: ( + + Fetch title & description from stored YouTube ID}> + + + + + + ), + }} + /> + {fetchError && ( +
+ {fetchError} +
+ )}
{ + console.error(`[setupProxy] yt-titles proxy error: ${err.code} — ${err.message}`); + if (!res.headersSent) { + res.writeHead(502, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ error: 'Proxy error', target: YTDLP_TITLES_TARGET, message: err.message })); + } + }, + }), + ); + // Facebook OAuth server + WebRTC relay: /fb-server/* → http://localhost:3002/* app.use( '/fb-server', diff --git a/src/utils/youtube.js b/src/utils/youtube.js new file mode 100644 index 0000000..94e8402 --- /dev/null +++ b/src/utils/youtube.js @@ -0,0 +1,34 @@ +// Utility: fetch video snippet (title + description) via YouTube Data API v3 +export async function fetchYoutubeSnippet(videoId, apiKey, timeoutMs = 10000) { + if (!videoId) throw new Error('No videoId provided'); + if (!apiKey) throw new Error('No YouTube API key configured'); + + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), timeoutMs); + try { + const url = `https://www.googleapis.com/youtube/v3/videos?part=snippet&id=${encodeURIComponent(videoId)}&key=${encodeURIComponent(apiKey)}`; + const res = await fetch(url, { signal: controller.signal }); + clearTimeout(timeout); + if (!res.ok) { + let body = ''; + try { body = await res.text(); } catch (e) {} + let msg = `HTTP ${res.status}`; + try { + const j = JSON.parse(body || '{}'); + if (j.error && j.error.message) msg = j.error.message; + } catch (e) {} + throw new Error(msg); + } + const data = await res.json(); + if (!data.items || data.items.length === 0) throw new Error('Video not found'); + const snippet = data.items[0].snippet || {}; + return { title: snippet.title || '', description: snippet.description || '' }; + } catch (err) { + if (err.name === 'AbortError') throw new Error('Tiempo de espera agotado al consultar YouTube'); + throw err; + } finally { + clearTimeout(timeout); + } +} + +export default { fetchYoutubeSnippet }; diff --git a/src/utils/ytdlp.js b/src/utils/ytdlp.js new file mode 100644 index 0000000..80ab3f9 --- /dev/null +++ b/src/utils/ytdlp.js @@ -0,0 +1,58 @@ +export async function fetchYtTitles(videoId, timeoutMs = 15000) { + const cfg = (typeof window !== 'undefined' && window.__RESTREAMER_CONFIG__) || {}; + const titlesHost = cfg.YTDLP_TITLES_URL ? cfg.YTDLP_TITLES_URL.replace(/\/$/, '') : null; + const primaryHost = cfg.YTDLP_URL ? cfg.YTDLP_URL.replace(/\/$/, '') : null; + + const tryFetchJson = async (url, ms) => { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), ms); + try { + const resp = await fetch(url, { signal: controller.signal }); + clearTimeout(timeoutId); + if (!resp.ok) return { ok: false, status: resp.status }; + const data = await resp.json(); + return { ok: true, data }; + } catch (e) { + clearTimeout(timeoutId); + return { ok: false, error: e }; + } + }; + + // 1) Try titles host (/info then /stream) + if (titlesHost) { + // try /info + const infoUrl = `${titlesHost}/info/${videoId}`; + const infoResp = await tryFetchJson(infoUrl, timeoutMs); + if (infoResp.ok) { + const d = infoResp.data; + return { title: d.title || d.video_title || '', description: d.description || d.video_description || '' }; + } + + // try /stream on the same host (some deployments expose metadata at /stream) + const streamUrlHost = `${titlesHost}/stream/${videoId}`; + const streamRespHost = await tryFetchJson(streamUrlHost, timeoutMs); + if (streamRespHost.ok) { + const d = streamRespHost.data; + return { title: d.title || d.video_title || '', description: d.description || d.video_description || '' }; + } + } + + // 2) Fallback to primary yt-dlp extractor (prefer /stream with longer timeout) + if (primaryHost) { + const streamUrl = `${primaryHost}/stream/${videoId}`; + const streamResp = await tryFetchJson(streamUrl, 90000); + if (streamResp.ok) { + const d = streamResp.data; + return { title: d.title || d.video_title || '', description: d.description || d.video_description || '' }; + } + // optionally try /info on primary host + const infoUrlPrim = `${primaryHost}/info/${videoId}`; + const infoRespPrim = await tryFetchJson(infoUrlPrim, timeoutMs); + if (infoRespPrim.ok) { + const d = infoRespPrim.data; + return { title: d.title || d.video_title || '', description: d.description || d.video_description || '' }; + } + } + + throw new Error('No titles available'); +} diff --git a/src/views/Edit/Profile.js b/src/views/Edit/Profile.js index a30f831..27f679a 100644 --- a/src/views/Edit/Profile.js +++ b/src/views/Edit/Profile.js @@ -810,5 +810,5 @@ Profile.defaultProps = { onStore: function (name, data) { return ''; }, - onYoutubeMetadata: function (title, description) {}, + onYoutubeMetadata: function (videoId, title, description) {}, }; diff --git a/src/views/Edit/SourceSelect.js b/src/views/Edit/SourceSelect.js index 586ef89..ad7ccc8 100644 --- a/src/views/Edit/SourceSelect.js +++ b/src/views/Edit/SourceSelect.js @@ -145,7 +145,7 @@ SourceSelect.defaultProps = { onChange: function (type, device, settings) {}, onRefresh: function () {}, onStore: function (name, data) {}, - onYoutubeMetadata: function (title, description) {}, + onYoutubeMetadata: function (videoId, title, description) {}, }; function Select(props) { diff --git a/src/views/Edit/Sources/Network.js b/src/views/Edit/Sources/Network.js index d8efcd3..5d5ff6e 100644 --- a/src/views/Edit/Sources/Network.js +++ b/src/views/Edit/Sources/Network.js @@ -783,6 +783,9 @@ const _runtimeCfg = window.__RESTREAMER_CONFIG__ || {}; const STREAM_SERVICE_BASE = _runtimeCfg.YTDLP_URL ? _runtimeCfg.YTDLP_URL.replace(/\/$/, '') + '/stream/' : '/yt-stream/'; +const TITLES_SERVICE_BASE = _runtimeCfg.YTDLP_TITLES_URL + ? _runtimeCfg.YTDLP_TITLES_URL.replace(/\/$/, '') + '/info/' + : '/yt-titles/'; const extractYouTubeVideoId = (url) => { if (!url) return ''; @@ -849,12 +852,34 @@ function Pull(props) { const data = await response.json(); if (data && data.stream_url) { props.onChange('', 'address')({ target: { value: data.stream_url } }); + // Store the YouTube ID in the settings so publications can reference it + if (videoId) { + try { + props.onChange('', 'youtube_id')({ target: { value: videoId } }); + } catch (e) {} + } setExtractorError(''); if (typeof props.onYoutubeMetadata === 'function') { - const title = data.title || data.video_title || ''; - const description = data.description || data.video_description || ''; + let title = data.title || data.video_title || ''; + let description = data.description || data.video_description || ''; + // After successful stream extraction, try the dedicated titles service + try { + const titlesController = new AbortController(); + const titlesTimeout = setTimeout(() => titlesController.abort(), 15000); + const tResp = await fetch(TITLES_SERVICE_BASE + videoId, { signal: titlesController.signal }); + clearTimeout(titlesTimeout); + if (tResp.ok) { + const tData = await tResp.json(); + const tTitle = tData.title || tData.video_title || ''; + const tDesc = tData.description || tData.video_description || ''; + if (tTitle) title = tTitle; + if (tDesc) description = tDesc; + } + } catch (e) { + console.warn('[yt-titles] fetch failed:', e && e.message ? e.message : e); + } if (title || description) { - props.onYoutubeMetadata(title, description); + props.onYoutubeMetadata(videoId, title, description); } } } else { @@ -1478,7 +1503,7 @@ Source.defaultProps = { skills: null, onChange: function (settings) {}, onProbe: function (settings, inputs) {}, - onYoutubeMetadata: function (title, description) {}, + onYoutubeMetadata: function (videoId, title, description) {}, }; function SourceIcon(props) { diff --git a/src/views/Edit/Wizard/Sources/Network.js b/src/views/Edit/Wizard/Sources/Network.js index 512c978..5e34629 100644 --- a/src/views/Edit/Wizard/Sources/Network.js +++ b/src/views/Edit/Wizard/Sources/Network.js @@ -33,6 +33,9 @@ const _runtimeCfg = window.__RESTREAMER_CONFIG__ || {}; const STREAM_SERVICE_BASE = _runtimeCfg.YTDLP_URL ? _runtimeCfg.YTDLP_URL.replace(/\/$/, '') + '/stream/' : '/yt-stream/'; +const TITLES_SERVICE_BASE = _runtimeCfg.YTDLP_TITLES_URL + ? _runtimeCfg.YTDLP_TITLES_URL.replace(/\/$/, '') + '/info/' + : '/yt-titles/'; const extractYouTubeVideoId = (url) => { if (!url) return ''; @@ -108,11 +111,12 @@ function Source(props) { const newSettings = { ...settings, address: data.stream_url }; handleChange(newSettings); setExtractorError(''); - if (typeof props.onYoutubeMetadata === 'function') { - const title = data.title || data.video_title || ''; - const description = data.description || data.video_description || ''; - if (title || description) props.onYoutubeMetadata(title, description); - } + // Store the YouTube ID in the wizard metadata via onYoutubeMetadata + if (videoId && typeof props.onYoutubeMetadata === 'function') { + try { + props.onYoutubeMetadata(videoId); + } catch (e) {} + } } else { setExtractorError('No stream_url found in service response.'); } diff --git a/src/views/Edit/Wizard/index.js b/src/views/Edit/Wizard/index.js index 06c9356..d82ac2e 100644 --- a/src/views/Edit/Wizard/index.js +++ b/src/views/Edit/Wizard/index.js @@ -217,15 +217,16 @@ export default function Wizard(props) { navigate(`/${_channelid}/edit`); }; - const handleYoutubeMetadata = (title, description) => { - setData((prev) => ({ - ...prev, - meta: { - ...prev.meta, - name: title || prev.meta?.name || '', - description: description || prev.meta?.description || '', - }, - })); + const handleYoutubeMetadata = (videoId, title, description) => { + setData((prev) => ({ + ...prev, + meta: { + ...prev.meta, + ...(videoId ? { youtube_id: videoId } : {}), + name: title || prev.meta?.name || '', + description: description || prev.meta?.description || '', + }, + })); }; const handleHelp = (what) => () => { diff --git a/src/views/Edit/index.js b/src/views/Edit/index.js index da72a43..ea2b166 100644 --- a/src/views/Edit/index.js +++ b/src/views/Edit/index.js @@ -278,18 +278,19 @@ export default function Edit(props) { }); }; - const handleYoutubeMetadata = (title, description) => { + const handleYoutubeMetadata = (videoId, title, description) => { setData((prev) => ({ - ...prev, - meta: { - ...prev.meta, - ...(title ? { name: title } : {}), - ...(description ? { description: description } : {}), - }, - })); - if (title || description) { - notify.Dispatch('success', 'youtube:metadata', i18n._(t`Title and description filled from YouTube`)); - } + ...prev, + meta: { + ...prev.meta, + ...(videoId ? { youtube_id: videoId } : {}), + ...(title ? { name: title } : {}), + ...(description ? { description: description } : {}), + }, + })); + if (title || description) { + notify.Dispatch('success', 'youtube:metadata', i18n._(t`Title and description filled from YouTube`)); + } }; const handleLicenseChange = (license) => { diff --git a/src/views/Publication/Edit.js b/src/views/Publication/Edit.js index fd4dca1..e48f19f 100644 --- a/src/views/Publication/Edit.js +++ b/src/views/Publication/Edit.js @@ -450,6 +450,7 @@ export default function Edit(props) { onChange={handleServiceChange} channelId={_channelid} publicationId={id} + restreamer={props.restreamer} /> diff --git a/src/views/Publication/Services/DLive.js b/src/views/Publication/Services/DLive.js index 6c9ef54..057f467 100644 --- a/src/views/Publication/Services/DLive.js +++ b/src/views/Publication/Services/DLive.js @@ -7,7 +7,10 @@ import TextField from '@mui/material/TextField'; import Logo from './logos/dlive.svg'; import FormInlineButton from '../../../misc/FormInlineButton'; -import YtMetadataInput from './YtMetadataInput'; +import InputAdornment from '@mui/material/InputAdornment'; +import IconButton from '@mui/material/IconButton'; +import Tooltip from '@mui/material/Tooltip'; +import RestoreIcon from '@mui/icons-material/RestoreOutlined'; const id = 'dlive'; const name = 'dlive'; @@ -94,11 +97,7 @@ function Service(props) { GET
- { - if (title) settings.title = title; - if (desc) settings.description = desc; - pushSettings(); - }} /> + {/* YouTube URL input removed — Reset will restore from stored metadata */} + Reset to stream title}> + { + const meta = props.metadata || {}; + const t = meta.name || meta.title || ''; + const d = meta.description || ''; + if (!t && !d) return; + if (t) settings.title = t; + if (d) settings.description = d; + pushSettings(); + try { + if (props.restreamer && typeof props.restreamer.SetEgressMetadata === 'function') { + await props.restreamer.SetEgressMetadata(props.channelId, props.publicationId, { name: settings.title || '', description: settings.description || '' }); + } + } catch (err) { console.warn('[reset] failed to save metadata', err); } + }}> + + + + + ), + } : undefined} /> @@ -119,6 +142,30 @@ function Service(props) { placeholder={props.metadata && props.metadata.description ? props.metadata.description : ''} value={settings.description} onChange={handleChange('description')} + InputProps={props.metadata && props.metadata.description ? { + endAdornment: ( + + Reset to stream description}> + { + const meta = props.metadata || {}; + const t = meta.name || meta.title || ''; + const d = meta.description || ''; + if (!t && !d) return; + if (t) settings.title = t; + if (d) settings.description = d; + pushSettings(); + try { + if (props.restreamer && typeof props.restreamer.SetEgressMetadata === 'function') { + await props.restreamer.SetEgressMetadata(props.channelId, props.publicationId, { name: settings.title || '', description: settings.description || '' }); + } + } catch (err) { console.warn('[reset] failed to save metadata', err); } + }}> + + + + + ), + } : undefined} /> diff --git a/src/views/Publication/Services/Facebook.js b/src/views/Publication/Services/Facebook.js index 1fd62a5..f422a9b 100644 --- a/src/views/Publication/Services/Facebook.js +++ b/src/views/Publication/Services/Facebook.js @@ -22,14 +22,17 @@ import Typography from '@mui/material/Typography'; import AddIcon from '@mui/icons-material/Add'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import DeleteIcon from '@mui/icons-material/Delete'; +import InputAdornment from '@mui/material/InputAdornment'; import RefreshIcon from '@mui/icons-material/Refresh'; +import RestoreIcon from '@mui/icons-material/RestoreOutlined'; import WarningIcon from '@mui/icons-material/Warning'; import Checkbox from '../../../misc/Checkbox'; import FormInlineButton from '../../../misc/FormInlineButton'; import Select from '../../../misc/Select'; import fbOAuth from '../../../utils/fbOAuth'; -import YtMetadataInput from './YtMetadataInput'; +import { fetchYtTitles } from '../../../utils/ytdlp'; +import { fetchYoutubeSnippet } from '../../../utils/youtube'; const id = 'facebook'; const name = 'Facebook Live'; @@ -450,11 +453,7 @@ function Service(props) { Stream settings - { - if (title) settings.title = title; - if (desc) settings.description = desc; - props.onChange(createOutput(settings), settings); - }} /> + {/* YouTube URL input removed — Reset will restore from stored metadata */} + + { + const meta = props.metadata || {}; + const t = meta.name || meta.title || ''; + const d = meta.description || ''; + if (!t && !d) return; + if (t) settings.title = t; + if (d) settings.description = d; + props.onChange(createOutput(settings), settings); + try { + if (props.restreamer && typeof props.restreamer.SetEgressMetadata === 'function') { + await props.restreamer.SetEgressMetadata(props.channelId, props.publicationId, { name: settings.title || '', description: settings.description || '' }); + } + } catch (err) { + console.warn('[reset] failed to save metadata', err); + } + }}> + + + + + ), + } : undefined} /> @@ -471,6 +496,32 @@ function Service(props) { placeholder={props.metadata?.description || ''} value={settings.description} onChange={handleChange('description')} + InputProps={props.metadata?.description ? { + endAdornment: ( + + + { + const meta = props.metadata || {}; + const t = meta.name || meta.title || ''; + const d = meta.description || ''; + if (!t && !d) return; + if (t) settings.title = t; + if (d) settings.description = d; + props.onChange(createOutput(settings), settings); + try { + if (props.restreamer && typeof props.restreamer.SetEgressMetadata === 'function') { + await props.restreamer.SetEgressMetadata(props.channelId, props.publicationId, { name: settings.title || '', description: settings.description || '' }); + } + } catch (err) { + console.warn('[reset] failed to save metadata', err); + } + }}> + + + + + ), + } : undefined} /> diff --git a/src/views/Publication/Services/Instagram.js b/src/views/Publication/Services/Instagram.js index 57f6a83..e1a7750 100644 --- a/src/views/Publication/Services/Instagram.js +++ b/src/views/Publication/Services/Instagram.js @@ -5,9 +5,12 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Trans } from '@lingui/macro'; import Grid from '@mui/material/Grid'; import TextField from '@mui/material/TextField'; +import InputAdornment from '@mui/material/InputAdornment'; +import IconButton from '@mui/material/IconButton'; +import Tooltip from '@mui/material/Tooltip'; +import RestoreIcon from '@mui/icons-material/RestoreOutlined'; import FormInlineButton from '../../../misc/FormInlineButton'; -import YtMetadataInput from './YtMetadataInput'; const id = 'instagram'; const name = 'Instagram'; @@ -97,11 +100,6 @@ function Service(props) { GET - { - if (title) settings.title = title; - if (desc) settings.description = desc; - pushSettings(); - }} /> + + { + const meta = props.metadata || {}; + const t = meta.name || meta.title || ''; + const d = meta.description || ''; + if (!t && !d) return; + if (t) settings.title = t; + if (d) settings.description = d; + props.onChange(createOutput(settings), settings); + try { if (props.restreamer && typeof props.restreamer.SetEgressMetadata === 'function') { + await props.restreamer.SetEgressMetadata(props.channelId, props.publicationId, { name: settings.title || '', description: settings.description || '' }); + } + } catch (err) { console.warn('[reset] failed to save metadata', err); } + }}> + + + + + ), + } : undefined} /> @@ -122,6 +143,29 @@ function Service(props) { placeholder={props.metadata && props.metadata.description ? props.metadata.description : ''} value={settings.description} onChange={handleChange('description')} + InputProps={props.metadata?.description ? { + endAdornment: ( + + + { + const meta = props.metadata || {}; + const t = meta.name || meta.title || ''; + const d = meta.description || ''; + if (!t && !d) return; + if (t) settings.title = t; + if (d) settings.description = d; + props.onChange(createOutput(settings), settings); + try { if (props.restreamer && typeof props.restreamer.SetEgressMetadata === 'function') { + await props.restreamer.SetEgressMetadata(props.channelId, props.publicationId, { name: settings.title || '', description: settings.description || '' }); + } + } catch (err) { console.warn('[reset] failed to save metadata', err); } + }}> + + + + + ), + } : undefined} /> diff --git a/src/views/Publication/Services/Linkedin.js b/src/views/Publication/Services/Linkedin.js index 35a5a27..c1ec735 100644 --- a/src/views/Publication/Services/Linkedin.js +++ b/src/views/Publication/Services/Linkedin.js @@ -7,7 +7,10 @@ import LinkedInIcon from '@mui/icons-material/LinkedIn'; import MenuItem from '@mui/material/MenuItem'; import Select from '../../../misc/Select'; import TextField from '@mui/material/TextField'; -import YtMetadataInput from './YtMetadataInput'; +import InputAdornment from '@mui/material/InputAdornment'; +import IconButton from '@mui/material/IconButton'; +import Tooltip from '@mui/material/Tooltip'; +import RestoreIcon from '@mui/icons-material/RestoreOutlined'; const id = 'linkedin'; const name = 'LinkedIn'; @@ -108,11 +111,7 @@ function Service(props) { placeholder="{custom_id}.channel.media.azure.net:2935/live/{custom_id}" /> - { - if (title) settings.title = title; - if (desc) settings.description = desc; - pushSettings(); - }} /> + {/* YouTube URL input removed — Reset will restore from stored metadata */} + Reset to stream title}> + { + const meta = props.metadata || {}; + const t = meta.name || meta.title || ''; + const d = meta.description || ''; + if (!t && !d) return; + if (t) settings.title = t; + if (d) settings.description = d; + pushSettings(); + try { + if (props.restreamer && typeof props.restreamer.SetEgressMetadata === 'function') { + await props.restreamer.SetEgressMetadata(props.channelId, props.publicationId, { name: settings.title || '', description: settings.description || '' }); + } + } catch (err) { console.warn('[reset] failed to save metadata', err); } + }}> + + + + + ), + } : undefined} /> @@ -133,6 +156,30 @@ function Service(props) { placeholder={props.metadata && props.metadata.description ? props.metadata.description : ''} value={settings.description} onChange={handleChange('description')} + InputProps={props.metadata && props.metadata.description ? { + endAdornment: ( + + Reset to stream description}> + { + const meta = props.metadata || {}; + const t = meta.name || meta.title || ''; + const d = meta.description || ''; + if (!t && !d) return; + if (t) settings.title = t; + if (d) settings.description = d; + pushSettings(); + try { + if (props.restreamer && typeof props.restreamer.SetEgressMetadata === 'function') { + await props.restreamer.SetEgressMetadata(props.channelId, props.publicationId, { name: settings.title || '', description: settings.description || '' }); + } + } catch (err) { console.warn('[reset] failed to save metadata', err); } + }}> + + + + + ), + } : undefined} /> diff --git a/src/views/Publication/Services/Rumble.js b/src/views/Publication/Services/Rumble.js index ef7991a..13c817e 100644 --- a/src/views/Publication/Services/Rumble.js +++ b/src/views/Publication/Services/Rumble.js @@ -7,7 +7,10 @@ import TextField from '@mui/material/TextField'; import Logo from './logos/rumble.svg'; import FormInlineButton from '../../../misc/FormInlineButton'; -import YtMetadataInput from './YtMetadataInput'; +import InputAdornment from '@mui/material/InputAdornment'; +import IconButton from '@mui/material/IconButton'; +import Tooltip from '@mui/material/Tooltip'; +import RestoreIcon from '@mui/icons-material/RestoreOutlined'; const id = 'rumble'; const name = 'Rumble'; @@ -119,11 +122,7 @@ function Service(props) { GET - { - if (title) settings.title = title; - if (desc) settings.description = desc; - pushSettings(); - }} /> + {/* YouTube URL input removed — Reset will restore from stored metadata */} + Reset to stream title}> + { + const meta = props.metadata || {}; + const t = meta.name || meta.title || ''; + const d = meta.description || ''; + if (!t && !d) return; + if (t) settings.title = t; + if (d) settings.description = d; + pushSettings(); + try { + if (props.restreamer && typeof props.restreamer.SetEgressMetadata === 'function') { + await props.restreamer.SetEgressMetadata(props.channelId, props.publicationId, { name: settings.title || '', description: settings.description || '' }); + } + } catch (err) { console.warn('[reset] failed to save metadata', err); } + }}> + + + + + ), + } : undefined} /> @@ -144,6 +167,30 @@ function Service(props) { placeholder={props.metadata && props.metadata.description ? props.metadata.description : ''} value={settings.description} onChange={handleChange('description')} + InputProps={props.metadata && props.metadata.description ? { + endAdornment: ( + + Reset to stream description}> + { + const meta = props.metadata || {}; + const t = meta.name || meta.title || ''; + const d = meta.description || ''; + if (!t && !d) return; + if (t) settings.title = t; + if (d) settings.description = d; + pushSettings(); + try { + if (props.restreamer && typeof props.restreamer.SetEgressMetadata === 'function') { + await props.restreamer.SetEgressMetadata(props.channelId, props.publicationId, { name: settings.title || '', description: settings.description || '' }); + } + } catch (err) { console.warn('[reset] failed to save metadata', err); } + }}> + + + + + ), + } : undefined} /> diff --git a/src/views/Publication/Services/Twitch.js b/src/views/Publication/Services/Twitch.js index da6abec..cd6fa98 100644 --- a/src/views/Publication/Services/Twitch.js +++ b/src/views/Publication/Services/Twitch.js @@ -6,10 +6,13 @@ import { Trans } from '@lingui/macro'; import Grid from '@mui/material/Grid'; import MenuItem from '@mui/material/MenuItem'; import TextField from '@mui/material/TextField'; +import InputAdornment from '@mui/material/InputAdornment'; +import IconButton from '@mui/material/IconButton'; +import Tooltip from '@mui/material/Tooltip'; +import RestoreIcon from '@mui/icons-material/RestoreOutlined'; import FormInlineButton from '../../../misc/FormInlineButton'; import Select from '../../../misc/Select'; -import YtMetadataInput from './YtMetadataInput'; const id = 'twitch'; const name = 'Twitch'; @@ -167,11 +170,6 @@ function Service(props) { GET - { - if (title) settings.title = title; - if (desc) settings.description = desc; - pushSettings(); - }} /> + + { + const meta = props.metadata || {}; + const t = meta.name || meta.title || ''; + const d = meta.description || ''; + if (!t && !d) return; + if (t) settings.title = t; + if (d) settings.description = d; + props.onChange(createOutput(settings), settings); + try { + if (props.restreamer && typeof props.restreamer.SetEgressMetadata === 'function') { + await props.restreamer.SetEgressMetadata(props.channelId, props.publicationId, { name: settings.title || '', description: settings.description || '' }); + } + } catch (err) { + console.warn('[reset] failed to save metadata', err); + } + }}> + + + + + ), + } : undefined} /> @@ -192,6 +216,32 @@ function Service(props) { placeholder={props.metadata && props.metadata.description ? props.metadata.description : ''} value={settings.description} onChange={handleChange('description')} + InputProps={props.metadata?.description ? { + endAdornment: ( + + + { + const meta = props.metadata || {}; + const t = meta.name || meta.title || ''; + const d = meta.description || ''; + if (!t && !d) return; + if (t) settings.title = t; + if (d) settings.description = d; + props.onChange(createOutput(settings), settings); + try { + if (props.restreamer && typeof props.restreamer.SetEgressMetadata === 'function') { + await props.restreamer.SetEgressMetadata(props.channelId, props.publicationId, { name: settings.title || '', description: settings.description || '' }); + } + } catch (err) { + console.warn('[reset] failed to save metadata', err); + } + }}> + + + + + ), + } : undefined} /> diff --git a/src/views/Publication/Services/Twitter.js b/src/views/Publication/Services/Twitter.js index d98ec84..6dae449 100644 --- a/src/views/Publication/Services/Twitter.js +++ b/src/views/Publication/Services/Twitter.js @@ -8,10 +8,13 @@ import Grid from '@mui/material/Grid'; import Link from '@mui/material/Link'; import MenuItem from '@mui/material/MenuItem'; import TextField from '@mui/material/TextField'; +import InputAdornment from '@mui/material/InputAdornment'; +import IconButton from '@mui/material/IconButton'; +import Tooltip from '@mui/material/Tooltip'; +import RestoreIcon from '@mui/icons-material/RestoreOutlined'; import FormInlineButton from '../../../misc/FormInlineButton'; import Select from '../../../misc/Select'; -import YtMetadataInput from './YtMetadataInput'; const id = 'twitter'; const name = 'Twitter'; @@ -191,11 +194,6 @@ function Service(props) { GET - { - if (title) settings.title = title; - if (desc) settings.description = desc; - pushSettings(); - }} /> + + { + const meta = props.metadata || {}; + const t = meta.name || meta.title || ''; + const d = meta.description || ''; + if (!t && !d) return; + if (t) settings.title = t; + if (d) settings.description = d; + props.onChange(createOutput(settings), settings); + try { + if (props.restreamer && typeof props.restreamer.SetEgressMetadata === 'function') { + await props.restreamer.SetEgressMetadata(props.channelId, props.publicationId, { name: settings.title || '', description: settings.description || '' }); + } + } catch (err) { console.warn('[reset] failed to save metadata', err); } + }}> + + + + + ), + } : undefined} /> @@ -216,6 +238,30 @@ function Service(props) { placeholder={props.metadata && props.metadata.description ? props.metadata.description : ''} value={settings.description} onChange={handleChange('description')} + InputProps={props.metadata?.description ? { + endAdornment: ( + + + { + const meta = props.metadata || {}; + const t = meta.name || meta.title || ''; + const d = meta.description || ''; + if (!t && !d) return; + if (t) settings.title = t; + if (d) settings.description = d; + props.onChange(createOutput(settings), settings); + try { + if (props.restreamer && typeof props.restreamer.SetEgressMetadata === 'function') { + await props.restreamer.SetEgressMetadata(props.channelId, props.publicationId, { name: settings.title || '', description: settings.description || '' }); + } + } catch (err) { console.warn('[reset] failed to save metadata', err); } + }}> + + + + + ), + } : undefined} /> diff --git a/src/views/Publication/Services/Youtube.js b/src/views/Publication/Services/Youtube.js index 01d0e8f..5d644da 100644 --- a/src/views/Publication/Services/Youtube.js +++ b/src/views/Publication/Services/Youtube.js @@ -10,11 +10,17 @@ import MenuItem from '@mui/material/MenuItem'; import MuiTextField from '@mui/material/TextField'; import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import InputAdornment from '@mui/material/InputAdornment'; +import RestoreIcon from '@mui/icons-material/RestoreOutlined'; +import Tooltip from '@mui/material/Tooltip'; + import Checkbox from '../../../misc/Checkbox'; import FormInlineButton from '../../../misc/FormInlineButton'; import Select from '../../../misc/Select'; import ytOAuth from '../../../utils/ytOAuth'; -import YtMetadataInput from './YtMetadataInput'; +import { fetchYtTitles } from '../../../utils/ytdlp'; +import { fetchYoutubeSnippet } from '../../../utils/youtube'; const id = 'youtube'; const name = 'YouTube Live'; @@ -229,13 +235,17 @@ function Service(props) { clearTimeout(timeoutId); // Enviar ACK al popup para que se cierre + // Enviar ACK al popup para que se cierre (acceder a .closed dentro de try/catch + // para evitar warnings cuando COOP/COEP bloquea el acceso a propiedades cross-origin) try { - if (authWindow && !authWindow.closed) { - authWindow.postMessage({ service: 'youtube', ack: true }, '*'); - // Dar 1 s al popup para procesar el ACK y cerrarse solo - setTimeout(() => { try { if (!authWindow.closed) authWindow.close(); } catch(e){} }, 1200); + let isClosed = false; + try { isClosed = !!authWindow.closed; } catch (e) { isClosed = false; } + if (authWindow && !isClosed) { + try { authWindow.postMessage({ service: 'youtube', ack: true }, '*'); } catch (e) { /* ignore */ } } - } catch (e) { /* popup puede haber cerrado ya */ } + // Dar 1 s al popup para procesar el ACK y cerrarse solo. Intento de cierre protegido. + setTimeout(() => { try { if (!authWindow.closed) authWindow.close(); } catch (e) { /* ignore */ } }, 1200); + } catch (e) { /* ignore */ } if (event.data.error) { setApiError('Authorization denied: ' + event.data.error); @@ -328,16 +338,22 @@ function Service(props) { window.addEventListener('message', listener); - // Detectar si el usuario cierra el popup manualmente - const pollClosed = setInterval(() => { - if (authWindow.closed) { - clearInterval(pollClosed); - // Si todavía estamos esperando (el listener no disparó), limpiar - window.removeEventListener('message', listener); - clearTimeout(timeoutId); - setConnecting(false); - } - }, 500); + // Detectar si el usuario cierra el popup manualmente. Acceder a authWindow.closed + // dentro de try/catch para evitar COOP/COEP warnings; si el acceso está bloqueado, + // confiar en el listener/postMessage para resolver el flujo. + const pollClosed = setInterval(() => { + try { + if (!authWindow || authWindow.closed) { + clearInterval(pollClosed); + // Si todavía estamos esperando (el listener no disparó), limpiar + window.removeEventListener('message', listener); + clearTimeout(timeoutId); + setConnecting(false); + } + } catch (e) { + // Acceso bloqueado por políticas COOP/COEP — ignorar y dejar que el listener maneje el cierre + } + }, 500); }; // ─── Pegar token manualmente ────────────────────────────────────────── @@ -557,11 +573,7 @@ function Service(props) { Optionally paste a YouTube URL or Video ID to auto-fill the title and description below. - { - if (title) settings.title = title; - if (desc) settings.description = desc; - pushSettings(settings); - }} /> + {/* YouTube URL input removed — Reset will restore from stored metadata */} {/* ── Título y descripción ────────────────────────────────── */} @@ -572,6 +584,32 @@ function Service(props) { placeholder={props.metadata && props.metadata.name ? props.metadata.name : ''} value={settings.title} onChange={handleChange('title')} + InputProps={props.metadata && props.metadata.name ? { + endAdornment: ( + + Reset to stream title}> + { + const meta = props.metadata || {}; + const t = meta.name || meta.title || ''; + const d = meta.description || ''; + if (!t && !d) return; + if (t) settings.title = t; + if (d) settings.description = d; + pushSettings(settings); + try { + if (props.restreamer && typeof props.restreamer.SetEgressMetadata === 'function') { + await props.restreamer.SetEgressMetadata(props.channelId, props.publicationId, { name: settings.title || '', description: settings.description || '' }); + } + } catch (err) { + console.warn('[reset] failed to save metadata', err); + } + }}> + + + + + ), + } : undefined} /> @@ -584,6 +622,32 @@ function Service(props) { placeholder={props.metadata && props.metadata.description ? props.metadata.description : ''} value={settings.description} onChange={handleChange('description')} + InputProps={props.metadata && props.metadata.description ? { + endAdornment: ( + + Reset to stream description}> + { + const meta = props.metadata || {}; + const t = meta.name || meta.title || ''; + const d = meta.description || ''; + if (!t && !d) return; + if (t) settings.title = t; + if (d) settings.description = d; + pushSettings(settings); + try { + if (props.restreamer && typeof props.restreamer.SetEgressMetadata === 'function') { + await props.restreamer.SetEgressMetadata(props.channelId, props.publicationId, { name: settings.title || '', description: settings.description || '' }); + } + } catch (err) { + console.warn('[reset] failed to save metadata', err); + } + }}> + + + + + ), + } : undefined} /> diff --git a/src/views/Publication/Services/YtMetadataInput.js b/src/views/Publication/Services/YtMetadataInput.js index a108bd6..487f13e 100644 --- a/src/views/Publication/Services/YtMetadataInput.js +++ b/src/views/Publication/Services/YtMetadataInput.js @@ -18,6 +18,7 @@ import InputAdornment from '@mui/material/InputAdornment'; import MuiTextField from '@mui/material/TextField'; import Typography from '@mui/material/Typography'; import YouTubeIcon from '@mui/icons-material/YouTube'; +import { fetchYoutubeSnippet } from '../../../utils/youtube'; // ── yt-dlp service base ──────────────────────────────────────────────────── const _runtimeCfg = (typeof window !== 'undefined' && window.__RESTREAMER_CONFIG__) || {}; @@ -25,6 +26,12 @@ const STREAM_SERVICE_BASE = _runtimeCfg.YTDLP_URL ? _runtimeCfg.YTDLP_URL.replace(/\/$/, '') + '/stream/' : '/yt-stream/'; +const TITLES_SERVICE_BASE = _runtimeCfg.YTDLP_TITLES_URL + ? _runtimeCfg.YTDLP_TITLES_URL.replace(/\/$/, '') + '/info/' + : '/yt-titles/'; + +const YOUTUBE_API_KEY = _runtimeCfg.YOUTUBE_API_KEY || _runtimeCfg.YT_API_KEY || process.env.REACT_APP_YOUTUBE_API_KEY || ''; + export const extractYouTubeVideoId = (url) => { if (!url) return ''; const trimmed = url.trim(); @@ -55,17 +62,52 @@ export default function YtMetadataInput({ onFetch }) { return; } setFetching(true); + // Prefer the dedicated titles service first (fast). If it fails, fallback to full stream extraction. + // If a YouTube API key is configured, prefer querying the YouTube Data API first. + if (YOUTUBE_API_KEY) { + try { + const y = await fetchYoutubeSnippet(videoId, YOUTUBE_API_KEY, 10000); + if (y && (y.title || y.description)) { + if (typeof onFetch === 'function') onFetch(videoId, y.title || '', y.description || ''); + setFetching(false); + return; + } + } catch (e) { + console.warn('[youtube api] failed:', e && e.message ? e.message : e); + // continue to titles service fallback + } + } + try { + const titlesController = new AbortController(); + const titlesTimeout = setTimeout(() => titlesController.abort(), 15000); + const tResp = await fetch(TITLES_SERVICE_BASE + videoId, { signal: titlesController.signal }); + clearTimeout(titlesTimeout); + if (tResp.ok) { + const tData = await tResp.json(); + const title = tData.title || tData.video_title || ''; + const description = tData.description || tData.video_description || ''; + if (title || description) { + if (typeof onFetch === 'function') onFetch(videoId, title, description); + setFetching(false); + return; + } + } + } catch (e) { + console.warn('[yt-titles] fetch failed:', e && e.message ? e.message : e); + } + + // Fallback to full stream extractor if titles service didn't return data const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), 90000); + const timeoutId = setTimeout(() => controller.abort(), 90000); try { const res = await fetch(STREAM_SERVICE_BASE + videoId, { signal: controller.signal }); clearTimeout(timeoutId); if (!res.ok) throw new Error('HTTP ' + res.status + ' – ' + res.statusText); const data = await res.json(); - const title = data.title || data.video_title || ''; + const title = data.title || data.video_title || ''; const description = data.description || data.video_description || ''; if (title || description) { - if (typeof onFetch === 'function') onFetch(title, description); + if (typeof onFetch === 'function') onFetch(videoId, title, description); } else { setError('No se encontró título ni descripción en la respuesta del servicio.'); } diff --git a/yarn.lock b/yarn.lock index 04464ad..ca75cfa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -372,6 +372,11 @@ "@babel/helper-create-class-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": + version "7.21.0-placeholder-for-preset-env.2" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz" + integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== + "@babel/plugin-proposal-private-property-in-object@^7.21.11": version "7.21.11" resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz" @@ -382,11 +387,6 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" -"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": - version "7.21.0-placeholder-for-preset-env.2" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz" - integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== - "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" @@ -1396,11 +1396,116 @@ resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz" integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== +"@esbuild/android-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd" + integrity sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA== + +"@esbuild/android-arm@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.19.tgz#5898f7832c2298bc7d0ab53701c57beb74d78b4d" + integrity sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A== + +"@esbuild/android-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.19.tgz#658368ef92067866d95fb268719f98f363d13ae1" + integrity sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww== + +"@esbuild/darwin-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz#584c34c5991b95d4d48d333300b1a4e2ff7be276" + integrity sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg== + +"@esbuild/darwin-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz#7751d236dfe6ce136cce343dce69f52d76b7f6cb" + integrity sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw== + +"@esbuild/freebsd-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz#cacd171665dd1d500f45c167d50c6b7e539d5fd2" + integrity sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ== + +"@esbuild/freebsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz#0769456eee2a08b8d925d7c00b79e861cb3162e4" + integrity sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ== + +"@esbuild/linux-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz#38e162ecb723862c6be1c27d6389f48960b68edb" + integrity sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg== + +"@esbuild/linux-arm@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz#1a2cd399c50040184a805174a6d89097d9d1559a" + integrity sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA== + +"@esbuild/linux-ia32@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz#e28c25266b036ce1cabca3c30155222841dc035a" + integrity sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ== + +"@esbuild/linux-loong64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz#0f887b8bb3f90658d1a0117283e55dbd4c9dcf72" + integrity sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ== + +"@esbuild/linux-mips64el@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz#f5d2a0b8047ea9a5d9f592a178ea054053a70289" + integrity sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A== + +"@esbuild/linux-ppc64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz#876590e3acbd9fa7f57a2c7d86f83717dbbac8c7" + integrity sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg== + +"@esbuild/linux-riscv64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz#7f49373df463cd9f41dc34f9b2262d771688bf09" + integrity sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA== + +"@esbuild/linux-s390x@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz#e2afd1afcaf63afe2c7d9ceacd28ec57c77f8829" + integrity sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q== + "@esbuild/linux-x64@0.17.19": version "0.17.19" resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz" integrity sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw== +"@esbuild/netbsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz#c29fb2453c6b7ddef9a35e2c18b37bda1ae5c462" + integrity sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q== + +"@esbuild/openbsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz#95e75a391403cb10297280d524d66ce04c920691" + integrity sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g== + +"@esbuild/sunos-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz#722eaf057b83c2575937d3ffe5aeb16540da7273" + integrity sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg== + +"@esbuild/win32-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz#9aa9dc074399288bdcdd283443e9aeb6b9552b6f" + integrity sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag== + +"@esbuild/win32-ia32@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz#95ad43c62ad62485e210f6299c7b2571e48d2b03" + integrity sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw== + +"@esbuild/win32-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz#8cfaf2ff603e9aabb910e9c0558c26cf32744061" + integrity sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA== + "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz" @@ -1922,7 +2027,7 @@ jiti "^1.17.1" lodash.get "^4.4.2" -"@lingui/core@^4.11.4", "@lingui/core@4.11.4": +"@lingui/core@4.11.4", "@lingui/core@^4.11.4": version "4.11.4" resolved "https://registry.npmjs.org/@lingui/core/-/core-4.11.4.tgz" integrity sha512-W0bBIFe44s//Qs+RQ+NMfzK5vAm9oEKyDddlN94Db6rzeUT/IJo7N+T75A6Bya8v/BrtF2G/W4b77eS3sd0utw== @@ -2100,18 +2205,6 @@ resolved "https://registry.npmjs.org/@mui/types/-/types-7.2.16.tgz" integrity sha512-qI8TV3M7ShITEEc8Ih15A2vLzZGLhD+/UPNwck/hcls2gwg7dyRjNGXcQYHKLB5Q7PuTRfrTkAoPa2VV1s67Ag== -"@mui/utils@^6.0.2", "@mui/utils@^6.1.0": - version "6.1.0" - resolved "https://registry.npmjs.org/@mui/utils/-/utils-6.1.0.tgz" - integrity sha512-oT8ZzMISRUhTVpdbYzY0CgrCBb3t/YEdcaM13tUnuTjZ15pdA6g5lx15ZJUdgYXV6PbJdw7tDQgMEr4uXK5TXQ== - dependencies: - "@babel/runtime" "^7.25.6" - "@mui/types" "^7.2.16" - "@types/prop-types" "^15.7.12" - clsx "^2.1.1" - prop-types "^15.8.1" - react-is "^18.3.1" - "@mui/utils@6.0.0-rc.0": version "6.0.0-rc.0" resolved "https://registry.npmjs.org/@mui/utils/-/utils-6.0.0-rc.0.tgz" @@ -2124,6 +2217,18 @@ prop-types "^15.8.1" react-is "^18.3.1" +"@mui/utils@^6.0.2", "@mui/utils@^6.1.0": + version "6.1.0" + resolved "https://registry.npmjs.org/@mui/utils/-/utils-6.1.0.tgz" + integrity sha512-oT8ZzMISRUhTVpdbYzY0CgrCBb3t/YEdcaM13tUnuTjZ15pdA6g5lx15ZJUdgYXV6PbJdw7tDQgMEr4uXK5TXQ== + dependencies: + "@babel/runtime" "^7.25.6" + "@mui/types" "^7.2.16" + "@types/prop-types" "^15.7.12" + clsx "^2.1.1" + prop-types "^15.8.1" + react-is "^18.3.1" + "@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": version "5.1.1-v1" resolved "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz" @@ -2139,7 +2244,7 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== @@ -2837,7 +2942,7 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@^5.58.0", "@typescript-eslint/utils@5.62.0": +"@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.58.0": version "5.62.0" resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz" integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== @@ -2878,19 +2983,19 @@ mux.js "7.0.3" video.js "^7 || ^8" -"@videojs/vhs-utils@^3.0.5": - version "3.0.5" - resolved "https://registry.npmjs.org/@videojs/vhs-utils/-/vhs-utils-3.0.5.tgz" - integrity sha512-PKVgdo8/GReqdx512F+ombhS+Bzogiofy1LgAj4tN8PfdBx3HSS7V5WfJotKTqtOWGwVfSWsrYN/t09/DSryrw== +"@videojs/vhs-utils@4.0.0", "@videojs/vhs-utils@^4.0.0": + version "4.0.0" + resolved "https://registry.npmjs.org/@videojs/vhs-utils/-/vhs-utils-4.0.0.tgz" + integrity sha512-xJp7Yd4jMLwje2vHCUmi8MOUU76nxiwII3z4Eg3Ucb+6rrkFVGosrXlMgGnaLjq724j3wzNElRZ71D/CKrTtxg== dependencies: "@babel/runtime" "^7.12.5" global "^4.4.0" url-toolkit "^2.2.1" -"@videojs/vhs-utils@^4.0.0", "@videojs/vhs-utils@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@videojs/vhs-utils/-/vhs-utils-4.0.0.tgz" - integrity sha512-xJp7Yd4jMLwje2vHCUmi8MOUU76nxiwII3z4Eg3Ucb+6rrkFVGosrXlMgGnaLjq724j3wzNElRZ71D/CKrTtxg== +"@videojs/vhs-utils@^3.0.5": + version "3.0.5" + resolved "https://registry.npmjs.org/@videojs/vhs-utils/-/vhs-utils-3.0.5.tgz" + integrity sha512-PKVgdo8/GReqdx512F+ombhS+Bzogiofy1LgAj4tN8PfdBx3HSS7V5WfJotKTqtOWGwVfSWsrYN/t09/DSryrw== dependencies: "@babel/runtime" "^7.12.5" global "^4.4.0" @@ -2905,7 +3010,7 @@ global "~4.4.0" is-function "^1.0.1" -"@webassemblyjs/ast@^1.11.5", "@webassemblyjs/ast@1.11.6": +"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": version "1.11.6" resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz" integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== @@ -3006,7 +3111,7 @@ "@webassemblyjs/wasm-gen" "1.11.6" "@webassemblyjs/wasm-parser" "1.11.6" -"@webassemblyjs/wasm-parser@^1.11.5", "@webassemblyjs/wasm-parser@1.11.6": +"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": version "1.11.6" resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz" integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== @@ -3105,7 +3210,7 @@ adjust-sourcemap-loader@^4.0.0: loader-utils "^2.0.0" regex-parser "^2.2.11" -aes-decrypter@^4.0.1, aes-decrypter@4.0.1: +aes-decrypter@4.0.1, aes-decrypter@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/aes-decrypter/-/aes-decrypter-4.0.1.tgz" integrity sha512-H1nh/P9VZXUf17AA5NQfJML88CFjVBDuGkp5zDHa7oEhYN9TTpNLJknRY1ie0iSKWlDf6JRnJKaZVDSQdPy6Cg== @@ -3141,6 +3246,16 @@ ajv-keywords@^5.1.0: dependencies: fast-deep-equal "^3.1.3" +ajv@8.12.0, ajv@^8.0.0, ajv@^8.6.0, ajv@^8.9.0: + version "8.12.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" @@ -3151,46 +3266,6 @@ ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0: - version "8.12.0" - resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" - integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - -ajv@^8.6.0: - version "8.12.0" - resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" - integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - -ajv@^8.9.0: - version "8.12.0" - resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" - integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - -ajv@8.12.0: - version "8.12.0" - resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" - integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== - 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-align@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz" @@ -3237,14 +3312,7 @@ ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -ansi-styles@^4.0.0: - version "4.3.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^4.1.0: +ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== @@ -3279,7 +3347,7 @@ arch@^2.2.0: resolved "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz" integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== -arg@^5.0.2, arg@5.0.2: +arg@5.0.2, arg@^5.0.2: version "5.0.2" resolved "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz" integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== @@ -3296,7 +3364,7 @@ argparse@^2.0.1: resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@^5.0.0, aria-query@^5.3.0, aria-query@5.3.0: +aria-query@5.3.0, aria-query@^5.0.0, aria-query@^5.3.0: version "5.3.0" resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz" integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== @@ -3981,6 +4049,11 @@ chalk-template@0.4.0: dependencies: chalk "^4.1.2" +chalk@5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz" + integrity sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w== + chalk@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" @@ -4009,31 +4082,7 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0: - version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^4.0.2: - version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^4.1.0: - version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -4046,11 +4095,6 @@ chalk@^5.0.1: resolved "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz" integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== -chalk@5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz" - integrity sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w== - char-regex@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" @@ -4091,7 +4135,7 @@ check-types@^11.2.3: resolved "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz" integrity sha512-+67P1GkJRaxQD6PKK0Et9DhwQB+vGg3PM5+aavopCpZT1lj9jeqfvpgTLAWErNj8qApkkmXlu/Ug74kmhagkXg== -chokidar@^3.4.2, chokidar@3.5.1: +chokidar@3.5.1, chokidar@^3.4.2: version "3.5.1" resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz" integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== @@ -4233,16 +4277,16 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - color-name@1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + colord@^2.9.1: version "2.9.3" resolved "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz" @@ -4317,7 +4361,7 @@ compressible@~2.0.16: dependencies: mime-db ">= 1.43.0 < 2" -compression@^1.7.4, compression@1.7.4: +compression@1.7.4, compression@^1.7.4: version "1.7.4" resolved "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz" integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== @@ -4362,27 +4406,7 @@ content-type@~1.0.4: resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -convert-source-map@^1.4.0: - version "1.9.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" - integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== - -convert-source-map@^1.5.0: - version "1.9.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" - integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== - -convert-source-map@^1.5.1: - version "1.9.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" - integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== - -convert-source-map@^1.6.0: - version "1.9.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" - integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== - -convert-source-map@^1.7.0: +convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.5.1, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.9.0" resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== @@ -4551,22 +4575,6 @@ css-select@^4.1.3: domutils "^2.8.0" nth-check "^2.0.1" -css-tree@^1.1.2: - version "1.1.3" - resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" - integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== - dependencies: - mdn-data "2.0.14" - source-map "^0.6.1" - -css-tree@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" - integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== - dependencies: - mdn-data "2.0.14" - source-map "^0.6.1" - css-tree@1.0.0-alpha.37: version "1.0.0-alpha.37" resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz" @@ -4575,6 +4583,14 @@ css-tree@1.0.0-alpha.37: mdn-data "2.0.4" source-map "^0.6.1" +css-tree@^1.1.2, css-tree@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + css-vendor@^2.0.8: version "2.0.8" resolved "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz" @@ -4705,26 +4721,19 @@ date-fns@^3.6.0: resolved "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz" integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww== -debug@^2.6.0: +debug@2.6.9, debug@^2.6.0, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@^2.6.8: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== +debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.4.3" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: - ms "2.0.0" - -debug@^2.6.9: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" + ms "^2.1.3" debug@^3.2.7: version "3.2.7" @@ -4733,20 +4742,6 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@4: - version "4.4.3" - resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" - integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== - dependencies: - ms "^2.1.3" - -debug@2.6.9: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - decimal.js@^10.2.1: version "10.4.3" resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz" @@ -4821,16 +4816,16 @@ delayed-stream@~1.0.0: resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== - depd@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + dequal@^2.0.0, dequal@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz" @@ -4941,6 +4936,14 @@ dom-helpers@^5.0.1: "@babel/runtime" "^7.8.7" csstype "^3.0.2" +dom-serializer@0: + version "0.2.2" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + dom-serializer@^1.0.1: version "1.4.1" resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz" @@ -4950,29 +4953,21 @@ dom-serializer@^1.0.1: domhandler "^4.2.0" entities "^2.0.0" -dom-serializer@0: - version "0.2.2" - resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz" - integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== - dependencies: - domelementtype "^2.0.1" - entities "^2.0.0" - dom-walk@^0.1.0: version "0.1.2" resolved "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz" integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== -domelementtype@^2.0.1, domelementtype@^2.2.0: - version "2.3.0" - resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz" - integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== - domelementtype@1: version "1.3.1" resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.3.0" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + domexception@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz" @@ -5244,12 +5239,7 @@ escape-html@~1.0.3: resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== -escape-string-regexp@^1.0.2: - version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== - -escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== @@ -5417,7 +5407,7 @@ eslint-plugin-testing-library@^5.0.1: dependencies: "@typescript-eslint/utils" "^5.58.0" -eslint-scope@^5.1.1: +eslint-scope@5.1.1, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -5441,25 +5431,12 @@ eslint-scope@^8.0.2: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-scope@5.1.1: - version "5.1.1" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - eslint-visitor-keys@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint-visitor-keys@^3.3.0: - version "3.4.3" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" - integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== - -eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: version "3.4.3" resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== @@ -5582,16 +5559,16 @@ espree@^9.6.0, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" -esprima@^4.0.0, esprima@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - esprima@1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz" integrity sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A== +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + esquery@^1.4.2, esquery@^1.5.0: version "1.6.0" resolved "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz" @@ -5606,12 +5583,7 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^4.2.0: +estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== @@ -5868,15 +5840,7 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -find-up@^4.0.0: - version "4.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -find-up@^4.1.0: +find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -5986,17 +5950,7 @@ fs-extra@^10.0.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^9.0.0: - version "9.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs-extra@^9.0.1: +fs-extra@^9.0.0, fs-extra@^9.0.1: version "9.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== @@ -6016,6 +5970,11 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fsevents@^2.3.2, fsevents@~2.3.1, fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + function-bind@^1.1.1, function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" @@ -6137,7 +6096,7 @@ global-prefix@^3.0.0: kind-of "^6.0.2" which "^1.3.1" -global@^4.3.1, global@^4.3.2, global@^4.4.0, global@~4.4.0, global@4.4.0: +global@4.4.0, global@^4.3.1, global@^4.3.2, global@^4.4.0, global@~4.4.0: version "4.4.0" resolved "https://registry.npmjs.org/global/-/global-4.4.0.tgz" integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== @@ -6409,16 +6368,6 @@ http-deceiver@^1.2.7: resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" - integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - http-errors@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" @@ -6430,6 +6379,16 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + http-parser-js@>=0.5.1: version "0.5.8" resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz" @@ -6482,7 +6441,7 @@ hyphenate-style-name@^1.0.3: resolved "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz" integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ== -iconv-lite@^0.4.24, iconv-lite@0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -6562,7 +6521,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@2, inherits@2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -6617,16 +6576,16 @@ invariant@^2.2.2: dependencies: loose-envify "^1.0.0" -ipaddr.js@^2.0.1: - version "2.1.0" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz" - integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ== - ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +ipaddr.js@^2.0.1: + version "2.1.0" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz" + integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ== + is-alphabetical@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz" @@ -7735,7 +7694,7 @@ jss-plugin-vendor-prefixer@^10.10.0: css-vendor "^2.0.8" jss "10.10.0" -jss@^10.10.0, jss@10.10.0: +jss@10.10.0, jss@^10.10.0: version "10.10.0" resolved "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz" integrity sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw== @@ -8364,7 +8323,7 @@ micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.3" picomatch "^2.3.1" -"mime-db@>= 1.43.0 < 2", mime-db@1.52.0: +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": version "1.52.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== @@ -8374,13 +8333,6 @@ mime-db@~1.33.0: resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz" integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== -mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: - version "2.1.35" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - mime-types@2.1.18: version "2.1.18" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz" @@ -8388,6 +8340,13 @@ mime-types@2.1.18: dependencies: mime-db "~1.33.0" +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mime@1.6.0: version "1.6.0" resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" @@ -8422,7 +8381,7 @@ minimalistic-assert@^1.0.0: resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2, minimatch@3.1.2: +minimatch@3.1.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -8475,16 +8434,16 @@ mpd-parser@^1.2.2, mpd-parser@^1.3.0: "@xmldom/xmldom" "^0.8.3" global "^4.4.0" -ms@^2.1.1, ms@^2.1.3, ms@2.1.3: - version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - ms@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== +ms@2.1.3, ms@^2.1.1, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + multicast-dns@^7.2.5: version "7.2.5" resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz" @@ -8498,7 +8457,7 @@ mute-stream@0.0.8: resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -mux.js@^7.0.1, mux.js@7.0.3: +mux.js@7.0.3, mux.js@^7.0.1: version "7.0.3" resolved "https://registry.npmjs.org/mux.js/-/mux.js-7.0.3.tgz" integrity sha512-gzlzJVEGFYPtl2vvEiJneSWAWD4nfYRHD5XgxmB2gWvXraMPOYk+sxfvexmNfjQUFpmk6hwLR5C6iSFmuwCHdQ== @@ -8779,14 +8738,7 @@ os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== -p-limit@^2.0.0: - version "2.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^2.2.0: +p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -9687,12 +9639,7 @@ punycode@^1.3.2: resolved "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== -punycode@^2.1.0: - version "2.3.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" - integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== - -punycode@^2.1.1: +punycode@^2.1.0, punycode@^2.1.1: version "2.3.1" resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== @@ -9743,21 +9690,16 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -range-parser@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - range-parser@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz" integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A== +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + raw-body@2.5.1: version "2.5.1" resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz" @@ -9845,12 +9787,7 @@ react-error-overlay@^6.0.11: resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz" integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg== -react-is@^16.13.1: - version "16.13.1" - resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== - -react-is@^16.7.0: +react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -10306,21 +10243,16 @@ safe-array-concat@^1.0.0, safe-array-concat@^1.0.1: has-symbols "^1.0.3" isarray "^2.0.5" -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@>=5.1.0, safe-buffer@~5.2.0, safe-buffer@5.2.1: +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@5.1.2: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - safe-regex-test@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.2.tgz" @@ -10367,6 +10299,15 @@ scheduler@^0.23.2: dependencies: loose-envify "^1.1.0" +schema-utils@2.7.0: + version "2.7.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz" + integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== + dependencies: + "@types/json-schema" "^7.0.4" + ajv "^6.12.2" + ajv-keywords "^3.4.1" + schema-utils@^2.6.5: version "2.7.1" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz" @@ -10395,15 +10336,6 @@ schema-utils@^4.0.0: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" -schema-utils@2.7.0: - version "2.7.0" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz" - integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== - dependencies: - "@types/json-schema" "^7.0.4" - ajv "^6.12.2" - ajv-keywords "^3.4.1" - select-hose@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz" @@ -10417,17 +10349,7 @@ selfsigned@^2.1.1: "@types/node-forge" "^1.3.0" node-forge "^1" -semver@^6.0.0: - version "6.3.1" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^6.3.0: - version "6.3.1" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^6.3.1: +semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== @@ -10645,7 +10567,7 @@ source-map-support@^0.4.15: dependencies: source-map "^0.5.6" -source-map-support@^0.5.6: +source-map-support@^0.5.6, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -10653,29 +10575,16 @@ source-map-support@^0.5.6: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@~0.5.20: - version "0.5.21" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" +source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== source-map@^0.5.6, source-map@^0.5.7: version "0.5.7" resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== -source-map@^0.6.0: - version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -source-map@^0.6.1, source-map@0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - source-map@^0.7.3: version "0.7.4" resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz" @@ -10688,16 +10597,6 @@ source-map@^0.8.0-beta.0: dependencies: whatwg-url "^7.0.0" -source-map@~0.6.0: - version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - sourcemap-codec@^1.4.8: version "1.4.8" resolved "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz" @@ -10760,29 +10659,15 @@ static-eval@2.0.2: dependencies: escodegen "^1.8.1" -"statuses@>= 1.4.0 < 2": - version "1.5.0" - resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" - integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== - statuses@2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== string-length@^4.0.1: version "4.0.2" @@ -10823,16 +10708,7 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^5.0.1: - version "5.1.2" - resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" - -string-width@^5.1.2: +string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== @@ -10883,6 +10759,20 @@ string.prototype.trimstart@^1.0.7: define-properties "^1.2.0" es-abstract "^1.22.1" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + stringify-entities@^4.0.0: version "4.0.3" resolved "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz" @@ -11015,14 +10905,7 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0: - version "7.2.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-color@^7.1.0: +supports-color@^7.0.0, supports-color@^7.1.0: version "7.2.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -11314,12 +11197,7 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.8.1: - version "1.14.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - -tslib@^1.9.0: +tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -11568,7 +11446,7 @@ universalify@^2.0.0: resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz" integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== -unpipe@~1.0.0, unpipe@1.0.0: +unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== @@ -11910,7 +11788,7 @@ webpack@^5.64.4: watchpack "^2.4.0" webpack-sources "^3.2.3" -websocket-driver@^0.7.4, websocket-driver@>=0.5.1: +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: version "0.7.4" resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==