diff --git a/packages/p2p-media-loader-core/src/p2p/peer.ts b/packages/p2p-media-loader-core/src/p2p/peer.ts index 2d127333..dfb4a4be 100644 --- a/packages/p2p-media-loader-core/src/p2p/peer.ts +++ b/packages/p2p-media-loader-core/src/p2p/peer.ts @@ -7,6 +7,7 @@ import { RequestError, RequestAbortErrorType, SegmentWithStream, + StreamType, } from "../types.js"; import * as Utils from "../utils/utils.js"; import * as Command from "./commands/index.js"; @@ -46,7 +47,8 @@ export class Peer { private readonly connection: PeerConnection, private readonly eventHandlers: PeerEventHandlers, private readonly peerConfig: PeerConfig, - eventTarget: EventTarget, + private readonly streamType: StreamType, + private readonly eventTarget: EventTarget, ) { this.onPeerClosed = eventTarget.getEventDispatcher("onPeerClose"); @@ -63,6 +65,7 @@ export class Peer { ); eventTarget.getEventDispatcher("onPeerConnect")({ peerId: this.id, + streamType, }); connection.on("error", this.onConnectionError); @@ -342,6 +345,11 @@ export class Peer { private onConnectionError = (error: Error) => { this.logger(`peer connection error ${this.id} %O`, error); + this.eventTarget.getEventDispatcher("onPeerError")({ + peerId: this.id, + streamType: this.streamType, + error, + }); const code = (error as { code?: string }).code; @@ -358,6 +366,7 @@ export class Peer { this.eventHandlers.onPeerClosed(this); this.onPeerClosed({ peerId: this.id, + streamType: this.streamType, }); this.logger(`peer closed ${this.id}`); }; diff --git a/packages/p2p-media-loader-core/src/p2p/tracker-client.ts b/packages/p2p-media-loader-core/src/p2p/tracker-client.ts index f21b0050..22c910e3 100644 --- a/packages/p2p-media-loader-core/src/p2p/tracker-client.ts +++ b/packages/p2p-media-loader-core/src/p2p/tracker-client.ts @@ -34,7 +34,7 @@ export class P2PTrackerClient { constructor( streamSwarmId: string, - stream: StreamWithSegments, + private readonly stream: StreamWithSegments, private readonly eventHandlers: P2PTrackerClientEventHandlers, private readonly config: StreamConfig, private readonly eventTarget: EventTarget, @@ -104,6 +104,7 @@ export class P2PTrackerClient { onSegmentsAnnouncement: this.eventHandlers.onSegmentsAnnouncement, }, this.config, + this.stream.type, this.eventTarget, ); this.logger( @@ -118,11 +119,19 @@ export class P2PTrackerClient { ) => { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions this.logger(`tracker warning (${this.streamShortId}: ${warning})`); + this.eventTarget.getEventDispatcher("onTrackerWarning")({ + streamType: this.stream.type, + warning, + }); }; private onTrackerClientError: TrackerClientEvents["error"] = (error) => { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions this.logger(`tracker error (${this.streamShortId}: ${error})`); + this.eventTarget.getEventDispatcher("onTrackerError")({ + streamType: this.stream.type, + error, + }); }; *peers() { diff --git a/packages/p2p-media-loader-core/src/requests/request.ts b/packages/p2p-media-loader-core/src/requests/request.ts index dfea86f7..181c2bc0 100644 --- a/packages/p2p-media-loader-core/src/requests/request.ts +++ b/packages/p2p-media-loader-core/src/requests/request.ts @@ -215,6 +215,7 @@ export class Request { this.currentAttempt?.downloadSource === "p2p" ? this.currentAttempt.peerId : undefined, + streamType: this.segment.stream.type, }); this._abortRequestCallback = undefined; this.manageBandwidthCalculatorsState("stop"); @@ -243,6 +244,7 @@ export class Request { this.currentAttempt.downloadSource === "p2p" ? this.currentAttempt.peerId : undefined, + streamType: this.segment.stream.type, }); this.notReceivingBytesTimeout.clear(); this.manageBandwidthCalculatorsState("stop"); @@ -269,6 +271,7 @@ export class Request { this.currentAttempt.downloadSource === "p2p" ? this.currentAttempt.peerId : undefined, + streamType: this.segment.stream.type, }); this.notReceivingBytesTimeout.clear(); this.manageBandwidthCalculatorsState("stop"); @@ -285,12 +288,14 @@ export class Request { this.setStatus("succeed"); this._totalBytes = this._loadedBytes; this.onSegmentLoaded({ + segmentUrl: this.segment.url, bytesLength: this.finalData.byteLength, downloadSource: this.currentAttempt.downloadSource, peerId: this.currentAttempt.downloadSource === "p2p" ? this.currentAttempt.peerId : undefined, + streamType: this.segment.stream.type, }); this.logger( diff --git a/packages/p2p-media-loader-core/src/types.ts b/packages/p2p-media-loader-core/src/types.ts index 5da7e9a7..e15c3f87 100644 --- a/packages/p2p-media-loader-core/src/types.ts +++ b/packages/p2p-media-loader-core/src/types.ts @@ -433,6 +433,9 @@ export type SegmentErrorDetails = { /** The peer ID, if the segment was downloaded from a peer. */ peerId: string | undefined; + + /** The type of stream that the segment is associated with. */ + streamType: StreamType; }; /** Represents details about a segment abort event. */ @@ -445,10 +448,16 @@ export type SegmentAbortDetails = { /** The peer ID, if the segment was downloaded from a peer. */ peerId: string | undefined; + + /** The type of stream that the segment is associated with. */ + streamType: StreamType; }; /** Represents the details about a loaded segment. */ export type SegmentLoadDetails = { + /** The URL of the loaded segment */ + segmentUrl: string; + /** The length of the segment in bytes. */ bytesLength: number; @@ -457,12 +466,42 @@ export type SegmentLoadDetails = { /** The peer ID, if the segment was downloaded from a peer. */ peerId: string | undefined; + + /** The segment that the event is about. */ + streamType: StreamType; }; /** Represents the details of a peer in a peer-to-peer network. */ export type PeerDetails = { /** The unique identifier for a peer in the network. */ peerId: string; + /** The type of stream that the peer is connected to. */ + streamType: StreamType; +}; + +/** Represents the details of a peer error event. */ +export type PeerErrorDetails = { + /** The unique identifier for a peer in the network. */ + peerId: string; + /** The type of stream that the peer is connected to. */ + streamType: StreamType; + /** The error that occurred during the peer-to-peer connection. */ + error: Error; +}; + +/** Represents the details of a tracker error event. */ +export type TrackerErrorDetails = { + /** The type of stream that the tracker is for. */ + streamType: StreamType; + /** The error that occurred during the tracker request. */ + error: unknown; +}; + +export type TrackerWarningDetails = { + /** The type of stream that the tracker is for. */ + streamType: StreamType; + /** The warning that occurred during the tracker request. */ + warning: unknown; }; /** @@ -501,17 +540,24 @@ export type CoreEventMap = { /** * Occurs when a new peer-to-peer connection is established. * - * @param peerId - The unique identifier of the peer that has just connected. + * @param params - Contains details about the peer that the event is about. */ onPeerConnect: (params: PeerDetails) => void; /** * Triggered when an existing peer-to-peer connection is closed. * - * @param peerId - The unique identifier of the peer whose connection has been closed. + * @param params - Contains details about the peer that the event is about. */ onPeerClose: (params: PeerDetails) => void; + /** + * Triggered when an error occurs during a peer-to-peer connection. + * + * @param params - Contains details about the error and the peer that the event is about. + */ + onPeerError: (params: PeerErrorDetails) => void; + /** * Invoked after a chunk of data from a segment has been successfully downloaded. * @@ -532,6 +578,20 @@ export type CoreEventMap = { * @param peerId - The peer ID, if the segment was downloaded from a peer */ onChunkUploaded: (bytesLength: number, peerId: string) => void; + + /** + * Called when an error occurs during the tracker request process. + * + * @param params - Contains information about the tracker error. + */ + onTrackerError: (params: TrackerErrorDetails) => void; + + /** + * Called when a warning occurs during the tracker request process. + * + * @param params - Contains information about the tracker warning. + */ + onTrackerWarning: (params: TrackerWarningDetails) => void; }; /** Defines the types of errors that can occur during a request abortion process. */ diff --git a/packages/p2p-media-loader-demo/src/components/P2PVideoDemo.tsx b/packages/p2p-media-loader-demo/src/components/P2PVideoDemo.tsx index 8067c8c1..d31234cf 100644 --- a/packages/p2p-media-loader-demo/src/components/P2PVideoDemo.tsx +++ b/packages/p2p-media-loader-demo/src/components/P2PVideoDemo.tsx @@ -91,12 +91,16 @@ export const P2PVideoDemo = ({ debugToolsEnabled = false }: DemoProps) => { }, []); const onPeerConnect = useCallback((params: PeerDetails) => { + if (params.streamType !== "main") return; + setPeers((peers) => { return [...peers, params.peerId]; }); }, []); const onPeerClose = useCallback((params: PeerDetails) => { + if (params.streamType !== "main") return; + setPeers((peers) => { return peers.filter((peer) => peer !== params.peerId); });