New logbuffer.py captures app messages (log()) and library logs (faster-whisper, huggingface_hub) into a ring buffer, mirrored to stderr. Settings gains a Log tab: monospace view, 1s refresh, auto-scroll, Copy, Clear — so model download/load progress is visible instead of an opaque "Loading…". transcribe and daemon now log via the buffer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
61 lines
1.5 KiB
Python
61 lines
1.5 KiB
Python
"""In-memory log buffer shown in Settings → Log, mirrored to stderr.
|
|
|
|
Our own messages go through log(); library logs (faster-whisper, huggingface_hub)
|
|
are captured via a logging handler so model download/load progress is visible
|
|
instead of an opaque "Loading…".
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import sys
|
|
import threading
|
|
import time
|
|
from collections import deque
|
|
|
|
_LINES: deque[str] = deque(maxlen=2000)
|
|
_LOCK = threading.Lock()
|
|
|
|
|
|
def log(msg: str, *, echo: bool = True) -> None:
|
|
line = f"{time.strftime('%H:%M:%S')} {msg}"
|
|
with _LOCK:
|
|
_LINES.append(line)
|
|
if echo:
|
|
print(line, file=sys.stderr, flush=True)
|
|
|
|
|
|
def lines() -> list[str]:
|
|
with _LOCK:
|
|
return list(_LINES)
|
|
|
|
|
|
def clear() -> None:
|
|
with _LOCK:
|
|
_LINES.clear()
|
|
|
|
|
|
class _BufferHandler(logging.Handler):
|
|
def emit(self, record: logging.LogRecord) -> None:
|
|
try:
|
|
log(self.format(record), echo=False)
|
|
except Exception: # noqa: BLE001 - never let logging break the app
|
|
pass
|
|
|
|
|
|
_installed = False
|
|
|
|
|
|
def install_logging() -> None:
|
|
"""Capture library log records into the buffer (once)."""
|
|
global _installed
|
|
if _installed:
|
|
return
|
|
_installed = True
|
|
handler = _BufferHandler()
|
|
handler.setFormatter(logging.Formatter("%(name)s: %(message)s"))
|
|
for name in ("faster_whisper", "huggingface_hub", "blitztext"):
|
|
lg = logging.getLogger(name)
|
|
lg.addHandler(handler)
|
|
lg.setLevel(logging.INFO)
|