support color output in console

This commit is contained in:
ArabCoders 2024-12-11 20:50:49 +03:00
parent 3b7338eb79
commit 82f59676c7
3 changed files with 81 additions and 41 deletions

View file

@ -4,8 +4,10 @@ import argparse
import asyncio
import base64
from datetime import datetime
import errno
import json
import os
import pty
import random
import shlex
import time
@ -603,46 +605,80 @@ class Main:
await self.sio.emit('cli_close', {'exitcode': 0})
return
proc = None
try:
async def _read_stream(streamType, stream):
while True:
line = await stream.readline()
if line:
await self.sio.emit('cli_output', {
'type': streamType,
'line': line.decode('utf-8')
})
else:
break
LOG.info(f'CLI: {data}')
args = ['yt-dlp', *shlex.split(data)]
args = ['yt-dlp'] + shlex.split(data)
_env = os.environ.copy()
_env.update({
"TERM": "xterm-256color",
"LANG": "en_US.UTF-8",
"SHELL": "/bin/bash",
"LC_ALL": "en_US.UTF-8",
"PWD": self.config.download_path,
"FORCE_COLOR": "1",
"PYTHONUNBUFFERED": "1",
})
master_fd, slave_fd = pty.openpty()
proc = await asyncio.create_subprocess_exec(
*args,
cwd=self.config.download_path,
stdin=asyncio.subprocess.DEVNULL,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
env=os.environ.copy().update({
"TERM": "xterm-256color",
"LANG": "en_US.UTF-8",
"SHELL": "/bin/bash",
"LC_ALL": "en_US.UTF-8",
"PWD": self.config.download_path,
}),
stdout=slave_fd,
stderr=slave_fd,
env=_env,
)
await asyncio.gather(
_read_stream('stdout', proc.stdout),
_read_stream('stderr', proc.stderr),
)
try:
os.close(slave_fd)
except Exception as e:
LOG.error(f'Error closing PTY: {str(e)}')
while proc.returncode is None:
await asyncio.sleep(0.1)
async def read_pty():
loop = asyncio.get_running_loop()
buffer = b''
while True:
try:
chunk = await loop.run_in_executor(None, lambda: os.read(master_fd, 1024))
except OSError as e:
if e.errno == errno.EIO:
break
else:
raise
await self.sio.emit('cli_close', {'exitcode': proc.returncode})
if not chunk:
# No more output
if buffer:
# Emit any remaining partial line
await self.sio.emit('cli_output', {
'type': 'stdout',
'line': buffer.decode('utf-8', errors='replace')
})
break
buffer += chunk
*lines, buffer = buffer.split(b'\n')
for line in lines:
await self.sio.emit('cli_output', {
'type': 'stdout',
'line': line.decode('utf-8', errors='replace')
})
try:
os.close(master_fd)
except Exception as e:
LOG.error(f'Error closing PTY: {str(e)}')
# Start reading output from PTY
read_task = asyncio.create_task(read_pty())
# Wait until process finishes
returncode = await proc.wait()
# Ensure reading is done
await read_task
await self.sio.emit('cli_close', {'exitcode': returncode})
except Exception as e:
LOG.error(f'CLI Error: {str(e)}')
await self.sio.emit('cli_output', {

View file

@ -15,7 +15,8 @@
<span class="icon"><font-awesome-icon icon="fa-solid fa-terminal" /></span> Terminal
</p>
<p class="card-header-icon">
<span v-tooltip.top="'Clear console window'" class="icon" @click="clearOutput"><font-awesome-icon icon="fa-solid fa-broom" /></span>
<span v-tooltip.top="'Clear console window'" class="icon" @click="clearOutput"><font-awesome-icon
icon="fa-solid fa-broom" /></span>
</p>
</header>
<section class="card-content p-0 m-0">
@ -23,16 +24,23 @@
</section>
<section class="card-content p-1 m-1">
<div class="field is-grouped">
<div class="control is-expanded has-icons-left">
<div class="control">
<span class="icon-text input is-unselectable">
<span class="icon"><font-awesome-icon icon="fa-solid fa-terminal" /></span>
<span>yt-dlp</span>
</span>
</div>
<div class="control is-expanded">
<input type="text" class="input" v-model="command" placeholder="ls -la" autocomplete="off"
ref="command_input" @keydown.enter="runCommand" :disabled="props.isLoading" id="command">
<span class="icon is-left"><font-awesome-icon icon="fa-solid fa-terminal"
:spin="props.isLoading" /></span>
</div>
<p class="control">
<button class="button is-primary" type="button" :disabled="props.isLoading || '' === command"
@click="runCommand">
<span class="icon"><font-awesome-icon icon="fa-solid fa-paper-plane" /></span>
<span class="icon">
<font-awesome-icon icon="fa-solid fa-spinner" spin v-if="props.isLoading" />
<font-awesome-icon icon="fa-solid fa-paper-plane" v-else />
</span>
</button>
</p>
</div>
@ -132,7 +140,8 @@ const runCommand = async () => {
}
emitter('runCommand', command.value)
terminal.value.writeln(`~ ${command.value}`)
terminal.value.writeln(`user@YTPTube ~`)
terminal.value.writeln(`$ yt-dlp ${command.value}`)
}
const clearOutput = async (withCommand = false) => {

View file

@ -7,12 +7,7 @@ import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import {
faCog, faTrash, faLink, faPlus, faTrashCan, faCircleXmark, faCircleCheck, faRotateRight, faDownload, faUpRightFromSquare,
faSpinner, faArrowUp, faArrowDown, faTasks, faCalendar, faArrowUpAZ, faArrowDownAZ, faEject, faGlobe, faMoon, faSun,
faTerminal,
faBroom,
faServer,
faPowerOff,
faPaperPlane,
faBoxArchive
faTerminal, faBroom, faServer, faPowerOff, faPaperPlane, faBoxArchive,
} from '@fortawesome/free-solid-svg-icons'
import { faSquare, faSquareCheck } from '@fortawesome/free-regular-svg-icons'