Build your next video application on Cloudflare

歴史的に、動画アプリケーションの構築は非常に難しいものでした。動画の録画、エンコード、そして再生の裏には多くの複雑な技術があります。幸いなことに、Cloudflare Streamではすべての難しい部分を取り除くことで、カスタム動画やストリーミングアプリケーションを簡単に構築できます。Cloudflare Stream、Access、Pages、Workersを組み合わせた、非常に少ないコードで高性能の動画アプリケーションを作成する方法を見てみましょう。

本日は、Cloudflare TVで放映された動画アプリケーションを構築していきます。ユーザー認証機能のほか、管理者が制作済みの動画をアップロードしたり、新しいコンテンツをライブ配信する機能を盛り込みます。Cloudflareサービスを使用して、YouTubeやTwitch用に自分のコンテンツを制作できることを想像してみてく

ださい!

動画のリストを取得する

アプリケーションのメインページに、すべての動画のリストを表示したいと思います。動画はCloudflare Streamでアップロードと保存を実現しますが、それについては後で詳しく説明します!このコードは、「傾向」動画やユーザーごとに選択された動画のみを表示するように変更できます。今のところ、検索APIを使用し、空の文字列を渡してすべてを返します。

import { getSignedStreamId } from "../../src/cfStream"

export async function onRequestGet(context) {
    const {
        request,
        env,
        params,
    } = context

    const { id } = params

    if (id) {
        const res = await fetch(`https://api.cloudflare.com/client/v4/accounts/${env.CF_ACCOUNT_ID}/stream/${id}`, {
            method: "GET",
            headers: {
                "Authorization": `Bearer ${env.CF_API_TOKEN_STREAM}`
            }
        })

        const video = (await res.json()).result

        if (video.meta.visibility !== "public") {
            return new Response(null, {status: 401})
        }

        const signedId = await getSignedStreamId(id, env.CF_STREAM_SIGNING_KEY)

        return new Response(JSON.stringify({
            signedId: `${signedId}`
        }), {
            headers: {
                "content-type": "application/json"
            }
        })
    } else {
        const url = new URL(request.url)
        const res = await (await fetch(`https://api.cloudflare.com/client/v4/accounts/${env.CF_ACCOUNT_ID}/stream?search=${url.searchParams.get("search") || ""}`, {
            headers: {
                "Authorization": `Bearer ${env.CF_API_TOKEN_STREAM}`
            }
        })).json()

        const filteredVideos = res.result.filter(x => x.meta.visibility === "public") 
        const videos = await Promise.all(filteredVideos.map(async x => {
            const signedId = await getSignedStreamId(x.uid, env.CF_STREAM_SIGNING_KEY)
            return {
                uid: x.uid,
                status: x.status,
                thumbnail: `https://videodelivery.net/${signedId}/thumbnails/thumbnail.jpg`,
                meta: {
                    name: x.meta.name
                },
                created: x.created,
                modified: x.modified,
                duration: x.duration,
            }
        }))
        return new Response(JSON.stringify(videos), {headers: {"content-type": "application/json"}})
    }
}

各動画を確認し、非公開の動画を除外して、サムネイルのURL、ID、作成日など、必要なメタデータを抽出します。

動画の再生

ユーザーがアプリケーションから動画を再生できるようにするには、動画を公開にするか、リクエストごとに署名する必要があります。動画を公開としてマークすると、このプロセスが簡単になります。しかし、動画へのアクセスを制御したい理由はたくさんあります。再生する前にユーザーにログインさせたり、何らかの方法でアクセスを制限する場合は、動画をプライベートとしてマークし、署名付きURLを使用してアクセスを制御します。動画の保護に関する詳細については、こちらをご覧ください。

アプリケーションをローカルでテストしている場合、または1日あたりのリクエストの予測数が10,000未満の場合は、/tokenエンドポイントを呼び出して署名付きトークンを生成します。1日あたりのリクエスト予測数が10,000以上の場合は、ここで行うように、JSON Webトークンを使用して自分のトークンに署名してください。

ユーザーが動画をアップロードできるようにする

次のステップは、ユーザーが動画をアップロードできる管理ページを作成することです。ユーザーによるアップロードを可能にするためのドキュメントは、 こちらにあります。

