update legacy apis

This commit is contained in:
2025-09-01 21:21:45 -04:00
parent bbed095926
commit d0f3af72f6
12 changed files with 160 additions and 63 deletions

View File

@@ -2,7 +2,7 @@
"arrowParens": "avoid",
"bracketSameLine": true,
"bracketSpacing": false,
"printWidth": 160,
"printWidth": 180,
"semi": true,
"singleAttributePerLine": false,
"singleQuote": true,

View File

@@ -1,4 +1,4 @@
import PCastApi from '../../src/index';
import PCastApi, {ReportKind} from '../../src/index';
const pcastUri = 'https://pcast-stg.phenixrts.com';
const application = {
@@ -6,34 +6,11 @@ const application = {
secret: 'AMAsDzr.dIuGMZ.Zu52Dt~MQvP!DZwYg'
};
const pcastApi = PCastApi.create(pcastUri, application);
const viewingReportRealTime = await pcastApi.generateViewingReport('RealTime', new Date('2025-08-02'), new Date('2025-09-01'), {});
const publishingReport = await pcastApi.reporting.generateReport(ReportKind.Publishing, {
start: '2025-08-02',
end: '2025-09-01'
});
console.log('pcastApi [%o]', pcastApi);
console.log();
console.log('ChannelsApi [%o]', pcastApi.channels);
const channelsList = await pcastApi.channels.list();
// const start = hrtime.bigint();
// const publishingReportCsv = await pcastApi.reporting.generateReport(ReportKind.Publishing, {
// start: moment().subtract(1, 'day').toISOString(),
// end: moment().toISOString(),
// applicationIds: [application.id]
// });
// const endPublishing = hrtime.bigint();
// console.log(publishingReportCsv);
// console.log(`Time taken: ${Number(endPublishing - start) / 1_000_000_000} seconds`);
// const viewingReportCsv = await pcastApi.reporting.generateReport(ReportKind.Viewing, {
// kind: ViewingReportKind.HLS,
// start: moment().subtract(1, 'day').toISOString(),
// end: moment().toISOString(),
// applicationIds: [application.id]
// });
// const endViewing = hrtime.bigint();
// console.log(`Time taken: ${Number(endViewing - endPublishing) / 1_000_000_000} seconds`);
// // const viewingReport = await CsvParser.parse(viewingReportCsv);
// console.log(viewingReportCsv);
const channelMembers = await pcastApi.channels.getMembers(channelsList[1].channelId);
console.log('[%o]', channelMembers);
console.log('[%o]', channelMembers.streams);
// console.log(viewingReportRealTime);
console.log(publishingReport);

View File

@@ -1,6 +1,6 @@
{
"name": "@techniker-me/pcast-api",
"version": "2025.1.4",
"version": "2025.1.5",
"type": "module",
"scripts": {
"ci-build": "bun run build",
@@ -9,7 +9,7 @@
"test:coverage": "bun test --coverage",
"preformat": "bun install",
"format": "prettier --write ./",
"prelint": "bun format",
"prelint:fix": "bun format",
"lint": "eslint src/**/*.ts",
"lint:fix": "eslint src/**/*.ts --fix",
"build:node": "bun build src/index.ts --outdir dist/node --target node --format esm --production",

View File

@@ -1,4 +1,5 @@
import {Channels, Streams, type ApplicationCredentials, PCastHttpRequests, Reporting} from './apis';
import {Channels, Streams, type ApplicationCredentials, PCastHttpRequests, Reporting, Channel, Member} from './apis';
import {ReportKind, ViewingReportKindMapping, type ViewingReportKindType} from './apis/Reporting';
export class PCastApi {
private readonly _channels: Channels;
@@ -12,7 +13,8 @@ export class PCastApi {
}
public static create(pcastUri: string, applicationCredentials: ApplicationCredentials): PCastApi {
const pcastHttpRequests = new PCastHttpRequests(pcastUri.replace(/\/+$/, '').endsWith('/pcast') ? pcastUri : `${pcastUri}/pcast`, applicationCredentials);
const pcastUriWithSuffix = pcastUri.replace(/\/+$/, '').endsWith('/pcast') ? pcastUri : `${pcastUri}/pcast`;
const pcastHttpRequests = new PCastHttpRequests(pcastUriWithSuffix, applicationCredentials);
const channels = new Channels(pcastHttpRequests);
const streams = new Streams(pcastHttpRequests);
const reporting = new Reporting(pcastHttpRequests);
@@ -31,4 +33,55 @@ export class PCastApi {
get reporting(): Reporting {
return this._reporting;
}
// ====== Legacy API =======
// @deprecated - use pcastApi.channels.create instead
public async createChannel(channelName: string, channelDescription: string, channelOptions: string[] = []): Promise<Channel> {
return this._channels.create(channelName, channelDescription, channelOptions);
}
// @deprecated - use pcastApi.channels.get instead
public async getChannelInfoByAlias(alias: string): Promise<Channel | undefined> {
return this._channels.get({alias});
}
// @deprecated - use pcastApi.channels.get instead
public async getChannelInfoByChannelId(channelId: string): Promise<Channel | undefined> {
return this._channels.get({channelId});
}
// @deprecated - use pcastApi.channels.getPublisherCount instead
public async getChannelPublisherCount(channelId: string): Promise<number> {
return this._channels.getPublisherCount(channelId);
}
// @deprecated - use pcastApi.channels.getMembers instead
public async getChannelMembers(channelId: string): Promise<Member[]> {
return this._channels.getMembers(channelId);
}
// @deprecated - use pcastApi.channels.getMembersByChannelAlias instead
public async getChannelMembersByChannelAlias(alias: string): Promise<Member[]> {
return this._channels.getMembersByChannelAlias(alias);
}
// @deprecated - use pcastApi.channels.delete instead
public async deleteChannel(channelId: string): Promise<Channel> {
return this._channels.delete({channelId});
}
// @deprecated - use pcastApi.reporting.generateReport instead
public async generateViewingReport(
kind: ViewingReportKindType,
reportStartTimestamp: Date,
reportEndTimestamp: Date,
options: Record<string, string | string[]> = {}
): Promise<string> {
return this._reporting.generateReport(ReportKind.Viewing, {
kind: ViewingReportKindMapping.convertViewingReportKindTypeToViewingReportKind(kind),
start: reportStartTimestamp.toISOString(),
end: reportEndTimestamp.toISOString(),
...options
});
}
}

View File

@@ -55,7 +55,7 @@ export class Channels {
this.initialize();
}
public async createChannel(name: string, description: string, channelOptions: string[] = []): Promise<Channel> {
public async create(name: string, description: string, channelOptions: string[] = []): Promise<Channel> {
if (!name || name.trim().length === 0) {
throw new ChannelError('Channel name cannot be empty', 'INVALID_NAME');
}
@@ -186,14 +186,42 @@ export class Channels {
return deletedChannel;
}
public clearCache(): void {
this._channelsByAlias.clear();
async getPublishSourceStreamId(channelId: string, retryCount: number = 3): Promise<string | null> {
const retryCountRemaining = retryCount || 3;
const channelMembers = await this.getMembers(channelId);
if (channelMembers.length === 0) {
if (retryCountRemaining > 0) {
return this.getPublishSourceStreamId(channelId, retryCountRemaining - 1);
}
public getCacheSize(): number {
return this._channelsByAlias.size;
return null;
}
const presenter = channelMembers.find(member => member.role === 'Presenter');
if (!presenter) {
if (retryCountRemaining > 0) {
return this.getPublishSourceStreamId(channelId, retryCountRemaining - 1);
}
return null;
}
const publishSourceStreamIdRegExp = /pcast:\/\/.*\/([^?]*)/;
return presenter.streams[0].uri.match(publishSourceStreamIdRegExp)?.[1] ?? null;
}
// TODO(AZ): Implement this
// public async fork(channelId: string): Promise<Channel>
// TODO(AZ): Implement this
// public async killChannel(channelId: string): Promise<Channel>
// TODO(AZ): Implement this
// public async publishViaUrl(mediaUriToPublish: string, token: string): Promise<Channel>
private async initialize(): Promise<void> {
try {
const channelsList = await this.list();

View File

@@ -1,11 +1,12 @@
import assertUnreachable from '../lang/assertUnreachable';
import assertUnreachable from '../../lang/assertUnreachable';
export enum ReportKind {
Publishing = 0,
Viewing = 1
Viewing = 1,
Ingest = 2
}
export type ReportKindType = 'Publishing' | 'Viewing';
export type ReportKindType = 'Publishing' | 'Viewing' | 'IngestReport';
export class ReportKindMapping {
public static convertReportKindTypeToReportKind(reportKindType: ReportKindType): ReportKind {
@@ -14,6 +15,8 @@ export class ReportKindMapping {
return ReportKind.Publishing;
case 'Viewing':
return ReportKind.Viewing;
case 'IngestReport':
return ReportKind.Ingest;
default:
assertUnreachable(reportKindType);
@@ -26,6 +29,8 @@ export class ReportKindMapping {
return 'Publishing';
case ReportKind.Viewing:
return 'Viewing';
case ReportKind.Ingest:
return 'IngestReport';
default:
assertUnreachable(reportKind);

View File

@@ -1,6 +1,6 @@
import {HttpMethod} from '../net/http/HttpMethod';
import type {PCastHttpRequests} from './PCastRequests';
import assertUnreachable from '../lang/assertUnreachable';
import {HttpMethod} from '../../net/http/HttpMethod';
import type {PCastHttpRequests} from '../PCastRequests';
import assertUnreachable from '../../lang/assertUnreachable';
import {ReportKind} from './ReportKind';
import {ViewingReportKind, ViewingReportKindMapping} from './ViewingReportKind';
@@ -32,6 +32,17 @@ export type ViewingReportOptions = {
end: string;
};
export type IngestBufferUnderrunOptions = {
kind: 'BufferUnderrun'; // Are there more kinds? https://phenixrts.com/docs/sdk_ref/rest-api/reporting/#generate-an-ingest-report
applicationIds?: string[];
streamIds?: string[];
channelIds?: string[];
channelAliases?: string[];
ingestIds?: string[];
start: string;
end: string;
};
export class Reporting {
private readonly _httpRequests: PCastHttpRequests;
@@ -39,12 +50,17 @@ export class Reporting {
this._httpRequests = httpRequests;
}
public async generateReport<ReportOptions extends PublishingReportOptions | ViewingReportOptions>(kind: ReportKind, options: ReportOptions): Promise<string> {
public async generateReport<ReportOptions extends PublishingReportOptions | ViewingReportOptions | IngestBufferUnderrunOptions>(
kind: ReportKind,
options: ReportOptions
): Promise<string> {
switch (kind) {
case ReportKind.Publishing:
return this.requestPublishingReport(options as PublishingReportOptions);
case ReportKind.Viewing:
return this.requestViewingReport(options as ViewingReportOptions);
case ReportKind.Ingest:
return this.requestIngestBufferUnderrun(options as IngestBufferUnderrunOptions);
default:
assertUnreachable(kind);
}
@@ -54,12 +70,12 @@ export class Reporting {
if (!options.start || !options.end) {
throw new Error('[Reporting] [requestPublishingReport] requires a start and end Date');
}
const publishingReportOptions = {
const publishingReport = {
...options
};
const requestPublishingOptions = {
body: JSON.stringify({publishingReport: publishingReportOptions})
body: JSON.stringify({publishingReport: publishingReport})
};
const response = await this._httpRequests.request<string>(HttpMethod.PUT, '/reporting/publishing', requestPublishingOptions);
@@ -67,17 +83,36 @@ export class Reporting {
}
private async requestViewingReport(options: ViewingReportOptions): Promise<string> {
const viewingReportOptions = {
const viewingReport = {
...options,
tags: options.tags ?? [],
originTags: options.originTags ?? [],
kind: ViewingReportKindMapping.convertViewingReportKindToViewingReportKindType(options.kind)
};
console.log(viewingReport);
const requestViewingOptions = {
body: JSON.stringify({viewingReport: viewingReportOptions})
body: JSON.stringify({viewingReport: viewingReport})
};
const response = await this._httpRequests.request<string>(HttpMethod.PUT, '/reporting/viewing', requestViewingOptions);
return response;
}
private async requestIngestBufferUnderrun(options: IngestBufferUnderrunOptions): Promise<string> {
const ingestReport = {
...options,
kind: 'BufferUnderrun'
};
const requestViewingOptions = {
body: JSON.stringify({ingestReport})
};
const response = await this._httpRequests.request<string>(HttpMethod.PUT, '/reporting/ingest', requestViewingOptions);
return response;
}
}

View File

@@ -1,4 +1,4 @@
import assertUnreachable from '../lang/assertUnreachable';
import assertUnreachable from '../../lang/assertUnreachable';
export enum ViewingReportKind {
RealTime = 0,

View File

@@ -0,0 +1,3 @@
export * from './Reporting';
export * from './ReportKind';
export * from './ViewingReportKind';

View File

@@ -2,5 +2,4 @@ export * from './Channels';
export * from './Reporting';
export * from './PCastRequests';
export * from './Stream';
export * from './ReportKind';
export * from './ViewingReportKind';
export * from './Reporting';

View File

@@ -1,16 +1,13 @@
import {PCastApi} from './PCastApi';
import type {Channels, Streams, Reporting, ReportKind, ViewingReportKind} from './apis';
import {Channels, Streams, Reporting, PublishingReportOptions, ViewingReportOptions, ReportKindType, ViewingReportKindType, ViewingReportKind, ReportKind} from './apis';
import type {ChannelId, Channel, ChannelAlias, Member, ChannelError} from './apis/Channels';
import type {HttpMethod} from './net/http/HttpMethod';
import type {HttpRequestError} from './net/http/HttpRequests';
import type {ChannelResponse, ChannelsResponse, MembersResponse} from './apis/IResponse';
import type {ApplicationCredentials} from './apis/PCastRequests';
import type {PublishingReportOptions, ViewingReportOptions} from './apis/Reporting';
import type {ReportKindType} from './apis/ReportKind';
import type {ViewingReportKindType} from './apis/ViewingReportKind';
export type {Channels, Streams, Reporting, ReportKind, ViewingReportKind};
export type {Channels, Streams, Reporting, ViewingReportKind};
export type {ChannelId, Channel, ChannelAlias, Member, ChannelError};
export type {HttpMethod, HttpRequestError};
export type {ChannelResponse, ChannelsResponse, MembersResponse};
@@ -18,5 +15,5 @@ export type {ApplicationCredentials};
export type {PublishingReportOptions, ViewingReportOptions};
export type {ReportKindType, ViewingReportKindType};
export {PCastApi};
export {PCastApi, ReportKind};
export default PCastApi;

View File

@@ -11,7 +11,7 @@
// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"verbatimModuleSyntax": false,
"emitDeclarationOnly": true,
"declaration": true,
"declarationDir": "./dist/types",