Skip to content

Local video thumbnail tool ​

简体中文

Generate a PNG thumbnail sheet from a selected local video, then download it or use it with the player's thumbnails option. This is a standalone constructor, not the Auto Thumbnail plugin. This page describes the unreleased branch and its approved compatibility modes.

Install and example ​

sh
yarn add artplayer-tool-thumbnail

ESM uses import ArtplayerToolThumbnail from 'artplayer-tool-thumbnail'. Scripts load dist/artplayer-tool-thumbnail.js, exposing ArtplayerToolThumbnail. The tool does not depend on the player; the original example below uses this site's DOM and ArtPlayer to display the result:

▶ Run Code
js
if (window.lastThumbnail) {
    window.lastThumbnail.destroy();
}

var $popups = document.querySelector('.popups');
var $popinner = document.querySelector('.popinner');
var $artplayer = document.querySelector('.artplayer-app');

$artplayer.innerHTML = 'Drop video file here or click to upload.';
var thumbnail = new ArtplayerToolThumbnail({
    fileInput: $artplayer,
    number: 60, // 数量
    width: 160, // 宽度
    column: 10, // 列数
    begin: 0, // 开始
    end: NaN, // 结束
});

window.lastThumbnail = thumbnail;

thumbnail.on('file', function (file) {
    console.log('Read video successfully: ' + file.name);
});

thumbnail.on('video', function (video) {
    console.log('Video size: ' + video.videoWidth + ' x ' + video.videoHeight);
    console.log('Video duration: ' + video.duration + 's');
    thumbnail.start();
});

thumbnail.on('canvas', function (canvas) {
    console.log('Build canvas successfully');
    console.log('Canvas size: ' + canvas.width + ' x ' + canvas.height);
    console.log('Preview density: ' + thumbnail.density + ' p/s');
});

thumbnail.on('update', function (url, percentage) {
    console.log('Processing: ' + Math.floor(percentage.toFixed(2) * 100) + '%');
    $popups.style.display = 'flex';
    $popinner.style.backgroundImage = 'url(' + url + ')';
});

thumbnail.on('download', function (name) {
    console.log('Start download preview: ' + name);
});

thumbnail.on('done', function () {
    $popups.style.display = 'none';
    thumbnail.download();
    console.log('Build preview image complete');

    [...Artplayer.instances].forEach(function (art) {
        art.destroy(true);
    });

    new Artplayer({
        container: $artplayer,
        url: thumbnail.videoUrl,
        autoSize: true,
        poster: thumbnail.thumbnailUrl,
        thumbnails: {
            url: thumbnail.thumbnailUrl,
            number: thumbnail.option.number,
            column: thumbnail.option.column,
        },
    });

    console.log('Build player complete');
});

File selection loads the video without automatically extracting images; the example calls start from the video event. Application code should handle both synchronous start errors and Promise rejection. The video notification does not guarantee metadata readiness; start waits for it. Subscribe before loading, especially with synchronous workspace-mode notifications.

Options and defaults ​

Successful construction needs fileInput: an existing file input or an Element upload wrapper. Missing input throws synchronously even though the type allows omitted constructor options. A wrapper receives an owned transparent input; caller inputs remain caller-owned. Selection/drop reads only the first file.

FieldDefaultMeaning
fileInputRequired for constructionFile input or upload wrapper
compatibilitypublished-3.5 behaviorAlternatively choose workspace-4.4
number60Frame count, numerically clamped to10–1000
width160Frame width, clamped to10–1000
height90Fixed default-mode height, clamped to10–1000
column10Columns, clamped to1–1000
begin0Start time in seconds
endNaNEnd time in seconds; NaN/0 uses media duration
delay300Default-mode milliseconds, clamped to10–1000

Use valid finite dimensions and integer counts/columns; historical numeric checks are not integer validation. Start normalizes the interval against media duration and requires end greater than begin, finite duration and number / intervalSeconds <= 1. The default60 frames therefore needs an interval of at least60 seconds; change the count for shorter files.

BehaviorDefault / published-3.5workspace-4.4
HeightKeep configured heightDerive from video aspect ratio at start and update option.height
video eventAfter src assignment plus delaySynchronously after src assignment
Frame waitPolicy delay after each seek plus frame readinessFrame readiness without a fixed extra delay
doneAnother delay × 2 after the last updateNo fixed final wait
Input valueRetainedCleared after reading the selected file

