Compare commits

...

36 commits

Author SHA1 Message Date
Tim
da675cd56c chore: update caniuse
Some checks failed
Build / build (push) Has been cancelled
2026-01-10 01:12:20 +01:00
Tim
9b3b0d67ba
Merge pull request #1095 from Stremio/feat/player-mute-shortcut-2
Player: Add mute shortcut
2026-01-10 01:02:18 +01:00
Tim
fc2d906a42 Merge branch 'development' of https://github.com/Stremio/stremio-web into feat/player-mute-shortcut-2 2026-01-10 00:56:47 +01:00
Tim
c15ca17d2d
Merge pull request #1097 from Stremio/refactor/player-shortcuts
Dev: use shortcuts provider on player
2026-01-09 23:21:16 +01:00
Timothy Z.
55963fd23e
Merge pull request #1106 from Stremio/chore/align-error-styles-across-app
Some checks failed
Build / build (push) Has been cancelled
App: update & align error color styles
2025-12-31 17:33:05 +02:00
Timothy Z.
80066b2f3f chore: update styles after trakt icon change 2025-12-31 17:31:37 +02:00
Timothy Z.
c8dfc31e6b
Merge pull request #1100 from PL7963/development
Some checks failed
Build / build (push) Has been cancelled
MetaDetails: Add missing backdrop filter to ratings
2025-12-26 20:01:13 +01:00
Coolkie
84a172d1bf fix(Ratings): move backdrop filter to ratings container 2025-12-26 08:24:04 +00:00
Coolkie
6fbc08a720 fix(Ratings): add backdrop filter to icon container 2025-12-25 17:57:57 +00:00
Tim
2bc0f3468c chore: update translations 2025-12-18 16:11:53 +01:00
Tim
c9a40aabd7 refactor: use shortcuts provider on player 2025-12-18 13:46:05 +01:00
Lachezar Lechev
7046622fb6
feat: player - mute shortcut
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-12-17 14:15:06 +02:00
Timothy Z.
5dc088b798
Merge pull request #1094 from Stremio/fix/selected-video-styles
Some checks failed
Build / build (push) Has been cancelled
SideDrawer: Always show selected video border
2025-12-16 12:05:26 +02:00
Timothy Z.
b5bd75fd94 Update styles.less 2025-12-16 11:47:58 +02:00
Timothy Z.
16b2eb8d17 chore: revert change 2025-12-16 11:47:24 +02:00
Botzy
c4ab2dc546 fix(Video): always show border of selected video 2025-12-16 11:00:04 +02:00
Timothy Z.
227f21c10f
Merge pull request #1092 from Stremio/fix/trakt-logo
Some checks are pending
Build / build (push) Waiting to run
Settings: update trakt logo styling
2025-12-15 18:54:51 +02:00
Timothy Z.
d21be690de chore: correct size 2025-12-15 17:29:07 +02:00
dexter21767-dev
6c7a2755fb update trakt logo styling 2025-12-15 16:17:49 +01:00
Timothy Z.
bfb5c484fc
Merge pull request #1079 from sagarchaulagai/development
Some checks failed
Build / build (push) Has been cancelled
Settings: Fix incorrect tab highlighting
2025-12-10 11:52:13 +02:00
Sagar Prasad Chaulagain
88fca500f1 fixes #1078 2025-12-09 08:48:12 +05:45
Timothy Z.
058bb58bfb
Merge pull request #1084 from Stremio/fix/addons-selectable-inputs
Some checks failed
Build / build (push) Has been cancelled
[Addons]: Fix default title for addons type select
2025-12-08 17:36:08 +02:00
Botzy
9a9cd2de12 fix: default title for addon type select 2025-12-08 17:06:50 +02:00
Timothy Z.
4881f2c340
Merge pull request #1082 from Stremio/fix/streaming-server-warning
Some checks are pending
Build / build (push) Waiting to run
Fix: Show Streaming Server warning correctly
2025-12-08 10:24:07 +01:00
Botzy
a744932949 fix: correct check for showing streaming server warning 2025-12-03 16:53:00 +02:00
Sagar Prasad Chaulagain
8148a2f8fe fixes #1078 2025-12-01 13:23:09 +05:45
Sagar Prasad Chaulagain
6aef6e1d04 Added small tolerance of 10px, fixes #1078 2025-12-01 13:13:55 +05:45
Timothy Z.
9cbfd15793 chore: bump v5.0.0-beta.29
Some checks failed
Build / build (push) Has been cancelled
bug fix release
2025-11-28 16:03:08 +02:00
Tim
292cd9d03e Merge branch 'development' of https://github.com/Stremio/stremio-web into development 2025-11-28 14:54:57 +01:00
Tim
cb74f3be65 fix(Settings): scroll sections error 2025-11-28 14:54:43 +01:00
Timothy Z.
f688a11751 chore: bump core v0.51.1 2025-11-28 15:46:39 +02:00
Tim
1f93175e98
Merge pull request #1077 from Stremio/fix/settings-shortcuts-overflow
Settings: Fix shortcuts layout issue on mobile
2025-11-28 14:20:03 +01:00
Tim
4b10795113 fix(Settings): shortcuts layout issue on mobile 2025-11-28 14:02:10 +01:00
Tim
aa571a7f8f
Merge pull request #1076 from Stremio/refactor/settings-remove-shortcuts-mobile
Settings: Remove shortcuts section on mobile
2025-11-28 13:34:01 +01:00
Tim
5e278a5244 refactor(Settings): remove shortcuts on mobile 2025-11-28 13:01:56 +01:00
Tim
17746db439 ci(release): add pnpm setup
Some checks are pending
Build / build (push) Waiting to run
2025-11-27 17:45:41 +01:00
22 changed files with 210 additions and 201 deletions

