polyv-live-cli
Version:
CLI tool for managing PolyV live streaming services.
370 lines • 14.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StreamServiceSdk = void 0;
const errors_1 = require("../utils/errors");
const sdk_1 = require("../sdk");
class StreamServiceSdk {
constructor(authConfig, serviceConfig) {
this.authConfig = authConfig;
this.config = serviceConfig;
}
async getStreamKey(request) {
try {
this.validateChannelId(request.channelId);
const client = (0, sdk_1.createSdkClient)(this.authConfig, this.config.baseUrl);
const pushUrl = await client.channel.getPushUrl(request.channelId);
const urlObj = new URL(pushUrl);
const pathParts = urlObj.pathname.split('/').filter(p => p);
const rtmpUrl = `${urlObj.protocol}//${urlObj.host}/${pathParts[0] || ''}`;
const streamKeyPath = pathParts.slice(1).join('/');
const streamKey = streamKeyPath ? `${streamKeyPath}?${urlObj.searchParams.toString()}` : urlObj.searchParams.toString();
return {
channelId: request.channelId,
rtmpUrl,
streamKey,
deployAddress: '',
inAddress: '',
metrics: {
fps: 0,
lfr: 0,
bandwidth: 0
}
};
}
catch (error) {
throw this.handleError(error, 'getStreamKey');
}
}
async startStream(request) {
try {
this.validateChannelId(request.channelId);
const client = (0, sdk_1.createSdkClient)(this.authConfig, this.config.baseUrl);
const userId = this.authConfig.userId || '';
if (!userId) {
throw new errors_1.PolyVValidationError('userId is required to start stream', 'userId', '', 'required');
}
await client.channel.setStatusStart({
channelId: request.channelId,
userId: userId,
});
return {
code: 200,
status: 'success',
message: 'Stream started successfully',
data: 'success'
};
}
catch (error) {
throw this.handleError(error, 'startStream');
}
}
async stopStream(request) {
try {
this.validateChannelId(request.channelId);
const client = (0, sdk_1.createSdkClient)(this.authConfig, this.config.baseUrl);
const userId = this.authConfig.userId || '';
if (!userId) {
throw new errors_1.PolyVValidationError('userId is required to stop stream', 'userId', '', 'required');
}
await client.channel.setStatusEnd({
channelId: request.channelId,
userId: userId,
});
return {
code: 200,
status: 'success',
message: 'Stream stopped successfully',
data: 'success'
};
}
catch (error) {
throw this.handleError(error, 'stopStream');
}
}
async getStreamStatus(request) {
try {
this.validateChannelId(request.channelId);
const client = (0, sdk_1.createSdkClient)(this.authConfig, this.config.baseUrl);
let streamMonitorData = null;
let streamInfoError;
try {
const streamInfo = await client.channel.getStreamInfo({
channelId: request.channelId,
});
streamMonitorData = {
deployAddress: streamInfo.deployAddress || '',
inAddress: streamInfo.inAddress || '',
streamName: streamInfo.streamName || '',
fps: streamInfo.fps || '0',
lfr: streamInfo.lfr,
inBandWidth: streamInfo.inBandWidth || '0',
retrievedAt: new Date(),
channelId: request.channelId
};
}
catch (error) {
if (error instanceof errors_1.PolyVAPIError) {
streamInfoError = {
code: error.code || 'STREAM_INFO_ERROR',
message: error.message
};
}
}
let channelStatus = 'unknown';
let statusText = 'Unknown';
let isLive = false;
let channelError;
try {
const channelInfo = await client.v4Channel.getChannel({
channelId: request.channelId,
});
if (channelInfo && channelInfo.watchStatus) {
const watchStatus = channelInfo.watchStatus;
switch (watchStatus) {
case 'live':
channelStatus = 'live';
statusText = 'Live';
isLive = true;
break;
case 'waiting':
case 'unStart':
channelStatus = 'waiting';
statusText = 'Waiting';
break;
case 'end':
case 'playback':
channelStatus = 'stopped';
statusText = 'Stopped';
break;
case 'banpush':
channelStatus = 'error';
statusText = 'Push Banned';
break;
default:
channelStatus = 'unknown';
statusText = watchStatus || 'Unknown';
}
}
}
catch (error) {
channelStatus = 'error';
statusText = 'Channel Error';
if (error instanceof errors_1.PolyVAPIError) {
channelError = {
code: error.code || 'CHANNEL_ERROR',
message: error.message
};
}
}
let duration;
let durationText;
if (isLive && streamMonitorData && streamMonitorData.retrievedAt) {
duration = Date.now() - streamMonitorData.retrievedAt.getTime();
durationText = this.formatDuration(duration);
}
let metrics;
if (streamMonitorData) {
const fpsValue = parseFloat(streamMonitorData.fps) || 0;
const lfrValue = parseFloat(streamMonitorData.lfr || '0') || 0;
const bandwidthValue = parseFloat(streamMonitorData.inBandWidth) || 0;
metrics = {
fps: fpsValue,
lfr: lfrValue,
bandwidth: bandwidthValue,
bandwidthText: this.formatBandwidth(bandwidthValue)
};
}
let network;
if (streamMonitorData) {
network = {
...(streamMonitorData.deployAddress && { deployAddress: streamMonitorData.deployAddress }),
...(streamMonitorData.inAddress && { inAddress: streamMonitorData.inAddress }),
...(streamMonitorData.streamName && { streamName: streamMonitorData.streamName })
};
}
let finalError;
if (channelStatus === 'waiting' && streamInfoError) {
finalError = streamInfoError;
}
else if (channelStatus === 'error') {
finalError = channelError || streamInfoError;
}
return {
channelId: request.channelId,
status: channelStatus,
statusText,
isLive,
...(duration !== undefined && { duration }),
...(durationText !== undefined && { durationText }),
...(metrics !== undefined && { metrics }),
...(network !== undefined && { network }),
lastUpdated: new Date(),
...(finalError !== undefined && { error: finalError })
};
}
catch (error) {
throw this.handleError(error, 'getStreamStatus');
}
}
async getLiveStatus(stream) {
try {
const client = (0, sdk_1.createSdkClient)(this.authConfig, this.config.baseUrl);
return await client.channel.getLiveStatus(stream);
}
catch (error) {
throw this.handleError(error, 'getLiveStatus');
}
}
async getLiveStatusList(options) {
try {
const client = (0, sdk_1.createSdkClient)(this.authConfig, this.config.baseUrl);
return await client.channel.getLiveStatusList(options);
}
catch (error) {
throw this.handleError(error, 'getLiveStatusList');
}
}
async getStreams(options) {
try {
const client = (0, sdk_1.createSdkClient)(this.authConfig, this.config.baseUrl);
return await client.channel.getStreams(options);
}
catch (error) {
throw this.handleError(error, 'getStreams');
}
}
async getHlsPullUrl(channelId) {
try {
this.validateChannelId(channelId);
const client = (0, sdk_1.createSdkClient)(this.authConfig, this.config.baseUrl);
return await client.channel.getHlsPullUrl(channelId);
}
catch (error) {
throw this.handleError(error, 'getHlsPullUrl');
}
}
async listDiskVideo(options) {
try {
const client = (0, sdk_1.createSdkClient)(this.authConfig, this.config.baseUrl);
return await client.channel.listDiskVideo(options);
}
catch (error) {
throw this.handleError(error, 'listDiskVideo');
}
}
async getCaptureImage(channelId) {
try {
const client = (0, sdk_1.createSdkClient)(this.authConfig, this.config.baseUrl);
return await client.channel.getCaptureImage(channelId);
}
catch (error) {
throw this.handleError(error, 'getCaptureImage');
}
}
async addDiskVideos(options) {
try {
const client = (0, sdk_1.createSdkClient)(this.authConfig, this.config.baseUrl);
return await client.channel.addDiskVideos(options);
}
catch (error) {
throw this.handleError(error, 'addDiskVideos');
}
}
async deleteDiskVideos(options) {
try {
const client = (0, sdk_1.createSdkClient)(this.authConfig, this.config.baseUrl);
return await client.channel.deleteDiskVideos(options);
}
catch (error) {
throw this.handleError(error, 'deleteDiskVideos');
}
}
async endDiskPush(options) {
try {
const client = (0, sdk_1.createSdkClient)(this.authConfig, this.config.baseUrl);
return await client.channel.endDiskPush(options);
}
catch (error) {
throw this.handleError(error, 'endDiskPush');
}
}
async banPush(options) {
try {
const client = (0, sdk_1.createSdkClient)(this.authConfig, this.config.baseUrl);
return await client.channel.banPush(options);
}
catch (error) {
throw this.handleError(error, 'banPush');
}
}
async resume(options) {
try {
const client = (0, sdk_1.createSdkClient)(this.authConfig, this.config.baseUrl);
return await client.channel.resume(options);
}
catch (error) {
throw this.handleError(error, 'resume');
}
}
async updateStreamType(options) {
try {
const client = (0, sdk_1.createSdkClient)(this.authConfig, this.config.baseUrl);
return await client.channel.updateStreamType(options);
}
catch (error) {
throw this.handleError(error, 'updateStreamType');
}
}
validateChannelId(channelId) {
if (!channelId || typeof channelId !== 'string' || channelId.trim() === '') {
throw new errors_1.PolyVValidationError('Channel ID cannot be empty', 'channelId', channelId, 'required');
}
}
formatDuration(duration) {
const seconds = Math.floor(duration / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
if (hours > 0) {
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
}
else if (minutes > 0) {
return `${minutes}m ${seconds % 60}s`;
}
else {
return `${seconds}s`;
}
}
formatBandwidth(bandwidth) {
if (bandwidth === 0)
return '0 bps';
const units = ['bps', 'Kbps', 'Mbps', 'Gbps'];
let unitIndex = 0;
let value = bandwidth;
while (value >= 1024 && unitIndex < units.length - 1) {
value /= 1024;
unitIndex++;
}
return `${value.toFixed(2)} ${units[unitIndex]}`;
}
handleError(error, operation) {
if (this.config.debug) {
console.error(`[StreamServiceSdk] Error in ${operation}:`, error);
}
if (error instanceof errors_1.PolyVError || error instanceof errors_1.PolyVAPIError || error instanceof errors_1.PolyVValidationError) {
return error;
}
if (error instanceof Error) {
const anyError = error;
if (anyError.polyvCode || anyError.code) {
return new errors_1.PolyVAPIError(error.message, anyError.code || 'API_ERROR', anyError.status || 500, {
polyvCode: anyError.polyvCode,
polyvMessage: anyError.polyvMessage || error.message,
});
}
return new errors_1.PolyVError(`Failed to ${operation}: ${error.message}`, 'STREAM_SERVICE_ERROR', 500, { originalError: error.message });
}
return new errors_1.PolyVError(`Failed to ${operation}: Unknown error`, 'UNKNOWN_ERROR', 500, { originalError: String(error) });
}
}
exports.StreamServiceSdk = StreamServiceSdk;
//# sourceMappingURL=stream.service.sdk.js.map