Consumers of unpublished4.4 workspace behavior add compatibility: 'workspace-4.4'; this mode ignores delay. Static DEFAULTS returns a fresh default object including published delay on every access, regardless of instance mode. Browser scheduling means delays are not exact timestamps.

Methods, state and output ​

MethodBehavior
setup(options?)Merge partial options, retain extra fields and transfer input listeners when needed; returns this
loadVideo(file?)Accept File; absent input is a no-op; check canPlayType and create a Blob URL
start()One extraction job returning Promise<void>; duplicates and ready-metadata preflight can throw synchronously
creatScreenshotDate()Historical spelling; return { time, x, y }[], with time in seconds
creatCanvas()Historical spelling; create the sheet with black background and footer text
download()Trigger PNG download when idle with file/image available; returns this, throws if not ready
inputChange(event) / ondrop(event)Bound input handlers, normally installed by the tool
errorHandle(condition, message)Emit error and throw when the condition fails
destroy()Synchronous, idempotent cancellation and owned-resource cleanup

Static creatVideo creates an offscreen muted/controls video in the document; callers invoking it directly own that extra node. Static ondragover calls preventDefault. Historical creat* names remain unchanged.

Fields include processing, option, video, duration, density, file, videoUrl, thumbnailUrl and optional event registry e. Duration is the selected interval, not necessarily full media duration; density is frames per interval second. File/URLs/density may be absent before their operation. Canvas listeners observe processing=false, update listeners true, and done listeners false.

Frames sample interval midpoints: begin + (i + 0.5) * duration / number. Sheet width is width × column; height is ceil(number / column) × height + 30. The30px footer retains source/layout text. Fractional coordinates keep historical behavior. Each frame produces a PNG update; previous thumbnail Blob URLs are revoked, leaving the latest thumbnailUrl. Download naming removes the last extension segment and adds .png; extensionless names retain the historical .png result.

Events and cleanup ​

on/once/emit/off return this. The third on/once argument sets callback this. off(name) removes all listeners for that event; off(name, callback) removes matches. Custom string, number and symbol events are supported. A listener exception stops the remaining callbacks in that dispatch.

EventArguments and timing
fileFile, synchronously before video.src assignment
videoHTMLVideoElement; timing depends on mode
canvasHTMLCanvasElement before extraction
updateLatest URL and0–1 progress
doneNo arguments, before the start Promise resolves
downloadFilename after link click; not proof of completed disk writing
errorUsually a message string; user callback failures can carry other values
destroyNo arguments, once after resource cleanup

Start can wait for the first file selection with no new metadata deadline. Replacing an existing source or destroying the instance rejects old work with AbortError without an error event for cancellation. Source, seek, draw, encoding and callback failures settle the job. Stale callbacks cannot update a newer result.

Destroy removes the owned video/generated input/listeners/Blob URLs and restores wrapper position if the tool still owns that write. Caller inputs and the emitter registry remain. Cleanup attempts all steps, then throws its first failure. Destroyed instances cannot recreate input/source resources; start rejects cancellation. Keep the tool alive while a player depends on videoUrl/thumbnailUrl: those URLs remain tool-owned.

MIME canPlayType, actual decoding, Canvas encoding and Blob URL support are separate conditions. Windows WebKit has a recorded native Blob-loading gap; successful guide navigation or HTTP video playback is not proof of local-file extraction.

TypeScript ​

Root and /legacy share one class declaration, with no /runtime or runtime .default self-alias. CommonJS TS supports import Thumbnail = require('artplayer-tool-thumbnail'); ESM uses default/type imports:

ts
import Thumbnail, { type Option } from 'artplayer-tool-thumbnail';

function createTool(input: HTMLInputElement) {
  const options: Option = { fileInput: input, number: 10, height: 90 };
  const tool = new Thumbnail(options);
  tool.on('update', (url, progress) => console.log(url, progress));
  tool.on('video', () => {
    void (async () => {
      try { await tool.start(); }
      catch (error) { console.error(error); }
    })();
  });
  return tool;
}

Types include SheetOptions, Compatibility, DefaultOptions, Option, ResolvedOption, ScreenshotPoint, Events, EventArgs, Listener and EventRegistry. Known events have precise arguments; custom protocols remain application-defined. The old workspace referenced a missing declaration, so the new types are not claimed as a recovered historical TS baseline. Missing complete old npm archives and rollback acceptance remain separately tracked.