View file

@ -10,7 +10,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Install NPM dependencies
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
run_install: false
- name: Install dependencies
run: pnpm install
- name: Build
env:

View file

@ -1,7 +1,7 @@
{
"name": "stremio",
"displayName": "Stremio",
"version": "5.0.0-beta.28",
"version": "5.0.0-beta.29",
"author": "Smart Code OOD",
"private": true,
"license": "gpl-2.0",
@ -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.0",
"@stremio/stremio-core-web": "0.51.1",
"@stremio/stremio-icons": "5.8.0",
"@stremio/stremio-video": "0.0.64",
"a-color-picker": "1.2.1",
@ -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#01aaa201e419782b26b9f2cbe4430795021426e5",
"stremio-translations": "github:Stremio/stremio-translations#0e7fbd8522148f5727ac6adee3b2eb96132c10ac",
"url": "0.11.4",
"use-long-press": "^3.2.0"
},

View file

@ -18,8 +18,8 @@ importers:
specifier: 5.2.0
version: 5.2.0
'@stremio/stremio-core-web':
specifier: 0.51.0
version: 0.51.0
specifier: 0.51.1
version: 0.51.1
'@stremio/stremio-icons':
specifier: 5.8.0
version: 5.8.0
@ -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#01aaa201e419782b26b9f2cbe4430795021426e5
version: https://codeload.github.com/Stremio/stremio-translations/tar.gz/01aaa201e419782b26b9f2cbe4430795021426e5
specifier: github:Stremio/stremio-translations#0e7fbd8522148f5727ac6adee3b2eb96132c10ac
version: https://codeload.github.com/Stremio/stremio-translations/tar.gz/0e7fbd8522148f5727ac6adee3b2eb96132c10ac
url:
specifier: 0.11.4
version: 0.11.4
@ -1302,8 +1302,8 @@ packages:
'@stremio/stremio-colors@5.2.0':
resolution: {integrity: sha512-dYlPgu9W/H7c9s1zmW5tiDnRenaUa4Hg1QCyOg1lhOcgSfM/bVTi5nnqX+IfvGTTUNA0zgzh8hI3o3miwnZxTg==}
'@stremio/stremio-core-web@0.51.0':
resolution: {integrity: sha512-rCgUrnG31wINZxf35LMvT3Cm/h+hEaJTagne99I9W7Wz8cGdMotVUxFzV8bkJLZh7q19+HkIr4WnEizkXuUZpg==}
'@stremio/stremio-core-web@0.51.1':
resolution: {integrity: sha512-BD8i6zkDdMPeCyH50Bb7SB8r4nYx4eJwz4kLEJEl0PFjdr0gOmwHtEIgNa89ShJLNXUjPnpv4sVSNxFRG8fb5Q==}
'@stremio/stremio-icons@5.8.0':
resolution: {integrity: sha512-IVUvQbIWfA4YEHCTed7v/sdQJCJ+OOCf84LTWpkE2W6GLQ+15WHcMEJrVkE1X3ekYJnGg3GjT0KLO6tKSU0P4w==}
@ -1808,8 +1808,8 @@ packages:
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
baseline-browser-mapping@2.8.7:
resolution: {integrity: sha512-bxxN2M3a4d1CRoQC//IqsR5XrLh0IJ8TCv2x6Y9N0nckNz/rTjZB3//GGscZziZOxmjP55rzxg/ze7usFI9FqQ==}
baseline-browser-mapping@2.9.14:
resolution: {integrity: sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg==}
hasBin: true
batch@0.6.1:
@ -1907,8 +1907,8 @@ packages:
caniuse-api@3.0.0:
resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==}
caniuse-lite@1.0.30001745:
resolution: {integrity: sha512-ywt6i8FzvdgrrrGbr1jZVObnVv6adj+0if2/omv9cmR2oiZs30zL4DIyaptKcbOrBdOIc74QTMoJvSE2QHh5UQ==}
caniuse-lite@1.0.30001763:
resolution: {integrity: sha512-mh/dGtq56uN98LlNX9qdbKnzINhX0QzhiWBFEkFfsFO4QyCvL8YegrJAazCwXIeqkIob8BlZPGM3xdnY+sgmvQ==}
centra@2.7.0:
resolution: {integrity: sha512-PbFMgMSrmgx6uxCdm57RUos9Tc3fclMvhLSATYN39XsDV29B89zZ3KA89jmY0vwSGazyU+uerqwa6t+KaodPcg==}
@ -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/01aaa201e419782b26b9f2cbe4430795021426e5:
resolution: {tarball: https://codeload.github.com/Stremio/stremio-translations/tar.gz/01aaa201e419782b26b9f2cbe4430795021426e5}
version: 1.44.13
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
string-length@4.0.2:
resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==}
@ -6561,7 +6561,7 @@ snapshots:
'@stremio/stremio-colors@5.2.0': {}
'@stremio/stremio-core-web@0.51.0':
'@stremio/stremio-core-web@0.51.1':
dependencies:
'@babel/runtime': 7.24.1
@ -7125,7 +7125,7 @@ snapshots:
autoprefixer@10.4.21(postcss@8.5.6):
dependencies:
browserslist: 4.26.2
caniuse-lite: 1.0.30001745
caniuse-lite: 1.0.30001763
fraction.js: 4.3.7
normalize-range: 0.1.2
picocolors: 1.1.1
@ -7226,7 +7226,7 @@ snapshots:
base64-js@1.5.1: {}
baseline-browser-mapping@2.8.7: {}
baseline-browser-mapping@2.9.14: {}
batch@0.6.1: {}
@ -7277,8 +7277,8 @@ snapshots:
browserslist@4.26.2:
dependencies:
baseline-browser-mapping: 2.8.7
caniuse-lite: 1.0.30001745
baseline-browser-mapping: 2.9.14
caniuse-lite: 1.0.30001763
electron-to-chromium: 1.5.224
node-releases: 2.0.21
update-browserslist-db: 1.1.3(browserslist@4.26.2)
@ -7338,11 +7338,11 @@ snapshots:
caniuse-api@3.0.0:
dependencies:
browserslist: 4.26.2
caniuse-lite: 1.0.30001745
caniuse-lite: 1.0.30001763
lodash.memoize: 4.1.2
lodash.uniq: 4.5.0
caniuse-lite@1.0.30001745: {}
caniuse-lite@1.0.30001763: {}
centra@2.7.0:
dependencies:
@ -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/01aaa201e419782b26b9f2cbe4430795021426e5: {}
stremio-translations@https://codeload.github.com/Stremio/stremio-translations/tar.gz/0e7fbd8522148f5727ac6adee3b2eb96132c10ac: {}
string-length@4.0.2:
dependencies:

