UNPKG

polyv-live-cli

Version:

CLI tool for managing PolyV live streaming services.

351 lines 15.9 kB
"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