You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

85 lines
2.2 KiB
JavaScript

"use strict";
const matchValue = require("match-value");
const { either, oneOrMore, EndOfInput, zeroOrMore, optional } = require("./operations");
const Newline = require("./simple/lines/newline").looseCRLF;
const RestOfLine = require("./simple/lines/rest-of-line").looseCRLF;
const Integer = require("./simple/numeric/integer");
const Decimal = require("./simple/numeric/decimal");
const spacedLine = require("./simple/template/spaced-line").looseCRLF;
const format = require("./simple/template/format");
function* MaybeEmptyLines() {
yield zeroOrMore(Newline);
}
function* Playlist() {
return yield either([ MediaPlaylist ]);
}
function* FormatHeader() {
yield spacedLine`#EXTM3U`;
}
function MetaField(namePrefix, valueParser) {
// NOTE: prefix must include `:` if required, as this varies between fields
return function* CommentField() {
yield format`#${namePrefix}`;
return yield valueParser;
};
}
function* MetaLine() {
let line = yield either([
MetaField("EXTINF:", function* MetaExtraInfo() {
// TODO: Support additional property fields in EXTINF, figure out what the correct parsing rules actually are for this
let [ duration, trackTitle ] = yield spacedLine`${Decimal},${optional(RestOfLine)}`;
return { type: "track", duration, trackTitle };
}),
MetaField("PLAYLIST:", function* MetaPlaylistTitle() {
return { type: "playlist", playlistTitle: yield RestOfLine };
}),
MetaField("EXTGRP:", function* MetaGroup() {
return { type: "groupStart", name: yield RestOfLine };
})
]);
return line;
}
function* MediaPlaylist() {
let playlistMeta = {};
yield FormatHeader;
let [ targetDuration ] = yield spacedLine`#EXT-X-TARGETDURATION:${Integer}`;
let items = yield oneOrMore(function* () {
let trackMeta = {};
let metaComments = yield zeroOrMore(MetaLine);
let url = yield RestOfLine;
yield MaybeEmptyLines;
for (let { type, ... properties } of metaComments) {
matchValue(type, {
track: () => {
Object.assign(trackMeta, properties);
},
playlist: () => {
Object.assign(playlistMeta, properties);
},
groupStart: () => {
// Ignore for now
}
});
}
return { url, ... trackMeta };
});
return { targetDuration, items, ... playlistMeta };
}
module.exports = { Playlist };