polyv-live-cli
Version:
CLI tool for managing PolyV live streaming services.
1,111 lines • 56.1 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChannelHandler = void 0;
const buffer_1 = require("buffer");
const fs_1 = require("fs");
const path_1 = require("path");
const base_handler_1 = require("./base.handler");
const channel_service_sdk_1 = require("../services/channel.service.sdk");
const errors_1 = require("../utils/errors");
const api_command_1 = require("../utils/api-command");
class ChannelHandler extends base_handler_1.BaseHandler {
constructor(authConfig, serviceConfig) {
super();
this.channelService = new channel_service_sdk_1.ChannelServiceSdk(authConfig, serviceConfig);
}
async create(options) {
return this.executeWithErrorHandling(async () => {
this.validateCreateOptions(options);
const request = this.transformToCreateRequest(options);
const channel = await this.channelService.createChannel(request);
this.displayChannelCreated(channel, options.output);
}, 'channel.create');
}
async listChannels(options = {}) {
return this.executeWithErrorHandling(async () => {
this.validateListOptions(options);
const request = this.transformToListRequest(options);
const channels = await this.channelService.listChannels(request);
this.displayChannelsList(channels, request, options.output);
}, 'channel.list');
}
async getChannelDetail(options) {
return this.executeWithErrorHandling(async () => {
this.validateGetOptions(options);
const request = this.transformToDetailRequest(options);
const channelDetail = await this.channelService.getChannelDetail(request);
this.displayChannelDetail(channelDetail, options.output);
}, 'channel.get');
}
async updateChannel(options) {
return this.executeWithErrorHandling(async () => {
this.validateUpdateOptions(options);
const request = this.transformToUpdateRequest(options);
await this.channelService.updateChannel(request);
this.displayChannelUpdated(options);
}, 'channel.update');
}
async deleteChannel(options) {
return this.executeWithErrorHandling(async () => {
this.validateSingleDeleteOptions(options);
if (!options.force) {
const { confirmDeletion } = await Promise.resolve().then(() => __importStar(require('../utils/confirmation')));
const confirmed = await confirmDeletion(`Are you sure you want to delete channel '${options.channelId}'? This action cannot be undone.`, 'yes');
if (!confirmed) {
this.displayInfo('Channel deletion cancelled by user');
return;
}
}
await this.channelService.deleteChannel(options.channelId);
this.displayChannelDeleted(options);
}, 'channel.delete');
}
async deleteChannels(options) {
return this.executeWithErrorHandling(async () => {
this.validateDeleteOptions(options);
const request = this.transformToDeleteRequest(options);
await this.channelService.batchDeleteChannels(request);
this.displayChannelsDeleted(options);
}, 'channel.delete');
}
async listChannelViewerGroups(options) {
return this.executeWithErrorHandling(async () => {
const result = await this.channelService.listChannelViewerGroups(this.buildChannelViewerParams(options));
this.displayChannelViewerResult('Channel viewer groups', result, options.output);
}, 'channel.viewer.group.list');
}
async createChannelViewerGroup(options) {
return this.executeWithErrorHandling(async () => {
const result = await this.channelService.createChannelViewerGroup(this.buildChannelViewerParams(options));
this.displayChannelViewerResult('Channel viewer group created', result, options.output);
}, 'channel.viewer.group.create');
}
async updateChannelViewerGroup(options) {
return this.executeWithErrorHandling(async () => {
await this.channelService.updateChannelViewerGroup(this.buildChannelViewerParams(options));
this.displayChannelViewerResult('Channel viewer group updated', { success: true }, options.output);
}, 'channel.viewer.group.update');
}
async deleteChannelViewerGroup(options) {
return this.executeWithErrorHandling(async () => {
await this.channelService.deleteChannelViewerGroup(this.buildChannelViewerParams(options));
this.displayChannelViewerResult('Channel viewer group deleted', { success: true }, options.output);
}, 'channel.viewer.group.delete');
}
async getChannelViewerGroupSetting(options) {
return this.executeWithErrorHandling(async () => {
const result = await this.channelService.getChannelViewerGroupSetting(this.buildChannelViewerParams(options));
this.displayChannelViewerResult('Channel viewer group setting', result, options.output);
}, 'channel.viewer.group-setting.get');
}
async updateChannelViewerGroupSetting(options) {
return this.executeWithErrorHandling(async () => {
if (options.channelViewerGroupEnabled === undefined && options.notInGroupWatchEnabled === undefined) {
throw new errors_1.PolyVValidationError('At least one group setting field must be provided', 'options', options, 'validation_failed');
}
await this.channelService.updateChannelViewerGroupSetting(this.buildChannelViewerParams(options));
this.displayChannelViewerResult('Channel viewer group setting updated', { success: true }, options.output);
}, 'channel.viewer.group-setting.update');
}
async listChannelViewers(options) {
return this.executeWithErrorHandling(async () => {
const result = await this.channelService.listChannelViewers(this.buildChannelViewerParams(options));
this.displayChannelViewerResult('Channel viewers', result, options.output);
}, 'channel.viewer.list');
}
async exportChannelViewers(options) {
return this.executeWithErrorHandling(async () => {
const result = await this.channelService.exportChannelViewers(this.buildChannelViewerParams(options));
this.displayChannelViewerResult('Channel viewer export', { exportUrl: result }, options.output);
}, 'channel.viewer.export');
}
async addChannelViewers(options) {
return this.executeWithErrorHandling(async () => {
await this.channelService.addChannelViewers(this.buildChannelViewerParams(options));
this.displayChannelViewerResult('Channel viewers added', {
success: true,
viewerIds: this.requireStringList(options.viewerIds, 'viewerIds')
}, options.output);
}, 'channel.viewer.add');
}
async deleteChannelViewers(options) {
return this.executeWithErrorHandling(async () => {
await this.channelService.deleteChannelViewers(this.buildChannelViewerParams(options));
this.displayChannelViewerResult('Channel viewers deleted', {
success: true,
viewerIds: this.requireStringList(options.viewerIds, 'viewerIds')
}, options.output);
}, 'channel.viewer.delete');
}
async transferChannelViewers(options) {
return this.executeWithErrorHandling(async () => {
await this.channelService.transferChannelViewers(this.buildChannelViewerParams(options));
this.displayChannelViewerResult('Channel viewers transferred', {
success: true,
viewerIds: this.requireStringList(options.viewerIds, 'viewerIds'),
targetGroupId: options.targetGroupId ?? null
}, options.output);
}, 'channel.viewer.transfer');
}
async importChannelViewers(options) {
return this.executeWithErrorHandling(async () => {
const importFile = this.readChannelViewerImportFile(options.file);
const result = await this.channelService.importChannelViewers({
...this.buildChannelViewerParams(options),
file: importFile.file
});
this.displayChannelViewerResult('Channel viewers imported', {
...result,
filePath: importFile.path,
fileSize: importFile.size
}, options.output);
}, 'channel.viewer.import');
}
async listUnrelatedChannelViewers(options) {
return this.executeWithErrorHandling(async () => {
const result = await this.channelService.listUnrelatedChannelViewers(this.buildChannelViewerParams(options));
this.displayChannelViewerResult('Unrelated channel viewers', result, options.output);
}, 'channel.viewer.unrelated-list');
}
async getRoleAccount(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId', 'account']);
const result = await this.channelService.getAccount(options.channelId, options.account);
this.displayHistoricalResult(result, options.output);
}, 'channel.role.get');
}
async listRoleAccounts(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId']);
const result = await this.channelService.getAccounts(options.channelId);
this.displayHistoricalResult(result, options.output);
}, 'channel.role.list');
}
async deleteRoleAccount(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId', 'account']);
await (0, api_command_1.confirmWrite)(options.force, `Delete role account ${options.account} from channel ${options.channelId}?`);
const result = await this.channelService.deleteAccount(options.channelId, options.account);
this.displayHistoricalResult({ success: true, result }, options.output, 'Role account deleted successfully');
}, 'channel.role.delete');
}
async batchCreateRoleAccounts(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId', 'accounts']);
await (0, api_command_1.confirmWrite)(options.force, `Create ${options.accounts.length} role account(s) in channel ${options.channelId}?`);
const result = await this.channelService.batchCreateAccounts(options.channelId, options.accounts);
this.displayHistoricalResult(result, options.output, 'Role accounts created successfully');
}, 'channel.role.batch-create');
}
async getChannelAdverts(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId']);
const result = await this.channelService.getChannelAdverts(options.channelId);
this.displayHistoricalResult(result, options.output);
}, 'channel.advert.list');
}
async getCallbackSetting(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId']);
const result = await this.channelService.getCallbackSetting(options.channelId);
this.displayHistoricalResult(result, options.output);
}, 'channel.callback.get');
}
async updateCallbackSetting(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId']);
await (0, api_command_1.confirmWrite)(options.force, `Update callback settings for channel ${options.channelId}?`);
const payload = this.compactHistoricalOptions(options, ['channelId', 'output', 'force']);
const result = await this.channelService.updateCallbackSetting(options.channelId, payload);
this.displayHistoricalResult(result ?? { success: true }, options.output, 'Callback settings updated successfully');
}, 'channel.callback.update');
}
async getPptRecordSetting(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId']);
const result = await this.channelService.getPptRecordSetting(options.channelId);
this.displayHistoricalResult(result, options.output);
}, 'channel.ppt-record.setting-get');
}
async listPptRecordTasks(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId']);
const result = await this.channelService.listPptRecordTasks(this.compactHistoricalOptions(options, ['output', 'force']));
this.displayHistoricalResult(result, options.output);
}, 'channel.ppt-record.list');
}
async addPptRecordTask(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId', 'videoId']);
await (0, api_command_1.confirmWrite)(options.force, `Create PPT record task for video ${options.videoId}?`);
const result = await this.channelService.addPptRecordTask(this.compactHistoricalOptions(options, ['output', 'force']));
this.displayHistoricalResult(result, options.output, 'PPT record task created successfully');
}, 'channel.ppt-record.add-task');
}
async updatePptRecordSetting(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId']);
await (0, api_command_1.confirmWrite)(options.force, `Update PPT record settings for channel ${options.channelId}?`);
const result = await this.channelService.updatePptRecordSetting(this.compactHistoricalOptions(options, ['output', 'force']));
this.displayHistoricalResult(result, options.output, 'PPT record settings updated successfully');
}, 'channel.ppt-record.setting-update');
}
async deletePptRecord(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId', 'taskIds']);
await (0, api_command_1.confirmWrite)(options.force, `Delete PPT record task(s) ${options.taskIds}?`);
const result = await this.channelService.deletePptRecord(this.compactHistoricalOptions(options, ['output', 'force']));
this.displayHistoricalResult(result, options.output, 'PPT record task(s) deleted successfully');
}, 'channel.ppt-record.delete');
}
async copyChannel(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId']);
await (0, api_command_1.confirmWrite)(options.force, `Copy channel ${options.channelId}?`);
const payload = this.compactHistoricalOptions(options, ['channelId', 'output', 'force']);
const result = await this.channelService.copyChannel(options.channelId, payload);
this.displayHistoricalResult({ channelId: result }, options.output, 'Channel copied successfully');
}, 'channel.copy');
}
async getUserChildrenChannels(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['childUserId', 'pageNumber', 'pageSize']);
const result = await this.channelService.getUserChildrenChannels(this.compactHistoricalOptions(options, ['output', 'force']));
this.displayHistoricalResult(result, options.output);
}, 'channel.children.list');
}
async getWatchApiToken(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId', 'viewerId']);
const result = await this.channelService.getWatchApiToken(this.compactHistoricalOptions(options, ['output', 'force']));
this.displayHistoricalResult(result, options.output);
}, 'channel.token.watch-api');
}
async getApiToken(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId', 'viewerId']);
const result = await this.channelService.getApiToken(this.compactHistoricalOptions(options, ['output', 'force']));
this.displayHistoricalResult(result, options.output);
}, 'channel.token.api');
}
async getTokenLoginUrl(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId']);
const result = await this.channelService.getTokenLoginUrl(this.compactHistoricalOptions(options, ['output', 'force']));
this.displayHistoricalResult(result, options.output);
}, 'channel.token.login-url');
}
async getChatToken(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId', 'userId', 'role']);
const result = await this.channelService.getChatToken(this.compactHistoricalOptions(options, ['output', 'force']));
this.displayHistoricalResult(result, options.output);
}, 'channel.token.chat');
}
async listChannelsFollow(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelIds']);
const result = await this.channelService.listChannelsFollow({ channelIds: options.channelIds });
this.displayHistoricalResult(result, options.output);
}, 'channel.follow.list');
}
async updateChannelsFollow(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelIds', 'qrCodeUrl']);
await (0, api_command_1.confirmWrite)(options.force, `Update follow settings for channel(s) ${options.channelIds}?`);
const result = await this.channelService.updateChannelsFollow(this.compactHistoricalOptions(options, ['output', 'force']));
this.displayHistoricalResult(result ?? { success: true }, options.output, 'Follow settings updated successfully');
}, 'channel.follow.update');
}
async batchAddSubmeeting(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId', 'subChannels']);
await (0, api_command_1.confirmWrite)(options.force, `Save ${options.subChannels.length} submeeting channel(s) for ${options.channelId}?`);
const result = await this.channelService.batchAddSubmeeting(this.compactHistoricalOptions(options, ['output', 'force']));
this.displayHistoricalResult(result, options.output, 'Submeeting channels saved successfully');
}, 'channel.submeeting.batch-add');
}
async stopQuestionnaires(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelIds']);
await (0, api_command_1.confirmWrite)(options.force, `Stop questionnaire(s) for channel(s) ${options.channelIds}?`);
const result = await this.channelService.channelsStopQuestionnaire({ channelIds: options.channelIds });
this.displayHistoricalResult(result, options.output, 'Questionnaire(s) stopped successfully');
}, 'channel.questionnaire.stop');
}
async batchUpdateDanmu(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelIds', 'closeDanmu', 'showDanmuInfoEnabled']);
await (0, api_command_1.confirmWrite)(options.force, `Update danmu settings for channel(s) ${options.channelIds}?`);
const result = await this.channelService.batchUpdateDanmu(this.compactHistoricalOptions(options, ['output', 'force']));
this.displayHistoricalResult(result, options.output, 'Danmu settings updated successfully');
}, 'channel.danmu.batch-update');
}
async setChannelToken(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId', 'token']);
await (0, api_command_1.confirmWrite)(options.force, `Set one-time login token for channel ${options.channelId}?`);
const result = await this.channelService.setChannelToken(this.compactHistoricalOptions(options, ['output', 'force']));
this.displayHistoricalResult(result, options.output, 'Channel token set successfully');
}, 'channel.token.set');
}
async setAccountToken(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId', 'token']);
await (0, api_command_1.confirmWrite)(options.force, `Set sub-channel account token for channel ${options.channelId}?`);
const result = await this.channelService.setAccountToken(this.compactHistoricalOptions(options, ['output', 'force']));
this.displayHistoricalResult(result, options.output, 'Account token set successfully');
}, 'channel.token.set-account');
}
async setMaxViewer(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId', 'userId', 'maxViewer']);
await (0, api_command_1.confirmWrite)(options.force, `Set max viewer count for channel ${options.channelId}?`);
const result = await this.channelService.setMaxViewer(options.channelId, options.userId, options.maxViewer);
this.displayHistoricalResult(result, options.output, 'Max viewer count updated successfully');
}, 'channel.max-viewer.set');
}
async updateChannelPassword(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['userId', 'passwd']);
await (0, api_command_1.confirmWrite)(options.force, options.channelId ? `Update password for channel ${options.channelId}?` : 'Update password for all channels?');
const result = await this.channelService.updateChannelPassword(options.userId, options.passwd, options.channelId);
this.displayHistoricalResult(result, options.output, 'Channel password updated successfully');
}, 'channel.password.update');
}
async setDiyUrlMarquee(options) {
return this.executeWithErrorHandling(async () => {
this.requireFields(options, ['channelId', 'marqueeRestrict']);
await (0, api_command_1.confirmWrite)(options.force, `Update marquee URL settings for channel ${options.channelId}?`);
const result = await this.channelService.setDiyUrlMarquee(this.compactHistoricalOptions(options, ['output', 'force']));
this.displayHistoricalResult(result, options.output, 'Marquee URL settings updated successfully');
}, 'channel.marquee-url.set');
}
validateCreateOptions(options) {
const errors = [];
if (!options.name || typeof options.name !== 'string') {
errors.push('Channel name is required and must be a string');
}
else if (options.name.trim().length === 0) {
errors.push('Channel name cannot be empty');
}
else if (options.name.length > 100) {
errors.push('Channel name cannot exceed 100 characters');
}
if (options.description !== undefined) {
if (typeof options.description !== 'string') {
errors.push('Description must be a string');
}
else if (options.description.length > 500) {
errors.push('Description cannot exceed 500 characters');
}
}
if (options.maxViewers !== undefined) {
if (typeof options.maxViewers !== 'number' || options.maxViewers < 0) {
errors.push('Max viewers must be a non-negative number');
}
else if (options.maxViewers > 100000) {
errors.push('Max viewers cannot exceed 100,000');
}
}
if (options.autoRecord !== undefined && typeof options.autoRecord !== 'boolean') {
errors.push('Auto record must be a boolean value');
}
if (options.output !== undefined && !['table', 'json'].includes(options.output)) {
errors.push('Output format must be either "table" or "json"');
}
if (options.scene !== undefined) {
const validScenes = ['topclass', 'alone', 'seminar', 'train', 'double', 'guide'];
if (!validScenes.includes(options.scene)) {
errors.push(`Scene must be one of: ${validScenes.join(', ')}`);
}
}
if (options.template !== undefined) {
const validTemplates = ['ppt', 'portrait_ppt', 'alone', 'portrait_alone', 'topclass', 'portrait_topclass', 'seminar'];
if (!validTemplates.includes(options.template)) {
errors.push(`Template must be one of: ${validTemplates.join(', ')}`);
}
}
if (options.password !== undefined) {
if (typeof options.password !== 'string') {
errors.push('Password must be a string');
}
else if (options.password.length < 6 || options.password.length > 16) {
errors.push('Password must be 6-16 characters long');
}
else if (!/^[a-zA-Z0-9]+$/.test(options.password)) {
errors.push('Password must contain only alphanumeric characters');
}
}
if (errors.length > 0) {
throw new errors_1.PolyVValidationError(`Channel creation options validation failed: ${errors.join(', ')}`, 'options', options, 'validation_failed');
}
}
transformToCreateRequest(options) {
const request = {
name: options.name.trim(),
newScene: options.scene || 'topclass',
template: options.template || 'ppt'
};
if (options.password) {
request.channelPasswd = options.password;
}
return request;
}
displayChannelCreated(channel, format = 'table') {
const displayData = {
channelId: channel.channelId,
name: channel.name,
userId: channel.userId,
password: channel.channelPasswd ? '***' : '-',
scene: channel.newScene,
template: channel.template,
status: channel.status || 'waiting',
created: channel.createdAt ? channel.createdAt.toLocaleString() : 'Just now'
};
this.displaySuccess('Channel created successfully', displayData, format);
if (format === 'table') {
this.displayInfo('Channel is ready for streaming configuration');
if (channel.channelPasswd) {
this.displayWarning('Channel password has been set - remember to provide it to viewers');
}
}
}
validateListOptions(options) {
const errors = [];
if (options.page !== undefined) {
if (typeof options.page !== 'number' || !Number.isInteger(options.page) || options.page < 1) {
errors.push('Page must be a positive integer (minimum 1)');
}
}
if (options.limit !== undefined) {
if (typeof options.limit !== 'number' || !Number.isInteger(options.limit) || options.limit < 1 || options.limit > 100) {
errors.push('Limit must be an integer between 1 and 100');
}
}
if (options.output !== undefined && !['table', 'json'].includes(options.output)) {
errors.push('Output format must be either "table" or "json"');
}
if (options.categoryId !== undefined && (typeof options.categoryId !== 'string' || options.categoryId.trim().length === 0)) {
errors.push('Category ID must be a non-empty string');
}
if (options.keyword !== undefined && (typeof options.keyword !== 'string' || options.keyword.trim().length === 0)) {
errors.push('Keyword must be a non-empty string');
}
if (options.labelId !== undefined && (typeof options.labelId !== 'string' || options.labelId.trim().length === 0)) {
errors.push('Label ID must be a non-empty string');
}
if (errors.length > 0) {
throw new errors_1.PolyVValidationError(`Channel list options validation failed: ${errors.join(', ')}`, 'options', options, 'validation_failed');
}
}
validateGetOptions(options) {
const errors = [];
if (!options.channelId) {
errors.push('Channel ID is required');
}
else if (typeof options.channelId !== 'string') {
errors.push('Channel ID must be a string');
}
else if (options.channelId.trim().length === 0) {
errors.push('Channel ID cannot be empty');
}
if (options.output !== undefined && !['table', 'json'].includes(options.output)) {
errors.push('Output format must be either "table" or "json"');
}
if (errors.length > 0) {
throw new errors_1.PolyVValidationError(`Channel get options validation failed: ${errors.join(', ')}`, 'options', options, 'validation_failed');
}
}
validateUpdateOptions(options) {
const errors = [];
if (!options.channelId) {
errors.push('Channel ID is required');
}
else if (typeof options.channelId !== 'string') {
errors.push('Channel ID must be a string');
}
else if (options.channelId.trim().length === 0) {
errors.push('Channel ID cannot be empty');
}
const updateFields = [
'name', 'description', 'publisher', 'password', 'maxViewers',
'autoRecord', 'startTime', 'endTime', 'pageView', 'likes',
'coverImg', 'splashImg'
];
const hasUpdateField = updateFields.some(field => options[field] !== undefined);
if (!hasUpdateField) {
errors.push('At least one update parameter must be provided');
}
if (options.name !== undefined) {
if (typeof options.name !== 'string') {
errors.push('Channel name must be a string');
}
else if (options.name.trim().length === 0) {
errors.push('Channel name cannot be empty');
}
else if (options.name.length > 100) {
errors.push('Channel name cannot exceed 100 characters');
}
}
if (options.description !== undefined) {
if (typeof options.description !== 'string') {
errors.push('Description must be a string');
}
else if (options.description.length > 500) {
errors.push('Description cannot exceed 500 characters');
}
}
if (options.publisher !== undefined) {
if (typeof options.publisher !== 'string') {
errors.push('Publisher must be a string');
}
}
if (options.password !== undefined) {
if (typeof options.password !== 'string') {
errors.push('Password must be a string');
}
else if (options.password.length < 6 || options.password.length > 16) {
errors.push('Password must be 6-16 characters long');
}
else if (!/^[a-zA-Z0-9]+$/.test(options.password)) {
errors.push('Password must contain only alphanumeric characters');
}
}
if (options.maxViewers !== undefined) {
if (typeof options.maxViewers !== 'number' || options.maxViewers <= 0) {
errors.push('Max viewers must be a positive number');
}
}
if (options.autoRecord !== undefined && typeof options.autoRecord !== 'boolean') {
errors.push('Auto record must be a boolean value');
}
if (options.startTime !== undefined) {
if (typeof options.startTime !== 'number' || options.startTime < 0) {
errors.push('Start time must be a non-negative timestamp');
}
}
if (options.endTime !== undefined) {
if (typeof options.endTime !== 'number' || options.endTime < 0) {
errors.push('End time must be a non-negative timestamp');
}
if (options.startTime !== undefined && options.endTime <= options.startTime) {
errors.push('End time must be greater than start time');
}
}
if (options.pageView !== undefined) {
if (typeof options.pageView !== 'number' || options.pageView < 0) {
errors.push('Page view count must be a non-negative number');
}
}
if (options.likes !== undefined) {
if (typeof options.likes !== 'number' || options.likes < 0) {
errors.push('Likes count must be a non-negative number');
}
}
if (options.coverImg !== undefined && typeof options.coverImg !== 'string') {
errors.push('Cover image URL must be a string');
}
if (options.splashImg !== undefined && typeof options.splashImg !== 'string') {
errors.push('Splash image URL must be a string');
}
if (options.output !== undefined && !['table', 'json'].includes(options.output)) {
errors.push('Output format must be either "table" or "json"');
}
if (errors.length > 0) {
throw new errors_1.PolyVValidationError(`Channel update options validation failed: ${errors.join(', ')}`, 'options', options, 'validation_failed');
}
}
transformToUpdateRequest(options) {
const request = {
channelId: options.channelId.trim()
};
const basicSetting = {};
let hasBasicSetting = false;
if (options.name !== undefined) {
basicSetting.name = options.name.trim();
hasBasicSetting = true;
}
if (options.description !== undefined) {
basicSetting.desc = options.description;
hasBasicSetting = true;
}
if (options.publisher !== undefined) {
basicSetting.publisher = options.publisher;
hasBasicSetting = true;
}
if (options.password !== undefined) {
basicSetting.channelPasswd = options.password;
hasBasicSetting = true;
}
if (options.maxViewers !== undefined) {
basicSetting.maxViewer = options.maxViewers;
hasBasicSetting = true;
}
if (options.startTime !== undefined) {
basicSetting.startTime = options.startTime;
hasBasicSetting = true;
}
if (options.endTime !== undefined) {
basicSetting.endTime = options.endTime;
hasBasicSetting = true;
}
if (options.pageView !== undefined) {
basicSetting.pageView = options.pageView;
hasBasicSetting = true;
}
if (options.likes !== undefined) {
basicSetting.likes = options.likes;
hasBasicSetting = true;
}
if (options.coverImg !== undefined) {
basicSetting.coverImg = options.coverImg;
hasBasicSetting = true;
}
if (options.splashImg !== undefined) {
basicSetting.splashImg = options.splashImg;
hasBasicSetting = true;
}
if (hasBasicSetting) {
request.basicSetting = basicSetting;
}
return request;
}
displayChannelUpdated(options) {
const updateSummary = [];
if (options.name !== undefined)
updateSummary.push(`Name: ${options.name}`);
if (options.description !== undefined)
updateSummary.push(`Description: ${options.description}`);
if (options.publisher !== undefined)
updateSummary.push(`Publisher: ${options.publisher}`);
if (options.password !== undefined)
updateSummary.push('Password: ***');
if (options.maxViewers !== undefined)
updateSummary.push(`Max Viewers: ${options.maxViewers}`);
if (options.startTime !== undefined)
updateSummary.push(`Start Time: ${new Date(options.startTime).toLocaleString()}`);
if (options.endTime !== undefined)
updateSummary.push(`End Time: ${new Date(options.endTime).toLocaleString()}`);
if (options.pageView !== undefined)
updateSummary.push(`Page Views: ${options.pageView}`);
if (options.likes !== undefined)
updateSummary.push(`Likes: ${options.likes}`);
if (options.coverImg !== undefined)
updateSummary.push(`Cover Image: ${options.coverImg}`);
if (options.splashImg !== undefined)
updateSummary.push(`Splash Image: ${options.splashImg}`);
this.displaySuccess(`Channel ${options.channelId} updated successfully`);
if (updateSummary.length > 0) {
this.displayInfo('Updated fields:');
updateSummary.forEach(summary => this.displayInfo(` • ${summary}`));
}
if (options.password) {
this.displayWarning('Channel password has been updated - remember to provide it to viewers');
}
}
validateSingleDeleteOptions(options) {
const errors = [];
if (!options.channelId) {
errors.push('Channel ID is required');
}
else if (typeof options.channelId !== 'string') {
errors.push('Channel ID must be a string');
}
else if (options.channelId.trim().length === 0) {
errors.push('Channel ID cannot be empty');
}
if (options.force !== undefined && typeof options.force !== 'boolean') {
errors.push('Force flag must be a boolean value');
}
if (options.output !== undefined && !['table', 'json'].includes(options.output)) {
errors.push('Output format must be either "table" or "json"');
}
if (errors.length > 0) {
throw new errors_1.PolyVValidationError(`Channel delete options validation failed: ${errors.join(', ')}`, 'options', options, 'validation_failed');
}
}
validateDeleteOptions(options) {
const errors = [];
if (!options.channelIds) {
errors.push('Channel IDs are required');
}
else if (!Array.isArray(options.channelIds)) {
errors.push('Channel IDs must be an array');
}
else if (options.channelIds.length === 0) {
errors.push('At least one channel ID must be provided');
}
else if (options.channelIds.length > 100) {
errors.push('Cannot delete more than 100 channels at once');
}
else {
for (let i = 0; i < options.channelIds.length; i++) {
const channelId = options.channelIds[i];
if (typeof channelId !== 'string') {
errors.push(`Channel ID at position ${i + 1} must be a string`);
}
else if (channelId.trim().length === 0) {
errors.push(`Channel ID at position ${i + 1} cannot be empty`);
}
}
}
if (options.output !== undefined && !['table', 'json'].includes(options.output)) {
errors.push('Output format must be either "table" or "json"');
}
if (errors.length > 0) {
throw new errors_1.PolyVValidationError(`Channel delete options validation failed: ${errors.join(', ')}`, 'options', options, 'validation_failed');
}
}
transformToDeleteRequest(options) {
return {
channelIds: options.channelIds.map((id) => id.trim())
};
}
displayChannelsDeleted(options) {
const channelCount = options.channelIds.length;
const channelText = channelCount === 1 ? 'channel' : 'channels';
this.displaySuccess(`Successfully deleted ${channelCount} ${channelText}`);
if (options.output === 'json') {
const jsonData = {
deleted: true,
channelIds: options.channelIds,
count: channelCount
};
this.displayData(jsonData, 'json');
}
else {
this.displayInfo('Deleted channels:');
options.channelIds.forEach((channelId) => {
this.displayInfo(` • Channel ${channelId}`);
});
}
}
displayChannelDeleted(options) {
this.displaySuccess(`Successfully deleted channel ${options.channelId}`);
if (options.output === 'json') {
const jsonData = {
deleted: true,
channelId: options.channelId,
timestamp: new Date().toISOString()
};
this.displayData(jsonData, 'json');
}
else {
this.displayInfo(`Channel ${options.channelId} has been permanently deleted`);
this.displayWarning('This action cannot be undone');
}
}
transformToListRequest(options) {
const request = {};
if (options.page !== undefined) {
request.page = options.page;
}
if (options.limit !== undefined) {
request.limit = options.limit;
}
if (options.categoryId) {
request.categoryId = options.categoryId.trim();
}
if (options.keyword) {
request.keyword = options.keyword.trim();
}
if (options.labelId) {
request.labelId = options.labelId.trim();
}
return request;
}
transformToDetailRequest(options) {
return {
channelId: options.channelId.trim()
};
}
displayChannelsList(channels, request, format = 'table') {
if (channels.length === 0) {
this.displayInfo('No channels found');
return;
}
if (format === 'json') {
const jsonData = {
channels: channels.map(channel => ({
channelId: channel.channelId,
name: channel.name,
status: channel.status,
scene: channel.scene,
template: channel.template,
createdAt: channel.createdAt.toISOString(),
description: channel.description,
maxViewers: channel.maxViewers
})),
pagination: {
page: request.page ?? 1,
limit: request.limit ?? 20,
total: channels.length
}
};
this.displayData(jsonData, 'json');
}
else {
const tableData = channels.map(channel => ({
'Channel ID': channel.channelId,
'Name': channel.name,
'Status': channel.status,
'Scene': channel.scene,
'Template': channel.template,
'Created': channel.createdAt.toLocaleDateString()
}));
this.displaySuccess(`Found ${channels.length} channels`, tableData, 'table');
const page = request.page ?? 1;
const limit = request.limit ?? 20;
this.displayInfo(`Page ${page}, showing up to ${limit} channels per page`);
}
}
displayChannelDetail(channelDetail, format = 'table') {
if (format === 'json') {
const jsonData = {
channelId: channelDetail.channelId,
name: channelDetail.name,
scene: channelDetail.scene,
newScene: channelDetail.newScene,
template: channelDetail.template,
channelPasswd: channelDetail.channelPasswd ? '***masked***' : null,
publisher: channelDetail.publisher,
startTime: channelDetail.startTime,
endTime: channelDetail.endTime,
pureRtcEnabled: channelDetail.pureRtcEnabled,
pageView: channelDetail.pageView,
likes: channelDetail.likes,
coverImg: channelDetail.coverImg,
splashImg: channelDetail.splashImg,
splashEnabled: channelDetail.splashEnabled,
bgImg: channelDetail.bgImg,
desc: channelDetail.desc,
consultingMenuEnabled: channelDetail.consultingMenuEnabled,
maxViewerRestrict: channelDetail.maxViewerRestrict,
maxViewer: channelDetail.maxViewer,
watchStatus: channelDetail.watchStatus,
watchStatusText: channelDetail.watchStatusText,
userCategory: channelDetail.userCategory,
authSettings: channelDetail.authSettings,
linkMicLimit: channelDetail.linkMicLimit,
createdAccountId: channelDetail.createdAccountId,
createdAccountEmail: channelDetail.createdAccountEmail,
createdTime: channelDetail.createdTime,
labelData: channelDetail.labelData,
clientAloneTemplateBackgroundUrl: channelDetail.clientAloneTemplateBackgroundUrl,
liveCdnBackgroundUrl: channelDetail.liveCdnBackgroundUrl,
pushUrl: channelDetail.pushUrl ? '***masked***' : null,
pushSecret: channelDetail.pushSecret ? '***masked***' : null,
streamType: channelDetail.streamType,
onlyOutEnabled: channelDetail.onlyOutEnabled
};
this.displayData(jsonData, 'json');
}
else {
this.displaySuccess(`Channel Details: ${channelDetail.name}`, null, 'table');
const basicInfo = {
'Channel ID': channelDetail.channelId.toString(),
'Name': channelDetail.name,
'Publisher': channelDetail.publisher || '-',
'Description': channelDetail.desc || '-',
'Status': `${channelDetail.watchStatus} (${channelDetail.watchStatusText})`,
'Scene': channelDetail.newScene !== 'undefined' ? channelDetail.newScene : channelDetail.scene,
'Template': channelDetail.template !== 'undefined' ? channelDetail.template : '-'
};
this.displayInfo('📋 Basic Information');
this.displayData(basicInfo, 'table');
const configInfo = {
'Channel Password': channelDetail.channelPasswd ? 'Set (***masked***)' : 'Not set',
'Pure RTC': channelDetail.pureRtcEnabled === 'Y' ? 'Enabled' : 'Disabled',
'Max Viewer Restrict': channelDetail.maxViewerRestrict === 'Y' ? 'Enabled' : 'Disabled',
'Max Viewers': channelDetail.maxViewer > 0 ? channelDetail.maxViewer.toString() : 'Unlimited',
'Link Mic Limit': channelDetail.linkMicLimit.toString(),
'Consulting Menu': channelDetail.consultingMenuEnabled === 'Y' ? 'Enabled' : 'Disabled',
'Splash Page': channelDetail.splashEnabled === 'Y' ? 'Enabled' : 'Disabled'
};
this.displayInfo('⚙️ Configuration');
this.displayData(configInfo, 'table');
const statsInfo = {
'Page Views': channelDetail.pageView.toString(),
'Likes': channelDetail.likes.toString(),
'Start Time': channelDetail.startTime > 0 ? new Date(channelDetail.startTime).toLocaleString() : 'Not set',
'End Time': channelDetail.endTime > 0 ? new Date(channelDetail.endTime).toLocaleString() : 'Not set',
'Created Time': new Date(channelDetail.createdTime).toLocaleString(),
'Created By': channelDetail.createdAccountEmail || channelDetail.createdAccountId
};
this.displayInfo('📊 Statistics & Timing');
this.displayData(statsInfo, 'table');
if (channelDetail.userCategory) {
const categoryInfo = {
'Category ID': channelDetail.userCategory.categoryId.toString(),
'Category Name': channelDetail.userCategory.categoryName,
'Rank': channelDetail.userCategory.rank.toString()
};
this.displayInfo('📁 Category Information');
this.displayData(categoryInfo, 'table');
}
if (channelDetail.authSettings && channelDetail.authSettings.length > 0) {
const authSummary = channelDetail.authSettings.map((auth) => ({
'Rank': auth.rank.toString(),
'Type': auth.authType,
'Enabled': auth.enabled,
'Global Setting': auth.globalSettingEnabled
}));
this.displayInfo('🔐 Authentication Settings');
this.displayData(authSummary, 'table');
}
if (channelDetail.labelData && channelDetail.labelData.length > 0) {
this.displayInfo(`Labels: ${channelDetail.labelData.join(', ')}`);
}
const urls = [];
if (channelDetail.coverImg)
urls.push(`Cover Image: ${channelDetail.coverImg}`);
if (channelDetail.splashImg)
urls.push(`Splash Image: ${channelDetail.splashImg}`);
if (channelDetail.bgImg)
urls.push(`Background Image: ${channelDetail.bgImg}`);
if (urls.length > 0) {
this.displayInfo('Images:\n' + urls.join('\n'));
}
}
}
requireFields(options, fields) {
const missing = fields.filter((field) => {
const value = options[field];
return value === undefined || value === null || value === '';
});
if (missing.length > 0) {
throw new errors_1.PolyVValidationError(`Missing required option(s): ${missing.join(', ')}`, 'options', options, 'validation_failed');
}
}
compactHistoricalOptions(options, skip = []) {
const skipped = new Set(skip);
return Object.fromEntries(Object.entries(options).filter(([key, value]) => !skipped.has(key) && value !== undefined && value !== ''));
}
displayHistoricalResult(result, format = 'table', successMessage) {
if (successMessage && format !== 'json') {
this.displaySuccess(successMessage);
}
if (result !== undefined) {
this.displayData(result, format);
}
else if (format === 'json') {
this.displayData({ success: true }, 'json');
}
}
buildChannelViewerParams(options) {
const params = {};
const source = options ?? {};
const skipped = new Set(['output', 'force', 'file', 'page', 'size', 'viewerIds', 'labelIds']);
Object.entries(source).forEach(([key, value]) => {
if (!skipped.has(key) && value !== undefined && value !== '') {
params[key] = value;
}
});
if (source.scope !== undefined) {
params['scope'] = this.normalizeChannelViewerScope(source.scope);
}
if (source.page !== undefined) {
params['pageNumber'] = source.page;
}
if (source.size !== undefined) {
params['pageSize'] = source.size;
}
if (source.viewerIds !== undefined) {
params['viewerIds'] = this.requireStringList(source.viewerIds, 'viewerIds');
}
if (source.labelIds !== undefined) {
params['labelIds'] = this.requireStringList(source.labelIds, 'labelIds');
}
return params;
}
normalizeChannelViewerScope(scope) {
if (scope === 'user' || scope === 'teacher') {
return scope;
}
throw new errors_1.PolyVValidationError('scope must be user or teacher', 'scope', scope, 'validation_failed');
}
requireStringList(value, field) {
const list = this.normalizeStringList(value);
if (list.length === 0) {
throw new errors_1.PolyVValidationError(`${field} must contain at least one value`, field, value, 'validation_failed');
}
return list;
}
normalizeStringList(value) {
if (value === undefined || value === null) {
return [];
}
const items = Array.isArray(value) ? value : String(value).split(',');
return items.map((item) => String(item).trim()).filter(Boolean);
}
readChannelViewerImportFile(filePath) {
if (typeof filePath !== 'string' || filePath.trim().length === 0) {
throw new errors_1.PolyVValidationError('file path is required', 'file', filePath, 'validation_failed');
}
const resolvedPath = (0, path_1.resolve)(filePath);
if (!(0, fs_1.existsSync)(resolvedPath)) {
throw new errors_1.PolyVValidationError(`file does not exist: ${resolvedPath}`, 'file', filePath, 'validation_failed');
}
const stats = (0, fs_1.statSync)(resolvedPath);
if (!stats.isFile()) {
throw new errors_1.PolyVValidationError(`file path must point to a file: ${resolvedPath}`, 'file', filePath, 'validation_failed');
}
if (stats.size <= 0) {
throw new errors_1.PolyVValidationError(`file must not be empty: ${resolvedPath}`, 'file', filePath, 'validation_failed');
}
const allowedExtensions = new Set(['.xls', '.xlsx', '.csv']);
const extension = (0, path_1.extname)(resolvedPath).toLowerCase();
if (!allowedExtensions.has(extension)) {
throw new errors_1.PolyVValidationError('file must be an .xls, .xlsx, or .csv file', 'file', filePath, 'validation_failed');
}
const buffer = (0, fs_1.readFileSync)(resolvedPath);
return {
file: new buffer_1.Blob([buffer]),
path: resolvedPath,
size: stats.size
};
}
displayChannelViewerResult(message, result, format = 'table') {
if (format === 'json') {
this.displayData(result ?? { success: true }, 'json');
return;
}
this.displaySuccess(message, result ?? { success: true }, 'table');
}
}
exports.ChannelHandler = ChannelHandler;
//# sourceMappingURL=channel.handler.js.map