Skip to content

Commit

Permalink
fix(WebVTT): Fix voices with styles and support to multiple styles (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
avelad authored and joeyparrish committed Jan 30, 2023
1 parent 3fa6ff0 commit 355a59f
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 84 deletions.
184 changes: 103 additions & 81 deletions lib/text/vtt_text_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,96 +221,113 @@ shaka.text.VttTextParser = class {
return;
}

if (!text[1].includes('::cue')) {
return;
}
let styleSelector = 'global';
// Look for what is within parentisesis. For example:
// <code>:: cue (b) {</code>, what we are looking for is <code>b</code>
const selector = text[1].match(/\((.*)\)/);
if (selector) {
styleSelector = selector.pop();
/** @type {!Array.<!Array.<string>>} */
const styleBlocks = [];
let lastBlockIndex = -1;
for (let i = 1; i < text.length; i++) {
if (text[i].includes('::cue')) {
styleBlocks.push([]);
lastBlockIndex = styleBlocks.length - 1;
}
if (lastBlockIndex == -1) {
continue;
}
styleBlocks[lastBlockIndex].push(text[i]);
if (text[i].includes('}')) {
lastBlockIndex = -1;
}
}

// We start at 2 to avoid '::cue' and end earlier to avoid '}'
let propertyLines = text.slice(2, -1);
if (text[1].includes('}')) {
const payload = /\{(.*?)\}/.exec(text[1]);
if (payload) {
propertyLines = payload[1].split(';');
for (const styleBlock of styleBlocks) {
let styleSelector = 'global';
// Look for what is within parentheses. For example:
// <code>:: cue (b) {</code>, what we are looking for is <code>b</code>
const selector = styleBlock[0].match(/\((.*)\)/);
if (selector) {
styleSelector = selector.pop();
}
}

const cue = new shaka.text.Cue(0, 0, '');
let validStyle = false;
for (let i = 0; i < propertyLines.length; i++) {
// We look for CSS properties. As a general rule they are separated by
// <code>:</code>. Eg: <code>color: red;</code>
const lineParts = /^\s*([^:]+):\s*(.*)/.exec(propertyLines[i]);
if (lineParts) {
const name = lineParts[1].trim();
const value = lineParts[2].trim().replace(';', '');
switch (name) {
case 'background-color':
validStyle = true;
cue.backgroundColor = value;
break;
case 'color':
validStyle = true;
cue.color = value;
break;
case 'font-family':
validStyle = true;
cue.fontFamily = value;
break;
case 'font-size':
validStyle = true;
cue.fontSize = value;
break;
case 'font-weight':
if (parseInt(value, 10) >= 700) {
// We start at 1 to avoid '::cue' and end earlier to avoid '}'
let propertyLines = styleBlock.slice(1, -1);
if (styleBlock[0].includes('}')) {
const payload = /\{(.*?)\}/.exec(styleBlock[0]);
if (payload) {
propertyLines = payload[1].split(';');
}
}

const cue = new shaka.text.Cue(0, 0, '');
let validStyle = false;
for (let i = 0; i < propertyLines.length; i++) {
// We look for CSS properties. As a general rule they are separated by
// <code>:</code>. Eg: <code>color: red;</code>
const lineParts = /^\s*([^:]+):\s*(.*)/.exec(propertyLines[i]);
if (lineParts) {
const name = lineParts[1].trim();
const value = lineParts[2].trim().replace(';', '');
switch (name) {
case 'background-color':
case 'background':
validStyle = true;
cue.fontWeight = shaka.text.Cue.fontWeight.BOLD;
}
break;
case 'font-style':
switch (value) {
case 'normal':
validStyle = true;
cue.fontStyle = shaka.text.Cue.fontStyle.NORMAL;
break;
case 'italic':
validStyle = true;
cue.fontStyle = shaka.text.Cue.fontStyle.ITALIC;
break;
case 'oblique':
cue.backgroundColor = value;
break;
case 'color':
validStyle = true;
cue.color = value;
break;
case 'font-family':
validStyle = true;
cue.fontFamily = value;
break;
case 'font-size':
validStyle = true;
cue.fontSize = value;
break;
case 'font-weight':
if (parseInt(value, 10) >= 700 || value == 'bold') {
validStyle = true;
cue.fontStyle = shaka.text.Cue.fontStyle.OBLIQUE;
break;
}
break;
case 'opacity':
validStyle = true;
cue.opacity = parseFloat(value);
break;
case 'text-shadow':
validStyle = true;
cue.textShadow = value;
break;
case 'white-space':
validStyle = true;
cue.wrapLine = value != 'noWrap';
break;
default:
shaka.log.warning('VTT parser encountered an unsupported style: ',
lineParts);
break;
cue.fontWeight = shaka.text.Cue.fontWeight.BOLD;
}
break;
case 'font-style':
switch (value) {
case 'normal':
validStyle = true;
cue.fontStyle = shaka.text.Cue.fontStyle.NORMAL;
break;
case 'italic':
validStyle = true;
cue.fontStyle = shaka.text.Cue.fontStyle.ITALIC;
break;
case 'oblique':
validStyle = true;
cue.fontStyle = shaka.text.Cue.fontStyle.OBLIQUE;
break;
}
break;
case 'opacity':
validStyle = true;
cue.opacity = parseFloat(value);
break;
case 'text-shadow':
validStyle = true;
cue.textShadow = value;
break;
case 'white-space':
validStyle = true;
cue.wrapLine = value != 'noWrap';
break;
default:
shaka.log.warning('VTT parser encountered an unsupported style: ',
lineParts);
break;
}
}
}
}

if (validStyle) {
styles.set(styleSelector, cue);
if (validStyle) {
styles.set(styleSelector, cue);
}
}
}

Expand Down Expand Up @@ -674,6 +691,11 @@ shaka.text.VttTextParser = class {
if (styleTag.startsWith('.voice-')) {
const voice = styleTag.split('-').pop();
styleTag = `v[voice="${voice}"]`;
// The specification allows to have quotes and not, so we check to
// see which one is being used.
if (!styles.has(styleTag)) {
styleTag = `v[voice=${voice}]`;
}
}
if (styles.has(styleTag)) {
VttTextParser.mergeStyle_(nestedCue, styles.get(styleTag));
Expand Down
8 changes: 5 additions & 3 deletions test/text/vtt_text_parser_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -1099,7 +1099,7 @@ describe('VttTextParser', () => {
startTime: 40,
endTime: 50,
payload: 'Test',
color: 'cyan',
color: 'red',
},
{
startTime: 40,
Expand All @@ -1111,11 +1111,13 @@ describe('VttTextParser', () => {
},
],
'WEBVTT\n\n' +
'STYLE\n::cue(v[voice="Shaka"]) { color: cyan; }\n\n' +
'STYLE\n' +
'::cue(v[voice="Shaka"]) { color: cyan; }\n' +
'::cue(v[voice=ShakaBis]) { color: red; }\n\n' +
'00:00:20.000 --> 00:00:40.000\n' +
'<v Shaka>Test\n\n' +
'00:00:40.000 --> 00:00:50.000\n' +
'<v Shaka>Test</v><i>2</i>',
'<v ShakaBis>Test</v><i>2</i>',
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});

Expand Down

0 comments on commit 355a59f

Please sign in to comment.