uppy
Version:
Almost as cute as a Puppy :dog:
312 lines (264 loc) • 8.73 kB
JavaScript
const Plugin = require('../Plugin')
const WebcamProvider = require('../../uppy-base/src/plugins/Webcam')
const Translator = require('../../core/Translator')
const { extend,
getFileTypeExtension,
supportsMediaRecorder } = require('../../core/Utils')
const WebcamIcon = require('./WebcamIcon')
const CameraScreen = require('./CameraScreen')
const PermissionsScreen = require('./PermissionsScreen')
/**
* Webcam
*/
module.exports = class Webcam extends Plugin {
constructor (core, opts) {
super(core, opts)
this.userMedia = true
this.protocol = location.protocol.match(/https/i) ? 'https' : 'http'
this.type = 'acquirer'
this.id = 'Webcam'
this.title = 'Webcam'
this.icon = WebcamIcon
this.focus = this.focus.bind(this)
const defaultLocale = {
strings: {
smile: 'Smile!'
}
}
// set default options
const defaultOptions = {
enableFlash: true,
onBeforeSnapshot: () => Promise.resolve(),
countdown: false,
locale: defaultLocale,
modes: [
'video-audio',
'video-only',
'audio-only',
'picture'
]
}
this.params = {
swfURL: 'webcam.swf',
width: 400,
height: 300,
dest_width: 800, // size of captured image
dest_height: 600, // these default to width/height
image_format: 'jpeg', // image format (may be jpeg or png)
jpeg_quality: 90, // jpeg image quality from 0 (worst) to 100 (best)
enable_flash: true, // enable flash fallback,
force_flash: false, // force flash mode,
flip_horiz: false, // flip image horiz (mirror mode)
fps: 30, // camera frames per second
upload_name: 'webcam', // name of file in upload post data
constraints: null, // custom user media constraints,
flashNotDetectedText: 'ERROR: No Adobe Flash Player detected. Webcam.js relies on Flash for browsers that do not support getUserMedia (like yours).',
noInterfaceFoundText: 'No supported webcam interface found.',
unfreeze_snap: true // Whether to unfreeze the camera after snap (defaults to true)
}
// merge default options with the ones set by user
this.opts = Object.assign({}, defaultOptions, opts)
this.locale = Object.assign({}, defaultLocale, this.opts.locale)
this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings)
// i18n
this.translator = new Translator({locale: this.locale})
this.i18n = this.translator.translate.bind(this.translator)
this.install = this.install.bind(this)
this.setPluginState = this.setPluginState.bind(this)
this.render = this.render.bind(this)
// Camera controls
this.start = this.start.bind(this)
this.stop = this.stop.bind(this)
this.takeSnapshot = this.takeSnapshot.bind(this)
this.startRecording = this.startRecording.bind(this)
this.stopRecording = this.stopRecording.bind(this)
this.oneTwoThreeSmile = this.oneTwoThreeSmile.bind(this)
// this.justSmile = this.justSmile.bind(this)
this.webcam = new WebcamProvider(this.opts, this.params)
this.webcamActive = false
if (this.opts.countdown) {
this.opts.onBeforeSnapshot = this.oneTwoThreeSmile
}
// if (typeof opts.onBeforeSnapshot === 'undefined' || !this.opts.onBeforeSnapshot) {
// if (this.opts.countdown) {
// this.opts.onBeforeSnapshot = this.oneTwoThreeSmile
// } else {
// this.opts.onBeforeSnapshot = this.justSmile
// }
// }
}
/**
* Little shorthand to update the state with my new state
*/
setPluginState (newState) {
const {state} = this.core
const webcam = Object.assign({}, state.webcam, newState)
this.core.setState({webcam})
}
start () {
this.webcamActive = true
this.webcam.start()
.then((stream) => {
this.stream = stream
this.setPluginState({
// videoStream: stream,
cameraReady: true
})
})
.catch((err) => {
this.setPluginState({
cameraError: err
})
})
}
startRecording () {
// TODO We can check here if any of the mime types listed in the
// mimeToExtensions map in Utils.js are supported, and prefer to use one of
// those.
// Right now we let the browser pick a type that it deems appropriate.
this.recorder = new MediaRecorder(this.stream)
this.recordingChunks = []
this.recorder.addEventListener('dataavailable', (event) => {
this.recordingChunks.push(event.data)
})
this.recorder.start()
this.setPluginState({
isRecording: true
})
}
stopRecording () {
return new Promise((resolve, reject) => {
this.recorder.addEventListener('stop', () => {
this.setPluginState({
isRecording: false
})
const mimeType = this.recordingChunks[0].type
const fileExtension = getFileTypeExtension(mimeType)
if (!fileExtension) {
reject(new Error(`Could not upload file: Unsupported media type "${mimeType}"`))
return
}
const file = {
source: this.id,
name: `webcam-${Date.now()}.${fileExtension}`,
type: mimeType,
data: new Blob(this.recordingChunks, { type: mimeType })
}
this.core.addFile(file)
this.recordingChunks = null
this.recorder = null
resolve()
})
this.recorder.stop()
})
}
stop () {
this.stream.getAudioTracks().forEach((track) => {
track.stop()
})
this.stream.getVideoTracks().forEach((track) => {
track.stop()
})
this.webcamActive = false
this.stream = null
this.streamSrc = null
}
oneTwoThreeSmile () {
return new Promise((resolve, reject) => {
let count = this.opts.countdown
let countDown = setInterval(() => {
if (!this.webcamActive) {
clearInterval(countDown)
this.captureInProgress = false
return reject('Webcam is not active')
}
if (count > 0) {
this.core.info(`${count}...`, 'warning', 800)
count--
} else {
clearInterval(countDown)
this.core.info(this.i18n('smile'), 'success', 1500)
setTimeout(() => resolve(), 1500)
}
}, 1000)
})
}
// justSmile () {
// return new Promise((resolve, reject) => {
// setTimeout(() => this.core.info(this.i18n('smile'), 'success', 1000), 1500)
// setTimeout(() => resolve(), 2000)
// })
// }
takeSnapshot () {
const opts = {
name: `webcam-${Date.now()}.jpg`,
mimeType: 'image/jpeg'
}
this.videoEl = this.target.querySelector('.UppyWebcam-video')
if (this.captureInProgress) return
this.captureInProgress = true
this.opts.onBeforeSnapshot().catch((err) => {
this.core.info(err, 'error', 5000)
return Promise.reject(`onBeforeSnapshot: ${err}`)
}).then(() => {
const video = this.target.querySelector('.UppyWebcam-video')
if (!video) {
this.captureInProgress = false
return Promise.reject('No video element found, likely due to the Webcam tab being closed.')
}
const image = this.webcam.getImage(video, opts)
const tagFile = {
source: this.id,
name: opts.name,
data: image.data,
type: opts.mimeType
}
this.captureInProgress = false
this.core.addFile(tagFile)
})
}
focus () {
if (this.opts.countdown) return
setTimeout(() => {
this.core.info(this.i18n('smile'), 'success', 1500)
}, 1000)
}
render (state) {
if (!this.webcamActive) {
this.start()
}
if (!state.webcam.cameraReady && !state.webcam.useTheFlash) {
return PermissionsScreen(state.webcam)
}
if (!this.streamSrc) {
this.streamSrc = this.stream ? URL.createObjectURL(this.stream) : null
}
return CameraScreen(extend(state.webcam, {
onSnapshot: this.takeSnapshot,
onStartRecording: this.startRecording,
onStopRecording: this.stopRecording,
onFocus: this.focus,
onStop: this.stop,
modes: this.opts.modes,
supportsRecording: supportsMediaRecorder(),
recording: state.webcam.isRecording,
getSWFHTML: this.webcam.getSWFHTML,
src: this.streamSrc
}))
}
install () {
this.webcam.init()
this.core.setState({
webcam: {
cameraReady: false
}
})
const target = this.opts.target
const plugin = this
this.target = this.mount(target, plugin)
}
uninstall () {
this.webcam.reset()
this.unmount()
}
}