INTEGRITY Документация

Создание превью YouTube с помощью Workers и Cloudflare Image Resizing

В этом руководстве вы узнаете, как программно создать собственное превью для YouTube с помощью Cloudflare Workers и Cloudflare Image Resizing. Собственное превью для YouTube может понадобиться, чтобы настроить его дизайн, призывы к действию и изображения, которые побуждают больше зрителей посмотреть ваше видео.

Это руководство поможет вам разобраться в работе с Изображения,Image Resizing и Cloudflare Workers.

Прежде чем начать

Во всех руководствах предполагается, что вы уже выполнили Руководство по началу работы, который поможет вам настроить аккаунт Cloudflare Workers, C3, а также Wrangler.

Чтобы пройти это руководство, убедитесь, что у вас установлены Node, Cargo и Wrangler установлен на вашем компьютере.

Цели обучения

В этом руководстве вы узнаете, как:

Загрузите изображение

Чтобы создать пользовательское изображение миниатюры, сначала загрузите фоновое изображение в Cloudflare Images. Оно будет использоваться как исходное изображение для преобразований при создании миниатюр.

Cloudflare Images позволяет хранить, изменять размер, оптимизировать и доставлять изображения быстро и безопасно. Чтобы начать, загрузите изображения в панель управления Cloudflare или используйте Upload API.

Загрузите через панель управления

Чтобы загрузить изображение через дашборд Cloudflare:

  1. На панели управления Cloudflare перейдите к разделу Преобразования страницу.

    Перейдите в Преобразования ↗
  2. Используйте Quick Upload чтобы перетащить изображение или щелкнуть и выбрать файл на своем компьютере.

  3. После загрузки изображения просмотрите его по сгенерированному URL.

Загрузите с помощью API

Чтобы загрузить изображение с помощью Загрузите по URL API см. пример ниже:

curl --request POST \
 --url https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/images/v1 \
 --header 'Authorization: Bearer <API_TOKEN>' \
 --form 'url=<PATH_TO_IMAGE>' \
 --form 'metadata={"key":"value"}' \
 --form 'requireSignedURLs=false'

После этого вы получите ответ, похожий на следующий:

{
	"result": {
		"id": "2cdc28f0-017a-49c4-9ed7-87056c83901",
		"filename": "image.jpeg",
		"metadata": {
			"key": "value"
		},
		"uploaded": "2022-01-31T16:39:28.458Z",
		"requireSignedURLs": false,
		"variants": [
			"https://imagedelivery.net/Vi7wi5KSItxGFsWRG2Us6Q/2cdc28f0-017a-49c4-9ed7-87056c83901/public",
			"https://imagedelivery.net/Vi7wi5KSItxGFsWRG2Us6Q/2cdc28f0-017a-49c4-9ed7-87056c83901/thumbnail"
		]
	},
	"success": true,
	"errors": [],
	"messages": []
}

Теперь, когда изображение загружено, вы будете использовать его как фоновое изображение для миниатюры видео.

Создайте Worker для преобразования текста в изображение

После загрузки изображения создайте Worker, который позволит преобразовывать текст в изображение. Это изображение можно использовать как наложение на загруженное фоновое изображение. Используйте rustwasm-worker-template.

Прежде чем начать, вам понадобится следующее:

Создайте новый проект Worker с помощью worker-rust шаблон:

cargo generate https://github.com/cloudflare/rustwasm-worker-template

Теперь вы внесёте несколько изменений в файлы в каталоге проекта.

  1. В lib.rs файл, добавьте следующий блок кода:
use worker::*;
mod utils;

#[event(fetch)]
pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> {
   // Optionally, get more helpful error messages written to the console in the case of a panic.
   utils::set_panic_hook();

   let router = Router::new();
   router
       .get("/", |_, _| Response::ok("Hello from Workers!"))
       .run(req, env)
       .await
}
  1. Обновите Cargo.toml файл в вашем worker-to-text каталог проекта для использования text-to-png, пакет Rust для рендеринга текста в PNG. Добавьте пакет как зависимость, выполнив команду:
cargo add [email protected]
  1. Импортируйте text_to_png библиотеку в worker-to-text проекта lib.rs файл.
use text_to_png::{TextPng, TextRenderer};
use worker::*;
mod utils;

#[event(fetch)]
pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> {
   // Optionally, get more helpful error messages written to the console in the case of a panic.
   utils::set_panic_hook();

   let router = Router::new();
   router
       .get("/", |_, _| Response::ok("Hello from Workers!"))
       .run(req, env)
       .await
}
  1. Обновите lib.rs чтобы создать handle-slash функция, которая запускает преобразование изображения на основе текста, переданного в URL в качестве параметра запроса.
use text_to_png::{TextPng, TextRenderer};
use worker::*;
mod utils;

#[event(fetch)]
pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> {
   // Optionally, get more helpful error messages written to the console in the case of a panic.
   utils::set_panic_hook();

   let router = Router::new();
   router
       .get("/", |_, _| Response::ok("Hello from Workers!"))
       .run(req, env)
       .await
}

async fn handle_slash(text: String) -> Result<Response> {}
  1. В handle-slash функция, вызовите TextRenderer присвоив его значению renderer, указав, что нужно использовать пользовательский шрифт. Затем используйте render_text_to_png_data метод для преобразования текста в изображение. В этом примере пользовательский шрифт (Inter-Bold.ttf) находится в /assets папка в корне проекта, которая будет использоваться для создания миниатюры. Вам нужно изменить эту часть кода, чтобы она указывала на ваш собственный файл шрифта.
use text_to_png::{TextPng, TextRenderer};
use worker::*;
mod utils;

#[event(fetch)]
pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> {
   // Optionally, get more helpful error messages written to the console in the case of a panic.
   utils::set_panic_hook();

   let router = Router::new();
   router
       .get("/", |_, _| Response::ok("Hello from Workers!"))
       .run(req, env)
       .await
}

async fn handle_slash(text: String) -> Result<Response> {
  let renderer = TextRenderer::try_new_with_ttf_font_data(include_bytes!("../assets/Inter-Bold.ttf"))
    .expect("Example font is definitely loadable");

  let text_png: TextPng = renderer.render_text_to_png_data(text.replace("+", " "), 60, "003682").unwrap();
}
  1. Перепишите Router функцию для вызова handle_slash когда в URL передан запрос, а иначе возвращать "Hello Worker!" в качестве ответа.
use text_to_png::{TextPng, TextRenderer};
use worker::*;
mod utils;

#[event(fetch)]
pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> {
   // Optionally, get more helpful error messages written to the console in the case of a panic.
   utils::set_panic_hook();

  let router = Router::new();
    router
      .get_async("/", |req, _| async move {
        if let Some(text) = req.url()?.query() {
          handle_slash(text.into()).await
        } else {
          handle_slash("Hello Worker!".into()).await
        }
      })
      .run(req, env)
        .await
}

async fn handle_slash(text: String) -> Result<Response> {
  let renderer = TextRenderer::try_new_with_ttf_font_data(include_bytes!("../assets/Inter-Bold.ttf"))
    .expect("Example font is definitely loadable");

  let text_png: TextPng = renderer.render_text_to_png_data(text.replace("+", " "), 60, "003682").unwrap();
}
  1. В вашем lib.rs файле присвойте заголовкам значение content-type: image/png чтобы ответ корректно отображался как изображение PNG.
use text_to_png::{TextPng, TextRenderer};
use worker::*;
mod utils;

#[event(fetch)]
pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> {
   // Optionally, get more helpful error messages written to the console in the case of a panic.
   utils::set_panic_hook();

   let router = Router::new();
    router
      .get_async("/", |req, _| async move {
        if let Some(text) = req.url()?.query() {
          handle_slash(text.into()).await
        } else {
          handle_slash("Hello Worker!".into()).await
        }
      })
      .run(req, env)
        .await
}

async fn handle_slash(text: String) -> Result<Response> {
  let renderer = TextRenderer::try_new_with_ttf_font_data(include_bytes!("../assets/Inter-Bold.ttf"))
    .expect("Example font is definitely loadable");

  let text_png: TextPng = renderer.render_text_to_png_data(text.replace("+", " "), 60, "003682").unwrap();

  let mut headers = Headers::new();
  headers.set("content-type", "image/png")?;

  Ok(Response::from_bytes(text_png.data)?.with_headers(headers))
}

Финальный lib.rs файл должен выглядеть следующим образом. Полный код в виде примера репозитория можно найти на GitHub.

use text_to_png::{TextPng, TextRenderer};
use worker::*;

mod utils;

#[event(fetch)]
pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> {
    // Optionally, get more helpful error messages written to the console in the case of a panic.
    utils::set_panic_hook();

    let router = Router::new();

    router
        .get_async("/", |req, _| async move {
            if let Some(text) = req.url()?.query() {
                handle_slash(text.into()).await
            } else {
                handle_slash("Hello Worker!".into()).await
            }
        })
        .run(req, env)
        .await
}

async fn handle_slash(text: String) -> Result<Response> {
    let renderer = TextRenderer::try_new_with_ttf_font_data(include_bytes!("../assets/Inter-Bold.ttf"))
    .expect("Example font is definitely loadable");

    let text = if text.len() > 128 {
        "Nope".into()
    } else {
        text
    };

    let text = urlencoding::decode(&text).map_err(|_| worker::Error::BadEncoding)?;

    let text_png: TextPng = renderer.render_text_to_png_data(text.replace("+", " "), 60, "003682").unwrap();

    let mut headers = Headers::new();
    headers.set("content-type", "image/png")?;

    Ok(Response::from_bytes(text_png.data)?.with_headers(headers))
}

После завершения обновления проекта запустите локальный сервер для разработки Worker, выполнив:

npx wrangler dev

В результате должен запуститься localhost экземпляр с отображаемым изображением:

Выполните wrangler dev, чтобы запустить локальный сервер для вашего Worker

При добавлении параметра запроса с произвольным текстом вы должны получить:

Следуйте инструкциям выше, чтобы получить выходное изображение

Чтобы развернуть Worker, откройте файл Wrangler и обновите name ключ с именем вашего проекта. Ниже приведён пример с именем проекта из этого руководства:

