Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"mdast-util-mdx-expression": "2.0.1",
"mermaid": "11.4.1",
"node-html-parser": "7.0.1",
"parse-duration": "2.1.3",
"prettier": "3.5.2",
"prettier-plugin-astro": "0.14.1",
"prettier-plugin-tailwindcss": "0.6.9",
Expand Down
116 changes: 87 additions & 29 deletions src/components/Stream.astro
Original file line number Diff line number Diff line change
@@ -1,55 +1,96 @@
---
interface Props {
videoId: string;
videoTitle: string;
thumbnailTimeOrURL: string;
moreVideosLink: string;
}
import { z } from "astro:schema";
import { Badge } from "@astrojs/starlight/components";
import parse from "parse-duration";

type Props = z.input<typeof props>;

const props = z
.object({
id: z.string(),
title: z.string(),
thumbnail: z.string().optional(),
chapters: z.record(z.string(), z.string()).optional(),
showMoreVideos: z.boolean().default(true),
})
.strict();

const { id, title, thumbnail, chapters, showMoreVideos } = props.parse(
Astro.props,
);

const {
videoId,
videoTitle,
thumbnailTimeOrURL,
moreVideosLink = "true",
} = Astro.props;
const BASE_URL = `https://customer-1mwganm1ma0xgnmj.cloudflarestream.com/`;

const customerId = "1mwganm1ma0xgnmj";
const baseUrl = `https://customer-${customerId}.cloudflarestream.com/`;
const url = new URL(`${id}/iframe`, BASE_URL);
const thumbnailUrl = new URL(`${id}/thumbnails/thumbnail.jpg`, BASE_URL);

const url = new URL(`${videoId}/iframe`, baseUrl);
url.searchParams.set("preload", "true");
url.searchParams.set("letterboxColor", "transparent");

// full url option
if (thumbnailTimeOrURL !== undefined) {
if (!thumbnailTimeOrURL.startsWith("http")) {
const thumbnailUrl = new URL(
`${videoId}/thumbnails/thumbnail.jpg`,
baseUrl,
);
if (thumbnail) {
if (thumbnail.startsWith("http")) {
url.searchParams.set("poster", thumbnail);
} else {
thumbnailUrl.searchParams.set("fit", "crop");
thumbnailUrl.searchParams.set("time", thumbnailTimeOrURL);
thumbnailUrl.searchParams.set("time", thumbnail);

url.searchParams.set("poster", encodeURI(thumbnailUrl.toString()));
} else {
url.searchParams.set("poster", thumbnailTimeOrURL);
}
}
---

<stream-player data-id={videoId} data-title={videoTitle}>
<stream-player data-id={id} data-title={title}>
<div style="position: relative; padding-top: 56.25%">
<iframe
src={url.toString()}
style="border: none; position: absolute; top: 0; left: 0; height: 100%; width: 100%;"
allow="accelerometer; gyroscope; autoplay; encrypted-media; picture-in-picture;"
allowfullscreen="true"
title={videoTitle}
id={videoId}></iframe>
title={title}
id={id}></iframe>
</div>

{
moreVideosLink == "true" && (
chapters && (
<p>
<strong>Chapters</strong>
<ul class="flex list-none gap-4 overflow-x-scroll pb-4 pl-0">
{Object.entries(chapters).map(([chapter, time]) => {
const totalSeconds = parse(time, "s");

const thumbnail = new URL(thumbnailUrl);
thumbnail.searchParams.set("fit", "crop");
thumbnail.searchParams.set("time", `${totalSeconds}s`);

return (
<li class="!mt-0">
<button
class="flex h-full w-36 cursor-pointer flex-col rounded border border-gray-200 bg-transparent p-4 dark:border-gray-700"
data-chapter={chapter}
data-time={totalSeconds}
>
<img
src={thumbnail.toString()}
alt={chapter}
class="rounded border border-accent"
/>
<div class="flex h-full flex-col items-center justify-between">
<strong class="line-clamp-2 text-xs" title={chapter}>
{chapter}
</strong>
<Badge text={time} variant="tip" class="w-fit" />
</div>
</button>
</li>
);
})}
</ul>
</p>
)
}

{
showMoreVideos && (
<a href="https://www.youtube.com/@CloudflareDevelopers" target="_blank">
Watch more videos on our Developer Channel
</a>
Expand Down Expand Up @@ -103,6 +144,23 @@ if (thumbnailTimeOrURL !== undefined) {
rate: stream.playbackRate,
});
});

const buttons =
this.querySelectorAll<HTMLButtonElement>("[data-chapter]");

for (const button of buttons) {
button.addEventListener("click", () => {
const chapter = button.dataset.chapter as string;
const time = Number(button.dataset.time);

stream.currentTime = time;

track("clicked chapter docs video", {
title,
chapter,
});
});
}
}
}

