Skip to content
Permalink
Browse files

#6 Read thumbnail from a video on the client side

  • Loading branch information
marcomontalbano committed Jan 11, 2020
1 parent a19bced commit a3aa931a3a8fc6deabd3660237c151331b28f4fd
Binary file not shown.
@@ -0,0 +1 @@
<?xml version="1.0" ?><svg height="20px" version="1.1" viewBox="0 0 20 20" width="20px" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" xmlns:xlink="http://www.w3.org/1999/xlink"><title/><desc/><defs/><g fill="none" fill-rule="evenodd" id="Page-1" stroke="none" stroke-width="1"><g fill="#000000" id="Icons-AV" transform="translate(-168.000000, -85.000000)"><g id="play-circle-outline" transform="translate(168.000000, 85.000000)"><path d="M8,14.5 L14,10 L8,5.5 L8,14.5 L8,14.5 Z M10,0 C4.5,0 0,4.5 0,10 C0,15.5 4.5,20 10,20 C15.5,20 20,15.5 20,10 C20,4.5 15.5,0 10,0 L10,0 Z M10,18 C5.6,18 2,14.4 2,10 C2,5.6 5.6,2 10,2 C14.4,2 18,5.6 18,10 C18,14.4 14.4,18 10,18 L10,18 Z" id="Shape"/></g></g></g></svg>
Binary file not shown.
@@ -8,6 +8,7 @@ import imageLoading from './images/loading.jpg';
const lambdaUrl = `${location.protocol}//${location.host}/.netlify/functions`;

const videoIcons = {
video: require('./images/providers/video.png'),
dailymotion: require('./images/providers/dailymotion.png'),
facebook: require('./images/providers/facebook.png'),
vimeo: require('./images/providers/vimeo.png'),
@@ -117,6 +118,73 @@ const loadErrorImage = (formElement) => {
});
}

const imageJsonConverter = (formElement, title, videoUrl, showPlayIcon, image = '') => {
fetch(`${lambdaUrl}/image-json`, {
method: 'POST',
body: new URLSearchParams([
['image', image],
['url', videoUrl],
['showPlayIcon', showPlayIcon],
])
}).then(r => r.json())
.then(data => {
if (data.error) {
return loadErrorImage(formElement);
}

loadImage(data.image, function() {
NProgress.done();
formElement.querySelector('[name="url"] ~ img').setAttribute('src', videoIcons[data.provider]);
document.querySelector('.preview img').replaceWith(this);
updateMarkdown(title, data.image, videoUrl);
});
})
.catch(() => {
loadErrorImage(formElement);
});
};

const videoConverter = (formElement, title, videoUrl, showPlayIcon) => {

const video = document.createElement('video');
var canvas = document.createElement('canvas');

video.setAttribute('crossOrigin', 'anonymous');

video.addEventListener('loadeddata', function() {
reloadRandomFrame();
}, false);

video.addEventListener('error', function() {
loadErrorImage(formElement);
}, false);

video.addEventListener('seeked', function() {
try {
const context = canvas.getContext('2d');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
context.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
const base64 = canvas.toDataURL('image/jpeg');
imageJsonConverter(formElement, title, videoUrl, showPlayIcon, base64);
} catch (e) {
loadErrorImage(formElement);
}
}, false);

video.src = videoUrl;

function reloadRandomFrame() {
if (!isNaN(video.duration)) {
const rand = Math.round(Math.random() * video.duration * 1000) + 1;
video.currentTime = rand / 1000;
}
}

};

import Video from './lambda/classes/Providers/Video';

