diff --git a/src/routes/MetaDetails/StreamsList/Stream/Stream.js b/src/routes/MetaDetails/StreamsList/Stream/Stream.js
index 6587c72d0..23a1ce607 100644
--- a/src/routes/MetaDetails/StreamsList/Stream/Stream.js
+++ b/src/routes/MetaDetails/StreamsList/Stream/Stream.js
@@ -4,15 +4,57 @@ const React = require('react');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const { default: Icon } = require('@stremio/stremio-icons/react');
-const { Button, Image, useProfile, platform, useToast } = require('stremio/common');
+const { Button, Image, useProfile, platform, useToast, Popup, useBinaryState } = require('stremio/common');
const { useServices } = require('stremio/services');
+const { useRouteFocused } = require('stremio-router');
const StreamPlaceholder = require('./StreamPlaceholder');
+const { t } = require('i18next');
const styles = require('./styles');
const Stream = ({ className, videoId, videoReleased, addonName, name, description, thumbnail, progress, deepLinks, ...props }) => {
const profile = useProfile();
const toast = useToast();
const { core } = useServices();
+ const routeFocused = useRouteFocused();
+
+ const [menuOpen, , closeMenu, toggleMenu] = useBinaryState(false);
+
+ React.useEffect(() => {
+ if (!routeFocused) {
+ closeMenu();
+ }
+ }, [routeFocused]);
+
+ const popupLabelOnMouseUp = React.useCallback((event) => {
+ if (!event.nativeEvent.togglePopupPrevented) {
+ if (event.nativeEvent.ctrlKey || event.nativeEvent.button === 2) {
+ event.preventDefault();
+ toggleMenu();
+ }
+ }
+ }, []);
+ const popupLabelOnContextMenu = React.useCallback((event) => {
+ if (!event.nativeEvent.togglePopupPrevented && !event.nativeEvent.ctrlKey) {
+ event.preventDefault();
+ }
+ }, [toggleMenu]);
+ const popupLabelOnLongPress = React.useCallback((event) => {
+ if (event.nativeEvent.pointerType !== 'mouse' && !event.nativeEvent.togglePopupPrevented) {
+ toggleMenu();
+ }
+ }, [toggleMenu]);
+ const popupMenuOnPointerDown = React.useCallback((event) => {
+ event.nativeEvent.togglePopupPrevented = true;
+ }, []);
+ const popupMenuOnContextMenu = React.useCallback((event) => {
+ event.nativeEvent.togglePopupPrevented = true;
+ }, []);
+ const popupMenuOnClick = React.useCallback((event) => {
+ event.nativeEvent.togglePopupPrevented = true;
+ }, []);
+ const popupMenuOnKeyDown = React.useCallback((event) => {
+ event.nativeEvent.buttonClickPrevented = true;
+ }, []);
const href = React.useMemo(() => {
return deepLinks ?
@@ -74,41 +116,117 @@ const Stream = ({ className, videoId, videoReleased, addonName, name, descriptio
}
}, [props.onClick, profile.settings, markVideoAsWatched]);
+ const streamLink = React.useMemo(() => {
+ return deepLinks?.externalPlayer?.download;
+ }, [deepLinks]);
+
+ const copyStreamLink = React.useCallback((event) => {
+ event.preventDefault();
+ if (streamLink && navigator?.clipboard) {
+ navigator.clipboard.writeText(deepLinks.externalPlayer.download)
+ .then(() => {
+ toast.show({
+ type: 'success',
+ title: t('PLAYER_COPY_STREAM_SUCCESS'),
+ timeout: 4000
+ });
+ })
+ .catch(() => {
+ toast.show({
+ type: 'error',
+ title: t('PLAYER_COPY_STREAM_ERROR'),
+ timeout: 4000,
+ });
+ });
+
+ } else {
+ toast.show({
+ type: 'error',
+ title: t('PLAYER_COPY_STREAM_ERROR'),
+ timeout: 4000,
+ });
+ }
+ closeMenu();
+ }, [streamLink]);
+
const renderThumbnailFallback = React.useCallback(() => (