node-m3u8
node-m3u8 copied to clipboard
few extra features
List of additions/changes (all backward compatible)
parser
- added an
optionsparameter tocreateStream(options)options.laxatrue/falseboolean which basically forgives the parsed text if it doesn't start by anEXTM3Utag, this is useful for merging many m3u8s into one, while tailing a large Live file.options.beforeItemEmit()a function hook which gets called before the emit of eachitemevent, passing thecurrentItemas the 1st argument. The reason this was useful in my use case is because I had m3u8s without the program-date-time tag, where the .ts files were named using timestamps, so before each item emit, I was doing something like thiscurrentItem.set('date', new Date(currentItem.get('uri')));- who knew thatffmpegdoes not support the inclusion ofEXT-X-PROGRAM-DATE-TIMEon each segment while generating m3u8s from a raw stream.
- added
parser['EXT-X-PROGRAM-DATE-TIME'](), sincem3u.toString()does output it if available but the current parser ignores it.
m3u
- added
m3u.clone()basically just returns a clone of them3uobject. - added
m3u.concat()which behaves exactly like howm3u.merge()behaves but it returns a newm3uobject, basically just concats the playlist items. - added
m3u.mergeByUri()to do a real unique merge, using eachitem.urias the identifier, returns a newm3uobject - added
m3u.mergeByDate()to do a real unique merge using eachitem.dateto find the stream gaps of stream-A and fill them in with slices of stream-B, returns a newm3uobject - added some slicing functions, which each returns a new
m3uobject with a slice of the PlaylistItems. Also, slicing a live m3u while using an end-range, will make the returned m3u as VOD, since the slice has an ending.m3u.sliceByIndex(), (aliasm3u.slice()) slices based on the segments indices, returns a newm3uobjectm3u.sliceBySeconds(), slices based on the position of each segment on the timeline of video seconds duration, returns a newm3uobjectm3u.sliceByDates()slices based on the position of each segment on the timeline of real time (depends on theEXT-X-PROGRAM-DATE-TIMEtag of course, or a custom item.date being set via thebeforeItemEmithook), returns a newm3uobject
- added
m3u.insertPlaylistItemsAfter([newItems], afterItem) - added
m3u.findDateGaps()used bym3u.mergeByDate() - added
m3u.sortByDate(), sorting is useful if you're merging un-ordered or random playlists - added
m3u.sortByUri() - added
m3u.resetTargetDuration()which would either take anewTargetDurationto compare it and set it, or atrueboolean to re-check all thePlaylistItemsand figure out the max duration to use. - added
m3u.isDateSupported() - added 2 functions to keep track if an
ENDLISTtag was actually found, then it's aVODsinceEXT-X-PLAYLIST-TYPEis optional and might not be there.m3u.isVOD()m3u.isLive()
- added
m3u.isMaster()if it's a master playlist or not.
Notes
- I think
m3u.merge()should return a clone instead of mutating self, but that would be breaking change - OR if you prefer mutating self all the time, we can change all the other merging and slicing functions, i.e.m3u.slice*(), to mutate self as well instead of returning a clone, let me know what you think - I was just trying to mimic what the javascriptArraydoes when doing anarray.slice()orarray.concat(), both of these 2 return a newarray.
Tests
I think I covered all the things I added :), still a little shy but I will submit more as I move along.
Wow, thanks for this. I'll be taking a closer look soon.
Wow, these are awesome additions! I'd love to take advantage of all of these. If you need help reviewing to get this PR merged I would be glad to. I have this module used in production now and I'd love these new additions/fixes.
I haven't had time yet. If you have time @blainsmith, it'd be appreciated.
Great, I will poke around at it. If you ever need another maintainer I'd be glad to help out. I do rely on this plugin a lot so I would love it to stay alive. We are using it at http://oddnetworks.com
So I found and issue with isLive() and isVOD() and that is with the following:
#EXTM3U
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-1",NAME="Audio",LANGUAGE="en",AUTOSELECT=YES,URI="audio_0_128000_hls.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=4928000,CODECS="avc1.64000d,mp4a.40.2",RESOLUTION=1280x544,AUDIO="audio-1"
video_0_2400000_hls.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2528000,CODECS="avc1.64000d,mp4a.40.2",RESOLUTION=854x362,AUDIO="audio-1"
video_0_1200000_hls.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1728000,CODECS="avc1.64000d,mp4a.40.2",RESOLUTION=640x272,AUDIO="audio-1"
video_0_800000_hls.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=928000,CODECS="avc1.64000d,mp4a.40.2",RESOLUTION=426x180,AUDIO="audio-1"
video_0_400000_hls.m3u8
This is actually a VOD, but it is the master.m3u8 file defining the bitrates. Unfortunately, you cannot tell it is either live or VOD until you parse the subsequent m3u8 urls (video_0_X_hls.m3u8). In most cases EXT-X-PLAYLIST-TYPE and EXT-X-ENDLIST will never appear in a file like this. Perhaps an isMaster() method to account for this 3rd format would help identify them.
Sounds good, I can add that in
@bog, @blainsmith any more comments? and/or any idea when this would be merged? if accepted
I could do with these new features as well :)
I'm working on a fork for now so I have can point to it from a package.json file. Will switch it to point here when this is merged :)
ping @bog