View file

@ -48,7 +48,7 @@
--color-x: #000000;
--color-reddit: #FF4500;
--color-imdb: #f5c518;
--color-trakt: #ED2224;
--color-trakt: rgb(255, 255, 255);
--color-placeholder: #60606080;
--color-placeholder-text: @color-surface-50;
--color-placeholder-background: @color-surface-dark5-20;

View file

@ -1,13 +1,15 @@
import React, { createContext, useCallback, useContext, useEffect } from 'react';
import React, { createContext, useCallback, useContext, useEffect, useRef } from 'react';
import shortcuts from './shortcuts.json';
const SHORTCUTS = shortcuts.map(({ shortcuts }) => shortcuts).flat();
export type ShortcutName = string;
export type ShortcutListener = () => void;
export type ShortcutListener = (combo: number) => void;
interface ShortcutsContext {
grouped: ShortcutGroup[],
on: (name: ShortcutName, listener: ShortcutListener) => void,
off: (name: ShortcutName, listener: ShortcutListener) => void,
}
const ShortcutsContext = createContext<ShortcutsContext>({} as ShortcutsContext);
@ -18,27 +20,38 @@ type Props = {
};
const ShortcutsProvider = ({ children, onShortcut }: Props) => {
const onKeyDown = useCallback(({ ctrlKey, shiftKey, key }: KeyboardEvent) => {
const listeners = useRef<Map<ShortcutName, Set<ShortcutListener>>>(new Map());
const onKeyDown = useCallback(({ ctrlKey, shiftKey, code, 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())) {
if (modifers && (keys.includes(code) || keys.includes(key.toUpperCase()))) {
const combo = combos.indexOf(keys);
listeners.current.get(name)?.forEach((listener) => listener(combo));
onShortcut(name as ShortcutName);
}
}));
}, [onShortcut]);
const on = (name: ShortcutName, listener: ShortcutListener) => {
!listeners.current.has(name) && listeners.current.set(name, new Set());
listeners.current.get(name)!.add(listener);
};
const off = (name: ShortcutName, listener: ShortcutListener) => {
listeners.current.get(name)?.delete(listener);
};
useEffect(() => {
document.addEventListener('keydown', onKeyDown);
return () => {
document.removeEventListener('keydown', onKeyDown);
};
return () => document.removeEventListener('keydown', onKeyDown);
}, [onKeyDown]);
return (
<ShortcutsContext.Provider value={{ grouped: shortcuts }}>
<ShortcutsContext.Provider value={{ grouped: shortcuts, on, off }}>
{children}
</ShortcutsContext.Provider>
);
@ -50,5 +63,5 @@ const useShortcuts = () => {
export {
ShortcutsProvider,
useShortcuts
useShortcuts,
};

View file

@ -1,5 +1,8 @@
import { ShortcutsProvider, useShortcuts } from './Shortcuts';
import onShortcut from './onShortcut';
export {
ShortcutsProvider,
useShortcuts,
onShortcut,
};

View file

@ -0,0 +1,15 @@
import { DependencyList, useCallback, useEffect } from 'react';
import { ShortcutListener, ShortcutName, useShortcuts } from './Shortcuts';
const onShortcut = (name: ShortcutName, listener: ShortcutListener, deps: DependencyList) => {
const shortcuts = useShortcuts();
const listenerCallback = useCallback(listener, deps);
useEffect(() => {
shortcuts.on(name, listenerCallback);
return () => shortcuts.off(name, listenerCallback);
}, [listenerCallback]);
};
export default onShortcut;

View file

@ -59,6 +59,11 @@
"label": "SETTINGS_SHORTCUT_VOLUME_DOWN",
"combos": [["ArrowDown"]]
},
{
"name": "mute",
"label": "SETTINGS_SHORTCUT_MUTE",
"combos": [["M"]]
},
{
"name": "subtitlesSize",
"label": "SETTINGS_SHORTCUT_SUBTITLES_SIZE",
@ -83,6 +88,16 @@
"name": "infoMenu",
"label": "SETTINGS_SHORTCUT_MENU_INFO",
"combos": [["I"]]
},
{
"name": "speedMenu",
"label": "SETTINGS_SHORTCUT_MENU_PLAYBACK_SPEED",
"combos": [["R"]]
},
{
"name": "statisticsMenu",
"label": "SETTINGS_SHORTCUT_MENU_STATISTICS",
"combos": [["D"]]
}
]
}

