feat: Implement logging filter for successful HTTP access logs and enhance modal styling for better usability

This commit is contained in:
JB
2025-10-09 05:51:45 -07:00
parent b0875c7486
commit 63f9474741
4 changed files with 112 additions and 146 deletions
+23
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
import atexit
import logging
import os
from pathlib import Path
from typing import Any, Optional
@@ -13,6 +14,23 @@ from .conversion_runner import run_conversion_job
from .service import build_service
class _SuppressSuccessfulAccessFilter(logging.Filter):
"""Filter out successful (HTTP 200) werkzeug access logs."""
def filter(self, record: logging.LogRecord) -> bool: # pragma: no cover - small utility
try:
message = record.getMessage()
except Exception: # pragma: no cover - defensive
return True
# Werkzeug access logs include the status code near the end, e.g.
# "GET /path HTTP/1.1" 200 -
# Treat any 2xx response as success to suppress.
return " 200 " not in message and " 201 " not in message and " 204 " not in message
_access_log_filter_attached = False
def _default_dirs() -> tuple[Path, Path]:
uploads_override = os.environ.get("ABOGEN_UPLOAD_ROOT")
outputs_override = os.environ.get("ABOGEN_OUTPUT_ROOT")
@@ -64,6 +82,11 @@ def create_app(config: Optional[dict[str, Any]] = None) -> Flask:
atexit.register(service.shutdown)
global _access_log_filter_attached
if not _access_log_filter_attached:
logging.getLogger("werkzeug").addFilter(_SuppressSuccessfulAccessFilter())
_access_log_filter_attached = True
return app
-41
View File
@@ -10,12 +10,6 @@ const initDashboard = () => {
const formulaInput = scope.querySelector('[data-role="voice-formula"]');
const languageSelect = uploadModal?.querySelector("#language") || document.getElementById("language");
const sourceText = scope.querySelector('[data-role="source-text"]');
const previewEl = scope.querySelector('[data-role="text-preview"]');
const previewBody = scope.querySelector('[data-role="preview-body"]');
const charCountEl = scope.querySelector('[data-role="char-count"]');
const wordCountEl = scope.querySelector('[data-role="word-count"]');
let lastTrigger = null;
const openUploadModal = (trigger) => {
@@ -156,36 +150,6 @@ const initDashboard = () => {
}
};
const updatePreview = () => {
if (!sourceText || !previewBody || !charCountEl || !wordCountEl) {
return;
}
const raw = sourceText.value || "";
const trimmed = raw.trim();
const charCount = raw.length;
const wordCount = trimmed ? trimmed.split(/\s+/).length : 0;
const charLabel = `${charCount.toLocaleString()} ${charCount === 1 ? "character" : "characters"}`;
const wordLabel = `${wordCount.toLocaleString()} ${wordCount === 1 ? "word" : "words"}`;
charCountEl.textContent = charLabel;
wordCountEl.textContent = wordLabel;
if (!trimmed) {
previewBody.textContent = "Paste text to see a live preview and character count.";
if (previewEl) {
previewEl.setAttribute("data-state", "empty");
}
return;
}
const snippet = trimmed.length > 1200 ? `${trimmed.slice(0, 1200)}` : trimmed;
previewBody.textContent = snippet;
if (previewEl) {
previewEl.setAttribute("data-state", "ready");
}
};
if (profileSelect) {
const hasSaved = selectFirstProfileIfAvailable();
profileSelect.addEventListener("change", updateVoiceControls);
@@ -196,11 +160,6 @@ const initDashboard = () => {
} else {
hydrateDefaultVoice();
}
if (sourceText) {
sourceText.addEventListener("input", updatePreview);
updatePreview();
}
};
if (document.readyState === "loading") {
+3
View File
@@ -150,6 +150,7 @@ body {
align-items: center;
justify-content: center;
z-index: 300;
overflow-y: auto;
}
.modal[data-open="true"] {
@@ -211,6 +212,7 @@ body {
display: flex;
flex-direction: column;
gap: 1.5rem;
flex: 1 1 auto;
}
.modal__footer {
@@ -246,6 +248,7 @@ body {
padding: 1.5rem 1.75rem 1.75rem;
overflow-y: auto;
max-height: calc(90vh - 150px);
flex: 1 1 auto;
}
.voice-browser__filters,
-19
View File
@@ -39,7 +39,6 @@
<form action="{{ url_for('web.enqueue_job') }}" method="post" enctype="multipart/form-data" class="upload-form">
<div class="modal__body">
<div class="grid grid--two form-grid">
<div class="grid">
<div class="field field--file">
<label for="source_file">Source File</label>
<input type="file" id="source_file" name="source_file" accept=".txt,.pdf,.epub,.md,.markdown">
@@ -129,24 +128,6 @@
<p class="hint">Creates a synchronized EPUB alongside audio output.</p>
</div>
</div>
<div class="grid">
<div class="field field--full">
<label for="source_text">Or Paste Text Directly</label>
<textarea id="source_text" name="source_text" rows="12" placeholder="Drop some text here if you don't have a file handy..." data-role="source-text"></textarea>
</div>
<div class="text-preview" data-role="text-preview" aria-live="polite">
<div class="text-preview__header">
<h2>Preview</h2>
<div class="text-preview__meta">
<span data-role="char-count">0 characters</span>
<span>·</span>
<span data-role="word-count">0 words</span>
</div>
</div>
<pre class="text-preview__body" data-role="preview-body">Paste text to see a live preview and character count.</pre>
</div>
</div>
</div>
</div>
<footer class="modal__footer">
<button type="button" class="button button--ghost" data-role="upload-modal-close">Cancel</button>