← Cloudflare Stream / stream / manage-video-library
Использование webhooks
Вебхуки уведомляют ваш сервис о том, что видео успешно обработано и готово к трансляции, либо о том, что видео перешло в состояние ошибки.
Подпишитесь на уведомления вебхуков
Чтобы подписаться на уведомления webhook для своего сервиса или изменить существующую подписку, сгенерируйте API-токен на Токены API аккаунта странице дашборда Cloudflare.
Перейдите в Токены API аккаунта ↗URL для уведомлений webhook должен включать протокол. Поддерживаются только http:// или https:// поддерживается.
curl -X PUT --header 'Authorization: Bearer <API_TOKEN>' \
https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/stream/webhook \
--data '{"notificationUrl":"<WEBHOOK_NOTIFICATION_URL>"}'{
"result": {
"notificationUrl": "http://www.your-service-webhook-handler.com",
"modified": "2019-01-01T01:02:21.076571Z",
"secret": "85011ed3a913c6ad5f9cf6c5573cc0a7"
},
"success": true,
"errors": [],
"messages": []
}Уведомления
Когда обработка видео в вашем аккаунте завершится, вы получите POST запрос-уведомление с информацией о видео.
{
"uid": "6b9e68b07dfee8cc2d116e4c51d6a957",
"creator": null,
"thumbnail": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/thumbnails/thumbnail.jpg",
"thumbnailTimestampPct": 0,
"readyToStream": true,
"status": {
"state": "ready",
"pctComplete": "39.000000",
"errorReasonCode": "",
"errorReasonText": ""
},
"meta": {
"filename": "small.mp4",
"filetype": "video/mp4",
"name": "small.mp4",
"relativePath": "null",
"type": "video/mp4"
},
"created": "2022-06-30T17:53:12.512033Z",
"modified": "2022-06-30T17:53:21.774299Z",
"size": 383631,
"preview": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/watch",
"allowedOrigins": [],
"requireSignedURLs": false,
"uploaded": "2022-06-30T17:53:12.511981Z",
"uploadExpiry": "2022-07-01T17:53:12.511973Z",
"maxSizeBytes": null,
"maxDurationSeconds": null,
"duration": 5.5,
"input": {
"width": 560,
"height": 320
},
"playback": {
"hls": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/manifest/video.m3u8",
"dash": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/manifest/video.mpd"
},
"watermark": null
}-
uid: уникальный идентификатор видео. -
readytoStream: возвращаетtrueкогда закодирован и готов к трансляции хотя бы один уровень качества. -
status: статус обработки.-
state: возвращаетreadyкогда обработка видео завершена и закодированы все уровни качества. -
pctComplete: процент завершённой обработки. Когда это значение достигает100, доступны все уровни качества.
-
-
meta: метаданные, связанные с загруженным файлом. -
created: метка времени создания записи видео.
Коды ошибок
Если видео не удалось успешно обработать, state поле возвращает error, и errReasonCode возвращает одно из значений, перечисленных ниже.
ERR_NON_VIDEO: загруженный файл не является видео.ERR_DURATION_EXCEED_CONSTRAINT: продолжительность видео превышает ограничения, заданные для прямой загрузки автором.ERR_FETCH_ORIGIN_ERROR: не удалось загрузить видео по URL.ERR_MALFORMED_VIDEO: видео является допустимым файлом, но содержит повреждённые данные, которые невозможно восстановить.ERR_DURATION_TOO_SHORT: продолжительность видео меньше 0.1 секунды.ERR_UNKNOWN: если Stream не может автоматически определить причину ошибки видео,ERR_UNKNOWNкод будет использован.
Помимо state поле, у видео readyToStream поле также должно быть true для воспроизведения видео.
{
"readyToStream": false,
"status": {
"state": "error",
"step": "encoding",
"pctComplete": "39",
"errReasonCode": "ERR_MALFORMED_VIDEO",
"errReasonText": "The video was deemed to be corrupted or malformed.",
}
}Проверка подлинности webhook
Cloudflare Stream подписывает запросы webhook, отправляемые на ваши URL уведомлений, и включает подпись каждого запроса в Webhook-Signature HTTP-заголовок. Это позволяет вашему приложению убедиться, что запросы вебхуков отправлены Stream.
Чтобы проверить подпись, нужно получить секрет подписи webhook. Это значение возвращается в ответе API при создании или получении webhook.
Чтобы проверить подпись, получите значение Webhook-Signature заголовок, который будет выглядеть примерно так, как в примере ниже.
Webhook-Signature: time=1230811200,sig1=60493ec9388b44585a29543bcf0de62e377d4da393246a8b1c901d0e3e672404
1. Разбор подписи
Получите Webhook-Signature заголовок из запроса вебхука и разделите строку с помощью , символ.
Разделите каждое значение ещё раз с помощью = символ.
Значение для time это текущий время UNIX ↗ когда сервер отправил запрос. sig1 это подпись тела запроса.
На этом этапе следует отклонять запросы с отметками времени, которые слишком устарели для вашего приложения.
2. Создание исходной строки для подписи
Подготовьте исходную строку подписи, объединив следующие строки:
- Значение
timeполе, например1230811200 - Символ
. - Тело запроса вебхука (включая символы новой строки, если применимо)
Для успешной проверки подписи каждый байт тела запроса должен оставаться неизменным.
3. Создание ожидаемой подписи
Вычислите HMAC с помощью функции SHA256 (HMAC-SHA256), используя секрет webhook и исходную строку из шага 2. Этот шаг зависит от языка программирования, используемого в вашем приложении.
Подпись Cloudflare кодируется в шестнадцатеричном формате.
4. Сравнение ожидаемой и фактической подписей
Сравните подпись в заголовке запроса с ожидаемой подписью. Желательно использовать функцию сравнения с постоянным временем выполнения.
Если подписи совпадают, можно быть уверенным, что вебхук отправлен Cloudflare.
Ограничения
- Вебхуки отправляются только после завершения обработки видео, а в теле запроса указывается, успешно ли она прошла.
- На аккаунт допускается только одна подписка на webhook.
- Cloudflare не может отправлять webhook на
localhostили локальные IP-адреса. Требуется общедоступный URL. Для локального тестирования используйте Quick Tunnel чтобы открыть локальный сервер для доступа из интернета. Пошаговое руководство см. в разделе Протестируйте вебхуки локально.
Примеры
Golang
Использование crypto/hmac ↗:
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"log"
)
func main() {
secret := []byte("secret from the Cloudflare API")
message := []byte("string from step 2")
hash := hmac.New(sha256.New, secret)
hash.Write(message)
hashToCheck := hex.EncodeToString(hash.Sum(nil))
log.Println(hashToCheck)
}Node.js
var crypto = require("crypto");
var key = "secret from the Cloudflare API";
var message = "string from step 2";
var hash = crypto.createHmac("sha256", key).update(message);
hash.digest("hex");Ruby
require 'openssl'
key = 'secret from the Cloudflare API'
message = 'string from step 2'
OpenSSL::HMAC.hexdigest('sha256', key, message)На JavaScript (например, для использования в Cloudflare Workers)
const key = "secret from the Cloudflare API";
const message = "string from step 2";
const getUtf8Bytes = (str) =>
new Uint8Array(
[...decodeURIComponent(encodeURIComponent(str))].map((c) =>
c.charCodeAt(0),
),
);
const keyBytes = getUtf8Bytes(key);
const messageBytes = getUtf8Bytes(message);
const cryptoKey = await crypto.subtle.importKey(
"raw",
keyBytes,
{ name: "HMAC", hash: "SHA-256" },
true,
["sign"],
);
const sig = await crypto.subtle.sign("HMAC", cryptoKey, messageBytes);
[...new Uint8Array(sig)].map((b) => b.toString(16).padStart(2, "0")).join("");