p2p-media-loader-core
Version:
P2P Media Loader core functionality
1,537 lines • 108 kB
JavaScript
//#region \0rolldown/runtime.js
var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescriptor, r = Object.getOwnPropertyNames, i = Object.getPrototypeOf, a = Object.prototype.hasOwnProperty, o = (e, t) => () => (t || (e((t = { exports: {} }).exports, t), e = null), t.exports), s = (e, n) => {
let r = {};
for (var i in e) t(r, i, {
get: e[i],
enumerable: !0
});
return n || t(r, Symbol.toStringTag, { value: "Module" }), r;
}, c = (e, i, o, s) => {
if (i && typeof i == "object" || typeof i == "function") for (var c = r(i), l = 0, u = c.length, d; l < u; l++) d = c[l], !a.call(e, d) && d !== o && t(e, d, {
get: ((e) => i[e]).bind(null, d),
enumerable: !(s = n(i, d)) || s.enumerable
});
return e;
}, l = (n, r, a) => (a = n == null ? {} : e(i(n)), c(r || !n || !n.__esModule ? t(a, "default", {
value: n,
enumerable: !0
}) : a, n)), u = class extends Error {
type;
cause;
constructor(e, t, n) {
super(t), this.type = e, this.cause = n;
}
}, d = class extends u {
name = "PeerError";
}, f = class extends u {
name = "PeerWarning";
}, p = class extends u {
name = "TrackerError";
}, m = class extends u {
name = "TrackerWarning";
}, h = class extends u {
name = "PeerConnectError";
}, g = class extends u {
name = "RequestError";
timestamp;
constructor(e, t, n) {
super(e, t, n), this.timestamp = performance.now();
}
}, _ = class extends u {
name = "CoreRequestError";
}, ee = class {
aborted = !1;
#e = /* @__PURE__ */ new Set();
addEventListener(e, t) {
this.#e.add(t);
}
removeEventListener(e, t) {
this.#e.delete(t);
}
dispatchEvent(e) {
this.aborted = !0;
for (let e of this.#e) try {
e();
} catch {}
this.#e.clear();
}
}, te = class {
signal = new ee();
abort() {
this.signal.dispatchEvent("abort");
}
}, ne = typeof AbortController < "u", re = ne ? AbortController : te, ie = class {
request;
httpConfig;
abortController = new re();
expectedBytesLength;
requestByteRange;
onChunkDownloaded;
isAborted() {
return this.abortController.signal.aborted;
}
constructor(e, t, n) {
this.request = e, this.httpConfig = t, this.onChunkDownloaded = n.getEventDispatcher("onChunkDownloaded");
let { byteRange: r } = this.request.segment;
r && (this.requestByteRange = { ...r });
}
execute() {
let e = {
onAbort: () => this.abortController.abort(),
notReceivingBytesTimeoutMs: this.httpConfig.httpNotReceivingBytesTimeoutMs
};
if (this.request.tryCompleteByLoadedBytes({ downloadSource: "http" }, e, this.httpConfig.validateHTTPSegment, "http-segment-validation-failed")) return;
this.request.loadedBytes !== 0 && (this.requestByteRange = this.requestByteRange ?? { start: 0 }, this.requestByteRange.start = this.requestByteRange.start + this.request.loadedBytes), this.request.totalBytes && (this.expectedBytesLength = this.request.totalBytes - this.request.loadedBytes);
let t = this.request.start({ downloadSource: "http" }, e);
this.fetch(t);
}
async fetch(e) {
let { segment: t } = this.request, n;
if (this.isAborted()) return;
let r = () => {
try {
n?.cancel().catch(() => {});
} catch {}
};
this.abortController.signal.addEventListener("abort", r);
let i = ne ? this.abortController.signal : void 0;
try {
let r = await this.httpConfig.httpRequestSetup?.(t.url, t.byteRange, i, this.requestByteRange);
if (this.isAborted()) throw new DOMException("Request aborted", "AbortError");
if (!r) {
let e = new Headers();
this.requestByteRange && e.set("Range", `bytes=${this.requestByteRange.start}-${this.requestByteRange.end ?? ""}`);
let n = { headers: e };
i && (n.signal = i), r = new Request(t.url, n);
}
if (this.isAborted()) throw new DOMException("Request aborted before request fetch", "AbortError");
let a = await window.fetch(r);
if (this.isAborted()) throw new DOMException("Request aborted", "AbortError");
if (this.handleResponseHeaders(a), e.firstBytesReceived(), !a.body || typeof a.body.getReader != "function") {
let n = await a.arrayBuffer();
if (this.isAborted()) throw new DOMException("Request aborted", "AbortError");
let r = new Uint8Array(n);
e.addLoadedChunk(r), this.onChunkDownloaded(r.byteLength, "http", void 0, t.stream.type, this.request.infoHash);
} else {
let r = a.body.getReader();
for (n = r;;) {
if (this.isAborted()) throw new DOMException("Request aborted", "AbortError");
let { done: n, value: i } = await r.read();
if (n) break;
if (this.isAborted()) throw new DOMException("Request aborted", "AbortError");
e.addLoadedChunk(i), this.onChunkDownloaded(i.byteLength, "http", void 0, t.stream.type, this.request.infoHash);
}
}
if (this.isAborted()) throw new DOMException("Request aborted", "AbortError");
if (this.request.totalBytes !== void 0 && this.request.loadedBytes !== this.request.totalBytes) throw new g("http-bytes-mismatch", `HTTP response truncated: received ${this.request.loadedBytes} of ${this.request.totalBytes} bytes`);
let o = await this.request.validateData(this.httpConfig.validateHTTPSegment);
if (this.isAborted()) throw new DOMException("Request aborted", "AbortError");
if (!o) throw this.request.clearLoadedBytes(), new g("http-segment-validation-failed");
e.completeOnSuccess();
} catch (t) {
this.handleError(t, e);
} finally {
this.abortController.signal.removeEventListener("abort", r);
}
}
handleResponseHeaders(e) {
if (!e.ok) throw e.status === 406 || e.status === 416 ? (this.request.clearLoadedBytes(), new g("http-bytes-mismatch", e.statusText)) : new g("http-error", e.statusText);
let { requestByteRange: t } = this;
if (t) if (e.status === 200) {
if (this.request.segment.byteRange) throw new g("http-unexpected-status-code");
this.request.clearLoadedBytes();
} else {
if (e.status !== 206) throw new g("http-unexpected-status-code", e.statusText);
let n = e.headers.get("Content-Length");
if (n && this.expectedBytesLength !== void 0 && this.expectedBytesLength !== +n) throw this.request.clearLoadedBytes(), new g("http-bytes-mismatch", e.statusText);
let r = e.headers.get("Content-Range"), i = r ? oe(r) : void 0;
if (i) {
let { from: n, to: r } = i, a = r !== void 0 && n !== void 0 ? r - n + 1 : void 0;
if (a !== void 0 && this.expectedBytesLength !== a || n !== void 0 && t.start !== n || r !== void 0 && t.end !== void 0 && t.end !== r) throw this.request.clearLoadedBytes(), new g("http-bytes-mismatch", e.statusText);
}
}
if (e.status === 200 && this.request.totalBytes === void 0) {
let t = e.headers.get("Content-Length");
t && this.request.setTotalBytes(+t);
}
}
handleError(e, t) {
if (!this.isAborted() && e instanceof Error) {
let n = e instanceof g ? e : new g("http-error", e.message);
t.failWithError(n);
}
}
}, ae = /^bytes (?:(?:(\d+)|)-(?:(\d+)|)|\*)\/(?:(\d+)|\*)$/;
function oe(e) {
let t = ae.exec(e.trim());
if (!t) return;
let [, n, r, i] = t;
return {
from: n ? parseInt(n) : void 0,
to: r ? parseInt(r) : void 0,
total: i ? parseInt(i) : void 0
};
}
//#endregion
//#region ../../node_modules/.pnpm/ms@2.1.3/node_modules/ms/index.js
var se = /* @__PURE__ */ o(((e, t) => {
var n = 1e3, r = n * 60, i = r * 60, a = i * 24, o = a * 7, s = a * 365.25;
t.exports = function(e, t) {
t ||= {};
var n = typeof e;
if (n === "string" && e.length > 0) return c(e);
if (n === "number" && isFinite(e)) return t.long ? u(e) : l(e);
throw Error("val is not a non-empty string or a valid number. val=" + JSON.stringify(e));
};
function c(e) {
if (e = String(e), !(e.length > 100)) {
var t = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);
if (t) {
var c = parseFloat(t[1]);
switch ((t[2] || "ms").toLowerCase()) {
case "years":
case "year":
case "yrs":
case "yr":
case "y": return c * s;
case "weeks":
case "week":
case "w": return c * o;
case "days":
case "day":
case "d": return c * a;
case "hours":
case "hour":
case "hrs":
case "hr":
case "h": return c * i;
case "minutes":
case "minute":
case "mins":
case "min":
case "m": return c * r;
case "seconds":
case "second":
case "secs":
case "sec":
case "s": return c * n;
case "milliseconds":
case "millisecond":
case "msecs":
case "msec":
case "ms": return c;
default: return;
}
}
}
}
function l(e) {
var t = Math.abs(e);
return t >= a ? Math.round(e / a) + "d" : t >= i ? Math.round(e / i) + "h" : t >= r ? Math.round(e / r) + "m" : t >= n ? Math.round(e / n) + "s" : e + "ms";
}
function u(e) {
var t = Math.abs(e);
return t >= a ? d(e, t, a, "day") : t >= i ? d(e, t, i, "hour") : t >= r ? d(e, t, r, "minute") : t >= n ? d(e, t, n, "second") : e + " ms";
}
function d(e, t, n, r) {
var i = t >= n * 1.5;
return Math.round(e / n) + " " + r + (i ? "s" : "");
}
})), ce = /* @__PURE__ */ o(((e, t) => {
function n(e) {
n.debug = n, n.default = n, n.coerce = c, n.disable = o, n.enable = i, n.enabled = s, n.humanize = se(), n.destroy = l, Object.keys(e).forEach((t) => {
n[t] = e[t];
}), n.names = [], n.skips = [], n.formatters = {};
function t(e) {
let t = 0;
for (let n = 0; n < e.length; n++) t = (t << 5) - t + e.charCodeAt(n), t |= 0;
return n.colors[Math.abs(t) % n.colors.length];
}
n.selectColor = t;
function n(e) {
let t, i = null, a, o;
function s(...e) {
if (!s.enabled) return;
let r = s, i = Number(/* @__PURE__ */ new Date());
r.diff = i - (t || i), r.prev = t, r.curr = i, t = i, e[0] = n.coerce(e[0]), typeof e[0] != "string" && e.unshift("%O");
let a = 0;
e[0] = e[0].replace(/%([a-zA-Z%])/g, (t, i) => {
if (t === "%%") return "%";
a++;
let o = n.formatters[i];
if (typeof o == "function") {
let n = e[a];
t = o.call(r, n), e.splice(a, 1), a--;
}
return t;
}), n.formatArgs.call(r, e), (r.log || n.log).apply(r, e);
}
return s.namespace = e, s.useColors = n.useColors(), s.color = n.selectColor(e), s.extend = r, s.destroy = n.destroy, Object.defineProperty(s, "enabled", {
enumerable: !0,
configurable: !1,
get: () => i === null ? (a !== n.namespaces && (a = n.namespaces, o = n.enabled(e)), o) : i,
set: (e) => {
i = e;
}
}), typeof n.init == "function" && n.init(s), s;
}
function r(e, t) {
let r = n(this.namespace + (t === void 0 ? ":" : t) + e);
return r.log = this.log, r;
}
function i(e) {
n.save(e), n.namespaces = e, n.names = [], n.skips = [];
let t = (typeof e == "string" ? e : "").trim().replace(/\s+/g, ",").split(",").filter(Boolean);
for (let e of t) e[0] === "-" ? n.skips.push(e.slice(1)) : n.names.push(e);
}
function a(e, t) {
let n = 0, r = 0, i = -1, a = 0;
for (; n < e.length;) if (r < t.length && (t[r] === e[n] || t[r] === "*")) t[r] === "*" ? (i = r, a = n, r++) : (n++, r++);
else if (i !== -1) r = i + 1, a++, n = a;
else return !1;
for (; r < t.length && t[r] === "*";) r++;
return r === t.length;
}
function o() {
let e = [...n.names, ...n.skips.map((e) => "-" + e)].join(",");
return n.enable(""), e;
}
function s(e) {
for (let t of n.skips) if (a(e, t)) return !1;
for (let t of n.names) if (a(e, t)) return !0;
return !1;
}
function c(e) {
return e instanceof Error ? e.stack || e.message : e;
}
function l() {
console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");
}
return n.enable(n.load()), n;
}
t.exports = n;
})), v = /* @__PURE__ */ l((/* @__PURE__ */ o(((e, t) => {
e.formatArgs = r, e.save = i, e.load = a, e.useColors = n, e.storage = o(), e.destroy = (() => {
let e = !1;
return () => {
e || (e = !0, console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."));
};
})(), e.colors = /* @__PURE__ */ "#0000CC.#0000FF.#0033CC.#0033FF.#0066CC.#0066FF.#0099CC.#0099FF.#00CC00.#00CC33.#00CC66.#00CC99.#00CCCC.#00CCFF.#3300CC.#3300FF.#3333CC.#3333FF.#3366CC.#3366FF.#3399CC.#3399FF.#33CC00.#33CC33.#33CC66.#33CC99.#33CCCC.#33CCFF.#6600CC.#6600FF.#6633CC.#6633FF.#66CC00.#66CC33.#9900CC.#9900FF.#9933CC.#9933FF.#99CC00.#99CC33.#CC0000.#CC0033.#CC0066.#CC0099.#CC00CC.#CC00FF.#CC3300.#CC3333.#CC3366.#CC3399.#CC33CC.#CC33FF.#CC6600.#CC6633.#CC9900.#CC9933.#CCCC00.#CCCC33.#FF0000.#FF0033.#FF0066.#FF0099.#FF00CC.#FF00FF.#FF3300.#FF3333.#FF3366.#FF3399.#FF33CC.#FF33FF.#FF6600.#FF6633.#FF9900.#FF9933.#FFCC00.#FFCC33".split(".");
function n() {
if (typeof window < "u" && window.process && (window.process.type === "renderer" || window.process.__nwjs)) return !0;
if (typeof navigator < "u" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) return !1;
let e;
return typeof document < "u" && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || typeof window < "u" && window.console && (window.console.firebug || window.console.exception && window.console.table) || typeof navigator < "u" && navigator.userAgent && (e = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(e[1], 10) >= 31 || typeof navigator < "u" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/);
}
function r(e) {
if (e[0] = (this.useColors ? "%c" : "") + this.namespace + (this.useColors ? " %c" : " ") + e[0] + (this.useColors ? "%c " : " ") + "+" + t.exports.humanize(this.diff), !this.useColors) return;
let n = "color: " + this.color;
e.splice(1, 0, n, "color: inherit");
let r = 0, i = 0;
e[0].replace(/%[a-zA-Z%]/g, (e) => {
e !== "%%" && (r++, e === "%c" && (i = r));
}), e.splice(i, 0, n);
}
e.log = console.debug || console.log || (() => {});
function i(t) {
try {
t ? e.storage.setItem("debug", t) : e.storage.removeItem("debug");
} catch {}
}
function a() {
let t;
try {
t = e.storage.getItem("debug") || e.storage.getItem("DEBUG");
} catch {}
return !t && typeof process < "u" && "env" in process && (t = process.env.DEBUG), t;
}
function o() {
try {
return localStorage;
} catch {}
}
t.exports = ce()(e);
var { formatters: s } = t.exports;
s.j = function(e) {
try {
return JSON.stringify(e);
} catch (e) {
return "[UnexpectedJSONParseError]: " + e.message;
}
};
})))(), 1), y = /* @__PURE__ */ function(e) {
return e[e.SegmentsAnnouncement = 0] = "SegmentsAnnouncement", e[e.SegmentRequest = 1] = "SegmentRequest", e[e.SegmentData = 2] = "SegmentData", e[e.SegmentDataSendingCompleted = 3] = "SegmentDataSendingCompleted", e[e.SegmentAbsent = 4] = "SegmentAbsent", e[e.CancelSegmentRequest = 5] = "CancelSegmentRequest", e;
}({});
//#endregion
//#region src/utils/utils.ts
function le() {
let e, t;
return {
promise: new Promise((n, r) => {
e = n, t = r;
}),
resolve: e,
reject: t
};
}
function ue(e) {
Promise.resolve().then(e);
}
function de(e, t) {
t ??= e.reduce((e, t) => e + t.byteLength, 0);
let n = new Uint8Array(t), r = 0;
for (let t of e) n.set(t, r), r += t.byteLength;
return n;
}
function fe(e) {
return e[Math.floor(Math.random() * e.length)];
}
function pe(e, t) {
if (e.length === 0) throw Error("Cannot get item from empty array");
if (e.length === 1) return e[0];
let n = 0, r = e.map((e) => {
let r = t(e);
return n += r, r;
}), i = Math.random() * n;
for (let t = 0; t < e.length; t++) if (i -= r[t], i <= 0) return e[t];
return e[e.length - 1];
}
function me(e) {
return new TextEncoder().encode(e);
}
function* he(e) {
for (let t = e.length - 1; t >= 0; t--) yield e[t];
}
function ge(e) {
return !!e && typeof e == "object" && !Array.isArray(e);
}
function _e(e) {
return Array.isArray(e);
}
function ve(e) {
function t(e) {
if (ge(e)) {
let n = {};
return Object.keys(e).forEach((r) => {
if (e[r] !== void 0) {
let i = t(e[r]);
i !== void 0 && (n[r] = i);
}
}), n;
} else return e;
}
return t(e);
}
function b(e) {
if (_e(e)) return e.map((e) => b(e));
if (ge(e)) {
let t = {};
for (let n of Object.keys(e)) t[n] = b(e[n]);
return t;
} else return e;
}
function ye(e) {
for (let t = e.length - 1; t > 0; t--) {
let n = Math.floor(Math.random() * (t + 1));
[e[t], e[n]] = [e[n], e[t]];
}
return e;
}
function x(e, t, n = {}) {
return typeof e != "object" || !e || typeof t != "object" || !t || Object.keys(t).forEach((r) => {
let i = typeof r == "symbol" ? r.toString() : String(r);
if (r === "__proto__" || r === "constructor" || r === "prototype") throw Error(`Attempt to modify restricted property '${i}'`);
let a = t[r], o = n[r];
r in e && (a === void 0 ? e[r] = o === void 0 ? void 0 : o : e[r] = a);
}), e;
}
function S(e) {
let { defaultConfig: t, baseConfig: n = {}, specificStreamConfig: r = {} } = e, i = b({
...t,
...n,
...r
}), a = Object.keys(t), o = {};
return a.forEach((e) => {
e in i && (o[e] = i[e]);
}), o;
}
//#endregion
//#region src/p2p/commands/binary-serialization.ts
var be = new TextEncoder(), xe = new TextDecoder("utf8"), C = /* @__PURE__ */ function(e) {
return e[e.Min = -1] = "Min", e[e.Int = 0] = "Int", e[e.SimilarIntArray = 1] = "SimilarIntArray", e[e.String = 2] = "String", e[e.Max = 3] = "Max", e;
}({});
function Se(e) {
if (e === 0) return 1;
let t = Math.floor(Math.log2(Math.abs(e))) + 2;
return Math.ceil(t / 8);
}
function Ce(e) {
let t = e < 0, n = Se(e), r = new Uint8Array(n);
e = Math.abs(e);
for (let t = 0; t < n; t++) {
let i = 8 * (n - 1 - t);
r[t] = Math.floor(e / 2 ** i) & 255;
}
return t && (r[0] |= 128), r;
}
function we(e) {
let t = e.length, n = (e, n) => e * 2 ** (8 * (t - 1 - n)), r = n(e[0] & 127, 0);
for (let i = 1; i < t; i++) r += n(e[i], i);
return (e[0] & 128) >> 7 && (r = -r), r;
}
function Te(e) {
let t = Ce(e), n = 0 | t.length;
return new Uint8Array([n, ...t]);
}
function Ee(e) {
if (e.length === 0) throw Error("Buffer is too short");
let t = e[0];
if (t >> 4) throw Error("Trying to deserialize integer with invalid serialized item code");
let n = t & 15;
if (n === 0) throw Error("Invalid integer: zero byte length");
if (n > 7) throw Error("Invalid integer: byte length exceeds safe integer limit");
let r = 1 + n;
if (e.length < r) throw Error("Buffer is too short");
return {
number: we(e.subarray(1, r)),
byteLength: n + 1
};
}
function De(e) {
let t = /* @__PURE__ */ new Map();
for (let n of e) {
let e = n & 255, r = n - e, i = t.get(r) ?? new w();
i.length || t.set(r, i), i.push(e);
}
let n = new w();
n.push([16, t.size]);
for (let [e, r] of t) {
let { length: t } = r, i = e + (t & 255);
r.unshift(Te(i)), n.push(r.getBuffer());
}
return n.getBuffer();
}
function Oe(e) {
if (e.length < 2) throw Error("Buffer is too short");
let [t, n] = e;
if (t >> 4 != 1) throw Error("Trying to deserialize similar int array with invalid serialized item code");
let r = 2, i = [];
for (let t = 0; t < n; t++) {
let { number: t, byteLength: n } = Ee(e.subarray(r));
r += n;
let a = t & 255, o = a === 0 ? 256 : a, s = t - a;
if (r + o > e.length) throw Error("Malformed similar int array: buffer too short");
for (let t = 0; t < o; t++) {
let t = e[r];
i.push(s + t), r++;
}
}
return {
numbers: i,
byteLength: r
};
}
function ke(e) {
let t = be.encode(e), { length: n } = t;
if (n > 4095) throw Error("String exceeds maximum length of 4095 bytes");
let r = new w();
return r.push([32 | n >> 8 & 15, n & 255]), r.push(t), r.getBuffer();
}
function Ae(e) {
if (e.length < 2) throw Error("Buffer is too short");
let [t, n] = e;
if (t >> 4 != 2) throw Error("Trying to deserialize bytes (sting) with invalid serialized item code.");
let r = (t & 15) << 8 | n;
if (e.length < r + 2) throw Error("Malformed string: buffer too short");
let i = e.subarray(2, r + 2);
return {
string: xe.decode(i),
byteLength: r + 2
};
}
var w = class {
#e = [];
#t = 0;
push(e) {
this.#n(e, "end");
}
unshift(e) {
this.#n(e, "start");
}
#n(e, t) {
let n;
n = e instanceof Uint8Array ? e : Array.isArray(e) ? new Uint8Array(e) : new Uint8Array([e]), this.#t += n.length, this.#e[t === "start" ? "unshift" : "push"](n);
}
getBytesChunks() {
return this.#e;
}
getBuffer() {
return de(this.#e, this.#t);
}
get length() {
return this.#t;
}
}, T = 4, E = N("cstr", T), D = N("cend", T), O = N("dstr", T), k = N("dend", T), je = [E, O], Me = [D, k], A = E.length + D.length;
function Ne(e) {
if (e.length < A) return !1;
let { length: t } = E, n = e.subarray(-t);
return je.some((t) => F(e, t, T)) && Me.some((e) => F(n, e, T));
}
function Pe(e) {
return e.length < A ? !1 : F(e, E, T);
}
function Fe(e) {
return e.length < A ? !1 : F(e.subarray(-4), D, T);
}
var j = class extends Error {
type;
constructor(e) {
super(), this.type = e;
}
}, Ie = class {
#e = new w();
#t = "joining";
#n;
constructor(e) {
this.#n = e;
}
addCommandChunk(e) {
if (this.#t === "completed") return;
let t = Pe(e);
if (!this.#e.length && !t) throw new j("no-first-chunk");
if (this.#e.length && t) throw new j("incomplete-joining");
this.#e.push(this.#r(e)), Fe(e) && (this.#t = "completed", this.#n(this.#e.getBuffer()));
}
#r(e) {
if (e.length < A) throw Error("Command chunk is too short to unframe");
return e.subarray(T, e.length - T);
}
}, M = class {
#e = new w();
#t = [];
#n = "creating";
#r;
constructor(e, t) {
this.#r = t, this.#e.push(e);
}
addInteger(e, t) {
this.#e.push(e.charCodeAt(0));
let n = Te(t);
this.#e.push(n);
}
addUniqueSimilarIntArr(e, t) {
this.#e.push(e.charCodeAt(0));
let n = De(t);
this.#e.push(n);
}
addString(e, t) {
this.#e.push(e.charCodeAt(0));
let n = ke(t);
this.#e.push(n);
}
complete() {
if (!this.#e.length) throw Error("Buffer is empty");
if (this.#n === "completed") return;
this.#n = "completed";
let e = this.#e.getBuffer();
if (e.length + A <= this.#r) {
this.#t.push(P(e, E, D));
return;
}
let t = Math.ceil(e.length / this.#r);
Math.ceil(e.length / t) + A > this.#r && t++;
for (let [n, r] of ze(e, t)) n === 0 ? this.#t.push(P(r, E, k)) : n === t - 1 ? this.#t.push(P(r, O, D)) : this.#t.push(P(r, O, k));
}
getResultBuffers() {
if (this.#n === "creating" || !this.#t.length) throw Error("Command is not complete.");
return this.#t;
}
};
function Le(e) {
let [t] = e, n = { c: t }, r = 1;
for (; r < e.length;) {
if (r + 1 >= e.length) throw Error("Malformed command buffer: truncated name/type header");
let t = String.fromCharCode(e[r]);
switch (r++, Re(e[r])) {
case C.Int:
{
let { number: i, byteLength: a } = Ee(e.subarray(r));
n[t] = i, r += a;
}
break;
case C.SimilarIntArray:
{
let { numbers: i, byteLength: a } = Oe(e.subarray(r));
n[t] = i, r += a;
}
break;
case C.String:
{
let { string: i, byteLength: a } = Ae(e.subarray(r));
n[t] = i, r += a;
}
break;
}
}
return Be(n);
}
function Re(e) {
let t = e >> 4;
if (t <= C.Min || t >= C.Max) throw Error("Not existing type");
return t;
}
function N(e, t) {
if (t && e.length !== t) throw Error("Wrong string length");
let n = new Uint8Array(t ?? e.length);
for (let t = 0; t < e.length; t++) n[t] = e.charCodeAt(t);
return n;
}
function* ze(e, t) {
let n = Math.ceil(e.length / t);
for (let r = 0; r < t; r++) yield [r, e.subarray(r * n, (r + 1) * n)];
}
function P(e, t, n) {
let r = new Uint8Array(e.length + t.length + n.length);
return r.set(t), r.set(e, t.length), r.set(n, t.length + e.length), r;
}
function F(e, t, n) {
for (let r = 0; r < n; r++) if (e[r] !== t[r]) return !1;
return !0;
}
function Be(e) {
switch (e.c) {
case y.SegmentsAnnouncement: return e;
case y.SegmentRequest: return I(e, "i", "r"), e;
case y.SegmentData: return I(e, "i", "r", "s"), e;
case y.SegmentAbsent:
case y.CancelSegmentRequest:
case y.SegmentDataSendingCompleted: return I(e, "i", "r"), e;
default: throw Error(`Unknown peer command type: ${String(e.c)}`);
}
}
function I(e, ...t) {
for (let n of t) if (typeof e[n] != "number") throw Error(`Expected number field "${n}", got ${typeof e[n]}`);
}
//#endregion
//#region src/p2p/commands/commands.ts
function Ve(e, t) {
let { c: n, p: r, l: i } = e, a = new M(n, t);
return i?.length && a.addUniqueSimilarIntArr("l", i), r?.length && a.addUniqueSimilarIntArr("p", r), a.complete(), a.getResultBuffers();
}
function He(e, t) {
let n = new M(e.c, t);
return n.addInteger("i", e.i), n.addInteger("r", e.r), n.complete(), n.getResultBuffers();
}
function Ue(e, t) {
let n = new M(e.c, t);
return n.addInteger("i", e.i), n.addInteger("s", e.s), n.addInteger("r", e.r), n.complete(), n.getResultBuffers();
}
function We(e, t) {
let n = new M(e.c, t);
return n.addInteger("i", e.i), n.addInteger("r", e.r), e.b && n.addInteger("b", e.b), n.complete(), n.getResultBuffers();
}
function Ge(e, t) {
switch (e.c) {
case y.CancelSegmentRequest:
case y.SegmentAbsent:
case y.SegmentDataSendingCompleted: return He(e, t);
case y.SegmentRequest: return We(e, t);
case y.SegmentsAnnouncement: return Ve(e, t);
case y.SegmentData: return Ue(e, t);
}
}
//#endregion
//#region src/p2p/commands/index.ts
var Ke = /* @__PURE__ */ s({
BinaryCommandChunksJoiner: () => Ie,
BinaryCommandJoiningError: () => j,
PeerCommandType: () => y,
deserializeCommand: () => Le,
isCommandChunk: () => Ne,
serializePeerCommand: () => Ge
});
//#endregion
//#region src/webtorrent/utils.ts
function qe(e, t = "RTC error") {
let n = e;
if (n.error instanceof Error) return n.error;
let r = n.error?.message ?? t;
return Error(r);
}
function Je(e, t = "RTC error") {
return qe(e, t).message;
}
function L(e) {
return e === "failed" || e === "closed" || e === "disconnected";
}
//#endregion
//#region src/webtorrent/data-channel-sender.ts
var Ye = 64 * 1024, Xe = class {
channel;
maxMessageSize;
#e;
constructor(e, t) {
this.channel = e, this.maxMessageSize = t;
}
async sendData(e, t) {
if (this.#e) throw Error("Already sending data");
if (this.channel.readyState !== "open") throw Error("Data channel is not open");
this.channel.bufferedAmountLowThreshold = Ye;
let { promise: n, resolve: r, reject: i } = le(), a = 0, o = !1, s = () => o ? !1 : (o = !0, this.#e = void 0, this.channel.removeEventListener("bufferedamountlow", f), this.channel.removeEventListener("closing", c), this.channel.removeEventListener("close", c), this.channel.removeEventListener("error", l), !0);
this.#e = { cancel: () => {
s() && i(/* @__PURE__ */ Error("Send cancelled"));
} };
let c = () => {
s() && i(/* @__PURE__ */ Error("Data channel closed"));
}, l = (e) => {
if (!s()) return;
let t = Je(e, "Unknown error");
i(/* @__PURE__ */ Error(`Data channel error: ${t}`));
}, u = ArrayBuffer.isView(e) ? e.buffer : e, d = ArrayBuffer.isView(e) ? e.byteOffset : 0, f = () => {
if (!o) {
if (this.channel.readyState !== "open") {
s() && i(/* @__PURE__ */ Error(`Data channel not open (state: ${this.channel.readyState})`));
return;
}
try {
for (; a < e.byteLength;) {
if (this.channel.bufferedAmount > Ye) return;
let n = Math.min(this.maxMessageSize, e.byteLength - a), r = new Uint8Array(u, d + a, n);
if (this.channel.send(r), a += n, t?.(n), !this.#e) return;
}
} catch (e) {
s() && i(e instanceof Error ? e : Error(String(e)));
return;
}
s() && r();
}
};
return this.channel.addEventListener("bufferedamountlow", f), this.channel.addEventListener("closing", c), this.channel.addEventListener("close", c), this.channel.addEventListener("error", l), f(), n;
}
cancel() {
this.#e?.cancel();
}
}, R = (0, v.default)("p2pml-core:peer-protocol"), Ze = class {
#e;
#t;
#n;
#r;
#i;
#a;
#o;
#s;
#c;
constructor(e, t, n, r, i) {
if (this.#a = e, this.#o = t, this.#s = n, this.#c = i, this.#t = new Xe(e, t.webRtcMaxMessageSize), this.#r = r.getEventDispatcher("onChunkDownloaded"), this.#i = r.getEventDispatcher("onChunkUploaded"), e.binaryType !== "arraybuffer") throw Error(`Expected binaryType "arraybuffer", got "${e.binaryType}"`);
e.addEventListener("message", this.#l);
}
#l = (e) => {
try {
let t = new Uint8Array(e.data);
Ne(t) ? this.#u(t) : (this.#s.onSegmentChunkReceived(t), this.#r(t.byteLength, "p2p", this.#c, this.#o.streamType, this.#o.infoHash));
} catch (e) {
R("error handling data channel message: %O", e), this.#s.onProtocolError(e);
}
};
sendCommand(e) {
if (this.#a.readyState !== "open") throw Error(`cannot send command ${e.c} (channel state: ${this.#a.readyState})`);
let t = Ge(e, this.#o.webRtcMaxMessageSize);
for (let e of t) this.#a.send(e);
}
stopUploadingSegmentData() {
this.#t.cancel(), this.#n = void 0;
}
getUploadingRequestId() {
return this.#n;
}
async splitSegmentDataToChunksAndUploadAsync(e, t) {
if (this.#n !== void 0) throw Error("Some segment data is already uploading.");
this.#n = t;
try {
await this.#t.sendData(e, (e) => {
this.#i(e, this.#c, this.#o.streamType, this.#o.infoHash);
});
} finally {
this.#n === t && (this.#n = void 0);
}
}
#u(e) {
this.#e ??= new Ie((e) => {
this.#e = void 0;
let t;
try {
t = Le(e);
} catch (e) {
R("error deserializing command: %O", e), this.#s.onProtocolError(e);
return;
}
this.#s.onCommandReceived(t);
});
try {
this.#e.addCommandChunk(e);
} catch (e) {
R("error receiving command chunks: %O", e), this.#e = void 0, this.#s.onProtocolError(e);
}
}
destroy() {
this.#a.removeEventListener("message", this.#l), this.#t.cancel(), this.#e = void 0, this.#n = void 0;
}
}, Qe = 1, z = class {
clearThresholdMs;
loadingsCount = 0;
bytes = [];
loadingOnlyTimestamps = [];
timestamps = [];
noLoadingsTime = 0;
loadingsStoppedAt = 0;
constructor(e = 2e4) {
this.clearThresholdMs = e;
}
addBytes(e, t = performance.now()) {
this.bytes.push(e), this.loadingOnlyTimestamps.push(t - this.noLoadingsTime), this.timestamps.push(t);
}
startLoading(e = performance.now()) {
this.clearStale(), this.loadingsCount === 0 && this.loadingsStoppedAt !== 0 && (this.noLoadingsTime += e - this.loadingsStoppedAt), this.loadingsCount++;
}
stopLoading(e = performance.now()) {
this.loadingsCount > 0 && (this.loadingsCount--, this.loadingsCount === 0 && (this.loadingsStoppedAt = e));
}
getBandwidthLoadingOnly(e, t = -Infinity) {
if (!this.loadingOnlyTimestamps.length) return 0;
let n = e * 1e3, r = this.loadingOnlyTimestamps[this.loadingOnlyTimestamps.length - 1], i = r, a = r - n, o = 0;
for (let e = this.bytes.length - 1; e >= 0; e--) {
let n = this.loadingOnlyTimestamps[e];
if (n < a || this.timestamps[e] < t) break;
i = n, o += this.bytes[e];
}
let s = Math.max(r - i, Qe);
return o * 8e3 / s;
}
getBandwidth(e, t = -Infinity, n = performance.now()) {
if (!this.timestamps.length) return 0;
let r = n - e * 1e3, i = n, a = 0;
for (let e = this.bytes.length - 1; e >= 0; e--) {
let n = this.timestamps[e];
if (n < r || n < t) break;
i = n, a += this.bytes[e];
}
let o = Math.max(n - i, Qe);
return a * 8e3 / o;
}
clearStale() {
if (!this.loadingOnlyTimestamps.length) return;
let e = this.loadingOnlyTimestamps[this.loadingOnlyTimestamps.length - 1] - this.clearThresholdMs, t = 0;
for (let n of this.loadingOnlyTimestamps) {
if (n > e) break;
t++;
}
this.bytes.splice(0, t), this.loadingOnlyTimestamps.splice(0, t), this.timestamps.splice(0, t);
}
clear() {
this.bytes.length = 0, this.loadingOnlyTimestamps.length = 0, this.timestamps.length = 0, this.loadingsCount = 0, this.noLoadingsTime = 0, this.loadingsStoppedAt = 0;
}
}, { PeerCommandType: B } = Ke, $e = class {
id;
channel;
eventTarget;
#e;
#t;
#n = /* @__PURE__ */ new Set();
#r = /* @__PURE__ */ new Set();
#i = 0;
#a = new z();
#o = {
value: 0,
timestamp: 0
};
#s = (0, v.default)("p2pml-core:peer");
#c = 0;
#l;
#u = !1;
connectedAt = performance.now();
#d;
#f;
#p;
get isDestroyed() {
return this.#u;
}
constructor(e, t, n, r, i, a) {
this.id = e, this.channel = t, this.eventTarget = a, this.#d = n, this.#f = r, this.#p = i, this.#e = new Ze(t, i, {
onSegmentChunkReceived: this.#h,
onCommandReceived: (e) => void this.#m(e).catch((t) => {
this.#s("error processing command %O: %O", e, t), this.destroy(!1, new d("protocol-violation", t instanceof Error ? t.message : "Error processing command", t));
}),
onProtocolError: (e) => {
this.destroy(!1, new d("protocol-violation", e instanceof Error ? e.message : "Protocol error", e));
}
}, a, e);
}
get downloadingSegment() {
return this.#t?.request.segment;
}
get isUploadingSegment() {
return this.#e.getUploadingRequestId() !== void 0;
}
getDownloadBandwidth() {
let e = performance.now();
return e - this.#o.timestamp > 1e3 && (this.#o.value = this.#a.getBandwidthLoadingOnly(15), this.#o.timestamp = e), this.#o.value;
}
getSegmentStatus(e) {
let { externalId: t } = e;
if (this.#n.has(t)) return "loaded";
if (this.#r.has(t)) return "http-loading";
}
#m = async (e) => {
switch (e.c) {
case B.SegmentsAnnouncement:
this.#n = new Set(e.l), this.#r = new Set(e.p), this.#f.onSegmentsAnnouncement();
break;
case B.SegmentRequest:
this.#l = e.r, this.#e.stopUploadingSegmentData(), this.#f.onSegmentRequested(this, e.i, e.r, e.b);
break;
case B.SegmentData:
{
if (!this.#t || this.#t.isSegmentDataCommandReceived) break;
let { request: t, controls: n, requestId: r } = this.#t;
if (t.segment.externalId !== e.i || r !== e.r) break;
this.#t.isSegmentDataCommandReceived = !0, n.firstBytesReceived(), t.totalBytes === void 0 ? t.setTotalBytes(t.loadedBytes + e.s) : t.totalBytes - t.loadedBytes !== e.s && this.#g("bytes-length-mismatch", "Peer response bytes length mismatch");
}
break;
case B.SegmentDataSendingCompleted: {
let t = this.#t;
if (!t?.isSegmentDataCommandReceived) return;
let { request: n, controls: r, requestId: i } = t;
if (n.segment.externalId !== e.i || i !== e.r) {
this.#g("protocol-violation", "Peer protocol violation");
return;
}
if (n.loadedBytes !== n.totalBytes) {
this.#g("bytes-length-mismatch", "Peer response bytes length mismatch");
return;
}
let a = await n.validateData(this.#p.validateP2PSegment);
if (this.#u || this.#t !== t) return;
if (!a) {
this.#g("validation-failed", "P2P segment validation failed");
return;
}
this.#i = 0, r.completeOnSuccess(), this.#a.stopLoading(), this.#t = void 0;
break;
}
case B.SegmentAbsent:
this.#n.delete(e.i), this.#t?.request.segment.externalId === e.i && this.#t.requestId === e.r && this.#_("peer-segment-absent");
break;
case B.CancelSegmentRequest:
if (this.#l === e.r && (this.#l = void 0), this.#e.getUploadingRequestId() !== e.r) break;
this.#e.stopUploadingSegmentData();
break;
}
};
#h = (e) => {
if (!this.#t?.isSegmentDataCommandReceived) return;
let { request: t, controls: n } = this.#t;
if (t.totalBytes !== void 0 && t.loadedBytes + e.byteLength > t.totalBytes) {
this.#g("bytes-length-mismatch", "Peer response bytes length mismatch");
return;
}
this.#a.addBytes(e.byteLength), this.#o.timestamp = 0, n.addLoadedChunk(e);
};
downloadSegment(e) {
if (this.#u) return;
if (this.#t) throw Error("Some segment already is downloading");
if (e.tryCompleteByLoadedBytes({
downloadSource: "p2p",
peerId: this.id
}, {
notReceivingBytesTimeoutMs: this.#p.p2pNotReceivingBytesTimeoutMs,
onAbort: () => void 0
}, this.#p.validateP2PSegment, "p2p-segment-validation-failed")) return;
this.#a.startLoading(), this.#c = (this.#c + 1) % 1e9, this.#t = {
request: e,
requestId: this.#c,
isSegmentDataCommandReceived: !1,
controls: e.start({
downloadSource: "p2p",
peerId: this.id
}, {
notReceivingBytesTimeoutMs: this.#p.p2pNotReceivingBytesTimeoutMs,
onAbort: (t) => {
if (!this.#t || this.#t.request !== e) return;
let { request: n, requestId: r } = this.#t;
this.#v(n.segment, r), this.#a.stopLoading(), t.type !== "abort" && (this.#a.clear(), this.#o.timestamp = 0, this.#s(`cleared bandwidth history due to ${t.type}`)), this.#t = void 0, t.type === "bytes-receiving-timeout" && this.#i++, this.#i >= this.#p.p2pErrorRetries ? this.destroy(!1, new d("timeout", "Too many timeout errors")) : t.type === "bytes-receiving-timeout" && this.#f.onWarning(new f("timeout-strike", `Timeout strike ${this.#i}/${this.#p.p2pErrorRetries}`));
}
})
};
let t = {
c: B.SegmentRequest,
r: this.#t.requestId,
i: e.segment.externalId
};
e.loadedBytes && (t.b = e.loadedBytes), this.#b(t) || this.#_("peer-closed");
}
async uploadSegmentData(e, t, n) {
if (this.#u) return;
if (t !== this.#l) {
this.#s(`discarding obsolete upload request ${t} for segment ${e.externalId}`);
return;
}
let { externalId: r } = e;
this.#s(`send segment ${e.externalId} to ${this.id} (byteLength: ${n.byteLength})`);
let i = {
c: B.SegmentData,
i: r,
r: t,
s: n.byteLength
};
if (this.#b(i)) try {
if (await this.#e.splitSegmentDataToChunksAndUploadAsync(n, t), this.isDestroyed || t !== this.#l) return;
this.#y(e, t), this.#s(`segment ${r} has been sent to ${this.id}`);
} catch (e) {
this.#s(`cancel segment uploading ${r}: %O`, e);
}
}
#g(e, t) {
this.#t?.request.clearLoadedBytes();
let n = new d(e, t);
this.#_("peer-closed", n), this.destroy(!1, n);
}
#_(e, t) {
if (!this.#t) return;
let { request: n, controls: r } = this.#t, { segment: i } = n;
this.#s(`cancel segment request ${i.externalId} (${e})`);
let a = new g(e, void 0, t);
r.failWithError(a), this.#a.stopLoading(), e !== "peer-segment-absent" && (this.#a.clear(), this.#o.timestamp = 0, this.#s(`cleared bandwidth history due to ${a.type}`)), this.#t = void 0;
}
sendSegmentsAnnouncementCommand(e, t) {
let n = {
c: B.SegmentsAnnouncement,
p: t,
l: e
};
this.#b(n);
}
sendSegmentAbsentCommand(e, t) {
this.#b({
c: B.SegmentAbsent,
i: e,
r: t
});
}
#v(e, t) {
this.#b({
c: B.CancelSegmentRequest,
i: e.externalId,
r: t
});
}
#y(e, t) {
this.#b({
c: B.SegmentDataSendingCompleted,
r: t,
i: e.externalId
});
}
destroy(e = !1, t) {
this.#u || (this.#u = !0, this.#_("peer-closed", t), this.#e.destroy(), e || this.#d(t), this.#s(`peer closed ${this.id}`));
}
#b(e) {
if (this.#u) return !1;
try {
return this.#e.sendCommand(e), !0;
} catch (t) {
return this.#s("error sending command %d: %O", e.c, t), !1;
}
}
}, V = class {
events = /* @__PURE__ */ new Map();
dispatchEvent(e, t, n, r, i, a) {
let o = this.events.get(e);
if (o) for (let e of o) try {
e(t, n, r, i, a);
} catch {}
}
getEventDispatcher(e) {
return (t, n, r, i, a) => {
let o = this.events.get(e);
if (o) for (let e of o) try {
e(t, n, r, i, a);
} catch {}
};
}
addEventListener(e, t) {
let n = this.events.get(e);
n ? this.events.set(e, [...n, t]) : this.events.set(e, [t]);
}
removeEventListener(e, t) {
let n = this.events.get(e);
if (!n) return;
let r = n.indexOf(t);
if (r === -1) return;
if (n.length === 1) {
this.events.delete(e);
return;
}
let i = n.slice();
i.splice(r, 1), this.events.set(e, i);
}
clear() {
this.events.clear();
}
}, H = typeof window < "u" ? window : {}, U = H.RTCPeerConnection ?? H.webkitRTCPeerConnection ?? H.mozRTCPeerConnection, et = H.RTCSessionDescription ?? H.webkitRTCSessionDescription ?? H.mozRTCSessionDescription, W = (() => {
if (!H.RTCPeerConnection && !H.webkitRTCPeerConnection && !H.mozRTCPeerConnection) return !1;
let e;
try {
e = new U();
let t = e.createOffer();
if (typeof t?.then == "function") return t.catch(() => {}), !0;
} catch {} finally {
try {
e?.close();
} catch {}
}
return !1;
})();
function tt(e, t) {
return W ? e.createOffer(t) : new Promise((n, r) => {
try {
e.createOffer(n, r, t);
} catch (e) {
r(e);
}
});
}
function nt(e, t) {
return W ? e.createAnswer(t) : new Promise((n, r) => {
try {
e.createAnswer(n, r, t);
} catch (e) {
r(e);
}
});
}
function rt(e, t) {
return W ? e.setLocalDescription(t) : new Promise((n, r) => {
try {
e.setLocalDescription(t, n, r);
} catch (e) {
r(e);
}
});
}
function it(e, t) {
return W ? e.setRemoteDescription(t) : new Promise((n, r) => {
try {
e.setRemoteDescription(t, n, r);
} catch (e) {
r(e);
}
});
}
//#endregion
//#region src/webtorrent/webtorrent-client/index.ts
var at = 5e4, ot = 15e3, st = 5, ct = 5e3;
function lt() {
let e = "";
for (let t = 0; t < 20; t++) e += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".charAt(Math.floor(Math.random() * 62));
return e;
}
var ut = new Set([
"offer",
"answer",
"pranswer",
"rollback"
]);
function dt(e) {
if (typeof e != "object" || !e) return !1;
let t = e;
return typeof t.type == "string" && ut.has(t.type) && typeof t.sdp == "string";
}
var ft = class e {
static #e = 120;
static #t = 20;
#n;
#r;
#i = new V();
#a = /* @__PURE__ */ new Map();
#o = /* @__PURE__ */ new Set();
#s = new re();
#c = null;
#l = null;
#u = 0;
#d = null;
#f = void 0;
#p = null;
#m = !1;
#h() {
return this.#s.signal.aborted;
}
#g() {
if (this.#s.signal.aborted) throw Error("Client destroyed");
}
constructor(e) {
this.#n = {
infoHash: e.infoHash,
peerId: e.peerId,
rtcConfig: e.rtcConfig,
channelConfig: e.channelConfig,
offerTimeout: e.offerTimeout ?? (() => at),
offersCount: e.offersCount ?? (() => st),
iceGatheringTimeout: e.iceGatheringTimeout ?? (() => ct),
connectionTimeout: e.connectionTimeout ?? (() => ot),
claimPeer: e.claimPeer ?? (() => !0),
shouldGenerateOffers: e.shouldGenerateOffers ?? (() => !0)
}, this.#r = e.wsClient;
}
addEventListener(e, t) {
this.#i.addEventListener(e, t);
}
removeEventListener(e, t) {
this.#i.removeEventListener(e, t);
}
start() {
this.#h() || this.#m || (this.#m = !0, this.#r.addEventListener("connected", this.#_), this.#r.addEventListener("disconnected", this.#v), this.#r.addEventListener("message", this.#y), this.#r.state === "connected" && this.#_());
}
destroy() {
this.#h() || (this.#s.abort(), this.#x(), this.#w(), this.#j(), this.#M(), this.#m && (this.#r.removeEventListener("connected", this.#_), this.#r.removeEventListener("disconnected", this.#v), this.#r.removeEventListener("message", this.#y)), this.#i.clear());
}
#_ = () => {
this.#b(e.#e), this.#S("started").catch((e) => {
this.#h() || this.#i.dispatchEvent("error", new p("announce-failed", `Initial announce failed: ${e instanceof Error ? e.message : String(e)}`, e));
});
};
#v = () => {
this.#x(), this.#l = null;
};
#y = (t) => {
if (this.#h()) return;
let n;
try {
let e = typeof t == "string" ? t : new TextDecoder().decode(t);
n = JSON.parse(e);
} catch (e) {
this.#i.dispatchEvent("error", new p("parse-error", `Failed to parse tracker message: ${e instanceof Error ? e.message : String(e)}`, e));
return;
}
if (typeof n != "object" || !n || Array.isArray(n)) return;
let r = n, i = r.info_hash;
if (typeof i == "string" && i !== this.#n.infoHash) return;
let a = r["warning message"];
typeof a == "string" && this.#i.dispatchEvent("warning", new m("tracker-response", a));
let o = r["failure reason"];
if (typeof o == "string") {
this.#i.dispatchEvent("error", new p("tracker-response", o));
return;
}
let { interval: s } = r;
if (typeof s == "number" && s > 0) {
let t = Math.max(e.#t, s);
this.#l !== t && this.#b(t);
}
let c = r["tracker id"];
typeof c == "string" && (this.#p = c);
let l = r.peer_id;
if (typeof l == "string" && l === this.#n.peerId) return;
let u = r.offer_id;
typeof l != "string" || typeof u != "string" || (dt(r.offer) ? this.#E({
sdp: r.offer,
peerId: l,
offerId: u
}).catch((e) => {
this.#h() || this.#i.dispatchEvent("error", new p("signaling-failed", `Failed to handle offer: ${e instanceof Error ? e.message : String(e)}`, e));
}) : dt(r.answer) && this.#D({
sdp: r.answer,
peerId: l,
offerId: u
}).catch((e) => {
this.#h() || this.#i.dispatchEvent("error", new p("signaling-failed", `Failed to handle answer: ${e instanceof Error ? e.message : String(e)}`, e));
}));
};
#b(e) {
this.#x(), this.#l = e;
let t = ++this.#u, n = async () => {
try {
await this.#S();
} catch (e) {
if (this.#h()) return;
this.#i.dispatchEvent("error", new p("announce-failed", `Announce failed: ${e instanceof Error ? e.message : String(e)}`, e));
}
!this.#h() && this.#l !== null && this.#u === t && (this.#c = setTimeout(n, this.#l * 1e3));
};
this.#c = setTimeout(n, e * 1e3);
}
#x() {
this.#c !== null && (clearTimeout(this.#c), this.#c = null);
}
async #S(e) {
if (this.#h() || this.#r.state !== "connected") return;
if (e && (this.#f = e), this.#d) return this.#d;
let t = (async () => {
let e = this.#n.shouldGenerateOffers() ? this.#n.offersCount() : 0, t = await Promise.all(Array.from({ length: e }, () => this.#C()));
if (this.#h()) {
for (let e of t) e && this.#A(e.offer_id);
return;
}
let n = [];
for (let e of t) e && n.push(e);
let r = this.#f;
this.#f = void 0;
let i = this.#T({
numwant: n.length,
offers: n,
event: r
});
if (this.#r.state !== "connected") {
for (let e of n) this.#A(e.offer_id);
return;
}
try {
this.#r.send(JSON.stringify(i));
} catch (e) {
for (let e of n) this.#A(e.offer_id);
throw e;
}
})();
this.#d = t;
try {
await t;
} finally {
this.#d === t && (this.#d = null);
}
}
async #C() {
if (this.#h()) return;
let e;
try {
e = new U(this.#n.rtcConfig?.()), this.#o.add(e);
let t = e.createDataChannel("webtorrent", this.#n.channelConfig), n = await tt(e);
this.#g(), await rt(e, n), this.#g(), await this.#O(e), this.#g();
let r = e.localDescription;
if (!r?.sdp) {
e.close();
return;
}
let i = lt();
return this.#a.set(i, {
connection: e,
channel: t,
timeoutId: setTimeout(() => {
this.#A(i);
}, this.#n.offerTimeout())
}), {
offer: {
type: r.type,
sdp: r.sdp
},
offer_id: i
};
} catch (t) {
e?.close(), this.#h() || this.#i.dispatchEvent("warning", new m("offer-failed", `Failed to create offer: ${t instanceof Error ? t.message : String(t)}`, t));
} finally {
e && this.#o.delete(e);
}
}
#w() {
if (this.#r.state !== "connected") return;
let e = this.#T({
numwant: 0,
offers: [],
event: "stopped"
});
try {
this.#r.send(JSON.stringify(e));
} catch {}
}
#T({ numwant: e, offers: t, event: n }) {
let r = {
action: "announce",
info_hash: this.#n.infoHash,
peer_id: this.#n.peerId,
numwant: e,
uploaded: 0,
downloaded: 0,
offers: t
};
return n && (r.event = n), this.#p && (r.trackerid = this.#p), r;
}
async #E({ sdp: e, peerId: t, offerId: n }) {
if (this.#h() || !this.#n.claimPeer(t)) return;
let r;
try {
r = new U(this.#n.rtcConfig?.()), this.#o.add(r), await it(r, new et(e)), this.#g();
let i = await nt(r);
this.#g(), await rt(r, i), this.#g(), await this.#O(r), this.#g();
let a = r.localDescription;
if (!a) throw Error("Failed to get local description after ICE gathering");
let o = {
action: "announce",
info_hash: this.#n.infoHash,
peer_id: this.#n.peerId,
to_peer_id: t,
offer_id: n,
answer: {
type: a.type,
sdp: a.sdp
}
};
this.#r.send(JSON.stringify(o));
let s = await this.#k(r);
this.#g(), this.#i.dispatchEvent("peerConnected", {
peerId: t,
connection: r,
channel: s
});
} catch (e) {
r?.close(), this.#i.dispatchEvent("peerConnectFailed", {
peerId: t,
error: new h("connection-failed", e instanceof Error ? e.message : String(e), e)
});
} finally {
r && this.#o.delete(r);
}
}
async #D({ sdp: e, peerId: t, offerId: n }) {
if (this.#h()) return;
let r = this.#a.get(n);
if (r) {
if (this.#a.delete(n), clearTimeout(r.timeoutId), !this.#n.claimPeer(t)) {
r.connection.close();
return;
}
this.#o.add(r.connection);
try {
await it(r.connection, new et(e)), this.#g();
let n = await this.#k(r.connection, r.channel);
this.#g(), this.#i.dispatchEvent("peerConnected", {
peerId: t,
connection: r.connection,
channel: n
});
} catch (e) {
r.connection.close(), this.#i.dispatchEvent("peerConnectFailed", {
peerId: t,
error: new h("connection-failed", e instanceof Error ? e.message : String(e), e)
});
} finally {
this.#o.delete(r.connection);
}
}
}
#O(e) {
return new Promise((t, n) => {
if (e.iceGatheringState === "complete") {
t();
return;
}
if (e.signalingState === "closed") {
n(/* @__PURE__ */ Error("RTCPeerConnection closed"));
return;
}
let r, i = () => {
clearTimeout(r), e.removeEventListener("icegatheringstatechange", a), e.removeEventListener("icecandidate", o), e.removeEventListener("signalingstatechange", s), this.#s.signal.removeEventListener("abort", c);
}, a = () => {
e.iceGatheringState === "complete" && (i(), t());
}, o = (e) => {
e.candidate === null && (i(), t());
}, s = () => {
e.signalingState === "closed" && (i(), n(/* @__PURE__ */ Error("RTCPeerConnection closed")));
},