/**
* @Datei text-track.js
* /
import TextTrackCueList from './text-track-cue-list';
import * as Fn from '../utils/fn.js';
importiere {TextTrackKind, TextTrackMode} von './track-enums';
import log from '../utils/log.js';
import window from 'global/window';
importiere Track aus './track.js';
import { isCrossOrigin } from '../utils/url.js';
importiere XHR von '@videojs/xhr';
importiere merge von '../utils/merge-options';
/**
* Nimmt den Inhalt einer webvtt-Datei und zerlegt ihn in Stichwörter
*
* @param {string} srcInhalt
* inhalt der webVTT-Datei
*
* @param {TextTrack} track
* TextTrack zum Hinzufügen von Stichwörtern. Die Hinweise stammen aus dem srcContent.
*
* @privat
* /
const parseCues = function(srcContent, track) {
const parser = new window.WebVTT.Parser(
fenster,
window.vttjs,
window.WebVTT.StringDecoder()
);
const errors = [];
parser.oncue = function(cue) {
track.addCue(cue);
};
parser.onparsingerror = function(error) {
errors.push(error);
};
parser.onflush = function() {
track.trigger({
typ: 'Geladene Daten',
ziel: Spur
});
};
parser.parse(srcContent);
if (errors.length > 0) {
if (window.console && window.console.groupCollapsed) {
window.console.groupCollapsed(`Text Track parsing errors for ${track.src}`);
}
errors.forEach((error) => log.error(error));
if (window.console && window.console.groupEnd) {
window.console.groupEnd();
}
}
parser.flush();
};
/**
* Lädt einen `TextTrack` von einer angegebenen Url.
*
* @param {string} src
* Url zum Laden des Tracks von.
*
* @param {TextTrack} track
* Spur zum Hinzufügen von Stichwörtern. Ergibt sich aus dem Inhalt am Ende von `url`.
*
* @privat
* /
const loadTrack = function(src, track) {
const opts = {
uri: src
};
const crossOrigin = isCrossOrigin(src);
if (crossOrigin) {
opts.cors = crossOrigin;
}
const withCredentials = track.tech_.crossOrigin() === 'use-credentials';
if (withCredentials) {
opts.withCredentials = withCredentials;
}
XHR(opts, Fn.bind(this, function(err, response, responseBody) {
if (err) {
return log.error(err, response);
}
track.loaded_ = true;
// Stellen Sie sicher, dass vttjs geladen wurde, andernfalls warten Sie, bis es fertig geladen ist
// HINWEIS: dies wird nur für den Aufbau von alt/video.novtt.js verwendet
if (typeof window.WebVTT !== 'function') {
if (track.tech_) {
// um die Verwendung vor der Definition von eslint-Fehlern zu verhindern, definieren wir loadHandler
// hier als ein Let
track.tech_.any(['vttjsloaded', 'vttjserror'], (event) => {
if (event.type === 'vttjserror') {
log.error(`vttjs konnte nicht geladen werden, der Versuch, ${track.src} zu verarbeiten, wurde abgebrochen`);
rückkehr;
}
return parseCues(responseBody, track);
});
}
} else {
parseCues(responseBody, track);
}
}));
};
/**
* Eine Darstellung einer einzelnen "TextSpur".
*
* @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack}
* @erweitert Spur
* /
class TextTrack extends Track {
/**
* Erstellen Sie eine Instanz dieser Klasse.
*
* @param {Object} options={}
* Objekt der Optionsnamen und -werte
*
* @param {Tech} options.tech
* Ein Verweis auf den Techniker, dem dieser TextTrack gehört.
*
* @param {TextTrack~Kind} [options.kind='subtitles']
* Eine gültige Textspurart.
*
* @param {TextTrack~Mode} [options.mode='disabled']
* Ein gültiger Textspurmodus.
*
* @param {string} [options.id='vjs_track_' + Guid.newGUID()]
* Eine eindeutige ID für diesen TextTrack.
*
* @param {string} [options.label='']
* Die Menübezeichnung für diesen Titel.
*
* @param {string} [options.language='']
* Ein gültiger zweistelliger Sprachcode.
*
* @param {string} [options.srclang='']
* Ein gültiger zweistelliger Sprachcode. Eine alternative, aber depriorisierte
* version von `options.language`
*
* @param {string} [options.src]
* Eine Url zu TextTrack-Stichwörtern.
*
* @param {boolean} [options.default]
* Ob diese Spur standardmäßig ein- oder ausgeschaltet sein soll.
* /
constructor(options = {}) {
if (!options.tech) {
throw new Error('Ein Techniker wurde nicht bereitgestellt.');
}
const settings = merge(options, {
art: TextTrackKind[options.kind] || 'Untertitel',
sprache: options.language || options.srclang || ''
});
let mode = TextTrackMode[settings.mode] || 'disabled';
const default_ = settings.default;
if (einstellungen.art === 'metadaten' || einstellungen.art === 'kapitel') {
modus = 'versteckt';
}
super(Einstellungen);
this.tech_ = settings.tech;
this.cues_ = [];
this.activeCues_ = [];
this.preload_ = this.tech_.preloadTextTracks !== false;
const cues = new TextTrackCueList(this.cues_);
const activeCues = new TextTrackCueList(this.activeCues_);
let changed = false;
this.timeupdateHandler = Fn.bind(this, function(event = {}) {
if (this.tech_.isDisposed()) {
rückkehr;
}
if (!this.tech_.isReady_) {
if (event.type !== 'timeupdate') {
this.rvf_ = this.tech_.requestVideoFrameCallback(this.timeupdateHandler);
}
rückkehr;
}
// Zugriff auf this.activeCues für die Nebeneffekte der Selbstaktualisierung
// aufgrund seiner Natur als Getter-Funktion. Nicht entfernen, sonst werden Cues
// Stoppen Sie die Aktualisierung!
// Verwenden Sie den Setter, um das Löschen von uglify zu verhindern (pure_getters-Regel)
this.activeCues = this.activeCues;
if (geändert) {
this.trigger('cuechange');
geändert = falsch;
}
if (event.type !== 'timeupdate') {
this.rvf_ = this.tech_.requestVideoFrameCallback(this.timeupdateHandler);
}
});
const disposeHandler = () => {
this.stopTracking();
};
this.tech_.one('dispose', disposeHandler);
if (Modus !== 'deaktiviert') {
this.startTracking();
}
Object.defineProperties(this, {
/**
* @Mitglied von TextTrack
* @member {boolean} default
* Wenn diese Spur standardmäßig ein- oder ausgeschaltet ist. Kann nicht geändert werden nach
* erstellung.
* @Instanz
*
* @readonly
* /
standard: {
get() {
return default_;
},
set() {}
},
/**
* @Mitglied von TextTrack
* @member {string} mode
* Setzt den Modus dieses TextTracks auf einen gültigen {@link TextTrack~Mode}. Will
* nicht gesetzt werden, wenn sie auf einen ungültigen Modus eingestellt sind.
* @Instanz
*
* @feuert TextTrack#Moduswechsel
* /
modus: {
get() {
rückkehrmodus;
},
set(newMode) {
if (!TextTrackMode[newMode]) {
rückkehr;
}
if (mode === newMode) {
rückkehr;
}
modus = newMode;
if (!this.preload_ && mode !== 'disabled' && this.cues.length === 0) {
// Last auf Abruf.
loadTrack(this.src, this);
}
this.stopTracking();
if (Modus !== 'deaktiviert') {
this.startTracking();
}
/**
* Ein Ereignis, das ausgelöst wird, wenn sich der Modus auf dieser Spur ändert. Dies ermöglicht
* die TextTrackList, die diesen Track enthält, um entsprechend zu handeln.
*
* > Anmerkung: Dies ist nicht Teil der Spezifikation!
*
* @Ereignis TextTrack#Moduswechsel
* @Typ {EventTarget~Event}
* /
this.trigger('modechange');
}
},
/**
* @Mitglied von TextTrack
* @member {TextTrackCueList} cues
* Die Texttrack-Cue-Liste für diesen TextTrack.
* @Instanz
* /
stichwörter: {
get() {
if (!this.loaded_) {
null zurückgeben;
}
hinweise zurückgeben;
},
set() {}
},
/**
* @Mitglied von TextTrack
* @member {TextTrackCueList} activeCues
* Die Liste der Text-Track-Cues, die derzeit für diesen TextTrack aktiv sind.
* @Instanz
* /
activeCues: {
get() {
if (!this.loaded_) {
null zurückgeben;
}
// nichts zu tun
if (this.cues.length === 0) {
return activeCues;
}
const = this.tech_.currentTime();
const active = [];
for (let i = 0, l = this.cues.length; i < l; i++) {
const cue = this.cues[i];
if (cue.startTime <= ct && cue.endTime >= ct) {
active.push(cue);
} else if (cue.startTime === cue.endTime &&
cue.startTime <= ct &&
cue.startTime + 0.5 >= ct) {
active.push(cue);
}
}
geändert = falsch;
if (active.length !== this.activeCues_.length) {
geändert = wahr;
} else {
for (let i = 0; i < active.length; i++) {
if (this.activeCues_.indexOf(active[i]) === -1) {
geändert = wahr;
}
}
}
this.activeCues_ = active;
activeCues.setCues_(this.activeCues_);
return activeCues;
},
/!\ Diesen Setter leer lassen (siehe den Timeupdate-Handler oben)
set() {}
}
});
if (einstellungen.src) {
this.src = settings.src;
if (!this.preload_) {
// Tracks werden bei Bedarf geladen.
// Tu so, als ob wir für andere Zwecke geladen wären.
this.loaded_ = true;
}
if (this.preload_ || (settings.kind !== 'subtitles' && settings.kind !== 'captions')) {
loadTrack(this.src, this);
}
} else {
this.loaded_ = true;
}
}
startTracking() {
// Präzisere Hinweise auf der Grundlage von requestVideoFrameCallback mit einem requestAnimationFram-Fallback
this.rvf_ = this.tech_.requestVideoFrameCallback(this.timeupdateHandler);
// Höre auch auf timeupdate, falls rVFC/rAF stoppt (Fenster im Hintergrund, Audio im Video el)
this.tech_.on('timeupdate', this.timeupdateHandler);
}
stopTracking() {
if (this.rvf_) {
this.tech_.cancelVideoFrameCallback(this.rvf_);
this.rvf_ = undefiniert;
}
this.tech_.off('timeupdate', this.timeupdateHandler);
}
/**
* Fügen Sie der internen Liste von Stichwörtern ein Stichwort hinzu.
*
* @param {TextTrack~Cue} cue
* Das Stichwort, um unsere interne Liste zu ergänzen
* /
addCue(originalCue) {
let cue = originalCue;
if (window.vttjs && !(originalCue instanceof window.vttjs.VTTCue)) {
cue = new window.vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);
for (const prop in originalCue) {
if (!(prop in cue)) {
cue[prop] = originalCue[prop];
}
}
// Sicherstellen, dass `id` übernommen wird
cue.id = originalCue.id;
cue.originalCue_ = originalCue;
}
const tracks = this.tech_.textTracks();
for (let i = 0; i < tracks.length; i++) {
if (tracks[i] !== this) {
tracks[i].removeCue(cue);
}
}
this.cues_.push(cue);
this.cues.setCues_(this.cues_);
}
/**
* Entfernen eines Stichworts aus unserer internen Liste
*
* @param {TextTrack~Cue} removeCue
* Das Stichwort zum Entfernen aus unserer internen Liste
* /
removeCue(removeCue) {
let i = this.cues_.length;
while (i--) {
const cue = this.cues_[i];
if (cue === removeCue || (cue.originalCue_ && cue.originalCue_ === removeCue)) {
this.cues_.splice(i, 1);
this.cues.setCues_(this.cues_);
pause;
}
}
}
}
/**
* cuechange - Ein oder mehrere Cues im Track sind aktiv geworden oder haben aufgehört, aktiv zu sein.
* /
TextTrack.prototype.allowedEvents_ = {
cuechange: "cuechange" (Stichwortwechsel)
};
standard-TextSpur exportieren;