mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-03-11 17:15:48 +00:00
Merge branch 'development' of https://github.com/Stremio/stremio-web into development
Some checks are pending
Build / build (push) Waiting to run
Some checks are pending
Build / build (push) Waiting to run
This commit is contained in:
commit
54b017c39f
6 changed files with 60 additions and 4 deletions
|
|
@ -8,6 +8,7 @@ const { default: Icon } = require('@stremio/stremio-icons/react');
|
||||||
const { usePlatform, useBinaryState, withCoreSuspender } = require('stremio/common');
|
const { usePlatform, useBinaryState, withCoreSuspender } = require('stremio/common');
|
||||||
const { AddonDetailsModal, Button, Image, MainNavBars, ModalDialog, SearchBar, SharePrompt, TextInput, MultiselectMenu } = require('stremio/components');
|
const { AddonDetailsModal, Button, Image, MainNavBars, ModalDialog, SearchBar, SharePrompt, TextInput, MultiselectMenu } = require('stremio/components');
|
||||||
const { useServices } = require('stremio/services');
|
const { useServices } = require('stremio/services');
|
||||||
|
const useToast = require('stremio/common/Toast/useToast');
|
||||||
const Addon = require('./Addon');
|
const Addon = require('./Addon');
|
||||||
const useInstalledAddons = require('./useInstalledAddons');
|
const useInstalledAddons = require('./useInstalledAddons');
|
||||||
const useRemoteAddons = require('./useRemoteAddons');
|
const useRemoteAddons = require('./useRemoteAddons');
|
||||||
|
|
@ -20,6 +21,7 @@ const Addons = ({ urlParams, queryParams }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const platform = usePlatform();
|
const platform = usePlatform();
|
||||||
const { core } = useServices();
|
const { core } = useServices();
|
||||||
|
const toast = useToast();
|
||||||
const installedAddons = useInstalledAddons(urlParams);
|
const installedAddons = useInstalledAddons(urlParams);
|
||||||
const remoteAddons = useRemoteAddons(urlParams);
|
const remoteAddons = useRemoteAddons(urlParams);
|
||||||
const [addonDetailsTransportUrl, setAddonDetailsTransportUrl] = useAddonDetailsTransportUrl(urlParams, queryParams);
|
const [addonDetailsTransportUrl, setAddonDetailsTransportUrl] = useAddonDetailsTransportUrl(urlParams, queryParams);
|
||||||
|
|
@ -29,7 +31,17 @@ const Addons = ({ urlParams, queryParams }) => {
|
||||||
const addAddonUrlInputRef = React.useRef(null);
|
const addAddonUrlInputRef = React.useRef(null);
|
||||||
const addAddonOnSubmit = React.useCallback(() => {
|
const addAddonOnSubmit = React.useCallback(() => {
|
||||||
if (addAddonUrlInputRef.current !== null) {
|
if (addAddonUrlInputRef.current !== null) {
|
||||||
setAddonDetailsTransportUrl(addAddonUrlInputRef.current.value);
|
try {
|
||||||
|
let url = new URL(addAddonUrlInputRef.current.value).toString();
|
||||||
|
setAddonDetailsTransportUrl(url);
|
||||||
|
} catch (e) {
|
||||||
|
toast.show({
|
||||||
|
type: 'error',
|
||||||
|
title: `Failed to parse addon url: ${addAddonUrlInputRef.current.value}`,
|
||||||
|
timeout: 10000
|
||||||
|
});
|
||||||
|
console.error('Failed to parse addon url:', e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [setAddonDetailsTransportUrl]);
|
}, [setAddonDetailsTransportUrl]);
|
||||||
const addAddonModalButtons = React.useMemo(() => {
|
const addAddonModalButtons = React.useMemo(() => {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@ const EpisodePicker = ({ className, onSubmit }: Props) => {
|
||||||
|
|
||||||
const { initialSeason, initialEpisode } = useMemo(() => {
|
const { initialSeason, initialEpisode } = useMemo(() => {
|
||||||
const splitPath = window.location.hash.split('/');
|
const splitPath = window.location.hash.split('/');
|
||||||
|
if (splitPath[splitPath.length - 1] === '') {
|
||||||
|
splitPath.pop();
|
||||||
|
}
|
||||||
const videoId = decodeURIComponent(splitPath[splitPath.length - 1]);
|
const videoId = decodeURIComponent(splitPath[splitPath.length - 1]);
|
||||||
const [, pathSeason, pathEpisode] = videoId ? videoId.split(':') : [];
|
const [, pathSeason, pathEpisode] = videoId ? videoId.split(':') : [];
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,11 @@ const MetaDetails = ({ urlParams, queryParams }) => {
|
||||||
const handleEpisodeSearch = React.useCallback((season, episode) => {
|
const handleEpisodeSearch = React.useCallback((season, episode) => {
|
||||||
const searchVideoHash = encodeURIComponent(`${urlParams.id}:${season}:${episode}`);
|
const searchVideoHash = encodeURIComponent(`${urlParams.id}:${season}:${episode}`);
|
||||||
const url = window.location.hash;
|
const url = window.location.hash;
|
||||||
const searchVideoPath = url.replace(encodeURIComponent(urlParams.videoId), searchVideoHash);
|
|
||||||
|
const searchVideoPath = (urlParams.videoId === undefined || urlParams.videoId === null || urlParams.videoId === '') ?
|
||||||
|
url + (!url.endsWith('/') ? '/' : '') + searchVideoHash
|
||||||
|
: url.replace(encodeURIComponent(urlParams.videoId), searchVideoHash);
|
||||||
|
|
||||||
window.location = searchVideoPath;
|
window.location = searchVideoPath;
|
||||||
}, [urlParams, window.location]);
|
}, [urlParams, window.location]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,10 @@ const Stream = ({ className, videoId, videoReleased, addonName, name, descriptio
|
||||||
}, [href, deepLinks]);
|
}, [href, deepLinks]);
|
||||||
|
|
||||||
const streamLink = React.useMemo(() => {
|
const streamLink = React.useMemo(() => {
|
||||||
|
return deepLinks?.externalPlayer?.streaming;
|
||||||
|
}, [deepLinks]);
|
||||||
|
|
||||||
|
const downloadLink = React.useMemo(() => {
|
||||||
return deepLinks?.externalPlayer?.download;
|
return deepLinks?.externalPlayer?.download;
|
||||||
}, [deepLinks]);
|
}, [deepLinks]);
|
||||||
|
|
||||||
|
|
@ -116,6 +120,28 @@ const Stream = ({ className, videoId, videoReleased, addonName, name, descriptio
|
||||||
}
|
}
|
||||||
}, [props.onClick, profile.settings, markVideoAsWatched]);
|
}, [props.onClick, profile.settings, markVideoAsWatched]);
|
||||||
|
|
||||||
|
const copyDownloadLink = React.useCallback((event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
closeMenu();
|
||||||
|
if (downloadLink) {
|
||||||
|
navigator.clipboard.writeText(downloadLink)
|
||||||
|
.then(() => {
|
||||||
|
toast.show({
|
||||||
|
type: 'success',
|
||||||
|
title: t('PLAYER_COPY_DOWNLOAD_LINK_SUCCESS'),
|
||||||
|
timeout: 4000
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast.show({
|
||||||
|
type: 'error',
|
||||||
|
title: t('PLAYER_COPY_DOWNLOAD_LINK_ERROR'),
|
||||||
|
timeout: 4000,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [downloadLink]);
|
||||||
|
|
||||||
const copyStreamLink = React.useCallback((event) => {
|
const copyStreamLink = React.useCallback((event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
closeMenu();
|
closeMenu();
|
||||||
|
|
@ -195,6 +221,13 @@ const Stream = ({ className, videoId, videoReleased, addonName, name, descriptio
|
||||||
<div className={styles['context-menu-option-label']}>{t('CTX_COPY_STREAM_LINK')}</div>
|
<div className={styles['context-menu-option-label']}>{t('CTX_COPY_STREAM_LINK')}</div>
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
downloadLink &&
|
||||||
|
<Button className={styles['context-menu-option-container']} title={t('CTX_DOWNLOAD_VIDEO')} onClick={copyDownloadLink}>
|
||||||
|
<Icon className={styles['menu-icon']} name={'download'} />
|
||||||
|
<div className={styles['context-menu-option-label']}>{t('CTX_COPY_VIDEO_DOWNLOAD_LINK')}</div>
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}, [copyStreamLink, onClick]);
|
}, [copyStreamLink, onClick]);
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ const useMetaDetails = (urlParams) => {
|
||||||
id: urlParams.id,
|
id: urlParams.id,
|
||||||
extra: []
|
extra: []
|
||||||
},
|
},
|
||||||
streamPath: typeof urlParams.videoId === 'string' ?
|
streamPath: typeof urlParams.videoId === 'string' && urlParams.videoId !== '' ?
|
||||||
{
|
{
|
||||||
resource: 'stream',
|
resource: 'stream',
|
||||||
type: urlParams.type,
|
type: urlParams.type,
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,11 @@ const useSeason = (urlParams, queryParams) => {
|
||||||
const setSeason = React.useCallback((season) => {
|
const setSeason = React.useCallback((season) => {
|
||||||
const nextQueryParams = new URLSearchParams(queryParams);
|
const nextQueryParams = new URLSearchParams(queryParams);
|
||||||
nextQueryParams.set('season', season);
|
nextQueryParams.set('season', season);
|
||||||
window.location.replace(`#${urlParams.path}?${nextQueryParams}`);
|
const path = urlParams.path.endsWith('/') ?
|
||||||
|
urlParams.path.slice(0, -1):
|
||||||
|
urlParams.path;
|
||||||
|
|
||||||
|
window.location.replace(`#${path}?${nextQueryParams}`);
|
||||||
}, [urlParams, queryParams]);
|
}, [urlParams, queryParams]);
|
||||||
return [season, setSeason];
|
return [season, setSeason];
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue