52308f18 by brandonocasey

fixing code review issues

1 parent a2a34318
......@@ -34,5 +34,5 @@ dist-test/
docs/api/
es5/
tmp
test/data/manifests.js
test/data/expected.js
test/test-manifests.js
test/test-expected.js
......
var fs = require('fs');
var path = require('path');
var shelljs = require('shelljs');
var basePath = path.resolve(__dirname, '..');
var testDataDir = path.join(basePath,'test', 'data');
var testDataDir = path.join(basePath,'test');
var manifestDir = path.join(basePath, 'utils', 'manifest');
var manifestFilepath = path.join(testDataDir, 'manifests.js');
var expectedFilepath = path.join(testDataDir, 'expected.js');
var manifestFilepath = path.join(testDataDir, 'test-manifests.js');
var expectedFilepath = path.join(testDataDir, 'test-expected.js');
var build = function() {
var manifests = 'export default {\n';
var expected = 'export default {\n';
shelljs.mkdir('-p', testDataDir);
var files = fs.readdirSync(manifestDir);
while (files.length > 0) {
var file = path.resolve(manifestDir, files.shift());
......
......@@ -35,29 +35,6 @@ export class LineStream extends Stream {
}
}
/**
* A line-level M3U8 parser event stream. It expects to receive input one
* line at a time and performs a context-free parse of its contents. A stream
* interpretation of a manifest can be useful if the manifest is expected to
* be too large to fit comfortably into memory or the entirety of the input
* is not immediately available. Otherwise, it's probably much easier to work
* with a regular `Parser` object.
*
* Produces `data` events with an object that captures the parser's
* interpretation of the input. That object has a property `tag` that is one
* of `uri`, `comment`, or `tag`. URIs only have a single additional
* property, `line`, which captures the entirety of the input without
* interpretation. Comments similarly have a single additional property
* `text` which is the input without the leading `#`.
*
* Tags always have a property `tagType` which is the lower-cased version of
* the M3U8 directive without the `#EXT` or `#EXT-X-` prefix. For instance,
* `#EXT-X-MEDIA-SEQUENCE` becomes `media-sequence` when parsed. Unrecognized
* tags are given the tag type `unknown` and a single additional property
* `data` with the remainder of the input.
*/
// "forgiving" attribute list psuedo-grammar:
// attributes -> keyvalue (',' keyvalue)*
// keyvalue -> key '=' value
......@@ -95,6 +72,27 @@ const parseAttributes = function(attributes) {
return result;
};
/**
* A line-level M3U8 parser event stream. It expects to receive input one
* line at a time and performs a context-free parse of its contents. A stream
* interpretation of a manifest can be useful if the manifest is expected to
* be too large to fit comfortably into memory or the entirety of the input
* is not immediately available. Otherwise, it's probably much easier to work
* with a regular `Parser` object.
*
* Produces `data` events with an object that captures the parser's
* interpretation of the input. That object has a property `tag` that is one
* of `uri`, `comment`, or `tag`. URIs only have a single additional
* property, `line`, which captures the entirety of the input without
* interpretation. Comments similarly have a single additional property
* `text` which is the input without the leading `#`.
*
* Tags always have a property `tagType` which is the lower-cased version of
* the M3U8 directive without the `#EXT` or `#EXT-X-` prefix. For instance,
* `#EXT-X-MEDIA-SEQUENCE` becomes `media-sequence` when parsed. Unrecognized
* tags are given the tag type `unknown` and a single additional property
* `data` with the remainder of the input.
*/
export class ParseStream extends Stream {
constructor() {
super();
......@@ -341,7 +339,24 @@ export class ParseStream extends Stream {
}
}
/**
* A parser for M3U8 files. The current interpretation of the input is
* exposed as a property `manifest` on parser objects. It's just two lines to
* create and parse a manifest once you have the contents available as a string:
*
* ```js
* var parser = new videojs.m3u8.Parser();
* parser.push(xhr.responseText);
* ```
*
* New input can later be applied to update the manifest object by calling
* `push` again.
*
* The parser attempts to create a usable manifest object even if the
* underlying input is somewhat nonsensical. It emits `info` and `warning`
* events during the parse if it encounters input that seems invalid or
* requires some property of the manifest object to be defaulted.
*/
export class Parser extends Stream {
constructor() {
super();
......@@ -493,10 +508,8 @@ export class Parser extends Stream {
if (!currentUri.attributes) {
currentUri.attributes = {};
}
currentUri.attributes = mergeOptions(
currentUri.attributes,
entry.attributes
);
currentUri.attributes = mergeOptions(currentUri.attributes,
entry.attributes);
},
discontinuity() {
currentUri.discontinuity = true;
......
import {ParseStream, LineStream, Parser} from '../src/m3u8';
import QUnit from 'qunit';
import testDataExpected from './data/expected.js';
import testDataManifests from './data/manifests.js';
import testDataExpected from './test-expected.js';
import testDataManifests from './test-manifests.js';
QUnit.module('LineStream', {
beforeEach() {
......@@ -68,11 +68,9 @@ QUnit.test('stops sending events after deregistering', function() {
this.lineStream.on('data', temporary);
this.lineStream.on('data', permanent);
this.lineStream.push('line one\n');
QUnit.strictEqual(
temporaryLines.length,
permanentLines.length,
'both callbacks receive the event'
);
QUnit.strictEqual(temporaryLines.length,
permanentLines.length,
'both callbacks receive the event');
QUnit.ok(this.lineStream.off('data', temporary), 'a listener was removed');
this.lineStream.push('line two\n');
......@@ -99,8 +97,8 @@ QUnit.test('parses comment lines', function() {
QUnit.ok(element, 'an event was triggered');
QUnit.strictEqual(element.type, 'comment', 'the type is comment');
QUnit.strictEqual(element.text,
manifest.slice(1, manifest.length - 1),
'the comment text is parsed');
manifest.slice(1, manifest.length - 1),
'the comment text is parsed');
});
QUnit.test('parses uri lines', function() {
let manifest = 'any non-blank line that does not start with a hash-mark is a URI\n';
......@@ -114,8 +112,8 @@ QUnit.test('parses uri lines', function() {
QUnit.ok(element, 'an event was triggered');
QUnit.strictEqual(element.type, 'uri', 'the type is uri');
QUnit.strictEqual(element.uri,
manifest.substring(0, manifest.length - 1),
'the uri text is parsed');
manifest.substring(0, manifest.length - 1),
'the uri text is parsed');
});
QUnit.test('parses unknown tag types', function() {
let manifest = '#EXT-X-EXAMPLE-TAG:some,additional,stuff\n';
......@@ -129,8 +127,8 @@ QUnit.test('parses unknown tag types', function() {
QUnit.ok(element, 'an event was triggered');
QUnit.strictEqual(element.type, 'tag', 'the type is tag');
QUnit.strictEqual(element.data,
manifest.slice(4, manifest.length - 1),
'unknown tag data is preserved');
manifest.slice(4, manifest.length - 1),
'unknown tag data is preserved');
});
// #EXTM3U
......@@ -200,8 +198,8 @@ QUnit.test('parses #EXTINF tags with a duration and title', function() {
QUnit.strictEqual(element.tagType, 'inf', 'the tag type is inf');
QUnit.strictEqual(element.duration, 13, 'the duration is parsed');
QUnit.strictEqual(element.title,
manifest.substring(manifest.indexOf(',') + 1, manifest.length - 1),
'the title is parsed');
manifest.substring(manifest.indexOf(',') + 1, manifest.length - 1),
'the title is parsed');
});
QUnit.test('parses #EXTINF tags with carriage returns', function() {
let manifest = '#EXTINF:13,Does anyone really use the title attribute?\r\n';
......@@ -217,8 +215,8 @@ QUnit.test('parses #EXTINF tags with carriage returns', function() {
QUnit.strictEqual(element.tagType, 'inf', 'the tag type is inf');
QUnit.strictEqual(element.duration, 13, 'the duration is parsed');
QUnit.strictEqual(element.title,
manifest.substring(manifest.indexOf(',') + 1, manifest.length - 2),
'the title is parsed');
manifest.substring(manifest.indexOf(',') + 1, manifest.length - 2),
'the title is parsed');
});
// #EXT-X-TARGETDURATION
......@@ -485,8 +483,8 @@ QUnit.test('parses #EXT-X-STREAM-INF with common attributes', function() {
QUnit.strictEqual(element.type, 'tag', 'the line type is tag');
QUnit.strictEqual(element.tagType, 'stream-inf', 'the tag type is stream-inf');
QUnit.strictEqual(element.attributes.CODECS,
'avc1.4d400d, mp4a.40.2',
'codecs are parsed');
'avc1.4d400d, mp4a.40.2',
'codecs are parsed');
});
QUnit.test('parses #EXT-X-STREAM-INF with arbitrary attributes', function() {
let manifest = '#EXT-X-STREAM-INF:NUMERIC=24,ALPHA=Value,MIXED=123abc\n';
......@@ -501,11 +499,9 @@ QUnit.test('parses #EXT-X-STREAM-INF with arbitrary attributes', function() {
QUnit.strictEqual(element.type, 'tag', 'the line type is tag');
QUnit.strictEqual(element.tagType, 'stream-inf', 'the tag type is stream-inf');
QUnit.strictEqual(element.attributes.NUMERIC, '24', 'numeric attributes are parsed');
QUnit.strictEqual(
element.attributes.ALPHA,
'Value',
'alphabetic attributes are parsed'
);
QUnit.strictEqual(element.attributes.ALPHA,
'Value',
'alphabetic attributes are parsed');
QUnit.strictEqual(element.attributes.MIXED, '123abc', 'mixed attributes are parsed');
});
// #EXT-X-ENDLIST
......@@ -593,23 +589,23 @@ QUnit.test('parses lightly-broken #EXT-X-KEY tags', function() {
this.lineStream.push(manifest);
QUnit.strictEqual(element.attributes.URI,
'https://example.com/single-quote',
'parsed a single-quoted uri');
'https://example.com/single-quote',
'parsed a single-quoted uri');
element = null;
manifest = '#EXT-X-KEYURI="https://example.com/key",METHOD=AES-128\n';
this.lineStream.push(manifest);
QUnit.strictEqual(element.tagType, 'key', 'parsed the tag type');
QUnit.strictEqual(element.attributes.URI,
'https://example.com/key',
'inferred a colon after the tag type');
'https://example.com/key',
'inferred a colon after the tag type');
element = null;
manifest = '#EXT-X-KEY: URI = "https://example.com/key",METHOD=AES-128\n';
this.lineStream.push(manifest);
QUnit.strictEqual(element.attributes.URI,
'https://example.com/key',
'trims and removes quotes around the URI');
'https://example.com/key',
'trims and removes quotes around the URI');
});
QUnit.test('ignores empty lines', function() {
......@@ -640,10 +636,9 @@ QUnit.test('parses static manifests as expected', function() {
let parser = new Parser();
parser.push(testDataManifests[key]);
QUnit.deepEqual(
parser.manifest,
testDataExpected[key],
key + '.m3u8 was parsed correctly'
QUnit.deepEqual(parser.manifest,
testDataExpected[key],
key + '.m3u8 was parsed correctly'
);
}
}
......
import manifests from './data/manifests';
import expected from './data/expected';
import manifests from './test-manifests';
import expected from './test-expected';
window.manifests = manifests;
window.expected = expected;
......