From: Thomas Fillon Date: Tue, 2 Dec 2014 15:12:28 +0000 (+0100) Subject: Server: add support for streaming transcoded media files X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=50b3d334239f03679fd537c261d54d925fbb2452;p=timeside.git Server: add support for streaming transcoded media files --- diff --git a/timeside/server/models.py b/timeside/server/models.py index 2d36944..21558f7 100644 --- a/timeside/server/models.py +++ b/timeside/server/models.py @@ -267,7 +267,7 @@ class Task(BaseResource): self.status = status self.save() - def run(self): + def run(self, streaming=False): self.status_setter(_RUNNING) results_root = 'results' @@ -293,7 +293,8 @@ class Task(BaseResource): proc.file_extension()]) result.file = os.path.join(item_path, media_file) result.save() - proc = proc(result.file.path, overwrite=True) + proc = proc(result.file.path, overwrite=True, + streaming=streaming) else: proc = proc() if proc.type == 'analyzer': @@ -308,7 +309,11 @@ class Task(BaseResource): hdf5_file = str(self.experience.uuid) + '.hdf5' item.hdf5 = os.path.join(item_path, hdf5_file) item.save() - pipe.run() + if streaming: + for chunk in pipe.stream(): + yield chunk + else: + pipe.run() item.lock_setter(True) #pipe.results.to_hdf5(item.hdf5.path) diff --git a/timeside/server/views.py b/timeside/server/views.py index d77ad69..4cc4578 100644 --- a/timeside/server/views.py +++ b/timeside/server/views.py @@ -24,14 +24,13 @@ from django.http import Http404 from django.views.generic.base import View from django.views.generic import DetailView, ListView -from django.views.generic.detail import SingleObjectMixin -from django.http import HttpResponse +from django.http import HttpResponse, StreamingHttpResponse from rest_framework import viewsets from timeside.server.models import Experience, Item, Result, Processor from timeside.server.models import Preset, Selection, Task, User -from timeside.server.models import _PENDING +from timeside.server.models import _DRAFT from timeside.server.serializers import ExperienceSerializer, ItemSerializer from timeside.server.serializers import PresetSerializer from timeside.server.serializers import ProcessorSerializer @@ -40,6 +39,7 @@ from timeside.server.serializers import SelectionSerializer from timeside.server.serializers import TaskSerializer from timeside.server.serializers import UserSerializer +import timeside.core from timeside.analyzer.core import AnalyzerResultContainer import os @@ -55,6 +55,12 @@ def stream_from_file(file): yield chunk +def stream_from_task(task): + for chunk in task.run(streaming=True): + yield chunk + task.save() + + class SelectionViewSet(viewsets.ModelViewSet): model = Selection @@ -153,7 +159,7 @@ class ItemDetail(DetailView): template_name = 'timeside/item_detail.html' -class ItemExport(DetailView, SingleObjectMixin): +class ItemExport(DetailView): model = Item def get(self, request, pk, extension): @@ -163,6 +169,7 @@ class ItemExport(DetailView, SingleObjectMixin): raise Http404('Unknown export file extension: %s' % extension) encoder = TS_ENCODERS_EXT[extension] + mime_type = timeside.core.get_processor(encoder).mime_type() # Get or Create Processor = encoder processor, created = Processor.objects.get_or_create(pid=encoder) # Get or Create Preset with processor @@ -172,21 +179,41 @@ class ItemExport(DetailView, SingleObjectMixin): try: result = Result.objects.get(item=item, preset=preset) if not os.path.exists(result.file.path): + # Result exists but not file (may have been deleted) result.delete() return self.get(request, pk, extension) + # Result and file exist --> OK return HttpResponse(stream_from_file(result.file.path), - mimetype=result.mime_type) + content_type=result.mime_type) except Result.DoesNotExist: # Result does not exist # the corresponding task has to be created and run - experience = Experience() - experience.save() - experience.presets.add(preset) - selection = Selection() - selection.save() - selection.items.add(item) + exp_title = "Transcode to %s" % extension + exp_description = ("Experience for transcoding an item to %s\n" + "Automatically generated by the TimeSide " + "application.") % mime_type + experience, created = Experience.objects.get_or_create( + title=exp_title, + description=exp_description) + if created: + experience.save() + experience.presets.add(preset) + + sel_title = "Singleton selection for item %d" % item.id + sel_description = ("Singleton selection for item %d\n" + "Automatically generated by the TimeSide " + "application.") % item.id + selection, created = Selection.objects.get_or_create( + title=sel_title, + description=sel_description) + if created: + selection.save() + selection.items.add(item) task = Task(experience=experience, selection=selection, - status=_PENDING) - task.save() # save task and run - # TODO : find a way to stream during task ... - return self.get(request, pk, extension) + status=_DRAFT) + task.save() + # Run task in streaming mode + response = StreamingHttpResponse() + response['streaming_content'] = task.run(streaming=True) + response['content-type'] = mime_type + return response