5c5df5d3cc
Add S3FileStorage adapter (any S3-compatible backend: AWS, MinIO, Garage) alongside the local adapter, selected via STORAGE_BACKEND. Proxied range streaming via get_object+Range; as_local_path downloads to a tempfile for ffmpeg/fpcalc. Rename track.file_path -> storage_uri across domain entity, ORM, repositories, port, and services, with an Alembic migration. Adds mocked S3 unit tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
32 lines
1.1 KiB
Python
32 lines
1.1 KiB
Python
"""File storage provider — singleton factory wired from config."""
|
|
|
|
from app.core.config import get_settings
|
|
from app.domain.ports import FileStorage
|
|
from app.infrastructure.storage.local import LocalFileStorage
|
|
from app.infrastructure.storage.s3 import S3FileStorage
|
|
|
|
_storage: FileStorage | None = None
|
|
|
|
|
|
def get_file_storage() -> FileStorage:
|
|
global _storage
|
|
if _storage is None:
|
|
settings = get_settings()
|
|
if settings.storage_backend == "s3":
|
|
if not settings.s3_bucket:
|
|
raise RuntimeError("S3_BUCKET must be set when STORAGE_BACKEND=s3")
|
|
_storage = S3FileStorage(
|
|
settings.s3_bucket,
|
|
endpoint_url=settings.s3_endpoint_url,
|
|
region_name=settings.s3_region,
|
|
access_key=settings.s3_access_key.get_secret_value()
|
|
if settings.s3_access_key
|
|
else None,
|
|
secret_key=settings.s3_secret_key.get_secret_value()
|
|
if settings.s3_secret_key
|
|
else None,
|
|
)
|
|
else:
|
|
_storage = LocalFileStorage(settings.media_path)
|
|
return _storage
|