import {z} from 'zod' export type ClipLayoutCrop = {bottom: number, left: number, right: number, top: number}; export type CustomLayoutDimensions = {height: number, width: number}; export type CustomLayoutPoint = {x: number, y: number}; export type CustomLayoutSlot = {corners?: ('none' | 'regular' | 'squircle') | undefined, dimensions?: CustomLayoutDimensions | undefined, hidden?: boolean | undefined, order?: number | undefined, point?: CustomLayoutPoint | undefined, shape?: ('none' | 'circle' | 'square' | 'portrait' | 'cutout' | 'blob' | 'blob-outline') | undefined}; export type ClipLayoutShape = {kind: 'fullscreen', screenFit?: ('cover' | 'letterbox') | undefined, style?: ('regular' | 'stretch') | undefined} | {kind: 'middle', shape?: ('circle' | 'square' | 'landscape' | 'portrait') | undefined} | {kind: 'side-by-side', position: 'left' | 'right' | 'top' | 'bottom', size?: ('S' | 'M' | 'L') | undefined, style: 'regular' | 'even' | 'overlap' | 'perspective'} | {kind: 'tv-presenter', position: 'left' | 'right', style: 'regular' | 'full' | 'overlap'} | {kind: 'cut-out', position?: ('left' | 'right') | undefined, style?: ('regular' | 'perspective') | undefined} | {kind: 'camera-bubble', position: 'left' | 'right' | 'top' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'middle', screenFit?: ('cover' | 'letterbox') | undefined, shape?: ('circle' | 'square' | 'landscape' | 'portrait') | undefined, size: 'S' | 'M' | 'L', style?: ('regular' | 'full') | undefined} | {kind: 'camera-only', punchIn?: boolean | undefined, shape?: ('circle' | 'square' | 'landscape' | 'portrait') | undefined, style: 'fullscreen' | 'middle'} | {kind: 'screen-only', screenFit?: ('cover' | 'letterbox') | undefined, style: 'fullscreen' | 'middle'} | {kind: 'custom', presentation?: CustomLayoutSlot | undefined, subject?: CustomLayoutSlot | undefined}; export type ClipMedia = {slot?: ('screen' | 'camera') | undefined, sourceId: string, type: 'image'} | {muted?: boolean | undefined, slot?: ('screen' | 'camera') | undefined, sourceId: string, startTimeMs?: number | undefined, type: 'video'} | {muted?: boolean | undefined, slot?: ('screen' | 'camera') | undefined, startTimeMs: number, type: 'recording'} | {documentId: string, slot?: ('screen' | 'camera') | undefined, type: 'motion'}; export type ClipPopOut = {amount?: number | undefined, background?: ('original' | 'color') | undefined, color?: string | undefined, outlineColor?: string | undefined, outlineWidth?: number | undefined}; export type LayoutTransitionStyle = 'spring' | 'hardCut'; export type UpdateClipLayoutRequest = {cameraStyle?: ('none' | 'circle' | 'square' | 'portrait' | 'cutout' | 'blob' | 'blob-outline' | null) | undefined, crop?: (ClipLayoutCrop | null) | undefined, durationMs?: number | undefined, layout?: ClipLayoutShape | undefined, media?: ClipMedia | undefined, popOut?: (ClipPopOut | null) | undefined, startTimeMs?: number | undefined, transitionStyle?: LayoutTransitionStyle | undefined}; export type PatchApiV1VideosIdClipsClipIdLayoutsLayoutIdArgs = {id: string, clipId: string, layoutId: string, body: UpdateClipLayoutRequest}; export type PatchApiV1VideosIdClipsClipIdLayoutsLayoutIdOptions = {baseUrl?: string | undefined, BearerAuth?: string | undefined, fetch?: typeof fetch | undefined}; export const clipLayoutCropOutput = z.object({bottom: z.number().gte(0), left: z.number().gte(0), right: z.number().gte(0), top: z.number().gte(0)}); export const customLayoutDimensions = z.object({height: z.number().gt(0), width: z.number().gt(0)}); export const customLayoutPoint = z.object({x: z.number(), y: z.number()}); export const customLayoutSlot = z.object({corners: z.enum(["none", "regular", "squircle"]).optional(), dimensions: customLayoutDimensions.optional(), hidden: z.boolean().optional(), order: z.number().int().gte(-9007199254740991).lte(9007199254740991).optional(), point: customLayoutPoint.optional(), shape: z.enum(["none", "circle", "square", "portrait", "cutout", "blob", "blob-outline"]).optional()}); export const clipLayoutShape = z.discriminatedUnion("kind", [z.object({kind: z.literal("fullscreen"), screenFit: z.enum(["cover", "letterbox"]).optional(), style: z.enum(["regular", "stretch"]).optional()}), z.object({kind: z.literal("middle"), shape: z.enum(["circle", "square", "landscape", "portrait"]).optional()}), z.object({kind: z.literal("side-by-side"), position: z.enum(["left", "right", "top", "bottom"]), size: z.enum(["S", "M", "L"]).optional(), style: z.enum(["regular", "even", "overlap", "perspective"])}), z.object({kind: z.literal("tv-presenter"), position: z.enum(["left", "right"]), style: z.enum(["regular", "full", "overlap"])}), z.object({kind: z.literal("cut-out"), position: z.enum(["left", "right"]).optional(), style: z.enum(["regular", "perspective"]).optional()}), z.object({kind: z.literal("camera-bubble"), position: z.enum(["left", "right", "top", "bottom", "top-left", "top-right", "bottom-left", "bottom-right", "middle"]), screenFit: z.enum(["cover", "letterbox"]).optional(), shape: z.enum(["circle", "square", "landscape", "portrait"]).optional(), size: z.enum(["S", "M", "L"]), style: z.enum(["regular", "full"]).optional()}), z.object({kind: z.literal("camera-only"), punchIn: z.boolean().optional(), shape: z.enum(["circle", "square", "landscape", "portrait"]).optional(), style: z.enum(["fullscreen", "middle"])}), z.object({kind: z.literal("screen-only"), screenFit: z.enum(["cover", "letterbox"]).optional(), style: z.enum(["fullscreen", "middle"])}), z.object({kind: z.literal("custom"), presentation: customLayoutSlot.optional(), subject: customLayoutSlot.optional()})]); export const clipMediaItem = z.object({documentId: z.string().optional(), height: z.number().int().gte(-9007199254740991).lte(9007199254740991).nullable().optional(), imageUrl: z.string().nullable().optional(), muted: z.boolean().nullable().optional(), slot: z.enum(["screen", "camera"]).optional(), sourceId: z.string().nullable().optional(), startTimeMs: z.number().nullable().optional(), type: z.enum(["image", "video", "motion"]), width: z.number().int().gte(-9007199254740991).lte(9007199254740991).nullable().optional()}); export const clipPopOut = z.object({amount: z.number().gte(0).lte(0.5).optional(), background: z.enum(["original", "color"]).optional(), color: z.string().optional(), outlineColor: z.string().optional(), outlineWidth: z.number().gte(0).lte(0.05).optional()}); export const layoutTransitionStyle = z.enum(["spring", "hardCut"]); export const clipLayout = z.object({cameraStyle: z.enum(["none", "circle", "square", "portrait", "cutout", "blob", "blob-outline"]).optional(), crop: clipLayoutCropOutput.nullable().optional(), durationMs: z.number().gte(0).nullable().optional(), followsBase: z.boolean().optional(), id: z.string(), layout: clipLayoutShape.nullable().optional(), media: z.array(clipMediaItem), popOut: clipPopOut.nullable().optional(), startTimeMs: z.number().gte(0).nullable().optional(), transitionStyle: layoutTransitionStyle.nullable().optional()}); export const clipLayoutResponse = z.object({layout: clipLayout}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Update a layout * * PATCH /v1/videos/{id}/clips/{clipId}/layouts/{layoutId} */ export const patchApiV1VideosIdClipsClipIdLayoutsLayoutId = async (args: PatchApiV1VideosIdClipsClipIdLayoutsLayoutIdArgs, options?: PatchApiV1VideosIdClipsClipIdLayoutsLayoutIdOptions) => { const {baseUrl = "https://api.tella.com", fetch: fetchFn = fetch, BearerAuth} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") if (BearerAuth !== undefined) headers.set('authorization', `Bearer ${BearerAuth}`) const url = `${baseUrl.replace(/\/+$/, '')}/v1/videos/${encodeURIComponent(String(args.id))}/clips/${encodeURIComponent(String(args.clipId))}/layouts/${encodeURIComponent(String(args.layoutId))}${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "PATCH", headers, body: JSON.stringify(args.body)}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(clipLayoutResponse, await res.json()) };