import type { Task, Helpers } from "graphile-worker"; import getVod from "@futureporn/fetchers/getVod.ts"; import { getStoryboard } from '@futureporn/utils/image.ts' import { getCdnUrl, uploadFile, type S3FileArgs } from '@futureporn/storage/s3.ts' import patchVodInDatabase from "@futureporn/fetchers/patchVodInDatabase.ts"; import { configs } from "../config"; interface Payload { vod_id?: string; video_url?: string; } function assertPayload(payload: any): asserts payload is Payload { if (typeof payload !== "object" || !payload) throw new Error("invalid payload (it must be an object)"); if (typeof payload.vod_id !== "string" && typeof payload.video_url !== "string") throw new Error("payload requires either vod_id or video_url, however both were not a string."); } async function doIntegratedRequest(vodId: string): Promise { const vod = await getVod(vodId) const s3_file = vod?.s3_file if (!s3_file) throw new Error(`vod ${vodId} was missing a s3_file.`); // we need to get a CDN url to the vod so we can download chunks of the file in order for Prevvy to create the storyboard image. const cdnUrl = getCdnUrl(configs.s3MainBucket, s3_file.s3_key) const tmpImagePath = await getStoryboard(cdnUrl) // we need to upload the image to S3 const uploadArgs: S3FileArgs = { filePath: tmpImagePath, s3AccessKeyId: configs.s3AccessKeyId, s3SecretAccessKey: configs.s3SecretAccessKey, s3BucketName: configs.s3MainBucket, s3Endpoint: configs.s3Region, s3Region: configs.s3Region } const upload = await uploadFile(uploadArgs) if (!upload) throw new Error(`failed to upload ${tmpImagePath} to S3`); if (!upload.Key) throw new Error(`failed to upload ${tmpImagePath} to S3 (upload.Key was missing)`); // we need to create a S3 file in the db const thumbnail = getCdnUrl(configs.s3MainBucket, upload.Key) await patchVodInDatabase(vodId, { thumbnail }) } async function doSoloRequest(videoUrl: string): Promise { return await getThumbnailUrl(videoUrl) } export const generate_thumbnail: Task = async function (payload: unknown, helpers: Helpers) { assertPayload(payload) const { vod_id, video_url } = payload helpers.logger.info(`🏗️ generate_thumbnail started with vod_id=${vod_id}, video_url=${video_url}`); // Determine what kind of request is being made. // It could be one of two scenarios // * Here is a VOD record in the database, please update it's thumbnail. (integratedRequest) // * Here is a video URL, please give us a thumbnail URL. (soloRequest) // const integratedRequest = (!!vod_id && !video_url) const soloRequest = (!!video_url && !vod_id) if (integratedRequest) { await doIntegratedRequest(vod_id) } else if (soloRequest) { await getThumbnailUrl(video_url) } else { throw new Error(`unsupported ambiguous request!`) } } export default generate_thumbnail