{
	"$schema": "./node_modules/wrangler/config-schema.json",
	"name": "worker-to-text"
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "worker-to-text"

Затем выполните npx wrangler deploy команду, чтобы развернуть Worker.

npx wrangler deploy

A .workers.dev домен будет создан для вашего Worker после выполнения wrangler deploy. Этот домен будет использоваться в главном изображении миниатюры.

Создайте Worker, который отображает исходное изображение

Создайте Worker, который отдаёт изображение, загруженное в Images, выполнив:

npm create cloudflare@latest -- thumbnail-image

Для настройки выберите следующие параметры:

Чтобы начать разработку Worker, cd в каталог вашего нового проекта:

cd thumbnail-image

Это создаст новый проект Worker с именем thumbnail-image. В src/index.js файл, добавьте следующий блок кода:

export default {
	async fetch(request, env) {
		const url = new URL(request.url);
		if (url.pathname === "/original-image") {
			const image = await fetch(
				`https://imagedelivery.net/${env.CLOUDFLARE_ACCOUNT_HASH}/${IMAGE_ID}/public`,
			);
			return image;
		}
		return new Response("Image Resizing with a Worker");
	},
};

Обновите env.CLOUDFLARE_ACCOUNT_HASH с вашим Cloudflare account ID. Обновите env.IMAGE_ID с вашим ID образа.

Запустите ваш Worker и перейдите к /original-image маршрут для проверки изображения.

Добавление произвольного текста на изображение

Теперь вы используете Преобразования изображений Cloudflare, с fetch метод, чтобы добавить динамическое текстовое изображение как наложение поверх фонового изображения. Сначала выведите итоговое изображение на отдельном маршруте. Назовите новый маршрут /thumbnail.

export default {
	async fetch(request, env) {
		const url = new URL(request.url);
		if (url.pathname === "/original-image") {
			const image = await fetch(
				`https://imagedelivery.net/${env.CLOUDFLARE_ACCOUNT_HASH}/${IMAGE_ID}/public`,
			);
			return image;
		}

		if (url.pathname === "/thumbnail") {
		}

		return new Response("Image Resizing with a Worker");
	},
};

Затем используйте fetch метод, чтобы применить изменения трансформации изображения поверх фонового изображения. Параметры наложения вложены в options.cf.image.

export default {
	async fetch(request, env) {
		const url = new URL(request.url);

		if (url.pathname === "/original-image") {
			const image = await fetch(
				`https://imagedelivery.net/${env.CLOUDFLARE_ACCOUNT_HASH}/${IMAGE_ID}/public`,
			);
			return image;
		}

		if (url.pathname === "/thumbnail") {
			fetch(imageURL, {
				cf: {
					image: {},
				},
			});
		}

		return new Response("Image Resizing with a Worker");
	},
};

imageURL это URL изображения, которое вы хотите использовать в качестве фонового изображения. В cf.image объект, укажите параметры, которые нужно применить к фоновому изображению.

Добавьте фоновое изображение в каталог assets на GitHub и отправьте изменения в GitHub. Скопируйте URL загруженного изображения: кликните по нему левой кнопкой мыши и выберите Копировать URL удаленного файла опцию.

Замените imageURL значение на скопированный удаленный URL.

if (url.pathname === "/thumbnail") {
	const imageURL =
		"https://github.com/lauragift21/social-image-demo/blob/1ed9044463b891561b7438ecdecbdd9da48cdb03/assets/cover.png?raw=true";
	fetch(imageURL, {
		cf: {
			image: {},
		},
	});
}

Затем добавьте параметры наложения в объект изображения. Измените размер изображения до нужной ширины и высоты для миниатюр YouTube и используйте draw параметр, чтобы добавить наложенный текст с использованием развёрнутого URL вашего text-to-image Worker.

fetch(imageURL, {
	cf: {
		image: {
			width: 1280,
			height: 720,
			draw: [
				{
					url: "https://text-to-image.examples.workers.dev",
					left: 40,
				},
			],
		},
	},
});

Преобразования изображений можно протестировать только после развёртывания Worker.

Чтобы развернуть Worker, откройте файл Wrangler и обновите name ключ с именем вашего проекта. Ниже приведён пример с именем проекта из этого руководства:

{
	"$schema": "./node_modules/wrangler/config-schema.json",
	"name": "thumbnail-image"
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "thumbnail-image"

Разверните ваш Worker, выполнив команду:

npx wrangler deploy

Команда развёртывает ваш Worker на пользовательских workers.dev поддомен. Перейдите в .workers.dev поддомен и перейдите в /thumbnail маршрут.

Вы должны увидеть изображение изменённого размера с текстом Hello Workers!.

Следуйте описанным выше шагам, чтобы сгенерировать изображение с изменённым размером.

Теперь вы сделаете применяемый текст динамическим. Динамический текст позволит вам изменять текст, и он будет автоматически обновляться на изображении.

Чтобы добавить динамический текст, добавьте любой текст к /thumbnail URL с помощью параметров запроса и передайте его в text-to-image URL Worker в качестве параметра.

for (const title of url.searchParams.values()) {
	try {
		const editedImage = await fetch(imageURL, {
			cf: {
				image: {
					width: 1280,
					height: 720,
					draw: [
						{
							url: `https://text-to-image.examples.workers.dev/?${title}`,
							left: 50,
						},
					],
				},
			},
		});
		return editedImage;
	} catch (error) {
		console.log(error);
	}
}

Пройдя это руководство, вы успешно создали собственный генератор превью для YouTube.

В этом руководстве вы узнали, как использовать Cloudflare Workers и Cloudflare Image Transformations для создания собственных превью для YouTube. Подробнее о Cloudflare Workers и Image Transformations читайте в Изменение размера изображения с помощью Worker.