p2p-media-loader-core
Version:
P2P Media Loader core functionality
180 lines • 7.02 kB
JavaScript
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