Skip to content

Instantly share code, notes, and snippets.

@erdesigns-eu
Last active August 5, 2022 21:25
Show Gist options
  • Save erdesigns-eu/c7586e5e6ae135e41f5230f3ee97a2f0 to your computer and use it in GitHub Desktop.
Save erdesigns-eu/c7586e5e6ae135e41f5230f3ee97a2f0 to your computer and use it in GitHub Desktop.
M3U Writer Class
/**
* Constantes used for constructing.
*/
const EXTGRP = 'EXTGRP';
const EXTPLS = 'PLAYLIST';
const GROUPTITLE = 'group-title';
/**
* M3U Writer Class
*/
export class m3uWriter {
// New Line Character(s)
#newLine = '\n';
// Use group-title directives?
#groupDirectives = false;
// Excluded attributes
#attributes = [];
// Excluded directives
#directives = [];
// Class constructor
constructor(options) {
const vm = this;
// Set newline character(s)
if (options && options.newLine && options.newLine.length) {
vm.#newLine = `${options.newLine}`;
}
// Set use group-title directives
if (options && options.groupDirectives && options.groupDirectives === true) {
vm.#groupDirectives = options.groupDirectives;
}
// Set attributes
if (options && options.attributes && Array.isArray(options.attributes)) {
vm.#attributes = [...options.attributes];
}
// Set directives
if (options && options.directives && Array.isArray(options.directives)) {
vm.#directives = [...options.directives];
}
}
// Format attribute
formatAttribute(attribute, value) {
return `${attribute.toLowerCase()}="${value}"`;
}
// Format directive
formatDirective(directive, value) {
return `#${directive.toUpperCase()}:${value}`;
}
// Format #EXTINF line from stream
formatStream(stream, group) {
const vm = this;
let res = `#EXTINF:${stream.length}`;
// Add attributes
stream.attributes.forEach((attribute) => {
if (!vm.#attributes.includes(attribute.attribute)) {
res += ` ${vm.formatAttribute(attribute.attribute, attribute.value)}`;
}
});
// Add group-title?
if (group && vm.#groupDirectives !== true) {
res += ` ${vm.formatAttribute(GROUPTITLE, group)}`;
}
// Add title
res += `,${stream.title}`
return res;
}
write(m3u) {
const vm = this;
// Return a promise (Async)
return new Promise((resolve, reject) => {
// Playlist output
let playlist = '#EXTM3U';
if (m3u.attributes && m3u.attributes.length) {
m3u.attributes.forEach((attribute) => {
playlist += ` ${vm.formatAttribute(attribute.attribute, attribute.value)}`;
});
}
// Function: Add line to playlist
const addLine = (line) => {
playlist += vm.#newLine;
playlist += line;
}
// Add playlist title
if (m3u.title && m3u.title.length) {
addLine(vm.formatDirective(EXTPLS, m3u.title));
}
// Add streams
if (m3u.groups && Array.isArray(m3u.groups)) {
// Loop over groups
m3u.groups.forEach((group) => {
// Loop over streams in group
group.streams.forEach((stream) => {
// Add stream #EXTINF
addLine(vm.formatStream(stream, group.title));
// Add group directive?
if (vm.#groupDirectives === true) {
addLine(vm.formatDirective(EXTGRP, group.title));
}
// Add directives
stream.directives.forEach((directive) => {
if (!vm.#directives.includes(directive.directive)) {
addLine(vm.formatDirective(directive.directive, directive.value));
}
});
// Add stream URL/Filename
addLine(stream.url);
});
});
} else {
reject('error.no-groups-defined');
return;
}
// Finished constructing M3U playlist
resolve(playlist);
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment