mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-10 21:50:14 -07:00
HTML5Video ops: use async/await
This commit is contained in:
@@ -7,7 +7,7 @@ import InfoIcon from '@mui/icons-material/Info';
|
||||
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
||||
|
||||
import { InlineError } from '~/common/components/InlineError';
|
||||
import { downloadVideoFrameAsPNG, renderVideoFrameToFile } from '~/common/util/videoUtils';
|
||||
import { downloadVideoFrameAsPNG, renderVideoFrameAsPNGFile } from '~/common/util/videoUtils';
|
||||
import { useCameraCapture } from '~/common/components/useCameraCapture';
|
||||
|
||||
|
||||
@@ -16,11 +16,12 @@ export function CameraCaptureModal(props: {
|
||||
onAttachImage: (file: File) => void
|
||||
// onOCR: (ocrText: string) => void }
|
||||
}) {
|
||||
// state
|
||||
// const [ocrProgress/*, setOCRProgress*/] = React.useState<number | null>(null);
|
||||
const [showInfo, setShowInfo] = React.useState(false);
|
||||
|
||||
// camera operations
|
||||
// state
|
||||
const [showInfo, setShowInfo] = React.useState(false);
|
||||
// const [ocrProgress/*, setOCRProgress*/] = React.useState<number | null>(null);
|
||||
|
||||
// external state
|
||||
const {
|
||||
videoRef,
|
||||
cameras, cameraIdx, setCameraIdx,
|
||||
@@ -29,10 +30,14 @@ export function CameraCaptureModal(props: {
|
||||
} = useCameraCapture();
|
||||
|
||||
|
||||
const stopAndClose = () => {
|
||||
// derived state
|
||||
const { onCloseModal, onAttachImage } = props;
|
||||
|
||||
|
||||
const stopAndClose = React.useCallback(() => {
|
||||
resetVideo();
|
||||
props.onCloseModal();
|
||||
};
|
||||
onCloseModal();
|
||||
}, [onCloseModal, resetVideo]);
|
||||
|
||||
/*const handleVideoOCRClicked = async () => {
|
||||
if (!videoRef.current) return;
|
||||
@@ -53,18 +58,21 @@ export function CameraCaptureModal(props: {
|
||||
props.onOCR(result.data.text);
|
||||
};*/
|
||||
|
||||
const handleVideoSnapClicked = () => {
|
||||
const handleVideoSnapClicked = React.useCallback(async () => {
|
||||
if (!videoRef.current) return;
|
||||
renderVideoFrameToFile(videoRef.current, 'camera', (file) => {
|
||||
props.onAttachImage(file);
|
||||
try {
|
||||
const file = await renderVideoFrameAsPNGFile(videoRef.current, 'camera');
|
||||
onAttachImage(file);
|
||||
stopAndClose();
|
||||
});
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error capturing video frame:', error);
|
||||
}
|
||||
}, [onAttachImage, stopAndClose, videoRef]);
|
||||
|
||||
const handleVideoDownloadClicked = () => {
|
||||
const handleVideoDownloadClicked = React.useCallback(() => {
|
||||
if (!videoRef.current) return;
|
||||
downloadVideoFrameAsPNG(videoRef.current, 'camera');
|
||||
};
|
||||
}, [videoRef]);
|
||||
|
||||
|
||||
return (
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
export function downloadVideoFrameAsPNG(videoElement: HTMLVideoElement, prefixName: string) {
|
||||
// video to canvas to png
|
||||
// current video frame -> canvas -> dataURL PNG
|
||||
const renderedFrame = _renderVideoFrameToCanvas(videoElement);
|
||||
const imageDataURL = renderedFrame.toDataURL('image/png');
|
||||
|
||||
@@ -13,20 +13,20 @@ export function downloadVideoFrameAsPNG(videoElement: HTMLVideoElement, prefixNa
|
||||
const link = document.createElement('a');
|
||||
link.download = _prettyFileName(prefixName, renderedFrame);
|
||||
link.href = imageDataURL;
|
||||
document.body.appendChild(link); // Ensure visibility in the DOM for Firefox
|
||||
link.click();
|
||||
document.body.removeChild(link); // Clean up
|
||||
}
|
||||
|
||||
export function renderVideoFrameToFile(videoElement: HTMLVideoElement, prefixName: string, callback: (file: File) => void) {
|
||||
// video to canvas
|
||||
export async function renderVideoFrameAsPNGFile(videoElement: HTMLVideoElement, prefixName: string): Promise<File> {
|
||||
// current video frame -> canvas -> Blob PNG
|
||||
const renderedFrame = _renderVideoFrameToCanvas(videoElement);
|
||||
const blob = await _canvasToBlob(renderedFrame, 'image/png');
|
||||
|
||||
// canvas to blob to file to callback
|
||||
renderedFrame.toBlob((blob) => {
|
||||
if (blob) {
|
||||
const file = new File([blob], _prettyFileName(prefixName, renderedFrame), { type: blob.type });
|
||||
callback(file);
|
||||
}
|
||||
}, 'image/png');
|
||||
// to File
|
||||
if (!blob)
|
||||
throw new Error('Failed to convert canvas to Blob');
|
||||
return new File([blob], _prettyFileName(prefixName, renderedFrame), { type: blob.type });
|
||||
}
|
||||
|
||||
function _prettyFileName(prefixName: string, renderedFrame: HTMLCanvasElement) {
|
||||
@@ -45,3 +45,11 @@ function _renderVideoFrameToCanvas(videoElement: HTMLVideoElement): HTMLCanvasEl
|
||||
return canvas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Blob object representing the image contained in the canvas
|
||||
* @param canvas The canvas element to convert to a Blob.
|
||||
* @param imageFormat Browsers are required to support image/png; many will support additional formats including image/jpeg and image/webp.
|
||||
*/
|
||||
async function _canvasToBlob(canvas: HTMLCanvasElement, imageFormat: string = 'image/png'): Promise<Blob | null> {
|
||||
return new Promise((resolve) => canvas.toBlob(resolve, imageFormat));
|
||||
}
|
||||
Reference in New Issue
Block a user