let memoFormSubmit;
document.querySelector('form').addEventListener('submit', function (e) {
e.preventDefault();
@@ -148,20 +216,9 @@ document.querySelector('form').addEventListener('submit', function (e) {
document.querySelector('.preview img').setAttribute('src', imageLoading);
updateMarkdown();

fetch(`${lambdaUrl}/image-json?showPlayIcon=${showPlayIcon}&url=${encodeURIComponent(videoUrl)}`).then(r => r.json())
.then(data => {
if (data.error) {
return loadErrorImage(formElement);
}

loadImage(data.image, function() {
NProgress.done();
formElement.querySelector('[name="url"] ~ img').setAttribute('src', videoIcons[data.provider]);
document.querySelector('.preview img').replaceWith(this);
updateMarkdown(title, data.image, videoUrl);
});
})
.catch(() => {
loadErrorImage(formElement);
});
if (Video.check(videoUrl)) {
videoConverter(formElement, title, videoUrl, showPlayIcon);
} else {
imageJsonConverter(formElement, title, videoUrl, showPlayIcon);
}
});
@@ -0,0 +1,25 @@
import VideoProvider from '../VideoProvider';

import crypto from 'crypto';

export default class Video extends VideoProvider {

get providerName() {
return 'video';
}

static get regex() {
return [
// - //asciinema.org/a/113463
/^https?\:\/\/.*\.(mp4|mov|webm)$/
];
}

static getVideoId(url = '') {
return super.getVideoId(url) ? crypto.createHash('md5').update(url).digest('hex') : undefined;
}

getThumbnail_asVideoUrl() {
return Promise.resolve(this.options.image);
}
}
@@ -0,0 +1,21 @@
import Video from './Video';

describe('Video', () => {
it('"regex" must be correct.', () => {
expect(Video.getVideoId('https://i.imgur.com/vhjwXMB.mp4')).toBe('f48ef897bfabed6334368c76e716f871');
expect(Video.check('https://asciinema.org/a/113463')).toBe(false);
});

it('all methods must work.', () => {
const url = 'https://i.imgur.com/vhjwXMB.mp4';
const video = new Video(url);

// static methods
expect(Video.check(url)).toBe(true);

// instance methods
expect(video.getId()).toBe('f48ef897bfabed6334368c76e716f871');
expect(video.providerName).toBe('video');
expect(video.url).toBe(url);
});
})
@@ -1,5 +1,3 @@
import cloudinary from './cloudinary';

export default class VideoProvider {
static get regex() {}

@@ -38,7 +36,7 @@ export default class VideoProvider {
return videoUrl;
}

return cloudinary.create(videoUrl, this, {
return this.options.ImageService.create(videoUrl, this, {
showPlayIcon: this.options.showPlayIcon
}).then(response => response.secure_url);
});
@@ -7,6 +7,7 @@ export default class VideoWrapper {
require('./Providers/Vimeo'),
require('./Providers/Asciinema'),
require('./Providers/GoogleDrive'),
require('./Providers/Video'),
];
}

@@ -1,12 +1,33 @@
import VideoWrapper from './VideoWrapper';

describe('VideoWrapper', () => {
it('"create" method must instantiate the correct Video Provider.', () => {
expect(VideoWrapper.create('https://www.dailymotion.com/video/x3ke49').providerName).toBe('dailymotion');
expect(VideoWrapper.create('https://vimeo.com/263856289').providerName).toBe('vimeo');
expect(VideoWrapper.create('https://www.youtube.com/watch?v=oRdzL2DX0yU').providerName).toBe('youtube');
expect(VideoWrapper.create('https://www.facebook.com/backintimetheparty/videos/1588846901182916/').providerName).toBe('facebook');
expect(VideoWrapper.create('https://asciinema.org/a/113463').providerName).toBe('asciinema');
expect(VideoWrapper.create('https://drive.google.com/file/d/5p_qEW432qT5_EWQjwTo-Q5FaEjjsWUvc/view').providerName).toBe('google-drive');
describe('"create" method must instantiate the correct Video Provider.', () => {
it('dailymotion', () => {
expect(VideoWrapper.create('https://www.dailymotion.com/video/x3ke49').providerName).toBe('dailymotion');
});

it('vimeo', () => {
expect(VideoWrapper.create('https://vimeo.com/263856289').providerName).toBe('vimeo');
});

it('youtube', () => {
expect(VideoWrapper.create('https://www.youtube.com/watch?v=oRdzL2DX0yU').providerName).toBe('youtube');
});

it('facebook', () => {
expect(VideoWrapper.create('https://www.facebook.com/backintimetheparty/videos/1588846901182916/').providerName).toBe('facebook');
});

it('asciinema', () => {
expect(VideoWrapper.create('https://asciinema.org/a/113463').providerName).toBe('asciinema');
});

it('google-drive', () => {
expect(VideoWrapper.create('https://drive.google.com/file/d/5p_qEW432qT5_EWQjwTo-Q5FaEjjsWUvc/view').providerName).toBe('google-drive');
});

it('video', () => {
expect(VideoWrapper.create('https://i.imgur.com/vhjwXMB.mp4').providerName).toBe('video');
});
});
});
@@ -1,4 +1,5 @@
import VideoWrapper from './classes/VideoWrapper';
import cloudinary from './classes/cloudinary';

import { sendLambdaEvent } from './classes/google-ua';

@@ -37,7 +38,8 @@ exports.handler = (event, context, callback) => {
try {
video = VideoWrapper.create(url, {
showPlayIcon: getParam(event, 'showPlayIcon') === 'true',
image: getParam(event, 'image')
image: getParam(event, 'image'),
ImageService: cloudinary
});
} catch (error) {
return callback(null, {

0 comments on commit a3aa931

Please sign in to comment.
You can’t perform that action at this time.