Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix thumbnail generation method on video recording #914

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
101 changes: 48 additions & 53 deletions src/stores/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,54 +144,47 @@ export const useVideoStore = defineStore('video', () => {

/**
* Extracts a thumbnail from the first frame of a video.
* @param {string} videoUrl - The URL of the video from which to extract the thumbnail.
* @param {Blob} firstChunkBlob
* @returns {Promise<string>} A promise that resolves with the base64-encoded image data of the thumbnail.
*/
const extractThumbnailFromVideo = async (videoUrl: string): Promise<string> => {
const extractThumbnailFromVideo = async (firstChunkBlob: Blob): Promise<string> => {
return new Promise<string>((resolve, reject) => {
fetch(videoUrl)
.then((response) => response.blob())
.then((videoBlob) => {
const videoObjectUrl = URL.createObjectURL(videoBlob)
const video = document.createElement('video')

let seekResolve: (() => void) | null = null
video.addEventListener('seeked', function () {
if (seekResolve) seekResolve()
})

video.addEventListener('error', () => {
URL.revokeObjectURL(videoObjectUrl)
reject('Error loading video')
})

video.src = videoObjectUrl

video.addEventListener('loadedmetadata', () => {
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
if (!context) {
URL.revokeObjectURL(videoObjectUrl)
reject('2D context not available.')
return
}

const [width, height] = [660, 370]
canvas.width = width
canvas.height = height

video.currentTime = 0
seekResolve = () => {
context.drawImage(video, 0, 0, width, height)
const base64ImageData = canvas.toDataURL('image/jpeg', 0.6)
resolve(base64ImageData)
URL.revokeObjectURL(videoObjectUrl)
}
})
})
.catch((error) => {
reject('Failed to fetch video: ' + error)
})
const videoObjectUrl = URL.createObjectURL(firstChunkBlob)
const video = document.createElement('video')

let seekResolve: (() => void) | null = null
video.addEventListener('seeked', function () {
if (seekResolve) seekResolve()
})

video.addEventListener('error', () => {
URL.revokeObjectURL(videoObjectUrl)
reject('Error loading video')
})

video.src = videoObjectUrl

video.addEventListener('loadedmetadata', () => {
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
if (!context) {
URL.revokeObjectURL(videoObjectUrl)
reject('2D context not available.')
return
}

const [width, height] = [660, 370]
canvas.width = width
canvas.height = height

video.currentTime = 0
seekResolve = () => {
context.drawImage(video, 0, 0, width, height)
const base64ImageData = canvas.toDataURL('image/jpeg', 0.6)
resolve(base64ImageData)
URL.revokeObjectURL(videoObjectUrl)
}
})
})
}

Expand Down Expand Up @@ -267,14 +260,16 @@ export const useVideoStore = defineStore('video', () => {

// Gets the thumbnail from the first chunk
if (chunksCount === 0) {
const videoChunk = await tempVideoChunksDB.getItem(chunkName)
if (videoChunk) {
const blob = videoChunk as Blob
const thumbnailBlob = new Blob([blob], { type: 'video/webm;codecs=vp9' })
const thumbnailUrl = URL.createObjectURL(thumbnailBlob)
const thumbnail = await extractThumbnailFromVideo(thumbnailUrl)
updatedInfo.thumbnail = thumbnail
unprocessedVideos.value = { ...unprocessedVideos.value, ...{ [recordingHash]: updatedInfo } }
try {
const videoChunk = await tempVideoChunksDB.getItem(chunkName)
if (videoChunk) {
const firstChunkBlob = new Blob([videoChunk as Blob], { type: 'video/webm;codecs=vp9' })
const thumbnail = await extractThumbnailFromVideo(firstChunkBlob)
updatedInfo.thumbnail = thumbnail
unprocessedVideos.value = { ...unprocessedVideos.value, ...{ [recordingHash]: updatedInfo } }
}
} catch (error) {
console.error('Failed to extract thumbnail:', error)
}
}
}
Expand Down