polyv-live-cli
Version:
CLI tool for managing PolyV live streaming services.
351 lines • 15.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PlaybackHandler = void 0;
const base_handler_1 = require("./base.handler");
const playback_service_sdk_1 = require("../services/playback.service.sdk");
const confirmation_1 = require("../utils/confirmation");
const api_command_1 = require("../utils/api-command");
class PlaybackHandler extends base_handler_1.BaseHandler {
constructor(authConfig, serviceConfig, playbackService) {
super();
this.playbackService = playbackService ?? new playback_service_sdk_1.PlaybackServiceSdk(authConfig, serviceConfig);
}
async listPlayback(options) {
return this.executeWithErrorHandling(async () => {
if (!options.channelId || options.channelId.trim() === '') {
throw new Error('channelId is required');
}
const result = await this.playbackService.getPlaybackList(options);
this.displayPlaybackList(result.contents, options.channelId, options.output);
}, 'playback.list');
}
async getPlayback(options) {
return this.executeWithErrorHandling(async () => {
if (!options.channelId || options.channelId.trim() === '') {
throw new Error('channelId is required');
}
if (!options.videoId || options.videoId.trim() === '') {
throw new Error('videoId is required');
}
const result = await this.playbackService.getPlaybackList({
channelId: options.channelId,
...(options.listType && { listType: options.listType }),
});
const playback = result.contents.find((item) => item.videoId === options.videoId);
if (!playback) {
this.displayInfo(`未找到回放视频 - 频道: ${options.channelId}, 视频ID: ${options.videoId}`);
return;
}
this.displayPlaybackDetail(playback, options.channelId, options.output);
}, 'playback.get');
}
async deletePlayback(options) {
return this.executeWithErrorHandling(async () => {
if (!options.channelId || options.channelId.trim() === '') {
throw new Error('channelId is required');
}
if (!options.videoId || options.videoId.trim() === '') {
throw new Error('videoId is required');
}
if (!options.force) {
if (!(0, confirmation_1.isInteractiveEnvironment)()) {
throw new Error('Interactive confirmation not available in non-TTY environment. Use --force flag to bypass confirmation.');
}
const confirmed = await (0, confirmation_1.confirmDeletion)(`确定要删除回放视频 '${options.videoId}' 吗?此操作无法撤销。`, 'yes');
if (!confirmed) {
this.displayInfo('删除操作已取消');
return;
}
}
let playbackInfo;
try {
const result = await this.playbackService.getPlaybackList({
channelId: options.channelId,
...(options.listType && { listType: options.listType }),
});
playbackInfo = result.contents.find((item) => item.videoId === options.videoId);
}
catch {
}
await this.playbackService.deletePlayback(options.channelId, options.videoId, options.listType);
this.displayDeleteResult(options, playbackInfo);
}, 'playback.delete');
}
async listPlaybackSettings(options) {
return this.executeWithErrorHandling(async () => {
const result = await this.playbackService.listPlaybackSettings(this.normalizeStringList(options.channelIds));
this.displayResult(result, options.output);
}, 'playback.setting-list');
}
async getPlaybackVideoInfo(options) {
return this.executeWithErrorHandling(async () => {
const result = await this.playbackService.getPlaybackVideoInfo(this.normalizeStringList(options.channelIds));
this.displayResult(result, options.output);
}, 'playback.video-info');
}
async updateChannelSubtitles(options) {
return this.executeWithErrorHandling(async () => {
await (0, api_command_1.confirmWrite)(options.force, `Update playback subtitles for channel ${options.channelId}?`);
await this.playbackService.updateChannelSubtitles(options.channelId, options.body);
this.displayResult({ channelId: options.channelId, updated: true }, options.output);
}, 'playback.subtitle.update-batch');
}
async getPlaybackEnabled(options) {
return this.executeWithErrorHandling(async () => {
const result = await this.playbackService.getPlaybackEnabled(options.channelId);
this.displayResult(result, options.output);
}, 'playback.enabled.get');
}
async setPlaybackEnabled(options) {
return this.executeWithErrorHandling(async () => {
await (0, api_command_1.confirmWrite)(options.force, `Set playback enabled to ${options.playBackEnabled} for user ${options.userId}?`);
const result = await this.playbackService.setPlaybackEnabled({
userId: options.userId,
playBackEnabled: options.playBackEnabled,
channelId: options.channelId,
});
this.displayResult(result ?? { updated: true }, options.output);
}, 'playback.enabled.set');
}
async addVodPlayback(options) {
return this.executeWithErrorHandling(async () => {
await (0, api_command_1.confirmWrite)(options.force, `Add VOD ${options.vid} to channel ${options.channelId} playback library?`);
const result = await this.playbackService.addVodPlayback({
channelId: options.channelId,
vid: options.vid,
setAsDefault: options.setAsDefault,
listType: options.listType,
});
this.displayResult(result, options.output);
}, 'playback.add-vod');
}
async updatePlaybackTitle(options) {
return this.executeWithErrorHandling(async () => {
await (0, api_command_1.confirmWrite)(options.force, `Update playback ${options.videoId} title?`);
await this.playbackService.updatePlaybackTitle(options.channelId, options.videoId, options.title);
this.displayResult({ channelId: options.channelId, videoId: options.videoId, title: options.title, updated: true }, options.output);
}, 'playback.title.update');
}
async movePlaybackVideo(options) {
return this.executeWithErrorHandling(async () => {
await (0, api_command_1.confirmWrite)(options.force, `Move playback video ${options.videoId} ${options.type}?`);
await this.playbackService.movePlaybackVideo({
channelId: options.channelId,
videoId: options.videoId,
type: options.type,
listType: options.listType,
});
this.displayResult({ channelId: options.channelId, videoId: options.videoId, type: options.type, moved: true }, options.output);
}, 'playback.sort.move');
}
async sortPlaybackVideos(options) {
return this.executeWithErrorHandling(async () => {
const videoIds = this.normalizeStringList(options.videoIds);
await (0, api_command_1.confirmWrite)(options.force, `Sort ${videoIds.length} playback video(s) for channel ${options.channelId}?`);
await this.playbackService.sortPlaybackVideos({
channelId: options.channelId,
videoIds,
listType: options.listType,
});
this.displayResult({ channelId: options.channelId, videoIds, sorted: true }, options.output);
}, 'playback.sort.set');
}
displayPlaybackList(contents, channelId, format = 'table') {
if (contents.length === 0) {
this.displayInfo(`暂无回放视频 - 频道: ${channelId}`);
return;
}
this.displayInfo(`回放列表 - 频道: ${channelId}`);
this.displayInfo(`共 ${contents.length} 个回放视频`);
if (format === 'json') {
this.displayData(contents, 'json');
}
else {
this.displayPlaybackTable(contents);
}
}
displayPlaybackTable(contents) {
const tableData = contents.map((item) => ({
'视频ID': item.videoId,
'标题': item.title,
'时长': item.duration,
'创建时间': this.formatTimestamp(item.createdTime),
'状态': item.status === 'Y' ? '可用' : '不可用',
}));
this.displayAsTable(tableData);
}
displayPlaybackDetail(playback, channelId, format = 'table') {
this.displayInfo(`回放详情 - 频道: ${channelId}`);
if (format === 'json') {
this.displayData(playback, 'json');
}
else {
this.displayPlaybackDetailTable(playback);
}
}
displayPlaybackDetailTable(playback) {
const tableData = [{
'视频ID': playback.videoId,
'标题': playback.title,
'时长': playback.duration,
'创建时间': this.formatTimestamp(playback.createdTime),
'状态': playback.status === 'Y' ? '可用' : '不可用',
}];
this.displayAsTable(tableData);
}
displayDeleteResult(options, playbackInfo) {
const format = options.output || 'table';
const resultData = {
channelId: options.channelId,
videoId: options.videoId,
title: playbackInfo?.title || '未知',
status: '已删除',
};
this.displayInfo(`删除回放视频 - 频道: ${options.channelId}`);
if (format === 'json') {
this.displayData(resultData, 'json');
}
else {
this.displayDeleteResultTable(resultData);
}
}
normalizeStringList(value) {
const items = Array.isArray(value) ? value : String(value ?? '').split(',');
const list = items.map((item) => String(item).trim()).filter(Boolean);
if (list.length === 0) {
throw new Error('list must not be empty');
}
return list;
}
displayResult(result, format = 'table') {
this.displayData(result ?? { success: true }, format);
}
displayDeleteResultTable(resultData) {
const tableData = [{
'频道': resultData.channelId,
'视频ID': resultData.videoId,
'标题': resultData.title,
'状态': resultData.status,
}];
this.displayAsTable(tableData);
}
formatTimestamp(timestamp) {
if (!timestamp)
return '-';
const date = new Date(timestamp);
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
});
}
async mergePlayback(options) {
return this.executeWithErrorHandling(async () => {
if (!options.channelId || options.channelId.trim() === '') {
throw new Error('channelId is required');
}
if (!options.fileIds || options.fileIds.trim() === '') {
throw new Error('fileIds is required');
}
const fileIdArray = options.fileIds
.split(',')
.map(id => id.trim())
.filter(id => id);
if (fileIdArray.length === 0) {
throw new Error('At least one valid fileId is required');
}
if (fileIdArray.length > 15) {
throw new Error('Maximum 15 files can be merged at once');
}
if (options.async) {
const asyncOptions = {};
if (options.fileName !== undefined) {
asyncOptions.fileName = options.fileName;
}
if (options.callbackUrl !== undefined) {
asyncOptions.callbackUrl = options.callbackUrl;
}
if (options.autoConvert !== undefined) {
asyncOptions.autoConvert = options.autoConvert;
}
if (options.mergeMp4 !== undefined) {
asyncOptions.mergeMp4 = options.mergeMp4;
}
await this.playbackService.mergePlaybackAsync(options.channelId, fileIdArray, asyncOptions);
this.displayAsyncMergeResult(options, fileIdArray);
}
else {
const result = await this.playbackService.mergePlayback(options.channelId, fileIdArray, options.fileName);
this.displayMergeResult(options, fileIdArray, result);
}
}, 'playback.merge');
}
displayMergeResult(options, fileIdArray, result) {
const format = options.output || 'table';
const resultData = {
channelId: options.channelId,
fileName: options.fileName || '未命名',
sourceFileCount: fileIdArray.length,
status: '成功',
fileId: result.fileId,
};
if (result.url !== undefined) {
resultData.url = result.url;
}
this.displayInfo(`合并成功`);
this.displayInfo(`合并录制文件 - 频道: ${options.channelId}`);
if (format === 'json') {
this.displayData(resultData, 'json');
}
else {
this.displayMergeResultTable(resultData);
}
}
displayMergeResultTable(resultData) {
const tableData = [{
'频道': resultData.channelId,
'文件名': resultData.fileName,
'源文件数': resultData.sourceFileCount,
'合并结果': resultData.status,
'文件地址': resultData.url ?? '-',
}];
this.displayAsTable(tableData);
}
displayAsyncMergeResult(options, fileIdArray) {
const format = options.output || 'table';
const resultData = {
channelId: options.channelId,
fileName: options.fileName || '未命名',
sourceFileCount: fileIdArray.length,
status: '处理中',
};
if (options.callbackUrl !== undefined) {
resultData.callbackUrl = options.callbackUrl;
}
this.displayInfo(`合并任务已提交`);
this.displayInfo(`合并录制文件(异步) - 频道: ${options.channelId}`);
if (format === 'json') {
this.displayData(resultData, 'json');
}
else {
this.displayAsyncMergeResultTable(resultData);
}
}
displayAsyncMergeResultTable(resultData) {
const tableData = [{
'频道': resultData.channelId,
'文件名': resultData.fileName,
'源文件数': resultData.sourceFileCount,
'状态': resultData.status,
'提示': resultData.callbackUrl
? '合并完成后将通过回调URL通知,文件ID将在回调中返回'
: '合并完成后请在频道回放列表查看(使用 playback list 命令)',
'说明': '异步合并不立即返回文件ID,请稍后查询',
}];
this.displayAsTable(tableData);
}
}
exports.PlaybackHandler = PlaybackHandler;
//# sourceMappingURL=playback.handler.js.map