Compare commits

...

547 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
Timothy Z.
d86bc3bbd9 chore: v5.0.0-beta.28 2025-11-27 18:27:28 +02:00
Timothy Z.
199b00b290
Merge pull request #1054 from Stremio/fix/player-next-video-behaviour
Player: next video behavior
2025-11-27 18:25:47 +02:00
Timothy Z.
57b2632486 correct bingewatching use 2025-11-27 18:24:42 +02:00
Timothy Z.
12c36f4df3
Merge pull request #998 from Stremio/feat/stream-converted-source
Feat/stream converted source
2025-11-27 17:26:32 +02:00
Timothy Z.
135ca80bd3
Merge pull request #1074 from Stremio/fix/player-slider-thumb-movement
Player: Fix Slider thumb movement
2025-11-27 17:26:09 +02:00
Timothy Z.
a037afd983 fix(Slider): thumb movement 2025-11-27 16:42:20 +02:00
Timothy Z.
0c833330a1
Merge pull request #1059 from Stremio/feat/details-selected-video-styles
Some checks are pending
Build / build (push) Waiting to run
Details: improve selected video logic
2025-11-27 16:26:00 +02:00
Timothy Z.
074daeeae8 Update pnpm-lock.yaml 2025-11-27 14:51:13 +02:00
Timothy Z.
5fe0353be5
Merge branch 'development' into fix/player-next-video-behaviour 2025-11-27 14:49:15 +02:00
Lachezar Lechev
3c8d62f3b6
fix: Player - video.load hook needs player.stream
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-11-27 13:03:29 +02:00
Lachezar Lechev
924cd715d2
chore: bump stremio-core-web to 0.51.0
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-11-27 12:52:40 +02:00
Lachezar Lechev
4c407392dd
fix(player): options video - download video should prefer downloadUrl instead of streamingUrl
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-11-27 12:14:02 +02:00
Lachezar Lechev
5f1841bfb8
fix: player and useStatistics - use player.stream
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-11-26 21:10:23 +02:00
Lachezar Lechev
4fba2a3770
Merge branch 'development' into feat/stream-converted-source 2025-11-26 20:01:19 +02:00
Tim
3bb7fc4dcc fix: meta preview import
Some checks failed
Build / build (push) Has been cancelled
2025-11-25 12:14:09 +01:00
Tim
c7ccf39cfd
Merge pull request #1071 from Stremio/chore/update-icons
Chore: Update icons
2025-11-25 12:09:29 +01:00
Tim
b66c3654e5
Merge pull request #1070 from Stremio/dependabot/github_actions/actions/checkout-6
chore(deps): bump actions/checkout from 5 to 6
2025-11-25 12:08:11 +01:00
Tim
1fec4bd0c5
Merge pull request #1069 from Stremio/dependabot/github_actions/svenstaro/upload-release-action-2.11.3
chore(deps): bump svenstaro/upload-release-action from 2.11.2 to 2.11.3
2025-11-25 12:07:55 +01:00
Botzy
67300d0159 update stremio icons 2025-11-25 12:21:39 +02:00
dependabot[bot]
9d81069398
chore(deps): bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-24 21:21:50 +00:00
dependabot[bot]
3017af0df9
chore(deps): bump svenstaro/upload-release-action from 2.11.2 to 2.11.3
Bumps [svenstaro/upload-release-action](https://github.com/svenstaro/upload-release-action) from 2.11.2 to 2.11.3.
- [Release notes](https://github.com/svenstaro/upload-release-action/releases)
- [Changelog](https://github.com/svenstaro/upload-release-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/svenstaro/upload-release-action/compare/2.11.2...2.11.3)

---
updated-dependencies:
- dependency-name: svenstaro/upload-release-action
  dependency-version: 2.11.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-24 21:21:45 +00:00
Tim
eac24c0360
Merge pull request #1067 from Stremio/fix/meta-preview-links-label
Some checks failed
Build / build (push) Has been cancelled
fix: links label on meta preview
2025-11-21 10:08:57 +01:00
Tim
af225b0135 fix: links label on meta preview 2025-11-21 10:00:53 +01:00
Timothy Z.
c3f1f6c911
Merge pull request #1057 from Stremio/fix/trailer-player-crashes
Some checks failed
Build / build (push) Has been cancelled
Player: prevent crash when destroying null video
2025-11-07 12:52:35 +02:00
Timothy Z.
c0bc34eb40 fix(Player): binge disabled navigation back 2025-10-31 16:42:53 +02:00
Timothy Z.
75804bac10 refactor(Video): add scroll-margin 2025-10-30 18:21:40 +02:00
Timothy Z.
a9c77da3c4 feat(Details): improve selected video logic 2025-10-30 18:01:44 +02:00
Timothy Z.
b876c920fc fix: useVideo check current before destroying 2025-10-30 11:28:51 +02:00
Tim
536be36005 build: fix Dockerfile
Some checks failed
Build / build (push) Has been cancelled
2025-10-27 16:21:50 +01:00
Timothy Z.
000d5be639
Merge pull request #974 from Stremio/feat/details-scroll-to-last-watched-video
Some checks are pending
Build / build (push) Waiting to run
Details: Scroll to last watched video
2025-10-27 16:49:23 +02:00
Tim
00ebd6c4d0 fix(Player): media session trailers crash 2025-10-27 15:46:44 +01:00
Lachezar Lechev
7456e8f15a
chore: revert space added by formatter 2025-10-27 15:21:45 +02:00
Lachezar Lechev
04e6780395
chore: remove package-lock.json 2025-10-27 15:20:33 +02:00
Timothy Z.
e316b07649 refactor(Video): add !watched check 2025-10-27 12:41:05 +02:00
Timothy Z.
8ab582080d fix(Video): content shifts during scroll 2025-10-27 12:31:45 +02:00
Tim
a35f7e7878
Merge pull request #1039 from Stremio/fix/update-dockerfile-pnpm
Some checks failed
Build / build (push) Has been cancelled
Dev: Update Dockerfile
2025-10-24 13:48:56 +02:00
Tim
1b6f4d09d3 build: update Dockerfile 2025-10-24 13:44:00 +02:00
Lachezar Lechev
3579a99df3
fix: settings - player - keep next video popup enabled regardless of bingeWatching setting
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-10-24 10:24:39 +03:00
Lachezar Lechev
3d163cf440
chore: player - clean up and use handleNextVideoNavigation
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-10-24 10:23:58 +03:00
Lachezar Lechev
32cf4cc12e
fix: package-lock.json & pnpm-lock.yaml - core bumped fix build
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-10-24 09:39:37 +03:00
Lachezar Lechev
54bcdf6360
Merge branch 'development' into fix/player-next-video-behaviour 2025-10-24 09:34:15 +03:00
Lachezar Lechev
1b15f0b5f1
fix: player - popup for next video should show up on disabled binge_watching
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-10-24 09:28:28 +03:00
Tim
cbc708bc64 chore: add missing interface languages
Some checks are pending
Build / build (push) Waiting to run
2025-10-23 20:41:44 +02:00
Timothy Z.
16877fa4bf Merge branch 'development' into feat/details-scroll-to-last-watched-video 2025-10-23 17:05:46 +03:00
Tim
af806bbfb1
Merge pull request #1043 from Stremio/feat/video-mode-setting
Some checks are pending
Build / build (push) Waiting to run
Settings: Add player video mode
2025-10-23 16:01:31 +02:00
Tim
6e65fa03d8 chore: update video 2025-10-23 15:59:34 +02:00
Tim
e3c4bc14bb Merge branch 'development' of https://github.com/Stremio/stremio-web into feat/video-mode-setting 2025-10-23 15:58:32 +02:00
Tim
5969bc9251
Merge pull request #1041 from Stremio/feat/shortcuts-modal
App: Add shortcuts modal
2025-10-23 15:57:34 +02:00
Tim
cf93c2dcbe chore: update translations 2025-10-23 15:55:39 +02:00
Timothy Z.
c416971d22
Merge pull request #1009 from actuallylost/chore/typos
chore: fix all typos and misspellings
2025-10-23 16:51:45 +03:00
Timothy Z.
72aa110d48
Merge pull request #1036 from v1ctorsales/fix/calendar-thumbnails
Calendar: Poster visibility improvements
2025-10-23 16:44:08 +03:00
Timothy Z.
309956b237
Merge pull request #1002 from ASiD-0/fix/#1000
Intro: make all text lowercase to match the rest
2025-10-23 16:35:02 +03:00
Lachezar Lechev
2f566f8626
chore: bump core to the fix branch
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-10-23 10:16:50 +03:00
Lachezar Lechev
20c7ba672a
fix: player - redirect to next video player deeplink only if bingeWatching is enabled, else go to stream list
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-10-23 10:05:35 +03:00
Tim
00ae74e9af
Merge pull request #994 from Stremio/chore/update-pull-user-from-api-action
Some checks failed
Build / build (push) Has been cancelled
Dev: Update PullUserFromAPI core action
2025-10-22 13:44:00 +02:00
Tim
8c8d3376db chore: update core 2025-10-22 13:41:51 +02:00
Tim
7b2e5305e0 Merge branch 'development' of https://github.com/Stremio/stremio-web into chore/update-pull-user-from-api-action 2025-10-22 13:41:11 +02:00
Tim
585a84ccd6
Merge pull request #1053 from Stremio/dependabot/github_actions/actions/setup-node-6
Some checks failed
Build / build (push) Has been cancelled
chore(deps): bump actions/setup-node from 5 to 6
2025-10-20 23:46:50 +02:00
dependabot[bot]
e7b0a1d1be
chore(deps): bump actions/setup-node from 5 to 6
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 5 to 6.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-20 21:13:21 +00:00
Victor Sales
eb61ad6943 fix: remove unused mediaquery 2025-10-17 18:20:31 +03:00
Victor Sales
18617b32c9 refactor: reduce duplicated CSS using less variables 2025-10-17 17:45:53 +03:00
Victor Sales
0433da66c1 fix: point event none for tablets on portrait mode 2025-10-16 19:59:00 +03:00
Tim
2776741e8c feat(Player): pass platform name to video 2025-10-16 15:21:28 +02:00
Victor Sales
3e0308dff1 fix: align banners with day for small desktops 2025-10-15 20:45:46 +03:00
Victor Sales
4361792cae fix: adapt items display for mobile landscape 2025-10-15 20:36:13 +03:00
Victor Sales
83752eb647 fix(calendar): adaptive display and style fixes 2025-10-14 18:34:43 +03:00
Tim
5c3b2b0b22 refactor(Shortcuts): use json to declare shortcuts 2025-10-14 17:22:08 +02:00
Tim
0143bf914c feat: add video mode setting 2025-10-14 16:48:37 +02:00
Tim
cf73c7942d
Merge pull request #1045 from Stremio/fix/use-fullscreen
Some checks failed
Build / build (push) Has been cancelled
fix: useFullscreen - catch exception on Firefox when using F shortcut
2025-10-14 11:03:38 +02:00
Lachezar Lechev
91fbfc1178
fix: useFullscreen - catch exception on Firefox when using keyboard F shortcut in web
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-10-14 12:00:43 +03:00
Lachezar Lechev
56b60beedb
fix: useFullscreen - catch exception on Firefox when using keyboard F shortcut in web
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-10-14 11:39:28 +03:00
Victor Sales
d2d28be6de style(responsive): add @phone-landscape media query 2025-10-13 16:27:15 +03:00
Tim
a97dd01869 refactor(shortcuts): use Ctrl + / for shortcuts modal 2025-10-13 12:55:12 +02:00
Victor Sales
e74072ebd5 fix(calendar): disable banner click in phone-portrait mode 2025-10-13 13:40:13 +03:00
Tim
9923152de7
Merge pull request #1014 from Stremio/feat/player-media-session
Player: Add media session support
2025-10-13 12:35:55 +02:00
Tim
3eff7f0903 refactor(Player): use poster for media session artwork 2025-10-13 12:33:46 +02:00
Tim
122e43dbe5 refactor(Player): remove handling of media keys 2025-10-13 12:26:42 +02:00
Tim
910242b201 Merge branch 'development' of https://github.com/Stremio/stremio-web into feat/player-media-session 2025-10-13 12:25:56 +02:00
Victor Sales
2e1ad64d02 refactor(calendar): replace fixed width with max-width for better banner scaling 2025-10-13 13:06:52 +03:00
Tim
4860a028c2 chore: update translations 2025-10-13 11:29:16 +02:00
Tim
9e99a2b308
Merge pull request #1042 from PeterDaveHello/patch-1
Fix GitHub Actions badge in README
2025-10-12 20:55:41 +02:00
Peter Dave Hello
62a650018b
Fix GitHub Actions badge in README 2025-10-13 01:14:13 +08:00
Victor Sales
fb9497a856 feat(calendar): redesign calendar cell layout for responsiveness and banner support 2025-10-12 18:23:34 +03:00
Victor Sales
539a7ebc10 fix(calendar): align day and more indicator inline in narrow desktop viewports 2025-10-12 13:41:42 +03:00
Tim
9fa0e46423 feat: add shortcuts modal 2025-10-12 12:06:59 +02:00
Victor Sales
b40ef9f3dc fix(calendar): redesign cell layout with rows for desktop 2025-10-12 00:58:36 +03:00
Timothy Z.
b05f28cc54 fix(docker): update dockerfile with pnpm 2025-10-12 00:44:39 +03:00
Victor Sales
f2c7382729 fix(calendar): apply grid-auto-rows 1fr for equal row height 2025-10-11 22:55:26 +03:00
Victor Sales
a3a7e14d15 chore(calendar): remove duplicated aspect-ratio rule from CSS 2025-10-11 16:26:56 +03:00
Victor Sales
c35c7c06e9 style(calendar): normalize indentation and align with project style 2025-10-11 16:22:27 +03:00
Victor Sales
d8904bdb5a style(calendar): normalize indentation to match project formatting 2025-10-11 16:17:10 +03:00
Victor Sales
06365262d1 fix(calendar): improve poster visibility and responsive scaling 2025-10-11 16:05:14 +03:00
Timothy Z.
b7863bf319
Merge branch 'development' into chore/typos 2025-10-11 15:02:32 +03:00
Timothy Z.
2dcc582cc2
Merge pull request #1035 from Stremio/refactor/meta-preview-action-buttons-styles
MetaPreview: hide ActionButton label on mobile
2025-10-10 19:41:04 +03:00
Timothy Z.
d832a9c136 refactor(ActionButton): hide labels on mobile 2025-10-10 18:22:06 +03:00
Timothy Z.
4a1b0d3287
Merge pull request #1030 from arkia09/screenshotsUpdate 2025-10-10 14:25:51 +03:00
Timothy Z.
23c4587564
Update CODE_OF_CONDUCT.md 2025-10-10 11:57:10 +03:00
Tim
10faa1c105 ci(pages_cleanup): rename job to 'cleanup' 2025-10-10 08:33:49 +02:00
Tim
593c8d55d0 Merge branch 'development' of https://github.com/Stremio/stremio-web into development 2025-10-10 08:28:37 +02:00
Tim
9e33186708 ci(pages_cleanup): remove dirs that don't have existing related branch 2025-10-10 08:28:18 +02:00
Simran Kaur
cd980af475 Updated metadetails to match original page 2025-10-10 11:01:22 +05:30
Simran Kaur
f44fa98502 Adjusted screnshots to match original aspect ratio 2025-10-10 10:50:40 +05:30
Timothy Z.
a56d3aafd3
Merge pull request #1025 from PsyGuy007-sys/feature/media-playpause
Player: Add support for media play/pause keys
2025-10-09 22:48:51 +03:00
Timothy Z.
5edbc899ea
Merge pull request #1032 from bashSunny101/patch-1
chore: fix typos and enhance clarity in CODE_OF_CONDUCT
2025-10-09 22:43:47 +03:00
Sunny Pal
ca3a2774d2
Fix typos and enhance clarity in CODE_OF_CONDUCT
Corrected typos and improved clarity in the CODE_OF_CONDUCT.md.

Fixes: #1031
2025-10-10 00:21:41 +05:30
Simran Kaur
a70828f168 New Updated Screenshots in README 2025-10-09 22:04:18 +05:30
PsyGuy007-sys
2dee307ac3 Add media key play/pause shortcuts 2025-10-08 09:29:55 +02:00
Tim
d38cf32773
Merge pull request #1016 from ckorber/pr/cause_args
Dev: Improve error logs
2025-10-07 21:24:46 +02:00
Christian
a0615bda42 Complete addition of cause argument
As mentioned in #296 error cast are now added with cause argument.

Signed-off-by: Christian <chr.korber@gmail.com>
2025-10-07 19:57:29 +02:00
Tim
ad5ab5c634
Merge pull request #1022 from NachoLZ/feature/add-docker-instructions
docs: Add instructions for running with Docker
2025-10-07 19:09:00 +02:00
Ignacio Lizana
e78866a77d docs: Add instructions for running with Docker 2025-10-07 18:38:38 +02:00
Tim
19c6e042fb
Merge pull request #1018 from NachoLZ/feature/disable-service-worker
Dev: add option to disable service worker
2025-10-07 17:56:23 +02:00
Ignacio Lizana
57571cf1fc
fix: Handle boolean value for SERVICE_WORKER_DISABLED
The `webpack.EnvironmentPlugin` provides the default value for `SERVICE_WORKER_DISABLED` as a boolean (`false`).

The previous implementation only checked for the string `'true'`, which would fail to correctly identify the boolean `true` case, causing the feature to not work as intended when the variable was set without being explicitly a string.

This commit updates the conditional check to handle both the boolean `true` and the string `'true'` to ensure the service worker is reliably disabled.

Co-authored-by: Tim <tymmesyde@gmail.com>
2025-10-07 17:44:23 +02:00
Ignacio Lizana
670f119027 feat: add option to disable service worker 2025-10-07 15:57:14 +02:00
Tim
49c11973d6 doc: update README 2025-10-07 13:25:57 +02:00
Tim
4a9c0fe5b4
Merge pull request #1008 from actuallylost/build/use-pnpm-instead-of-npm
build: replace npm with pnpm
2025-10-07 13:23:59 +02:00
Tim
c26dac2154 feat(Player): add media session support 2025-10-06 14:49:50 +02:00
Lachezar Lechev
f39944373b
chore: bump core-web revision
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-10-03 15:01:17 +03:00
Lachezar Lechev
ec34a84154
chore: bump core-web revision
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-10-02 09:21:41 +03:00
actuallylost
39bdb374e1
chore: remove unused no-var comment 2025-09-29 14:00:19 +03:00
actuallylost
ecca656c68
deps: update stylistic eslint to v5 & stylistic eslint react to v4; change eslint rule name to match actual 2025-09-29 13:59:23 +03:00
actuallylost
c3d6b315d5
revert: workflow double to single quote 2025-09-29 12:20:51 +03:00
Tim
1e241c7926 ci(auto_assign): only run from stremio repo
Some checks failed
Build / build (push) Has been cancelled
2025-09-29 10:55:19 +02:00
actuallylost
67f5446030
chore: fix all typos and misspellings 2025-09-27 16:23:06 +03:00
actuallylost
f3a14403de
build: migrate workflow and npm scripts to use pnpm; add pnpm-lock; add es2016 tsconfig lib 2025-09-27 15:33:31 +03:00
Tim
90f834e893
Merge pull request #981 from asnaek/development
Some checks failed
Build / build (push) Has been cancelled
Update SearchParamsHandler.js
2025-09-25 08:09:07 +02:00
Timothy Z.
10a98fcecf chore: code styles 2025-09-23 23:45:57 +03:00
Aris Sidiropoulos
5bea8a83c6 fix(Intro): clean up unused css class 2025-09-19 16:45:25 +03:00
Aris Sidiropoulos
010f2e0390 fix(Intro): follow up commit, best practice solution 2025-09-19 11:43:36 +03:00
Timothy Z.
cf3119b0a0
Merge pull request #1003 from CDrosos/patch-2
Some checks failed
Build / build (push) Has been cancelled
Update Intro.js added translatable error messages
2025-09-19 11:17:59 +03:00
Christopher Drosos
83bb34e505
Update Intro.js added translatable error messages 2025-09-18 16:57:34 +03:00
Aris Sidiropoulos
cfa99f0e38 fix(Intro): make all text lowercase to match the rest 2025-09-17 20:11:35 +03:00
Lachezar Lechev
3de03989bd
Merge branch 'development' into feat/stream-converted-source 2025-09-16 16:57:45 +03:00
Timothy Z.
3f685173c1
Merge pull request #999 from Stremio/refactor/code-of-conduct
refactor(CODE_OF_CONDUCT): simplify and add AI clarifications
2025-09-16 16:49:49 +03:00
Tim
872243fc5c chore: v5.0.0-beta.27 2025-09-16 11:51:42 +02:00
Timothy Z.
185740c834 Update CODE_OF_CONDUCT.md 2025-09-12 17:05:01 +03:00
Timothy Z.
b9b79a833d Update CODE_OF_CONDUCT.md 2025-09-12 16:54:38 +03:00
Timothy Z.
ed0ca136d1
refactor(CODE_OF_CONDUCT): simplify and AI clarifications 2025-09-12 15:59:37 +03:00
Timothy Z.
6aabd75d5e
Merge pull request #997 from Stremio/dependabot/github_actions/actions/setup-node-5
chore(deps): bump actions/setup-node from 4 to 5
2025-09-12 14:25:48 +03:00
Timothy Z.
8005cd849a
Merge pull request #996 from Stremio/dependabot/github_actions/actions/github-script-8
chore(deps): bump actions/github-script from 7 to 8
2025-09-12 14:25:11 +03:00
Tim
138e3c0d48 chore: update stremio-video 2025-09-11 08:52:19 +02:00
Lachezar Lechev
4001ea3acc
chore: update stremio-core-web dep 2025-09-10 21:35:57 +03:00
Lachezar Lechev
88ed546414
chore: change PullUserFromAPI action and include args
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-09-10 21:35:38 +03:00
a snaek
f271c97502
Merge branch 'Stremio:development' into development 2025-09-10 16:44:39 +02:00
dependabot[bot]
9e55bc8273
chore(deps): bump actions/setup-node from 4 to 5
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 5.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 21:23:04 +00:00
dependabot[bot]
fd675e5926
chore(deps): bump actions/github-script from 7 to 8
Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 8.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 21:23:01 +00:00
Lachezar Lechev
672dbdeb28
Merge branch 'development' into feat/stream-converted-source 2025-09-04 18:26:53 +03:00
Lachezar Lechev
d177f86018
chore: change PullUserFromAPI action and include args
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-09-03 12:07:30 +03:00
Timothy Z.
be072e8391
Merge pull request #991 from Stremio/fix/search-page-dvc
Some checks failed
Build / build (push) Has been cancelled
Search: dynamic view calculation fix on PWA
2025-08-25 19:16:11 +03:00
Timothy Z.
8000a7089a fix(Search): dynamic view calc 2025-08-25 17:23:48 +03:00
Timothy Z.
f9b059d9e4
Merge pull request #990 from Stremio/fix/meta-preview-buttons-position
Some checks are pending
Build / build (push) Waiting to run
MetaPreview: MetaActions Button positioning fix
2025-08-25 15:11:59 +03:00
Timothy Z.
36721b40f1 fix(MetaPreview): button positioning 2025-08-25 14:56:23 +03:00
Timothy Z.
4eb297a4f2
Merge pull request #984 from Stremio/dependabot/github_actions/actions/checkout-5
Some checks failed
Build / build (push) Has been cancelled
chore(deps): bump actions/checkout from 4 to 5
2025-08-12 12:34:12 +03:00
dependabot[bot]
178974ddfd
chore(deps): bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-12 05:09:18 +00:00
a snaek
80f25b8d45
Merge branch 'Stremio:development' into development 2025-08-12 00:51:59 +02:00
Timothy Z.
665cf7dd2a
Merge pull request #975 from Stremio/fix/player-negative-number-from-stremio-video
Some checks failed
Build / build (push) Has been cancelled
fix: Player - TimeChanged & Seek time & duration now have Math.max(0, x)
2025-08-07 12:24:55 +03:00
a snaek
53dfddec74
Update SearchParamsHandler.js 2025-08-05 10:23:15 +02:00
Lachezar Lechev
3c2f8cb89b
fix: Player - selected check before video.load
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-07-18 12:08:48 +03:00
Lachezar Lechev
0bec58b158
fix: package-lock
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-07-18 12:08:21 +03:00
Lachezar Lechev
20bbe12a8a
fix: Player - TimeChanged & Seek duration now has Math.max(0, x)
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-07-15 22:04:57 +03:00
Lachezar Lechev
3c2914aca2
fix: Player - TimeChanged & Seek time now has Math.max(0, x)
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-07-15 21:58:17 +03:00
Lachezar Lechev
746e5ba0d8
feat: Player - use player.stream field
chore: bump core-web to feature branch

Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-07-15 21:56:47 +03:00
Tim
0e3aa9c2c8 feat: scroll to last watched video on details page 2025-07-15 17:00:08 +02:00
Tim
40c77d3d17
Merge pull request #972 from Stremio/dependabot/github_actions/svenstaro/upload-release-action-2.11.2
Some checks failed
Build / build (push) Has been cancelled
chore(deps): bump svenstaro/upload-release-action from 2.11.1 to 2.11.2
2025-07-09 18:29:50 +02:00
dependabot[bot]
41863e5d75
chore(deps): bump svenstaro/upload-release-action from 2.11.1 to 2.11.2
Bumps [svenstaro/upload-release-action](https://github.com/svenstaro/upload-release-action) from 2.11.1 to 2.11.2.
- [Release notes](https://github.com/svenstaro/upload-release-action/releases)
- [Changelog](https://github.com/svenstaro/upload-release-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/svenstaro/upload-release-action/compare/2.11.1...2.11.2)

---
updated-dependencies:
- dependency-name: svenstaro/upload-release-action
  dependency-version: 2.11.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-08 00:13:48 +00:00
Tim
a9b9631241
Merge pull request #970 from Stremio/feat/player-subtitles-size-shortcuts
Some checks failed
Build / build (push) Has been cancelled
Player: Add subtitles size shortcuts
2025-07-07 14:33:37 +02:00
Tim
85fea50c15
Merge pull request #965 from Stremio/feat/player-subtitles-settings-hold-click
Player: Support holding click for subtitles settings
2025-07-07 14:27:44 +02:00
Tim
e98bdf2023
Merge pull request #964 from Stremio/feat/player-download-subtitles
Player: Add option to download subtitles
2025-07-07 14:27:32 +02:00
Tim
fb3f8d6918 chore: update stremio-translations 2025-07-07 14:11:58 +02:00
Tim
59953e991d feat: add subtitles size shortcuts 2025-07-04 16:35:18 +02:00
Tim
5adc0937dd
Merge pull request #969 from Stremio/refactor/remove-settings-videos-menu-shortcut
Some checks failed
Build / build (push) Has been cancelled
Settings: remove videos menu shortcut
2025-07-04 15:40:04 +02:00
Tim
71bf98dac8 refactor(Settings): remove videos menu shortcut 2025-07-04 15:35:51 +02:00
Tim
083ec3a12e ci(release): remove netlify artifact upload
Some checks are pending
Build / build (push) Waiting to run
2025-07-04 08:41:38 +02:00
Tim
4419eeb33f ci(pages_cleanup): fetch full history
Some checks are pending
Build / build (push) Waiting to run
2025-07-03 17:14:52 +02:00
Tim
05371f3617 ci(pages_cleanup): add cron and manual trigger 2025-07-03 17:09:16 +02:00
Tim
4ad0fb2962 ci: add pages cleanup workflow 2025-07-03 16:59:09 +02:00
Tim
b742e385ea
Merge pull request #968 from Stremio/fix/shell-external-subtitles-disabled
Player(Desktop): Fix external subtitles selection
2025-07-03 15:51:49 +02:00
Tim
6c0d5288d3 chore: update stremio-video 2025-07-03 15:48:12 +02:00
Tim
c042c553e3 chore: update stremio-video 2025-07-03 14:39:19 +02:00
Tim
1b09119e52
Merge pull request #967 from Stremio/fix/transition-prop-error
Some checks are pending
Build / build (push) Waiting to run
fix: Transition transitionEnded prop error
2025-07-03 12:06:43 +02:00
Tim
595dfb22a3 fix: Transition transitionEnded prop error 2025-07-03 11:56:06 +02:00
Tim
aef0ecb5be
Merge pull request #966 from Stremio/fix/player-indicator-subtitles-menu
Player: Hide subtitles delay indicator if subtitles menu is open
2025-07-03 11:30:18 +02:00
Tim
4632d6e09a fix(Player): hide indicator if subtitles menu is open 2025-07-03 11:22:14 +02:00
Tim
f04948240a feat: support holding click for subtitles settings 2025-07-03 08:45:22 +02:00
Tim
cff57d7d59 feat(Player): add option to download subtitles 2025-07-02 15:31:56 +02:00
Timothy Z.
c2a2eca4e9
Merge pull request #960 from Stremio/dependabot/github_actions/svenstaro/upload-release-action-2.11.1
Some checks failed
Build / build (push) Has been cancelled
chore(deps): bump svenstaro/upload-release-action from 2.10.0 to 2.11.1
2025-07-01 12:19:46 +03:00
dependabot[bot]
76e8aa9091
chore(deps): bump svenstaro/upload-release-action from 2.10.0 to 2.11.1
Bumps [svenstaro/upload-release-action](https://github.com/svenstaro/upload-release-action) from 2.10.0 to 2.11.1.
- [Release notes](https://github.com/svenstaro/upload-release-action/releases)
- [Changelog](https://github.com/svenstaro/upload-release-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/svenstaro/upload-release-action/compare/2.10.0...2.11.1)

---
updated-dependencies:
- dependency-name: svenstaro/upload-release-action
  dependency-version: 2.11.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-01 00:49:34 +00:00
Timothy Z.
3083b812be bump(bug fix): v5.0.0-beta.26
Some checks are pending
Build / build (push) Waiting to run
2025-06-30 14:31:02 +03:00
Timothy Z.
8a82ff083a
Merge pull request #958 from Stremio/fix/fullscreen-shortcut-search-bug
App(Search): fullscreen shortcut interference
2025-06-30 14:20:05 +03:00
Timothy Z.
066819d283 fix(Search): fullscreen shortcut interference 2025-06-30 13:09:04 +03:00
Timothy Z.
0b16f1c80e
Merge pull request #957 from Stremio/fix/trailers-button-crash
Some checks failed
Build / build (push) Has been cancelled
fix(MetaDetails): trailers button crash
2025-06-28 14:57:18 +03:00
Timothy Z.
8bd0456e09
Merge pull request #956 from Stremio/refactor/ratings-remove-transition
refactor(Ratings): remove btn transition
2025-06-28 14:45:30 +03:00
Timothy Z.
8c985619b8 fix(MetaDetails): trailers button crash 2025-06-28 14:44:49 +03:00
Timothy Z.
1780b49a38 refactor(Ratings): remove btn transition 2025-06-28 14:42:29 +03:00
Timothy Z.
63aecc6764 bump(bug fix): v5.0.0-beta.25
Some checks are pending
Build / build (push) Waiting to run
2025-06-27 13:57:44 +03:00
Timothy Z.
c1c08cdfa1
Merge pull request #954 from Stremio/fix/right-click-video-menu-crash
Video: right click menu crash
2025-06-27 13:54:43 +03:00
Timothy Z.
8f0b58f38e chore(styles): lint 2025-06-27 13:10:20 +03:00
Timothy Z.
8821eaf4a1 fix(Video): right click menu crash 2025-06-27 13:09:22 +03:00
Timothy Z.
9f56557c79 bump: v5.0.0-beta.24
Some checks are pending
Build / build (push) Waiting to run
2025-06-26 15:00:27 +03:00
Timothy Z.
306dd09f24
Merge pull request #931 from Stremio/feat/user-item-ratings
MetaPreview: Implement user item ratings
2025-06-26 14:48:19 +03:00
Timothy Z.
64c05e92eb chore(pkgs): bump core-web 2025-06-26 12:53:39 +03:00
Timothy Z.
5d88ff4212 chore: update core-web 2025-06-26 12:11:27 +03:00
Timothy Z.
80fc8c755f refactor(Ratings): rename RatingStatus to Rating 2025-06-26 12:06:55 +03:00
Timothy Z.
ee5269e1c8
Merge pull request #950 from Stremio/feat/langs-alphabetic-ordering
Some checks are pending
Build / build (push) Waiting to run
2025-06-25 17:58:36 +03:00
Timothy Z.
b75d20971c
Merge pull request #949 from Stremio/refactor/Multiselect-menu-fixed-height
MultiselectMenu: use fixed height
2025-06-25 17:19:31 +03:00
Timothy Z.
652a042a55 fix(Settings): correctly sort without matchingidx 2025-06-25 16:59:16 +03:00
Timothy Z.
f7f97b551c feat(Settings): langs alphabetic ordering 2025-06-25 16:54:21 +03:00
Timothy Z.
3cc6066a12 refactor(MultiselectMenu): fixed height 2025-06-25 16:39:35 +03:00
Tim
125a2650f8
Merge pull request #948 from Stremio/fix/discover-catalog-select
Discover: fix invalid catalog crash
2025-06-25 14:12:56 +02:00
Tim
4207fb52d6 fix(Discover): catalog select error 2025-06-25 13:27:07 +02:00
Timothy Z.
d326cd5052
Merge pull request #933 from JSOClarke/development
Some checks are pending
Build / build (push) Waiting to run
Settings: Default langs priority and Alphabetic Ordering
2025-06-24 22:57:36 +03:00
Timothy Z.
5eff16695c chore: remove unnecessary "," (2) 2025-06-24 22:18:42 +03:00
Timothy Z.
41a5bb7cef chore: remove unnecessary "," 2025-06-24 22:18:09 +03:00
Timothy Z.
cbe3a5d35e refactor(Ratings): use pointer-events to disable 2025-06-24 13:26:50 +03:00
Timothy Z.
c726398402 chore: run lint 2025-06-24 13:22:32 +03:00
Timothy Z.
7fb0a8c1be remove(usePlayerOptions): unnecessary dep 2025-06-24 13:02:12 +03:00
Timothy Z.
f49a243009 refactor(Settings): simplify handling for langs 2025-06-24 13:01:11 +03:00
Timothy Z.
ec1e098c99
Merge pull request #914 from Stremio/fix/workaround-binge-watching-full-support
Some checks are pending
Build / build (push) Waiting to run
Shell: Fix binge watching flow
2025-06-24 11:17:34 +03:00
Timothy Z.
f2490ee775 refactor(Dropdown): clean up 2025-06-24 09:54:57 +03:00
Timothy Z.
75bb1b0489 Merge branch 'development' into pr/933 2025-06-24 09:41:02 +03:00
Tim
b4f49c3637
Merge pull request #945 from Stremio/dependabot/github_actions/svenstaro/upload-release-action-2.10.0
Some checks are pending
Build / build (push) Waiting to run
chore(deps): bump svenstaro/upload-release-action from 2.9.0 to 2.10.0
2025-06-24 05:12:20 +02:00
dependabot[bot]
1d1b84bcd0
chore(deps): bump svenstaro/upload-release-action from 2.9.0 to 2.10.0
---
updated-dependencies:
- dependency-name: svenstaro/upload-release-action
  dependency-version: 2.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-24 03:10:56 +00:00
Tim
df897c6389 ci: prevent dependabot from triggering auto assign workflow 2025-06-24 05:09:02 +02:00
Timothy Z.
5ba7622f72
Merge pull request #936 from mesalilac/jump-to-currently-playing
Some checks failed
Build / build (push) Has been cancelled
2025-06-21 18:18:21 +03:00
Jordan Clarke
a7a36d6f11 Settings: Default langs priority and Alphabetic Ordering #933 2025-06-21 12:54:25 +01:00
Jordan Clarke
e4c917ff20 Merge branch 'development' of https://github.com/JSOClarke/stremio-web into development 2025-06-21 12:53:22 +01:00
Jordan Clarke
5f82d6e9da Fixed Linting Errors in Dropdown.tsx and removed the seperator logic from dropdown and style from less file 2025-06-21 12:46:41 +01:00
Jordan Clarke
f4a9c88c68 Fixed linting issues, refactored sorting. 2025-06-21 12:42:57 +01:00
Tim
ab7fa8748a
Merge pull request #942 from Stremio/refactor/shell-init
Some checks are pending
Build / build (push) Waiting to run
refactor(Shell): remove init logic
2025-06-21 01:24:42 +02:00
Timothy Z.
3aa421f768 chore(pkgs): fix integrity match 2025-06-20 20:26:13 +03:00
Timothy Z.
703514b02d Merge branch 'development' of https://github.com/JSOClarke/stremio-web into pr/933 2025-06-20 20:12:45 +03:00
Timothy Z.
19178c8ac3 Merge branch 'development' into pr/933 2025-06-20 20:12:43 +03:00
Timothy Z.
5fe16b8268 Merge branch 'development' into fix/workaround-binge-watching-full-support 2025-06-20 20:11:44 +03:00
Timothy Z.
f35b726359 Merge branch 'development' into feat/user-item-ratings 2025-06-20 20:11:36 +03:00
Jordan Clarke
8ba2c4741e Removed CODE_MAP, added fallbacks, simplified getPriority, removed hardcoded English. Ordering Logic same, neater 2025-06-20 12:14:45 +01:00
Tim
1c1163888e refactor(service): remove init shell logic 2025-06-20 12:11:34 +02:00
Timothy Z.
a3c895dfc6 refactor(SideDrawer): simplify refs handing 2025-06-20 12:08:55 +03:00
Timothy Z.
00d89aec75 chore: fix builds 2025-06-20 11:46:07 +03:00
Timothy Z.
2e5958e957
Merge branch 'development' into jump-to-currently-playing 2025-06-20 11:41:04 +03:00
Tim
6cca3a549c
Merge pull request #940 from Stremio/refactor/settings
Some checks are pending
Build / build (push) Waiting to run
refactor: rewrite settings route
2025-06-20 10:32:38 +02:00
Abdalrzag Eisa
82783a4de7
only scroll if transitionEnded is true 2025-06-20 07:08:08 +03:00
Abdalrzag Eisa
f920ab48f3
pass sideDrawerTransitionEnded into SideDrawer 2025-06-20 07:06:00 +03:00
Abdalrzag Eisa
37de79a0dc
add a callback function prop onTransitionEnd 2025-06-20 07:04:48 +03:00
Tim
a77cf17133 fix(Settings): incorect translation key 2025-06-19 03:05:53 +02:00
Tim
3e91f55d22 fix(MultiselectMenu): support disabled prop 2025-06-19 03:05:32 +02:00
Tim
409267cb44 Merge branch 'development' of https://github.com/Stremio/stremio-web into refactor/settings 2025-06-18 23:38:17 +02:00
Tim
5f085d259c
Merge pull request #938 from Stremio/feat/player-subtitles-delay-shortcuts
Some checks failed
Build / build (push) Has been cancelled
Player: add subtitles delay shortcuts
2025-06-18 23:33:12 +02:00
Tim
00bac0aca2 refactor(Settings): add translation for shortcuts and 2025-06-18 23:30:10 +02:00
Tim
cf654ae825 chore: update stremio-translations 2025-06-18 23:28:57 +02:00
Tim
c30129c9e2 Merge branch 'development' of https://github.com/Stremio/stremio-web into feat/player-subtitles-delay-shortcuts 2025-06-18 17:28:33 +02:00
Tim
1b7d618a89 Merge branch 'development' of https://github.com/Stremio/stremio-web into refactor/settings 2025-06-18 11:14:10 +02:00
Tim
74a319285c
Merge pull request #920 from Stremio/feat/untranslated-strings
Some checks are pending
Build / build (push) Waiting to run
Fix: Web App - Replace hardcoded strings with translation keys
2025-06-18 10:57:20 +02:00
Botzy
48a6da5ca7 fix: translations commit latest hash 2025-06-18 11:41:43 +03:00
Botzy
1789ddd06a fix: translations commit hash 2025-06-18 11:36:34 +03:00
Botzy
485501c95b Merge branch 'development' into feat/untranslated-strings 2025-06-18 11:30:14 +03:00
Tim
cb405878a0 chore: update stremio-translations 2025-06-18 09:59:54 +02:00
Tim
7ec7e8eb03 refactor: rewrite settings route 2025-06-18 09:48:12 +02:00
Timothy Z.
29e7522f82
Merge pull request #937 from Stremio/fix/sidedrawer-styles-mobile
Some checks failed
Build / build (push) Has been cancelled
2025-06-16 16:23:04 +03:00
Tim
f6d4e3f4a6 feat: add player subtitles delay shortcuts 2025-06-16 15:22:10 +02:00
Timothy Z.
1e710c4d46
Merge pull request #924 from Stremio/dependabot/github_actions/actions/github-script-7
Some checks failed
Build / build (push) Has been cancelled
chore(deps): bump actions/github-script from 6 to 7
2025-06-14 23:52:24 +03:00
Abdalrzag Eisa
5f106f49d3
fix: SideDrawer glitched element movement.
Even when using `onTransitionEnd`, the `scrollIntoView` problem persists.
The solution that i found is to use `requestAnimationFrame` two times,
turns out that's enough time after the component mounts to fix the problem.
2025-06-14 23:18:08 +03:00
Abdalrzag Eisa
a0d3a50122
fix: pass correct prop to SideDrawer 2025-06-14 22:53:57 +03:00
Abdalrzag Eisa
dfaba09ef2
clear up naming 2025-06-14 22:50:47 +03:00
Abdalrzag Eisa
f7f9e6a408
remove container and use forwardRef 2025-06-14 22:42:14 +03:00
Abdalrzag Eisa
dad52d61ed
use onTransitionEnded and remove setTimeout 2025-06-14 22:24:09 +03:00
Timothy Z.
86bd1b276a fix(SideDrawer): mobile styles 2025-06-14 18:24:57 +03:00
Timothy Z.
4dcdff7700
Merge pull request #935 from val-makkas/fix/sidedrawer-show-desc
Some checks are pending
Build / build (push) Waiting to run
fix(SideDrawer): add show more/less button for description
2025-06-14 17:46:25 +03:00
val_makkas
d5eb6a515f fixed info box styling 2025-06-14 08:49:48 +03:00
Abdalrzag Eisa
524bcd90da
automatically jump to video on mount, remove button 2025-06-14 02:38:11 +03:00
Abdalrzag Eisa
7dc0958e39
Add jump-to-currently-playing-btn class 2025-06-13 22:00:58 +03:00
Abdalrzag Eisa
40871dc8f2
Add button jump-to-currently-playing-video 2025-06-13 21:59:19 +03:00
Abdalrzag Eisa
73823e9e07
Pass currentlyPlayingVideoID into SideDrawer 2025-06-13 21:56:36 +03:00
val_makkas
48d95d9d6f fix(SideDrawer): add show more/less button for description 2025-06-12 22:07:50 +03:00
Tim
cbd0e87729 refactor: rating logic 2025-06-12 15:22:22 +02:00
Tim
df365a431d chore: update stremio-core-web 2025-06-12 15:19:59 +02:00
Timothy Z.
f4d02ac151 refactor(MetaPreview): ratings only on supp src 2025-06-12 13:31:39 +03:00
Tim
b7f75d1bbe chore: update stremio-core-web 2025-06-11 17:30:01 +02:00
Tim
21ad21c82e refactor: rating logic 2025-06-11 16:08:30 +02:00
Tim
69047471bd chore: update stremio-core-web 2025-06-11 16:08:12 +02:00
Tim
bbe966bc88 chore(MetaPreview): fix format 2025-06-10 18:27:34 +02:00
Tim
52e2b23912 chore: update stremio-core-web 2025-06-10 18:24:13 +02:00
Tim
dff998d723 Merge branch 'feat/user-item-ratings' of https://github.com/Stremio/stremio-web into feat/user-item-ratings 2025-06-10 18:10:04 +02:00
Tim
a5ffa2677d refactor(MetaPreview): remove use of metaId for Rate action 2025-06-10 18:09:11 +02:00
Timothy Z.
945a6d16b1 chore(pkgs): use staging core-web 2025-06-10 18:27:40 +03:00
Botzy
7c3dd67eb9 fix: video release date locale formatting 2025-06-10 14:18:04 +03:00
Botzy
2f0ec456fe Merge branch 'development' into feat/untranslated-strings 2025-06-10 13:57:12 +03:00
Jordan Clarke
fb3d4e29fa Cleaner Fix for Stremio#916 - Added System priority and Alphabetic Ordering on all dropdowns from settings screen. 2025-06-08 13:58:15 +01:00
Jordan Clarke
bd5a8e988f Improved Fix for Stremio#916 - Added System priority and Alphabetic Ordering on other dropdowns. 2025-06-08 13:06:14 +01:00
Jordan Clarke
d329139abe Fix for #916 - Added System priority and Alphabetic Ordering - First Commit Ever :) 2025-06-08 01:14:43 +01:00
Timothy Z.
48851a62cb refactor(Ratings): disabled styles 2025-06-06 17:14:58 +03:00
Timothy Z.
64707dee21 refactor(Ratings): disable actions when not ready 2025-06-06 16:50:53 +03:00
Timothy Z.
1297a2926b fix(Ratings): mobile styles 2025-06-05 20:06:21 +03:00
Timothy Z.
40907e9448 refactor(ActionButton): remove dead code (3) 2025-06-05 19:50:03 +03:00
Timothy Z.
6c7035db9c refactor(ActionButton): remove dead code (2) 2025-06-05 19:49:05 +03:00
Timothy Z.
4df2c4c9ca refactor(ActionButton): remove dead code 2025-06-05 19:48:31 +03:00
Timothy Z.
3dbcff6fb9 fix(Ratings): icons styles 2025-06-05 19:47:33 +03:00
Timothy Z.
ecfaa518a0 chore(Ratings): bump stremio-icons 2025-06-05 19:47:22 +03:00
Timothy Z.
be73839349 refactor(Ratings): align with new design 2025-06-05 19:00:31 +03:00
Timothy Z.
dc9cfb12f3 refactor(ratings): remove dead code 2025-06-05 17:54:57 +03:00
Timothy Z.
38d5290d91 refactor(MetaPreview): usage of ratings 2025-06-05 16:34:03 +03:00
Timothy Z.
beb873e34e refactor(MetaPreview): discover fix 2025-06-04 14:56:11 +03:00
Timothy Z.
ad680ca2a5 refactor(useRating): simplify 2025-06-03 15:05:43 +03:00
Timothy Z.
faee4166c3 feat(MetaPreview): impl user item ratings 2025-06-03 14:58:19 +03:00
Timothy Z.
d75c9b1d99
Merge pull request #855 from Stremio/feat/replace-multiselect-settings
Some checks failed
Build / build (push) Has been cancelled
Аpp: Replace Multiselect usage with MultiselectMenu
2025-06-03 12:54:55 +03:00
Timothy Z.
eab1b8def3 refactor(StreamingServerInputs): non null value 2025-06-03 12:48:44 +03:00
Timothy Z.
a162397d29 refactor(MultiselectMenu): lint & types 2025-06-03 12:39:08 +03:00
Timothy Z.
fd4c9e73c8 refactor(MultiselectMenu): use value only 2025-06-03 12:34:14 +03:00
dependabot[bot]
015e770e42
chore(deps): bump actions/github-script from 6 to 7
Bumps [actions/github-script](https://github.com/actions/github-script) from 6 to 7.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 21:49:25 +00:00
Tim
75f647e3a4
Merge pull request #922 from Stremio/refactor/shell-open-external
Some checks failed
Build / build (push) Has been cancelled
refactor(common): remove use of ipc for opening external url
2025-06-01 12:15:19 +02:00
Tim
824763a277 refactor(common): remove use of ipc for opening external url 2025-05-31 17:12:42 +02:00
Botzy
8768b9ced3 added missing modal button labels and \input placeholder translation keys 2025-05-28 19:02:28 +03:00
Botzy
d692d09041 fix: replace all remaining untranslated strings with translation keys 2025-05-28 17:17:34 +03:00
Botzy
e704abcb03 feat: make translation test fail if hardcoded strings are found 2025-05-28 17:16:08 +03:00
Botzy
d27f92fb01 fix(Discover): use correct translation func 2025-05-28 16:30:04 +03:00
Botzy
4f9cd61286 fix(Discover): dropdown options translations 2025-05-28 13:36:21 +03:00
Botzy
66ad1ea59f fix: move scan translations test to test folder 2025-05-28 12:25:55 +03:00
Botzy
bafa6a7ad2 Feat: refactor the translations scanner into a test 2025-05-28 12:20:55 +03:00
Timothy Z.
67c1b814c3
Merge pull request #902 from Stremio/feat/add-auto-label-assignments-on-prs
Some checks failed
Build / build (push) Has been cancelled
chore(Github): auto assignments on PRs
2025-05-28 11:00:11 +03:00
Timothy Z.
83a7f6fd3d
Merge pull request #917 from Stremio/fix/trakt-logout-button
Some checks are pending
Build / build (push) Waiting to run
Settings: fix trakt logout button
2025-05-27 21:36:24 +03:00
Tim
0875b89e5e
Merge pull request #921 from Stremio/feat/shell-deeplinks
App(Shell): full deeplink support
2025-05-27 20:34:31 +02:00
Timothy Z.
8968055493
fix(Settings): trakt text checks repetition 2025-05-27 21:33:13 +03:00
Tim
5d9a005686 style(App): use singlequote for string 2025-05-27 20:30:23 +02:00
Tim
597b366ce2 fix(common): allow board regex to match empty 2025-05-27 20:26:02 +02:00
Tim
41546d65d2 feat: full deeplink support for shell 2025-05-27 20:16:26 +02:00
Botzy
18b70402a4 fix lint 2025-05-27 18:32:20 +03:00
Botzy
ce54ac9aac feat: update translations dependency 2025-05-27 18:27:19 +03:00
Botzy
3c249e5925 fix hardcoded titles with no translations 2025-05-27 18:20:18 +03:00
Botzy
51a1da958b fix: season number translation key 2025-05-27 17:55:47 +03:00
Botzy
fe871f03f1 fix: dropdown options and titles translations 2025-05-27 17:55:17 +03:00
Botzy
5a942c5f73 fix spacing 2025-05-27 16:53:57 +03:00
Botzy
a7eb1801e3 fix(Settings): added missing translation keys 2025-05-27 14:24:59 +03:00
Botzy
a25d23559f fix(NotFound): added missing translation keys 2025-05-27 14:23:39 +03:00
Botzy
7b57c0f508 fix(MetaDetails): added missing translation keys 2025-05-27 12:24:35 +03:00
Botzy
db40abfad3 fix(Library): added missing translation keys 2025-05-27 12:12:39 +03:00
Botzy
61fdf3113e fix(Discover): added missing translation keys 2025-05-27 12:07:57 +03:00
Botzy
64c553d253 fix(Calendar): added missing translation keys 2025-05-27 12:06:58 +03:00
Tim
b3bd68eb32
Merge pull request #919 from Stremio/feat/shell-fullscreen-key-f
Some checks are pending
Build / build (push) Waiting to run
App(Shell): toggle fullscreen with F key
2025-05-27 10:34:37 +02:00
Tim
2b44367a26 feat: toggle fullscreen with F key with shell 2025-05-27 10:28:02 +02:00
Tim
19bf6abb00
Merge pull request #918 from Stremio/fix/toggle-fullscreen
App(Shell): fix toggle fullscreen
2025-05-27 10:24:20 +02:00
Tim
6dfa3fdae0 fix: toggle fullscreen 2025-05-27 10:19:39 +02:00
dexter21767-dev
e8ac50135b fix trakt logout button 2025-05-26 20:00:06 +01:00
Botzy
789173bb5b fix(Intro): added missing translation keys 2025-05-23 18:18:35 +03:00
Botzy
5f81804b00 fix(MetaDetails): added missing translation keys 2025-05-23 18:03:47 +03:00
Botzy
2de0a517e0 fix(Multiselect): added missing translation keys 2025-05-23 17:43:06 +03:00
Botzy
f24ad7d069 fix(Addons): added missing translation keys 2025-05-23 17:17:30 +03:00
Botzy
aba31c8ceb fix(Addons): added missing translations 2025-05-23 17:05:01 +03:00
Botzy
657f9cd29e feat: added scanner for missed translations 2025-05-23 16:34:41 +03:00
Timothy Z.
718a64877c
Merge pull request #864 from Stremio/feat/replace-multiselect-multiselectmenu
Refactor: Replace Multiselect with MultiselectMenu component
2025-05-22 22:26:39 +03:00
Botzy
4d82c2f890 Merge branch 'feat/replace-multiselect-settings' into feat/replace-multiselect-multiselectmenu 2025-05-22 18:42:25 +03:00
Botzy
42a55a254d Merge branch 'development' into feat/replace-multiselect-settings 2025-05-22 17:00:30 +03:00
Timothy Z.
bed2a58060
Merge pull request #913 from Stremio/feat/allow-disabling-subs-globally
Some checks failed
Build / build (push) Has been cancelled
Settings: Allow disabling subtitles globally
2025-05-22 16:53:12 +03:00
Timothy Z.
5f53b9b44a
Merge pull request #915 from Stremio/fix/discover-metaitem-tap-mobile
Fix(Discover): enable direct navigation to meta item on mobile on first tap
2025-05-22 15:21:20 +03:00
Timothy Z.
7b7c700533 chore(styles): align styles code 2025-05-22 15:01:21 +03:00
Timothy Z.
fa07709d31 refactor(Discover): use a reference instead 2025-05-22 14:59:31 +03:00
Timothy Z.
28578e1dea refactor(Player): reset global var 2025-05-21 15:11:32 +03:00
Botzy
2e72f5af9d refactor(Discover): check for window innerWitdth instead isMobile to enable direct navigation 2025-05-21 14:10:42 +03:00
Botzy
01c5100aaf fix(Discover): enable direct navigation to meta item on mobile instead focus first 2025-05-20 19:38:37 +03:00
Timothy Z.
440713ee68 fix(Player): reset the flag back to false 2025-05-20 15:54:20 +03:00
Timothy Z.
38f7e5a0f8 chore(Player): update logs 2025-05-20 15:32:02 +03:00
Timothy Z.
365294946a chore(Player): add logs 2025-05-20 15:12:29 +03:00
Timothy Z.
b4c0ab551e fix(Player): binge watching 2025-05-20 15:09:50 +03:00
Timothy Z.
fff0ebe85d fix(Player): workaround for binge watching 2025-05-19 17:02:32 +03:00
Timothy Z.
1d8401e4df feat(Settings): allow disabling subs globally 2025-05-19 10:56:28 +03:00
Timothy Z.
a6f84d18d1
Merge pull request #907 from Stremio/fix/exit-fullscreen
Some checks failed
Build / build (push) Has been cancelled
fix: exitFullscreen
2025-05-14 11:30:48 +03:00
Timothy Z.
1ae009c0bc
Merge pull request #906 from Stremio/fix/player-statistics-show-max-100-percent
fix: StatisticsMenu - max of 100% for completed
2025-05-14 11:30:34 +03:00
Timothy Z.
1d95b9efbd
Merge pull request #908 from Stremio/fix/search-page-styles
Some checks are pending
Build / build (push) Waiting to run
Search: align styles with board catalogs
2025-05-13 17:02:51 +03:00
Timothy Z.
62f8bb367f refactor(Search): align completely to Board 2025-05-13 15:18:30 +03:00
Timothy Z.
878af40c1d fix(Search): align styles with board catalogs 2025-05-13 14:33:35 +03:00
Timothy Z.
cc105f327c chore: bump v5.0.0-beta.23
Some checks are pending
Build / build (push) Waiting to run
2025-05-13 11:52:31 +03:00
Timothy Z.
252de8c496
Merge pull request #903 from Stremio/fix/binge-watching-bugs
Player(Shell): Fix binge watching bugs
2025-05-13 11:15:47 +03:00
Lachezar Lechev
2dec01923a
fix: StatisticsMenu - max of 100% for completed
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-05-12 13:47:09 +03:00
Lachezar Lechev
f8ab1a7dbc
fix: exitFullscreen
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-05-12 13:45:53 +03:00
Timothy Z.
4d53952368 remove(Player): unnecessary returns 2025-05-05 17:51:12 +02:00
Timothy Z.
b51791baa0 fix(Player): replace history entry
- fix for the navigation
2025-05-02 19:55:09 +03:00
Timothy Z.
17312f64fd revert: chore lint 1 2025-05-02 19:48:04 +03:00
Timothy Z.
ebb15463b4 chore(Player): lint (2) 2025-05-02 19:47:04 +03:00
Timothy Z.
3985c88346 chore(Player): lint 2025-05-02 19:44:55 +03:00
Timothy Z.
f3a7ef5978 fix(Player): binge watching in shell 2025-05-02 19:42:49 +03:00
Timothy Z.
ce0c5da3fd test: again 2025-05-02 01:46:13 +03:00
Timothy Z.
d6372c4f86 test: another test 2025-05-02 01:30:58 +03:00
Timothy Z.
79f06153c8 Update Player.js 2025-05-02 01:14:48 +03:00
Timothy Z.
f446047f8b test(Player): fix binge watching (6) 2025-05-02 00:53:42 +03:00
Timothy Z.
9b405c53d8 test(player): fix binge watching (5) 2025-05-02 00:22:39 +03:00
Timothy Z.
9aed64d998 test(Player): fix binge watching (4) 2025-05-02 00:00:15 +03:00
Timothy Z.
672a0067ce test(Player): fix binge watching 2025-05-01 23:37:44 +03:00
Timothy Z.
18ac3583b4 chore(Player): lint (2) 2025-05-01 23:07:00 +03:00
Timothy Z.
7924200dab
chore(player): lint 2025-05-01 23:01:02 +03:00
Timothy Z.
980d0038ec test(player): fix binge watching (3) 2025-05-01 22:52:39 +03:00
Timothy Z.
0efd1453bb test(player): fix binge watching (2) 2025-05-01 22:41:04 +03:00
Timothy Z.
6bfe079030 test(player): use replace instead of href 2025-05-01 22:23:14 +03:00
Timothy Z.
5b56c58e5b fix(player): redirect to MetaDetails 2025-05-01 21:56:37 +03:00
Timothy Z.
107564b9d4 feat(gh): auto assignments on PRs 2025-04-30 12:18:08 +03:00
Timothy Z.
242a4a8110 chore(bump): v5.0.0-beta.22
Some checks failed
Build / build (push) Has been cancelled
2025-04-28 16:55:22 +03:00
Timothy Z.
aeb9265e3b
Merge pull request #898 from Stremio/fix/apple-login-shell
Some checks failed
Build / build (push) Has been cancelled
Shell: Fix Apple ID login
2025-04-25 14:39:17 +03:00
Timothy Z.
22ba03dea2 refactor(useapplelogin): fallback in case no name 2025-04-25 14:10:01 +03:00
Timothy Z.
1568ba1bb2 refactor(useapplelogin): use correct url 2025-04-24 16:49:01 +03:00
Timothy Z.
d09c760a1c fix(useapplelogin ): lint 2025-04-24 16:01:44 +03:00
Timothy Z.
63624a9554 refactor(useapplelogin): use w/out popup instead 2025-04-24 15:59:48 +03:00
Timothy Z.
fb7c5642b0 test: do not use popup 2025-04-24 13:04:12 +03:00
Владимир Борисов
1b43484013
Merge pull request #896 from Stremio/fix/app-site-association-fix-apple
Some checks failed
Build / build (push) Has been cancelled
misc: use wildcard for app site association
2025-04-17 13:48:48 +03:00
AlvinHV
ad4df3bac5 fix: use wildcard for app site association 2025-04-17 01:15:10 +04:00
Timothy Z.
1247fc18f7
Merge pull request #895 from Stremio/fix/binge-watching-next-episode
Some checks are pending
Build / build (push) Waiting to run
Player: Fix Auto play next episode
2025-04-16 19:47:11 +03:00
Botzy
0809fbdf53 Merge branch 'development' of github.com:Stremio/stremio-web into fix/binge-watching-next-episode 2025-04-15 16:31:14 +03:00
Botzy
6faf70d33e fix(Player): update video event handlers on handlers changes 2025-04-15 16:30:13 +03:00
Timothy Z.
69ee1da68e Merge branch 'development' into feat/replace-multiselect-settings 2025-04-15 14:45:32 +03:00
Timothy Z.
3f631b1b72
Merge pull request #836 from Stremio/fix/discover-page-remove-duplicated-filters
Some checks failed
Build / build (push) Has been cancelled
Discover: Remove duplicated filters in filter modal
2025-04-15 13:44:25 +03:00
Timothy Z.
a8eb284379 Merge branch 'development' into fix/discover-page-remove-duplicated-filters 2025-04-15 13:00:52 +03:00
Timothy Z.
55dac0d36f
Merge pull request #827 from Stremio/feat/season-episode-inputs
Streams List: Season and Episode picker when no streams loaded
2025-04-15 12:50:06 +03:00
Timothy Z.
e6bd8d66e8
Merge pull request #839 from Stremio/fix/library-default-filter
Library: Default type All filter
2025-04-15 11:58:00 +03:00
Timothy Z.
a8931d94df
Merge pull request #889 from Stremio/feat/example-apple-login
App: Implement Apple login
2025-04-15 11:55:23 +03:00
Timothy Z.
57d16957f2 chore(useapplelogin): update redirect to prod 2025-04-14 23:26:04 +03:00
Timothy Z.
303dd9858b refactor(useapplelogin): handle error cases 2025-04-14 19:29:12 +03:00
Timothy Z.
3aac148258 Update useAppleLogin.ts 2025-04-14 17:23:09 +03:00
Timothy Z.
66abce42c5 revert(useapplelogin): timeoutid 2025-04-14 17:20:54 +03:00
Timothy Z.
0744fdfb8d refactor(Intro): new button design 2025-04-14 16:16:02 +03:00
Timothy Z.
922de40134 fix(useapplelogin): add timeout for close 2025-04-14 16:12:27 +03:00
Timothy Z.
52dc7722ad Revert "fix(useapplelogin): remove state signin"
This reverts commit e454cecc45.
2025-04-14 14:53:44 +03:00
Timothy Z.
e454cecc45 fix(useapplelogin): remove state signin 2025-04-14 14:38:19 +03:00
Timothy Z.
468dc604ae Revert "chore: add debug logs"
This reverts commit 0c3b7e8d4a.
2025-04-14 14:12:05 +03:00
Timothy Z.
0c3b7e8d4a chore: add debug logs 2025-04-14 13:36:40 +03:00
Timothy Z.
befcef6dd2 fix: update core-web 2025-04-14 13:18:12 +03:00
Timothy Z.
09c1b0c45e chore(core-web): v0.49.3 2025-04-14 13:12:08 +03:00
Timothy Z.
43c76b6a4b refactor(Intro): adjust styles 2025-04-10 22:48:56 +03:00
Timothy Z.
5acc32411d fix(app): apple login types 2025-04-10 21:54:11 +03:00
Timothy Z.
2ce1619313 chore: remove logs & use correct core action 2025-04-10 21:52:57 +03:00
Timothy Z.
2713c8b46d fix(useapplelogin): jwt errors 2025-04-10 21:31:34 +03:00
Timothy Z.
b86887e111 chore: add testing logs 2025-04-10 21:11:41 +03:00
Timothy Z.
846445001c fix(useapplelogin): get sub id from token 2025-04-10 21:10:50 +03:00
Timothy Z.
50edda2557 fix(useapplelogin): use redirect uri for testing 2025-04-10 20:45:51 +03:00
Timothy Z.
a1acb7423a fix(useapplelogin): auth response 2025-04-10 20:40:05 +03:00
Tim
97eaa5f00a
Merge pull request #892 from Stremio/fix/shell-volume-disabled
Some checks failed
Build / build (push) Has been cancelled
Player(Shell): fix volume disabled
2025-04-10 19:26:47 +02:00
Tim
6b9a3a91c7 chore: update stremio-video 2025-04-10 19:23:23 +02:00
Timothy Z.
fdfc939abb
Merge pull request #891 from Stremio/fix/player-options-menu-crash
Some checks are pending
Build / build (push) Waiting to run
Player: Fix options menu crash when no stream loaded up
2025-04-10 16:59:06 +03:00
Timothy Z.
96f0baadc2 fix(Player): on options menu open crash 2025-04-10 16:46:19 +03:00
Timothy Z.
200d3a76d8 fix(useapplelogin): use correct client id 2025-04-10 14:20:23 +03:00
Timothy Z.
d457db6f1e refactor(useapplelogin): update redirect url 2025-04-10 12:52:56 +03:00
Timothy Z.
a0fa5e2a92 fix(chore): lint 2025-04-10 12:45:06 +03:00
Timothy Z.
592fb17fa1 refactor(Apple login): support new endpoint 2025-04-10 12:42:41 +03:00
Timothy Z.
9a6f01b6ad Merge branch 'development' into feat/example-apple-login 2025-04-10 12:32:50 +03:00
Timothy Z.
0eefcb4782
Merge pull request #888 from Stremio/fix-safari-font
Some checks failed
Build / build (push) Has been cancelled
Fix Font Weight for Safari
2025-04-08 17:02:17 +03:00
Alexandru Branza
2ef3f52c1c Fix Font Weight for Safari 2025-04-08 16:55:36 +03:00
Timothy Z.
3530e3c7d0
Merge pull request #885 from Stremio/fix/non-latin-chars-font
Some checks failed
Build / build (push) Has been cancelled
App: fix non latin characters font issue
2025-04-04 23:29:43 +03:00
Tim
9aa0490989 fix(App): non latin chars font 2025-04-04 18:58:36 +02:00
Botzy
7c932a93e5 feat(MetaDetails): added season picker when no metadetails loaded in videos list 2025-04-02 14:35:04 +03:00
Botzy
639d5f8d1c refactor(EpisodePicker): moved EpisodePicker to higher level in MetaDetails folder 2025-04-02 14:25:41 +03:00
Timothy Z.
e156c27b64 refactor(NumberInput): simplify 2025-04-01 11:57:45 +03:00
Timothy Z.
4eca979d97 refactor(EpisodePicker): simplify 2025-04-01 11:57:37 +03:00
Timothy Z.
7915424fa4 fix(EpisodePicker): lint 2025-03-31 13:53:38 +03:00
Timothy Z.
b2f5fb74c8 refactor(EpisodePicker): simplify 2025-03-31 13:51:31 +03:00
Timothy Z.
cdaad5c834 fix(NumberInput): lint 2025-03-25 18:12:29 +02:00
Timothy Z.
60ba559500 refactor(EpisodePicker): simplify 2025-03-25 18:11:14 +02:00
Timothy Z.
56762353f2 refactor(NumberInput): simplify 2025-03-25 18:03:36 +02:00
Timothy Z.
7636beabdc Merge branch 'development' into feat/season-episode-inputs 2025-03-25 17:18:52 +02:00
Timothy Z.
3f58bb8e46 Merge branch 'development' into feat/season-episode-inputs 2025-03-25 17:18:46 +02:00
Timothy Z.
f5fb2ed37a feat(Auth): example apple login 2025-03-24 16:18:29 +02:00
Botzy
7147c954c9 refactor(Addons): replace Multiselect with MultiselectMenu component 2025-03-12 20:07:21 +02:00
Botzy
a96a44b0dd refactor(Discover): replace Multiselect with MultiselectMenu component 2025-03-12 19:27:49 +02:00
Botzy
db7277714b fix(Dropdown): use option value as key instead missing id 2025-03-12 19:26:43 +02:00
Botzy
a21e5698c8 refactor(Library): replace Multiselect with MultiselectMenu 2025-03-11 20:45:18 +02:00
Botzy
5365c1739e fix(MultiselectMenu): keep background color when state is open 2025-03-11 19:43:04 +02:00
Botzy
98784779b5 refactor(StreamsList): replace Multiselect with MultiselectMenu 2025-03-11 19:31:46 +02:00
Botzy
7f244c4fdd fix(Settings): revert input container padding change for all fields and apply only to multiselect menu 2025-03-10 15:05:10 +02:00
Botzy
4b64439271 Merge branch 'feat/replace-multiselect-settings' of github.com:Stremio/stremio-web into feat/replace-multiselect-settings 2025-03-10 14:57:59 +02:00
Botzy
79d9e886be fix(Settings): align MultiselectMenu styles to multiselect ones 2025-03-10 14:57:39 +02:00
Botzy
5f8aaf395d fix(Settings): display name of default UI language option 2025-03-10 14:41:16 +02:00
Timothy Z.
48e07c3008 Merge branch 'development' into feat/replace-multiselect-settings 2025-03-05 12:03:48 +01:00
Timothy Z.
20577e2431 Merge branch 'development' into fix/library-default-filter 2025-03-05 12:03:35 +01:00
Timothy Z.
86c29f2201 Merge branch 'development' into fix/discover-page-remove-duplicated-filters 2025-03-05 12:03:29 +01:00
Timothy Z.
ad260f3ded Merge branch 'development' into feat/season-episode-inputs 2025-03-05 12:03:22 +01:00
Botzy
7ea974f1da refactor(Settings): use MultiselectMenu instead Multiselect 2025-02-28 17:49:20 +02:00
Botzy
794f4e48ac feat(MultiselectMenu): handle title function 2025-02-28 17:45:13 +02:00
Botzy
a50e3c7186 fix(Discover): apply new lines in styles where missed 2025-02-21 15:09:05 +02:00
Botzy
0506d53f4e fix(Discover): fix mobile styles for filter button 2025-02-19 17:38:17 +02:00
Botzy
1b17bc81cb Merge branch 'development' into fix/discover-page-remove-duplicated-filters 2025-02-19 17:30:19 +02:00
Botzy
b8c328507e fix(Library): default to type All filter after selected type is no longer available 2025-02-18 18:05:18 +02:00
Botzy
224b6e6f76 fix(Discover): improve styles to handle filters edge cases 2025-02-18 15:35:14 +02:00
Botzy
b1365e31d4 fix(Multiselect): destruct options from props to not pass them to component 2025-02-18 15:33:11 +02:00
Botzy
2816f7bcea Merge branch 'development' into fix/discover-page-remove-duplicated-filters 2025-02-18 11:54:50 +02:00
Botzy
dad4804bad fix(Discover): Hide duplicated catalog type filter on mobile 2025-02-17 12:57:20 +02:00
Botsy
6999ef6a8d
Update src/components/NumberInput/NumberInput.tsx
Co-authored-by: Timothy Z. <timothy@stremio.com>
2025-02-13 12:37:36 +02:00
Botzy
675328ca08 fix(StreamsList): show EpisodePicker only if type is series 2025-02-12 17:27:37 +02:00
Botzy
07cc2a9b2d refactor(EpisodePicker): apply suggested improvements 2025-02-12 16:59:37 +02:00
Botzy
4cd9db53d1 fix(NumberInput): remove unused import 2025-02-12 16:01:28 +02:00
Botzy
f678375633 refactor(NumberInput): apply suggested improvements 2025-02-12 16:00:41 +02:00
Botsy
10a36d2c4d
Update src/components/NumberInput/NumberInput.tsx
Co-authored-by: Timothy Z. <timothy@stremio.com>
2025-02-12 15:46:52 +02:00
Botzy
aa3dedf8be fix: linting 2025-02-12 15:38:14 +02:00
Botsy
232c64b613
Update src/routes/MetaDetails/StreamsList/EpisodePicker/EpisodePicker.tsx
Co-authored-by: Timothy Z. <timothy@stremio.com>
2025-02-12 15:34:47 +02:00
Botsy
37020f340c
Update src/routes/MetaDetails/StreamsList/StreamsList.js
Co-authored-by: Timothy Z. <timothy@stremio.com>
2025-02-12 15:34:35 +02:00
Botsy
3dcd0020c9
Update src/components/NumberInput/NumberInput.tsx
Co-authored-by: Timothy Z. <timothy@stremio.com>
2025-02-12 15:34:24 +02:00
Botsy
d7974babdd
Update src/components/NumberInput/NumberInput.tsx
Co-authored-by: Timothy Z. <timothy@stremio.com>
2025-02-12 15:34:07 +02:00
Botzy
6274115f8f refactor(EpisodePicker): simplify setting season and episode initial state 2025-02-12 13:55:34 +02:00
Botzy
7a79e31f95 fix(EpisodePicker): use default export in StreamsList 2025-02-12 13:14:01 +02:00
Botzy
1721810467 fix(NumberInput): follow style name convention 2025-02-12 13:13:06 +02:00
Botzy
3bc075d0a1 fix(EpisodePicker): remove named export 2025-02-12 12:44:21 +02:00
Botzy
3c2ab92bd6 fix(EpisodePicker): make 0 the initial value for season when no value provided, remove placeholder 2025-02-11 19:27:49 +02:00
Botzy
3f60df9073 refactor(EpisodePicker): improve styles and typings 2025-02-11 19:05:46 +02:00
Botzy
6ca94a2124 fix(EpisodePicker): unify styles with install addons button 2025-02-11 16:41:36 +02:00
Botzy
f7494d6e97 fix(EpisodePicker): typings 2025-02-10 17:52:57 +02:00
Botzy
fbdfa110b5 fix(StreamsList): add scroll when episode picker is shown on landscape orientation on mobile 2025-02-10 17:38:32 +02:00
Botzy
d407e6c7b7 fix(NumberInput): min & max validation when entering value from keyboard 2025-02-10 15:12:43 +02:00
Botzy
0c9b9927cc Merge branch 'development' into feat/season-episode-inputs 2025-02-10 15:02:39 +02:00
Botzy
34808d6014 fix(EpisodePicker): set season and episode from url 2025-02-10 14:18:13 +02:00
Botzy
6b30b90893 fix(MetaDetails): handle search for any episode and season 2025-02-10 13:31:44 +02:00
Botzy
5e98355896 fix(NumberInput): fix check for min and max values 2025-02-10 12:37:40 +02:00
Botzy
7fa4f462c5 feat(StreamsList): added upcoming label when no results 2025-02-07 19:23:33 +02:00
Botzy
36a2896525 fix(NumberInput): use color variable for font color 2025-02-07 19:13:23 +02:00
Botzy
538e462b12 fix(StreamsList): hide Install addons button if episode is upcoming 2025-02-07 18:23:28 +02:00
Botzy
39f168a34c fix(EpisodePicker): handle season 0 as value 2025-02-07 18:22:24 +02:00
Botzy
461c9d3d53 fix(EpisodePicker): fix export 2025-02-07 17:49:27 +02:00
Botzy
15c6a231a6 feat(EpisodePicker): added season and episode picker when no streams loaded 2025-02-07 17:27:53 +02:00
Botzy
e8a6e72b13 feat(NumberInput): added NumberInput common component 2025-02-07 17:06:26 +02:00
220 changed files with 15696 additions and 17934 deletions

66
.github/workflows/auto_assign.yml vendored Normal file
View file

@ -0,0 +1,66 @@
name: PR and Issue Workflow
on:
pull_request:
types: [opened, reopened]
issues:
types: [opened]
jobs:
auto-assign-and-label:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
# Auto assign PR to author
- name: Auto Assign PR to Author
if: github.event.pull_request.head.repo.fork == false && github.event_name == 'pull_request' && github.actor != 'dependabot[bot]'
uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const pr = context.payload.pull_request;
if (pr) {
await github.rest.issues.addAssignees({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
assignees: [pr.user.login]
});
console.log(`Assigned PR #${pr.number} to author @${pr.user.login}`);
}
# Dynamic labeling based on PR/Issue title
- name: Label PRs and Issues
if: github.event.pull_request.head.repo.fork == false && github.actor != 'dependabot[bot]'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const prTitle = context.payload.pull_request ? context.payload.pull_request.title : context.payload.issue.title;
const issueNumber = context.payload.pull_request ? context.payload.pull_request.number : context.payload.issue.number;
const isIssue = context.payload.issue !== undefined;
const labelMappings = [
{ pattern: /^feat(ure)?/i, label: 'feature' },
{ pattern: /^fix/i, label: 'bug' },
{ pattern: /^refactor/i, label: 'refactor' },
{ pattern: /^chore/i, label: 'chore' },
{ pattern: /^docs?/i, label: 'documentation' },
{ pattern: /^perf(ormance)?/i, label: 'performance' },
{ pattern: /^test/i, label: 'testing' }
];
let labelsToAdd = [];
for (const mapping of labelMappings) {
if (mapping.pattern.test(prTitle)) {
labelsToAdd.push(mapping.label);
}
}
if (labelsToAdd.length > 0) {
github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
labels: labelsToAdd
});
}

View file

@ -5,7 +5,7 @@ on:
branches:
- development
tags-ignore:
- '**'
- "**"
pull_request:
branches:
- development
@ -20,20 +20,26 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
run_install: false
- name: Setup node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
cache: "pnpm"
- name: Install NPM dependencies
run: npm ci
run: pnpm install
- name: Build
run: npm run build
run: pnpm build
- name: Test
run: npm test
run: pnpm test
- name: Lint
run: npm run lint
# Create recursivelly the destiantion dir with
run: pnpm lint
# Create recursively the destination dir with
# "--parrents where no error if existing, make parent directories as needed."
- run: mkdir -p ./build/${{ github.head_ref || github.ref_name }}
- name: Deploy to GitHub Pages

53
.github/workflows/pages_cleanup.yml vendored Normal file
View file

@ -0,0 +1,53 @@
name: GitHub Pages Cleanup
on:
schedule:
- cron: '0 0 * * 0'
workflow_dispatch:
permissions:
contents: write
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: gh-pages
fetch-depth: 0
- name: Delete directories that don't have existing branch
run: |
branches=( $(git branch -r | grep origin | grep -v HEAD | sed 's|origin/||') )
declare -p branches
find . -mindepth 1 -maxdepth 2 -type d -not -path '*/\.*' | while read -r dir; do
path="${dir#./}"
if [[ " ${branches[*]} " =~ " $path " ]]; then
continue
fi
keep_parent=false
for branch in "${branches[@]}"; do
if [[ "$branch" == "$path/"* ]]; then
keep_parent=true
break
fi
done
if ! $keep_parent; then
echo "Deleting $dir"
rm -rf "$dir"
fi
done
- name: Commit and push
run: |
git config --global user.name 'GitHub Pages Cleanup'
git config --global user.email 'actions@stremio.com'
git add -A
git diff --cached --quiet || git commit -m "cleanup"
git push origin gh-pages

View file

@ -9,26 +9,25 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install NPM dependencies
run: npm install
uses: actions/checkout@v6
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
run_install: false
- name: Install dependencies
run: pnpm install
- name: Build
env:
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
run: npm run build
run: pnpm build
- name: Zip build artifact
run: zip -r stremio-web.zip ./build
- name: Upload build artifact to GitHub release assets
uses: svenstaro/upload-release-action@2.9.0
uses: svenstaro/upload-release-action@2.11.3
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: stremio-web.zip
asset_name: stremio-web.zip
tag: ${{ github.ref }}
overwrite: true
- name: Upload build artifact to Netlify
run: |
curl -H "Content-Type: application/zip" \
-H "Authorization: Bearer ${{ secrets.netlify_access_token }}" \
--data-binary "@stremio-web.zip" \
https://api.netlify.com/api/v1/sites/stremio-development.netlify.com/deploys

View file

@ -1,67 +1,26 @@
{
"applinks": {
"apps": [],
"details": [
{
"appID": "9EWRZ4QP3J.com.stremio.one",
"paths": [
"/",
"/#/player/*",
"/#/discover/*",
"/#/detail/*",
"/#/library/*",
"/#/addons/*",
"/#/settings",
"/#/search/*"
],
"components": [
{
"/": "/",
"#": "/player/*",
"comment": "Matches deep link for player"
},
{
"/": "/",
"#": "/discover/*",
"comment": "Matches deep link for discover"
},
{
"/": "/",
"#": "/detail/*",
"comment": "Matches deep link for detail"
},
{
"/": "/",
"#": "/library/*",
"comment": "Matches deep link for library"
},
{
"/": "/",
"#": "/addons/*",
"comment": "Matches deep link for addons"
},
{
"/": "/",
"#": "/settings",
"comment": "Matches deep link for settings"
},
{
"/": "/",
"#": "/search/*",
"comment": "Matches deep link for search"
}
]
}
"applinks": {
"apps": [],
"details": [
{
"appIDs": [
"9EWRZ4QP3J.com.stremio.one"
],
"appID": "9EWRZ4QP3J.com.stremio.one",
"paths": [
"*"
]
},
"activitycontinuation": {
"apps": [
"9EWRZ4QP3J.com.stremio.one"
]
},
"webcredentials": {
"apps": [
"9EWRZ4QP3J.com.stremio.one"
]
}
}
}
]
},
"activitycontinuation": {
"apps": [
"9EWRZ4QP3J.com.stremio.one"
]
},
"webcredentials": {
"apps": [
"9EWRZ4QP3J.com.stremio.one"
]
}
}

View file

@ -2,35 +2,42 @@
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and community a harassment-free experience for everyone, regardless of age, level of experience, education, nationality or race.
We as contributors and maintainers want to make contributing to our project and community a nice experience for everyone.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
Examples of positive behavior:
- Using welcoming and inclusive language.
- Being respectful of differing viewpoints and experiences.
- Using welcoming language.
- Being respectful.
- Accepting constructive criticism.
- Focusing on what is best for the community.
- Showing empathy towards other community members.
Examples of unacceptable behavior by participants include:
Examples of bad behavior:
- The use of sexualized language or imagery and unwelcome sexual attention or advances.
- Trolling, insulting/derogatory comments, and personal or political attacks.
- Use of sexualized language.
- Trolling, insulting comments, and personal or political attacks.
- Public or private harassment.
- Publishing others private information, such as a physical or electronic address, without explicit permission.
- Other conduct which could reasonably be considered inappropriate in a professional setting.
- Submitting entirely generated by AI PRs with agents such as Devin, Claude Code, Cursor Agent etc.
- Submitting PRs which in majority contain only AI generated code (including docs & comments) and do not solve an actual issue.
- Spamming issues because of no ETAs on issues.
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers are responsible for enforcing this code of conduct. They can remove or edit comments, code, and other contributions that don't follow these rules. They can also ban users who behave inappropriately.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, pull requests, and other contributions that do not align with this Code of Conduct, as well as to temporarily or permanently ban any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Suggestions for newbies
- Contributors are welcomed to use AI models as "help" in solving issues, but you must always double check the code that you're submitting.
- Refrain from excessive comments generated by AI.
- Refrain from docs generated entirely by AI.
- Always check what files you are committing and submitting to the PR when you are using any agent for help or an AI model.
- If you don't know how to tackle a problem and AI can't help you, please just ask or look in Stack Overlflow, Google, Medium etc.
- Learning how to code is fun and easier when using AI, but sometimes it might be just too much ... what are you going to learn, if AI does everything for you and you don't know what the code you are submitting actually does?!
## Scope
This Code of Conduct applies within all `stremio-web` spaces, and also applies when an individual is officially representing the project or its community in public spaces.
This Code of Conduct applies everywhere in `stremio-web` repository, and also applies when an individual is officially representing the project or its community in other spaces.
## Enforcement

View file

@ -3,29 +3,39 @@
ARG NODE_VERSION=20-alpine
FROM node:$NODE_VERSION AS base
# Setup pnpm
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
RUN apk add --no-cache git
# Meta
LABEL Description="Stremio Web" Vendor="Smart Code OOD" Version="1.0.0"
RUN mkdir -p /var/www/stremio-web
WORKDIR /var/www/stremio-web
# Install app dependencies
FROM base AS prebuild
# Setup app
FROM base AS app
RUN apk update && apk upgrade && \
apk add --no-cache git
WORKDIR /var/www/stremio-web
COPY . .
RUN npm install
RUN npm run build
COPY package.json pnpm-lock.yaml /var/www/stremio-web
RUN pnpm i --frozen-lockfile
# Bundle app source
FROM base AS final
COPY . /var/www/stremio-web
RUN pnpm build
WORKDIR /var/www/stremio-web
COPY . .
COPY --from=prebuild /var/www/stremio-web/node_modules ./node_modules
COPY --from=prebuild /var/www/stremio-web/build ./build
# Setup server
FROM base AS server
RUN pnpm i express@4
# Finalize
FROM base
COPY http_server.js /var/www/stremio-web
COPY --from=server /var/www/stremio-web/node_modules /var/www/stremio-web/node_modules
COPY --from=app /var/www/stremio-web/build /var/www/stremio-web/build
EXPOSE 8080
CMD ["node", "http_server.js"]

View file

@ -1,6 +1,6 @@
# Stremio - Freedom to Stream
![Build](https://github.com/stremio/stremio-web/workflows/Build/badge.svg?branch=development)
[![Build](https://github.com/Stremio/stremio-web/actions/workflows/build.yml/badge.svg)](https://github.com/Stremio/stremio-web/actions/workflows/build.yml)
[![Github Page](https://img.shields.io/website?label=Page&logo=github&up_message=online&down_message=offline&url=https%3A%2F%2Fstremio.github.io%2Fstremio-web%2F)](https://stremio.github.io/stremio-web/development)
Stremio is a modern media center that's a one-stop solution for your video entertainment. You discover, watch and organize video content from easy to install addons.
@ -10,24 +10,31 @@ Stremio is a modern media center that's a one-stop solution for your video enter
### Prerequisites
* Node.js 12 or higher
* npm 6 or higher
* [pnpm](https://pnpm.io/installation) 10 or higher
### Install dependencies
```bash
npm install
pnpm install
```
### Start development server
```bash
npm start
pnpm start
```
### Production build
```bash
npm run build
pnpm run build
```
### Run with Docker
```bash
docker build -t stremio-web .
docker run -p 8080:8080 stremio-web
```
## Screenshots

View file

@ -82,7 +82,7 @@ export default [
'@stylistic/semi-spacing': 'error',
'@stylistic/space-before-blocks': 'error',
'@stylistic/no-trailing-spaces': 'error',
'@stylistic/func-call-spacing': 'error',
'@stylistic/function-call-spacing': 'error',
'@stylistic/semi': 'error',
'@stylistic/no-extra-semi': 'error',
'@stylistic/eol-last': 'error',

15494
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
{
"name": "stremio",
"displayName": "Stremio",
"version": "5.0.0-beta.21",
"version": "5.0.0-beta.29",
"author": "Smart Code OOD",
"private": true,
"license": "gpl-2.0",
@ -10,15 +10,16 @@
"start-prod": "webpack serve --mode production",
"build": "webpack --mode production",
"test": "jest",
"lint": "eslint src"
"lint": "eslint src",
"scan-translations": "pnpx jest ./tests/i18nScan.test.js"
},
"dependencies": {
"@babel/runtime": "7.26.0",
"@sentry/browser": "8.42.0",
"@stremio/stremio-colors": "5.2.0",
"@stremio/stremio-core-web": "0.49.2",
"@stremio/stremio-icons": "5.4.1",
"@stremio/stremio-video": "0.0.53",
"@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",
"bowser": "2.11.0",
"buffer": "6.0.3",
@ -40,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#a6be0425573917c2e82b66d28968c1a4d444cb96",
"stremio-translations": "github:Stremio/stremio-translations#0e7fbd8522148f5727ac6adee3b2eb96132c10ac",
"url": "0.11.4",
"use-long-press": "^3.2.0"
},
@ -49,9 +50,11 @@
"@babel/preset-env": "7.26.0",
"@babel/preset-react": "7.26.3",
"@eslint/js": "^9.16.0",
"@stylistic/eslint-plugin": "^2.11.0",
"@stylistic/eslint-plugin-jsx": "^2.11.0",
"@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",
"babel-loader": "9.2.1",
@ -70,6 +73,7 @@
"mini-css-extract-plugin": "2.9.2",
"postcss-loader": "8.1.1",
"readdirp": "4.0.2",
"recast": "0.23.11",
"terser-webpack-plugin": "5.3.10",
"thread-loader": "^4.0.4",
"ts-loader": "^9.5.1",

11030
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 947 KiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

View file

@ -6,11 +6,12 @@ const { useTranslation } = require('react-i18next');
const { Router } = require('stremio-router');
const { Core, Shell, Chromecast, DragAndDrop, KeyboardShortcuts, ServicesProvider } = require('stremio/services');
const { NotFound } = require('stremio/routes');
const { FileDropProvider, PlatformProvider, ToastProvider, TooltipProvider, CONSTANTS, withCoreSuspender, useShell } = require('stremio/common');
const { FileDropProvider, PlatformProvider, ToastProvider, TooltipProvider, ShortcutsProvider, CONSTANTS, withCoreSuspender, useShell, useBinaryState } = require('stremio/common');
const ServicesToaster = require('./ServicesToaster');
const DeepLinkHandler = require('./DeepLinkHandler');
const SearchParamsHandler = require('./SearchParamsHandler');
const { default: UpdaterBanner } = require('./UpdaterBanner');
const { default: ShortcutsModal } = require('./ShortcutsModal');
const ErrorDialog = require('./ErrorDialog');
const withProtectedRoutes = require('./withProtectedRoutes');
const routerViewsConfig = require('./routerViewsConfig');
@ -38,6 +39,14 @@ const App = () => {
};
}, []);
const [initialized, setInitialized] = React.useState(false);
const [shortcutModalOpen,, closeShortcutsModal, toggleShortcutModal] = useBinaryState(false);
const onShortcut = React.useCallback((name) => {
if (name === 'shortcuts') {
toggleShortcutModal();
}
}, [toggleShortcutModal]);
React.useEffect(() => {
let prevPath = window.location.hash.slice(1);
const onLocationHashChange = () => {
@ -102,12 +111,18 @@ const App = () => {
// Handle shell events
React.useEffect(() => {
const onOpenMedia = (data) => {
if (data.startsWith('stremio:///')) return;
if (data.startsWith('stremio://')) {
const transportUrl = data.replace('stremio://', 'https://');
if (URL.canParse(transportUrl)) {
window.location.href = `#/addons?addon=${encodeURIComponent(transportUrl)}`;
try {
const { protocol, hostname, pathname, searchParams } = new URL(data);
if (protocol === CONSTANTS.PROTOCOL) {
if (hostname.length) {
const transportUrl = `https://${hostname}${pathname}`;
window.location.href = `#/addons?addon=${encodeURIComponent(transportUrl)}`;
} else {
window.location.href = `#${pathname}?${searchParams.toString()}`;
}
}
} catch (e) {
console.error('Failed to open media:', e);
}
};
@ -153,7 +168,8 @@ const App = () => {
services.core.transport.dispatch({
action: 'Ctx',
args: {
action: 'PullUserFromAPI'
action: 'PullUserFromAPI',
args: {}
}
});
services.core.transport.dispatch({
@ -197,15 +213,20 @@ const App = () => {
<ToastProvider className={styles['toasts-container']}>
<TooltipProvider className={styles['tooltip-container']}>
<FileDropProvider className={styles['file-drop-container']}>
<ServicesToaster />
<DeepLinkHandler />
<SearchParamsHandler />
<UpdaterBanner className={styles['updater-banner-container']} />
<RouterWithProtectedRoutes
className={styles['router']}
viewsConfig={routerViewsConfig}
onPathNotMatch={onPathNotMatch}
/>
<ShortcutsProvider onShortcut={onShortcut}>
{
shortcutModalOpen && <ShortcutsModal onClose={closeShortcutsModal}/>
}
<ServicesToaster />
<DeepLinkHandler />
<SearchParamsHandler />
<UpdaterBanner className={styles['updater-banner-container']} />
<RouterWithProtectedRoutes
className={styles['router']}
viewsConfig={routerViewsConfig}
onPathNotMatch={onPathNotMatch}
/>
</ShortcutsProvider>
</FileDropProvider>
</TooltipProvider>
</ToastProvider>

View file

@ -36,7 +36,13 @@ const SearchParamsHandler = () => {
},
},
});
core.transport.dispatch({
action: 'Ctx',
args: {
action: 'AddServerUrl',
args: streamingServerUrl,
},
});
toast.show({
type: 'success',
title: `Using streaming server at ${streamingServerUrl}`,

View file

@ -0,0 +1,59 @@
// Copyright (C) 2017-2023 Smart code 203358507
import React, { useEffect } from 'react';
import { createPortal } from 'react-dom';
import { useTranslation } from 'react-i18next';
import Icon from '@stremio/stremio-icons/react';
import { useShortcuts } from 'stremio/common';
import { Button, ShortcutsGroup } from 'stremio/components';
import styles from './styles.less';
type Props = {
onClose: () => void,
};
const ShortcutsModal = ({ onClose }: Props) => {
const { t } = useTranslation();
const { grouped } = useShortcuts();
useEffect(() => {
const onKeyDown = ({ key }: KeyboardEvent) => {
key === 'Escape' && onClose();
};
document.addEventListener('keydown', onKeyDown);
return () => document.removeEventListener('keydown', onKeyDown);
}, []);
return createPortal((
<div className={styles['shortcuts-modal']}>
<div className={styles['backdrop']} onClick={onClose} />
<div className={styles['container']}>
<div className={styles['header']}>
<div className={styles['title']}>
{t('SETTINGS_NAV_SHORTCUTS')}
</div>
<Button className={styles['close-button']} title={t('BUTTON_CLOSE')} onClick={onClose}>
<Icon className={styles['icon']} name={'close'} />
</Button>
</div>
<div className={styles['content']}>
{
grouped.map(({ name, label, shortcuts }) => (
<ShortcutsGroup
key={name}
label={label}
shortcuts={shortcuts}
/>
))
}
</div>
</div>
</div>
), document.body);
};
export default ShortcutsModal;

View file

@ -0,0 +1,2 @@
import ShortcutsModal from './ShortcutsModal';
export default ShortcutsModal;

View file

@ -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;
}
}
}

View file

@ -35,7 +35,7 @@
@top-overlay-size: 5.25rem;
@bottom-overlay-size: 0rem;
@overlap-size: 3rem;
@transparency-grandient-pad: 6rem;
@transparency-gradient-pad: 6rem;
:root {
--landscape-shape-ratio: 0.5625;
@ -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;
@ -69,7 +69,7 @@
--top-overlay-size: @top-overlay-size;
--bottom-overlay-size: @bottom-overlay-size;
--overlap-size: @overlap-size;
--transparency-grandient-pad: @transparency-grandient-pad;
--transparency-gradient-pad: @transparency-gradient-pad;
--safe-area-inset-top: @safe-area-inset-top;
--safe-area-inset-right: @safe-area-inset-right;
--safe-area-inset-bottom: @safe-area-inset-bottom;
@ -151,14 +151,13 @@ svg {
html {
width: @html-width;
height: @html-height;
font-family: 'PlusJakartaSans', 'sans-serif';
font-family: 'PlusJakartaSans', 'Arial', 'Helvetica', 'sans-serif';
overflow: auto;
overscroll-behavior: none;
user-select: none;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
@media (display-mode: standalone) {
width: @html-standalone-width;
height: @html-standalone-height;
@ -168,6 +167,7 @@ html {
width: 100%;
height: 100%;
background: linear-gradient(41deg, var(--primary-background-color) 0%, var(--secondary-background-color) 100%);
-webkit-font-smoothing: antialiased;
:global(#app) {
position: relative;

View file

@ -106,6 +106,8 @@ const EXTERNAL_PLAYERS = [
const WHITELISTED_HOSTS = ['stremio.com', 'strem.io', 'stremio.zendesk.com', 'google.com', 'youtube.com', 'twitch.tv', 'twitter.com', 'x.com', 'netflix.com', 'adex.network', 'amazon.com', 'forms.gle'];
const PROTOCOL = 'stremio:';
module.exports = {
CHROMECAST_RECEIVER_APP_ID,
DEFAULT_STREAMING_SERVER_URL,
@ -127,4 +129,5 @@ module.exports = {
SUPPORTED_LOCAL_SUBTITLES,
EXTERNAL_PLAYERS,
WHITELISTED_HOSTS,
PROTOCOL,
};

View file

@ -42,7 +42,7 @@ const FileDropProvider = ({ className, children }: Props) => {
.then((buffer) => {
listeners
.filter(([type]) => file.type ? type === file.type : isFileType(buffer, type))
.forEach(([, listerner]) => listerner(file.name, buffer));
.forEach(([, listener]) => listener(file.name, buffer));
});
}

View file

@ -1,6 +1,5 @@
import React, { createContext, useContext } from 'react';
import { WHITELISTED_HOSTS } from 'stremio/common/CONSTANTS';
import useShell from 'stremio/common/useShell';
import { name, isMobile } from './device';
interface PlatformContext {
@ -16,19 +15,13 @@ type Props = {
};
const PlatformProvider = ({ children }: Props) => {
const shell = useShell();
const openExternal = (url: string) => {
try {
const { hostname } = new URL(url);
const isWhitelisted = WHITELISTED_HOSTS.some((host: string) => hostname.endsWith(host));
const finalUrl = !isWhitelisted ? `https://www.stremio.com/warning#${encodeURIComponent(url)}` : url;
if (shell.active) {
shell.send('open-external', finalUrl);
} else {
window.open(finalUrl, '_blank');
}
window.open(finalUrl, '_blank');
} catch (e) {
console.error('Failed to parse external url:', e);
}

View file

@ -0,0 +1,67 @@
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 = (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);
type Props = {
children: JSX.Element,
onShortcut: (name: ShortcutName) => void,
};
const ShortcutsProvider = ({ children, onShortcut }: Props) => {
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(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);
}, [onKeyDown]);
return (
<ShortcutsContext.Provider value={{ grouped: shortcuts, on, off }}>
{children}
</ShortcutsContext.Provider>
);
};
const useShortcuts = () => {
return useContext(ShortcutsContext);
};
export {
ShortcutsProvider,
useShortcuts,
};

View file

@ -0,0 +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

@ -0,0 +1,104 @@
[
{
"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": "mute",
"label": "SETTINGS_SHORTCUT_MUTE",
"combos": [["M"]]
},
{
"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"]]
},
{
"name": "speedMenu",
"label": "SETTINGS_SHORTCUT_MENU_PLAYBACK_SPEED",
"combos": [["R"]]
},
{
"name": "statisticsMenu",
"label": "SETTINGS_SHORTCUT_MENU_STATISTICS",
"combos": [["D"]]
}
]
}
]

11
src/common/Shortcuts/types.d.ts vendored Normal file
View file

@ -0,0 +1,11 @@
type Shortcut = {
name: string,
label: string,
combos: string[][],
};
type ShortcutGroup = {
name: string,
label: string,
shortcuts: Shortcut[],
};

View file

@ -1,6 +1,7 @@
// Copyright (C) 2017-2023 Smart code 203358507
const React = require('react');
const { useTranslation } = require('react-i18next');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const { default: Icon } = require('@stremio/stremio-icons/react');
@ -8,6 +9,7 @@ const { Button } = require('stremio/components');
const styles = require('./styles');
const ToastItem = ({ title, message, dataset, onSelect, onClose, ...props }) => {
const { t } = useTranslation();
const type = React.useMemo(() => {
return ['success', 'alert', 'info', 'error'].includes(props.type) ?
props.type
@ -74,7 +76,7 @@ const ToastItem = ({ title, message, dataset, onSelect, onClose, ...props }) =>
null
}
</div>
<Button className={styles['close-button-container']} title={'Close'} tabIndex={-1} onClick={closeButtonOnClick}>
<Button className={styles['close-button-container']} title={t('BUTTON_CLOSE')} tabIndex={-1} onClick={closeButtonOnClick}>
<Icon className={styles['icon']} name={'close'} />
</Button>
</Button>

View file

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

11
src/common/Toast/useToast.d.ts vendored Normal file
View file

@ -0,0 +1,11 @@
type ToastOptions = {
type: string,
title: string,
timeout: number,
};
declare const useToast: () => {
show: (options: ToastOptions) => void,
};
export = useToast;

View file

@ -82,6 +82,19 @@
transform: translateY(100%);
}
.fade-enter {
opacity: 0;
}
.fade-active {
opacity: 1;
transition: opacity 0.3s cubic-bezier(0.32, 0, 0.67, 0);
}
.fade-exit {
opacity: 0;
}
@keyframes fade-in-no-motion {
0% {
opacity: 0;

View file

@ -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, onShortcut } = require('./Shortcuts');
const comparatorWithPriorities = require('./comparatorWithPriorities');
const CONSTANTS = require('./CONSTANTS');
const { withCoreSuspender, useCoreSuspender } = require('./CoreSuspender');
@ -15,6 +16,7 @@ const routesRegexp = require('./routesRegexp');
const useAnimationFrame = require('./useAnimationFrame');
const useBinaryState = require('./useBinaryState');
const { default: useFullscreen } = require('./useFullscreen');
const { default: useInterval } = require('./useInterval');
const useLiveRef = require('./useLiveRef');
const useModelState = require('./useModelState');
const useNotifications = require('./useNotifications');
@ -23,15 +25,20 @@ const useProfile = require('./useProfile');
const { default: useSettings } = require('./useSettings');
const { default: useShell } = require('./useShell');
const useStreamingServer = require('./useStreamingServer');
const { default: useTimeout } = require('./useTimeout');
const useTorrent = require('./useTorrent');
const useTranslate = require('./useTranslate');
const { default: useOrientation } = require('./useOrientation');
const { default: useLanguageSorting } = require('./useLanguageSorting');
module.exports = {
FileDropProvider,
onFileDrop,
PlatformProvider,
usePlatform,
ShortcutsProvider,
useShortcuts,
onShortcut,
ToastProvider,
useToast,
TooltipProvider,
@ -48,6 +55,7 @@ module.exports = {
useAnimationFrame,
useBinaryState,
useFullscreen,
useInterval,
useLiveRef,
useModelState,
useNotifications,
@ -56,7 +64,9 @@ module.exports = {
useSettings,
useShell,
useStreamingServer,
useTimeout,
useTorrent,
useTranslate,
useOrientation,
useLanguageSorting,
};

View file

@ -51,6 +51,10 @@
"name": "فارسی",
"codes": ["fa-IR", "fas"]
},
{
"name": "Suomi",
"codes": ["fi-FI", "fin"]
},
{
"name": "Français",
"codes": ["fr-FR", "fre"]
@ -119,13 +123,17 @@
"name": "português",
"codes": ["pt-PT", "por"]
},
{
"name": "Română",
"codes": ["ro-RO", "ron"]
},
{
"name": "русский язык",
"codes": ["ru-RU", "rus"]
},
{
"name": "Svenska",
"codes": ["sv-SE", "swe"]
"name": "Slovenčina",
"codes": ["sk-SK", "slk"]
},
{
"name": "slovenski jezik",
@ -135,6 +143,10 @@
"name": "српски језик",
"codes": ["sr-RS", "srp"]
},
{
"name": "Svenska",
"codes": ["sv-SE", "swe"]
},
{
"name": "తెలుగు",
"codes": ["te-IN", "tel"]

View file

@ -6,7 +6,7 @@ const routesRegexp = {
urlParamsNames: []
},
board: {
regexp: /^\/?$/,
regexp: /^\/?(?:board)?$/,
urlParamsNames: []
},
discover: {

View file

@ -10,11 +10,15 @@ const useFullscreen = () => {
const [fullscreen, setFullscreen] = useState(false);
const requestFullscreen = useCallback(() => {
const requestFullscreen = useCallback(async () => {
if (shell.active) {
shell.send('win-set-visibility', { fullscreen: true });
} else {
document.documentElement.requestFullscreen();
try {
await document.documentElement.requestFullscreen();
} catch (err) {
console.error('Error enabling fullscreen', err);
}
}
}, []);
@ -22,7 +26,9 @@ const useFullscreen = () => {
if (shell.active) {
shell.send('win-set-visibility', { fullscreen: false });
} else {
document.exitFullscreen();
if (document.fullscreenElement === document.documentElement) {
document.exitFullscreen();
}
}
}, []);
@ -40,10 +46,24 @@ const useFullscreen = () => {
};
const onKeyDown = (event: KeyboardEvent) => {
const activeElement = document.activeElement as HTMLElement;
const inputFocused =
activeElement &&
(activeElement.tagName === 'INPUT' ||
activeElement.tagName === 'TEXTAREA' ||
activeElement.tagName === 'SELECT' ||
activeElement.isContentEditable);
if (event.code === 'Escape' && settings.escExitFullscreen) {
exitFullscreen();
}
if (event.code === 'KeyF' && !inputFocused) {
toggleFullscreen();
}
if (event.code === 'F11' && shell.active) {
toggleFullscreen();
}
@ -58,7 +78,7 @@ const useFullscreen = () => {
document.removeEventListener('keydown', onKeyDown);
document.removeEventListener('fullscreenchange', onFullscreenChange);
};
}, [settings.escExitFullscreen]);
}, [settings.escExitFullscreen, toggleFullscreen]);
return [fullscreen, requestFullscreen, exitFullscreen, toggleFullscreen];
};

26
src/common/useInterval.ts Normal file
View file

@ -0,0 +1,26 @@
import { useEffect, useRef } from 'react';
const useInterval = (duration: number) => {
const interval = useRef<NodeJS.Timer | null>(null);
const start = (callback: () => void) => {
cancel();
interval.current = setInterval(callback, duration);
};
const cancel = () => {
interval.current && clearInterval(interval.current);
interval.current = null;
};
useEffect(() => {
return () => cancel();
}, []);
return {
start,
cancel,
};
};
export default useInterval;

View file

@ -0,0 +1,38 @@
import { useMemo } from 'react';
import interfaceLanguages from 'stremio/common/interfaceLanguages.json';
const useLanguageSorting = (options: MultiselectMenuOption[]) => {
const userLangCode = useMemo(() => {
const lang = interfaceLanguages.find((l) => l.codes.includes(navigator.language || 'en-US'));
if (lang) {
const threeLetter = lang.codes[1] || 'eng';
const fullLocale = navigator.language || 'en-US';
return [threeLetter, fullLocale];
}
return ['eng'];
}, []);
const isLanguageDropdown = useMemo(() => {
return options?.some((opt) => interfaceLanguages.some((l) => l.name === opt.label));
}, [options]);
const sortedOptions = useMemo(() => {
const matchingIndex = options.findIndex((opt) => {
const lang = interfaceLanguages.find((l) => l.name === opt.label);
return userLangCode.some((code) => lang?.codes.includes(code));
});
if (matchingIndex === -1) {
return [...options].sort((a, b) => a.label.localeCompare(b.label));
}
const matchingOption = options[matchingIndex];
const otherOptions = options.filter((_, idx) => idx !== matchingIndex).sort((a, b) => a.label.localeCompare(b.label));
return [matchingOption, ...otherOptions];
}, [options, userLangCode, isLanguageDropdown]);
return { userLangCode, isLanguageDropdown, sortedOptions };
};
export default useLanguageSorting;

View file

@ -1,2 +1,2 @@
declare const useNotifcations: () => Notifications;
export = useNotifcations;
declare const useNotifications: () => Notifications;
export = useNotifications;

26
src/common/useTimeout.ts Normal file
View file

@ -0,0 +1,26 @@
import { useEffect, useRef } from 'react';
const useTimeout = (duration: number) => {
const timeout = useRef<NodeJS.Timeout | null>(null);
const start = (callback: () => void) => {
cancel();
timeout.current = setTimeout(callback, duration);
};
const cancel = () => {
timeout.current && clearTimeout(timeout.current);
timeout.current = null;
};
useEffect(() => {
return () => cancel();
}, []);
return {
start,
cancel,
};
};
export default useTimeout;

View file

@ -1,6 +1,7 @@
// Copyright (C) 2017-2023 Smart code 203358507
const React = require('react');
const { useTranslation } = require('react-i18next');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const { default: Icon } = require('@stremio/stremio-icons/react');
@ -8,6 +9,7 @@ const { default: Image } = require('stremio/components/Image');
const styles = require('./styles');
const AddonDetails = ({ className, id, name, version, logo, description, types, transportUrl, official }) => {
const { t } = useTranslation();
const renderLogoFallback = React.useCallback(() => (
<Icon className={styles['icon']} name={'addons'} />
), []);
@ -24,7 +26,7 @@ const AddonDetails = ({ className, id, name, version, logo, description, types,
<span className={styles['name']}>{typeof name === 'string' && name.length > 0 ? name : id}</span>
{
typeof version === 'string' && version.length > 0 ?
<span className={styles['version']}>v. {version}</span>
<span className={styles['version']}>{t('ADDON_VERSION_SHORT', {version})}</span>
:
null
}
@ -41,7 +43,7 @@ const AddonDetails = ({ className, id, name, version, logo, description, types,
{
typeof transportUrl === 'string' && transportUrl.length > 0 ?
<div className={styles['section-container']}>
<span className={styles['section-header']}>URL: </span>
<span className={styles['section-header']}>{`${t('URL')}:`}</span>
<span className={classnames(styles['section-label'], styles['transport-url-label'])}>{transportUrl}</span>
</div>
:
@ -50,7 +52,7 @@ const AddonDetails = ({ className, id, name, version, logo, description, types,
{
Array.isArray(types) && types.length > 0 ?
<div className={styles['section-container']}>
<span className={styles['section-header']}>Supported types: </span>
<span className={styles['section-header']}>{`${t('ADDON_SUPPORTED_TYPES')}:`} </span>
<span className={styles['section-label']}>
{
types.length === 1 ?
@ -66,7 +68,7 @@ const AddonDetails = ({ className, id, name, version, logo, description, types,
{
!official ?
<div className={styles['section-container']}>
<div className={classnames(styles['section-label'], styles['disclaimer-label'])}>Using third-party add-ons will always be subject to your responsibility and the governing law of the jurisdiction you are located.</div>
<div className={classnames(styles['section-label'], styles['disclaimer-label'])}>{t('ADDON_DISCLAIMER')}</div>
</div>
:
null

View file

@ -1,6 +1,7 @@
// Copyright (C) 2017-2023 Smart code 203358507
const React = require('react');
const { useTranslation } = require('react-i18next');
const PropTypes = require('prop-types');
const ModalDialog = require('stremio/components/ModalDialog');
const { withCoreSuspender } = require('stremio/common/CoreSuspender');
@ -43,13 +44,14 @@ function withRemoteAndLocalAddon(AddonDetails) {
}
const AddonDetailsModal = ({ transportUrl, onCloseRequest }) => {
const { t } = useTranslation();
const { core } = useServices();
const platform = usePlatform();
const addonDetails = useAddonDetails(transportUrl);
const modalButtons = React.useMemo(() => {
const cancelButton = {
className: styles['cancel-button'],
label: 'Cancel',
label: t('BUTTON_CANCEL'),
props: {
onClick: (event) => {
if (typeof onCloseRequest === 'function') {
@ -67,7 +69,7 @@ const AddonDetailsModal = ({ transportUrl, onCloseRequest }) => {
addonDetails.remoteAddon.content.content.manifest.behaviorHints.configurable ?
{
className: styles['configure-button'],
label: 'Configure',
label: t('ADDON_CONFIGURE'),
props: {
onClick: (event) => {
platform.openExternal(transportUrl.replace('manifest.json', 'configure'));
@ -86,7 +88,7 @@ const AddonDetailsModal = ({ transportUrl, onCloseRequest }) => {
const toggleButton = addonDetails.localAddon !== null ?
{
className: styles['uninstall-button'],
label: 'Uninstall',
label: t('ADDON_UNINSTALL'),
props: {
onClick: (event) => {
core.transport.dispatch({
@ -113,7 +115,7 @@ const AddonDetailsModal = ({ transportUrl, onCloseRequest }) => {
{
className: styles['install-button'],
label: 'Install',
label: t('ADDON_INSTALL'),
props: {
onClick: (event) => {
core.transport.dispatch({
@ -141,21 +143,21 @@ const AddonDetailsModal = ({ transportUrl, onCloseRequest }) => {
return addonDetails.remoteAddon?.content.type === 'Ready' ? addonDetails.remoteAddon.content.content.manifest.background : null;
}, [addonDetails.remoteAddon]);
return (
<ModalDialog className={styles['addon-details-modal-container']} title={'Stremio addon'} buttons={modalButtons} background={modalBackground} onCloseRequest={onCloseRequest}>
<ModalDialog className={styles['addon-details-modal-container']} title={t('STREMIO_COMMUNITY_ADDON')} buttons={modalButtons} background={modalBackground} onCloseRequest={onCloseRequest}>
{
addonDetails.selected === null ?
<div className={styles['addon-details-message-container']}>
Loading addon manifest
{t('ADDON_LOADING_MANIFEST')}
</div>
:
addonDetails.remoteAddon === null || addonDetails.remoteAddon.content.type === 'Loading' ?
<div className={styles['addon-details-message-container']}>
Loading addon manifest from {addonDetails.selected.transportUrl}
{t('ADDON_LOADING_MANIFEST_FROM', { origin: addonDetails.selected.transportUrl})}
</div>
:
addonDetails.remoteAddon.content.type === 'Err' && addonDetails.localAddon === null ?
<div className={styles['addon-details-message-container']}>
Failed to get addon manifest from {addonDetails.selected.transportUrl}
{t('ADDON_LOADING_MANIFEST_FAILED', {origin: addonDetails.selected.transportUrl})}
<div>{addonDetails.remoteAddon.content.content.message}</div>
</div>
:
@ -174,17 +176,18 @@ AddonDetailsModal.propTypes = {
onCloseRequest: PropTypes.func
};
const AddonDetailsModalFallback = ({ onCloseRequest }) => (
<ModalDialog
const AddonDetailsModalFallback = ({ onCloseRequest }) => {
const { t } = useTranslation();
return <ModalDialog
className={styles['addon-details-modal-container']}
title={'Stremio addon'}
title={t('STREMIO_COMMUNITY_ADDON')}
onCloseRequest={onCloseRequest}
>
<div className={styles['addon-details-message-container']}>
Loading addon manifest
{t('ADDON_LOADING_MANIFEST')}
</div>
</ModalDialog>
);
</ModalDialog>;
};
AddonDetailsModalFallback.propTypes = AddonDetailsModal.propTypes;

View file

@ -86,7 +86,7 @@
}
}
@media only screen and (min-width: @small) and (orientation: portait) {
@media only screen and (min-width: @small) and (orientation: portrait) {
.bottom-sheet {
display: none;
}

View file

@ -7,6 +7,7 @@ import styles from './Button.less';
type Props = {
className?: string,
style?: object,
href?: string,
target?: string
title?: string,
@ -15,6 +16,8 @@ type Props = {
children: React.ReactNode,
onKeyDown?: (event: React.KeyboardEvent) => void,
onMouseDown?: (event: React.MouseEvent) => void,
onMouseUp?: (event: React.MouseEvent) => void,
onMouseLeave?: (event: React.MouseEvent) => void,
onLongPress?: () => void,
onClick?: (event: React.MouseEvent<HTMLDivElement>) => void,
onDoubleClick?: () => void,

View file

@ -23,6 +23,7 @@
.link {
font-size: 0.9rem;
color: var(--primary-accent-color);
margin-left: 0.5rem;
&:hover {
text-decoration: underline;
@ -69,7 +70,7 @@
}
&.error {
border-color: var(--color-trakt);
border-color: var(--danger-accent-color);
}
&.checked {

View file

@ -80,7 +80,6 @@ const Checkbox = React.forwardRef<HTMLInputElement, Props>(({ name, disabled, cl
</div>
<div>
<span>{label}</span>
{' '}
{
href && link ?
<Button className={styles['link']} href={href} target={'_blank'} tabIndex={-1}>

View file

@ -1,75 +1,85 @@
// Copyright (C) 2017-2023 Smart code 203358507
const React = require('react');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const AColorPicker = require('a-color-picker');
const { useTranslation } = require('react-i18next');
const { Button } = require('stremio/components');
const ModalDialog = require('stremio/components/ModalDialog');
const useBinaryState = require('stremio/common/useBinaryState');
const ColorPicker = require('./ColorPicker');
const styles = require('./styles');
import React, { useCallback, useLayoutEffect, useMemo, useState } from 'react';
import classnames from 'classnames';
import * as AColorPicker from 'a-color-picker';
import { useTranslation } from 'react-i18next';
import { Button } from 'stremio/components';
import ModalDialog from 'stremio/components/ModalDialog';
import useBinaryState from 'stremio/common/useBinaryState';
import ColorPicker from './ColorPicker';
import styles from './ColorInput.less';
const parseColor = (value) => {
const parseColor = (value: string) => {
const color = AColorPicker.parseColor(value, 'hexcss4');
return typeof color === 'string' ? color : '#ffffffff';
};
const ColorInput = ({ className, value, dataset, onChange, ...props }) => {
type Props = {
className: string,
value: string,
onChange?: (value: string) => void,
onClick?: (event: React.MouseEvent) => void,
};
const ColorInput = ({ className, value, onChange, ...props }: Props) => {
const { t } = useTranslation();
const [modalOpen, openModal, closeModal] = useBinaryState(false);
const [tempValue, setTempValue] = React.useState(() => {
const [tempValue, setTempValue] = useState(() => {
return parseColor(value);
});
const labelButtonStyle = React.useMemo(() => ({
const labelButtonStyle = useMemo(() => ({
backgroundColor: value
}), [value]);
const isTransparent = React.useMemo(() => {
const isTransparent = useMemo(() => {
return parseColor(value).endsWith('00');
}, [value]);
const labelButtonOnClick = React.useCallback((event) => {
const labelButtonOnClick = useCallback((event: React.MouseEvent) => {
if (typeof props.onClick === 'function') {
props.onClick(event);
}
// @ts-expect-error: Property 'openModalPrevented' does not exist on type 'MouseEvent'.
if (!event.nativeEvent.openModalPrevented) {
openModal();
}
}, [props.onClick]);
const modalDialogOnClick = React.useCallback((event) => {
const modalDialogOnClick = useCallback((event: React.MouseEvent) => {
// @ts-expect-error: Property 'openModalPrevented' does not exist on type 'MouseEvent'.
event.nativeEvent.openModalPrevented = true;
}, []);
const modalButtons = React.useMemo(() => {
const selectButtonOnClick = (event) => {
const modalButtons = useMemo(() => {
const selectButtonOnClick = () => {
if (typeof onChange === 'function') {
onChange({
type: 'change',
value: tempValue,
dataset: dataset,
reactEvent: event,
nativeEvent: event.nativeEvent
});
onChange(tempValue);
}
closeModal();
};
return [
{
label: 'Select',
label: t('SELECT'),
props: {
'data-autofocus': true,
onClick: selectButtonOnClick
}
}
];
}, [tempValue, dataset, onChange]);
const colorPickerOnInput = React.useCallback((event) => {
setTempValue(parseColor(event.value));
}, [tempValue, onChange]);
const colorPickerOnInput = useCallback((color: string) => {
setTempValue(parseColor(color));
}, []);
React.useLayoutEffect(() => {
useLayoutEffect(() => {
setTempValue(parseColor(value));
}, [value, modalOpen]);
return (
<Button title={isTransparent ? t('BUTTON_COLOR_TRANSPARENT') : value} {...props} style={labelButtonStyle} className={classnames(className, styles['color-input-container'])} onClick={labelButtonOnClick}>
{
@ -82,7 +92,7 @@ const ColorInput = ({ className, value, dataset, onChange, ...props }) => {
}
{
modalOpen ?
<ModalDialog title={'Choose a color:'} buttons={modalButtons} onCloseRequest={closeModal} onClick={modalDialogOnClick}>
<ModalDialog title={t('CHOOSE_COLOR')} buttons={modalButtons} onCloseRequest={closeModal} onClick={modalDialogOnClick}>
<ColorPicker className={styles['color-picker-container']} value={tempValue} onInput={colorPickerOnInput} />
</ModalDialog>
:
@ -92,12 +102,4 @@ const ColorInput = ({ className, value, dataset, onChange, ...props }) => {
);
};
ColorInput.propTypes = {
className: PropTypes.string,
value: PropTypes.string,
dataset: PropTypes.object,
onChange: PropTypes.func,
onClick: PropTypes.func
};
module.exports = ColorInput;
export default ColorInput;

View file

@ -21,7 +21,7 @@ const ColorPicker = ({ className, value, onInput }) => {
showRGB: false,
showAlpha: true
});
const pickerClipboard = pickerElementRef.current.querySelector('.a-color-picker-clipbaord');
const pickerClipboard = pickerElementRef.current.querySelector('.a-color-picker-clipboard');
if (pickerClipboard instanceof HTMLElement) {
pickerClipboard.tabIndex = -1;
}
@ -29,10 +29,7 @@ const ColorPicker = ({ className, value, onInput }) => {
React.useLayoutEffect(() => {
if (typeof onInput === 'function') {
pickerRef.current.on('change', (picker, value) => {
onInput({
type: 'input',
value: parseColor(value)
});
onInput(parseColor(value));
});
}
return () => {

View file

@ -16,7 +16,7 @@
box-shadow: 0 0 .2rem var(--color-surfacedark);
}
:global(.a-color-picker-clipbaord) {
:global(.a-color-picker-clipboard) {
pointer-events: none;
}
}

View file

@ -1,6 +0,0 @@
// Copyright (C) 2017-2023 Smart code 203358507
const ColorInput = require('./ColorInput');
module.exports = ColorInput;

View file

@ -0,0 +1,6 @@
// Copyright (C) 2017-2023 Smart code 203358507
import ColorInput from './ColorInput';
export default ColorInput;

View file

@ -1,5 +1,5 @@
// Copyright (C) 2017-2023 Smart code 203358507
const ContineWatchingItem = require('./ContinueWatchingItem');
const ContinueWatchingItem = require('./ContinueWatchingItem');
module.exports = ContineWatchingItem;
module.exports = ContinueWatchingItem;

View file

@ -65,8 +65,16 @@
padding: 0 1rem;
.icon-container {
height: 2rem;
width: 2rem;
.icon {
width: 2rem;
height: 2rem;
}
}
.label-container {
display: none;
}
}
}

View file

@ -3,18 +3,18 @@
const React = require('react');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const { useTranslation } = require('react-i18next');
const { Button } = require('stremio/components');
const useTranslate = require('stremio/common/useTranslate');
const styles = require('./styles');
const MetaLinks = ({ className, label, links }) => {
const { t } = useTranslation();
const { string, stringWithPrefix } = useTranslate();
return (
<div className={classnames(className, styles['meta-links-container'])}>
{
typeof label === 'string' && label.length > 0 ?
<div className={styles['label-container']}>
{t(`LINKS_${label.toUpperCase()}`)}
{ stringWithPrefix(label.toUpperCase(), 'LINKS') }
</div>
:
null
@ -24,7 +24,7 @@ const MetaLinks = ({ className, label, links }) => {
<div className={styles['links-container']}>
{links.map(({ label, href }, index) => (
<Button key={index} className={styles['link-container']} title={label} href={href}>
{ t(label) }
{ string(label) }
</Button>
))}
</div>

View file

@ -17,6 +17,7 @@ const ActionButton = require('./ActionButton');
const MetaLinks = require('./MetaLinks');
const MetaPreviewPlaceholder = require('./MetaPreviewPlaceholder');
const styles = require('./styles');
const { Ratings } = require('./Ratings');
const ALLOWED_LINK_REDIRECTS = [
routesRegexp.search.regexp,
@ -24,7 +25,7 @@ const ALLOWED_LINK_REDIRECTS = [
routesRegexp.metadetails.regexp
];
const MetaPreview = ({ className, compact, name, logo, background, runtime, releaseInfo, released, description, deepLinks, links, trailerStreams, inLibrary, toggleInLibrary }) => {
const MetaPreview = React.forwardRef(({ className, compact, name, logo, background, runtime, releaseInfo, released, description, deepLinks, links, trailerStreams, inLibrary, toggleInLibrary, ratingInfo }, ref) => {
const { t } = useTranslation();
const [shareModalOpen, openShareModal, closeShareModal] = useBinaryState(false);
const linksGroups = React.useMemo(() => {
@ -98,7 +99,7 @@ const MetaPreview = ({ className, compact, name, logo, background, runtime, rele
<div className={styles['logo-placeholder']}>{name}</div>
), [name]);
return (
<div className={classnames(className, styles['meta-preview-container'], { [styles['compact']]: compact })}>
<div className={classnames(className, styles['meta-preview-container'], { [styles['compact']]: compact })} ref={ref}>
{
typeof background === 'string' && background.length > 0 ?
<div className={styles['background-image-layer']}>
@ -232,6 +233,15 @@ const MetaPreview = ({ className, compact, name, logo, background, runtime, rele
:
null
}
{
!compact && ratingInfo !== null ?
<Ratings
ratingInfo={ratingInfo}
className={styles['ratings']}
/>
:
null
}
{
linksGroups.has(CONSTANTS.SHARE_LINK_CATEGORY) && !compact ?
<React.Fragment>
@ -261,7 +271,7 @@ const MetaPreview = ({ className, compact, name, logo, background, runtime, rele
</div>
</div>
);
};
});
MetaPreview.Placeholder = MetaPreviewPlaceholder;
@ -287,7 +297,8 @@ MetaPreview.propTypes = {
})),
trailerStreams: PropTypes.array,
inLibrary: PropTypes.bool,
toggleInLibrary: PropTypes.func
toggleInLibrary: PropTypes.func,
ratingInfo: PropTypes.object,
};
module.exports = MetaPreview;

View file

@ -0,0 +1,62 @@
// Copyright (C) 2017-2025 Smart code 203358507
@import (reference) '~stremio/common/screen-sizes.less';
@height: 4rem;
@width: 4rem;
@height-mobile: 3rem;
@width-mobile: 3rem;
.ratings-container {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
background-color: var(--overlay-color);
border-radius: 2rem;
height: @height;
width: fit-content;
backdrop-filter: blur(5px);
.icon-container {
display: flex;
justify-content: center;
align-items: center;
height: @height;
width: @width;
padding: 0 1rem;
cursor: pointer;
.icon {
width: calc(@width / 2);
height: calc(@height / 2);
color: var(--primary-foreground-color);
opacity: 0.7;
&:hover {
opacity: 1;
}
}
&.disabled {
pointer-events: none;
}
}
}
@media @phone-landscape {
.ratings-container {
height: @height-mobile;
.icon-container {
height: @height-mobile;
width: @width-mobile;
.icon {
width: 1.75rem;
height: 1.75rem;
}
}
}
}

View file

@ -0,0 +1,31 @@
// Copyright (C) 2017-2025 Smart code 203358507
import React, { useMemo } from 'react';
import useRating from './useRating';
import styles from './Ratings.less';
import Icon from '@stremio/stremio-icons/react';
import classNames from 'classnames';
type Props = {
metaId?: string;
ratingInfo?: Loadable<RatingInfo>;
className?: string;
};
const Ratings = ({ ratingInfo, className }: Props) => {
const { onLiked, onLoved, liked, loved } = useRating(ratingInfo);
const disabled = useMemo(() => ratingInfo?.type !== 'Ready', [ratingInfo]);
return (
<div className={classNames(styles['ratings-container'], className)}>
<div className={classNames(styles['icon-container'], { [styles['disabled']]: disabled })} onClick={onLiked}>
<Icon name={liked ? 'thumbs-up' : 'thumbs-up-outline'} className={styles['icon']} />
</div>
<div className={classNames(styles['icon-container'], { [styles['disabled']]: disabled })} onClick={onLoved}>
<Icon name={loved ? 'heart' : 'heart-outline'} className={styles['icon']} />
</div>
</div>
);
};
export default Ratings;

View file

@ -0,0 +1,5 @@
// Copyright (C) 2017-2025 Smart code 203358507
import Ratings from './Ratings';
export { Ratings };

View file

@ -0,0 +1,48 @@
// Copyright (C) 2017-2025 Smart code 203358507
import { useMemo, useCallback } from 'react';
import { useServices } from 'stremio/services';
const useRating = (ratingInfo?: Loadable<RatingInfo>) => {
const { core } = useServices();
const setRating = useCallback((status: Rating) => {
core.transport.dispatch({
action: 'MetaDetails',
args: {
action: 'Rate',
args: status,
},
});
}, []);
const status = useMemo(() => {
const content = ratingInfo?.type === 'Ready' ? ratingInfo.content as RatingInfo : null;
return content?.status;
}, [ratingInfo]);
const liked = useMemo(() => {
return status === 'liked';
}, [status]);
const loved = useMemo(() => {
return status === 'loved';
}, [status]);
const onLiked = useCallback(() => {
setRating(status === 'liked' ? null : 'liked');
}, [status]);
const onLoved = useCallback(() => {
setRating(status === 'loved' ? null : 'loved');
}, [status]);
return {
onLiked,
onLoved,
liked,
loved,
};
};
export default useRating;

View file

@ -159,7 +159,6 @@
display: flex;
flex-direction: row;
align-items: flex-end;
max-height: 15rem;
flex-wrap: wrap;
padding-top: 3.5rem;
overflow: visible;
@ -209,6 +208,11 @@
}
}
}
.ratings {
margin-bottom: 1rem;
margin-right: 1rem;
}
}
.share-prompt {
@ -236,6 +240,10 @@
border-radius: 2rem;
}
}
.ratings {
margin-right: 0;
}
}
}

View file

@ -1,6 +1,7 @@
// Copyright (C) 2017-2023 Smart code 203358507
const React = require('react');
const { useTranslation } = require('react-i18next');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const { useRouteFocused, useModalsContainer } = require('stremio-router');
@ -10,6 +11,7 @@ const { Modal } = require('stremio-router');
const styles = require('./styles');
const ModalDialog = ({ className, title, buttons, children, dataset, onCloseRequest, background, ...props }) => {
const { t } = useTranslation();
const routeFocused = useRouteFocused();
const modalsContainer = useModalsContainer();
const modalContainerRef = React.useRef(null);
@ -60,7 +62,7 @@ const ModalDialog = ({ className, title, buttons, children, dataset, onCloseRequ
<Modal ref={modalContainerRef} {...props} className={classnames(className, styles['modal-container'])} onMouseDown={onModalContainerMouseDown}>
<div className={styles['modal-dialog-container']} onMouseDown={onModalDialogContainerMouseDown}>
<div className={styles['modal-dialog-background']} style={{backgroundImage: `url('${background}')`}} />
<Button className={styles['close-button-container']} title={'Close'} onClick={closeButtonOnClick}>
<Button className={styles['close-button-container']} title={t('BUTTON_CLOSE')} onClick={closeButtonOnClick}>
<Icon className={styles['icon']} name={'close'} />
</Button>
<div className={styles['modal-dialog-content']}>

View file

@ -1,6 +1,7 @@
// Copyright (C) 2017-2023 Smart code 203358507
const React = require('react');
const { useTranslation } = require('react-i18next');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const { default: Icon } = require('@stremio/stremio-icons/react');
@ -10,16 +11,17 @@ const ModalDialog = require('stremio/components/ModalDialog');
const useBinaryState = require('stremio/common/useBinaryState');
const styles = require('./styles');
const Multiselect = ({ className, mode, direction, title, disabled, dataset, renderLabelContent, renderLabelText, onOpen, onClose, onSelect, ...props }) => {
const Multiselect = ({ className, mode, direction, title, disabled, dataset, options, renderLabelContent, renderLabelText, onOpen, onClose, onSelect, ...props }) => {
const { t } = useTranslation();
const [menuOpen, , closeMenu, toggleMenu] = useBinaryState(false);
const options = React.useMemo(() => {
return Array.isArray(props.options) ?
props.options.filter((option) => {
const filteredOptions = React.useMemo(() => {
return Array.isArray(options) ?
options.filter((option) => {
return option && (typeof option.value === 'string' || option.value === null);
})
:
[];
}, [props.options]);
}, [options]);
const selected = React.useMemo(() => {
return Array.isArray(props.selected) ?
props.selected.filter((value) => {
@ -94,7 +96,7 @@ const Multiselect = ({ className, mode, direction, title, disabled, dataset, ren
:
selected.length > 0 ?
selected.map((value) => {
const option = options.find((option) => option.value === value);
const option = filteredOptions.find((option) => option.value === value);
return option && typeof option.label === 'string' ?
option.label
:
@ -109,12 +111,12 @@ const Multiselect = ({ className, mode, direction, title, disabled, dataset, ren
}
{children}
</Button>
), [menuOpen, title, disabled, options, selected, labelOnClick, renderLabelContent, renderLabelText]);
), [menuOpen, title, disabled, filteredOptions, selected, labelOnClick, renderLabelContent, renderLabelText]);
const renderMenu = React.useCallback(() => (
<div className={styles['menu-container']} onKeyDown={menuOnKeyDown} onClick={menuOnClick}>
{
options.length > 0 ?
options.map(({ label, title, value }) => (
filteredOptions.length > 0 ?
filteredOptions.map(({ label, title, value }) => (
<Button key={value} className={classnames(styles['option-container'], { 'selected': selected.includes(value) })} title={typeof title === 'string' ? title : typeof label === 'string' ? label : value} data-value={value} onClick={optionOnClick}>
<div className={styles['label']}>{typeof label === 'string' ? label : value}</div>
<div className={styles['icon']} />
@ -122,11 +124,11 @@ const Multiselect = ({ className, mode, direction, title, disabled, dataset, ren
))
:
<div className={styles['no-options-container']}>
<div className={styles['label']}>No options available</div>
<div className={styles['label']}>{t('NO_OPTIONS')}</div>
</div>
}
</div>
), [options, selected, menuOnKeyDown, menuOnClick, optionOnClick]);
), [filteredOptions, selected, menuOnKeyDown, menuOnClick, optionOnClick]);
const renderPopupLabel = React.useMemo(() => (labelProps) => {
return renderLabel({
...labelProps,

View file

@ -50,7 +50,7 @@
.modal-container, .popup-menu-container {
.menu-container {
max-height: calc(3.2rem * 7);
max-height: calc(3rem * 7);
.option-container {
display: flex;

View file

@ -2,7 +2,8 @@
@import (reference) '~stremio/common/screen-sizes.less';
@parent-height: 10rem;
@parent-height: 12rem;
@item-height: 3rem;
.dropdown {
background: var(--modal-background-color);
@ -18,7 +19,7 @@
&.open {
display: block;
max-height: calc(3.3rem * 7);
max-height: calc(@item-height * 7);
overflow: auto;
}

View file

@ -10,23 +10,25 @@ import styles from './Dropdown.less';
type Props = {
options: MultiselectMenuOption[];
selectedOption?: MultiselectMenuOption | null;
value?: any;
menuOpen: boolean | (() => void);
level: number;
setLevel: (level: number) => void;
onSelect: (value: number) => void;
onSelect: (value: any) => void;
};
const Dropdown = ({ level, setLevel, options, onSelect, selectedOption, menuOpen }: Props) => {
const Dropdown = ({ level, setLevel, options, onSelect, value, menuOpen }: Props) => {
const { t } = useTranslation();
const optionsRef = useRef(new Map());
const containerRef = useRef(null);
const handleSetOptionRef = useCallback((value: number) => (node: HTMLButtonElement | null) => {
const selectedOption = options.find((opt) => opt.value === value);
const handleSetOptionRef = useCallback((optionValue: any) => (node: HTMLButtonElement | null) => {
if (node) {
optionsRef.current.set(value, node);
optionsRef.current.set(optionValue, node);
} else {
optionsRef.current.delete(value);
optionsRef.current.delete(optionValue);
}
}, []);
@ -63,11 +65,11 @@ const Dropdown = ({ level, setLevel, options, onSelect, selectedOption, menuOpen
.filter((option: MultiselectMenuOption) => !option.hidden)
.map((option: MultiselectMenuOption) => (
<Option
key={option.id}
key={option.value}
ref={handleSetOptionRef(option.value)}
option={option}
onSelect={onSelect}
selectedOption={selectedOption}
selectedValue={value}
/>
))
}

View file

@ -1,6 +1,9 @@
// Copyright (C) 2017-2024 Smart code 203358507
@height: 3rem;
.option {
height: @height;
font-size: var(--font-size-normal);
color: var(--primary-foreground-color);
align-items: center;

View file

@ -8,13 +8,12 @@ import Icon from '@stremio/stremio-icons/react';
type Props = {
option: MultiselectMenuOption;
selectedOption?: MultiselectMenuOption | null;
onSelect: (value: number) => void;
selectedValue?: any;
onSelect: (value: any) => void;
};
const Option = forwardRef<HTMLButtonElement, Props>(({ option, selectedOption, onSelect }, ref) => {
// consider using option.id === selectedOption?.id instead
const selected = useMemo(() => option?.value === selectedOption?.value, [option, selectedOption]);
const Option = forwardRef<HTMLButtonElement, Props>(({ option, selectedValue, onSelect }, ref) => {
const selected = useMemo(() => option?.value === selectedValue, [option, selectedValue]);
const handleClick = useCallback(() => {
onSelect(option.value);

View file

@ -1,6 +1,7 @@
// Copyright (C) 2017-2024 Smart code 203358507
@border-radius: 2.75rem;
@height: 3rem;
.multiselect-menu {
position: relative;
@ -14,14 +15,22 @@
}
.multiselect-button {
color: var(--primary-foreground-color);
height: @height;
padding: 0.75rem 1.5rem;
display: flex;
flex: 1;
justify-content: space-between;
align-items: center;
gap: 0 0.5rem;
border-radius: @border-radius;
.label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: var(--primary-foreground-color);
}
.icon {
width: 1rem;
color: var(--primary-foreground-color);
@ -33,7 +42,7 @@
}
}
&:hover {
&:hover, &.active {
background-color: var(--overlay-color);
}
}

View file

@ -11,31 +11,41 @@ import useOutsideClick from 'stremio/common/useOutsideClick';
type Props = {
className?: string,
title?: string;
title?: string | (() => string | null);
options: MultiselectMenuOption[];
selectedOption?: MultiselectMenuOption;
onSelect: (value: number) => void;
value?: any;
disabled?: boolean,
onSelect: (value: any) => void;
};
const MultiselectMenu = ({ className, title, options, selectedOption, onSelect }: Props) => {
const MultiselectMenu = ({ className, title, options, value, disabled, onSelect }: Props) => {
const [menuOpen, , closeMenu, toggleMenu] = useBinaryState(false);
const multiselectMenuRef = useOutsideClick(() => closeMenu());
const [level, setLevel] = React.useState<number>(0);
const onOptionSelect = (value: number) => {
level ? setLevel(level + 1) : onSelect(value), closeMenu();
const selectedOption = options.find((opt) => opt.value === value);
const onOptionSelect = (selectedValue: string | number) => {
level ? setLevel(level + 1) : onSelect(selectedValue), closeMenu();
};
return (
<div className={classNames(styles['multiselect-menu'], className)} ref={multiselectMenuRef}>
<div className={classNames(styles['multiselect-menu'], { [styles['active']]: menuOpen }, className)} ref={multiselectMenuRef}>
<Button
className={classNames(styles['multiselect-button'], { [styles['open']]: menuOpen })}
disabled={disabled}
onClick={toggleMenu}
tabIndex={0}
aria-haspopup='listbox'
aria-expanded={menuOpen}
>
{title}
<div className={styles['label']}>
{
typeof title === 'function'
? title()
: title ?? selectedOption?.label
}
</div>
<Icon name={'caret-down'} className={classNames(styles['icon'], { [styles['open']]: menuOpen })} />
</Button>
{
@ -46,7 +56,7 @@ const MultiselectMenu = ({ className, title, options, selectedOption, onSelect }
options={options}
onSelect={onOptionSelect}
menuOpen={menuOpen}
selectedOption={selectedOption}
value={value}
/>
: null
}

View file

@ -1,7 +1,7 @@
type MultiselectMenuOption = {
id?: number;
label: string;
value: number;
value: string | number | null;
destination?: string;
default?: boolean;
hidden?: boolean;

View file

@ -0,0 +1,65 @@
// Copyright (C) 2017-2025 Smart code 203358507
.number-input {
user-select: text;
display: flex;
max-width: 14rem;
height: 3.5rem;
margin-bottom: 1rem;
color: var(--primary-foreground-color);
background: var(--overlay-color);
border-radius: 3.5rem;
.button {
flex: none;
width: 3.5rem;
height: 3.5rem;
padding: 1rem;
background: var(--overlay-color);
border: none;
border-radius: 100%;
cursor: pointer;
z-index: 1;
.icon {
width: 100%;
height: 100%;
}
}
.number-display {
display: flex;
flex: 1;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 0 1rem;
&::-moz-focus-inner {
border: none;
}
.label {
font-size: 0.8rem;
font-weight: 400;
opacity: 0.7;
}
.value {
font-size: 1.2rem;
display: flex;
justify-content: center;
width: 100%;
color: var(--primary-foreground-color);
text-align: center;
appearance: none;
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
}
}
}

View file

@ -0,0 +1,113 @@
// Copyright (C) 2017-2025 Smart code 203358507
import Icon from '@stremio/stremio-icons/react';
import React, { ChangeEvent, forwardRef, memo, useCallback, useState } from 'react';
import { type KeyboardEvent, type InputHTMLAttributes } from 'react';
import classnames from 'classnames';
import styles from './NumberInput.less';
import Button from '../Button';
type Props = InputHTMLAttributes<HTMLInputElement> & {
containerClassName?: string;
className?: string;
disabled?: boolean;
showButtons?: boolean;
defaultValue?: number;
label?: string;
min?: number;
max?: number;
value?: number;
onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;
onSubmit?: (event: KeyboardEvent<HTMLInputElement>) => void;
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
};
const NumberInput = forwardRef<HTMLInputElement, Props>(({ defaultValue = 0, showButtons, onKeyDown, onSubmit, min, max, onChange, ...props }, ref) => {
const [value, setValue] = useState(defaultValue);
const displayValue = props.value ?? value;
const handleKeyDown = useCallback((event: KeyboardEvent<HTMLInputElement>) => {
onKeyDown?.(event);
if (event.key === 'Enter') {
onSubmit?.(event);
}
}, [onKeyDown, onSubmit]);
const handleValueChange = (newValue: number) => {
if (props.value === undefined) {
setValue(newValue);
}
onChange?.({ target: { value: newValue.toString() }} as ChangeEvent<HTMLInputElement>);
};
const handleIncrement = () => {
handleValueChange(clampValueToRange((displayValue || 0) + 1));
};
const handleDecrement = () => {
handleValueChange(clampValueToRange((displayValue || 0) - 1));
};
const clampValueToRange = (value: number): number => {
const minValue = min ?? 0;
if (value < minValue) {
return minValue;
}
if (max !== undefined && value > max) {
return max;
}
return value;
};
const handleInputChange = useCallback(({ target: { valueAsNumber }}: ChangeEvent<HTMLInputElement>) => {
handleValueChange(clampValueToRange(valueAsNumber || 0));
}, []);
return (
<div className={classnames(props.containerClassName, styles['number-input'])}>
{
showButtons ?
<Button
className={styles['button']}
onClick={handleDecrement}
disabled={props.disabled || (min !== undefined ? displayValue <= min : false)}>
<Icon className={styles['icon']} name={'remove'} />
</Button>
: null
}
<div className={classnames(styles['number-display'], { [styles['buttons-container']]: showButtons })}>
{
props.label ?
<div className={styles['label']}>{props.label}</div>
: null
}
<input
ref={ref}
type={'number'}
tabIndex={0}
value={displayValue}
{...props}
className={classnames(props.className, styles['value'], { [styles.disabled]: props.disabled })}
onChange={handleInputChange}
onKeyDown={handleKeyDown}
/>
</div>
{
showButtons ?
<Button
className={styles['button']} onClick={handleIncrement} disabled={props.disabled || (max !== undefined ? displayValue >= max : false)}>
<Icon className={styles['icon']} name={'add'} />
</Button>
: null
}
</div>
);
});
NumberInput.displayName = 'NumberInput';
export default memo(NumberInput);

View file

@ -0,0 +1,5 @@
// Copyright (C) 2017-2025 Smart code 203358507
import NumberInput from './NumberInput';
export default NumberInput;

View file

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

View file

@ -70,7 +70,7 @@ const SharePrompt = ({ className, url }) => {
onClick={selectInputContent}
tabIndex={-1}
/>
<Button className={styles['copy-button']} title={'Copy to clipboard'} onClick={copyToClipboard}>
<Button className={styles['copy-button']} title={t('CTX_COPY_TO_CLIPBOARD')} onClick={copyToClipboard}>
<Icon className={styles['icon']} name={'link'} />
<div className={styles['label']}>{ t('COPY') }</div>
</Button>

View file

@ -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;
}
}
}

View file

@ -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 (
<div className={styles['combos']}>
{
combos.map((keys, index) => (
<div className={styles['combo']} key={index}>
<Keys keys={keys} />
{
index < (combos.length - 1) && (
<div className={styles['separator']}>
{ t('SETTINGS_SHORTCUT_OR') }
</div>
)
}
</div>
))
}
</div>
);
};
export default Combos;

View file

@ -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);
}

View file

@ -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<string, string> = 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) => (
<Fragment key={key}>
<kbd>
{keyLabelMap[key] ?? key.toUpperCase()}
</kbd>
{
index < (filteredKeys.length - 1) && (
<div className={styles['separator']}>
{
isRange ? t('SETTINGS_SHORTCUT_TO') : '+'
}
</div>
)
}
</Fragment>
))
);
};
export default Keys;

View file

@ -0,0 +1,2 @@
import Keys from './Keys';
export default Keys;

View file

@ -0,0 +1,2 @@
import Combos from './Combos';
export default Combos;

View file

@ -0,0 +1,44 @@
.shortcuts-group {
flex: 1 1 0;
position: relative;
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;
}
}
}
}

View file

@ -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 (
<div className={classNames(className, styles['shortcuts-group'])}>
<div className={styles['title']}>
{t(label)}
</div>
<div className={styles['shortcuts']}>
{
shortcuts.map(({ name, label, combos }) => (
<div className={styles['shortcut']} key={name}>
<div className={styles['label']}>
{t(label)}
</div>
<Combos combos={combos} />
</div>
))
}
</div>
</div>
);
};
export default ShortcutsGroup;

View file

@ -0,0 +1,2 @@
import ShortcutsGroup from './ShortcutsGroup';
export default ShortcutsGroup;

View file

@ -142,11 +142,11 @@ const Slider = ({ className, value, buffered, minimumValue, maximumValue, disabl
<div className={styles['layer']}>
<div
className={classnames(styles['track-after'], { [styles['audio-boost']]: audioBoost })}
style={{ '--mask-width': `calc(${thumbPosition} * 100%)` }}
style={{ '--mask-width': `calc(${thumbPosition.toFixed(3)} * 100%)` }}
/>
</div>
<div className={styles['layer']}>
<div className={styles['thumb']} style={{ marginLeft: `calc(100% * ${thumbPosition})` }} />
<div className={styles['thumb']} style={{ marginLeft: `calc(100% * ${thumbPosition.toFixed(3)})` }} />
</div>
</div>
);

View file

@ -1,26 +0,0 @@
// Copyright (C) 2017-2023 Smart code 203358507
const React = require('react');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const { Button } = require('stremio/components');
const styles = require('./styles');
const Toggle = React.forwardRef(({ className, checked, children, ...props }, ref) => {
return (
<Button {...props} ref={ref} className={classnames(className, styles['toggle-container'], { 'checked': checked })}>
<div className={styles['toggle']} />
{children}
</Button>
);
});
Toggle.displayName = 'Toggle';
Toggle.propTypes = {
className: PropTypes.string,
checked: PropTypes.bool,
children: PropTypes.node
};
module.exports = Toggle;

View file

@ -0,0 +1,27 @@
// Copyright (C) 2017-2023 Smart code 203358507
import React, { forwardRef } from 'react';
import classnames from 'classnames';
import { Button } from 'stremio/components';
import styles from './Toggle.less';
type Props = {
className?: string,
checked: boolean,
disabled?: boolean,
tabIndex?: number,
children?: React.ReactNode,
};
const Toggle = forwardRef(({ className, checked, children, ...props }: Props, ref) => {
return (
<Button {...props} ref={ref} className={classnames(className, styles['toggle-container'], { 'checked': checked })}>
<div className={styles['toggle']} />
{children}
</Button>
);
});
Toggle.displayName = 'Toggle';
export default Toggle;

View file

@ -1,5 +0,0 @@
// Copyright (C) 2017-2023 Smart code 203358507
const Toggle = require('./Toggle');
module.exports = Toggle;

View file

@ -0,0 +1,5 @@
// Copyright (C) 2017-2023 Smart code 203358507
import Toggle from './Toggle';
export default Toggle;

View file

@ -1,9 +1,9 @@
// Copyright (C) 2017-2023 Smart code 203358507
const React = require('react');
const { useTranslation } = require('react-i18next');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const { t } = require('i18next');
const { useRouteFocused } = require('stremio-router');
const { default: Icon } = require('@stremio/stremio-icons/react');
const { Button, Image, Popup } = require('stremio/components');
@ -12,10 +12,12 @@ const useProfile = require('stremio/common/useProfile');
const VideoPlaceholder = require('./VideoPlaceholder');
const styles = require('./styles');
const Video = ({ className, id, title, thumbnail, season, episode, released, upcoming, watched, progress, scheduled, seasonWatched, deepLinks, onMarkVideoAsWatched, onMarkSeasonAsWatched, ...props }) => {
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) {
@ -67,10 +69,23 @@ const Video = ({ className, id, title, thumbnail, season, episode, released, upc
}
}
}, [deepLinks]);
const renderLabel = React.useMemo(() => function renderLabel({ className, id, title, thumbnail, episode, released, upcoming, watched, progress, scheduled, children, ...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;
React.useEffect(() => {
if (selected && ref.current) {
if ((progress && watched) || !watched) {
ref.current.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'start'
});
}
}
}, [selected]);
return (
<Button {...props} className={classnames(className, styles['video-container'])} title={title}>
<Button {...props} ref={ref} className={classnames(className, styles['video-container'], { [styles['selected']]: selected })} title={title}>
{
typeof thumbnail === 'string' && thumbnail.length > 0 ?
<div className={styles['thumbnail-container']}>
@ -107,12 +122,12 @@ const Video = ({ className, id, title, thumbnail, season, episode, released, upc
{
released instanceof Date && !isNaN(released.getTime()) ?
<div className={styles['released-container']}>
{released.toLocaleString(undefined, { year: 'numeric', month: 'short', day: 'numeric' })}
{released.toLocaleString(profile.settings.interfaceLanguage, { year: 'numeric', month: 'short', day: 'numeric' })}
</div>
:
scheduled ?
<div className={styles['released-container']} title={'To be announced'}>
TBA
<div className={styles['released-container']} title={t('TBA')}>
{t('TBA')}
</div>
:
null
@ -121,7 +136,7 @@ const Video = ({ className, id, title, thumbnail, season, episode, released, upc
{
upcoming && !watched ?
<div className={styles['upcoming-container']}>
<div className={styles['flag-label']}>Upcoming</div>
<div className={styles['flag-label']}>{t('UPCOMING')}</div>
</div>
:
null
@ -130,7 +145,7 @@ const Video = ({ className, id, title, thumbnail, season, episode, released, upc
watched ?
<div className={styles['watched-container']}>
<Icon className={styles['flag-icon']} name={'eye'} />
<div className={styles['flag-label']}>Watched</div>
<div className={styles['flag-label']}>{t('CTX_WATCHED')}</div>
</div>
:
null
@ -141,14 +156,14 @@ const Video = ({ className, id, title, thumbnail, season, episode, released, upc
{children}
</Button>
);
}, []);
}, [selected]);
const renderMenu = React.useMemo(() => function renderMenu() {
return (
<div className={styles['context-menu-content']} onPointerDown={popupMenuOnPointerDown} onContextMenu={popupMenuOnContextMenu} onClick={popupMenuOnClick} onKeyDown={popupMenuOnKeyDown}>
<Button className={styles['context-menu-option-container']} title={'Watch'}>
<Button className={styles['context-menu-option-container']} title={t('CTX_WATCH')}>
<div className={styles['context-menu-option-label']}>{t('CTX_WATCH')}</div>
</Button>
<Button className={styles['context-menu-option-container']} title={watched ? 'Mark as non-watched' : 'Mark as watched'} onClick={toggleWatchedOnClick}>
<Button className={styles['context-menu-option-container']} title={watched ? t('CTX_MARK_NON_WATCHED') : t('CTX_MARK_WATCHED')} onClick={toggleWatchedOnClick}>
<div className={styles['context-menu-option-label']}>{watched ? t('CTX_MARK_NON_WATCHED') : t('CTX_MARK_WATCHED')}</div>
</Button>
<Button className={styles['context-menu-option-container']} title={seasonWatched ? t('CTX_UNMARK_REST') : t('CTX_MARK_REST')} onClick={toggleWatchedSeasonOnClick}>
@ -202,6 +217,7 @@ Video.propTypes = {
progress: PropTypes.number,
scheduled: PropTypes.bool,
seasonWatched: PropTypes.bool,
selected: PropTypes.bool,
deepLinks: PropTypes.shape({
metaDetailsStreams: PropTypes.string,
player: PropTypes.string

View file

@ -19,6 +19,11 @@
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;
}
&:hover,
&:focus,
@ -172,6 +177,20 @@
}
}
&.selected {
animation: border 3s ease-in-out forwards;
}
@keyframes border {
0% {
border: 0.15rem solid var(--primary-accent-color);
}
100% {
border: 0.15rem solid transparent;
}
}
.context-menu-container {
max-width: calc(90% - 1.5rem);
z-index: 2;

View file

@ -19,11 +19,13 @@ import ModalDialog from './ModalDialog';
import Multiselect from './Multiselect';
import MultiselectMenu from './MultiselectMenu';
import { HorizontalNavBar, VerticalNavBar } from './NavBar';
import NumberInput from './NumberInput';
import Popup from './Popup';
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';
@ -52,11 +54,13 @@ export {
MultiselectMenu,
HorizontalNavBar,
VerticalNavBar,
NumberInput,
Popup,
RadioButton,
SearchBar,
SharePrompt,
Slider,
ShortcutsGroup,
TextInput,
Toggle,
Transition,

View file

@ -15,6 +15,7 @@
<div id="app"></div>
<%= htmlWebpackPlugin.tags.bodyTags %>
<script src="//www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>
<script async src="https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js"></script>
</body>
</html>

View file

@ -36,7 +36,7 @@ i18n
const root = ReactDOM.createRoot(document.getElementById('app'));
root.render(<App />);
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
if (process.env.NODE_ENV === 'production' && process.env.SERVICE_WORKER_DISABLED !== 'true' && process.env.SERVICE_WORKER_DISABLED !== true && 'serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('service-worker.js')
.catch((registrationError) => {

2
src/modules.d.ts vendored
View file

@ -3,4 +3,6 @@ declare module '*.less' {
export = resource;
}
declare module 'stremio-router';
declare module 'stremio/components/NavBar';
declare module 'stremio/components/ModalDialog';

View file

@ -89,7 +89,7 @@ const Addon = ({ className, id, name, version, logo, description, types, behavio
</div>
{
typeof version === 'string' && version.length > 0 ?
<div className={styles['version-container']} title={`v.${version}`}>v.{version}</div>
<div className={styles['version-container']} title={t('ADDON_VERSION_SHORT', {version})}>{t('ADDON_VERSION_SHORT', {version})}</div>
:
null
}

View file

@ -6,7 +6,7 @@ const classnames = require('classnames');
const { useTranslation } = require('react-i18next');
const { default: Icon } = require('@stremio/stremio-icons/react');
const { usePlatform, useBinaryState, withCoreSuspender } = require('stremio/common');
const { AddonDetailsModal, Button, Image, MainNavBars, Multiselect, ModalDialog, SearchBar, SharePrompt, TextInput } = require('stremio/components');
const { AddonDetailsModal, Button, Image, MainNavBars, ModalDialog, SearchBar, SharePrompt, TextInput, MultiselectMenu } = require('stremio/components');
const { useServices } = require('stremio/services');
const Addon = require('./Addon');
const useInstalledAddons = require('./useInstalledAddons');
@ -107,7 +107,7 @@ const Addons = ({ urlParams, queryParams }) => {
<div className={styles['addons-content']}>
<div className={styles['selectable-inputs-container']}>
{selectInputs.map((selectInput, index) => (
<Multiselect
<MultiselectMenu
{...selectInput}
key={index}
className={styles['select-input-container']}
@ -124,7 +124,7 @@ const Addons = ({ urlParams, queryParams }) => {
value={search}
onChange={searchInputOnChange}
/>
<Button className={styles['filter-button']} title={'All filters'} onClick={openFiltersModal}>
<Button className={styles['filter-button']} title={t('ALL_FILTERS')} onClick={openFiltersModal}>
<Icon className={styles['filter-icon']} name={'filters'} />
</Button>
</div>
@ -132,12 +132,12 @@ const Addons = ({ urlParams, queryParams }) => {
installedAddons.selected !== null ?
installedAddons.selectable.types.length === 0 ?
<div className={styles['message-container']}>
No addons ware installed!
{t('NO_ADDONS')}
</div>
:
installedAddons.catalog.length === 0 ?
<div className={styles['message-container']}>
No addons ware installed for that type!
{t('NO_ADDONS_FOR_TYPE')}
</div>
:
<div className={styles['addons-list-container']}>
@ -216,9 +216,9 @@ const Addons = ({ urlParams, queryParams }) => {
</div>
{
filtersModalOpen ?
<ModalDialog title={'Addons filters'} className={styles['filters-modal']} onCloseRequest={closeFiltersModal}>
<ModalDialog title={t('ADDONS_FILTERS')} className={styles['filters-modal']} onCloseRequest={closeFiltersModal}>
{selectInputs.map((selectInput, index) => (
<Multiselect
<MultiselectMenu
{...selectInput}
key={index}
className={styles['select-input-container']}
@ -265,7 +265,7 @@ const Addons = ({ urlParams, queryParams }) => {
<span className={styles['name']}>{typeof sharedAddon.manifest.name === 'string' && sharedAddon.manifest.name.length > 0 ? sharedAddon.manifest.name : sharedAddon.manifest.id}</span>
{
typeof sharedAddon.manifest.version === 'string' && sharedAddon.manifest.version.length > 0 ?
<span className={styles['version']}>v. {sharedAddon.manifest.version}</span>
<span className={styles['version']}>{t('ADDON_VERSION_SHORT', { version: sharedAddon.manifest.version })}</span>
:
null
}

Some files were not shown because too many files have changed in this diff Show more