Skip to content

Commit 83503e3

Browse files
committed
Add video path support
1 parent 8e11a09 commit 83503e3

File tree

3 files changed

+147
-91
lines changed

3 files changed

+147
-91
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import type { LoaderFunctionArgs } from "@remix-run/server-runtime";
2+
import env from "~/env/env.server";
3+
4+
// this route used as proxy for images to cloudflare endpoint
5+
// https://developers.cloudflare.com/fundamentals/get-started/reference/cdn-cgi-endpoint/
6+
7+
export const loader = async ({ request }: LoaderFunctionArgs) => {
8+
if (env.RESIZE_ORIGIN !== undefined) {
9+
const url = new URL(request.url);
10+
const imgUrl = new URL(env.RESIZE_ORIGIN + url.pathname);
11+
imgUrl.search = url.search;
12+
13+
const response = await fetch(imgUrl.href, {
14+
headers: {
15+
accept: request.headers.get("accept") ?? "",
16+
"accept-encoding": request.headers.get("accept-encoding") ?? "",
17+
},
18+
});
19+
20+
const responseWHeaders = new Response(response.body, response);
21+
22+
if (false === responseWHeaders.ok) {
23+
console.error(
24+
`Request to Image url ${imgUrl.href} responded with status = ${responseWHeaders.status}`
25+
);
26+
}
27+
28+
return responseWHeaders;
29+
}
30+
31+
return new Response("Not supported", {
32+
status: 200,
33+
});
34+
};

packages/sdk-components-react/src/video.tsx

Lines changed: 102 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import {
44
type ComponentProps,
55
useEffect,
66
useId,
7+
useContext,
78
} from "react";
89
import type { Atom } from "nanostores";
10+
import { ReactSdkContext } from "@webstudio-is/react-sdk/runtime";
911

1012
export const defaultTag = "video";
1113

@@ -27,116 +29,126 @@ export const Video = forwardRef<
2729
$visible?: Atom<boolean>;
2830
$timeline?: boolean;
2931
}
30-
>(({ $progress, $visible, $timeline, children, ...props }, ref) => {
31-
const id = useId();
32-
const videoIdProps = {
33-
[videoIdAttribute]: id,
34-
};
35-
36-
useEffect(() => {
37-
if ($progress === undefined) {
38-
return;
39-
}
40-
41-
if ($visible === undefined) {
42-
return;
43-
}
44-
45-
const video = document.querySelector(`[${videoIdAttribute}="${id}"]`);
46-
47-
if (video === null) {
48-
return;
49-
}
50-
51-
if (false === video instanceof HTMLVideoElement) {
52-
return;
53-
}
54-
55-
// Safari IOS does not seek video without play called at least once
56-
// this is in case autoPlay is not set
57-
video.play().catch(() => {
58-
/**/
59-
});
60-
video.pause();
61-
62-
if ($timeline) {
63-
return $progress.subscribe((progress) => {
64-
if (video.readyState < READY_STATE.HAVE_METADATA) {
65-
return;
66-
}
32+
>(
33+
(
34+
{ $progress, $visible, $timeline, children, src: srcProp, ...props },
35+
ref
36+
) => {
37+
const id = useId();
38+
const videoIdProps = {
39+
[videoIdAttribute]: id,
40+
};
41+
const { assetBaseUrl } = useContext(ReactSdkContext);
6742

68-
if (!video.paused) {
69-
video.pause();
70-
}
43+
const src = srcProp?.startsWith(assetBaseUrl)
44+
? `/cgi/video/${srcProp.slice(assetBaseUrl.length)}`
45+
: srcProp;
7146

72-
if (video.seeking) {
73-
return;
74-
}
47+
useEffect(() => {
48+
if ($progress === undefined) {
49+
return;
50+
}
7551

76-
let duration = video.duration;
52+
if ($visible === undefined) {
53+
return;
54+
}
7755

78-
if (Number.isNaN(duration)) {
79-
return;
80-
}
56+
const video = document.querySelector(`[${videoIdAttribute}="${id}"]`);
8157

82-
if (!Number.isFinite(duration)) {
83-
// Set to 60s on streaming videos
84-
duration = 60;
85-
}
58+
if (video === null) {
59+
return;
60+
}
61+
62+
if (false === video instanceof HTMLVideoElement) {
63+
return;
64+
}
8665

87-
video.currentTime = (progress ?? 0) * duration;
66+
// Safari IOS does not seek video without play called at least once
67+
// this is in case autoPlay is not set
68+
video.play().catch(() => {
69+
/**/
8870
});
89-
}
71+
video.pause();
72+
73+
if ($timeline) {
74+
return $progress.subscribe((progress) => {
75+
if (video.readyState < READY_STATE.HAVE_METADATA) {
76+
return;
77+
}
78+
79+
if (!video.paused) {
80+
video.pause();
81+
}
82+
83+
if (video.seeking) {
84+
return;
85+
}
86+
87+
let duration = video.duration;
9088

91-
let isPlaying = false;
92-
let isVisible = false;
89+
if (Number.isNaN(duration)) {
90+
return;
91+
}
9392

94-
const unsubscribeVisible = $visible.subscribe((visible) => {
95-
isVisible = visible;
93+
if (!Number.isFinite(duration)) {
94+
// Set to 60s on streaming videos
95+
duration = 60;
96+
}
9697

97-
// Seek video only if it's invisible to avoid jumps
98-
if (isVisible === false && isPlaying === false && !video.loop) {
99-
video.currentTime = 0;
98+
video.currentTime = (progress ?? 0) * duration;
99+
});
100100
}
101-
});
102101

103-
const unsubscribeProgress = $progress.subscribe((progress) => {
104-
if (
105-
isPlaying &&
106-
(progress === undefined || progress === 0 || progress === 1)
107-
) {
108-
isPlaying = false;
109-
video.pause();
102+
let isPlaying = false;
103+
let isVisible = false;
104+
105+
const unsubscribeVisible = $visible.subscribe((visible) => {
106+
isVisible = visible;
110107

111108
// Seek video only if it's invisible to avoid jumps
112109
if (isVisible === false && isPlaying === false && !video.loop) {
113110
video.currentTime = 0;
114111
}
112+
});
115113

116-
return;
117-
}
114+
const unsubscribeProgress = $progress.subscribe((progress) => {
115+
if (
116+
isPlaying &&
117+
(progress === undefined || progress === 0 || progress === 1)
118+
) {
119+
isPlaying = false;
120+
video.pause();
121+
122+
// Seek video only if it's invisible to avoid jumps
123+
if (isVisible === false && isPlaying === false && !video.loop) {
124+
video.currentTime = 0;
125+
}
118126

119-
if (!isPlaying) {
120-
isPlaying = true;
121-
if (!video.ended) {
122-
video.play().catch(() => {
123-
/**/
124-
});
127+
return;
125128
}
126-
}
127-
});
128129

129-
return () => {
130-
unsubscribeProgress();
131-
unsubscribeVisible();
132-
};
133-
}, [$progress, $timeline, $visible, id]);
134-
135-
return (
136-
<video {...props} {...videoIdProps} ref={ref}>
137-
{children}
138-
</video>
139-
);
140-
});
130+
if (!isPlaying) {
131+
isPlaying = true;
132+
if (!video.ended) {
133+
video.play().catch(() => {
134+
/**/
135+
});
136+
}
137+
}
138+
});
139+
140+
return () => {
141+
unsubscribeProgress();
142+
unsubscribeVisible();
143+
};
144+
}, [$progress, $timeline, $visible, id]);
145+
146+
return (
147+
<video src={src} {...props} {...videoIdProps} ref={ref}>
148+
{children}
149+
</video>
150+
);
151+
}
152+
);
141153

142154
Video.displayName = "Video";

packages/sdk-components-react/src/video.ws.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,17 @@ export const meta: WsComponentMeta = {
2323
};
2424

2525
export const propsMeta: WsComponentPropsMeta = {
26-
props,
26+
props: {
27+
...props,
28+
// Automatically generated props don't have the right control.
29+
src: {
30+
type: "string",
31+
control: "file",
32+
label: "Source",
33+
required: false,
34+
accept: ".mp4,.webm,.mpg,.mpeg,.mov",
35+
},
36+
},
2737
initialProps: [
2838
"id",
2939
"className",

0 commit comments

Comments
 (0)