UNPKG

polyv-live-cli

Version:

CLI tool for managing PolyV live streaming services.

370 lines 14.6 kB
"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