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 from __future__ import annotations
import atexit import atexit
import logging
import os import os
from pathlib import Path from pathlib import Path
from typing import Any, Optional from typing import Any, Optional
@@ -13,6 +14,23 @@ from .conversion_runner import run_conversion_job
from .service import build_service 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]: def _default_dirs() -> tuple[Path, Path]:
uploads_override = os.environ.get("ABOGEN_UPLOAD_ROOT") uploads_override = os.environ.get("ABOGEN_UPLOAD_ROOT")
outputs_override = os.environ.get("ABOGEN_OUTPUT_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) 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 return app
-41
View File
@@ -10,12 +10,6 @@ const initDashboard = () => {
const formulaInput = scope.querySelector('[data-role="voice-formula"]'); const formulaInput = scope.querySelector('[data-role="voice-formula"]');
const languageSelect = uploadModal?.querySelector("#language") || document.getElementById("language"); 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; let lastTrigger = null;
const openUploadModal = (trigger) => { 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) { if (profileSelect) {
const hasSaved = selectFirstProfileIfAvailable(); const hasSaved = selectFirstProfileIfAvailable();
profileSelect.addEventListener("change", updateVoiceControls); profileSelect.addEventListener("change", updateVoiceControls);
@@ -196,11 +160,6 @@ const initDashboard = () => {
} else { } else {
hydrateDefaultVoice(); hydrateDefaultVoice();
} }
if (sourceText) {
sourceText.addEventListener("input", updatePreview);
updatePreview();
}
}; };
if (document.readyState === "loading") { if (document.readyState === "loading") {
+3
View File
@@ -150,6 +150,7 @@ body {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
z-index: 300; z-index: 300;
overflow-y: auto;
} }
.modal[data-open="true"] { .modal[data-open="true"] {
@@ -211,6 +212,7 @@ body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1.5rem; gap: 1.5rem;
flex: 1 1 auto;
} }
.modal__footer { .modal__footer {
@@ -246,6 +248,7 @@ body {
padding: 1.5rem 1.75rem 1.75rem; padding: 1.5rem 1.75rem 1.75rem;
overflow-y: auto; overflow-y: auto;
max-height: calc(90vh - 150px); max-height: calc(90vh - 150px);
flex: 1 1 auto;
} }
.voice-browser__filters, .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"> <form action="{{ url_for('web.enqueue_job') }}" method="post" enctype="multipart/form-data" class="upload-form">
<div class="modal__body"> <div class="modal__body">
<div class="grid grid--two form-grid"> <div class="grid grid--two form-grid">
<div class="grid">
<div class="field field--file"> <div class="field field--file">
<label for="source_file">Source File</label> <label for="source_file">Source File</label>
<input type="file" id="source_file" name="source_file" accept=".txt,.pdf,.epub,.md,.markdown"> <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> <p class="hint">Creates a synchronized EPUB alongside audio output.</p>
</div> </div>
</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> </div>
<footer class="modal__footer"> <footer class="modal__footer">
<button type="button" class="button button--ghost" data-role="upload-modal-close">Cancel</button> <button type="button" class="button button--ghost" data-role="upload-modal-close">Cancel</button>