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

1from django.conf import settings 

2from rest_framework.decorators import action 

3from rest_framework.exceptions import NotFound 

4from rest_framework.response import Response 

5 

6from drf_spectacular.utils import extend_schema, extend_schema_view 

7 

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 

29 

30 

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.""" 

40 

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] 

46 

47 serializer_class = AudioSerializer 

48 

49 def get_queryset(self): 

50 return super().get_queryset().select_related("sensitive_audio", "audioset") 

51 

52 def include_addons(self, serializer): 

53 return serializer.validated_data.get("peaks") 

54 

55 # Extra actions 

56 

57 async def get_image_proxy_media_info(self) -> image_proxy.MediaInfo: 

58 audio = await self.aget_object() 

59 

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.") 

67 

68 return image_proxy.MediaInfo( 

69 media_identifier=audio.identifier, 

70 media_provider=audio.provider, 

71 image_url=image_url, 

72 ) 

73 

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) 

82 

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. 

92 

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 """ 

97 

98 audio = self.get_object() 

99 

100 obj = {"points": audio.get_or_create_waveform()} 

101 serializer = self.get_serializer(obj) 

102 

103 return Response(status=200, data=serializer.data) 

104 

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. 

114 

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 """ 

118 

119 return super().report(request, identifier)