From 0e3aa9c2c88f55b65bd9689a3a1b7a67a0127eb7 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 15 Jul 2025 17:00:08 +0200 Subject: [PATCH 001/117] feat: scroll to last watched video on details page --- src/components/Video/Video.js | 34 +++++++------------ src/routes/MetaDetails/MetaDetails.js | 1 + .../MetaDetails/VideosList/VideosList.js | 5 ++- src/routes/Player/SideDrawer/SideDrawer.tsx | 15 ++++---- 4 files changed, 25 insertions(+), 30 deletions(-) diff --git a/src/components/Video/Video.js b/src/components/Video/Video.js index 229fe758d..9d604828a 100644 --- a/src/components/Video/Video.js +++ b/src/components/Video/Video.js @@ -12,11 +12,12 @@ const useProfile = require('stremio/common/useProfile'); const VideoPlaceholder = require('./VideoPlaceholder'); const styles = require('./styles'); -const Video = React.forwardRef(({ className, id, title, thumbnail, season, episode, released, upcoming, watched, progress, scheduled, seasonWatched, deepLinks, onMarkVideoAsWatched, onMarkSeasonAsWatched, ...props }, ref) => { +const Video = ({ className, id, title, thumbnail, season, episode, released, upcoming, watched, progress, scheduled, seasonWatched, selected, deepLinks, onMarkVideoAsWatched, onMarkSeasonAsWatched, ...props }) => { const routeFocused = useRouteFocused(); const profile = useProfile(); const { t } = useTranslation(); const [menuOpen, , closeMenu, toggleMenu] = useBinaryState(false); + const popupLabelOnMouseUp = React.useCallback((event) => { if (!event.nativeEvent.togglePopupPrevented) { if (event.nativeEvent.ctrlKey || event.nativeEvent.button === 2) { @@ -68,27 +69,17 @@ const Video = React.forwardRef(({ className, id, title, thumbnail, season, episo } } }, [deepLinks]); - const renderLabel = React.useMemo(() => function renderLabel({ className, id, title, thumbnail, episode, released, upcoming, watched, progress, scheduled, children, ref: popupRef, ...props }) { + const renderLabel = React.useMemo(() => function renderLabel({ className, id, title, thumbnail, episode, released, upcoming, watched, progress, scheduled, children, ref, ...props }) { const blurThumbnail = profile.settings.hideSpoilers && season && episode && !watched; - const handleRef = React.useCallback((node) => { - if (popupRef) { - if (typeof popupRef === 'function') { - popupRef(node); - } else { - popupRef.current = node; - } - } - if (ref) { - if (typeof ref === 'function') { - ref(node); - } else { - ref.current = node; - } - } - }, [popupRef]); + + React.useEffect(() => { + selected && ref.current?.scrollIntoView({ + behavior: 'smooth', + }); + }, [selected]); return ( - ); - }, []); + }, [selected]); const renderMenu = React.useMemo(() => function renderMenu() { return (
@@ -203,7 +194,7 @@ const Video = React.forwardRef(({ className, id, title, thumbnail, season, episo renderMenu={renderMenu} /> ); -}); +}; Video.Placeholder = VideoPlaceholder; @@ -220,6 +211,7 @@ Video.propTypes = { progress: PropTypes.number, scheduled: PropTypes.bool, seasonWatched: PropTypes.bool, + selected: PropTypes.bool, deepLinks: PropTypes.shape({ metaDetailsStreams: PropTypes.string, player: PropTypes.string diff --git a/src/routes/MetaDetails/MetaDetails.js b/src/routes/MetaDetails/MetaDetails.js index d806ffed5..fd27478b5 100644 --- a/src/routes/MetaDetails/MetaDetails.js +++ b/src/routes/MetaDetails/MetaDetails.js @@ -190,6 +190,7 @@ const MetaDetails = ({ urlParams, queryParams }) => { metaItem={metaDetails.metaItem} libraryItem={metaDetails.libraryItem} season={season} + selectedVideoId={metaDetails.libraryItem?.state?.video_id} seasonOnSelect={seasonOnSelect} toggleNotifications={toggleNotifications} /> diff --git a/src/routes/MetaDetails/VideosList/VideosList.js b/src/routes/MetaDetails/VideosList/VideosList.js index 88d53d427..8891947db 100644 --- a/src/routes/MetaDetails/VideosList/VideosList.js +++ b/src/routes/MetaDetails/VideosList/VideosList.js @@ -11,9 +11,10 @@ const SeasonsBar = require('./SeasonsBar'); const { default: EpisodePicker } = require('../EpisodePicker'); const styles = require('./styles'); -const VideosList = ({ className, metaItem, libraryItem, season, seasonOnSelect, toggleNotifications }) => { +const VideosList = ({ className, metaItem, libraryItem, season, seasonOnSelect, selectedVideoId, toggleNotifications }) => { const { core } = useServices(); const profile = useProfile(); + const showNotificationsToggle = React.useMemo(() => { return metaItem?.content?.content?.inLibrary && metaItem?.content?.content?.videos?.length; }, [metaItem]); @@ -178,6 +179,7 @@ const VideosList = ({ className, metaItem, libraryItem, season, seasonOnSelect, deepLinks={video.deepLinks} scheduled={video.scheduled} seasonWatched={seasonWatched} + selected={video.id === selectedVideoId} onMarkVideoAsWatched={onMarkVideoAsWatched} onMarkSeasonAsWatched={onMarkSeasonAsWatched} /> @@ -195,6 +197,7 @@ VideosList.propTypes = { metaItem: PropTypes.object, libraryItem: PropTypes.object, season: PropTypes.number, + selectedVideoId: PropTypes.string, seasonOnSelect: PropTypes.func, toggleNotifications: PropTypes.func, }; diff --git a/src/routes/Player/SideDrawer/SideDrawer.tsx b/src/routes/Player/SideDrawer/SideDrawer.tsx index 299c37b23..d5b13e11f 100644 --- a/src/routes/Player/SideDrawer/SideDrawer.tsx +++ b/src/routes/Player/SideDrawer/SideDrawer.tsx @@ -1,6 +1,6 @@ // Copyright (C) 2017-2024 Smart code 203358507 -import React, { useMemo, useCallback, useState, forwardRef, memo, useRef } from 'react'; +import React, { useMemo, useCallback, useState, forwardRef, memo } from 'react'; import classNames from 'classnames'; import Icon from '@stremio/stremio-icons/react'; import { useServices } from 'stremio/services'; @@ -21,7 +21,8 @@ type Props = { const SideDrawer = memo(forwardRef(({ seriesInfo, className, closeSideDrawer, selected, ...props }: Props, ref) => { const { core } = useServices(); const [season, setSeason] = useState(seriesInfo?.season); - const selectedVideoRef = useRef(null); + const [selectedVideoId, setSelectedVideoId] = useState(null); + const metaItem = useMemo(() => { return seriesInfo ? { @@ -78,11 +79,9 @@ const SideDrawer = memo(forwardRef(({ seriesInfo, classNa event.stopPropagation(); }; - const onTransitionEnd = () => { - selectedVideoRef.current?.scrollIntoView({ - behavior: 'smooth', - }); - }; + const onTransitionEnd = useCallback(() => { + setSelectedVideoId(selected); + }, [selected]); return (
@@ -114,7 +113,6 @@ const SideDrawer = memo(forwardRef(({ seriesInfo, classNa {videos.map((video, index) => (
+
+ ), document.body); +}; + +export default ShortcutsModal; diff --git a/src/App/ShortcutsModal/index.ts b/src/App/ShortcutsModal/index.ts new file mode 100644 index 000000000..5a7549fac --- /dev/null +++ b/src/App/ShortcutsModal/index.ts @@ -0,0 +1,2 @@ +import ShortcutsModal from './ShortcutsModal'; +export default ShortcutsModal; diff --git a/src/App/ShortcutsModal/styles.less b/src/App/ShortcutsModal/styles.less new file mode 100644 index 000000000..ebbc19c62 --- /dev/null +++ b/src/App/ShortcutsModal/styles.less @@ -0,0 +1,91 @@ +@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less'; + +.shortcuts-modal { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + display: flex; + align-items: center; + justify-content: center; + + .backdrop { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background-color: @color-background-dark5-40; + cursor: pointer; + } + + .container { + position: relative; + display: flex; + flex-direction: column; + gap: 1rem; + max-height: 80%; + max-width: 80%; + border-radius: var(--border-radius); + background-color: var(--modal-background-color); + box-shadow: var(--outer-glow); + overflow-y: auto; + + .header { + flex: none; + display: flex; + justify-content: space-between; + align-items: center; + height: 5rem; + padding-left: 2.5rem; + padding-right: 1rem; + + .title { + position: relative; + font-size: 1.5rem; + font-weight: 500; + color: var(--primary-foreground-color); + } + + .close-button { + position: relative; + width: 3rem; + height: 3rem; + padding: 0.5rem; + border-radius: var(--border-radius); + z-index: 2; + + .icon { + display: block; + width: 100%; + height: 100%; + color: var(--primary-foreground-color); + opacity: 0.4; + } + + &:hover, &:focus { + .icon { + opacity: 1; + color: var(--primary-foreground-color); + } + } + + &:focus { + outline-color: var(--primary-foreground-color); + } + } + } + + .content { + position: relative; + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 3rem; + padding: 0 2.5rem; + padding-bottom: 2rem; + overflow-y: auto; + } + } +} \ No newline at end of file diff --git a/src/common/Shortcuts/Shortcuts.tsx b/src/common/Shortcuts/Shortcuts.tsx new file mode 100644 index 000000000..f41e08271 --- /dev/null +++ b/src/common/Shortcuts/Shortcuts.tsx @@ -0,0 +1,54 @@ +import React, { createContext, useCallback, useContext, useEffect } from 'react'; +import shortcuts from './shortcuts'; + +const SHORTCUTS = shortcuts.map(({ shortcuts }) => shortcuts).flat(); + +export type ShortcutName = string; +export type ShortcutListener = () => void; + +interface ShortcutsContext { + grouped: ShortcutGroup[], +} + +const ShortcutsContext = createContext({} as ShortcutsContext); + +type Props = { + children: JSX.Element, + onShortcut: (name: ShortcutName) => void, +}; + +const ShortcutsProvider = ({ children, onShortcut }: Props) => { + const onKeyDown = useCallback(({ ctrlKey, shiftKey, key }: KeyboardEvent) => { + SHORTCUTS.forEach(({ name, combos }) => combos.forEach((keys) => { + const modifers = (keys.includes('Ctrl') ? ctrlKey : true) + && (keys.includes('Shift') ? shiftKey : true); + + if (modifers && keys.includes(key.toUpperCase())) { + onShortcut(name as ShortcutName); + } + })); + }, [onShortcut]); + + useEffect(() => { + document.addEventListener('keydown', onKeyDown); + + return () => { + document.removeEventListener('keydown', onKeyDown); + }; + }, [onKeyDown]); + + return ( + + {children} + + ); +}; + +const useShortcuts = () => { + return useContext(ShortcutsContext); +}; + +export { + ShortcutsProvider, + useShortcuts +}; diff --git a/src/common/Shortcuts/index.ts b/src/common/Shortcuts/index.ts new file mode 100644 index 000000000..f7fa38a18 --- /dev/null +++ b/src/common/Shortcuts/index.ts @@ -0,0 +1,5 @@ +import { ShortcutsProvider, useShortcuts } from './Shortcuts'; +export { + ShortcutsProvider, + useShortcuts, +}; diff --git a/src/common/Shortcuts/shortcuts.ts b/src/common/Shortcuts/shortcuts.ts new file mode 100644 index 000000000..d918733af --- /dev/null +++ b/src/common/Shortcuts/shortcuts.ts @@ -0,0 +1,91 @@ +const shortcuts: ShortcutGroup[] = [ + { + name: 'general', + label: 'SETTINGS_NAV_GENERAL', + shortcuts: [ + { + name: 'navigateTabs', + label: 'SETTINGS_SHORTCUT_NAVIGATE_MENUS', + combos: [['1', '2', '3', '4', '5', '6']], + }, + { + name: 'navigateSearch', + label: 'SETTINGS_SHORTCUT_GO_TO_SEARCH', + combos: [['0']], + }, + { + name: 'fullscreen', + label: 'SETTINGS_SHORTCUT_FULLSCREEN', + combos: [['F']], + }, + { + name: 'exit', + label: 'SETTINGS_SHORTCUT_EXIT_BACK', + combos: [['Escape']], + }, + { + name: 'shortcuts', + label: 'SETTINGS_SHORTCUT_SHORTCUTS', + combos: [['Ctrl', '?']], + }, + ] + }, + { + name: 'player', + label: 'SETTINGS_NAV_PLAYER', + shortcuts: [ + { + name: 'playPause', + label: 'SETTINGS_SHORTCUT_PLAY_PAUSE', + combos: [['Space']], + }, + { + name: 'seekForward', + label: 'SETTINGS_SHORTCUT_SEEK_FORWARD', + combos: [['ArrowRight'], ['Shift', 'ArrowRight']], + }, + { + name: 'seekBackward', + label: 'SETTINGS_SHORTCUT_SEEK_BACKWARD', + combos: [['ArrowLeft'], ['Shift', 'ArrowLeft']], + }, + { + name: 'volumeUp', + label: 'SETTINGS_SHORTCUT_VOLUME_UP', + combos: [['ArrowUp']], + }, + { + name: 'volumeDown', + label: 'SETTINGS_SHORTCUT_VOLUME_DOWN', + combos: [['ArrowDown']], + }, + { + name: 'subtitlesSize', + label: 'SETTINGS_SHORTCUT_SUBTITLES_SIZE', + combos: [['-'], ['=']], + }, + { + name: 'subtitlesDelay', + label: 'SETTINGS_SHORTCUT_SUBTITLES_DELAY', + combos: [['G'], ['H']], + }, + { + name: 'subtitlesMenu', + label: 'SETTINGS_SHORTCUT_MENU_SUBTITLES', + combos: [['S']], + }, + { + name: 'audioMenu', + label: 'SETTINGS_SHORTCUT_MENU_AUDIO', + combos: [['A']], + }, + { + name: 'infoMenu', + label: 'SETTINGS_SHORTCUT_MENU_INFO', + combos: [['I']], + }, + ] + }, +]; + +export default shortcuts; diff --git a/src/common/Shortcuts/types.d.ts b/src/common/Shortcuts/types.d.ts new file mode 100644 index 000000000..e4180616d --- /dev/null +++ b/src/common/Shortcuts/types.d.ts @@ -0,0 +1,11 @@ +type Shortcut = { + name: string, + label: string, + combos: string[][], +}; + +type ShortcutGroup = { + name: string, + label: string, + shortcuts: Shortcut[], +}; diff --git a/src/common/index.js b/src/common/index.js index 25df5c158..0b9cb252f 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -4,6 +4,7 @@ const { FileDropProvider, onFileDrop } = require('./FileDrop'); const { PlatformProvider, usePlatform } = require('./Platform'); const { ToastProvider, useToast } = require('./Toast'); const { TooltipProvider, Tooltip } = require('./Tooltips'); +const { ShortcutsProvider, useShortcuts } = require('./Shortcuts'); const comparatorWithPriorities = require('./comparatorWithPriorities'); const CONSTANTS = require('./CONSTANTS'); const { withCoreSuspender, useCoreSuspender } = require('./CoreSuspender'); @@ -35,6 +36,8 @@ module.exports = { onFileDrop, PlatformProvider, usePlatform, + ShortcutsProvider, + useShortcuts, ToastProvider, useToast, TooltipProvider, diff --git a/src/components/ShortcutsGroup/Combos/Combos.less b/src/components/ShortcutsGroup/Combos/Combos.less new file mode 100644 index 000000000..a862d54ca --- /dev/null +++ b/src/components/ShortcutsGroup/Combos/Combos.less @@ -0,0 +1,22 @@ +.combos { + position: relative; + display: flex; + overflow: visible; + + .combo { + position: relative; + display: flex; + overflow: visible; + + .separator { + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 3.5rem; + font-size: 1rem; + color: var(--primary-foreground-color); + opacity: 0.6; + } + } +} \ No newline at end of file diff --git a/src/components/ShortcutsGroup/Combos/Combos.tsx b/src/components/ShortcutsGroup/Combos/Combos.tsx new file mode 100644 index 000000000..0168441bc --- /dev/null +++ b/src/components/ShortcutsGroup/Combos/Combos.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import Keys from './Keys'; +import styles from './Combos.less'; + +type Props = { + combos: string[][], +}; + +const Combos = ({ combos }: Props) => { + const { t } = useTranslation(); + + return ( +
+ { + combos.map((keys, index) => ( +
+ + { + index < (combos.length - 1) && ( +
+ { t('SETTINGS_SHORTCUT_OR') } +
+ ) + } +
+ )) + } +
+ ); +}; + +export default Combos; diff --git a/src/components/ShortcutsGroup/Combos/Keys/Keys.less b/src/components/ShortcutsGroup/Combos/Keys/Keys.less new file mode 100644 index 000000000..7bb8c76e7 --- /dev/null +++ b/src/components/ShortcutsGroup/Combos/Keys/Keys.less @@ -0,0 +1,26 @@ +kbd { + flex: none; + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + height: 2.5rem; + min-width: 2.5rem; + padding: 0 1rem; + font-size: 1rem; + font-weight: 500; + color: var(--primary-foreground-color); + border-radius: 0.25em; + box-shadow: 0 4px 0 1px rgba(255, 255, 255, 0.1); + background-color: var(--overlay-color); +} + +.separator { + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 2.5rem; + font-size: 1rem; + color: var(--primary-foreground-color); +} \ No newline at end of file diff --git a/src/components/ShortcutsGroup/Combos/Keys/Keys.tsx b/src/components/ShortcutsGroup/Combos/Keys/Keys.tsx new file mode 100644 index 000000000..71ec610da --- /dev/null +++ b/src/components/ShortcutsGroup/Combos/Keys/Keys.tsx @@ -0,0 +1,51 @@ +import React, { Fragment, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import styles from './Keys.less'; + +type Props = { + keys: string[], +}; + +const Keys = ({ keys }: Props) => { + const { t } = useTranslation(); + + const keyLabelMap: Record = useMemo(() => ({ + 'Shift': `⇧ ${t('SETTINGS_SHORTCUT_SHIFT')}`, + 'Space': t('SETTINGS_SHORTCUT_SPACE'), + 'Ctrl': t('SETTINGS_SHORTCUT_CTRL'), + 'Escape': t('SETTINGS_SHORTCUT_ESC'), + 'ArrowUp': '↑', + 'ArrowDown': '↓', + 'ArrowLeft': '←', + 'ArrowRight': '→', + }), [t]); + + const isRange = useMemo(() => { + return keys.length > 1 && keys.every((key) => !Number.isNaN(parseInt(key))); + }, [keys]); + + const filteredKeys = useMemo(() => { + return isRange ? [keys[0], keys[keys.length - 1]] : keys; + }, [keys, isRange]); + + return ( + filteredKeys.map((key, index) => ( + + + {keyLabelMap[key] ?? key.toUpperCase()} + + { + index < (filteredKeys.length - 1) && ( +
+ { + isRange ? t('SETTINGS_SHORTCUT_TO') : '+' + } +
+ ) + } +
+ )) + ); +}; + +export default Keys; diff --git a/src/components/ShortcutsGroup/Combos/Keys/index.ts b/src/components/ShortcutsGroup/Combos/Keys/index.ts new file mode 100644 index 000000000..ba8d58731 --- /dev/null +++ b/src/components/ShortcutsGroup/Combos/Keys/index.ts @@ -0,0 +1,2 @@ +import Keys from './Keys'; +export default Keys; diff --git a/src/components/ShortcutsGroup/Combos/index.ts b/src/components/ShortcutsGroup/Combos/index.ts new file mode 100644 index 000000000..c66667f91 --- /dev/null +++ b/src/components/ShortcutsGroup/Combos/index.ts @@ -0,0 +1,2 @@ +import Combos from './Combos'; +export default Combos; diff --git a/src/components/ShortcutsGroup/ShortcutsGroup.less b/src/components/ShortcutsGroup/ShortcutsGroup.less new file mode 100644 index 000000000..f0fdd975c --- /dev/null +++ b/src/components/ShortcutsGroup/ShortcutsGroup.less @@ -0,0 +1,44 @@ +.shortcuts-group { + flex: 1 1 0; + position: relative; + min-width: 30rem; + display: flex; + flex-direction: column; + gap: 2rem; + overflow: visible; + + .title { + flex: none; + display: flex; + font-size: 1rem; + font-weight: 400; + color: var(--primary-foreground-color); + opacity: 0.6; + } + + .shortcuts { + position: relative; + display: flex; + flex-direction: column; + gap: 2rem; + overflow: visible; + + .shortcut { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + gap: 2rem; + overflow: visible; + + .label { + position: relative; + font-size: 1rem; + color: var(--primary-foreground-color); + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + } + } +} diff --git a/src/components/ShortcutsGroup/ShortcutsGroup.tsx b/src/components/ShortcutsGroup/ShortcutsGroup.tsx new file mode 100644 index 000000000..069d5d1e8 --- /dev/null +++ b/src/components/ShortcutsGroup/ShortcutsGroup.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import classNames from 'classnames'; +import { useTranslation } from 'react-i18next'; +import Combos from './Combos'; +import styles from './ShortcutsGroup.less'; + +type Props = { + className?: string, + label: string, + shortcuts: Shortcut[], +}; + +const ShortcutsGroup = ({ className, label, shortcuts }: Props) => { + const { t } = useTranslation(); + + return ( +
+
+ {t(label)} +
+ +
+ { + shortcuts.map(({ name, label, combos }) => ( +
+
+ {t(label)} +
+ +
+ )) + } +
+
+ ); +}; + +export default ShortcutsGroup; diff --git a/src/components/ShortcutsGroup/index.ts b/src/components/ShortcutsGroup/index.ts new file mode 100644 index 000000000..11f8d0678 --- /dev/null +++ b/src/components/ShortcutsGroup/index.ts @@ -0,0 +1,2 @@ +import ShortcutsGroup from './ShortcutsGroup'; +export default ShortcutsGroup; diff --git a/src/components/index.ts b/src/components/index.ts index a5638007e..a47c2c709 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -25,6 +25,7 @@ import RadioButton from './RadioButton'; import SearchBar from './SearchBar'; import SharePrompt from './SharePrompt'; import Slider from './Slider'; +import ShortcutsGroup from './ShortcutsGroup'; import TextInput from './TextInput'; import Toggle from './Toggle'; import Transition from './Transition'; @@ -59,6 +60,7 @@ export { SearchBar, SharePrompt, Slider, + ShortcutsGroup, TextInput, Toggle, Transition, diff --git a/src/routes/Settings/Shortcuts/Shortcuts.less b/src/routes/Settings/Shortcuts/Shortcuts.less index 40d97987d..186cfa837 100644 --- a/src/routes/Settings/Shortcuts/Shortcuts.less +++ b/src/routes/Settings/Shortcuts/Shortcuts.less @@ -1,27 +1,4 @@ -.shortcut-container { - display: flex; - align-items: center; - justify-content: center; - padding: 0; - overflow: visible; - - kbd { - flex: 0 1 auto; - height: 2.5rem; - min-width: 2.5rem; - line-height: 2.5rem; - padding: 0 1rem; - font-weight: 500; - color: var(--primary-foreground-color); - border-radius: 0.25em; - box-shadow: 0 4px 0 1px var(--modal-background-color); - background-color: var(--overlay-color); - } - - .label { - flex: none; - margin: 0 1rem; - white-space: nowrap; - color: var(--primary-foreground-color); - } +.shortcuts-group { + width: 100%; + margin-bottom: 3rem; } \ No newline at end of file diff --git a/src/routes/Settings/Shortcuts/Shortcuts.tsx b/src/routes/Settings/Shortcuts/Shortcuts.tsx index d852280a6..a0599a503 100644 --- a/src/routes/Settings/Shortcuts/Shortcuts.tsx +++ b/src/routes/Settings/Shortcuts/Shortcuts.tsx @@ -1,97 +1,24 @@ import React, { forwardRef } from 'react'; -import { Section, Option } from '../components'; +import { Section } from '../components'; +import { ShortcutsGroup } from 'stremio/components'; +import { useShortcuts } from 'stremio/common'; import styles from './Shortcuts.less'; -import { useTranslation } from 'react-i18next'; const Shortcuts = forwardRef((_, ref) => { - const { t } = useTranslation(); + const { grouped } = useShortcuts(); return (
- - - - - - - - - - - - - - + { + grouped.map(({ name, label, shortcuts }) => ( + + )) + }
); }); diff --git a/tsconfig.json b/tsconfig.json index d55ac9356..c4b0a8626 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "lib": [ "ES2016", "DOM", "DOM.Iterable"], + "lib": ["ESNext", "DOM", "DOM.Iterable"], "jsx": "react", "baseUrl": "./src", "outDir": "./dist", From 539a7ebc10c8840c34d69f4c667b63da0da9ae8b Mon Sep 17 00:00:00 2001 From: Victor Sales <36749678+v1ctorsales@users.noreply.github.com> Date: Sun, 12 Oct 2025 13:41:42 +0300 Subject: [PATCH 024/117] fix(calendar): align day and more indicator inline in narrow desktop viewports --- src/routes/Calendar/Table/Cell/Cell.less | 69 +++++++++++++++--------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/src/routes/Calendar/Table/Cell/Cell.less b/src/routes/Calendar/Table/Cell/Cell.less index e989b4cf7..d66084392 100644 --- a/src/routes/Calendar/Table/Cell/Cell.less +++ b/src/routes/Calendar/Table/Cell/Cell.less @@ -28,7 +28,7 @@ .heading { position: relative; - width: 60%; + width: 100%; display: flex; align-items: start; padding: 0; @@ -52,7 +52,7 @@ position: relative; display: flex; flex-direction: row; - gap: 1rem; + gap: 0.1em; padding: 0.1rem; width: 90%; @@ -80,13 +80,10 @@ } .poster { - flex: auto; - z-index: 0; - position: relative; - height: 100%; - width: 100%; + height: auto; + max-height: 100%; + aspect-ratio: 2 / 3; object-fit: cover; - opacity: 1; } .icon, .poster { @@ -134,27 +131,19 @@ } } -@media only screen and (orientation: portrait) { +@media @phone-portrait { .cell { flex-direction: column; display: grid; - .heading { - justify-content: center; - } .items { - padding: 1px; - gap: 0.15rem; - justify-content: space-evenly; + padding: 1px; + gap: 0.15rem; + justify-content: space-evenly; - .item{ - width: 80%; - } - } - - .more { - display: flex; - display: none; + .item { + width: 80%; + } } } } @@ -180,4 +169,36 @@ } } } -} \ No newline at end of file +} + +@media only screen and (max-width: @minimum) and (orientation: portrait) and (pointer: fine) { + .cell { + position: relative; + flex-direction: row; + align-items: center; + display: flex; + + .items { + display: none; + } + + .heading { + display: flex; + align-items: center; + gap: 0.25rem; + width: auto; + } + + .day { + margin-right: 0.25rem; + } + + .more { + display: inline-flex; + position: relative; + height: auto; + padding: 0; + width: 30%; + } + } +} From fb9497a85631f28bc4eca0a39a45e52f608d496a Mon Sep 17 00:00:00 2001 From: Victor Sales <36749678+v1ctorsales@users.noreply.github.com> Date: Sun, 12 Oct 2025 18:23:34 +0300 Subject: [PATCH 025/117] feat(calendar): redesign calendar cell layout for responsiveness and banner support --- src/routes/Calendar/Table/Cell/Cell.less | 56 +++++++++++------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/routes/Calendar/Table/Cell/Cell.less b/src/routes/Calendar/Table/Cell/Cell.less index d66084392..0fdf615d2 100644 --- a/src/routes/Calendar/Table/Cell/Cell.less +++ b/src/routes/Calendar/Table/Cell/Cell.less @@ -28,10 +28,9 @@ .heading { position: relative; - width: 100%; + flex: 1 1 40%; display: flex; - align-items: start; - padding: 0; + align-items: flex-start; .day { flex: none; @@ -54,7 +53,10 @@ flex-direction: row; gap: 0.1em; padding: 0.1rem; - width: 90%; + flex: 1 1 60%; + overflow-x: auto; + overflow-y: hidden; + min-width: 0; .item { flex: none; @@ -63,8 +65,9 @@ justify-content: center; height: 100%; aspect-ratio: 2 / 3; - border-radius: var(--border-radius); - max-height: clamp(8rem, 25vh, 14rem); + border-radius: calc(var(--border-radius) /2); + max-height: 100%; + width: 100%; .icon { flex: none; @@ -84,6 +87,7 @@ max-height: 100%; aspect-ratio: 2 / 3; object-fit: cover; + border-radius: inherit } .icon, .poster { @@ -115,7 +119,10 @@ &.today { .heading { .day { + width: auto; background-color: var(--primary-accent-color); + height: auto; + padding: 0.3rem; } } } @@ -142,12 +149,21 @@ justify-content: space-evenly; .item { - width: 80%; + width: 100%; } } } } +@media only screen and (max-height: @xxsmall) and (max-width: @xxsmall) and (orientation: landscape) { + .cell{ + .items{ + width: 100%; + } + } +} + + @media only screen and (max-height: @medium) and (max-width: @medium) and (orientation: landscape) { .cell { gap: 0; @@ -161,12 +177,6 @@ .items { width: 100%; - - .item { - pointer-events: none; - border-radius: calc(var(--border-radius) / 2); - width: 80%; - } } } } @@ -175,30 +185,16 @@ .cell { position: relative; flex-direction: row; - align-items: center; display: flex; - .items { - display: none; - } - .heading { display: flex; - align-items: center; gap: 0.25rem; width: auto; - } + .day{ + font-size: 0.6rem; + } - .day { - margin-right: 0.25rem; - } - - .more { - display: inline-flex; - position: relative; - height: auto; - padding: 0; - width: 30%; } } } From 4860a028c2410bed51e6155b8147de8ceacc1e39 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 13 Oct 2025 11:29:16 +0200 Subject: [PATCH 026/117] chore: update translations --- package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index c824ad411..61e713041 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "react-i18next": "^15.1.3", "react-is": "18.3.1", "spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", - "stremio-translations": "github:Stremio/stremio-translations#abe7684165a031755e9aee39da26daa806ba7824", + "stremio-translations": "github:Stremio/stremio-translations#5e5e3776ff1a1684648eaa3fa6c3a6caeb67cc37", "url": "0.11.4", "use-long-press": "^3.2.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 121d0dbea..110fd7d71 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -90,8 +90,8 @@ importers: specifier: github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6 version: https://codeload.github.com/Stremio/spatial-navigation/tar.gz/64871b1422466f5f45d24ebc8bbd315b2ebab6a6 stremio-translations: - specifier: github:Stremio/stremio-translations#abe7684165a031755e9aee39da26daa806ba7824 - version: https://codeload.github.com/Stremio/stremio-translations/tar.gz/abe7684165a031755e9aee39da26daa806ba7824 + specifier: github:Stremio/stremio-translations#5e5e3776ff1a1684648eaa3fa6c3a6caeb67cc37 + version: https://codeload.github.com/Stremio/stremio-translations/tar.gz/5e5e3776ff1a1684648eaa3fa6c3a6caeb67cc37 url: specifier: 0.11.4 version: 0.11.4 @@ -4527,9 +4527,9 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} - stremio-translations@https://codeload.github.com/Stremio/stremio-translations/tar.gz/abe7684165a031755e9aee39da26daa806ba7824: - resolution: {tarball: https://codeload.github.com/Stremio/stremio-translations/tar.gz/abe7684165a031755e9aee39da26daa806ba7824} - version: 1.44.12 + stremio-translations@https://codeload.github.com/Stremio/stremio-translations/tar.gz/5e5e3776ff1a1684648eaa3fa6c3a6caeb67cc37: + resolution: {tarball: https://codeload.github.com/Stremio/stremio-translations/tar.gz/5e5e3776ff1a1684648eaa3fa6c3a6caeb67cc37} + version: 1.44.13 string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} @@ -10283,7 +10283,7 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 - stremio-translations@https://codeload.github.com/Stremio/stremio-translations/tar.gz/abe7684165a031755e9aee39da26daa806ba7824: {} + stremio-translations@https://codeload.github.com/Stremio/stremio-translations/tar.gz/5e5e3776ff1a1684648eaa3fa6c3a6caeb67cc37: {} string-length@4.0.2: dependencies: From 2e1ad64d02b23b7a508a708acab62616d4e79acd Mon Sep 17 00:00:00 2001 From: Victor Sales <36749678+v1ctorsales@users.noreply.github.com> Date: Mon, 13 Oct 2025 13:06:52 +0300 Subject: [PATCH 027/117] refactor(calendar): replace fixed width with max-width for better banner scaling --- src/routes/Calendar/Table/Cell/Cell.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/Calendar/Table/Cell/Cell.less b/src/routes/Calendar/Table/Cell/Cell.less index 0fdf615d2..95116de20 100644 --- a/src/routes/Calendar/Table/Cell/Cell.less +++ b/src/routes/Calendar/Table/Cell/Cell.less @@ -51,7 +51,7 @@ position: relative; display: flex; flex-direction: row; - gap: 0.1em; + gap: 0.2rem; padding: 0.1rem; flex: 1 1 60%; overflow-x: auto; @@ -67,7 +67,7 @@ aspect-ratio: 2 / 3; border-radius: calc(var(--border-radius) /2); max-height: 100%; - width: 100%; + max-width: 100%; .icon { flex: none; From e74072ebd5c2e548589630022d2dae156d3cebaf Mon Sep 17 00:00:00 2001 From: Victor Sales <36749678+v1ctorsales@users.noreply.github.com> Date: Mon, 13 Oct 2025 13:40:13 +0300 Subject: [PATCH 028/117] fix(calendar): disable banner click in phone-portrait mode --- src/routes/Calendar/Table/Cell/Cell.less | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/routes/Calendar/Table/Cell/Cell.less b/src/routes/Calendar/Table/Cell/Cell.less index 95116de20..3a103c913 100644 --- a/src/routes/Calendar/Table/Cell/Cell.less +++ b/src/routes/Calendar/Table/Cell/Cell.less @@ -138,6 +138,17 @@ } } +@media only screen and (max-width: @minimum) and (orientation: portrait) { + .cell{ + .items{ + .item{ + pointer-events: none; + } + + } + } +} + @media @phone-portrait { .cell { flex-direction: column; @@ -150,6 +161,7 @@ .item { width: 100%; + pointer-events: none; } } } @@ -187,6 +199,12 @@ flex-direction: row; display: flex; + .items{ + .item{ + pointer-events: none; + } + } + .heading { display: flex; gap: 0.25rem; From a97dd01869f0342f0449b4d40278ea2b7a4a70e4 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 13 Oct 2025 12:55:12 +0200 Subject: [PATCH 029/117] refactor(shortcuts): use Ctrl + / for shortcuts modal --- src/common/Shortcuts/shortcuts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/Shortcuts/shortcuts.ts b/src/common/Shortcuts/shortcuts.ts index d918733af..614495f0a 100644 --- a/src/common/Shortcuts/shortcuts.ts +++ b/src/common/Shortcuts/shortcuts.ts @@ -26,7 +26,7 @@ const shortcuts: ShortcutGroup[] = [ { name: 'shortcuts', label: 'SETTINGS_SHORTCUT_SHORTCUTS', - combos: [['Ctrl', '?']], + combos: [['Ctrl', '/']], }, ] }, From d2d28be6ded2956837e6167854d99fd78f5cff8d Mon Sep 17 00:00:00 2001 From: Victor Sales <36749678+v1ctorsales@users.noreply.github.com> Date: Mon, 13 Oct 2025 16:27:15 +0300 Subject: [PATCH 030/117] style(responsive): add @phone-landscape media query --- src/routes/Calendar/Table/Cell/Cell.less | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/routes/Calendar/Table/Cell/Cell.less b/src/routes/Calendar/Table/Cell/Cell.less index 3a103c913..754715f32 100644 --- a/src/routes/Calendar/Table/Cell/Cell.less +++ b/src/routes/Calendar/Table/Cell/Cell.less @@ -167,12 +167,19 @@ } } -@media only screen and (max-height: @xxsmall) and (max-width: @xxsmall) and (orientation: landscape) { - .cell{ - .items{ - width: 100%; +@media @phone-landscape { + .cell { + flex-direction: row; + + .items { + padding: 1px; + gap: 0.15rem; + + .item { + pointer-events: none; + } + } } - } } From 0143bf914c1e343894d3d5f09d612b710445d1a9 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 13 Oct 2025 15:13:21 +0200 Subject: [PATCH 031/117] feat: add video mode setting --- src/routes/Player/Player.js | 1 + src/routes/Settings/Player/Player.tsx | 12 +++++++ .../Settings/Player/usePlayerOptions.ts | 33 +++++++++++++++++++ src/types/models/Ctx.d.ts | 1 + 4 files changed, 47 insertions(+) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index eab38bd96..4231418c6 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -345,6 +345,7 @@ const Player = ({ urlParams, queryParams }) => { forceTranscoding: forceTranscoding || casting, maxAudioChannels: settings.surroundSound ? 32 : 2, hardwareDecoding: settings.hardwareDecoding, + videoMode: settings.videoMode, streamingServerURL: streamingServer.baseUrl ? casting ? streamingServer.baseUrl diff --git a/src/routes/Settings/Player/Player.tsx b/src/routes/Settings/Player/Player.tsx index 72a941e81..29b98d650 100644 --- a/src/routes/Settings/Player/Player.tsx +++ b/src/routes/Settings/Player/Player.tsx @@ -3,6 +3,7 @@ import { ColorInput, MultiselectMenu, Toggle } from 'stremio/components'; import { useServices } from 'stremio/services'; import { Category, Option, Section } from '../components'; import usePlayerOptions from './usePlayerOptions'; +import { usePlatform } from 'stremio/common'; type Props = { profile: Profile, @@ -10,6 +11,7 @@ type Props = { const Player = forwardRef(({ profile }: Props, ref) => { const { shell } = useServices(); + const platform = usePlatform(); const { subtitlesLanguageSelect, @@ -26,6 +28,7 @@ const Player = forwardRef(({ profile }: Props, ref) => { bingeWatchingToggle, playInBackgroundToggle, hardwareDecodingToggle, + videoModeSelect, pauseOnMinimizeToggle, } = usePlayerOptions(profile); @@ -129,6 +132,15 @@ const Player = forwardRef(({ profile }: Props, ref) => { /> } + { + shell.active && platform.name === 'windows' && + + } { shell.active && - { - shell.active && - - } } + { + shell.active && + + } ); From 9dbf950a40eccad92b20142ebe337020ed4c6049 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 14 Jan 2026 03:06:48 +0100 Subject: [PATCH 101/117] chore: replace deprecated lodash.isequal --- package.json | 3 +- pnpm-lock.yaml | 28 ++++++------------- src/App/SearchParamsHandler.js | 4 +-- src/common/useModelState.js | 4 +-- src/router/Router/Router.js | 6 ++-- .../Settings/Streaming/useStreamingOptions.ts | 4 +-- 6 files changed, 19 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index bf2b7a498..adb9c3044 100644 --- a/package.json +++ b/package.json @@ -25,13 +25,13 @@ "buffer": "6.0.3", "classnames": "2.5.1", "eventemitter3": "5.0.1", + "fast-equals": "^6.0.0", "filter-invalid-dom-props": "3.0.1", "hat": "^0.0.3", "i18next": "^24.0.5", "langs": "github:Stremio/nodejs-langs", "lodash.debounce": "4.0.8", "lodash.intersection": "4.4.0", - "lodash.isequal": "4.5.0", "lodash.throttle": "4.1.1", "magnet-uri": "6.2.0", "prop-types": "15.8.1", @@ -53,7 +53,6 @@ "@stylistic/eslint-plugin": "^5.4.0", "@stylistic/eslint-plugin-jsx": "^4.4.1", "@types/hat": "^0.0.4", - "@types/lodash.isequal": "^4.5.8", "@types/lodash.throttle": "^4.1.9", "@types/react": "^18.3.13", "@types/react-dom": "^18.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 95b44e9e2..aebe75436 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: eventemitter3: specifier: 5.0.1 version: 5.0.1 + fast-equals: + specifier: ^6.0.0 + version: 6.0.0 filter-invalid-dom-props: specifier: 3.0.1 version: 3.0.1 @@ -59,9 +62,6 @@ importers: lodash.intersection: specifier: 4.4.0 version: 4.4.0 - lodash.isequal: - specifier: 4.5.0 - version: 4.5.0 lodash.throttle: specifier: 4.1.1 version: 4.1.1 @@ -120,9 +120,6 @@ importers: '@types/hat': specifier: ^0.0.4 version: 0.0.4 - '@types/lodash.isequal': - specifier: ^4.5.8 - version: 4.5.8 '@types/lodash.throttle': specifier: ^4.1.9 version: 4.1.9 @@ -1401,9 +1398,6 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/lodash.isequal@4.5.8': - resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==} - '@types/lodash.throttle@4.1.9': resolution: {integrity: sha512-PCPVfpfueguWZQB7pJQK890F2scYKoDUL3iM522AptHWn7d5NQmeS/LTEHIcLr5PaTzl3dK2Z0xSUHHTHwaL5g==} @@ -2502,6 +2496,10 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-equals@6.0.0: + resolution: {integrity: sha512-PFhhIGgdM79r5Uztdj9Zb6Tt1zKafqVfdMGwVca1z5z6fbX7DmsySSuJd8HiP6I1j505DCS83cLxo5rmSNeVEA==} + engines: {node: '>=6.0.0'} + fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} @@ -3402,10 +3400,6 @@ packages: lodash.intersection@4.4.0: resolution: {integrity: sha512-N+L0cCfnqMv6mxXtSPeKt+IavbOBBSiAEkKyLasZ8BVcP9YXQgxLO12oPR8OyURwKV8l5vJKiE1M8aS70heuMg==} - lodash.isequal@4.5.0: - resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} - deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. - lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} @@ -6706,10 +6700,6 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/lodash.isequal@4.5.8': - dependencies: - '@types/lodash': 4.17.20 - '@types/lodash.throttle@4.1.9': dependencies: '@types/lodash': 4.17.20 @@ -8091,6 +8081,8 @@ snapshots: fast-deep-equal@3.1.3: {} + fast-equals@6.0.0: {} + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -9174,8 +9166,6 @@ snapshots: lodash.intersection@4.4.0: {} - lodash.isequal@4.5.0: {} - lodash.memoize@4.1.2: {} lodash.merge@4.6.2: {} diff --git a/src/App/SearchParamsHandler.js b/src/App/SearchParamsHandler.js index 8834e6e0c..86f10d0c4 100644 --- a/src/App/SearchParamsHandler.js +++ b/src/App/SearchParamsHandler.js @@ -1,7 +1,7 @@ // Copyright (C) 2017-2023 Smart code 203358507 const React = require('react'); -const isEqual = require('lodash.isequal'); +const { deepEqual } = require('fast-equals'); const { withCoreSuspender, useProfile, useToast } = require('stremio/common'); const { useServices } = require('stremio/services'); @@ -18,7 +18,7 @@ const SearchParamsHandler = () => { setSearchParams((previousSearchParams) => { const currentSearchParams = Object.fromEntries(searchParams.entries()); - return isEqual(previousSearchParams, currentSearchParams) ? previousSearchParams : currentSearchParams; + return deepEqual(previousSearchParams, currentSearchParams) ? previousSearchParams : currentSearchParams; }); }; diff --git a/src/common/useModelState.js b/src/common/useModelState.js index 42672dc22..da637b8ff 100644 --- a/src/common/useModelState.js +++ b/src/common/useModelState.js @@ -2,7 +2,7 @@ const React = require('react'); const throttle = require('lodash.throttle'); -const isEqual = require('lodash.isequal'); +const { deepEqual } = require('fast-equals'); const intersection = require('lodash.intersection'); const { useCoreSuspender } = require('stremio/common/CoreSuspender'); const { useRouteFocused } = require('stremio-router'); @@ -19,7 +19,7 @@ const useModelState = ({ action, ...args }) => { const [state, setState] = React.useReducer( (prevState, nextState) => { return Object.keys(prevState).reduce((result, key) => { - result[key] = isEqual(prevState[key], nextState[key]) ? prevState[key] : nextState[key]; + result[key] = deepEqual(prevState[key], nextState[key]) ? prevState[key] : nextState[key]; return result; }, {}); }, diff --git a/src/router/Router/Router.js b/src/router/Router/Router.js index db4c9e957..2c7830bc3 100644 --- a/src/router/Router/Router.js +++ b/src/router/Router/Router.js @@ -5,7 +5,7 @@ const ReactIs = require('react-is'); const PropTypes = require('prop-types'); const classnames = require('classnames'); const UrlUtils = require('url'); -const isEqual = require('lodash.isequal'); +const { deepEqual } = require('fast-equals'); const { RouteFocusedProvider } = require('../RouteFocusedContext'); const Route = require('../Route'); const routeConfigForPath = require('./routeConfigForPath'); @@ -54,11 +54,11 @@ const Router = ({ className, onPathNotMatch, onRouteChange, ...props }) => { return { key: `${routeViewIndex}${routeIndex}`, component: routeConfig.component, - urlParams: view !== null && isEqual(view.urlParams, urlParams) ? + urlParams: view !== null && deepEqual(view.urlParams, urlParams) ? view.urlParams : urlParams, - queryParams: view !== null && isEqual(Array.from(view.queryParams.entries()), Array.from(queryParams.entries())) ? + queryParams: view !== null && deepEqual(Array.from(view.queryParams.entries()), Array.from(queryParams.entries())) ? view.queryParams : queryParams diff --git a/src/routes/Settings/Streaming/useStreamingOptions.ts b/src/routes/Settings/Streaming/useStreamingOptions.ts index 140909132..0e22afc0b 100644 --- a/src/routes/Settings/Streaming/useStreamingOptions.ts +++ b/src/routes/Settings/Streaming/useStreamingOptions.ts @@ -2,7 +2,7 @@ import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import isEqual from 'lodash.isequal'; +import { deepEqual } from 'fast-equals'; import { useServices } from 'stremio/services'; const CACHE_SIZES = [0, 2147483648, 5368709120, 10737418240, null]; @@ -160,7 +160,7 @@ const useStreamingOptions = (streamingServer: StreamingServer) => { btRequestTimeout: settings.btRequestTimeout }; const isCustomTorrentProfileSelected = Object.values(TORRENT_PROFILES).every((torrentProfile) => { - return !isEqual(torrentProfile, selectedTorrentProfile); + return !deepEqual(torrentProfile, selectedTorrentProfile); }); return { options: Object.keys(TORRENT_PROFILES) From f37e11964486f97fe6934f465488ea24b822bd18 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 14 Jan 2026 03:21:08 +0100 Subject: [PATCH 102/117] chore: replace clean-webpack-plugin --- package.json | 1 - pnpm-lock.yaml | 157 +--------------------------------------------- webpack.config.js | 7 +-- 3 files changed, 4 insertions(+), 161 deletions(-) diff --git a/package.json b/package.json index adb9c3044..4e2b8ee5b 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,6 @@ "@types/react": "^18.3.13", "@types/react-dom": "^18.3.1", "babel-loader": "9.2.1", - "clean-webpack-plugin": "4.0.0", "copy-webpack-plugin": "12.0.2", "css-loader": "6.11.0", "cssnano": "7.0.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aebe75436..d0fb6ef7c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -132,9 +132,6 @@ importers: babel-loader: specifier: 9.2.1 version: 9.2.1(@babel/core@7.26.0)(webpack@5.97.0) - clean-webpack-plugin: - specifier: 4.0.0 - version: 4.0.0(webpack@5.97.0) copy-webpack-plugin: specifier: 12.0.2 version: 12.0.2(webpack@5.97.0) @@ -896,14 +893,6 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@isaacs/balanced-match@4.0.1': - resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} - engines: {node: 20 || >=22} - - '@isaacs/brace-expansion@5.0.0': - resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} - engines: {node: 20 || >=22} - '@istanbuljs/load-nyc-config@1.1.0': resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} @@ -1368,9 +1357,6 @@ packages: '@types/express@4.17.23': resolution: {integrity: sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==} - '@types/glob@7.2.0': - resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} - '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} @@ -1407,10 +1393,6 @@ packages: '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - '@types/minimatch@6.0.0': - resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==} - deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed. - '@types/node-forge@1.3.14': resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==} @@ -1695,14 +1677,6 @@ packages: resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} engines: {node: '>= 0.4'} - array-union@1.0.2: - resolution: {integrity: sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==} - engines: {node: '>=0.10.0'} - - array-uniq@1.0.3: - resolution: {integrity: sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==} - engines: {node: '>=0.10.0'} - array.prototype.findlast@1.2.5: resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} engines: {node: '>= 0.4'} @@ -1937,12 +1911,6 @@ packages: resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} engines: {node: '>= 10.0'} - clean-webpack-plugin@4.0.0: - resolution: {integrity: sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==} - engines: {node: '>=10.0.0'} - peerDependencies: - webpack: '>=4.0.0 <6.0.0' - cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -2212,10 +2180,6 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} - del@4.1.1: - resolution: {integrity: sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==} - engines: {node: '>=6'} - depd@1.1.2: resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} engines: {node: '>= 0.6'} @@ -2700,10 +2664,6 @@ packages: resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} engines: {node: '>=18'} - globby@6.1.0: - resolution: {integrity: sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==} - engines: {node: '>=0.10.0'} - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -3008,18 +2968,6 @@ packages: resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} engines: {node: '>=0.10.0'} - is-path-cwd@2.2.0: - resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==} - engines: {node: '>=6'} - - is-path-in-cwd@2.1.0: - resolution: {integrity: sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==} - engines: {node: '>=6'} - - is-path-inside@2.1.0: - resolution: {integrity: sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==} - engines: {node: '>=6'} - is-plain-obj@3.0.0: resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} engines: {node: '>=10'} @@ -3522,10 +3470,6 @@ packages: minimalistic-assert@1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - minimatch@10.0.3: - resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} - engines: {node: 20 || >=22} - minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -3691,10 +3635,6 @@ packages: resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - p-map@2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - p-retry@6.2.1: resolution: {integrity: sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==} engines: {node: '>=16.17'} @@ -3752,9 +3692,6 @@ packages: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} - path-is-inside@1.0.2: - resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -3792,22 +3729,10 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} - pify@2.3.0: - resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} - engines: {node: '>=0.10.0'} - pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} - pinkie-promise@2.0.1: - resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} - engines: {node: '>=0.10.0'} - - pinkie@2.0.4: - resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} - engines: {node: '>=0.10.0'} - pirates@4.0.7: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} @@ -4301,11 +4226,6 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - rollup@2.79.2: resolution: {integrity: sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==} engines: {node: '>=10.0.0'} @@ -5968,12 +5888,6 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@isaacs/balanced-match@4.0.1': {} - - '@isaacs/brace-expansion@5.0.0': - dependencies: - '@isaacs/balanced-match': 4.0.1 - '@istanbuljs/load-nyc-config@1.1.0': dependencies: camelcase: 5.3.1 @@ -6669,11 +6583,6 @@ snapshots: '@types/qs': 6.14.0 '@types/serve-static': 1.15.8 - '@types/glob@7.2.0': - dependencies: - '@types/minimatch': 6.0.0 - '@types/node': 24.5.2 - '@types/graceful-fs@4.1.9': dependencies: '@types/node': 24.5.2 @@ -6708,10 +6617,6 @@ snapshots: '@types/mime@1.3.5': {} - '@types/minimatch@6.0.0': - dependencies: - minimatch: 10.0.3 - '@types/node-forge@1.3.14': dependencies: '@types/node': 24.5.2 @@ -7055,12 +6960,6 @@ snapshots: is-string: 1.1.1 math-intrinsics: 1.1.0 - array-union@1.0.2: - dependencies: - array-uniq: 1.0.3 - - array-uniq@1.0.3: {} - array.prototype.findlast@1.2.5: dependencies: call-bind: 1.0.8 @@ -7371,11 +7270,6 @@ snapshots: dependencies: source-map: 0.6.1 - clean-webpack-plugin@4.0.0(webpack@5.97.0): - dependencies: - del: 4.1.1 - webpack: 5.97.0(webpack-cli@5.1.4) - cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -7673,16 +7567,6 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 - del@4.1.1: - dependencies: - '@types/glob': 7.2.0 - globby: 6.1.0 - is-path-cwd: 2.2.0 - is-path-in-cwd: 2.1.0 - p-map: 2.1.0 - pify: 4.0.1 - rimraf: 2.7.1 - depd@1.1.2: {} depd@2.0.0: {} @@ -8298,14 +8182,6 @@ snapshots: slash: 5.1.0 unicorn-magic: 0.3.0 - globby@6.1.0: - dependencies: - array-union: 1.0.2 - glob: 7.2.3 - object-assign: 4.1.1 - pify: 2.3.0 - pinkie-promise: 2.0.1 - gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -8586,16 +8462,6 @@ snapshots: is-obj@1.0.1: {} - is-path-cwd@2.2.0: {} - - is-path-in-cwd@2.1.0: - dependencies: - is-path-inside: 2.1.0 - - is-path-inside@2.1.0: - dependencies: - path-is-inside: 1.0.2 - is-plain-obj@3.0.0: {} is-plain-object@2.0.4: @@ -9273,10 +9139,6 @@ snapshots: minimalistic-assert@1.0.1: {} - minimatch@10.0.3: - dependencies: - '@isaacs/brace-expansion': 5.0.0 - minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 @@ -9443,8 +9305,6 @@ snapshots: dependencies: p-limit: 4.0.0 - p-map@2.1.0: {} - p-retry@6.2.1: dependencies: '@types/retry': 0.12.2 @@ -9497,8 +9357,6 @@ snapshots: path-is-absolute@1.0.1: {} - path-is-inside@1.0.2: {} - path-key@3.1.1: {} path-parse@1.0.7: {} @@ -9523,15 +9381,8 @@ snapshots: picomatch@4.0.3: {} - pify@2.3.0: {} - - pify@4.0.1: {} - - pinkie-promise@2.0.1: - dependencies: - pinkie: 2.0.4 - - pinkie@2.0.4: {} + pify@4.0.1: + optional: true pirates@4.0.7: {} @@ -10010,10 +9861,6 @@ snapshots: reusify@1.1.0: {} - rimraf@2.7.1: - dependencies: - glob: 7.2.3 - rollup@2.79.2: optionalDependencies: fsevents: 2.3.3 diff --git a/webpack.config.js b/webpack.config.js index df9835c87..b1f35a0b6 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -7,7 +7,6 @@ const webpack = require('webpack'); const threadLoader = require('thread-loader'); const HtmlWebPackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const WorkboxPlugin = require('workbox-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin'); @@ -44,7 +43,8 @@ module.exports = (env, argv) => ({ }, output: { path: path.join(__dirname, 'build'), - filename: `${COMMIT_HASH}/scripts/[name].js` + filename: `${COMMIT_HASH}/scripts/[name].js`, + clean: true, }, module: { rules: [ @@ -221,9 +221,6 @@ module.exports = (env, argv) => ({ new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] }), - new CleanWebpackPlugin({ - cleanOnceBeforeBuildPatterns: ['*'] - }), argv.mode === 'production' && new WorkboxPlugin.GenerateSW({ maximumFileSizeToCacheInBytes: 20000000, From a11df877a626b5ece46ae6d38c682c1ad3775179 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 14 Jan 2026 04:15:41 +0100 Subject: [PATCH 103/117] chore: remove deprecated webpack-pwa-manifest --- images/icon_196x196.png | Bin 0 -> 9252 bytes images/icon_512x512.png | Bin 0 -> 27344 bytes images/maskable_icon_196x196.png | Bin 0 -> 7028 bytes images/maskable_icon_512x512.png | Bin 0 -> 18585 bytes manifest.json | 59 +++ package.json | 1 - pnpm-lock.yaml | 771 ------------------------------- src/index.html | 1 + webpack.config.js | 53 +-- 9 files changed, 61 insertions(+), 824 deletions(-) create mode 100644 images/icon_196x196.png create mode 100644 images/icon_512x512.png create mode 100644 images/maskable_icon_196x196.png create mode 100644 images/maskable_icon_512x512.png create mode 100644 manifest.json diff --git a/images/icon_196x196.png b/images/icon_196x196.png new file mode 100644 index 0000000000000000000000000000000000000000..d5bc3e90f3bd490ef2c8aa0683bf3568c6b01d8a GIT binary patch literal 9252 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 literal 0 HcmV?d00001 diff --git a/images/maskable_icon_196x196.png b/images/maskable_icon_196x196.png new file mode 100644 index 0000000000000000000000000000000000000000..eb84355ad5f6a7f57f4654dc84c37baf6640a7d8 GIT binary patch literal 7028 zcmc(Ec|4Ts`~Q$+&sJuN##pDCG)rN!gc#dIL+6wb%~&dBO_4Cx22Yc+G}b=Hlt?H} zWi2w6803%&5keG_tq8w+M(2Fa>-YWR`{(zM8PCge-`9Oz@9TYC@9TLc?ud=Kq}Vnw z7z`$9X<=#y{&IL95n=FMlgspi!M5vJni?Ms5}2$F_wAd{8kiYdo?V$bcEa)J!#%VN z5zHro2w^{=ZVhny;wc9TjE1qv>bvOI0-Et}&B5|RYT*ZQI+J3!<*P_J@%*)fq zIT;6~d``kijd(YO=l9q?Dwu~wB;@_B zLM}P8Qs*>5B>9XQRF#i?9ZhyHa-1Fha_+tP2U+s5&MAYCyrD8^`fy3rEnX2njY9X{Pk>ZVenqCv!*<; z{C5TCmTV=y*^`q(l;kfp8CwXA3y#@CtF7%aWkR#+G8$+4zlJz{B3k+6%K0oP_CH_| zd$bK2+VryP9h$BbSUc6-yI92oj6T@PtjkC_-)dXpH7g*fFmuY1RPeTRxcyz(MVX3AIB;61QhQp{R|fgS?ftGD)ysPO_CMJWt3#0T5r&=|4te+bGRzYtZst66V&O~B zjcZ=LGae0thZE9hAuaZO&wmgF6&}4(`~3UtC+74+zsW;Eonv3`d+-ts=OYyvb6=~1 zX`pxVKlzW8uvHVps{0@VZAIN1gtTf+jv<}fdEx%~M5*|)l#dm_=>!Osca!?aqPuW( zae7rey?aVn!so11u}a-(ul|;z?oeHYlDEGS@CqcLzbg`Lb@4;WsdGNT@y>-Hw;XDU zNYc3zYgmO4W=BTw=3@P;3N0Ade|UkpINuTY9Ew}vp z)=n+=*0;mzGCq9$_T;htOFrW`cDMc^#--uIzCo;we?Hc)Ez(<%D`Sl00IHi$uSy-h zHc20hsH1t!p1k;YM~xE$CLkyzBCTwMF$j$FdRIDZmc9*H6Qvw~Qwd}5!94#|zrLt@ z%vY=S3Sn#Pzgc7R5QWE8!S2$uh)mwGF#*8DQY(!^Cs+xB(VH8KG>zre^^OZm(5zdM`YM0G+RVsi+-wj&=ZlDR zsiqM<4CwM$it>okylo(ah0VbqEp_WNvPaE#lhWIKikeS5D+w-fAOE}Yo9L$ z#f>Y;$BP$&jhn*sSXaHciEG>Z?ZD?GyPEE%rh?QW#N6~I84;S?& zu3uwr^_SD{uZqhi)4G_H2%IVjMazZWiAEmKF6wQq&t~7jXJLNh8~2A=_t=)O0}q0B zW(r)EBw++{jyua5{@5h5_6ezJp$&KTEO9I4>QR`KLM-_kFP8i*2cb$ zs#{8h%YIt57uKPo+pe7AsD?{Fnj$q$I+|$r59!W-rr<;AELV=KGQftFrJUzEykzU2 zKPHmTJ(HTw#}ajE-@yjw!ew7AV|^N)tqs{-|Jljd@1;cacvaT+DA3%(#VS)T>#ic( zieyHbgwtO9hnX8#S>6?pd^9)dt{RVU*7P*?V*qYX6%z2Gg(b;OLU5j;&(pm>OSiuIAbeaEO>JH;-|CN-Y#ioXZ5S7*{a_B?=BrqXkm)42d$cOgh)al@x zlZEZFWsH1KK_0f`$^HeQg6&Ep56)qq3?wO+%i3fqy1O0cLN`=FRt*GlO0X7x{kdvv z_$f5w=%{>xGf%m;?kuJEW*}+Hqbs_%Tn|7!pcv`=koI3=T5)T09R;UOiwCWpOWloR zyu*9cq1BA&3+!01W}EjsBU<)k}VV!HPknbd52L4cRlt>Onh&u zc<(etwBki{nD5Yy>yP!@y1PArvK{5MDFE_7+k%kSHYI}0Yeb==iFY6T>t{Lk+@ifH zJx@u1dQnKo*Z}FUsEnMeI_#30RL>_(0^OnJeCzd&?g~C>^7h~i79VRK@BGBtNjM(P z9+q2-om+4lzkD#kR0cEzw}uOd$1<^&-aTI}wOrQyG2{EAKioZPTl#q30W`Z>bR$cN zt$pzc-%#PwokxKok-rsOyJQRD_n96>#6aVCtBuA=NQXCVn+$w9Z;iX*L~_vYY9l@R zfG|2b-mWiaoQ+Gv`wKDN^}fz_JIF!glx!?P!E3ZKAxo5VP@GIiT(|zITdOW9uh$N8r1}=gS5FWxw zCz%&+jPU#1XyUybWZ^l6t~Q_^fXYvC#3L_jKkyoh0__HL;yiw{fE79}Vtb#4%8jWs z@J^$%c7V1=BoTRjz3zRzqvP!-*6xDe^`_pZxgPbc2mo}XG_%O`JW2L}Q!KZ-ii>R* zt6#kOx@D_MqoBO-?jPSLU0#%n{1PdMryGAO)_*UgO+--9>tD)7yvub-qqC5K^9{(= z*~{6h-^99$0(d${=Y4>QU+clnb&Cw@uT6aOc?FJ#KA*qch;c>vV~h&k^tA@Q36-$4 zFp<5P7_Fv;ihE%CyUOkxDNSM}kq?Xnf4OJ0wMo!HS==oG^N*W}8%x&zo6w}Pek2Ts z%#700zN-JGqh_U2jVZ0GzgF&$ziDO(&XcY=ileVPE0Ef6+i2>)lk)d$C&D~FH<2(wX##~ z#OJ3s#}C$-$d$nM@9T?4+d{ZpbMQGNf(AXXn?<#sfQFPdvLW3 z+D8iqUOKyaLxg@I^-u12;QT8W(b6gjyx0K(|3LWb0vP^4|Nf)>Y9cH`=-;{epDS_e zw;33f&hTuWt4klgp-Q&Zb5R-uF~mq*5J{h6LH0m=?uz$V|Z}a^!DdW$XX2_D< za}sfF-<4ET(LSg1ZtXVkDUpuB7*!ki`2P6I`1j)KtBOEy)P{f+KN5-;^Km$d#3?A2 z`nEE?`PCauC?A+t#M%60HWBUzEX{3(!>ejc(OUtS0+Lc^3$Vm(t`sE?l!AZnLen`19}e$^*+85(Wk;OthK?!78p9GHKAIoIDVk z9#y;|6BVW4kY}>>VS@9)A(2-VlNr&2R=Z?qAW@PyFud{dYk0+=+F|wcfX8>2wGhS) znXncXQV{Z^?n>h>D_)trZgH3SC@}_edWibwx^ARj;}ak0=-n_{CdKL~Ky;Yn2Efgx zSahWenKPwkZw*Yc3-qi}lr)MJ*+U*b6!#4hP2&=!suvJ;`83Q0lz4X8+j&{@rf)OA zlEyZ;W;0<3V5`VxF+7tsH5ffso}9(-#KF6_C9wKr3|pU)c0*Mx&W_|xC2NGnzHwQ` zp|(jb3RMFEmJiHY{$?h)<(tT;Fj_VjwvG?R|D zrc;{oVg+&zF5K3nM6*$U0FstpF9=hW$TZV;v$WR>`!F@Vln&>lUGxciO*=dfonG zZy;2!;npE@t1_Ic=cDU=e))iLS)?C`KN7c}!j-lM`NL5?)Pm;27iO!UxBA}IY0;%{ z^GrW7s0~LYa1D9c^a!$o&yFkUJg?Y^W|mG z3sBg5?Hh8<8hLQ-d6dOR1d)US9h!qyu|mXfbU|t{NrrWMT|&|?0JMHY*T z271J*ci4{}Ps@AEQdSJ+?2y&sb>d7c+uduEaZbcbi25RrgVxwZFh$`A`Oh%nD~q93 za}zLC_D4iL52_og%{Z&L0cmx}gk+-;nyPv=TO;d08&#o1$Z32@Ml5*gT;3_TY;x5Mh^`92BIIeM5DK;g^&7$Elf12fqwYul{j+RREBRXF>$@ zLR$#br=0LoCGqm7GkC!DEhy{QPve*4Tz0p#o_A3t?@Lr2Lfkb~1hqp>_B?v=TNifk zmBn4=Hd3@?SstAfW!f2_92$T_T%#q4MyW6+c|zCPvPrhHcLjg1pPunFR7s(zhM{SN z@Z;!$Gu5F?@^kb)eaz}$ip0ROF|rx}WQ9lp7C?b=2%sSJHwfdM6%T$V;`4_w`}w09 zRe0G3pJ>>!U*`+Q>6e&^A^4ijYrYiTQ<4xvv^pQN=v{D63tyoHeJx%sxwW?7AG>Od zGUD)uk5iy$H}kL7H;725NV>JnwAHpJSt`+XmVlR0oSSY=m8*-VdzSV`nr;=i!e+-${T1Nn`om0)@N&O9HZ;}Cy9^2Z zq@D#{i9sqV+4Kv~gVu)7QFSIjSX7v}?R){q)yUQE8fbm_#?U+*z{a1EQy{7_lN{2w8r!@-QcK3c@WaHRkK9}S+!UzqG264tkDP@ z=G=r&oCr%RWn}a`ixod+*95~gpf#Ea{_AP7LMS5u8PjIAHwZ;Q12TRPm5Qf9@aC%a z%1zH}fQ1%g-oTj}@yD}Eh%b7L&D$?hwQ6;7#=tQuo}I|gNYJ{@CCk#*^%8AM$AT++ z;O9|SZ1VU}b(D*%av(IK;`UBDe+qdcRJA3xFx)Efv@4#?LaT`Z_PLXoH&k~K;^V$a zP%M%?<>maK+Gm6dJC;GPfyA{s3QDkbK#*kO~QtOPJ8|H``?uP*sy;5i!0=O~|;^?yT~!=nE&N37T! z8uv$+Ulk5@vfpU2#mOW2H^*o3)(`F^$}~#5i$K&I{GpGGAdh6}83`I5cS<)jRU91e zJE2r1iaIHPd$l5lbbZjXbgvBiQdh?OBYXzT8;LlZWW0vOQ8%)*N;?{Vlh|H=D4f5C zit1ORK3vvxGIn}8V(S|x9FHJ22m$RU%ge@q!@yotvoxF*+v!<;;d={o$L2$d*bG(j z;&YSDzVEw>hhA(UOB2C3YxNNx0|kNge3sS=L2eagSH53ISZ38EC=8{bVk^R}_ARw8(}EYAT;Qf8U%l!ux~9!LyHtI$KL4D3mS&|?c->R+f0o8s8XXf=Nz z50R867MYA~-adZNwtdhOemJWp;{@Z_MqJ;D{StkP)1*c)V1S}=4~JPsuOecRm~we9%IyFaU@QQM}>t_SZqg^ zXw^haP^U7f!bLqwwR}EU-+(oM51Ixp$TAkZbGP^8y!SYBeg$&QR7JZbA%{zj0(x5Amn(`g)>gR zH%Anfn%2xX5TO%*>VM!?>2k`#LDuoM`Jh+>NF!Ax8)w6dl2($oh|sUveAnhfs|`cP zS|w!FQ=)oAZ7d^5T94j?#o-Y?vsxb7Leq_-WK}q#(2}nF` zbktm4mM7n=zdRAdE3o-2PJtwN1%6r~p#5qW zR(}Kcw?b~L-Jr#O`oojLyW=0U|K8Lwc<^inHZ!+7*|71`;0k(P>f|J`il}^H)f4*p ztrz>3ZDHP;KD!uc1pEn5U@ z&-3B)GG#)o|EF)ztm&tp?d#rAcUj%0&CRc%17&?y*WcV99Jl}UVz2UvsKU1}tvgM= z$ajQ;ZgaN==v;@YA6tf6_2#o&=dKkeI(I}nFZ$(G?npMOh0>_X4~27XP0S(@3H7MZw4{y&e1W7_}# literal 0 HcmV?d00001 diff --git a/images/maskable_icon_512x512.png b/images/maskable_icon_512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..804b24a20e845782916a614181ef6a69088f09bf GIT binary patch literal 18585 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

?{NeiGmz$r`OwO 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⁡(U>;ypz8qN{s1qvSPbb!!MF;MeGRVA$3D=wA$9;I%& z41-zd-sbuAk@Z5Oh`{qSR#-?*G4HM04Y;8#S7{+v37W`|judLcXQqmR9J7j8 zrGT>n!3=rgu~%<4F!44+9y?|UgG(+`eNNd5Tx zLvxGG;nQJa@F8zfsVKNvKW=ODNO!-KYqE#VEUcvi(T6+9%R(B z)rYA0FkNro=gCFn4XG3_2W;o+s9IJ%45H(?s1#D<6uX(I^}}YZ5uvJA7aq#B!T3o( zNYxP}hE&h%s#+!T{z;KFC9={gsm3D2!BI~}6*ZP?kma+{vOUL^6=O>yX~_LLmB{`y ze5nTM8@|~iG&=9j7n`Xew??XlB~mhXAOL@~-+h6ixklVjl?X%>O5UZ*N69TmE$bku&5^rz%qun+N#LNz!TIfp142qu+d@B%xf zv%FEx#@ocb(#p@ZQ54j;e2gk;v;aiIuesF4o#Oc@)0cso`P^X+Q3H^G@0xg(TcD4i zHFbvp$XZ$HvDSki^3=&bDLQll2jt*Z^ zdaMujsm{`|hW06MyS5tOF(um6DoGWEJ$+GWGcfk;`w?#f5bhP1;MgwFQ%oP|LL-jq z6vtiz)I0CPM&+NcEyLL**n^*{KL}b70wgfPidIQT>-CS0%`m|j{}KJw2wS#-9N`=6VzBE<`=}K>boQqyo5b- z3*nEQa9n6WGqc`jVoeTkpi`kx9Q4b4^f7K_LpFa^qG}nvQeUm2L{Ur)64FPLrTUP8 u{!d&cP*PILbosx}|3l#a4+1#EBV2QU1Reh?@V^{7q^7K?^zM?e-~R*48CR76 literal 0 HcmV?d00001 diff --git a/manifest.json b/manifest.json new file mode 100644 index 000000000..3505fb7c4 --- /dev/null +++ b/manifest.json @@ -0,0 +1,59 @@ +{ + "name": "Stremio Web", + "short_name": "Stremio", + "description": "Freedom To Stream", + "background_color": "#161523", + "theme_color": "#2a2843", + "orientation": "any", + "display": "standalone", + "display_override": ["standalone"], + "scope": "./", + "start_url": "./", + "icons": [ + { + "src": "favicons/icon_256x256.ico", + "sizes": "256x256", + "type": "image/vnd.microsoft.icon" + }, + { + "src": "images/maskable_icon_512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "images/maskable_icon_196x196.png", + "sizes": "196x196", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "images/icon_512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any" + }, + { + "src": "images/icon_196x196.png", + "sizes": "196x196", + "type": "image/png", + "purpose": "any" + } + ], + "screenshots": [ + { + "src": "screenshots/board_wide.webp", + "sizes": "1440x900", + "type": "image/webp", + "form_factor": "wide", + "label": "Homescreen of Stremio" + }, + { + "src": "screenshots/board_narrow.webp", + "sizes": "414x896", + "type": "image/webp", + "form_factor": "narrow", + "label": "Homescreen of Stremio" + } + ] +} \ No newline at end of file diff --git a/package.json b/package.json index 4e2b8ee5b..8b6efe5cc 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,6 @@ "webpack": "5.97.0", "webpack-cli": "5.1.4", "webpack-dev-server": "^5.1.0", - "webpack-pwa-manifest": "^4.3.0", "workbox-webpack-plugin": "^7.3.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d0fb6ef7c..48afa6562 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -201,9 +201,6 @@ importers: webpack-dev-server: specifier: ^5.1.0 version: 5.2.2(webpack-cli@5.1.4)(webpack@5.97.0) - webpack-pwa-manifest: - specifier: ^4.3.0 - version: 4.3.0 workbox-webpack-plugin: specifier: ^7.3.0 version: 7.3.0(@types/babel__core@7.20.5)(webpack@5.97.0) @@ -967,171 +964,6 @@ packages: resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jimp/bmp@0.16.13': - resolution: {integrity: sha512-9edAxu7N2FX7vzkdl5Jo1BbACfycUtBQX+XBMcHA2bk62P8R0otgkHg798frgAk/WxQIzwxqOH6wMiCwrlAzdQ==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/core@0.16.13': - resolution: {integrity: sha512-qXpA1tzTnlkTku9yqtuRtS/wVntvE6f3m3GNxdTdtmc+O+Wcg9Xo2ABPMh7Nc0AHbMKzwvwgB2JnjZmlmJEObg==} - - '@jimp/custom@0.16.13': - resolution: {integrity: sha512-LTATglVUPGkPf15zX1wTMlZ0+AU7cGEGF6ekVF1crA8eHUWsGjrYTB+Ht4E3HTrCok8weQG+K01rJndCp/l4XA==} - - '@jimp/gif@0.16.13': - resolution: {integrity: sha512-yFAMZGv3o+YcjXilMWWwS/bv1iSqykFahFMSO169uVMtfQVfa90kt4/kDwrXNR6Q9i6VHpFiGZMlF2UnHClBvg==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/jpeg@0.16.13': - resolution: {integrity: sha512-BJHlDxzTlCqP2ThqP8J0eDrbBfod7npWCbJAcfkKqdQuFk0zBPaZ6KKaQKyKxmWJ87Z6ohANZoMKEbtvrwz1AA==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/plugin-blit@0.16.13': - resolution: {integrity: sha512-8Z1k96ZFxlhK2bgrY1JNWNwvaBeI/bciLM0yDOni2+aZwfIIiC7Y6PeWHTAvjHNjphz+XCt01WQmOYWCn0ML6g==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/plugin-blur@0.16.13': - resolution: {integrity: sha512-PvLrfa8vkej3qinlebyhLpksJgCF5aiysDMSVhOZqwH5nQLLtDE9WYbnsofGw4r0VVpyw3H/ANCIzYTyCtP9Cg==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/plugin-circle@0.16.13': - resolution: {integrity: sha512-RNave7EFgZrb5V5EpdvJGAEHMnDAJuwv05hKscNfIYxf0kR3KhViBTDy+MoTnMlIvaKFULfwIgaZWzyhuINMzA==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/plugin-color@0.16.13': - resolution: {integrity: sha512-xW+9BtEvoIkkH/Wde9ql4nAFbYLkVINhpgAE7VcBUsuuB34WUbcBl/taOuUYQrPEFQJ4jfXiAJZ2H/rvKjCVnQ==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/plugin-contain@0.16.13': - resolution: {integrity: sha512-QayTXw4tXMwU6q6acNTQrTTFTXpNRBe+MgTGMDU0lk+23PjlFCO/9sacflelG8lsp7vNHhAxFeHptDMAksEYzg==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - '@jimp/plugin-blit': '>=0.3.5' - '@jimp/plugin-resize': '>=0.3.5' - '@jimp/plugin-scale': '>=0.3.5' - - '@jimp/plugin-cover@0.16.13': - resolution: {integrity: sha512-BSsP71GTNaqWRcvkbWuIVH+zK7b3TSNebbhDkFK0fVaUTzHuKMS/mgY4hDZIEVt7Rf5FjadAYtsujHN9w0iSYA==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - '@jimp/plugin-crop': '>=0.3.5' - '@jimp/plugin-resize': '>=0.3.5' - '@jimp/plugin-scale': '>=0.3.5' - - '@jimp/plugin-crop@0.16.13': - resolution: {integrity: sha512-WEl2tPVYwzYL8OKme6Go2xqiWgKsgxlMwyHabdAU4tXaRwOCnOI7v4021gCcBb9zn/oWwguHuKHmK30Fw2Z/PA==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/plugin-displace@0.16.13': - resolution: {integrity: sha512-qt9WKq8vWrcjySa9DyQ0x/RBMHQeiVjdVSY1SJsMjssPUf0pS74qorcuAkGi89biN3YoGUgPkpqECnAWnYwgGA==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/plugin-dither@0.16.13': - resolution: {integrity: sha512-5/N3yJggbWQTlGZHQYJPmQXEwR52qaXjEzkp1yRBbtdaekXE3BG/suo0fqeoV/csf8ooI78sJzYmIrxNoWVtgQ==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/plugin-fisheye@0.16.13': - resolution: {integrity: sha512-2rZmTdFbT/cF9lEZIkXCYO0TsT114Q27AX5IAo0Sju6jVQbvIk1dFUTnwLDadTo8wkJlFzGqMQ24Cs8cHWOliA==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/plugin-flip@0.16.13': - resolution: {integrity: sha512-EmcgAA74FTc5u7Z+hUO/sRjWwfPPLuOQP5O64x5g4j0T12Bd29IgsYZxoutZo/rb3579+JNa/3wsSEmyVv1EpA==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - '@jimp/plugin-rotate': '>=0.3.5' - - '@jimp/plugin-gaussian@0.16.13': - resolution: {integrity: sha512-A1XKfGQD0iDdIiKqFYi8nZMv4dDVYdxbrmgR7y/CzUHhSYdcmoljLIIsZZM3Iks/Wa353W3vtvkWLuDbQbch1w==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/plugin-invert@0.16.13': - resolution: {integrity: sha512-xFMrIn7czEZbdbMzZWuaZFnlLGJDVJ82y5vlsKsXRTG2kcxRsMPXvZRWHV57nSs1YFsNqXSbrC8B98n0E32njQ==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/plugin-mask@0.16.13': - resolution: {integrity: sha512-wLRYKVBXql2GAYgt6FkTnCfE+q5NomM7Dlh0oIPGAoMBWDyTx0eYutRK6PlUrRK2yMHuroAJCglICTbxqGzowQ==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/plugin-normalize@0.16.13': - resolution: {integrity: sha512-3tfad0n9soRna4IfW9NzQdQ2Z3ijkmo21DREHbE6CGcMIxOSvfRdSvf1qQPApxjTSo8LTU4MCi/fidx/NZ0GqQ==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/plugin-print@0.16.13': - resolution: {integrity: sha512-0m6i3p01PGRkGAK9r53hDYrkyMq+tlhLOIbsSTmZyh6HLshUKlTB7eXskF5OpVd5ZUHoltlNc6R+ggvKIzxRFw==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - '@jimp/plugin-blit': '>=0.3.5' - - '@jimp/plugin-resize@0.16.13': - resolution: {integrity: sha512-qoqtN8LDknm3fJm9nuPygJv30O3vGhSBD2TxrsCnhtOsxKAqVPJtFVdGd/qVuZ8nqQANQmTlfqTiK9mVWQ7MiQ==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/plugin-rotate@0.16.13': - resolution: {integrity: sha512-Ev+Jjmj1nHYw897z9C3R9dYsPv7S2/nxdgfFb/h8hOwK0Ovd1k/+yYS46A0uj/JCKK0pQk8wOslYBkPwdnLorw==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - '@jimp/plugin-blit': '>=0.3.5' - '@jimp/plugin-crop': '>=0.3.5' - '@jimp/plugin-resize': '>=0.3.5' - - '@jimp/plugin-scale@0.16.13': - resolution: {integrity: sha512-05POQaEJVucjTiSGMoH68ZiELc7QqpIpuQlZ2JBbhCV+WCbPFUBcGSmE7w4Jd0E2GvCho/NoMODLwgcVGQA97A==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - '@jimp/plugin-resize': '>=0.3.5' - - '@jimp/plugin-shadow@0.16.13': - resolution: {integrity: sha512-nmu5VSZ9hsB1JchTKhnnCY+paRBnwzSyK5fhkhtQHHoFD5ArBQ/5wU8y6tCr7k/GQhhGq1OrixsECeMjPoc8Zw==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - '@jimp/plugin-blur': '>=0.3.5' - '@jimp/plugin-resize': '>=0.3.5' - - '@jimp/plugin-threshold@0.16.13': - resolution: {integrity: sha512-+3zArBH0OE3Rhjm4HyAokMsZlIq5gpQec33CncyoSwxtRBM2WAhUVmCUKuBo+Lr/2/4ISoY4BWpHKhMLDix6cA==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - '@jimp/plugin-color': '>=0.8.0' - '@jimp/plugin-resize': '>=0.8.0' - - '@jimp/plugins@0.16.13': - resolution: {integrity: sha512-CJLdqODEhEVs4MgWCxpWL5l95sCBlkuSLz65cxEm56X5akIsn4LOlwnKoSEZioYcZUBvHhCheH67AyPTudfnQQ==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/png@0.16.13': - resolution: {integrity: sha512-8cGqINvbWJf1G0Her9zbq9I80roEX0A+U45xFby3tDWfzn+Zz8XKDF1Nv9VUwVx0N3zpcG1RPs9hfheG4Cq2kg==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/tiff@0.16.13': - resolution: {integrity: sha512-oJY8d9u95SwW00VPHuCNxPap6Q1+E/xM5QThb9Hu+P6EGuu6lIeLaNBMmFZyblwFbwrH+WBOZlvIzDhi4Dm/6Q==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/types@0.16.13': - resolution: {integrity: sha512-mC0yVNUobFDjoYLg4hoUwzMKgNlxynzwt3cDXzumGvRJ7Kb8qQGOWJQjQFo5OxmGExqzPphkirdbBF88RVLBCg==} - peerDependencies: - '@jimp/custom': '>=0.3.5' - - '@jimp/utils@0.16.13': - resolution: {integrity: sha512-VyCpkZzFTHXtKgVO35iKN0sYR10psGpV6SkcSeV4oF7eSYlR8Bl6aQLCzVeFjvESF7mxTmIiI3/XrMobVrtxDA==} - '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1312,9 +1144,6 @@ packages: '@surma/rollup-plugin-off-main-thread@2.2.3': resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} - '@tokenizer/token@0.3.0': - resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -1396,9 +1225,6 @@ packages: '@types/node-forge@1.3.14': resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==} - '@types/node@16.9.1': - resolution: {integrity: sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==} - '@types/node@24.5.2': resolution: {integrity: sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==} @@ -1590,10 +1416,6 @@ packages: a-color-picker@1.2.1: resolution: {integrity: sha512-aMCUKd2zTDWK2YWnjz0k3YhFc9XL0jZlPIywF6XkP6i3wdq2iHTEnl1BFPZkOVDV89M12t+zeZ8m23cfzn57/Q==} - abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} - accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -1653,9 +1475,6 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} - any-base@1.1.0: - resolution: {integrity: sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==} - anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} @@ -1790,9 +1609,6 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - bmp-js@0.1.0: - resolution: {integrity: sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==} - body-parser@1.20.3: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -1824,16 +1640,9 @@ packages: bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - buffer-equal@0.0.1: - resolution: {integrity: sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==} - engines: {node: '>=0.4.0'} - buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -1878,9 +1687,6 @@ packages: caniuse-lite@1.0.30001763: resolution: {integrity: sha512-mh/dGtq56uN98LlNX9qdbKnzINhX0QzhiWBFEkFfsFO4QyCvL8YegrJAazCwXIeqkIob8BlZPGM3xdnY+sgmvQ==} - centra@2.7.0: - resolution: {integrity: sha512-PbFMgMSrmgx6uxCdm57RUos9Tc3fclMvhLSATYN39XsDV29B89zZ3KA89jmY0vwSGazyU+uerqwa6t+KaodPcg==} - chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -2038,9 +1844,6 @@ packages: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} engines: {node: '>=8'} - css-color-names@1.0.1: - resolution: {integrity: sha512-/loXYOch1qU1biStIFsHH8SxTmOseh1IJqFvy8IujXOm1h+QjUdDhkzOrR5HG8K8mlxREj0yfi8ewCHx0eMxzA==} - css-declaration-sorter@7.3.0: resolution: {integrity: sha512-LQF6N/3vkAMYF4xoHLJfG718HRJh34Z8BnNhd6bosOMIVjMlhuZK5++oZa3uYAgrI5+7x2o27gUqTR2U/KjUOQ==} engines: {node: ^14 || ^16 || >=18} @@ -2223,9 +2026,6 @@ packages: dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} - dom-walk@0.1.2: - resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==} - domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} @@ -2424,10 +2224,6 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} - event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} - eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} @@ -2442,9 +2238,6 @@ packages: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} - exif-parser@0.1.12: - resolution: {integrity: sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==} - exit@0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} @@ -2495,10 +2288,6 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} - file-type@16.5.4: - resolution: {integrity: sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==} - engines: {node: '>=10'} - filelist@1.0.4: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} @@ -2621,9 +2410,6 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - gifwrap@0.9.4: - resolution: {integrity: sha512-MDMwbhASQuVeD4JKd1fKgNgCRL3fGqMM4WaqpNhWO0JiMOAjbQdumbs4BbBZEy9/M00EHEjKN3HieVhCUlwjeQ==} - glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -2645,9 +2431,6 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported - global@4.4.0: - resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==} - globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -2815,9 +2598,6 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} - image-q@4.0.0: - resolution: {integrity: sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==} - image-size@0.5.5: resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} engines: {node: '>=0.10.0'} @@ -2921,9 +2701,6 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-function@1.0.2: - resolution: {integrity: sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==} - is-generator-fn@2.1.0: resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} engines: {node: '>=6'} @@ -3206,16 +2983,10 @@ packages: node-notifier: optional: true - jimp@0.16.1: - resolution: {integrity: sha512-+EKVxbR36Td7Hfd23wKGIeEyHbxShZDX6L8uJkgVW3ESA9GiTEPK08tG1XI2r/0w5Ch0HyJF5kPqF9K7EmGjaw==} - jiti@1.21.7: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true - jpeg-js@0.4.4: - resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==} - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -3320,9 +3091,6 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - load-bmfont@1.4.2: - resolution: {integrity: sha512-qElWkmjW9Oq1F9EI5Gt7aD9zcdHb9spJCW1L/dmPf7KzCCEJxq8nhHz5eCgI9aMf7vrG/wyaCqdsI+Iy9ZTlog==} - loader-runner@4.3.0: resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} engines: {node: '>=6.11.5'} @@ -3449,18 +3217,10 @@ packages: engines: {node: '>=4'} hasBin: true - mime@2.4.6: - resolution: {integrity: sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==} - engines: {node: '>=4.0.0'} - hasBin: true - mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} - min-document@2.19.0: - resolution: {integrity: sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==} - mini-css-extract-plugin@2.9.2: resolution: {integrity: sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==} engines: {node: '>= 12.13.0'} @@ -3481,13 +3241,6 @@ packages: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -3581,9 +3334,6 @@ packages: obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} - omggif@1.0.10: - resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==} - on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -3643,9 +3393,6 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - pako@1.0.11: - resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - param-case@3.0.4: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} @@ -3653,18 +3400,6 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - parse-bmfont-ascii@1.0.6: - resolution: {integrity: sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==} - - parse-bmfont-binary@1.0.6: - resolution: {integrity: sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==} - - parse-bmfont-xml@1.1.6: - resolution: {integrity: sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==} - - parse-headers@2.0.6: - resolution: {integrity: sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==} - parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} @@ -3706,18 +3441,6 @@ packages: resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} engines: {node: '>=18'} - peek-readable@4.1.0: - resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==} - engines: {node: '>=8'} - - phin@2.9.3: - resolution: {integrity: sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - - phin@3.7.1: - resolution: {integrity: sha512-GEazpTWwTZaEQ9RhL7Nyz0WwqilbqgLahDM3D0hxWwmVDI52nXEybHqiN6/elwpkJBhcuj+WbBu+QfT0uhPGfQ==} - engines: {node: '>= 8'} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -3737,10 +3460,6 @@ packages: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} - pixelmatch@4.0.2: - resolution: {integrity: sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA==} - hasBin: true - pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} @@ -3749,10 +3468,6 @@ packages: resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} engines: {node: '>=14.16'} - pngjs@3.4.0: - resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==} - engines: {node: '>=4.0.0'} - possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -4009,10 +3724,6 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -4119,14 +3830,6 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} - readable-stream@4.7.0: - resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - readable-web-to-node-stream@3.0.4: - resolution: {integrity: sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==} - engines: {node: '>=8'} - readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -4154,9 +3857,6 @@ packages: regenerate@1.4.2: resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} - regenerator-runtime@0.13.11: - resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} - regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} @@ -4502,10 +4202,6 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - strtok3@6.3.0: - resolution: {integrity: sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==} - engines: {node: '>=10'} - stylehacks@7.0.6: resolution: {integrity: sha512-iitguKivmsueOmTO0wmxURXBP8uqOO+zikLGZ7Mm9e/94R4w5T999Js2taS/KBOnQ/wdC3jN3vNSrkGDrlnqQg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} @@ -4585,15 +4281,9 @@ packages: thunky@1.1.0: resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} - timm@1.7.1: - resolution: {integrity: sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==} - tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} - tinycolor2@1.6.0: - resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} - tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} @@ -4605,10 +4295,6 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - token-types@4.2.1: - resolution: {integrity: sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==} - engines: {node: '>=10'} - tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} @@ -4766,9 +4452,6 @@ packages: '@types/react': optional: true - utif@2.0.1: - resolution: {integrity: sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==} - util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -4858,10 +4541,6 @@ packages: resolution: {integrity: sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==} engines: {node: '>=10.0.0'} - webpack-pwa-manifest@4.3.0: - resolution: {integrity: sha512-3hK8Qg58SyLCUIz4PBYnfUPM6iJ5K88h8Uhc3MxmlJcVtDF/11aBBdUTdQkqc9bo6Cb8Q1v2xdsB2XO6pzTbiA==} - engines: {node: '>=6.0.0'} - webpack-sources@1.4.3: resolution: {integrity: sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==} @@ -5000,24 +4679,6 @@ packages: resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} engines: {node: '>=18'} - xhr@2.6.0: - resolution: {integrity: sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==} - - xml-parse-from-string@1.0.1: - resolution: {integrity: sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==} - - xml2js@0.5.0: - resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==} - engines: {node: '>=4.0.0'} - - xmlbuilder@11.0.1: - resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} - engines: {node: '>=4.0'} - - xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -6060,255 +5721,6 @@ snapshots: '@types/yargs': 17.0.33 chalk: 4.1.2 - '@jimp/bmp@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/utils': 0.16.13 - bmp-js: 0.1.0 - - '@jimp/core@0.16.13': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/utils': 0.16.13 - any-base: 1.1.0 - buffer: 5.7.1 - exif-parser: 0.1.12 - file-type: 16.5.4 - load-bmfont: 1.4.2 - mkdirp: 0.5.6 - phin: 2.9.3 - pixelmatch: 4.0.2 - tinycolor2: 1.6.0 - transitivePeerDependencies: - - debug - - '@jimp/custom@0.16.13': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/core': 0.16.13 - transitivePeerDependencies: - - debug - - '@jimp/gif@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/utils': 0.16.13 - gifwrap: 0.9.4 - omggif: 1.0.10 - - '@jimp/jpeg@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/utils': 0.16.13 - jpeg-js: 0.4.4 - - '@jimp/plugin-blit@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/utils': 0.16.13 - - '@jimp/plugin-blur@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/utils': 0.16.13 - - '@jimp/plugin-circle@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/utils': 0.16.13 - - '@jimp/plugin-color@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/utils': 0.16.13 - tinycolor2: 1.6.0 - - '@jimp/plugin-contain@0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-blit@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-scale@0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13)))': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/plugin-blit': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-resize': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-scale': 0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13)) - '@jimp/utils': 0.16.13 - - '@jimp/plugin-cover@0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-crop@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-scale@0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13)))': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/plugin-crop': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-resize': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-scale': 0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13)) - '@jimp/utils': 0.16.13 - - '@jimp/plugin-crop@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/utils': 0.16.13 - - '@jimp/plugin-displace@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/utils': 0.16.13 - - '@jimp/plugin-dither@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/utils': 0.16.13 - - '@jimp/plugin-fisheye@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/utils': 0.16.13 - - '@jimp/plugin-flip@0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-rotate@0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-blit@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-crop@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13)))': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/plugin-rotate': 0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-blit@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-crop@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13)) - '@jimp/utils': 0.16.13 - - '@jimp/plugin-gaussian@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/utils': 0.16.13 - - '@jimp/plugin-invert@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/utils': 0.16.13 - - '@jimp/plugin-mask@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/utils': 0.16.13 - - '@jimp/plugin-normalize@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/utils': 0.16.13 - - '@jimp/plugin-print@0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-blit@0.16.13(@jimp/custom@0.16.13))': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/plugin-blit': 0.16.13(@jimp/custom@0.16.13) - '@jimp/utils': 0.16.13 - load-bmfont: 1.4.2 - transitivePeerDependencies: - - debug - - '@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/utils': 0.16.13 - - '@jimp/plugin-rotate@0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-blit@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-crop@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13))': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/plugin-blit': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-crop': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-resize': 0.16.13(@jimp/custom@0.16.13) - '@jimp/utils': 0.16.13 - - '@jimp/plugin-scale@0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13))': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/plugin-resize': 0.16.13(@jimp/custom@0.16.13) - '@jimp/utils': 0.16.13 - - '@jimp/plugin-shadow@0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-blur@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13))': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/plugin-blur': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-resize': 0.16.13(@jimp/custom@0.16.13) - '@jimp/utils': 0.16.13 - - '@jimp/plugin-threshold@0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-color@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13))': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/plugin-color': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-resize': 0.16.13(@jimp/custom@0.16.13) - '@jimp/utils': 0.16.13 - - '@jimp/plugins@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/plugin-blit': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-blur': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-circle': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-color': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-contain': 0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-blit@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-scale@0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13))) - '@jimp/plugin-cover': 0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-crop@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-scale@0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13))) - '@jimp/plugin-crop': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-displace': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-dither': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-fisheye': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-flip': 0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-rotate@0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-blit@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-crop@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13))) - '@jimp/plugin-gaussian': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-invert': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-mask': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-normalize': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-print': 0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-blit@0.16.13(@jimp/custom@0.16.13)) - '@jimp/plugin-resize': 0.16.13(@jimp/custom@0.16.13) - '@jimp/plugin-rotate': 0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-blit@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-crop@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13)) - '@jimp/plugin-scale': 0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13)) - '@jimp/plugin-shadow': 0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-blur@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13)) - '@jimp/plugin-threshold': 0.16.13(@jimp/custom@0.16.13)(@jimp/plugin-color@0.16.13(@jimp/custom@0.16.13))(@jimp/plugin-resize@0.16.13(@jimp/custom@0.16.13)) - timm: 1.7.1 - transitivePeerDependencies: - - debug - - '@jimp/png@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/utils': 0.16.13 - pngjs: 3.4.0 - - '@jimp/tiff@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - utif: 2.0.1 - - '@jimp/types@0.16.13(@jimp/custom@0.16.13)': - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/bmp': 0.16.13(@jimp/custom@0.16.13) - '@jimp/custom': 0.16.13 - '@jimp/gif': 0.16.13(@jimp/custom@0.16.13) - '@jimp/jpeg': 0.16.13(@jimp/custom@0.16.13) - '@jimp/png': 0.16.13(@jimp/custom@0.16.13) - '@jimp/tiff': 0.16.13(@jimp/custom@0.16.13) - timm: 1.7.1 - - '@jimp/utils@0.16.13': - dependencies: - '@babel/runtime': 7.26.0 - regenerator-runtime: 0.13.11 - '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -6514,8 +5926,6 @@ snapshots: magic-string: 0.25.9 string.prototype.matchall: 4.0.12 - '@tokenizer/token@0.3.0': {} - '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.28.4 @@ -6621,8 +6031,6 @@ snapshots: dependencies: '@types/node': 24.5.2 - '@types/node@16.9.1': {} - '@types/node@24.5.2': dependencies: undici-types: 7.12.0 @@ -6873,10 +6281,6 @@ snapshots: dependencies: is-plain-object: 2.0.4 - abort-controller@3.0.0: - dependencies: - event-target-shim: 5.0.1 - accepts@1.3.8: dependencies: mime-types: 2.1.35 @@ -6929,8 +6333,6 @@ snapshots: ansi-styles@5.2.0: {} - any-base@1.1.0: {} - anymatch@3.1.3: dependencies: normalize-path: 3.0.0 @@ -7123,8 +6525,6 @@ snapshots: binary-extensions@2.3.0: {} - bmp-js@0.1.0: {} - body-parser@1.20.3: dependencies: bytes: 3.1.2 @@ -7176,15 +6576,8 @@ snapshots: dependencies: node-int64: 0.4.0 - buffer-equal@0.0.1: {} - buffer-from@1.1.2: {} - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - buffer@6.0.3: dependencies: base64-js: 1.5.1 @@ -7233,12 +6626,6 @@ snapshots: caniuse-lite@1.0.30001763: {} - centra@2.7.0: - dependencies: - follow-redirects: 1.15.11 - transitivePeerDependencies: - - debug - chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -7402,8 +6789,6 @@ snapshots: crypto-random-string@2.0.0: {} - css-color-names@1.0.1: {} - css-declaration-sorter@7.3.0(postcss@8.5.6): dependencies: postcss: 8.5.6 @@ -7605,8 +6990,6 @@ snapshots: domhandler: 5.0.3 entities: 4.5.0 - dom-walk@0.1.2: {} - domelementtype@2.3.0: {} domhandler@4.3.1: @@ -7895,8 +7278,6 @@ snapshots: etag@1.8.1: {} - event-target-shim@5.0.1: {} - eventemitter3@4.0.7: {} eventemitter3@5.0.1: {} @@ -7915,8 +7296,6 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 2.0.0 - exif-parser@0.1.12: {} - exit@0.1.2: {} expect@29.7.0: @@ -7999,12 +7378,6 @@ snapshots: dependencies: flat-cache: 4.0.1 - file-type@16.5.4: - dependencies: - readable-web-to-node-stream: 3.0.4 - strtok3: 6.3.0 - token-types: 4.2.1 - filelist@1.0.4: dependencies: minimatch: 5.1.6 @@ -8131,11 +7504,6 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 - gifwrap@0.9.4: - dependencies: - image-q: 4.0.0 - omggif: 1.0.10 - glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -8159,11 +7527,6 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 - global@4.4.0: - dependencies: - min-document: 2.19.0 - process: 0.11.10 - globals@14.0.0: {} globals@15.15.0: {} @@ -8328,10 +7691,6 @@ snapshots: ignore@7.0.5: {} - image-q@4.0.0: - dependencies: - '@types/node': 16.9.1 - image-size@0.5.5: optional: true @@ -8426,8 +7785,6 @@ snapshots: is-fullwidth-code-point@3.0.0: {} - is-function@1.0.2: {} - is-generator-fn@2.1.0: {} is-generator-function@1.1.0: @@ -8895,20 +8252,8 @@ snapshots: - supports-color - ts-node - jimp@0.16.1: - dependencies: - '@babel/runtime': 7.26.0 - '@jimp/custom': 0.16.13 - '@jimp/plugins': 0.16.13(@jimp/custom@0.16.13) - '@jimp/types': 0.16.13(@jimp/custom@0.16.13) - regenerator-runtime: 0.13.11 - transitivePeerDependencies: - - debug - jiti@1.21.7: {} - jpeg-js@0.4.4: {} - js-tokens@4.0.0: {} js-yaml@3.14.1: @@ -8999,19 +8344,6 @@ snapshots: lines-and-columns@1.2.4: {} - load-bmfont@1.4.2: - dependencies: - buffer-equal: 0.0.1 - mime: 1.6.0 - parse-bmfont-ascii: 1.0.6 - parse-bmfont-binary: 1.0.6 - parse-bmfont-xml: 1.1.6 - phin: 3.7.1 - xhr: 2.6.0 - xtend: 4.0.2 - transitivePeerDependencies: - - debug - loader-runner@4.3.0: {} locate-path@5.0.0: @@ -9123,14 +8455,8 @@ snapshots: mime@1.6.0: {} - mime@2.4.6: {} - mimic-fn@2.1.0: {} - min-document@2.19.0: - dependencies: - dom-walk: 0.1.2 - mini-css-extract-plugin@2.9.2(webpack@5.97.0): dependencies: schema-utils: 4.3.2 @@ -9151,12 +8477,6 @@ snapshots: dependencies: brace-expansion: 2.0.2 - minimist@1.2.8: {} - - mkdirp@0.5.6: - dependencies: - minimist: 1.2.8 - ms@2.0.0: {} ms@2.1.3: {} @@ -9243,8 +8563,6 @@ snapshots: obuf@1.1.2: {} - omggif@1.0.10: {} - on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -9313,8 +8631,6 @@ snapshots: p-try@2.2.0: {} - pako@1.0.11: {} - param-case@3.0.4: dependencies: dot-case: 3.0.4 @@ -9324,17 +8640,6 @@ snapshots: dependencies: callsites: 3.1.0 - parse-bmfont-ascii@1.0.6: {} - - parse-bmfont-binary@1.0.6: {} - - parse-bmfont-xml@1.1.6: - dependencies: - xml-parse-from-string: 1.0.1 - xml2js: 0.5.0 - - parse-headers@2.0.6: {} - parse-json@5.2.0: dependencies: '@babel/code-frame': 7.27.1 @@ -9365,16 +8670,6 @@ snapshots: path-type@6.0.0: {} - peek-readable@4.1.0: {} - - phin@2.9.3: {} - - phin@3.7.1: - dependencies: - centra: 2.7.0 - transitivePeerDependencies: - - debug - picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -9386,10 +8681,6 @@ snapshots: pirates@4.0.7: {} - pixelmatch@4.0.2: - dependencies: - pngjs: 3.4.0 - pkg-dir@4.2.0: dependencies: find-up: 4.1.0 @@ -9398,8 +8689,6 @@ snapshots: dependencies: find-up: 6.3.0 - pngjs@3.4.0: {} - possible-typed-array-names@1.1.0: {} postcss-calc@10.1.1(postcss@8.5.6): @@ -9633,8 +8922,6 @@ snapshots: process-nextick-args@2.0.1: {} - process@0.11.10: {} - prompts@2.4.2: dependencies: kleur: 3.0.3 @@ -9744,18 +9031,6 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 - readable-stream@4.7.0: - dependencies: - abort-controller: 3.0.0 - buffer: 6.0.3 - events: 3.3.0 - process: 0.11.10 - string_decoder: 1.3.0 - - readable-web-to-node-stream@3.0.4: - dependencies: - readable-stream: 4.7.0 - readdirp@3.6.0: dependencies: picomatch: 2.3.1 @@ -9791,8 +9066,6 @@ snapshots: regenerate@1.4.2: {} - regenerator-runtime@0.13.11: {} - regenerator-runtime@0.14.1: {} regexp.prototype.flags@1.5.4: @@ -10203,11 +9476,6 @@ snapshots: strip-json-comments@3.1.1: {} - strtok3@6.3.0: - dependencies: - '@tokenizer/token': 0.3.0 - peek-readable: 4.1.0 - stylehacks@7.0.6(postcss@8.5.6): dependencies: browserslist: 4.26.2 @@ -10283,12 +9551,8 @@ snapshots: thunky@1.1.0: {} - timm@1.7.1: {} - tiny-invariant@1.3.3: {} - tinycolor2@1.6.0: {} - tmpl@1.0.5: {} to-regex-range@5.0.1: @@ -10297,11 +9561,6 @@ snapshots: toidentifier@1.0.1: {} - token-types@4.2.1: - dependencies: - '@tokenizer/token': 0.3.0 - ieee754: 1.2.1 - tr46@1.0.1: dependencies: punycode: 2.3.1 @@ -10458,10 +9717,6 @@ snapshots: optionalDependencies: '@types/react': 18.3.24 - utif@2.0.1: - dependencies: - pako: 1.0.11 - util-deprecate@1.0.2: {} utila@0.4.0: {} @@ -10574,14 +9829,6 @@ snapshots: flat: 5.0.2 wildcard: 2.0.1 - webpack-pwa-manifest@4.3.0: - dependencies: - css-color-names: 1.0.1 - jimp: 0.16.1 - mime: 2.4.6 - transitivePeerDependencies: - - debug - webpack-sources@1.4.3: dependencies: source-list-map: 2.0.1 @@ -10828,24 +10075,6 @@ snapshots: dependencies: is-wsl: 3.1.0 - xhr@2.6.0: - dependencies: - global: 4.4.0 - is-function: 1.0.2 - parse-headers: 2.0.6 - xtend: 4.0.2 - - xml-parse-from-string@1.0.1: {} - - xml2js@0.5.0: - dependencies: - sax: 1.4.1 - xmlbuilder: 11.0.1 - - xmlbuilder@11.0.1: {} - - xtend@4.0.2: {} - y18n@5.0.8: {} yallist@3.1.1: {} diff --git a/src/index.html b/src/index.html index 033f9a267..ff8166004 100644 --- a/src/index.html +++ b/src/index.html @@ -7,6 +7,7 @@ + Stremio - Freedom to Stream <%= htmlWebpackPlugin.tags.headTags %> diff --git a/webpack.config.js b/webpack.config.js index b1f35a0b6..5c6df3871 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -10,7 +10,6 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const WorkboxPlugin = require('workbox-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin'); -const WebpackPwaManifest = require('webpack-pwa-manifest'); const packageJson = require('./package.json'); const COMMIT_HASH = execSync('git rev-parse HEAD').toString().trim(); @@ -233,6 +232,7 @@ module.exports = (env, argv) => ({ { from: 'images', to: 'images' }, { from: 'screenshots/*.webp', to: './' }, { from: '.well-known', to: '.well-known' }, + { from: 'manifest.json', to: 'manifest.json' }, ] }), new MiniCssExtractPlugin({ @@ -245,56 +245,5 @@ module.exports = (env, argv) => ({ faviconsPath: 'favicons', imagesPath: 'images', }), - new WebpackPwaManifest({ - name: 'Stremio Web', - short_name: 'Stremio', - description: 'Freedom To Stream', - background_color: '#161523', - theme_color: '#2a2843', - orientation: 'any', - display: 'standalone', - display_override: ['standalone'], - scope: './', - start_url: './', - publicPath: './', - icons: [ - { - src: 'images/icon.png', - destination: 'icons', - sizes: [196, 512], - purpose: 'any' - }, - { - src: 'images/maskable_icon.png', - destination: 'maskable_icons', - sizes: [196, 512], - purpose: 'maskable', - ios: true - }, - { - src: 'favicons/favicon.ico', - destination: 'favicons', - sizes: [256], - } - ], - screenshots : [ - { - src: 'screenshots/board_wide.webp', - sizes: '1440x900', - type: 'image/webp', - form_factor: 'wide', - label: 'Homescreen of Stremio' - }, - { - src: 'screenshots/board_narrow.webp', - sizes: '414x896', - type: 'image/webp', - form_factor: 'narrow', - label: 'Homescreen of Stremio' - } - ], - fingerprints: false, - ios: true - }), ].filter(Boolean) }); From 64b13d60920394c9b79f9032f3200fb23972e178 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 14 Jan 2026 04:34:49 +0100 Subject: [PATCH 104/117] chore: move assets to dedicated folder --- {favicons => assets/favicons}/favicon.ico | Bin {fonts => assets/fonts}/PlusJakartaSans.ttf | Bin {images => assets/images}/anonymous.png | Bin {images => assets/images}/background_1.svg | 0 {images => assets/images}/background_2.svg | 0 {images => assets/images}/calendar_placeholder.png | Bin {images => assets/images}/default_avatar.png | Bin {images => assets/images}/empty.png | Bin {images => assets/images}/icon.png | Bin {images => assets/images}/icon_196x196.png | Bin {images => assets/images}/icon_512x512.png | Bin {images => assets/images}/library_placeholder.png | Bin {images => assets/images}/logo.png | Bin {images => assets/images}/maskable_icon.png | Bin {images => assets/images}/maskable_icon_196x196.png | Bin {images => assets/images}/maskable_icon_512x512.png | Bin {images => assets/images}/stremio_symbol.png | Bin {screenshots => assets/screenshots}/board.png | Bin .../screenshots}/board_narrow.webp | Bin {screenshots => assets/screenshots}/board_wide.webp | Bin {screenshots => assets/screenshots}/discover.png | Bin {screenshots => assets/screenshots}/metadetails.png | Bin src/App/ErrorDialog/ErrorDialog.js | 2 +- src/App/styles.less | 2 +- .../NavBar/HorizontalNavBar/HorizontalNavBar.js | 2 +- .../HorizontalNavBar/NavMenu/NavMenuContent.js | 4 ++-- src/routes/Calendar/Placeholder/Placeholder.tsx | 2 +- src/routes/Discover/Discover.js | 4 ++-- src/routes/Intro/Intro.js | 2 +- src/routes/Intro/styles.less | 2 +- src/routes/Library/Library.js | 4 ++-- src/routes/Library/Placeholder/Placeholder.tsx | 2 +- src/routes/MetaDetails/MetaDetails.js | 6 +++--- src/routes/MetaDetails/StreamsList/StreamsList.js | 4 ++-- src/routes/MetaDetails/VideosList/VideosList.js | 2 +- src/routes/NotFound/NotFound.js | 2 +- .../Player/BufferingLoader/BufferingLoader.js | 2 +- src/routes/Search/Search.js | 2 +- src/routes/Settings/General/User/User.tsx | 4 ++-- webpack.config.js | 6 +++--- 40 files changed, 27 insertions(+), 27 deletions(-) rename {favicons => assets/favicons}/favicon.ico (100%) rename {fonts => assets/fonts}/PlusJakartaSans.ttf (100%) rename {images => assets/images}/anonymous.png (100%) rename {images => assets/images}/background_1.svg (100%) rename {images => assets/images}/background_2.svg (100%) rename {images => assets/images}/calendar_placeholder.png (100%) rename {images => assets/images}/default_avatar.png (100%) rename {images => assets/images}/empty.png (100%) rename {images => assets/images}/icon.png (100%) rename {images => assets/images}/icon_196x196.png (100%) rename {images => assets/images}/icon_512x512.png (100%) rename {images => assets/images}/library_placeholder.png (100%) rename {images => assets/images}/logo.png (100%) rename {images => assets/images}/maskable_icon.png (100%) rename {images => assets/images}/maskable_icon_196x196.png (100%) rename {images => assets/images}/maskable_icon_512x512.png (100%) rename {images => assets/images}/stremio_symbol.png (100%) rename {screenshots => assets/screenshots}/board.png (100%) rename {screenshots => assets/screenshots}/board_narrow.webp (100%) rename {screenshots => assets/screenshots}/board_wide.webp (100%) rename {screenshots => assets/screenshots}/discover.png (100%) rename {screenshots => assets/screenshots}/metadetails.png (100%) diff --git a/favicons/favicon.ico b/assets/favicons/favicon.ico similarity index 100% rename from favicons/favicon.ico rename to assets/favicons/favicon.ico diff --git a/fonts/PlusJakartaSans.ttf b/assets/fonts/PlusJakartaSans.ttf similarity index 100% rename from fonts/PlusJakartaSans.ttf rename to assets/fonts/PlusJakartaSans.ttf diff --git a/images/anonymous.png b/assets/images/anonymous.png similarity index 100% rename from images/anonymous.png rename to assets/images/anonymous.png diff --git a/images/background_1.svg b/assets/images/background_1.svg similarity index 100% rename from images/background_1.svg rename to assets/images/background_1.svg diff --git a/images/background_2.svg b/assets/images/background_2.svg similarity index 100% rename from images/background_2.svg rename to assets/images/background_2.svg diff --git a/images/calendar_placeholder.png b/assets/images/calendar_placeholder.png similarity index 100% rename from images/calendar_placeholder.png rename to assets/images/calendar_placeholder.png diff --git a/images/default_avatar.png b/assets/images/default_avatar.png similarity index 100% rename from images/default_avatar.png rename to assets/images/default_avatar.png diff --git a/images/empty.png b/assets/images/empty.png similarity index 100% rename from images/empty.png rename to assets/images/empty.png diff --git a/images/icon.png b/assets/images/icon.png similarity index 100% rename from images/icon.png rename to assets/images/icon.png diff --git a/images/icon_196x196.png b/assets/images/icon_196x196.png similarity index 100% rename from images/icon_196x196.png rename to assets/images/icon_196x196.png diff --git a/images/icon_512x512.png b/assets/images/icon_512x512.png similarity index 100% rename from images/icon_512x512.png rename to assets/images/icon_512x512.png diff --git a/images/library_placeholder.png b/assets/images/library_placeholder.png similarity index 100% rename from images/library_placeholder.png rename to assets/images/library_placeholder.png diff --git a/images/logo.png b/assets/images/logo.png similarity index 100% rename from images/logo.png rename to assets/images/logo.png diff --git a/images/maskable_icon.png b/assets/images/maskable_icon.png similarity index 100% rename from images/maskable_icon.png rename to assets/images/maskable_icon.png diff --git a/images/maskable_icon_196x196.png b/assets/images/maskable_icon_196x196.png similarity index 100% rename from images/maskable_icon_196x196.png rename to assets/images/maskable_icon_196x196.png diff --git a/images/maskable_icon_512x512.png b/assets/images/maskable_icon_512x512.png similarity index 100% rename from images/maskable_icon_512x512.png rename to assets/images/maskable_icon_512x512.png diff --git a/images/stremio_symbol.png b/assets/images/stremio_symbol.png similarity index 100% rename from images/stremio_symbol.png rename to assets/images/stremio_symbol.png diff --git a/screenshots/board.png b/assets/screenshots/board.png similarity index 100% rename from screenshots/board.png rename to assets/screenshots/board.png diff --git a/screenshots/board_narrow.webp b/assets/screenshots/board_narrow.webp similarity index 100% rename from screenshots/board_narrow.webp rename to assets/screenshots/board_narrow.webp diff --git a/screenshots/board_wide.webp b/assets/screenshots/board_wide.webp similarity index 100% rename from screenshots/board_wide.webp rename to assets/screenshots/board_wide.webp diff --git a/screenshots/discover.png b/assets/screenshots/discover.png similarity index 100% rename from screenshots/discover.png rename to assets/screenshots/discover.png diff --git a/screenshots/metadetails.png b/assets/screenshots/metadetails.png similarity index 100% rename from screenshots/metadetails.png rename to assets/screenshots/metadetails.png diff --git a/src/App/ErrorDialog/ErrorDialog.js b/src/App/ErrorDialog/ErrorDialog.js index a2b31ba41..eac9b34ac 100644 --- a/src/App/ErrorDialog/ErrorDialog.js +++ b/src/App/ErrorDialog/ErrorDialog.js @@ -22,7 +22,7 @@ const ErrorDialog = ({ className }) => {
{'
diff --git a/src/App/styles.less b/src/App/styles.less index ae6555500..e6fd7d747 100644 --- a/src/App/styles.less +++ b/src/App/styles.less @@ -5,7 +5,7 @@ @font-face { font-family: 'PlusJakartaSans'; - src: url('/fonts/PlusJakartaSans.ttf') format('truetype'); + src: url('/assets/fonts/PlusJakartaSans.ttf') format('truetype'); } :global { diff --git a/src/components/NavBar/HorizontalNavBar/HorizontalNavBar.js b/src/components/NavBar/HorizontalNavBar/HorizontalNavBar.js index 6be35cd5d..65bf30c94 100644 --- a/src/components/NavBar/HorizontalNavBar/HorizontalNavBar.js +++ b/src/components/NavBar/HorizontalNavBar/HorizontalNavBar.js @@ -35,7 +35,7 @@ const HorizontalNavBar = React.memo(({ className, route, query, title, backButto
{'
diff --git a/src/components/NavBar/HorizontalNavBar/NavMenu/NavMenuContent.js b/src/components/NavBar/HorizontalNavBar/NavMenu/NavMenuContent.js index de0a02212..4b2fdc87e 100644 --- a/src/components/NavBar/HorizontalNavBar/NavMenu/NavMenuContent.js +++ b/src/components/NavBar/HorizontalNavBar/NavMenu/NavMenuContent.js @@ -52,12 +52,12 @@ const NavMenuContent = ({ onClick }) => { className={styles['avatar-container']} style={{ backgroundImage: profile.auth === null ? - `url('${require('/images/anonymous.png')}')` + `url('${require('/assets/images/anonymous.png')}')` : profile.auth.user.avatar ? `url('${profile.auth.user.avatar}')` : - `url('${require('/images/default_avatar.png')}')` + `url('${require('/assets/images/default_avatar.png')}')` }} />
diff --git a/src/routes/Calendar/Placeholder/Placeholder.tsx b/src/routes/Calendar/Placeholder/Placeholder.tsx index c84e7a1b8..1f87acb26 100644 --- a/src/routes/Calendar/Placeholder/Placeholder.tsx +++ b/src/routes/Calendar/Placeholder/Placeholder.tsx @@ -17,7 +17,7 @@ const Placeholder = () => {
{'
diff --git a/src/routes/Discover/Discover.js b/src/routes/Discover/Discover.js index 6f32d1f9a..a28a86405 100644 --- a/src/routes/Discover/Discover.js +++ b/src/routes/Discover/Discover.js @@ -133,14 +133,14 @@ const Discover = ({ urlParams, queryParams }) => { discover.catalog === null ?
- {' + {'
{t('NO_CATALOG_SELECTED')}
: discover.catalog.content.type === 'Err' ?
- {' + {'
{discover.catalog.content.content}
: diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index f04302fc7..5a2f80aaa 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -296,7 +296,7 @@ const Intro = ({ queryParams }) => {
- {' + {'
{t('WEBSITE_SLOGAN_NEW_NEW')} diff --git a/src/routes/Intro/styles.less b/src/routes/Intro/styles.less index 31a09d54c..4a6f6ec8e 100644 --- a/src/routes/Intro/styles.less +++ b/src/routes/Intro/styles.less @@ -19,7 +19,7 @@ bottom: -1rem; left: -1rem; right: -1rem; - background: url('/images/background_1.svg'), url('/images/background_2.svg'); + background: url('/assets/images/background_1.svg'), url('/assets/images/background_2.svg'); background-color: var(--primary-background-color); background-position: bottom left, top right; background-size: 53%, 54%; diff --git a/src/routes/Library/Library.js b/src/routes/Library/Library.js index 16a4c79b5..8307b786a 100644 --- a/src/routes/Library/Library.js +++ b/src/routes/Library/Library.js @@ -85,7 +85,7 @@ const Library = ({ model, urlParams, queryParams }) => {
{'
{model === 'library' ? t('LIBRARY_NOT_LOADED') : t('BOARD_CONTINUE_WATCHING_NOT_LOADED')}
@@ -96,7 +96,7 @@ const Library = ({ model, urlParams, queryParams }) => {
{'
{model === 'library' ? t('LIBRARY_EMPTY') : t('BOARD_CONTINUE_WATCHING_EMPTY')}
diff --git a/src/routes/Library/Placeholder/Placeholder.tsx b/src/routes/Library/Placeholder/Placeholder.tsx index d854a2d54..a066324e0 100644 --- a/src/routes/Library/Placeholder/Placeholder.tsx +++ b/src/routes/Library/Placeholder/Placeholder.tsx @@ -17,7 +17,7 @@ const Placeholder = () => {
{'
diff --git a/src/routes/MetaDetails/MetaDetails.js b/src/routes/MetaDetails/MetaDetails.js index fd27478b5..926671080 100644 --- a/src/routes/MetaDetails/MetaDetails.js +++ b/src/routes/MetaDetails/MetaDetails.js @@ -130,20 +130,20 @@ const MetaDetails = ({ urlParams, queryParams }) => { metaPath === null ?
- {' + {'
{t('ERR_NO_META_SELECTED')}
: metaDetails.metaItem === null ?
- {' + {'
{t('ERR_NO_ADDONS_FOR_META')}
: metaDetails.metaItem.content.type === 'Err' ?
- {' + {'
{t('ERR_NO_META_FOUND')}
: diff --git a/src/routes/MetaDetails/StreamsList/StreamsList.js b/src/routes/MetaDetails/StreamsList/StreamsList.js index eebc0c3cf..7021644ab 100644 --- a/src/routes/MetaDetails/StreamsList/StreamsList.js +++ b/src/routes/MetaDetails/StreamsList/StreamsList.js @@ -132,7 +132,7 @@ const StreamsList = ({ className, video, type, onEpisodeSearch, ...props }) => { : null } - {' + {'
{t('ERR_NO_ADDONS_FOR_STREAMS')}
: @@ -148,7 +148,7 @@ const StreamsList = ({ className, video, type, onEpisodeSearch, ...props }) => {
{t('UPCOMING')}...
: null } - {' + {'
{t('NO_STREAM')}
{ showInstallAddonsButton ? diff --git a/src/routes/MetaDetails/VideosList/VideosList.js b/src/routes/MetaDetails/VideosList/VideosList.js index 8891947db..3cc3832d6 100644 --- a/src/routes/MetaDetails/VideosList/VideosList.js +++ b/src/routes/MetaDetails/VideosList/VideosList.js @@ -124,7 +124,7 @@ const VideosList = ({ className, metaItem, libraryItem, season, seasonOnSelect, metaItem.content.type === 'Err' || videosForSeason.length === 0 ?
- {' + {'
{t('ERR_NO_VIDEOS_FOR_META')}
: diff --git a/src/routes/NotFound/NotFound.js b/src/routes/NotFound/NotFound.js index d984496bc..a3c97d8c1 100644 --- a/src/routes/NotFound/NotFound.js +++ b/src/routes/NotFound/NotFound.js @@ -19,7 +19,7 @@ const NotFound = () => {
{'
{t('PAGE_NOT_FOUND')}
diff --git a/src/routes/Player/BufferingLoader/BufferingLoader.js b/src/routes/Player/BufferingLoader/BufferingLoader.js index 3d5664ae4..a762506f4 100644 --- a/src/routes/Player/BufferingLoader/BufferingLoader.js +++ b/src/routes/Player/BufferingLoader/BufferingLoader.js @@ -13,7 +13,7 @@ const BufferingLoader = React.forwardRef(({ className, logo }, ref) => { className={styles['buffering-loader']} src={logo} alt={' '} - fallbackSrc={require('/images/stremio_symbol.png')} + fallbackSrc={require('/assets/images/stremio_symbol.png')} />
); diff --git a/src/routes/Search/Search.js b/src/routes/Search/Search.js index 58e6e834b..6990ccfd8 100644 --- a/src/routes/Search/Search.js +++ b/src/routes/Search/Search.js @@ -78,7 +78,7 @@ const Search = ({ queryParams }) => {
{'
{ t.string('STREMIO_TV_SEARCH_NO_ADDONS') }
diff --git a/src/routes/Settings/General/User/User.tsx b/src/routes/Settings/General/User/User.tsx index 6b44e9903..7555e521e 100644 --- a/src/routes/Settings/General/User/User.tsx +++ b/src/routes/Settings/General/User/User.tsx @@ -14,12 +14,12 @@ const User = ({ profile }: Props) => { const avatar = useMemo(() => ( !profile.auth ? - `url('${require('/images/anonymous.png')}')` + `url('${require('/assets/images/anonymous.png')}')` : profile.auth.user.avatar ? `url('${profile.auth.user.avatar}')` : - `url('${require('/images/default_avatar.png')}')` + `url('${require('/assets/images/default_avatar.png')}')` ), [profile.auth]); const onLogout = useCallback(() => { diff --git a/webpack.config.js b/webpack.config.js index 5c6df3871..3a2cdc58c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -228,9 +228,9 @@ module.exports = (env, argv) => ({ }), new CopyWebpackPlugin({ patterns: [ - { from: 'favicons', to: 'favicons' }, - { from: 'images', to: 'images' }, - { from: 'screenshots/*.webp', to: './' }, + { from: 'assets/favicons', to: 'favicons' }, + { from: 'assets/images', to: 'images' }, + { from: 'assets/screenshots/*.webp', to: 'screenshots/[name][ext]' }, { from: '.well-known', to: '.well-known' }, { from: 'manifest.json', to: 'manifest.json' }, ] From bb0aecc194789450240b2cca3d200689b947b668 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 14 Jan 2026 04:40:01 +0100 Subject: [PATCH 105/117] chore: remove commit hash from fonts path --- webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webpack.config.js b/webpack.config.js index 3a2cdc58c..8768d753f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -154,7 +154,7 @@ module.exports = (env, argv) => ({ exclude: /node_modules/, type: 'asset/resource', generator: { - filename: `${COMMIT_HASH}/fonts/[name][ext][query]` + filename: 'fonts/[name][ext][query]' } }, { From 889dface675cd63c7b3c22d6676f554df5d85a7d Mon Sep 17 00:00:00 2001 From: Coolkie <44529370+PL7963@users.noreply.github.com> Date: Wed, 14 Jan 2026 22:12:05 +0800 Subject: [PATCH 106/117] Update README.md image paths --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b7cd999dc..ecc03a921 100644 --- a/README.md +++ b/README.md @@ -41,15 +41,15 @@ docker run -p 8080:8080 stremio-web ### Board -![Board](/screenshots/board.png) +![Board](/assets/screenshots/board.png) ### Discover -![Discover](/screenshots/discover.png) +![Discover](/assets/screenshots/discover.png) ### Meta Details -![Meta Details](/screenshots/metadetails.png) +![Meta Details](/assets/screenshots/metadetails.png) ## License From 487fde70e049dcce8809a5fdc0f9691ec50a0a84 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 20 Jan 2026 21:25:32 +0100 Subject: [PATCH 107/117] chore: update video --- package.json | 2 +- pnpm-lock.yaml | 32 ++++++-------------------------- 2 files changed, 7 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index 8b6efe5cc..6a1047e2e 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "@stremio/stremio-colors": "5.2.0", "@stremio/stremio-core-web": "0.51.1", "@stremio/stremio-icons": "5.8.0", - "@stremio/stremio-video": "0.0.64", + "@stremio/stremio-video": "0.0.70", "a-color-picker": "1.2.1", "bowser": "2.11.0", "buffer": "6.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 48afa6562..f964d9bf9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,8 +24,8 @@ importers: specifier: 5.8.0 version: 5.8.0 '@stremio/stremio-video': - specifier: 0.0.64 - version: 0.0.64 + specifier: 0.0.70 + version: 0.0.70 a-color-picker: specifier: 1.2.1 version: 1.2.1 @@ -1126,8 +1126,8 @@ packages: '@stremio/stremio-icons@5.8.0': resolution: {integrity: sha512-IVUvQbIWfA4YEHCTed7v/sdQJCJ+OOCf84LTWpkE2W6GLQ+15WHcMEJrVkE1X3ekYJnGg3GjT0KLO6tKSU0P4w==} - '@stremio/stremio-video@0.0.64': - resolution: {integrity: sha512-29w/lwU8BB6ai8LUyCnpRc2F9kPf7cpys40NCobt70MqBP/UqvYISsrnD/ijoBwvtpKdZ6ptv5h9BbDj6rrerw==} + '@stremio/stremio-video@0.0.70': + resolution: {integrity: sha512-a0flQYAUdrZNMm7mmts2vpZOqN1nus7Hs9Mjl4mrN5rtduD0ojUyhD5J4lPcCpZ7WB0YdEUOGLXR19qHpgoKmg==} '@stylistic/eslint-plugin-jsx@4.4.1': resolution: {integrity: sha512-83SInq4u7z71vWwGG+6ViOtlOmZ6tSrDkMPhrvdBBTGMLA0gs22WSdhQ4vZP3oJ5Xg4ythvqeUiFSedvVxzhyA==} @@ -3738,9 +3738,6 @@ packages: prr@1.0.1: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} - punycode@1.3.2: - resolution: {integrity: sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==} - punycode@1.4.1: resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} @@ -3759,11 +3756,6 @@ packages: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} - querystring@0.2.0: - resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==} - engines: {node: '>=0.4.x'} - deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. - queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -4420,9 +4412,6 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - url@0.11.0: - resolution: {integrity: sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==} - url@0.11.4: resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} engines: {node: '>= 0.4'} @@ -5887,7 +5876,7 @@ snapshots: '@stremio/stremio-icons@5.8.0': {} - '@stremio/stremio-video@0.0.64': + '@stremio/stremio-video@0.0.70': dependencies: buffer: 6.0.3 color: 4.2.3 @@ -5897,7 +5886,7 @@ snapshots: hls.js: https://github.com/Stremio/hls.js/releases/download/v1.5.4-patch2/hls.js-1.5.4-patch2.tgz lodash.clonedeep: 4.5.0 magnet-uri: 6.2.0 - url: 0.11.0 + url: 0.11.4 video-name-parser: 1.4.6 vtt.js: https://codeload.github.com/jaruba/vtt.js/tar.gz/84d33d157848407d790d78423dacc41a096294f0 @@ -8941,8 +8930,6 @@ snapshots: prr@1.0.1: optional: true - punycode@1.3.2: {} - punycode@1.4.1: {} punycode@2.3.1: {} @@ -8957,8 +8944,6 @@ snapshots: dependencies: side-channel: 1.1.0 - querystring@0.2.0: {} - queue-microtask@1.2.3: {} randombytes@2.1.0: @@ -9688,11 +9673,6 @@ snapshots: dependencies: punycode: 2.3.1 - url@0.11.0: - dependencies: - punycode: 1.3.2 - querystring: 0.2.0 - url@0.11.4: dependencies: punycode: 1.4.1 From f046e65e7369c9f7087abb8b30716c885e632377 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 20 Jan 2026 21:38:44 +0100 Subject: [PATCH 108/117] chore: update translations --- package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 6a1047e2e..1173798d8 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "react-i18next": "^15.1.3", "react-is": "18.3.1", "spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", - "stremio-translations": "github:Stremio/stremio-translations#0e7fbd8522148f5727ac6adee3b2eb96132c10ac", + "stremio-translations": "github:Stremio/stremio-translations#7c0c337f32163aa13158bb90cd6133da43feafef", "url": "0.11.4", "use-long-press": "^3.2.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f964d9bf9..8b5f17c81 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -90,8 +90,8 @@ importers: specifier: github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6 version: https://codeload.github.com/Stremio/spatial-navigation/tar.gz/64871b1422466f5f45d24ebc8bbd315b2ebab6a6 stremio-translations: - specifier: github:Stremio/stremio-translations#0e7fbd8522148f5727ac6adee3b2eb96132c10ac - version: https://codeload.github.com/Stremio/stremio-translations/tar.gz/0e7fbd8522148f5727ac6adee3b2eb96132c10ac + specifier: github:Stremio/stremio-translations#7c0c337f32163aa13158bb90cd6133da43feafef + version: https://codeload.github.com/Stremio/stremio-translations/tar.gz/7c0c337f32163aa13158bb90cd6133da43feafef url: specifier: 0.11.4 version: 0.11.4 @@ -4133,9 +4133,9 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} - stremio-translations@https://codeload.github.com/Stremio/stremio-translations/tar.gz/0e7fbd8522148f5727ac6adee3b2eb96132c10ac: - resolution: {tarball: https://codeload.github.com/Stremio/stremio-translations/tar.gz/0e7fbd8522148f5727ac6adee3b2eb96132c10ac} - version: 1.44.14 + stremio-translations@https://codeload.github.com/Stremio/stremio-translations/tar.gz/7c0c337f32163aa13158bb90cd6133da43feafef: + resolution: {tarball: https://codeload.github.com/Stremio/stremio-translations/tar.gz/7c0c337f32163aa13158bb90cd6133da43feafef} + version: 1.45.0 string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} @@ -9378,7 +9378,7 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 - stremio-translations@https://codeload.github.com/Stremio/stremio-translations/tar.gz/0e7fbd8522148f5727ac6adee3b2eb96132c10ac: {} + stremio-translations@https://codeload.github.com/Stremio/stremio-translations/tar.gz/7c0c337f32163aa13158bb90cd6133da43feafef: {} string-length@4.0.2: dependencies: From 5aaee645491a2790d27737e27646dc6337a9f7f7 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 20 Jan 2026 21:39:03 +0100 Subject: [PATCH 109/117] chore: add new interface languages --- src/common/interfaceLanguages.json | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/common/interfaceLanguages.json b/src/common/interfaceLanguages.json index ce3504691..91b87d5af 100644 --- a/src/common/interfaceLanguages.json +++ b/src/common/interfaceLanguages.json @@ -3,6 +3,10 @@ "name": "العربية", "codes": ["ar-AR", "ara"] }, + { + "name": "Беларуская", + "codes": ["be-BY", "bel"] + }, { "name": "български език", "codes": ["bg-BG", "bul"] @@ -13,7 +17,7 @@ }, { "name": "català", - "codes": ["ca-CA", "cat"] + "codes": ["ca-ES", "cat"] }, { "name": "čeština", @@ -43,6 +47,10 @@ "name": "español", "codes": ["es-ES", "spa"] }, + { + "name": "Eesti", + "codes": ["et-EE", "est"] + }, { "name": "euskara", "codes": ["eu-ES", "eus"] @@ -111,6 +119,10 @@ "name": "Norsk nynorsk", "codes": ["nn-NO", "nno"] }, + { + "name": "ਪੰਜਾਬੀ", + "codes": ["pa-IN", "pan"] + }, { "name": "język polski", "codes": ["pl-PL", "pol"] @@ -151,6 +163,10 @@ "name": "తెలుగు", "codes": ["te-IN", "tel"] }, + { + "name": "தமிழ்", + "codes": ["tl-TM", "tam"] + }, { "name": "Türkçe", "codes": ["tr-TR", "tur"] @@ -159,6 +175,10 @@ "name": "українська мова", "codes": ["uk-UA", "ukr"] }, + { + "name": "اُرْدُو", + "codes": ["ur-PK", "urd"] + }, { "name": "Tiếng Việt", "codes": ["vi-VN", "vie"] From 7cd49b516f734f148a24a36e89d58a16b27189ea Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 20 Jan 2026 22:01:33 +0100 Subject: [PATCH 110/117] refactor(Settings): move interface settings to dedicated section --- src/routes/Settings/General/General.tsx | 45 +-------------- src/routes/Settings/Interface/Interface.tsx | 57 +++++++++++++++++++ src/routes/Settings/Interface/index.ts | 2 + .../useInterfaceOptions.ts} | 4 +- src/routes/Settings/Menu/Menu.tsx | 3 + src/routes/Settings/Settings.tsx | 7 +++ src/routes/Settings/constants.ts | 1 + 7 files changed, 74 insertions(+), 45 deletions(-) create mode 100644 src/routes/Settings/Interface/Interface.tsx create mode 100644 src/routes/Settings/Interface/index.ts rename src/routes/Settings/{General/useGeneralOptions.ts => Interface/useInterfaceOptions.ts} (96%) diff --git a/src/routes/Settings/General/General.tsx b/src/routes/Settings/General/General.tsx index 8f5496dbd..49cfdfdd6 100644 --- a/src/routes/Settings/General/General.tsx +++ b/src/routes/Settings/General/General.tsx @@ -1,13 +1,12 @@ import React, { forwardRef, useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Button, MultiselectMenu, Toggle } from 'stremio/components'; +import { Button } from 'stremio/components'; import { useServices } from 'stremio/services'; import { usePlatform, useToast } from 'stremio/common'; import { Section, Option, Link } from '../components'; import User from './User'; import useDataExport from './useDataExport'; import styles from './General.less'; -import useGeneralOptions from './useGeneralOptions'; type Props = { profile: Profile, @@ -15,18 +14,11 @@ type Props = { const General = forwardRef(({ profile }: Props, ref) => { const { t } = useTranslation(); - const { core, shell } = useServices(); + const { core } = useServices(); const platform = usePlatform(); const toast = useToast(); const [dataExport, loadDataExport] = useDataExport(); - const { - interfaceLanguageSelect, - quitOnCloseToggle, - escExitFullscreenToggle, - hideSpoilersToggle, - } = useGeneralOptions(profile); - const [traktAuthStarted, setTraktAuthStarted] = useState(false); const isTraktAuthenticated = useMemo(() => { @@ -143,39 +135,6 @@ const General = forwardRef(({ profile }: Props, ref) => { - -
- - { - shell.active && - - } - { - shell.active && - - } - -
; }); diff --git a/src/routes/Settings/Interface/Interface.tsx b/src/routes/Settings/Interface/Interface.tsx new file mode 100644 index 000000000..a4b429a56 --- /dev/null +++ b/src/routes/Settings/Interface/Interface.tsx @@ -0,0 +1,57 @@ +import React, { forwardRef } from 'react'; +import { useServices } from 'stremio/services'; +import { MultiselectMenu, Toggle } from 'stremio/components'; +import { Section, Option } from '../components'; +import useInterfaceOptions from './useInterfaceOptions'; + +type Props = { + profile: Profile, +}; + +const Interface = forwardRef(({ profile }: Props, ref) => { + const { shell } = useServices(); + + const { + interfaceLanguageSelect, + quitOnCloseToggle, + escExitFullscreenToggle, + hideSpoilersToggle, + } = useInterfaceOptions(profile); + + return ( +
+ + { + shell.active && + + } + { + shell.active && + + } + +
+ ); +}); + +export default Interface; diff --git a/src/routes/Settings/Interface/index.ts b/src/routes/Settings/Interface/index.ts new file mode 100644 index 000000000..480fa5ff4 --- /dev/null +++ b/src/routes/Settings/Interface/index.ts @@ -0,0 +1,2 @@ +import Interface from './Interface'; +export default Interface; diff --git a/src/routes/Settings/General/useGeneralOptions.ts b/src/routes/Settings/Interface/useInterfaceOptions.ts similarity index 96% rename from src/routes/Settings/General/useGeneralOptions.ts rename to src/routes/Settings/Interface/useInterfaceOptions.ts index a4ab84c5e..67780b7e1 100644 --- a/src/routes/Settings/General/useGeneralOptions.ts +++ b/src/routes/Settings/Interface/useInterfaceOptions.ts @@ -2,7 +2,7 @@ import { useMemo } from 'react'; import { interfaceLanguages, useLanguageSorting } from 'stremio/common'; import { useServices } from 'stremio/services'; -const useGeneralOptions = (profile: Profile) => { +const useInterfaceOptions = (profile: Profile) => { const { core } = useServices(); const interfaceLanguageOptions = useMemo(() => @@ -89,4 +89,4 @@ const useGeneralOptions = (profile: Profile) => { }; }; -export default useGeneralOptions; +export default useInterfaceOptions; diff --git a/src/routes/Settings/Menu/Menu.tsx b/src/routes/Settings/Menu/Menu.tsx index ceafee94b..33cf41dc0 100644 --- a/src/routes/Settings/Menu/Menu.tsx +++ b/src/routes/Settings/Menu/Menu.tsx @@ -26,6 +26,9 @@ const Menu = ({ selected, streamingServer, onSelect }: Props) => { + diff --git a/src/routes/Settings/Settings.tsx b/src/routes/Settings/Settings.tsx index 2db68da2f..727da9e82 100644 --- a/src/routes/Settings/Settings.tsx +++ b/src/routes/Settings/Settings.tsx @@ -9,6 +9,7 @@ import { MainNavBars } from 'stremio/components'; import { SECTIONS } from './constants'; import Menu from './Menu'; import General from './General'; +import Interface from './Interface'; import Player from './Player'; import Streaming from './Streaming'; import Shortcuts from './Shortcuts'; @@ -23,12 +24,14 @@ const Settings = () => { const sectionsContainerRef = useRef(null); const generalSectionRef = useRef(null); + const interfaceSectionRef = useRef(null); const playerSectionRef = useRef(null); const streamingServerSectionRef = useRef(null); const shortcutsSectionRef = useRef(null); const sections = useMemo(() => ([ { ref: generalSectionRef, id: SECTIONS.GENERAL }, + { ref: interfaceSectionRef, id: SECTIONS.INTERFACE }, { ref: playerSectionRef, id: SECTIONS.PLAYER }, { ref: streamingServerSectionRef, id: SECTIONS.STREAMING }, { ref: shortcutsSectionRef, id: SECTIONS.SHORTCUTS }, @@ -82,6 +85,10 @@ const Settings = () => { ref={generalSectionRef} profile={profile} /> + Date: Wed, 21 Jan 2026 09:47:17 +0100 Subject: [PATCH 111/117] feat: remember selected tracks on player --- src/routes/Player/Player.js | 62 ++++++++++++++++++++++++---------- src/routes/Player/usePlayer.js | 21 ++++++++++-- src/types/models/Player.d.ts | 15 ++++++++ 3 files changed, 79 insertions(+), 19 deletions(-) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index 3ae2f066c..d6814193f 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -29,6 +29,9 @@ const styles = require('./styles'); const Video = require('./Video'); const { default: Indicator } = require('./Indicator/Indicator'); +const findTrackByLang = (tracks, lang) => tracks.find((track) => track.lang === lang || langs.where('1', track.lang)?.[2] === lang); +const findTrackById = (tracks, id) => tracks.find((track) => track.id === id); + const Player = ({ urlParams, queryParams }) => { const { t } = useTranslation(); const services = useServices(); @@ -37,7 +40,7 @@ const Player = ({ urlParams, queryParams }) => { return queryParams.has('forceTranscoding'); }, [queryParams]); const profile = useProfile(); - const [player, videoParamsChanged, timeChanged, seek, pausedChanged, ended, nextVideo] = usePlayer(urlParams); + const [player, videoParamsChanged, streamStateChanged, timeChanged, seek, pausedChanged, ended, nextVideo] = usePlayer(urlParams); const [settings, updateSettings] = useSettings(); const streamingServer = useStreamingServer(); const statistics = useStatistics(player, streamingServer); @@ -224,15 +227,32 @@ const Player = ({ urlParams, queryParams }) => { const onSubtitlesTrackSelected = React.useCallback((id) => { video.setSubtitlesTrack(id); - }, []); + streamStateChanged({ + subtitleTrack: { + id, + embedded: true, + }, + }); + }, [streamStateChanged]); const onExtraSubtitlesTrackSelected = React.useCallback((id) => { video.setExtraSubtitlesTrack(id); - }, []); + streamStateChanged({ + subtitleTrack: { + id, + embedded: false, + }, + }); + }, [streamStateChanged]); const onAudioTrackSelected = React.useCallback((id) => { video.setProp('selectedAudioTrackId', id); - }, []); + streamStateChanged({ + audioTrack: { + id, + }, + }); + }, [streamStateChanged]); const onExtraSubtitlesDelayChanged = React.useCallback((delay) => { video.setProp('extraSubtitlesDelay', delay); @@ -444,41 +464,49 @@ const Player = ({ urlParams, queryParams }) => { } }, [player.nextVideo, video.state.time, video.state.duration]); + // Auto subtitles track selection React.useEffect(() => { if (!defaultSubtitlesSelected.current) { - const findTrackByLang = (tracks, lang) => tracks.find((track) => track.lang === lang || langs.where('1', track.lang)?.[2] === lang); - if (settings.subtitlesLanguage === null) { - onSubtitlesTrackSelected(null); - onExtraSubtitlesTrackSelected(null); + video.setSubtitlesTrack(null); + video.setExtraSubtitlesTrack(null); defaultSubtitlesSelected.current = true; return; } - const subtitlesTrack = findTrackByLang(video.state.subtitlesTracks, settings.subtitlesLanguage); - const extraSubtitlesTrack = findTrackByLang(video.state.extraSubtitlesTracks, settings.subtitlesLanguage); + const savedTrackId = player.streamState?.subtitleTrack?.id; + const subtitlesTrack = savedTrackId ? + findTrackById(video.state.subtitlesTracks, savedTrackId) : + findTrackByLang(video.state.subtitlesTracks, settings.subtitlesLanguage); + + const extraSubtitlesTrack = savedTrackId ? + findTrackById(video.state.extraSubtitlesTracks, savedTrackId) : + findTrackByLang(video.state.extraSubtitlesTracks, settings.subtitlesLanguage); if (subtitlesTrack && subtitlesTrack.id) { - onSubtitlesTrackSelected(subtitlesTrack.id); + video.setSubtitlesTrack(subtitlesTrack.id); defaultSubtitlesSelected.current = true; } else if (extraSubtitlesTrack && extraSubtitlesTrack.id) { - onExtraSubtitlesTrackSelected(extraSubtitlesTrack.id); + video.setExtraSubtitlesTrack(extraSubtitlesTrack.id); defaultSubtitlesSelected.current = true; } } - }, [video.state.subtitlesTracks, video.state.extraSubtitlesTracks]); + }, [video.state.subtitlesTracks, video.state.extraSubtitlesTracks, player.streamState]); + // Auto audio track selection React.useEffect(() => { if (!defaultAudioTrackSelected.current) { - const findTrackByLang = (tracks, lang) => tracks.find((track) => track.lang === lang || langs.where('1', track.lang)?.[2] === lang); - const audioTrack = findTrackByLang(video.state.audioTracks, settings.audioLanguage); + const savedTrackId = player.streamState?.audioTrack?.id; + const audioTrack = savedTrackId ? + findTrackById(video.state.audioTracks, savedTrackId) : + findTrackByLang(video.state.audioTracks, settings.audioLanguage); if (audioTrack && audioTrack.id) { - onAudioTrackSelected(audioTrack.id); + video.setProp('selectedAudioTrackId', audioTrack.id); defaultAudioTrackSelected.current = true; } } - }, [video.state.audioTracks]); + }, [video.state.audioTracks, player.streamState]); React.useEffect(() => { defaultSubtitlesSelected.current = false; diff --git a/src/routes/Player/usePlayer.js b/src/routes/Player/usePlayer.js index 4ca2574ba..2584517eb 100644 --- a/src/routes/Player/usePlayer.js +++ b/src/routes/Player/usePlayer.js @@ -86,6 +86,9 @@ const usePlayer = (urlParams) => { }; } }, [urlParams]); + + const player = useModelState({ model: 'player', action, map }); + const videoParamsChanged = React.useCallback((videoParams) => { core.transport.dispatch({ action: 'Player', @@ -153,8 +156,22 @@ const usePlayer = (urlParams) => { }, 'player'); }, []); - const player = useModelState({ model: 'player', action, map }); - return [player, videoParamsChanged, timeChanged, seek, pausedChanged, ended, nextVideo]; + const streamStateChanged = React.useCallback((partialStreamState) => { + return core.transport.dispatch({ + action: 'Player', + args: { + action: 'StreamStateChanged', + args: { + state: { + ...player.streamState, + ...partialStreamState, + }, + }, + }, + }, 'player'); + }, [player.streamState]); + + return [player, videoParamsChanged, streamStateChanged, timeChanged, seek, pausedChanged, ended, nextVideo]; }; module.exports = usePlayer; diff --git a/src/types/models/Player.d.ts b/src/types/models/Player.d.ts index 7f7dbc318..a076f78f4 100644 --- a/src/types/models/Player.d.ts +++ b/src/types/models/Player.d.ts @@ -30,6 +30,20 @@ type SeriesInfo = { season: number, }; +type SubtitlesTrackState = { + id: string, + embedded: boolean, +}; + +type AudioTrackState = { + id: string, +}; + +type StreamState = { + subtitleTrack?: SubtitlesTrackState, + audioTrack?: AudioTrackState, +}; + type Player = { addon: Addon | null, libraryItem: LibraryItemPlayer | null, @@ -42,6 +56,7 @@ type Player = { subtitlesPath: ResourceRequestPath, } | null, seriesInfo: SeriesInfo | null, + streamState: StreamState | null, subtitles: Subtitle[], title: string | null, }; From 0dcc07c46903ebe5dc95cb0d34fc4fe8a4ede5f7 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 22 Jan 2026 00:12:53 +0100 Subject: [PATCH 112/117] chore: update core --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 1173798d8..0c9ca54ec 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "@babel/runtime": "7.26.0", "@sentry/browser": "8.42.0", "@stremio/stremio-colors": "5.2.0", - "@stremio/stremio-core-web": "0.51.1", + "@stremio/stremio-core-web": "0.52.0", "@stremio/stremio-icons": "5.8.0", "@stremio/stremio-video": "0.0.70", "a-color-picker": "1.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8b5f17c81..435a809e8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,8 +18,8 @@ importers: specifier: 5.2.0 version: 5.2.0 '@stremio/stremio-core-web': - specifier: 0.51.1 - version: 0.51.1 + specifier: 0.52.0 + version: 0.52.0 '@stremio/stremio-icons': specifier: 5.8.0 version: 5.8.0 @@ -1120,8 +1120,8 @@ packages: '@stremio/stremio-colors@5.2.0': resolution: {integrity: sha512-dYlPgu9W/H7c9s1zmW5tiDnRenaUa4Hg1QCyOg1lhOcgSfM/bVTi5nnqX+IfvGTTUNA0zgzh8hI3o3miwnZxTg==} - '@stremio/stremio-core-web@0.51.1': - resolution: {integrity: sha512-BD8i6zkDdMPeCyH50Bb7SB8r4nYx4eJwz4kLEJEl0PFjdr0gOmwHtEIgNa89ShJLNXUjPnpv4sVSNxFRG8fb5Q==} + '@stremio/stremio-core-web@0.52.0': + resolution: {integrity: sha512-zT0P8JspGZ1oI9/11f3RIt7XG9b/1fOZE+xSnP+oAyhRmzzkqrnPUJkHdJdgoVD9XELDFAS2awNfl5/eRdh5kA==} '@stremio/stremio-icons@5.8.0': resolution: {integrity: sha512-IVUvQbIWfA4YEHCTed7v/sdQJCJ+OOCf84LTWpkE2W6GLQ+15WHcMEJrVkE1X3ekYJnGg3GjT0KLO6tKSU0P4w==} @@ -5870,7 +5870,7 @@ snapshots: '@stremio/stremio-colors@5.2.0': {} - '@stremio/stremio-core-web@0.51.1': + '@stremio/stremio-core-web@0.52.0': dependencies: '@babel/runtime': 7.24.1 From a30307789c6d8cee36c4731e7c162faddda42b19 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 22 Jan 2026 09:57:46 +0100 Subject: [PATCH 113/117] feat: remember subtitles settings on player --- src/routes/Player/Player.js | 64 +++++++++++++++++++----------------- src/types/models/Player.d.ts | 3 ++ 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index 502dfcedb..d82207893 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -41,7 +41,7 @@ const Player = ({ urlParams, queryParams }) => { }, [queryParams]); const profile = useProfile(); const [player, videoParamsChanged, streamStateChanged, timeChanged, seek, pausedChanged, ended, nextVideo] = usePlayer(urlParams); - const [settings, updateSettings] = useSettings(); + const [settings] = useSettings(); const streamingServer = useStreamingServer(); const statistics = useStatistics(player, streamingServer); const video = useVideo(); @@ -256,7 +256,8 @@ const Player = ({ urlParams, queryParams }) => { const onExtraSubtitlesDelayChanged = React.useCallback((delay) => { video.setProp('extraSubtitlesDelay', delay); - }, []); + streamStateChanged({ subtitleDelay: delay }); + }, [streamStateChanged]); const onIncreaseSubtitlesDelay = React.useCallback(() => { const delay = video.state.extraSubtitlesDelay + 250; @@ -269,8 +270,10 @@ const Player = ({ urlParams, queryParams }) => { }, [video.state.extraSubtitlesDelay, onExtraSubtitlesDelayChanged]); const onSubtitlesSizeChanged = React.useCallback((size) => { - updateSettings({ subtitlesSize: size }); - }, [updateSettings]); + video.setProp('subtitlesSize', size); + video.setProp('extraSubtitlesSize', size); + streamStateChanged({ subtitleSize: size }); + }, [streamStateChanged]); const onUpdateSubtitlesSize = React.useCallback((delta) => { const sizeIndex = CONSTANTS.SUBTITLES_SIZES.indexOf(video.state.subtitlesSize); @@ -279,8 +282,10 @@ const Player = ({ urlParams, queryParams }) => { }, [video.state.subtitlesSize, onSubtitlesSizeChanged]); const onSubtitlesOffsetChanged = React.useCallback((offset) => { - updateSettings({ subtitlesOffset: offset }); - }, [updateSettings]); + video.setProp('subtitlesOffset', offset); + video.setProp('extraSubtitlesOffset', offset); + streamStateChanged({ subtitleOffset: offset }); + }, [streamStateChanged]); const onDismissNextVideoPopup = React.useCallback(() => { closeNextVideoPopup(); @@ -408,31 +413,6 @@ const Player = ({ urlParams, queryParams }) => { } }, [player.subtitles, video.state.stream]); - React.useEffect(() => { - video.setProp('subtitlesSize', settings.subtitlesSize); - video.setProp('extraSubtitlesSize', settings.subtitlesSize); - }, [settings.subtitlesSize]); - - React.useEffect(() => { - video.setProp('subtitlesOffset', settings.subtitlesOffset); - video.setProp('extraSubtitlesOffset', settings.subtitlesOffset); - }, [settings.subtitlesOffset]); - - React.useEffect(() => { - video.setProp('subtitlesTextColor', settings.subtitlesTextColor); - video.setProp('extraSubtitlesTextColor', settings.subtitlesTextColor); - }, [settings.subtitlesTextColor]); - - React.useEffect(() => { - video.setProp('subtitlesBackgroundColor', settings.subtitlesBackgroundColor); - video.setProp('extraSubtitlesBackgroundColor', settings.subtitlesBackgroundColor); - }, [settings.subtitlesBackgroundColor]); - - React.useEffect(() => { - video.setProp('subtitlesOutlineColor', settings.subtitlesOutlineColor); - video.setProp('extraSubtitlesOutlineColor', settings.subtitlesOutlineColor); - }, [settings.subtitlesOutlineColor]); - React.useEffect(() => { !seeking && timeChanged(video.state.time, video.state.duration, video.state.manifest?.name); }, [video.state.time, video.state.duration, video.state.manifest, seeking]); @@ -509,6 +489,28 @@ const Player = ({ urlParams, queryParams }) => { } }, [video.state.audioTracks, player.streamState]); + // Saved subtitles settings + React.useEffect(() => { + if (video.state.stream !== null) { + const subtitlesDelay = player.streamState?.subtitleDelay; + if (typeof subtitlesDelay === 'number') { + video.setProp('extraSubtitlesDelay', subtitlesDelay); + } + + const subtitlesSize = player.streamState?.subtitleSize; + if (typeof subtitlesSize === 'number') { + video.setProp('subtitlesSize', subtitlesSize); + video.setProp('extraSubtitlesSize', subtitlesSize); + } + + const subtitlesOffset = player.streamState?.subtitleOffset; + if (typeof subtitlesOffset === 'number') { + video.setProp('subtitlesOffset', subtitlesOffset); + video.setProp('extraSubtitlesOffset', subtitlesOffset); + } + } + }, [video.state.stream, player.streamState]); + React.useEffect(() => { defaultSubtitlesSelected.current = false; defaultAudioTrackSelected.current = false; diff --git a/src/types/models/Player.d.ts b/src/types/models/Player.d.ts index a076f78f4..321127316 100644 --- a/src/types/models/Player.d.ts +++ b/src/types/models/Player.d.ts @@ -41,6 +41,9 @@ type AudioTrackState = { type StreamState = { subtitleTrack?: SubtitlesTrackState, + subtitleDelay?: number, + subtitleSize?: number, + subtitleOffset?: number, audioTrack?: AudioTrackState, }; From e85b67268d3437a009c43f1f0b54884bcfdde3f7 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 22 Jan 2026 10:21:42 +0100 Subject: [PATCH 114/117] refactor(Player): wrap video setProp calls in functions --- src/routes/Player/Player.js | 63 ++++++++++++++------------------- src/routes/Player/useVideo.js | 66 ++++++++++++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 37 deletions(-) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index d82207893..196bb813b 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -96,17 +96,12 @@ const Player = ({ urlParams, queryParams }) => { const isNavigating = React.useRef(false); const onImplementationChanged = React.useCallback(() => { - video.setProp('subtitlesSize', settings.subtitlesSize); - video.setProp('subtitlesOffset', settings.subtitlesOffset); - video.setProp('subtitlesTextColor', settings.subtitlesTextColor); - video.setProp('subtitlesBackgroundColor', settings.subtitlesBackgroundColor); - video.setProp('subtitlesOutlineColor', settings.subtitlesOutlineColor); - video.setProp('extraSubtitlesSize', settings.subtitlesSize); - video.setProp('extraSubtitlesOffset', settings.subtitlesOffset); - video.setProp('extraSubtitlesTextColor', settings.subtitlesTextColor); - video.setProp('extraSubtitlesBackgroundColor', settings.subtitlesBackgroundColor); - video.setProp('extraSubtitlesOutlineColor', settings.subtitlesOutlineColor); - }, [settings.subtitlesSize, settings.subtitlesOffset, settings.subtitlesTextColor, settings.subtitlesBackgroundColor, settings.subtitlesOutlineColor]); + video.setSubtitlesSize(settings.subtitlesSize); + video.setSubtitlesOffset(settings.subtitlesOffset); + video.setSubtitlesTextColor(settings.subtitlesTextColor); + video.setSubtitlesBackgroundColor(settings.subtitlesBackgroundColor); + video.setSubtitlesOutlineColor(settings.subtitlesOutlineColor); + }, [settings]); const handleNextVideoNavigation = React.useCallback((deepLinks, bingeWatching, ended) => { if (ended) { @@ -193,36 +188,36 @@ const Player = ({ urlParams, queryParams }) => { }, []); const onPlayRequested = React.useCallback(() => { - video.setProp('paused', false); + video.setPaused(false); setSeeking(false); }, []); const onPlayRequestedDebounced = React.useCallback(debounce(onPlayRequested, 200), []); const onPauseRequested = React.useCallback(() => { - video.setProp('paused', true); + video.setPaused(true); }, []); const onPauseRequestedDebounced = React.useCallback(debounce(onPauseRequested, 200), []); const onMuteRequested = React.useCallback(() => { - video.setProp('muted', true); + video.setMuted(true); }, []); const onUnmuteRequested = React.useCallback(() => { - video.setProp('muted', false); + video.setMuted(false); }, []); const onVolumeChangeRequested = React.useCallback((volume) => { - video.setProp('volume', volume); + video.setVolume(volume); }, []); const onSeekRequested = React.useCallback((time) => { - video.setProp('time', time); + video.setTime(time); seek(time, video.state.duration, video.state.manifest?.name); }, [video.state.duration, video.state.manifest]); const onPlaybackSpeedChanged = React.useCallback((rate) => { - video.setProp('playbackSpeed', rate); + video.setPlaybackSpeed(rate); }, []); const onSubtitlesTrackSelected = React.useCallback((id) => { @@ -246,7 +241,7 @@ const Player = ({ urlParams, queryParams }) => { }, [streamStateChanged]); const onAudioTrackSelected = React.useCallback((id) => { - video.setProp('selectedAudioTrackId', id); + video.setAudioTrack(id); streamStateChanged({ audioTrack: { id, @@ -255,7 +250,7 @@ const Player = ({ urlParams, queryParams }) => { }, [streamStateChanged]); const onExtraSubtitlesDelayChanged = React.useCallback((delay) => { - video.setProp('extraSubtitlesDelay', delay); + video.setSubtitlesDelay(delay); streamStateChanged({ subtitleDelay: delay }); }, [streamStateChanged]); @@ -270,8 +265,7 @@ const Player = ({ urlParams, queryParams }) => { }, [video.state.extraSubtitlesDelay, onExtraSubtitlesDelayChanged]); const onSubtitlesSizeChanged = React.useCallback((size) => { - video.setProp('subtitlesSize', size); - video.setProp('extraSubtitlesSize', size); + video.setSubtitlesSize(size); streamStateChanged({ subtitleSize: size }); }, [streamStateChanged]); @@ -282,8 +276,7 @@ const Player = ({ urlParams, queryParams }) => { }, [video.state.subtitlesSize, onSubtitlesSizeChanged]); const onSubtitlesOffsetChanged = React.useCallback((offset) => { - video.setProp('subtitlesOffset', offset); - video.setProp('extraSubtitlesOffset', offset); + video.setSubtitlesOffset(offset); streamStateChanged({ subtitleOffset: offset }); }, [streamStateChanged]); @@ -483,7 +476,7 @@ const Player = ({ urlParams, queryParams }) => { findTrackByLang(video.state.audioTracks, settings.audioLanguage); if (audioTrack && audioTrack.id) { - video.setProp('selectedAudioTrackId', audioTrack.id); + video.setAudioTrack(audioTrack.id); defaultAudioTrackSelected.current = true; } } @@ -492,21 +485,19 @@ const Player = ({ urlParams, queryParams }) => { // Saved subtitles settings React.useEffect(() => { if (video.state.stream !== null) { - const subtitlesDelay = player.streamState?.subtitleDelay; - if (typeof subtitlesDelay === 'number') { - video.setProp('extraSubtitlesDelay', subtitlesDelay); + const delay = player.streamState?.subtitleDelay; + if (typeof delay === 'number') { + video.setSubtitlesDelay(delay); } - const subtitlesSize = player.streamState?.subtitleSize; - if (typeof subtitlesSize === 'number') { - video.setProp('subtitlesSize', subtitlesSize); - video.setProp('extraSubtitlesSize', subtitlesSize); + const size = player.streamState?.subtitleSize; + if (typeof size === 'number') { + video.setSubtitlesSize(size); } - const subtitlesOffset = player.streamState?.subtitleOffset; - if (typeof subtitlesOffset === 'number') { - video.setProp('subtitlesOffset', subtitlesOffset); - video.setProp('extraSubtitlesOffset', subtitlesOffset); + const offset = player.streamState?.subtitleOffset; + if (typeof offset === 'number') { + video.setSubtitlesOffset(offset); } } }, [video.state.stream, player.streamState]); diff --git a/src/routes/Player/useVideo.js b/src/routes/Player/useVideo.js index 9b54ba129..b3a5d2e39 100644 --- a/src/routes/Player/useVideo.js +++ b/src/routes/Player/useVideo.js @@ -94,6 +94,30 @@ const useVideo = () => { dispatch({ type: 'setProp', propName: name, propValue: value }); }; + const setPaused = (state) => { + setProp('paused', state); + }; + + const setVolume = (volume) => { + setProp('volume', volume); + }; + + const setMuted = (state) => { + setProp('muted', state); + }; + + const setTime = (time) => { + setProp('time', time); + }; + + const setPlaybackSpeed = (rate) => { + setProp('playbackSpeed', rate); + }; + + const setAudioTrack = (id) => { + setProp('selectedAudioTrackId', id); + }; + const setSubtitlesTrack = (id) => { setProp('selectedSubtitlesTrackId', id); setProp('selectedExtraSubtitlesTrackId', null); @@ -104,6 +128,35 @@ const useVideo = () => { setProp('selectedExtraSubtitlesTrackId', id); }; + const setSubtitlesDelay = (delay) => { + setProp('extraSubtitlesDelay', delay); + }; + + const setSubtitlesSize = (size) => { + setProp('subtitlesSize', size); + setProp('extraSubtitlesSize', size); + }; + + const setSubtitlesOffset = (offset) => { + setProp('subtitlesOffset', offset); + setProp('extraSubtitlesOffset', offset); + }; + + const setSubtitlesTextColor = (color) => { + setProp('subtitlesTextColor', color); + setProp('extraSubtitlesTextColor', color); + }; + + const setSubtitlesBackgroundColor = (color) => { + setProp('subtitlesBackgroundColor', color); + setProp('extraSubtitlesBackgroundColor', color); + }; + + const setSubtitlesOutlineColor = (color) => { + setProp('subtitlesOutlineColor', color); + setProp('extraSubtitlesOutlineColor', color); + }; + const onError = (error) => { events.emit('error', error); }; @@ -171,8 +224,19 @@ const useVideo = () => { unload, addExtraSubtitlesTracks, addLocalSubtitles, - setProp, + setPaused, + setVolume, + setMuted, + setTime, + setPlaybackSpeed, + setAudioTrack, setSubtitlesTrack, + setSubtitlesDelay, + setSubtitlesSize, + setSubtitlesOffset, + setSubtitlesTextColor, + setSubtitlesBackgroundColor, + setSubtitlesOutlineColor, setExtraSubtitlesTrack, }; }; From 15575ee699687f0c7d76afbd6a5aa322bd71bb3a Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 22 Jan 2026 10:24:14 +0100 Subject: [PATCH 115/117] refactor(Player): set audio menu max-height same as subtitles menu --- src/routes/Player/AudioMenu/AudioMenu.less | 1 + 1 file changed, 1 insertion(+) diff --git a/src/routes/Player/AudioMenu/AudioMenu.less b/src/routes/Player/AudioMenu/AudioMenu.less index 344ec13e7..ae3e8acb1 100644 --- a/src/routes/Player/AudioMenu/AudioMenu.less +++ b/src/routes/Player/AudioMenu/AudioMenu.less @@ -7,6 +7,7 @@ align-self: stretch; display: flex; flex-direction: column; + max-height: 25rem; width: 16rem; .header { From cce556e639ab97b131037db66bbe1a7df71516e4 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 22 Jan 2026 10:32:07 +0100 Subject: [PATCH 116/117] feat(Player): show disabled state when ends of range are reached for subtitles settings --- .../Player/SubtitlesMenu/Stepper/Stepper.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/routes/Player/SubtitlesMenu/Stepper/Stepper.tsx b/src/routes/Player/SubtitlesMenu/Stepper/Stepper.tsx index 0d402a455..9307eb2fe 100644 --- a/src/routes/Player/SubtitlesMenu/Stepper/Stepper.tsx +++ b/src/routes/Player/SubtitlesMenu/Stepper/Stepper.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useRef } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import classNames from 'classnames'; import Icon from '@stremio/stremio-icons/react'; @@ -37,6 +37,14 @@ const Stepper = ({ className, label, value, unit, step, min, max, disabled, onCh timeout.cancel(); }; + const decreaseDisabled = useMemo(() => { + return disabled || typeof value !== 'number' || (typeof min === 'number' && value <= min); + }, [disabled, min, value]); + + const increaseDisabled = useMemo(() => { + return disabled || typeof value !== 'number' || (typeof max === 'number' && value >= max); + }, [disabled, max, value]); + const updateValue = useCallback((delta: number) => { onChange(clamp(localValue.current + delta, min, max)); }, [onChange]); @@ -72,7 +80,7 @@ const Stepper = ({ className, label, value, unit, step, min, max, disabled, onCh
- { disabled ? '--' : `${value}${unit}` } + { valueLabel }