### Description Added filters for genre and md5 to the files API, e.g. `/api/v2/files?genre=soul` **This is a new feature**: Yes **I have updated the documentation to reflect these changes**: No There should be a schema and docs that are generated automatically. I don't know where that is. ### Testing Notes **What I did:** - Used docker to deploy locally - Confirmed filters work at http://localhost:8080/api/v2/files?genre=Soul **How you can replicate my testing:** - `make clean dev` - Upload some files! - Visit http://localhost:8080/api/v2/files - You can use the filters <img width="658" alt="Screenshot 2024-12-23 at 01 36 01" src="https://github.com/user-attachments/assets/ba19f7f3-fb3e-495d-8937-d451c70d326c" /> <img width="652" alt="Screenshot 2024-12-23 at 01 35 56" src="https://github.com/user-attachments/assets/c7191131-a963-463a-b52f-9d0952192555" /> _How can the reviewer validate this PR?_ - See above - wrote tests to confirm filters work
63 lines
2.1 KiB
Python
63 lines
2.1 KiB
Python
import logging
|
|
import os
|
|
from os import remove
|
|
|
|
from django.conf import settings
|
|
from django.http import HttpResponse
|
|
from django.utils.encoding import filepath_to_uri
|
|
from django_filters import rest_framework as filters
|
|
from rest_framework import status, viewsets
|
|
from rest_framework.decorators import action
|
|
from rest_framework.exceptions import APIException
|
|
|
|
from ...schedule.models import Schedule
|
|
from ..models import File
|
|
from ..serializers import FileSerializer
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class FileInUse(APIException):
|
|
status_code = status.HTTP_409_CONFLICT
|
|
default_detail = "The file is currently used"
|
|
default_code = "file_in_use"
|
|
|
|
|
|
class FileViewSet(viewsets.ModelViewSet):
|
|
queryset = File.objects.all()
|
|
serializer_class = FileSerializer
|
|
model_permission_name = "file"
|
|
filter_backends = (filters.DjangoFilterBackend,)
|
|
filterset_fields = ("md5", "genre")
|
|
|
|
# pylint: disable=invalid-name,unused-argument
|
|
@action(detail=True, methods=["GET"])
|
|
def download(self, request, pk=None):
|
|
instance: File = self.get_object()
|
|
|
|
response = HttpResponse()
|
|
# HTTP headers must be USASCII encoded, or Nginx might not find the file and
|
|
# will return a 404.
|
|
redirect_uri = filepath_to_uri(os.path.join("/api/_media", instance.filepath))
|
|
response["X-Accel-Redirect"] = redirect_uri
|
|
return response
|
|
|
|
def perform_destroy(self, instance: File):
|
|
if Schedule.is_file_scheduled_in_the_future(file_id=instance.id):
|
|
raise FileInUse("file is scheduled in the future")
|
|
|
|
try:
|
|
if instance.filepath is None:
|
|
logger.warning("file does not have a filepath: %d", instance.id)
|
|
return
|
|
|
|
path = os.path.join(settings.CONFIG.storage.path, instance.filepath)
|
|
|
|
if not os.path.isfile(path):
|
|
logger.warning("file does not exist in storage: %d", instance.id)
|
|
return
|
|
|
|
remove(path)
|
|
except OSError as exception:
|
|
raise APIException("could not delete file from storage") from exception
|