過去,構(gòu)建視頻應(yīng)用程序十分困難。在錄制、編碼和播放視頻背后有許多復(fù)雜的技術(shù)。幸運(yùn)的是,Cloudflare Stream分擔(dān)走了所有困難的部分,現(xiàn)在您可以輕松構(gòu)建自定義視頻和流媒體應(yīng)用程序。讓我們看一下,我們可以如何結(jié)合Cloudflare Stream、Access、Pages和Workers,使用極少的代碼創(chuàng)建一個(gè)高性能的視頻應(yīng)用程序。
今天,我們將構(gòu)建一個(gè)受Cloudflare TV啟發(fā)的視頻應(yīng)用程序。我們將提供用戶身份驗(yàn)證,管理員可以上傳錄制的視頻或直播新內(nèi)容。能夠使用Cloudflare服務(wù)打造自己的YouTube或Twich,這多么令人興奮!
提取視頻列表
我們想要在應(yīng)用程序主頁顯示所有視頻的列表,使用Cloudflare Stream上傳和存儲(chǔ)的所有視頻,之后還會(huì)有更多功能!該代碼可以更改,以僅顯示“熱門”視頻或?yàn)槊總€(gè)用戶選擇的視頻精選。目前,我們將使用搜索API并在一個(gè)空字符串中傳遞以返回全部。
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"}})
}
}
我們將仔細(xì)檢查每個(gè)視頻,過濾掉任何私人視頻,并抽取出我們需要的元數(shù)據(jù),例如縮略圖URL、ID和創(chuàng)建日期。
播放視頻
要允許用戶從我們的應(yīng)用程序播放視頻,則需要公開視頻,否則您將需要簽署每個(gè)請(qǐng)求。將視頻標(biāo)記為“公開”可使整個(gè)過程更為簡(jiǎn)單。然而,可能出于許多原因,您想要控制對(duì)視頻的訪問。如果您希望用戶登錄后才可播放視頻,或者想要能夠以任何方式限制訪問,可將視頻標(biāo)記為“私人”并使用簽名的URL來控制訪問。
如果您在本地測(cè)試您的應(yīng)用程序,或希望每天的請(qǐng)求數(shù)少于10,000個(gè),您可以調(diào)用/token端點(diǎn)以生成一個(gè)簽名令牌。如果您希望每天超過10,000個(gè)請(qǐng)求,請(qǐng)使用JSON Web Tokens簽署您自己的令牌,就像我們這里所做的一樣。
允許用戶上傳視頻
下一步是構(gòu)建管理員頁面,用戶可在其中上傳他們的視頻。
Cloudflare Stream API使該過程得到了簡(jiǎn)化。您可使用您的API令牌和帳戶ID生成一個(gè)唯一的一次性上傳URL。僅需確保您的令牌擁有Stream:Edit權(quán)限即可。我們從應(yīng)用程序鉤入所有POST請(qǐng)求,并返回生成的上傳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
]
管理員頁面包含一個(gè)表單,允許用戶從他們的計(jì)算機(jī)拖放或上傳視頻。當(dāng)一個(gè)已登錄用戶點(diǎn)擊上傳表單上的“提交”時(shí),應(yīng)用程序會(huì)生成一個(gè)唯一的URL,然后向其發(fā)布FormData。該代碼可有效用于構(gòu)建視頻分享網(wǎng)站或與任何允許用戶生成內(nèi)容的應(yīng)用程序搭配使用。
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添加實(shí)時(shí)視頻
您還可以使用Stream Live,結(jié)合我們已經(jīng)討論過的技術(shù),向您的應(yīng)用程序添加直播部分。您可以允許已登錄用戶開始直播,并允許其他已登錄用戶(甚至是未登錄用戶)實(shí)時(shí)觀看!直播內(nèi)容將自動(dòng)保存至您的帳戶,因此,當(dāng)直播結(jié)束后就可以立即在應(yīng)用程序的主要區(qū)域查看直播內(nèi)容。
使用中間件保護(hù)我們的應(yīng)用程序
我們將所有經(jīng)過身份驗(yàn)證的頁面都放在此中間件函數(shù)后方。它會(huì)檢查請(qǐng)求標(biāo)頭以確保用戶正在發(fā)送有效的經(jīng)過身份驗(yàn)證的用戶電子郵件。
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管理上傳、顯示和觀看視頻,使用Workers來管理fetch請(qǐng)求和處理API調(diào)用。現(xiàn)在,讓我們使用Cloudflare Pages將所有功能結(jié)合起來!
Pages提供一種簡(jiǎn)單的方法來部署和托管靜態(tài)網(wǎng)站。不過現(xiàn)在,Pages可與Workers平臺(tái)無縫集成(公告文章鏈接)。使用該新集成,我們可以使用單個(gè)可讀存儲(chǔ)庫(kù)來部署這一整個(gè)應(yīng)用程序。
控制訪問
一些應(yīng)用程序更適合公開;另一些應(yīng)用程序包含敏感數(shù)據(jù),應(yīng)當(dāng)限制向特定用戶開放。此應(yīng)用程序的主頁是公開的,我們已使用Cloudflare Access將管理員頁面限制為僅向員工開放。如果您要構(gòu)建內(nèi)部學(xué)習(xí)服務(wù),甚至想要推出新網(wǎng)站的測(cè)試版,您都可以輕松使用Access保護(hù)您的整個(gè)應(yīng)用程序!
當(dāng)用戶點(diǎn)擊我們演示網(wǎng)站上的管理員鏈接時(shí),將提示他們輸入電子郵件地址。如果他們輸入有效的Cloudflare電子郵件,則應(yīng)用程序會(huì)向他們發(fā)送訪問代碼。否則,他們將無法訪問該頁面。