mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-04-14 00:40:25 +00:00
Merge branch 'development' into feat/989/watched-on-discover-and-details
This commit is contained in:
commit
b058eb536e
10 changed files with 115 additions and 16 deletions
|
|
@ -44,7 +44,7 @@ const ServicesToaster = () => {
|
|||
}
|
||||
case 'MagnetParsed': {
|
||||
toast.show({
|
||||
type: 'success',
|
||||
type: 'info',
|
||||
title: 'Magnet link parsed',
|
||||
timeout: 4000
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ const React = require('react');
|
|||
|
||||
const ToastContext = React.createContext({
|
||||
show: () => { },
|
||||
remove: () => { },
|
||||
clear: () => { }
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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' });
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
@ -63,6 +64,7 @@ module.exports = {
|
|||
useShell,
|
||||
useStreamingServer,
|
||||
useTimeout,
|
||||
usePlayUrl,
|
||||
useTorrent,
|
||||
useTranslate,
|
||||
useOrientation,
|
||||
|
|
|
|||
65
src/common/usePlayUrl.ts
Normal file
65
src/common/usePlayUrl.ts
Normal 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;
|
||||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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']}>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -52,6 +52,9 @@ function CoreTransport(args) {
|
|||
this.decodeStream = async function(stream) {
|
||||
return bridge.call(['decodeStream'], [stream]);
|
||||
};
|
||||
this.encodeStream = async function(stream) {
|
||||
return bridge.call(['encodeStream'], [stream]);
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = CoreTransport;
|
||||
|
|
|
|||
1
src/services/Core/types.d.ts
vendored
1
src/services/Core/types.d.ts
vendored
|
|
@ -17,6 +17,7 @@ interface CoreTransport {
|
|||
getState: (model: string) => Promise<object>,
|
||||
dispatch: (action: Action, model?: string) => Promise<void>,
|
||||
decodeStream: (stream: string) => Promise<Stream>,
|
||||
encodeStream: (stream: object) => Promise<string>,
|
||||
analytics: (event: AnalyticsEvent) => Promise<void>,
|
||||
on: (name: string, listener: () => void) => void,
|
||||
off: (name: string, listener: () => void) => void,
|
||||
|
|
|
|||
Loading…
Reference in a new issue