diff options
author | Samuel Newman <mozzius@protonmail.com> | 2024-11-22 17:58:29 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-22 17:58:29 +0000 |
commit | 378107492194a5f408747790015c4ca1d624302b (patch) | |
tree | e12e121265d081b49c50d7cd0cc5f71c5f477bf7 /src/view/com/composer/videos/pickVideo.web.ts | |
parent | 76ca72cf727e926101ec60eb232f0797e6584b49 (diff) | |
download | voidsky-378107492194a5f408747790015c4ca1d624302b.tar.zst |
Add gif support to web (#6433)
* add gif support to web * rm set dimensions * rm effect from preview * rm log * rm use of {cause: error} * fix lint
Diffstat (limited to 'src/view/com/composer/videos/pickVideo.web.ts')
-rw-r--r-- | src/view/com/composer/videos/pickVideo.web.ts | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/src/view/com/composer/videos/pickVideo.web.ts b/src/view/com/composer/videos/pickVideo.web.ts new file mode 100644 index 000000000..56a38fa56 --- /dev/null +++ b/src/view/com/composer/videos/pickVideo.web.ts @@ -0,0 +1,94 @@ +import {ImagePickerAsset, ImagePickerResult} from 'expo-image-picker' + +import {SUPPORTED_MIME_TYPES} from '#/lib/constants' + +// mostly copied from expo-image-picker and adapted to support gifs +// also adds support for reading video metadata + +export async function pickVideo(): Promise<ImagePickerResult> { + const input = document.createElement('input') + input.style.display = 'none' + input.setAttribute('type', 'file') + // TODO: do we need video/* here? -sfn + input.setAttribute('accept', SUPPORTED_MIME_TYPES.join(',')) + input.setAttribute('id', String(Math.random())) + + document.body.appendChild(input) + + return new Promise(resolve => { + input.addEventListener('change', async () => { + if (input.files) { + const file = input.files[0] + resolve({ + canceled: false, + assets: [await getVideoMetadata(file)], + }) + } else { + resolve({canceled: true, assets: null}) + } + document.body.removeChild(input) + }) + + const event = new MouseEvent('click') + input.dispatchEvent(event) + }) +} + +// TODO: we're converting to a dataUrl here, and then converting back to an +// ArrayBuffer in the compressVideo function. This is a bit wasteful, but it +// lets us use the ImagePickerAsset type, which the rest of the code expects. +// We should unwind this and just pass the ArrayBuffer/objectUrl through the system +// instead of a string -sfn +export const getVideoMetadata = (file: File): Promise<ImagePickerAsset> => { + return new Promise((resolve, reject) => { + const reader = new FileReader() + reader.onload = () => { + const uri = reader.result as string + + if (file.type === 'image/gif') { + const img = new Image() + img.onload = () => { + resolve({ + uri, + mimeType: 'image/gif', + width: img.width, + height: img.height, + // todo: calculate gif duration. seems possible if you read the bytes + // https://codepen.io/Ryman/pen/nZpYwY + // for now let's just let the server reject it, since that seems uncommon -sfn + duration: null, + }) + } + img.onerror = (_ev, _source, _lineno, _colno, error) => { + console.log('Failed to grab GIF metadata', error) + reject(new Error('Failed to grab GIF metadata')) + } + img.src = uri + } else { + const video = document.createElement('video') + const blobUrl = URL.createObjectURL(file) + + video.preload = 'metadata' + video.src = blobUrl + + video.onloadedmetadata = () => { + URL.revokeObjectURL(blobUrl) + resolve({ + uri, + mimeType: file.type, + width: video.videoWidth, + height: video.videoHeight, + // convert seconds to ms + duration: video.duration * 1000, + }) + } + video.onerror = (_ev, _source, _lineno, _colno, error) => { + URL.revokeObjectURL(blobUrl) + console.log('Failed to grab video metadata', error) + reject(new Error('Failed to grab video metadata')) + } + } + } + reader.readAsDataURL(file) + }) +} |