Files
ObsiGate/backend/routers/helpers.py
T

52 lines
2.1 KiB
Python

"""Shared helpers for the file routers (ROADMAP #85, tranche 6a).
Petites fonctions pures extraites de :mod:`backend.main` sans changement
de comportement. Regroupées ici car utilisées par plusieurs routers
(``files_read`` aujourd'hui, ``files_media`` / mutations ensuite) :
- :func:`content_disposition` — aussi utilisée par ``_stream_file_with_range``
(resté dans ``main`` jusqu'à la tranche media).
- :func:`media_max_inline_bytes` — aussi utilisée par ``/api/media``.
"""
from __future__ import annotations
import os
from pathlib import Path
def content_disposition(disposition: str, filename: str) -> str:
"""Build a header-safe Content-Disposition value.
HTTP header values must be ASCII. Unicode filenames are sent per
RFC 5987 via ``filename*`` (percent-encoded UTF-8) with a pure-ASCII
``filename`` fallback. This avoids a UnicodeDecodeError / HTTP 500 when
the filename contains accented characters (e.g. 'Bière blonde…pdf').
"""
from urllib.parse import quote
ascii_name = "".join(c for c in filename if c.isascii() and (c.isalnum() or c in " _-.")).strip() or "file"
ext = Path(filename).suffix
if ext and not Path(ascii_name).suffix:
ascii_name = ascii_name + ext
return f"{disposition}; filename=\"{ascii_name}\"; filename*=UTF-8''{quote(filename)}"
def media_max_inline_bytes() -> int:
"""Maximum size (bytes) for inline audio/video playback (roadmap #109-A3).
Configurable via ``OBSIGATE_MEDIA_MAX_INLINE_MB`` (default 500 MB). Files
above the limit are not streamed in the viewer (the UI falls back to the
download button), which keeps a single uvicorn worker from being pinned by
multi-gigabyte media. Invalid or non-positive values fall back to default.
"""
default_mb = 500
raw = os.environ.get("OBSIGATE_MEDIA_MAX_INLINE_MB", "").strip()
if not raw:
return default_mb * 1024 * 1024
try:
mb = float(raw)
except ValueError:
return default_mb * 1024 * 1024
if mb <= 0:
return default_mb * 1024 * 1024
return int(mb * 1024 * 1024)