Compare commits

...

405 commits

Author SHA1 Message Date
Timothy Z.
5db6df4fc2
Merge pull request #1217 from Stremio/fix/subtitles-selections-issues
Some checks are pending
Build / build (push) Waiting to run
2026-04-20 17:30:27 +03:00
Timothy Z.
5580da8ecf fix: reselection loop 2026-04-20 16:57:45 +03:00
Timothy Z.
38a811ce75 bump: stremio-core-web v0.56.4 2026-04-20 16:43:56 +03:00
Timothy Z.
de78766151 bump: stremio-video to 0.0.75 2026-04-20 16:38:32 +03:00
Timothy Z.
5bc74a9da4
Merge pull request #1199 from Stremio/fix/statistics-menu-handler
Some checks are pending
Build / build (push) Waiting to run
Player: Fix statistics menu shortcut handler
2026-04-20 12:20:55 +03:00
Timothy Z.
fe651a2cca
Merge pull request #1198 from Stremio/fix/blur-window-cancel-shortcut-events
Player: Fix cancel shortcut events on blur
2026-04-20 12:20:47 +03:00
Timothy Z.
377d57cf16
Merge pull request #1197 from Stremio/fix/correctly-select-embedded-subs-first
Player: Prioritize embedded subs first
2026-04-20 12:20:35 +03:00
Timothy Z.
694fb833aa fix: statistics menu handler 2026-04-17 21:03:11 +03:00
Timothy Z.
f74f9aafa0 fix: on blue cancel shortcut events 2026-04-17 20:53:48 +03:00
Timothy Z.
bbfe25177b fix: prioritize embedded subs first 2026-04-17 17:38:31 +03:00
Timothy Z.
5560813b37
Merge pull request #1196 from Stremio/fix/space-bar-events-play-pause-handing 2026-04-16 12:27:02 +03:00
Timothy Z.
8fcffa2c3e Update Player.js 2026-04-16 12:17:22 +03:00
Timothy Z.
3e93afbe21 fix: space bar holding issues 2026-04-16 12:13:48 +03:00
Timothy Z.
81fdb18536 bump: release v5.0.0-beta.34 2026-04-15 00:47:24 +03:00
Timothy Z.
5e8db65551
Merge pull request #1194 from Stremio/fix/subtitles-grouping-logic
Player: Use combination of set with map to avoid crashes on SubtitlesMenu
2026-04-15 00:45:47 +03:00
Timothy Z.
a322de9093 fix: use set with map to avoid crash on older devices 2026-04-14 21:34:10 +03:00
Tim
35970825fa
Merge pull request #1179 from ignaciojsoler/feat/persist-subtitle-language
fix: persist subtitle language preference across streams
2026-04-14 13:16:39 +02:00
Timothy Z.
7b30c4e805 bump: stremio-video to v0.0.73 2026-04-14 13:32:34 +03:00
Timothy Z.
b48740f5c2
Merge pull request #1193 from Stremio/dependabot/github_actions/pnpm/action-setup-6
chore(deps): bump pnpm/action-setup from 5 to 6
2026-04-14 11:59:19 +03:00
Timothy Z.
79a771cbc3
Merge pull request #1192 from Stremio/dependabot/github_actions/actions/github-script-9
chore(deps): bump actions/github-script from 8 to 9
2026-04-14 11:59:06 +03:00
dependabot[bot]
a1e19f0ea5
chore(deps): bump pnpm/action-setup from 5 to 6
Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 5 to 6.
- [Release notes](https://github.com/pnpm/action-setup/releases)
- [Commits](https://github.com/pnpm/action-setup/compare/v5...v6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-13 22:03:50 +00:00
dependabot[bot]
73917c14a4
chore(deps): bump actions/github-script from 8 to 9
Bumps [actions/github-script](https://github.com/actions/github-script) from 8 to 9.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v8...v9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-13 22:03:46 +00:00
Timothy Z.
bf65b6001e bump: release v5.0.0-beta.33 2026-04-13 20:17:17 +03:00
Timothy Z.
35b7b7082c Revert "bump: release v5.0.0-beta.33"
This reverts commit 33d09916cb.
2026-04-13 20:16:48 +03:00
Timothy Z.
33d09916cb bump: release v5.0.0-beta.33 2026-04-13 20:12:33 +03:00
Timothy Z.
29d491d00b bump: stremio-core to v0.56.3 2026-04-13 20:06:40 +03:00
Timothy Z.
3ce58df582 bump: stremio-video 0.0.72 2026-04-13 19:49:54 +03:00
Timothy Z.
7cd439a656 chore: bump core to v0.56.2 2026-04-13 19:30:19 +03:00
Timothy Z.
9e0dd0e942 bump: release v5.0.0-beta.33 2026-04-13 17:50:17 +03:00
Timothy Z.
b003f64f4f
Merge pull request #1060 from higorgoulart/feat/989/watched-on-discover-and-details
Details | Discover: Implement mark as watched button
2026-04-13 17:20:03 +03:00
Timothy Z.
b058eb536e
Merge branch 'development' into feat/989/watched-on-discover-and-details 2026-04-13 17:19:53 +03:00
Timothy Z.
0502c9e47a chore: update core to 0.56.1 2026-04-13 17:19:17 +03:00
Timothy Z.
1f293b7cbc
Merge pull request #1180 from Stremio/feat/magnet-http-streams-handing
feat: handle http/magnets from search bar
2026-04-13 17:17:42 +03:00
Ignacio
b69165e868 fix: pass track object to subtitle callbacks and fix lang fallback chain 2026-04-10 10:27:25 -03:00
Timothy Z.
234d83ad34 chore: use dev build 2026-04-08 13:49:00 +03:00
Timothy Z.
3052a724f4 Merge branch 'development' into pr/1060 2026-04-08 13:48:17 +03:00
Timothy Z.
880304f46f
Merge pull request #1191 from Stremio/refactor/player-events
Player: refactor events
2026-04-07 13:13:25 +03:00
Timothy Z.
e860752f41 fix(player): cancel hold-to-speed-up timer when menus open (2) 2026-04-06 21:33:18 +03:00
Timothy Z.
b2d81c09ee fix(player): cancel hold-to-speed-up timer when menus open 2026-04-06 21:33:08 +03:00
Timothy Z.
49240a4e0f fix(player/statistics): prevent menu from closing on inside click 2026-04-06 21:31:36 +03:00
Timothy Z.
129f510047 fix(player): block mute and speed/subtitle shortcuts while a menu is open 2026-04-06 21:30:46 +03:00
Timothy Z.
33c3f6d9f0 refactor(player): fold nextVideoPopupOpen into menusOpen and gate playback shortcuts via onShortcut 2026-04-06 21:29:33 +03:00
Timothy Z.
117f932596 chore(shortcuts): add enabled param to onShortcut hook 2026-04-06 21:25:56 +03:00
Timothy Z.
de5d43225b
Merge pull request #1190 from Stremio/refactor/context-menu-transition-usage
ContextMenu: align with transition usage
2026-04-06 21:15:47 +03:00
Timothy Z.
aa76396a84 ContextMenu: align with transition usage 2026-04-06 21:14:57 +03:00
Timothy Z.
c99d7c5d82 use new action metaItemMarkAsWatched 2026-04-05 15:05:47 +03:00
Timothy Z.
4e735afacc
Merge pull request #1185 from mrcanelas/fix/windows-emoji-rendering
feat(ui): add Twemoji font fallback for country flags on Windows
2026-04-05 13:20:27 +03:00
Tim
6dfd0fcbde refactor(player): momoize menus 2026-04-02 09:56:21 +02:00
Tim
1b9e2a194f fix: cancel animation frame on cleanup for Transition 2026-04-02 09:54:19 +02:00
Tim
3f5dedd072 feat(player): add transitions to menus 2026-04-02 09:53:20 +02:00
mrcanelas
0e1d22d279 feat(ui): add font fallback for country flags on Windows 2026-04-01 18:52:22 -03:00
Tim
eb23d3e4db
Merge pull request #1184 from Stremio/feat/player-subtitles-sort-default-language
Player: Sort subtitles menu languages by language settings
2026-04-01 22:50:45 +02:00
Tim
73409fff8c feat(player): sort subtitles menu languages by default language 2026-04-01 22:39:48 +02:00
Tim
70bcf339be
Merge pull request #1182 from Stremio/fix/shell-subtitles-menu-split-languages
Player: Fix multiple languages entries from player subtitles menu
2026-04-01 18:04:55 +02:00
Tim
50cd992f1c fix(Player): embedded track labels on subtitles menu 2026-04-01 18:03:36 +02:00
Tim
c131d7f75e fix: normalize subtitles languages from player menu 2026-04-01 17:32:24 +02:00
Timothy Z.
e260e52322 refactor: change type to info 2026-03-31 13:45:08 +03:00
Timothy Z.
de32e3a765 fix: correct message for fail 2026-03-31 13:11:48 +03:00
Timothy Z.
6cb0c75555 feat: add loading state 2026-03-31 12:56:08 +03:00
Timothy Z.
0a88fe5d01
Merge pull request #1181 from mrcanelas/feat/addon-name-breakline
feat: support line breaks in addon names to match TV app behavior
2026-03-30 21:40:05 +03:00
Timothy Z.
ccc5832611 chore update core for testing 2026-03-30 21:39:13 +03:00
mrcanelas
c2c18bc4f6 feat: support line breaks in addon names to match TV app behavior 2026-03-30 13:26:36 -03:00
Timothy Z.
e0961ec686 remove the try catch 2026-03-30 18:47:20 +03:00
Timothy Z.
fcd85bdcf4 feat: handle http/magnets from search bar 2026-03-30 18:40:24 +03:00
Ignacio
9fe7430bc7 fix: persist subtitle language preference across streams 2026-03-29 11:03:42 -03:00
Timothy Z.
9dd44a7ef1
Merge pull request #1177 from Stremio/fix/discover-deeplinks-crash
Fix crash when no extra option is selected in Discover
2026-03-27 15:34:35 +02:00
Timothy Z.
f2023c5a5a
Merge pull request #1176 from Stremio/fix/streams-list-loading-layout-shift
fix: move streams loading indicator below list to prevent layout shift
2026-03-27 15:32:58 +02:00
Timothy Z.
ebc7c5bdde
Merge pull request #1175 from Stremio/fix/hold-speed-up-during-menus
fix: prevent hold-to-speed-up when menus are open
2026-03-27 15:31:52 +02:00
Timothy Z.
070807f148 Fix crash when no extra option is selected in Discover
Guard against `selectedExtra` being undefined when `options.find()`
returns no match, preventing "Cannot read properties of undefined
(reading 'deepLinks')" TypeError.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:00:21 +02:00
Timothy Z.
d2199e49f0 fix: add border radius to top corners of loading indicator
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 23:40:17 +02:00
Timothy Z.
2cc122ea1c fix: sticky loading indicator on mobile to keep it visible
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 23:33:54 +02:00
Timothy Z.
079d37cd77 fix: move streams loading indicator below the list to prevent layout shift
Closes Stremio/stremio-bugs#2187

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 23:29:01 +02:00
Timothy Z.
003ea9f2dc fix: prevent hold-to-speed-up when menus or popups are open
Closes Stremio/stremio-bugs#2440

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 20:56:09 +02:00
Timothy Z.
7ab75cfc88
Merge pull request #1120 from ArtificialSloth/fix/prevent-autoplay-when-using-external-player
Fix/Prevent autoplay when using an external player
2026-03-26 19:57:14 +02:00
Timothy Z.
504a3bb0be
Merge pull request #1173 from Stremio/fix/space-hold-reset-playback-speed
Player: Fix spacebar and left click hold behavior
2026-03-26 19:54:14 +02:00
Timothy Z.
1f56ddf8e3
Merge pull request #1174 from Stremio/feat/shortcut-playback-speed-increase-decrease
Shortcuts: Decrease "[" and increase "]" playback speed
2026-03-26 19:54:01 +02:00
Botzy
80e065778f chore: update translations 2026-03-26 18:27:52 +02:00
Botzy
0286994773 feat(Shortcuts): added shortcuts for increasing and decreasing playback speed 2026-03-26 18:15:46 +02:00
Botzy
3fdda2cdad fix: set initial playback speed to 1 instead of null 2026-03-26 17:56:46 +02:00
Botzy
866db891ef fix: mouse hold and release to not reset playback speed to 1x 2026-03-26 17:13:11 +02:00
Botzy
48ba0b8fba fix: mouse hold click to take effect only outside control bar 2026-03-26 17:04:40 +02:00
Botzy
0c78e39a02 fix: after spacebar key up set previously selected playback speed 2026-03-26 17:04:05 +02:00
Timothy Z.
89233243f2
Merge pull request #1170 from Stremio/dependabot/github_actions/pnpm/action-setup-5
chore(deps): bump pnpm/action-setup from 4 to 5
2026-03-24 14:19:17 +02:00
dependabot[bot]
6a981f275b
chore(deps): bump pnpm/action-setup from 4 to 5
Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 4 to 5.
- [Release notes](https://github.com/pnpm/action-setup/releases)
- [Commits](https://github.com/pnpm/action-setup/compare/v4...v5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-23 21:24:19 +00:00
Timothy Z.
74f1d660be
Merge pull request #1168 from mrcanelas/fix/streams-list-episode-title
fix: improve episode title formatting in StreamsList component
2026-03-23 17:15:14 +02:00
mrcanelas
562854d902 fix: improve episode title formatting in StreamsList component 2026-03-22 19:29:27 -03:00
Timothy Z.
effa6e430e
Merge pull request #1167 from schobiDotDev/fix/subtitle-addon-label
fix: use addon-provided subtitle label instead of URL
2026-03-22 15:07:06 +02:00
schobidotdev
3c2e144f1d fix: use addon-provided subtitle label instead of URL 2026-03-22 13:03:29 +01:00
Timothy Z.
70ea2a5772
Merge pull request #1151 from Stremio/feat/stream-copy-magnet-option
MetaDetails: Add Copy Magnet Link option
2026-03-21 19:26:52 +02:00
Timothy Z.
ca46494480
Merge branch 'development' into feat/stream-copy-magnet-option 2026-03-21 19:23:12 +02:00
Timothy Z.
b1a2d0354b update core 2026-03-21 19:22:03 +02:00
Tim
50ab7d9f23 chore: v5.0.0-beta.32 2026-03-19 16:48:37 +01:00
Tim
b32cf42b96 chore: update core 2026-03-19 16:47:45 +01:00
Tim
2f4dff7dd8
Merge pull request #1160 from Stremio/dependabot/github_actions/svenstaro/upload-release-action-2.11.5
chore(deps): bump svenstaro/upload-release-action from 2.11.4 to 2.11.5
2026-03-16 23:39:45 +01:00
dependabot[bot]
1565b85ea3
chore(deps): bump svenstaro/upload-release-action from 2.11.4 to 2.11.5
Bumps [svenstaro/upload-release-action](https://github.com/svenstaro/upload-release-action) from 2.11.4 to 2.11.5.
- [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.4...2.11.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-16 21:33:37 +00:00
Timothy Z.
7823a783ea feat: update translations 2026-03-13 20:41:28 +02:00
Timothy Z.
6e53d5131e
Merge pull request #1156 from Stremio/feat/player-next-episode-shortcut
Player: Shortcut [Shift + N] for playing next episode
2026-03-13 15:11:23 +02:00
Botzy
eb4f4b9cce fix: translation key for shortcut Shift + N 2026-03-13 11:05:42 +02:00
Botzy
041748ef71 feat(Player): added shortcut for next episode 2026-03-11 18:56:32 +02:00
Timothy Z.
c3f67454ff
Merge pull request #1150 from Stremio/fix/shortcuts-section-button-visibility
Settings: Correct shortcuts menu button visibility
2026-03-11 18:01:09 +02:00
Timothy Z.
89515a2a75
Merge pull request #1154 from Stremio/feat/hold-to-speedup
Player: Hold spacebar or left click to speed up 2x
2026-03-11 17:26:49 +02:00
Botzy
5b83fa00b5 feat: hold left mouse btn or spacebar to speed up 2x 2026-03-09 18:15:08 +02:00
Botzy
182782a60f fix: speed menu to display 0.25 speed option 2026-03-09 15:19:02 +02:00
Tim
b24426250b
Merge pull request #1153 from m-dragoev/codex/i18n-key-fixes
fix: use valid translation keys
2026-03-08 13:23:14 +01:00
Miroslav Dragoev
cdb65a7973 fix: use valid i18n keys in player and seasons placeholder 2026-03-08 14:19:18 +02:00
Timothy Z.
097857d27c chore: update core 2026-03-07 06:55:16 +05:00
Tim
31a5cc6f1a chore: update langs 2026-03-06 14:27:33 +01:00
Timothy Z.
eef3cef1d4 add copy magnet link option to stream 2026-03-04 21:14:03 +02:00
Timothy Z.
3d119db049 improve selected section logic for edge cases 2026-03-04 20:48:01 +02:00
Timothy Z.
df69e6eb18 change shortcuts visibility on mobile 2026-03-04 20:20:31 +02:00
Timothy Z.
6f0e7aa290 bump v5.0.0-beta.31 2026-03-04 17:14:33 +02:00
Timothy Z.
389a91aeca
Merge pull request #1147 from Stremio/feat/add-support-for-more-external-players
Settings: support infuse & vidhub external players
2026-03-04 14:52:28 +02:00
Timothy Z.
d8278e99da
Merge pull request #1148 from Stremio/refactor/improve-ios-vision-os-detection
Dev: improve ios visionos detection
2026-03-04 14:45:44 +02:00
Timothy Z.
0567bbd8ac chore: bump core 2026-03-04 14:44:57 +02:00
Timothy Z.
c8d107d036 refactor: improve ios vision os detection 2026-03-04 13:53:40 +02:00
Timothy Z.
c8f3a70f41 feat: support infuse & vidhub external players 2026-03-04 13:52:52 +02:00
Timothy Z.
8e87d2515b
Merge pull request #1145 from Stremio/dependabot/github_actions/svenstaro/upload-release-action-2.11.4
chore(deps): bump svenstaro/upload-release-action from 2.11.3 to 2.11.4
2026-03-03 12:51:21 +02:00
dependabot[bot]
cc799b610f
chore(deps): bump svenstaro/upload-release-action from 2.11.3 to 2.11.4
Bumps [svenstaro/upload-release-action](https://github.com/svenstaro/upload-release-action) from 2.11.3 to 2.11.4.
- [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.3...2.11.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-02 23:01:15 +00:00
Timothy Z.
83ff6d54a4
Merge pull request #1130 from fawazorg/fix/translate-hardcoded-strings
fix: hardcoded strings and add missing translation keys
2026-03-02 15:18:40 +02:00
Timothy Z.
2f0005080b chore: update translations pkg 2026-03-02 15:14:49 +02:00
Timothy Z.
14765d8c22 Merge branch 'development' into pr/1130 2026-03-02 15:13:10 +02:00
Timothy Z.
8367dce25d
Merge pull request #704 from ArtificialSloth/feat/LibItem-behavior
Library: Improve LibraryItem behaviour hints
2026-03-02 13:41:36 +02:00
Timothy Z.
68c0a4fb13 refactor: simplify handling on the libitem 2026-03-02 13:38:45 +02:00
Timothy Z.
e26c33fe33 Merge branch 'development' into pr/704 2026-03-02 13:33:22 +02:00
Timothy Z.
091f94e8fd
Merge pull request #1140 from Stremio/fix/apple-pwa-status-bar-color
PWA: navigation bar styles, fullscreen, safe areas
2026-02-23 17:31:54 +03:00
Timothy Z.
3f5097f0a0 fix(sidedrawer): always respect safe areas 2026-02-23 16:07:41 +02:00
Timothy Z.
ac9ca71b53 fix: ios pwa styles for the standalone display 2026-02-23 15:59:29 +02:00
Timothy Z.
fab318e647 lower the bottom safe inset revert to primary bg 2026-02-23 15:44:01 +02:00
Timothy Z.
c95f314a50 fix: sideDrawer safe areas 2026-02-23 15:35:55 +02:00
Timothy Z.
8623627f4d refactor: use both vars for html background 2026-02-23 15:23:37 +02:00
Timothy Z.
b49eb516fd
Merge pull request #1139 from Stremio/fix/correct-allowed-hosts-logic
Dev: Fix host whitelist logic
2026-02-23 16:12:41 +03:00
Timothy Z.
df0d24a7d9 fix: PWA safe area inset bg color 2026-02-23 15:08:24 +02:00
Timothy Z.
3098f6417f fix: ios PWA status bar style 2026-02-23 14:50:17 +02:00
Timothy Z.
ec6db02829 fix: host whitelist logic 2026-02-23 13:07:04 +02:00
Fawazorg
1e0963e8af fix: missing key translate for server add url 2026-02-09 00:17:46 +03:00
Fawazorg
ce2c021e5f fix: normalize catalog name casing for add-on filter translation key 2026-02-08 20:54:35 +03:00
Fawazorg
6bd28847f2 fix: disabled translation key for next video popup 2026-02-07 23:18:27 +03:00
Tim
a77faea0b9
Merge pull request #1127 from fawazorg/fix/translate-hardcoded-strings
fix: replace hardcoded strings with translation keys
2026-02-07 19:10:56 +01:00
Fawazorg
93ed428e8b Fix Email key 2026-02-07 20:54:57 +03:00
Fawazorg
fa13597748 fix: replace hardcoded strings with translation keys 2026-02-07 20:29:13 +03:00
Timothy Z.
c368951952
Merge pull request #1122 from Stremio/fix/use-translate-key-prefix-correction
MetaDetails: Correctly translate metadata
2026-02-03 11:16:51 +02:00
Botzy
0a1746dfe2 fix: links prefix and revert change to useTranslate 2026-02-03 11:13:31 +02:00
Timothy Z.
e29adde4bd fix: use translate prefix correction 2026-02-03 16:44:25 +08:00
ArtificialSloth
c28a52a73c fix: add playingOnExternalDevice flag used to pause the video if it starts playing while using an external player. 2026-01-27 20:00:58 -05:00
Timothy Z.
0df0cdb44b
Merge pull request #1118 from Stremio/fix/image-linter-errors
Image: update return renderFallback type
2026-01-26 16:09:44 +02:00
Timothy Z.
1c9813ebc9 fix(Image): update return renderFallback type 2026-01-22 21:04:57 +02:00
Timothy Z.
dbed391a86 chore: bump v5.0.0-beta.30 2026-01-22 16:44:08 +02:00
Timothy Z.
9503e90e54
Merge pull request #675 from GaryGosh/feat/captions-shortkey
Player: Implement shortkey to toggle caption
2026-01-22 15:22:33 +02:00
Timothy Z.
3cad491040 chore: update translations 2026-01-22 15:20:21 +02:00
Timothy Z.
d456adff0e refactor: correctly set tracks 2026-01-22 15:20:09 +02:00
Timothy Z.
1c441b9bc0 Update Player.js 2026-01-22 15:13:36 +02:00
Timothy Z.
8911473210 refactor: remove the block which is handled by useff 2026-01-22 14:54:33 +02:00
Tim
54b017c39f Merge branch 'development' of https://github.com/Stremio/stremio-web into development 2026-01-22 13:51:03 +01:00
Timothy Z.
ea5e302af7 refactor: simplfy subs handling 2026-01-22 14:48:52 +02:00
Timothy Z.
67358359bf refactor: remember the sub track
handle both external and embedded
2026-01-22 14:38:59 +02:00
Timothy Z.
92d0644c9f Merge branch 'development' into pr/675 2026-01-22 14:18:05 +02:00
Timothy Z.
9c478148bc Merge branch 'development' into pr/704 2026-01-22 13:07:56 +02:00
Timothy Z.
58e38a6077
Merge pull request #1049 from Stremio/fix/meta-details-path-for-episode-picker
fix: metaDetails redirect when route ends with /
2026-01-22 12:46:21 +02:00
Timothy Z.
f2c9bb6d88 chore: remove comments 2026-01-22 12:37:53 +02:00
Timothy Z.
86db4c47e4 Merge branch 'development' into fix/meta-details-path-for-episode-picker 2026-01-22 12:36:31 +02:00
Timothy Z.
3b7944a6f7
Merge pull request #1091 from Stremio/feat/dispatching-addon-install-action-can-throw-exeception
Dev: Addon install action exceptions
2026-01-22 12:33:53 +02:00
Timothy Z.
4ce5ef3744
Merge pull request #1111 from Stremio/fix/copy-download-and-copy-streaming-urls
fix(streams): copy stream link returns streamable url
2026-01-22 12:32:51 +02:00
Timothy Z.
370443609b Merge branch 'development' into fix/copy-download-and-copy-streaming-urls 2026-01-22 12:25:43 +02:00
Timothy Z.
cf0ff1f4ca
fix: trnslations 2026-01-22 12:25:20 +02:00
Tim
17ee0e95e4 fix(Player): side drawer layout on large screens 2026-01-22 11:22:27 +01:00
Timothy Z.
a5265bacf9
Merge branch 'development' into feat/dispatching-addon-install-action-can-throw-exeception 2026-01-22 12:15:09 +02:00
Tim
e6e05573cb fix(Player): disable value labels of subtitles settings if value is null 2026-01-22 10:48:14 +01:00
Tim
cce556e639 feat(Player): show disabled state when ends of range are reached for subtitles settings 2026-01-22 10:44:28 +01:00
Tim
15575ee699 refactor(Player): set audio menu max-height same as subtitles menu 2026-01-22 10:27:11 +01:00
Tim
e85b67268d refactor(Player): wrap video setProp calls in functions 2026-01-22 10:21:42 +01:00
Tim
89fbbb3451
Merge pull request #1116 from Stremio/feat/player-remember-subtitles-settings
Player: Remember subtitles settings
2026-01-22 10:04:19 +01:00
Tim
a30307789c feat: remember subtitles settings on player 2026-01-22 09:57:46 +01:00
Tim
11f51dd86c
Merge pull request #1066 from Stremio/feat/ass-subtitles-styling-setting
feat: add ass subtitles styling setting
2026-01-22 00:16:56 +01:00
Tim
9853c28683 Merge branch 'development' of https://github.com/Stremio/stremio-web into feat/ass-subtitles-styling-setting 2026-01-22 00:14:36 +01:00
Tim
0dcc07c469 chore: update core 2026-01-22 00:12:53 +01:00
Tim
cd111942a5
Merge pull request #1115 from Stremio/feat/player-remember-selected-tracks
Player: Remember selected tracks
2026-01-21 10:07:33 +01:00
Tim
8339dc8a00
Merge pull request #1114 from Stremio/refactor/settings-interface-section
Settings: Move interface settings to dedicated section
2026-01-21 09:54:06 +01:00
Tim
39741d1372 feat: remember selected tracks on player 2026-01-21 09:48:08 +01:00
Tim
7cd49b516f refactor(Settings): move interface settings to dedicated section 2026-01-20 22:01:33 +01:00
Tim
5aaee64549 chore: add new interface languages 2026-01-20 21:39:03 +01:00
Tim
f046e65e73 chore: update translations 2026-01-20 21:38:44 +01:00
Tim
487fde70e0 chore: update video 2026-01-20 21:25:32 +01:00
Lachezar Lechev
e11366b374
fix(streams): copy stream link returns streamable url
feat(streams): add copy download link

Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2026-01-15 17:58:43 +02:00
Tim
9966ae43d5
Merge pull request #1109 from PL7963/patch-1
Update README.md image paths
2026-01-14 17:17:45 +01:00
Coolkie
889dface67
Update README.md image paths 2026-01-14 22:12:05 +08:00
Tim
bb0aecc194 chore: remove commit hash from fonts path 2026-01-14 04:40:01 +01:00
Tim
64b13d6092 chore: move assets to dedicated folder 2026-01-14 04:39:34 +01:00
Tim
a11df877a6 chore: remove deprecated webpack-pwa-manifest 2026-01-14 04:15:41 +01:00
Tim
f37e119644 chore: replace clean-webpack-plugin 2026-01-14 03:21:19 +01:00
Tim
9dbf950a40 chore: replace deprecated lodash.isequal 2026-01-14 03:12:15 +01:00
Tim
55b86179ca Merge branch 'development' of https://github.com/Stremio/stremio-web into feat/ass-subtitles-styling-setting 2026-01-13 18:21:31 +01:00
Tim
3e8c9999fe refactor(Settings): move ASS subtitles setting to advanced 2026-01-13 18:19:37 +01:00
Tim
0b179b88e8 chore: update translations 2026-01-13 18:18:38 +01:00
Tim
da675cd56c chore: update caniuse 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
higorgoulart
b9540af66f Merge branch 'development' into feat/989/watched-on-discover-and-details 2026-01-06 11:48:35 -03:00
Timothy Z.
55963fd23e
Merge pull request #1106 from Stremio/chore/align-error-styles-across-app
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
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
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
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
Lachezar Lechev
673c22a014
chore: bump core-web to feature branch
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-12-15 11:34:00 +02:00
Lachezar Lechev
07d2744f66
feat(Addon): add addon shows toast if url is not valid
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-12-15 11:30:36 +02:00
Timothy Z.
bfb5c484fc
Merge pull request #1079 from sagarchaulagai/development
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
[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
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
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 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
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 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
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
Tim
e8bee4997a feat: add ass subtitles styling setting 2025-11-21 09:11:35 +01:00
Timothy Z.
71e0bb4481 chore: update copyright 2025-11-19 14:48:11 +02:00
Timothy Z.
6bf3b8147d refactor(ActionsGroup): simplify 2025-11-19 14:47:17 +02:00
Timothy Z.
f73fa5931e refactor(Discover): simplify 2025-11-19 14:31:04 +02:00
higorgoulart
a9d9c8d808 feat: load model 2025-11-17 19:39:03 -03:00
higorgoulart
c70211153e feat: review facts 2025-11-15 14:04:14 -03:00
Neeraj TK
5eb55d3aaf Added the toggle subtitles shortcut 2025-11-14 17:45:45 +05:30
Neeraj TK
e1e6fe075b
Merge branch 'development' into feat/captions-shortkey 2025-11-14 17:32:10 +05:30
higorgoulart
9ccc6b8271 feat: change metaDetails action 2025-11-12 18:37:41 -03:00
higorgoulart
67f4f349bb feat: remove add to library 2025-11-11 18:48:24 -03:00
higorgoulart
97c3b7d004 feat: rename component & fix style 2025-11-11 17:30:00 -03:00
higorgoulart
373ccf351a feat: review facts 2025-11-08 14:00:01 -03:00
higorgoulart
987201edd3 feat: review facts 2025-11-08 13:59:10 -03:00
higorgoulart
ff08e377fc feat: remove unused component & fix spacing 2025-11-07 17:57:06 -03:00
Timothy Z.
c3f1f6c911
Merge pull request #1057 from Stremio/fix/trailer-player-crashes
Player: prevent crash when destroying null video
2025-11-07 12:52:35 +02:00
higorgoulart
3b2d1f365c feat: icons group component 2025-11-04 19:23:39 -03:00
higorgoulart
852f478f1e feat: change trailer order & fix discover mark as watched 2025-11-04 17:52:09 -03:00
higorgoulart
6833bb719d feat: watched on discover & details 2025-10-31 19:52:30 -03: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 2025-10-27 16:21:50 +01:00
Timothy Z.
000d5be639
Merge pull request #974 from Stremio/feat/details-scroll-to-last-watched-video
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
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 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
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
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
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
Lachezar Lechev
2de2e89446
fix: meta details - don't set streamPath if videoId is empty string
- fix season selection path inconsistencies

Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-10-17 14:09:27 +03:00
Lachezar Lechev
ea69521912
fix: metaDetails redirect when route ends with /
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
2025-10-17 12:46:46 +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
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 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
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
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
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
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
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
a snaek
80f25b8d45
Merge branch 'Stremio:development' into development 2025-08-12 00:51:59 +02: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
Timothy Z.
56989781c8 Merge branch 'development' into pr/704 2025-07-16 17:40:00 +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
ArtificialSloth
5ab324f125 resolve upstream conflicts 2025-06-30 00:58:33 -04:00
Neeraj TK
ea5d05c31d
Merge branch 'development' into feat/captions-shortkey 2025-01-14 00:55:20 +05:30
Neeraj TK
881c808003 formatting revert 2024-09-23 20:07:11 +05:30
Neeraj TK
d2db62f33a retain last selected subtitle upon toggling 2024-09-23 20:05:36 +05:30
Neeraj TK
3b730a2bd8 added shortkey to toggle caption. UX improvement. 2024-08-11 04:32:23 +05:30
177 changed files with 12600 additions and 16630 deletions

View file

@ -13,8 +13,8 @@ jobs:
steps:
# Auto assign PR to author
- name: Auto Assign PR to Author
if: github.event_name == 'pull_request' && github.actor != 'dependabot[bot]'
uses: actions/github-script@v8
if: github.event.pull_request.head.repo.fork == false && github.event_name == 'pull_request' && github.actor != 'dependabot[bot]'
uses: actions/github-script@v9
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
@ -31,10 +31,10 @@ jobs:
# Dynamic labeling based on PR/Issue title
- name: Label PRs and Issues
if: github.actor != 'dependabot[bot]'
if: github.event.pull_request.head.repo.fork == false && github.actor != 'dependabot[bot]'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |

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@v5
uses: actions/checkout@v6
- name: Install pnpm
uses: pnpm/action-setup@v6
with:
version: 10
run_install: false
- name: Setup node
uses: actions/setup-node@v5
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

View file

@ -9,22 +9,39 @@ permissions:
contents: write
jobs:
build:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
ref: gh-pages
fetch-depth: 0
- name: Delete directories older than 1 year
- name: Delete directories that don't have existing branch
run: |
for dir in $(find . -mindepth 1 -maxdepth 2 -type d -not -path '*/\.*'); do
if ! git log -1 --since="1 year ago" -- "$dir" | grep -q .; then
echo "Deleting $dir"
rm -rf "$dir"
fi
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

View file

@ -9,20 +9,25 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Install NPM dependencies
run: npm install
uses: actions/checkout@v6
- name: Install pnpm
uses: pnpm/action-setup@v6
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.11.2
uses: svenstaro/upload-release-action@2.11.5
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: stremio-web.zip
asset_name: stremio-web.zip
tag: ${{ github.ref }}
overwrite: true
overwrite: true

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,39 +10,46 @@ 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
### Board
![Board](/screenshots/board.png)
![Board](/assets/screenshots/board.png)
### Discover
![Discover](/screenshots/discover.png)
![Discover](/assets/screenshots/discover.png)
### Meta Details
![Meta Details](/screenshots/metadetails.png)
![Meta Details](/assets/screenshots/metadetails.png)
## License

View file

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

View file

Before

Width:  |  Height:  |  Size: 652 KiB

After

Width:  |  Height:  |  Size: 652 KiB

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 202 KiB

After

Width:  |  Height:  |  Size: 202 KiB

View file

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 2.1 MiB

View file

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View file

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View file

Before

Width:  |  Height:  |  Size: 202 KiB

After

Width:  |  Height:  |  Size: 202 KiB

View file

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View file

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View file

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

View file

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View file

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

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',

59
manifest.json Normal file
View file

@ -0,0 +1,59 @@
{
"name": "Stremio Web",
"short_name": "Stremio",
"description": "Freedom To Stream",
"background_color": "#161523",
"theme_color": "#2a2843",
"orientation": "any",
"display": "standalone",
"display_override": ["standalone"],
"scope": "./",
"start_url": "./",
"icons": [
{
"src": "favicons/icon_256x256.ico",
"sizes": "256x256",
"type": "image/vnd.microsoft.icon"
},
{
"src": "images/maskable_icon_512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "images/maskable_icon_196x196.png",
"sizes": "196x196",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "images/icon_512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "images/icon_196x196.png",
"sizes": "196x196",
"type": "image/png",
"purpose": "any"
}
],
"screenshots": [
{
"src": "screenshots/board_wide.webp",
"sizes": "1440x900",
"type": "image/webp",
"form_factor": "wide",
"label": "Homescreen of Stremio"
},
{
"src": "screenshots/board_narrow.webp",
"sizes": "414x896",
"type": "image/webp",
"form_factor": "narrow",
"label": "Homescreen of Stremio"
}
]
}

15562
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.27",
"version": "5.0.0-beta.34",
"author": "Smart Code OOD",
"private": true,
"license": "gpl-2.0",
@ -11,27 +11,27 @@
"build": "webpack --mode production",
"test": "jest",
"lint": "eslint src",
"scan-translations": "npx jest ./tests/i18nScan.test.js"
"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.4",
"@stremio/stremio-icons": "5.7.1",
"@stremio/stremio-video": "0.0.62",
"@stremio/stremio-core-web": "0.56.4",
"@stremio/stremio-icons": "5.8.0",
"@stremio/stremio-video": "0.0.75",
"a-color-picker": "1.2.1",
"bowser": "2.11.0",
"buffer": "6.0.3",
"classnames": "2.5.1",
"eventemitter3": "5.0.1",
"fast-equals": "^6.0.0",
"filter-invalid-dom-props": "3.0.1",
"hat": "^0.0.3",
"i18next": "^24.0.5",
"langs": "github:Stremio/nodejs-langs",
"lodash.debounce": "4.0.8",
"lodash.intersection": "4.4.0",
"lodash.isequal": "4.5.0",
"lodash.throttle": "4.1.1",
"magnet-uri": "6.2.0",
"prop-types": "15.8.1",
@ -41,7 +41,7 @@
"react-i18next": "^15.1.3",
"react-is": "18.3.1",
"spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6",
"stremio-translations": "github:Stremio/stremio-translations#abe7684165a031755e9aee39da26daa806ba7824",
"stremio-translations": "github:Stremio/stremio-translations#90ea718c18750a0e9cd6824b0ef7c512a41cb90b",
"url": "0.11.4",
"use-long-press": "^3.2.0"
},
@ -50,15 +50,13 @@
"@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",
"clean-webpack-plugin": "4.0.0",
"copy-webpack-plugin": "12.0.2",
"css-loader": "6.11.0",
"cssnano": "7.0.6",
@ -82,7 +80,6 @@
"webpack": "5.97.0",
"webpack-cli": "5.1.4",
"webpack-dev-server": "^5.1.0",
"webpack-pwa-manifest": "^4.3.0",
"workbox-webpack-plugin": "^7.3.0"
}
}

10076
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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 947 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 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 = () => {
@ -159,7 +168,8 @@ const App = () => {
services.core.transport.dispatch({
action: 'Ctx',
args: {
action: 'PullUserFromAPI'
action: 'PullUserFromAPI',
args: {}
}
});
services.core.transport.dispatch({
@ -203,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

@ -22,7 +22,7 @@ const ErrorDialog = ({ className }) => {
<div className={classnames(className, styles['error-container'])}>
<Image
className={styles['error-image']}
src={require('/images/empty.png')}
src={require('/assets/images/empty.png')}
alt={' '}
/>
<div className={styles['error-message']}>

View file

@ -1,7 +1,7 @@
// Copyright (C) 2017-2023 Smart code 203358507
const React = require('react');
const isEqual = require('lodash.isequal');
const { deepEqual } = require('fast-equals');
const { withCoreSuspender, useProfile, useToast } = require('stremio/common');
const { useServices } = require('stremio/services');
@ -18,7 +18,7 @@ const SearchParamsHandler = () => {
setSearchParams((previousSearchParams) => {
const currentSearchParams = Object.fromEntries(searchParams.entries());
return isEqual(previousSearchParams, currentSearchParams) ? previousSearchParams : currentSearchParams;
return deepEqual(previousSearchParams, currentSearchParams) ? previousSearchParams : currentSearchParams;
});
};
@ -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

@ -44,7 +44,7 @@ const ServicesToaster = () => {
}
case 'MagnetParsed': {
toast.show({
type: 'success',
type: 'info',
title: 'Magnet link parsed',
timeout: 4000
});

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

@ -5,7 +5,13 @@
@font-face {
font-family: 'PlusJakartaSans';
src: url('/fonts/PlusJakartaSans.ttf') format('truetype');
src: url('/assets/fonts/PlusJakartaSans.ttf') format('truetype');
}
@font-face {
font-family: 'TwemojiFlags';
src: url('/assets/fonts/TwemojiFlags.woff2') format('woff2');
unicode-range: U+1F1E6-1F1FF;
}
:global {
@ -23,8 +29,8 @@
// HTML sizes
@html-width: ~"calc(max(var(--small-viewport-width), var(--dynamic-viewport-width)))";
@html-height: ~"calc(max(var(--small-viewport-height), var(--dynamic-viewport-height)))";
@html-standalone-width: ~"calc(max(100%, var(--small-viewport-width)))";
@html-standalone-height: ~"calc(max(100%, var(--small-viewport-height)))";
@html-standalone-width: ~"calc(max(100%, var(--large-viewport-width)))";
@html-standalone-height: ~"calc(max(100%, var(--large-viewport-height)))";
// Safe area insets
@safe-area-inset-top: env(safe-area-inset-top, 0rem);
@ -35,7 +41,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 +54,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 +75,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,11 +157,12 @@ svg {
html {
width: @html-width;
height: @html-height;
font-family: 'PlusJakartaSans', 'Arial', 'Helvetica', 'sans-serif';
font-family: 'PlusJakartaSans', 'Arial', 'Helvetica', 'TwemojiFlags', 'sans-serif';
overflow: auto;
overscroll-behavior: none;
user-select: none;
touch-action: manipulation;
background-color: var(--primary-background-color);
-webkit-tap-highlight-color: transparent;
@media (display-mode: standalone) {

View file

@ -2,6 +2,8 @@
const CHROMECAST_RECEIVER_APP_ID = '1634F54B';
const DEFAULT_STREAMING_SERVER_URL = 'http://127.0.0.1:11470/';
const DEFAULT_SUBTITLES_LANGUAGE = 'eng';
const LOCAL_SUBTITLES_LANGUAGE = 'local';
const SUBTITLES_SIZES = [75, 100, 125, 150, 175, 200, 250];
const SUBTITLES_FONTS = ['PlusJakartaSans', 'Arial', 'Halvetica', 'Times New Roman', 'Verdana', 'Courier', 'Lucida Console', 'sans-serif', 'serif', 'monospace'];
const SEEK_TIME_DURATIONS = [3000, 5000, 10000, 15000, 20000, 30000];
@ -97,6 +99,16 @@ const EXTERNAL_PLAYERS = [
value: 'moonplayer',
platforms: ['visionos'],
},
{
label: 'Infuse',
value: 'infuse',
platforms: ['ios', 'visionos', 'macos'],
},
{
label: 'Vidhub',
value: 'vidhub',
platforms: ['ios'],
},
{
label: 'M3U Playlist',
value: 'm3u',
@ -111,6 +123,8 @@ const PROTOCOL = 'stremio:';
module.exports = {
CHROMECAST_RECEIVER_APP_ID,
DEFAULT_STREAMING_SERVER_URL,
DEFAULT_SUBTITLES_LANGUAGE,
LOCAL_SUBTITLES_LANGUAGE,
SUBTITLES_SIZES,
SUBTITLES_FONTS,
SEEK_TIME_DURATIONS,

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

@ -18,7 +18,9 @@ const PlatformProvider = ({ children }: Props) => {
const openExternal = (url: string) => {
try {
const { hostname } = new URL(url);
const isWhitelisted = WHITELISTED_HOSTS.some((host: string) => hostname.endsWith(host));
const isWhitelisted = WHITELISTED_HOSTS.some((host: string) =>
hostname === host || hostname.endsWith('.' + host)
);
const finalUrl = !isWhitelisted ? `https://www.stremio.com/warning#${encodeURIComponent(url)}` : url;
window.open(finalUrl, '_blank');

View file

@ -11,13 +11,21 @@ const APPLE_MOBILE_DEVICES = [
const { userAgent, platform, maxTouchPoints } = globalThis.navigator;
// this detects ipad properly in safari
// while bowser does not
const isIOS = APPLE_MOBILE_DEVICES.includes(platform) || (userAgent.includes('Mac') && 'ontouchend' in document);
// Vision Pro uniquely supports the WebXR Device API (navigator.xr),
// while iPads and iPhones do not — this is the most reliable discriminator.
// Both Vision Pro and iPads (iPadOS 13+) report 'Macintosh' in the UA
// and have maxTouchPoints > 1, so we cannot rely on those alone.
const isMacLikeWithTouch = userAgent.includes('Macintosh') && maxTouchPoints > 1;
const isVisionOS = isMacLikeWithTouch && 'xr' in globalThis.navigator;
// Edge case: iPad is included in this function
// Keep in mind maxTouchPoints for Vision Pro might change in the future
const isVisionOS = userAgent.includes('Macintosh') && maxTouchPoints === 5;
// Detect iOS/iPadOS devices:
// - Older iPads expose 'iPad' in navigator.platform
// - iPadOS 13+ exposes 'MacIntel' but has touch support ('ontouchend' in document)
// - Exclude Vision OS devices which also pass the touch check
const isIOS = !isVisionOS && (
APPLE_MOBILE_DEVICES.includes(platform) ||
(userAgent.includes('Mac') && 'ontouchend' in document)
);
const bowser = Bowser.getParser(userAgent);
const os = bowser.getOSName().toLowerCase();

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,16 @@
import { DependencyList, useCallback, useEffect } from 'react';
import { ShortcutListener, ShortcutName, useShortcuts } from './Shortcuts';
const onShortcut = (name: ShortcutName, listener: ShortcutListener, deps: DependencyList, enabled = true) => {
const shortcuts = useShortcuts();
const listenerCallback = useCallback(listener, deps);
useEffect(() => {
if (!enabled) return;
shortcuts.on(name, listenerCallback);
return () => shortcuts.off(name, listenerCallback);
}, [listenerCallback, enabled]);
};
export default onShortcut;

View file

@ -0,0 +1,124 @@
[
{
"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": "speedDown",
"label": "SETTINGS_SHORTCUT_DECREASE_PLAYBACK_SPEED",
"combos": [["["]]
},
{
"name": "speedUp",
"label": "SETTINGS_SHORTCUT_INCREASE_PLAYBACK_SPEED",
"combos": [["]"]]
},
{
"name": "toggleSubtitles",
"label": "SETTINGS_SHORTCUT_TOGGLE_SUBTITLES",
"combos": [["C"]]
},
{
"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"]]
},
{
"name": "playNext",
"label": "SETTINGS_SHORTCUT_PLAY_NEXT",
"combos": [["Shift", "N"]]
}
]
}
]

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

@ -6,6 +6,7 @@ const React = require('react');
const ToastContext = React.createContext({
show: () => { },
remove: () => { },
clear: () => { }
});

View file

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

View file

@ -42,7 +42,7 @@ const ToastProvider = ({ className, children }) => {
},
show: (item) => {
if (filters.some((filter) => filter(item))) {
return;
return null;
}
const timeout = typeof item.timeout === 'number' && !isNaN(item.timeout) ?
@ -64,6 +64,11 @@ const ToastProvider = ({ className, children }) => {
onClose: itemOnClose
}
});
return id;
},
remove: (id) => {
clearTimeout(id);
dispatch({ type: 'remove', id });
},
clear: () => {
dispatch({ type: 'clear' });

View file

@ -88,7 +88,7 @@
.fade-active {
opacity: 1;
transition: opacity 0.3s cubic-bezier(0.32, 0, 0.67, 0);
transition: opacity 0.1s cubic-bezier(0.32, 0, 0.67, 0);
}
.fade-exit {

View file

@ -1,25 +0,0 @@
// Copyright (C) 2017-2023 Smart code 203358507
const comparatorWithPriorities = (priorities) => {
return (a, b) => {
if (isNaN(priorities[a]) && isNaN(priorities[b])) {
return a.localeCompare(b);
} else if (isNaN(priorities[a])) {
if (priorities[b] === Number.NEGATIVE_INFINITY) {
return -1;
} else {
return 1;
}
} else if (isNaN(priorities[b])) {
if (priorities[a] === Number.NEGATIVE_INFINITY) {
return 1;
} else {
return -1;
}
} else {
return priorities[b] - priorities[a];
}
};
};
module.exports = comparatorWithPriorities;

View file

@ -4,7 +4,7 @@ const { FileDropProvider, onFileDrop } = require('./FileDrop');
const { PlatformProvider, usePlatform } = require('./Platform');
const { ToastProvider, useToast } = require('./Toast');
const { TooltipProvider, Tooltip } = require('./Tooltips');
const comparatorWithPriorities = require('./comparatorWithPriorities');
const { ShortcutsProvider, useShortcuts, onShortcut } = require('./Shortcuts');
const CONSTANTS = require('./CONSTANTS');
const { withCoreSuspender, useCoreSuspender } = require('./CoreSuspender');
const getVisibleChildrenRange = require('./getVisibleChildrenRange');
@ -25,6 +25,7 @@ const { default: useSettings } = require('./useSettings');
const { default: useShell } = require('./useShell');
const useStreamingServer = require('./useStreamingServer');
const { default: useTimeout } = require('./useTimeout');
const { default: usePlayUrl } = require('./usePlayUrl');
const useTorrent = require('./useTorrent');
const useTranslate = require('./useTranslate');
const { default: useOrientation } = require('./useOrientation');
@ -35,11 +36,13 @@ module.exports = {
onFileDrop,
PlatformProvider,
usePlatform,
ShortcutsProvider,
useShortcuts,
onShortcut,
ToastProvider,
useToast,
TooltipProvider,
Tooltip,
comparatorWithPriorities,
CONSTANTS,
withCoreSuspender,
useCoreSuspender,
@ -61,6 +64,7 @@ module.exports = {
useShell,
useStreamingServer,
useTimeout,
usePlayUrl,
useTorrent,
useTranslate,
useOrientation,

View file

@ -3,6 +3,10 @@
"name": "العربية",
"codes": ["ar-AR", "ara"]
},
{
"name": "Беларуская",
"codes": ["be-BY", "bel"]
},
{
"name": "български език",
"codes": ["bg-BG", "bul"]
@ -13,7 +17,7 @@
},
{
"name": "català",
"codes": ["ca-CA", "cat"]
"codes": ["ca-ES", "cat"]
},
{
"name": "čeština",
@ -43,6 +47,10 @@
"name": "español",
"codes": ["es-ES", "spa"]
},
{
"name": "Eesti",
"codes": ["et-EE", "est"]
},
{
"name": "euskara",
"codes": ["eu-ES", "eus"]
@ -51,6 +59,10 @@
"name": "فارسی",
"codes": ["fa-IR", "fas"]
},
{
"name": "Suomi",
"codes": ["fi-FI", "fin"]
},
{
"name": "Français",
"codes": ["fr-FR", "fre"]
@ -87,6 +99,10 @@
"name": "한국어",
"codes": ["ko-KR", "kor"]
},
{
"name": "Lietuvių",
"codes": ["lt-LT", "ltu"]
},
{
"name": "македонски јазик",
"codes": ["mk-MK", "mkd"]
@ -95,6 +111,10 @@
"name": "ဗမာစာ",
"codes": ["my-BM", "mya"]
},
{
"name": "नेपाली",
"codes": ["ne-NP", "nep"]
},
{
"name": "Norsk bokmål",
"codes": ["nb-NO", "nob"]
@ -107,6 +127,10 @@
"name": "Norsk nynorsk",
"codes": ["nn-NO", "nno"]
},
{
"name": "ਪੰਜਾਬੀ",
"codes": ["pa-IN", "pan"]
},
{
"name": "język polski",
"codes": ["pl-PL", "pol"]
@ -119,13 +143,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,10 +163,18 @@
"name": "српски језик",
"codes": ["sr-RS", "srp"]
},
{
"name": "Svenska",
"codes": ["sv-SE", "swe"]
},
{
"name": "తెలుగు",
"codes": ["te-IN", "tel"]
},
{
"name": "தமிழ்",
"codes": ["tl-TM", "tam"]
},
{
"name": "Türkçe",
"codes": ["tr-TR", "tur"]
@ -147,6 +183,10 @@
"name": "українська мова",
"codes": ["uk-UA", "ukr"]
},
{
"name": "اُرْدُو",
"codes": ["ur-PK", "urd"]
},
{
"name": "Tiếng Việt",
"codes": ["vi-VN", "vie"]

View file

@ -6,11 +6,16 @@ const all = langs.all().map((lang) => ({
label: lang.local,
alpha2: lang['1'],
alpha3: [lang['2'], lang['2B'], lang['2T'], lang['3']],
locale: lang['locale'],
ietf: lang['ietf'],
}));
const find = (code: string) => {
return all.find(({ alpha2, alpha3, locale }) => [alpha2, ...alpha3, locale].includes(code));
return all.find(({ alpha2, alpha3, ietf }) => [alpha2, ...alpha3, ietf].includes(code));
};
const toCode = (code: string) => {
const language = find(code);
return language?.[2] ?? code;
};
const label = (code: string) => {
@ -21,5 +26,6 @@ const label = (code: string) => {
export {
all,
find,
toCode,
label,
};

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);
}
}
}, []);

View file

@ -2,7 +2,7 @@
const React = require('react');
const throttle = require('lodash.throttle');
const isEqual = require('lodash.isequal');
const { deepEqual } = require('fast-equals');
const intersection = require('lodash.intersection');
const { useCoreSuspender } = require('stremio/common/CoreSuspender');
const { useRouteFocused } = require('stremio-router');
@ -19,7 +19,7 @@ const useModelState = ({ action, ...args }) => {
const [state, setState] = React.useReducer(
(prevState, nextState) => {
return Object.keys(prevState).reduce((result, key) => {
result[key] = isEqual(prevState[key], nextState[key]) ? prevState[key] : nextState[key];
result[key] = deepEqual(prevState[key], nextState[key]) ? prevState[key] : nextState[key];
return result;
}, {});
},

View file

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

65
src/common/usePlayUrl.ts Normal file
View file

@ -0,0 +1,65 @@
import { useCallback } from 'react';
import magnet from 'magnet-uri';
import { useServices } from 'stremio/services';
import useToast from 'stremio/common/Toast/useToast';
import useTorrent from 'stremio/common/useTorrent';
import useStreamingServer from 'stremio/common/useStreamingServer';
const HTTP_REGEX = /^https?:\/\/.+/i;
const usePlayUrl = () => {
const { core } = useServices();
const toast = useToast();
const { createTorrentFromMagnet } = useTorrent();
const streamingServer = useStreamingServer();
const handlePlayUrl = useCallback(async (text: string): Promise<boolean> => {
if (!text || !text.trim()) return false;
const trimmed = text.trim();
if (HTTP_REGEX.test(trimmed)) {
toast.show({
type: 'success',
title: 'Loading HTTP stream…',
timeout: 3000
});
try {
const encoded = await core.transport.encodeStream({ url: trimmed });
if (typeof encoded === 'string') {
window.location.hash = `#/player/${encodeURIComponent(encoded)}`;
return true;
}
} catch (e) {
console.error('Failed to encode stream:', e);
}
toast.show({
type: 'error',
title: 'Failed to load HTTP stream.',
timeout: 5000
});
return false;
}
const parsed = magnet.decode(trimmed);
if (parsed && typeof parsed.infoHash === 'string') {
const serverReady = streamingServer.settings !== null
&& streamingServer.settings.type === 'Ready';
if (!serverReady) {
toast.show({
type: 'error',
title: 'Streaming server is not available. Cannot play magnet links.',
timeout: 5000
});
return false;
}
createTorrentFromMagnet(trimmed);
return true;
}
return false;
}, [streamingServer.settings, createTorrentFromMagnet]);
return { handlePlayUrl };
};
export default usePlayUrl;

View file

@ -6,14 +6,22 @@ const { useServices } = require('stremio/services');
const useToast = require('stremio/common/Toast/useToast');
const useStreamingServer = require('stremio/common/useStreamingServer');
const CREATE_TORRENT_TIMEOUT = 20000;
const useTorrent = () => {
const { core } = useServices();
const streamingServer = useStreamingServer();
const toast = useToast();
const createTorrentTimeout = React.useRef(null);
const parsingToastId = React.useRef(null);
const createTorrentFromMagnet = React.useCallback((text) => {
const parsed = magnet.decode(text);
if (parsed && typeof parsed.infoHash === 'string') {
parsingToastId.current = toast.show({
type: 'success',
title: 'Loading magnet link…',
timeout: CREATE_TORRENT_TIMEOUT
});
core.transport.dispatch({
action: 'StreamingServer',
args: {
@ -23,12 +31,13 @@ const useTorrent = () => {
});
clearTimeout(createTorrentTimeout.current);
createTorrentTimeout.current = setTimeout(() => {
toast.remove(parsingToastId.current);
toast.show({
type: 'error',
title: 'It\'s taking a long time to get metadata from the torrent.',
timeout: 10000
title: 'Failed to parse magnet link.',
timeout: 8000
});
}, 10000);
}, CREATE_TORRENT_TIMEOUT);
}
}, []);
React.useEffect(() => {
@ -36,6 +45,7 @@ const useTorrent = () => {
const [, { type }] = streamingServer.torrent;
if (type === 'Ready') {
clearTimeout(createTorrentTimeout.current);
toast.remove(parsingToastId.current);
}
}
}, [streamingServer.torrent]);

View file

@ -8,7 +8,7 @@
@width-mobile: 3rem;
.ratings-container {
.group-container {
display: flex;
flex-direction: row;
align-items: center;
@ -17,6 +17,7 @@
border-radius: 2rem;
height: @height;
width: fit-content;
backdrop-filter: blur(5px);
.icon-container {
display: flex;
@ -45,7 +46,7 @@
}
@media @phone-landscape {
.ratings-container {
.group-container {
height: @height-mobile;
.icon-container {

View file

@ -0,0 +1,45 @@
// Copyright (C) 2017-2025 Smart code 203358507
import classNames from 'classnames';
import React from 'react';
import Icon from '@stremio/stremio-icons/react';
import { Tooltip } from 'stremio/common/Tooltips';
import styles from './ActionsGroup.less';
type Item = {
icon: string;
label?: string;
filled?: string;
disabled?: boolean;
className?: string;
onClick?: () => void;
};
type Props = {
items: Item[];
className?: string;
};
const ActionsGroup = ({ items, className }: Props) => {
return (
<div className={classNames(styles['group-container'], className)}>
{
items.map((item, index) => (
<div
key={index}
className={classNames(styles['icon-container'], item.className, { [styles['disabled']]: item.disabled })}
onClick={item.onClick}
>
{
item.label &&
<Tooltip label={item.label} position={'top'} />
}
<Icon name={item.icon} className={styles['icon']} />
</div>
))
}
</div>
);
};
export default ActionsGroup;

View file

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

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

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

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

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,5 +1,6 @@
import React, { memo, RefObject, useCallback, useEffect, useMemo, useState } from 'react';
import { createPortal } from 'react-dom';
import Transition from '../Transition';
import styles from './ContextMenu.less';
const PADDING = 8;
@ -47,7 +48,6 @@ const ContextMenu = ({ children, on, autoClose }: Props) => {
}, [position, containerSize]);
const close = () => {
setPosition([0, 0]);
setActive(false);
};
@ -78,23 +78,25 @@ const ContextMenu = ({ children, on, autoClose }: Props) => {
};
}, [on]);
return active && createPortal((
<div
className={styles['context-menu-container']}
onMouseDown={close}
onTouchStart={close}
>
return createPortal((
<Transition when={active} name={'fade'}>
<div
ref={ref}
className={styles['context-menu']}
style={style}
onMouseDown={stopPropagation}
onTouchStart={stopPropagation}
onClick={onClick}
className={styles['context-menu-container']}
onMouseDown={close}
onTouchStart={close}
>
{children}
<div
ref={ref}
className={styles['context-menu']}
style={style}
onMouseDown={stopPropagation}
onTouchStart={stopPropagation}
onClick={onClick}
>
{children}
</div>
</div>
</div>
</Transition>
), document.body);
};

View file

@ -5,24 +5,11 @@ const PropTypes = require('prop-types');
const { useServices } = require('stremio/services');
const LibItem = require('stremio/components/LibItem');
const ContinueWatchingItem = ({ _id, notifications, deepLinks, ...props }) => {
const ContinueWatchingItem = ({ _id, notifications, ...props }) => {
const { core } = useServices();
const onClick = React.useCallback(() => {
if (deepLinks?.metaDetailsVideos ?? deepLinks?.metaDetailsStreams) {
window.location = deepLinks?.metaDetailsVideos ?? deepLinks?.metaDetailsStreams;
}
}, [deepLinks]);
const onPlayClick = React.useCallback((event) => {
event.stopPropagation();
if (deepLinks?.player ?? deepLinks?.metaDetailsStreams ?? deepLinks?.metaDetailsVideos) {
window.location = deepLinks?.player ?? deepLinks?.metaDetailsStreams ?? deepLinks?.metaDetailsVideos;
}
}, [deepLinks]);
const onDismissClick = React.useCallback((event) => {
event.stopPropagation();
event.preventDefault();
if (typeof _id === 'string') {
core.transport.dispatch({
action: 'Ctx',
@ -47,8 +34,6 @@ const ContinueWatchingItem = ({ _id, notifications, deepLinks, ...props }) => {
_id={_id}
posterChangeCursor={true}
notifications={notifications}
onClick={onClick}
onPlayClick={onPlayClick}
onDismissClick={onDismissClick}
/>
);

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

@ -7,7 +7,7 @@ type Props = {
src: string,
alt: string,
fallbackSrc: string,
renderFallback: () => void,
renderFallback: () => React.ReactNode,
onError: (event: React.SyntheticEvent<HTMLImageElement>) => void,
};

View file

@ -29,7 +29,7 @@ const LibItem = ({ _id, removable, notifications, watched, ...props }) => {
case 'details':
return props.deepLinks && (typeof props.deepLinks.metaDetailsVideos === 'string' || typeof props.deepLinks.metaDetailsStreams === 'string');
case 'watched':
return props.deepLinks && (typeof props.deepLinks.metaDetailsVideos === 'string' || typeof props.deepLinks.metaDetailsStreams === 'string');
return typeof watched !== 'undefined' && props.deepLinks && (typeof props.deepLinks.metaDetailsVideos === 'string' || typeof props.deepLinks.metaDetailsStreams === 'string');
case 'dismiss':
return typeof _id === 'string' && props.progress !== null && !isNaN(props.progress) && props.progress > 0;
case 'remove':
@ -119,6 +119,16 @@ const LibItem = ({ _id, removable, notifications, watched, ...props }) => {
}
}, [_id, props.deepLinks, props.optionOnSelect]);
const onPlayClick = React.useMemo(() => {
if (props.deepLinks && typeof props.deepLinks.player === 'string') {
return (event) => {
event.preventDefault();
window.location = props.deepLinks.player;
};
}
return null;
}, [props.deepLinks]);
return (
<MetaItem
{...props}
@ -126,6 +136,7 @@ const LibItem = ({ _id, removable, notifications, watched, ...props }) => {
newVideos={newVideos}
options={options}
optionOnSelect={optionOnSelect}
onPlayClick={onPlayClick}
/>
);
};

View file

@ -18,14 +18,14 @@ const MetaItem = React.memo(({ className, type, name, poster, posterShape, poste
const [menuOpen, onMenuOpen, onMenuClose] = useBinaryState(false);
const href = React.useMemo(() => {
return deepLinks ?
typeof deepLinks.player === 'string' ?
deepLinks.player
typeof deepLinks.metaDetailsStreams === 'string' ?
deepLinks.metaDetailsStreams
:
typeof deepLinks.metaDetailsStreams === 'string' ?
deepLinks.metaDetailsStreams
typeof deepLinks.metaDetailsVideos === 'string' ?
deepLinks.metaDetailsVideos
:
typeof deepLinks.metaDetailsVideos === 'string' ?
deepLinks.metaDetailsVideos
typeof deepLinks.player === 'string' ?
deepLinks.player
:
null
:

View file

@ -66,6 +66,15 @@
.icon-container {
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

@ -8,6 +8,7 @@ const { useTranslation } = require('react-i18next');
const { default: Icon } = require('@stremio/stremio-icons/react');
const { default: Button } = require('stremio/components/Button');
const { default: Image } = require('stremio/components/Image');
const { default: ActionsGroup } = require('stremio/components/ActionsGroup');
const ModalDialog = require('stremio/components/ModalDialog');
const SharePrompt = require('stremio/components/SharePrompt');
const CONSTANTS = require('stremio/common/CONSTANTS');
@ -25,7 +26,7 @@ const ALLOWED_LINK_REDIRECTS = [
routesRegexp.metadetails.regexp
];
const MetaPreview = React.forwardRef(({ className, compact, name, logo, background, runtime, releaseInfo, released, description, deepLinks, links, trailerStreams, inLibrary, toggleInLibrary, ratingInfo }, ref) => {
const MetaPreview = React.forwardRef(({ className, compact, name, logo, background, runtime, releaseInfo, released, description, deepLinks, links, trailerStreams, inLibrary, toggleInLibrary, watched, toggleWatched, ratingInfo }, ref) => {
const { t } = useTranslation();
const [shareModalOpen, openShareModal, closeShareModal] = useBinaryState(false);
const linksGroups = React.useMemo(() => {
@ -98,6 +99,18 @@ const MetaPreview = React.forwardRef(({ className, compact, name, logo, backgrou
const renderLogoFallback = React.useCallback(() => (
<div className={styles['logo-placeholder']}>{name}</div>
), [name]);
const metaItemActions = React.useMemo(() => [
{
icon: inLibrary ? 'remove-from-library' : 'add-to-library',
label: inLibrary ? t('REMOVE_FROM_LIB') : t('ADD_TO_LIB'),
onClick: typeof toggleInLibrary === 'function' ? toggleInLibrary : null,
},
{
icon: watched ? 'eye-off' : 'eye',
label: watched ? t('CTX_MARK_UNWATCHED') : t('CTX_MARK_WATCHED'),
onClick: typeof toggleWatched === 'function' ? toggleWatched : undefined,
},
], [inLibrary, watched, toggleInLibrary, toggleWatched]);
return (
<div className={classnames(className, styles['meta-preview-container'], { [styles['compact']]: compact })} ref={ref}>
{
@ -195,19 +208,6 @@ const MetaPreview = React.forwardRef(({ className, compact, name, logo, backgrou
}
</div>
<div className={styles['action-buttons-container']}>
{
typeof toggleInLibrary === 'function' ?
<ActionButton
className={styles['action-button']}
icon={inLibrary ? 'remove-from-library' : 'add-to-library'}
label={inLibrary ? t('REMOVE_FROM_LIB') : t('ADD_TO_LIB')}
tooltip={compact}
tabIndex={compact ? -1 : 0}
onClick={toggleInLibrary}
/>
:
null
}
{
typeof trailerHref === 'string' ?
<ActionButton
@ -221,6 +221,11 @@ const MetaPreview = React.forwardRef(({ className, compact, name, logo, backgrou
:
null
}
{
typeof toggleInLibrary === 'function' && typeof toggleWatched === 'function'
? <ActionsGroup items={metaItemActions} className={styles['group-container']} />
: null
}
{
typeof showHref === 'string' && compact ?
<ActionButton
@ -237,7 +242,7 @@ const MetaPreview = React.forwardRef(({ className, compact, name, logo, backgrou
!compact && ratingInfo !== null ?
<Ratings
ratingInfo={ratingInfo}
className={styles['ratings']}
className={styles['group-container']}
/>
:
null
@ -298,6 +303,8 @@ MetaPreview.propTypes = {
trailerStreams: PropTypes.array,
inLibrary: PropTypes.bool,
toggleInLibrary: PropTypes.func,
watched: PropTypes.bool,
toggleWatched: PropTypes.func,
ratingInfo: PropTypes.object,
};

View file

@ -2,9 +2,7 @@
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';
import { ActionsGroup } from 'stremio/components';
type Props = {
metaId?: string;
@ -16,15 +14,21 @@ const Ratings = ({ ratingInfo, className }: Props) => {
const { onLiked, onLoved, liked, loved } = useRating(ratingInfo);
const disabled = useMemo(() => ratingInfo?.type !== 'Ready', [ratingInfo]);
const items = useMemo(() => [
{
icon: liked ? 'thumbs-up' : 'thumbs-up-outline',
disabled,
onClick: onLiked,
},
{
icon: loved ? 'heart' : 'heart-outline',
disabled,
onClick: onLoved,
},
], [liked, loved, disabled]);
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>
<ActionsGroup items={items} className={className} />
);
};

View file

@ -32,7 +32,7 @@
.action-buttons-container {
justify-content: space-between;
.action-button:not(:last-child) {
.action-button:not(:last-child), .group-container:not(:last-child) {
margin-right: 0;
}
}
@ -207,11 +207,20 @@
}
}
}
}
.group-container {
margin-bottom: 1rem;
.ratings {
margin-bottom: 1rem;
margin-right: 1rem;
&:global(.wide) {
width: auto;
padding: 0 2rem;
border-radius: 4rem;
}
&:not(:last-child) {
margin-right: 1rem;
}
}
}
}
@ -233,17 +242,13 @@
padding-top: 1.5rem;
gap: 0.5rem;
.action-button {
.action-button, .group-container {
padding: 0 1.5rem !important;
margin-right: 0rem !important;
height: 3rem;
border-radius: 2rem;
}
}
.ratings {
margin-right: 0;
}
}
}
@ -272,6 +277,10 @@
&::-webkit-scrollbar {
display: none;
}
.action-button {
padding: 0 1rem !important;
}
}
}

View file

@ -35,7 +35,7 @@ const HorizontalNavBar = React.memo(({ className, route, query, title, backButto
<div className={styles['logo-container']}>
<Image
className={styles['logo']}
src={require('/images/stremio_symbol.png')}
src={require('/assets/images/stremio_symbol.png')}
alt={' '}
/>
</div>

View file

@ -10,7 +10,8 @@ const { Button } = require('stremio/components');
const { default: useFullscreen } = require('stremio/common/useFullscreen');
const useProfile = require('stremio/common/useProfile');
const usePWA = require('stremio/common/usePWA');
const useTorrent = require('stremio/common/useTorrent');
const { default: usePlayUrl } = require('stremio/common/usePlayUrl');
const useToast = require('stremio/common/Toast/useToast');
const { withCoreSuspender } = require('stremio/common/CoreSuspender');
const useStreamingServer = require('stremio/common/useStreamingServer');
const styles = require('./styles');
@ -20,7 +21,8 @@ const NavMenuContent = ({ onClick }) => {
const { core } = useServices();
const profile = useProfile();
const streamingServer = useStreamingServer();
const { createTorrentFromMagnet } = useTorrent();
const { handlePlayUrl } = usePlayUrl();
const toast = useToast();
const [fullscreen, requestFullscreen, exitFullscreen] = useFullscreen();
const [isIOSPWA, isAndroidPWA] = usePWA();
const streamingServerWarningDismissed = React.useMemo(() => {
@ -40,11 +42,18 @@ const NavMenuContent = ({ onClick }) => {
const onPlayMagnetLinkClick = React.useCallback(async () => {
try {
const clipboardText = await navigator.clipboard.readText();
createTorrentFromMagnet(clipboardText);
const handled = await handlePlayUrl(clipboardText);
if (!handled) {
toast.show({
type: 'error',
title: 'Clipboard does not contain a valid URL or magnet link.',
timeout: 5000
});
}
} catch(e) {
console.error(e);
}
}, []);
}, [handlePlayUrl]);
return (
<div className={classnames(styles['nav-menu-container'], 'animation-fade-in', { [styles['with-warning']]: !streamingServerWarningDismissed } )} onClick={onClick}>
<div className={styles['user-info-container']}>
@ -52,12 +61,12 @@ const NavMenuContent = ({ onClick }) => {
className={styles['avatar-container']}
style={{
backgroundImage: profile.auth === null ?
`url('${require('/images/anonymous.png')}')`
`url('${require('/assets/images/anonymous.png')}')`
:
profile.auth.user.avatar ?
`url('${profile.auth.user.avatar}')`
:
`url('${require('/images/default_avatar.png')}')`
`url('${require('/assets/images/default_avatar.png')}')`
}}
/>
<div className={styles['user-info-details']}>

View file

@ -9,7 +9,7 @@ const { default: Icon } = require('@stremio/stremio-icons/react');
const { useRouteFocused } = require('stremio-router');
const Button = require('stremio/components/Button').default;
const TextInput = require('stremio/components/TextInput').default;
const useTorrent = require('stremio/common/useTorrent');
const { default: usePlayUrl } = require('stremio/common/usePlayUrl');
const { withCoreSuspender } = require('stremio/common/CoreSuspender');
const useSearchHistory = require('./useSearchHistory');
const useLocalSearch = require('./useLocalSearch');
@ -21,7 +21,7 @@ const SearchBar = React.memo(({ className, query, active }) => {
const routeFocused = useRouteFocused();
const searchHistory = useSearchHistory();
const localSearch = useLocalSearch();
const { createTorrentFromMagnet } = useTorrent();
const { handlePlayUrl } = usePlayUrl();
const [historyOpen, openHistory, closeHistory, ] = useBinaryState(query === null ? true : false);
const [currentQuery, setCurrentQuery] = React.useState(query || '');
@ -52,12 +52,14 @@ const SearchBar = React.memo(({ className, query, active }) => {
const value = searchInputRef.current.value;
setCurrentQuery(value);
openHistory();
try {
createTorrentFromMagnet(value);
} catch (error) {
console.error('Failed to create torrent from magnet:', error);
}, []);
const queryInputOnPaste = React.useCallback((event) => {
const pasted = event.clipboardData.getData('text');
if (pasted) {
handlePlayUrl(pasted);
}
}, [createTorrentFromMagnet]);
}, [handlePlayUrl]);
const queryInputOnSubmit = React.useCallback((event) => {
event.preventDefault();
@ -108,6 +110,7 @@ const SearchBar = React.memo(({ className, query, active }) => {
defaultValue={query}
tabIndex={-1}
onChange={queryInputOnChange}
onPaste={queryInputOnPaste}
onSubmit={queryInputOnSubmit}
onClick={openHistory}
/>

View file

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

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

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