From a016adfca6890ccec57dac96d7e4c6e5ce6d0690 Mon Sep 17 00:00:00 2001 From: hzrd149 Date: Thu, 5 Sep 2024 17:06:03 -0500 Subject: [PATCH] Add Support for embedding HLS videos --- .changeset/lovely-cougars-fix.md | 5 +++++ src/components/external-embeds/types/video.tsx | 13 ++++++++++++- .../note/timeline-note/text-note-contents.tsx | 2 ++ src/helpers/url.ts | 7 +++++++ .../channels/components/channel-message-content.tsx | 2 ++ src/views/dms/components/direct-message-content.tsx | 2 ++ src/views/notifications/index.tsx | 1 + 7 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 .changeset/lovely-cougars-fix.md diff --git a/.changeset/lovely-cougars-fix.md b/.changeset/lovely-cougars-fix.md new file mode 100644 index 000000000..b8e9c70b2 --- /dev/null +++ b/.changeset/lovely-cougars-fix.md @@ -0,0 +1,5 @@ +--- +"nostrudel": minor +--- + +Add Support for embedding HLS videos diff --git a/src/components/external-embeds/types/video.tsx b/src/components/external-embeds/types/video.tsx index adee84c8f..c29d6270f 100644 --- a/src/components/external-embeds/types/video.tsx +++ b/src/components/external-embeds/types/video.tsx @@ -1,9 +1,10 @@ import styled from "@emotion/styled"; -import { isVideoURL } from "../../../helpers/url"; +import { isStreamURL, isVideoURL } from "../../../helpers/url"; import useAppSettings from "../../../hooks/use-app-settings"; import useElementTrustBlur from "../../../hooks/use-element-trust-blur"; import ExpandableEmbed from "../expandable-embed"; +import { LiveVideoPlayer } from "../../live-video-player"; const StyledVideo = styled.video` max-width: 30rem; @@ -37,3 +38,13 @@ export function renderVideoUrl(match: URL) { ); } + +export function renderStreamUrl(match: URL) { + if (!isStreamURL(match)) return null; + + return ( + + + + ); +} diff --git a/src/components/note/timeline-note/text-note-contents.tsx b/src/components/note/timeline-note/text-note-contents.tsx index 574d36dac..4bf7dfae8 100644 --- a/src/components/note/timeline-note/text-note-contents.tsx +++ b/src/components/note/timeline-note/text-note-contents.tsx @@ -31,6 +31,7 @@ import { renderModelUrl, renderCodePenURL, renderArchiveOrgURL, + renderStreamUrl, } from "../../external-embeds"; import { LightboxProvider } from "../../lightbox-provider"; import MediaOwnerProvider from "../../../providers/local/media-owner-provider"; @@ -57,6 +58,7 @@ function buildContents(event: NostrEvent | EventTemplate, simpleLinks = false) { renderSoundCloudUrl, renderImageUrl, renderVideoUrl, + renderStreamUrl, renderAudioUrl, renderModelUrl, renderCodePenURL, diff --git a/src/helpers/url.ts b/src/helpers/url.ts index a04718d0b..fc4687645 100644 --- a/src/helpers/url.ts +++ b/src/helpers/url.ts @@ -2,6 +2,7 @@ export const convertToUrl = (url: string | URL) => (url instanceof URL ? url : n export const IMAGE_EXT = [".svg", ".gif", ".png", ".jpg", ".jpeg", ".webp", ".avif"]; export const VIDEO_EXT = [".mp4", ".mkv", ".webm", ".mov"]; +export const STREAM_EXT = [".m3u8"]; export const AUDIO_EXT = [".mp3", ".wav", ".ogg", ".aac"]; export function isMediaURL(url: string | URL) { @@ -19,6 +20,12 @@ export function isVideoURL(url: string | URL) { return VIDEO_EXT.some((ext) => u.pathname.endsWith(ext) || ipfsFilename?.endsWith(ext)); } +export function isStreamURL(url: string | URL) { + const u = new URL(url); + const ipfsFilename = u.searchParams.get("filename"); + + return STREAM_EXT.some((ext) => u.pathname.endsWith(ext) || ipfsFilename?.endsWith(ext)); +} export function isAudioURL(url: string | URL) { const u = new URL(url); const ipfsFilename = u.searchParams.get("filename"); diff --git a/src/views/channels/components/channel-message-content.tsx b/src/views/channels/components/channel-message-content.tsx index 36d90d437..bd508a02d 100644 --- a/src/views/channels/components/channel-message-content.tsx +++ b/src/views/channels/components/channel-message-content.tsx @@ -22,6 +22,7 @@ import { renderSoundCloudUrl, renderSpotifyUrl, renderStemstrUrl, + renderStreamUrl, renderTidalUrl, renderTwitterUrl, renderVideoUrl, @@ -53,6 +54,7 @@ const ChannelMessageContent = memo(({ message, children, ...props }: BoxProps & renderSoundCloudUrl, renderImageUrl, renderVideoUrl, + renderStreamUrl, renderAudioUrl, renderGenericUrl, ]); diff --git a/src/views/dms/components/direct-message-content.tsx b/src/views/dms/components/direct-message-content.tsx index 8dcd9eef1..9f4223076 100644 --- a/src/views/dms/components/direct-message-content.tsx +++ b/src/views/dms/components/direct-message-content.tsx @@ -13,6 +13,7 @@ import { renderSoundCloudUrl, renderSpotifyUrl, renderStemstrUrl, + renderStreamUrl, renderTidalUrl, renderTwitterUrl, renderVideoUrl, @@ -46,6 +47,7 @@ export default function DirectMessageContent({ renderSoundCloudUrl, renderImageUrl, renderVideoUrl, + renderStreamUrl, renderAudioUrl, renderGenericUrl, ]); diff --git a/src/views/notifications/index.tsx b/src/views/notifications/index.tsx index 3265a2954..348ee420e 100644 --- a/src/views/notifications/index.tsx +++ b/src/views/notifications/index.tsx @@ -25,6 +25,7 @@ import { useTimelineDates } from "../../hooks/timeline/use-timeline-dates"; import useCacheEntryHeight from "../../hooks/timeline/use-cache-entry-height"; import useVimNavigation from "./use-vim-navigation"; import { PersistentSubject } from "../../classes/subject"; +import useRouteStateValue from "../../hooks/use-route-state-value"; function TimeMarker({ date, ids }: { date: Dayjs; ids: string[] }) { const readAll = useCallback(() => {