m3u8
Version:
streaming m3u8 parser for Apple's HTTP Live Streaming protocol
126 lines (107 loc) • 3.3 kB
JavaScript
var util = require('util'),
ChunkedStream = require('chunked-stream'),
M3U = require('./m3u'),
PlaylistItem = require('./m3u/PlaylistItem'),
StreamItem = require('./m3u/StreamItem'),
IframeStreamItem = require('./m3u/IframeStreamItem'),
MediaItem = require('./m3u/MediaItem');
// used for splitting strings by commas not within double quotes
var NON_QUOTED_COMMA = /,(?=(?:[^"]|"[^"]*")*$)/;
var m3uParser = module.exports = function m3uParser() {
ChunkedStream.apply(this, ['\n', true]);
this.linesRead = 0;
this.m3u = new M3U;
this.on('data', this.parse.bind(this));
var self = this;
this.on('end', function() {
self.emit('m3u', self.m3u);
});
};
util.inherits(m3uParser, ChunkedStream);
m3uParser.M3U = M3U;
m3uParser.createStream = function() {
return new m3uParser;
};
m3uParser.prototype.parse = function parse(line) {
line = line.trim();
if (this.linesRead == 0) {
if (line != '#EXTM3U') {
return this.emit('error', new Error(
'Non-valid M3U file. First line: ' + line
));
}
this.linesRead++;
return true;
}
switch(['#EXT-X-ENDLIST', ''].indexOf(line)) {
case 0:
this.m3u.set('playlistType', 'VOD');
case 1:
return true;
}
if (line.indexOf('#') == 0) {
this.parseLine(line);
} else {
if (this.currentItem.attributes.uri != undefined) {
this.addItem(new PlaylistItem);
}
this.currentItem.set('uri', line);
this.emit('item', this.currentItem);
}
this.linesRead++;
};
m3uParser.prototype.parseLine = function parseLine(line) {
var parts = line.slice(1).split(/:(.*)/);
var tag = parts[0];
var data = parts[1];
if (typeof this[tag] == 'function') {
this[tag](data, tag);
} else {
this.m3u.set(tag, data);
}
};
m3uParser.prototype.addItem = function addItem(item) {
this.m3u.addItem(item);
this.currentItem = item;
return item;
};
m3uParser.prototype['EXTINF'] = function parseInf(data) {
this.addItem(new PlaylistItem);
data = data.split(',');
this.currentItem.set('duration', parseFloat(data[0]));
this.currentItem.set('title', data[1]);
if (this.playlistDiscontinuity) {
this.currentItem.set('discontinuity', true);
this.playlistDiscontinuity = false;
}
};
m3uParser.prototype['EXT-X-DISCONTINUITY'] = function parseInf() {
this.playlistDiscontinuity = true;
}
m3uParser.prototype['EXT-X-BYTERANGE'] = function parseByteRange(data) {
this.currentItem.set('byteRange', data);
};
m3uParser.prototype['EXT-X-STREAM-INF'] = function(data) {
this.addItem(new StreamItem(this.parseAttributes(data)));
};
m3uParser.prototype['EXT-X-I-FRAME-STREAM-INF'] = function(data) {
this.addItem(new IframeStreamItem(this.parseAttributes(data)));
this.emit('item', this.currentItem);
};
m3uParser.prototype['EXT-X-MEDIA'] = function(data) {
this.addItem(new MediaItem(this.parseAttributes(data)));
this.emit('item', this.currentItem);
};
m3uParser.prototype.parseAttributes = function parseAttributes(data) {
data = data.split(NON_QUOTED_COMMA);
var self = this;
return data.map(function(attribute) {
var keyValue = attribute.split(/=(.+)/).map(function(str) {
return str.trim();
});
return {
key : keyValue[0],
value : keyValue[1]
};
});
};