@tldraw/tlschema
Version:
tldraw infinite canvas SDK (schema).
176 lines (169 loc) • 4.71 kB
text/typescript
import { createMigrationIds, createRecordMigrationSequence } from '@tldraw/store'
import { T } from '@tldraw/validate'
import { RecordProps } from '../recordsWithProps'
import { TLBaseAsset, createAssetValidator } from './TLBaseAsset'
/**
* An asset record representing video files that can be displayed in video shapes.
* Video assets store metadata about video files including dimensions, MIME type,
* animation status, and file source information. They are referenced by TLVideoShape
* instances to display video content on the canvas.
*
* @example
* ```ts
* import { TLVideoAsset } from '@tldraw/tlschema'
*
* const videoAsset: TLVideoAsset = {
* id: 'asset:video123',
* typeName: 'asset',
* type: 'video',
* props: {
* w: 1920,
* h: 1080,
* name: 'my-video.mp4',
* isAnimated: true,
* mimeType: 'video/mp4',
* src: 'https://example.com/video.mp4',
* fileSize: 5242880
* },
* meta: {}
* }
* ```
*
* @public
*/
export type TLVideoAsset = TLBaseAsset<
'video',
{
/** The width of the video in pixels */
w: number
/** The height of the video in pixels */
h: number
/** The original filename or display name of the video */
name: string
/** Whether the video contains animation/motion (true for most videos) */
isAnimated: boolean
/** The MIME type of the video file (e.g., 'video/mp4', 'video/webm'), null if unknown */
mimeType: string | null
/** The source URL or data URI for the video file, null if not yet available */
src: string | null
/** The file size in bytes, optional for backward compatibility */
fileSize?: number
}
>
/** @public */
export const videoAssetProps = {
w: T.number,
h: T.number,
name: T.string,
isAnimated: T.boolean,
mimeType: T.string.nullable(),
src: T.srcUrl.nullable(),
fileSize: T.number.optional(),
} satisfies RecordProps<TLVideoAsset>
/** Validator for video assets. @public */
export const videoAssetValidator: T.Validator<TLVideoAsset> = createAssetValidator(
'video',
T.object(videoAssetProps)
)
const Versions = createMigrationIds('com.tldraw.asset.video', {
AddIsAnimated: 1,
RenameWidthHeight: 2,
MakeUrlsValid: 3,
AddFileSize: 4,
MakeFileSizeOptional: 5,
} as const)
/**
* Version identifiers for video asset migration sequences. These versions track
* the evolution of the video asset schema over time, enabling proper data migration
* when the asset structure changes.
*
* @example
* ```ts
* import { videoAssetVersions } from '@tldraw/tlschema'
*
* // Check the current version of a specific migration
* console.log(videoAssetVersions.AddFileSize) // 4
* ```
*
* @public
*/
export { Versions as videoAssetVersions }
/**
* Migration sequence for video assets that handles schema evolution over time.
* This sequence defines how video asset data should be transformed when upgrading
* or downgrading between different schema versions. Each migration step handles
* specific changes like adding properties, renaming fields, or changing data formats.
*
* The migrations handle:
* - Adding animation detection (isAnimated property)
* - Renaming width/height properties to w/h for consistency
* - Ensuring URL validity for src properties
* - Adding file size tracking
* - Making file size optional for backward compatibility
*
* @public
*/
export const videoAssetMigrations = createRecordMigrationSequence({
sequenceId: 'com.tldraw.asset.video',
recordType: 'asset',
filter: (asset) => (asset as TLVideoAsset).type === 'video',
sequence: [
{
id: Versions.AddIsAnimated,
up: (asset: any) => {
asset.props.isAnimated = false
},
down: (asset: any) => {
delete asset.props.isAnimated
},
},
{
id: Versions.RenameWidthHeight,
up: (asset: any) => {
asset.props.w = asset.props.width
asset.props.h = asset.props.height
delete asset.props.width
delete asset.props.height
},
down: (asset: any) => {
asset.props.width = asset.props.w
asset.props.height = asset.props.h
delete asset.props.w
delete asset.props.h
},
},
{
id: Versions.MakeUrlsValid,
up: (asset: any) => {
if (!T.srcUrl.isValid(asset.props.src)) {
asset.props.src = ''
}
},
down: (_asset) => {
// noop
},
},
{
id: Versions.AddFileSize,
up: (asset: any) => {
asset.props.fileSize = -1
},
down: (asset: any) => {
delete asset.props.fileSize
},
},
{
id: Versions.MakeFileSizeOptional,
up: (asset: any) => {
if (asset.props.fileSize === -1) {
asset.props.fileSize = undefined
}
},
down: (asset: any) => {
if (asset.props.fileSize === undefined) {
asset.props.fileSize = -1
}
},
},
],
})