UNPKG

p2p-media-loader-core

Version:
192 lines 7.84 kB
import { P2PTrackerClient } from "./tracker-client.js"; import * as StreamUtils from "../utils/stream.js"; import * as Utils from "../utils/utils.js"; export class P2PLoader { constructor(streamManifestUrl, stream, requests, segmentStorage, config, eventTarget, onSegmentAnnouncement) { Object.defineProperty(this, "streamManifestUrl", { enumerable: true, configurable: true, writable: true, value: streamManifestUrl }); Object.defineProperty(this, "stream", { enumerable: true, configurable: true, writable: true, value: stream }); Object.defineProperty(this, "requests", { enumerable: true, configurable: true, writable: true, value: requests }); Object.defineProperty(this, "segmentStorage", { enumerable: true, configurable: true, writable: true, value: segmentStorage }); Object.defineProperty(this, "config", { enumerable: true, configurable: true, writable: true, value: config }); Object.defineProperty(this, "eventTarget", { enumerable: true, configurable: true, writable: true, value: eventTarget }); Object.defineProperty(this, "onSegmentAnnouncement", { enumerable: true, configurable: true, writable: true, value: onSegmentAnnouncement }); Object.defineProperty(this, "trackerClient", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "isAnnounceMicrotaskCreated", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "onPeerConnected", { enumerable: true, configurable: true, writable: true, value: (peer) => { if (this.config.isP2PUploadDisabled) return; const { httpLoading, loaded } = this.getSegmentsAnnouncement(); peer.sendSegmentsAnnouncementCommand(loaded, httpLoading); } }); Object.defineProperty(this, "broadcastAnnouncement", { enumerable: true, configurable: true, writable: true, value: (sendEmptyAnnouncement = false) => { if (sendEmptyAnnouncement) { this.sendSegmentsAnnouncement([], []); return; } if (this.isAnnounceMicrotaskCreated || this.config.isP2PUploadDisabled) { return; } const { loaded, httpLoading } = this.getSegmentsAnnouncement(); this.sendSegmentsAnnouncement(loaded, httpLoading); } }); Object.defineProperty(this, "sendSegmentsAnnouncement", { enumerable: true, configurable: true, writable: true, value: (loaded, httpLoading) => { this.isAnnounceMicrotaskCreated = true; queueMicrotask(() => { for (const peer of this.trackerClient.peers()) { peer.sendSegmentsAnnouncementCommand(loaded, httpLoading); } this.isAnnounceMicrotaskCreated = false; }); } }); Object.defineProperty(this, "onSegmentRequested", { enumerable: true, configurable: true, writable: true, value: async (peer, segmentExternalId, requestId, byteFrom) => { const segment = StreamUtils.getSegmentFromStreamByExternalId(this.stream, segmentExternalId); if (!segment) return; if (this.config.isP2PUploadDisabled) { peer.sendSegmentAbsentCommand(segmentExternalId, requestId); return; } const swarmId = this.config.swarmId ?? this.streamManifestUrl; const streamSwarmId = StreamUtils.getStreamSwarmId(swarmId, this.stream); const segmentData = await this.segmentStorage.getSegmentData(swarmId, streamSwarmId, segment.externalId); if (!segmentData) { peer.sendSegmentAbsentCommand(segmentExternalId, requestId); return; } await peer.uploadSegmentData(segment, requestId, byteFrom !== undefined ? segmentData.slice(byteFrom) : segmentData); } }); const swarmId = this.config.swarmId ?? this.streamManifestUrl; const streamSwarmId = StreamUtils.getStreamSwarmId(swarmId, this.stream); this.trackerClient = new P2PTrackerClient(streamSwarmId, this.stream, { onPeerConnected: this.onPeerConnected, // eslint-disable-next-line @typescript-eslint/no-misused-promises onSegmentRequested: this.onSegmentRequested, onSegmentsAnnouncement: this.onSegmentAnnouncement, }, this.config, this.eventTarget); this.eventTarget.addEventListener(`onStorageUpdated-${streamSwarmId}`, this.broadcastAnnouncement); this.segmentStorage.setSegmentChangeCallback((streamId) => { this.eventTarget.dispatchEvent(`onStorageUpdated-${streamId}`); }); this.trackerClient.start(); } downloadSegment(segment) { const peersWithSegment = []; for (const peer of this.trackerClient.peers()) { if (!peer.downloadingSegment && peer.getSegmentStatus(segment) === "loaded") { peersWithSegment.push(peer); } } if (peersWithSegment.length === 0) return; const peer = Utils.getRandomItem(peersWithSegment); const request = this.requests.getOrCreateRequest(segment); peer.downloadSegment(request); } isSegmentLoadingOrLoadedBySomeone(segment) { for (const peer of this.trackerClient.peers()) { if (peer.getSegmentStatus(segment)) return true; } return false; } isSegmentLoadedBySomeone(segment) { for (const peer of this.trackerClient.peers()) { if (peer.getSegmentStatus(segment) === "loaded") return true; } return false; } get connectedPeerCount() { let count = 0; const iterator = this.trackerClient.peers(); while (!iterator.next().done) count++; return count; } getSegmentsAnnouncement() { const swarmId = this.config.swarmId ?? this.streamManifestUrl; const streamSwarmId = StreamUtils.getStreamSwarmId(swarmId, this.stream); const loaded = this.segmentStorage.getStoredSegmentIds(swarmId, streamSwarmId); const httpLoading = []; for (const request of this.requests.httpRequests()) { const segment = this.stream.segments.get(request.segment.runtimeId); if (!segment) continue; httpLoading.push(segment.externalId); } return { loaded, httpLoading }; } destroy() { const swarmId = this.config.swarmId ?? this.streamManifestUrl; const streamSwarmId = StreamUtils.getStreamSwarmId(swarmId, this.stream); this.eventTarget.removeEventListener(`onStorageUpdated-${streamSwarmId}`, this.broadcastAnnouncement); this.trackerClient.destroy(); } } //# sourceMappingURL=loader.js.map