diff --git a/src/app/molecules/media/Media.jsx b/src/app/molecules/media/Media.jsx index e2b61775cb..dea58a606d 100644 --- a/src/app/molecules/media/Media.jsx +++ b/src/app/molecules/media/Media.jsx @@ -15,6 +15,8 @@ import ExternalSVG from '../../../../public/res/ic/outlined/external.svg'; import PlaySVG from '../../../../public/res/ic/outlined/play.svg'; import { getBlobSafeMimeType } from '../../../util/mimetypes'; +import initMatrix from '../../../client/initMatrix'; +import settings from '../../../client/state/settings'; async function getDecryptedBlob(response, type, decryptData) { const arrayBuffer = await response.arrayBuffer(); @@ -361,6 +363,202 @@ Video.propTypes = { blurhash: PropTypes.string, }; +function IframePlayer({ + children, link, sitename, title, thumbnail, +}) { + const [videoStarted, setVideoStarted] = useState(false); + + const handlePlayVideo = () => { + setVideoStarted(true); + }; + + return ( +
+
+
+ {`${sitename} - ${title}`} + + window.open(link)} + /> +
+ +
+ {videoStarted ? ( +
+ {children} +
+ ) : ( + <> + {`${sitename} + + + )} +
+
+
+ ); +} +IframePlayer.propTypes = { + children: PropTypes.node.isRequired, + link: PropTypes.string.isRequired, + sitename: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + thumbnail: PropTypes.string.isRequired, +}; + +function Embed({ link }) { + const url = new URL(link); + + if (settings.showYoutubeEmbedPlayer && (((url.host === 'www.youtube.com' || url.host === 'youtube.com') && (url.pathname === '/watch' || url.pathname.startsWith('/shorts/'))) || url.host === 'youtu.be' || url.host === 'www.youtu.be')) { + return ; + } + + const [urlPreviewInfo, setUrlPreviewInfo] = useState(); + const mx = initMatrix.matrixClient; + + useEffect(() => { + let unmounted = false; + + async function getUrlPreview() { + try { + const info = await mx.getUrlPreview(link, 0); + if (unmounted) return; + setUrlPreviewInfo(info); + } catch { + setUrlPreviewInfo(); + } + } + + getUrlPreview(); + + return () => { + unmounted = true; + }; + }); + + if (urlPreviewInfo != null) { + const imageURL = urlPreviewInfo['og:image'] || urlPreviewInfo['og:image:secure_url']; + const image = (imageURL != null) ? ( + + ) : null; + + // Image only embed + if (image != null && urlPreviewInfo['og:title'] == null && urlPreviewInfo['og:description'] == null) { + return ( +
+
+ {image} +
+
+ ); + } + + const embedTitle = urlPreviewInfo['og:title'] || urlPreviewInfo['og:site_name']; + + return ( +
+
+
+ {(embedTitle != null) && ( + + {embedTitle} + + )} + + {(urlPreviewInfo['og:description'] != null) && ( + + {urlPreviewInfo['og:description']} + + )} +
+ +
+ {image} +
+
+
+ ); + } + + return null; +} +Embed.propTypes = { + link: PropTypes.string.isRequired, +}; + +function YoutubeEmbed({ link }) { + const [urlPreviewInfo, setUrlPreviewInfo] = useState(null); + const mx = initMatrix.matrixClient; + const url = new URL(link); + + // fix for no embed information on www.youtu.be + if (url.host === 'www.youtu.be') { + url.host = 'youtu.be'; + } + + useEffect(() => { + let unmounted = false; + + async function getThumbnail() { + const info = await mx.getUrlPreview(url.toString(), 0); + if (unmounted) return; + + setUrlPreviewInfo(info); + } + + getThumbnail(); + + return () => { + unmounted = true; + }; + }); + + let videoID; + if (url.host === 'youtu.be' || url.host === 'www.youtu.be') { + videoID = url.pathname.slice(1); + } else if (url.pathname.startsWith('/shorts/')) { + videoID = url.pathname.slice(8); + } else { + videoID = url.searchParams.get('v'); + } + + let embedURL = `https://www.youtube-nocookie.com/embed/${videoID}?autoplay=1`; + if (url.searchParams.has('t')) { // timestamp flag + embedURL += `&start=${url.searchParams.get('t')}`; + } + + if (urlPreviewInfo !== null) { + return ( +
+ +