import document from 'global/document';
import Promise from 'es6-promise';
import { isValidUrl, getYtId } from '../utils';
const HT_PARAMS_PATTERN = /([src|subtitle]+)=([a-zA-Z0-9:/.\-_?=&]+)/g;
const SUPPORTED_SUBTITLES = ['vtt', 'srt'];
const parseHTParams = (params) => {
const obj = {};
let m;
while ((m = HT_PARAMS_PATTERN.exec(params)) !== null) {
if (m.index === HT_PARAMS_PATTERN.lastIndex) {
HT_PARAMS_PATTERN.lastIndex++;
}
obj[m[1]] = m[2];
}
return obj;
};
class VideoItem {
videoManager = null;
constructor(videoManager, elem) {
this.videoManager = videoManager;
const dataHtValue = elem.getAttribute('data-ht');
const data = parseHTParams(dataHtValue);
// Pega o type e o source
const source = this.getSource(data);
if (source.type == 'youtube') this.createYtPlayer(elem, source, this.getSubtitle(data));
else this.createGenericPlayer(elem, source, this.getSubtitle(data));
}
/**
* Verifica se uma legenda é compativel
* @param subtitle - Objeto com o src e type da legenda
*/
checkSubtitle = (subtitle) => {
return subtitle && SUPPORTED_SUBTITLES.indexOf(subtitle.type, 0) != -1;
}
/**
* Cria um novo player em mp4
* @param elem - Elemento atual do video
* @param source - Source com type e src
* @param subtitle - Subtitle com type e src
*/
createGenericPlayer = (elem, source, subtitle) => {
// Cria um player sem legenda caso a legenda seja incompativel
if (!this.checkSubtitle(subtitle)) {
this.createPlayerWithoutSubtitle(elem, source);
return;
}
this.downloadSubtitle(subtitle).then((vttData) => {
this.videoManager.htVideo.createAccessibilePlayer(elem, source.src, source.type, vttData);
}).catch(() => { this.createPlayerWithoutSubtitle(elem, source, subtitle); });
}
/**
* Cria um novo player do youtube
* @param elem - Elemento atual do video
* @param source - Source com type e src
* @param subtitle - Subtitle com type e src
*/
createYtPlayer = (elem, source, subtitle) => {
// Cria uma solicitação de legenda do tipo yt no ht-translation caso nenhuma legenda tenha sido informada
if (!subtitle) {
subtitle = {
'type': 'yt',
// Verifica se o src do video é uma url valida, senão consideramos um id do youtube
'src': isValidUrl(source.src) ? getYtId(source.src) : source.src
};
}
// Cria um player sem legenda caso a legenda seja incompativel
if (subtitle.type != 'yt' && !this.checkSubtitle(subtitle)) {
this.createPlayerWithoutSubtitle(elem, source, subtitle);
return;
}
// Baixa a legenda
this.downloadSubtitle(subtitle).then((vttData) => {
this.videoManager.htVideo.createAccessibilePlayer(elem, source.src, source.type, vttData);
}).catch(() => { this.createPlayerWithoutSubtitle(elem, source); });
}
/** Cria um player sem legenda, criado quando uma legenda não é compativel */
createPlayerWithoutSubtitle = (elem, source, subtitle) => {
this.videoManager.htVideo.createAccessibilePlayer(elem, source.src, source.type, 'Formato de legenda não suportado', true);
this.videoManager.ht.logger.warn('Tradutor de vídeos | Formato de legenda não suportado', source, subtitle);
}
/**
* Obtém o source de um objeto ht-data
* @param data - Objeto ht-data
*/
getSource = (data) => {
if (data.src) {
const [type, ...sourceUrl] = data.src.split(':');
const src = sourceUrl.join(':');
return { 'type': type, 'src': src };
}
}
/**
* Obtém a legenda de um objeto ht-data
* @param data - Objeto ht-data
*/
getSubtitle = (data) => {
if (data.subtitle) {
const [type, ...sourceUrl] = data.subtitle.split(':');
const src = sourceUrl.join(':');
return { 'type': type, 'src': src };
}
}
/**
* Baixa a legenda através do nosso serviço
* @param subtitle - Legenda com src e type
*/
downloadSubtitle = (subtitle) => {
// Pega o arquivo local caso vtt ou srt, pra outros tipos consulta o serviço
const url = subtitle.type == 'yt' ? `${TRANSLATE_SUBTITLE_URL}?type=${subtitle.type}&src=${subtitle.src}` : subtitle.src;
return new Promise((resolve, reject) => {
const xmlHttp = new XMLHttpRequest();
xmlHttp.open('GET', url);
xmlHttp.onload = () => {
const data = xmlHttp.responseText;
if (xmlHttp.status == 200) resolve(data);
else reject();
};
xmlHttp.onerror = reject;
xmlHttp.send();
});
}
}
class VideoManager {
ht = null;
htVideo = null;
hasProblem = false;
currentVideo = null;
static vjsMinVersion = 5;
/**
* @param {HT} ht
*/
constructor(ht) {
this.ht = ht;
this.ht.logger.debug('init videoManager');
this.ht.once('errorOnAuth', () => {
this.hasProblem = true;
this.htVideo.error = true;
if (this.ht.pageTranslatorManager) this.ht.pageTranslatorManager.activated = true;
});
this.loadDependencies().then(() => {
this.ht.startHugo().then((hugo) => {
this.htVideo = new HTVideo(hugo);
this.htVideo.on('hugoViewChanged', this.onVideoActivated);
this.ht.emit('videoManagerReady');
this.search();
});
}).catch(() => {
this.ht.logger.error('Falha ao baixar dependencias do tradutor de vídeos');
});
}
_loadDependenciesPromise = null;
/** Carrega todas as dependencias */
loadDependencies = () => {
if (this._loadDependenciesPromise) return this._loadDependenciesPromise;
return this._loadDependenciesPromise = new Promise((resolve, reject) => {
if (!window.videojs)
this.loadVideoJsDependencies()
.then(() => this.loadVideoPlayerScript())
.then(() => resolve())
.catch(() => reject());
else {
if (window.videojs.VERSION && window.videojs.VERSION.split('.')[0] < VideoManager.vjsMinVersion)
this.ht.logger.warn(`A versão ${window.videojs.VERSION} do videojs pode apresentar instabilidade com o Tradutor de Vídeos. Considere atualizar para versão ${VideoManager.vjsMinVersion}.0.0 ou superior`);
this.loadVideoPlayerScript()
.then(() => resolve())
.catch(() => reject());
}
});
};
_loadVideoPlayerPromise = null;
loadVideoPlayerScript = () => {
if (this._loadVideoPlayerPromise) return this._loadVideoPlayerPromise;
return this._loadVideoPlayerPromise = new Promise((resolve, reject) => {
const scriptElem = document.createElement('script');
scriptElem.src = VIDEO_PLAYER_SCRIPT_URL;
scriptElem.onload = () => {
this.ht.logger.debug('ht video carregado');
resolve();
};
scriptElem.onerror = reject;
this.ht.config.parentElement.appendChild(scriptElem);
});
};
loadVideoJsDependencies = () => (
this.loadVideojsScript().then(() => this.loadVideojsStyle())
)
_loadVideojsScriptPromise = null;
loadVideojsScript = () => {
if (this._loadVideojsScriptPromise) return this._loadVideojsScriptPromise;
return this._loadVideojsScriptPromise = new Promise((resolve, reject) => {
const scriptElem = document.createElement('script');
scriptElem.src = VIDEOJS_SCRIPT_URL;
scriptElem.onload = () => {
this.ht.logger.debug(`Versão ${videojs.VERSION} do videojs injetada`);
resolve();
};
scriptElem.onerror = reject;
document.head.appendChild(scriptElem);
});
}
_loadVideojsStylePromise = null;
loadVideojsStyle = () => {
if (this._loadVideojsStylePromise) return this._loadVideojsStylePromise;
return this._loadVideojsStylePromise = new Promise((resolve, reject) => {
const styleElem = document.createElement('link');
styleElem.type = 'text/css';
styleElem.rel = 'stylesheet';
styleElem.setAttribute('rel', 'stylesheet');
styleElem.onload = () => {
this.ht.logger.debug('CSS do videojs injetado');
resolve();
};
styleElem.onerror = reject;
styleElem.href = VIDEOJS_CSS_URL;
document.head.appendChild(styleElem);
});
}
/**
* Adiciona os componentes de acessibilidade em um player existente do videojs
* @param vjsPlayer - Player atual do videojs
*/
makeVideojsAccessibile = (vjsPlayer) => {
this.ht.logger.debug('Componentes adicionados', vjsPlayer);
this.htVideo.makeVideojsAccessibile(vjsPlayer);
}
/**
* Chamado quando a opção de Libras é ativada em um video
* @param video - Video que teve foi ativado
*/
onVideoActivated = (video) => {
this.ht.logger.debug('ativado', video);
this.currentVideo = video;
this.ht._auth();
if (this.ht.pageTranslatorManager) this.ht.pageTranslatorManager.activated = false;
}
/**
* Desativa o vídeo acessivel atual
*/
disableCurrentAcessibleVideo = () => {
if (this.currentVideo) {
this.currentVideo.signLanguageButton.toggleActive(false);
this.currentVideo.pause();
}
}
/**
* @function
*/
search = () => {
const elems = Array.prototype.map.bind(document.querySelectorAll('video[data-ht]'))(elem => (elem));
elems.forEach((elem) => { this.getItem(elem); });
};
elemItems = [];
/**
* @function
*/
getItem = (elem) => {
const elemItem = this.elemItems.filter(elemItem => (elemItem.elem === elem))[0];
if (elemItem) return elemItem.item;
this.elemItems.push({ elem, item: new VideoItem(this, elem) });
return this.getItem(elem);
};
}
export default VideoManager;