diff --git a/package.json b/package.json index 4fe8c9adf..5bed50260 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "stremio", "displayName": "Stremio", - "version": "5.0.0-beta.34", + "version": "5.0.0-beta.35", "author": "Smart Code OOD", "private": true, "license": "gpl-2.0", @@ -18,8 +18,8 @@ "@sentry/browser": "8.42.0", "@stremio/stremio-colors": "5.2.0", "@stremio/stremio-core-web": "0.56.4", - "@stremio/stremio-icons": "5.8.0", - "@stremio/stremio-video": "0.0.75", + "@stremio/stremio-icons": "5.10.0", + "@stremio/stremio-video": "0.0.77", "a-color-picker": "1.2.1", "bowser": "2.11.0", "buffer": "6.0.3", @@ -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#90ea718c18750a0e9cd6824b0ef7c512a41cb90b", + "stremio-translations": "github:Stremio/stremio-translations#c23317eec194b5a3318e98c2ea6acae5cfa32e2a", "url": "0.11.4", "use-long-press": "^3.2.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4c9853d5f..b6aacf8a4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,11 +21,11 @@ importers: specifier: 0.56.4 version: 0.56.4 '@stremio/stremio-icons': - specifier: 5.8.0 - version: 5.8.0 + specifier: 5.10.0 + version: 5.10.0 '@stremio/stremio-video': - specifier: 0.0.75 - version: 0.0.75 + specifier: 0.0.77 + version: 0.0.77 a-color-picker: specifier: 1.2.1 version: 1.2.1 @@ -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#90ea718c18750a0e9cd6824b0ef7c512a41cb90b - version: https://codeload.github.com/Stremio/stremio-translations/tar.gz/90ea718c18750a0e9cd6824b0ef7c512a41cb90b + specifier: github:Stremio/stremio-translations#c23317eec194b5a3318e98c2ea6acae5cfa32e2a + version: https://codeload.github.com/Stremio/stremio-translations/tar.gz/c23317eec194b5a3318e98c2ea6acae5cfa32e2a url: specifier: 0.11.4 version: 0.11.4 @@ -1123,11 +1123,11 @@ packages: '@stremio/stremio-core-web@0.56.4': resolution: {integrity: sha512-tFAMYgKrJ1bkvHRMpxDykM/844sDjgRPFk6FLhjQiwh01OHIyEgDqGo/NgwFM+CuMR4mW676SDvwNHkK0Xqg3w==} - '@stremio/stremio-icons@5.8.0': - resolution: {integrity: sha512-IVUvQbIWfA4YEHCTed7v/sdQJCJ+OOCf84LTWpkE2W6GLQ+15WHcMEJrVkE1X3ekYJnGg3GjT0KLO6tKSU0P4w==} + '@stremio/stremio-icons@5.10.0': + resolution: {integrity: sha512-Zw/vGC3D2yeQfk8xv/tfMJTDvbCPOI91tBg4XpR2+EgbZSX8Xvm7Vz457PIhFPhTAwdOPHp0VX0M3gzjbt0zOg==} - '@stremio/stremio-video@0.0.75': - resolution: {integrity: sha512-oKXMq156BVagzziWoTsmgNYABCSfwV9hR/TM6+JR4lne5pW4qmUN17ba/Fxsr+USKHeCKUaz1u0asKBj06HfyA==} + '@stremio/stremio-video@0.0.77': + resolution: {integrity: sha512-bnKBS5a9R3+M0zx95YpDUiPs1gXcKCsybgdxfZmpWuQaN0RE9bTBAUlIfBSrcEjVhufMOvg+cfXScT+0fBzTTw==} '@stylistic/eslint-plugin-jsx@4.4.1': resolution: {integrity: sha512-83SInq4u7z71vWwGG+6ViOtlOmZ6tSrDkMPhrvdBBTGMLA0gs22WSdhQ4vZP3oJ5Xg4ythvqeUiFSedvVxzhyA==} @@ -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/90ea718c18750a0e9cd6824b0ef7c512a41cb90b: - resolution: {tarball: https://codeload.github.com/Stremio/stremio-translations/tar.gz/90ea718c18750a0e9cd6824b0ef7c512a41cb90b} - version: 1.48.0 + stremio-translations@https://codeload.github.com/Stremio/stremio-translations/tar.gz/c23317eec194b5a3318e98c2ea6acae5cfa32e2a: + resolution: {tarball: https://codeload.github.com/Stremio/stremio-translations/tar.gz/c23317eec194b5a3318e98c2ea6acae5cfa32e2a} + version: 1.51.0 string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} @@ -5874,9 +5874,9 @@ snapshots: dependencies: '@babel/runtime': 7.24.1 - '@stremio/stremio-icons@5.8.0': {} + '@stremio/stremio-icons@5.10.0': {} - '@stremio/stremio-video@0.0.75': + '@stremio/stremio-video@0.0.77': dependencies: buffer: 6.0.3 color: 4.2.3 @@ -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/90ea718c18750a0e9cd6824b0ef7c512a41cb90b: {} + stremio-translations@https://codeload.github.com/Stremio/stremio-translations/tar.gz/c23317eec194b5a3318e98c2ea6acae5cfa32e2a: {} string-length@4.0.2: dependencies: diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index a5756fc42..e1566c5d5 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -10,6 +10,7 @@ type Props = { style?: object, href?: string, target?: string + download?: string, title?: string, disabled?: boolean, tabIndex?: number, diff --git a/src/components/ContextMenu/ContextMenu.tsx b/src/components/ContextMenu/ContextMenu.tsx index cb2d1f50c..1ac72949a 100644 --- a/src/components/ContextMenu/ContextMenu.tsx +++ b/src/components/ContextMenu/ContextMenu.tsx @@ -7,17 +7,20 @@ const PADDING = 8; type Coordinates = [number, number]; type Size = [number, number]; +type Lock = 'top' | 'right' | 'bottom' | 'left'; type Props = { children: React.ReactNode, on: RefObject[], autoClose: boolean, + lock?: Lock, }; -const ContextMenu = ({ children, on, autoClose }: Props) => { +const ContextMenu = ({ children, on, autoClose, lock }: Props) => { const [active, setActive] = useState(false); const [position, setPosition] = useState([0, 0]); const [containerSize, setContainerSize] = useState([0, 0]); + const [triggerRect, setTriggerRect] = useState(null); const ref = useCallback((element: HTMLDivElement) => { element && setContainerSize([element.offsetWidth, element.offsetHeight]); @@ -26,7 +29,32 @@ const ContextMenu = ({ children, on, autoClose }: Props) => { const style = useMemo(() => { const [viewportWidth, viewportHeight] = [window.innerWidth, window.innerHeight]; const [containerWidth, containerHeight] = containerSize; - const [x, y] = position; + + let x: number; + let y: number; + + if (lock && triggerRect) { + switch (lock) { + case 'top': + x = triggerRect.left; + y = triggerRect.top - containerHeight; + break; + case 'bottom': + x = triggerRect.left; + y = triggerRect.bottom; + break; + case 'left': + x = triggerRect.left - containerWidth; + y = triggerRect.top; + break; + case 'right': + x = triggerRect.right; + y = triggerRect.top; + break; + } + } else { + [x, y] = position; + } const left = Math.max( PADDING, @@ -45,7 +73,7 @@ const ContextMenu = ({ children, on, autoClose }: Props) => { ); return { top, left }; - }, [position, containerSize]); + }, [position, containerSize, lock, triggerRect]); const close = () => { setActive(false); @@ -55,12 +83,17 @@ const ContextMenu = ({ children, on, autoClose }: Props) => { event.stopPropagation(); }; - const onContextMenu = (event: MouseEvent) => { + const onContextMenu = useCallback((event: MouseEvent) => { event.preventDefault(); - setPosition([event.clientX, event.clientY]); + if (lock) { + const target = event.currentTarget as HTMLElement; + setTriggerRect(target.getBoundingClientRect()); + } else { + setPosition([event.clientX, event.clientY]); + } setActive(true); - }; + }, [lock]); const handleKeyDown = useCallback((event: KeyboardEvent) => event.key === 'Escape' && close(), []); @@ -76,7 +109,7 @@ const ContextMenu = ({ children, on, autoClose }: Props) => { on.forEach((ref) => ref.current && ref.current.removeEventListener('contextmenu', onContextMenu)); document.removeEventListener('keydown', handleKeyDown); }; - }, [on]); + }, [on, onContextMenu, handleKeyDown]); return createPortal(( diff --git a/src/components/NavBar/HorizontalNavBar/HorizontalNavBar.js b/src/components/NavBar/HorizontalNavBar/HorizontalNavBar.js index 65bf30c94..6fed91c8a 100644 --- a/src/components/NavBar/HorizontalNavBar/HorizontalNavBar.js +++ b/src/components/NavBar/HorizontalNavBar/HorizontalNavBar.js @@ -12,7 +12,7 @@ const NavMenu = require('./NavMenu'); const styles = require('./styles'); const { t } = require('i18next'); -const HorizontalNavBar = React.memo(({ className, route, query, title, backButton, searchBar, fullscreenButton, navMenu, ...props }) => { +const HorizontalNavBar = React.memo(({ className, route, query, title, backButton, searchBar, fullscreenButton, navMenu, hdrInfo, ...props }) => { const backButtonOnClick = React.useCallback(() => { window.history.back(); }, []); @@ -53,6 +53,14 @@ const HorizontalNavBar = React.memo(({ className, route, query, title, backButto null }
+ { + hdrInfo && (hdrInfo.gamma === 'pq' || hdrInfo.gamma === 'hlg') ? +
+ +
+ : + null + } { !isIOSPWA && fullscreenButton ? @@ -194,6 +200,9 @@ ControlBar.propTypes = { volume: PropTypes.number, muted: PropTypes.bool, playbackSpeed: PropTypes.number, + videoScale: PropTypes.string, + videoScaleLabel: PropTypes.string, + onVideoScaleChanged: PropTypes.func, subtitlesTracks: PropTypes.array, audioTracks: PropTypes.array, metaItem: PropTypes.object, diff --git a/src/routes/Player/Indicator/Indicator.tsx b/src/routes/Player/Indicator/Indicator.tsx index 7525fe1cd..c7591b028 100644 --- a/src/routes/Player/Indicator/Indicator.tsx +++ b/src/routes/Player/Indicator/Indicator.tsx @@ -7,7 +7,13 @@ import styles from './Indicator.less'; type Property = { label: string, - format: (value: number) => string, + format: (value: number | string) => string, +}; + +const VIDEO_SCALE_KEYS: Record = { + 'contain': 'PLAYER_SCALE_FIT', + 'cover': 'PLAYER_SCALE_CROP', + 'fill': 'PLAYER_SCALE_STRETCH', }; const PROPERTIES: Record = { @@ -15,9 +21,13 @@ const PROPERTIES: Record = { label: 'SUBTITLES_DELAY', format: (value) => `${(value / 1000).toFixed(2)}s`, }, + 'videoScale': { + label: 'VIDEO_SCALE', + format: (value) => t(VIDEO_SCALE_KEYS[String(value)] || String(value)), + }, }; -type VideoState = Record; +type VideoState = Record; type Props = { className: string, @@ -28,6 +38,7 @@ type Props = { const Indicator = ({ className, videoState, disabled }: Props) => { const timeout = useRef(null); const prevVideoState = useRef(videoState); + const initialized = useRef>(new Set()); const [shown, show, hide] = useBinaryState(false); const [current, setCurrent] = useState(null); @@ -49,11 +60,15 @@ const Indicator = ({ className, videoState, disabled }: Props) => { const next = videoState[property]; if (next && next !== prev) { - setCurrent(property); - show(); + if (!initialized.current.has(property)) { + initialized.current.add(property); + } else { + setCurrent(property); + show(); - timeout.current && clearTimeout(timeout.current); - timeout.current = setTimeout(hide, 1000); + timeout.current && clearTimeout(timeout.current); + timeout.current = setTimeout(hide, 1000); + } } } diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index 77e8a952c..42ff18e7b 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -98,6 +98,9 @@ const Player = ({ urlParams, queryParams }) => { const isNavigating = React.useRef(false); + const VIDEO_SCALES = ['contain', 'cover', 'fill']; + const VIDEO_SCALE_LABELS = { contain: t('PLAYER_SCALE_FIT'), cover: t('PLAYER_SCALE_CROP'), fill: t('PLAYER_SCALE_STRETCH') }; + const playbackSpeed = React.useRef(video.state.playbackSpeed || 1); const pressTimer = React.useRef(null); const longPress = React.useRef(false); @@ -236,6 +239,13 @@ const Player = ({ urlParams, queryParams }) => { }, []); + const onVideoScaleChanged = React.useCallback(() => { + const currentScale = video.state.videoScale || 'contain'; + const currentIndex = VIDEO_SCALES.indexOf(currentScale); + const nextScale = VIDEO_SCALES[(currentIndex + 1) % VIDEO_SCALES.length]; + video.setVideoScale(nextScale); + }, [video.state.videoScale]); + const onSubtitlesTrackSelected = React.useCallback((track) => { video.setSubtitlesTrack(track?.id ?? null); streamStateChanged({ @@ -599,6 +609,29 @@ const Player = ({ urlParams, queryParams }) => { useMediaSession(video.state, player, onPlayRequested, onPauseRequested, onNextVideoRequested); + React.useEffect(() => { + const onMediaKey = (action) => { + switch (action) { + case 'play-pause': + video.state.paused ? onPlayRequested() : onPauseRequested(); + break; + case 'next-track': + if (player.nextVideo !== null) { + video.setTime(0); + onNextVideoRequested(); + } + break; + case 'previous-track': + if (video.state.time !== null && video.state.time > 5000) { + onSeekRequested(0); + } + break; + } + }; + shell.on('media-key', onMediaKey); + return () => shell.off('media-key', onMediaKey); + }, [video.state.paused, video.state.time, player.nextVideo, onPlayRequested, onPauseRequested, onNextVideoRequested, onSeekRequested]); + onShortcut('seekForward', (combo) => { if (video.state.time !== null) { const seekDuration = combo === 1 ? settings.seekShortTimeDuration : settings.seekTimeDuration; @@ -916,6 +949,7 @@ const Player = ({ urlParams, queryParams }) => { title={player.title !== null ? player.title : ''} backButton={true} fullscreenButton={true} + hdrInfo={video.state.hdrInfo} onMouseMove={onBarMouseMove} onMouseOver={onBarMouseMove} /> @@ -955,6 +989,9 @@ const Player = ({ urlParams, queryParams }) => { onToggleSubtitlesMenu={toggleSubtitlesMenu} onToggleAudioMenu={toggleAudioMenu} onToggleSpeedMenu={toggleSpeedMenu} + videoScale={video.state.videoScale} + videoScaleLabel={VIDEO_SCALE_LABELS[video.state.videoScale || 'contain']} + onVideoScaleChanged={onVideoScaleChanged} onToggleStatisticsMenu={toggleStatisticsMenu} onToggleSideDrawer={toggleSideDrawer} onMouseMove={onBarMouseMove} diff --git a/src/routes/Player/SubtitlesMenu/SubtitleVariant/SubtitleVariant.less b/src/routes/Player/SubtitlesMenu/SubtitleVariant/SubtitleVariant.less new file mode 100644 index 000000000..600b79658 --- /dev/null +++ b/src/routes/Player/SubtitlesMenu/SubtitleVariant/SubtitleVariant.less @@ -0,0 +1,82 @@ +// Copyright (C) 2017-2026 Smart code 203358507 + +@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less'; + +.variant-option { + display: flex; + flex-direction: row; + align-items: center; + height: 4rem; + padding: 0 1.5rem; + margin-bottom: 0.5rem; + border-radius: var(--border-radius); + + &:global(.selected), &:hover { + background-color: var(--overlay-color); + } + + .info { + flex: 1; + display: flex; + flex-direction: column; + gap: 0.25rem; + + .variant-label { + flex: 1; + font-size: 1.1rem; + line-height: 1.5rem; + color: var(--primary-foreground-color); + text-wrap: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + + .variant-origin { + font-size: 0.9rem; + color: var(--color-placeholder-text); + text-wrap: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + } + + .icon { + flex: none; + width: 0.5rem; + height: 0.5rem; + border-radius: 100%; + margin-left: 1rem; + background-color: var(--secondary-accent-color); + } +} + +.context-menu-option { + display: flex; + flex-direction: row; + align-items: center; + gap: 1rem; + min-width: 16rem; + padding: 1.25rem 1.5rem; + + &:hover, &:focus { + background-color: var(--overlay-color); + } + + .menu-icon { + flex: none; + width: 1.4rem; + height: 1.4rem; + color: var(--color-placeholder); + } + + .context-menu-option-label { + flex: 1; + min-width: 0; + font-size: 1rem; + font-weight: 400; + color: var(--primary-foreground-color); + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } +} diff --git a/src/routes/Player/SubtitlesMenu/SubtitleVariant/SubtitleVariant.tsx b/src/routes/Player/SubtitlesMenu/SubtitleVariant/SubtitleVariant.tsx new file mode 100644 index 000000000..9bd8f909c --- /dev/null +++ b/src/routes/Player/SubtitlesMenu/SubtitleVariant/SubtitleVariant.tsx @@ -0,0 +1,136 @@ +// Copyright (C) 2017-2026 Smart code 203358507 + +import React, { useCallback, useMemo, useRef } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Button, ContextMenu } from 'stremio/components'; +import { languages, useToast } from 'stremio/common'; +import classNames from 'classnames'; +import Icon from '@stremio/stremio-icons/react'; +import styles from './SubtitleVariant.less'; + +type SubtitlesTrack = { + id: string, + addonSubtitleId?: string, + lang: string, + origin: string, + label?: string, + url?: string, + fallbackUrl?: string, + embedded?: boolean, + local?: boolean, + exclusive?: boolean, +}; + +type Props = { + track: SubtitlesTrack, + selected: boolean, + onSelect: (track: SubtitlesTrack) => void, +}; + +const hasValidLabel = (label?: string) => label && label.length > 0 && !label.startsWith('http'); + +const SubtitleVariant = ({ track, selected, onSelect }: Props) => { + const { t } = useTranslation(); + const toast = useToast(); + const buttonRef = useRef(null); + const triggers = useMemo(() => [buttonRef], []); + + const downloadUrl = track.fallbackUrl || track.url; + const variantLabel = hasValidLabel(track.label) ? track.label : languages.label(track.lang); + const downloadFileName = hasValidLabel(track.label) ? track.label : `subtitle-${track.lang || 'unknown'}`; + const canCopyUrl = typeof downloadUrl === 'string' && !downloadUrl.startsWith('blob:'); + const hoverTitle = hasValidLabel(track.label) + ? track.label + : downloadUrl?.split('/').pop()?.split('?')[0] || variantLabel; + + const onSelectClick = useCallback(() => { + onSelect(track); + }, [onSelect, track]); + + const copyToClipboard = useCallback((value: string, successKey: string, errorKey: string) => { + navigator.clipboard.writeText(value) + .then(() => toast.show({ type: 'success', title: t(successKey), timeout: 4000 })) + .catch(() => toast.show({ type: 'error', title: t(errorKey), timeout: 4000 })); + }, [toast, t]); + + const onCopyUrlClick = useCallback(() => { + if (downloadUrl) { + copyToClipboard(downloadUrl, 'PLAYER_COPY_SUBTITLE_URL_SUCCESS', 'PLAYER_COPY_SUBTITLE_URL_ERROR'); + } + }, [downloadUrl, copyToClipboard]); + + const onCopyIdClick = useCallback(() => { + if (track.addonSubtitleId) { + copyToClipboard(track.addonSubtitleId, 'PLAYER_COPY_SUBTITLE_ID_SUCCESS', 'PLAYER_COPY_SUBTITLE_ID_ERROR'); + } + }, [track.addonSubtitleId, copyToClipboard]); + + return ( + + : + null + } + {canCopyUrl ? + + : + null + } + {track.addonSubtitleId ? + + : + null + } + + } + + ); +}; + +export default SubtitleVariant; diff --git a/src/routes/Player/SubtitlesMenu/SubtitleVariant/index.ts b/src/routes/Player/SubtitlesMenu/SubtitleVariant/index.ts new file mode 100644 index 000000000..16bda596b --- /dev/null +++ b/src/routes/Player/SubtitlesMenu/SubtitleVariant/index.ts @@ -0,0 +1,5 @@ +// Copyright (C) 2017-2026 Smart code 203358507 + +import SubtitleVariant from './SubtitleVariant'; + +export default SubtitleVariant; diff --git a/src/routes/Player/SubtitlesMenu/SubtitlesMenu.js b/src/routes/Player/SubtitlesMenu/SubtitlesMenu.js index 742904f3a..a71969bd5 100644 --- a/src/routes/Player/SubtitlesMenu/SubtitlesMenu.js +++ b/src/routes/Player/SubtitlesMenu/SubtitlesMenu.js @@ -9,6 +9,7 @@ const { Button } = require('stremio/components'); const styles = require('./styles'); const { t } = require('i18next'); const { default: Stepper } = require('./Stepper'); +const { default: SubtitleVariant } = require('./SubtitleVariant'); const ORIGIN_PRIORITIES = [ 'LOCAL', @@ -102,9 +103,8 @@ const SubtitlesMenu = React.memo(React.forwardRef((props, ref) => { } } }, [allSubtitles, props.onSubtitlesTrackSelected, props.onExtraSubtitlesTrackSelected]); - const subtitlesTrackOnClick = React.useCallback((event) => { - const track = subtitlesTracksForLanguage.find((t) => t.id === event.currentTarget.dataset.id) ?? null; - if (track?.embedded) { + const subtitlesTrackOnSelect = React.useCallback((track) => { + if (track.embedded) { if (typeof props.onSubtitlesTrackSelected === 'function') { props.onSubtitlesTrackSelected(track); } @@ -113,7 +113,7 @@ const SubtitlesMenu = React.memo(React.forwardRef((props, ref) => { props.onExtraSubtitlesTrackSelected(track); } } - }, [subtitlesTracksForLanguage, props.onSubtitlesTrackSelected, props.onExtraSubtitlesTrackSelected]); + }, [props.onSubtitlesTrackSelected, props.onExtraSubtitlesTrackSelected]); const onSubtitlesDelayChanged = React.useCallback((value) => { if (typeof props.selectedExtraSubtitlesTrackId === 'string') { if (props.extraSubtitlesDelay !== null && !isNaN(props.extraSubtitlesDelay)) { @@ -190,24 +190,12 @@ const SubtitlesMenu = React.memo(React.forwardRef((props, ref) => { subtitlesTracksForLanguage.length > 0 ?
{subtitlesTracksForLanguage.map((track, index) => ( - + ))}
: @@ -276,7 +264,11 @@ SubtitlesMenu.propTypes = { id: PropTypes.string.isRequired, lang: PropTypes.string.isRequired, origin: PropTypes.string.isRequired, - label: PropTypes.string.isRequired + label: PropTypes.string, + url: PropTypes.string, + embedded: PropTypes.bool, + local: PropTypes.bool, + exclusive: PropTypes.bool })), selectedExtraSubtitlesTrackId: PropTypes.string, extraSubtitlesOffset: PropTypes.number, diff --git a/src/routes/Player/SubtitlesMenu/styles.less b/src/routes/Player/SubtitlesMenu/styles.less index bed7be75d..b0aa3b051 100644 --- a/src/routes/Player/SubtitlesMenu/styles.less +++ b/src/routes/Player/SubtitlesMenu/styles.less @@ -27,7 +27,7 @@ overflow-y: auto; padding: 0 1rem; - .language-option, .variant-option { + .language-option { display: flex; flex-direction: row; align-items: center; @@ -40,13 +40,10 @@ background-color: var(--overlay-color); } - .language-label, .variant-label { + .language-label { flex: 1; font-size: 1.1rem; color: var(--primary-foreground-color); - } - - .language-label, .variant-label, .variant-origin { text-wrap: nowrap; text-overflow: ellipsis; } @@ -60,26 +57,6 @@ background-color: var(--secondary-accent-color); } } - - .variant-option { - height: 4rem; - - .info { - flex: 1; - display: flex; - flex-direction: column; - gap: 0.25rem; - - .variant-label { - line-height: 1.5rem; - } - - .variant-origin { - font-size: 0.9rem; - color: var(--color-placeholder-text); - } - } - } } } diff --git a/src/routes/Player/styles.less b/src/routes/Player/styles.less index 4894791f0..4ded33221 100644 --- a/src/routes/Player/styles.less +++ b/src/routes/Player/styles.less @@ -65,7 +65,6 @@ html:not(.active-slider-within) { top: 0; left: 0; z-index: -1; - box-shadow: 0 0 8rem 6rem @color-background-dark5; content: ""; } @@ -102,7 +101,6 @@ html:not(.active-slider-within) { bottom: 0; left: 0; z-index: -1; - box-shadow: 0 0 8rem 8rem @color-background-dark5; content: ""; } } diff --git a/src/routes/Player/useVideo.js b/src/routes/Player/useVideo.js index b3a5d2e39..241a5af00 100644 --- a/src/routes/Player/useVideo.js +++ b/src/routes/Player/useVideo.js @@ -22,6 +22,7 @@ const useVideo = () => { muted: null, playbackSpeed: null, videoParams: null, + hdrInfo: null, audioTracks: [], selectedAudioTrackId: null, subtitlesTracks: [], @@ -142,6 +143,10 @@ const useVideo = () => { setProp('extraSubtitlesOffset', offset); }; + const setVideoScale = (scale) => { + setProp('videoScale', scale); + }; + const setSubtitlesTextColor = (color) => { setProp('subtitlesTextColor', color); setProp('extraSubtitlesTextColor', color); @@ -238,6 +243,7 @@ const useVideo = () => { setSubtitlesBackgroundColor, setSubtitlesOutlineColor, setExtraSubtitlesTrack, + setVideoScale, }; };