Merge branch 'main' into v2

This commit is contained in:
Vanilagy
2026-07-07 21:00:13 +02:00
317 changed files with 55314 additions and 5009 deletions
+19 -4
View File
@@ -1,14 +1,29 @@
<!DOCTYPE html>
<html lang="en" translate="no">
<html lang="en-US" translate="no">
<head>
<meta charset="UTF-8">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Thumbnail generation example | Mediabunny</title>
<meta name="description" content="Select or drop a media file, and Mediabunny will extract video thumbnails for it.">
<script type="module" src="./../base.ts"></script>
<script type="module" src="./thumbnail-generation.ts"></script>
<link rel="stylesheet" href="../base.css">
<link rel="icon" href="../../docs/public/mediabunny-logo.svg">
<link rel="canonical" href="https://mediabunny.dev/examples/thumbnail-generation/">
<meta property="og:site_name" content="Mediabunny">
<meta property="og:type" content="website">
<meta property="og:title" content="Thumbnail generation example | Mediabunny">
<meta property="og:description" content="Select or drop a media file, and Mediabunny will extract video thumbnails for it.">
<meta property="og:url" content="https://mediabunny.dev/examples/thumbnail-generation/">
<meta property="og:image" content="https://mediabunny.dev/mediabunny-og-image.png">
<meta property="og:locale" content="en-US">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@vanilagy">
<meta name="twitter:title" content="Thumbnail generation example | Mediabunny">
<meta name="twitter:description" content="Select or drop a media file, and Mediabunny will extract video thumbnails for it.">
<meta name="twitter:image" content="https://mediabunny.dev/mediabunny-og-image.png">
<script type="application/ld+json">{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Examples","item":"https://mediabunny.dev/examples"},{"@type":"ListItem","position":2,"name":"Thumbnail generation"}]}</script>
</head>
<body class="flex flex-col items-center py-10 bg-gray-50 text-gray-800 dark:bg-zinc-900 dark:text-zinc-200 px-2">
@@ -31,11 +46,11 @@
</a>
</div>
<p class="text-xs opacity-60 mt-2" id="file-name"></p>
<p class="text-xs opacity-60 mt-2 mx-auto text-center max-w-[36rem]" id="file-name"></p>
<hr class="w-full max-w-96 my-4 border-gray-300 dark:border-zinc-700" style="display: none;">
<p id="error-element" class="text-red-500"></p>
<p id="error-element" class="text-red-500 mx-auto text-center max-w-[36rem]"></p>
<div id="thumbnail-container" class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4"></div>
<a href="/" class="fixed top-0 left-0 flex gap-2 py-2 px-5 items-center">
@@ -35,7 +35,7 @@ const generateThumbnails = async (resource: File | string) => {
throw new Error('File has no video track.');
}
if (videoTrack.codec === null) {
if (await videoTrack.getCodec() === null) {
throw new Error('Unsupported video codec.');
}
@@ -44,12 +44,14 @@ const generateThumbnails = async (resource: File | string) => {
}
// Compute width and height of the thumbnails such that the larger dimension is equal to THUMBNAIL_SIZE
const width = videoTrack.displayWidth > videoTrack.displayHeight
const displayWidth = await videoTrack.getDisplayWidth();
const displayHeight = await videoTrack.getDisplayHeight();
const width = displayWidth > displayHeight
? THUMBNAIL_SIZE
: Math.floor(THUMBNAIL_SIZE * videoTrack.displayWidth / videoTrack.displayHeight);
const height = videoTrack.displayHeight > videoTrack.displayWidth
: Math.floor(THUMBNAIL_SIZE * displayWidth / displayHeight);
const height = displayHeight > displayWidth
? THUMBNAIL_SIZE
: Math.floor(THUMBNAIL_SIZE * videoTrack.displayHeight / videoTrack.displayWidth);
: Math.floor(THUMBNAIL_SIZE * displayHeight / displayWidth);
// Create thumbnail elements
const thumbnailElements = [];
@@ -128,7 +130,7 @@ const generateThumbnails = async (resource: File | string) => {
selectMediaButton.addEventListener('click', () => {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'video/*,video/x-matroska,audio/*,audio/aac';
fileInput.accept = 'video/*,video/x-matroska,video/mp2t,.ts,audio/*,audio/aac';
fileInput.addEventListener('change', () => {
const file = fileInput.files?.[0];
if (!file) {
@@ -145,7 +147,7 @@ loadUrlButton.addEventListener('click', () => {
const url = prompt(
'Please enter a URL of a media file. Note that it must be HTTPS and support cross-origin requests, so have the'
+ ' right CORS headers set.',
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
'https://mediabunny.dev/big-buck-bunny.mp4',
);
if (!url) {
return;