2020-08-16 20:24:45 +02:00
|
|
|
import browser from '../../scripts/browser';
|
|
|
|
import { appRouter } from '../../components/appRouter';
|
2020-08-14 08:46:34 +02:00
|
|
|
import loading from '../../components/loading/loading';
|
2022-04-12 16:22:00 -04:00
|
|
|
import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../components/backdrop/backdrop';
|
2022-10-14 10:53:16 -04:00
|
|
|
import Events from '../../utils/events.ts';
|
2018-10-23 01:05:09 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
/* globals YT */
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2020-10-29 23:52:52 +03:00
|
|
|
const errorCodes = {
|
|
|
|
2: 'YoutubeBadRequest',
|
|
|
|
5: 'YoutubePlaybackError',
|
|
|
|
100: 'YoutubeNotFound',
|
|
|
|
101: 'YoutubeDenied',
|
|
|
|
150: 'YoutubeDenied'
|
|
|
|
};
|
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
function zoomIn(elem, iterations) {
|
|
|
|
const keyframes = [
|
|
|
|
{ transform: 'scale3d(.2, .2, .2) ', opacity: '.6', offset: 0 },
|
|
|
|
{ transform: 'none', opacity: '1', offset: 1 }
|
|
|
|
];
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
const timing = { duration: 240, iterations: iterations };
|
|
|
|
return elem.animate(keyframes, timing);
|
|
|
|
}
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
function createMediaElement(instance, options) {
|
2021-01-26 22:20:12 -05:00
|
|
|
return new Promise(function (resolve) {
|
2020-07-17 20:51:01 +01:00
|
|
|
const dlg = document.querySelector('.youtubePlayerContainer');
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
if (!dlg) {
|
2021-01-26 15:48:00 -05:00
|
|
|
import('./style.scss').then(() => {
|
2020-07-17 20:51:01 +01:00
|
|
|
loading.show();
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
const dlg = document.createElement('div');
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
dlg.classList.add('youtubePlayerContainer');
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
if (options.fullscreen) {
|
|
|
|
dlg.classList.add('onTop');
|
|
|
|
}
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
dlg.innerHTML = '<div id="player"></div>';
|
|
|
|
const videoElement = dlg.querySelector('#player');
|
2018-10-23 01:05:09 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
document.body.insertBefore(dlg, document.body.firstChild);
|
|
|
|
instance.videoDialog = dlg;
|
2018-10-23 01:05:09 +03:00
|
|
|
|
2020-09-26 23:55:02 +03:00
|
|
|
if (options.fullscreen) {
|
2020-11-21 23:04:42 +03:00
|
|
|
document.body.classList.add('hide-scroll');
|
2020-09-26 23:55:02 +03:00
|
|
|
}
|
2020-09-03 01:40:36 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
if (options.fullscreen && dlg.animate && !browser.slow) {
|
|
|
|
zoomIn(dlg, 1).onfinish = function () {
|
|
|
|
resolve(videoElement);
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
resolve(videoElement);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
2020-10-31 12:54:40 +03:00
|
|
|
// we need to hide scrollbar when starting playback from page with animated background
|
2020-09-26 23:55:02 +03:00
|
|
|
if (options.fullscreen) {
|
2020-11-21 23:04:42 +03:00
|
|
|
document.body.classList.add('hide-scroll');
|
2020-09-26 23:55:02 +03:00
|
|
|
}
|
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
resolve(dlg.querySelector('#player'));
|
2019-01-10 15:39:37 +03:00
|
|
|
}
|
2020-07-17 20:51:01 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function onVideoResize() {
|
|
|
|
const instance = this;
|
|
|
|
const player = instance.currentYoutubePlayer;
|
|
|
|
const dlg = instance.videoDialog;
|
|
|
|
if (player && dlg) {
|
|
|
|
player.setSize(dlg.offsetWidth, dlg.offsetHeight);
|
|
|
|
}
|
|
|
|
}
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
function clearTimeUpdateInterval(instance) {
|
|
|
|
if (instance.timeUpdateInterval) {
|
|
|
|
clearInterval(instance.timeUpdateInterval);
|
|
|
|
}
|
|
|
|
instance.timeUpdateInterval = null;
|
|
|
|
}
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
function onEndedInternal(instance) {
|
|
|
|
clearTimeUpdateInterval(instance);
|
|
|
|
const resizeListener = instance.resizeListener;
|
|
|
|
if (resizeListener) {
|
|
|
|
window.removeEventListener('resize', resizeListener);
|
|
|
|
window.removeEventListener('orientationChange', resizeListener);
|
|
|
|
instance.resizeListener = null;
|
2018-10-23 01:05:09 +03:00
|
|
|
}
|
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
const stopInfo = {
|
|
|
|
src: instance._currentSrc
|
|
|
|
};
|
2018-10-23 01:05:09 +03:00
|
|
|
|
2020-09-08 02:05:02 -04:00
|
|
|
Events.trigger(instance, 'stopped', [stopInfo]);
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
instance._currentSrc = null;
|
|
|
|
if (instance.currentYoutubePlayer) {
|
|
|
|
instance.currentYoutubePlayer.destroy();
|
2018-10-23 01:05:09 +03:00
|
|
|
}
|
2020-07-17 20:51:01 +01:00
|
|
|
instance.currentYoutubePlayer = null;
|
|
|
|
}
|
2018-10-23 01:05:09 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
// 4. The API will call this function when the video player is ready.
|
|
|
|
function onPlayerReady(event) {
|
|
|
|
event.target.playVideo();
|
|
|
|
}
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2021-01-26 22:20:12 -05:00
|
|
|
function onTimeUpdate() {
|
2020-09-08 02:05:02 -04:00
|
|
|
Events.trigger(this, 'timeupdate');
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
function onPlaying(instance, playOptions, resolve) {
|
|
|
|
if (!instance.started) {
|
|
|
|
instance.started = true;
|
|
|
|
resolve();
|
|
|
|
clearTimeUpdateInterval(instance);
|
|
|
|
instance.timeUpdateInterval = setInterval(onTimeUpdate.bind(instance), 500);
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
if (playOptions.fullscreen) {
|
|
|
|
appRouter.showVideoOsd().then(function () {
|
|
|
|
instance.videoDialog.classList.remove('onTop');
|
2019-01-10 15:39:37 +03:00
|
|
|
});
|
2020-07-17 20:51:01 +01:00
|
|
|
} else {
|
2022-04-12 16:22:00 -04:00
|
|
|
setBackdropTransparency(TRANSPARENCY_LEVEL.Backdrop);
|
2020-07-17 20:51:01 +01:00
|
|
|
instance.videoDialog.classList.remove('onTop');
|
2019-01-10 15:39:37 +03:00
|
|
|
}
|
2018-10-23 01:05:09 +03:00
|
|
|
|
2020-11-08 11:00:57 +00:00
|
|
|
loading.hide();
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function setCurrentSrc(instance, elem, options) {
|
|
|
|
return new Promise(function (resolve, reject) {
|
2020-08-16 20:24:45 +02:00
|
|
|
instance._currentSrc = options.url;
|
|
|
|
const params = new URLSearchParams(options.url.split('?')[1]); /* eslint-disable-line compat/compat */
|
|
|
|
// 3. This function creates an <iframe> (and YouTube player)
|
|
|
|
// after the API code downloads.
|
|
|
|
window.onYouTubeIframeAPIReady = function () {
|
|
|
|
instance.currentYoutubePlayer = new YT.Player('player', {
|
|
|
|
height: instance.videoDialog.offsetHeight,
|
|
|
|
width: instance.videoDialog.offsetWidth,
|
2020-11-08 11:00:57 +00:00
|
|
|
videoId: params.get('v'),
|
2020-08-16 20:24:45 +02:00
|
|
|
events: {
|
|
|
|
'onReady': onPlayerReady,
|
|
|
|
'onStateChange': function (event) {
|
|
|
|
if (event.data === YT.PlayerState.PLAYING) {
|
|
|
|
onPlaying(instance, options, resolve);
|
|
|
|
} else if (event.data === YT.PlayerState.ENDED) {
|
|
|
|
onEndedInternal(instance);
|
|
|
|
} else if (event.data === YT.PlayerState.PAUSED) {
|
2020-09-08 02:05:02 -04:00
|
|
|
Events.trigger(instance, 'pause');
|
2019-01-10 15:39:37 +03:00
|
|
|
}
|
2020-07-17 20:51:01 +01:00
|
|
|
},
|
2020-11-21 22:51:24 +03:00
|
|
|
'onError': (e) => reject(errorCodes[e.data] || 'ErrorDefault')
|
2020-08-16 20:24:45 +02:00
|
|
|
},
|
|
|
|
playerVars: {
|
|
|
|
controls: 0,
|
|
|
|
enablejsapi: 1,
|
|
|
|
modestbranding: 1,
|
|
|
|
rel: 0,
|
|
|
|
showinfo: 0,
|
|
|
|
fs: 0,
|
|
|
|
playsinline: 1
|
2018-10-23 01:05:09 +03:00
|
|
|
}
|
2020-08-16 20:24:45 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
let resizeListener = instance.resizeListener;
|
|
|
|
if (resizeListener) {
|
|
|
|
window.removeEventListener('resize', resizeListener);
|
|
|
|
window.addEventListener('resize', resizeListener);
|
2020-07-17 20:51:01 +01:00
|
|
|
} else {
|
2020-08-16 20:24:45 +02:00
|
|
|
resizeListener = instance.resizeListener = onVideoResize.bind(instance);
|
|
|
|
window.addEventListener('resize', resizeListener);
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
2020-08-16 20:24:45 +02:00
|
|
|
window.removeEventListener('orientationChange', resizeListener);
|
|
|
|
window.addEventListener('orientationChange', resizeListener);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!window.YT) {
|
|
|
|
const tag = document.createElement('script');
|
|
|
|
tag.src = 'https://www.youtube.com/iframe_api';
|
|
|
|
const firstScriptTag = document.getElementsByTagName('script')[0];
|
|
|
|
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
|
|
|
|
} else {
|
|
|
|
window.onYouTubeIframeAPIReady();
|
|
|
|
}
|
2020-07-17 20:51:01 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
class YoutubePlayer {
|
|
|
|
constructor() {
|
2019-01-10 15:39:37 +03:00
|
|
|
this.name = 'Youtube Player';
|
|
|
|
this.type = 'mediaplayer';
|
|
|
|
this.id = 'youtubeplayer';
|
|
|
|
|
|
|
|
// Let any players created by plugins take priority
|
|
|
|
this.priority = 1;
|
2018-10-23 01:05:09 +03:00
|
|
|
}
|
2020-07-17 20:51:01 +01:00
|
|
|
play(options) {
|
2019-01-10 15:39:37 +03:00
|
|
|
this.started = false;
|
2020-07-17 20:51:01 +01:00
|
|
|
const instance = this;
|
2019-01-10 15:39:37 +03:00
|
|
|
|
|
|
|
return createMediaElement(this, options).then(function (elem) {
|
|
|
|
return setCurrentSrc(instance, elem, options);
|
|
|
|
});
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
|
|
|
stop(destroyPlayer) {
|
|
|
|
const src = this._currentSrc;
|
2019-01-10 15:39:37 +03:00
|
|
|
|
|
|
|
if (src) {
|
|
|
|
if (this.currentYoutubePlayer) {
|
|
|
|
this.currentYoutubePlayer.stopVideo();
|
|
|
|
}
|
|
|
|
onEndedInternal(this);
|
|
|
|
|
|
|
|
if (destroyPlayer) {
|
|
|
|
this.destroy();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.resolve();
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
|
|
|
destroy() {
|
2022-04-12 16:22:00 -04:00
|
|
|
setBackdropTransparency(TRANSPARENCY_LEVEL.None);
|
2020-09-03 01:40:36 +03:00
|
|
|
document.body.classList.remove('hide-scroll');
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
const dlg = this.videoDialog;
|
2019-01-10 15:39:37 +03:00
|
|
|
if (dlg) {
|
|
|
|
this.videoDialog = null;
|
|
|
|
|
|
|
|
dlg.parentNode.removeChild(dlg);
|
|
|
|
}
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
|
|
|
canPlayMediaType(mediaType) {
|
2019-01-10 15:39:37 +03:00
|
|
|
mediaType = (mediaType || '').toLowerCase();
|
|
|
|
|
|
|
|
return mediaType === 'audio' || mediaType === 'video';
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
2021-01-26 22:20:12 -05:00
|
|
|
canPlayItem() {
|
2019-01-10 15:39:37 +03:00
|
|
|
// Does not play server items
|
|
|
|
return false;
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
|
|
|
canPlayUrl(url) {
|
2019-01-10 15:39:37 +03:00
|
|
|
return url.toLowerCase().indexOf('youtube.com') !== -1;
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
|
|
|
getDeviceProfile() {
|
2019-01-10 15:39:37 +03:00
|
|
|
return Promise.resolve({});
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
|
|
|
currentSrc() {
|
2019-01-10 15:39:37 +03:00
|
|
|
return this._currentSrc;
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
2021-01-26 22:20:12 -05:00
|
|
|
setSubtitleStreamIndex() {
|
2022-03-01 10:57:48 -05:00
|
|
|
// not supported
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
|
|
|
canSetAudioStreamIndex() {
|
2019-01-10 15:39:37 +03:00
|
|
|
return false;
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
2021-01-26 22:20:12 -05:00
|
|
|
setAudioStreamIndex() {
|
2022-03-01 10:57:48 -05:00
|
|
|
// not supported
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
2019-01-10 15:39:37 +03:00
|
|
|
// Save this for when playback stops, because querying the time at that point might return 0
|
2020-07-17 20:51:01 +01:00
|
|
|
currentTime(val) {
|
|
|
|
const currentYoutubePlayer = this.currentYoutubePlayer;
|
2019-01-10 15:39:37 +03:00
|
|
|
|
|
|
|
if (currentYoutubePlayer) {
|
|
|
|
if (val != null) {
|
|
|
|
currentYoutubePlayer.seekTo(val / 1000, true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return currentYoutubePlayer.getCurrentTime() * 1000;
|
|
|
|
}
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
2021-01-26 22:20:12 -05:00
|
|
|
duration() {
|
2020-07-17 20:51:01 +01:00
|
|
|
const currentYoutubePlayer = this.currentYoutubePlayer;
|
2019-01-10 15:39:37 +03:00
|
|
|
|
|
|
|
if (currentYoutubePlayer) {
|
|
|
|
return currentYoutubePlayer.getDuration() * 1000;
|
|
|
|
}
|
|
|
|
return null;
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
|
|
|
pause() {
|
|
|
|
const currentYoutubePlayer = this.currentYoutubePlayer;
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2018-10-23 01:05:09 +03:00
|
|
|
if (currentYoutubePlayer) {
|
|
|
|
currentYoutubePlayer.pauseVideo();
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
const instance = this;
|
2019-01-10 15:39:37 +03:00
|
|
|
|
|
|
|
// This needs a delay before the youtube player will report the correct player state
|
|
|
|
setTimeout(function () {
|
2020-09-08 02:05:02 -04:00
|
|
|
Events.trigger(instance, 'pause');
|
2019-01-10 15:39:37 +03:00
|
|
|
}, 200);
|
2018-10-23 01:05:09 +03:00
|
|
|
}
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
|
|
|
unpause() {
|
|
|
|
const currentYoutubePlayer = this.currentYoutubePlayer;
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2018-10-23 01:05:09 +03:00
|
|
|
if (currentYoutubePlayer) {
|
|
|
|
currentYoutubePlayer.playVideo();
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
const instance = this;
|
2019-01-10 15:39:37 +03:00
|
|
|
|
|
|
|
// This needs a delay before the youtube player will report the correct player state
|
|
|
|
setTimeout(function () {
|
2020-09-08 02:05:02 -04:00
|
|
|
Events.trigger(instance, 'unpause');
|
2019-01-10 15:39:37 +03:00
|
|
|
}, 200);
|
2018-10-23 01:05:09 +03:00
|
|
|
}
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
|
|
|
paused() {
|
|
|
|
const currentYoutubePlayer = this.currentYoutubePlayer;
|
2019-01-10 15:39:37 +03:00
|
|
|
|
|
|
|
if (currentYoutubePlayer) {
|
|
|
|
return currentYoutubePlayer.getPlayerState() === 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
|
|
|
volume(val) {
|
2019-01-10 15:39:37 +03:00
|
|
|
if (val != null) {
|
|
|
|
return this.setVolume(val);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.getVolume();
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
|
|
|
setVolume(val) {
|
|
|
|
const currentYoutubePlayer = this.currentYoutubePlayer;
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2022-10-03 14:22:02 -04:00
|
|
|
if (currentYoutubePlayer && val != null) {
|
|
|
|
currentYoutubePlayer.setVolume(val);
|
2019-01-10 15:39:37 +03:00
|
|
|
}
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
|
|
|
getVolume() {
|
|
|
|
const currentYoutubePlayer = this.currentYoutubePlayer;
|
2019-01-10 15:39:37 +03:00
|
|
|
|
|
|
|
if (currentYoutubePlayer) {
|
|
|
|
return currentYoutubePlayer.getVolume();
|
|
|
|
}
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
|
|
|
setMute(mute) {
|
|
|
|
const currentYoutubePlayer = this.currentYoutubePlayer;
|
2019-01-10 15:39:37 +03:00
|
|
|
|
|
|
|
if (mute) {
|
|
|
|
if (currentYoutubePlayer) {
|
|
|
|
currentYoutubePlayer.mute();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (currentYoutubePlayer) {
|
|
|
|
currentYoutubePlayer.unMute();
|
|
|
|
}
|
|
|
|
}
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
|
|
|
isMuted() {
|
|
|
|
const currentYoutubePlayer = this.currentYoutubePlayer;
|
2019-01-10 15:39:37 +03:00
|
|
|
|
|
|
|
if (currentYoutubePlayer) {
|
|
|
|
return currentYoutubePlayer.isMuted();
|
|
|
|
}
|
2020-07-17 20:51:01 +01:00
|
|
|
}
|
|
|
|
}
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2020-07-17 20:51:01 +01:00
|
|
|
export default YoutubePlayer;
|