mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 13:40:27 +02:00
feat: Implement logging filter for successful HTTP access logs and enhance modal styling for better usability
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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") {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user