npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nolawnchairs/m3u8

v1.6.0

Published

Library for parsing, manipulating and producing ad-hoc m3u8 files for HLS

Readme

Lib M3U8

A library to parse and create variants of M3u8 manifest files.

Installation

npm install @nolawnchairs/m3u8

Note: This is a private NPM library used internally. No guarantees are made about the stability or quality of the library.

Usage

Instantiate either a master or media manifest file with the string contents of a valid m3u8 file.

Loading a master (variant manifest)

const contents = await fs.readFile('master.m3u8', { encoding: 'utf-8' })
const masterM3u8 = new MasterM3u8(contents)

Obtaining variants

Access the variants from a master manifest. The variants accessor returns a string array containing the sources for all variants referenced in the manifest.

const variants = masterM3u8.variants

Loading a variant

const contents = await fs.readFile(variants[0], { encoding: 'utf-8' })
const variantM3u8 = new MediaM3u8(contents)

Generating slices

Any MediaM3u8 variant can be sliced into smaller parts, which can be marshaled into either EVENT or VOD formats.

Create a new instance of M3u8Slicer, providing the source MediaM3u8 instance, a TargetResolver and an optional timing ratio.

The TargetResolver is a class that takes two functional operators in its constructor that will be used to resolve the actual URL to the encryption key, and that of the actual media source, respectively.

const resolver = new TargetResolver(
  value => value.replace('encryption.key', '/path/to/this/encryption.key'),
  value => `https://example.com/${value}`
)

const slicer = new M3u8Slicer(variantM3u8, resolver)

Create a live slice, which will produce a manifest with the #EXT-X-PLAYLIST-TYPE as EVENT, and without the #EXT-X-ENDLIST line at the end. The method takes three parameters:

  • The numeric value to set to the #EXT-X-MEDIA-SEQUENCE tag
  • The starting segment index of the slice
  • The number of segments to include
// The first 5 segments
const slice1 = slicer.toLiveSlice(0, 0, 5)

// The next 5 segments
const slice2 = slicer.toLiveSlice(1, 4, 5)

Create a VOD slice, which will produce a manifest with the #EXT-X-PLAYLIST-TYPE as VOD, and includes the #EXT-X-ENDLIST line at the end. The method takes two parameters:

  • The starting segment index of the slice
  • The number of segments to include

The VOD slice will always include the #EXT-X-MEDIA-SEQUENCE tag with the value of 0.

// Make a slice from the 10th segment with a length of 10 segments
const vodSlice = slicer.toVodSlice(9, 10)

Create a live transition slice, which will produce a manifest with the #EXT-X-PLAYLIST-TYPE as EVENT, and without the #EXT-X-ENDLIST line at the end, but will include part of a different manifest using the same indexing. The method takes three parameters:

  • The numeric value to set to the #EXT-X-MEDIA-SEQUENCE tag
  • The starting segment index of the slice
  • The M3u8Slicer instance of the slice to which the transition will be made

Note: This method assumes a slice of length 3, since this is a library specific to the Montage platform

const nextSlicer = new M3u8Slicer(otherM3u8, resolver)
const transition = slice.toLiveTransitionSlice(0, 0, nextSlicer)

The M3u8Slicer class can create a slice representation of the entire manifest with the TargetResolver applied to all segments. The method takes no parameters.

const slicer = new M3u8Slicer(variantM3u8, resolver)
const slice = slicer.toResolvedSlice()

The M3u8Slicer class can also be used to create a slice representation of the entire manifest with all properties left intact. The method takes no parameters.

const clone = slicer.toCloneSlice()

Note this method will NOT run the TargetResolver on the slice.

Slices

The M3u8Slice objects include the following properties and methods:

Properties:

  • meta - IM3u8Line[] - The metadata for this created manifest slice
  • segments - IM3u8MediaSegment[] - The media segments
  • offsetMillis - number - The start offset, in milliseconds, that represents the time offset since the start of the media, based on the #EXT-X-TARGETDURATION tag's value
  • offsetSeconds - number - The start offset, in seconds, floored to an integer
  • mediaExhausted - boolean - Whether the slice was created at the end of a manifest file

