Refactor Channels and Streams APIs: Update Member streams type to an array, enhance error handling in publishUri method, and add extractMediaType utility function.

This commit is contained in:
2025-12-07 04:41:30 -05:00
parent f1c1d3ec92
commit 4673547b95
2 changed files with 50 additions and 27 deletions

View File

@@ -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<ChannelAlias, Channel> = new Map();
@@ -195,22 +193,23 @@ export class Channels {
}
public async getPublishSourceStreamId(channelId: string, retryCount: number = 3): Promise<string | null> {
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<ForkChannelResponse> {

View File

@@ -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<IResponse<string>>(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;
}
}
}