UNPKG

p2p-media-loader-core

Version:
180 lines 7.02 kB
import TrackerClient from "bittorrent-tracker"; import debug from "debug"; import * as PeerUtil from "../utils/peer.js"; import * as LoggerUtils from "../utils/logger.js"; import { Peer } from "./peer.js"; import { utf8ToUintArray } from "../utils/utils.js"; function isSafariOrWkWebview() { const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); const isWkWebview = /\b(iPad|iPhone|Macintosh).*AppleWebKit(?!.*Safari)/i.test(navigator.userAgent); return isSafari || isWkWebview; } export class P2PTrackerClient { constructor(streamSwarmId, stream, eventHandlers, config, eventTarget) { Object.defineProperty(this, "stream", { enumerable: true, configurable: true, writable: true, value: stream }); Object.defineProperty(this, "eventHandlers", { enumerable: true, configurable: true, writable: true, value: eventHandlers }); 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, "streamShortId", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "client", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_peers", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "logger", { enumerable: true, configurable: true, writable: true, value: debug("p2pml-core:p2p-tracker-client") }); Object.defineProperty(this, "onReceivePeerConnection", { enumerable: true, configurable: true, writable: true, value: (peerConnection) => { const itemId = Peer.getPeerIdFromConnection(peerConnection); let peerItem = this._peers.get(itemId); if (peerItem?.peer) { peerConnection.destroy(); return; } if (!peerItem) { peerItem = { potentialConnections: new Set() }; peerConnection.idUtf8 = itemId; this._peers.set(itemId, peerItem); } peerItem.potentialConnections.add(peerConnection); peerConnection.on("connect", () => { if (peerItem.peer) return; for (const connection of peerItem.potentialConnections) { if (connection !== peerConnection) connection.destroy(); } peerItem.potentialConnections.clear(); peerItem.peer = new Peer(peerConnection, { onPeerClosed: this.onPeerClosed, onSegmentRequested: this.eventHandlers.onSegmentRequested, onSegmentsAnnouncement: this.eventHandlers.onSegmentsAnnouncement, }, this.config, this.stream.type, this.eventTarget); this.logger(`connected with peer: ${peerItem.peer.id} ${this.streamShortId}`); this.eventHandlers.onPeerConnected(peerItem.peer); }); } }); Object.defineProperty(this, "onTrackerClientWarning", { enumerable: true, configurable: true, writable: true, value: (warning) => { this.logger("tracker warning %s:", this.streamShortId, warning); this.eventTarget.getEventDispatcher("onTrackerWarning")({ streamType: this.stream.type, warning, }); } }); Object.defineProperty(this, "onTrackerClientError", { enumerable: true, configurable: true, writable: true, value: (error) => { this.logger("tracker error in stream %s:", this.streamShortId, error); this.eventTarget.getEventDispatcher("onTrackerError")({ streamType: this.stream.type, error, }); } }); Object.defineProperty(this, "onPeerClosed", { enumerable: true, configurable: true, writable: true, value: (peer) => { this.logger(`peer closed: ${peer.id}`); this._peers.delete(peer.id); } }); const streamHash = PeerUtil.getStreamHash(streamSwarmId); this.streamShortId = LoggerUtils.getStreamString(stream); let peerId = P2PTrackerClient.PEER_ID_BY_INFO_HASH.get(streamHash); if (!peerId) { peerId = PeerUtil.generatePeerId(config.trackerClientVersionPrefix); P2PTrackerClient.PEER_ID_BY_INFO_HASH.set(streamHash, peerId); } this.client = new TrackerClient({ infoHash: utf8ToUintArray(streamHash), peerId: utf8ToUintArray(peerId), announce: isSafariOrWkWebview() ? config.announceTrackers.slice(0, 1) // Safari has issues with multiple trackers : config.announceTrackers, rtcConfig: this.config.rtcConfig, }); this.client.on("peer", this.onReceivePeerConnection); this.client.on("warning", this.onTrackerClientWarning); this.client.on("error", this.onTrackerClientError); this.logger(`create new client; \nstream: ${this.streamShortId}; hash: ${streamHash}\npeerId: ${peerId}`); } start() { this.client.start(); } destroy() { this.client.destroy(); for (const { peer, potentialConnections } of this._peers.values()) { peer?.destroy(); for (const connection of potentialConnections) { connection.destroy(); } } this._peers.clear(); this.logger("destroy client; stream:", this.streamShortId); } *peers() { for (const peerItem of this._peers.values()) { if (peerItem.peer) yield peerItem.peer; } } static clearPeerIdCache() { P2PTrackerClient.PEER_ID_BY_INFO_HASH.clear(); } } Object.defineProperty(P2PTrackerClient, "PEER_ID_BY_INFO_HASH", { enumerable: true, configurable: true, writable: true, value: new Map() }); //# sourceMappingURL=tracker-client.js.map