diff --git a/.well-known/apple-app-site-association b/.well-known/apple-app-site-association
new file mode 100644
index 000000000..54b0dd1bf
--- /dev/null
+++ b/.well-known/apple-app-site-association
@@ -0,0 +1,67 @@
+{
+ "applinks": {
+ "apps": [],
+ "details": [
+ {
+ "appID": "9EWRZ4QP3J.com.stremio.one",
+ "paths": [
+ "/",
+ "/#/player/*",
+ "/#/discover/*",
+ "/#/detail/*",
+ "/#/library/*",
+ "/#/addons/*",
+ "/#/settings",
+ "/#/search/*"
+ ],
+ "components": [
+ {
+ "/": "/",
+ "#": "/player/*",
+ "comment": "Matches deep link for player"
+ },
+ {
+ "/": "/",
+ "#": "/discover/*",
+ "comment": "Matches deep link for discover"
+ },
+ {
+ "/": "/",
+ "#": "/detail/*",
+ "comment": "Matches deep link for detail"
+ },
+ {
+ "/": "/",
+ "#": "/library/*",
+ "comment": "Matches deep link for library"
+ },
+ {
+ "/": "/",
+ "#": "/addons/*",
+ "comment": "Matches deep link for addons"
+ },
+ {
+ "/": "/",
+ "#": "/settings",
+ "comment": "Matches deep link for settings"
+ },
+ {
+ "/": "/",
+ "#": "/search/*",
+ "comment": "Matches deep link for search"
+ }
+ ]
+ }
+ ]
+ },
+ "activitycontinuation": {
+ "apps": [
+ "9EWRZ4QP3J.com.stremio.one"
+ ]
+ },
+ "webcredentials": {
+ "apps": [
+ "9EWRZ4QP3J.com.stremio.one"
+ ]
+ }
+}
diff --git a/package-lock.json b/package-lock.json
index 0824f882f..3fca2228f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,7 +12,7 @@
"@babel/runtime": "7.26.0",
"@sentry/browser": "8.42.0",
"@stremio/stremio-colors": "5.2.0",
- "@stremio/stremio-core-web": "0.49.0",
+ "@stremio/stremio-core-web": "0.49.2",
"@stremio/stremio-icons": "5.4.1",
"@stremio/stremio-video": "0.0.53",
"a-color-picker": "1.2.1",
@@ -36,7 +36,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#62bcc6e8f44258203c7375af59210771efb6f634",
+ "stremio-translations": "github:Stremio/stremio-translations#4bb1b7e31df274f538b8588c2a2b360d6e14ab27",
"url": "0.11.4",
"use-long-press": "^3.2.0"
},
@@ -3371,9 +3371,9 @@
"integrity": "sha512-dYlPgu9W/H7c9s1zmW5tiDnRenaUa4Hg1QCyOg1lhOcgSfM/bVTi5nnqX+IfvGTTUNA0zgzh8hI3o3miwnZxTg=="
},
"node_modules/@stremio/stremio-core-web": {
- "version": "0.49.0",
- "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.49.0.tgz",
- "integrity": "sha512-oxJRVAE6z6Eh1B0qomdz6L2CVaTkwt70kDNC1TmHyGNo+Hhp2RaMlygqBKvBLXyHUXi82R67Mc11gT/JqlmaMw==",
+ "version": "0.49.2",
+ "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.49.2.tgz",
+ "integrity": "sha512-IYU+pdHkq4iEfqZ9G+DFZheIE53nY8XyhI1OJLvZp68/4ntRwssXwfj9InHK2Wau20fH+oV2KD1ZWb0CsTLqPA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "7.24.1"
@@ -13372,9 +13372,9 @@
}
},
"node_modules/stremio-translations": {
- "version": "1.44.9",
- "resolved": "git+ssh://git@github.com/Stremio/stremio-translations.git#62bcc6e8f44258203c7375af59210771efb6f634",
- "integrity": "sha512-8Sc5Qvd4IiObwGXkmj1XFXFavUc15My5po6G48HHDBbp42SVc5I/t7h+1yxW1A81byyBCXbL23a9iU9v49vpQA==",
+ "version": "1.44.10",
+ "resolved": "git+ssh://git@github.com/Stremio/stremio-translations.git#4bb1b7e31df274f538b8588c2a2b360d6e14ab27",
+ "integrity": "sha512-+RLkoytMyqP90mn9Wkh1MhwB2fxVuvMxsxxceGnFgYlyyEL8fxuHTRnSaBjWBw+xFtsaeMLmDfA1n3l+UEzg4A==",
"license": "MIT"
},
"node_modules/string_decoder": {
diff --git a/package.json b/package.json
index 4cab1781f..afed7e885 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,7 @@
"@babel/runtime": "7.26.0",
"@sentry/browser": "8.42.0",
"@stremio/stremio-colors": "5.2.0",
- "@stremio/stremio-core-web": "0.49.0",
+ "@stremio/stremio-core-web": "0.49.2",
"@stremio/stremio-icons": "5.4.1",
"@stremio/stremio-video": "0.0.53",
"a-color-picker": "1.2.1",
@@ -40,7 +40,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#62bcc6e8f44258203c7375af59210771efb6f634",
+ "stremio-translations": "github:Stremio/stremio-translations#4bb1b7e31df274f538b8588c2a2b360d6e14ab27",
"url": "0.11.4",
"use-long-press": "^3.2.0"
},
diff --git a/src/App/App.js b/src/App/App.js
index d3a1ce188..803515b09 100644
--- a/src/App/App.js
+++ b/src/App/App.js
@@ -100,14 +100,29 @@ const App = () => {
};
}, []);
- // Handle shell window visibility changed event
+ // Handle shell events
React.useEffect(() => {
const onWindowVisibilityChanged = (state) => {
setWindowHidden(state.visible === false && state.visibility === 0);
};
+ const onOpenMedia = (data) => {
+ if (data.startsWith('stremio:///')) return;
+ if (data.startsWith('stremio://')) {
+ const transportUrl = data.replace('stremio://', 'https://');
+ if (URL.canParse(transportUrl)) {
+ window.location.href = `#/addons?addon=${encodeURIComponent(transportUrl)}`;
+ }
+ }
+ };
+
shell.on('win-visibility-changed', onWindowVisibilityChanged);
- return () => shell.off('win-visibility-changed', onWindowVisibilityChanged);
+ shell.on('open-media', onOpenMedia);
+
+ return () => {
+ shell.off('win-visibility-changed', onWindowVisibilityChanged);
+ shell.off('open-media', onOpenMedia);
+ };
}, []);
React.useEffect(() => {
diff --git a/src/components/MainNavBars/MainNavBars.less b/src/components/MainNavBars/MainNavBars.less
index ca816a72f..10538cd7c 100644
--- a/src/components/MainNavBars/MainNavBars.less
+++ b/src/components/MainNavBars/MainNavBars.less
@@ -34,7 +34,7 @@
bottom: 0;
left: var(--vertical-nav-bar-size);
z-index: 0;
- overflow: scroll;
+ overflow: hidden;
}
}
diff --git a/src/components/Slider/Slider.js b/src/components/Slider/Slider.js
index 0e5c96c97..d0d4721b8 100644
--- a/src/components/Slider/Slider.js
+++ b/src/components/Slider/Slider.js
@@ -31,14 +31,18 @@ const Slider = ({ className, value, buffered, minimumValue, maximumValue, disabl
const retainThumb = React.useCallback(() => {
window.addEventListener('blur', onBlur);
window.addEventListener('mouseup', onMouseUp);
+ window.addEventListener('touchend', onTouchEnd);
window.addEventListener('mousemove', onMouseMove);
+ window.addEventListener('touchmove', onTouchMove);
document.documentElement.className = classnames(document.documentElement.className, styles['active-slider-within']);
}, []);
const releaseThumb = React.useCallback(() => {
cancelThumbAnimation();
window.removeEventListener('blur', onBlur);
window.removeEventListener('mouseup', onMouseUp);
+ window.removeEventListener('touchend', onTouchEnd);
window.removeEventListener('mousemove', onMouseMove);
+ window.removeEventListener('touchmove', onTouchMove);
const classList = document.documentElement.className.split(' ');
const classIndex = classList.indexOf(styles['active-slider-within']);
if (classIndex !== -1) {
@@ -85,6 +89,36 @@ const Slider = ({ className, value, buffered, minimumValue, maximumValue, disabl
retainThumb();
}, []);
+ const onTouchStart = React.useCallback((event) => {
+ const touch = event.touches[0];
+ const value = calculateValueForMouseX(touch.clientX);
+ if (typeof onSlideRef.current === 'function') {
+ onSlideRef.current(value);
+ }
+
+ retainThumb();
+ event.preventDefault();
+ }, []);
+ const onTouchMove = React.useCallback((event) => {
+ requestThumbAnimation(() => {
+ const touch = event.touches[0];
+ const value = calculateValueForMouseX(touch.clientX);
+ if (typeof onSlideRef.current === 'function') {
+ onSlideRef.current(value);
+ }
+ });
+
+ event.preventDefault();
+ }, []);
+ const onTouchEnd = React.useCallback((event) => {
+ const touch = event.changedTouches[0];
+ const value = calculateValueForMouseX(touch.clientX);
+ if (typeof onCompleteRef.current === 'function') {
+ onCompleteRef.current(value);
+ }
+
+ releaseThumb();
+ }, []);
React.useLayoutEffect(() => {
if (!routeFocused || disabled) {
releaseThumb();
@@ -98,7 +132,7 @@ const Slider = ({ className, value, buffered, minimumValue, maximumValue, disabl
const thumbPosition = Math.max(0, Math.min(1, (valueRef.current - minimumValueRef.current) / (maximumValueRef.current - minimumValueRef.current)));
const bufferedPosition = Math.max(0, Math.min(1, (bufferedRef.current - minimumValueRef.current) / (maximumValueRef.current - minimumValueRef.current)));
return (
-
+
diff --git a/src/components/Slider/styles.less b/src/components/Slider/styles.less
index 2bdbbfe72..41478924e 100644
--- a/src/components/Slider/styles.less
+++ b/src/components/Slider/styles.less
@@ -46,7 +46,8 @@ html.active-slider-within {
width: 100%;
height: var(--track-size);
border-radius: var(--track-size);
- background-color: var(--overlay-color);
+ background-color: var(--primary-accent-color);
+ opacity: 0.2;
&.audio-boost {
opacity: 0.3;
diff --git a/src/components/Video/Video.js b/src/components/Video/Video.js
index 0bcb569a5..94fbefcc7 100644
--- a/src/components/Video/Video.js
+++ b/src/components/Video/Video.js
@@ -8,11 +8,13 @@ const { useRouteFocused } = require('stremio-router');
const { default: Icon } = require('@stremio/stremio-icons/react');
const { Button, Image, Popup } = require('stremio/components');
const useBinaryState = require('stremio/common/useBinaryState');
+const useProfile = require('stremio/common/useProfile');
const VideoPlaceholder = require('./VideoPlaceholder');
const styles = require('./styles');
const Video = ({ className, id, title, thumbnail, season, episode, released, upcoming, watched, progress, scheduled, seasonWatched, deepLinks, onMarkVideoAsWatched, onMarkSeasonAsWatched, ...props }) => {
const routeFocused = useRouteFocused();
+ const profile = useProfile();
const [menuOpen, , closeMenu, toggleMenu] = useBinaryState(false);
const popupLabelOnMouseUp = React.useCallback((event) => {
if (!event.nativeEvent.togglePopupPrevented) {
@@ -66,13 +68,14 @@ const Video = ({ className, id, title, thumbnail, season, episode, released, upc
}
}, [deepLinks]);
const renderLabel = React.useMemo(() => function renderLabel({ className, id, title, thumbnail, episode, released, upcoming, watched, progress, scheduled, children, ...props }) {
+ const blurThumbnail = profile.settings.hideSpoilers && season && episode && !watched;
return (