Methods:

  • marshal - The method that converts the slice to an m3u8-formatted string
  • appendDiscontinuity - appends another slice to this slice, adding an #EXT-X-DISCONTINUITY tag to the beginning of the appended slice.
  • insertMeta - inserts a meta element into the manifest
  • modifyMeta - modifies the value to a specific meta element from the manifest
  • omitMeta - removes a specific meta element from the manifest
  • modifyEachSegment - apply a modifier function to each segment in the manifest
  • modifySegmentMeta - apply a modifier function to each segment's metadata
  • omitSegmentMeta - remove a specific element from each segment's metadata

Note: appending a discontinuity will mutate the slice to which it's applied. The insert. modify and omit methods will immutably create a new M3u8Slice instance.

const slicer1 = new M3u8Slicer(firstM3u8)
const slice1 = slicer.toLiveSlice(0, 0, 10)

const slicer2 = new M3u8Slicer(secondM3u8)
const slice2 = slicer.toLiveSlice(0, 0, 5)

// Append the 5 segments from the second slice into the first
slice1.appendDiscontinuity(slice2)

// Marshal the result
const composite = slice1.marshal()

Note: Appending a discontinuity will work for both live and VOD slices.

Modifying slices

The M3u8Slice class also includes methods that can be used to generate slices with modified metadata and segments.

These methods all return a new M3u8Slice instance, and do not mutate the original slice.

Inserting meta

const slice = slicer.toCloneSlice()
const modified = slice.insertMeta(M3u8Tag.EXT_X_PROGRAM_DATE_TIME, new Date().toISOString())
const marshaled = modified.marshal()

Modifying meta

const slice = slicer.toCloneSlice()
const modified = slice.modifyMeta(M3u8Tag.EXT_X_PROGRAM_DATE_TIME, new Date().toISOString())
const marshaled = modified.marshal()

Omitting meta

const slice = slicer.toCloneSlice()
const modified = slice.omitMeta(M3u8Tag.EXT_X_PROGRAM_DATE_TIME)
const marshaled = modified.marshal()

Since all slice modifier methods return a new M3u8Slice instance, they can be chained together.

const slice = slicer.toCloneSlice()
  .insertMeta(M3u8Tag.EXT_X_PROGRAM_DATE_TIME, new Date().toISOString())
  .modifyMeta(M3u8Tag.EXT_X_PROGRAM_DATE_TIME, new Date().toISOString())
  .omitMeta(M3u8Tag.EXT_X_PROGRAM_DATE_TIME)

Modifying each segment

const slice = slicer.toCloneSlice()
  .modifyEachSegment(({ source, ...rest }) => ({
    ...rest,
    source: `https://example.com/${source}`
  }))

Modifying segment meta

const slice = slicer.toCloneSlice()
const modified = slice.modifySegmentMeta(M3u8Tag.EXT_X_KEY,
  ({ value }) => value.replace('encryption.key', 'https://example.com/keys/encryption.key'))

Omitting segment meta

const slice = slicer.toCloneSlice().omitSegmentMeta(M3u8Tag.EXT_X_KEY)

Full Example

const contents = await fs.readFile('master.m3u8', { encoding: 'utf-8' })
const masterM3u8 = new MasterM3u8(contents)

const resolver = new TargetResolver(
  value => value.replace('encryption.key', '/path/to/this/encryption.key'),
  value => `https://example.com/${value}`
)

for (const variant of masterM3u8.variants) {
  const variantContents = await fs.readFile(variant, { encoding: 'utf-8' })
  const variantM3u8 = new MediaM3u8(contents)
  const slicer = new M3u8Slicer(variantM3u8, resolver)

  const results = []

  let i = 0
  for (const segment of variantM3u8.segments) {
    const slice = slicer.toLiveSlice(i, i, 3)
    results.push(slice.marshal())
  }
}