m3u-parser-generator
Version:
Library to parse and generate m3u or m3u8 IPTV playlist files
102 lines (94 loc) • 4.2 kB
text/typescript
import {
M3uCustomData,
M3uDirectives,
M3uMedia,
M3uPlaylist,
M3uAttributes,
DEFAULT_MEDIA_DURATION
} from "./m3u-playlist";
/**
* M3u generator class to generate m3u playlist string from playlist object
*/
export class M3uGenerator {
/**
* Generate is static method to generate m3u playlist string from playlist object
* @param playlist - playlist object to generate m3u playlist string
* @returns final m3u playlist string
* @example
* ```ts
* const playlist = new M3uPlaylist();
* playlist.title = 'Test playlist';
* M3uGenerator.generate(playlist);
* ```
*/
static generate(playlist: M3uPlaylist): string {
const pls = playlist.title ? `${M3uDirectives.PLAYLIST}:${playlist.title}` : undefined;
const customData = this.getCustomDataDirective(playlist.customData);
const medias = playlist.medias.map(item => this.getMedia(item)).join('\n');
const attributesString = this.getAttributes(playlist.attributes);
return [M3uDirectives.EXTM3U + attributesString, pls, customData, medias].filter(item => item).join('\n');
}
/**
* Get generated media part string from m3u playlist media object
* @param media - media object
* @returns media part string with info, group and location each on separated line
* @private
*/
private static getMedia(media: M3uMedia): string {
const attributesString = this.getAttributes(media.attributes);
const info = this.shouldAddInfoDirective(media, attributesString) ? `${M3uDirectives.EXTINF}:${media.duration}${attributesString},${media.name}` : null;
const group = media.group ? `${M3uDirectives.EXTGRP}:${media.group}` : null;
const bytes = media.bytes ? `${M3uDirectives.EXTBYT}:${media.bytes}` : null;
const image = media.image ? `${M3uDirectives.EXTIMG}:${media.image}` : null;
const album = media.album ? `${M3uDirectives.EXTALB}:${media.album}` : null;
const artist = media.artist ? `${M3uDirectives.EXTART}:${media.artist}` : null;
const genre = media.genre ? `${M3uDirectives.EXTGENRE}:${media.genre}` : null;
const extraAttributesFromUrl = media.extraAttributesFromUrl ? `${M3uDirectives.EXTATTRFROMURL}:${media.extraAttributesFromUrl}` : null;
const extraHttpHeaders = media.extraHttpHeaders ? `${M3uDirectives.EXTHTTP}:${JSON.stringify(media.extraHttpHeaders)}` : null;
const kodiProps = media.kodiProps ? [...media.kodiProps].map(([key, value]) => `${M3uDirectives.KODIPROP}:${key}=${value}`).join('\n') : null;
const customData = this.getCustomDataDirective(media.customData);
return [
info,
group,
bytes,
image,
album,
artist,
genre,
extraAttributesFromUrl,
extraHttpHeaders,
kodiProps,
customData,
media.location
].filter(item => item).join('\n');
}
/**
* Get generated string of custom directives for both, playlist and media
* @param customData - custom data object, that represents unknown directives
* @private
*/
private static getCustomDataDirective(customData: M3uCustomData[]): string {
return customData.map(data => `${data.directive}:${data.value}`).join('\n');
}
/**
* Get generated attributes media part string from m3u attributes object
* @param attributes - attributes object
* @returns attributes generated string (attributeName="attributeValue" ...)
* @private
*/
private static getAttributes(attributes: M3uAttributes): string {
const keys = Object.keys(attributes);
return keys.length ? ' ' + keys.map(key => `${key}="${attributes[key]}"`).join(' ') : '';
}
/**
* Method to determine if we need to add info directive or not based on media object and attributes string.
* At least media duration, media name or some attributes must be present to return true
* @param media - m3u media object
* @param attributesString - m3u attributes string
* @returns boolean if we should add info directive into final media
* @private
*/
private static shouldAddInfoDirective(media: M3uMedia, attributesString: string): boolean {
return media.duration !== DEFAULT_MEDIA_DURATION || attributesString !== '' || media.name !== undefined;
}
}