Skip to content

Commit fa8ee1a

Browse files
authored
[Docs Site] Overhaul Stream component and add chapters (#20475)
* [Docs Site] Overhaul Stream component and add chapters * update demo video
1 parent 5e1aa05 commit fa8ee1a

File tree

40 files changed

+261
-135
lines changed

40 files changed

+261
-135
lines changed

package-lock.json

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
"mdast-util-mdx-expression": "2.0.1",
7878
"mermaid": "11.4.1",
7979
"node-html-parser": "7.0.1",
80+
"parse-duration": "2.1.3",
8081
"prettier": "3.5.2",
8182
"prettier-plugin-astro": "0.14.1",
8283
"prettier-plugin-tailwindcss": "0.6.9",

src/components/Stream.astro

Lines changed: 87 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,96 @@
11
---
2-
interface Props {
3-
videoId: string;
4-
videoTitle: string;
5-
thumbnailTimeOrURL: string;
6-
moreVideosLink: string;
7-
}
2+
import { z } from "astro:schema";
3+
import { Badge } from "@astrojs/starlight/components";
4+
import parse from "parse-duration";
5+
6+
type Props = z.input<typeof props>;
7+
8+
const props = z
9+
.object({
10+
id: z.string(),
11+
title: z.string(),
12+
thumbnail: z.string().optional(),
13+
chapters: z.record(z.string(), z.string()).optional(),
14+
showMoreVideos: z.boolean().default(true),
15+
})
16+
.strict();
17+
18+
const { id, title, thumbnail, chapters, showMoreVideos } = props.parse(
19+
Astro.props,
20+
);
821
9-
const {
10-
videoId,
11-
videoTitle,
12-
thumbnailTimeOrURL,
13-
moreVideosLink = "true",
14-
} = Astro.props;
22+
const BASE_URL = `https://customer-1mwganm1ma0xgnmj.cloudflarestream.com/`;
1523
16-
const customerId = "1mwganm1ma0xgnmj";
17-
const baseUrl = `https://customer-${customerId}.cloudflarestream.com/`;
24+
const url = new URL(`${id}/iframe`, BASE_URL);
25+
const thumbnailUrl = new URL(`${id}/thumbnails/thumbnail.jpg`, BASE_URL);
1826
19-
const url = new URL(`${videoId}/iframe`, baseUrl);
2027
url.searchParams.set("preload", "true");
2128
url.searchParams.set("letterboxColor", "transparent");
2229
23-
// full url option
24-
if (thumbnailTimeOrURL !== undefined) {
25-
if (!thumbnailTimeOrURL.startsWith("http")) {
26-
const thumbnailUrl = new URL(
27-
`${videoId}/thumbnails/thumbnail.jpg`,
28-
baseUrl,
29-
);
30+
if (thumbnail) {
31+
if (thumbnail.startsWith("http")) {
32+
url.searchParams.set("poster", thumbnail);
33+
} else {
3034
thumbnailUrl.searchParams.set("fit", "crop");
31-
thumbnailUrl.searchParams.set("time", thumbnailTimeOrURL);
35+
thumbnailUrl.searchParams.set("time", thumbnail);
3236
3337
url.searchParams.set("poster", encodeURI(thumbnailUrl.toString()));
34-
} else {
35-
url.searchParams.set("poster", thumbnailTimeOrURL);
3638
}
3739
}
3840
---
3941

40-
<stream-player data-id={videoId} data-title={videoTitle}>
42+
<stream-player data-id={id} data-title={title}>
4143
<div style="position: relative; padding-top: 56.25%">
4244
<iframe
4345
src={url.toString()}
4446
style="border: none; position: absolute; top: 0; left: 0; height: 100%; width: 100%;"
4547
allow="accelerometer; gyroscope; autoplay; encrypted-media; picture-in-picture;"
4648
allowfullscreen="true"
47-
title={videoTitle}
48-
id={videoId}></iframe>
49+
title={title}
50+
id={id}></iframe>
4951
</div>
5052

5153
{
52-
moreVideosLink == "true" && (
54+
chapters && (
55+
<p>
56+
<strong>Chapters</strong>
57+
<ul class="flex list-none gap-4 overflow-x-scroll pb-4 pl-0">
58+
{Object.entries(chapters).map(([chapter, time]) => {
59+
const totalSeconds = parse(time, "s");
60+
61+
const thumbnail = new URL(thumbnailUrl);
62+
thumbnail.searchParams.set("fit", "crop");
63+
thumbnail.searchParams.set("time", `${totalSeconds}s`);
64+
65+
return (
66+
<li class="!mt-0">
67+
<button
68+
class="flex h-full w-36 cursor-pointer flex-col rounded border border-gray-200 bg-transparent p-4 dark:border-gray-700"
69+
data-chapter={chapter}
70+
data-time={totalSeconds}
71+
>
72+
<img
73+
src={thumbnail.toString()}
74+
alt={chapter}
75+
class="rounded border border-accent"
76+
/>
77+
<div class="flex h-full flex-col items-center justify-between">
78+
<strong class="line-clamp-2 text-xs" title={chapter}>
79+
{chapter}
80+
</strong>
81+
<Badge text={time} variant="tip" class="w-fit" />
82+
</div>
83+
</button>
84+
</li>
85+
);
86+
})}
87+
</ul>
88+
</p>
89+
)
90+
}
91+
92+
{
93+
showMoreVideos && (
5394
<a href="https://www.youtube.com/@CloudflareDevelopers" target="_blank">
5495
Watch more videos on our Developer Channel
5596
</a>
@@ -103,6 +144,23 @@ if (thumbnailTimeOrURL !== undefined) {
103144
rate: stream.playbackRate,
104145
});
105146
});
147+
148+
const buttons =
149+
this.querySelectorAll<HTMLButtonElement>("[data-chapter]");
150+
151+
for (const button of buttons) {
152+
button.addEventListener("click", () => {
153+
const chapter = button.dataset.chapter as string;
154+
const time = Number(button.dataset.time);
155+
156+
stream.currentTime = time;
157+
158+
track("clicked chapter docs video", {
159+
title,
160+
chapter,
161+
});
162+
});
163+
}
106164
}
107165
}
108166

src/components/homepage/FeaturedContentSection.astro

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const { title, text, image, cards, imagePosition } = props.parse(Astro.props);
3232
<div class="flex h-1/3 items-center">
3333
{
3434
imagePosition === "before" && (
35-
<div class="w-full m-8">
35+
<div class="m-8 w-full">
3636
<Image
3737
src={image.light}
3838
alt={`${title} section image`}
@@ -55,13 +55,16 @@ const { title, text, image, cards, imagePosition } = props.parse(Astro.props);
5555
<div class="[&>article]:gap-0">
5656
<Card title="">
5757
<Stream
58-
videoId="d89f290431f98e551f2b1467f85d6561"
59-
videoTitle="foo"
60-
thumbnailTimeOrURL="https://pub-d9bf66e086fb4b639107aa52105b49dd.r2.dev/cloudflare-stack.jpg"
61-
moreVideosLink="false"
58+
id="d89f290431f98e551f2b1467f85d6561"
59+
title="foo"
60+
thumbnail="https://pub-d9bf66e086fb4b639107aa52105b49dd.r2.dev/cloudflare-stack.jpg"
61+
showMoreVideos={false}
6262
/>
6363

64-
<LinkCard title={"Explore our Developer Platform"} href={"/products/?product-group=Developer+platform"} />
64+
<LinkCard
65+
title={"Explore our Developer Platform"}
66+
href={"/products/?product-group=Developer+platform"}
67+
/>
6568
</Card>
6669
</div>
6770
) : (
@@ -80,7 +83,7 @@ const { title, text, image, cards, imagePosition } = props.parse(Astro.props);
8083

8184
{
8285
imagePosition === "after" && (
83-
<div class="w-full m-8">
86+
<div class="m-8 w-full">
8487
<Image
8588
src={image.light}
8689
alt={`${title} section image`}

src/content/docs/china-network/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ The Cloudflare China Network provides:
2626

2727
### Video Introduction
2828

29-
<Stream videoId="b7933a5b3636ca29f834128ca92665b3" videoTitle="China Network Overview" thumbnailTimeOrURL="1s" />
29+
<Stream id="b7933a5b3636ca29f834128ca92665b3" title="China Network Overview" thumbnail="1s" />
3030

3131

3232
## Availability

src/content/docs/cloudflare-for-platforms/workers-for-platforms/index.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,18 @@ Workers for Platforms is built on top of [Cloudflare Workers](/workers/). Worker
2424

2525
<br></br>
2626

27-
<Stream videoId="c8afb7a0a811f07db4b4ffaf56c277bc" videoTitle="Workers for Platforms Overview" thumbnailTimeOrURL="8.6s" />
27+
<Stream id="c8afb7a0a811f07db4b4ffaf56c277bc" title="Workers for Platforms Overview" thumbnail="8.6s" />
2828

2929
***
3030

3131
## Features
3232

3333
<Feature header="Get started" href="/cloudflare-for-platforms/workers-for-platforms/get-started/configuration/" cta="Get started">
34-
Learn how to set up Workers for Platforms.
34+
Learn how to set up Workers for Platforms.
3535
</Feature>
3636

3737
<Feature header="Workers for Platforms architecture" href="/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/" cta="Learn more">
38-
Learn about Workers for Platforms architecture.
38+
Learn about Workers for Platforms architecture.
3939
</Feature>
4040

4141
***

src/content/docs/d1/tutorials/build-a-comments-api/index.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ cd d1-example
4242
## Video Tutorial
4343

4444
<Stream
45-
videoId="8d20dd6cf5679f3272ca44a9fa01728c"
46-
videoTitle="Build a Comments API with D1"
47-
thumbnailTimeOrURL="22s"
45+
id="8d20dd6cf5679f3272ca44a9fa01728c"
46+
title="Build a Comments API with D1"
47+
thumbnail="22s"
4848
/>
4949

5050
## 1. Install Hono

src/content/docs/durable-objects/get-started/video-series/app-frontend.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ tableOfContents: false
1111
import { Details, DirectoryListing, Stream } from "~/components";
1212

1313
<Stream
14-
videoId="efc08fd03da0dfebd2e4402af519acb5"
15-
videoTitle="Building the App Frontend and UI"
16-
thumbnailTimeOrURL="2.5s"
14+
id="efc08fd03da0dfebd2e4402af519acb5"
15+
title="Building the App Frontend and UI"
16+
thumbnail="2.5s"
1717
/>
1818

1919
Now, we're moving to the frontend. In this video, we'll set up the frontend starter code (the starter code is located in the Veet GitHub repository), connect to Durable Objects using a call room ID, and display a local video preview.

src/content/docs/durable-objects/get-started/video-series/deploy-app.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ next:
1313
import { Details, DirectoryListing, Stream } from "~/components";
1414

1515
<Stream
16-
videoId="aaa652e0e05bc09ac35451d9cbd4b341"
17-
videoTitle="Deploy your Video Call app"
18-
thumbnailTimeOrURL="2.5s"
16+
id="aaa652e0e05bc09ac35451d9cbd4b341"
17+
title="Deploy your Video Call app"
18+
thumbnail="2.5s"
1919
/>
2020

2121
We're almost done with the project, and in this video, we'll add the finishing touches. Learn how to handle call disconnections, wire up essential media controls like muting/unmuting and video toggling, and integrate a TURN server to ensure reliable connections even behind firewalls. By the end of this video, your app will be fully functional and ready for deployment.

src/content/docs/durable-objects/get-started/video-series/durable-objects.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ tableOfContents: false
1111
import { Details, DirectoryListing, Stream } from "~/components";
1212

1313
<Stream
14-
videoId="fe3a2f97642951e692e86b3af36a4251"
15-
videoTitle="What are Durable Objects?"
16-
thumbnailTimeOrURL="2.5s"
14+
id="fe3a2f97642951e692e86b3af36a4251"
15+
title="What are Durable Objects?"
16+
thumbnail="2.5s"
1717
/>
1818

1919
In this video, we will show how Durable Objects work and start building a video call app together.

0 commit comments

Comments
 (0)