1 | const Writable = require('stream').Writable;
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | module.exports = class m3u8Parser extends Writable {
|
11 | constructor() {
|
12 | super();
|
13 | this._lastLine = '';
|
14 | this._seq = 0;
|
15 | this._nextItemDuration = null;
|
16 | this.on('finish', () => {
|
17 | this._parseLine(this._lastLine);
|
18 | this.emit('end');
|
19 | });
|
20 | }
|
21 |
|
22 | _parseLine(line) {
|
23 | let match = line.match(/^#(EXT[A-Z0-9-]+)(?::(.*))?/);
|
24 | if (match) {
|
25 |
|
26 | const tag = match[1];
|
27 | const value = match[2] || null;
|
28 | switch (tag) {
|
29 | case 'EXT-X-PROGRAM-DATE-TIME':
|
30 | this.emit('starttime', new Date(value).getTime());
|
31 | break;
|
32 | case 'EXT-X-MEDIA-SEQUENCE':
|
33 | this._seq = parseInt(value);
|
34 | break;
|
35 | case 'EXTINF':
|
36 | this._nextItemDuration =
|
37 | Math.round(parseFloat(value.split(',')[0]) * 1000);
|
38 | break;
|
39 | case 'EXT-X-ENDLIST':
|
40 | this.emit('endlist');
|
41 | break;
|
42 | }
|
43 |
|
44 | } else if (!/^#/.test(line) && line.trim()) {
|
45 |
|
46 | this.emit('item', {
|
47 | url: line.trim(),
|
48 | seq: this._seq++,
|
49 | duration: this._nextItemDuration,
|
50 | });
|
51 | }
|
52 | }
|
53 |
|
54 | _write(chunk, encoding, callback) {
|
55 | let lines = chunk.toString('utf8').split('\n');
|
56 | if (this._lastLine) { lines[0] = this._lastLine + lines[0]; }
|
57 | lines.forEach((line, i) => {
|
58 | if (i < lines.length - 1) {
|
59 | this._parseLine(line);
|
60 | } else {
|
61 |
|
62 | this._lastLine = line;
|
63 | }
|
64 | });
|
65 | callback();
|
66 | }
|
67 | };
|