polyv-live-cli
Version:
CLI tool for managing PolyV live streaming services.
340 lines • 11.9 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BatchOperationsDialog = void 0;
const blessed_1 = __importDefault(require("blessed"));
class BatchOperationsDialog {
constructor(config) {
this.selectedChannels = [];
this.isVisible = false;
this.currentOperation = null;
this.operationInProgress = false;
this.config = config;
this.screen = config.screen;
this.eventBus = config.eventBus;
this.createDialog();
this.setupEventListeners();
}
createDialog() {
this.dialog = blessed_1.default.box({
parent: this.screen,
top: 'center',
left: 'center',
width: '60%',
height: '70%',
border: { type: 'line' },
style: {
fg: 'white',
bg: 'black',
border: { fg: 'cyan' }
},
shadow: true,
keys: true,
vi: true,
mouse: true,
hidden: true,
label: ' Batch Operations ',
tags: true
});
this.operationsList = blessed_1.default.list({
parent: this.dialog,
top: 1,
left: 1,
width: '100%-2',
height: '60%',
border: { type: 'line' },
style: {
fg: 'white',
bg: 'black',
border: { fg: 'gray' },
selected: { bg: 'blue', fg: 'white' }
},
label: ' Available Operations ',
keys: true,
vi: true,
mouse: true,
scrollable: true,
items: this.getOperationItems(),
tags: true
});
this.statusBox = blessed_1.default.box({
parent: this.dialog,
top: '60%',
left: 1,
width: '100%-2',
height: 6,
border: { type: 'line' },
style: {
fg: 'white',
bg: 'black',
border: { fg: 'gray' }
},
label: ' Operation Status ',
scrollable: true,
tags: true
});
this.progressBar = blessed_1.default.progressbar({
parent: this.dialog,
bottom: 4,
left: 1,
width: '100%-2',
height: 3,
border: { type: 'line' },
style: {
fg: 'white',
bg: 'black',
border: { fg: 'gray' },
bar: { bg: 'green' }
},
label: ' Progress ',
filled: 0,
hidden: true
});
this.footerBox = blessed_1.default.box({
parent: this.dialog,
bottom: 0,
left: 0,
width: '100%',
height: 2,
border: { type: 'line' },
style: {
fg: 'gray',
bg: 'black',
border: { fg: 'gray' }
},
content: this.getFooterContent(),
tags: true
});
this.setupKeyBindings();
}
getOperationItems() {
return [
'{green-fg}Start Streaming{/green-fg} - Start broadcasting on selected channels',
'{red-fg}Stop Streaming{/red-fg} - Stop broadcasting on selected channels',
'{yellow-fg}Refresh Data{/yellow-fg} - Refresh information for selected channels',
'{cyan-fg}Export Data{/cyan-fg} - Export selected channels data to file',
'{red-fg}Delete Channels{/red-fg} - {bold}DANGER:{/bold} Permanently delete selected channels'
];
}
setupKeyBindings() {
this.dialog.key(['escape', 'q'], () => {
if (!this.operationInProgress) {
this.hide();
}
});
this.dialog.key(['enter'], () => {
if (!this.operationInProgress) {
this.executeSelectedOperation();
}
});
this.dialog.key(['up', 'k'], () => {
this.operationsList.up(1);
this.screen.render();
});
this.dialog.key(['down', 'j'], () => {
this.operationsList.down(1);
this.screen.render();
});
this.dialog.key(['1'], () => {
this.operationsList.select(0);
this.executeSelectedOperation();
});
this.dialog.key(['2'], () => {
this.operationsList.select(1);
this.executeSelectedOperation();
});
this.dialog.key(['3'], () => {
this.operationsList.select(2);
this.executeSelectedOperation();
});
this.dialog.key(['4'], () => {
this.operationsList.select(3);
this.executeSelectedOperation();
});
this.dialog.key(['5'], () => {
this.operationsList.select(4);
this.executeSelectedOperation();
});
}
setupEventListeners() {
this.eventBus.on('batchOperation:completed', (result) => {
this.handleOperationCompleted(result);
});
this.eventBus.on('batchOperation:progress', (data) => {
this.updateProgress(data.current, data.total, data.channel);
});
}
show(selectedChannels) {
this.selectedChannels = selectedChannels;
this.isVisible = true;
this.operationInProgress = false;
this.updateStatusBox(`Ready to perform batch operations on ${selectedChannels.length} selected channels.`);
this.progressBar.hide();
this.dialog.show();
this.operationsList.focus();
this.screen.render();
this.eventBus.emit('batchOperations:shown', {
selectedChannels: selectedChannels.length,
timestamp: new Date()
});
}
hide() {
this.isVisible = false;
this.dialog.hide();
this.screen.render();
if (this.selectedChannels.length > 0) {
this.eventBus.emit('batchOperations:hidden', {
selectedChannels: this.selectedChannels.length,
timestamp: new Date()
});
}
this.selectedChannels = [];
this.currentOperation = null;
this.operationInProgress = false;
}
executeSelectedOperation() {
if (this.operationInProgress || this.selectedChannels.length === 0) {
return;
}
const selectedIndex = this.operationsList.selected;
const operations = ['start', 'stop', 'refresh', 'export', 'delete'];
const operation = operations[selectedIndex];
if (!operation)
return;
if (operation === 'delete') {
this.showConfirmationDialog(operation);
return;
}
this.startOperation(operation);
}
showConfirmationDialog(operation) {
const confirmDialog = blessed_1.default.question({
parent: this.screen,
top: 'center',
left: 'center',
width: '50%',
height: 'shrink',
border: { type: 'line' },
style: {
fg: 'white',
bg: 'red',
border: { fg: 'red' }
},
label: ' Confirmation Required ',
tags: true
});
const message = operation === 'delete'
? `Are you sure you want to DELETE ${this.selectedChannels.length} channels?\nThis action cannot be undone!`
: `Are you sure you want to perform ${operation} on ${this.selectedChannels.length} channels?`;
confirmDialog.ask(message, (_err, confirmed) => {
this.screen.remove(confirmDialog);
this.screen.render();
if (confirmed) {
this.startOperation(operation);
}
});
}
startOperation(operation) {
this.currentOperation = operation;
this.operationInProgress = true;
this.updateStatusBox(`Starting ${operation} operation on ${this.selectedChannels.length} channels...`);
this.progressBar.show();
this.progressBar.setProgress(0);
this.refreshFooter();
this.eventBus.emit('batchOperation:start', {
operation,
channels: this.selectedChannels,
timestamp: new Date()
});
}
updateProgress(current, total, channelInfo) {
if (!this.isVisible || !this.operationInProgress)
return;
const percentage = total > 0 ? Math.round((current / total) * 100) : 0;
this.progressBar.setProgress(percentage);
const statusText = channelInfo
? `Processing ${current}/${total} - ${channelInfo}`
: `Processing ${current}/${total} channels (${percentage}%)`;
this.updateStatusBox(statusText);
this.screen.render();
}
handleOperationCompleted(result) {
if (!this.isVisible || result.operation !== this.currentOperation)
return;
this.operationInProgress = false;
this.progressBar.setProgress(100);
const statusText = this.formatOperationResult(result);
this.updateStatusBox(statusText);
this.refreshFooter();
if (result.failureCount === 0) {
setTimeout(() => {
if (this.isVisible && !this.operationInProgress) {
this.hide();
}
}, 3000);
}
}
formatOperationResult(result) {
const { operation, totalChannels, successCount, failureCount } = result;
let statusText = `{bold}Operation Complete:{/bold} ${operation}\n`;
statusText += `Total: ${totalChannels}, Success: {green-fg}${successCount}{/green-fg}, Failed: {red-fg}${failureCount}{/red-fg}\n`;
if (failureCount > 0 && result.errors.length > 0) {
statusText += '\n{red-fg}Errors:{/red-fg}\n';
result.errors.slice(0, 3).forEach(error => {
statusText += `- ${error.channelId}: ${error.error}\n`;
});
if (result.errors.length > 3) {
statusText += `... and ${result.errors.length - 3} more errors\n`;
}
}
return statusText;
}
updateStatusBox(content) {
this.statusBox.setContent(content);
}
getFooterContent() {
if (this.operationInProgress) {
return `{center}{gray-fg}Operation in progress... Please wait{/gray-fg}{/center}`;
}
const colorSupport = this.config.showColors ? 'Colors: ON' : 'Colors: OFF';
return `{center}{gray-fg}Enter: Execute | ↑↓: Navigate | 1-5: Quick Select | ESC/q: Close | ${colorSupport}{/gray-fg}{/center}`;
}
updateFooter() {
this.footerBox.setContent(this.getFooterContent());
}
refreshFooter() {
this.updateFooter();
this.screen.render();
}
isShowing() {
return this.isVisible;
}
getSelectedChannels() {
return [...this.selectedChannels];
}
getOperationStatus() {
return {
operation: this.currentOperation,
inProgress: this.operationInProgress
};
}
destroy() {
this.hide();
if (this.dialog && this.screen) {
try {
if (typeof this.screen.remove === 'function') {
this.screen.remove(this.dialog);
}
}
catch (error) {
}
}
this.eventBus.emit('batchOperations:destroyed', {
timestamp: new Date()
});
}
}
exports.BatchOperationsDialog = BatchOperationsDialog;
//# sourceMappingURL=batch-operations.dialog.js.map