102 lines
2.3 KiB
TypeScript
102 lines
2.3 KiB
TypeScript
import { useState, useEffect, useRef } from 'react';
|
|
import { CanvasLogicalWidth, MediaSet } from './Waveform';
|
|
|
|
interface Props {
|
|
mediaSet: MediaSet;
|
|
style: React.CSSProperties;
|
|
}
|
|
|
|
enum State {
|
|
Loading,
|
|
Ready,
|
|
Error,
|
|
}
|
|
|
|
export const Thumbnails: React.FC<Props> = ({ mediaSet, style }: Props) => {
|
|
const [image, _setImage] = useState(new Image());
|
|
const [state, setState] = useState(State.Loading);
|
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
|
|
// load thumbnail image when available:
|
|
useEffect(() => {
|
|
if (mediaSet == null) return;
|
|
|
|
image.src = `http://localhost:8888/api/media_sets/${mediaSet.id}/thumbnails`;
|
|
image.onload = () => {
|
|
setState(State.Ready);
|
|
};
|
|
}, []);
|
|
|
|
// render canvas if image has been loaded successfully:
|
|
useEffect(() => {
|
|
if (state != State.Ready) return;
|
|
if (mediaSet == null) return;
|
|
|
|
const canvas = canvasRef.current;
|
|
if (canvas == null) {
|
|
console.error('no canvas available');
|
|
return;
|
|
}
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
if (ctx == null) {
|
|
console.error('no thumbnail 2d context available');
|
|
return;
|
|
}
|
|
|
|
const tw = mediaSet.video.thumbnailWidth;
|
|
const th = mediaSet.video.thumbnailHeight;
|
|
const iw = image.width;
|
|
const { width: pw, height: ph } = canvas.getBoundingClientRect();
|
|
|
|
// set canvas logical width to suit the aspect ratio:
|
|
// TODO: confirm this is needed.
|
|
const ar = tw / th;
|
|
const par = pw / ph;
|
|
canvas.width = tw * (par / ar);
|
|
|
|
const durationSecs = mediaSet.video.durationMillis / 1000;
|
|
|
|
for (let dx = 0; dx < canvas.width; dx += tw) {
|
|
const secs = Math.floor((dx / canvas.width) * durationSecs);
|
|
const sx = (secs * tw) % iw;
|
|
const sy = Math.floor(secs / (iw / tw)) * th;
|
|
ctx.drawImage(image, sx, sy, tw, th, dx, 0, tw, th);
|
|
}
|
|
}, [state]);
|
|
|
|
// rendering
|
|
|
|
if (mediaSet == null || mediaSet.video == null) {
|
|
console.error('unexpected null video');
|
|
return null;
|
|
}
|
|
|
|
if (state == State.Loading) {
|
|
return (
|
|
<>
|
|
<div>Loading...</div>
|
|
</>
|
|
);
|
|
}
|
|
|
|
if (state == State.Error) {
|
|
return (
|
|
<>
|
|
<span>Something went wrong</span>
|
|
</>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<canvas
|
|
ref={canvasRef}
|
|
style={style}
|
|
width={CanvasLogicalWidth}
|
|
height={100}
|
|
></canvas>
|
|
</>
|
|
);
|
|
};
|