videomail-client
Version:
A wicked npm package to record videos directly in the browser, wohooo!
346 lines (293 loc) • 9.6 kB
JavaScript
import defined from 'defined'
import UAParser from 'ua-parser-js'
import VideomailError from './videomailError'
const FALLBACK_VIDEO_TYPE = 'mp4'
const Browser = function (options) {
options = options || {}
const firefoxDownload = 'http://www.mozilla.org/firefox/update/'
const edgeDownload = 'https://www.microsoft.com/en-us/download/details.aspx?id=48126'
const chromeDownload = 'http://www.google.com/chrome/'
const chromiumDownload = 'http://www.chromium.org/getting-involved/download-chromium'
const ua = defined(
options.fakeUaString,
typeof window !== 'undefined' && window.navigator && window.navigator.userAgent,
''
)
const uaParser = new UAParser(ua).getResult()
const isIOS = uaParser.os.name === 'iOS'
const browserVersion = parseFloat(uaParser.browser.version)
const isChrome = uaParser.browser.name === 'Chrome'
const isBrave = uaParser.browser.name === 'Brave'
const isChromium = uaParser.browser.name === 'Chromium'
const firefox = uaParser.browser.name === 'Firefox'
const osVersion = parseFloat(uaParser.os.version)
const isWindows = uaParser.os.name === 'Windows'
const isEdge = uaParser.browser.name === 'Edge' || (isWindows && osVersion >= 10)
const isIE = /IE/.test(uaParser.browser.name)
const isSafari = /Safari/.test(uaParser.browser.name)
const isOpera = /Opera/.test(uaParser.browser.name)
const isAndroid = /Android/.test(uaParser.os.name)
const chromeBased = isChrome || isChromium
const isFacebook = uaParser.browser.name === 'Facebook' // Facebook App for iOS & Android
const isMobile = isIOS || isAndroid
const isOkSafari = isSafari && browserVersion >= 11
const isOkIOS = isIOS && osVersion >= 11
const isBadIOS = isIOS && osVersion < 11
// unfortunately need to be able to fake https because tape-run can't run on https
const isHTTPS = options.fakeHttps || window.location.protocol === 'https:'
const okBrowser =
chromeBased ||
firefox ||
isAndroid ||
isOpera ||
isEdge ||
isOkSafari ||
isOkIOS ||
isBrave
const self = this
let videoType
function getRecommendation() {
let warning
if (firefox) {
if (isIOS) {
warning =
'Firefox on iOS is not ready for cameras yet. Hopefully in near future ...'
} else {
warning =
'Probably you need to <a href="' +
firefoxDownload +
'" target="_blank">' +
'upgrade Firefox</a> to fix this.'
}
} else if (isChrome) {
if (isIOS) {
warning =
"Use Safari instead. Apple doesn't give Chrome access to iPhone cameras (booo)."
} else {
warning =
'Probably you need to <a href="' +
chromeDownload +
'" target="_blank">' +
'upgrade Chrome</a> to fix this.'
}
} else if (isChromium) {
warning =
'Probably you need to <a href="' +
chromiumDownload +
'" target="_blank">' +
'upgrade Chromium</a> to fix this.'
} else if (isIE) {
warning =
'Instead of Internet Explorer you need to upgrade to' +
' <a href="' +
edgeDownload +
'" target="_blank">Edge</a>.'
} else if (isOkSafari) {
warning =
'Probably you need to shut down Safari and restart it, this for correct webcam access.'
} else if (isSafari) {
warning =
'Safari below version 11 has no webcam support.<br/>Better upgrade Safari or pick' +
' <a href="' +
chromeDownload +
'" target="_blank">Chrome</a>,' +
' <a href="' +
firefoxDownload +
'" target="_blank">Firefox</a> or Android.'
}
return warning
}
function getUserMediaWarning() {
let warning
if (isBadIOS) {
warning =
'On iPads or iPhones below iOS v11 this camera feature is missing.<br/><br/>' +
'For now, we recommend you to upgrade iOS or to use an Android device.'
} else {
warning = getRecommendation()
}
if (!warning) {
if (self.isChromeBased() || self.isFirefox() || isSafari) {
warning = 'For the webcam feature, your browser needs an upgrade.'
} else {
if (isFacebook) {
warning =
'Hence we recommend you to use a real browser like ' +
'<a href="' +
chromeDownload +
'" target="_blank">Chrome</a>, ' +
'<a href="' +
firefoxDownload +
'" target="_blank">Firefox</a> or ' +
'<a href="' +
edgeDownload +
'" target="_blank">Edge</a>.'
} else {
warning =
'Hence we recommend you to use either ' +
'<a href="' +
chromeDownload +
'" target="_blank">Chrome</a>, ' +
'<a href="' +
firefoxDownload +
'" target="_blank">Firefox</a>, ' +
'<a href="' +
edgeDownload +
'" target="_blank">Edge</a> or Android.'
}
}
}
return warning
}
this.canRecord = function () {
const hasNavigator = typeof navigator !== 'undefined'
let canRecord = false
if (hasNavigator && navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
canRecord = true
} else {
const getUserMediaType = hasNavigator && typeof navigator.getUserMedia_
canRecord = getUserMediaType === 'function'
}
return canRecord
}
this.checkRecordingCapabilities = function () {
let err
if (!isHTTPS) {
err = VideomailError.create(
{
message: 'Sorry, your page is insecure'
},
'Please switch to HTTPS to ensure all is encrypted.',
options,
{
classList: [VideomailError.BROWSER_PROBLEM]
}
)
} else if (!okBrowser || !this.canRecord()) {
const classList = []
if (isBadIOS) {
classList.push(VideomailError.IOS_PROBLEM)
} else {
classList.push(VideomailError.BROWSER_PROBLEM)
}
let message
// good to be able to distinguish between two reasons why and what sort of camera it is
if (!okBrowser) {
if (isMobile) {
message = 'Sorry, your browser is unable to use your mobile camera'
} else {
message = 'Sorry, your browser is unable to use webcams'
}
} else {
if (isMobile) {
if (isFacebook) {
message = 'Sorry, the Facebook app cannot record from your mobile camera'
} else {
message = 'Sorry, your browser cannot record from your mobile camera'
}
} else {
message = 'Sorry, your browser cannot record from webcams'
}
}
if (isBadIOS) {
// on older iPhones length of JSON is limited and breaking
// so just don't report and ignore
options.reportErrors = false
}
err = VideomailError.create(
{
message: message
},
getUserMediaWarning(),
options,
{
classList: classList
}
)
}
return err
}
this.checkBufferTypes = function () {
let err
if (typeof window === 'undefined' || typeof window.atob === 'undefined') {
err = VideomailError.create('atob is not supported', options)
} else if (typeof window.ArrayBuffer === 'undefined') {
err = VideomailError.create('ArrayBuffers are not supported', options)
} else if (typeof window.Uint8Array === 'undefined') {
err = VideomailError.create('Uint8Arrays are not supported', options)
}
return err
}
function canPlayType(video, type) {
let canPlayType
if (video && video.canPlayType) {
canPlayType = video.canPlayType('video/' + type)
}
// definitely cannot be played here
if (canPlayType === '') {
return false
}
return canPlayType
}
this.getVideoType = function (video) {
if (!videoType && video) {
if (canPlayType(video, 'mp4')) {
videoType = 'mp4'
} else if (canPlayType(video, 'webm')) {
videoType = 'webm'
}
}
if (videoType !== 'webm' && videoType !== 'mp4') {
// we only support these two. anything else defaults to the fallback.
videoType = FALLBACK_VIDEO_TYPE
}
if (!videoType || videoType === '') {
// just as a fallback
videoType = FALLBACK_VIDEO_TYPE
}
return videoType
}
this.getNoAccessIssue = function () {
const message = 'Unable to access webcam'
let explanation
if (this.isChromeBased()) {
explanation = 'Click on the allow button to grant access to your webcam.'
} else if (this.isFirefox()) {
explanation = 'Please grant Firefox access to your webcam.'
} else {
explanation = 'Your system does not let your browser access your webcam.'
}
return VideomailError.create(message, explanation, options)
}
this.isChromeBased = function () {
return chromeBased
}
this.isFirefox = function () {
return firefox
}
this.isEdge = function () {
return isEdge
}
this.isAndroid = function () {
return isAndroid
}
this.isMobile = function () {
return uaParser.device.type === 'mobile'
}
this.isOkSafari = function () {
return isOkSafari
}
this.isIOS = function () {
return isIOS
}
this.getUsefulData = function () {
return {
browser: uaParser.browser,
device: uaParser.device,
os: uaParser.os,
engine: uaParser.engine,
userAgent: ua
}
}
}
export default Browser