From 4673547b95458a0f462df5d93795bfefd8ca6dc5 Mon Sep 17 00:00:00 2001 From: Alexander Zinn Date: Sun, 7 Dec 2025 04:41:30 -0500 Subject: [PATCH] Refactor Channels and Streams APIs: Update Member streams type to an array, enhance error handling in publishUri method, and add extractMediaType utility function. --- src/apis/Channels.ts | 49 ++++++++++++++++++++++---------------------- src/apis/Stream.ts | 28 +++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 27 deletions(-) diff --git a/src/apis/Channels.ts b/src/apis/Channels.ts index 8b49b54..2bf8655 100644 --- a/src/apis/Channels.ts +++ b/src/apis/Channels.ts @@ -19,14 +19,12 @@ export type Member = { sessionId: string; screenName: string; role: string; - streams: [ - { - type: string; - uri: string; - audioState: string; - videoState: string; - } - ]; + streams: { + type: string; + uri: string; + audioState: string; + videoState: string; + }[]; state: string; lastUpdate: number; }; @@ -36,6 +34,16 @@ type GetChannelParams = { channelId?: string; }; +type KillChannelResponse = { + status: string; + killedMembers: Member[]; +}; + +type ForkChannelResponse = { + status: string; + members: Member[]; +}; + export class ChannelError extends Error { constructor( message: string, @@ -46,16 +54,6 @@ export class ChannelError extends Error { } } -type KillChannelResponse = { - status: string; - killedMembers: Member[]; -}; - -type ForkChannelResponse = { - status: string; - members: Member[]; -}; - export class Channels { private readonly _httpRequests: PCastHttpRequests; private readonly _channelsByAlias: Map = new Map(); @@ -195,22 +193,23 @@ export class Channels { } public async getPublishSourceStreamId(channelId: string, retryCount: number = 3): Promise { - const retryCountRemaining = retryCount || 3; const channelMembers = await this.getMembers(channelId); - + console.log('channelMembers [%o] retryCount [%d]', channelMembers, retryCount); if (channelMembers.length === 0) { - if (retryCountRemaining > 0) { - return this.getPublishSourceStreamId(channelId, retryCountRemaining - 1); + if (retryCount > 0) { + + return this.getPublishSourceStreamId(channelId, retryCount - 1); } return null; } + const presenter = channelMembers.find(member => member.role === 'Presenter'); if (!presenter) { - if (retryCountRemaining > 0) { - return this.getPublishSourceStreamId(channelId, retryCountRemaining - 1); + if (retryCount > 0) { + return this.getPublishSourceStreamId(channelId, retryCount - 1); } return null; @@ -218,7 +217,7 @@ export class Channels { const publishSourceStreamIdRegExp = /pcast:\/\/.*\/([^?]*)/; - return presenter.streams[0].uri.match(publishSourceStreamIdRegExp)?.[1] ?? null; + return presenter.streams[0]?.uri?.match(publishSourceStreamIdRegExp)?.[1] ?? null; } public async fork(sourceChannelId: string, destinationChannelId: string): Promise { diff --git a/src/apis/Stream.ts b/src/apis/Stream.ts index 79cea8b..5003859 100644 --- a/src/apis/Stream.ts +++ b/src/apis/Stream.ts @@ -10,10 +10,10 @@ export class Streams { } public async publishUri(mediaUri: string, token: string) { - const mediaType = mediaUri.split('.')?.at(-1); + const mediaType = this.extractMediaType(mediaUri); if (!mediaType) { - throw new Error('Invalid media URI no media type found'); + throw new Error('Invalid media URI: no media type found'); } const response = await this._httpRequests.request>(HttpMethod.PUT, `/stream/publish/uri/${mediaType}`, { @@ -26,4 +26,28 @@ export class Streams { return response; } + + private extractMediaType(uri: string): string | undefined { + try { + const url = new URL(uri); + const pathname = url.pathname; + const lastDotIndex = pathname.lastIndexOf('.'); + + if (lastDotIndex === -1 || lastDotIndex === pathname.length - 1) { + return undefined; + } + + return pathname.slice(lastDotIndex + 1).toLowerCase(); + } catch { + // Fallback for non-URL strings (e.g., relative paths) + const cleanUri = uri.split('?')[0]?.split('#')[0]; + const lastDotIndex = cleanUri?.lastIndexOf('.'); + + if (lastDotIndex === -1 || lastDotIndex === cleanUri?.length || lastDotIndex === undefined) { + return undefined; + } + + return cleanUri?.slice(lastDotIndex + 1).toLowerCase() ?? undefined; + } + } }