Frontend komplett überarbeitet, Verbleibende Zeit für Ladebalken und Zeit Angabe sowie Lesbare Speicher Größe hinzugefügt
This commit is contained in:
parent
f3d9c98b38
commit
cd5204fa12
10 changed files with 247 additions and 39 deletions
|
|
@ -21,6 +21,7 @@ class Convert:
|
|||
obj.task = asyncio.create_task(self.convert_video(obj))
|
||||
self.active_tasks.add(obj)
|
||||
logging.info(f"Warteschlange started Auftrag - {obj.task}")
|
||||
obj.status = 3
|
||||
|
||||
if len(self.active_tasks) >= self.yaml["task_max"]:
|
||||
break
|
||||
|
|
@ -46,6 +47,7 @@ class Convert:
|
|||
result = None
|
||||
|
||||
logging.info(f"Starte Konvertierung: {command}")
|
||||
await self.obj_websocket.send_websocket(self.obj_path.active_path_to_dict())
|
||||
|
||||
try:
|
||||
# Starte den Subprozess asynchron
|
||||
|
|
@ -61,7 +63,7 @@ class Convert:
|
|||
await obj_process.read_out(obj)
|
||||
await obj.process.wait()
|
||||
|
||||
# Prozess beendet, Status auswerten
|
||||
# Prself.obj_websocket.send_websocket(self.obj_path.active_path_to_dict())ozess beendet, Status auswerten
|
||||
if obj.process.returncode == 0:
|
||||
obj.status = 0
|
||||
result = "Finished"
|
||||
|
|
@ -76,6 +78,7 @@ class Convert:
|
|||
logging.info(f"Prozess {result}({obj.process.returncode}): {obj.source_file_name}")
|
||||
await self.obj_websocket.send_websocket(obj.to_dict())
|
||||
|
||||
await self.obj_websocket.send_websocket(self.obj_path.active_path_to_dict())
|
||||
self.active_process.discard(obj)
|
||||
self.active_tasks.discard(obj)
|
||||
self.obj_path.save_paths()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ class Process:
|
|||
self.size: list = [0, "KiB"]
|
||||
|
||||
self.time: int = 0
|
||||
self.time_remaining = 0
|
||||
self.loading = 0
|
||||
self.frames: int = 0
|
||||
|
||||
|
||||
|
|
@ -30,6 +32,10 @@ class Process:
|
|||
line_decoded = line.decode()
|
||||
|
||||
self.process_line_extract(obj, line_decoded)
|
||||
#logging.info(line_decoded)
|
||||
|
||||
self.time_remaining = obj.format_time(obj.time_remaining())
|
||||
self.loading = (self.frames / obj.source_frames_total) * 100
|
||||
|
||||
await self.obj_websocket.send_websocket(self.to_dict())
|
||||
|
||||
|
|
@ -47,7 +53,7 @@ class Process:
|
|||
self.save_stat_value(obj)
|
||||
elif i == 101 or i == 501:
|
||||
i = 0
|
||||
time = obj.format_time(obj.time_remaining())
|
||||
time = self.time_remaining
|
||||
if time != " ":
|
||||
logging.info(f"Time remaining: {time}")
|
||||
|
||||
|
|
@ -60,25 +66,29 @@ class Process:
|
|||
|
||||
# Quantizer
|
||||
q = re.findall(r"q=\s*(\d+).\d+", line)
|
||||
#logging.info(f"q: {q}")
|
||||
self.quantizer = int(q[0]) if q else 0
|
||||
|
||||
# Bitrate
|
||||
bitrate = re.findall(r"bitrate=\s*(\d+)", line)
|
||||
#logging.info(f"bitrate: {bitrate}")
|
||||
self.bitrate[0] = int(bitrate[0]) if bitrate else 0
|
||||
|
||||
# Speed
|
||||
speed = re.findall(r"speed=\s*(\d+\.\d+)", line)
|
||||
#logging.info(f"speed: {speed}")
|
||||
self.speed = float(speed[0]) if speed else 0.0
|
||||
|
||||
# File Size
|
||||
size = re.findall(r"size=\s*(\d+)", line)
|
||||
if size and int(size[0]) > self.size[0]:
|
||||
self.size[0] = int(size[0])
|
||||
self.size = obj.size_convert("KiB", None, "storage", int(size[0]))
|
||||
obj.process_size = self.size
|
||||
|
||||
# Time
|
||||
media_time = re.findall(r"time=\s*(\d+:\d+:\d+)", line)
|
||||
time_v = media_time[0] if media_time else "00:00:00"
|
||||
#logging.info(media_time)
|
||||
if self.time < obj.time_in_sec(time_v):
|
||||
self.time = obj.time_in_sec(time_v)
|
||||
obj.process_time = self.time
|
||||
|
|
@ -103,12 +113,15 @@ class Process:
|
|||
obj.stat_speed = [obj.stat_speed[0] + self.speed, obj.stat_speed[1] + 1]
|
||||
|
||||
def to_dict(self):
|
||||
return {"data_flow": {self.id: {
|
||||
return {"data_flow": {
|
||||
"id": self.id,
|
||||
"frames": self.frames,
|
||||
"fps": self.fps,
|
||||
"quantizer": self.quantizer,
|
||||
"size": self.size,
|
||||
"time": self.time,
|
||||
"time_remaining": self.time_remaining,
|
||||
"loading": self.loading,
|
||||
"bitrate": self.bitrate,
|
||||
"speed": self.speed
|
||||
}}}
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -176,4 +176,13 @@ class Path:
|
|||
|
||||
return count
|
||||
|
||||
def active_path_to_dict(self):
|
||||
list_active_paths = {}
|
||||
|
||||
for obj in self.paths.values():
|
||||
if obj.status == 3:
|
||||
list_active_paths.update({obj.id: obj.to_dict_active_paths()})
|
||||
|
||||
return {"data_convert": list_active_paths}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class Media:
|
|||
self.process_start: int = 0
|
||||
self.process_end: int = 0
|
||||
self.process_time: int = 0
|
||||
self.process_size: int = 0
|
||||
self.process_size: list = [0, "KiB"]
|
||||
self.process_frames: int = 0
|
||||
self.process_time_remaining: int = 0
|
||||
|
||||
|
|
@ -79,9 +79,21 @@ class Media:
|
|||
|
||||
return output_string
|
||||
|
||||
@staticmethod
|
||||
def to_dict():
|
||||
return "Fertig mit der Welt"
|
||||
def to_dict_active_paths(self):
|
||||
return {
|
||||
"source_file_name": self.source_file_name,
|
||||
"source_file": self.source_file,
|
||||
"source_duration": self.source_duration,
|
||||
"source_size": self.source_size,
|
||||
"source_frame_rate": self.source_frame_rate,
|
||||
"source_frames_total": self.source_frames_total,
|
||||
"source_time": self.source_time,
|
||||
|
||||
# target
|
||||
"target_file_name": self.target_file_name,
|
||||
"target_file": self.target_file,
|
||||
"target_size": self.target_size
|
||||
}
|
||||
|
||||
def to_dict_stat(self):
|
||||
return {self.id: {
|
||||
|
|
|
|||
|
|
@ -3,11 +3,28 @@ import yaml
|
|||
import traceback
|
||||
from logging.handlers import TimedRotatingFileHandler, RotatingFileHandler
|
||||
|
||||
# === ANSI-Farbcodes für Konsole ===
|
||||
RESET = "\x1b[0m" # Farbe zurücksetzen
|
||||
RED = "\x1b[31m" # Fehler: Rot
|
||||
YELLOW = "\x1b[33m" # Warnung: Gelb
|
||||
GREEN = "\x1b[32m" # Info: Grün
|
||||
BLUE = "\x1b[34m" # Debug: Blau
|
||||
|
||||
class CustomFormatter(logging.Formatter):
|
||||
def format(self, record):
|
||||
if record.levelno >= logging.ERROR and record.exc_info:
|
||||
record.msg = f"{record.msg}\n{traceback.format_exc()}"
|
||||
|
||||
# Farbe nach Level
|
||||
if record.levelno == logging.DEBUG:
|
||||
record.msg = f"{BLUE}{record.msg}{RESET}"
|
||||
elif record.levelno == logging.INFO:
|
||||
record.msg = f"{GREEN}{record.msg}{RESET}"
|
||||
elif record.levelno == logging.WARNING:
|
||||
record.msg = f"{YELLOW}{record.msg}{RESET}"
|
||||
elif record.levelno >= logging.ERROR:
|
||||
record.msg = f"{RED}{record.msg}{RESET}"
|
||||
|
||||
return super().format(record)
|
||||
|
||||
class Settings:
|
||||
|
|
|
|||
|
|
@ -37,9 +37,13 @@ class Server:
|
|||
|
||||
async def handle_client(self, websocket):
|
||||
self.websocket = websocket
|
||||
self.clients.add(websocket)
|
||||
|
||||
global var_convert_active
|
||||
|
||||
if websocket not in self.clients:
|
||||
self.clients.add(websocket)
|
||||
await self.send_websocket(self.obj_path.active_path_to_dict())
|
||||
|
||||
try:
|
||||
async for message in websocket:
|
||||
data = json.loads(message)
|
||||
|
|
@ -82,15 +86,27 @@ class Server:
|
|||
|
||||
@staticmethod
|
||||
async def handle_index(request):
|
||||
query = request.rel_url.query
|
||||
if "debug" in query:
|
||||
logging.info("Debug-Modus aktiv beim Seitenaufruf")
|
||||
|
||||
return web.FileResponse("./client/index.html")
|
||||
|
||||
async def handle_ip(self, request):
|
||||
query = request.rel_url.query
|
||||
if "debug" in query:
|
||||
logging.info("Debug-Modus aktiv beim Seitenaufruf")
|
||||
|
||||
ip = self.yaml.get("server_ip", "localhost")
|
||||
port = self.yaml.get("server_port", 8000)
|
||||
return web.json_response({"server_ip": ip, "server_port": port})
|
||||
|
||||
@staticmethod
|
||||
async def handle_stat(request):
|
||||
query = request.rel_url.query
|
||||
if "debug" in query:
|
||||
logging.info("Debug-Modus aktiv beim Seitenaufruf")
|
||||
|
||||
obj_stat = Stat()
|
||||
return web.json_response(obj_stat.read_stat())
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<header>
|
||||
<h1>Video Konvertierung</h1>
|
||||
<nav>
|
||||
<button onclick="alert('Statistiken bald verfügbar')">Statistiken</button>
|
||||
<button onclick="toggleStatHidden()">Statistiken</button>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
|
@ -28,10 +28,12 @@
|
|||
</section>
|
||||
|
||||
<!-- === Statistikbereich === -->
|
||||
<section id="stat">
|
||||
<section id="stat" hidden=True>
|
||||
<h2>Allgemeine Statistiken</h2>
|
||||
<p>Hier könnten Diagramme, Durchschnittswerte etc. angezeigt werden.</p>
|
||||
</section>
|
||||
<script src="/client/client.js"></script>
|
||||
|
||||
<script src="/client/media_conversion.js"></script>
|
||||
<script src="/client/media_stat.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -61,6 +61,19 @@ nav button:hover {
|
|||
margin: 0 0 0.5rem;
|
||||
}
|
||||
|
||||
.video-card-values {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr auto;
|
||||
grid-auto-rows: auto;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.video-card-values-items {
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.video-card .actions button {
|
||||
background-color: #444;
|
||||
color: white;
|
||||
|
|
@ -75,6 +88,14 @@ nav button:hover {
|
|||
background-color: #666;
|
||||
}
|
||||
|
||||
.delete-button {
|
||||
grid-column: 4;
|
||||
grid-row: 1 / span 3; /* oder wie viele Zeilen du hast */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
background-color: #333;
|
||||
|
|
@ -94,6 +115,22 @@ nav button:hover {
|
|||
display: block;
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
width: 100%;
|
||||
background: #333;
|
||||
border-radius: 5px;
|
||||
margin-top: 10px;
|
||||
height: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 100%;
|
||||
width: 0%;
|
||||
background: linear-gradient(90deg, #4caf50, #00c853);
|
||||
transition: width 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
/* === Bereich: Warteschleife === */
|
||||
#queue {
|
||||
padding: 1rem;
|
||||
|
|
|
|||
121
client/media_conversion.js
Normal file
121
client/media_conversion.js
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/**
|
||||
* @returns {Promise<{server_ip: string, server_port: number}>}
|
||||
*/
|
||||
|
||||
let videoActive = []
|
||||
|
||||
async function getServerConfig() {
|
||||
const response = await fetch('/api/ip');
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async function getMediaStat() {
|
||||
const response = await fetch('/api/stats');
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
getServerConfig()
|
||||
.then(data => {
|
||||
const websocketIp = data.server_ip;
|
||||
const websocketPort = data.server_port;
|
||||
const ws = new WebSocket(`ws://${websocketIp}:${websocketPort}`);
|
||||
|
||||
ws.onopen = function() {
|
||||
console.log("WebSocket ist geöffnet");
|
||||
ws.send(JSON.stringify({"data_message": "Server Adresse: " + websocketIp + ":" + websocketPort}));
|
||||
};
|
||||
|
||||
ws.onmessage = function(messageEvent) {
|
||||
try{
|
||||
console.log(messageEvent.data);
|
||||
let packet = JSON.parse(messageEvent.data);
|
||||
|
||||
if (packet.data_flow !== undefined){
|
||||
updateVideoElement(packet);
|
||||
} else if(packet.data_convert !== undefined){
|
||||
createVideoElement(packet);
|
||||
}
|
||||
} catch (e){
|
||||
console.error("Error parsing data flow");
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = function() {
|
||||
console.log("WebSocket wurde geschlossen");
|
||||
};
|
||||
|
||||
ws.onerror = function(errorEvent) {
|
||||
console.error("WebSocket-Fehler: ", errorEvent);
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching settings:', error);
|
||||
});
|
||||
|
||||
function createVideoElement(packet){
|
||||
const active_Conversions = document.getElementById('active-conversions');
|
||||
|
||||
Object.keys(packet.data_convert).forEach(key => {
|
||||
const video = packet.data_convert[key];
|
||||
|
||||
if(!videoActive[key]){
|
||||
const card = document.createElement('div');
|
||||
card.className = 'video-card';
|
||||
card.id = key
|
||||
|
||||
card.innerHTML = `
|
||||
<h3 title="${video.source_file_name}" align="center">${video.source_file_name} - ${video.target_file_name}</h3>
|
||||
<div class="progress-container">
|
||||
<div class="progress-bar"></div>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="video-card-values">
|
||||
<div title="Anzahl Frames" class="video-card-values-items"><b>Frames:</b> <span class="frames">0</span> Anz</div>
|
||||
<div title="Größe" class="video-card-values-items"><b>Größe:</b> <span class="size">0</span> <span class="size_unit"></span></div>
|
||||
<div title="FPS" class="video-card-values-items"><b>FPS:</b> <span class="fps">0</span></div>
|
||||
<div title="Quantizer" class="video-card-values-items"><b>Quanitzer:</b> <span class="quantizer">0</span></div>
|
||||
<div title="Bitrate" class="video-card-values-items"><b>Bitrate:</b> <span class="bitrate">0</span> <span class="bitrate_unit"></span></div>
|
||||
<div title="Speed" class="video-card-values-items"><b>Speed:</b> <span class="speed">0</span></div>
|
||||
<div title="Zeit" class="video-card-values-items"><b>Zeit:</b> <span class="time_remaining">0</span></div>
|
||||
<div class="video-card-values-items delete-button"><button class="delete-button">Löschen</button></div>
|
||||
</div>
|
||||
<div class="tooltip">
|
||||
Quelle: ${video.key}<br>
|
||||
</div>`;
|
||||
|
||||
|
||||
active_Conversions.appendChild(card)
|
||||
videoActive[key] = video;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateVideoElement(packet){
|
||||
let video = packet.data_flow;
|
||||
let container = document.getElementById(video.id);
|
||||
|
||||
container.querySelector(".frames").textContent = video.frames || 0;
|
||||
container.querySelector(".size").textContent = video.size[0] || 0;
|
||||
container.querySelector(".size_unit").textContent = video.size[1] || "KB";
|
||||
container.querySelector(".fps").textContent = video.fps || 0;
|
||||
container.querySelector(".quantizer").textContent = video.quantizer || 0;
|
||||
container.querySelector(".bitrate").textContent = video.bitrate[0] || 0;
|
||||
container.querySelector(".bitrate_unit").textContent = video.bitrate[1] || 0;
|
||||
container.querySelector(".speed").textContent = video.speed || 0;
|
||||
container.querySelector(".time_remaining").textContent = video.time_remaining || 0;
|
||||
|
||||
let progressBar = container.querySelector(".progress-bar");
|
||||
let progress = video.loading; // Annahme: `loading` kommt als Zahl von 0 bis 100
|
||||
progressBar.style.width = progress + "%"
|
||||
|
||||
if (videoActive[video.id].status == 3) {
|
||||
progressBar.style.background = "linear-gradient(90deg, #4caf50, #00c853)";
|
||||
container.querySelector(".finished").textContent = "Läuft";
|
||||
} else if(videoActive[video.id].status == 0) {
|
||||
progressBar.style.background = "#4caf50";
|
||||
container.querySelector(".finished").textContent = "Fertig";
|
||||
} else if(videoActive[video.id].status == 1) {
|
||||
progressBar.style.background = "#ff0000";
|
||||
container.querySelector(".finished").textContent = "Fehler";
|
||||
}
|
||||
}
|
||||
|
|
@ -11,32 +11,10 @@ async function getMediaStat() {
|
|||
return await response.json();
|
||||
}
|
||||
|
||||
getServerConfig()
|
||||
.then(data => {
|
||||
const websocketIp = data.server_ip;
|
||||
const websocketPort = data.server_port;
|
||||
const ws = new WebSocket(`ws://${websocketIp}:${websocketPort}`);
|
||||
|
||||
ws.onopen = function() {
|
||||
console.log("WebSocket ist geöffnet");
|
||||
ws.send(JSON.stringify({"data_message": "Server Adresse: " + websocketIp + ":" + websocketPort}));
|
||||
};
|
||||
|
||||
ws.onmessage = function(messageEvent) {
|
||||
console.log(messageEvent.data);
|
||||
};
|
||||
|
||||
ws.onclose = function() {
|
||||
console.log("WebSocket wurde geschlossen");
|
||||
};
|
||||
|
||||
ws.onerror = function(errorEvent) {
|
||||
console.error("WebSocket-Fehler: ", errorEvent);
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching settings:', error);
|
||||
});
|
||||
async function toggleStatHidden() {
|
||||
const section_stat = document.getElementById("stat")
|
||||
section_stat.hidden = !section_stat.hidden
|
||||
}
|
||||
|
||||
getMediaStat()
|
||||
.then(data => {
|
||||
Loading…
Reference in a new issue