openplayerjs-youtube
Version:
OpenPlayer extension to play YouTube videos
344 lines (320 loc) • 11 kB
JavaScript
import loadScript from 'simple-load-script';
const YouTube = (element, media, autoplay = false, options = {}) => {
let player;
let interval;
let _volume = 1;
let _ended = false;
let _paused = true;
const promise = new Promise(resolve => {
resolve();
});
const id = `op-yt__${element.id || new Date().getTime()}`;
const isAudio = element.tagName === 'AUDIO';
const opts = {
url: 'https://www.youtube.com/iframe_api',
autoplay: 0,
controls: 0,
disablekb: 1,
end: 0,
loop: 0,
modestbranding: 0,
playsinline: 0,
rel: 0,
showinfo: 0,
start: 0,
iv_load_policy: 3,
// custom to inject `-nocookie` element in URL
nocookie: false,
};
const playerVars = { ...opts, ...options };
const { url: library, nocookie: noCookies , ...rest } = playerVars;
/**
* Formats:
* - http://www.youtube.com/watch?feature=player_embedded&v=VIDEO_ID
* - http://www.youtube.com/v/VIDEO_ID?version=3
* - http://youtu.be/VIDEO_ID
* - http://www.youtube-nocookie.com/watch?feature=player_embedded&v=VIDEO_ID
* - https://youtube.com/watch?v=VIDEO_ID
* @param {string} url
* @returns {string}
*/
function _getYouTubeId(url) {
return url.match(/(?:(?:youtu\.be\/)|(?:v=)|(?:\/v\/))(\w+)/)[1];
}
const _startInterval = () => {
// create timer
interval = setInterval(() => {
const event = new CustomEvent('timeupdate');
element.dispatchEvent(event);
}, 250);
}
const _stopInterval = () => {
if (interval) {
clearInterval(interval);
}
}
const playerSettings = {
id,
videoId: _getYouTubeId(media.src),
height: isAudio ? 1 : element.offsetHeight,
width: isAudio ? 1 : element.offsetWidth,
widget_referrer: window.location.host,
origin: window.location.host,
playerVars: rest,
events: {
onReady: e => {
player = e.target;
const iframe = player.getIframe();
// Check if browser is a Chromium browser or the main element is muted; if so, mute it
// @see https://github.com/videojs/videojs-youtube/issues/593
if ('msLaunchUri' in window.navigator && !('documentMode' in document) || /chrome/i.test(window.navigator.userAgent) || element.muted) {
player.mute();
}
['mouseover', 'mouseout'].forEach(event => {
iframe.addEventListener(event, ev => {
const newEvent = new CustomEvent(ev.type);
element.dispatchEvent(newEvent);
});
});
['loadedmetadata', 'loadeddata', 'canplay'].forEach(event => {
const ev = new CustomEvent(event);
element.dispatchEvent(ev);
});
},
onStateChange: e => {
let events = [];
switch (e.data) {
case 0: // YT.PlayerState.ENDED
events = ['ended'];
_paused = false;
_ended = !playerVars.loop;
if (!playerVars.loop) {
_stopInterval();
}
break;
case 1: // YT.PlayerState.PLAYING
events = ['play', 'playing'];
_paused = false;
_ended = false;
_startInterval();
break;
case 2: // YT.PlayerState.PAUSED
events = ['pause'];
_paused = true;
_ended = false;
_stopInterval();
break;
case 3: // YT.PlayerState.BUFFERING
events = ['progress'];
_ended = false;
break;
case 5: // YT.PlayerState.CUED
events = ['loadeddata', 'loadedmetadata', 'canplay'];
_paused = true;
_ended = false;
break;
default: // not started
events = ['loadedmetadata'];
_paused = true;
_ended = false;
break;
}
for (let i = 0, total = events.length; i < total; i++) {
const event = new CustomEvent(events[i]);
element.dispatchEvent(event);
}
},
// @see https://developers.google.com/youtube/iframe_api_reference#onError
onError: e => {
let message = '';
switch (e.data) {
case 2:
message = `The request contains an invalid parameter value. Verify that video ID has 11
characters and that contains no invalid characters, such as exclamation points or asterisks.`;
break;
case 5:
message = `The requested content cannot be played in an HTML5 player or another error
related to the HTML5 player has occurred.`;
break;
case 100:
message = 'The video requested was not found. Either video has been removed or has been marked as private.';
break;
case 101:
case 105:
message = 'The owner of the requested video does not allow it to be played in embedded players.';
break;
default:
message = 'Unknown error.';
break;
}
console.error(`YouTube Error: ${message}`);
}
}
};
function create() {
if (document.getElementById(id)) {
return;
}
const container = document.createElement('div');
container.id = id;
// If `noCookies` feature was enabled, modify original URL
if (noCookies) {
media.src = media.src.replace('.com', '-nocookie.com');
}
element.parentNode.insertBefore(container, element);
element.style.display = 'none';
if (isAudio || element.hasAttribute('playsinline')) {
playerVars.playsinline = 1;
}
if (autoplay) {
playerVars.autoplay = 1;
}
if (element.loop) {
playerVars.loop = 1;
}
if ((playerVars.loop === 1 || media.src.indexOf('loop=') > -1)
&& !playerVars.playlist && media.src.indexOf('playlist=') === -1) {
playerVars.playlist = _getYouTubeId(media.src);
}
playerVars.controls = 0;
playerVars.enablejsapi = 1;
if (typeof YT === 'undefined' || !YT.loaded) {
loadScript(library);
}
return this;
}
function load() {
return null;
}
function canPlayType(mimeType) {
return mimeType === 'video/x-youtube';
}
function play() {
player.playVideo();
}
function pause() {
player.pauseVideo();
}
function destroy() {
if (player) {
player.destroy();
}
}
// @see https://developers.google.com/youtube/iframe_api_reference#Requirements
window.onYouTubeIframeAPIReady = () => {
create();
return new YT.Player(id, playerSettings);
};
return Object.freeze({
promise,
create,
load,
canPlayType,
play,
pause,
destroy,
set src(source) {
_source = typeof source === 'string' ? source : source[0].src;
if (player) {
const videoId = _getYouTubeId(_source);
if (autoplay) {
player.loadVideoById(videoId);
} else {
player.cueVideoById(videoId);
}
}
},
get src() {
if (player) {
return player.getVideoUrl();
}
return '';
},
set volume(value) {
_volume = value;
if (player) {
player.setVolume(value * 100);
setTimeout(() => {
const event = new CustomEvent('volumechange');
element.dispatchEvent(event);
}, 50);
}
},
get volume() {
if (player) {
_volume = player.getVolume() / 100;
}
return _volume;
},
set muted(value) {
if (player) {
if (value) {
player.mute();
} else {
player.unMute();
}
setTimeout(() => {
const event = new CustomEvent('volumechange');
element.dispatchEvent(event);
}, 50);
}
},
get muted() {
if (player) {
return player.isMuted();
}
return false;
},
set playbackRate(value) {
if (player) {
player.setPlaybackRate(value);
}
},
get playbackRate() {
return player ? player.getPlaybackRate() : 1;
},
set defaultPlaybackRate(value) {
if (player) {
player.setPlaybackRate(value);
}
},
get defaultPlaybackRate() {
return player ? player.getPlaybackRate() : 1;
},
set currentTime(value) {
if (player) {
player.seekTo(value);
setTimeout(() => {
const event = new CustomEvent('timeupdate');
element.dispatchEvent(event);
}, 50);
}
},
get currentTime() {
if (player) {
return player.getCurrentTime();
}
return 0;
},
get duration() {
if (player) {
return player.getDuration();
}
return 0;
},
get paused() {
return _paused;
},
get ended() {
return _ended;
}
});
};
if (OpenPlayerJS) {
OpenPlayerJS.addMedia(
'youtube',
'video/x-youtube',
url => (/\/\/(www\.youtube|youtu\.?be)/i.test(url) ? 'video/x-youtube' : null),
YouTube
);
}