mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-14 15:56:38 +03:00
feat!: Remove LRC, SBV, SSA support (#9330)
Related to https://github.com/shaka-project/shaka-player/issues/9321
This commit is contained in:
committed by
GitHub
parent
e85b648ef0
commit
4bc2518e29
@@ -298,12 +298,6 @@ Shaka Player supports:
|
||||
(depends on browser support via MediaSource).
|
||||
- SubRip (SRT)
|
||||
- UTF-8 encoding only
|
||||
- LyRiCs (LRC)
|
||||
- UTF-8 encoding only
|
||||
- SubStation Alpha (SSA, ASS)
|
||||
- UTF-8 encoding only
|
||||
- SubViewer (SBV)
|
||||
- UTF-8 encoding only
|
||||
|
||||
Subtitles are rendered by the browser by default. Applications can create a
|
||||
[text display plugin][] for customer rendering to go beyond browser-supported
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
# Optional plugins related to text parsing and displaying.
|
||||
|
||||
+../../lib/text/lrc_text_parser.js
|
||||
+../../lib/text/sbv_text_parser.js
|
||||
+../../lib/text/srt_text_parser.js
|
||||
+../../lib/text/ssa_text_parser.js
|
||||
|
||||
@@ -39,9 +39,6 @@ __Subtitle/caption parsers__
|
||||
- TTML: {@linksource shaka.text.TtmlTextParser} and
|
||||
{@linksource shaka.text.Mp4TtmlParser}
|
||||
- SubRip (SRT): {@linksource shaka.text.SrtTextParser}
|
||||
- LyRiCs (LRC): {@linksource shaka.text.LrcTextParser}
|
||||
- SubStation Alpha (SSA, ASS): {@linksource shaka.text.SsaTextParser}
|
||||
- SubViewer (SBV): {@linksource shaka.text.SbvTextParser}
|
||||
|
||||
__Subtitle/caption displayers__
|
||||
- Configured at runtime on a Player instance
|
||||
|
||||
@@ -94,14 +94,10 @@ shaka.net.NetworkingUtils.EXTENSIONS_TO_MIME_TYPES_ = new Map()
|
||||
.set('aac', 'audio/aac')
|
||||
.set('flac', 'audio/flac')
|
||||
.set('wav', 'audio/wav')
|
||||
.set('sbv', 'text/x-subviewer')
|
||||
.set('srt', 'text/srt')
|
||||
.set('vtt', 'text/vtt')
|
||||
.set('webvtt', 'text/vtt')
|
||||
.set('ttml', 'application/ttml+xml')
|
||||
.set('lrc', 'application/x-subtitle-lrc')
|
||||
.set('ssa', 'text/x-ssa')
|
||||
.set('ass', 'text/x-ssa')
|
||||
.set('jpeg', 'image/jpeg')
|
||||
.set('jpg', 'image/jpeg')
|
||||
.set('png', 'image/png')
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
/*! @license
|
||||
* Shaka Player
|
||||
* Copyright 2016 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
goog.provide('shaka.text.LrcTextParser');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('shaka.log');
|
||||
goog.require('shaka.text.Cue');
|
||||
goog.require('shaka.text.TextEngine');
|
||||
goog.require('shaka.util.StringUtils');
|
||||
|
||||
|
||||
/**
|
||||
* LRC file format: https://en.wikipedia.org/wiki/LRC_(file_format)
|
||||
*
|
||||
* @implements {shaka.extern.TextParser}
|
||||
* @export
|
||||
*/
|
||||
shaka.text.LrcTextParser = class {
|
||||
/**
|
||||
* @override
|
||||
* @export
|
||||
*/
|
||||
parseInit(data) {
|
||||
goog.asserts.assert(false, 'LRC does not have init segments');
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @export
|
||||
*/
|
||||
setManifestType(manifestType) {
|
||||
// Unused.
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @export
|
||||
*/
|
||||
parseMedia(data, time) {
|
||||
const StringUtils = shaka.util.StringUtils;
|
||||
const LrcTextParser = shaka.text.LrcTextParser;
|
||||
|
||||
// Get the input as a string.
|
||||
const str = StringUtils.fromUTF8(data);
|
||||
|
||||
/** @type {shaka.text.Cue} */
|
||||
let prevCue = null;
|
||||
|
||||
/** @type {!Array<!shaka.text.Cue>} */
|
||||
const cues = [];
|
||||
const lines = str.split(/\r?\n/);
|
||||
for (const line of lines) {
|
||||
if (!line || /^\s+$/.test(line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// LRC content
|
||||
const match = LrcTextParser.lyricLine_.exec(line);
|
||||
if (match) {
|
||||
const startTime = LrcTextParser.parseTime_(match[1]);
|
||||
// This time can be overwritten by a subsequent cue.
|
||||
// By default we add 2 seconds of duration.
|
||||
const endTime = time.segmentEnd ? time.segmentEnd : startTime + 2;
|
||||
const payload = match[2];
|
||||
const cue = new shaka.text.Cue(startTime, endTime, payload);
|
||||
|
||||
// Update previous
|
||||
if (prevCue) {
|
||||
prevCue.endTime = startTime;
|
||||
cues.push(prevCue);
|
||||
}
|
||||
prevCue = cue;
|
||||
continue;
|
||||
}
|
||||
shaka.log.warning('LrcTextParser encountered an unknown line.', line);
|
||||
}
|
||||
if (prevCue) {
|
||||
cues.push(prevCue);
|
||||
}
|
||||
|
||||
return cues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a LRC time from the given parser.
|
||||
*
|
||||
* @param {string} string
|
||||
* @return {number}
|
||||
* @private
|
||||
*/
|
||||
static parseTime_(string) {
|
||||
const LrcTextParser = shaka.text.LrcTextParser;
|
||||
const match = LrcTextParser.timeFormat_.exec(string);
|
||||
const minutes = parseInt(match[1], 10);
|
||||
const seconds = parseFloat(match[2].replace(',', '.'));
|
||||
return minutes * 60 + seconds;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @private {!RegExp}
|
||||
* @example [00:12.0]Text or [00:12.00]Text or [00:12.000]Text or
|
||||
* [00:12,0]Text or [00:12,00]Text or [00:12,000]Text
|
||||
*/
|
||||
shaka.text.LrcTextParser.lyricLine_ =
|
||||
/^\[(\d{1,2}:\d{1,2}(?:[.,]\d{1,3})?)\](.*)/;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @private {!RegExp}
|
||||
* @example 00:12.0 or 00:12.00 or 00:12.000 or
|
||||
* 00:12,0 or 00:12,00 or 00:12,000
|
||||
*/
|
||||
shaka.text.LrcTextParser.timeFormat_ =
|
||||
/^(\d+):(\d{1,2}(?:[.,]\d{1,3})?)$/;
|
||||
|
||||
shaka.text.TextEngine.registerParser(
|
||||
'application/x-subtitle-lrc', () => new shaka.text.LrcTextParser());
|
||||
@@ -1,91 +0,0 @@
|
||||
/*! @license
|
||||
* Shaka Player
|
||||
* Copyright 2016 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
goog.provide('shaka.text.SbvTextParser');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('shaka.text.Cue');
|
||||
goog.require('shaka.text.TextEngine');
|
||||
goog.require('shaka.util.Error');
|
||||
goog.require('shaka.util.StringUtils');
|
||||
goog.require('shaka.util.TextParser');
|
||||
|
||||
|
||||
/**
|
||||
* @implements {shaka.extern.TextParser}
|
||||
* @export
|
||||
*/
|
||||
shaka.text.SbvTextParser = class {
|
||||
/**
|
||||
* @override
|
||||
* @export
|
||||
*/
|
||||
parseInit(data) {
|
||||
goog.asserts.assert(false, 'SubViewer does not have init segments');
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @export
|
||||
*/
|
||||
setManifestType(manifestType) {
|
||||
// Unused.
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @export
|
||||
*/
|
||||
parseMedia(data, time) {
|
||||
const StringUtils = shaka.util.StringUtils;
|
||||
|
||||
// Get the input as a string.
|
||||
const strFromData = StringUtils.fromUTF8(data);
|
||||
// remove dos newlines
|
||||
let str = strFromData.replace(/\r+/g, '');
|
||||
// trim white space start and end
|
||||
str = str.trim();
|
||||
|
||||
/** @type {!Array<!shaka.text.Cue>} */
|
||||
const cues = [];
|
||||
|
||||
// Supports no cues
|
||||
if (str == '') {
|
||||
return cues;
|
||||
}
|
||||
|
||||
// get cues
|
||||
const blocklist = str.split('\n\n');
|
||||
for (const block of blocklist) {
|
||||
const lines = block.split('\n');
|
||||
// Parse the times.
|
||||
const parser = new shaka.util.TextParser(lines[0]);
|
||||
const start = parser.parseTime();
|
||||
const expect = parser.readRegex(/,/g);
|
||||
const end = parser.parseTime();
|
||||
|
||||
if (start == null || expect == null || end == null) {
|
||||
throw new shaka.util.Error(
|
||||
shaka.util.Error.Severity.CRITICAL,
|
||||
shaka.util.Error.Category.TEXT,
|
||||
shaka.util.Error.Code.INVALID_TEXT_CUE,
|
||||
'Could not parse cue time range in SubViewer');
|
||||
}
|
||||
|
||||
// Get the payload.
|
||||
const payload = lines.slice(1).join('\n').trim();
|
||||
|
||||
const cue = new shaka.text.Cue(start, end, payload);
|
||||
cues.push(cue);
|
||||
}
|
||||
|
||||
return cues;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
shaka.text.TextEngine.registerParser(
|
||||
'text/x-subviewer', () => new shaka.text.SbvTextParser());
|
||||
@@ -1,347 +0,0 @@
|
||||
/*! @license
|
||||
* Shaka Player
|
||||
* Copyright 2016 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// cspell:ignore AABBGGRR HAABBGGRR
|
||||
|
||||
goog.provide('shaka.text.SsaTextParser');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('shaka.log');
|
||||
goog.require('shaka.text.Cue');
|
||||
goog.require('shaka.text.TextEngine');
|
||||
goog.require('shaka.util.StringUtils');
|
||||
|
||||
|
||||
/**
|
||||
* Documentation: http://moodub.free.fr/video/ass-specs.doc
|
||||
* https://en.wikipedia.org/wiki/SubStation_Alpha
|
||||
* @implements {shaka.extern.TextParser}
|
||||
* @export
|
||||
*/
|
||||
shaka.text.SsaTextParser = class {
|
||||
/**
|
||||
* @override
|
||||
* @export
|
||||
*/
|
||||
parseInit(data) {
|
||||
goog.asserts.assert(false, 'SSA does not have init segments');
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @export
|
||||
*/
|
||||
setManifestType(manifestType) {
|
||||
// Unused.
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @export
|
||||
*/
|
||||
parseMedia(data, time) {
|
||||
const StringUtils = shaka.util.StringUtils;
|
||||
const SsaTextParser = shaka.text.SsaTextParser;
|
||||
|
||||
// Get the input as a string.
|
||||
const str = StringUtils.fromUTF8(data);
|
||||
|
||||
const section = {
|
||||
styles: '',
|
||||
events: '',
|
||||
};
|
||||
|
||||
let tag = null;
|
||||
let lines = null;
|
||||
const parts = str.split(/\r?\n\s*\r?\n/);
|
||||
for (const part of parts) {
|
||||
lines = part;
|
||||
// SSA content
|
||||
const match = SsaTextParser.ssaContent_.exec(part);
|
||||
if (match) {
|
||||
tag = match[1];
|
||||
lines = match[2];
|
||||
}
|
||||
if (tag == 'V4 Styles' || tag == 'V4+ Styles') {
|
||||
section.styles = lines;
|
||||
if (section.events) {
|
||||
section.styles += '\n' + lines;
|
||||
} else {
|
||||
section.styles = lines;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (tag == 'Events') {
|
||||
if (section.events) {
|
||||
section.events += '\n' + lines;
|
||||
} else {
|
||||
section.events = lines;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (tag == 'Script Info') {
|
||||
continue;
|
||||
}
|
||||
shaka.log.warning('SsaTextParser parser encountered an unknown part.',
|
||||
lines);
|
||||
}
|
||||
|
||||
// Process styles
|
||||
const styles = [];
|
||||
|
||||
// Used to be able to iterate over the style parameters.
|
||||
let styleColumns = null;
|
||||
|
||||
const styleLines = section.styles.split(/\r?\n/);
|
||||
for (const line of styleLines) {
|
||||
if (/^\s*;/.test(line)) {
|
||||
// Skip comment
|
||||
continue;
|
||||
}
|
||||
const lineParts = SsaTextParser.lineParts_.exec(line);
|
||||
if (lineParts) {
|
||||
const name = lineParts[1].trim();
|
||||
const value = lineParts[2].trim();
|
||||
if (name == 'Format') {
|
||||
styleColumns = value.split(SsaTextParser.valuesFormat_);
|
||||
continue;
|
||||
}
|
||||
if (name == 'Style') {
|
||||
const values = value.split(SsaTextParser.valuesFormat_);
|
||||
const style = {};
|
||||
for (let c = 0; c < styleColumns.length && c < values.length; c++) {
|
||||
style[styleColumns[c]] = values[c];
|
||||
}
|
||||
styles.push(style);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process cues
|
||||
/** @type {!Array<!shaka.text.Cue>} */
|
||||
const cues = [];
|
||||
|
||||
// Used to be able to iterate over the event parameters.
|
||||
let eventColumns = null;
|
||||
|
||||
const eventLines = section.events.split(/\r?\n/);
|
||||
for (const line of eventLines) {
|
||||
if (/^\s*;/.test(line)) {
|
||||
// Skip comment
|
||||
continue;
|
||||
}
|
||||
const lineParts = SsaTextParser.lineParts_.exec(line);
|
||||
if (lineParts) {
|
||||
const name = lineParts[1].trim();
|
||||
const value = lineParts[2].trim();
|
||||
if (name == 'Format') {
|
||||
eventColumns = value.split(SsaTextParser.valuesFormat_);
|
||||
continue;
|
||||
}
|
||||
if (name == 'Dialogue') {
|
||||
const values = value.split(SsaTextParser.valuesFormat_);
|
||||
const data = {};
|
||||
for (let c = 0; c < eventColumns.length && c < values.length; c++) {
|
||||
data[eventColumns[c]] = values[c];
|
||||
}
|
||||
|
||||
const startTime = SsaTextParser.parseTime_(data['Start']);
|
||||
const endTime = SsaTextParser.parseTime_(data['End']);
|
||||
|
||||
// Note: Normally, you should take the "Text" field, but if it
|
||||
// has a comma, it fails.
|
||||
const payload = values.slice(eventColumns.length - 1).join(',')
|
||||
.replace(/\\N/g, '\n') // '\n' for new line
|
||||
.replace(/\{[^}]+\}/g, ''); // {\pos(400,570)}
|
||||
|
||||
const cue = new shaka.text.Cue(startTime, endTime, payload);
|
||||
|
||||
const styleName = data['Style'];
|
||||
const styleData = styles.find((s) => s['Name'] == styleName);
|
||||
if (styleData) {
|
||||
SsaTextParser.addStyle_(cue, styleData);
|
||||
}
|
||||
cues.push(cue);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds applicable style properties to a cue.
|
||||
*
|
||||
* @param {shaka.text.Cue} cue
|
||||
* @param {Object} style
|
||||
* @private
|
||||
*/
|
||||
static addStyle_(cue, style) {
|
||||
const Cue = shaka.text.Cue;
|
||||
const SsaTextParser = shaka.text.SsaTextParser;
|
||||
const fontFamily = style['Fontname'];
|
||||
if (fontFamily) {
|
||||
cue.fontFamily = fontFamily;
|
||||
}
|
||||
const fontSize = style['Fontsize'];
|
||||
if (fontSize) {
|
||||
cue.fontSize = fontSize + 'px';
|
||||
}
|
||||
const color = style['PrimaryColour'];
|
||||
if (color) {
|
||||
const ccsColor = SsaTextParser.parseSsaColor_(color);
|
||||
if (ccsColor) {
|
||||
cue.color = ccsColor;
|
||||
}
|
||||
}
|
||||
const backgroundColor = style['BackColour'];
|
||||
if (backgroundColor) {
|
||||
const cssBackgroundColor = SsaTextParser.parseSsaColor_(backgroundColor);
|
||||
if (cssBackgroundColor) {
|
||||
cue.backgroundColor = cssBackgroundColor;
|
||||
}
|
||||
}
|
||||
const bold = style['Bold'];
|
||||
if (bold) {
|
||||
cue.fontWeight = Cue.fontWeight.BOLD;
|
||||
}
|
||||
const italic = style['Italic'];
|
||||
if (italic) {
|
||||
cue.fontStyle = Cue.fontStyle.ITALIC;
|
||||
}
|
||||
const underline = style['Underline'];
|
||||
if (underline) {
|
||||
cue.textDecoration.push(Cue.textDecoration.UNDERLINE);
|
||||
}
|
||||
const letterSpacing = style['Spacing'];
|
||||
if (letterSpacing) {
|
||||
cue.letterSpacing = letterSpacing + 'px';
|
||||
}
|
||||
const alignment = style['Alignment'];
|
||||
if (alignment) {
|
||||
const alignmentInt = parseInt(alignment, 10);
|
||||
switch (alignmentInt) {
|
||||
case 1:
|
||||
cue.displayAlign = Cue.displayAlign.AFTER;
|
||||
cue.textAlign = Cue.textAlign.START;
|
||||
break;
|
||||
case 2:
|
||||
cue.displayAlign = Cue.displayAlign.AFTER;
|
||||
cue.textAlign = Cue.textAlign.CENTER;
|
||||
break;
|
||||
case 3:
|
||||
cue.displayAlign = Cue.displayAlign.AFTER;
|
||||
cue.textAlign = Cue.textAlign.END;
|
||||
break;
|
||||
case 5:
|
||||
cue.displayAlign = Cue.displayAlign.BEFORE;
|
||||
cue.textAlign = Cue.textAlign.START;
|
||||
break;
|
||||
case 6:
|
||||
cue.displayAlign = Cue.displayAlign.BEFORE;
|
||||
cue.textAlign = Cue.textAlign.CENTER;
|
||||
break;
|
||||
case 7:
|
||||
cue.displayAlign = Cue.displayAlign.BEFORE;
|
||||
cue.textAlign = Cue.textAlign.END;
|
||||
break;
|
||||
case 9:
|
||||
cue.displayAlign = Cue.displayAlign.CENTER;
|
||||
cue.textAlign = Cue.textAlign.START;
|
||||
break;
|
||||
case 10:
|
||||
cue.displayAlign = Cue.displayAlign.CENTER;
|
||||
cue.textAlign = Cue.textAlign.CENTER;
|
||||
break;
|
||||
case 11:
|
||||
cue.displayAlign = Cue.displayAlign.CENTER;
|
||||
cue.textAlign = Cue.textAlign.END;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const opacity = style['AlphaLevel'];
|
||||
if (opacity) {
|
||||
cue.opacity = parseFloat(opacity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a SSA color .
|
||||
*
|
||||
* @param {string} colorString
|
||||
* @return {?string}
|
||||
* @private
|
||||
*/
|
||||
static parseSsaColor_(colorString) {
|
||||
// The SSA V4+ color can be represented in hex (&HAABBGGRR) or in decimal
|
||||
// format (byte order AABBGGRR) and in both cases the alpha channel's
|
||||
// value needs to be inverted as in case of SSA the 0xFF alpha value means
|
||||
// transparent and 0x00 means opaque
|
||||
/** @type {number} */
|
||||
const abgr = parseInt(colorString.replace('&H', ''), 16);
|
||||
if (abgr >= 0) {
|
||||
const a = ((abgr >> 24) & 0xFF) ^ 0xFF; // Flip alpha.
|
||||
const alpha = a / 255;
|
||||
const b = (abgr >> 16) & 0xFF;
|
||||
const g = (abgr >> 8) & 0xFF;
|
||||
const r = abgr & 0xff;
|
||||
return 'rgba(' + r + ',' + g + ',' + b + ',' + alpha + ')';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a SSA time from the given parser.
|
||||
*
|
||||
* @param {string} string
|
||||
* @return {number}
|
||||
* @private
|
||||
*/
|
||||
static parseTime_(string) {
|
||||
const SsaTextParser = shaka.text.SsaTextParser;
|
||||
const match = SsaTextParser.timeFormat_.exec(string);
|
||||
const hours = match[1] ? parseInt(match[1].replace(':', ''), 10) : 0;
|
||||
const minutes = parseInt(match[2], 10);
|
||||
const seconds = parseFloat(match[3]);
|
||||
return hours * 3600 + minutes * 60 + seconds;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @private {!RegExp}
|
||||
* @example [V4 Styles]\nFormat: Name\nStyle: DefaultVCD
|
||||
*/
|
||||
shaka.text.SsaTextParser.ssaContent_ =
|
||||
/^\s*\[([^\]]+)\]\r?\n([\s\S]*)/;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @private {!RegExp}
|
||||
* @example Style: DefaultVCD,...
|
||||
*/
|
||||
shaka.text.SsaTextParser.lineParts_ =
|
||||
/^\s*([^:]+):\s*(.*)/;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @private {!RegExp}
|
||||
* @example Style: DefaultVCD,...
|
||||
*/
|
||||
shaka.text.SsaTextParser.valuesFormat_ = /\s*,\s*/;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @private {!RegExp}
|
||||
* @example 0:00:01.1 or 0:00:01.18 or 0:00:01.180
|
||||
*/
|
||||
shaka.text.SsaTextParser.timeFormat_ =
|
||||
/^(\d+:)?(\d{1,2}):(\d{1,2}(?:[.]\d{1,3})?)?$/;
|
||||
|
||||
shaka.text.TextEngine.registerParser(
|
||||
'text/x-ssa', () => new shaka.text.SsaTextParser());
|
||||
@@ -61,14 +61,11 @@ goog.require('shaka.polyfill.VideoPlayPromise');
|
||||
goog.require('shaka.polyfill.VideoPlaybackQuality');
|
||||
goog.require('shaka.polyfill');
|
||||
goog.require('shaka.text.Cue');
|
||||
goog.require('shaka.text.LrcTextParser');
|
||||
goog.require('shaka.text.Mp4TtmlParser');
|
||||
goog.require('shaka.text.Mp4VttParser');
|
||||
goog.require('shaka.text.TextEngine');
|
||||
goog.require('shaka.text.SbvTextParser');
|
||||
goog.require('shaka.text.SpeechToText');
|
||||
goog.require('shaka.text.SrtTextParser');
|
||||
goog.require('shaka.text.SsaTextParser');
|
||||
goog.require('shaka.text.TtmlTextParser');
|
||||
goog.require('shaka.text.VttTextParser');
|
||||
goog.require('shaka.text.WebVttGenerator');
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/*! @license
|
||||
* Shaka Player
|
||||
* Copyright 2016 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
describe('LrcTextParser', () => {
|
||||
it('supports no cues', () => {
|
||||
verifyHelper([],
|
||||
'',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('handles a blank line at the start of the file', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{startTime: 0, endTime: 2, payload: 'Test'},
|
||||
],
|
||||
'\n\n' +
|
||||
'[00:00.00]Test',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('handles a blank line at the end of the file', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{startTime: 0, endTime: 2, payload: 'Test'},
|
||||
],
|
||||
'[00:00.00]Test' +
|
||||
'\n\n',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('handles no blank line at the end of the file', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{startTime: 0, endTime: 2, payload: 'Test'},
|
||||
],
|
||||
'[00:00.00]Test',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('supports multiple cues', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{startTime: 0, endTime: 10, payload: 'Test'},
|
||||
{startTime: 10, endTime: 20, payload: 'Test2'},
|
||||
{startTime: 20, endTime: 22, payload: 'Test3'},
|
||||
],
|
||||
'[00:00.00]Test\n' +
|
||||
'[00:10.00]Test2\n' +
|
||||
'[00:20.00]Test3',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('supports different time formats', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{startTime: 0.1, endTime: 10.001, payload: 'Test'},
|
||||
{startTime: 10.001, endTime: 20.02, payload: 'Test2'},
|
||||
{startTime: 20.02, endTime: 30.1, payload: 'Test3'},
|
||||
{startTime: 30.1, endTime: 40.001, payload: 'Test4'},
|
||||
{startTime: 40.001, endTime: 50.02, payload: 'Test5'},
|
||||
{startTime: 50.02, endTime: 52.02, payload: 'Test6'},
|
||||
],
|
||||
'[00:00.1]Test\n' +
|
||||
'[00:10.001]Test2\n' +
|
||||
'[00:20.02]Test3\n' +
|
||||
'[00:30,1]Test4\n' +
|
||||
'[00:40,001]Test5\n' +
|
||||
'[00:50,02]Test6',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {!Array} cues
|
||||
* @param {string} text
|
||||
* @param {shaka.extern.TextParser.TimeContext} time
|
||||
*/
|
||||
function verifyHelper(cues, text, time) {
|
||||
const BufferUtils = shaka.util.BufferUtils;
|
||||
const StringUtils = shaka.util.StringUtils;
|
||||
|
||||
const data = BufferUtils.toUint8(StringUtils.toUTF8(text));
|
||||
|
||||
const parser = new shaka.text.LrcTextParser();
|
||||
const result = parser.parseMedia(data, time);
|
||||
|
||||
const expected = cues.map((cue) => {
|
||||
if (cue.nestedCues) {
|
||||
cue.nestedCues = cue.nestedCues.map(
|
||||
(nestedCue) => jasmine.objectContaining(nestedCue));
|
||||
}
|
||||
return jasmine.objectContaining(cue);
|
||||
});
|
||||
expect(result).toEqual(expected);
|
||||
}
|
||||
});
|
||||
@@ -1,81 +0,0 @@
|
||||
/*! @license
|
||||
* Shaka Player
|
||||
* Copyright 2016 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
describe('SbvTextParser', () => {
|
||||
it('supports no cues', () => {
|
||||
verifyHelper([],
|
||||
'',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('handles a blank line at the end of the file', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{startTime: 20, endTime: 40, payload: 'Test'},
|
||||
],
|
||||
'0:00:20.000,0:00:40.000\n' +
|
||||
'Test\n\n',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('handles no blank line at the end of the file', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{startTime: 20, endTime: 40, payload: 'Test'},
|
||||
],
|
||||
'0:00:20.000,0:00:40.000\n' +
|
||||
'Test\n',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('handles no newline after the final text payload', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{startTime: 20, endTime: 40, payload: 'Test'},
|
||||
],
|
||||
'0:00:20.000,0:00:40.000\n' +
|
||||
'Test',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('supports multiple cues', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{startTime: 20, endTime: 40, payload: 'Test'},
|
||||
{startTime: 40, endTime: 50, payload: 'Test2'},
|
||||
],
|
||||
'0:00:20.000,0:00:40.000\n' +
|
||||
'Test\n\n' +
|
||||
'0:00:40.000,0:00:50.000\n' +
|
||||
'Test2',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {!Array} cues
|
||||
* @param {string} text
|
||||
* @param {shaka.extern.TextParser.TimeContext} time
|
||||
*/
|
||||
function verifyHelper(cues, text, time) {
|
||||
const BufferUtils = shaka.util.BufferUtils;
|
||||
const StringUtils = shaka.util.StringUtils;
|
||||
|
||||
const data = BufferUtils.toUint8(StringUtils.toUTF8(text));
|
||||
|
||||
const parser = new shaka.text.SbvTextParser();
|
||||
const result = parser.parseMedia(data, time);
|
||||
|
||||
const expected = cues.map((cue) => {
|
||||
if (cue.nestedCues) {
|
||||
cue.nestedCues = cue.nestedCues.map(
|
||||
(nestedCue) => jasmine.objectContaining(nestedCue),
|
||||
);
|
||||
}
|
||||
return jasmine.objectContaining(cue);
|
||||
});
|
||||
expect(result).toEqual(expected);
|
||||
}
|
||||
});
|
||||
@@ -1,384 +0,0 @@
|
||||
/*! @license
|
||||
* Shaka Player
|
||||
* Copyright 2016 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// cspell:ignore FCFC
|
||||
|
||||
describe('SsaTextParser', () => {
|
||||
const Cue = shaka.text.Cue;
|
||||
|
||||
it('supports no cues', () => {
|
||||
verifyHelper([],
|
||||
'',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('handles a blank line at the start of the file', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{startTime: 0, endTime: 2, payload: 'Test'},
|
||||
],
|
||||
'\n\n' +
|
||||
'[Script Info]\n' +
|
||||
'Title: Foo\n\n' +
|
||||
'[V4+ Styles]\n' +
|
||||
'Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, ' +
|
||||
'OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ' +
|
||||
'ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, ' +
|
||||
'Alignment, MarginL, MarginR, MarginV, Encoding\n' +
|
||||
'Style: DefaultVCD, Arial,28,&H00B4FCFC,&H00B4FCFC,&H00000008,' +
|
||||
'&H80000008,-1,0,0,0,100,100,0.00,0.00,1,1.00,2.00,2,30,30,30,0\n\n' +
|
||||
'[Events]\n' +
|
||||
'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, ' +
|
||||
'Effect, Text\n' +
|
||||
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('handles a blank line at the end of the file', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{startTime: 0, endTime: 2, payload: 'Test'},
|
||||
],
|
||||
'[Script Info]\n' +
|
||||
'Title: Foo\n\n' +
|
||||
'[V4+ Styles]\n' +
|
||||
'Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, ' +
|
||||
'OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ' +
|
||||
'ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, ' +
|
||||
'Alignment, MarginL, MarginR, MarginV, Encoding\n' +
|
||||
'Style: DefaultVCD, Arial,28,&H00B4FCFC,&H00B4FCFC,&H00000008,' +
|
||||
'&H80000008,-1,0,0,0,100,100,0.00,0.00,1,1.00,2.00,2,30,30,30,0\n\n' +
|
||||
'[Events]\n' +
|
||||
'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, ' +
|
||||
'Effect, Text\n' +
|
||||
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test' +
|
||||
'\n\n',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('handles no blank line at the end of the file', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{startTime: 0, endTime: 2, payload: 'Test'},
|
||||
],
|
||||
'[Script Info]\n' +
|
||||
'Title: Foo\n\n' +
|
||||
'[V4+ Styles]\n' +
|
||||
'Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, ' +
|
||||
'OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ' +
|
||||
'ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, ' +
|
||||
'Alignment, MarginL, MarginR, MarginV, Encoding\n' +
|
||||
'Style: DefaultVCD, Arial,28,&H00B4FCFC,&H00B4FCFC,&H00000008,' +
|
||||
'&H80000008,-1,0,0,0,100,100,0.00,0.00,1,1.00,2.00,2,30,30,30,0\n\n' +
|
||||
'[Events]\n' +
|
||||
'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, ' +
|
||||
'Effect, Text\n' +
|
||||
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('supports no styles', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{startTime: 0, endTime: 2, payload: 'Test'},
|
||||
],
|
||||
'[Script Info]\n' +
|
||||
'Title: Foo\n\n' +
|
||||
'[Events]\n' +
|
||||
'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, ' +
|
||||
'Effect, Text\n' +
|
||||
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('support no script info', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{startTime: 0, endTime: 2, payload: 'Test'},
|
||||
],
|
||||
'[V4+ Styles]\n' +
|
||||
'Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, ' +
|
||||
'OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ' +
|
||||
'ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, ' +
|
||||
'Alignment, MarginL, MarginR, MarginV, Encoding\n' +
|
||||
'Style: DefaultVCD, Arial,28,&H00B4FCFC,&H00B4FCFC,&H00000008,' +
|
||||
'&H80000008,-1,0,0,0,100,100,0.00,0.00,1,1.00,2.00,2,30,30,30,0\n\n' +
|
||||
'[Events]\n' +
|
||||
'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, ' +
|
||||
'Effect, Text\n' +
|
||||
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('supports only events', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{startTime: 0, endTime: 2, payload: 'Test'},
|
||||
],
|
||||
'[Events]\n' +
|
||||
'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, ' +
|
||||
'Effect, Text\n' +
|
||||
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('supports text with commas', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{startTime: 0, endTime: 2, payload: 'Test,1,Test2'},
|
||||
],
|
||||
'[Events]\n' +
|
||||
'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, ' +
|
||||
'Effect, Text\n' +
|
||||
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test,1,Test2',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('supports different time formats', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{startTime: 0, endTime: 2, payload: 'Test'},
|
||||
{startTime: 4.5, endTime: 6.1, payload: 'Test2'},
|
||||
{startTime: 8.01, endTime: 10.001, payload: 'Test3'},
|
||||
],
|
||||
'[Events]\n' +
|
||||
'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, ' +
|
||||
'Effect, Text\n' +
|
||||
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test\n' +
|
||||
'Dialogue: 0,0:00:04.5,0:00:06.1,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test2\n' +
|
||||
'Dialogue: 0,0:00:08.01,0:00:10.001,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test3',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('supports multiple cues', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{startTime: 0, endTime: 2, payload: 'Test'},
|
||||
{startTime: 4.5, endTime: 6.1, payload: 'Test2'},
|
||||
{startTime: 8.01, endTime: 10.1, payload: 'Test3'},
|
||||
],
|
||||
'[Events]\n' +
|
||||
'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, ' +
|
||||
'Effect, Text\n' +
|
||||
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test\n\n' +
|
||||
'Dialogue: 0,0:00:04.50,0:00:06.10,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test2\n\n' +
|
||||
'Dialogue: 0,0:00:08.01,0:00:10.10,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test3',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('supports fontFamily style', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{
|
||||
startTime: 0,
|
||||
endTime: 2,
|
||||
payload: 'Test',
|
||||
fontFamily: 'Arial',
|
||||
},
|
||||
],
|
||||
'[V4+ Styles]\n' +
|
||||
'Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, ' +
|
||||
'OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ' +
|
||||
'ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, ' +
|
||||
'Alignment, MarginL, MarginR, MarginV, Encoding\n' +
|
||||
'Style: DefaultVCD, Arial,28,&H00B4FCFC,&H00B4FCFC,&H00000008,' +
|
||||
'&H80000008,-1,0,0,0,100,100,0.00,0.00,1,1.00,2.00,2,30,30,30,0\n\n' +
|
||||
'[Events]\n' +
|
||||
'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, ' +
|
||||
'Effect, Text\n' +
|
||||
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('supports color & backgroundColor style', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{
|
||||
startTime: 0,
|
||||
endTime: 2,
|
||||
payload: 'Test',
|
||||
color: 'rgba(252,252,180,1)',
|
||||
backgroundColor: 'rgba(8,0,0,0)',
|
||||
},
|
||||
],
|
||||
'[V4+ Styles]\n' +
|
||||
'Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, ' +
|
||||
'OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ' +
|
||||
'ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, ' +
|
||||
'Alignment, MarginL, MarginR, MarginV, Encoding\n' +
|
||||
'Style: DefaultVCD, Arial,28,&H00B4FCFC,&H00B4FCFC,&H00000008,' +
|
||||
'&HFF000008,-1,0,0,0,100,100,0.00,0.00,1,1.00,2.00,2,30,30,30,0\n\n' +
|
||||
'[Events]\n' +
|
||||
'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, ' +
|
||||
'Effect, Text\n' +
|
||||
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('supports bold style', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{
|
||||
startTime: 0,
|
||||
endTime: 2,
|
||||
payload: 'Test',
|
||||
fontWeight: Cue.fontWeight.BOLD,
|
||||
},
|
||||
],
|
||||
'[V4+ Styles]\n' +
|
||||
'Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, ' +
|
||||
'OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ' +
|
||||
'ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, ' +
|
||||
'Alignment, MarginL, MarginR, MarginV, Encoding\n' +
|
||||
'Style: DefaultVCD, Arial,28,&H00B4FCFC,&H00B4FCFC,&H00000008,' +
|
||||
'&H80000008,-1,1,0,0,100,100,0.00,0.00,1,1.00,2.00,2,30,30,30,0\n\n' +
|
||||
'[Events]\n' +
|
||||
'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, ' +
|
||||
'Effect, Text\n' +
|
||||
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('supports italic style', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{
|
||||
startTime: 0,
|
||||
endTime: 2,
|
||||
payload: 'Test',
|
||||
fontStyle: Cue.fontStyle.ITALIC,
|
||||
},
|
||||
],
|
||||
'[V4+ Styles]\n' +
|
||||
'Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, ' +
|
||||
'OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ' +
|
||||
'ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, ' +
|
||||
'Alignment, MarginL, MarginR, MarginV, Encoding\n' +
|
||||
'Style: DefaultVCD, Arial,28,&H00B4FCFC,&H00B4FCFC,&H00000008,' +
|
||||
'&H80000008,-1,0,1,0,100,100,0.00,0.00,1,1.00,2.00,2,30,30,30,0\n\n' +
|
||||
'[Events]\n' +
|
||||
'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, ' +
|
||||
'Effect, Text\n' +
|
||||
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('supports underline style', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{
|
||||
startTime: 0,
|
||||
endTime: 2,
|
||||
payload: 'Test',
|
||||
textDecoration: [Cue.textDecoration.UNDERLINE],
|
||||
},
|
||||
],
|
||||
'[V4+ Styles]\n' +
|
||||
'Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, ' +
|
||||
'OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ' +
|
||||
'ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, ' +
|
||||
'Alignment, MarginL, MarginR, MarginV, Encoding\n' +
|
||||
'Style: DefaultVCD, Arial,28,&H00B4FCFC,&H00B4FCFC,&H00000008,' +
|
||||
'&H80000008,-1,0,0,1,100,100,0.00,0.00,1,1.00,2.00,2,30,30,30,0\n\n' +
|
||||
'[Events]\n' +
|
||||
'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, ' +
|
||||
'Effect, Text\n' +
|
||||
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('supports letterSpacing style', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{
|
||||
startTime: 0,
|
||||
endTime: 2,
|
||||
payload: 'Test',
|
||||
letterSpacing: '2px',
|
||||
},
|
||||
],
|
||||
'[V4+ Styles]\n' +
|
||||
'Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, ' +
|
||||
'OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ' +
|
||||
'ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, ' +
|
||||
'Alignment, MarginL, MarginR, MarginV, Encoding\n' +
|
||||
'Style: DefaultVCD, Arial,28,&H00B4FCFC,&H00B4FCFC,&H00000008,' +
|
||||
'&H80000008,-1,0,0,0,100,100,2,0.00,1,1.00,2.00,2,30,30,30,0\n\n' +
|
||||
'[Events]\n' +
|
||||
'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, ' +
|
||||
'Effect, Text\n' +
|
||||
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
it('supports V4 style', () => {
|
||||
verifyHelper(
|
||||
[
|
||||
{
|
||||
startTime: 0,
|
||||
endTime: 2,
|
||||
payload: 'Test',
|
||||
fontFamily: 'Arial',
|
||||
},
|
||||
],
|
||||
'[V4 Styles]\n' +
|
||||
'Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, ' +
|
||||
'TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, ' +
|
||||
'Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, '+
|
||||
'Encoding\n' +
|
||||
'Style: DefaultVCD, Arial,28,11861244,11861244,11861244,' +
|
||||
'-2147483640,-1,0,1,1,2,2,30,30,30,0,0\n\n' +
|
||||
'[Events]\n' +
|
||||
'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, ' +
|
||||
'Effect, Text\n' +
|
||||
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
|
||||
',,{\\pos(400,570)}Test',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {!Array} cues
|
||||
* @param {string} text
|
||||
* @param {shaka.extern.TextParser.TimeContext} time
|
||||
*/
|
||||
function verifyHelper(cues, text, time) {
|
||||
const BufferUtils = shaka.util.BufferUtils;
|
||||
const StringUtils = shaka.util.StringUtils;
|
||||
|
||||
const data = BufferUtils.toUint8(StringUtils.toUTF8(text));
|
||||
|
||||
const parser = new shaka.text.SsaTextParser();
|
||||
const result = parser.parseMedia(data, time);
|
||||
|
||||
const expected = cues.map((cue) => {
|
||||
if (cue.nestedCues) {
|
||||
cue.nestedCues = cue.nestedCues.map(
|
||||
(nestedCue) => jasmine.objectContaining(nestedCue));
|
||||
}
|
||||
return jasmine.objectContaining(cue);
|
||||
});
|
||||
expect(result).toEqual(expected);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user