/**
* @file text-track.js
*/
從“./text-track-cue-list”導入 TextTrackCueList;
從 '../utils/fn.js' 導入 * 作為 Fn;
從 './track-enums' 導入 {TextTrackKind, TextTrackMode};
從 '../utils/log.js' 導入日誌;
從“全局/窗口”導入窗口;
從 './track.js' 導入軌道;
從'../utils/url.js'導入{isCrossOrigin};
從“@videojs/xhr”導入 XHR;
從'../utils/merge-options'導入合併;
/**
* 獲取 webvtt 文件內容並將其解析為提示
*
* @param {string} 源內容
* webVTT 文件內容
*
* @param {TextTrack} 軌道
* TextTrack 添加提示。提示來自 srcContent。
*
* @私人的
*/
const parseCues = function(srcContent, track) {
const parser = new window.WebVTT.Parser(
窗戶,
窗口.vttjs,
窗口.WebVTT.StringDecoder()
);
常量錯誤 = [];
parser.oncue = 函數(提示){
track.addCue(提示);
};
parser.onparsingerror = 函數(錯誤){
錯誤推送(錯誤);
};
parser.onflush = function() {
track.觸發器({
類型:'加載數據',
目標:跟踪
});
};
parser.parse(srcContent);
如果(錯誤。長度> 0){
如果(window.console && window.console.groupCollapsed){
window.console.groupCollapsed(`${track.src}` 的文本軌道解析錯誤);
}
errors.forEach((error) => log.error(error));
如果(window.console && window.console.groupEnd){
window.console.groupEnd();
}
}
解析器.flush();
};
/**
* 從指定的 url 加載一個 `TextTrack`。
*
* @param {string} 源
* 加載軌道的 URL。
*
* @param {TextTrack} 軌道
* 跟踪以添加提示。來自 url 末尾的內容。
*
* @私人的
*/
const loadTrack = function(src, track) {
常量選擇= {
uri: 源代碼
};
const crossOrigin = isCrossOrigin(src);
如果(跨域){
opts.cors = crossOrigin;
}
const withCredentials = track.tech_.crossOrigin() === 'use-credentials';
如果(withCredentials){
opts.withCredentials = withCredentials;
}
XHR(opts, Fn.bind(this, function(err, response, responseBody) {
如果(錯誤){
返回 log.error(err, response);
}
track.loaded_ = true;
// 確保 vttjs 已經加載,否則,等待它加載完成
// 注意:這僅用於 alt/video.novtt.js 構建
如果(窗口的類型。== '功能') {
如果(track.tech_){
// 為了防止在定義 eslint 之前使用錯誤,我們定義了 loadHandler
// 作為 let 在這裡
track.tech_.any(['vttjsloaded', 'vttjserror'], (event) => {
如果 (event.type === 'vttjserror') {
log.error(`vttjs 加載失敗,停止嘗試處理 ${track.src}`);
返回;
}
返回 parseCues(responseBody, track);
});
}
}其他{
parseCues(響應體,軌道);
}
}));
};
/**
* 單個 TextTrack 的表示。
*
* @see [規範]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack}
* @extends 軌道
*/
類 TextTrack 擴展軌道 {
/**
* 創建此類的一個實例。
*
* @param {Object} 選項={}
* 選項名稱和值的對象
*
* @param {Tech} options.tech
* 對擁有此 TextTrack 的技術的引用。
*
* @param {TextTrack~Kind} [options.kind='subtitles']
* 一個有效的文本軌道類型。
*
* @param {TextTrack~Mode} [options.mode='disabled']
* 一個有效的文本軌道模式。
*
* @param {string} [options.id='vjs_track_' + Guid.newGUID()]
* 此 TextTrack 的唯一 ID。
*
* @param {string} [options.label='']
* 此軌道的菜單標籤。
*
* @param {string} [options.language='']
* 有效的雙字符語言代碼。
*
* @param {string} [options.srclang='']
* 有效的雙字符語言代碼。一個替代方案,但被取消優先級
* `options.language` 版本
*
* @param {string} [options.src]
* TextTrack 提示的 url。
*
* @param {boolean} [options.default]
* 如果此軌道應默認打開或關閉。
*/
構造函數(選項={}){
如果(!options.tech){
throw new Error('未提供技術。');
}
常量設置=合併(選項,{
種類:TextTrackKind[options.kind] || “字幕”,
語言:options.language ||選項.srclang || ''
});
讓模式 = TextTrackMode[設置.模式] || '禁用';
const default_ = settings.default;
if (settings.kind === 'metadata' || settings.kind === 'chapters') {
模式='隱藏';
}
超級(設置);
this.tech_ = settings.tech;
this.cues_ = [];
this.activeCues_ = [];
這個。預加載 _ = 這個。技術預加載文本軌道!== 假的;
const cues = new TextTrackCueList(this.cues_);
const activeCues = new TextTrackCueList(this.activeCues_);
讓改變=假;
this.timeupdateHandler = Fn.bind(this, function(event = {}) {
如果(this.tech_.isDisposed()){
返回;
}
如果(!this.tech_.isReady_){
如果(事件類型!=='timeupdate'){
this.rvf_ = this.tech_.requestVideoFrameCallback(this.timeupdateHandler);
}
返回;
}
// 訪問 this.activeCues 以獲得更新自身的副作用
// 由於其作為 getter 函數的性質。不要刪除或提示會
// 停止更新!
// 使用 setter 防止 uglify 刪除(pure_getters 規則)
this.activeCues = this.activeCues;
如果(改變){
this.trigger('cuechange');
改變=假;
}
如果(事件類型!=='timeupdate'){
this.rvf_ = this.tech_.requestVideoFrameCallback(this.timeupdateHandler);
}
});
const disposeHandler = () => {
this.stopTracking();
};
this.tech_.one('dispose', disposeHandler);
如果(模式!=='禁用'){
這個.startTracking();
}
Object.defineProperties(這個,{
/**
* @memberof TextTrack
* @member {boolean} 默認值
* 如果此軌道默認設置為打開或關閉。之後無法更改
* 創作。
* @實例
*
* @只讀
*/
默認: {
得到() {
返回默認_;
},
放() {}
},
/**
* @memberof TextTrack
* @member {string} 模式
* 將此 TextTrack 的模式設置為有效的 {@link TextTrack~Mode}。將要
* 如果設置為無效模式則不設置。
* @實例
*
* @fires TextTrack#modechange
*/
模式: {
得到() {
返回方式;
},
設置(新模式){
如果(!文本跟踪模式 [新模式]) {
返回;
}
如果(模式===新模式){
返回;
}
模式=新模式;
如果(!這個。預加載 _ && 模式!== '禁用' && 這個 .cu.cu.length === 0) {
// 按需加載。
loadTrack(this.src, this);
}
this.stopTracking();
如果(模式!=='禁用'){
這個.startTracking();
}
/**
* 在此軌道上模式更改時觸發的事件。這允許
* 保存此軌道以相應操作的 TextTrackList。
*
* > 注意:這不是規範的一部分!
*
* @event TextTrack#modechange
* @type {EventTarget~Event}
*/
this.trigger('modechange');
}
},
/**
* @memberof TextTrack
* @member {TextTrackCueList} 提示
* 此 TextTrack 的文本軌道提示列表。
* @實例
*/
線索:{
得到() {
如果(!this.loaded_){
返回空值;
}
返回提示;
},
放() {}
},
/**
* @memberof TextTrack
* @member {TextTrackCueList} activeCues
* 當前為此 TextTrack 激活的列表文本軌道提示。
* @實例
*/
活動線索:{
得到() {
如果(!this.loaded_){
返回空值;
}
// 沒事做
如果(this.cues.length === 0){
返回活動提示;
}
const ct = this.tech_.currentTime();
const 活動 = [];
對於(讓我 = 0,升 = 這個。線索。長度; < 我; 我 ++){
const cue = this.cues[i];
如果(cue.startTime <= ct && cue.endTime >= ct){
主動推送(提示);
} else if (cue.startTime === cue.endTime &&
cue.startTime <= ct &&
cue.startTime + 0.5 >= ct) {
主動推送(提示);
}
}
改變=假;
如果(活動。長度!== 這個. 活動 _. 長度) {
改變=真;
}其他{
為 (讓我 = 0; 我 < 活躍. 長度; 我 ++) {
如果 (this.activeCues_.indexOf(active[i]) === -1) {
改變=真;
}
}
}
this.activeCues_ = active;
activeCues.setCues_(this.activeCues_);
返回活動提示;
},
///!\ 保持這個設置器為空(請參閱上面的時間更新處理程序)
放() {}
}
});
如果(設置.src){
this.src = settings.src;
如果(!this.preload_){
// 曲目將按需加載。
// 就像我們為了其他目的而加載一樣。
this.loaded_ = true;
}
如果(這個。預加載 _ ||(設置。== '字幕' && 設置。種!== '字幕')) {
loadTrack(this.src, this);
}
}其他{
this.loaded_ = true;
}
}
開始跟踪(){
// 基於帶有 requestAnimationFram 回退的 requestVideoFrameCallback 的更精確的提示
this.rvf_ = this.tech_.requestVideoFrameCallback(this.timeupdateHandler);
// 如果 rVFC/rAF 停止(後台窗口,視頻 el 中的音頻),還要聽 timeupdate
this.tech_.on('timeupdate', this.timeupdateHandler);
}
停止跟踪(){
如果(this.rvf_){
this.tech_.cancelVideoFrameCallback(this.rvf_);
this.rvf_ = 未定義;
}
this.tech_.off('timeupdate', this.timeupdateHandler);
}
/**
* 將提示添加到提示的內部列表。
*
* @param {TextTrack~Cue} 提示
* 添加到我們內部列表的提示
*/
添加提示(原始提示){
讓 cue = originalCue;
如果(窗口 .vttjs &&!(原始提示執行個體視窗. VTTJ.VTTCUE)) {
cue = new window.vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);
for (const prop in originalCue) {
如果(!(提示中的道具)){
cue[prop] = originalCue[prop];
}
}
// 確保複製了 `id`
cue.id = originalCue.id;
cue.originalCue_ = originalCue;
}
const tracks = this.tech_.textTracks();
對於(讓我 = 0; 我 < 跟踪。長度; 我 ++){
如果(曲目[i]!==這個){
tracks[i].removeCue(提示);
}
}
this.cues_.push(cue);
this.cues.setCues_(this.cues_);
}
/**
* 從我們的內部列表中刪除一個提示
*
* @param {TextTrack~Cue} removeCue
* 從我們的內部列表中刪除的提示
*/
removeCue(removeCue){
讓我 = this.cues_.length;
當我 - ) {
const cue = this.cues_[i];
如果 (cue === removeCue || (cue.originalCue_ && cue.originalCue_ === removeCue)) {
this.cues_.splice(i, 1);
this.cues.setCues_(this.cues_);
休息;
}
}
}
}
/**
* cuechange - 音軌中的一個或多個提示已激活或停止激活。
*/
TextTrack.prototype.allowedEvents_ = {
cuechange: 'cuechange'
};
導出默認 TextTrack;