sanity-plugin-mux-input
Version:
An input component that integrates Sanity Studio with Mux video encoding/hosting service.
420 lines (378 loc) • 14.5 kB
text/typescript
import type {ObjectInputProps, PreviewLayoutKey, PreviewProps, SchemaType} from 'sanity'
import type {PartialDeep} from 'type-fest'
export interface MuxInputConfig {
/**
* Enable static renditions by setting this to 'standard'. Can be overwritten on a per-asset basis.
* Requires `"encoding_tier": "smart"`
* @see {@link https://docs.mux.com/guides/video/enable-static-mp4-renditions#why-enable-mp4-support}
* @defaultValue 'none'
*/
mp4_support: 'none' | 'standard'
/**
* Max resolution tier can be used to control the maximum resolution_tier your asset is encoded, stored, and streamed at.
* Requires `"encoding_tier": "smart"`
* @see {@link https://docs.mux.com/guides/stream-videos-in-4k}
* @defaultValue '1080p'
*/
max_resolution_tier: '2160p' | '1440p' | '1080p'
/**
* The encoding tier informs the cost, quality, and available platform features for the asset.
* @see {@link https://docs.mux.com/guides/use-encoding-tiers}
* @defaultValue 'smart'
*/
encoding_tier: 'baseline' | 'smart'
/**
* Normalize the audio track loudness level.
* @see {@link https://docs.mux.com/guides/adjust-audio-levels#how-to-turn-on-audio-normalization}
* @defaultValue false
*/
normalize_audio: boolean
/**
* Enables signed URLs by default, if you configured them with your API token.
* @see {@link https://docs.mux.com/guides/secure-video-playback}
* @defaultValue false
*/
defaultSigned?: boolean
/**
* Auto-generate captions for these languages by default.
* Requires `"encoding_tier": "smart"`
*
* @see {@link https://docs.mux.com/guides/add-autogenerated-captions-and-use-transcripts}
* @deprecated use `defaultAutogeneratedSubtitleLang` instead. Only a single autogenerated
*/
defaultAutogeneratedSubtitleLangs?: SupportedMuxLanguage[]
/**
* Auto-generate captions for this language by default. Users can still
* Requires `"encoding_tier": "smart"`
*
* @see {@link https://docs.mux.com/guides/add-autogenerated-captions-and-use-transcripts}
*/
defaultAutogeneratedSubtitleLang?: SupportedMuxLanguage
/**
* Whether or not to allow content editors to override asset upload
* configuration settings when uploading a video to Mux.
*
* @see {@link https://docs.mux.com/guides/secure-video-playback}
* @defaultValue false
*/
disableUploadConfig?: boolean
/**
* Whether or not to allow content editors to add text tracks alongside their
* asset when uploading a video to Mux.
*
* @see {@link https://docs.mux.com/guides/secure-video-playback}
* @defaultValue false
*/
disableTextTrackConfig?: boolean
}
export interface PluginConfig extends MuxInputConfig {
/**
* How the videos browser should appear as a studio tool in Sanity's top navigation
*
* Pass `false` if you want to disable it.
* @defaultValue {title: 'Videos', icon: VideoIcon}
**/
tool:
| false
| {
title?: string
icon?: React.ComponentType
}
/**
* The roles that are allowed to configure the plugin.
*
* If not set, all roles will be allowed to configure the plugin.
* @defaultValue []
*/
allowedRolesForConfiguration: string[]
}
export const SUPPORTED_MUX_LANGUAGES = [
{label: 'English', code: 'en', state: 'Stable'},
{label: 'Spanish', code: 'es', state: 'Stable'},
{label: 'Italian', code: 'it', state: 'Stable'},
{label: 'Portuguese', code: 'pt', state: 'Stable'},
{label: 'German', code: 'de', state: 'Stable'},
{label: 'French', code: 'fr', state: 'Stable'},
{label: 'Polish', code: 'pl', state: 'Beta'},
{label: 'Russian', code: 'ru', state: 'Beta'},
{label: 'Dutch', code: 'nl', state: 'Beta'},
{label: 'Catalan', code: 'ca', state: 'Beta'},
{label: 'Turkish', code: 'tr', state: 'Beta'},
{label: 'Swedish', code: 'sv', state: 'Beta'},
{label: 'Ukrainian', code: 'uk', state: 'Beta'},
{label: 'Norwegian', code: 'no', state: 'Beta'},
{label: 'Finnish', code: 'fi', state: 'Beta'},
{label: 'Slovak', code: 'sk', state: 'Beta'},
{label: 'Greek', code: 'el', state: 'Beta'},
{label: 'Czech', code: 'cs', state: 'Beta'},
{label: 'Croatian', code: 'hr', state: 'Beta'},
{label: 'Danish', code: 'da', state: 'Beta'},
{label: 'Romanian', code: 'ro', state: 'Beta'},
{label: 'Bulgarian', code: 'bg', state: 'Beta'},
] as const
export const ENCODING_TIERS = [
{label: 'Baseline', value: 'baseline'},
{label: 'Smart', value: 'smart'},
] as const
export const SUPPORTED_MUX_LANGUAGES_VALUES = SUPPORTED_MUX_LANGUAGES.map((l) => l.code)
export type SupportedMuxLanguage = (typeof SUPPORTED_MUX_LANGUAGES_VALUES)[number]
export interface TextTrack {
_id: string
name: string
}
export interface AutogeneratedTextTrack extends TextTrack {
type: 'autogenerated'
language_code: SupportedMuxLanguage
}
export interface CustomTextTrack extends TextTrack {
type: 'subtitles' | 'captions'
language_code: string
file: {
contents: string
type: string
name: string
size: number
}
}
export function isCustomTextTrack(track: Partial<UploadTextTrack>): track is CustomTextTrack {
return track.type !== 'autogenerated'
}
export function isAutogeneratedTrack(
track: Partial<UploadTextTrack>
): track is AutogeneratedTextTrack {
return track.type === 'autogenerated'
}
export type UploadTextTrack = AutogeneratedTextTrack | CustomTextTrack
export interface UploadConfig
extends Pick<
MuxInputConfig,
'encoding_tier' | 'max_resolution_tier' | 'mp4_support' | 'normalize_audio'
> {
text_tracks: UploadTextTrack[]
signed_policy: boolean
public_policy: boolean
}
/**
* Data sent to Mux to create a new asset.
* @docs {@link https://docs.mux.com/api-reference#video/operation/create-direct-upload}
*/
export interface MuxNewAssetSettings
extends Pick<
MuxInputConfig,
'encoding_tier' | 'max_resolution_tier' | 'mp4_support' | 'normalize_audio'
> {
/** An array of objects that each describe an input file to be used to create the asset.*/
input?: {
/** The URL of the file that Mux should download and use. */
url?: string
/** Generate subtitle tracks using automatic speech recognition with this configuration. This may only be provided for the first input object (the main input file). */
generated_subtitles?: {
/** A name for this subtitle track. */
name: string
/** Arbitrary metadata set for the subtitle track. Max 255 characters. */
passthrough?: string
/** The language to generate subtitles in. */
language_code: SupportedMuxLanguage
}[]
/** The time offset in seconds from the beginning of the video indicating the clip's starting marker. */
start_time?: number
/** The time offset in seconds from the beginning of the video indicating the clip's ending marker. */
end_time?: number
/** This parameter is required for text type tracks. */
type: 'video' | 'audio' | 'text'
/** Type of text track. This parameter only supports subtitles value. */
text_type?: 'subtitles'
/** The language code value must be a valid BCP 47 specification compliant value. */
language_code?: string
/** The name of the track containing a human-readable description. This value must be unique within each group of text or audio track types. */
name?: string
/** Indicates the track provides Subtitles for the Deaf or Hard-of-hearing (SDH). */
closed_captions?: boolean
/// @TODO Huhh?>?? Below
/** This optional parameter should be used tracks with type of text and text_type set to subtitles. */
passthrough?: string
}[]
/** An array of playback policy names that you want applied to this asset and available through playback_ids. */
playback_policy: ('public' | 'signed' | 'drm')[]
/** Arbitrary user-supplied metadata that will be included in the asset details and related webhooks. */
passthrough?: string
}
export interface Secrets {
token: string | null
secretKey: string | null
enableSignedUrls: boolean
signingKeyId: string | null
signingKeyPrivate: string | null
}
// This narrowed type indicates that there may be assets that are signed, and we have the secrets to access them
// enabledSignedUrls might be false but that's only relevant for future uploads and their playback policy
export interface SignableSecrets extends Omit<Secrets, 'signingKeyId' | 'signingKeyPrivate'> {
signingKeyId: string
signingKeyPrivate: string
}
export type MuxImageOrigin = `https://image.mux.com`
export type MuxThumbnailUrl = `${MuxImageOrigin}/${string}/thumbnail.png?${string}`
export type MuxAnimatedThumbnailUrl = `${MuxImageOrigin}/${string}/animated.gif?${string}`
export type MuxStoryboardUrl = `${MuxImageOrigin}/${string}/storyboard.vtt?${string}`
export type MuxVideoOrigin = `https://stream.mux.com`
export type MuxVideoUrl = `${MuxVideoOrigin}/${string}.m3u8?${string}`
export type MuxApiUrl = MuxThumbnailUrl | MuxAnimatedThumbnailUrl | MuxStoryboardUrl | MuxVideoUrl
// 'preserve' by default
// @url: https://docs.mux.com/guides/video/get-images-from-a-video#thumbnail-query-string-parameters
export type FitMode = 'preserve' | 'crop' | 'smartcrop' | 'pad'
export interface ThumbnailOptions {
fit_mode?: FitMode
height?: number
time?: number
width?: number
}
export interface AnimatedThumbnailOptions {
// Starting time code for the animation, if no end is set it'll have a 5s duration
// The start and end timecodes uses `asset.thumbTime` to create an iOS `Live Photo` effect by showing you the 5 secodnds before, and after, the thumb time`
start?: number
// End code, can't be longer than 10s after the start code
end?: number
// Max 640px, 320px by default
width?: number
// Preserves aspect ratio, like width, you can't set both the height and width, max 640
height?: number
// The fps is 15 by default, but can go up to 30
fps?: number
}
export interface AssetThumbnailOptions {
asset: Pick<VideoAssetDocument, 'playbackId' | 'data' | 'thumbTime' | 'filename' | 'assetId'>
}
export type PlaybackPolicy = 'signed' | 'public'
export interface MuxErrors {
type: string
messages: string[]
}
export interface MuxPlaybackId {
id: string
policy: PlaybackPolicy
}
export interface MuxVideoTrack {
type: 'video'
id: string
max_width: number
max_height: number
// if the fps can't be reliably determined, this will be -1
max_frame_rate: -1 | number
// top-level duration is always set, while track level duration is not
duration?: number
}
export interface MuxAudioTrack {
type: 'audio'
id: string
duration?: number
max_channels: number
max_channel_layout: 'stereo' | string
}
export interface MuxTextTrack {
type: 'text'
id: string
text_type?: 'subtitles'
// https://docs.mux.com/api-reference/video#operation/list-assets:~:text=text%20type%20tracks.-,tracks%5B%5D.,text_source,-string
text_source?: 'uploaded' | 'embedded' | 'generated_live' | 'generated_live_final'
// BCP 47 language code
language_code?: 'en' | 'en-US' | string
// The name of the track containing a human-readable description. The hls manifest will associate a subtitle text track with this value
name?: 'English' | string
closed_captions?: boolean
// Max 255 characters
passthrough?: string
status: 'preparing' | 'ready' | 'errored'
}
export type MuxTrack = MuxVideoTrack | MuxAudioTrack
// Typings lifted from https://docs.mux.com/api-reference/video#tag/assets
export interface MuxAsset {
id: string
/** In seconds (instead of JS's default milliseconds) */
created_at: string
status: 'preparing' | 'ready' | 'errored'
duration: number
max_stored_resolution: 'Audio only' | 'SD' | 'HD' | 'FHD' | 'UHD'
// if the fps can't be reliably determined, this will be -1
max_stored_frame_rate: -1 | number
// The aspect ratio of the asset in the form of width:height, for example 16:9
aspect_ratio: `${number}:${number}`
playback_ids: MuxPlaybackId[]
tracks: MuxTrack[]
errors?: MuxErrors
upload_id: string
is_live?: boolean
// We use passthrough to set the mux.videoAsset._id of the asset that originally uploaded the video
passthrough: string
live_stream_id?: string
master?: {
status: 'ready' | 'preparing' | 'errored'
// Temporary URL to master MP4, expires after 24 hours
url: string
}
master_access: 'temporary' | 'none'
mp4_support: 'standard' | 'none'
// Asset Identifier of the video used as the source for creating the clip.
source_asset_id?: string
// Normalize the audio track loudness level. This parameter is only applicable to on-demand (not live) assets., default false
normalize_audio?: boolean
// The object does not exist if no static renditions have been requested
static_renditions?: {
status: 'ready' | 'preparing' | 'disabled' | 'errored'
files: {
name: 'low.mp4' | 'medium.mp4' | 'high.mp4' | 'audio.m4a'
ext: 'mp4' | 'm4a'
height: number
width: number
bitrate: number
filesize: number
}[]
}
recording_times?: {
started_at: string
duration: number
type: 'content' | 'slate'
}[]
// https://docs.mux.com/guides/video/minimize-processing-time
non_standard_input_reasons?: {
video_codec?: string
audio_codec?: string
video_gop_size?: 'high'
video_frame_rate?: string
video_resolution?: string
video_bitrate?: 'high'
pixel_aspect_ratio?: string
video_edit_list?: 'non-standard'
audio_edit_list?: 'non-standard'
unexpected_media_file_parameters?: 'non-standard'
test?: boolean
}
meta?: {
title?: string
}
}
export interface VideoAssetDocument {
_id: string
_type: 'mux.videoAsset'
_createdAt: string
_updatedAt?: string
status?: string
assetId?: string
playbackId?: string
filename?: string
thumbTime?: number
// Docs for what goes in `data` https://docs.mux.com/api-reference/video#tag/assets
data?: PartialDeep<MuxAsset>
}
export type Reference = {_type: 'reference'; _ref: string}
// @TODO add Reference, and ReferenceSchemaType in the generic
export type MuxInputProps = ObjectInputProps<{
asset?: Reference
}>
export interface MuxInputPreviewProps extends Omit<PreviewProps<PreviewLayoutKey>, 'value'> {
schemaType: SchemaType
value?: {
asset?: Reference
} | null
}
/** Whether the VideosBrowser was opened from a field in a document, or from the standalone studio tool */
export type PluginPlacement = 'input' | 'tool'