@iptv/playlist
Version:
An extremely fast M3U playlist parser and generator for Node and the browser.
121 lines (120 loc) • 4.27 kB
JavaScript
const x = {
"tvg-id": "tvgId",
"tvg-name": "tvgName",
"tvg-language": "tvgLanguage",
"tvg-logo": "tvgLogo",
"tvg-url": "tvgUrl",
"tvg-rec,": "tvgRec",
"group-title": "groupTitle",
timeshift: "timeshift",
catchup: "catchup",
"catchup-days": "catchupDays",
"catchup-source": "catchupSource",
"x-tvg-url": "xTvgUrl",
"url-tvg": "urlTvg"
}, $ = 32, O = 35, I = 61, U = 58, N = 44, T = 45, b = 49, w = 50, C = 51, L = 52, M = 53, E = 54, S = 55, y = 56, D = 57, P = 48, A = 10, R = 9, X = 13, v = 69, l = 88, f = 84, j = 73, k = 78, q = 70, H = 77, z = 72, Q = 80, V = 85, B = 104, G = 112, p = 116, J = [
P,
b,
w,
C,
L,
M,
E,
S,
y,
D,
T
];
function K(r) {
const e = [], c = {};
let t = 0, o = {}, g = "", s = null, u = null;
for (; t < r.length; ) {
const a = r.charCodeAt(t);
if (t === u)
throw new Error("Infinite loop detected while parsing M3U file");
if (u = t, a === $ || a === R || a === X || a === A) {
t++, a === A && (g = "");
continue;
}
let n = r.indexOf(`
`, t);
if (n === -1 && (n = r.length), J.includes(a)) {
let h = r.indexOf(" ", t), d = r.indexOf(",", t), i = -1;
d > -1 && (i = d), h > -1 && d > -1 && h < d && (i = h), d === -1 && h > -1 && (i = h), i === -1 && (i = n), o.duration = parseInt(
r.slice(t, i),
10
), t = i;
continue;
}
if (a === O)
if (s = null, r.charCodeAt(t + 1) === v && r.charCodeAt(t + 2) === l && r.charCodeAt(t + 3) === f && r.charCodeAt(t + 4) === H && r.charCodeAt(t + 5) === C && r.charCodeAt(t + 6) === V)
t += 6, s = "header";
else if (r.charCodeAt(t + 1) === v && r.charCodeAt(t + 2) === l && r.charCodeAt(t + 3) === f && r.charCodeAt(t + 4) === j && r.charCodeAt(t + 5) === k && r.charCodeAt(t + 6) === q && r.charCodeAt(t + 7) === U)
t += 7, s = "channel", o = {};
else {
t = n;
continue;
}
if (a === B || a === z) {
if (
// http
!((r.charCodeAt(t + 1) === p || r.charCodeAt(t + 1) === f) && (r.charCodeAt(t + 2) === p || r.charCodeAt(t + 2) === f) && (r.charCodeAt(t + 3) === G || r.charCodeAt(t + 3) === Q)) || !(o.name || o.tvgName || o.tvgId)
) {
t = n;
continue;
}
s = "http";
const h = r.slice(t, n).trim();
o.url || (o.url = h), o.urls || (o.urls = []), o.urls = [...o.urls, h], t = n, e.indexOf(o) === -1 && e.push(o);
continue;
}
if (a === N) {
s = "channel", o.name = r.slice(t + 1, n).trim(), t = n, s = null;
continue;
}
if (a > 64 && a < 91 || a > 96 && a < 123 || a === 45) {
const h = r.indexOf("=", t);
if (h === -1 || h > n) {
t = n, g = "";
continue;
}
g = r.slice(
t,
h
), t = h;
continue;
}
if (a === I) {
t = t + 2;
const h = r.indexOf('"', t), d = r.slice(
t,
h
);
t = h;
const i = x[g];
i ? s === "header" ? c[i] = d : o[i] = d : (o.extras = o.extras || {}, o.extras[g] = d), g = "";
}
t++;
}
return { channels: e, headers: c };
}
function W(r) {
let e = "#EXTM3U";
if (r.headers && Object.entries(r.headers))
for (const [c, t] of Object.entries(r.headers))
e += ` ${c}="${t}"`;
for (const c of r.channels)
if (c.url) {
if (e += `
#EXTINF:`, e += c != null && c.duration ? c.duration : "-1", c.tvgId && (e += ` tvg-id="${c.tvgId}"`), c.tvgName && (e += ` tvg-name="${c.tvgName}"`), c.tvgLanguage && (e += ` tvg-language="${c.tvgLanguage}"`), c.tvgLogo && (e += ` tvg-logo="${c.tvgLogo}"`), c.tvgRec && (e += ` tvg-rec="${c.tvgRec}"`), c.tvgChno && (e += ` tvg-chno="${c.tvgChno}"`), c.groupTitle && (e += ` group-title="${c.groupTitle}"`), c.tvgUrl && (e += ` tvg-url="${c.tvgUrl}"`), c.timeshift && (e += ` timeshift="${c.timeshift}"`), c.catchup && (e += ` catchup="${c.catchup}"`), c.catchupDays && (e += ` catchup-days="${c.catchupDays}"`), c.catchupSource && (e += ` catchup-source="${c.catchupSource}"`), c.extras)
for (const [t, o] of Object.entries(c.extras))
e += ` ${t}="${o}"`;
e += ",", c.name && (e += c.name), e += `
${c.url}`;
}
return e;
}
export {
K as parseM3U,
W as writeM3U
};