Skip to content

Commit 2ffae08

Browse files
Ahtesham QuraishAhtesham Quraish
authored andcommitted
add support for cloudfront
1 parent c04240c commit 2ffae08

5 files changed

Lines changed: 96 additions & 87 deletions

File tree

frontends/main/src/page-components/TiptapEditor/extensions/node/LearningResource/LearningResourceNode.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,12 @@ declare module "@tiptap/core" {
1919

2020
const NodeWrapper = styled(NodeViewWrapper)({
2121
position: "relative",
22-
marginBottom: "20px",
2322
cursor: "pointer",
2423
})
2524

2625
const StyledLearningResourceCard = styled(ResourceCard)(({ theme }) => ({
2726
position: "relative",
28-
27+
marginBottom: "20px",
2928
".ProseMirror-selectednode &": {
3029
borderColor: theme.custom.colors.red,
3130
userSelect: "none",
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import React from "react"
2+
import styled from "@emotion/styled"
3+
import { isVideoUrl } from "./lib"
4+
5+
const MediaContainer = styled.div(({ theme }) => ({
6+
position: "relative",
7+
width: "100%",
8+
aspectRatio: "16 / 9",
9+
overflow: "hidden",
10+
11+
iframe: {
12+
width: "100%",
13+
height: "100%",
14+
borderRadius: "6px",
15+
display: "block",
16+
},
17+
18+
video: {
19+
width: "100%",
20+
height: "100%",
21+
borderRadius: "6px",
22+
display: "block",
23+
objectFit: "contain",
24+
backgroundColor: "#000",
25+
},
26+
27+
".layout-full & iframe, .layout-full & video": {
28+
borderRadius: 0,
29+
},
30+
".ProseMirror-selectednode .layout-wide &": {
31+
border: `1px solid ${theme.custom.colors.red}`,
32+
padding: "8px",
33+
borderRadius: "10px",
34+
},
35+
".ProseMirror-selectednode .layout-full &": {
36+
border: `1px solid ${theme.custom.colors.red}`,
37+
padding: "8px 0",
38+
borderWidth: "1px 0",
39+
},
40+
}))
41+
42+
interface MediaDisplayProps {
43+
src: string
44+
caption?: string
45+
}
46+
47+
export const MediaDisplay = ({ src, caption }: MediaDisplayProps) => {
48+
return (
49+
<MediaContainer>
50+
{isVideoUrl(src) ? (
51+
<video src={src} controls title={caption}>
52+
{/* Empty track required by jsx-a11y/media-has-caption */}
53+
<track kind="captions" />
54+
Your browser does not support the video tag.
55+
</video>
56+
) : (
57+
<iframe src={src} frameBorder="0" allowFullScreen title={caption} />
58+
)}
59+
</MediaContainer>
60+
)
61+
}

frontends/main/src/page-components/TiptapEditor/extensions/node/MediaEmbed/MediaEmbedNodeView.tsx

Lines changed: 2 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { FullWidth, WideWidth, DefaultWidth } from "./Icons"
55
import { RiCloseLargeLine } from "@remixicon/react"
66
import { ActionButton } from "@mitodl/smoot-design"
77
import { EditableCaption } from "../shared/EditableCaption"
8-
import { isVideoUrl } from "./lib"
8+
import { MediaDisplay } from "./MediaDisplay"
99

1010
const StyledNodeViewWrapper = styled(NodeViewWrapper, {
1111
shouldForwardProp: (prop) =>
@@ -111,43 +111,6 @@ const MediaLayoutToolbar = styled.div({
111111
},
112112
})
113113

114-
const MediaContainer = styled.div(({ theme }) => ({
115-
position: "relative",
116-
width: "100%",
117-
aspectRatio: "16 / 9",
118-
overflow: "hidden",
119-
120-
iframe: {
121-
width: "100%",
122-
height: "100%",
123-
borderRadius: "6px",
124-
display: "block",
125-
},
126-
127-
video: {
128-
width: "100%",
129-
height: "100%",
130-
borderRadius: "6px",
131-
display: "block",
132-
objectFit: "contain",
133-
backgroundColor: "#000",
134-
},
135-
136-
".layout-full & iframe, .layout-full & video": {
137-
borderRadius: 0,
138-
},
139-
".ProseMirror-selectednode .layout-wide &": {
140-
border: `1px solid ${theme.custom.colors.red}`,
141-
padding: "8px",
142-
borderRadius: "10px",
143-
},
144-
".ProseMirror-selectednode .layout-full &": {
145-
border: `1px solid ${theme.custom.colors.red}`,
146-
padding: "8px 0",
147-
borderWidth: "1px 0",
148-
},
149-
}))
150-
151114
interface MediaEmbedNodeProps {
152115
node: NodeViewProps["node"]
153116
editor: NodeViewProps["editor"]
@@ -225,16 +188,7 @@ export const MediaEmbedNodeView = ({
225188
</MediaLayoutToolbar>
226189
)}
227190

228-
<MediaContainer>
229-
{isVideoUrl(src) ? (
230-
<video src={src} controls title={caption}>
231-
<track kind="captions" />
232-
Your browser does not support the video tag.
233-
</video>
234-
) : (
235-
<iframe src={src} frameBorder="0" allowFullScreen title={caption} />
236-
)}
237-
</MediaContainer>
191+
<MediaDisplay src={src} caption={caption} />
238192

239193
<EditableCaption
240194
caption={caption}

frontends/main/src/page-components/TiptapEditor/extensions/node/MediaEmbed/MediaEmbedViewer.tsx

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from "react"
22
import styled from "@emotion/styled"
33
import { EditableCaption } from "../shared/EditableCaption"
4-
import { isVideoUrl } from "./lib"
4+
import { MediaDisplay } from "./MediaDisplay"
55

66
const StyledWrapper = styled.div({
77
position: "relative",
@@ -24,29 +24,6 @@ const StyledWrapper = styled.div({
2424
},
2525
})
2626

27-
const MediaContainer = styled.div({
28-
position: "relative",
29-
width: "100%",
30-
aspectRatio: "16 / 9",
31-
overflow: "hidden",
32-
33-
iframe: {
34-
width: "100%",
35-
height: "100%",
36-
borderRadius: "6px",
37-
display: "block",
38-
},
39-
40-
video: {
41-
width: "100%",
42-
height: "100%",
43-
borderRadius: "6px",
44-
display: "block",
45-
objectFit: "contain",
46-
backgroundColor: "#000",
47-
},
48-
})
49-
5027
interface MediaEmbedNode {
5128
attrs: {
5229
src?: string
@@ -62,16 +39,7 @@ export const MediaEmbedViewer = ({ node }: { node?: MediaEmbedNode }) => {
6239

6340
return (
6441
<StyledWrapper className={`layout-${layout}`}>
65-
<MediaContainer>
66-
{isVideoUrl(src) ? (
67-
<video src={src} controls title={caption}>
68-
<track kind="captions" />
69-
Your browser does not support the video tag.
70-
</video>
71-
) : (
72-
<iframe src={src} frameBorder="0" allowFullScreen title={caption} />
73-
)}
74-
</MediaContainer>
42+
<MediaDisplay src={src} caption={caption} />
7543

7644
<EditableCaption
7745
caption={caption}

frontends/main/src/page-components/TiptapEditor/extensions/node/MediaEmbed/lib.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
export function isVideoUrl(url: string): boolean {
22
try {
33
const parsed = new URL(url)
4-
return parsed.pathname.toLowerCase().endsWith(".mp4")
4+
return (
5+
parsed.pathname.toLowerCase().endsWith(".mp4") ||
6+
parsed.pathname.toLowerCase().endsWith(".m3u8")
7+
)
58
} catch {
69
return false
710
}
@@ -15,17 +18,41 @@ export function convertToEmbedUrl(url: string): string | null {
1518
} catch {
1619
return null // not a valid URL
1720
}
18-
1921
const hostname = parsed.hostname.replace("www.", "")
2022

23+
// Helper to ensure URL has protocol
24+
const ensureProtocol = (urlOrHost: string) => {
25+
return urlOrHost.startsWith("http") ? urlOrHost : `https://${urlOrHost}`
26+
}
27+
2128
// --- MIT LEARN MP4 VIDEOS ---
2229
if (
23-
hostname === "learn.mit.edu" &&
30+
hostname ===
31+
new URL(
32+
ensureProtocol(
33+
process.env.NEXT_PUBLIC_ORIGIN || "https://learn.mit.edu",
34+
),
35+
).hostname.replace("www.", "") &&
2436
parsed.pathname.toLowerCase().endsWith(".mp4")
2537
) {
2638
return url // Return the URL as-is for video element
2739
}
2840

41+
// --- CLOUDFRONT M3U8 VIDEOS ---
42+
43+
if (
44+
hostname ===
45+
new URL(
46+
ensureProtocol(
47+
process.env.NEXT_PUBLIC_CLOUDFRONT_DOMAIN ||
48+
"https://d3tsb3m56iwvoq.cloudfront.net",
49+
),
50+
).hostname.replace("www.", "") &&
51+
parsed.pathname.toLowerCase().endsWith(".m3u8")
52+
) {
53+
return url // Return the URL as-is for video element
54+
}
55+
2956
// --- YOUTUBE WATCH ---
3057
if (hostname === "youtube.com" && parsed.pathname === "/watch") {
3158
const videoId = parsed.searchParams.get("v")

0 commit comments

Comments
 (0)