node-m3u8 icon indicating copy to clipboard operation
node-m3u8 copied to clipboard

few extra features

Open akhoury opened this issue 9 years ago • 10 comments

List of additions/changes (all backward compatible)

parser

  • added an options parameter to createStream(options)
    • options.lax a true/false boolean which basically forgives the parsed text if it doesn't start by an EXTM3U tag, 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 each item event, passing the currentItem as 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 this currentItem.set('date', new Date(currentItem.get('uri'))); - who knew that ffmpeg does not support the inclusion of EXT-X-PROGRAM-DATE-TIME on each segment while generating m3u8s from a raw stream.
  • added parser['EXT-X-PROGRAM-DATE-TIME'](), since m3u.toString() does output it if available but the current parser ignores it.

m3u

  • added m3u.clone() basically just returns a clone of the m3u object.
  • added m3u.concat() which behaves exactly like how m3u.merge() behaves but it returns a new m3u object, basically just concats the playlist items.
  • added m3u.mergeByUri() to do a real unique merge, using each item.uri as the identifier, returns a new m3u object
  • added m3u.mergeByDate() to do a real unique merge using each item.date to find the stream gaps of stream-A and fill them in with slices of stream-B, returns a new m3u object
  • added some slicing functions, which each returns a new m3u object 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(), (alias m3u.slice()) slices based on the segments indices, returns a new m3u object
    • m3u.sliceBySeconds(), slices based on the position of each segment on the timeline of video seconds duration, returns a new m3u object
    • m3u.sliceByDates() slices based on the position of each segment on the timeline of real time (depends on the EXT-X-PROGRAM-DATE-TIME tag of course, or a custom item.date being set via the beforeItemEmit hook), returns a new m3u object
  • added m3u.insertPlaylistItemsAfter([newItems], afterItem)
  • added m3u.findDateGaps() used by m3u.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 a newTargetDuration to compare it and set it, or a true boolean to re-check all the PlaylistItems and figure out the max duration to use.
  • added m3u.isDateSupported()
  • added 2 functions to keep track if an ENDLIST tag was actually found, then it's a VOD since EXT-X-PLAYLIST-TYPE is 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 javascript Array does when doing an array.slice() or array.concat(), both of these 2 return a new array.

Tests

I think I covered all the things I added :), still a little shy but I will submit more as I move along.

akhoury avatar Feb 25 '16 21:02 akhoury

Wow, thanks for this. I'll be taking a closer look soon.

bog avatar Mar 22 '16 13:03 bog

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.

blainsmith avatar May 17 '16 19:05 blainsmith

I haven't had time yet. If you have time @blainsmith, it'd be appreciated.

bog avatar May 17 '16 19:05 bog

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

blainsmith avatar May 17 '16 19:05 blainsmith

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.

blainsmith avatar May 18 '16 17:05 blainsmith

Sounds good, I can add that in

akhoury avatar May 18 '16 17:05 akhoury

@bog, @blainsmith any more comments? and/or any idea when this would be merged? if accepted

akhoury avatar Jun 28 '16 03:06 akhoury

I could do with these new features as well :)

tjenkinson avatar Jul 11 '16 21:07 tjenkinson

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 :)

tjenkinson avatar Jul 14 '16 16:07 tjenkinson

ping @bog

akhoury avatar Dec 22 '16 22:12 akhoury