このプロセスは、Cloudflare Stream APIを使用すると簡単になります。APIトークンとアカウントIDを使用して、一意の1回限りのアップロード用のURLを生成します。トークンにStream:Edit権限があることを確認してください。アプリケーションからのすべてのPOSTリクエストをフックし、生成されたアップロード用URLを返します。

export const cfTeamsAccessAuthMiddleware = async ({request, data, env, next}) => {
    try {
        const userEmail = request.headers.get("cf-access-authenticated-user-email")

        if (!userEmail) {
            throw new Error("User not found, make sure application is behind Cloudflare Access")
        }
  
        // Pass user info to next handlers
        data.user = {
            email: userEmail
        }
  
        return next()
    } catch (e) {
        return new Response(e.toString(), {status: 401})
    }
}

export const onRequest = [
    cfTeamsAccessAuthMiddleware
]

管理ページには、ユーザーが自分のコンピュータから動画をドラッグアンドドロップまたはアップロードできるフォームが含まれています。ログインしたユーザーがアップロードのフォームで[送信]を押すと、アプリケーションは一意のURLを生成し、そこにFormDataを投稿します。このコードは、動画共有サイトの構築や、ユーザー生成コンテンツを可能にするアプリケーションとうまく機能します。

async function getOneTimeUploadUrl() {
    const res = await fetch('/api/admin/videos', {method: 'POST', headers: {'accept': 'application/json'}})
    const upload = await res.json()
    return upload.uploadURL
}

async function uploadVideo() {
    const videoInput = document.getElementById("video");

    const oneTimeUploadUrl = await getOneTimeUploadUrl();
    const video = videoInput.files[0];
    const formData = new FormData();
    formData.append("file", video);

    const uploadResult = await fetch(oneTimeUploadUrl, {
        method: "POST",
        body: formData,
    })
}

Stream Liveを使ったリアルタイム動画の追加

Stream Liveをすでにご紹介した手法と組み合わせて使用して、アプリケーションにライブストリーミングセクションを追加することもできます。ログインしたユーザーが配信を開始し、他のログインしたユーザー、さらには一般のユーザーがリアルタイムでそれを視聴できるようにすることができます!配信内容は自動的にアカウントに保存されるため、アプリケーションのメインセクションで配信が終了した直後に表示されます。

ミドルウェアを使用してアプリを保護する

認証されたすべてのページをこのミドルウェア関数の背後に配置します。リクエストヘッダーをチェックして、ユーザーが有効な認証済みユーザーのEメールを送信していることを確認します。

export const cfTeamsAccessAuthMiddleware = async ({request, data, env, next}) => {
    try {
        const userEmail = request.headers.get("cf-access-authenticated-user-email")

        if (!userEmail) {
            throw new Error("User not found, make sure application is behind Cloudflare Access")
        }
  
        // Pass user info to next handlers
        data.user = {
            email: userEmail
        }
  
        return next()
    } catch (e) {
        return new Response(e.toString(), {status: 401})
    }
}

export const onRequest = [
    cfTeamsAccessAuthMiddleware
]

すべてをPagesと一緒にまとめる

私たちにはログインフローを制御するCloudflare Accessがあります。Stream APIを使用して、動画のアップロード、表示、視聴を管理します。フェッチリクエストの管理とAPI呼び出しの処理には、Workersを使用します。今度は、Cloudflare Pages使用してすべてを結び付ける番です!

Pagesは、静的Webサイトをデプロイしてホストする簡単な方法を提供します。しかし今では、PagesはWorkersプラットフォームとシームレスに統合されています (発表記事へのリンク)。この新しい統合により、このアプリケーション全体を単一の読み取り可能なリポジトリにデプロイできます。

アクセスを制御する

アプリケーションによっては公開した方がよいもの、機密データが含まれているため特定のユーザーに制限する必要があるものがあります。メインページはこのアプリケーション用に公開になっており、管理ページを従業員に制限するためにCloudflare Accessを使用しました。内部学習サービスを構築している場合、または新しいサイトをベータ版で立ち上げたい場合でも、Accessを使用してアプリケーション全体を簡単に保護できます。

ユーザーがデモサイトの管理リンクをクリックすると、メールアドレスの入力を求められます。有効なCloudflareメールを入力すると、アプリケーションはアクセスコードを送信します。入力しない場合、そのページにアクセスできなくなります。

ソースコードを確認して、今すぐ動画アプリケーションの構築を始めましょう!