Files
mediabunny/examples/crop/crop.ts
T

140 lines
3.5 KiB
TypeScript

import {
Input,
Output,
WebMOutputFormat,
BufferTarget,
Conversion,
BlobSource,
ALL_FORMATS,
} from 'mediabunny';
const selectMediaButton = document.querySelector(
'#select-file',
) as HTMLButtonElement;
const cropButton = document.querySelector('#crop-button') as HTMLButtonElement;
const fileNameElement = document.querySelector(
'#file-name',
) as HTMLParagraphElement;
let selectedFile: File | null = null;
const horizontalRule = document.querySelector('hr') as HTMLHRElement;
const outputContainer = document.querySelector(
'#output-container',
) as HTMLDivElement;
const errorElement = document.querySelector(
'#error-element',
) as HTMLParagraphElement;
const cropTopInput = document.querySelector('#crop-top') as HTMLInputElement;
const cropLeftInput = document.querySelector('#crop-left') as HTMLInputElement;
const cropWidthInput = document.querySelector(
'#crop-width',
) as HTMLInputElement;
const cropHeightInput = document.querySelector(
'#crop-height',
) as HTMLInputElement;
const cropVideo = async (file: File) => {
fileNameElement.textContent = file.name;
horizontalRule.style.display = '';
errorElement.textContent = '';
outputContainer.innerHTML = '';
try {
const input = new Input({
source: new BlobSource(file),
formats: ALL_FORMATS,
});
const videoTrack = await input.getPrimaryVideoTrack();
if (!videoTrack) {
throw new Error('File has no video track.');
}
if (videoTrack.codec === null) {
throw new Error('Unsupported video codec.');
}
if (!(await videoTrack.canDecode())) {
throw new Error('Unable to decode the video track.');
}
const output = new Output({
format: new WebMOutputFormat(),
target: new BufferTarget(),
});
const conversion = await Conversion.init({
input,
output,
video: {
crop: {
top: parseInt(cropTopInput.value) || 0,
left: parseInt(cropLeftInput.value) || 0,
width: parseInt(cropWidthInput.value) || 300,
height: parseInt(cropHeightInput.value) || 300,
},
},
});
await conversion.execute();
const buffer = output.target.buffer;
if (!buffer) {
throw new Error('Failed to generate output buffer');
}
const blob = new Blob([buffer], { type: 'video/webm' });
const url = URL.createObjectURL(blob);
const video = document.createElement('video');
video.src = url;
video.controls = true;
video.className = 'rounded-lg overflow-hidden bg-zinc-100 dark:bg-zinc-800';
outputContainer.appendChild(video);
} catch (error) {
console.error(error);
errorElement.textContent = String(error);
outputContainer.innerHTML = '';
}
};
const updateSelectedFile = (file: File | null) => {
selectedFile = file;
fileNameElement.textContent = file ? file.name : '';
cropButton.disabled = !file;
errorElement.textContent = '';
outputContainer.innerHTML = '';
};
selectMediaButton.addEventListener('click', () => {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'video/*,video/x-matroska';
fileInput.addEventListener('change', () => {
const file = fileInput.files?.[0];
updateSelectedFile(file || null);
});
fileInput.click();
});
document.addEventListener('dragover', (event) => {
event.preventDefault();
event.dataTransfer!.dropEffect = 'copy';
});
document.addEventListener('drop', (event) => {
event.preventDefault();
if (!event.dataTransfer?.files) {
updateSelectedFile(null);
return;
}
const file = event.dataTransfer.files[0] ?? null;
updateSelectedFile(file);
});
cropButton.addEventListener('click', () => {
if (selectedFile) {
void cropVideo(selectedFile);
}
});