View file

@ -27,7 +27,7 @@
&.error {
.icon-container {
.icon {
color: var(--color-trakt);
color: var(--danger-accent-color);
}
}
}

View file

@ -4,7 +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 { ShortcutsProvider, useShortcuts, onShortcut } = require('./Shortcuts');
const comparatorWithPriorities = require('./comparatorWithPriorities');
const CONSTANTS = require('./CONSTANTS');
const { withCoreSuspender, useCoreSuspender } = require('./CoreSuspender');
@ -38,6 +38,7 @@ module.exports = {
usePlatform,
ShortcutsProvider,
useShortcuts,
onShortcut,
ToastProvider,
useToast,
TooltipProvider,

View file

@ -70,7 +70,7 @@
}
&.error {
border-color: var(--color-trakt);
border-color: var(--danger-accent-color);
}
&.checked {

View file

@ -17,6 +17,7 @@
border-radius: 2rem;
height: @height;
width: fit-content;
backdrop-filter: blur(5px);
.icon-container {
display: flex;

View file

@ -52,7 +52,7 @@
}
&.error {
border-color: var(--color-trakt);
border-color: var(--danger-accent-color);
}
&.selected {

View file

@ -1,7 +1,7 @@
.shortcuts-group {
flex: 1 1 0;
position: relative;
min-width: 30rem;
width: 30rem;
display: flex;
flex-direction: column;
gap: 2rem;

View file

@ -19,6 +19,7 @@
padding: 0.5rem;
margin-bottom: 0.5rem;
border-radius: var(--border-radius);
border: 0.15rem solid transparent;
@supports (scroll-margin: 1.25rem) {
scroll-margin: 1.25rem;

View file

@ -50,7 +50,7 @@ const mapSelectableInputs = (installedAddons, remoteAddons, t) => {
remoteAddons.selected !== null ?
t.stringWithPrefix(remoteAddons.selected.request.path.type, 'TYPE_')
:
typeSelect.title;
t.string('SELECT_TYPE');
},
onSelect: (value) => {
window.location = value;

View file

@ -22,11 +22,10 @@ const Board = () => {
const profile = useProfile();
const boardCatalogsOffset = continueWatchingPreview.items.length > 0 ? 1 : 0;
const scrollContainerRef = React.useRef();
const streamingServerWarningDismissed = React.useMemo(() => {
return streamingServer.settings !== null && streamingServer.settings.type === 'Ready' || (
!isNaN(profile.settings.streamingServerWarningDismissed.getTime()) &&
profile.settings.streamingServerWarningDismissed.getTime() > Date.now()
);
const showStreamingServerWarning = React.useMemo(() => {
return streamingServer.settings !== null && streamingServer.settings.type === 'Err' && (
isNaN(profile.settings.streamingServerWarningDismissed.getTime()) ||
profile.settings.streamingServerWarningDismissed.getTime() < Date.now());
}, [profile.settings, streamingServer.settings]);
const onVisibleRangeChange = React.useCallback(() => {
const range = getVisibleChildrenRange(scrollContainerRef.current);
@ -103,7 +102,7 @@ const Board = () => {
</div>
</MainNavBars>
{
!streamingServerWarningDismissed ?
showStreamingServerWarning ?
<StreamingServerWarning className={styles['board-warning-container']} />
:
null

View file

@ -8,7 +8,7 @@ const langs = require('langs');
const { useTranslation } = require('react-i18next');
const { useRouteFocused } = require('stremio-router');
const { useServices } = require('stremio/services');
const { onFileDrop, useSettings, useProfile, useFullscreen, useBinaryState, useToast, useStreamingServer, withCoreSuspender, CONSTANTS, useShell, usePlatform } = require('stremio/common');
const { onFileDrop, useSettings, useProfile, useFullscreen, useBinaryState, useToast, useStreamingServer, withCoreSuspender, CONSTANTS, useShell, usePlatform, onShortcut } = require('stremio/common');
const { HorizontalNavBar, Transition, ContextMenu } = require('stremio/components');
const BufferingLoader = require('./BufferingLoader');
const VolumeChangeIndicator = require('./VolumeChangeIndicator');
@ -568,7 +568,7 @@ const Player = ({ urlParams, queryParams }) => {
const videoId = player.selected ? player.selected?.streamRequest?.path?.id : null;
const video = metaItem ? metaItem.videos.find(({ id }) => id === videoId) : null;
const videoInfo = video && video.season && video.episode ? ` (${video.season}x${video.episode})`: null;
const videoInfo = video && video.season && video.episode ? ` (${video.season}x${video.episode})` : null;
const videoTitle = video ? `${video.title}${videoInfo}` : null;
const metaTitle = metaItem ? metaItem.name : null;
const imageUrl = metaItem ? metaItem.logo : null;
@ -597,117 +597,99 @@ const Player = ({ urlParams, queryParams }) => {
navigator.mediaSession.setActionHandler('nexttrack', nexVideoCallback);
}, [player.nextVideo, onPlayRequested, onPauseRequested, onNextVideoRequested]);
React.useLayoutEffect(() => {
const onKeyDown = (event) => {
switch (event.code) {
case 'Space': {
if (!menusOpen && !nextVideoPopupOpen && video.state.paused !== null) {
if (video.state.paused) {
onPlayRequested();
setSeeking(false);
} else {
onPauseRequested();
}
}
break;
}
case 'ArrowRight': {
if (!menusOpen && !nextVideoPopupOpen && video.state.time !== null) {
const seekDuration = event.shiftKey ? settings.seekShortTimeDuration : settings.seekTimeDuration;
setSeeking(true);
onSeekRequested(video.state.time + seekDuration);
}
break;
}
case 'ArrowLeft': {
if (!menusOpen && !nextVideoPopupOpen && video.state.time !== null) {
const seekDuration = event.shiftKey ? settings.seekShortTimeDuration : settings.seekTimeDuration;
setSeeking(true);
onSeekRequested(video.state.time - seekDuration);
}
break;
}
case 'ArrowUp': {
if (!menusOpen && !nextVideoPopupOpen && video.state.volume !== null) {
onVolumeChangeRequested(Math.min(video.state.volume + 5, 200));
}
break;
}
case 'ArrowDown': {
if (!menusOpen && !nextVideoPopupOpen && video.state.volume !== null) {
onVolumeChangeRequested(Math.max(video.state.volume - 5, 0));
}
break;
}
case 'KeyS': {
closeMenus();
if ((Array.isArray(video.state.subtitlesTracks) && video.state.subtitlesTracks.length > 0) ||
(Array.isArray(video.state.extraSubtitlesTracks) && video.state.extraSubtitlesTracks.length > 0)) {
toggleSubtitlesMenu();
}
break;
}
case 'KeyA': {
closeMenus();
if (Array.isArray(video.state.audioTracks) && video.state.audioTracks.length > 0) {
toggleAudioMenu();
}
break;
}
case 'KeyI': {
closeMenus();
if (player.metaItem !== null && player.metaItem.type === 'Ready') {
toggleSideDrawer();
}
break;
}
case 'KeyR': {
closeMenus();
if (video.state.playbackSpeed !== null) {
toggleSpeedMenu();
}
break;
}
case 'KeyD': {
closeMenus();
if (streamingServer.statistics !== null && streamingServer.statistics.type !== 'Err' && player.selected && typeof player.selected.stream.infoHash === 'string' && typeof player.selected.stream.fileIdx === 'number') {
toggleStatisticsMenu();
}
break;
}
case 'KeyG': {
onDecreaseSubtitlesDelay();
break;
}
case 'KeyH': {
onIncreaseSubtitlesDelay();
break;
}
case 'Minus': {
onUpdateSubtitlesSize(-1);
break;
}
case 'Equal': {
onUpdateSubtitlesSize(1);
break;
}
case 'Escape': {
closeMenus();
!settings.escExitFullscreen && window.history.back();
break;
}
onShortcut('playPause', () => {
if (!menusOpen && !nextVideoPopupOpen && video.state.paused !== null) {
if (video.state.paused) {
onPlayRequested();
setSeeking(false);
} else {
onPauseRequested();
}
};
}
}, [menusOpen, nextVideoPopupOpen, video.state.paused, onPlayRequested, onPauseRequested]);
onShortcut('seekForward', (combo) => {
if (!menusOpen && !nextVideoPopupOpen && video.state.time !== null) {
const seekDuration = combo === 1 ? settings.seekShortTimeDuration : settings.seekTimeDuration;
setSeeking(true);
onSeekRequested(video.state.time + seekDuration);
}
}, [menusOpen, nextVideoPopupOpen, video.state.time, onSeekRequested]);
onShortcut('seekBackward', (combo) => {
if (!menusOpen && !nextVideoPopupOpen && video.state.time !== null) {
const seekDuration = combo === 1 ? settings.seekShortTimeDuration : settings.seekTimeDuration;
setSeeking(true);
onSeekRequested(video.state.time - seekDuration);
}
}, [menusOpen, nextVideoPopupOpen, video.state.time, onSeekRequested]);
onShortcut('mute', () => {
video.state.muted === true ? onUnmuteRequested() : onMuteRequested();
}, [video.state.muted]);
onShortcut('volumeUp', () => {
if (!menusOpen && !nextVideoPopupOpen && video.state.volume !== null) {
onVolumeChangeRequested(Math.min(video.state.volume + 5, 200));
}
}, [menusOpen, nextVideoPopupOpen, video.state.volume]);
onShortcut('volumeDown', () => {
if (!menusOpen && !nextVideoPopupOpen && video.state.volume !== null) {
onVolumeChangeRequested(Math.min(video.state.volume - 5, 200));
}
}, [menusOpen, nextVideoPopupOpen, video.state.volume]);
onShortcut('subtitlesDelay', (combo) => {
combo === 1 ? onIncreaseSubtitlesDelay() : onDecreaseSubtitlesDelay();
}, [onIncreaseSubtitlesDelay, onDecreaseSubtitlesDelay]);
onShortcut('subtitlesSize', (combo) => {
combo === 1 ? onUpdateSubtitlesSize(-1) : onUpdateSubtitlesSize(1);
}, [onUpdateSubtitlesSize, onUpdateSubtitlesSize]);
onShortcut('subtitlesMenu', () => {
closeMenus();
if (video.state?.subtitlesTracks?.length > 0 || video.state?.extraSubtitlesTracks?.length > 0) {
toggleSubtitlesMenu();
}
}, [video.state.subtitlesTracks, video.state.extraSubtitlesTracks, toggleSubtitlesMenu]);
onShortcut('audioMenu', () => {
closeMenus();
if (video.state?.audioTracks?.length > 0) {
toggleAudioMenu();
}
}, [video.state.audioTracks, toggleAudioMenu]);
onShortcut('infoMenu', () => {
closeMenus();
if (player.metaItem?.type === 'Ready') {
toggleSideDrawer();
}
}, [player.metaItem, toggleSideDrawer]);
onShortcut('speedMenu', () => {
closeMenus();
if (video.state.playbackSpeed !== null) {
toggleSpeedMenu();
}
}, [video.state.playbackSpeed, toggleSpeedMenu]);
onShortcut('statisticsMenu', () => {
closeMenus();
const stream = player.selected?.stream;
if (streamingServer?.statistics?.type !== 'Err' && typeof stream === 'string' && typeof stream === 'number') {
toggleStatisticsMenu();
}
}, [player.selected, streamingServer.statistics, toggleStatisticsMenu]);
onShortcut('exit', () => {
closeMenus();
!settings.escExitFullscreen && window.history.back();
}, [settings.escExitFullscreen]);
React.useLayoutEffect(() => {
const onKeyUp = (event) => {
if (event.code === 'ArrowRight' || event.code === 'ArrowLeft') {
setSeeking(false);
@ -725,39 +707,14 @@ const Player = ({ urlParams, queryParams }) => {
}
};
if (routeFocused) {
window.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);
window.addEventListener('wheel', onWheel);
}
return () => {
window.removeEventListener('keydown', onKeyDown);
window.removeEventListener('keyup', onKeyUp);
window.removeEventListener('wheel', onWheel);
};
}, [
player.metaItem,
player.selected,
streamingServer.statistics,
settings.seekTimeDuration,
settings.seekShortTimeDuration,
settings.escExitFullscreen,
routeFocused,
menusOpen,
nextVideoPopupOpen,
video.state.paused,
video.state.time,
video.state.volume,
video.state.audioTracks,
video.state.subtitlesTracks,
video.state.extraSubtitlesTracks,
video.state.playbackSpeed,
toggleSubtitlesMenu,
toggleStatisticsMenu,
toggleSideDrawer,
onDecreaseSubtitlesDelay,
onIncreaseSubtitlesDelay,
onUpdateSubtitlesSize,
]);
}, [routeFocused, menusOpen, video.state.volume]);
React.useEffect(() => {
video.events.on('error', onError);

View file

@ -4,7 +4,7 @@ import React, { useCallback, useLayoutEffect, useMemo, useRef, useState } from '
import classnames from 'classnames';
import throttle from 'lodash.throttle';
import { useRouteFocused } from 'stremio-router';
import { useProfile, useStreamingServer, withCoreSuspender } from 'stremio/common';
import { usePlatform, useProfile, useStreamingServer, withCoreSuspender } from 'stremio/common';
import { MainNavBars } from 'stremio/components';
import { SECTIONS } from './constants';
import Menu from './Menu';
@ -18,6 +18,7 @@ import styles from './Settings.less';
const Settings = () => {
const { routeFocused } = useRouteFocused();
const profile = useProfile();
const platform = usePlatform();
const streamingServer = useStreamingServer();
const sectionsContainerRef = useRef<HTMLDivElement>(null);
@ -37,14 +38,10 @@ const Settings = () => {
const updateSelectedSectionId = useCallback(() => {
const container = sectionsContainerRef.current;
if (container!.scrollTop + container!.clientHeight >= container!.scrollHeight - 50) {
setSelectedSectionId(sections[sections.length - 1].id);
} else {
for (let i = sections.length - 1; i >= 0; i--) {
if (sections[i].ref.current!.offsetTop - container!.offsetTop <= container!.scrollTop) {
setSelectedSectionId(sections[i].id);
break;
}
for (const section of sections) {
const sectionContainer = section.ref.current;
if (sectionContainer && (sectionContainer.offsetTop + container!.offsetTop) < container!.scrollTop + 50) {
setSelectedSectionId(section.id);
}
}
}, []);
@ -94,7 +91,9 @@ const Settings = () => {
profile={profile}
streamingServer={streamingServer}
/>
<Shortcuts ref={shortcutsSectionRef} />
{
!platform.isMobile && <Shortcuts ref={shortcutsSectionRef} />
}
<Info streamingServer={streamingServer} />
</div>
</div>

View file

@ -69,7 +69,7 @@
.cancel {
&:hover {
.icon {
color: var(--color-trakt);
color: var(--danger-accent-color);
}
}
}

View file

@ -52,7 +52,7 @@
}
&.error {
background-color: var(--color-trakt);
background-color: var(--danger-accent-color);
}
}
@ -92,7 +92,7 @@
background-color: var(--overlay-color);
.icon {
color: var(--color-trakt);
color: var(--danger-accent-color);
opacity: 1 !important;
}
}

View file

@ -22,8 +22,8 @@
gap: 0.75rem;
.icon {
width: 3rem;
height: 3rem;
width: 4rem;
height: 4rem;
color: var(--primary-foreground-color);
}