Skip to content

Commit

Permalink
v1.1.0: caching, simpler canThumbnail
Browse files Browse the repository at this point in the history
- Added optional thumbnail caching using `localStorage`
- Made `canThumbnail` only check for `localStorage`, and only get calculated once
  • Loading branch information
pseudosavant committed Dec 30, 2020
1 parent fcd61f6 commit a42d201
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 19 deletions.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# video-thumbnail.js
Library to convert a URL into a image data URI
Library to convert a URL into a image [data URI](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) or [objectURL](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL)

### Supported features
## Supported features

* Video thumbnail generation from a URL (must be `same-origin` or support `CORS`)

### How to use
## How to use

#### Simple
### Simple
```js
async function getCoolThumbnail() {
const url = 'https://mycool.com/videosite/video.mp4';
Expand All @@ -16,18 +16,19 @@ async function getCoolThumbnail() {
}
```

#### Advanced
### Advanced
```js
async function getCoolThumbnail() {
const url = 'https://mycool.com/videosite/video.mp4';
const time = 0.1; // Grab thumbnail from 10% into the video
const size = 480; // Maximum of 480px wide thumbnail
const type = 'dataURI'; // `videoThumbnail` can return a `dataURI` or `objectURL`
const cache = true; // Cache thumbnails in `localStorage`
const mime = {
type: 'image/jpeg',
quality: 0.5 // Quality is not required for `image/png`
};
const thumbnailURI = await videoThumbnail(url, {time, size, type, mime});
const thumbnailURI = await videoThumbnail(url, {time, size, type, mime, cache});
// Do something with `thumbnailURI`
}
```
Expand Down
4 changes: 3 additions & 1 deletion src/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@
thumbnails: {
timestamp: 0.25, // How far into the clip (relatively) should it grab the thumbnail from (e.g. 0.10 = 10%)
size: 480, // Maximum width of thumbnails. Setting this smaller will save localStorage space.
cache: true,
mime: {
type: 'image/jpeg',
quality: 0.5
Expand Down Expand Up @@ -521,9 +522,10 @@
const size = app.options.thumbnails.size;
const time = app.options.thumbnails.timestamp;
const mime = app.options.thumbnails.mime;
const cache = app.options.thumbnails.cache;

const start = Date.now();
const thumbnailURI = await videoThumbnail(url, {time, size, type, mime});
const thumbnailURI = await videoThumbnail(url, {time, size, type, mime, cache});
const duration = Math.round(Date.now() - start);

const msg = `${url} (${app.options.thumbnails.size}px max, ${type}): ${duration}ms`;
Expand Down
76 changes: 64 additions & 12 deletions src/video-thumbnail.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,25 @@
// https://github.com/pseudosavant/video-thumbnail.js
// © 2020 Paul Ellis (https://github.com/pseudosavant)
// License: MIT
// v1.0.0
// v1.1.0

function store(key, val) {
try {
return localStorage.setItem(key, val);
} catch (e) {
console.warn(`Failed to store: ${key}`, e);
return null;
}
}

function retrieve(key) {
try {
return localStorage.getItem(key);
} catch (e) {
console.warn(`Failed to retrieve: ${key}`, e);
return null;
}
}

function getVideo(url) {
const $player = document.createElement('video');
Expand Down Expand Up @@ -69,27 +87,61 @@
return (n > 0 && n < 1);
}

const canThumbnail = (function(){
const falseMessage = 'Thumbnail support: false';

// Must support `localStorage`
try {
const key = '__canThumbnailTest__';
const val = 'true';

localStorage.setItem(key, val);
const supported = localStorage.getItem(key) === val;
localStorage.removeItem(key);

if (!supported) console.info(falseMessage);

return supported;
} catch (e) {
console.info(falseMessage);
return false;
}
})();

async function getThumbnailDataURI(url, opts) {
if (!canThumbnail) return undefined;

const def = {
time: 0.1,
size: 480,
mime: { type: 'image/png' },
type: 'dataURI'
type: 'dataURI',
cache: false
};

try {
const isImageMimeType = (s) => (/image\/.+/i).test(s);
const isImageMimeType = (s) => (/image\/.+/i).test(s);

const time = (typeof opts.time === 'number' && opts.time >= 0 ? opts.time : def.size);
const size = (typeof opts.size === 'number' && opts.size > 0 ? opts.size : def.size);
const mime = (opts.mime && isImageMimeType(opts.mime.type) ? opts.mime : def.mime);
const type = (opts.type === 'objectURL' ? opts.type : def.type);
const time = (typeof opts.time === 'number' && opts.time >= 0 ? opts.time : def.size);
const size = (typeof opts.size === 'number' && opts.size > 0 ? opts.size : def.size);
const mime = (opts.mime && isImageMimeType(opts.mime.type) ? opts.mime : def.mime);
const type = (opts.type === 'objectURL' ? opts.type : def.type);
const cache = (typeof opts.cache === 'boolean' ? opts.cache : def.cache);

const $player = await getVideo(url);
const dataURI = await videoToDataURI($player, time, size, mime, type);
$player.src = ''; // Unset video
try {
const key = `video-thumbnail.js-cache-${size}|${time}|${url}`;
const cachedURI = retrieve(key);

if (cache && cachedURI) {
return cachedURI;
} else {
const $player = await getVideo(url);
const dataURI = await videoToDataURI($player, time, size, mime, type);
$player.src = ''; // Unset video

if (cache) store(key, dataURI);

return dataURI;
return dataURI;
}
} catch (e) {
console.info(`Unable to create thumbnail for: ${url}`, e);
}
Expand Down

0 comments on commit a42d201

Please sign in to comment.