diff --git a/contentcuration/contentcuration/frontend/shared/vuex/file/utils.js b/contentcuration/contentcuration/frontend/shared/vuex/file/utils.js index 8b1764cc0e..64c9b900c8 100644 --- a/contentcuration/contentcuration/frontend/shared/vuex/file/utils.js +++ b/contentcuration/contentcuration/frontend/shared/vuex/file/utils.js @@ -92,7 +92,7 @@ export function extractMetadata(file, preset = null) { const mediaElement = document.createElement(isVideo ? 'video' : 'audio'); // Add a listener to read the metadata once it has loaded. mediaElement.addEventListener('loadedmetadata', () => { - metadata.duration = mediaElement.duration; + metadata.duration = Math.ceil(mediaElement.duration); // Override preset based off video resolution if (isVideo) { metadata.preset = diff --git a/contentcuration/contentcuration/tests/viewsets/test_file.py b/contentcuration/contentcuration/tests/viewsets/test_file.py index 6b3394ad01..be19e660af 100644 --- a/contentcuration/contentcuration/tests/viewsets/test_file.py +++ b/contentcuration/contentcuration/tests/viewsets/test_file.py @@ -390,6 +390,7 @@ def setUp(self): "name": "le_studio", "file_format": file_formats.MP3, "preset": format_presets.AUDIO, + "duration": 10.123 } def test_required_keys(self): @@ -402,6 +403,16 @@ def test_required_keys(self): self.assertEqual(response.status_code, 400) + def test_duration_invalid(self): + self.file["duration"] = '1.23' + + self.client.force_authenticate(user=self.user) + response = self.client.post( + reverse("file-upload-url"), self.file, format="json", + ) + + self.assertEqual(response.status_code, 400) + def test_insufficient_storage(self): self.file["size"] = 100000000000000 @@ -414,3 +425,5 @@ def test_upload_url(self): self.client.force_authenticate(user=self.user) response = self.client.post(reverse("file-upload-url"), self.file, format="json",) self.assertEqual(response.status_code, 200) + file = models.File.objects.get(checksum=self.file["checksum"]) + self.assertEqual(11, file.duration) diff --git a/contentcuration/contentcuration/viewsets/file.py b/contentcuration/contentcuration/viewsets/file.py index b594a55bc9..de64dfa935 100644 --- a/contentcuration/contentcuration/viewsets/file.py +++ b/contentcuration/contentcuration/viewsets/file.py @@ -1,4 +1,5 @@ import codecs +import math from django.core.exceptions import PermissionDenied from django.http import HttpResponseBadRequest @@ -142,6 +143,12 @@ def upload_url(self, request): reason="Must specify: size, checksum, name, file_format, and preset" ) + duration = request.data.get("duration") + if duration is not None: + if not isinstance(duration, (int, float)): + return HttpResponseBadRequest(reason="File duration must be a number") + duration = math.ceil(duration) + try: request.user.check_space(float(size), checksum) except PermissionDenied: @@ -165,7 +172,7 @@ def upload_url(self, request): file_format_id=file_format, preset_id=preset, uploaded_by=request.user, - duration=request.data.get("duration"), + duration=duration, ) # Avoid using our file_on_disk attribute for checks