Browse Source
Add m3u8.pegjs.
Add m3u8.pegjs.
Generate the parser file via 'npm run peg'. There is a simple test script in test/pegtest.js. The test can be run via 'npm run testpeg' which will generate a new copy of the parser and then run the test file.pull/80/head

3 changed files with 281 additions and 2 deletions
@ -0,0 +1,269 @@ |
|||
/***** Start *****/ |
|||
start |
|||
= tags:tags+ .* { |
|||
var obj = {}; |
|||
tags.forEach(function(tag) { for (var p in tag) { obj[p] = tag[p]; }}); |
|||
return obj; |
|||
} |
|||
|
|||
tags |
|||
= tag:m3uTag _ { return tag; } |
|||
/ tag:extinfTag _ { return tag; } |
|||
/ tag:targetDurationTag _ { return tag; } |
|||
/ tag:mediaSequenceTag _ { return tag; } |
|||
/ tag:keyTag _ { return tag; } |
|||
/ tag:programDateTimeTag _ { return tag; } |
|||
/ tag:allowCacheTag _ { return tag; } |
|||
/ tag:playlistTypeTag _ { return tag; } |
|||
/ tag:endlistTag _ { return tag; } |
|||
/ tag:mediaTag _ { return tag; } |
|||
/ tag:streamInfTag _ { return tag; } |
|||
/ tag:discontinuityTag _ { return tag; } |
|||
/ tag:discontinuitySequenceTag _ { return tag; } |
|||
/ tag:iframesOnlyTag _ { return tag; } |
|||
/ tag:mapTag _ { return tag; } |
|||
/ tag:iframeStreamInf _ { return tag; } |
|||
/ tag:startTag _ { return tag; } |
|||
/ tag:versionTag _ { return tag; } |
|||
|
|||
/***** Tags *****/ |
|||
|
|||
m3uTag |
|||
= tag:"#EXTM3U" { return {openTag: true}; } |
|||
|
|||
extinfTag |
|||
= tag:'#EXTINF' ":" duration:number "," _ title:text? _ byteRange:byteRangeTag? file:mediaFile { |
|||
var fileObj = {}; |
|||
fileObj[file] = { |
|||
byteRange: byteRange, |
|||
title: title, |
|||
duration: duration, |
|||
file: file |
|||
}; |
|||
return fileObj; |
|||
} |
|||
|
|||
byteRangeTag |
|||
= tag:"EXT-X-BYTERANGE" ":" length:int ("@" offset:int)? { return {length: length, offset: offset}; } |
|||
|
|||
targetDurationTag |
|||
= tag:"#EXT-X-TARGETDURATION" ":" seconds:int { return {targetDuration: seconds}; } |
|||
|
|||
mediaSequenceTag |
|||
= tag:'#EXT-X-MEDIA-SEQUENCE' ":" sequenceNumber:int { return {mediaSequence: sequenceNumber}; } |
|||
|
|||
keyTag |
|||
= tag:'#EXT-X-KEY' ":" attrs:keyAttributes { return {key: attrs}; } |
|||
|
|||
programDateTimeTag |
|||
= tag:'#EXT-X-PROGRAM-DATE-TIME' ":" date:date |
|||
|
|||
allowCacheTag |
|||
= tag:'#EXT-X-ALLOW-CACHE' ":" answer:answer { return {allowCache: answer}; } |
|||
|
|||
playlistTypeTag |
|||
= tag:'#EXT-X-PLAYLIST-TYPE' ":" type:playlistType { return {playlistType: type}; } |
|||
|
|||
endlistTag |
|||
= tag:'#EXT-X-ENDLIST' { return {closeTag: true}; } |
|||
|
|||
mediaTag |
|||
= tag:'#EXT-MEDIA' ":" attrs:mediaAttributes { return {media: attrs}; } |
|||
|
|||
streamInfTag |
|||
= tag:'#EXT-X-STREAM-INF' ":" attrs:streamInfAttrs _ file:mediaFile { |
|||
var fileObj = {}; |
|||
fileObj[file] = { |
|||
attributes: attrs, |
|||
file: file |
|||
}; |
|||
return fileObj; |
|||
} |
|||
|
|||
discontinuityTag |
|||
= tag:'#EXT-X-DISCONTINUITY' |
|||
|
|||
discontinuitySequenceTag |
|||
= tag:'#EXT-X-DISCONTINUITY-SEQUENCE' ":" sequence:int { return {discontinuitySequence: sequence}; } |
|||
|
|||
iframesOnlyTag |
|||
= tag:'#EXT-X-I-FRAMES-ONLY' |
|||
|
|||
mapTag |
|||
= tag:'#EXT-X-MAP' ":" attrs:mapAttributes { return {map: attrs}; } |
|||
|
|||
iframeStreamInf |
|||
= tag:'#EXT-X-I-FRAME-STREAM-INF' ":" attrs:iframeStreamAttrs { return {iframeStream: attrs}; } |
|||
|
|||
startTag |
|||
= tag:'EXT-X-START' ":" attrs:startAttributes { return {start: attrs}; } |
|||
|
|||
versionTag |
|||
= tag:'#EXT-X-VERSION' ":" version:int { return {version: version}; } |
|||
|
|||
/***** Helpers *****/ |
|||
|
|||
mediaFile |
|||
= file:[ -~]+ { return file.join(''); } |
|||
|
|||
keyAttributes |
|||
= attrs:keyAttribute+ |
|||
|
|||
keyAttribute |
|||
= "METHOD" "=" method:keyMethod |
|||
/ "URI" "=" uri:quotedString |
|||
/ "IV" "=" iv:hexint |
|||
/ "KEYFORMAT" "=" keyFormat:quotedString |
|||
/ "KEYFORMATVERSIONS" "=" keyFormatVersions:quotedString |
|||
|
|||
keyMethod |
|||
= "NONE" |
|||
/ "AES-128" |
|||
/ "SAMPLE-AES" |
|||
|
|||
mediaAttributes |
|||
= attrs:mediaAttribute+ |
|||
|
|||
mediaAttribute |
|||
= "TYPE" "=" type:enumeratedString |
|||
/ "URI" "=" uri:quotedString |
|||
/ "GROUP-ID" "=" groupId:quotedString |
|||
/ "LANGUAGE" "=" langauge:quotedString |
|||
/ "ASSOC-LANGUAGE" "=" assocLanguage:quotedString |
|||
/ "NAME" "=" name:quotedString |
|||
/ "DEFAULT" "=" default:answer |
|||
/ "AUTOSELECT" "="autoselect:answer |
|||
/ "FORCE" "=" force:answer |
|||
/ "INSTREAM-ID" "=" instreamId:quotedString |
|||
/ "CHARACTERISTICS" "=" characteristics:quotedString |
|||
|
|||
streamInfAttrs |
|||
= attrs:streamInfAttr+ |
|||
|
|||
streamInfAttr |
|||
= streamInfSharedAttr |
|||
/ "AUDIO" "=" audio:quotedString |
|||
/ "SUBTITLES" "=" subtitles:quotedString |
|||
/ "CLOSED-CAPTION" "=" captions:"NONE" |
|||
/ "CLOSED-CAPTION" "=" captions:quotedString |
|||
|
|||
streamInfSharedAttr |
|||
= "BANDWIDTH" "=" bandwidth:int |
|||
/ "CODECS" "=" codec:quotedString |
|||
/ "RESOLUTION" "=" resolution:resolution |
|||
/ "VIDEO" "=" video:quotedString |
|||
|
|||
mapAttributes |
|||
= attrs:mapAttribute+ |
|||
|
|||
mapAttribute |
|||
= "URI" "=" uri:quotedString |
|||
/ "BYTERANGE" "=" byteRange:quotedString |
|||
|
|||
iframeStreamAttrs |
|||
= iframeStreamAttr+ |
|||
|
|||
iframeStreamAttr |
|||
= streamInfSharedAttr |
|||
/ "URI" "=" uri:quotedString |
|||
|
|||
startAttributes |
|||
= attrs:startAttribute+ |
|||
|
|||
startAttribute |
|||
= "TIME-OFFSET" "=" timeOffset:number |
|||
/ "PRECISE" "=" precise:answer |
|||
|
|||
answer "answer" |
|||
= "YES" |
|||
/ "NO" |
|||
|
|||
playlistType |
|||
= "EVENT" |
|||
/ "VOD" |
|||
|
|||
/***** Date *****/ |
|||
|
|||
date "date" |
|||
= year:year "-" month:month "-" day:day "T" time:time timezone:timezone |
|||
|
|||
year "year" |
|||
= digit digit digit digit |
|||
|
|||
month "month" |
|||
= [01] digit |
|||
|
|||
day "day" |
|||
= [0-3] digit |
|||
|
|||
time "time" |
|||
= [0-2] digit ":" [0-5] digit ":" [0-5] digit "." digit+ |
|||
/ [0-2] digit ":" [0-5] digit ":" [0-5] digit |
|||
/ [0-2] digit ":" [0-5] digit |
|||
|
|||
timezone "timezone" |
|||
= [+-] [0-2] digit ":" [0-5] digit |
|||
/ "Z" |
|||
|
|||
/***** Numbers *****/ |
|||
|
|||
number "number" |
|||
= parts:(int frac) _ { return parseFloat(parts.join('')); } |
|||
/ parts:(int) _ { return parts; } |
|||
|
|||
resolution |
|||
= int "x" int |
|||
|
|||
int |
|||
= first:digit19 rest:digits { return parseInt(first + rest.join(''), 10); } |
|||
/ digit:digit { return parseInt(digit, 10); } |
|||
/ neg:"-" first:digit19 rest:digits { return parseInt(neg + first + rest.join(''), 10); } |
|||
/ neg:"-" digit { return parseInt(neg + digit, 10); } |
|||
|
|||
hexint |
|||
= "0x" hexDigits:hexDigit+ { return '0x' + hexDigits.join(''); } |
|||
/ "0X" hexDigits:hexDigit+ { return '0x' + hexDigits.join(''); } |
|||
|
|||
frac |
|||
= dec:"." digits:digits { return parseFloat(dec + digits.join('')); } |
|||
|
|||
digits |
|||
= digit+ |
|||
|
|||
digit |
|||
= [0-9] |
|||
|
|||
digit19 |
|||
= [1-9] |
|||
|
|||
hexDigit |
|||
= [0-9a-fA-F] |
|||
|
|||
/***** Text *****/ |
|||
|
|||
enumeratedString |
|||
= chars:enumeratedChar+ _ { return chars.join('') } |
|||
|
|||
quotedString |
|||
= '"' '"' _ { return ""; } |
|||
/ '"' chars:quotedChar+ '"' _ { return chars.join(''); } |
|||
|
|||
enumeratedChar |
|||
= [^'" \n\t\r] |
|||
/ [a-zA-Z0-9] |
|||
|
|||
quotedChar |
|||
= [^\r\n"] |
|||
/ char:char |
|||
|
|||
text "text" |
|||
= text:char+ { return text.join(''); } |
|||
|
|||
char "char" |
|||
= [ -~] |
|||
|
|||
_ "whitespace" |
|||
= whitespace* |
|||
|
|||
whitespace |
|||
= [ \t\n\r] |
@ -0,0 +1,6 @@ |
|||
var fs = require('fs'); |
|||
var path = require('path'); |
|||
var manifest = fs.readFileSync(__dirname + '/fixtures/prog_index.m3u8').toString(); |
|||
var parser = require('../src/m3u8/m3u8-generated.js'); |
|||
var parsed = parser.parse(manifest); |
|||
console.log(parsed); |
Write
Preview
Loading…
Cancel
Save
Reference in new issue