UNPKG

polyv-live-cli

Version:

CLI tool for managing PolyV live streaming services.

1,111 lines 56.1 kB
"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