about summary refs log tree commit diff
path: root/src/view/com/composer/videos/pickVideo.web.ts
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2024-11-22 17:58:29 +0000
committerGitHub <noreply@github.com>2024-11-22 17:58:29 +0000
commit378107492194a5f408747790015c4ca1d624302b (patch)
treee12e121265d081b49c50d7cd0cc5f71c5f477bf7 /src/view/com/composer/videos/pickVideo.web.ts
parent76ca72cf727e926101ec60eb232f0797e6584b49 (diff)
downloadvoidsky-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.ts94
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)
+  })
+}