Skip to content

Commit

Permalink
Merge pull request #800 from interval/display-video
Browse files Browse the repository at this point in the history
🎬 Add basic video component
  • Loading branch information
rcoppolo authored Sep 13, 2022
2 parents d62a6cb + c0771ef commit d3f5007
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 5 deletions.
4 changes: 4 additions & 0 deletions src/classes/IOClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import search from '../components/search'
import selectMultiple from '../components/selectMultiple'
import displayLink from '../components/displayLink'
import displayImage from '../components/displayImage'
import displayVideo from '../components/displayVideo'
import urlInput from '../components/url'
import { date, datetime } from '../components/inputDate'
import { file } from '../components/upload'
Expand Down Expand Up @@ -480,6 +481,9 @@ export class IOClient {
propsRequired: true,
componentDef: displayTable(this.logger),
}),
video: this.createIOMethod('DISPLAY_VIDEO', {
componentDef: displayVideo,
}),
},
experimental: {
spreadsheet: this.createIOMethod('INPUT_SPREADSHEET', {
Expand Down
71 changes: 71 additions & 0 deletions src/components/displayVideo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { T_IO_PROPS, ImageSize } from '../ioSchema'
import { IntervalError } from '..'

const MAX_BUFFER_SIZE_MB = 50

export default function displayVideo(
props: {
width?: T_IO_PROPS<'DISPLAY_VIDEO'>['width']
height?: T_IO_PROPS<'DISPLAY_VIDEO'>['height']
size?: ImageSize
muted?: T_IO_PROPS<'DISPLAY_VIDEO'>['muted']
loop?: T_IO_PROPS<'DISPLAY_VIDEO'>['loop']
} & (
| {
url: string
}
| {
buffer: Buffer
}
)
) {
const size = props.size
delete props.size
props.width = size ? size : props.width
props.height = size ? size : props.height

if ('buffer' in props) {
if (Buffer.byteLength(props.buffer) > MAX_BUFFER_SIZE_MB * 1000 * 1000) {
throw new IntervalError(
`Buffer for io.display.video is too large, must be under ${MAX_BUFFER_SIZE_MB} MB`
)
}

const data = props.buffer.toString('base64')

// using first character as a simple check for common video formats:
let mime
switch (data[0]) {
case 'A':
mime = 'video/mp4'
break
case 'G':
mime = 'video/webm'
break
case 'T':
mime = 'video/ogg'
break
case 'U':
mime = 'video/avi'
break
default:
// A fallback of `video/unknown` doesn't work like it does for images.
// Various formats seem to play fine in chrome with mp4.
// Still relying on the switch ^^ for correctness though.
mime = 'video/mp4'
break
}

return {
props: {
...props,
url: `data:${mime};base64,${data}`,
},
async prepareProps(props: T_IO_PROPS<'DISPLAY_VIDEO'>) {
return props
},
}
} else {
return { props }
}
}
14 changes: 14 additions & 0 deletions src/examples/basic/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,20 @@ const interval = new Interval({
}),
])
},
videos: async () => {
await io.group([
io.display.video('Video via url', {
url: 'https://upload.wikimedia.org/wikipedia/commons/a/ad/The_Kid_scenes.ogv',
size: 'large',
muted: true,
}),
io.display.video('Video via buffer', {
loop: true,
buffer: fs.readFileSync('./src/examples/static/canyon.mp4'),
size: 'large',
}),
])
},
enter_a_number: async io => {
const num = await io.input.number('Enter a number')

Expand Down
Binary file added src/examples/static/canyon.mp4
Binary file not shown.
19 changes: 14 additions & 5 deletions src/ioSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,9 +259,7 @@ export const CURRENCIES = [
export const currencyCode = z.enum(CURRENCIES)
export type CurrencyCode = z.infer<typeof currencyCode>

export const imageSize = z
.enum(['thumbnail', 'small', 'medium', 'large'])
.optional()
export const imageSize = z.enum(['thumbnail', 'small', 'medium', 'large'])
export type ImageSize = z.infer<typeof imageSize>

export const dateObject = z.object({
Expand Down Expand Up @@ -494,8 +492,8 @@ export const ioSchema = {
DISPLAY_IMAGE: {
props: z.object({
alt: z.string().optional(),
width: imageSize,
height: imageSize,
width: imageSize.optional(),
height: imageSize.optional(),
url: z.string(),
}),
state: z.null(),
Expand Down Expand Up @@ -566,6 +564,17 @@ export const ioSchema = {
state: z.null(),
returns: z.null(),
},
DISPLAY_VIDEO: {
props: z.object({
width: imageSize.optional(),
height: imageSize.optional(),
url: z.string(),
loop: z.boolean().optional(),
muted: z.boolean().optional(),
}),
state: z.null(),
returns: z.null(),
},
}

export type IoMethod = {
Expand Down

0 comments on commit d3f5007

Please sign in to comment.