p2p-media-loader-core
Version:
P2P Media Loader core functionality
367 lines • 26.2 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _P2PLoader_instances, _P2PLoader_webtorrentManager, _P2PLoader_peersMap, _P2PLoader_swarmId, _P2PLoader_streamSwarmId, _P2PLoader_isAnnounceMicrotaskCreated, _P2PLoader_webtorrentManagerLogger, _P2PLoader_churnLogger, _P2PLoader_infoHash, _P2PLoader_streamManifestUrl, _P2PLoader_stream, _P2PLoader_requests, _P2PLoader_segmentStorage, _P2PLoader_config, _P2PLoader_webTorrentSocketPool, _P2PLoader_eventTarget, _P2PLoader_onSegmentAnnouncement, _P2PLoader_churnCleanupTimeoutId, _P2PLoader_onPeerConnect, _P2PLoader_onPeerConnectError, _P2PLoader_onPeerClose, _P2PLoader_onPeerError, _P2PLoader_onPeerWarning, _P2PLoader_onTrackerWarning, _P2PLoader_onTrackerError, _P2PLoader_churnCleanup, _P2PLoader_getSegmentsAnnouncement, _P2PLoader_onPeerConnectedWebTorrent, _P2PLoader_onPeerDisconnectedWebTorrent, _P2PLoader_sendSegmentsAnnouncement, _P2PLoader_onSegmentRequested;
import { Peer } from "./peer.js";
import { WebTorrentManager } from "../webtorrent/webtorrent-manager/index.js";
import * as StreamUtils from "../utils/stream.js";
import * as Utils from "../utils/utils.js";
import * as PeerUtil from "../utils/peer.js";
import debug from "debug";
const MIN_CHURN_CLEANUP_INTERVAL_MS = 1000;
export class P2PLoader {
constructor(streamManifestUrl, stream, requests, segmentStorage, config, webTorrentSocketPool, eventTarget, peerId, onSegmentAnnouncement) {
var _a;
_P2PLoader_instances.add(this);
_P2PLoader_webtorrentManager.set(this, void 0);
_P2PLoader_peersMap.set(this, new Map());
_P2PLoader_swarmId.set(this, void 0);
_P2PLoader_streamSwarmId.set(this, void 0);
_P2PLoader_isAnnounceMicrotaskCreated.set(this, false);
_P2PLoader_webtorrentManagerLogger.set(this, debug("p2pml-core:webtorrent-manager"));
_P2PLoader_churnLogger.set(this, debug("p2pml-core:churn-cleanup"));
_P2PLoader_infoHash.set(this, void 0);
_P2PLoader_streamManifestUrl.set(this, void 0);
_P2PLoader_stream.set(this, void 0);
_P2PLoader_requests.set(this, void 0);
_P2PLoader_segmentStorage.set(this, void 0);
_P2PLoader_config.set(this, void 0);
_P2PLoader_webTorrentSocketPool.set(this, void 0);
_P2PLoader_eventTarget.set(this, void 0);
_P2PLoader_onSegmentAnnouncement.set(this, void 0);
_P2PLoader_churnCleanupTimeoutId.set(this, void 0);
_P2PLoader_onPeerConnect.set(this, void 0);
_P2PLoader_onPeerConnectError.set(this, void 0);
_P2PLoader_onPeerClose.set(this, void 0);
_P2PLoader_onPeerError.set(this, void 0);
_P2PLoader_onPeerWarning.set(this, void 0);
_P2PLoader_onTrackerWarning.set(this, void 0);
_P2PLoader_onTrackerError.set(this, void 0);
_P2PLoader_churnCleanup.set(this, () => {
// Schedule the next run dynamically based on the current config value, enforcing a minimum safe interval
__classPrivateFieldSet(this, _P2PLoader_churnCleanupTimeoutId, setTimeout(__classPrivateFieldGet(this, _P2PLoader_churnCleanup, "f"), Math.max(MIN_CHURN_CLEANUP_INTERVAL_MS, __classPrivateFieldGet(this, _P2PLoader_config, "f").p2pChurnCleanupIntervalMs)), "f");
const excessPeersCount = __classPrivateFieldGet(this, _P2PLoader_peersMap, "f").size - __classPrivateFieldGet(this, _P2PLoader_config, "f").p2pMaxPeers;
if (excessPeersCount <= 0)
return;
const eligiblePeers = [];
const now = performance.now();
for (const peer of __classPrivateFieldGet(this, _P2PLoader_peersMap, "f").values()) {
// Don't drop peers that are actively downloading or uploading to avoid interrupting streams
if (peer.downloadingSegment || peer.isUploadingSegment)
continue;
// Give new peers a grace period to establish connections and prove their bandwidth
if (now - peer.connectedAt < __classPrivateFieldGet(this, _P2PLoader_config, "f").p2pChurnGracePeriodMs) {
continue;
}
eligiblePeers.push(peer);
}
if (eligiblePeers.length === 0)
return;
eligiblePeers.sort((a, b) => {
return (a.getDownloadBandwidth() - b.getDownloadBandwidth() ||
a.connectedAt - b.connectedAt);
});
const peersToDrop = eligiblePeers.slice(0, excessPeersCount);
__classPrivateFieldGet(this, _P2PLoader_churnLogger, "f").call(this, `Background churn cleanup: dropping ${peersToDrop.length} excess peers ` +
`(total: ${__classPrivateFieldGet(this, _P2PLoader_peersMap, "f").size}, target: ${__classPrivateFieldGet(this, _P2PLoader_config, "f").p2pMaxPeers}, eligible: ${eligiblePeers.length})`);
for (const peer of peersToDrop) {
__classPrivateFieldGet(this, _P2PLoader_churnLogger, "f").call(this, `dropping excess peer ${peer.id} with bandwidth ${peer.getDownloadBandwidth()}`);
peer.destroy();
}
});
_P2PLoader_onPeerConnectedWebTorrent.set(this, (event) => {
__classPrivateFieldGet(this, _P2PLoader_webtorrentManagerLogger, "f").call(this, `peerConnected: peerId=${event.peerId}`);
if (__classPrivateFieldGet(this, _P2PLoader_peersMap, "f").has(event.peerId)) {
event.close();
return;
}
const peer = new Peer(event.peerId, event.channel, event.close, {
onSegmentRequested: (peer, segmentExternalId, requestId, byteFrom) => {
__classPrivateFieldGet(this, _P2PLoader_onSegmentRequested, "f").call(this, peer, segmentExternalId, requestId, byteFrom).catch((error) => {
__classPrivateFieldGet(this, _P2PLoader_webtorrentManagerLogger, "f").call(this, `Error in onSegmentRequested ${segmentExternalId} for peer ${peer.id}:`, error);
});
},
onSegmentsAnnouncement: __classPrivateFieldGet(this, _P2PLoader_onSegmentAnnouncement, "f"),
onWarning: (warning) => {
__classPrivateFieldGet(this, _P2PLoader_onPeerWarning, "f").call(this, {
peerId: peer.id,
infoHash: __classPrivateFieldGet(this, _P2PLoader_infoHash, "f"),
streamType: __classPrivateFieldGet(this, _P2PLoader_stream, "f").type,
warning,
});
},
}, {
p2pNotReceivingBytesTimeoutMs: __classPrivateFieldGet(this, _P2PLoader_config, "f").p2pNotReceivingBytesTimeoutMs,
webRtcMaxMessageSize: __classPrivateFieldGet(this, _P2PLoader_config, "f").webRtcMaxMessageSize,
p2pErrorRetries: __classPrivateFieldGet(this, _P2PLoader_config, "f").p2pErrorRetries,
validateP2PSegment: __classPrivateFieldGet(this, _P2PLoader_config, "f").validateP2PSegment,
streamType: __classPrivateFieldGet(this, _P2PLoader_stream, "f").type,
infoHash: __classPrivateFieldGet(this, _P2PLoader_infoHash, "f"),
}, __classPrivateFieldGet(this, _P2PLoader_eventTarget, "f"));
__classPrivateFieldGet(this, _P2PLoader_peersMap, "f").set(event.peerId, peer);
__classPrivateFieldGet(this, _P2PLoader_onPeerConnect, "f").call(this, {
peerId: event.peerId,
infoHash: __classPrivateFieldGet(this, _P2PLoader_infoHash, "f"),
streamType: __classPrivateFieldGet(this, _P2PLoader_stream, "f").type,
});
if (__classPrivateFieldGet(this, _P2PLoader_config, "f").isP2PUploadDisabled)
return;
const { httpLoading, loaded } = __classPrivateFieldGet(this, _P2PLoader_instances, "m", _P2PLoader_getSegmentsAnnouncement).call(this);
peer.sendSegmentsAnnouncementCommand(loaded, httpLoading);
});
_P2PLoader_onPeerDisconnectedWebTorrent.set(this, (event) => {
var _a;
__classPrivateFieldGet(this, _P2PLoader_webtorrentManagerLogger, "f").call(this, "peerDisconnected: peerId=%s error=%s reason=%s", event.peerId, (_a = event.error) === null || _a === void 0 ? void 0 : _a.message, event.disconnectReason);
const peer = __classPrivateFieldGet(this, _P2PLoader_peersMap, "f").get(event.peerId);
if (!peer)
return;
__classPrivateFieldGet(this, _P2PLoader_peersMap, "f").delete(event.peerId);
peer.destroy(true);
if (event.error) {
__classPrivateFieldGet(this, _P2PLoader_onPeerError, "f").call(this, {
peerId: event.peerId,
infoHash: __classPrivateFieldGet(this, _P2PLoader_infoHash, "f"),
streamType: __classPrivateFieldGet(this, _P2PLoader_stream, "f").type,
error: event.error,
});
}
__classPrivateFieldGet(this, _P2PLoader_onPeerClose, "f").call(this, {
peerId: peer.id,
infoHash: __classPrivateFieldGet(this, _P2PLoader_infoHash, "f"),
streamType: __classPrivateFieldGet(this, _P2PLoader_stream, "f").type,
});
});
Object.defineProperty(this, "broadcastAnnouncement", {
enumerable: true,
configurable: true,
writable: true,
value: (sendEmptyAnnouncement = false) => {
if (sendEmptyAnnouncement) {
__classPrivateFieldGet(this, _P2PLoader_sendSegmentsAnnouncement, "f").call(this, sendEmptyAnnouncement);
return;
}
if (__classPrivateFieldGet(this, _P2PLoader_isAnnounceMicrotaskCreated, "f") || __classPrivateFieldGet(this, _P2PLoader_config, "f").isP2PUploadDisabled) {
return;
}
__classPrivateFieldGet(this, _P2PLoader_sendSegmentsAnnouncement, "f").call(this);
}
});
_P2PLoader_sendSegmentsAnnouncement.set(this, (sendEmptyAnnouncement = false) => {
__classPrivateFieldSet(this, _P2PLoader_isAnnounceMicrotaskCreated, true, "f");
Utils.queueMicrotask(() => {
const { loaded = [], httpLoading = [] } = sendEmptyAnnouncement
? {}
: __classPrivateFieldGet(this, _P2PLoader_instances, "m", _P2PLoader_getSegmentsAnnouncement).call(this);
for (const peer of __classPrivateFieldGet(this, _P2PLoader_peersMap, "f").values()) {
peer.sendSegmentsAnnouncementCommand(loaded, httpLoading);
}
__classPrivateFieldSet(this, _P2PLoader_isAnnounceMicrotaskCreated, false, "f");
});
});
_P2PLoader_onSegmentRequested.set(this, (peer, segmentExternalId, requestId, byteFrom) => __awaiter(this, void 0, void 0, function* () {
const segment = StreamUtils.getSegmentFromStreamByExternalId(__classPrivateFieldGet(this, _P2PLoader_stream, "f"), segmentExternalId);
if (!segment)
return;
if (__classPrivateFieldGet(this, _P2PLoader_config, "f").isP2PUploadDisabled) {
peer.sendSegmentAbsentCommand(segmentExternalId, requestId);
return;
}
let segmentData;
try {
segmentData = yield __classPrivateFieldGet(this, _P2PLoader_segmentStorage, "f").getSegmentData(__classPrivateFieldGet(this, _P2PLoader_swarmId, "f"), __classPrivateFieldGet(this, _P2PLoader_streamSwarmId, "f"), segment.externalId);
}
catch (error) {
__classPrivateFieldGet(this, _P2PLoader_webtorrentManagerLogger, "f").call(this, `Storage error for segment ${segmentExternalId} requested by peer ${peer.id}:`, error);
}
const peerClosedWhileAwait = !__classPrivateFieldGet(this, _P2PLoader_peersMap, "f").has(peer.id);
if (peerClosedWhileAwait)
return;
if (!segmentData) {
peer.sendSegmentAbsentCommand(segmentExternalId, requestId);
return;
}
yield peer.uploadSegmentData(segment, requestId, byteFrom !== undefined
? new Uint8Array(segmentData).subarray(byteFrom)
: segmentData);
}));
__classPrivateFieldSet(this, _P2PLoader_streamManifestUrl, streamManifestUrl, "f");
__classPrivateFieldSet(this, _P2PLoader_stream, stream, "f");
__classPrivateFieldSet(this, _P2PLoader_requests, requests, "f");
__classPrivateFieldSet(this, _P2PLoader_segmentStorage, segmentStorage, "f");
__classPrivateFieldSet(this, _P2PLoader_config, config, "f");
__classPrivateFieldSet(this, _P2PLoader_webTorrentSocketPool, webTorrentSocketPool, "f");
__classPrivateFieldSet(this, _P2PLoader_eventTarget, eventTarget, "f");
__classPrivateFieldSet(this, _P2PLoader_onSegmentAnnouncement, onSegmentAnnouncement, "f");
__classPrivateFieldSet(this, _P2PLoader_onPeerConnect, eventTarget.getEventDispatcher("onPeerConnect"), "f");
__classPrivateFieldSet(this, _P2PLoader_onPeerConnectError, eventTarget.getEventDispatcher("onPeerConnectError"), "f");
__classPrivateFieldSet(this, _P2PLoader_onPeerClose, eventTarget.getEventDispatcher("onPeerClose"), "f");
__classPrivateFieldSet(this, _P2PLoader_onPeerError, eventTarget.getEventDispatcher("onPeerError"), "f");
__classPrivateFieldSet(this, _P2PLoader_onPeerWarning, eventTarget.getEventDispatcher("onPeerWarning"), "f");
__classPrivateFieldSet(this, _P2PLoader_onTrackerWarning, eventTarget.getEventDispatcher("onTrackerWarning"), "f");
__classPrivateFieldSet(this, _P2PLoader_onTrackerError, eventTarget.getEventDispatcher("onTrackerError"), "f");
__classPrivateFieldSet(this, _P2PLoader_swarmId, (_a = __classPrivateFieldGet(this, _P2PLoader_config, "f").swarmId) !== null && _a !== void 0 ? _a : __classPrivateFieldGet(this, _P2PLoader_streamManifestUrl, "f"), "f");
__classPrivateFieldSet(this, _P2PLoader_streamSwarmId, StreamUtils.getStreamSwarmId(__classPrivateFieldGet(this, _P2PLoader_swarmId, "f"), __classPrivateFieldGet(this, _P2PLoader_stream, "f")), "f");
const streamHash = PeerUtil.getStreamHash(__classPrivateFieldGet(this, _P2PLoader_streamSwarmId, "f"));
__classPrivateFieldSet(this, _P2PLoader_infoHash, streamHash, "f");
__classPrivateFieldSet(this, _P2PLoader_webtorrentManager, new WebTorrentManager({
infoHash: streamHash,
peerId,
trackerUrls: __classPrivateFieldGet(this, _P2PLoader_config, "f").announceTrackers,
rtcConfig: () => __classPrivateFieldGet(this, _P2PLoader_config, "f").rtcConfig,
socketPool: __classPrivateFieldGet(this, _P2PLoader_webTorrentSocketPool, "f"),
maxPeers: () => __classPrivateFieldGet(this, _P2PLoader_config, "f").p2pMaxPeers,
maxPeersMultiplier: () => __classPrivateFieldGet(this, _P2PLoader_config, "f").p2pChurnMaxPeersMultiplier,
offersCount: () => __classPrivateFieldGet(this, _P2PLoader_config, "f").webRtcOffersCount,
offerTimeout: () => __classPrivateFieldGet(this, _P2PLoader_config, "f").webRtcOfferTimeoutMs,
iceGatheringTimeout: () => __classPrivateFieldGet(this, _P2PLoader_config, "f").webRtcIceGatheringTimeoutMs,
connectionTimeout: () => __classPrivateFieldGet(this, _P2PLoader_config, "f").webRtcConnectionTimeoutMs,
}), "f");
__classPrivateFieldGet(this, _P2PLoader_webtorrentManager, "f").addEventListener("peerConnected", __classPrivateFieldGet(this, _P2PLoader_onPeerConnectedWebTorrent, "f"));
__classPrivateFieldGet(this, _P2PLoader_webtorrentManager, "f").addEventListener("peerDisconnected", __classPrivateFieldGet(this, _P2PLoader_onPeerDisconnectedWebTorrent, "f"));
__classPrivateFieldGet(this, _P2PLoader_webtorrentManager, "f").addEventListener("peerConnectFailed", (event) => {
__classPrivateFieldGet(this, _P2PLoader_webtorrentManagerLogger, "f").call(this, `Peer connection failed (${event.peerId}) from tracker ${event.trackerUrl}:`, event.error);
__classPrivateFieldGet(this, _P2PLoader_onPeerConnectError, "f").call(this, {
peerId: event.peerId,
infoHash: __classPrivateFieldGet(this, _P2PLoader_infoHash, "f"),
streamType: __classPrivateFieldGet(this, _P2PLoader_stream, "f").type,
trackerUrl: event.trackerUrl,
error: event.error,
});
});
__classPrivateFieldGet(this, _P2PLoader_webtorrentManager, "f").addEventListener("warning", (event) => {
__classPrivateFieldGet(this, _P2PLoader_webtorrentManagerLogger, "f").call(this, `Tracker warning (${event.trackerUrl}):`, event.warning);
__classPrivateFieldGet(this, _P2PLoader_onTrackerWarning, "f").call(this, {
trackerUrl: event.trackerUrl,
infoHash: __classPrivateFieldGet(this, _P2PLoader_infoHash, "f"),
streamType: __classPrivateFieldGet(this, _P2PLoader_stream, "f").type,
warning: event.warning,
});
});
__classPrivateFieldGet(this, _P2PLoader_webtorrentManager, "f").addEventListener("error", (event) => {
__classPrivateFieldGet(this, _P2PLoader_webtorrentManagerLogger, "f").call(this, `Tracker error (${event.trackerUrl}):`, event.error);
__classPrivateFieldGet(this, _P2PLoader_onTrackerError, "f").call(this, {
trackerUrl: event.trackerUrl,
infoHash: __classPrivateFieldGet(this, _P2PLoader_infoHash, "f"),
streamType: __classPrivateFieldGet(this, _P2PLoader_stream, "f").type,
error: event.error,
});
});
__classPrivateFieldGet(this, _P2PLoader_eventTarget, "f").addEventListener(`onStorageUpdated-${__classPrivateFieldGet(this, _P2PLoader_streamSwarmId, "f")}`, this.broadcastAnnouncement);
__classPrivateFieldGet(this, _P2PLoader_webtorrentManager, "f").start();
__classPrivateFieldSet(this, _P2PLoader_churnCleanupTimeoutId, setTimeout(__classPrivateFieldGet(this, _P2PLoader_churnCleanup, "f"), Math.max(MIN_CHURN_CLEANUP_INTERVAL_MS, __classPrivateFieldGet(this, _P2PLoader_config, "f").p2pChurnCleanupIntervalMs)), "f");
}
downloadSegment(segment) {
const peersWithSegment = [];
for (const peer of __classPrivateFieldGet(this, _P2PLoader_peersMap, "f").values()) {
if (!peer.downloadingSegment &&
peer.getSegmentStatus(segment) === "loaded") {
peersWithSegment.push(peer);
}
}
if (peersWithSegment.length === 0)
return;
const selectedPeer = selectPeerForDownload(peersWithSegment);
const request = __classPrivateFieldGet(this, _P2PLoader_requests, "f").getOrCreateRequest(segment);
selectedPeer.downloadSegment(request);
}
isSegmentLoadingOrLoadedBySomeone(segment) {
for (const peer of __classPrivateFieldGet(this, _P2PLoader_peersMap, "f").values()) {
if (peer.getSegmentStatus(segment))
return true;
}
return false;
}
isSegmentLoadedBySomeone(segment) {
for (const peer of __classPrivateFieldGet(this, _P2PLoader_peersMap, "f").values()) {
if (peer.getSegmentStatus(segment) === "loaded")
return true;
}
return false;
}
get connectedPeerCount() {
return __classPrivateFieldGet(this, _P2PLoader_peersMap, "f").size;
}
*peers() {
for (const peer of __classPrivateFieldGet(this, _P2PLoader_peersMap, "f").values()) {
yield peer;
}
}
destroy() {
clearTimeout(__classPrivateFieldGet(this, _P2PLoader_churnCleanupTimeoutId, "f"));
__classPrivateFieldSet(this, _P2PLoader_churnCleanupTimeoutId, undefined, "f");
__classPrivateFieldGet(this, _P2PLoader_eventTarget, "f").removeEventListener(`onStorageUpdated-${__classPrivateFieldGet(this, _P2PLoader_streamSwarmId, "f")}`, this.broadcastAnnouncement);
// webtorrentManager.destroy() internally clears its event target and dispatches
// synchronous "peerDisconnected" events for active peers. These events trigger
// our #onPeerDisconnectedWebTorrent handler, which destroys peer wrappers and
// removes them from #peersMap. We destroy webtorrentManager first to prevent
// redundant WebRTC connection closing calls during the manual peer cleanup loop.
__classPrivateFieldGet(this, _P2PLoader_webtorrentManager, "f").destroy();
for (const peer of __classPrivateFieldGet(this, _P2PLoader_peersMap, "f").values()) {
peer.destroy();
}
__classPrivateFieldGet(this, _P2PLoader_peersMap, "f").clear();
}
}
_P2PLoader_webtorrentManager = new WeakMap(), _P2PLoader_peersMap = new WeakMap(), _P2PLoader_swarmId = new WeakMap(), _P2PLoader_streamSwarmId = new WeakMap(), _P2PLoader_isAnnounceMicrotaskCreated = new WeakMap(), _P2PLoader_webtorrentManagerLogger = new WeakMap(), _P2PLoader_churnLogger = new WeakMap(), _P2PLoader_infoHash = new WeakMap(), _P2PLoader_streamManifestUrl = new WeakMap(), _P2PLoader_stream = new WeakMap(), _P2PLoader_requests = new WeakMap(), _P2PLoader_segmentStorage = new WeakMap(), _P2PLoader_config = new WeakMap(), _P2PLoader_webTorrentSocketPool = new WeakMap(), _P2PLoader_eventTarget = new WeakMap(), _P2PLoader_onSegmentAnnouncement = new WeakMap(), _P2PLoader_churnCleanupTimeoutId = new WeakMap(), _P2PLoader_onPeerConnect = new WeakMap(), _P2PLoader_onPeerConnectError = new WeakMap(), _P2PLoader_onPeerClose = new WeakMap(), _P2PLoader_onPeerError = new WeakMap(), _P2PLoader_onPeerWarning = new WeakMap(), _P2PLoader_onTrackerWarning = new WeakMap(), _P2PLoader_onTrackerError = new WeakMap(), _P2PLoader_churnCleanup = new WeakMap(), _P2PLoader_onPeerConnectedWebTorrent = new WeakMap(), _P2PLoader_onPeerDisconnectedWebTorrent = new WeakMap(), _P2PLoader_sendSegmentsAnnouncement = new WeakMap(), _P2PLoader_onSegmentRequested = new WeakMap(), _P2PLoader_instances = new WeakSet(), _P2PLoader_getSegmentsAnnouncement = function _P2PLoader_getSegmentsAnnouncement() {
const loaded = __classPrivateFieldGet(this, _P2PLoader_segmentStorage, "f").getStoredSegmentIds(__classPrivateFieldGet(this, _P2PLoader_swarmId, "f"), __classPrivateFieldGet(this, _P2PLoader_streamSwarmId, "f"));
const httpLoading = [];
for (const request of __classPrivateFieldGet(this, _P2PLoader_requests, "f").httpRequests()) {
const segment = __classPrivateFieldGet(this, _P2PLoader_stream, "f").segments.get(request.segment.runtimeId);
if (!segment)
continue;
httpLoading.push(segment.externalId);
}
return { loaded, httpLoading };
};
export function selectPeerForDownload(peersWithSegment) {
if (peersWithSegment.length === 1) {
return peersWithSegment[0];
}
let maxSpeed = 0;
for (const peer of peersWithSegment) {
const speed = peer.getDownloadBandwidth();
if (speed > maxSpeed)
maxSpeed = speed;
}
if (maxSpeed > 0) {
const baseSpeed = Math.max(1, maxSpeed * 0.1);
let unprovenPeersCount = 0;
let provenPeersWeight = 0;
for (const peer of peersWithSegment) {
if (peer.getDownloadBandwidth() <= baseSpeed) {
unprovenPeersCount++;
}
else {
provenPeersWeight += peer.getDownloadBandwidth();
}
}
let adjustedBaseSpeed = baseSpeed;
if (unprovenPeersCount > 0 &&
provenPeersWeight > 0 &&
unprovenPeersCount * baseSpeed > provenPeersWeight) {
adjustedBaseSpeed = provenPeersWeight / unprovenPeersCount;
}
return Utils.getWeightedRandomItem(peersWithSegment, (peer) => Math.max(peer.getDownloadBandwidth(), adjustedBaseSpeed));
}
else {
return Utils.getRandomItem(peersWithSegment);
}
}
//# sourceMappingURL=loader.js.map