Lightweight HLS/Video downloader. Pause/Resume. AES-128. fMP4. Mobile + Desktop.
// ==UserScript==
// @name StreamGrabber
// @namespace https://github.com/streamgrabber-lite
// @version 2.4.0
// @author StreamGrabber
// @description Lightweight HLS/Video downloader. Pause/Resume. AES-128. fMP4. Mobile + Desktop.
// @license MIT
// @match *://*/*
// @exclude *://*.gov/*
// @exclude *://*.gov.*/*
// @exclude *://*.mil/*
// @exclude *://*.mil.*/*
// @exclude *://*.int/*
// @exclude *://*.edu/*
// @exclude *://*.edu.*/*
// @exclude *://*.ac.*/*
// @exclude *://*.nic.in/*
// @exclude *://localhost/*
// @exclude *://127.0.0.1/*
// @exclude *://[::1]/*
// @exclude *://10.*.*.*/*
// @exclude *://192.168.*.*/*
// @exclude *://172.1[6-9].*/*
// @exclude *://172.2[0-9].*/*
// @exclude *://172.3[0-1].*/*
// @exclude *://*.google.*/*
// @exclude *://*.bing.com/*
// @exclude *://*.duckduckgo.com/*
// @exclude *://*.baidu.com/*
// @exclude *://*.yahoo.com/*
// @exclude *://*.yandex.*/*
// @exclude *://*.facebook.com/*
// @exclude *://*.instagram.com/*
// @exclude *://*.x.com/*
// @exclude *://*.twitter.com/*
// @exclude *://*.linkedin.com/*
// @exclude *://*.tiktok.com/*
// @exclude *://*.reddit.com/*
// @exclude *://*.redd.it/*
// @exclude *://*.pinterest.com/*
// @exclude *://*.snapchat.com/*
// @exclude *://*.tumblr.com/*
// @exclude *://*.threads.net/*
// @exclude *://*.bluesky.social/*
// @exclude *://*.mastodon.social/*
// @exclude *://*.youtube.*/*
// @exclude *://*.twitch.tv/*
// @exclude *://*.netflix.com/*
// @exclude *://*.disneyplus.com/*
// @exclude *://*.hulu.com/*
// @exclude *://*.hbomax.com/*
// @exclude *://*.max.com/*
// @exclude *://*.paramountplus.com/*
// @exclude *://*.peacocktv.com/*
// @exclude *://*.primevideo.com/*
// @exclude *://*.spotify.com/*
// @exclude *://*.soundcloud.com/*
// @exclude *://*.deezer.com/*
// @exclude *://*.tidal.com/*
// @exclude *://*.whatsapp.*/*
// @exclude *://*.telegram.*/*
// @exclude *://*.discord.*/*
// @exclude *://*.skype.com/*
// @exclude *://*.chase.com/*
// @exclude *://*.bankofamerica.com/*
// @exclude *://*.wellsfargo.com/*
// @exclude *://*.citibank.com/*
// @exclude *://*.capitalone.com/*
// @exclude *://*.americanexpress.com/*
// @exclude *://*.paypal.com/*
// @exclude *://*.stripe.com/*
// @exclude *://*.venmo.com/*
// @exclude *://*.coinbase.com/*
// @exclude *://*.binance.com/*
// @exclude *://*.fidelity.com/*
// @exclude *://*.vanguard.com/*
// @exclude *://*.schwab.com/*
// @exclude *://*.robinhood.com/*
// @exclude *://*.amazon.*/*
// @exclude *://*.ebay.com/*
// @exclude *://*.target.com/*
// @exclude *://*.walmart.com/*
// @exclude *://*.bestbuy.com/*
// @exclude *://*.etsy.com/*
// @exclude *://*.aliexpress.com/*
// @exclude *://*.alibaba.com/*
// @exclude *://*.shopify.com/*
// @exclude *://*.nytimes.com/*
// @exclude *://*.cnn.com/*
// @exclude *://*.bbc.*/*
// @exclude *://*.reuters.com/*
// @exclude *://*.theguardian.*/*
// @exclude *://*.forbes.com/*
// @exclude *://*.bloomberg.com/*
// @exclude *://*.wsj.com/*
// @exclude *://*.wikipedia.*/*
// @exclude *://*.wikimedia.*/*
// @exclude *://*.coursera.org/*
// @exclude *://*.udemy.com/*
// @exclude *://*.khanacademy.org/*
// @exclude *://*.duolingo.com/*
// @exclude *://*.quora.com/*
// @exclude *://*.github.*/*
// @exclude *://*.gitlab.*/*
// @exclude *://*.stackoverflow.com/*
// @exclude *://*.npmjs.com/*
// @exclude *://*.docker.com/*
// @exclude *://*.git-scm.com/*
// @exclude *://*.atlassian.*/*
// @exclude *://*.jira.com/*
// @exclude *://*.slack.com/*
// @exclude *://*.zoom.*/*
// @exclude *://*.microsoft.com/*
// @exclude *://*.office.com/*
// @exclude *://*.outlook.com/*
// @exclude *://*.live.com/*
// @exclude *://*.notion.so/*
// @exclude *://*.trello.com/*
// @exclude *://*.asana.com/*
// @exclude *://*.monday.com/*
// @exclude *://*.dropbox.com/*
// @exclude *://*.box.com/*
// @exclude *://*.wetransfer.com/*
// @exclude *://*.mega.nz/*
// @exclude *://*.icloud.com/*
// @exclude *://*.speedtest.net/*
// @exclude *://*.canva.com/*
// @exclude *://*.adobe.com/*
// @exclude *://*.figma.com/*
// @exclude *://*.lastpass.com/*
// @exclude *://*.1password.com/*
// @exclude *://*.bitwarden.com/*
// @exclude *://*.dashlane.com/*
// @exclude *://*.okta.com/*
// @exclude *://*.chatgpt.com/*
// @exclude *://*.openai.com/*
// @exclude *://*.claude.ai/*
// @exclude *://*.anthropic.com/*
// @exclude *://*.perplexity.ai/*
// @connect *
// @grant GM_addStyle
// @grant GM_download
// @grant GM_getValue
// @grant GM_info
// @grant GM_notification
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
const CFG = {
/** Max retry attempts per segment */
RETRIES: 3,
/** Concurrent segment downloads */
CONCURRENCY: 6,
/** Segment request timeout (ms) */
REQUEST_TIMEOUT: 6e4,
/** Manifest request timeout (ms) */
MANIFEST_TIMEOUT: 3e4,
/** Skip videos smaller than this */
SMALL_BYTES: 1 * 1024 * 1024,
/** FAB idle opacity delay (ms) */
UI_IDLE_MS: 5e3,
/** Enrichment queue debounce (ms) */
ENRICH_DELAY: 150,
/** Detection debounce (ms) */
DETECT_DEBOUNCE: 50,
/** Enrichment timeout (ms) */
ENRICH_TIMEOUT: 1e4,
/** Is top frame */
IS_TOP: window.self === window.top
};
const CACHE = {
/** Max text cache entries */
TEXT_MAX: 256,
/** Max items in DB */
DB_MAX: 120,
/** Cache cleanup interval (ms) */
CLEAR_MS: 12e4
};
const SETTINGS_KEYS = {
EXCLUDE_SMALL: "sg_exclude_small"
};
function getSetting(key, defaultValue) {
return GM_getValue(key, defaultValue);
}
function setSetting(key, value) {
GM_setValue(key, value);
}
const blobRegistry = /* @__PURE__ */ new Map();
function pruneBlobs(predicate) {
const removed = [];
for (const [url, info] of blobRegistry) {
if (predicate(url, info)) {
blobRegistry.delete(url);
removed.push(url);
}
}
return removed;
}
class Subscribable {
listeners = /* @__PURE__ */ new Set();
subscribe(fn) {
this.listeners.add(fn);
return () => this.listeners.delete(fn);
}
dispatch(payload) {
for (const fn of this.listeners) {
try {
fn(payload);
} catch (e) {
console.error("[SG] Event dispatch error:", e);
}
}
}
}
class AppState {
/** Detected media items (keyed by URL) */
items = /* @__PURE__ */ new Map();
/** Watched video elements */
watchedVideos = /* @__PURE__ */ new WeakSet();
/** Settings */
excludeSmall;
/** Cached valid count */
_validCount = 0;
_validCountDirty = true;
/** Events */
events = {
itemAdded: new Subscribable(),
updated: new Subscribable()
};
constructor() {
this.excludeSmall = getSetting(SETTINGS_KEYS.EXCLUDE_SMALL, true);
}
// ----------------------------------------
// Item Management
// ----------------------------------------
hasItem(url) {
return this.items.has(url);
}
getItem(url) {
return this.items.get(url);
}
addItem(item) {
if (this.items.has(item.url)) return false;
this.items.set(item.url, item);
this.invalidateCount();
this.enforceLimit();
this.events.itemAdded.dispatch(item);
this.events.updated.dispatch();
return true;
}
enforceLimit() {
while (this.items.size > CACHE.DB_MAX) {
const first = this.items.keys().next().value;
if (first === void 0) break;
this.items.get(first);
this.items.delete(first);
}
this.invalidateCount();
}
/**
* Count valid items (excluding invalid/error)
*/
get validCount() {
if (this._validCountDirty) {
let n = 0;
for (const item of this.items.values()) {
if (item.hlsType !== "invalid" && item.hlsType !== "error") {
n++;
}
}
this._validCount = n;
this._validCountDirty = false;
}
return this._validCount;
}
/**
* Mark valid count as dirty (needs recalculation)
*/
invalidateCount() {
this._validCountDirty = true;
}
/**
* Get all items (newest first)
*/
getAllItems() {
return Array.from(this.items.values()).reverse();
}
/**
* Get filtered items based on settings
*/
/**
* Filter items based on current settings
*/
filterItems(items) {
if (!this.excludeSmall) return items;
return items.filter(
(item) => item.size == null || item.size >= CFG.SMALL_BYTES
);
}
/**
* Get filtered items based on settings
*/
getFilteredItems() {
return this.filterItems(this.getAllItems());
}
// ----------------------------------------
// Settings
// ----------------------------------------
setExcludeSmall(v) {
this.excludeSmall = v;
setSetting(SETTINGS_KEYS.EXCLUDE_SMALL, v);
}
// ----------------------------------------
// Cleanup
// ----------------------------------------
clear() {
this.items.clear();
pruneBlobs(() => true);
this.invalidateCount();
this.events.updated.dispatch();
}
/**
* Trim stale blobs and enforce limits
*/
trim() {
this.enforceLimit();
const now = Date.now();
const removedUrls = pruneBlobs((_, info) => {
const idle = now - (info.ts || 0);
return !!(info.revoked && idle > CACHE.CLEAR_MS);
});
for (const href of removedUrls) {
if (this.items.has(href)) {
this.items.delete(href);
this.invalidateCount();
}
}
}
}
const state = new AppState();
if (CFG.IS_TOP) {
setInterval(() => state.trim(), CACHE.CLEAR_MS);
window.addEventListener("pagehide", () => state.trim());
window.addEventListener("beforeunload", () => state.trim());
}
const PATTERNS = {
http: /^https?:/i,
blob: /^blob:/i,
m3u8: /\.m3u8(\b|[?#]|$)/i,
video: /\.(mp4|mkv|webm|avi|mov|m4v|flv|ogv|ogg)([?#]|$)/i,
segment: /\.(m4s|init|seg|fmp4|ts|m2ts)([?#]|$)/i,
m3u8Type: /mpegurl|vnd\.apple\.mpegurl|application\/x-mpegurl/i,
videoType: /^video\//i,
videoTypeAlt: /(matroska|mp4|webm|quicktime)/i,
resolutionCombined: /(?:^|[_\-\/])(\d{3,4})([px])(\d{3,4})?(?:[_\-\/\.]|$)|resolution[=_]?(\d{3,4})|quality[=_]?(\d{3,4})|[_\-]hd(\d{3,4})|(\d{3,4})\.m3u8/i
};
const isHttp = (u) => typeof u === "string" && PATTERNS.http.test(u);
const isBlob = (u) => typeof u === "string" && PATTERNS.blob.test(u);
const isM3U8Url = (u) => PATTERNS.m3u8.test(u || "");
const isVideoUrl = (u) => PATTERNS.video.test(u || "");
const isSegmentUrl = (u) => PATTERNS.segment.test(u || "");
const looksM3U8Type = (t) => PATTERNS.m3u8Type.test(t || "");
const looksVideoType = (t) => PATTERNS.videoType.test(t || "") || PATTERNS.videoTypeAlt.test(t || "");
function safeAbsUrl(url, base) {
try {
return new URL(url, base).href;
} catch {
return url;
}
}
const SIZE_UNITS = ["B", "KB", "MB", "GB", "TB"];
function formatBytes(n) {
if (n == null) return "";
let i = 0;
let v = n;
while (v >= 1024 && i < SIZE_UNITS.length - 1) {
v /= 1024;
i++;
}
const decimals = v < 10 && i > 0 ? 1 : 0;
return `${v.toFixed(decimals)} ${SIZE_UNITS[i]}`;
}
function formatDuration(seconds) {
if (!seconds || seconds <= 0) return null;
const h2 = Math.floor(seconds / 3600);
const m = Math.floor(seconds % 3600 / 60);
const s = Math.floor(seconds % 60);
const pad = (n) => String(n).padStart(2, "0");
return h2 > 0 ? `${h2}:${pad(m)}:${pad(s)}` : `${m}:${pad(s)}`;
}
document.createElement("div");
function shortId() {
return Math.random().toString(36).slice(2);
}
const SERIALIZABLE_KEYS = [
"url",
"kind",
"label",
"sublabel",
"size",
"type",
"origin",
"pageTitle",
"enriched",
"enriching",
"hlsType",
"isLive",
"encrypted",
"duration",
"segCount",
"resolution",
"isVod",
"isFmp4",
"variantCount",
"variants",
"bestVariant",
"variant"
];
function serializeMediaItem(item) {
const result = {};
for (const key of SERIALIZABLE_KEYS) {
if (item[key] !== void 0) {
result[key] = item[key];
}
}
return result;
}
function parseRange(v) {
if (!v) return null;
const m = /bytes=(\d+)-(\d+)?/i.exec(v);
if (!m) return null;
return {
start: +m[1],
end: m[2] != null ? +m[2] : null
};
}
function lruGet(map, key) {
if (!map.has(key)) return void 0;
const v = map.get(key);
map.delete(key);
map.set(key, v);
return v;
}
function lruSet(map, key, val, max) {
if (map.has(key)) map.delete(key);
map.set(key, val);
if (typeof max === "number" && isFinite(max)) {
while (map.size > max) {
const first = map.keys().next().value;
if (first !== void 0) map.delete(first);
}
}
}
function once(cache, inflight, key, loader, max) {
const cached = lruGet(cache, key);
if (cached !== void 0) return Promise.resolve(cached);
if (inflight.has(key)) return inflight.get(key);
const p = (async () => {
try {
const v = await loader();
lruSet(cache, key, v, max);
return v;
} finally {
inflight.delete(key);
}
})();
inflight.set(key, p);
return p;
}
function cleanFilename(s) {
return (s || "video").replace(/[\\/:*?"<>|]/g, "_").slice(0, 120).trim() || "video";
}
const EXT_MAP = {
webm: "webm",
matroska: "mkv",
mkv: "mkv",
quicktime: "mov",
mov: "mov",
mp2t: "ts",
mpegts: "ts",
ogg: "ogg",
mp4: "mp4"
};
function extFromType(t) {
const lc = t.toLowerCase();
for (const [key, ext] of Object.entries(EXT_MAP)) {
if (lc.includes(key)) return ext;
}
return "mp4";
}
function guessExt(url, type) {
const m = /(?:\.([a-z0-9]+))([?#]|$)/i.exec(url || "");
if (m) return m[1].toLowerCase();
return type ? extFromType(type) : "mp4";
}
function generateFilename(options) {
const base = cleanFilename(options.title || document.title);
const qualSuffix = options.quality ? `_${options.quality}` : "";
const ext = options.ext || "mp4";
return `${base}${qualSuffix}.${ext}`;
}
function sortVariantsByQuality(variants) {
return [...variants].sort(
(a, b) => (b.h || 0) - (a.h || 0) || (b.avg || b.peak || 0) - (a.avg || a.peak || 0)
);
}
function buildLabel(parts) {
const items = [];
if (parts.resolution) {
items.push(parts.resolution);
}
if (parts.bitrate) {
items.push(`${Math.round(parts.bitrate / 1e3)}k`);
}
if (parts.duration && parts.duration > 0) {
const dur = formatDuration(parts.duration);
if (dur) items.push(dur);
}
if (parts.size != null) {
items.push(`~${formatBytes(parts.size)}`);
}
if (parts.extra) {
items.push(...parts.extra);
}
return items.length > 0 ? items.join(" • ") : "Video Stream";
}
function buildSublabel(segCount, isFmp42) {
const format = isFmp42 ? "fMP4" : "TS";
return `${segCount} segments • ${format}`;
}
function extractResFromUrl(url) {
if (!url) return null;
const m = PATTERNS.resolutionCombined.exec(url);
if (m) {
if (m[1]) {
const w = parseInt(m[1], 10);
const sep = m[2];
const hStr = m[3];
if (sep === "x" && hStr) {
return `${w}x${hStr}`;
}
if (w >= 144 && w <= 4320) return `${w}p`;
}
const val = m[4] || m[5] || m[6] || m[7];
if (val) {
const h2 = parseInt(val, 10);
if (h2 >= 144 && h2 <= 4320) return `${h2}p`;
}
}
return null;
}
function getBlobInfo(url, registry) {
if (!isBlob(url)) return null;
const info = registry.get(url);
if (!info) return null;
info.ts = Date.now();
return info;
}
function getBlobSlice(blob, rangeHeader) {
if (!rangeHeader) return blob;
const range = parseRange(rangeHeader);
if (!range) return blob;
return blob.slice(range.start, range.end == null ? blob.size : range.end + 1);
}
function notify(message, options = {}) {
GM_notification({
text: message,
title: options.title ?? "StreamGrabber",
timeout: options.timeout ?? 3e3,
onclick: options.onclick
});
}
function notifyDownloadComplete(filename) {
notify(`Download complete: ${filename}`);
}
function getErrorMessage(e) {
if (e instanceof Error) return e.message;
if (typeof e === "string") return e;
return String(e);
}
function alertError(e, prefix) {
const msg = getErrorMessage(e);
alert(msg);
}
let onDetect = null;
const earlyDetections = [];
const recentlyRevoked = /* @__PURE__ */ new Set();
setInterval(() => {
if (recentlyRevoked.size > 0) {
recentlyRevoked.clear();
}
}, CACHE.CLEAR_MS);
function setDetectionCallback(cb) {
onDetect = cb;
if (earlyDetections.length > 0) {
const pending = [...earlyDetections];
earlyDetections.length = 0;
pending.forEach(({ url, metadata }) => cb(url, metadata));
}
}
function checkContent(content) {
if (typeof content !== "string") return false;
return /^\s*#EXTM3U/i.test(content);
}
function extractEpisodeFromUrl(url) {
const targetUrl = window.location.href;
const patterns = [
/[#?&]ep(?:isode)?[=:](\d+)/i,
// #ep=1, ?episode=1
/\/ep(?:isode)?[-_]?(\d+)/i,
// /ep-1, /episode_1, /ep1
/\/e(\d+)(?:[^a-z0-9]|$)/i,
// /e1 (followed by non-alphanumeric)
/[-_]ep(?:isode)?[-_]?(\d+)/i,
// -ep-1, _episode_1
/[-_](\d{1,3})(?:[^0-9]|$)/
// -1 at end (episode number)
];
for (const pattern of patterns) {
const match = targetUrl.match(pattern);
if (match?.[1]) {
return `Episode ${match[1]}`;
}
}
return null;
}
function buildEnhancedTitle() {
const baseTitle = document.title;
const episodeInfo = extractEpisodeFromUrl();
if (episodeInfo && !/episode\s*\d+/i.test(baseTitle)) {
return `${baseTitle} • ${episodeInfo}`;
}
return baseTitle;
}
function emitDetection(url, metadata) {
const meta = {
...metadata,
pageTitle: buildEnhancedTitle()
};
if (onDetect) {
onDetect(url, meta);
} else {
earlyDetections.push({ url, metadata: meta });
}
}
function hookCreateObjectURL() {
const original = URL.createObjectURL;
URL.createObjectURL = function(obj) {
const href = original.call(this, obj);
try {
const now = Date.now();
if (obj instanceof Blob) {
const type = obj.type || "";
const info = {
blob: obj,
type,
size: obj.size,
kind: "other",
ts: now
};
if (looksM3U8Type(type)) {
info.kind = "m3u8";
blobRegistry.set(href, info);
emitDetection(href);
} else if (looksVideoType(type)) {
info.kind = "video";
blobRegistry.set(href, info);
emitDetection(href);
} else {
const needsCheck = /octet-stream|text\/plain|^$/.test(type);
if (needsCheck && obj.size > 0 && obj.size < 5 * 1024 * 1024) {
obj.slice(0, Math.min(2048, obj.size)).text().then((text) => {
if (checkContent(text)) {
if (!recentlyRevoked.has(href)) {
info.kind = "m3u8";
blobRegistry.set(href, info);
emitDetection(href);
}
}
}).catch(() => {
});
}
}
}
} catch (e) {
console.error("[SG] createObjectURL hook error:", e);
}
return href;
};
}
function hookRevokeObjectURL() {
const original = URL.revokeObjectURL;
URL.revokeObjectURL = function(href) {
try {
const info = blobRegistry.get(href);
if (info) {
info.revoked = true;
info.ts = Date.now();
} else {
recentlyRevoked.add(href);
}
} catch {
}
return original.call(this, href);
};
}
function hookFetch() {
const originalFetch = window.fetch;
const originalResText = window.Response.prototype.text;
if (typeof originalFetch !== "function") return;
window.Response.prototype.text = function() {
const response = this;
return originalResText.call(this).then((text) => {
try {
if (checkContent(text)) {
emitDetection(response.url);
}
} catch (e) {
console.error("[SG] Detection error in Response.text:", e);
}
return text;
});
};
window.fetch = function(...args) {
try {
const input = args[0];
const url = typeof input === "string" ? input : input instanceof Request ? input.url : input.href;
if (url) emitDetection(url);
} catch {
}
return originalFetch.apply(this, args);
};
}
function hookXHR() {
const original = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url, ...rest) {
try {
const urlStr = typeof url === "string" ? url : url?.href;
if (urlStr) emitDetection(urlStr);
} catch {
}
const result = original.call(this, method, url, ...rest);
this.addEventListener("load", () => {
try {
if (!this.responseType || this.responseType === "text") {
const content = this.responseText;
const targetUrl = typeof url === "string" ? url : url?.href;
if (targetUrl && checkContent(content)) {
emitDetection(targetUrl);
}
}
} catch {
}
});
return result;
};
}
function hookPerformanceObserver() {
try {
const observer2 = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if ("name" in entry && typeof entry.name === "string") {
emitDetection(entry.name);
}
}
});
observer2.observe({ entryTypes: ["resource"] });
} catch {
}
}
let hooksInstalled = false;
function installHooks() {
if (hooksInstalled) return;
hooksInstalled = true;
hookCreateObjectURL();
hookRevokeObjectURL();
hookFetch();
hookXHR();
hookPerformanceObserver();
}
let onScan = () => {
};
function setScanCallback(cb) {
onScan = cb;
}
function watchVideo(video) {
if (state.watchedVideos.has(video)) return;
state.watchedVideos.add(video);
const emitSources = () => {
const sources = [
video.currentSrc || video.src,
...Array.from(video.querySelectorAll("source")).map((s) => s.src)
].filter(Boolean);
sources.forEach(onScan);
};
const events = ["loadstart", "loadedmetadata", "canplay"];
events.forEach((ev) => video.addEventListener(ev, emitSources));
emitSources();
}
function scanVideos() {
document.querySelectorAll("video").forEach((v) => watchVideo(v));
}
let observer = null;
let debounceTimer;
function startVideoObserver() {
if (observer) return;
observer = new MutationObserver(() => {
if (debounceTimer) clearTimeout(debounceTimer);
debounceTimer = window.setTimeout(() => {
scanVideos();
debounceTimer = void 0;
}, 1e3);
});
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
}
const pendingUrls$1 = /* @__PURE__ */ new Set();
function debounceDetect(url, callback) {
if (pendingUrls$1.has(url)) return;
pendingUrls$1.add(url);
setTimeout(() => {
pendingUrls$1.delete(url);
callback(url);
}, CFG.DETECT_DEBOUNCE);
}
function createMediaItem(url, kind, metadata = {}) {
const { size = null, type = null, pageTitle } = metadata;
let label;
if (kind === "hls") {
const res = extractResFromUrl(url);
label = res ? `${res} • Analyzing...` : "Analyzing...";
} else {
label = guessExt(url, type).toUpperCase();
}
return {
url,
kind,
label,
sublabel: null,
size,
type,
origin: document.location.origin,
pageTitle,
enriched: false,
enriching: false,
hlsType: null,
isLive: false,
encrypted: false,
_enrichPromise: null
};
}
let onItemDetected = () => {
};
function setItemDetectedCallback(cb) {
onItemDetected = cb;
}
function processUrl(url, metadata) {
try {
if (!url || !isHttp(url) && !isBlob(url)) return;
if (isSegmentUrl(url)) return;
const lowerUrl = url.toLowerCase();
if (lowerUrl.includes("ping.gif") || lowerUrl.includes("jwpltx.com") || lowerUrl.includes("doubleclick") || lowerUrl.includes("analytics") || lowerUrl.includes("/stats/")) {
return;
}
if (state.hasItem(url)) return;
let size = metadata?.size ?? null;
let type = metadata?.type ?? null;
const pageTitle = metadata?.pageTitle;
if (isBlob(url)) {
const info = blobRegistry.get(url);
if (info) {
size = size ?? info.size;
type = type ?? info.type;
}
if (size != null && size < 512 * 1024 && info?.kind !== "m3u8") {
return;
}
}
const isHls = isM3U8Url(url) || isBlob(url) && blobRegistry.get(url)?.kind === "m3u8";
const isVideo = isVideoUrl(url) || isBlob(url) && blobRegistry.get(url)?.kind === "video";
const kind = isHls ? "hls" : isVideo ? "video" : null;
if (!kind) return;
const item = createMediaItem(url, kind, { size, type, pageTitle });
if (state.addItem(item)) {
onItemDetected(item);
}
} catch (e) {
console.error("[SG] processUrl error:", e);
}
}
let initialized = false;
function initDetection() {
if (initialized) return;
initialized = true;
setDetectionCallback((url, metadata) => debounceDetect(url, (u) => processUrl(u, metadata)));
setScanCallback((url) => debounceDetect(url, (u) => processUrl(u)));
installHooks();
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => {
scanVideos();
startVideoObserver();
});
} else {
scanVideos();
startVideoObserver();
}
}
function getDefaultExportFromCjs(x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
}
var eventemitter3 = { exports: {} };
var hasRequiredEventemitter3;
function requireEventemitter3() {
if (hasRequiredEventemitter3) return eventemitter3.exports;
hasRequiredEventemitter3 = 1;
(function(module) {
var has = Object.prototype.hasOwnProperty, prefix = "~";
function Events() {
}
if (Object.create) {
Events.prototype = /* @__PURE__ */ Object.create(null);
if (!new Events().__proto__) prefix = false;
}
function EE(fn, context, once2) {
this.fn = fn;
this.context = context;
this.once = once2 || false;
}
function addListener(emitter, event, fn, context, once2) {
if (typeof fn !== "function") {
throw new TypeError("The listener must be a function");
}
var listener = new EE(fn, context || emitter, once2), evt = prefix ? prefix + event : event;
if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;
else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);
else emitter._events[evt] = [emitter._events[evt], listener];
return emitter;
}
function clearEvent(emitter, evt) {
if (--emitter._eventsCount === 0) emitter._events = new Events();
else delete emitter._events[evt];
}
function EventEmitter2() {
this._events = new Events();
this._eventsCount = 0;
}
EventEmitter2.prototype.eventNames = function eventNames() {
var names = [], events, name;
if (this._eventsCount === 0) return names;
for (name in events = this._events) {
if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);
}
if (Object.getOwnPropertySymbols) {
return names.concat(Object.getOwnPropertySymbols(events));
}
return names;
};
EventEmitter2.prototype.listeners = function listeners(event) {
var evt = prefix ? prefix + event : event, handlers = this._events[evt];
if (!handlers) return [];
if (handlers.fn) return [handlers.fn];
for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
ee[i] = handlers[i].fn;
}
return ee;
};
EventEmitter2.prototype.listenerCount = function listenerCount(event) {
var evt = prefix ? prefix + event : event, listeners = this._events[evt];
if (!listeners) return 0;
if (listeners.fn) return 1;
return listeners.length;
};
EventEmitter2.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return false;
var listeners = this._events[evt], len = arguments.length, args, i;
if (listeners.fn) {
if (listeners.once) this.removeListener(event, listeners.fn, void 0, true);
switch (len) {
case 1:
return listeners.fn.call(listeners.context), true;
case 2:
return listeners.fn.call(listeners.context, a1), true;
case 3:
return listeners.fn.call(listeners.context, a1, a2), true;
case 4:
return listeners.fn.call(listeners.context, a1, a2, a3), true;
case 5:
return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
case 6:
return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
}
for (i = 1, args = new Array(len - 1); i < len; i++) {
args[i - 1] = arguments[i];
}
listeners.fn.apply(listeners.context, args);
} else {
var length = listeners.length, j;
for (i = 0; i < length; i++) {
if (listeners[i].once) this.removeListener(event, listeners[i].fn, void 0, true);
switch (len) {
case 1:
listeners[i].fn.call(listeners[i].context);
break;
case 2:
listeners[i].fn.call(listeners[i].context, a1);
break;
case 3:
listeners[i].fn.call(listeners[i].context, a1, a2);
break;
case 4:
listeners[i].fn.call(listeners[i].context, a1, a2, a3);
break;
default:
if (!args) for (j = 1, args = new Array(len - 1); j < len; j++) {
args[j - 1] = arguments[j];
}
listeners[i].fn.apply(listeners[i].context, args);
}
}
}
return true;
};
EventEmitter2.prototype.on = function on(event, fn, context) {
return addListener(this, event, fn, context, false);
};
EventEmitter2.prototype.once = function once2(event, fn, context) {
return addListener(this, event, fn, context, true);
};
EventEmitter2.prototype.removeListener = function removeListener(event, fn, context, once2) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return this;
if (!fn) {
clearEvent(this, evt);
return this;
}
var listeners = this._events[evt];
if (listeners.fn) {
if (listeners.fn === fn && (!once2 || listeners.once) && (!context || listeners.context === context)) {
clearEvent(this, evt);
}
} else {
for (var i = 0, events = [], length = listeners.length; i < length; i++) {
if (listeners[i].fn !== fn || once2 && !listeners[i].once || context && listeners[i].context !== context) {
events.push(listeners[i]);
}
}
if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;
else clearEvent(this, evt);
}
return this;
};
EventEmitter2.prototype.removeAllListeners = function removeAllListeners(event) {
var evt;
if (event) {
evt = prefix ? prefix + event : event;
if (this._events[evt]) clearEvent(this, evt);
} else {
this._events = new Events();
this._eventsCount = 0;
}
return this;
};
EventEmitter2.prototype.off = EventEmitter2.prototype.removeListener;
EventEmitter2.prototype.addListener = EventEmitter2.prototype.on;
EventEmitter2.prefixed = prefix;
EventEmitter2.EventEmitter = EventEmitter2;
{
module.exports = EventEmitter2;
}
})(eventemitter3);
return eventemitter3.exports;
}
var eventemitter3Exports = requireEventemitter3();
const EventEmitter = /* @__PURE__ */ getDefaultExportFromCjs(eventemitter3Exports);
class TimeoutError extends Error {
name = "TimeoutError";
constructor(message, options) {
super(message, options);
Error.captureStackTrace?.(this, TimeoutError);
}
}
const getAbortedReason = (signal) => signal.reason ?? new DOMException("This operation was aborted.", "AbortError");
function pTimeout(promise, options) {
const {
milliseconds,
fallback,
message,
customTimers = { setTimeout, clearTimeout },
signal
} = options;
let timer;
let abortHandler;
const wrappedPromise = new Promise((resolve, reject) => {
if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
}
if (signal?.aborted) {
reject(getAbortedReason(signal));
return;
}
if (signal) {
abortHandler = () => {
reject(getAbortedReason(signal));
};
signal.addEventListener("abort", abortHandler, { once: true });
}
promise.then(resolve, reject);
if (milliseconds === Number.POSITIVE_INFINITY) {
return;
}
const timeoutError = new TimeoutError();
timer = customTimers.setTimeout.call(void 0, () => {
if (fallback) {
try {
resolve(fallback());
} catch (error) {
reject(error);
}
return;
}
if (typeof promise.cancel === "function") {
promise.cancel();
}
if (message === false) {
resolve();
} else if (message instanceof Error) {
reject(message);
} else {
timeoutError.message = message ?? `Promise timed out after ${milliseconds} milliseconds`;
reject(timeoutError);
}
}, milliseconds);
});
const cancelablePromise = wrappedPromise.finally(() => {
cancelablePromise.clear();
if (abortHandler && signal) {
signal.removeEventListener("abort", abortHandler);
}
});
cancelablePromise.clear = () => {
customTimers.clearTimeout.call(void 0, timer);
timer = void 0;
};
return cancelablePromise;
}
function lowerBound(array, value, comparator) {
let first = 0;
let count = array.length;
while (count > 0) {
const step = Math.trunc(count / 2);
let it = first + step;
if (comparator(array[it], value) <= 0) {
first = ++it;
count -= step + 1;
} else {
count = step;
}
}
return first;
}
class PriorityQueue {
#queue = [];
enqueue(run, options) {
const { priority = 0, id } = options ?? {};
const element = {
priority,
id,
run
};
if (this.size === 0 || this.#queue[this.size - 1].priority >= priority) {
this.#queue.push(element);
return;
}
const index = lowerBound(this.#queue, element, (a, b) => b.priority - a.priority);
this.#queue.splice(index, 0, element);
}
setPriority(id, priority) {
const index = this.#queue.findIndex((element) => element.id === id);
if (index === -1) {
throw new ReferenceError(`No promise function with the id "${id}" exists in the queue.`);
}
const [item] = this.#queue.splice(index, 1);
this.enqueue(item.run, { priority, id });
}
dequeue() {
const item = this.#queue.shift();
return item?.run;
}
filter(options) {
return this.#queue.filter((element) => element.priority === options.priority).map((element) => element.run);
}
get size() {
return this.#queue.length;
}
}
class PQueue extends EventEmitter {
#carryoverIntervalCount;
#isIntervalIgnored;
#intervalCount = 0;
#intervalCap;
#rateLimitedInInterval = false;
#rateLimitFlushScheduled = false;
#interval;
#intervalEnd = 0;
#lastExecutionTime = 0;
#intervalId;
#timeoutId;
#strict;
// Circular buffer implementation for better performance
#strictTicks = [];
#strictTicksStartIndex = 0;
#queue;
#queueClass;
#pending = 0;
// The `!` is needed because of https://github.com/microsoft/TypeScript/issues/32194
#concurrency;
#isPaused;
// Use to assign a unique identifier to a promise function, if not explicitly specified
#idAssigner = 1n;
// Track currently running tasks for debugging
#runningTasks = /* @__PURE__ */ new Map();
/**
Get or set the default timeout for all tasks. Can be changed at runtime.
Operations will throw a `TimeoutError` if they don't complete within the specified time.
The timeout begins when the operation is dequeued and starts execution, not while it's waiting in the queue.
@example
```
const queue = new PQueue({timeout: 5000});
// Change timeout for all future tasks
queue.timeout = 10000;
```
*/
timeout;
constructor(options) {
super();
options = {
carryoverIntervalCount: false,
intervalCap: Number.POSITIVE_INFINITY,
interval: 0,
concurrency: Number.POSITIVE_INFINITY,
autoStart: true,
queueClass: PriorityQueue,
strict: false,
...options
};
if (!(typeof options.intervalCap === "number" && options.intervalCap >= 1)) {
throw new TypeError(`Expected \`intervalCap\` to be a number from 1 and up, got \`${options.intervalCap?.toString() ?? ""}\` (${typeof options.intervalCap})`);
}
if (options.interval === void 0 || !(Number.isFinite(options.interval) && options.interval >= 0)) {
throw new TypeError(`Expected \`interval\` to be a finite number >= 0, got \`${options.interval?.toString() ?? ""}\` (${typeof options.interval})`);
}
if (options.strict && options.interval === 0) {
throw new TypeError("The `strict` option requires a non-zero `interval`");
}
if (options.strict && options.intervalCap === Number.POSITIVE_INFINITY) {
throw new TypeError("The `strict` option requires a finite `intervalCap`");
}
this.#carryoverIntervalCount = options.carryoverIntervalCount ?? options.carryoverConcurrencyCount ?? false;
this.#isIntervalIgnored = options.intervalCap === Number.POSITIVE_INFINITY || options.interval === 0;
this.#intervalCap = options.intervalCap;
this.#interval = options.interval;
this.#strict = options.strict;
this.#queue = new options.queueClass();
this.#queueClass = options.queueClass;
this.concurrency = options.concurrency;
if (options.timeout !== void 0 && !(Number.isFinite(options.timeout) && options.timeout > 0)) {
throw new TypeError(`Expected \`timeout\` to be a positive finite number, got \`${options.timeout}\` (${typeof options.timeout})`);
}
this.timeout = options.timeout;
this.#isPaused = options.autoStart === false;
this.#setupRateLimitTracking();
}
#cleanupStrictTicks(now) {
while (this.#strictTicksStartIndex < this.#strictTicks.length) {
const oldestTick = this.#strictTicks[this.#strictTicksStartIndex];
if (oldestTick !== void 0 && now - oldestTick >= this.#interval) {
this.#strictTicksStartIndex++;
} else {
break;
}
}
const shouldCompact = this.#strictTicksStartIndex > 100 && this.#strictTicksStartIndex > this.#strictTicks.length / 2 || this.#strictTicksStartIndex === this.#strictTicks.length;
if (shouldCompact) {
this.#strictTicks = this.#strictTicks.slice(this.#strictTicksStartIndex);
this.#strictTicksStartIndex = 0;
}
}
// Helper methods for interval consumption
#consumeIntervalSlot(now) {
if (this.#strict) {
this.#strictTicks.push(now);
} else {
this.#intervalCount++;
}
}
#rollbackIntervalSlot() {
if (this.#strict) {
if (this.#strictTicks.length > this.#strictTicksStartIndex) {
this.#strictTicks.pop();
}
} else if (this.#intervalCount > 0) {
this.#intervalCount--;
}
}
#getActiveTicksCount() {
return this.#strictTicks.length - this.#strictTicksStartIndex;
}
get #doesIntervalAllowAnother() {
if (this.#isIntervalIgnored) {
return true;
}
if (this.#strict) {
return this.#getActiveTicksCount() < this.#intervalCap;
}
return this.#intervalCount < this.#intervalCap;
}
get #doesConcurrentAllowAnother() {
return this.#pending < this.#concurrency;
}
#next() {
this.#pending--;
if (this.#pending === 0) {
this.emit("pendingZero");
}
this.#tryToStartAnother();
this.emit("next");
}
#onResumeInterval() {
this.#timeoutId = void 0;
this.#onInterval();
this.#initializeIntervalIfNeeded();
}
#isIntervalPausedAt(now) {
if (this.#strict) {
this.#cleanupStrictTicks(now);
const activeTicksCount = this.#getActiveTicksCount();
if (activeTicksCount >= this.#intervalCap) {
const oldestTick = this.#strictTicks[this.#strictTicksStartIndex];
const delay = this.#interval - (now - oldestTick);
this.#createIntervalTimeout(delay);
return true;
}
return false;
}
if (this.#intervalId === void 0) {
const delay = this.#intervalEnd - now;
if (delay < 0) {
if (this.#lastExecutionTime > 0) {
const timeSinceLastExecution = now - this.#lastExecutionTime;
if (timeSinceLastExecution < this.#interval) {
this.#createIntervalTimeout(this.#interval - timeSinceLastExecution);
return true;
}
}
this.#intervalCount = this.#carryoverIntervalCount ? this.#pending : 0;
} else {
this.#createIntervalTimeout(delay);
return true;
}
}
return false;
}
#createIntervalTimeout(delay) {
if (this.#timeoutId !== void 0) {
return;
}
this.#timeoutId = setTimeout(() => {
this.#onResumeInterval();
}, delay);
}
#clearIntervalTimer() {
if (this.#intervalId) {
clearInterval(this.#intervalId);
this.#intervalId = void 0;
}
}
#clearTimeoutTimer() {
if (this.#timeoutId) {
clearTimeout(this.#timeoutId);
this.#timeoutId = void 0;
}
}
#tryToStartAnother() {
if (this.#queue.size === 0) {
this.#clearIntervalTimer();
this.emit("empty");
if (this.#pending === 0) {
this.#clearTimeoutTimer();
if (this.#strict && this.#strictTicksStartIndex > 0) {
const now = Date.now();
this.#cleanupStrictTicks(now);
}
this.emit("idle");
}
return false;
}
let taskStarted = false;
if (!this.#isPaused) {
const now = Date.now();
const canInitializeInterval = !this.#isIntervalPausedAt(now);
if (this.#doesIntervalAllowAnother && this.#doesConcurrentAllowAnother) {
const job = this.#queue.dequeue();
if (!this.#isIntervalIgnored) {
this.#consumeIntervalSlot(now);
this.#scheduleRateLimitUpdate();
}
this.emit("active");
job();
if (canInitializeInterval) {
this.#initializeIntervalIfNeeded();
}
taskStarted = true;
}
}
return taskStarted;
}
#initializeIntervalIfNeeded() {
if (this.#isIntervalIgnored || this.#intervalId !== void 0) {
return;
}
if (this.#strict) {
return;
}
this.#intervalId = setInterval(() => {
this.#onInterval();
}, this.#interval);
this.#intervalEnd = Date.now() + this.#interval;
}
#onInterval() {
if (!this.#strict) {
if (this.#intervalCount === 0 && this.#pending === 0 && this.#intervalId) {
this.#clearIntervalTimer();
}
this.#intervalCount = this.#carryoverIntervalCount ? this.#pending : 0;
}
this.#processQueue();
this.#scheduleRateLimitUpdate();
}
/**
Executes all queued functions until it reaches the limit.
*/
#processQueue() {
while (this.#tryToStartAnother()) {
}
}
get concurrency() {
return this.#concurrency;
}
set concurrency(newConcurrency) {
if (!(typeof newConcurrency === "number" && newConcurrency >= 1)) {
throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${newConcurrency}\` (${typeof newConcurrency})`);
}
this.#concurrency = newConcurrency;
this.#processQueue();
}
/**
Updates the priority of a promise function by its id, affecting its execution order. Requires a defined concurrency limit to take effect.
For example, this can be used to prioritize a promise function to run earlier.
```js
import PQueue from 'p-queue';
const queue = new PQueue({concurrency: 1});
queue.add(async () => '🦄', {priority: 1});
queue.add(async () => '🦀', {priority: 0, id: '🦀'});
queue.add(async () => '🦄', {priority: 1});
queue.add(async () => '🦄', {priority: 1});
queue.setPriority('🦀', 2);
```
In this case, the promise function with `id: '🦀'` runs second.
You can also deprioritize a promise function to delay its execution:
```js
import PQueue from 'p-queue';
const queue = new PQueue({concurrency: 1});
queue.add(async () => '🦄', {priority: 1});
queue.add(async () => '🦀', {priority: 1, id: '🦀'});
queue.add(async () => '🦄');
queue.add(async () => '🦄', {priority: 0});
queue.setPriority('🦀', -1);
```
Here, the promise function with `id: '🦀'` executes last.
*/
setPriority(id, priority) {
if (typeof priority !== "number" || !Number.isFinite(priority)) {
throw new TypeError(`Expected \`priority\` to be a finite number, got \`${priority}\` (${typeof priority})`);
}
this.#queue.setPriority(id, priority);
}
async add(function_, options = {}) {
options = {
timeout: this.timeout,
...options,
// Assign unique ID if not provided
id: options.id ?? (this.#idAssigner++).toString()
};
return new Promise((resolve, reject) => {
const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
this.#queue.enqueue(async () => {
this.#pending++;
this.#runningTasks.set(taskSymbol, {
id: options.id,
priority: options.priority ?? 0,
// Match priority-queue default
startTime: Date.now(),
timeout: options.timeout
});
let eventListener;
try {
try {
options.signal?.throwIfAborted();
} catch (error) {
this.#rollbackIntervalConsumption();
this.#runningTasks.delete(taskSymbol);
throw error;
}
this.#lastExecutionTime = Date.now();
let operation = function_({ signal: options.signal });
if (options.timeout) {
operation = pTimeout(Promise.resolve(operation), {
milliseconds: options.timeout,
message: `Task timed out after ${options.timeout}ms (queue has ${this.#pending} running, ${this.#queue.size} waiting)`
});
}
if (options.signal) {
const { signal } = options;
operation = Promise.race([operation, new Promise((_resolve, reject2) => {
eventListener = () => {
reject2(signal.reason);
};
signal.addEventListener("abort", eventListener, { once: true });
})]);
}
const result = await operation;
resolve(result);
this.emit("completed", result);
} catch (error) {
reject(error);
this.emit("error", error);
} finally {
if (eventListener) {
options.signal?.removeEventListener("abort", eventListener);
}
this.#runningTasks.delete(taskSymbol);
queueMicrotask(() => {
this.#next();
});
}
}, options);
this.emit("add");
this.#tryToStartAnother();
});
}
async addAll(functions, options) {
return Promise.all(functions.map(async (function_) => this.add(function_, options)));
}
/**
Start (or resume) executing enqueued tasks within concurrency limit. No need to call this if queue is not paused (via `options.autoStart = false` or by `.pause()` method.)
*/
start() {
if (!this.#isPaused) {
return this;
}
this.#isPaused = false;
this.#processQueue();
return this;
}
/**
Put queue execution on hold.
*/
pause() {
this.#isPaused = true;
}
/**
Clear the queue.
*/
clear() {
this.#queue = new this.#queueClass();
this.#clearIntervalTimer();
this.#updateRateLimitState();
this.emit("empty");
if (this.#pending === 0) {
this.#clearTimeoutTimer();
this.emit("idle");
}
this.emit("next");
}
/**
Can be called multiple times. Useful if you for example add additional items at a later time.
@returns A promise that settles when the queue becomes empty.
*/
async onEmpty() {
if (this.#queue.size === 0) {
return;
}
await this.#onEvent("empty");
}
/**
@returns A promise that settles when the queue size is less than the given limit: `queue.size < limit`.
If you want to avoid having the queue grow beyond a certain size you can `await queue.onSizeLessThan()` before adding a new item.
Note that this only limits the number of items waiting to start. There could still be up to `concurrency` jobs already running that this call does not include in its calculation.
*/
async onSizeLessThan(limit) {
if (this.#queue.size < limit) {
return;
}
await this.#onEvent("next", () => this.#queue.size < limit);
}
/**
The difference with `.onEmpty` is that `.onIdle` guarantees that all work from the queue has finished. `.onEmpty` merely signals that the queue is empty, but it could mean that some promises haven't completed yet.
@returns A promise that settles when the queue becomes empty, and all promises have completed; `queue.size === 0 && queue.pending === 0`.
*/
async onIdle() {
if (this.#pending === 0 && this.#queue.size === 0) {
return;
}
await this.#onEvent("idle");
}
/**
The difference with `.onIdle` is that `.onPendingZero` only waits for currently running tasks to finish, ignoring queued tasks.
@returns A promise that settles when all currently running tasks have completed; `queue.pending === 0`.
*/
async onPendingZero() {
if (this.#pending === 0) {
return;
}
await this.#onEvent("pendingZero");
}
/**
@returns A promise that settles when the queue becomes rate-limited due to intervalCap.
*/
async onRateLimit() {
if (this.isRateLimited) {
return;
}
await this.#onEvent("rateLimit");
}
/**
@returns A promise that settles when the queue is no longer rate-limited.
*/
async onRateLimitCleared() {
if (!this.isRateLimited) {
return;
}
await this.#onEvent("rateLimitCleared");
}
/**
@returns A promise that rejects when any task in the queue errors.
Use with `Promise.race([queue.onError(), queue.onIdle()])` to fail fast on the first error while still resolving normally when the queue goes idle.
Important: The promise returned by `add()` still rejects. You must handle each `add()` promise (for example, `.catch(() => {})`) to avoid unhandled rejections.
@example
```
import PQueue from 'p-queue';
const queue = new PQueue({concurrency: 2});
queue.add(() => fetchData(1)).catch(() => {});
queue.add(() => fetchData(2)).catch(() => {});
queue.add(() => fetchData(3)).catch(() => {});
// Stop processing on first error
try {
await Promise.race([
queue.onError(),
queue.onIdle()
]);
} catch (error) {
queue.pause(); // Stop processing remaining tasks
console.error('Queue failed:', error);
}
```
*/
// eslint-disable-next-line @typescript-eslint/promise-function-async
onError() {
return new Promise((_resolve, reject) => {
const handleError = (error) => {
this.off("error", handleError);
reject(error);
};
this.on("error", handleError);
});
}
async #onEvent(event, filter) {
return new Promise((resolve) => {
const listener = () => {
if (filter && !filter()) {
return;
}
this.off(event, listener);
resolve();
};
this.on(event, listener);
});
}
/**
Size of the queue, the number of queued items waiting to run.
*/
get size() {
return this.#queue.size;
}
/**
Size of the queue, filtered by the given options.
For example, this can be used to find the number of items remaining in the queue with a specific priority level.
*/
sizeBy(options) {
return this.#queue.filter(options).length;
}
/**
Number of running items (no longer in the queue).
*/
get pending() {
return this.#pending;
}
/**
Whether the queue is currently paused.
*/
get isPaused() {
return this.#isPaused;
}
#setupRateLimitTracking() {
if (this.#isIntervalIgnored) {
return;
}
this.on("add", () => {
if (this.#queue.size > 0) {
this.#scheduleRateLimitUpdate();
}
});
this.on("next", () => {
this.#scheduleRateLimitUpdate();
});
}
#scheduleRateLimitUpdate() {
if (this.#isIntervalIgnored || this.#rateLimitFlushScheduled) {
return;
}
this.#rateLimitFlushScheduled = true;
queueMicrotask(() => {
this.#rateLimitFlushScheduled = false;
this.#updateRateLimitState();
});
}
#rollbackIntervalConsumption() {
if (this.#isIntervalIgnored) {
return;
}
this.#rollbackIntervalSlot();
this.#scheduleRateLimitUpdate();
}
#updateRateLimitState() {
const previous = this.#rateLimitedInInterval;
if (this.#isIntervalIgnored || this.#queue.size === 0) {
if (previous) {
this.#rateLimitedInInterval = false;
this.emit("rateLimitCleared");
}
return;
}
let count;
if (this.#strict) {
const now = Date.now();
this.#cleanupStrictTicks(now);
count = this.#getActiveTicksCount();
} else {
count = this.#intervalCount;
}
const shouldBeRateLimited = count >= this.#intervalCap;
if (shouldBeRateLimited !== previous) {
this.#rateLimitedInInterval = shouldBeRateLimited;
this.emit(shouldBeRateLimited ? "rateLimit" : "rateLimitCleared");
}
}
/**
Whether the queue is currently rate-limited due to intervalCap.
*/
get isRateLimited() {
return this.#rateLimitedInInterval;
}
/**
Whether the queue is saturated. Returns `true` when:
- All concurrency slots are occupied and tasks are waiting, OR
- The queue is rate-limited and tasks are waiting
Useful for detecting backpressure and potential hanging tasks.
```js
import PQueue from 'p-queue';
const queue = new PQueue({concurrency: 2});
// Backpressure handling
if (queue.isSaturated) {
console.log('Queue is saturated, waiting for capacity...');
await queue.onSizeLessThan(queue.concurrency);
}
// Monitoring for stuck tasks
setInterval(() => {
if (queue.isSaturated) {
console.warn(`Queue saturated: ${queue.pending} running, ${queue.size} waiting`);
}
}, 60000);
```
*/
get isSaturated() {
return this.#pending === this.#concurrency && this.#queue.size > 0 || this.isRateLimited && this.#queue.size > 0;
}
/**
The tasks currently being executed. Each task includes its `id`, `priority`, `startTime`, and `timeout` (if set).
Returns an array of task info objects.
```js
import PQueue from 'p-queue';
const queue = new PQueue({concurrency: 2});
// Add tasks with IDs for better debugging
queue.add(() => fetchUser(123), {id: 'user-123'});
queue.add(() => fetchPosts(456), {id: 'posts-456', priority: 1});
// Check what's running
console.log(queue.runningTasks);
// => [{
// id: 'user-123',
// priority: 0,
// startTime: 1759253001716,
// timeout: undefined
// }, {
// id: 'posts-456',
// priority: 1,
// startTime: 1759253001916,
// timeout: undefined
// }]
```
*/
get runningTasks() {
return [...this.#runningTasks.values()].map((task) => ({ ...task }));
}
}
const objectToString = Object.prototype.toString;
const isError = (value) => objectToString.call(value) === "[object Error]";
const errorMessages = /* @__PURE__ */ new Set([
"network error",
// Chrome
"Failed to fetch",
// Chrome
"NetworkError when attempting to fetch resource.",
// Firefox
"The Internet connection appears to be offline.",
// Safari 16
"Network request failed",
// `cross-fetch`
"fetch failed",
// Undici (Node.js)
"terminated",
// Undici (Node.js)
" A network error occurred.",
// Bun (WebKit)
"Network connection lost"
// Cloudflare Workers (fetch)
]);
function isNetworkError(error) {
const isValid = error && isError(error) && error.name === "TypeError" && typeof error.message === "string";
if (!isValid) {
return false;
}
const { message, stack } = error;
if (message === "Load failed") {
return stack === void 0 || "__sentry_captured__" in error;
}
if (message.startsWith("error sending request for url")) {
return true;
}
return errorMessages.has(message);
}
function validateRetries(retries) {
if (typeof retries === "number") {
if (retries < 0) {
throw new TypeError("Expected `retries` to be a non-negative number.");
}
if (Number.isNaN(retries)) {
throw new TypeError("Expected `retries` to be a valid number or Infinity, got NaN.");
}
} else if (retries !== void 0) {
throw new TypeError("Expected `retries` to be a number or Infinity.");
}
}
function validateNumberOption(name, value, { min = 0, allowInfinity = false } = {}) {
if (value === void 0) {
return;
}
if (typeof value !== "number" || Number.isNaN(value)) {
throw new TypeError(`Expected \`${name}\` to be a number${allowInfinity ? " or Infinity" : ""}.`);
}
if (!allowInfinity && !Number.isFinite(value)) {
throw new TypeError(`Expected \`${name}\` to be a finite number.`);
}
if (value < min) {
throw new TypeError(`Expected \`${name}\` to be ≥ ${min}.`);
}
}
class AbortError extends Error {
constructor(message) {
super();
if (message instanceof Error) {
this.originalError = message;
({ message } = message);
} else {
this.originalError = new Error(message);
this.originalError.stack = this.stack;
}
this.name = "AbortError";
this.message = message;
}
}
function calculateDelay(retriesConsumed, options) {
const attempt = Math.max(1, retriesConsumed + 1);
const random = options.randomize ? Math.random() + 1 : 1;
let timeout = Math.round(random * options.minTimeout * options.factor ** (attempt - 1));
timeout = Math.min(timeout, options.maxTimeout);
return timeout;
}
function calculateRemainingTime(start, max) {
if (!Number.isFinite(max)) {
return max;
}
return max - (performance.now() - start);
}
async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTime, options }) {
const normalizedError = error instanceof Error ? error : new TypeError(`Non-error was thrown: "${error}". You should only throw errors.`);
if (normalizedError instanceof AbortError) {
throw normalizedError.originalError;
}
const retriesLeft = Number.isFinite(options.retries) ? Math.max(0, options.retries - retriesConsumed) : options.retries;
const maxRetryTime = options.maxRetryTime ?? Number.POSITIVE_INFINITY;
const context = Object.freeze({
error: normalizedError,
attemptNumber,
retriesLeft,
retriesConsumed
});
await options.onFailedAttempt(context);
if (calculateRemainingTime(startTime, maxRetryTime) <= 0) {
throw normalizedError;
}
const consumeRetry = await options.shouldConsumeRetry(context);
const remainingTime = calculateRemainingTime(startTime, maxRetryTime);
if (remainingTime <= 0 || retriesLeft <= 0) {
throw normalizedError;
}
if (normalizedError instanceof TypeError && !isNetworkError(normalizedError)) {
if (consumeRetry) {
throw normalizedError;
}
options.signal?.throwIfAborted();
return false;
}
if (!await options.shouldRetry(context)) {
throw normalizedError;
}
if (!consumeRetry) {
options.signal?.throwIfAborted();
return false;
}
const delayTime = calculateDelay(retriesConsumed, options);
const finalDelay = Math.min(delayTime, remainingTime);
options.signal?.throwIfAborted();
if (finalDelay > 0) {
await new Promise((resolve, reject) => {
const onAbort = () => {
clearTimeout(timeoutToken);
options.signal?.removeEventListener("abort", onAbort);
reject(options.signal.reason);
};
const timeoutToken = setTimeout(() => {
options.signal?.removeEventListener("abort", onAbort);
resolve();
}, finalDelay);
if (options.unref) {
timeoutToken.unref?.();
}
options.signal?.addEventListener("abort", onAbort, { once: true });
});
}
options.signal?.throwIfAborted();
return true;
}
async function pRetry(input, options = {}) {
options = { ...options };
validateRetries(options.retries);
if (Object.hasOwn(options, "forever")) {
throw new Error("The `forever` option is no longer supported. For many use-cases, you can set `retries: Infinity` instead.");
}
options.retries ??= 10;
options.factor ??= 2;
options.minTimeout ??= 1e3;
options.maxTimeout ??= Number.POSITIVE_INFINITY;
options.maxRetryTime ??= Number.POSITIVE_INFINITY;
options.randomize ??= false;
options.onFailedAttempt ??= () => {
};
options.shouldRetry ??= () => true;
options.shouldConsumeRetry ??= () => true;
validateNumberOption("factor", options.factor, { min: 0, allowInfinity: false });
validateNumberOption("minTimeout", options.minTimeout, { min: 0, allowInfinity: false });
validateNumberOption("maxTimeout", options.maxTimeout, { min: 0, allowInfinity: true });
validateNumberOption("maxRetryTime", options.maxRetryTime, { min: 0, allowInfinity: true });
if (!(options.factor > 0)) {
options.factor = 1;
}
options.signal?.throwIfAborted();
let attemptNumber = 0;
let retriesConsumed = 0;
const startTime = performance.now();
while (Number.isFinite(options.retries) ? retriesConsumed <= options.retries : true) {
attemptNumber++;
try {
options.signal?.throwIfAborted();
const result = await input(attemptNumber);
options.signal?.throwIfAborted();
return result;
} catch (error) {
if (await onAttemptFailure({
error,
attemptNumber,
retriesConsumed,
startTime,
options
})) {
retriesConsumed++;
}
}
}
throw new Error("Retry attempts exhausted without throwing an error.");
}
const textCache = /* @__PURE__ */ new Map();
const textInflight = /* @__PURE__ */ new Map();
function gmGet(opts) {
return new Promise((resolve, reject) => {
const onAbort = () => reject(new Error("Aborted"));
if (opts.signal) {
if (opts.signal.aborted) {
return onAbort();
}
opts.signal.addEventListener("abort", onAbort, { once: true });
}
const cleanup = () => {
if (opts.signal) {
opts.signal.removeEventListener("abort", onAbort);
}
};
const req = GM_xmlhttpRequest({
method: "GET",
url: opts.url,
responseType: opts.responseType,
headers: opts.headers || {},
timeout: opts.timeout ?? CFG.REQUEST_TIMEOUT,
onprogress: (e) => opts.onprogress?.({ loaded: e.loaded, total: e.total }),
onload: (r) => {
cleanup();
if (r.status >= 200 && r.status < 300) {
resolve(r.response);
} else {
reject(new Error(`HTTP ${r.status}`));
}
},
onerror: () => {
cleanup();
reject(new Error("Network error"));
},
ontimeout: () => {
cleanup();
reject(new Error("Timeout"));
},
onabort: () => {
cleanup();
reject(new Error("Aborted"));
}
});
if (opts.signal) {
opts.signal.addEventListener("abort", () => req.abort(), { once: true });
}
});
}
async function fetchText(url, signal) {
const blobInfo = getBlobInfo(url, blobRegistry);
if (blobInfo) {
if (!blobInfo.blob) throw new Error("Blob not found");
return blobInfo.blob.text();
}
return pRetry((attempt) => {
if (signal?.aborted) throw new Error("Aborted");
return gmGet({
url,
responseType: "text",
timeout: CFG.MANIFEST_TIMEOUT,
signal
});
}, {
retries: CFG.RETRIES,
onFailedAttempt: (e) => {
if (signal?.aborted) throw new Error("Aborted");
console.warn(`[SG] Fetch text failed (attempt ${e.attemptNumber}): ${e.error.message}`);
},
signal
// p-retry supports signal in newer versions, but if not, logic above handles it
});
}
function getText(url, signal) {
return once(textCache, textInflight, url, () => fetchText(url, signal), CACHE.TEXT_MAX);
}
class BlobStrategy {
async fetch(url, options) {
const blobInfo = getBlobInfo(url, blobRegistry);
if (!blobInfo || !blobInfo.blob) {
throw new Error("Blob not found");
}
if (options.signal?.aborted) {
throw new Error("Aborted");
}
const part = getBlobSlice(blobInfo.blob, options.headers?.Range);
if (options.onprogress) {
setTimeout(() => {
if (!options.signal?.aborted) {
options.onprogress({ loaded: part.size, total: part.size });
}
}, 0);
}
return new Promise((resolve, reject) => {
const reader = new FileReader();
const onAbort = () => {
reader.abort();
reject(new Error("Aborted"));
};
if (options.signal) {
options.signal.addEventListener("abort", onAbort, { once: true });
}
reader.onload = () => {
if (options.signal) options.signal.removeEventListener("abort", onAbort);
resolve(reader.result);
};
reader.onerror = () => {
if (options.signal) options.signal.removeEventListener("abort", onAbort);
reject(reader.error || new Error("Blob read error"));
};
reader.readAsArrayBuffer(part);
});
}
}
class NativeStrategy {
async fetch(url, options) {
const response = await fetch(url, {
method: "GET",
headers: options.headers,
signal: options.signal
});
if (!response.ok) throw new Error(`Status ${response.status}`);
if (!response.body) throw new Error("No body");
const reader = response.body.getReader();
const contentLength = +(response.headers.get("Content-Length") || "0");
let received = 0;
const chunks = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
received += value.length;
if (options.onprogress && contentLength) {
options.onprogress({ loaded: received, total: contentLength });
}
}
const result = new Uint8Array(received);
let offset = 0;
for (const chunk of chunks) {
result.set(chunk, offset);
offset += chunk.length;
}
return result.buffer;
}
}
class GmStrategy {
async fetch(url, options) {
return gmGet({
url,
responseType: "arraybuffer",
headers: options.headers,
timeout: options.timeout,
onprogress: options.onprogress,
signal: options.signal
});
}
}
function getBin(url, headers = {}, timeout = CFG.REQUEST_TIMEOUT, onprogress, signal) {
const isBlobUrl = getBlobInfo(url, blobRegistry);
if (isBlobUrl) {
return new BlobStrategy().fetch(url, { headers, timeout, onprogress, signal });
}
return new NativeStrategy().fetch(url, { headers, timeout, onprogress, signal }).catch((err) => {
if (signal?.aborted || err.name === "AbortError" || err.message === "Aborted") {
throw err;
}
return new GmStrategy().fetch(url, { headers, timeout, onprogress, signal });
});
}
var hlsParser_min = { exports: {} };
var hasRequiredHlsParser_min;
function requireHlsParser_min() {
if (hasRequiredHlsParser_min) return hlsParser_min.exports;
hasRequiredHlsParser_min = 1;
(function(module, exports$1) {
!(function(t, e) {
module.exports = e();
})(self, (() => (() => {
var t = { 31: function(t2, e2, n2) {
function r(t3) {
return r = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(t4) {
return typeof t4;
} : function(t4) {
return t4 && "function" == typeof Symbol && t4.constructor === Symbol && t4 !== Symbol.prototype ? "symbol" : typeof t4;
}, r(t3);
}
function i(t3, e3) {
var n3 = Object.keys(t3);
if (Object.getOwnPropertySymbols) {
var r2 = Object.getOwnPropertySymbols(t3);
e3 && (r2 = r2.filter((function(e4) {
return Object.getOwnPropertyDescriptor(t3, e4).enumerable;
}))), n3.push.apply(n3, r2);
}
return n3;
}
function a(t3) {
for (var e3 = 1; e3 < arguments.length; e3++) {
var n3 = null != arguments[e3] ? arguments[e3] : {};
e3 % 2 ? i(Object(n3), true).forEach((function(e4) {
d(t3, e4, n3[e4]);
})) : Object.getOwnPropertyDescriptors ? Object.defineProperties(t3, Object.getOwnPropertyDescriptors(n3)) : i(Object(n3)).forEach((function(e4) {
Object.defineProperty(t3, e4, Object.getOwnPropertyDescriptor(n3, e4));
}));
}
return t3;
}
function o(t3, e3, n3) {
return e3 = u(e3), (function(t4, e4) {
if (e4 && ("object" == r(e4) || "function" == typeof e4)) return e4;
if (void 0 !== e4) throw new TypeError("Derived constructors may only return object or undefined");
return (function(t5) {
if (void 0 === t5) throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
return t5;
})(t4);
})(t3, s() ? Reflect.construct(e3, n3 || [], u(t3).constructor) : e3.apply(t3, n3));
}
function s() {
try {
var t3 = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], (function() {
})));
} catch (t4) {
}
return (s = function() {
return !!t3;
})();
}
function u(t3) {
return u = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function(t4) {
return t4.__proto__ || Object.getPrototypeOf(t4);
}, u(t3);
}
function c(t3, e3) {
if ("function" != typeof e3 && null !== e3) throw new TypeError("Super expression must either be null or a function");
t3.prototype = Object.create(e3 && e3.prototype, { constructor: { value: t3, writable: true, configurable: true } }), Object.defineProperty(t3, "prototype", { writable: false }), e3 && l(t3, e3);
}
function l(t3, e3) {
return l = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function(t4, e4) {
return t4.__proto__ = e4, t4;
}, l(t3, e3);
}
function f(t3, e3, n3) {
return Object.defineProperty(t3, "prototype", { writable: false }), t3;
}
function E(t3, e3) {
if (!(t3 instanceof e3)) throw new TypeError("Cannot call a class as a function");
}
function d(t3, e3, n3) {
return (e3 = h2(e3)) in t3 ? Object.defineProperty(t3, e3, { value: n3, enumerable: true, configurable: true, writable: true }) : t3[e3] = n3, t3;
}
function h2(t3) {
var e3 = (function(t4, e4) {
if ("object" != r(t4) || !t4) return t4;
var n3 = t4[Symbol.toPrimitive];
if (void 0 !== n3) {
var i2 = n3.call(t4, e4);
if ("object" != r(i2)) return i2;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === e4 ? String : Number)(t4);
})(t3, "string");
return "symbol" == r(e3) ? e3 : e3 + "";
}
var p, I = this && this.__createBinding || (Object.create ? function(t3, e3, n3, r2) {
void 0 === r2 && (r2 = n3);
var i2 = Object.getOwnPropertyDescriptor(e3, n3);
i2 && !("get" in i2 ? !e3.__esModule : i2.writable || i2.configurable) || (i2 = { enumerable: true, get: function() {
return e3[n3];
} }), Object.defineProperty(t3, r2, i2);
} : function(t3, e3, n3, r2) {
void 0 === r2 && (r2 = n3), t3[r2] = e3[n3];
}), v = this && this.__setModuleDefault || (Object.create ? function(t3, e3) {
Object.defineProperty(t3, "default", { enumerable: true, value: e3 });
} : function(t3, e3) {
t3.default = e3;
}), A = this && this.__importStar || (p = function(t3) {
return p = Object.getOwnPropertyNames || function(t4) {
var e3 = [];
for (var n3 in t4) Object.prototype.hasOwnProperty.call(t4, n3) && (e3[e3.length] = n3);
return e3;
}, p(t3);
}, function(t3) {
if (t3 && t3.__esModule) return t3;
var e3 = {};
if (null != t3) for (var n3 = p(t3), r2 = 0; r2 < n3.length; r2++) "default" !== n3[r2] && I(e3, t3, n3[r2]);
return v(e3, t3), e3;
});
Object.defineProperty(e2, "__esModule", { value: true }), e2.ContentSteering = e2.RenditionReport = e2.PrefetchSegment = e2.PartialSegment = e2.Segment = e2.MediaPlaylist = e2.MasterPlaylist = e2.Playlist = e2.SpliceInfo = e2.DateRange = e2.MediaInitializationSection = e2.Key = e2.SessionData = e2.Variant = e2.Rendition = void 0;
var y = A(n2(203)), S = f((function t3(e3) {
var n3 = e3.type, r2 = e3.uri, i2 = e3.groupId, a2 = e3.language, o2 = e3.assocLanguage, s2 = e3.name, u2 = e3.isDefault, c2 = e3.autoselect, l2 = e3.forced, T = e3.instreamId, f2 = e3.characteristics, h3 = e3.channels, p2 = e3.pathwayId;
E(this, t3), d(this, "type", void 0), d(this, "uri", void 0), d(this, "groupId", void 0), d(this, "language", void 0), d(this, "assocLanguage", void 0), d(this, "name", void 0), d(this, "isDefault", void 0), d(this, "autoselect", void 0), d(this, "forced", void 0), d(this, "instreamId", void 0), d(this, "characteristics", void 0), d(this, "channels", void 0), d(this, "pathwayId", void 0), y.PARAMCHECK(n3, i2, s2), y.CONDITIONALASSERT(["SUBTITLES" === n3, r2], ["CLOSED-CAPTIONS" === n3, T], ["CLOSED-CAPTIONS" === n3, !r2], [l2, "SUBTITLES" === n3]), this.type = n3, this.uri = r2, this.groupId = i2, this.language = a2, this.assocLanguage = o2, this.name = s2, this.isDefault = u2, this.autoselect = c2, this.forced = l2, this.instreamId = T, this.characteristics = f2, this.channels = h3, this.pathwayId = p2;
}));
e2.Rendition = S;
var N = f((function t3(e3) {
var n3 = e3.uri, r2 = e3.isIFrameOnly, i2 = void 0 !== r2 && r2, a2 = e3.bandwidth, o2 = e3.averageBandwidth, s2 = e3.score, u2 = e3.codecs, c2 = e3.resolution, l2 = e3.frameRate, T = e3.hdcpLevel, f2 = e3.allowedCpc, h3 = e3.videoRange, p2 = e3.stableVariantId, I2 = e3.pathwayId, v2 = e3.programId, A2 = e3.audio, S2 = void 0 === A2 ? [] : A2, N2 = e3.video, m2 = void 0 === N2 ? [] : N2, X2 = e3.subtitles, g2 = void 0 === X2 ? [] : X2, b2 = e3.closedCaptions, O2 = void 0 === b2 ? [] : b2, R2 = e3.currentRenditions, P2 = void 0 === R2 ? { audio: 0, video: 0, subtitles: 0, closedCaptions: 0 } : R2;
E(this, t3), d(this, "uri", void 0), d(this, "isIFrameOnly", void 0), d(this, "bandwidth", void 0), d(this, "averageBandwidth", void 0), d(this, "score", void 0), d(this, "codecs", void 0), d(this, "resolution", void 0), d(this, "frameRate", void 0), d(this, "hdcpLevel", void 0), d(this, "allowedCpc", void 0), d(this, "videoRange", void 0), d(this, "stableVariantId", void 0), d(this, "pathwayId", void 0), d(this, "programId", void 0), d(this, "audio", void 0), d(this, "video", void 0), d(this, "subtitles", void 0), d(this, "closedCaptions", void 0), d(this, "currentRenditions", void 0), y.PARAMCHECK(n3, a2), this.uri = n3, this.isIFrameOnly = i2, this.bandwidth = a2, this.averageBandwidth = o2, this.score = s2, this.codecs = u2, this.resolution = c2, this.frameRate = l2, this.hdcpLevel = T, this.allowedCpc = f2, this.videoRange = h3, this.stableVariantId = p2, this.pathwayId = I2, this.programId = v2, this.audio = S2, this.video = m2, this.subtitles = g2, this.closedCaptions = O2, this.currentRenditions = P2;
}));
e2.Variant = N;
var m = f((function t3(e3) {
var n3 = e3.id, r2 = e3.value, i2 = e3.uri, a2 = e3.language;
E(this, t3), d(this, "id", void 0), d(this, "value", void 0), d(this, "uri", void 0), d(this, "language", void 0), y.PARAMCHECK(n3, r2 || i2), y.ASSERT("SessionData cannot have both value and uri, shoud be either.", !(r2 && i2)), this.id = n3, this.value = r2, this.uri = i2, this.language = a2;
}));
e2.SessionData = m;
var X = f((function t3(e3) {
var n3 = e3.method, r2 = e3.uri, i2 = e3.iv, a2 = e3.format, o2 = e3.formatVersion;
E(this, t3), d(this, "method", void 0), d(this, "uri", void 0), d(this, "iv", void 0), d(this, "format", void 0), d(this, "formatVersion", void 0), y.PARAMCHECK(n3), y.CONDITIONALPARAMCHECK(["NONE" !== n3, r2]), y.CONDITIONALASSERT(["NONE" === n3, !(r2 || i2 || a2 || o2)]), this.method = n3, this.uri = r2, this.iv = i2, this.format = a2, this.formatVersion = o2;
}));
e2.Key = X;
var g = f((function t3(e3) {
var n3 = e3.serverUri, r2 = e3.pathwayId;
E(this, t3), d(this, "serverUri", void 0), d(this, "pathwayId", void 0), this.serverUri = n3, this.pathwayId = r2;
}));
e2.ContentSteering = g;
var b = f((function t3(e3) {
var n3 = e3.hint, r2 = void 0 !== n3 && n3, i2 = e3.uri, a2 = e3.mimeType, o2 = e3.byterange;
E(this, t3), d(this, "hint", void 0), d(this, "uri", void 0), d(this, "mimeType", void 0), d(this, "byterange", void 0), y.PARAMCHECK(i2), this.hint = r2, this.uri = i2, this.mimeType = a2, this.byterange = o2;
}));
e2.MediaInitializationSection = b;
var O = f((function t3(e3) {
var n3 = e3.id, r2 = e3.classId, i2 = e3.start, a2 = e3.cue, o2 = e3.end, s2 = e3.duration, u2 = e3.plannedDuration, c2 = e3.endOnNext, l2 = e3.attributes, T = void 0 === l2 ? {} : l2;
E(this, t3), d(this, "id", void 0), d(this, "classId", void 0), d(this, "start", void 0), d(this, "cue", void 0), d(this, "end", void 0), d(this, "duration", void 0), d(this, "plannedDuration", void 0), d(this, "endOnNext", void 0), d(this, "attributes", void 0), y.PARAMCHECK(n3), y.CONDITIONALPARAMCHECK([true === c2, r2]), y.CONDITIONALASSERT([o2, i2], [o2, i2 <= o2], [s2, s2 >= 0], [u2, u2 >= 0]), this.id = n3, this.classId = r2, this.start = i2, this.cue = a2, this.end = o2, this.duration = s2, this.plannedDuration = u2, this.endOnNext = c2, this.attributes = T;
}));
e2.DateRange = O;
var R = f((function t3(e3) {
var n3 = e3.type, r2 = e3.duration, i2 = e3.tagName, a2 = e3.value;
E(this, t3), d(this, "type", void 0), d(this, "duration", void 0), d(this, "tagName", void 0), d(this, "value", void 0), y.PARAMCHECK(n3), y.CONDITIONALPARAMCHECK(["OUT" === n3, r2]), y.CONDITIONALPARAMCHECK(["RAW" === n3, i2]), this.type = n3, this.duration = r2, this.tagName = i2, this.value = a2;
}));
e2.SpliceInfo = R;
var P = f((function t3(e3) {
E(this, t3), d(this, "type", void 0), y.PARAMCHECK(e3), this.type = e3;
})), D = (function(t3) {
function e3(t4) {
var n3, r2 = t4.isMasterPlaylist, i2 = t4.uri, a2 = t4.version, s2 = t4.independentSegments, u2 = void 0 !== s2 && s2, c2 = t4.start, l2 = t4.source, T = t4.defines;
return E(this, e3), d(n3 = o(this, e3, ["playlist"]), "isMasterPlaylist", void 0), d(n3, "uri", void 0), d(n3, "version", void 0), d(n3, "independentSegments", void 0), d(n3, "start", void 0), d(n3, "source", void 0), d(n3, "defines", void 0), y.PARAMCHECK(r2), n3.isMasterPlaylist = r2, n3.uri = i2, n3.version = a2, n3.independentSegments = u2, n3.start = c2, n3.source = l2, n3.defines = T, n3;
}
return c(e3, t3), f(e3);
})(P);
e2.Playlist = D;
var L = (function(t3) {
function e3() {
var t4, n3 = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {};
E(this, e3), d(t4 = o(this, e3, [a(a({}, n3), {}, { isMasterPlaylist: true })]), "variants", void 0), d(t4, "currentVariant", void 0), d(t4, "sessionDataList", void 0), d(t4, "sessionKeyList", void 0), d(t4, "contentSteering", void 0);
var r2 = n3.variants, i2 = void 0 === r2 ? [] : r2, s2 = n3.currentVariant, u2 = n3.sessionDataList, c2 = void 0 === u2 ? [] : u2, l2 = n3.sessionKeyList, T = void 0 === l2 ? [] : l2, f2 = n3.contentSteering, h3 = void 0 === f2 ? void 0 : f2;
return t4.variants = i2, t4.currentVariant = s2, t4.sessionDataList = c2, t4.sessionKeyList = T, t4.contentSteering = h3, t4;
}
return c(e3, t3), f(e3);
})(D);
e2.MasterPlaylist = L;
var C = (function(t3) {
function e3() {
var t4, n3 = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {};
E(this, e3), d(t4 = o(this, e3, [a(a({}, n3), {}, { isMasterPlaylist: false })]), "targetDuration", void 0), d(t4, "mediaSequenceBase", void 0), d(t4, "discontinuitySequenceBase", void 0), d(t4, "endlist", void 0), d(t4, "playlistType", void 0), d(t4, "isIFrame", void 0), d(t4, "segments", void 0), d(t4, "prefetchSegments", void 0), d(t4, "lowLatencyCompatibility", void 0), d(t4, "partTargetDuration", void 0), d(t4, "renditionReports", void 0), d(t4, "skip", void 0), d(t4, "hash", void 0);
var r2 = n3.targetDuration, i2 = n3.mediaSequenceBase, s2 = void 0 === i2 ? 0 : i2, u2 = n3.discontinuitySequenceBase, c2 = void 0 === u2 ? 0 : u2, l2 = n3.endlist, T = void 0 !== l2 && l2, f2 = n3.playlistType, h3 = n3.isIFrame, p2 = n3.segments, I2 = void 0 === p2 ? [] : p2, v2 = n3.prefetchSegments, A2 = void 0 === v2 ? [] : v2, y2 = n3.lowLatencyCompatibility, S2 = n3.partTargetDuration, N2 = n3.renditionReports, m2 = void 0 === N2 ? [] : N2, X2 = n3.skip, g2 = void 0 === X2 ? 0 : X2, b2 = n3.hash;
return t4.targetDuration = r2, t4.mediaSequenceBase = s2, t4.discontinuitySequenceBase = c2, t4.endlist = T, t4.playlistType = f2, t4.isIFrame = h3, t4.segments = I2, t4.prefetchSegments = A2, t4.lowLatencyCompatibility = y2, t4.partTargetDuration = S2, t4.renditionReports = m2, t4.skip = g2, t4.hash = b2, t4;
}
return c(e3, t3), f(e3);
})(D);
e2.MediaPlaylist = C;
var M = (function(t3) {
function e3(t4) {
var n3, r2 = t4.uri, i2 = t4.mimeType, a2 = t4.data, s2 = t4.duration, u2 = t4.title, c2 = t4.byterange, l2 = t4.discontinuity, T = t4.mediaSequenceNumber, f2 = void 0 === T ? 0 : T, h3 = t4.discontinuitySequence, p2 = void 0 === h3 ? 0 : h3, I2 = t4.key, v2 = t4.map, A2 = t4.programDateTime, y2 = t4.dateRange, S2 = t4.markers, N2 = void 0 === S2 ? [] : S2, m2 = t4.parts, X2 = void 0 === m2 ? [] : m2, g2 = t4.gap;
return E(this, e3), d(n3 = o(this, e3, ["segment"]), "uri", void 0), d(n3, "mimeType", void 0), d(n3, "data", void 0), d(n3, "duration", void 0), d(n3, "title", void 0), d(n3, "byterange", void 0), d(n3, "discontinuity", void 0), d(n3, "mediaSequenceNumber", void 0), d(n3, "discontinuitySequence", void 0), d(n3, "key", void 0), d(n3, "map", void 0), d(n3, "programDateTime", void 0), d(n3, "dateRange", void 0), d(n3, "markers", void 0), d(n3, "parts", void 0), d(n3, "gap", void 0), n3.uri = r2, n3.mimeType = i2, n3.data = a2, n3.duration = s2, n3.title = u2, n3.byterange = c2, n3.discontinuity = l2, n3.mediaSequenceNumber = f2, n3.discontinuitySequence = p2, n3.key = I2, n3.map = v2, n3.programDateTime = A2, n3.dateRange = y2, n3.markers = N2, n3.parts = X2, n3.gap = g2, n3;
}
return c(e3, t3), f(e3);
})(P);
e2.Segment = M;
var U = (function(t3) {
function e3(t4) {
var n3, r2 = t4.hint, i2 = void 0 !== r2 && r2, a2 = t4.uri, s2 = t4.duration, u2 = t4.independent, c2 = t4.byterange, l2 = t4.gap;
return E(this, e3), d(n3 = o(this, e3, ["part"]), "hint", void 0), d(n3, "uri", void 0), d(n3, "duration", void 0), d(n3, "independent", void 0), d(n3, "byterange", void 0), d(n3, "gap", void 0), y.PARAMCHECK(a2), n3.hint = i2, n3.uri = a2, n3.duration = s2, n3.independent = u2, n3.duration = s2, n3.byterange = c2, n3.gap = l2, n3;
}
return c(e3, t3), f(e3);
})(P);
e2.PartialSegment = U;
var w = (function(t3) {
function e3(t4) {
var n3, r2 = t4.uri, i2 = t4.discontinuity, a2 = t4.mediaSequenceNumber, s2 = void 0 === a2 ? 0 : a2, u2 = t4.discontinuitySequence, c2 = void 0 === u2 ? 0 : u2, l2 = t4.key;
return E(this, e3), d(n3 = o(this, e3, ["prefetch"]), "uri", void 0), d(n3, "discontinuity", void 0), d(n3, "mediaSequenceNumber", void 0), d(n3, "discontinuitySequence", void 0), d(n3, "key", void 0), y.PARAMCHECK(r2), n3.uri = r2, n3.discontinuity = i2, n3.mediaSequenceNumber = s2, n3.discontinuitySequence = c2, n3.key = l2, n3;
}
return c(e3, t3), f(e3);
})(P);
e2.PrefetchSegment = w;
var Y = f((function t3(e3) {
var n3 = e3.uri, r2 = e3.lastMSN, i2 = e3.lastPart;
E(this, t3), d(this, "uri", void 0), d(this, "lastMSN", void 0), d(this, "lastPart", void 0), y.PARAMCHECK(n3), this.uri = n3, this.lastMSN = r2, this.lastPart = i2;
}));
e2.RenditionReport = Y;
}, 73: function(t2, e2, n2) {
var r, i = this && this.__createBinding || (Object.create ? function(t3, e3, n3, r2) {
void 0 === r2 && (r2 = n3);
var i2 = Object.getOwnPropertyDescriptor(e3, n3);
i2 && !("get" in i2 ? !e3.__esModule : i2.writable || i2.configurable) || (i2 = { enumerable: true, get: function() {
return e3[n3];
} }), Object.defineProperty(t3, r2, i2);
} : function(t3, e3, n3, r2) {
void 0 === r2 && (r2 = n3), t3[r2] = e3[n3];
}), a = this && this.__setModuleDefault || (Object.create ? function(t3, e3) {
Object.defineProperty(t3, "default", { enumerable: true, value: e3 });
} : function(t3, e3) {
t3.default = e3;
}), o = this && this.__importStar || (r = function(t3) {
return r = Object.getOwnPropertyNames || function(t4) {
var e3 = [];
for (var n3 in t4) Object.prototype.hasOwnProperty.call(t4, n3) && (e3[e3.length] = n3);
return e3;
}, r(t3);
}, function(t3) {
if (t3 && t3.__esModule) return t3;
var e3 = {};
if (null != t3) for (var n3 = r(t3), o2 = 0; o2 < n3.length; o2++) "default" !== n3[o2] && i(e3, t3, n3[o2]);
return a(e3, t3), e3;
}), s = this && this.__importDefault || function(t3) {
return t3 && t3.__esModule ? t3 : { default: t3 };
};
Object.defineProperty(e2, "__esModule", { value: true }), e2.setOptions = e2.getOptions = e2.types = e2.stringify = e2.parse = void 0;
const u = n2(203);
Object.defineProperty(e2, "getOptions", { enumerable: true, get: function() {
return u.getOptions;
} }), Object.defineProperty(e2, "setOptions", { enumerable: true, get: function() {
return u.setOptions;
} });
const c = s(n2(377));
e2.parse = c.default;
const l = s(n2(887));
e2.stringify = l.default;
const T = o(n2(31));
e2.types = T;
}, 203: (t2, e2) => {
function n2(t3, e3) {
return (function(t4) {
if (Array.isArray(t4)) return t4;
})(t3) || (function(t4, e4) {
var n3 = null == t4 ? null : "undefined" != typeof Symbol && t4[Symbol.iterator] || t4["@@iterator"];
if (null != n3) {
var r2, i2, a2, o2, s2 = [], u = true, c = false;
try {
if (a2 = (n3 = n3.call(t4)).next, 0 === e4) ;
else for (; !(u = (r2 = a2.call(n3)).done) && (s2.push(r2.value), s2.length !== e4); u = true) ;
} catch (t5) {
c = true, i2 = t5;
} finally {
try {
if (!u && null != n3.return && (o2 = n3.return(), Object(o2) !== o2)) return;
} finally {
if (c) throw i2;
}
}
return s2;
}
})(t3, e3) || i(t3, e3) || (function() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
})();
}
function r(t3, e3) {
var n3 = "undefined" != typeof Symbol && t3[Symbol.iterator] || t3["@@iterator"];
if (!n3) {
if (Array.isArray(t3) || (n3 = i(t3)) || e3) {
n3 && (t3 = n3);
var r2 = 0, a2 = function() {
};
return { s: a2, n: function() {
return r2 >= t3.length ? { done: true } : { done: false, value: t3[r2++] };
}, e: function(t4) {
throw t4;
}, f: a2 };
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var o2, s2 = true, u = false;
return { s: function() {
n3 = n3.call(t3);
}, n: function() {
var t4 = n3.next();
return s2 = t4.done, t4;
}, e: function(t4) {
u = true, o2 = t4;
}, f: function() {
try {
s2 || null == n3.return || n3.return();
} finally {
if (u) throw o2;
}
} };
}
function i(t3, e3) {
if (t3) {
if ("string" == typeof t3) return a(t3, e3);
var n3 = {}.toString.call(t3).slice(8, -1);
return "Object" === n3 && t3.constructor && (n3 = t3.constructor.name), "Map" === n3 || "Set" === n3 ? Array.from(t3) : "Arguments" === n3 || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n3) ? a(t3, e3) : void 0;
}
}
function a(t3, e3) {
(null == e3 || e3 > t3.length) && (e3 = t3.length);
for (var n3 = 0, r2 = Array(e3); n3 < e3; n3++) r2[n3] = t3[n3];
return r2;
}
Object.defineProperty(e2, "__esModule", { value: true }), e2.THROW = s, e2.ASSERT = function(t3) {
for (var e3 = arguments.length, i2 = new Array(e3 > 1 ? e3 - 1 : 0), a2 = 1; a2 < e3; a2++) i2[a2 - 1] = arguments[a2];
var o2, u = r(i2.entries());
try {
for (u.s(); !(o2 = u.n()).done; ) {
var c = n2(o2.value, 2), l = c[0];
c[1] || s(new Error("".concat(t3, " : Failed at [").concat(l, "]")));
}
} catch (t4) {
u.e(t4);
} finally {
u.f();
}
}, e2.CONDITIONALASSERT = function() {
for (var t3 = arguments.length, e3 = new Array(t3), i2 = 0; i2 < t3; i2++) e3[i2] = arguments[i2];
var a2, o2 = r(e3.entries());
try {
for (o2.s(); !(a2 = o2.n()).done; ) {
var u = n2(a2.value, 2), c = u[0], l = n2(u[1], 2), T = l[0], f = l[1];
T && (f || s(new Error("Conditional Assert : Failed at [".concat(c, "]"))));
}
} catch (t4) {
o2.e(t4);
} finally {
o2.f();
}
}, e2.PARAMCHECK = function() {
for (var t3 = arguments.length, e3 = new Array(t3), i2 = 0; i2 < t3; i2++) e3[i2] = arguments[i2];
var a2, o2 = r(e3.entries());
try {
for (o2.s(); !(a2 = o2.n()).done; ) {
var u = n2(a2.value, 2), c = u[0];
void 0 === u[1] && s(new Error("Param Check : Failed at [".concat(c, "]")));
}
} catch (t4) {
o2.e(t4);
} finally {
o2.f();
}
}, e2.CONDITIONALPARAMCHECK = function() {
for (var t3 = arguments.length, e3 = new Array(t3), i2 = 0; i2 < t3; i2++) e3[i2] = arguments[i2];
var a2, o2 = r(e3.entries());
try {
for (o2.s(); !(a2 = o2.n()).done; ) {
var u = n2(a2.value, 2), c = u[0], l = n2(u[1], 2), T = l[0], f = l[1];
T && (void 0 === f && s(new Error("Conditional Param Check : Failed at [".concat(c, "]"))));
}
} catch (t4) {
o2.e(t4);
} finally {
o2.f();
}
}, e2.INVALIDPLAYLIST = function(t3) {
s(new Error("Invalid Playlist : ".concat(t3)));
}, e2.toNumber = function(t3) {
var e3 = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : 10;
if ("number" == typeof t3) return t3;
var n3 = 10 === e3 ? Number.parseFloat(t3) : Number.parseInt(t3, e3);
if (Number.isNaN(n3)) return 0;
return n3;
}, e2.hexToByteSequence = function(t3) {
(t3.startsWith("0x") || t3.startsWith("0X")) && (t3 = t3.slice(2));
for (var e3 = new Uint8Array(t3.length / 2), n3 = 0; n3 < t3.length; n3 += 2) e3[n3 / 2] = Number.parseInt(t3.slice(n3, n3 + 2), 16);
return e3;
}, e2.byteSequenceToHex = function(t3) {
var e3 = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : 0, n3 = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : t3.byteLength;
n3 <= e3 && s(new Error("end must be larger than start : start=".concat(e3, ", end=").concat(n3)));
for (var r2 = [], i2 = e3; i2 < n3; i2++) r2.push("0".concat((255 & t3[i2]).toString(16).toUpperCase()).slice(-2));
return "0x".concat(r2.join(""));
}, e2.tryCatch = function(t3, e3) {
try {
return t3();
} catch (t4) {
return e3(t4);
}
}, e2.splitAt = function(t3, e3) {
for (var n3 = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : 0, r2 = -1, i2 = 0, a2 = 0; i2 < t3.length; i2++) if (t3[i2] === e3) {
if (a2++ === n3) return [t3.slice(0, i2), t3.slice(i2 + 1)];
r2 = i2;
}
if (-1 !== r2) return [t3.slice(0, r2), t3.slice(r2 + 1)];
return [t3];
}, e2.trim = function(t3) {
var e3 = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : " ";
if (!t3) return t3;
if (t3 = t3.trim(), " " === e3) return t3;
t3.startsWith(e3) && (t3 = t3.slice(1));
t3.endsWith(e3) && (t3 = t3.slice(0, -1));
return t3;
}, e2.splitByCommaWithPreservingQuotes = function(t3) {
for (var e3 = [], n3 = true, r2 = 0, i2 = [], a2 = 0; a2 < t3.length; a2++) {
var o2 = t3[a2];
n3 && "," === o2 ? (e3.push(t3.slice(r2, a2).trim()), r2 = a2 + 1) : '"' !== o2 && "'" !== o2 || (n3 ? (i2.push(o2), n3 = false) : o2 === i2.at(-1) ? (i2.pop(), n3 = true) : i2.push(o2));
}
return e3.push(t3.slice(r2).trim()), e3;
}, e2.camelify = function(t3) {
var e3, n3 = [], i2 = false, a2 = r(t3);
try {
for (a2.s(); !(e3 = a2.n()).done; ) {
var o2 = e3.value;
"-" !== o2 && "_" !== o2 ? i2 ? (n3.push(o2.toUpperCase()), i2 = false) : n3.push(o2.toLowerCase()) : i2 = true;
}
} catch (t4) {
a2.e(t4);
} finally {
a2.f();
}
return n3.join("");
}, e2.formatDate = function(t3) {
var e3 = t3.getUTCFullYear(), n3 = ("0" + (t3.getUTCMonth() + 1)).slice(-2), r2 = ("0" + t3.getUTCDate()).slice(-2), i2 = ("0" + t3.getUTCHours()).slice(-2), a2 = ("0" + t3.getUTCMinutes()).slice(-2), o2 = ("0" + t3.getUTCSeconds()).slice(-2), s2 = ("00" + t3.getUTCMilliseconds()).slice(-3);
return "".concat(e3, "-").concat(n3, "-").concat(r2, "T").concat(i2, ":").concat(a2, ":").concat(o2, ".").concat(s2, "Z");
}, e2.hasOwnProp = function(t3, e3) {
return Object.hasOwn(t3, e3);
}, e2.setOptions = function() {
var t3 = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {};
o = Object.assign(o, t3);
}, e2.getOptions = function() {
return Object.assign({}, o);
};
var o = {};
function s(t3) {
if (o.strictMode) throw t3;
o.silent || console.error(t3.message);
}
}, 377: function(t2, e2, n2) {
function r(t3, e3) {
return (function(t4) {
if (Array.isArray(t4)) return t4;
})(t3) || (function(t4, e4) {
var n3 = null == t4 ? null : "undefined" != typeof Symbol && t4[Symbol.iterator] || t4["@@iterator"];
if (null != n3) {
var r2, i2, a2, o2, s2 = [], u2 = true, c2 = false;
try {
if (a2 = (n3 = n3.call(t4)).next, 0 === e4) {
if (Object(n3) !== n3) return;
u2 = false;
} else for (; !(u2 = (r2 = a2.call(n3)).done) && (s2.push(r2.value), s2.length !== e4); u2 = true) ;
} catch (t5) {
c2 = true, i2 = t5;
} finally {
try {
if (!u2 && null != n3.return && (o2 = n3.return(), Object(o2) !== o2)) return;
} finally {
if (c2) throw i2;
}
}
return s2;
}
})(t3, e3) || a(t3, e3) || (function() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
})();
}
function i(t3, e3) {
var n3 = "undefined" != typeof Symbol && t3[Symbol.iterator] || t3["@@iterator"];
if (!n3) {
if (Array.isArray(t3) || (n3 = a(t3)) || e3) {
n3 && (t3 = n3);
var r2 = 0, i2 = function() {
};
return { s: i2, n: function() {
return r2 >= t3.length ? { done: true } : { done: false, value: t3[r2++] };
}, e: function(t4) {
throw t4;
}, f: i2 };
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var o2, s2 = true, u2 = false;
return { s: function() {
n3 = n3.call(t3);
}, n: function() {
var t4 = n3.next();
return s2 = t4.done, t4;
}, e: function(t4) {
u2 = true, o2 = t4;
}, f: function() {
try {
s2 || null == n3.return || n3.return();
} finally {
if (u2) throw o2;
}
} };
}
function a(t3, e3) {
if (t3) {
if ("string" == typeof t3) return o(t3, e3);
var n3 = {}.toString.call(t3).slice(8, -1);
return "Object" === n3 && t3.constructor && (n3 = t3.constructor.name), "Map" === n3 || "Set" === n3 ? Array.from(t3) : "Arguments" === n3 || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n3) ? o(t3, e3) : void 0;
}
}
function o(t3, e3) {
(null == e3 || e3 > t3.length) && (e3 = t3.length);
for (var n3 = 0, r2 = Array(e3); n3 < e3; n3++) r2[n3] = t3[n3];
return r2;
}
var s, u = this && this.__createBinding || (Object.create ? function(t3, e3, n3, r2) {
void 0 === r2 && (r2 = n3);
var i2 = Object.getOwnPropertyDescriptor(e3, n3);
i2 && !("get" in i2 ? !e3.__esModule : i2.writable || i2.configurable) || (i2 = { enumerable: true, get: function() {
return e3[n3];
} }), Object.defineProperty(t3, r2, i2);
} : function(t3, e3, n3, r2) {
void 0 === r2 && (r2 = n3), t3[r2] = e3[n3];
}), c = this && this.__setModuleDefault || (Object.create ? function(t3, e3) {
Object.defineProperty(t3, "default", { enumerable: true, value: e3 });
} : function(t3, e3) {
t3.default = e3;
}), l = this && this.__importStar || (s = function(t3) {
return s = Object.getOwnPropertyNames || function(t4) {
var e3 = [];
for (var n3 in t4) Object.prototype.hasOwnProperty.call(t4, n3) && (e3[e3.length] = n3);
return e3;
}, s(t3);
}, function(t3) {
if (t3 && t3.__esModule) return t3;
var e3 = {};
if (null != t3) for (var n3 = s(t3), r2 = 0; r2 < n3.length; r2++) "default" !== n3[r2] && u(e3, t3, n3[r2]);
return c(e3, t3), e3;
});
Object.defineProperty(e2, "__esModule", { value: true });
var T = l(n2(203)), f = n2(31);
function E(t3) {
return T.trim(t3, '"');
}
function d(t3) {
var e3 = T.splitAt(t3, ",");
return { duration: T.toNumber(e3[0]), title: decodeURIComponent(escape(e3[1])) };
}
function h2(t3) {
var e3 = T.splitAt(t3, "@");
return { length: T.toNumber(e3[0]), offset: e3[1] ? T.toNumber(e3[1]) : -1 };
}
function p(t3) {
var e3 = T.splitAt(t3, "x");
return { width: T.toNumber(e3[0]), height: T.toNumber(e3[1]) };
}
function I(t3) {
var e3 = "ALLOWED-CPC: Each entry must consit of KEYFORMAT and Content Protection Configuration", n3 = t3.split(",");
0 === n3.length && T.INVALIDPLAYLIST(e3);
var a2, o2 = [], s2 = i(n3);
try {
for (s2.s(); !(a2 = s2.n()).done; ) {
var u2 = a2.value, c2 = r(T.splitAt(u2, ":"), 2), l2 = c2[0], f2 = c2[1];
l2 && f2 ? o2.push({ format: l2, cpcList: f2.split("/") }) : T.INVALIDPLAYLIST(e3);
}
} catch (t4) {
s2.e(t4);
} finally {
s2.f();
}
return o2;
}
function v(t3) {
return t3.startsWith('"') ? E(t3) : t3.startsWith("0x") || t3.startsWith("0X") ? T.hexToByteSequence(t3) : T.toNumber(t3);
}
function A(t3, e3) {
e3.IV && t3.compatibleVersion < 2 && (t3.compatibleVersion = 2), (e3.KEYFORMAT || e3.KEYFORMATVERSIONS) && t3.compatibleVersion < 5 && (t3.compatibleVersion = 5);
}
function y(t3) {
var e3, n3, a2, o2 = {}, s2 = i(T.splitByCommaWithPreservingQuotes(t3));
try {
for (s2.s(); !(e3 = s2.n()).done; ) {
var u2 = e3.value, c2 = r(T.splitAt(u2, "="), 2), l2 = c2[0], f2 = c2[1], d2 = E(f2);
switch (l2) {
case "URI":
o2[l2] = d2;
break;
case "START-DATE":
case "END-DATE":
o2[l2] = new Date(d2);
break;
case "IV":
o2[l2] = (n3 = d2, a2 = void 0, 16 !== (a2 = T.hexToByteSequence(n3)).length && T.INVALIDPLAYLIST("IV must be a 128-bit unsigned integer"), a2);
break;
case "BYTERANGE":
o2[l2] = h2(d2);
break;
case "RESOLUTION":
o2[l2] = p(d2);
break;
case "ALLOWED-CPC":
o2[l2] = I(d2);
break;
case "END-ON-NEXT":
case "DEFAULT":
case "AUTOSELECT":
case "FORCED":
case "PRECISE":
case "CAN-BLOCK-RELOAD":
case "INDEPENDENT":
case "GAP":
o2[l2] = "YES" === d2;
break;
case "DURATION":
case "PLANNED-DURATION":
case "BANDWIDTH":
case "AVERAGE-BANDWIDTH":
case "FRAME-RATE":
case "TIME-OFFSET":
case "CAN-SKIP-UNTIL":
case "HOLD-BACK":
case "PART-HOLD-BACK":
case "PART-TARGET":
case "BYTERANGE-START":
case "BYTERANGE-LENGTH":
case "LAST-MSN":
case "LAST-PART":
case "SKIPPED-SEGMENTS":
case "SCORE":
case "PROGRAM-ID":
o2[l2] = T.toNumber(d2);
break;
default:
l2.startsWith("SCTE35-") ? o2[l2] = T.hexToByteSequence(d2) : l2.startsWith("X-") ? o2[l2] = v(f2) : ("VIDEO-RANGE" === l2 && "SDR" !== d2 && "HLG" !== d2 && "PQ" !== d2 && T.INVALIDPLAYLIST('VIDEO-RANGE: unknown value "'.concat(d2, '"')), o2[l2] = d2);
}
}
} catch (t4) {
s2.e(t4);
} finally {
s2.f();
}
return o2;
}
function S() {
T.INVALIDPLAYLIST("The file contains both media and master playlist tags.");
}
function N(t3, e3, n3) {
var r2, a2 = (r2 = e3.attributes, new f.Rendition({ type: r2.TYPE, uri: r2.URI, groupId: r2["GROUP-ID"], language: r2.LANGUAGE, assocLanguage: r2["ASSOC-LANGUAGE"], name: r2.NAME, isDefault: r2.DEFAULT, autoselect: r2.AUTOSELECT, forced: r2.FORCED, instreamId: r2["INSTREAM-ID"], characteristics: r2.CHARACTERISTICS, channels: r2.CHANNELS, pathwayId: r2["PATHWAY-ID"] })), o2 = t3[T.camelify(n3)], s2 = (function(t4, e4) {
var n4, r3 = false, a3 = i(t4);
try {
for (a3.s(); !(n4 = a3.n()).done; ) {
var o3 = n4.value;
if (o3.name === e4.name) return "All EXT-X-MEDIA tags in the same Group MUST have different NAME attributes.";
o3.isDefault && (r3 = true);
}
} catch (t5) {
a3.e(t5);
} finally {
a3.f();
}
return r3 && e4.isDefault ? "EXT-X-MEDIA A Group MUST NOT have more than one member with a DEFAULT attribute of YES." : "";
})(o2, a2);
s2 && T.INVALIDPLAYLIST(s2), o2.push(a2), a2.isDefault && (t3.currentRenditions[T.camelify(n3)] = o2.length - 1);
}
function m(t3, e3, n3, r2, a2) {
var o2, s2 = new f.Variant({ uri: n3, bandwidth: e3.BANDWIDTH, averageBandwidth: e3["AVERAGE-BANDWIDTH"], score: e3.SCORE, codecs: e3.CODECS, resolution: e3.RESOLUTION, frameRate: e3["FRAME-RATE"], hdcpLevel: e3["HDCP-LEVEL"], allowedCpc: e3["ALLOWED-CPC"], videoRange: e3["VIDEO-RANGE"], stableVariantId: e3["STABLE-VARIANT-ID"], pathwayId: e3["STABLE-PATHWAY-ID"], programId: e3["PROGRAM-ID"] }), u2 = i(t3);
try {
for (u2.s(); !(o2 = u2.n()).done; ) {
var c2 = o2.value;
if ("EXT-X-MEDIA" === c2.name) {
var l2 = c2.attributes, E2 = l2.TYPE;
if (E2 && l2["GROUP-ID"] || T.INVALIDPLAYLIST("EXT-X-MEDIA TYPE attribute is REQUIRED."), e3[E2] === l2["GROUP-ID"] && (N(s2, c2, E2), "CLOSED-CAPTIONS" === E2)) {
var d2, h3 = i(s2.closedCaptions);
try {
for (h3.s(); !(d2 = h3.n()).done; ) {
var p2 = d2.value.instreamId;
if (p2 && p2.startsWith("SERVICE") && a2.compatibleVersion < 7) {
a2.compatibleVersion = 7;
break;
}
}
} catch (t4) {
h3.e(t4);
} finally {
h3.f();
}
}
}
}
} catch (t4) {
u2.e(t4);
} finally {
u2.f();
}
return (function(t4, e4, n4) {
for (var r3 = function() {
var r4 = a3[i2];
"CLOSED-CAPTIONS" === r4 && "NONE" === t4[r4] ? (n4.isClosedCaptionsNone = true, e4.closedCaptions = []) : t4[r4] && !e4[T.camelify(r4)].some((function(e5) {
return e5.groupId === t4[r4];
})) && T.INVALIDPLAYLIST("".concat(r4, " attribute MUST match the value of the GROUP-ID attribute of an EXT-X-MEDIA tag whose TYPE attribute is ").concat(r4, "."));
}, i2 = 0, a3 = ["AUDIO", "VIDEO", "SUBTITLES", "CLOSED-CAPTIONS"]; i2 < a3.length; i2++) r3();
})(e3, s2, a2), s2.isIFrameOnly = r2, s2;
}
function X(t3, e3, n3, r2, i2, a2, o2) {
for (var s2 = new f.Segment({ uri: e3, mediaSequenceNumber: i2, discontinuitySequence: a2 }), u2 = false, c2 = false, l2 = n3; l2 <= r2; l2++) {
var E2 = D(t3[l2]), d2 = E2.name, h3 = E2.value, p2 = E2.attributes;
if ("EXTINF" === d2) !Number.isInteger(h3.duration) && o2.compatibleVersion < 3 && (o2.compatibleVersion = 3), Math.round(h3.duration) > o2.targetDuration && T.INVALIDPLAYLIST("EXTINF duration, when rounded to the nearest integer, MUST be less than or equal to the target duration"), s2.duration = h3.duration, s2.title = h3.title;
else if ("EXT-X-BYTERANGE" === d2) o2.compatibleVersion < 4 && (o2.compatibleVersion = 4), s2.byterange = h3;
else if ("EXT-X-DISCONTINUITY" === d2) s2.parts.length > 0 && T.INVALIDPLAYLIST("EXT-X-DISCONTINUITY must appear before the first EXT-X-PART tag of the Parent Segment."), s2.discontinuity = true;
else if ("EXT-X-GAP" === d2) o2.compatibleVersion < 8 && (o2.compatibleVersion = 8), s2.gap = true;
else if ("EXT-X-KEY" === d2) s2.parts.length > 0 && T.INVALIDPLAYLIST("EXT-X-KEY must appear before the first EXT-X-PART tag of the Parent Segment."), A(o2, p2), s2.key = new f.Key({ method: p2.METHOD, uri: p2.URI, iv: p2.IV, format: p2.KEYFORMAT, formatVersion: p2.KEYFORMATVERSIONS });
else if ("EXT-X-MAP" === d2) s2.parts.length > 0 && T.INVALIDPLAYLIST("EXT-X-MAP must appear before the first EXT-X-PART tag of the Parent Segment."), o2.compatibleVersion < 5 && (o2.compatibleVersion = 5), o2.hasMap = true, s2.map = new f.MediaInitializationSection({ uri: p2.URI, byterange: p2.BYTERANGE });
else if ("EXT-X-PROGRAM-DATE-TIME" === d2) s2.programDateTime = h3;
else if ("EXT-X-DATERANGE" === d2) {
for (var I2 = {}, v2 = 0, y2 = Object.keys(p2); v2 < y2.length; v2++) {
var S2 = y2[v2];
(S2.startsWith("SCTE35-") || S2.startsWith("X-")) && (I2[S2] = p2[S2]);
}
s2.dateRange = new f.DateRange({ id: p2.ID, classId: p2.CLASS, start: p2["START-DATE"], cue: p2.CUE, end: p2["END-DATE"], duration: p2.DURATION, plannedDuration: p2["PLANNED-DURATION"], endOnNext: p2["END-ON-NEXT"], attributes: I2 });
} else if ("EXT-X-CUE-OUT" === d2) s2.markers.push(new f.SpliceInfo({ type: "OUT", duration: p2 && p2.DURATION || h3 }));
else if ("EXT-X-CUE-IN" === d2) s2.markers.push(new f.SpliceInfo({ type: "IN" }));
else if ("EXT-X-CUE-OUT-CONT" === d2 || "EXT-X-CUE" === d2 || "EXT-OATCLS-SCTE35" === d2 || "EXT-X-ASSET" === d2 || "EXT-X-SCTE35" === d2) s2.markers.push(new f.SpliceInfo({ type: "RAW", tagName: d2, value: h3 }));
else if ("EXT-X-PRELOAD-HINT" !== d2 || p2.TYPE) if ("EXT-X-PRELOAD-HINT" === d2 && "PART" === p2.TYPE && c2) T.INVALIDPLAYLIST("Servers should not add more than one EXT-X-PRELOAD-HINT tag with the same TYPE attribute to a Playlist.");
else if ("EXT-X-PART" !== d2 && "EXT-X-PRELOAD-HINT" !== d2 || p2.URI) {
if ("EXT-X-PRELOAD-HINT" === d2 && "MAP" === p2.TYPE) u2 && T.INVALIDPLAYLIST("Servers should not add more than one EXT-X-PRELOAD-HINT tag with the same TYPE attribute to a Playlist."), u2 = true, o2.hasMap = true, s2.map = new f.MediaInitializationSection({ hint: true, uri: p2.URI, byterange: { length: p2["BYTERANGE-LENGTH"], offset: p2["BYTERANGE-START"] || 0 } });
else if ("EXT-X-PART" === d2 || "EXT-X-PRELOAD-HINT" === d2 && "PART" === p2.TYPE) {
"EXT-X-PART" !== d2 || p2.DURATION || T.INVALIDPLAYLIST("EXT-X-PART: DURATION attribute is mandatory"), "EXT-X-PRELOAD-HINT" === d2 && (c2 = true);
var N2 = new f.PartialSegment({ hint: "EXT-X-PRELOAD-HINT" === d2, uri: p2.URI, byterange: "EXT-X-PART" === d2 ? p2.BYTERANGE : { length: p2["BYTERANGE-LENGTH"], offset: p2["BYTERANGE-START"] || 0 }, duration: p2.DURATION, independent: p2.INDEPENDENT, gap: p2.GAP });
s2.gap && !N2.gap && T.INVALIDPLAYLIST("Partial segments must have GAP=YES if they are in a gap (EXT-X-GAP)"), s2.parts.push(N2);
}
} else T.INVALIDPLAYLIST("EXT-X-PART / EXT-X-PRELOAD-HINT: URI attribute is mandatory");
else T.INVALIDPLAYLIST("EXT-X-PRELOAD-HINT: TYPE attribute is mandatory");
}
return s2;
}
function g(t3, e3, n3, r2, i2, a2, o2) {
for (var s2 = new f.PrefetchSegment({ uri: e3, mediaSequenceNumber: i2, discontinuitySequence: a2 }), u2 = n3; u2 <= r2; u2++) {
var c2 = t3[u2], l2 = c2.name, E2 = c2.attributes;
"EXTINF" === l2 ? T.INVALIDPLAYLIST("A prefetch segment must not be advertised with an EXTINF tag.") : "EXT-X-DISCONTINUITY" === l2 ? T.INVALIDPLAYLIST("A prefetch segment must not be advertised with an EXT-X-DISCONTINUITY tag.") : "EXT-X-PREFETCH-DISCONTINUITY" === l2 ? s2.discontinuity = true : "EXT-X-KEY" === l2 ? (A(o2, E2), s2.key = new f.Key({ method: E2.METHOD, uri: E2.URI, iv: E2.IV, format: E2.KEYFORMAT, formatVersion: E2.KEYFORMATVERSIONS })) : "EXT-X-MAP" === l2 && T.INVALIDPLAYLIST("Prefetch segments must not be advertised with an EXT-X-MAP tag.");
}
return s2;
}
function b(t3, e3) {
var n3, a2 = new f.MediaPlaylist(), o2 = -1, s2 = 0, u2 = false, c2 = false, l2 = 0, E2 = null, d2 = null, h3 = false, p2 = i(t3.entries());
try {
for (p2.s(); !(n3 = p2.n()).done; ) {
var I2 = r(n3.value, 2), v2 = I2[0], A2 = I2[1], y2 = D(A2), S2 = y2.name, N2 = y2.value, m2 = y2.attributes;
if ("Segment" !== y2.category) {
if ("EXT-X-VERSION" === S2) void 0 === a2.version ? a2.version = N2 : T.INVALIDPLAYLIST("A Playlist file MUST NOT contain more than one EXT-X-VERSION tag.");
else if ("EXT-X-TARGETDURATION" === S2) a2.targetDuration = e3.targetDuration = N2;
else if ("EXT-X-MEDIA-SEQUENCE" === S2) a2.segments.length > 0 && T.INVALIDPLAYLIST("The EXT-X-MEDIA-SEQUENCE tag MUST appear before the first Media Segment in the Playlist."), a2.mediaSequenceBase = s2 = N2;
else if ("EXT-X-DISCONTINUITY-SEQUENCE" === S2) a2.segments.length > 0 && T.INVALIDPLAYLIST("The EXT-X-DISCONTINUITY-SEQUENCE tag MUST appear before the first Media Segment in the Playlist."), u2 && T.INVALIDPLAYLIST("The EXT-X-DISCONTINUITY-SEQUENCE tag MUST appear before any EXT-X-DISCONTINUITY tag."), a2.discontinuitySequenceBase = l2 = N2;
else if ("EXT-X-ENDLIST" === S2) a2.endlist = true;
else if ("EXT-X-PLAYLIST-TYPE" === S2) a2.playlistType = N2;
else if ("EXT-X-I-FRAMES-ONLY" === S2) e3.compatibleVersion < 4 && (e3.compatibleVersion = 4), a2.isIFrame = true;
else if ("EXT-X-INDEPENDENT-SEGMENTS" === S2) a2.independentSegments && T.INVALIDPLAYLIST("EXT-X-INDEPENDENT-SEGMENTS tag MUST NOT appear more than once in a Playlist"), a2.independentSegments = true;
else if ("EXT-X-START" === S2) a2.start && T.INVALIDPLAYLIST("EXT-X-START tag MUST NOT appear more than once in a Playlist"), "number" != typeof m2["TIME-OFFSET"] && T.INVALIDPLAYLIST("EXT-X-START: TIME-OFFSET attribute is REQUIRED"), a2.start = { offset: m2["TIME-OFFSET"], precise: m2.PRECISE || false };
else if ("EXT-X-SERVER-CONTROL" === S2) m2["CAN-BLOCK-RELOAD"] || T.INVALIDPLAYLIST("EXT-X-SERVER-CONTROL: CAN-BLOCK-RELOAD=YES is mandatory for Low-Latency HLS"), a2.lowLatencyCompatibility = { canBlockReload: m2["CAN-BLOCK-RELOAD"], canSkipUntil: m2["CAN-SKIP-UNTIL"], holdBack: m2["HOLD-BACK"], partHoldBack: m2["PART-HOLD-BACK"] };
else if ("EXT-X-PART-INF" === S2) m2["PART-TARGET"] || T.INVALIDPLAYLIST("EXT-X-PART-INF: PART-TARGET attribute is mandatory"), a2.partTargetDuration = m2["PART-TARGET"];
else if ("EXT-X-RENDITION-REPORT" === S2) m2.URI || T.INVALIDPLAYLIST("EXT-X-RENDITION-REPORT: URI attribute is mandatory"), 0 === m2.URI.search(/^[a-z]+:/) && T.INVALIDPLAYLIST("EXT-X-RENDITION-REPORT: URI must be relative to the playlist uri"), a2.renditionReports.push(new f.RenditionReport({ uri: m2.URI, lastMSN: m2["LAST-MSN"], lastPart: m2["LAST-PART"] }));
else if ("EXT-X-SKIP" === S2) m2["SKIPPED-SEGMENTS"] || T.INVALIDPLAYLIST("EXT-X-SKIP: SKIPPED-SEGMENTS attribute is mandatory"), e3.compatibleVersion < 9 && (e3.compatibleVersion = 9), a2.skip = m2["SKIPPED-SEGMENTS"], s2 += a2.skip;
else if ("EXT-X-PREFETCH" === S2) {
var b2 = g(t3, N2, -1 === o2 ? v2 : o2, v2 - 1, s2++, l2, e3);
b2 && (b2.discontinuity && (b2.discontinuitySequence++, l2 = b2.discontinuitySequence), b2.key ? E2 = b2.key : b2.key = E2, a2.prefetchSegments.push(b2)), c2 = true, o2 = -1;
} else if ("EXT-X-DEFINE" === S2) a2.defines || (a2.defines = []), a2.defines.push(m2);
else if ("string" == typeof A2) {
-1 === o2 && T.INVALIDPLAYLIST("A URI line is not preceded by any segment tags"), a2.targetDuration || T.INVALIDPLAYLIST("The EXT-X-TARGETDURATION tag is REQUIRED"), c2 && T.INVALIDPLAYLIST("These segments must appear after all complete segments.");
var R2 = X(t3, A2, o2, v2 - 1, s2++, l2, e3);
if (R2) {
var P2 = r(O(a2, R2, l2, E2, d2), 3);
l2 = P2[0], E2 = P2[1], d2 = P2[2], !h3 && R2.parts.length > 0 && (h3 = true);
}
o2 = -1;
}
} else -1 === o2 && (o2 = v2), "EXT-X-DISCONTINUITY" === S2 && (u2 = true);
}
} catch (t4) {
p2.e(t4);
} finally {
p2.f();
}
if (-1 !== o2) {
var L = X(t3, "", o2, t3.length - 1, s2++, l2, e3);
if (L) {
var C, M = L.parts;
!(M.length > 0) || a2.endlist || null !== (C = M.at(-1)) && void 0 !== C && C.hint || T.INVALIDPLAYLIST("If the Playlist contains EXT-X-PART tags and does not contain an EXT-X-ENDLIST tag, the Playlist must contain an EXT-X-PRELOAD-HINT tag with a TYPE=PART attribute"), O(a2, L, E2, d2), !h3 && L.parts.length > 0 && (h3 = true);
}
}
return (function(t4) {
for (var e4 = /* @__PURE__ */ new Map(), n4 = /* @__PURE__ */ new Map(), r2 = false, a3 = false, o3 = t4.length - 1; o3 >= 0; o3--) {
var s3 = t4[o3], u3 = s3.programDateTime, c3 = s3.dateRange;
if (u3 && (a3 = true), c3 && c3.start) {
r2 = true, c3.endOnNext && (c3.end || c3.duration) && T.INVALIDPLAYLIST("An EXT-X-DATERANGE tag with an END-ON-NEXT=YES attribute MUST NOT contain DURATION or END-DATE attributes.");
var l3 = c3.start.getTime(), f2 = c3.duration || 0;
c3.end && c3.duration && l3 + 1e3 * f2 !== c3.end.getTime() && T.INVALIDPLAYLIST("END-DATE MUST be equal to the value of the START-DATE attribute plus the value of the DURATION"), c3.endOnNext && (c3.end = e4.get(c3.classId)), e4.set(c3.classId, c3.start);
var E3 = c3.end ? c3.end.getTime() : c3.start.getTime() + 1e3 * (c3.duration || 0), d3 = n4.get(c3.classId);
if (d3) {
var h4, p3 = i(d3);
try {
for (p3.s(); !(h4 = p3.n()).done; ) {
var I3 = h4.value;
(I3.start <= l3 && I3.end > l3 || I3.start >= l3 && I3.start < E3) && T.INVALIDPLAYLIST("DATERANGE tags with the same CLASS should not overlap");
}
} catch (t5) {
p3.e(t5);
} finally {
p3.f();
}
d3.push({ start: l3, end: E3 });
} else c3.classId && n4.set(c3.classId, [{ start: l3, end: E3 }]);
}
}
r2 && !a3 && T.INVALIDPLAYLIST("If a Playlist contains an EXT-X-DATERANGE tag, it MUST also contain at least one EXT-X-PROGRAM-DATE-TIME tag.");
})(a2.segments), a2.lowLatencyCompatibility && (function(t4, e4) {
var n4 = t4.lowLatencyCompatibility, a3 = t4.targetDuration, o3 = t4.partTargetDuration, s3 = t4.segments, u3 = t4.renditionReports, c3 = n4.canSkipUntil, l3 = n4.holdBack, f2 = n4.partHoldBack;
c3 < 6 * a3 && T.INVALIDPLAYLIST("The Skip Boundary must be at least six times the EXT-X-TARGETDURATION.");
l3 < 3 * a3 && T.INVALIDPLAYLIST("HOLD-BACK must be at least three times the EXT-X-TARGETDURATION.");
if (e4) {
void 0 === o3 && T.INVALIDPLAYLIST("EXT-X-PART-INF is required if a Playlist contains one or more EXT-X-PART tags"), void 0 === f2 && T.INVALIDPLAYLIST("EXT-X-PART: PART-HOLD-BACK attribute is mandatory"), f2 < o3 && T.INVALIDPLAYLIST("PART-HOLD-BACK must be at least PART-TARGET");
var E3, d3 = i(s3.entries());
try {
for (d3.s(); !(E3 = d3.n()).done; ) {
var h4 = r(E3.value, 2), p3 = h4[0], I3 = h4[1].parts;
I3.length > 0 && p3 < s3.length - 3 && T.INVALIDPLAYLIST("Remove EXT-X-PART tags from the Playlist after they are greater than three target durations from the end of the Playlist.");
var v3, A3 = i(I3.entries());
try {
for (A3.s(); !(v3 = A3.n()).done; ) {
var y3 = r(v3.value, 2), S3 = y3[0], N3 = y3[1].duration;
void 0 !== N3 && (N3 > o3 && T.INVALIDPLAYLIST("PART-TARGET is the maximum duration of any Partial Segment"), S3 < I3.length - 1 && N3 < 0.85 * o3 && T.INVALIDPLAYLIST("All Partial Segments except the last part of a segment must have a duration of at least 85% of PART-TARGET"));
}
} catch (t5) {
A3.e(t5);
} finally {
A3.f();
}
}
} catch (t5) {
d3.e(t5);
} finally {
d3.f();
}
}
var m3, X2 = i(u3);
try {
for (X2.s(); !(m3 = X2.n()).done; ) {
var g2, b3 = m3.value, O2 = s3.at(-1);
null !== (g2 = b3.lastMSN) && void 0 !== g2 || (b3.lastMSN = O2.mediaSequenceNumber), (null === b3.lastPart || void 0 === b3.lastPart) && O2.parts.length > 0 && (b3.lastPart = O2.parts.length - 1);
}
} catch (t5) {
X2.e(t5);
} finally {
X2.f();
}
})(a2, h3), a2;
}
function O(t3, e3, n3, r2, i2) {
var a2 = e3.discontinuity, o2 = e3.key, s2 = e3.map, u2 = e3.byterange, c2 = e3.uri;
if (a2 && (e3.discontinuitySequence = n3 + 1), o2 || (e3.key = r2), s2 || (e3.map = i2), u2 && -1 === u2.offset) {
var l2 = t3.segments;
if (l2.length > 0) {
var f2 = l2.at(-1);
f2.byterange && f2.uri === c2 ? u2.offset = f2.byterange.offset + f2.byterange.length : T.INVALIDPLAYLIST("If offset of EXT-X-BYTERANGE is not present, a previous Media Segment MUST be a sub-range of the same media resource");
} else T.INVALIDPLAYLIST("If offset of EXT-X-BYTERANGE is not present, a previous Media Segment MUST appear in the Playlist file");
}
return t3.segments.push(e3), [e3.discontinuitySequence, e3.key, e3.map];
}
function R(t3, e3) {
var n3 = (function(t4) {
var e4 = t4.indexOf(":");
return -1 === e4 ? [t4.slice(1).trim(), null] : [t4.slice(1, e4).trim(), t4.slice(e4 + 1).trim()];
})(t3), i2 = r(n3, 2), a2 = i2[0], o2 = i2[1], s2 = (function(t4) {
switch (t4) {
case "EXTM3U":
case "EXT-X-VERSION":
case "EXT-X-CONTENT-STEERING":
return "Basic";
case "EXTINF":
case "EXT-X-BYTERANGE":
case "EXT-X-DISCONTINUITY":
case "EXT-X-PREFETCH-DISCONTINUITY":
case "EXT-X-KEY":
case "EXT-X-MAP":
case "EXT-X-PROGRAM-DATE-TIME":
case "EXT-X-DATERANGE":
case "EXT-X-CUE-OUT":
case "EXT-X-CUE-IN":
case "EXT-X-CUE-OUT-CONT":
case "EXT-X-CUE":
case "EXT-OATCLS-SCTE35":
case "EXT-X-ASSET":
case "EXT-X-SCTE35":
case "EXT-X-PART":
case "EXT-X-PRELOAD-HINT":
case "EXT-X-GAP":
return "Segment";
case "EXT-X-TARGETDURATION":
case "EXT-X-MEDIA-SEQUENCE":
case "EXT-X-DISCONTINUITY-SEQUENCE":
case "EXT-X-ENDLIST":
case "EXT-X-PLAYLIST-TYPE":
case "EXT-X-I-FRAMES-ONLY":
case "EXT-X-SERVER-CONTROL":
case "EXT-X-PART-INF":
case "EXT-X-PREFETCH":
case "EXT-X-RENDITION-REPORT":
case "EXT-X-SKIP":
return "MediaPlaylist";
case "EXT-X-MEDIA":
case "EXT-X-STREAM-INF":
case "EXT-X-I-FRAME-STREAM-INF":
case "EXT-X-SESSION-DATA":
case "EXT-X-SESSION-KEY":
return "MasterPlaylist";
case "EXT-X-INDEPENDENT-SEGMENTS":
case "EXT-X-START":
case "EXT-X-DEFINE":
return "MediaorMasterPlaylist";
default:
return "Unknown";
}
})(a2);
if ((function(t4, e4) {
if ("Segment" === t4 || "MediaPlaylist" === t4) return void 0 === e4.isMasterPlaylist ? void (e4.isMasterPlaylist = false) : void (e4.isMasterPlaylist && S());
if ("MasterPlaylist" === t4) {
if (void 0 === e4.isMasterPlaylist) return void (e4.isMasterPlaylist = true);
false === e4.isMasterPlaylist && S();
}
})(s2, e3), "Unknown" === s2) return null;
"MediaPlaylist" === s2 && "EXT-X-RENDITION-REPORT" !== a2 && "EXT-X-PREFETCH" !== a2 && (e3.hash[a2] && T.INVALIDPLAYLIST("There MUST NOT be more than one Media Playlist tag of each type in any Media Playlist"), e3.hash[a2] = true);
var u2 = (function(t4, e4) {
switch (t4) {
case "EXTM3U":
case "EXT-X-DISCONTINUITY":
case "EXT-X-ENDLIST":
case "EXT-X-I-FRAMES-ONLY":
case "EXT-X-INDEPENDENT-SEGMENTS":
case "EXT-X-CUE-IN":
case "EXT-X-GAP":
return [null, null];
case "EXT-X-VERSION":
case "EXT-X-TARGETDURATION":
case "EXT-X-MEDIA-SEQUENCE":
case "EXT-X-DISCONTINUITY-SEQUENCE":
return [T.toNumber(e4), null];
case "EXT-X-CUE-OUT":
return Number.isNaN(Number(e4)) ? [null, y(e4)] : [T.toNumber(e4), null];
case "EXT-X-KEY":
case "EXT-X-MAP":
case "EXT-X-DATERANGE":
case "EXT-X-MEDIA":
case "EXT-X-STREAM-INF":
case "EXT-X-I-FRAME-STREAM-INF":
case "EXT-X-SESSION-DATA":
case "EXT-X-SESSION-KEY":
case "EXT-X-START":
case "EXT-X-SERVER-CONTROL":
case "EXT-X-PART-INF":
case "EXT-X-PART":
case "EXT-X-PRELOAD-HINT":
case "EXT-X-RENDITION-REPORT":
case "EXT-X-SKIP":
case "EXT-X-DEFINE":
return [null, y(e4)];
case "EXTINF":
return [d(e4), null];
case "EXT-X-BYTERANGE":
return [h2(e4), null];
case "EXT-X-PROGRAM-DATE-TIME":
return [new Date(e4), null];
default:
return [e4, null];
}
})(a2, o2), c2 = r(u2, 2);
return { name: a2, category: s2, value: c2[0], attributes: c2[1] };
}
function P(t3, e3) {
var n3;
return e3.isMasterPlaylist ? n3 = (function(t4, e4) {
var n4, a2 = new f.MasterPlaylist(), o2 = false, s2 = i(t4.entries());
try {
var u2 = function() {
var i2 = r(n4.value, 2), s3 = i2[0], u3 = D(i2[1]), c3 = u3.name, l3 = u3.value, E3 = u3.attributes;
if ("EXT-X-VERSION" === c3) a2.version = l3;
else if ("EXT-X-CONTENT-STEERING-SERVER" === c3) {
var d3 = new f.ContentSteering({ serverUri: E3["SERVER-URI"], pathwayId: E3["PATHWAY-ID"] });
a2.contentSteering = d3;
} else if ("EXT-X-STREAM-INF" === c3) {
var h3 = t4[s3 + 1];
("string" != typeof h3 || h3.startsWith("#EXT")) && T.INVALIDPLAYLIST("EXT-X-STREAM-INF must be followed by a URI line");
var p2 = m(t4, E3, h3, false, e4);
p2 && ("number" == typeof p2.score && (o2 = true, p2.score < 0 && T.INVALIDPLAYLIST("SCORE attribute on EXT-X-STREAM-INF must be positive decimal-floating-point number.")), a2.variants.push(p2));
} else if ("EXT-X-I-FRAME-STREAM-INF" === c3) {
var I2 = m(t4, E3, E3.URI, true, e4);
I2 && a2.variants.push(I2);
} else if ("EXT-X-SESSION-DATA" === c3) {
var v2 = new f.SessionData({ id: E3["DATA-ID"], value: E3.VALUE, uri: E3.URI, language: E3.LANGUAGE });
a2.sessionDataList.some((function(t5) {
return t5.id === v2.id && t5.language === v2.language;
})) && T.INVALIDPLAYLIST("A Playlist MUST NOT contain more than one EXT-X-SESSION-DATA tag with the same DATA-ID attribute and the same LANGUAGE attribute."), a2.sessionDataList.push(v2);
} else if ("EXT-X-SESSION-KEY" === c3) {
"NONE" === E3.METHOD && T.INVALIDPLAYLIST("EXT-X-SESSION-KEY: The value of the METHOD attribute MUST NOT be NONE");
var y2 = new f.Key({ method: E3.METHOD, uri: E3.URI, iv: E3.IV, format: E3.KEYFORMAT, formatVersion: E3.KEYFORMATVERSIONS });
a2.sessionKeyList.some((function(t5) {
return (function(t6, e5) {
if (t6.method !== e5.method) return false;
if (t6.uri !== e5.uri) return false;
if (t6.iv) {
if (!e5.iv) return false;
if (t6.iv.byteLength !== e5.iv.byteLength) return false;
for (var n5 = 0; n5 < t6.iv.byteLength; n5++) if (t6.iv[n5] !== e5.iv[n5]) return false;
} else if (e5.iv) return false;
return t6.format === e5.format && t6.formatVersion === e5.formatVersion;
})(t5, y2);
})) && T.INVALIDPLAYLIST("A Master Playlist MUST NOT contain more than one EXT-X-SESSION-KEY tag with the same METHOD, URI, IV, KEYFORMAT, and KEYFORMATVERSIONS attribute values."), A(e4, E3), a2.sessionKeyList.push(y2);
} else "EXT-X-INDEPENDENT-SEGMENTS" === c3 ? (a2.independentSegments && T.INVALIDPLAYLIST("EXT-X-INDEPENDENT-SEGMENTS tag MUST NOT appear more than once in a Playlist"), a2.independentSegments = true) : "EXT-X-START" === c3 ? (a2.start && T.INVALIDPLAYLIST("EXT-X-START tag MUST NOT appear more than once in a Playlist"), "number" != typeof E3["TIME-OFFSET"] && T.INVALIDPLAYLIST("EXT-X-START: TIME-OFFSET attribute is REQUIRED"), a2.start = { offset: E3["TIME-OFFSET"], precise: E3.PRECISE || false }) : "EXT-X-DEFINE" === c3 && (a2.defines || (a2.defines = []), a2.defines.push(E3));
};
for (s2.s(); !(n4 = s2.n()).done; ) u2();
} catch (t5) {
s2.e(t5);
} finally {
s2.f();
}
if (o2) {
var c2, l2 = i(a2.variants);
try {
for (l2.s(); !(c2 = l2.n()).done; ) "number" != typeof c2.value.score && T.INVALIDPLAYLIST("If any Variant Stream contains the SCORE attribute, then all Variant Streams in the Master Playlist SHOULD have a SCORE attribute");
} catch (t5) {
l2.e(t5);
} finally {
l2.f();
}
}
if (e4.isClosedCaptionsNone) {
var E2, d2 = i(a2.variants);
try {
for (d2.s(); !(E2 = d2.n()).done; ) E2.value.closedCaptions.length > 0 && T.INVALIDPLAYLIST("If there is a variant with CLOSED-CAPTIONS attribute of NONE, all EXT-X-STREAM-INF tags MUST have this attribute with a value of NONE");
} catch (t5) {
d2.e(t5);
} finally {
d2.f();
}
}
return a2;
})(t3, e3) : !(n3 = b(t3, e3)).isIFrame && e3.hasMap && e3.compatibleVersion < 6 && (e3.compatibleVersion = 6), e3.compatibleVersion > 1 && (!n3.version || n3.version < e3.compatibleVersion) && T.INVALIDPLAYLIST("EXT-X-VERSION needs to be ".concat(e3.compatibleVersion, " or higher.")), n3;
}
function D(t3) {
return "string" == typeof t3 ? {} : t3;
}
e2.default = function(t3) {
var e3 = { version: void 0, isMasterPlaylist: void 0, hasMap: false, targetDuration: 0, compatibleVersion: 1, isClosedCaptionsNone: false, hash: {} }, n3 = P((function(t4, e4) {
var n4, r2 = [], a2 = i(t4.split("\n"));
try {
for (a2.s(); !(n4 = a2.n()).done; ) {
var o2 = n4.value.trim();
if (o2) if (o2.startsWith("#")) {
if (o2.startsWith("#EXT")) {
var s2 = R(o2, e4);
s2 && r2.push(s2);
}
} else r2.push(o2);
}
} catch (t5) {
a2.e(t5);
} finally {
a2.f();
}
return 0 !== r2.length && "EXTM3U" === r2[0].name || T.INVALIDPLAYLIST("The EXTM3U tag MUST be the first line."), r2;
})(t3, e3), e3);
return n3.source = t3, n3;
};
}, 887: function(t2, e2, n2) {
function r(t3) {
return r = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(t4) {
return typeof t4;
} : function(t4) {
return t4 && "function" == typeof Symbol && t4.constructor === Symbol && t4 !== Symbol.prototype ? "symbol" : typeof t4;
}, r(t3);
}
function i(t3, e3) {
return (function(t4) {
if (Array.isArray(t4)) return t4;
})(t3) || (function(t4, e4) {
var n3 = null == t4 ? null : "undefined" != typeof Symbol && t4[Symbol.iterator] || t4["@@iterator"];
if (null != n3) {
var r2, i2, a2, o2, s2 = [], u2 = true, c2 = false;
try {
if (a2 = (n3 = n3.call(t4)).next, 0 === e4) {
if (Object(n3) !== n3) return;
u2 = false;
} else for (; !(u2 = (r2 = a2.call(n3)).done) && (s2.push(r2.value), s2.length !== e4); u2 = true) ;
} catch (t5) {
c2 = true, i2 = t5;
} finally {
try {
if (!u2 && null != n3.return && (o2 = n3.return(), Object(o2) !== o2)) return;
} finally {
if (c2) throw i2;
}
}
return s2;
}
})(t3, e3) || o(t3, e3) || (function() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
})();
}
function a(t3, e3) {
var n3 = "undefined" != typeof Symbol && t3[Symbol.iterator] || t3["@@iterator"];
if (!n3) {
if (Array.isArray(t3) || (n3 = o(t3)) || e3) {
n3 && (t3 = n3);
var r2 = 0, i2 = function() {
};
return { s: i2, n: function() {
return r2 >= t3.length ? { done: true } : { done: false, value: t3[r2++] };
}, e: function(t4) {
throw t4;
}, f: i2 };
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var a2, s2 = true, u2 = false;
return { s: function() {
n3 = n3.call(t3);
}, n: function() {
var t4 = n3.next();
return s2 = t4.done, t4;
}, e: function(t4) {
u2 = true, a2 = t4;
}, f: function() {
try {
s2 || null == n3.return || n3.return();
} finally {
if (u2) throw a2;
}
} };
}
function o(t3, e3) {
if (t3) {
if ("string" == typeof t3) return s(t3, e3);
var n3 = {}.toString.call(t3).slice(8, -1);
return "Object" === n3 && t3.constructor && (n3 = t3.constructor.name), "Map" === n3 || "Set" === n3 ? Array.from(t3) : "Arguments" === n3 || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n3) ? s(t3, e3) : void 0;
}
}
function s(t3, e3) {
(null == e3 || e3 > t3.length) && (e3 = t3.length);
for (var n3 = 0, r2 = Array(e3); n3 < e3; n3++) r2[n3] = t3[n3];
return r2;
}
function u(t3, e3) {
for (var n3 = 0; n3 < e3.length; n3++) {
var r2 = e3[n3];
r2.enumerable = r2.enumerable || false, r2.configurable = true, "value" in r2 && (r2.writable = true), Object.defineProperty(t3, p(r2.key), r2);
}
}
function c(t3, e3, n3) {
return e3 = h2(e3), (function(t4, e4) {
if (e4 && ("object" == r(e4) || "function" == typeof e4)) return e4;
if (void 0 !== e4) throw new TypeError("Derived constructors may only return object or undefined");
return (function(t5) {
if (void 0 === t5) throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
return t5;
})(t4);
})(t3, E() ? Reflect.construct(e3, [], h2(t3).constructor) : e3.apply(t3, n3));
}
function l(t3, e3, n3, r2) {
var i2 = T(h2(t3.prototype), e3, n3);
return 2 & r2 && "function" == typeof i2 ? function(t4) {
return i2.apply(n3, t4);
} : i2;
}
function T() {
return T = "undefined" != typeof Reflect && Reflect.get ? Reflect.get.bind() : function(t3, e3, n3) {
var r2 = (function(t4, e4) {
for (; !{}.hasOwnProperty.call(t4, e4) && null !== (t4 = h2(t4)); ) ;
return t4;
})(t3, e3);
if (r2) {
var i2 = Object.getOwnPropertyDescriptor(r2, e3);
return i2.get ? i2.get.call(arguments.length < 3 ? t3 : n3) : i2.value;
}
}, T.apply(null, arguments);
}
function f(t3) {
var e3 = "function" == typeof Map ? /* @__PURE__ */ new Map() : void 0;
return f = function(t4) {
if (null === t4 || !(function(t5) {
try {
return -1 !== Function.toString.call(t5).indexOf("[native code]");
} catch (e4) {
return "function" == typeof t5;
}
})(t4)) return t4;
if ("function" != typeof t4) throw new TypeError("Super expression must either be null or a function");
if (void 0 !== e3) {
if (e3.has(t4)) return e3.get(t4);
e3.set(t4, n3);
}
function n3() {
return (function(t5, e4, n4) {
if (E()) return Reflect.construct.apply(null, arguments);
var r2 = [null];
r2.push.apply(r2, e4);
var i2 = new (t5.bind.apply(t5, r2))();
return n4 && d(i2, n4.prototype), i2;
})(t4, arguments, h2(this).constructor);
}
return n3.prototype = Object.create(t4.prototype, { constructor: { value: n3, enumerable: false, writable: true, configurable: true } }), d(n3, t4);
}, f(t3);
}
function E() {
try {
var t3 = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], (function() {
})));
} catch (t4) {
}
return (E = function() {
return !!t3;
})();
}
function d(t3, e3) {
return d = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function(t4, e4) {
return t4.__proto__ = e4, t4;
}, d(t3, e3);
}
function h2(t3) {
return h2 = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function(t4) {
return t4.__proto__ || Object.getPrototypeOf(t4);
}, h2(t3);
}
function p(t3) {
var e3 = (function(t4, e4) {
if ("object" != r(t4) || !t4) return t4;
var n3 = t4[Symbol.toPrimitive];
if (void 0 !== n3) {
var i2 = n3.call(t4, e4);
if ("object" != r(i2)) return i2;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return String(t4);
})(t3, "string");
return "symbol" == r(e3) ? e3 : e3 + "";
}
var I, v = this && this.__createBinding || (Object.create ? function(t3, e3, n3, r2) {
void 0 === r2 && (r2 = n3);
var i2 = Object.getOwnPropertyDescriptor(e3, n3);
i2 && !("get" in i2 ? !e3.__esModule : i2.writable || i2.configurable) || (i2 = { enumerable: true, get: function() {
return e3[n3];
} }), Object.defineProperty(t3, r2, i2);
} : function(t3, e3, n3, r2) {
void 0 === r2 && (r2 = n3), t3[r2] = e3[n3];
}), A = this && this.__setModuleDefault || (Object.create ? function(t3, e3) {
Object.defineProperty(t3, "default", { enumerable: true, value: e3 });
} : function(t3, e3) {
t3.default = e3;
}), y = this && this.__importStar || (I = function(t3) {
return I = Object.getOwnPropertyNames || function(t4) {
var e3 = [];
for (var n3 in t4) Object.prototype.hasOwnProperty.call(t4, n3) && (e3[e3.length] = n3);
return e3;
}, I(t3);
}, function(t3) {
if (t3 && t3.__esModule) return t3;
var e3 = {};
if (null != t3) for (var n3 = I(t3), r2 = 0; r2 < n3.length; r2++) "default" !== n3[r2] && v(e3, t3, n3[r2]);
return A(e3, t3), e3;
});
Object.defineProperty(e2, "__esModule", { value: true });
var S = y(n2(203)), N = ["#EXTINF", "#EXT-X-BYTERANGE", "#EXT-X-DISCONTINUITY", "#EXT-X-STREAM-INF", "#EXT-X-CUE-OUT", "#EXT-X-CUE-IN", "#EXT-X-KEY", "#EXT-X-MAP"], m = ["#EXT-X-MEDIA"], X = (function(t3) {
function e3(t4) {
var n4, r3, i3, a2;
return (function(t5, e4) {
if (!(t5 instanceof e4)) throw new TypeError("Cannot call a class as a function");
})(this, e3), n4 = c(this, e3), r3 = n4, a2 = void 0, (i3 = p(i3 = "baseUri")) in r3 ? Object.defineProperty(r3, i3, { value: a2, enumerable: true, configurable: true, writable: true }) : r3[i3] = a2, n4.baseUri = t4, n4;
}
return (function(t4, e4) {
if ("function" != typeof e4 && null !== e4) throw new TypeError("Super expression must either be null or a function");
t4.prototype = Object.create(e4 && e4.prototype, { constructor: { value: t4, writable: true, configurable: true } }), Object.defineProperty(t4, "prototype", { writable: false }), e4 && d(t4, e4);
})(e3, t3), n3 = e3, r2 = [{ key: "push", value: function() {
for (var t4 = this, n4 = arguments.length, r3 = new Array(n4), i3 = 0; i3 < n4; i3++) r3[i3] = arguments[i3];
for (var a2 = function() {
var n5 = s2[o2];
if (!n5.startsWith("#")) return l(e3, "push", t4, 3)([n5]), 0;
if (N.some((function(t5) {
return n5.startsWith(t5);
}))) return l(e3, "push", t4, 3)([n5]), 0;
if (t4.includes(n5)) {
if (m.some((function(t5) {
return n5.startsWith(t5);
}))) return 0;
S.INVALIDPLAYLIST("Redundant item (".concat(n5, ")"));
}
l(e3, "push", t4, 3)([n5]);
}, o2 = 0, s2 = r3; o2 < s2.length; o2++) a2();
return this.length;
} }, { key: "join", value: function() {
for (var t4 = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : ",", n4 = this.length - 1; n4 >= 0; n4--) this[n4] || this.splice(n4, 1);
return l(e3, "join", this, 3)([t4]);
} }], r2 && u(n3.prototype, r2), i2 && u(n3, i2), Object.defineProperty(n3, "prototype", { writable: false }), n3;
var n3, r2, i2;
})(f(Array));
function g(t3, e3) {
var n3 = 1e3;
e3 && (n3 = Math.pow(10, e3));
var r2 = Math.round(t3 * n3) / n3;
return e3 ? r2.toFixed(e3) : r2;
}
function b(t3) {
var e3 = ['DATA-ID="'.concat(t3.id, '"')];
return t3.language && e3.push('LANGUAGE="'.concat(t3.language, '"')), t3.value ? e3.push('VALUE="'.concat(t3.value, '"')) : t3.uri && e3.push('URI="'.concat(t3.uri, '"')), "#EXT-X-SESSION-DATA:".concat(e3.join(","));
}
function O(t3, e3) {
var n3 = e3 ? "#EXT-X-SESSION-KEY" : "#EXT-X-KEY", r2 = ["METHOD=".concat(t3.method)];
return t3.uri && r2.push('URI="'.concat(t3.uri, '"')), t3.iv && (16 !== t3.iv.byteLength && S.INVALIDPLAYLIST("IV must be a 128-bit unsigned integer"), r2.push("IV=".concat(S.byteSequenceToHex(t3.iv)))), t3.format && r2.push('KEYFORMAT="'.concat(t3.format, '"')), t3.formatVersion && r2.push('KEYFORMATVERSIONS="'.concat(t3.formatVersion, '"')), "".concat(n3, ":").concat(r2.join(","));
}
function R(t3, e3) {
var n3 = e3.isIFrameOnly ? "#EXT-X-I-FRAME-STREAM-INF" : "#EXT-X-STREAM-INF", r2 = ["BANDWIDTH=".concat(e3.bandwidth)];
if (e3.averageBandwidth && r2.push("AVERAGE-BANDWIDTH=".concat(e3.averageBandwidth)), e3.isIFrameOnly && r2.push('URI="'.concat(e3.uri, '"')), e3.codecs && r2.push('CODECS="'.concat(e3.codecs, '"')), e3.resolution && r2.push("RESOLUTION=".concat(e3.resolution.width, "x").concat(e3.resolution.height)), e3.frameRate && r2.push("FRAME-RATE=".concat(g(e3.frameRate, 3))), e3.hdcpLevel && r2.push("HDCP-LEVEL=".concat(e3.hdcpLevel)), e3.audio.length > 0) {
r2.push('AUDIO="'.concat(e3.audio[0].groupId, '"'));
var i2, o2 = a(e3.audio);
try {
for (o2.s(); !(i2 = o2.n()).done; ) {
var s2 = i2.value;
t3.push(P(s2));
}
} catch (t4) {
o2.e(t4);
} finally {
o2.f();
}
}
if (e3.video.length > 0) {
r2.push('VIDEO="'.concat(e3.video[0].groupId, '"'));
var u2, c2 = a(e3.video);
try {
for (c2.s(); !(u2 = c2.n()).done; ) {
var l2 = u2.value;
t3.push(P(l2));
}
} catch (t4) {
c2.e(t4);
} finally {
c2.f();
}
}
if (e3.subtitles.length > 0) {
r2.push('SUBTITLES="'.concat(e3.subtitles[0].groupId, '"'));
var T2, f2 = a(e3.subtitles);
try {
for (f2.s(); !(T2 = f2.n()).done; ) {
var E2 = T2.value;
t3.push(P(E2));
}
} catch (t4) {
f2.e(t4);
} finally {
f2.f();
}
}
if (S.getOptions().allowClosedCaptionsNone && 0 === e3.closedCaptions.length) r2.push("CLOSED-CAPTIONS=NONE");
else if (e3.closedCaptions.length > 0) {
r2.push('CLOSED-CAPTIONS="'.concat(e3.closedCaptions[0].groupId, '"'));
var d2, h3 = a(e3.closedCaptions);
try {
for (h3.s(); !(d2 = h3.n()).done; ) {
var p2 = d2.value;
t3.push(P(p2));
}
} catch (t4) {
h3.e(t4);
} finally {
h3.f();
}
}
if (e3.score && r2.push("SCORE=".concat(e3.score)), e3.allowedCpc) {
var I2, v2 = [], A2 = a(e3.allowedCpc);
try {
for (A2.s(); !(I2 = A2.n()).done; ) {
var y2 = I2.value, N2 = y2.format, m2 = y2.cpcList;
v2.push("".concat(N2, ":").concat(m2.join("/")));
}
} catch (t4) {
A2.e(t4);
} finally {
A2.f();
}
r2.push('ALLOWED-CPC="'.concat(v2.join(","), '"'));
}
e3.videoRange && r2.push("VIDEO-RANGE=".concat(e3.videoRange)), e3.stableVariantId && r2.push('STABLE-VARIANT-ID="'.concat(e3.stableVariantId, '"')), e3.pathwayId && r2.push('PATHWAY-ID="'.concat(e3.pathwayId, '"')), e3.programId && r2.push("PROGRAM-ID=".concat(e3.programId)), t3.push("".concat(n3, ":").concat(r2.join(","))), e3.isIFrameOnly || t3.push("".concat(e3.uri));
}
function P(t3) {
var e3 = ["TYPE=".concat(t3.type), 'GROUP-ID="'.concat(t3.groupId, '"'), 'NAME="'.concat(t3.name, '"')];
return void 0 !== t3.isDefault && e3.push("DEFAULT=".concat(t3.isDefault ? "YES" : "NO")), void 0 !== t3.autoselect && e3.push("AUTOSELECT=".concat(t3.autoselect ? "YES" : "NO")), void 0 !== t3.forced && e3.push("FORCED=".concat(t3.forced ? "YES" : "NO")), t3.language && e3.push('LANGUAGE="'.concat(t3.language, '"')), t3.assocLanguage && e3.push('ASSOC-LANGUAGE="'.concat(t3.assocLanguage, '"')), t3.instreamId && e3.push('INSTREAM-ID="'.concat(t3.instreamId, '"')), t3.characteristics && e3.push('CHARACTERISTICS="'.concat(t3.characteristics, '"')), t3.channels && e3.push('CHANNELS="'.concat(t3.channels, '"')), t3.uri && e3.push('URI="'.concat(t3.uri, '"')), "#EXT-X-MEDIA:".concat(e3.join(","));
}
function D(t3, e3, n3, r2) {
var i2, o2, s2, u2 = arguments.length > 4 && void 0 !== arguments[4] ? arguments[4] : 1, c2 = false, l2 = "";
if (e3.discontinuity && t3.push("#EXT-X-DISCONTINUITY"), e3.gap && t3.push("#EXT-X-GAP"), e3.key) {
var T2 = O(e3.key);
T2 !== n3 && (t3.push(T2), n3 = T2);
}
if (e3.map) {
var f2 = (function(t4) {
var e4 = ['URI="'.concat(t4.uri, '"')];
t4.byterange && e4.push('BYTERANGE="'.concat(L(t4.byterange), '"'));
return "#EXT-X-MAP:".concat(e4.join(","));
})(e3.map);
f2 !== r2 && (t3.push(f2), r2 = f2);
}
if (e3.programDateTime && t3.push("#EXT-X-PROGRAM-DATE-TIME:".concat(S.formatDate(e3.programDateTime))), e3.dateRange && t3.push((function(t4) {
var e4 = ['ID="'.concat(t4.id, '"')];
t4.start && e4.push('START-DATE="'.concat(S.formatDate(t4.start), '"'));
t4.cue && e4.push('CUE="'.concat(t4.cue, '"'));
t4.end && e4.push('END-DATE="'.concat(S.formatDate(t4.end), '"'));
t4.duration && e4.push("DURATION=".concat(t4.duration));
t4.plannedDuration && e4.push("PLANNED-DURATION=".concat(t4.plannedDuration));
t4.classId && e4.push('CLASS="'.concat(t4.classId, '"'));
t4.endOnNext && e4.push("END-ON-NEXT=YES");
for (var n4 = 0, r3 = Object.keys(t4.attributes); n4 < r3.length; n4++) {
var i3 = r3[n4];
i3.startsWith("X-") ? "number" == typeof t4.attributes[i3] ? e4.push("".concat(i3, "=").concat(t4.attributes[i3])) : e4.push("".concat(i3, '="').concat(t4.attributes[i3], '"')) : i3.startsWith("SCTE35-") && e4.push("".concat(i3, "=").concat(S.byteSequenceToHex(t4.attributes[i3])));
}
return "#EXT-X-DATERANGE:".concat(e4.join(","));
})(e3.dateRange)), e3.markers.length > 0 && (l2 = (function(t4, e4) {
var n4, r3 = "", i3 = a(e4);
try {
for (i3.s(); !(n4 = i3.n()).done; ) {
var o3 = n4.value;
if ("OUT" === o3.type) r3 = "OUT", t4.push("#EXT-X-CUE-OUT:DURATION=".concat(o3.duration));
else if ("IN" === o3.type) r3 = "IN", t4.push("#EXT-X-CUE-IN");
else if ("RAW" === o3.type) {
var s3 = o3.value ? ":".concat(o3.value) : "";
t4.push("#".concat(o3.tagName).concat(s3));
}
}
} catch (t5) {
i3.e(t5);
} finally {
i3.f();
}
return r3;
})(t3, e3.markers)), e3.parts.length > 0 && (c2 = (function(t4, e4) {
var n4, r3 = false, i3 = a(e4);
try {
for (i3.s(); !(n4 = i3.n()).done; ) {
var o3 = n4.value;
if (o3.hint) {
var s3 = [];
if (s3.push("TYPE=PART", 'URI="'.concat(o3.uri, '"')), o3.byterange) {
var u3 = o3.byterange, c3 = u3.offset, l3 = u3.length;
s3.push("BYTERANGE-START=".concat(c3)), l3 && s3.push("BYTERANGE-LENGTH=".concat(l3));
}
t4.push("#EXT-X-PRELOAD-HINT:".concat(s3.join(","))), r3 = true;
} else {
var T3 = [];
T3.push("DURATION=".concat(o3.duration), 'URI="'.concat(o3.uri, '"')), o3.byterange && T3.push("BYTERANGE=".concat(L(o3.byterange))), o3.independent && T3.push("INDEPENDENT=YES"), o3.gap && T3.push("GAP=YES"), t4.push("#EXT-X-PART:".concat(T3.join(",")));
}
}
} catch (t5) {
i3.e(t5);
} finally {
i3.f();
}
return r3;
})(t3, e3.parts)), c2) return [n3, r2];
if ("number" == typeof e3.duration && !Number.isNaN(e3.duration)) {
var E2 = u2 < 3 ? Math.round(e3.duration) : g(e3.duration, (i2 = e3.duration, o2 = i2.toString(10), -1 === (s2 = o2.indexOf(".")) ? 0 : o2.length - s2 - 1));
t3.push("#EXTINF:".concat(E2, ",").concat(unescape(encodeURIComponent(e3.title || ""))));
}
return e3.byterange && t3.push("#EXT-X-BYTERANGE:".concat(L(e3.byterange))), Array.prototype.push.call(t3, "".concat(e3.uri)), [n3, r2, l2];
}
function L(t3) {
var e3 = t3.offset, n3 = t3.length;
return "".concat(n3, "@").concat(e3);
}
function C(t3) {
var e3 = [];
for (var n3 in t3) e3.push("".concat(n3, '="').concat(t3[n3], '"'));
return "#EXT-X-DEFINE:".concat(e3.join(","));
}
e2.default = function(t3, e3) {
S.PARAMCHECK(t3), S.ASSERT("Not a playlist", "playlist" === t3.type);
var n3 = new X(t3.uri);
if (n3.push("#EXTM3U"), t3.version && n3.push("#EXT-X-VERSION:".concat(t3.version)), t3.independentSegments && n3.push("#EXT-X-INDEPENDENT-SEGMENTS"), t3.start && n3.push("#EXT-X-START:TIME-OFFSET=".concat(g(t3.start.offset)).concat(t3.start.precise ? ",PRECISE=YES" : "")), t3.defines) {
var r2, o2 = a(t3.defines);
try {
for (o2.s(); !(r2 = o2.n()).done; ) {
var s2 = r2.value;
n3.push(C(s2));
}
} catch (t4) {
o2.e(t4);
} finally {
o2.f();
}
}
return t3.isMasterPlaylist ? (function(t4, e4, n4) {
var r3, o3;
e4.contentSteering && t4.push((r3 = e4.contentSteering, o3 = ['SERVER-URI="'.concat(r3.serverUri, '"'), 'PATHWAY-ID="'.concat(r3.pathwayId, '"')], "#EXT-X-CONTENT-STEERING:".concat(o3.join(","))));
var s3, u2 = a(e4.sessionDataList);
try {
for (u2.s(); !(s3 = u2.n()).done; ) {
var c2 = s3.value;
t4.push(b(c2));
}
} catch (t5) {
u2.e(t5);
} finally {
u2.f();
}
var l2, T2 = a(e4.sessionKeyList);
try {
for (T2.s(); !(l2 = T2.n()).done; ) {
var f2 = l2.value;
t4.push(O(f2, true));
}
} catch (t5) {
T2.e(t5);
} finally {
T2.f();
}
var E2, d2 = a(e4.variants.entries());
try {
for (d2.s(); !(E2 = d2.n()).done; ) {
var h3 = i(E2.value, 2), p2 = h3[0], I2 = h3[1], v2 = t4.length;
R(t4, I2), null != n4 && n4.variantProcessor && n4.variantProcessor(t4, v2, t4.length - 1, I2, p2);
}
} catch (t5) {
d2.e(t5);
} finally {
d2.f();
}
})(n3, t3, e3) : (function(t4, e4, n4) {
var r3 = "", o3 = "", s3 = false;
if (e4.targetDuration && t4.push("#EXT-X-TARGETDURATION:".concat(e4.targetDuration)), e4.lowLatencyCompatibility) {
var u2 = e4.lowLatencyCompatibility, c2 = u2.canBlockReload, l2 = u2.canSkipUntil, T2 = u2.holdBack, f2 = u2.partHoldBack, E2 = [];
E2.push("CAN-BLOCK-RELOAD=".concat(c2 ? "YES" : "NO")), void 0 !== l2 && E2.push("CAN-SKIP-UNTIL=".concat(l2)), void 0 !== T2 && E2.push("HOLD-BACK=".concat(T2)), void 0 !== f2 && E2.push("PART-HOLD-BACK=".concat(f2)), t4.push("#EXT-X-SERVER-CONTROL:".concat(E2.join(",")));
}
e4.partTargetDuration && t4.push("#EXT-X-PART-INF:PART-TARGET=".concat(e4.partTargetDuration)), e4.mediaSequenceBase && t4.push("#EXT-X-MEDIA-SEQUENCE:".concat(e4.mediaSequenceBase)), e4.discontinuitySequenceBase && t4.push("#EXT-X-DISCONTINUITY-SEQUENCE:".concat(e4.discontinuitySequenceBase)), e4.playlistType && t4.push("#EXT-X-PLAYLIST-TYPE:".concat(e4.playlistType)), e4.isIFrame && t4.push("#EXT-X-I-FRAMES-ONLY"), e4.skip > 0 && t4.push("#EXT-X-SKIP:SKIPPED-SEGMENTS=".concat(e4.skip));
var d2, h3 = a(e4.segments.entries());
try {
for (h3.s(); !(d2 = h3.n()).done; ) {
var p2, I2 = i(d2.value, 2), v2 = I2[0], A2 = I2[1], y2 = t4.length, N2 = i(D(t4, A2, r3, o3, e4.version), 3);
r3 = N2[0], o3 = N2[1], "OUT" === (p2 = N2[2]) ? s3 = true : "IN" === p2 && s3 && (s3 = false), null != n4 && n4.segmentProcessor && n4.segmentProcessor(t4, y2, t4.length - 1, A2, v2);
}
} catch (t5) {
h3.e(t5);
} finally {
h3.f();
}
"VOD" === e4.playlistType && s3 && t4.push("#EXT-X-CUE-IN"), e4.prefetchSegments.length > 2 && S.INVALIDPLAYLIST("The server must deliver no more than two prefetch segments");
var m2, X2 = a(e4.prefetchSegments);
try {
for (X2.s(); !(m2 = X2.n()).done; ) {
var g2 = m2.value;
g2.discontinuity && t4.push("#EXT-X-PREFETCH-DISCONTINUITY"), t4.push("#EXT-X-PREFETCH:".concat(g2.uri));
}
} catch (t5) {
X2.e(t5);
} finally {
X2.f();
}
e4.endlist && t4.push("#EXT-X-ENDLIST");
var b2, O2 = a(e4.renditionReports);
try {
for (O2.s(); !(b2 = O2.n()).done; ) {
var R2 = b2.value, P2 = [];
P2.push('URI="'.concat(R2.uri, '"'), "LAST-MSN=".concat(R2.lastMSN)), void 0 !== R2.lastPart && P2.push("LAST-PART=".concat(R2.lastPart)), t4.push("#EXT-X-RENDITION-REPORT:".concat(P2.join(",")));
}
} catch (t5) {
O2.e(t5);
} finally {
O2.f();
}
})(n3, t3, e3), n3.join("\n");
};
} }, e = {};
var n = (function n2(r) {
var i = e[r];
if (void 0 !== i) return i.exports;
var a = e[r] = { exports: {} };
return t[r].call(a.exports, a, a.exports, n2), a.exports;
})(73);
return n;
})()));
})(hlsParser_min);
return hlsParser_min.exports;
}
var hlsParser_minExports = requireHlsParser_min();
function parseManifest(text, baseUrl) {
const manifest = hlsParser_minExports.parse(text);
if (manifest.isMasterPlaylist) {
return {
isMaster: true,
variants: buildVariants(manifest, baseUrl)
};
}
const media = manifest;
const parsed = buildMedia(media, baseUrl);
return {
isMaster: false,
segments: parsed.segs,
mediaSeq: parsed.mediaSeq,
endList: parsed.endList,
targetDuration: media.targetDuration
};
}
function buildVariants(master, baseUrl) {
const out = [];
for (const v of master.variants) {
if (!v.uri) continue;
const res = v.resolution;
const w = res?.width ?? null;
const h2 = res?.height ?? null;
out.push({
url: safeAbsUrl(v.uri, baseUrl),
res: w && h2 ? `${w}x${h2}` : null,
w,
h: h2,
peak: v.bandwidth ?? null,
avg: v.averageBandwidth ?? null,
codecs: v.codecs ?? null
});
}
return out;
}
function rangeHeaderFromByterange(br, fallbackStart) {
if (!br || typeof br.length !== "number") {
return { header: null, next: fallbackStart };
}
const start = typeof br.offset === "number" ? br.offset : fallbackStart;
const end = start + br.length - 1;
return {
header: `bytes=${start}-${end}`,
next: end + 1
};
}
function buildMedia(media, baseUrl) {
const segs = [];
let lastNext = 0;
let prevMapSig = null;
for (let i = 0; i < media.segments.length; i++) {
const s = media.segments[i];
let rangeHeader = null;
if (s.byterange) {
const r = rangeHeaderFromByterange(s.byterange, lastNext);
rangeHeader = r.header;
lastNext = r.next;
} else {
lastNext = 0;
}
let map = null;
let needMap = false;
if (s.map?.uri) {
const mapUri = safeAbsUrl(s.map.uri, baseUrl);
let mRange = null;
if (s.map.byterange) {
const mr = rangeHeaderFromByterange(s.map.byterange, 0);
mRange = mr.header;
}
map = { uri: mapUri, rangeHeader: mRange };
const sig = `${mapUri}|${mRange || ""}`;
needMap = sig !== prevMapSig;
if (needMap) prevMapSig = sig;
}
let key = null;
if (s.key?.method && s.key.method !== "NONE") {
key = {
method: String(s.key.method).toUpperCase(),
uri: s.key.uri ? safeAbsUrl(s.key.uri, baseUrl) : null,
iv: s.key.iv?.toString() ?? null
};
}
segs.push({
uri: safeAbsUrl(s.uri, baseUrl),
dur: s.duration || 0,
range: rangeHeader,
key,
map,
needMap
});
}
return {
segs,
mediaSeq: media.mediaSequenceBase || 0,
endList: media.endlist ?? false
};
}
function computeExactBytes(parsed) {
let exact = true;
let total = 0;
const seenInit = /* @__PURE__ */ new Set();
for (const s of parsed.segs) {
if (s.range) {
const r = parseRange(s.range);
if (!r || r.end == null) {
exact = false;
} else {
total += r.end - r.start + 1;
}
} else {
exact = false;
}
if (s.needMap && s.map) {
if (s.map.rangeHeader) {
const key = `${s.map.uri}|${s.map.rangeHeader}`;
if (!seenInit.has(key)) {
seenInit.add(key);
const mr = parseRange(s.map.rangeHeader);
if (!mr || mr.end == null) {
exact = false;
} else {
total += mr.end - mr.start + 1;
}
}
} else {
exact = false;
}
}
}
return exact ? total : null;
}
function calcDuration(segments) {
return segments.reduce((sum, s) => sum + s.dur, 0);
}
function isFmp4(segments) {
if (segments.length === 0) return false;
return segments.some((s) => s.map) || /\.m4s(\?|$)/i.test(segments[0].uri);
}
function hasEncryption(segments) {
return segments.some((s) => s.key?.method === "AES-128");
}
const queue = new PQueue({ concurrency: 2 });
const pendingUrls = /* @__PURE__ */ new Set();
async function analyzeMediaPlaylist(url, text, variant, signal) {
const txt = await getText(url, signal);
const man = parseManifest(txt, url);
if (man.isMaster && man.variants) {
const sorted = sortVariantsByQuality(man.variants);
const best = sorted[0];
const count = man.variants.length;
const qualityText = count === 1 ? "quality" : "qualities";
const parts = [`${count} ${qualityText}`];
if (best?.res) {
parts.push(`up to ${best.res}`);
} else if (best?.h) {
parts.push(`up to ${best.h}p`);
}
return {
label: parts.join(" • "),
hlsType: "master",
variantCount: count,
variants: man.variants,
bestVariant: best,
enriched: true
};
}
if (man.segments && man.segments.length > 0) {
const segs = man.segments;
const segCount = segs.length;
const duration = calcDuration(segs);
const isVod = man.endList ?? false;
const exactBytes = computeExactBytes({
segs,
mediaSeq: man.mediaSeq ?? 0
});
const res = variant?.res ?? extractResFromUrl(url);
const fmp4 = isFmp4(segs);
const encrypted = hasEncryption(segs);
const label = buildLabel({
resolution: res,
duration,
size: exactBytes
});
return {
label,
sublabel: buildSublabel(segCount, fmp4),
hlsType: "media",
duration,
segCount,
resolution: res,
isVod,
isFmp4: fmp4,
encrypted,
isLive: !isVod,
size: exactBytes ?? null,
enriched: true
};
}
return {
label: "Empty or Invalid",
hlsType: "invalid",
enriched: true
};
}
async function performEnrichment(item, signal) {
try {
const data = await analyzeMediaPlaylist(item.url, void 0, void 0, signal);
Object.assign(item, data);
return true;
} catch (e) {
console.error("[SG] Enrichment failed:", e);
item.label = "Parse Error";
item.hlsType = "error";
item.enriched = true;
return false;
}
}
async function enrichItem(item) {
if (!item || item.enriched || item.enriching) return false;
item.enriching = true;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), CFG.ENRICH_TIMEOUT);
try {
item._enrichPromise = performEnrichment(item, controller.signal);
await item._enrichPromise;
clearTimeout(timeoutId);
return true;
} catch (e) {
clearTimeout(timeoutId);
const error = e;
const isAbort = error.message === "Aborted" || error.name === "AbortError";
console.error(`[SG] enrichItem error: ${error.message}`);
item.label = isAbort ? "Timeout" : "Parse Error";
item.hlsType = "error";
item.enriched = true;
return false;
} finally {
item.enriching = false;
item._enrichPromise = null;
}
}
function queueEnrich(item, onComplete) {
if (pendingUrls.has(item.url)) return;
pendingUrls.add(item.url);
queue.add(async () => {
try {
if (item && item.kind === "hls" && !item.enriched) {
await enrichItem(item);
onComplete?.();
}
} finally {
pendingUrls.delete(item.url);
}
});
}
async function enrichNow(item) {
if (item._enrichPromise) {
await item._enrichPromise;
return item.enriched && item.hlsType !== "error" && item.hlsType !== "invalid";
}
return enrichItem(item);
}
const MAGIC = "SG_MSG_v1";
class MessageBus {
static instance;
handlers = /* @__PURE__ */ new Map();
initialized = false;
constructor() {
this.handleMessage = this.handleMessage.bind(this);
}
static get() {
if (!MessageBus.instance) {
MessageBus.instance = new MessageBus();
}
return MessageBus.instance;
}
init() {
if (this.initialized) return;
this.initialized = true;
window.addEventListener("message", this.handleMessage);
console.log("[SG] MessageBus initialized");
}
on(type, handler) {
if (!this.handlers.has(type)) {
this.handlers.set(type, /* @__PURE__ */ new Set());
}
this.handlers.get(type).add(handler);
}
off(type, handler) {
const set = this.handlers.get(type);
if (set) {
set.delete(handler);
if (set.size === 0) {
this.handlers.delete(type);
}
}
}
send(type, payload = {}, target = window.top) {
try {
const msg = { type, payload, magic: MAGIC };
target.postMessage(msg, "*");
} catch (e) {
console.error("[SG] Failed to send message:", type, e);
}
}
sendToTop(type, payload = {}) {
if (window.top) {
this.send(type, payload, window.top);
}
}
handleMessage(ev) {
const data = ev.data;
if (!data || typeof data !== "object" || !data.type) return;
if (data.magic !== MAGIC) return;
if (ev.source === window) return;
const handlers = this.handlers.get(data.type);
if (handlers) {
handlers.forEach((fn) => {
try {
fn(data.payload || {}, ev.source);
} catch (e) {
console.error("[SG] Error in message handler:", e);
}
});
}
}
}
const pickerRequests = /* @__PURE__ */ new Map();
function sendDetection(item) {
MessageBus.get().sendToTop("SG_DETECT", {
item: serializeMediaItem(item)
});
}
function registerPickerRequest(id, resolver) {
pickerRequests.set(id, resolver);
}
function resolvePickerRequest(id, item) {
const resolver = pickerRequests.get(id);
if (resolver) {
pickerRequests.delete(id);
resolver(item);
}
}
function initMessaging() {
const bus = MessageBus.get();
bus.init();
console.log("[SG] Messaging initialized");
}
function hexToU8(hex) {
const clean = (hex || "").replace(/^0x/i, "").replace(/[^0-9a-f]/gi, "");
if (clean.length === 0) return new Uint8Array(0);
const evenHex = clean.length % 2 === 0 ? clean : "0" + clean;
const chunks = evenHex.match(/.{1,2}/g);
if (!chunks) return new Uint8Array(0);
return new Uint8Array(chunks.map((b) => parseInt(b, 16)));
}
function ivFromSeq(n) {
let seq = BigInt(n >>> 0);
const iv = new Uint8Array(16);
for (let i = 15; i >= 0; i--) {
iv[i] = Number(seq & 0xffn);
seq >>= 8n;
}
return iv;
}
const keyCache = /* @__PURE__ */ new Map();
const keyInflight = /* @__PURE__ */ new Map();
const MAX_CACHED_KEYS = 10;
function u8ToHex(u8) {
let res = "";
for (let i = 0; i < u8.length; i++) {
res += u8[i].toString(16).padStart(2, "0");
}
return res;
}
async function aesCbcDecrypt(buf, keyBytes, iv) {
const hex = u8ToHex(keyBytes);
const key = await once(
keyCache,
keyInflight,
hex,
() => crypto.subtle.importKey(
"raw",
keyBytes,
{ name: "AES-CBC" },
false,
["decrypt"]
),
MAX_CACHED_KEYS
);
return crypto.subtle.decrypt(
{ name: "AES-CBC", iv },
key,
buf
);
}
const CONFIG = {
/** Buffer threshold before flushing to Blob */
MAX_BUFFER_SIZE: 100 * 1024 * 1024,
// 100MB
/** Hard limit to prevent browser crashes */
MAX_FILE_SIZE: 1.9 * 1024 * 1024 * 1024,
// ~1.9GB (Chrome limit ~2GB)
/** Delay before revoking blob URLs */
URL_REVOKE_DELAY: 6e4
};
const log = {
info: (msg) => console.log(`[SG] ${msg}`),
warn: (msg, ...args) => console.warn(`[SG] ${msg}`, ...args),
error: (msg, ...args) => console.error(`[SG] ${msg}`, ...args)
};
const revokeUrlLater = (url, delay = CONFIG.URL_REVOKE_DELAY) => {
setTimeout(() => URL.revokeObjectURL(url), delay);
};
const wrapSaveError = (e) => new Error(`Failed to save file: ${e.message}`);
const silentAbort = (abortable) => abortable.abort().catch(() => {
});
const silentRemove = (root, name) => root.removeEntry(name).catch(() => {
});
const getExtension = (filename, fallback = "") => filename.split(".").pop() || fallback;
const addPartSuffix = (filename, part) => {
const lastDot = filename.lastIndexOf(".");
return lastDot === -1 ? `${filename}.part${part}` : `${filename.slice(0, lastDot)}.part${part}${filename.slice(lastDot)}`;
};
class WriterState {
closed = false;
aborted = false;
get isClosed() {
return this.closed;
}
get isAborted() {
return this.aborted;
}
setClosed() {
this.closed = true;
}
setAborted() {
this.aborted = true;
}
/**
* Asserts writer is open and valid.
* @param warnOnly If true, returns false on close instead of throwing.
*/
assertWritable(warnOnly = false) {
if (this.aborted) throw new Error("Writer was aborted");
if (this.closed) {
if (warnOnly) {
log.warn("Attempted to write after close");
return false;
}
throw new Error("Writer was closed");
}
return true;
}
}
function mapGMDownloadError(error, details) {
switch (error) {
case "not_enabled":
return "Downloads are disabled in userscript settings";
case "not_whitelisted":
return "URL not whitelisted for download. Check userscript settings.";
case "not_permitted":
return "Download not permitted. Try allowing downloads in browser settings.";
case "not_supported":
return "Download not supported by your userscript manager";
case "not_succeeded":
return details || "Download failed. Check if the file location is writable.";
default:
return `Download failed: ${error || "unknown"}${details ? ` - ${details}` : ""}`;
}
}
function downloadViaAnchor(url, filename) {
return new Promise((resolve) => {
const a = document.createElement("a");
a.href = url;
a.download = filename;
a.style.display = "none";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
setTimeout(() => {
resolve();
}, 500);
});
}
function downloadWithGM(url, filename, onSuccess) {
return new Promise((resolve, reject) => {
GM_download({
url,
name: filename,
saveAs: true,
onload: () => {
onSuccess?.();
resolve();
},
onerror: (err) => {
reject(new Error(mapGMDownloadError(err?.error, err?.details)));
},
ontimeout: () => {
reject(new Error("Download timed out"));
}
});
});
}
async function triggerDownload(url, filename, onSuccess) {
try {
if (typeof GM_download === "function") {
await downloadWithGM(url, filename, onSuccess);
} else {
log.info("GM_download unavailable, using anchor fallback");
await downloadViaAnchor(url, filename);
onSuccess?.();
}
} finally {
revokeUrlLater(url);
}
}
async function createNativeWriter(suggestedName, mimeType) {
if (typeof window.showSaveFilePicker !== "function" || window.isSecureContext === false) {
return null;
}
try {
const ext = getExtension(suggestedName, "mp4");
const handle = await window.showSaveFilePicker({
suggestedName,
types: [
{
description: "Video file",
accept: { [mimeType]: [`.${ext}`] }
}
]
});
const stream = await handle.createWritable();
const state2 = new WriterState();
return {
async write(chunk) {
if (!state2.assertWritable(true)) return;
await stream.write(chunk);
},
async close() {
if (state2.isAborted) throw new Error("Writer was aborted");
if (state2.isClosed) return;
state2.setClosed();
try {
await stream.close();
notifyDownloadComplete(suggestedName);
} catch (e) {
log.error("Native close error:", e);
throw wrapSaveError(e);
}
},
abort() {
if (state2.isClosed || state2.isAborted) return;
state2.setAborted();
silentAbort(stream);
}
};
} catch (e) {
const error = e;
if (error.name === "AbortError") throw e;
log.warn("File System Access API failed, using fallback:", error.message);
return null;
}
}
const STALE_THRESHOLD_MS = 24 * 60 * 60 * 1e3;
async function cleanupStaleTempFiles(root) {
try {
const now = Date.now();
const deletePromises = [];
for await (const [name, handle] of root.entries()) {
if (handle.kind === "file" && name.endsWith(".tmp") && name.startsWith("sg_download_")) {
const parts = name.split("_");
if (parts.length >= 3) {
const ts = parseInt(parts[2], 10);
if (!isNaN(ts) && now - ts > STALE_THRESHOLD_MS) {
log.info(`Cleaning up stale temp file: ${name}`);
deletePromises.push(silentRemove(root, name));
}
}
}
}
if (deletePromises.length > 0) {
await Promise.all(deletePromises);
}
} catch (e) {
log.warn("Failed to cleanup stale temp files:", e);
}
}
async function createOpfsWriter(suggestedName, mimeType) {
if (!navigator.storage?.getDirectory) {
return null;
}
try {
const root = await navigator.storage.getDirectory();
cleanupStaleTempFiles(root).catch(() => {
});
const tempName = `sg_download_${Date.now()}_${Math.random().toString(36).slice(2)}.tmp`;
const fileHandle = await root.getFileHandle(tempName, { create: true });
const writable = await fileHandle.createWritable();
const state2 = new WriterState();
return {
async write(chunk) {
if (!state2.assertWritable(true)) return;
await writable.write(chunk);
},
async close() {
if (state2.isAborted) throw new Error("Writer was aborted");
if (state2.isClosed) return;
state2.setClosed();
try {
await writable.close();
const file = await fileHandle.getFile();
const url = URL.createObjectURL(file);
await triggerDownload(url, suggestedName, () => {
notifyDownloadComplete(suggestedName);
});
setTimeout(() => {
silentRemove(root, tempName);
}, CONFIG.URL_REVOKE_DELAY + 5e3);
} catch (e) {
log.error("OPFS close error:", e);
throw wrapSaveError(e);
}
},
abort() {
if (state2.isClosed || state2.isAborted) return;
state2.setAborted();
silentAbort(writable);
silentRemove(root, tempName);
}
};
} catch (e) {
log.warn("OPFS initialization failed:", e);
return null;
}
}
function createBlobWriter(suggestedName, mimeType) {
const blobParts = [];
let currentBuffer = [];
let currentBufferSize = 0;
let currentPartSize = 0;
let partNumber = 1;
const state2 = new WriterState();
const flushBuffer = () => {
if (currentBufferSize === 0) return;
const blob = new Blob(currentBuffer, { type: mimeType });
blobParts.push(blob);
currentBuffer = [];
currentBufferSize = 0;
};
const clearMemory = () => {
currentBuffer = [];
currentBufferSize = 0;
blobParts.length = 0;
currentPartSize = 0;
};
const savePart = async (isFinal) => {
flushBuffer();
if (blobParts.length === 0 && !isFinal) return;
if (blobParts.length === 0 && isFinal && partNumber === 1) {
log.warn("Saving empty file");
}
let filename = suggestedName;
if (partNumber > 1 || !isFinal && partNumber === 1) {
filename = addPartSuffix(suggestedName, partNumber);
}
log.info(`Saving part ${partNumber}: ${filename}, Size: ${(currentPartSize / 1024 / 1024).toFixed(2)} MB`);
const blob = new Blob(blobParts, { type: mimeType });
blobParts.length = 0;
currentBuffer = [];
currentBufferSize = 0;
currentPartSize = 0;
const url = URL.createObjectURL(blob);
await triggerDownload(url, filename, () => {
if (isFinal) notifyDownloadComplete(suggestedName);
});
};
return {
async write(chunk) {
if (!state2.assertWritable(true)) return;
if (currentPartSize + chunk.length > CONFIG.MAX_FILE_SIZE) {
log.warn(`File part ${partNumber} limit reached (~1.9GB). Splitting file...`);
await savePart(false);
partNumber++;
}
currentBuffer.push(chunk);
currentBufferSize += chunk.length;
currentPartSize += chunk.length;
if (currentBufferSize >= CONFIG.MAX_BUFFER_SIZE) {
flushBuffer();
if (blobParts.length % 5 === 0) {
log.info(`Part ${partNumber} Buffer: ${blobParts.length} blobs, Size: ${(currentPartSize / 1024 / 1024).toFixed(2)} MB`);
}
}
},
async close() {
if (state2.isAborted) throw new Error("Writer was aborted");
if (state2.isClosed) return;
state2.setClosed();
await savePart(true);
},
abort() {
if (state2.isClosed || state2.isAborted) return;
state2.setAborted();
clearMemory();
}
};
}
async function createFileWriter(suggestedName, mimeType) {
try {
const nativeWriter = await createNativeWriter(suggestedName, mimeType);
if (nativeWriter) {
log.info("Using native File System Access API");
return nativeWriter;
}
} catch (e) {
if (e.name === "AbortError") {
log.info("User cancelled file picker");
throw e;
}
log.warn("Native writer creation failed:", e);
}
const opfsWriter = await createOpfsWriter(suggestedName);
if (opfsWriter) {
log.info("Using OPFS writer");
return opfsWriter;
}
log.info("Using blob fallback for download");
return createBlobWriter(suggestedName, mimeType);
}
const INFLIGHT_CACHE_TIMEOUT_MS = 100;
const MAX_BUFFERED_SEGMENTS = 50;
const BACKPRESSURE_CHECK_MS = 50;
class ResourceCache {
cache = /* @__PURE__ */ new Map();
inflight = /* @__PURE__ */ new Map();
async fetch(key, fetcher, signal) {
if (signal?.aborted) {
throw new AbortError(new Error("Aborted"));
}
const result = await once(
this.cache,
this.inflight,
key,
fetcher,
INFLIGHT_CACHE_TIMEOUT_MS
);
if (signal?.aborted) {
throw new AbortError(new Error("Aborted"));
}
return result;
}
clear() {
this.cache.clear();
this.inflight.clear();
}
}
class SequentialWriter {
constructor(writer) {
this.writer = writer;
}
buffers = /* @__PURE__ */ new Map();
writePtr = 0;
writeChain = Promise.resolve();
totalBytes = 0;
writeError = null;
get bufferedCount() {
return this.buffers.size;
}
get bytesWritten() {
return this.totalBytes;
}
get error() {
return this.writeError;
}
shouldThrottle() {
return this.buffers.size >= MAX_BUFFERED_SEGMENTS;
}
enqueue(index, data) {
this.buffers.set(index, data);
this.flush();
}
flush() {
this.writeChain = this.writeChain.then(async () => {
while (this.buffers.has(this.writePtr)) {
const chunk = this.buffers.get(this.writePtr);
this.buffers.delete(this.writePtr);
try {
await this.writer.write(chunk);
this.totalBytes += chunk.length;
} catch (e) {
this.writeError = e;
throw e;
}
this.writePtr++;
}
});
}
async finalize() {
await this.writeChain;
if (this.writeError) {
throw this.writeError;
}
}
async close() {
await this.finalize();
await this.writer.close();
}
abort() {
this.buffers.clear();
this.writer.abort();
}
}
class ProgressTracker {
constructor(total, onUpdate) {
this.total = total;
this.onUpdate = onUpdate;
}
inProgress = /* @__PURE__ */ new Map();
rafId = 0;
_done = 0;
byteDone = 0;
avgLen = 0;
get done() {
return this._done;
}
get averageSize() {
return this.avgLen;
}
setProgress(index, loaded, total) {
this.inProgress.set(index, { loaded, total });
this.scheduleUpdate();
}
markComplete(index, bytes) {
this.inProgress.delete(index);
this._done++;
this.byteDone += bytes;
this.avgLen = this.byteDone / this._done;
this.scheduleUpdate();
}
clear(index) {
this.inProgress.delete(index);
}
scheduleUpdate() {
if (this.rafId) return;
this.rafId = requestAnimationFrame(() => {
this.rafId = 0;
const partial = this.calculatePartial();
const pct = (this._done + partial) / this.total * 100;
this.onUpdate(pct, this._done, this.total);
});
}
calculatePartial() {
let partial = 0;
this.inProgress.forEach(({ loaded, total }) => {
if (total > 0) {
partial += Math.min(1, loaded / total);
} else if (this.avgLen > 0) {
partial += Math.min(1, loaded / this.avgLen);
}
});
return partial;
}
}
class SegmentFetcher {
constructor(mediaSeq) {
this.mediaSeq = mediaSeq;
}
keyCache = new ResourceCache();
mapCache = new ResourceCache();
async fetch(segment, index, signal, onProgress) {
this.validateEncryption(segment);
const [keyBytes, mapBytes] = await Promise.all([
this.fetchKey(segment, signal),
this.fetchMap(segment, signal)
]);
const buf = await this.download(segment, signal, onProgress);
const decrypted = keyBytes ? await this.decrypt(buf, keyBytes, segment, index) : buf;
return this.prependMap(new Uint8Array(decrypted), mapBytes);
}
validateEncryption(segment) {
if (segment.key?.method && segment.key.method !== "AES-128") {
throw new AbortError(
new Error(`Unsupported key method: ${segment.key.method}`)
);
}
}
async download(segment, signal, onProgress) {
const headers = segment.range ? { Range: segment.range } : {};
try {
return await getBin(
segment.uri,
headers,
CFG.REQUEST_TIMEOUT,
(e) => onProgress(e.loaded, e.total),
signal
);
} catch (err) {
if (signal.aborted) {
throw new AbortError(new Error("Aborted"));
}
throw err;
}
}
async fetchKey(segment, signal) {
if (!segment.key || segment.key.method !== "AES-128" || !segment.key.uri) {
return null;
}
return this.keyCache.fetch(
segment.key.uri,
async () => {
const buf = await getBin(segment.key.uri, {}, CFG.REQUEST_TIMEOUT, void 0, signal);
return new Uint8Array(buf);
},
signal
);
}
async fetchMap(segment, signal) {
if (!segment.needMap || !segment.map?.uri) {
return null;
}
const cacheKey = `${segment.map.uri}|${segment.map.rangeHeader || ""}`;
return this.mapCache.fetch(
cacheKey,
async () => {
const headers = segment.map.rangeHeader ? { Range: segment.map.rangeHeader } : {};
const buf = await getBin(segment.map.uri, headers, CFG.REQUEST_TIMEOUT, void 0, signal);
return new Uint8Array(buf);
},
signal
);
}
async decrypt(buf, keyBytes, segment, index) {
const iv = segment.key.iv ? hexToU8(segment.key.iv) : ivFromSeq(this.mediaSeq + index);
return aesCbcDecrypt(buf, keyBytes, iv);
}
prependMap(data, mapBytes) {
if (!mapBytes?.length) return data;
const joined = new Uint8Array(mapBytes.length + data.length);
joined.set(mapBytes, 0);
joined.set(data, mapBytes.length);
return joined;
}
clear() {
this.keyCache.clear();
this.mapCache.clear();
}
}
class SegmentDownloader {
constructor(segments, mediaSeq, writer, onProgress, onComplete) {
this.segments = segments;
this.onProgress = onProgress;
this.onComplete = onComplete;
this.queue = new PQueue({ concurrency: CFG.CONCURRENCY });
this.writer = new SequentialWriter(writer);
this.progress = new ProgressTracker(segments.length, onProgress);
this.fetcher = new SegmentFetcher(mediaSeq);
}
queue;
controllers = /* @__PURE__ */ new Map();
writer;
progress;
fetcher;
paused = false;
canceled = false;
finalized = false;
async start() {
for (let i = 0; i < this.segments.length; i++) {
this.queue.add(() => this.downloadSegment(i));
}
await this.queue.onIdle();
await this.finalize();
}
togglePause() {
this.paused = !this.paused;
if (this.paused) {
this.queue.pause();
} else {
this.queue.start();
}
return this.paused;
}
cancel() {
if (this.canceled) return;
this.canceled = true;
this.abortAll();
this.queue.clear();
this.writer.abort();
this.cleanup();
}
async downloadSegment(index) {
if (this.canceled) return;
await this.waitForBackpressure();
if (this.canceled) return;
const controller = new AbortController();
this.controllers.set(index, controller);
try {
await pRetry(
async () => {
if (this.canceled) {
throw new AbortError(new Error("Canceled"));
}
const data = await this.fetcher.fetch(
this.segments[index],
index,
controller.signal,
(loaded, total) => {
this.progress.setProgress(index, loaded, total);
}
);
this.writer.enqueue(index, data);
this.progress.markComplete(index, data.length);
},
{
retries: CFG.RETRIES,
signal: controller.signal,
onFailedAttempt: ({ error, attemptNumber }) => {
if (!this.canceled) {
console.warn(
`[SG] Segment ${index} failed (attempt ${attemptNumber}): ${error.message}`
);
}
}
}
);
} catch (error) {
this.handleSegmentError(index, error);
} finally {
this.controllers.delete(index);
}
}
async waitForBackpressure() {
while (this.writer.shouldThrottle() && !this.canceled) {
await new Promise((r) => setTimeout(r, BACKPRESSURE_CHECK_MS));
}
}
handleSegmentError(index, error) {
this.progress.clear(index);
if (this.canceled || error instanceof AbortError) {
return;
}
console.error(`[SG] Segment ${index} fatal error:`, error);
this.canceled = true;
this.abortAll();
this.queue.clear();
}
abortAll() {
for (const controller of this.controllers.values()) {
controller.abort();
}
this.controllers.clear();
}
async finalize() {
if (this.finalized) return;
this.finalized = true;
const success = !this.canceled && this.progress.done === this.segments.length && !this.writer.error;
try {
if (success) {
await this.writer.close();
this.onComplete(true);
} else {
this.writer.abort();
const msg = this.writer.error ? "Write failed" : this.canceled ? "Canceled" : "Incomplete download";
this.onComplete(false, msg);
}
} catch (e) {
console.error("[SG] Finalize error:", e);
this.onComplete(false, "Finalization failed");
} finally {
this.cleanup();
}
}
cleanup() {
this.fetcher.clear();
}
}
async function downloadSegments(parsed, filename, isFmp42, card) {
const total = parsed.segs.length;
console.log("[SG] Starting segment download:", { filename, segments: total });
const mime = isFmp42 ? "video/mp4" : "video/mp2t";
let writer;
try {
writer = await createFileWriter(filename, mime);
} catch (e) {
if (e.name === "AbortError") {
card.remove();
return;
}
throw e;
}
const downloader = new SegmentDownloader(
parsed.segs,
parsed.mediaSeq,
writer,
(pct, done, total2) => {
card.update(pct, `${done}/${total2}`);
},
(success, message) => {
if (success) {
card.update(100, "");
card.done(true);
} else {
card.done(false, message);
}
}
);
card.setOnStop(() => {
const paused = downloader.togglePause();
return paused ? "paused" : "resumed";
});
card.setOnCancel(() => {
downloader.cancel();
card.remove();
});
await downloader.start();
}
async function downloadDirect(url, delegate, pageTitle) {
console.log("[SG] Direct download:", url);
const info = blobRegistry.get(url);
const ext = guessExt(url, info?.type);
const filename = generateFilename({ title: pageTitle, ext });
let dlUrl = url;
let cleanup = () => {
};
if (info?.blob) {
dlUrl = URL.createObjectURL(info.blob);
cleanup = () => URL.revokeObjectURL(dlUrl);
}
const card = delegate.createCard(filename, url);
card.setOnCancel(() => {
cleanup();
card.remove();
});
GM_download({
url: dlUrl,
name: filename,
saveAs: true,
onprogress: (e) => {
if (e.lengthComputable) {
card.update(
e.loaded / e.total * 100,
`${formatBytes(e.loaded)}/${formatBytes(e.total)}`
);
} else {
card.update(0, formatBytes(e.loaded));
}
},
onload: () => {
card.update(100, "");
card.done(true);
cleanup();
notifyDownloadComplete(filename);
},
onerror: (err) => {
const errorMsg = err?.error || "unknown";
console.error("[SG] Download error:", { error: errorMsg, url });
card.done(false, errorMsg === "not_succeeded" ? "Save failed" : errorMsg);
cleanup();
},
ontimeout: () => {
card.done(false, "Timeout");
cleanup();
}
});
}
async function downloadHls(url, preVariant, delegate, pageTitle) {
console.log("[SG] HLS download:", url);
const data = await analyzeMediaPlaylist(url);
let mediaUrl = preVariant ? preVariant.url : url;
let chosenVariant = preVariant;
if (!preVariant && data.hlsType === "master" && data.variants && data.variants.length > 0) {
const variants = sortVariantsByQuality(data.variants);
if (variants.length === 0) {
throw new Error("No variants found");
}
const items = [];
for (const v of variants) {
let data2;
try {
data2 = await analyzeMediaPlaylist(v.url, void 0, v);
} catch {
data2 = { label: buildLabel({ resolution: v.res }), hlsType: "error" };
}
items.push({
url: v.url,
kind: "variant",
label: data2.label || "Unknown",
sublabel: data2.sublabel || null,
size: data2.size ?? null,
type: null,
origin: document.location.origin,
pageTitle,
enriched: true,
enriching: false,
hlsType: "media",
isLive: false,
encrypted: false,
variant: v
});
}
const selected = await delegate.pickVariant(items);
if (!selected) return;
chosenVariant = selected.variant ?? null;
mediaUrl = selected.url;
}
const mediaTxt = await getText(mediaUrl);
const mediaMan = parseManifest(mediaTxt, mediaUrl);
if (!mediaMan.segments || mediaMan.segments.length === 0) {
throw new Error("Invalid playlist: no segments");
}
const parsed = {
segs: mediaMan.segments,
mediaSeq: mediaMan.mediaSeq ?? 0,
endList: mediaMan.endList ?? false
};
const fmp4 = isFmp4(parsed.segs);
const ext = fmp4 ? "mp4" : "ts";
const filename = generateFilename({
title: pageTitle,
ext,
quality: chosenVariant?.res
});
const card = delegate.createCard(filename, url, parsed.segs.length);
await downloadSegments(parsed, filename, fmp4, card);
}
async function handleItem(item, delegate) {
if (item.isRemote && item.remoteWin) {
if (item.remoteWin.closed) {
throw new Error("Source frame is gone");
}
if (!item.url.startsWith("blob:")) {
if (item.kind === "hls" || item.kind === "variant") {
return downloadHls(item.url, item.variant ?? null, delegate, item.pageTitle);
}
if (item.kind === "video") {
return downloadDirect(item.url, delegate, item.pageTitle);
}
}
item.remoteWin.postMessage(
{
type: "SG_CMD_DOWNLOAD",
payload: { url: item.url, kind: item.kind, variant: item.variant, pageTitle: item.pageTitle }
},
"*"
);
return;
}
if (item.kind === "hls" && !item.enriched) {
delegate.setBusy(true);
try {
if (item._enrichPromise) {
await item._enrichPromise;
} else {
await enrichNow(item);
}
} catch (e) {
throw new Error(`Failed to analyze stream: ${e.message}`);
} finally {
delegate.setBusy(false);
}
if (item.hlsType === "error" || item.hlsType === "invalid") {
throw new Error("Cannot download: Stream analysis failed or invalid");
}
}
if (item.kind === "video") {
return downloadDirect(item.url, delegate, item.pageTitle);
}
if (item.kind === "variant") {
return downloadHls(item.url, item.variant ?? null, delegate, item.pageTitle);
}
if (item.kind === "hls") {
return downloadHls(item.url, null, delegate, item.pageTitle);
}
}
const css = '@import"https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;700&family=Urbanist:wght@300;400;500;600;700&display=swap";#sg-fab-container,#sg-modal-container,#sg-toast-container{font-family:Urbanist,system-ui,-apple-system,sans-serif!important;font-size:14px!important;line-height:1.5!important;box-sizing:border-box!important;letter-spacing:.02em!important}#sg-fab-container *,#sg-modal-container *,#sg-toast-container *{box-sizing:border-box!important;outline:none!important}:host{color-scheme:dark;forced-color-adjust:none;--sg-bg-deep: #050505;--sg-bg-surface: #0a0a0a;--sg-bg-glass: rgba(10, 10, 10, .7);--sg-gold-dim: #8a7e58;--sg-gold: #D4AF37;--sg-gold-bright: #f4d060;--sg-gold-glow: rgba(212, 175, 55, .3);--sg-border-subtle: rgba(255, 255, 255, .08);--sg-border-gold: rgba(212, 175, 55, .25);--sg-text-main: #f0f0f0;--sg-text-muted: #888888;--sg-text-gold: #e5c55d;--sg-success: #10b981;--sg-error: #ef4444;--sg-warn: #f59e0b;--sg-radius-lg: 16px;--sg-radius-md: 10px;--sg-radius-sm: 6px;--sg-shadow-glow: 0 0 20px var(--sg-gold-glow);--sg-glass-blur: blur(16px);--sg-font-display: "Cinzel", serif;--sg-font-body: "Urbanist", sans-serif;--sg-transition: all .3s cubic-bezier(.25, .8, .25, 1)}@keyframes sg-spin{to{transform:rotate(360deg)}}@keyframes sg-breathe{0%,to{box-shadow:0 0 10px var(--sg-gold-glow);border-color:var(--sg-gold)}50%{box-shadow:0 0 25px var(--sg-gold-glow);border-color:var(--sg-gold-bright)}}@keyframes sg-fade-up{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes sg-scale-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.sg-fab{all:initial!important;position:fixed!important;right:30px!important;bottom:30px!important;z-index:2147483647!important;width:60px!important;height:60px!important;border-radius:20px!important;padding:0!important;display:none!important;align-items:center!important;justify-content:center!important;background:linear-gradient(145deg,#151515,#000)!important;color:var(--sg-gold)!important;border:1px solid var(--sg-border-gold)!important;border-top:1px solid var(--sg-border-subtle)!important;cursor:pointer!important;font-family:var(--sg-font-body)!important;box-shadow:0 10px 30px #00000080,0 0 0 1px #00000080!important;transition:var(--sg-transition)!important}.sg-fab.show{display:flex!important;animation:sg-scale-in .4s ease-out backwards!important}.sg-fab:hover{transform:translateY(-2px) scale(1.05)!important;box-shadow:0 15px 40px #0009,var(--sg-shadow-glow)!important;border-color:var(--sg-gold-bright)!important;color:var(--sg-gold-bright)!important}.sg-fab:active{transform:translateY(0) scale(.95)!important}.sg-fab.busy{pointer-events:none!important;animation:sg-breathe 2s infinite!important}.sg-fab.busy>.sg-fab-icon{opacity:0!important}.sg-fab.busy:after{content:""!important;position:absolute!important;inset:0!important;margin:auto!important;width:24px!important;height:24px!important;border:2px solid transparent!important;border-top-color:var(--sg-gold)!important;border-right-color:var(--sg-gold)!important;border-radius:50%!important;animation:sg-spin .8s linear infinite!important}.sg-fab-icon{all:initial!important;display:flex!important;align-items:center!important;justify-content:center!important;color:inherit!important;font-size:0!important}.sg-fab svg{width:26px!important;height:26px!important;fill:none!important;stroke:currentColor!important;stroke-width:2!important;filter:drop-shadow(0 0 2px rgba(212,175,55,.5))!important}.sg-badge{all:initial!important;position:absolute!important;top:-6px!important;right:-6px!important;background:var(--sg-gold)!important;color:#000!important;font-weight:700!important;font-size:11px!important;font-family:var(--sg-font-body)!important;padding:2px 7px!important;border-radius:10px!important;display:none!important;border:2px solid #000!important;min-width:22px!important;text-align:center!important;box-shadow:0 4px 8px #00000080!important}.sg-badge.show{display:inline-block!important;animation:sg-scale-in .3s cubic-bezier(.18,.89,.32,1.28)!important}.sg-modal{all:initial!important;position:fixed!important;inset:0!important;z-index:2147483647!important;display:none!important;align-items:center!important;justify-content:center!important;background:#0009!important;backdrop-filter:blur(8px)!important;font-family:var(--sg-font-body)!important;opacity:0!important;transition:opacity .3s ease!important}.sg-modal.show{display:flex!important;opacity:1!important}.sg-card{all:initial!important;display:flex!important;flex-direction:column!important;background:#0a0a0ad9!important;backdrop-filter:var(--sg-glass-blur) saturate(180%)!important;-webkit-backdrop-filter:var(--sg-glass-blur) saturate(180%)!important;border:1px solid var(--sg-border-gold)!important;border-radius:var(--sg-radius-lg)!important;color:var(--sg-text-main)!important;width:min(580px,94vw)!important;max-height:85vh!important;overflow:hidden!important;box-shadow:0 20px 50px #000000b3,0 0 0 1px #ffffff0d!important;font-family:var(--sg-font-body)!important;transform:translateY(20px)!important;transition:transform .4s cubic-bezier(.19,1,.22,1)!important}.sg-modal.show .sg-card{transform:translateY(0)!important}.sg-card-head{all:initial!important;display:grid!important;grid-template-columns:1fr auto!important;align-items:center!important;padding:24px 28px 20px!important;border-bottom:1px solid var(--sg-border-subtle)!important;background:linear-gradient(to bottom,rgba(255,255,255,.03),transparent)!important;font-family:var(--sg-font-body)!important;position:relative!important}.sg-card-head:after{content:""!important;position:absolute!important;bottom:0!important;left:28px!important;width:40px!important;height:2px!important;background:var(--sg-gold)!important;box-shadow:0 0 8px var(--sg-gold)!important}.sg-card-title{all:initial!important;font-family:var(--sg-font-display)!important;font-size:20px!important;letter-spacing:.05em!important;font-weight:600!important;color:var(--sg-text-gold)!important;text-transform:uppercase!important;text-shadow:0 2px 10px rgba(0,0,0,.5)!important}.sg-card-body{all:initial!important;display:flex!important;flex-direction:column!important;gap:16px!important;padding:24px 28px 28px!important;overflow-y:auto!important;max-height:calc(85vh - 80px)!important;font-family:var(--sg-font-body)!important;background-image:radial-gradient(circle at 100% 0%,rgba(212,175,55,.03) 0%,transparent 25%),radial-gradient(circle at 0% 100%,rgba(212,175,55,.02) 0%,transparent 20%)!important}.sg-card-body::-webkit-scrollbar{width:6px!important}.sg-card-body::-webkit-scrollbar-thumb{background:#ffffff1a!important;border-radius:3px!important}.sg-card-body::-webkit-scrollbar-thumb:hover{background:var(--sg-gold-dim)!important}.sg-btn{all:initial!important;display:flex!important;align-items:center!important;justify-content:center!important;background:#ffffff08!important;border:1px solid var(--sg-border-subtle)!important;color:var(--sg-text-muted)!important;border-radius:var(--sg-radius-sm)!important;padding:8px!important;cursor:pointer!important;min-width:36px!important;min-height:36px!important;transition:var(--sg-transition)!important;font-family:var(--sg-font-body)!important}.sg-btn:hover{background:#ffffff14!important;color:var(--sg-text-main)!important;border-color:#fff3!important;transform:translateY(-1px)!important}.sg-btn svg{width:18px!important;height:18px!important;fill:none!important;stroke:currentColor!important}.sg-btn-small{padding:6px!important;min-width:28px!important;min-height:28px!important}.sg-btn-small svg{width:14px!important;height:14px!important}.sg-option{all:initial!important;display:flex!important;align-items:center!important;gap:12px!important;font-size:14px!important;color:var(--sg-text-muted)!important;padding:14px 18px!important;background:#0003!important;border:1px solid var(--sg-border-subtle)!important;border-radius:var(--sg-radius-md)!important;cursor:pointer!important;font-family:var(--sg-font-body)!important;transition:var(--sg-transition)!important}.sg-option:hover{background:#ffffff08!important;border-color:#ffffff26!important;color:var(--sg-text-main)!important}.sg-option input[type=checkbox]{width:18px!important;height:18px!important;cursor:pointer!important;accent-color:var(--sg-gold)!important;margin:0!important;filter:sepia(100%) hue-rotate(5deg) brightness(.9) saturate(1.5)!important}.sg-list{all:initial!important;display:flex!important;flex-direction:column!important;gap:12px!important;font-family:var(--sg-font-body)!important}.sg-item{all:initial!important;display:block!important;background:linear-gradient(90deg,#ffffff05,#fff0)!important;border:1px solid var(--sg-border-subtle)!important;border-left:2px solid transparent!important;border-radius:var(--sg-radius-md)!important;padding:16px 20px!important;cursor:pointer!important;transition:var(--sg-transition)!important;font-family:var(--sg-font-body)!important;animation:sg-fade-up .5s ease-out backwards!important}.sg-item:nth-child(1){animation-delay:.05s!important}.sg-item:nth-child(2){animation-delay:.1s!important}.sg-item:nth-child(3){animation-delay:.15s!important}.sg-item:nth-child(4){animation-delay:.2s!important}.sg-item:hover{background:#ffffff0a!important;border-color:#ffffff1a!important;border-left-color:var(--sg-gold)!important;transform:translate(4px)!important;box-shadow:0 4px 20px #0000004d!important}.sg-item:focus{outline:1px solid var(--sg-gold)!important}.sg-item-top{all:initial!important;display:flex!important;align-items:flex-start!important;justify-content:space-between!important;gap:14px!important;margin-bottom:8px!important;font-family:var(--sg-font-body)!important}.sg-item-title{all:initial!important;display:flex!important;align-items:center!important;flex-wrap:wrap!important;gap:10px!important;font-weight:600!important;font-size:15px!important;color:var(--sg-text-main)!important;line-height:1.4!important;flex:1!important;font-family:var(--sg-font-body)!important}.sg-item-url{all:initial!important;display:block!important;font-size:12px!important;color:#555!important;white-space:nowrap!important;overflow:hidden!important;text-overflow:ellipsis!important;font-family:Consolas,Monaco,monospace!important;opacity:.6!important}.sg-item-sub{all:initial!important;display:flex!important;align-items:center!important;gap:10px!important;font-size:12px!important;color:var(--sg-text-muted)!important;margin-bottom:6px!important;font-family:var(--sg-font-body)!important}.sg-item-title-context{all:initial!important;display:block!important;font-size:13px!important;color:var(--sg-text-gold)!important;margin-bottom:6px!important;font-family:var(--sg-font-body)!important;font-weight:500!important;opacity:.85!important}.sg-item-size{color:var(--sg-gold-dim)!important}.sg-badge-type{all:initial!important;display:inline-flex!important;align-items:center!important;font-size:10px!important;padding:4px 8px!important;border-radius:4px!important;font-weight:700!important;text-transform:uppercase!important;letter-spacing:.5px!important;white-space:nowrap!important;font-family:var(--sg-font-body)!important;border:1px solid transparent!important;background:#ffffff0d!important;color:#fff!important}.sg-badge-type.master{border-color:#6366f166!important;color:#a5b4fc!important;background:#6366f11a!important}.sg-badge-type.video{border-color:#10b98166!important;color:#6ee7b7!important;background:#10b9811a!important}.sg-badge-type.direct{background:#f59e0b!important;color:#fff!important}.sg-badge-type.live{background:#ef4444!important;color:#fff!important}.sg-badge-type.encrypted{background:#8b5cf6!important;color:#fff!important}.sg-badge-type.analyzing{background:#6b7280!important;color:#fff!important;animation:sg-pulse 1s infinite!important}.sg-badge-type.remote{background:#06b6d4!important;color:#fff!important}.sg-badge-type.error{background:#e74c3c!important;color:#fff!important}.sg-copy-btn{all:initial!important;display:flex!important;align-items:center!important;justify-content:center!important;background:transparent!important;border:1px solid var(--sg-border-subtle)!important;color:var(--sg-gold)!important;border-radius:6px!important;padding:8px!important;cursor:pointer!important;flex-shrink:0!important;transition:var(--sg-transition)!important;font-family:var(--sg-font-body)!important}.sg-copy-btn svg{width:18px!important;height:18px!important;display:block!important}.sg-copy-btn:hover{border-color:var(--sg-gold-dim)!important;color:var(--sg-gold)!important;background:#d4af370d!important}.sg-copy-btn.copied{border-color:var(--sg-success)!important;color:var(--sg-success)!important}.sg-empty{all:initial!important;display:flex!important;flex-direction:column!important;align-items:center!important;padding:60px 40px!important;color:var(--sg-text-muted)!important;font-size:15px!important;text-align:center!important;line-height:1.6!important;font-family:var(--sg-font-body)!important;border:1px dashed var(--sg-border-subtle)!important;border-radius:var(--sg-radius-md)!important}.sg-empty small{display:block!important;margin-top:12px!important;font-size:13px!important;color:#555!important;font-style:italic!important}.sg-toast{all:initial!important;position:fixed!important;right:30px!important;bottom:110px!important;z-index:2147483648!important;display:flex!important;flex-direction:column!important;gap:16px!important;max-width:400px!important;max-height:70vh!important;overflow-y:auto!important;align-items:flex-end!important;font-family:var(--sg-font-body)!important;pointer-events:none!important}.sg-toast>*{pointer-events:auto!important}.sg-progress{all:initial!important;display:flex!important;flex-direction:column!important;background:#0f0f0ff2!important;backdrop-filter:blur(10px)!important;color:var(--sg-text-main)!important;border:1px solid var(--sg-border-gold)!important;border-left:3px solid var(--sg-gold)!important;border-radius:var(--sg-radius-md)!important;padding:16px 20px!important;min-width:340px!important;box-shadow:0 10px 40px #0009!important;font-family:var(--sg-font-body)!important;animation:sg-fade-up .4s cubic-bezier(.18,.89,.32,1.28)!important}.sg-progress-row{all:initial!important;display:flex!important;align-items:center!important;justify-content:space-between!important;gap:12px!important;margin-bottom:12px!important;font-family:var(--sg-font-body)!important}.sg-progress-name{all:initial!important;font-weight:600!important;font-size:14px!important;white-space:nowrap!important;overflow:hidden!important;text-overflow:ellipsis!important;max-width:220px!important;color:var(--sg-text-main)!important;font-family:var(--sg-font-body)!important;letter-spacing:.01em!important}.sg-progress-ctrls{all:initial!important;display:flex!important;gap:6px!important;margin-left:auto!important;font-family:system-ui,sans-serif!important}.sg-progress-bar{all:initial!important;display:block!important;height:6px!important;background:#ffffff1a!important;border-radius:3px!important;overflow:hidden!important;margin-bottom:12px!important}.sg-progress-fill{display:block!important;height:100%!important;width:0;min-width:0!important;background-color:var(--sg-gold)!important;background:linear-gradient(90deg,var(--sg-gold),var(--sg-gold-bright))!important;box-shadow:0 0 10px var(--sg-gold-glow)!important;transition:width .2s linear!important}.sg-progress-status{all:initial!important;display:flex!important;justify-content:space-between!important;font-size:12px!important;font-family:var(--sg-font-body)!important}.sg-progress-status span:first-child{color:var(--sg-text-muted)!important;font-variant-numeric:tabular-nums!important}.sg-progress-status span:last-child{color:#e0e0e0!important}.sg-progress.paused .sg-progress-fill{background:#f59e0b!important}.sg-progress.minimized{padding:10px 14px!important;min-width:auto!important;background:#000!important;border-left-width:1px!important}.sg-progress.minimized .sg-progress-bar,.sg-progress.minimized .sg-progress-status,.sg-progress.minimized .sg-progress-name{display:none!important}.sg-progress.minimized .sg-progress-row{margin-bottom:0!important}.sg-variants{all:initial!important;display:flex!important;flex-wrap:wrap!important;gap:8px!important;margin-top:10px!important;padding-top:10px!important;border-top:1px dashed var(--sg-border-subtle)!important;font-family:var(--sg-font-body)!important}.sg-variant-chip{all:initial!important;display:inline-flex!important;align-items:center!important;justify-content:center!important;font-size:13px!important;font-weight:500!important;color:var(--sg-text-main)!important;font-family:var(--sg-font-body)!important;background:#ffffff1a!important;border:1px solid var(--sg-border-subtle)!important;border-radius:6px!important;padding:6px 14px!important;min-width:40px!important;cursor:pointer!important;transition:var(--sg-transition)!important}.sg-variant-chip:hover{background:#ffffff1a!important;color:var(--sg-gold)!important;border-color:#ffffff26!important;transform:translateY(-1px)!important}.sg-variant-chip:active{transform:translateY(0)!important}.sg-variant-chip:focus{border-color:var(--sg-gold)!important;box-shadow:0 0 8px var(--sg-gold-glow)!important}.sg-progress.minimized .sg-progress-ctrls{margin:0!important;gap:0!important}.sg-progress.minimized .sg-progress-ctrls>*:not(.btn-minimize){display:none!important}@media(max-width:640px){.sg-fab{right:20px!important;bottom:20px!important}.sg-toast{left:16px!important;right:16px!important;bottom:80px!important;max-width:none!important;align-items:stretch!important}.sg-progress{min-width:0!important;width:100%!important}.sg-card{max-height:90vh!important;width:100%!important;margin:8px!important;border-radius:10px!important}.sg-card-body{max-height:calc(90vh - 70px)!important}}';
const STYLES = css;
const ICONS = {
download: `<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/>
<path d="M7 10l5 5 5-5"/>
<path d="M12 15V3"/>
</svg>`,
close: `<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M18 6L6 18M6 6l12 12"/>
</svg>`,
copy: `<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="8" y="8" width="12" height="12" rx="2"/>
<path d="M16 8V6a2 2 0 00-2-2H6a2 2 0 00-2 2v8a2 2 0 002 2h2"/>
</svg>`,
check: `<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M20 6L9 17l-5-5"/>
</svg>`,
pause: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="6" y="4" width="4" height="16"/>
<rect x="14" y="4" width="4" height="16"/>
</svg>`,
play: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="5 3 19 12 5 21 5 3"/>
</svg>`,
cancel: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<path d="M15 9l-6 6M9 9l6 6"/>
</svg>`,
minimize: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M19 9l-7 7-7-7"/>
</svg>`,
maximize: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 15l7-7 7 7"/>
</svg>`
};
const cn = (...classes) => classes.filter(Boolean).join(" ");
function h(tag, attrs, children) {
const el = document.createElement(tag);
if (attrs) {
for (const [key, val] of Object.entries(attrs)) {
if (val == null || val === false) continue;
el.setAttribute(key, val === true ? "" : String(val));
}
}
if (children) {
if (typeof children === "string") {
el.innerHTML = children;
} else {
for (const child of children) {
el.append(typeof child === "string" ? document.createTextNode(child) : child);
}
}
}
return el;
}
async function copyToClipboard(text, btn) {
try {
await navigator.clipboard.writeText(text);
} catch {
const textarea = Object.assign(document.createElement("textarea"), {
value: text,
style: "position:fixed;opacity:0;pointer-events:none"
});
const root = btn.getRootNode();
const target = root instanceof ShadowRoot || root instanceof Document ? root : document.body;
target.appendChild(textarea);
textarea.select();
const ok = document.execCommand("copy");
textarea.remove();
if (!ok) return false;
}
const original = btn.innerHTML;
btn.innerHTML = ICONS.check;
btn.classList.add("copied");
setTimeout(() => {
btn.innerHTML = original;
btn.classList.remove("copied");
}, 1200);
return true;
}
let fabEl = null;
let fabIcon = null;
let fabBadge = null;
function renderFab(container, state2, onClick) {
if (!fabEl) {
fabEl = h("button", { class: "sg-fab", type: "button" });
fabIcon = h("span", { class: "sg-fab-icon" }, ICONS.download);
fabBadge = h("span", { class: "sg-badge" });
fabEl.append(fabIcon, fabBadge);
fabEl.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
onClick();
});
container.appendChild(fabEl);
}
fabEl.className = cn("sg-fab", state2.show && "show", state2.busy && "busy", state2.idle && "idle");
fabEl.disabled = state2.busy;
fabEl.title = `Download media (${state2.count})`;
if (fabBadge) {
fabBadge.className = cn("sg-badge", state2.count > 0 && "show");
fabBadge.textContent = state2.count > 99 ? "99+" : String(state2.count);
}
}
let modalEl = null;
let listEl = null;
let titleEl = null;
let filterCb = null;
function renderModal(container, show, title, items, showFilter, excludeSmall, onClose, onSelect, onFilterChange) {
if (!show) {
if (modalEl) {
modalEl.classList.remove("show");
if (!container.contains(modalEl)) {
modalEl = null;
listEl = null;
}
}
return;
}
if (!modalEl || !container.contains(modalEl)) {
container.innerHTML = "";
modalEl = h("div", { class: "sg-modal" });
modalEl.addEventListener("click", (e) => e.target === modalEl && onClose());
const card = h("div", { class: "sg-card", role: "dialog", "aria-modal": "true" });
const header = h("div", { class: "sg-card-head" });
titleEl = h("div", { class: "sg-card-title" });
header.append(
titleEl,
createIconButton(ICONS.close, "Close (Esc)", onClose)
);
card.appendChild(header);
const body = h("div", { class: "sg-card-body" });
const label = h("label", { class: "sg-option" });
filterCb = h("input", { type: "checkbox" });
filterCb.addEventListener("change", () => onFilterChange(filterCb.checked));
label.append(filterCb, " Exclude small (< 1MB)");
body.appendChild(label);
listEl = h("div", { class: "sg-list" });
body.appendChild(listEl);
card.appendChild(body);
modalEl.appendChild(card);
container.appendChild(modalEl);
}
modalEl.classList.add("show");
if (titleEl) titleEl.textContent = title;
if (filterCb) {
filterCb.checked = excludeSmall || false;
const label = filterCb.parentElement;
if (label) {
label.style.display = showFilter && items.some((i) => i.size != null) ? "flex" : "none";
}
}
if (listEl) {
listEl.innerHTML = "";
if (items.length === 0) {
const empty = h("div", { class: "sg-empty" });
empty.innerHTML = "No media detected.<br><small>Play a video to detect streams.</small>";
listEl.appendChild(empty);
} else {
items.forEach((item) => listEl.appendChild(createItemElement(item, onSelect)));
}
}
}
function createIconButton(icon, title, onClick) {
const btn = h("button", { class: "sg-btn", title, type: "button" }, icon);
btn.addEventListener("click", (e) => {
e.preventDefault();
onClick();
});
return btn;
}
function createItemElement(item, onSelect) {
const el = h("div", { class: "sg-item", role: "button", tabindex: "0" });
const top = h("div", { class: "sg-item-top" });
const titleDiv = h("div", { class: "sg-item-title" });
titleDiv.appendChild(Object.assign(h("span"), { textContent: item.label }));
getBadges(item).forEach((b) => titleDiv.appendChild(b));
top.appendChild(titleDiv);
if (item.size) {
top.appendChild(Object.assign(h("span", { class: "sg-item-size" }), { textContent: formatBytes(item.size) }));
}
const copyBtn = h("button", { class: "sg-copy-btn", title: "Copy URL", type: "button" }, ICONS.copy);
copyBtn.addEventListener("click", (e) => {
e.stopPropagation();
copyToClipboard(item.url, copyBtn);
});
top.appendChild(copyBtn);
el.appendChild(top);
if (item.sublabel) {
el.appendChild(Object.assign(h("div", { class: "sg-item-sub" }), { textContent: item.sublabel }));
}
if (item.pageTitle) {
el.appendChild(Object.assign(h("div", { class: "sg-item-title-context" }), { textContent: item.pageTitle }));
}
if (item.hlsType === "master" && item.variants && item.variants.length > 0) {
const variantsDiv = h("div", { class: "sg-variants" });
const sorted = sortVariantsByQuality(item.variants).slice(0, 6);
for (const v of sorted) {
if (!v.res && !v.peak) continue;
const label = v.res || (v.peak ? `${Math.round(v.peak / 1024)}k` : "?");
const chip = h("button", { class: "sg-variant-chip", type: "button" });
chip.textContent = label;
chip.addEventListener("click", (e) => {
e.stopPropagation();
onSelect({
...item,
kind: "variant",
variant: v,
label,
hlsType: "media",
size: null
// Reset size as variant size is unknown until parsed
});
});
variantsDiv.appendChild(chip);
}
if (variantsDiv.children.length > 0) {
el.appendChild(variantsDiv);
}
}
el.addEventListener("click", (e) => {
if (e.target.closest(".sg-copy-btn") || e.target.closest(".sg-variant-chip")) {
return;
}
onSelect(item);
});
el.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
onSelect(item);
}
});
return el;
}
function getBadges(item) {
const badges = [];
const add = (text, type) => {
const el = h("span", { class: `sg-badge-type ${type}` });
el.textContent = text;
badges.push(el);
};
if (item.kind === "hls") {
if (item.hlsType === "error" || item.hlsType === "invalid") {
add(item.hlsType === "error" ? "Error" : "Invalid", "error");
} else if (item.hlsType === "master") {
add("Master", "master");
} else if (item.hlsType === "media") {
add("Video", "video");
} else if (item.enriching) {
add("...", "analyzing");
} else {
add("HLS", "video");
}
if (item.isLive) add("Live", "live");
if (item.encrypted) add("🔒", "encrypted");
} else if (item.kind === "video") {
add("Direct", "direct");
} else if (item.kind === "variant") {
add("Quality", "video");
}
if (item.isRemote) add("iFrame", "remote");
return badges;
}
class ProgressCard {
constructor(container, title, src, segs = 0) {
this.container = container;
this.title = title;
this.src = src;
this.status = segs ? `${segs} segments` : "Starting...";
this.el = h("div", { class: "sg-progress", id: `sg-progress-${shortId()}` });
const row = h("div", { class: "sg-progress-row" });
row.appendChild(Object.assign(h("div", { class: "sg-progress-name", title: src }), { textContent: title }));
const ctrls = h("div", { class: "sg-progress-ctrls" });
this.minBtn = h("button", { class: "sg-btn sg-btn-sm btn-minimize", title: "Minimize", type: "button" }, ICONS.minimize);
this.minBtn.addEventListener("click", () => this.toggleMinimize());
ctrls.appendChild(this.minBtn);
const cancelBtn = h("button", { class: "sg-btn sg-btn-sm", title: "Cancel", type: "button" }, ICONS.cancel);
cancelBtn.addEventListener("click", () => this.onCancelFn?.());
ctrls.appendChild(cancelBtn);
row.appendChild(ctrls);
this.el.appendChild(row);
const bar = h("div", { class: "sg-progress-bar" });
this.fillEl = h("div", { class: "sg-progress-fill" });
bar.appendChild(this.fillEl);
this.el.appendChild(bar);
const statusRow = h("div", { class: "sg-progress-status" });
this.statusEl = h("span");
this.statusEl.textContent = this.status;
this.percentEl = h("span");
this.percentEl.textContent = "0%";
statusRow.append(this.statusEl, this.percentEl);
this.el.appendChild(statusRow);
container.appendChild(this.el);
}
el;
fillEl;
statusEl;
percentEl;
pauseBtn = null;
minBtn;
minimized = false;
paused = false;
percent = 0;
status;
onStop;
onCancelFn;
toggleMinimize() {
this.minimized = !this.minimized;
this.minBtn.innerHTML = this.minimized ? ICONS.maximize : ICONS.minimize;
this.minBtn.title = this.minimized ? "Expand" : "Minimize";
this.updateClass();
}
updateClass() {
this.el.className = cn("sg-progress", this.minimized && "minimized", this.paused && "paused");
}
update(percent, text = "") {
this.percent = Math.max(0, Math.min(100, percent));
if (text) this.status = text;
this.fillEl.style.setProperty("width", `${this.percent}%`, "important");
this.statusEl.textContent = this.status;
this.percentEl.textContent = `${Math.floor(this.percent)}%`;
}
done(ok = true, msg) {
this.fillEl.style.width = "100%";
this.fillEl.classList.add(ok ? "success" : "error");
this.statusEl.textContent = msg || (ok ? "Complete ✓" : "Failed ✗");
this.percentEl.textContent = "100%";
setTimeout(() => this.remove(), 2500);
}
remove() {
this.el.remove();
}
setOnStop(fn) {
this.onStop = fn;
if (this.pauseBtn) return;
this.pauseBtn = h("button", { class: "sg-btn sg-btn-sm", title: "Pause", type: "button" }, ICONS.pause);
this.pauseBtn.addEventListener("click", () => {
if (!this.onStop) return;
const result = this.onStop();
this.paused = result === "paused";
this.pauseBtn.innerHTML = this.paused ? ICONS.play : ICONS.pause;
this.pauseBtn.title = this.paused ? "Resume" : "Pause";
this.updateClass();
});
this.minBtn.before(this.pauseBtn);
}
setOnCancel(fn) {
this.onCancelFn = fn;
}
}
function createProgressCard(container, title, src, segs = 0) {
return new ProgressCard(container, title, src, segs);
}
let mounted = false;
let fabContainer = null;
let modalContainer = null;
let toastContainer = null;
const fabState = { show: false, busy: false, idle: false, count: 0 };
const modalState = { show: false, title: "Select Media", items: [] };
let idleTimer = null;
let pendingPicker = null;
let onFabClick = null;
let onItemSelected = null;
function mountUI() {
if (!CFG.IS_TOP || mounted) return;
if (!document.body) {
const event = document.readyState === "loading" ? "DOMContentLoaded" : "load";
window.addEventListener(event, () => mountUI(), { once: true });
return;
}
const host = document.createElement("div");
host.id = "sg-host";
host.setAttribute("data-darkreader-ignore", "true");
host.classList.add("darkreader");
Object.assign(host.style, {
position: "fixed",
top: "0",
left: "0",
width: "0",
height: "0",
zIndex: "2147483647",
pointerEvents: "none",
// Allow clicks to pass through the wrapper
colorScheme: "dark"
});
const shadow = host.attachShadow({ mode: "open" });
const styleEl = document.createElement("style");
styleEl.classList.add("darkreader");
styleEl.textContent = STYLES;
shadow.append(styleEl);
fabContainer = document.createElement("div");
fabContainer.id = "sg-fab-container";
fabContainer.style.pointerEvents = "auto";
modalContainer = document.createElement("div");
modalContainer.id = "sg-modal-container";
modalContainer.style.pointerEvents = "auto";
toastContainer = document.createElement("div");
toastContainer.id = "sg-toast-container";
toastContainer.className = "sg-toast";
shadow.append(fabContainer, modalContainer, toastContainer);
document.documentElement.append(host);
fabContainer.addEventListener("mouseenter", clearIdle);
fabContainer.addEventListener("mouseleave", resetIdle);
document.addEventListener("keydown", (e) => e.key === "Escape" && modalState.show && closeModal());
mounted = true;
render();
}
function ensureMounted() {
if (!CFG.IS_TOP) return false;
if (!mounted) mountUI();
return mounted;
}
function render() {
if (!mounted || !fabContainer || !modalContainer) return;
renderFab(fabContainer, fabState, handleFabClick);
const items = modalState.show ? getFilteredItems() : [];
renderModal(
modalContainer,
modalState.show,
modalState.title,
items,
pendingPicker?.filterable ?? true,
state.excludeSmall,
closeModal,
handleItemSelect,
handleFilterChange
);
}
function showFab() {
if (!CFG.IS_TOP) return;
fabState.show = true;
fabState.count = state.validCount;
if (ensureMounted()) {
render();
resetIdle();
}
}
function setFabBusy(busy) {
fabState.busy = busy;
if (mounted) render();
}
function updateBadge() {
fabState.count = state.validCount;
if (mounted) render();
}
function resetIdle() {
clearIdle();
idleTimer = setTimeout(() => {
fabState.idle = true;
if (mounted) render();
}, CFG.UI_IDLE_MS);
}
function clearIdle() {
fabState.idle = false;
if (idleTimer) clearTimeout(idleTimer);
if (mounted) render();
}
function getFilteredItems() {
const items = modalState.items.length > 0 ? modalState.items : state.getAllItems();
return state.filterItems(items);
}
function openModal(title = "Select Media", items) {
if (!ensureMounted()) return;
modalState.show = true;
modalState.title = title;
modalState.items = items ?? [];
render();
}
function closeModal() {
modalState.show = false;
modalState.items = [];
if (mounted) render();
if (pendingPicker) {
pendingPicker.resolve(null);
pendingPicker = null;
}
}
function handleItemSelect(item) {
closeModal();
if (pendingPicker) {
pendingPicker.resolve(item);
pendingPicker = null;
} else {
onItemSelected?.(item);
}
}
function handleFilterChange(checked) {
state.setExcludeSmall(checked);
render();
}
function handleFabClick() {
if (!mounted) return;
clearIdle();
resetIdle();
onFabClick?.();
}
function pickFromList(items, { title = "Select Media", filterable = true } = {}) {
if (!ensureMounted()) return Promise.resolve(null);
return new Promise((resolve) => {
pendingPicker = { resolve, title, filterable };
openModal(title, items);
});
}
function createProgress(title, src, segs = 0) {
if (!ensureMounted() || !toastContainer) {
return { update() {
}, done() {
}, remove() {
}, setOnStop() {
}, setOnCancel() {
} };
}
return createProgressCard(toastContainer, title, src, segs);
}
function registerMenuCommands() {
if (!CFG.IS_TOP) return;
GM_registerMenuCommand("Show Download Panel", () => {
ensureMounted();
showFab();
handleFabClick();
});
GM_registerMenuCommand("Clear Cache", () => {
state.clear();
updateBadge();
GM_notification({ text: "Cache cleared", title: "StreamGrabber", timeout: 2e3 });
});
}
function setUICallbacks(cbs) {
if (cbs.onFabClick) onFabClick = cbs.onFabClick;
if (cbs.onItemSelected) onItemSelected = cbs.onItemSelected;
}
function refreshUI() {
if (!CFG.IS_TOP) return;
updateBadge();
if (mounted && modalState.show) render();
}
class RemoteProgressCard {
id;
bus;
onStopFn;
onCancelFn;
constructor(title, src) {
this.id = shortId();
this.bus = MessageBus.get();
this.bus.sendToTop("SG_PROGRESS_START", {
id: this.id,
title,
src
});
this.handleControl = this.handleControl.bind(this);
this.bus.on("SG_CMD_CONTROL", this.handleControl);
}
handleControl(payload) {
if (payload.id !== this.id) return;
const action = payload.action;
if (action === "stop" && this.onStopFn) {
this.onStopFn();
} else if (action === "cancel" && this.onCancelFn) {
this.onCancelFn();
}
}
update(percent, text) {
this.bus.sendToTop("SG_PROGRESS_UPDATE", {
id: this.id,
p: percent,
txt: text || ""
});
}
done(ok = true, msg = "") {
this.bus.sendToTop("SG_PROGRESS_DONE", {
id: this.id,
ok,
msg
});
this.cleanup();
}
remove() {
this.cleanup();
}
setOnStop(fn) {
this.onStopFn = fn;
}
setOnCancel(fn) {
this.onCancelFn = fn;
}
cleanup() {
this.bus.off("SG_CMD_CONTROL", this.handleControl);
}
}
const remoteJobs = /* @__PURE__ */ new Map();
function init() {
console.log(`[SG] StreamGrabber v${GM_info?.script?.version || "2.1.3"} initializing...`, {
isTop: CFG.IS_TOP,
readyState: document.readyState,
href: location.href.slice(0, 100)
});
setUICallbacks({
onFabClick: handleFabClickAction,
onItemSelected: handleItemAction
});
if (CFG.IS_TOP) {
mountUI();
registerMenuCommands();
}
initMessaging();
if (CFG.IS_TOP) {
setupTopHandlers();
} else {
setupChildHandlers();
}
setItemDetectedCallback((item) => {
if (CFG.IS_TOP) {
console.log("[SG] Detected:", item.kind, item.url.slice(0, 60));
showFab();
updateBadge();
if (item.kind === "hls") {
queueEnrich(item, () => refreshUI());
}
} else {
console.log("[SG] [iframe] Forwarding detection:", item.kind, item.url.slice(0, 60));
sendDetection(item);
}
});
state.events.itemAdded.subscribe(() => {
showFab();
updateBadge();
});
state.events.updated.subscribe(() => {
refreshUI();
});
initDetection();
console.log("[SG] Initialization complete", { isTop: CFG.IS_TOP });
}
function setupTopHandlers() {
const bus = MessageBus.get();
setupNavigationHandlers();
bus.on("SG_DETECT", (payload, source) => {
const remoteItem = payload.item;
if (!remoteItem) return;
remoteItem.remoteWin = source;
remoteItem.isRemote = true;
console.log("[SG] Received detection from iframe:", remoteItem.kind, remoteItem.url.slice(0, 60));
if (state.addItem(remoteItem)) {
showFab();
updateBadge();
if (remoteItem.kind === "hls") {
queueEnrich(remoteItem, () => refreshUI());
}
}
});
bus.on("SG_PROGRESS_START", (payload, source) => {
const { id, title, src } = payload;
if (remoteJobs.has(id)) return;
try {
const card = createProgress(title, src);
card.setOnStop(() => {
MessageBus.get().send("SG_CMD_CONTROL", { id, action: "stop" }, source);
return "paused";
});
card.setOnCancel(() => {
MessageBus.get().send("SG_CMD_CONTROL", { id, action: "cancel" }, source);
card.remove();
remoteJobs.delete(id);
});
remoteJobs.set(id, card);
} catch (e) {
console.error("[SG] Failed to create remote progress card:", e);
}
});
bus.on("SG_PROGRESS_UPDATE", (payload) => {
const { id, p, txt } = payload;
remoteJobs.get(id)?.update(p, txt);
});
bus.on("SG_PROGRESS_DONE", (payload) => {
const { id, ok, msg } = payload;
const card = remoteJobs.get(id);
if (card) {
card.done(ok, msg);
setTimeout(() => remoteJobs.delete(id), 2500);
}
});
bus.on("SG_CMD_PICK", (payload, source) => {
const { id, items, title } = payload;
pickFromList(items, { title, filterable: true }).then((selected) => {
MessageBus.get().send("SG_CMD_PICK_RESULT", { id, item: selected }, source);
});
});
}
function setupNavigationHandlers() {
window.addEventListener("popstate", () => {
console.log("[SG] Navigation detected (popstate), clearing state...");
state.clear();
});
window.addEventListener("hashchange", () => {
console.log("[SG] Hash change detected, clearing state...");
state.clear();
});
const originalPush = history.pushState;
history.pushState = function(...args) {
const result = originalPush.apply(this, args);
console.log("[SG] Navigation detected (pushState), clearing state...");
state.clear();
return result;
};
const originalReplace = history.replaceState;
history.replaceState = function(...args) {
const result = originalReplace.apply(this, args);
console.log("[SG] Navigation detected (replaceState), clearing state...");
state.clear();
return result;
};
}
function setupChildHandlers() {
const bus = MessageBus.get();
bus.on("SG_CMD_DOWNLOAD", (payload) => {
const { url, kind, variant, pageTitle } = payload;
console.log("[SG] [iframe] Received download command:", { url, kind });
handleDownloadCommand(url, kind, variant, pageTitle);
});
bus.on("SG_CMD_PICK_RESULT", (payload) => {
const { id, item } = payload;
resolvePickerRequest(id, item);
});
}
async function handleFabClickAction() {
setFabBusy(true);
try {
const items = state.getFilteredItems();
if (items.length === 0) {
alert("No media detected yet. Try playing a video first.");
return;
}
const selected = await pickFromList(items, {
title: "Select Media",
filterable: true
});
if (!selected) return;
await handleItemAction(selected);
} catch (e) {
alertError(e);
} finally {
setFabBusy(false);
}
}
async function handleItemAction(item) {
try {
await handleItem(
item,
{
createCard: createProgress,
pickVariant: (items) => pickFromList(items, { title: "Select Quality", filterable: true }),
setBusy: setFabBusy
}
);
} catch (e) {
alertError(e);
}
}
async function handleDownloadCommand(url, kind, variant, pageTitle) {
console.log("[SG] [iframe] Received download command:", { url, kind });
try {
const createProxyCard = (title, src) => {
return new RemoteProgressCard(title, src);
};
if (kind === "hls" || kind === "variant") {
await downloadHls(
url,
variant,
{
createCard: createProxyCard,
pickVariant: async (items) => {
return new Promise((resolve) => {
const pickId = shortId();
registerPickerRequest(pickId, resolve);
MessageBus.get().sendToTop("SG_CMD_PICK", {
id: pickId,
items,
title: "Select Quality"
});
});
},
setBusy: () => {
}
},
pageTitle
);
} else if (kind === "video") {
await downloadDirect(url, {
createCard: createProxyCard,
pickVariant: async () => null,
setBusy: () => {
}
}, pageTitle);
}
} catch (e) {
console.error("[SG] [iframe] Download error:", e);
}
}
init();
})();