mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 13:40:27 +02:00
136 lines
4.0 KiB
Python
136 lines
4.0 KiB
Python
from __future__ import annotations
|
|
|
|
import atexit
|
|
import logging
|
|
import os
|
|
from pathlib import Path
|
|
from typing import Any, Optional
|
|
|
|
from flask import Flask
|
|
|
|
from abogen.utils import get_user_cache_path, get_user_output_path, get_user_settings_dir
|
|
|
|
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")
|
|
|
|
if uploads_override:
|
|
uploads = Path(os.path.expanduser(uploads_override)).resolve()
|
|
else:
|
|
uploads = Path(get_user_cache_path("web/uploads"))
|
|
|
|
if outputs_override:
|
|
outputs = Path(os.path.expanduser(outputs_override)).resolve()
|
|
else:
|
|
outputs = Path(get_user_output_path("web"))
|
|
|
|
uploads.mkdir(parents=True, exist_ok=True)
|
|
outputs.mkdir(parents=True, exist_ok=True)
|
|
return uploads, outputs
|
|
|
|
|
|
def _get_secret_key() -> str:
|
|
env_key = os.environ.get("ABOGEN_SECRET_KEY")
|
|
if env_key:
|
|
return env_key
|
|
|
|
try:
|
|
settings_dir = Path(get_user_settings_dir())
|
|
settings_dir.mkdir(parents=True, exist_ok=True)
|
|
secret_file = settings_dir / ".secret_key"
|
|
if secret_file.exists():
|
|
return secret_file.read_text(encoding="utf-8").strip()
|
|
|
|
key = os.urandom(24).hex()
|
|
secret_file.write_text(key, encoding="utf-8")
|
|
return key
|
|
except Exception:
|
|
# Fallback if we can't write to settings dir
|
|
return os.urandom(24).hex()
|
|
|
|
|
|
def create_app(config: Optional[dict[str, Any]] = None) -> Flask:
|
|
uploads_dir, outputs_dir = _default_dirs()
|
|
|
|
app = Flask(
|
|
__name__,
|
|
static_folder="static",
|
|
template_folder="templates",
|
|
)
|
|
base_config = {
|
|
"SECRET_KEY": _get_secret_key(),
|
|
"UPLOAD_FOLDER": str(uploads_dir),
|
|
"OUTPUT_FOLDER": str(outputs_dir),
|
|
"MAX_CONTENT_LENGTH": 1024 * 1024 * 400, # 400 MB uploads
|
|
}
|
|
if config:
|
|
base_config.update(config)
|
|
app.config.update(base_config)
|
|
|
|
service = build_service(
|
|
runner=run_conversion_job,
|
|
output_root=Path(app.config["OUTPUT_FOLDER"]),
|
|
uploads_root=Path(app.config["UPLOAD_FOLDER"]),
|
|
)
|
|
app.extensions["conversion_service"] = service
|
|
|
|
from abogen.webui.routes import (
|
|
main_bp,
|
|
jobs_bp,
|
|
settings_bp,
|
|
voices_bp,
|
|
entities_bp,
|
|
books_bp,
|
|
api_bp,
|
|
)
|
|
|
|
app.register_blueprint(main_bp)
|
|
app.register_blueprint(jobs_bp, url_prefix="/jobs")
|
|
app.register_blueprint(settings_bp, url_prefix="/settings")
|
|
app.register_blueprint(voices_bp, url_prefix="/voices")
|
|
app.register_blueprint(entities_bp, url_prefix="/overrides")
|
|
app.register_blueprint(books_bp, url_prefix="/find-books")
|
|
app.register_blueprint(api_bp, url_prefix="/api")
|
|
|
|
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
|
|
|
|
|
|
def main() -> None:
|
|
app = create_app()
|
|
host = os.environ.get("ABOGEN_HOST", "0.0.0.0")
|
|
port = int(os.environ.get("ABOGEN_PORT", "8808"))
|
|
debug = os.environ.get("ABOGEN_DEBUG", "false").lower() == "true"
|
|
app.run(host=host, port=port, debug=debug)
|
|
|
|
|
|
if __name__ == "__main__": # pragma: no cover
|
|
main()
|