{"id":13564250,"url":"https://github.com/videojs/m3u8-parser","last_synced_at":"2025-04-03T21:30:34.661Z","repository":{"id":41344869,"uuid":"60372736","full_name":"videojs/m3u8-parser","owner":"videojs","description":"An m3u8 parser.","archived":false,"fork":false,"pushed_at":"2023-11-23T11:35:15.000Z","size":1141,"stargazers_count":450,"open_issues_count":21,"forks_count":96,"subscribers_count":25,"default_branch":"main","last_synced_at":"2024-05-22T04:42:43.919Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/videojs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-06-03T19:30:26.000Z","updated_at":"2024-05-19T15:52:49.000Z","dependencies_parsed_at":"2024-01-07T09:40:55.474Z","dependency_job_id":"d43df7a3-5974-405b-97c0-6c0f6569a580","html_url":"https://github.com/videojs/m3u8-parser","commit_stats":{"total_commits":99,"total_committers":26,"mean_commits":"3.8076923076923075","dds":0.7777777777777778,"last_synced_commit":"f38d60de6cabcf53c2d7552f373e3e21a4533c3c"},"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/videojs%2Fm3u8-parser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/videojs%2Fm3u8-parser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/videojs%2Fm3u8-parser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/videojs%2Fm3u8-parser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/videojs","download_url":"https://codeload.github.com/videojs/m3u8-parser/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243793601,"owners_count":20348881,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T13:01:28.632Z","updated_at":"2025-04-03T21:30:34.005Z","avatar_url":"https://github.com/videojs.png","language":"JavaScript","readme":"# m3u8-parser\n[![Build Status](https://travis-ci.org/videojs/m3u8-parser.svg?branch=master)](https://travis-ci.org/videojs/m3u8-parser)\n[![Greenkeeper badge](https://badges.greenkeeper.io/videojs/m3u8-parser.svg)](https://greenkeeper.io/)\n[![Slack Status](http://slack.videojs.com/badge.svg)](http://slack.videojs.com)\n\n[![NPM](https://nodei.co/npm/m3u8-parser.png?downloads=true\u0026downloadRank=true)](https://nodei.co/npm/m3u8-parser/)\n\nm3u8 parser\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Constructor Options](#constructor-options)\n  - [Parsed Output](#parsed-output)\n- [Supported Tags](#supported-tags)\n  - [Basic Playlist Tags](#basic-playlist-tags)\n  - [Media Segment Tags](#media-segment-tags)\n  - [Media Playlist Tags](#media-playlist-tags)\n  - [Main Playlist Tags](#main-playlist-tags)\n  - [Experimental Tags](#experimental-tags)\n    - [EXT-X-CUE-OUT](#ext-x-cue-out)\n    - [EXT-X-CUE-OUT-CONT](#ext-x-cue-out-cont)\n    - [EXT-X-CUE-IN](#ext-x-cue-in)\n  - [Not Yet Supported](#not-yet-supported)\n  - [Custom Parsers](#custom-parsers)\n- [Including the Parser](#including-the-parser)\n  - [`\u003cscript\u003e` Tag](#script-tag)\n  - [Browserify](#browserify)\n  - [RequireJS/AMD](#requirejsamd)\n- [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n## Installation\n\n```sh\nnpm install --save m3u8-parser\n```\n\nThe npm installation is preferred, but Bower works, too.\n\n```sh\nbower install  --save m3u8-parser\n```\n\n## Usage\n\n```js\nvar manifest = [\n  '#EXTM3U',\n  '#EXT-X-VERSION:3',\n  '#EXT-X-TARGETDURATION:6',\n  '#EXT-X-MEDIA-SEQUENCE:0',\n  '#EXT-X-DISCONTINUITY-SEQUENCE:0',\n  '#EXTINF:6,',\n  '0.ts',\n  '#EXTINF:6,',\n  '1.ts',\n  '#EXT-X-PROGRAM-DATE-TIME:2019-02-14T02:14:00.106Z'\n  '#EXTINF:6,',\n  '2.ts',\n  '#EXT-X-ENDLIST'\n].join('\\n');\n\nvar parser = new m3u8Parser.Parser();\n\nparser.push(manifest);\nparser.end();\n\nvar parsedManifest = parser.manifest;\n```\n### Constructor Options\n\nThe constructor optinally takes an options object with two properties. These are needed when using `#EXT-X-DEFINE` for variable replacement.\n\n```js\nvar parser = new m3u8Parser.Parser({\n  url: 'https://exmaple.com/video.m3u8?param_a=34\u0026param_b=abc',\n  mainDefinitions: {\n    param_c: 'def'\n  }\n});\n```\n\n* `options.url` _string_ The URL from which the playlist was fetched. If the request was redirected this should be the final URL. This is required if using `QUERYSTRING` rules with `#EXT-X-DEFINE`.\n* `options.mainDefinitions` _object_ An object of definitions from the main playlist. This is required if using `IMPORT` rules with `#EXT-X-DEFINE`.\n\n### Parsed Output\n\nThe parser ouputs a plain javascript object with the following structure:\n\n```js\nManifest {\n  allowCache: boolean,\n  endList: boolean,\n  mediaSequence: number,\n  dateRanges: [],\n  discontinuitySequence: number,\n  playlistType: string,\n  custom: {},\n  playlists: [\n    {\n      attributes: {},\n      Manifest\n    }\n  ],\n  mediaGroups: {\n    AUDIO: {\n      'GROUP-ID': {\n        NAME: {\n          default: boolean,\n          autoselect: boolean,\n          language: string,\n          uri: string,\n          instreamId: string,\n          characteristics: string,\n          forced: boolean\n        }\n      }\n    },\n    VIDEO: {},\n    'CLOSED-CAPTIONS': {},\n    SUBTITLES: {}\n  },\n  dateTimeString: string,\n  dateTimeObject: Date,\n  targetDuration: number,\n  totalDuration: number,\n  discontinuityStarts: [number],\n  segments: [\n    {\n      title: string,\n      byterange: {\n        length: number,\n        offset: number\n      },\n      duration: number,\n      programDateTime:  number,\n      attributes: {},\n      discontinuity: number,\n      uri: string,\n      timeline: number,\n      key: {\n        method: string,\n        uri: string,\n        iv: string\n      },\n      map: {\n        uri: string,\n        byterange: {\n          length: number,\n          offset: number\n        }\n      },\n      'cue-out': string,\n      'cue-out-cont': string,\n      'cue-in': string,\n      custom: {}\n    }\n  ]\n}\n```\n\n## Supported Tags\n\n### Basic Playlist Tags\n\n* [EXTM3U](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.1.1)\n* [EXT-X-VERSION](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.1.2)\n\n### Media Segment Tags\n\n* [EXTINF](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.2.1)\n* [EXT-X-BYTERANGE](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.2.2)\n* [EXT-X-DISCONTINUITY](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.2.3)\n* [EXT-X-KEY](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.2.4)\n* [EXT-X-MAP](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.2.5)\n* [EXT-X-PROGRAM-DATE-TIME](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.2.6)\n* [EXT-X-DATERANGE](https://datatracker.ietf.org/doc/html/draft-pantos-http-live-streaming-23#section-4.3.2.7)\n* [EXT-X-I-FRAMES-ONLY](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.3.6)\n\n### Media Playlist Tags\n\n* [EXT-X-TARGETDURATION](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.3.1)\n* [EXT-X-MEDIA-SEQUENCE](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.3.2)\n* [EXT-X-DISCONTINUITY-SEQUENCE](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.3.3)\n* [EXT-X-ENDLIST](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.3.4)\n* [EXT-X-PLAYLIST-TYPE](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.3.5)\n* [EXT-X-START](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.5.2)\n* [EXT-X-INDEPENDENT-SEGMENTS](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.5.1)\n* [EXT-X-DEFINE](https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-4.4.2.3)\n\n### Main Playlist Tags\n\n* [EXT-X-MEDIA](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.4.1)\n* [EXT-X-STREAM-INF](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.4.2)\n* [EXT-X-I-FRAME-STREAM-INF](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.4.3)\n* [EXT-X-CONTENT-STEERING](https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-4.4.6.6)\n* [EXT-X-DEFINE](https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-4.4.2.3)\n\n### Experimental Tags\n\nm3u8-parser supports 3 additional **Media Segment Tags** not present in the HLS specification.\n\n#### EXT-X-CUE-OUT\n\nThe `EXT-X-CUE-OUT` indicates that the following media segment is a break in main content and the start of interstitial content. Its format is:\n\n```\n#EXT-X-CUE-OUT:\u003cduration\u003e\n```\n\nwhere `duration` is a decimal-floating-point or decimal-integer number that specifies the total duration of the interstitial in seconds.\n\n#### EXT-X-CUE-OUT-CONT\n\nThe `EXT-X-CUE-OUT-CONT` indicates that the following media segment is a part of interstitial content and not the main content. Every media segment following a media segment with an `EXT-X-CUE-OUT` tag *SHOULD* have an `EXT-X-CUE-OUT-CONT` applied to it until there is an `EXT-X-CUE-IN` tag. A media segment between a `EXT-X-CUE-OUT` and `EXT-X-CUE-IN` segment without a `EXT-X-CUE-OUT-CONT` is assumed to be part of the interstitial. Its format is:\n\n```\n#EXT-X-CUE-OUT-CONT:\u003cn\u003e/\u003cduration\u003e\n```\n\nwhere `n` is a decimal-floating-point or decimal-integer number that specifies the time in seconds the first sample of the media segment lies within the interstitial content and `duration` is a decimal-floating-point or decimal-integer number that specifies the total duration of the interstitial in seconds. `n` *SHOULD* be the sum of `EXTINF` durations for all preceding media segments up to the `EXT-X-CUE-OUT` tag for the current interstitial. `duration` *SHOULD* match the `duration` specified in the `EXT-X-CUE-OUT` tag for the current interstitial.'\n\n#### EXT-X-CUE-IN\n\nThe `EXT-X-CUE-IN` indicates the end of the interstitial and the return of the main content. Its format is:\n\n```\n#EXT-X-CUE-IN\n```\n\nThere *SHOULD* be a closing `EXT-X-CUE-IN` tag for every `EXT-X-CUE-OUT` tag. If a second `EXT-X-CUE-OUT` tag is encountered before an `EXT-X-CUE-IN` tag, the client *MAY* choose to ignore the `EXT-X-CUE-OUT` and treat it as part of the interstitial, or reject the playlist.\n\nExample media playlist using `EXT-X-CUE-` tags.\n\n```\n#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-TARGETDURATION:10\n#EXTINF:10,\n0.ts\n#EXTINF:10,\n1.ts\n#EXT-X-CUE-OUT:30\n#EXTINF:10,\n2.ts\n#EXT-X-CUE-OUT-CONT:10/30\n#EXTINF:10,\n3.ts\n#EXT-X-CUE-OUT-CONT:20/30\n#EXTINF:10,\n4.ts\n#EXT-X-CUE-IN\n#EXTINF:10,\n5.ts\n#EXTINF:10,\n6.ts\n#EXT-X-ENDLIST\n```\n\n### Not Yet Supported\n\n* [EXT-X-SESSION-DATA](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.4.4)\n* [EXT-X-SESSION-KEY](http://tools.ietf.org/html/draft-pantos-http-live-streaming#section-4.3.4.5)\n\n### Custom Parsers\n\nTo add a parser for a non-standard tag the parser object allows for the specification of custom tags using regular expressions. If a custom parser is specified, a `custom` object is appended to the manifest object.\n\n```js\nconst manifest = [\n  '#EXTM3U',\n  '#EXT-X-VERSION:3',\n  '#VOD-FRAMERATE:29.97',\n  ''\n].join('\\n');\n\nconst parser = new m3u8Parser.Parser();\nparser.addParser({\n  expression: /^#VOD-FRAMERATE/,\n  customType: 'framerate'\n});\n\nparser.push(manifest);\nparser.end();\nparser.manifest.custom.framerate // \"#VOD-FRAMERATE:29.97\"\n```\n\nCustom parsers may additionally be provided a data parsing function that take a line and return a value.\n\n```js\nconst manifest = [\n  '#EXTM3U',\n  '#EXT-X-VERSION:3',\n  '#VOD-FRAMERATE:29.97',\n  ''\n].join('\\n');\n\nconst parser = new m3u8Parser.Parser();\nparser.addParser({\n  expression: /^#VOD-FRAMERATE/,\n  customType: 'framerate',\n  dataParser: function(line) {\n    return parseFloat(line.split(':')[1]);\n  }\n});\n\nparser.push(manifest);\nparser.end();\nparser.manifest.custom.framerate // 29.97\n```\n\nCustom parsers may also extract data at a segment level by passing `segment: true` to the options object. Having a segment level custom parser will add a `custom` object to the segment data.\n\n```js\nconst manifest = [\n    '#EXTM3U',\n    '#VOD-TIMING:1511816599485',\n    '#EXTINF:8.0,',\n    'ex1.ts',\n    ''\n  ].join('\\n');\n\nconst parser = new m3u8Parser.Parser();\nparser.addParser({\n  expression: /#VOD-TIMING/,\n  customType: 'vodTiming',\n  segment: true\n});\n\nparser.push(manifest);\nparser.end();\nparser.manifest.segments[0].custom.vodTiming // #VOD-TIMING:1511816599485\n```\n\nCustom parsers may also map a tag to another tag. The old tag will not be replaced and all matching registered mappers and parsers will be executed.\n```js\nconst manifest = [\n    '#EXTM3U',\n    '#EXAMPLE',\n    '#EXTINF:8.0,',\n    'ex1.ts',\n    ''\n  ].join('\\n');\n\nconst parser = new m3u8Parser.Parser();\nparser.addTagMapper({\n  expression: /#EXAMPLE/,\n  map(line) {\n    return `#NEW-TAG:123`;\n  }\n});\nparser.addParser({\n  expression: /#NEW-TAG/,\n  customType: 'mappingExample',\n  segment: true\n});\n\nparser.push(manifest);\nparser.end();\nparser.manifest.segments[0].custom.mappingExample // #NEW-TAG:123\n```\n\n## Including the Parser\n\nTo include m3u8-parser on your website or web application, use any of the following methods.\n\n### `\u003cscript\u003e` Tag\n\nThis is the simplest case. Get the script in whatever way you prefer and include it on your page.\n\n```html\n\u003cscript src=\"//path/to/m3u8-parser.min.js\"\u003e\u003c/script\u003e\n\u003cscript\u003e\n  var parser = new m3u8Parser.Parser();\n\u003c/script\u003e\n```\n\n### Browserify\n\nWhen using with Browserify, install m3u8-parser via npm and `require` the parser as you would any other module.\n\n```js\nvar m3u8Parser = require('m3u8-parser');\n\nvar parser = new m3u8Parser.Parser();\n```\n\nWith ES6:\n```js\nimport { Parser } from 'm3u8-parser';\n\nconst parser = new Parser();\n```\n\n### RequireJS/AMD\n\nWhen using with RequireJS (or another AMD library), get the script in whatever way you prefer and `require` the parser as you normally would:\n\n```js\nrequire(['m3u8-parser'], function(m3u8Parser) {\n  var parser = new m3u8Parser.Parser();\n});\n```\n\n## License\n\nApache-2.0. Copyright (c) Brightcove, Inc\n","funding_links":[],"categories":["JavaScript","HLS","HarmonyOS"],"sub_categories":["Talks Presentations Podcasts","Windows Manager"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvideojs%2Fm3u8-parser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvideojs%2Fm3u8-parser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvideojs%2Fm3u8-parser/lists"}