add copy magnet link option to stream

This commit is contained in:
Timothy Z. 2026-03-04 21:14:03 +02:00
parent 6f0e7aa290
commit eef3cef1d4
2 changed files with 83 additions and 8 deletions

View file

@ -93,6 +93,10 @@ const Stream = ({ className, videoId, videoReleased, addonName, name, descriptio
return deepLinks?.externalPlayer?.download; return deepLinks?.externalPlayer?.download;
}, [deepLinks]); }, [deepLinks]);
const magnetLink = React.useMemo(() => {
return deepLinks?.externalPlayer?.magnet;
}, [deepLinks]);
const markVideoAsWatched = React.useCallback(() => { const markVideoAsWatched = React.useCallback(() => {
if (typeof videoId === 'string') { if (typeof videoId === 'string') {
core.transport.dispatch({ core.transport.dispatch({
@ -106,6 +110,10 @@ const Stream = ({ className, videoId, videoReleased, addonName, name, descriptio
}, [videoId, videoReleased]); }, [videoId, videoReleased]);
const onClick = React.useCallback((event) => { const onClick = React.useCallback((event) => {
if (event.nativeEvent.togglePopupPrevented) {
return;
}
if (profile.settings.playerType !== null) { if (profile.settings.playerType !== null) {
markVideoAsWatched(); markVideoAsWatched();
toast.show({ toast.show({
@ -120,6 +128,28 @@ const Stream = ({ className, videoId, videoReleased, addonName, name, descriptio
} }
}, [props.onClick, profile.settings, markVideoAsWatched]); }, [props.onClick, profile.settings, markVideoAsWatched]);
const copyMagnetLink = React.useCallback((event) => {
event.preventDefault();
closeMenu();
if (magnetLink) {
navigator.clipboard.writeText(magnetLink)
.then(() => {
toast.show({
type: 'success',
title: t('PLAYER_COPY_MAGNET_LINK_SUCCESS'),
timeout: 4000
});
})
.catch(() => {
toast.show({
type: 'error',
title: t('PLAYER_COPY_MAGNET_LINK_ERROR'),
timeout: 4000,
});
});
}
}, [magnetLink]);
const copyDownloadLink = React.useCallback((event) => { const copyDownloadLink = React.useCallback((event) => {
event.preventDefault(); event.preventDefault();
closeMenu(); closeMenu();
@ -221,6 +251,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>
} }
{
magnetLink &&
<Button className={styles['context-menu-option-container']} title={t('CTX_COPY_MAGNET_LINK')} onClick={copyMagnetLink}>
<Icon className={styles['menu-icon']} name={'magnet-link'} />
<div className={styles['context-menu-option-label']}>{t('CTX_COPY_MAGNET_LINK')}</div>
</Button>
}
{ {
downloadLink && downloadLink &&
<Button className={styles['context-menu-option-container']} title={t('CTX_DOWNLOAD_VIDEO')} onClick={copyDownloadLink}> <Button className={styles['context-menu-option-container']} title={t('CTX_DOWNLOAD_VIDEO')} onClick={copyDownloadLink}>
@ -267,6 +304,7 @@ Stream.propTypes = {
player: PropTypes.string, player: PropTypes.string,
externalPlayer: PropTypes.shape({ externalPlayer: PropTypes.shape({
download: PropTypes.string, download: PropTypes.string,
magnet: PropTypes.string,
streaming: PropTypes.string, streaming: PropTypes.string,
playlist: PropTypes.string, playlist: PropTypes.string,
fileName: PropTypes.string, fileName: PropTypes.string,

View file

@ -14,13 +14,17 @@ const OptionsMenu = ({ className, stream, playbackDevices, extraSubtitlesTracks,
const { core } = useServices(); const { core } = useServices();
const platform = usePlatform(); const platform = usePlatform();
const toast = useToast(); const toast = useToast();
const [streamingUrl, downloadUrl] = React.useMemo(() => { const [streamingUrl, downloadUrl, magnetUrl] = React.useMemo(() => {
return stream !== null ? return stream !== null ?
stream.deepLinks && stream.deepLinks &&
stream.deepLinks.externalPlayer && stream.deepLinks.externalPlayer &&
[stream.deepLinks.externalPlayer.streaming, stream.deepLinks.externalPlayer.download] [
stream.deepLinks.externalPlayer.streaming,
stream.deepLinks.externalPlayer.download,
stream.deepLinks.externalPlayer.magnet,
]
: :
[null, null]; [null, null, null];
}, [stream]); }, [stream]);
const externalDevices = React.useMemo(() => { const externalDevices = React.useMemo(() => {
return playbackDevices.filter(({ type }) => type === 'external'); return playbackDevices.filter(({ type }) => type === 'external');
@ -53,11 +57,33 @@ const OptionsMenu = ({ className, stream, playbackDevices, extraSubtitlesTracks,
}); });
} }
}, [streamingUrl, downloadUrl]); }, [streamingUrl, downloadUrl]);
const onDownloadVideoButtonClick = React.useCallback(() => { const onCopyMagnetButtonClick = React.useCallback(() => {
if (downloadUrl || streamingUrl ) { if (magnetUrl) {
platform.openExternal(downloadUrl || streamingUrl); navigator.clipboard.writeText(magnetUrl)
.then(() => {
toast.show({
type: 'success',
title: 'Copied',
message: t('PLAYER_COPY_MAGNET_LINK_SUCCESS'),
timeout: 3000
});
})
.catch((e) => {
console.error(e);
toast.show({
type: 'error',
title: t('Error'),
message: `${t('PLAYER_COPY_MAGNET_LINK_ERROR')}: ${magnetUrl}`,
timeout: 3000
});
});
} }
}, [streamingUrl, downloadUrl]); }, [magnetUrl]);
const onDownloadVideoButtonClick = React.useCallback(() => {
if (downloadUrl) {
platform.openExternal(downloadUrl);
}
}, [downloadUrl]);
const onDownloadSubtitlesClick = React.useCallback(() => { const onDownloadSubtitlesClick = React.useCallback(() => {
subtitlesTrackUrl && platform.openExternal(subtitlesTrackUrl); subtitlesTrackUrl && platform.openExternal(subtitlesTrackUrl);
@ -95,7 +121,18 @@ const OptionsMenu = ({ className, stream, playbackDevices, extraSubtitlesTracks,
null null
} }
{ {
streamingUrl || downloadUrl ? magnetUrl ?
<Option
icon={'magnet-link'}
label={t('CTX_COPY_MAGNET_LINK')}
disabled={stream === null}
onClick={onCopyMagnetButtonClick}
/>
:
null
}
{
downloadUrl ?
<Option <Option
icon={'download'} icon={'download'}
label={t('CTX_DOWNLOAD_VIDEO')} label={t('CTX_DOWNLOAD_VIDEO')}