Source code for ferrosoft.apps.ferrobase.views.media

#  Copyright (c) 2025 Ferrosoft GmbH. All rights reserved.
from pathlib import Path

from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, FileResponse
from django.utils.http import content_disposition_header
from servestatic.utils import AsyncFile, AsyncFileIterator

from ferrosoft.apps.ferrobase.models import StoredFile, LocalUser
from ferrosoft.config import require_settings


FORCE_DOWNLOAD_PARAM = "dl"
"""Query parameter (``?dl=1``) that forces a media file to download as an attachment."""


[docs] class CustomFileResponse(FileResponse): """FileResponse subclass that sets ``Content-Disposition`` from an async iterator.""" def _set_streaming_content(self, value): """Apply ``content_disposition_header`` before deferring to the base class.""" if content_disposition := content_disposition_header( self.as_attachment, self.filename ): self.headers["Content-Disposition"] = content_disposition self.file_to_stream = None return super()._set_streaming_content(value)
[docs] @login_required def restrict_media(request, path): """Authenticated media-serving endpoint with per-file access control. Looks up the :class:`StoredFile` for ``path`` via :meth:`StoredFile.get_authenticated_file` (returns ``None`` when the current user has no access). The file is streamed asynchronously via :class:`servestatic.utils.AsyncFile`; ``?dl=1`` forces a download. """ file = StoredFile.get_authenticated_file(path, LocalUser.load(request.user)) if file is None: return HttpResponse(status=401) settings = require_settings("MEDIA_ROOT") full_path = Path(settings["MEDIA_ROOT"]) / path kwargs = {"content_type": file.media_type} as_attachment = request.GET.get(FORCE_DOWNLOAD_PARAM) == "1" if as_attachment: kwargs.update({"as_attachment": True, "filename": file.file_name}) file_iter = AsyncFileIterator(AsyncFile(full_path, mode="rb")) return CustomFileResponse(file_iter, **kwargs)