mirror of
				https://github.com/musix-org/musix-oss
				synced 2025-11-04 03:39:32 +00:00 
			
		
		
		
	Updated everything
This commit is contained in:
		
							
								
								
									
										21
									
								
								node_modules/m3u8stream/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								node_modules/m3u8stream/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,21 +0,0 @@
 | 
			
		||||
MIT License
 | 
			
		||||
 | 
			
		||||
Copyright (C) 2017 by fent
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE. 
 | 
			
		||||
							
								
								
									
										70
									
								
								node_modules/m3u8stream/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										70
									
								
								node_modules/m3u8stream/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,70 +0,0 @@
 | 
			
		||||
# node-m3u8stream
 | 
			
		||||
 | 
			
		||||
Reads segments from a [m3u8 playlist][1] or [DASH MPD file][2] into a consumable stream.
 | 
			
		||||
 | 
			
		||||
[1]: https://tools.ietf.org/html/draft-pantos-http-live-streaming-20
 | 
			
		||||
[2]: https://dashif.org/docs/DASH-IF-IOP-v4.2-clean.pdf
 | 
			
		||||
 | 
			
		||||
[](http://travis-ci.org/fent/node-m3u8stream)
 | 
			
		||||
[](https://david-dm.org/fent/node-m3u8stream)
 | 
			
		||||
[](https://codecov.io/gh/fent/node-m3u8stream)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Usage
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const fs = require('fs');
 | 
			
		||||
const m3u8stream = require('m3u8stream')
 | 
			
		||||
 | 
			
		||||
m3u8stream('http://somesite.com/link/to/the/playlist.m3u8')
 | 
			
		||||
    .pipe(fs.createWriteStream('videofile.mp4'));
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# API
 | 
			
		||||
 | 
			
		||||
### m3u8stream(url, [options])
 | 
			
		||||
 | 
			
		||||
Creates a readable stream of binary media data. `options` can have the following
 | 
			
		||||
 | 
			
		||||
* `begin` - Where to begin playing the video. Accepts an absolute unix timestamp or date, and a relative time in the formats `1:23:45.123` and `1m2s`.
 | 
			
		||||
* `liveBuffer` - How much buffer in milliseconds to have for live streams. Default is `20000`.
 | 
			
		||||
* `chunkReadahead` - How many chunks to preload ahead. Default is `3`.
 | 
			
		||||
* `highWaterMark` - How much of the download to buffer into the stream. See [node's docs](https://nodejs.org/api/stream.html#stream_constructor_new_stream_writable_options) for more. Note that the actual amount buffered can be higher since each chunk request maintains its own buffer.
 | 
			
		||||
* `requestOptions` - Any options you want to pass to [miniget](https://github.com/fent/node-miniget), such as `headers`.
 | 
			
		||||
* `parser` - Either "m3u8" or "dash-mpd". Defaults to guessing based on the playlist url ending in `.m3u8` or `.mpd`.
 | 
			
		||||
* `id` - For playlist containing multiple media options. If not given, the first representation will be picked.
 | 
			
		||||
 | 
			
		||||
### Stream#end()
 | 
			
		||||
 | 
			
		||||
If called, stops requesting segments, and refreshing the playlist.
 | 
			
		||||
 | 
			
		||||
#### Event: progress
 | 
			
		||||
* `Object` - Current segment with the following fields,
 | 
			
		||||
  - `number` - number
 | 
			
		||||
  - `number` - size
 | 
			
		||||
  - `number` - duration
 | 
			
		||||
  - `string` - url
 | 
			
		||||
* `number` - Total number of segments.
 | 
			
		||||
* `number` - Bytes downloaded up to this point.
 | 
			
		||||
 | 
			
		||||
For static non-live playlists, emitted each time a segment has finished downloading. Since total download size is unknown until all segment endpoints are hit, progress is calculated based on how many segments are available.
 | 
			
		||||
 | 
			
		||||
### Limitations
 | 
			
		||||
 | 
			
		||||
Currently, it does not support [encrypted media segments](https://tools.ietf.org/html/draft-pantos-http-live-streaming-20#section-4.3.2.4). This is because the sites where this was tested on and intended for, YouTube and Twitch, don't use it.
 | 
			
		||||
 | 
			
		||||
This does not parse master playlists, only media playlists. If you want to parse a master playlist to get links to media playlists, you can try the [m3u8 module](https://github.com/tedconf/node-m3u8).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Install
 | 
			
		||||
 | 
			
		||||
    npm install m3u8stream
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Tests
 | 
			
		||||
Tests are written with [mocha](https://mochajs.org)
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npm test
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										178
									
								
								node_modules/m3u8stream/lib/dash-mpd-parser.js
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										178
									
								
								node_modules/m3u8stream/lib/dash-mpd-parser.js
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,178 +0,0 @@
 | 
			
		||||
const Writable  = require('stream').Writable;
 | 
			
		||||
const sax       = require('sax');
 | 
			
		||||
const parseTime = require('./parse-time');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A wrapper around sax that emits segments.
 | 
			
		||||
 *
 | 
			
		||||
 * @extends WRitableStream
 | 
			
		||||
 * @constructor
 | 
			
		||||
 */
 | 
			
		||||
module.exports = class DashMPDParser extends Writable {
 | 
			
		||||
  constructor(targetID) {
 | 
			
		||||
    super();
 | 
			
		||||
    this._parser = sax.createStream(false, { lowercasetags: true });
 | 
			
		||||
    this._parser.on('error', this.emit.bind(this, 'error'));
 | 
			
		||||
 | 
			
		||||
    let lastTag;
 | 
			
		||||
    let currtime = 0;
 | 
			
		||||
    let seq = 0;
 | 
			
		||||
    let segmentTemplate;
 | 
			
		||||
    let timescale, offset, duration, baseURL;
 | 
			
		||||
    let timeline = [];
 | 
			
		||||
    let getSegments = false;
 | 
			
		||||
    let isStatic;
 | 
			
		||||
    let treeLevel;
 | 
			
		||||
    let periodStart;
 | 
			
		||||
 | 
			
		||||
    const tmpl = (str) => {
 | 
			
		||||
      const context = {
 | 
			
		||||
        RepresentationID: targetID,
 | 
			
		||||
        Number: seq,
 | 
			
		||||
        Time: currtime,
 | 
			
		||||
      };
 | 
			
		||||
      return str.replace(/\$(\w+)\$/g, (m, p1) => context[p1]);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    this._parser.on('opentag', (node) => {
 | 
			
		||||
      switch (node.name) {
 | 
			
		||||
        case 'mpd':
 | 
			
		||||
          currtime =
 | 
			
		||||
            new Date(node.attributes.availabilitystarttime).getTime();
 | 
			
		||||
          isStatic = node.attributes.type !== 'dynamic';
 | 
			
		||||
          break;
 | 
			
		||||
        case 'period':
 | 
			
		||||
          // Reset everything on <Period> tag.
 | 
			
		||||
          seq = 0;
 | 
			
		||||
          timescale = 1000;
 | 
			
		||||
          duration = 0;
 | 
			
		||||
          offset = 0;
 | 
			
		||||
          baseURL = [];
 | 
			
		||||
          treeLevel = 0;
 | 
			
		||||
          periodStart = parseTime.durationStr(node.attributes.start) || 0;
 | 
			
		||||
          break;
 | 
			
		||||
        case 'segmentlist':
 | 
			
		||||
          seq = parseInt(node.attributes.startnumber) || seq;
 | 
			
		||||
          timescale = parseInt(node.attributes.timescale) || timescale;
 | 
			
		||||
          duration = parseInt(node.attributes.duration) || duration;
 | 
			
		||||
          offset = parseInt(node.attributes.presentationtimeoffset) || offset;
 | 
			
		||||
          break;
 | 
			
		||||
        case 'segmenttemplate':
 | 
			
		||||
          segmentTemplate = node.attributes;
 | 
			
		||||
          seq = parseInt(node.attributes.startnumber) || seq;
 | 
			
		||||
          timescale = parseInt(node.attributes.timescale) || timescale;
 | 
			
		||||
          break;
 | 
			
		||||
        case 'segmenttimeline':
 | 
			
		||||
        case 'baseurl':
 | 
			
		||||
          lastTag = node.name;
 | 
			
		||||
          break;
 | 
			
		||||
        case 's':
 | 
			
		||||
          timeline.push([
 | 
			
		||||
            parseInt(node.attributes.d),
 | 
			
		||||
            parseInt(node.attributes.r)
 | 
			
		||||
          ]);
 | 
			
		||||
          break;
 | 
			
		||||
        case 'adaptationset':
 | 
			
		||||
        case 'representation':
 | 
			
		||||
          treeLevel++;
 | 
			
		||||
          if (targetID == null) {
 | 
			
		||||
            targetID = node.attributes.id;
 | 
			
		||||
          }
 | 
			
		||||
          getSegments = node.attributes.id === targetID + '';
 | 
			
		||||
          if (getSegments) {
 | 
			
		||||
            if (periodStart) {
 | 
			
		||||
              currtime += periodStart;
 | 
			
		||||
            }
 | 
			
		||||
            if (offset) {
 | 
			
		||||
              currtime -= offset / timescale * 1000;
 | 
			
		||||
            }
 | 
			
		||||
            this.emit('starttime', currtime);
 | 
			
		||||
          }
 | 
			
		||||
          if (getSegments && segmentTemplate && timeline.length) {
 | 
			
		||||
            if (segmentTemplate.initialization) {
 | 
			
		||||
              this.emit('item', {
 | 
			
		||||
                url: baseURL.filter(s => !!s).join('') +
 | 
			
		||||
                  tmpl(segmentTemplate.initialization),
 | 
			
		||||
                seq: seq - 1,
 | 
			
		||||
                duration: 0,
 | 
			
		||||
              });
 | 
			
		||||
            }
 | 
			
		||||
            for (let [duration, repeat] of timeline) {
 | 
			
		||||
              duration = duration / timescale * 1000;
 | 
			
		||||
              repeat = repeat || 1;
 | 
			
		||||
              for (let i = 0; i < repeat; i++) {
 | 
			
		||||
                this.emit('item', {
 | 
			
		||||
                  url: baseURL.filter(s => !!s).join('') +
 | 
			
		||||
                    tmpl(segmentTemplate.media),
 | 
			
		||||
                  seq: seq++,
 | 
			
		||||
                  duration,
 | 
			
		||||
                });
 | 
			
		||||
                currtime += duration;
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        case 'initialization':
 | 
			
		||||
          if (getSegments) {
 | 
			
		||||
            this.emit('item', {
 | 
			
		||||
              url: baseURL.filter(s => !!s).join('') + node.attributes.sourceurl,
 | 
			
		||||
              seq: seq++,
 | 
			
		||||
              duration: 0,
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        case 'segmenturl':
 | 
			
		||||
          if (getSegments) {
 | 
			
		||||
            let tl = timeline.shift();
 | 
			
		||||
            let segmentDuration = (tl && tl[0] || duration) / timescale * 1000;
 | 
			
		||||
            this.emit('item', {
 | 
			
		||||
              url: baseURL.filter(s => !!s).join('') + node.attributes.media,
 | 
			
		||||
              seq: seq++,
 | 
			
		||||
              duration: segmentDuration,
 | 
			
		||||
            });
 | 
			
		||||
            currtime += segmentDuration;
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    const onEnd = () => {
 | 
			
		||||
      if (isStatic) { this.emit('endlist'); }
 | 
			
		||||
      if (!getSegments) {
 | 
			
		||||
        this.emit('error', Error(`Representation '${targetID}' not found`));
 | 
			
		||||
      }
 | 
			
		||||
      this.emit('end');
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    this._parser.on('closetag', (tagName) => {
 | 
			
		||||
      switch (tagName) {
 | 
			
		||||
        case 'adaptationset':
 | 
			
		||||
        case 'representation':
 | 
			
		||||
          treeLevel--;
 | 
			
		||||
          break;
 | 
			
		||||
        case 'segmentlist':
 | 
			
		||||
          if (getSegments) {
 | 
			
		||||
            this.emit('endearly');
 | 
			
		||||
            onEnd();
 | 
			
		||||
            this._parser.removeAllListeners();
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this._parser.on('text', (text) => {
 | 
			
		||||
      if (lastTag === 'baseurl') {
 | 
			
		||||
        baseURL[treeLevel] = text;
 | 
			
		||||
        lastTag = null;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this.on('finish', onEnd);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _write(chunk, encoding, callback) {
 | 
			
		||||
    this._parser.write(chunk, encoding);
 | 
			
		||||
    callback();
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										178
									
								
								node_modules/m3u8stream/lib/index.js
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										178
									
								
								node_modules/m3u8stream/lib/index.js
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,178 +0,0 @@
 | 
			
		||||
const PassThrough   = require('stream').PassThrough;
 | 
			
		||||
const urlResolve    = require('url').resolve;
 | 
			
		||||
const miniget       = require('miniget');
 | 
			
		||||
const m3u8Parser    = require('./m3u8-parser');
 | 
			
		||||
const DashMPDParser = require('./dash-mpd-parser');
 | 
			
		||||
const Queue         = require('./queue');
 | 
			
		||||
const parseTime     = require('./parse-time');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {string} playlistURL
 | 
			
		||||
 * @param {Object} options
 | 
			
		||||
 * @return {stream.Readable}
 | 
			
		||||
 */
 | 
			
		||||
module.exports = (playlistURL, options) => {
 | 
			
		||||
  const stream = new PassThrough();
 | 
			
		||||
  options = options || {};
 | 
			
		||||
  const chunkReadahead = options.chunkReadahead || 3;
 | 
			
		||||
  const liveBuffer = options.liveBuffer || 20000; // 20 seconds
 | 
			
		||||
  const requestOptions = options.requestOptions;
 | 
			
		||||
  const Parser = {
 | 
			
		||||
    'm3u8': m3u8Parser,
 | 
			
		||||
    'dash-mpd': DashMPDParser,
 | 
			
		||||
  }[options.parser || (/\.mpd$/.test(playlistURL) ? 'dash-mpd' : 'm3u8')];
 | 
			
		||||
  if (!Parser) {
 | 
			
		||||
    throw TypeError(`parser '${options.parser}' not supported`);
 | 
			
		||||
  }
 | 
			
		||||
  let relativeBegin = typeof options.begin === 'string';
 | 
			
		||||
  let begin = relativeBegin ?
 | 
			
		||||
    parseTime.humanStr(options.begin) :
 | 
			
		||||
    Math.max(options.begin - liveBuffer, 0) || 0;
 | 
			
		||||
  let liveBegin = Date.now() - liveBuffer;
 | 
			
		||||
 | 
			
		||||
  let currSegment;
 | 
			
		||||
  const streamQueue = new Queue((req, callback) => {
 | 
			
		||||
    currSegment = req;
 | 
			
		||||
    // Count the size manually, since the `content-length` header is not
 | 
			
		||||
    // always there.
 | 
			
		||||
    let size = 0;
 | 
			
		||||
    req.on('data', (chunk) => size += chunk.length);
 | 
			
		||||
    req.pipe(stream, { end: false });
 | 
			
		||||
    req.on('end', () => callback(size));
 | 
			
		||||
  }, { concurrency: 1 });
 | 
			
		||||
 | 
			
		||||
  let segmentNumber = 0;
 | 
			
		||||
  let downloaded = 0;
 | 
			
		||||
  const requestQueue = new Queue((segment, callback) => {
 | 
			
		||||
    let req = miniget(urlResolve(playlistURL, segment.url), requestOptions);
 | 
			
		||||
    req.on('error', callback);
 | 
			
		||||
    streamQueue.push(req, (size) => {
 | 
			
		||||
      downloaded += size;
 | 
			
		||||
      stream.emit('progress', {
 | 
			
		||||
        num: ++segmentNumber,
 | 
			
		||||
        size: size,
 | 
			
		||||
        url: segment.url,
 | 
			
		||||
        duration: segment.duration,
 | 
			
		||||
      }, requestQueue.total, downloaded);
 | 
			
		||||
      callback();
 | 
			
		||||
    });
 | 
			
		||||
  }, { concurrency: chunkReadahead });
 | 
			
		||||
 | 
			
		||||
  const onError = (err) => {
 | 
			
		||||
    if (ended) { return; }
 | 
			
		||||
    stream.emit('error', err);
 | 
			
		||||
    // Stop on any error.
 | 
			
		||||
    stream.end();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // When to look for items again.
 | 
			
		||||
  let refreshThreshold;
 | 
			
		||||
  let minRefreshTime;
 | 
			
		||||
  let refreshTimeout;
 | 
			
		||||
  let fetchingPlaylist = false;
 | 
			
		||||
  let ended = false;
 | 
			
		||||
  let isStatic = false;
 | 
			
		||||
  let lastRefresh;
 | 
			
		||||
 | 
			
		||||
  const onQueuedEnd = (err) => {
 | 
			
		||||
    currSegment = null;
 | 
			
		||||
    if (err) {
 | 
			
		||||
      onError(err);
 | 
			
		||||
    } else if (!fetchingPlaylist && !ended && !isStatic &&
 | 
			
		||||
      requestQueue.tasks.length + requestQueue.active === refreshThreshold) {
 | 
			
		||||
      let ms = Math.max(0, minRefreshTime - (Date.now() - lastRefresh));
 | 
			
		||||
      refreshTimeout = setTimeout(refreshPlaylist, ms);
 | 
			
		||||
    } else if ((ended || isStatic) &&
 | 
			
		||||
      !requestQueue.tasks.length && !requestQueue.active) {
 | 
			
		||||
      stream.end();
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  let currPlaylist;
 | 
			
		||||
  let lastSeq;
 | 
			
		||||
  const refreshPlaylist = () => {
 | 
			
		||||
    fetchingPlaylist = true;
 | 
			
		||||
    lastRefresh = Date.now();
 | 
			
		||||
    currPlaylist = miniget(playlistURL, requestOptions);
 | 
			
		||||
    currPlaylist.on('error', onError);
 | 
			
		||||
    const parser = currPlaylist.pipe(new Parser(options.id));
 | 
			
		||||
    let starttime = null;
 | 
			
		||||
    parser.on('starttime', (a) => {
 | 
			
		||||
      starttime = a;
 | 
			
		||||
      if (relativeBegin && begin >= 0) {
 | 
			
		||||
        begin += starttime;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    parser.on('endlist', () => { isStatic = true; });
 | 
			
		||||
    parser.on('endearly', () => { currPlaylist.unpipe(parser); });
 | 
			
		||||
 | 
			
		||||
    let addedItems = [];
 | 
			
		||||
    let liveAddedItems = [];
 | 
			
		||||
    const addItem = (item, isLive) => {
 | 
			
		||||
      if (item.seq <= lastSeq) { return; }
 | 
			
		||||
      lastSeq = item.seq;
 | 
			
		||||
      begin = item.time;
 | 
			
		||||
      requestQueue.push(item, onQueuedEnd);
 | 
			
		||||
      addedItems.push(item);
 | 
			
		||||
      if (isLive) {
 | 
			
		||||
        liveAddedItems.push(item);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let tailedItems = [], tailedItemsDuration = 0;
 | 
			
		||||
    parser.on('item', (item) => {
 | 
			
		||||
      item.time = starttime;
 | 
			
		||||
      if (!starttime || begin <= item.time) {
 | 
			
		||||
        addItem(item, liveBegin <= item.time);
 | 
			
		||||
      } else {
 | 
			
		||||
        tailedItems.push(item);
 | 
			
		||||
        tailedItemsDuration += item.duration;
 | 
			
		||||
        // Only keep the last `liveBuffer` of items.
 | 
			
		||||
        while (tailedItems.length > 1 &&
 | 
			
		||||
          tailedItemsDuration - tailedItems[0].duration > liveBuffer) {
 | 
			
		||||
          tailedItemsDuration -= tailedItems.shift().duration;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      starttime += item.duration;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    parser.on('end', () => {
 | 
			
		||||
      currPlaylist = null;
 | 
			
		||||
      // If we are too ahead of the stream, make sure to get the
 | 
			
		||||
      // latest available items with a small buffer.
 | 
			
		||||
      if (!addedItems.length && tailedItems.length) {
 | 
			
		||||
        tailedItems.forEach((item) => { addItem(item, true); });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Refresh the playlist when remaining segments get low.
 | 
			
		||||
      refreshThreshold = Math.max(1, Math.ceil(addedItems.length * 0.01));
 | 
			
		||||
 | 
			
		||||
      // Throttle refreshing the playlist by looking at the duration
 | 
			
		||||
      // of live items added on this refresh.
 | 
			
		||||
      minRefreshTime =
 | 
			
		||||
        addedItems.reduce(((total, item) => item.duration + total), 0);
 | 
			
		||||
 | 
			
		||||
      fetchingPlaylist = false;
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
  refreshPlaylist();
 | 
			
		||||
 | 
			
		||||
  stream.end = () => {
 | 
			
		||||
    ended = true;
 | 
			
		||||
    streamQueue.die();
 | 
			
		||||
    requestQueue.die();
 | 
			
		||||
    clearTimeout(refreshTimeout);
 | 
			
		||||
    if (currPlaylist) {
 | 
			
		||||
      currPlaylist.unpipe();
 | 
			
		||||
      currPlaylist.abort();
 | 
			
		||||
    }
 | 
			
		||||
    if (currSegment) {
 | 
			
		||||
      currSegment.unpipe();
 | 
			
		||||
      currSegment.abort();
 | 
			
		||||
    }
 | 
			
		||||
    PassThrough.prototype.end.call(stream);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return stream;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										67
									
								
								node_modules/m3u8stream/lib/m3u8-parser.js
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										67
									
								
								node_modules/m3u8stream/lib/m3u8-parser.js
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,67 +0,0 @@
 | 
			
		||||
const Writable = require('stream').Writable;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A very simple m3u8 playlist file parser that detects tags and segments.
 | 
			
		||||
 *
 | 
			
		||||
 * @extends WritableStream
 | 
			
		||||
 * @constructor
 | 
			
		||||
 */
 | 
			
		||||
module.exports = class m3u8Parser extends Writable {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super();
 | 
			
		||||
    this._lastLine = '';
 | 
			
		||||
    this._seq = 0;
 | 
			
		||||
    this._nextItemDuration = null;
 | 
			
		||||
    this.on('finish', () => {
 | 
			
		||||
      this._parseLine(this._lastLine);
 | 
			
		||||
      this.emit('end');
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _parseLine(line) {
 | 
			
		||||
    let match = line.match(/^#(EXT[A-Z0-9-]+)(?::(.*))?/);
 | 
			
		||||
    if (match) {
 | 
			
		||||
      // This is a tag.
 | 
			
		||||
      const tag = match[1];
 | 
			
		||||
      const value = match[2] || null;
 | 
			
		||||
      switch (tag) {
 | 
			
		||||
        case 'EXT-X-PROGRAM-DATE-TIME':
 | 
			
		||||
          this.emit('starttime', new Date(value).getTime());
 | 
			
		||||
          break;
 | 
			
		||||
        case 'EXT-X-MEDIA-SEQUENCE':
 | 
			
		||||
          this._seq = parseInt(value);
 | 
			
		||||
          break;
 | 
			
		||||
        case 'EXTINF':
 | 
			
		||||
          this._nextItemDuration =
 | 
			
		||||
            Math.round(parseFloat(value.split(',')[0]) * 1000);
 | 
			
		||||
          break;
 | 
			
		||||
        case 'EXT-X-ENDLIST':
 | 
			
		||||
          this.emit('endlist');
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    } else if (!/^#/.test(line) && line.trim()) {
 | 
			
		||||
      // This is a segment
 | 
			
		||||
      this.emit('item', {
 | 
			
		||||
        url: line.trim(),
 | 
			
		||||
        seq: this._seq++,
 | 
			
		||||
        duration: this._nextItemDuration,
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _write(chunk, encoding, callback) {
 | 
			
		||||
    let lines = chunk.toString('utf8').split('\n');
 | 
			
		||||
    if (this._lastLine) { lines[0] = this._lastLine + lines[0]; }
 | 
			
		||||
    lines.forEach((line, i) => {
 | 
			
		||||
      if (i < lines.length - 1) {
 | 
			
		||||
        this._parseLine(line);
 | 
			
		||||
      } else {
 | 
			
		||||
        // Save the last line in case it has been broken up.
 | 
			
		||||
        this._lastLine = line;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    callback();
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										51
									
								
								node_modules/m3u8stream/lib/parse-time.js
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										51
									
								
								node_modules/m3u8stream/lib/parse-time.js
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,51 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Converts human friendly time to milliseconds. Supports the format
 | 
			
		||||
 * 00:00:00.000 for hours, minutes, seconds, and milliseconds respectively.
 | 
			
		||||
 * And 0ms, 0s, 0m, 0h, and together 1m1s.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {string|number} time
 | 
			
		||||
 * @return {number}
 | 
			
		||||
 */
 | 
			
		||||
const numberFormat = /^\d+$/;
 | 
			
		||||
const timeFormat = /^(?:(?:(\d+):)?(\d{1,2}):)?(\d{1,2})(?:\.(\d{3}))?$/;
 | 
			
		||||
const timeUnits = {
 | 
			
		||||
  ms: 1,
 | 
			
		||||
  s: 1000,
 | 
			
		||||
  m: 60000,
 | 
			
		||||
  h: 3600000,
 | 
			
		||||
};
 | 
			
		||||
exports.humanStr = (time) => {
 | 
			
		||||
  if (typeof time === 'number') { return time; }
 | 
			
		||||
  if (numberFormat.test(time)) { return +time; }
 | 
			
		||||
  const firstFormat = timeFormat.exec(time);
 | 
			
		||||
  if (firstFormat) {
 | 
			
		||||
    return +(firstFormat[1] || 0) * timeUnits.h +
 | 
			
		||||
      +(firstFormat[2] || 0) * timeUnits.m +
 | 
			
		||||
      +firstFormat[3] * timeUnits.s +
 | 
			
		||||
      +(firstFormat[4] || 0);
 | 
			
		||||
  } else {
 | 
			
		||||
    let total = 0;
 | 
			
		||||
    const r = /(-?\d+)(ms|s|m|h)/g;
 | 
			
		||||
    let rs;
 | 
			
		||||
    while ((rs = r.exec(time)) != null) {
 | 
			
		||||
      total += +rs[1] * timeUnits[rs[2]];
 | 
			
		||||
    }
 | 
			
		||||
    return total;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Parses a duration string in the form of "123.456S", returns milliseconds.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {string} time
 | 
			
		||||
 * @return {number}
 | 
			
		||||
 */
 | 
			
		||||
exports.durationStr = (time) => {
 | 
			
		||||
  let total = 0;
 | 
			
		||||
  const r = /(\d+(?:\.\d+)?)(S|M|H)/g;
 | 
			
		||||
  let rs;
 | 
			
		||||
  while ((rs = r.exec(time)) != null) {
 | 
			
		||||
    total += +rs[1] * timeUnits[rs[2].toLowerCase()];
 | 
			
		||||
  }
 | 
			
		||||
  return total;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										55
									
								
								node_modules/m3u8stream/lib/queue.js
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										55
									
								
								node_modules/m3u8stream/lib/queue.js
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,55 +0,0 @@
 | 
			
		||||
module.exports = class Queue {
 | 
			
		||||
  /**
 | 
			
		||||
   * A really simple queue with concurrency.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Function(Object, Function)} worker
 | 
			
		||||
   * @param {Object} options
 | 
			
		||||
   */
 | 
			
		||||
  constructor(worker, options) {
 | 
			
		||||
    this._worker = worker;
 | 
			
		||||
    options = options || {};
 | 
			
		||||
    this._concurrency = options.concurrency || 1;
 | 
			
		||||
    this.tasks = [];
 | 
			
		||||
    this.total = 0;
 | 
			
		||||
    this.active = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Push a task to the queue.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Object} item
 | 
			
		||||
   * @param {Function(Error)} callback
 | 
			
		||||
   */
 | 
			
		||||
  push(item, callback) {
 | 
			
		||||
    this.tasks.push({ item, callback });
 | 
			
		||||
    this.total++;
 | 
			
		||||
    this._next();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Process next job in queue.
 | 
			
		||||
   */
 | 
			
		||||
  _next() {
 | 
			
		||||
    if (this.active >= this._concurrency || !this.tasks.length) { return; }
 | 
			
		||||
    const { item, callback } = this.tasks.shift();
 | 
			
		||||
    let callbackCalled = false;
 | 
			
		||||
    this.active++;
 | 
			
		||||
    this._worker(item, (err) => {
 | 
			
		||||
      if (callbackCalled) { return; }
 | 
			
		||||
      this.active--;
 | 
			
		||||
      callbackCalled = true;
 | 
			
		||||
      if (callback) { callback(err); }
 | 
			
		||||
      this._next();
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Stops processing queued jobs.
 | 
			
		||||
   */
 | 
			
		||||
  die() {
 | 
			
		||||
    this.tasks = [];
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										72
									
								
								node_modules/m3u8stream/package.json
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										72
									
								
								node_modules/m3u8stream/package.json
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,72 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "_from": "m3u8stream@^0.6.2",
 | 
			
		||||
  "_id": "m3u8stream@0.6.2",
 | 
			
		||||
  "_inBundle": false,
 | 
			
		||||
  "_integrity": "sha512-WsuM2bd5pPN80xvfrB+1DZqr4M7+kJl8byi6+ZCy6cmVjEiHhmr/desN53Ngsa6Hs13kYumeVgT4wL0oIJ+v6g==",
 | 
			
		||||
  "_location": "/m3u8stream",
 | 
			
		||||
  "_phantomChildren": {},
 | 
			
		||||
  "_requested": {
 | 
			
		||||
    "type": "range",
 | 
			
		||||
    "registry": true,
 | 
			
		||||
    "raw": "m3u8stream@^0.6.2",
 | 
			
		||||
    "name": "m3u8stream",
 | 
			
		||||
    "escapedName": "m3u8stream",
 | 
			
		||||
    "rawSpec": "^0.6.2",
 | 
			
		||||
    "saveSpec": null,
 | 
			
		||||
    "fetchSpec": "^0.6.2"
 | 
			
		||||
  },
 | 
			
		||||
  "_requiredBy": [
 | 
			
		||||
    "/ytdl-core"
 | 
			
		||||
  ],
 | 
			
		||||
  "_resolved": "https://registry.npmjs.org/m3u8stream/-/m3u8stream-0.6.2.tgz",
 | 
			
		||||
  "_shasum": "c2c62ddce88156d80378dd59be010c8b8e9d120b",
 | 
			
		||||
  "_spec": "m3u8stream@^0.6.2",
 | 
			
		||||
  "_where": "C:\\Users\\matia\\Musix test\\node_modules\\ytdl-core",
 | 
			
		||||
  "author": {
 | 
			
		||||
    "name": "fent",
 | 
			
		||||
    "url": "https://github.com/fent"
 | 
			
		||||
  },
 | 
			
		||||
  "bugs": {
 | 
			
		||||
    "url": "https://github.com/fent/node-m3u8stream/issues"
 | 
			
		||||
  },
 | 
			
		||||
  "bundleDependencies": false,
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "miniget": "^1.4.0",
 | 
			
		||||
    "sax": "^1.2.4"
 | 
			
		||||
  },
 | 
			
		||||
  "deprecated": false,
 | 
			
		||||
  "description": "Reads segments from a m3u8 or dash playlist into a consumable stream.",
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "istanbul": "^0.4.5",
 | 
			
		||||
    "lolex": "^3.0.0",
 | 
			
		||||
    "mocha": "^5.0.0",
 | 
			
		||||
    "nock": "^10.0.0"
 | 
			
		||||
  },
 | 
			
		||||
  "engines": {
 | 
			
		||||
    "node": ">=6"
 | 
			
		||||
  },
 | 
			
		||||
  "files": [
 | 
			
		||||
    "lib"
 | 
			
		||||
  ],
 | 
			
		||||
  "homepage": "https://github.com/fent/node-m3u8stream#readme",
 | 
			
		||||
  "keywords": [
 | 
			
		||||
    "m3u8",
 | 
			
		||||
    "hls",
 | 
			
		||||
    "dash",
 | 
			
		||||
    "live",
 | 
			
		||||
    "playlist",
 | 
			
		||||
    "segments",
 | 
			
		||||
    "stream"
 | 
			
		||||
  ],
 | 
			
		||||
  "license": "MIT",
 | 
			
		||||
  "main": "./lib/index.js",
 | 
			
		||||
  "name": "m3u8stream",
 | 
			
		||||
  "repository": {
 | 
			
		||||
    "type": "git",
 | 
			
		||||
    "url": "git://github.com/fent/node-m3u8stream.git"
 | 
			
		||||
  },
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "test": "istanbul cover node_modules/.bin/_mocha -- test/*-test.js"
 | 
			
		||||
  },
 | 
			
		||||
  "version": "0.6.2"
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user