Coverage for api/views/audio_views.py: 97%
52 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 06:14 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 06:14 +0000
1from django.conf import settings
2from rest_framework.decorators import action
3from rest_framework.exceptions import NotFound
4from rest_framework.response import Response
6from drf_spectacular.utils import extend_schema, extend_schema_view
8from api.constants.media_types import AUDIO_TYPE
9from api.docs.audio_docs import (
10 detail,
11 related,
12 report,
13 search,
14 stats,
15 waveform,
16)
17from api.docs.audio_docs import thumbnail as thumbnail_docs
18from api.models import Audio
19from api.models.audio import AudioAddOn
20from api.serializers.audio_serializers import (
21 AudioReportRequestSerializer,
22 AudioSearchRequestSerializer,
23 AudioSerializer,
24 AudioWaveformSerializer,
25)
26from api.utils import image_proxy
27from api.utils.throttle import AnonThumbnailRateThrottle, OAuth2IdThumbnailRateThrottle
28from api.views.media_views import MediaViewSet
31@extend_schema(tags=["audio"])
32@extend_schema_view(
33 list=search,
34 stats=stats,
35 retrieve=detail,
36 related=related,
37)
38class AudioViewSet(MediaViewSet):
39 """Viewset for all endpoints pertaining to audio."""
41 model_class = Audio
42 addon_model_class = AudioAddOn
43 media_type = AUDIO_TYPE
44 query_serializer_class = AudioSearchRequestSerializer
45 default_index = settings.MEDIA_INDEX_MAPPING[AUDIO_TYPE]
47 serializer_class = AudioSerializer
49 def get_queryset(self):
50 return super().get_queryset().select_related("sensitive_audio", "audioset")
52 def include_addons(self, serializer):
53 return serializer.validated_data.get("peaks")
55 # Extra actions
57 async def get_image_proxy_media_info(self) -> image_proxy.MediaInfo:
58 audio = await self.aget_object()
60 image_url = None
61 if audio_thumbnail := audio.thumbnail:
62 image_url = audio_thumbnail
63 elif audio.audio_set and (audio_thumbnail := audio.audio_set.thumbnail): 63 ↛ 64line 63 didn't jump to line 64 because the condition on line 63 was never true
64 image_url = audio_thumbnail
65 if not image_url:
66 raise NotFound("Could not find artwork.")
68 return image_proxy.MediaInfo(
69 media_identifier=audio.identifier,
70 media_provider=audio.provider,
71 image_url=image_url,
72 )
74 @thumbnail_docs
75 @MediaViewSet.thumbnail_action
76 async def thumbnail(self, request, identifier):
77 """
78 Retrieve the scaled down and compressed thumbnail of the artwork of an
79 audio track or its audio set.
80 """
81 return await super().thumbnail(request)
83 @waveform
84 @action(
85 detail=True,
86 serializer_class=AudioWaveformSerializer,
87 throttle_classes=[AnonThumbnailRateThrottle, OAuth2IdThumbnailRateThrottle],
88 )
89 def waveform(self, *_, **__):
90 """
91 Get the waveform peaks for an audio track.
93 The peaks are provided as a list of numbers, each of these numbers being
94 a fraction between 0 and 1. The list contains approximately 1000 numbers,
95 although it can be slightly higher or lower, depending on the track's length.
96 """
98 audio = self.get_object()
100 obj = {"points": audio.get_or_create_waveform()}
101 serializer = self.get_serializer(obj)
103 return Response(status=200, data=serializer.data)
105 @report
106 @action(
107 detail=True,
108 methods=["post"],
109 serializer_class=AudioReportRequestSerializer,
110 )
111 def report(self, request, identifier):
112 """
113 Report an issue about a specified audio track to Openverse.
115 By using this endpoint, you can report an audio track if it infringes
116 copyright, contains mature or sensitive content or some other reason.
117 """
119 return super().report(request, identifier)