mirror of
				https://github.com/musix-org/musix-oss
				synced 2025-11-04 09:49:32 +00:00 
			
		
		
		
	Updated
This commit is contained in:
		
							
								
								
									
										178
									
								
								node_modules/m3u8stream/lib/dash-mpd-parser.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								node_modules/m3u8stream/lib/dash-mpd-parser.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,178 @@
 | 
			
		||||
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
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								node_modules/m3u8stream/lib/index.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,178 @@
 | 
			
		||||
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
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								node_modules/m3u8stream/lib/m3u8-parser.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
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
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								node_modules/m3u8stream/lib/parse-time.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 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
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								node_modules/m3u8stream/lib/queue.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
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 = [];
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
		Reference in New Issue
	
	Block a user