Expand Down
17 changes: 10 additions & 7 deletions src/components/homepage/FeaturedContentSection.astro
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const { title, text, image, cards, imagePosition } = props.parse(Astro.props);
<div class="flex h-1/3 items-center">
{
imagePosition === "before" && (
<div class="w-full m-8">
<div class="m-8 w-full">
<Image
src={image.light}
alt={`${title} section image`}
Expand All @@ -55,13 +55,16 @@ const { title, text, image, cards, imagePosition } = props.parse(Astro.props);
<div class="[&>article]:gap-0">
<Card title="">
<Stream
videoId="d89f290431f98e551f2b1467f85d6561"
videoTitle="foo"
thumbnailTimeOrURL="https://pub-d9bf66e086fb4b639107aa52105b49dd.r2.dev/cloudflare-stack.jpg"
moreVideosLink="false"
id="d89f290431f98e551f2b1467f85d6561"
title="foo"
thumbnail="https://pub-d9bf66e086fb4b639107aa52105b49dd.r2.dev/cloudflare-stack.jpg"
showMoreVideos={false}
/>

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

{
imagePosition === "after" && (
<div class="w-full m-8">
<div class="m-8 w-full">
<Image
src={image.light}
alt={`${title} section image`}
Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/china-network/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ The Cloudflare China Network provides:

### Video Introduction

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


## Availability
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,18 @@ Workers for Platforms is built on top of [Cloudflare Workers](/workers/). Worker

<br></br>

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

***

## Features

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

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

***
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ cd d1-example
## Video Tutorial

<Stream
videoId="8d20dd6cf5679f3272ca44a9fa01728c"
videoTitle="Build a Comments API with D1"
thumbnailTimeOrURL="22s"
id="8d20dd6cf5679f3272ca44a9fa01728c"
title="Build a Comments API with D1"
thumbnail="22s"
/>

## 1. Install Hono
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ tableOfContents: false
import { Details, DirectoryListing, Stream } from "~/components";

<Stream
videoId="efc08fd03da0dfebd2e4402af519acb5"
videoTitle="Building the App Frontend and UI"
thumbnailTimeOrURL="2.5s"
id="efc08fd03da0dfebd2e4402af519acb5"
title="Building the App Frontend and UI"
thumbnail="2.5s"
/>

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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ next:
import { Details, DirectoryListing, Stream } from "~/components";

<Stream
videoId="aaa652e0e05bc09ac35451d9cbd4b341"
videoTitle="Deploy your Video Call app"
thumbnailTimeOrURL="2.5s"
id="aaa652e0e05bc09ac35451d9cbd4b341"
title="Deploy your Video Call app"
thumbnail="2.5s"
/>

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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ tableOfContents: false
import { Details, DirectoryListing, Stream } from "~/components";

<Stream
videoId="fe3a2f97642951e692e86b3af36a4251"
videoTitle="What are Durable Objects?"
thumbnailTimeOrURL="2.5s"
id="fe3a2f97642951e692e86b3af36a4251"
title="What are Durable Objects?"
thumbnail="2.5s"
/>

In this video, we will show how Durable Objects work and start building a video call app together.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ tableOfContents: false
import { Details, DirectoryListing, Stream } from "~/components";

<Stream
videoId="558cdd841276a1aba1426af6293d6d15"
videoTitle="Introduction to Durable Objects"
thumbnailTimeOrURL="2.5s"
id="558cdd841276a1aba1426af6293d6d15"
title="Introduction to Durable Objects"
thumbnail="2.5s"
/>

In this episode, we will present an overview of the final project, discuss its underlying architecture, and access resources to set up the project locally.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ tableOfContents: false
import { Details, DirectoryListing, Stream } from "~/components";

<Stream
videoId="3b3a88940d3b1c635dbb6df0516218ab"
videoTitle="Make and Answer WebRTC calls"
thumbnailTimeOrURL="2.5s"
id="3b3a88940d3b1c635dbb6df0516218ab"
title="Make and Answer WebRTC calls"
thumbnail="2.5s"
/>

In this video, we'll build on the frontend we set up earlier by adding functionality for making and answering WebRTC video calls. You'll learn how to create peer-to-peer connections, handle ICE candidates, and seamlessly send and receive video streams between users.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ tableOfContents: false
import { Details, DirectoryListing, Stream } from "~/components";

<Stream
videoId="a02e5f9e58999d96c3ec9dbb0efb9707"
videoTitle="Real-time messaging with WebSockets"
thumbnailTimeOrURL="2.5s"
id="a02e5f9e58999d96c3ec9dbb0efb9707"
title="Real-time messaging with WebSockets"
thumbnail="2.5s"
/>

Now, we'll take it a step further by enabling our server to receive and broadcast messages. In this video, you'll learn how to route and broadcast incoming messages from WebSocket connections and implement error handling such as closed WebSocket connections. By the end, you will have completed the backend for our video call app.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ tableOfContents: false
import { Details, DirectoryListing, Stream } from "~/components";

<Stream
videoId="86c64a50e0ea53dadd1ea1194bdeda92"
videoTitle="Create a Serverless Websocket 'Backend'"
thumbnailTimeOrURL="2.5s"
id="86c64a50e0ea53dadd1ea1194bdeda92"
title="Create a Serverless Websocket 'Backend'"
thumbnail="2.5s"
/>

In this video, we'll create a WebSocket backend using serverless technology, making the process simpler than ever before. You'll learn how to create your first Durable Object, set up a WebSocket server to coordinate connections, and keep track of connected clients.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ import { Render, Tabs, TabItem, Stream, Card } from "~/components";

<Tabs>
<TabItem label="Watch this episode">
Build your new corporate network with Cloudflare, connecting any network into our modern SASE platform and secure applications, users, devices and your company data. In this video you will learn all the different methods of connecting networks to Cloudflare and what services can be used to improve security and performance.

Build your new corporate network with Cloudflare, connecting any network into our modern SASE platform and secure applications, users, devices and your company data. In this video you will learn all the different methods of connecting networks to Cloudflare and what services can be used to improve security and performance.

<Card>
<Stream
videoId="0f5d548ef3f606e6833b78e0193847c4"
videoTitle="Connect and secure from any network to anywhere"
thumbnailTimeOrURL="https://pub-d9bf66e086fb4b639107aa52105b49dd.r2.dev/Connect-and-secure-from-any-network-to-anywhere.jpg"
moreVideosLink="false"
id="0f5d548ef3f606e6833b78e0193847c4"
title="Connect and secure from any network to anywhere"
thumbnail="https://pub-d9bf66e086fb4b639107aa52105b49dd.r2.dev/Connect-and-secure-from-any-network-to-anywhere.jpg"
showMoreVideos={false}
/>

**Related content**
Expand All @@ -32,10 +32,10 @@ import { Render, Tabs, TabItem, Stream, Card } from "~/components";
- [Protect data center networks](/reference-architecture/diagrams/network/protect-data-center-networks/)
- [Protect hybrid cloud networks](/reference-architecture/diagrams/network/protect-hybrid-cloud-networks-with-cloudflare-magic-transit/)

</Card>
</Card>
</TabItem>

<TabItem label="Series overview">
<Render file="sase-series-navigation" />
</TabItem>
<Render file="sase-series-navigation" />
</TabItem>
</Tabs>
Loading
Loading