From: Thomas Fillon Date: Tue, 4 Apr 2017 22:44:35 +0000 (+0200) Subject: Serve media file through nginx X-Git-Tag: search~2^2~19^2~15 X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=5fb8d59335531f98f60b4b34387e187a8d009cc9;p=telemeta.git Serve media file through nginx --- diff --git a/telemeta/templates/telemeta/mediaitem_detail.html b/telemeta/templates/telemeta/mediaitem_detail.html index 72608174..0385bc0e 100644 --- a/telemeta/templates/telemeta/mediaitem_detail.html +++ b/telemeta/templates/telemeta/mediaitem_detail.html @@ -33,8 +33,7 @@ soundManager.flashVersion = 9; soundManager.url = "{{ STATIC_URL }}SoundManager2/swf/"; soundManager.debugMode = false; - soundManager.allowPolling = true; - soundManager.useHTML5Audio = false; + soundManager.useHTML5Audio = true; soundManager.preferFlash = false; //initializing the visualizers to be passed to the player diff --git a/telemeta/views/admin.py b/telemeta/views/admin.py index 051379e6..ec579eb6 100644 --- a/telemeta/views/admin.py +++ b/telemeta/views/admin.py @@ -22,6 +22,8 @@ from telemeta.views.core import * +import telemeta.models + class AdminView(object): """Provide Admin web UI methods""" diff --git a/telemeta/views/collection.py b/telemeta/views/collection.py index daff446d..d440221a 100644 --- a/telemeta/views/collection.py +++ b/telemeta/views/collection.py @@ -22,6 +22,7 @@ from telemeta.views.core import * +from telemeta.views.core import serve_media from telemeta.views.epub import * class CollectionView(object): @@ -37,7 +38,7 @@ class CollectionView(object): title = ugettext('Collection') + ' : ' + public_id + ' : ' + mess description = ugettext('Please login or contact the website administator to get a private access.') messages.error(request, title) - return render(request, 'telemeta/messages.html', {'description' : description}) + return render(request, 'telemeta/messages.html', {'description': description}) playlists = get_playlists_names(request) @@ -52,8 +53,8 @@ class CollectionView(object): last_revision = None return render(request, template, {'collection': collection, 'playlists': playlists, - 'items': items, 'related_media': related_media, - 'parents': parents, 'last_revision': last_revision }) + 'items': items, 'related_media': related_media, + 'parents': parents, 'last_revision': last_revision}) @method_decorator(permission_required('telemeta.change_mediacollection')) def collection_edit(self, request, public_id, template='telemeta/collection_edit.html'): @@ -70,7 +71,7 @@ class CollectionView(object): else: form = MediaCollectionForm(instance=collection) - return render(request, template, {'collection': collection, "form": form,}) + return render(request, template, {'collection': collection, "form": form, }) @method_decorator(permission_required('telemeta.add_mediacollection')) def collection_add(self, request, template='telemeta/collection_add.html'): @@ -87,7 +88,7 @@ class CollectionView(object): else: form = MediaCollectionForm(instance=collection) - return render(request, template, {'collection': collection, "form": form,}) + return render(request, template, {'collection': collection, "form": form, }) @method_decorator(permission_required('telemeta.add_mediacollection')) def collection_copy(self, request, public_id, template='telemeta/collection_edit.html'): @@ -105,7 +106,7 @@ class CollectionView(object): collection = MediaCollection.objects.get(public_id=public_id) form = MediaCollectionForm(instance=collection) - return render(request, template, {'collection': collection, "form": form,}) + return render(request, template, {'collection': collection, "form": form, }) def collection_playlist(self, request, public_id, template, mimetype): try: @@ -130,16 +131,13 @@ class CollectionView(object): def related_media_collection_stream(self, request, public_id, media_id): collection = MediaCollection.objects.get(public_id=public_id) media = MediaCollectionRelated.objects.get(collection=collection, id=media_id) - response = StreamingHttpResponse(stream_from_file(media.file.path), content_type=media.mime_type) -# response['Content-Disposition'] = 'attachment' + response = serve_media(media.file.path, content_type=media.mime_type) return response def related_media_collection_download(self, request, public_id, media_id): collection = MediaCollection.objects.get(public_id=public_id) media = MediaCollectionRelated.objects.get(collection=collection, id=media_id) - filename = media.file.path.split(os.sep)[-1] - response = StreamingHttpResponse(stream_from_file(media.file.path), content_type=media.mime_type) - response['Content-Disposition'] = 'attachment; ' + 'filename=' + filename + response = serve_media(media.file.path, content_type=media.mime_type) return response @method_decorator(permission_required('telemeta.change_mediacollection')) @@ -155,7 +153,7 @@ class CollectionView(object): else: formset = MediaCollectionRelatedFormSet(instance=collection) - return render(request, template, {'collection': collection, 'formset': formset,}) + return render(request, template, {'collection': collection, 'formset': formset, }) class CollectionZipView(View): @@ -211,7 +209,7 @@ class CollectionZipView(View): response = StreamingHttpResponse(zip_file, content_type='application/zip') response['Content-Disposition'] = "attachment; filename=%s.%s" % \ - (collection.code, 'zip') + (collection.code, 'zip') return response @method_decorator(login_required) @@ -309,7 +307,7 @@ class CollectionEditView(CollectionViewMixin, UpdateWithInlinesView): return super(CollectionEditView, self).forms_valid(form, inlines) def get_success_url(self): - return reverse_lazy('telemeta-collection-detail', kwargs={'public_id':self.code}) + return reverse_lazy('telemeta-collection-detail', kwargs={'public_id': self.code}) def get_context_data(self, **kwargs): context = super(CollectionEditView, self).get_context_data(**kwargs) @@ -334,7 +332,7 @@ class CollectionAddView(CollectionViewMixin, CreateWithInlinesView): return super(CollectionAddView, self).forms_valid(form, inlines) def get_success_url(self): - return reverse_lazy('telemeta-collection-detail', kwargs={'public_id':self.object.code}) + return reverse_lazy('telemeta-collection-detail', kwargs={'public_id': self.object.code}) @method_decorator(permission_required('telemeta.add_mediacollection')) def dispatch(self, *args, **kwargs): @@ -359,7 +357,6 @@ class CollectionCopyView(CollectionAddView): return super(CollectionCopyView, self).dispatch(*args, **kwargs) - class CollectionEpubView(BaseEpubMixin, View): "Download collection data embedded in an EPUB3 file" diff --git a/telemeta/views/core.py b/telemeta/views/core.py index 9b92fc3e..1677d1d8 100644 --- a/telemeta/views/core.py +++ b/telemeta/views/core.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- # Copyright (C) 2007-2010 Samalyse SARL -# Copyright (C) 2010-2012 Parisson SARL +# Copyright (C) 2010-2017 Parisson SARL +# Copyright (C) 2010-2017 Guillaume Pellerin +# Copyright (C) 2010-2017 Thomas Fillon # This file is part of Telemeta. @@ -19,6 +21,7 @@ # Authors: Olivier Guilyardi # Guillaume Pellerin +# Thomas Fillon import re import os @@ -39,6 +42,7 @@ from django.contrib.auth import authenticate, login from django.template import RequestContext, loader from django import template from django.http import HttpResponse, HttpResponseRedirect, StreamingHttpResponse +from django.http import FileResponse from django.http import Http404 from django.shortcuts import render_to_response, redirect, get_object_or_404 from django.views.generic import * @@ -64,8 +68,9 @@ from django.views.generic.edit import DeletionMixin, BaseDeleteView from django.contrib.sites.models import Site from django.template.defaultfilters import slugify + from telemeta.models import * -import telemeta.models + import telemeta.interop.oai as oai from telemeta.interop.oaidatasource import TelemetaOAIDataSource from telemeta.util.unaccent import unaccent @@ -92,10 +97,12 @@ class TelemetaBaseMixin(object): class FixedFileWrapper(FileWrapper): + def __iter__(self): self.filelike.seek(0) return self + def send_file(request, filename, content_type='image/jpeg'): """ Send a file through Django without loading the whole file into @@ -107,20 +114,37 @@ def send_file(request, filename, content_type='image/jpeg'): response['Content-Length'] = os.path.getsize(filename) return response -def nginx_media_accel(request, filename): - """Send a protected medie file through nginx with X-Accel-Redirect""" + +def serve_media(filename, content_type="", buffering=True): + if True: + return nginx_media_accel(filename, content_type=content_type, + buffering=buffering) + else: + pass + # response = StreamingHttpResponse(stream_from_file(media.file.path), content_type=media.mime_type) + # response['Content-Disposition'] = 'attachment; ' + 'filename=' + filename + + +def nginx_media_accel(media_path, content_type="", buffering=True): + """Send a protected media file through nginx with X-Accel-Redirect""" response = HttpResponse() - url = settings.MEDIA_URL + filename - # let nginx determine the correct content type - response['Content-Type'] = "" + url = settings.MEDIA_URL + media_path + filename = os.path.basename(media_path) + response['Content-Disposition'] = "attachment; filename=%s" % (filename) + response['Content-Type'] = content_type response['X-Accel-Redirect'] = url + if not buffering: + response['X-Accel-Buffering'] = 'no' + return response -def render(request, template, data = None, mimetype = None): + +def render(request, template, data=None, mimetype=None): return render_to_response(template, data, context_instance=RequestContext(request), mimetype=mimetype) + def stream_from_processor(decoder, encoder, flag): pipe = decoder | encoder for chunk in pipe.stream(): @@ -128,6 +152,7 @@ def stream_from_processor(decoder, encoder, flag): flag.value = True flag.save() + def stream_from_file(file): chunk_size = 0x100000 f = open(file, 'r') @@ -138,6 +163,7 @@ def stream_from_file(file): break yield chunk + def get_item_access(item, user): # Item access rules according to this workflow: # https://docs.google.com/spreadsheet/ccc?key=0ArKCjajoOT-fdDhJSDZoaUhqdDJvVkY5U3BXUWpNT0E#gid=0 @@ -149,7 +175,7 @@ def get_item_access(item, user): access = 'full' elif item.collection.public_access != 'mixed': - if user.is_authenticated() : + if user.is_authenticated(): if item.collection.public_access == 'metadata' and item.collection.auto_period_access: access = 'full' else: @@ -158,7 +184,7 @@ def get_item_access(item, user): access = item.collection.public_access elif item.collection.public_access == 'mixed': - if user.is_authenticated() : + if user.is_authenticated(): if item.public_access == 'metadata' and item.auto_period_access: access = 'full' else: @@ -185,6 +211,7 @@ def get_item_access(item, user): return access + def get_revisions(nb, user=None): last_revisions = Revision.objects.order_by('-time') if user: @@ -203,6 +230,7 @@ def get_revisions(nb, user=None): revisions.append({'revision': revision, 'element': element}) return revisions + def get_playlists(request, user=None): if not user: user = request.user @@ -219,9 +247,9 @@ def get_playlists(request, user=None): element = mods[type].objects.get(id=resource.resource_id) except: element = None - resources.append({'element': element, 'type': resource.resource_type, 'public_id': resource.public_id }) + resources.append({'element': element, 'type': resource.resource_type, 'public_id': resource.public_id}) playlists.append({'playlist': playlist, 'resources': resources}) - #add by Killian Mary for sort playlist by title + # add by Killian Mary for sort playlist by title playlists.sort(key=lambda x: x['playlist'].title) return playlists @@ -255,6 +283,7 @@ def check_related_media(medias): media.title = title.replace('\n', '').strip() media.save() + def auto_code(collection): items = collection.items.all() suffixes = [] @@ -272,18 +301,18 @@ def auto_code(collection): suffixes.append(suffix) if suffixes: - return collection.code + '_' + str(max(suffixes)+1) + return collection.code + '_' + str(max(suffixes) + 1) else: return collection.code + '_001' def get_room(content_type=None, id=None, name=None): rooms = jqchat.models.Room.objects.filter(content_type=content_type, - object_id=id) + object_id=id) if not rooms: room = jqchat.models.Room.objects.create(content_type=content_type, - object_id=id, - name=name[:254]) + object_id=id, + name=name[:254]) else: room = rooms[0] return room diff --git a/telemeta/views/epub.py b/telemeta/views/epub.py index db7e64a0..c2943b24 100644 --- a/telemeta/views/epub.py +++ b/telemeta/views/epub.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2015 Parisson SARL +# Copyright (C) 2015-2017 Parisson SARL # This file is part of Telemeta. @@ -19,13 +19,15 @@ # Authors: Guillaume Pellerin -from telemeta.views.core import * -from telemeta.models import * +from telemeta.views.core import TelemetaBaseMixin +from telemeta.models import Site from collections import OrderedDict from ebooklib import epub from django.template.loader import render_to_string from django.utils.translation import ugettext_lazy as _ +import os + class BaseEpubMixin(TelemetaBaseMixin): "Download corpus data embedded in an EPUB3 file" @@ -84,7 +86,7 @@ class BaseEpubMixin(TelemetaBaseMixin): else: chap_num = self.collection.code.split('_')[-1] context = {'title': 'chapitre ' + chap_num, - 'mode_single': mode_single} + 'mode_single': mode_single} else: self.collections = self.corpus.children.all() mode_single = False @@ -121,7 +123,7 @@ class BaseEpubMixin(TelemetaBaseMixin): id = item.old_code for c in id: if c.isalpha(): - id = id.replace(c, '.' + str(ord(c)-96)) + id = id.replace(c, '.' + str(ord(c) - 96)) items[item] = float(id) items = OrderedDict(sorted(items.items(), key=lambda t: t[1])) @@ -143,7 +145,7 @@ class BaseEpubMixin(TelemetaBaseMixin): image = open(self.default_image, 'r') default_image_relative_path = 'images' + os.sep + os.path.split(self.default_image)[-1] epub_item = epub.EpubItem(file_name=default_image_relative_path, - content=image.read()) + content=image.read()) self.book.add_item(epub_item) self.default_image_added = True @@ -167,8 +169,8 @@ class BaseEpubMixin(TelemetaBaseMixin): last_collection = True context = {'collection': collection, 'title': title, 'subtitle': subtitle, 'mode_single': mode_single, - 'site': self.site, 'items': items, 'default_image': default_image_relative_path, - 'default_image_end': default_image_end_relative_path, 'last_collection': last_collection} + 'site': self.site, 'items': items, 'default_image': default_image_relative_path, + 'default_image_end': default_image_end_relative_path, 'last_collection': last_collection} c = epub.EpubHtml(title=chapter_title, file_name=collection.code + '.xhtml', lang='fr') c.content = render_to_string(self.template, context) self.book.add_item(c) @@ -179,7 +181,7 @@ class BaseEpubMixin(TelemetaBaseMixin): # - add manual link # - add section # - add auto created links to chapters - self.book.toc = (( self.chapters )) + self.book.toc = ((self.chapters)) self.book.spine = self.chapters # add navigation files @@ -188,7 +190,7 @@ class BaseEpubMixin(TelemetaBaseMixin): nav = epub.EpubNav() self.book.add_item(nav) if not mode_single: - self.book.spine.insert(0,'nav') + self.book.spine.insert(0, 'nav') # create spin, add cover page as first page cover = epub.EpubHtml(title=self.full_title, file_name='cover-bis' + '.xhtml') @@ -204,6 +206,3 @@ class BaseEpubMixin(TelemetaBaseMixin): # write epub file epub.write_epub(self.path, self.book, {}) - - - diff --git a/telemeta/views/item.py b/telemeta/views/item.py index c5e59407..0f87f2b3 100644 --- a/telemeta/views/item.py +++ b/telemeta/views/item.py @@ -21,8 +21,9 @@ # Authors: Olivier Guilyardi # Guillaume Pellerin - from telemeta.views.core import * +from telemeta.views.core import serve_media +from telemeta.views.core import TelemetaBaseMixin from telemeta.views.marker import * import timeside.core import timeside.server as ts @@ -47,11 +48,11 @@ class ItemBaseMixin(TelemetaBaseMixin): graphers = [] for grapher in self.graphers: if grapher.id() == self.default_grapher_id: - graphers.insert(0, {'name':grapher.name(), 'id': grapher.id()}) + graphers.insert(0, {'name': grapher.name(), 'id': grapher.id()}) elif not hasattr(grapher, '_staging'): - graphers.append({'name':grapher.name(), 'id': grapher.id()}) + graphers.append({'name': grapher.name(), 'id': grapher.id()}) elif not grapher._staging: - graphers.append({'name':grapher.name(), 'id': grapher.id()}) + graphers.append({'name': grapher.name(), 'id': grapher.id()}) return graphers def get_grapher(self, id): @@ -65,7 +66,7 @@ class ItemBaseMixin(TelemetaBaseMixin): for encoder in self.encoders: if encoder.file_extension() in self.export_formats: formats.append({'name': encoder.format(), - 'extension': encoder.file_extension()}) + 'extension': encoder.file_extension()}) return formats def item_previous_next(self, item): @@ -87,8 +88,8 @@ class ItemBaseMixin(TelemetaBaseMixin): previous_pk = pks[-2] next_pk = pks[0] else: - previous_pk = pks[pks.index(pk)-1] - next_pk = pks[pks.index(pk)+1] + previous_pk = pks[pks.index(pk) - 1] + next_pk = pks[pks.index(pk) + 1] for it in items: if it.pk == previous_pk: previous = it @@ -97,8 +98,8 @@ class ItemBaseMixin(TelemetaBaseMixin): previous = previous.public_id next = next.public_id else: - previous = item.public_id - next = item.public_id + previous = item.public_id + next = item.public_id return previous, next @jsonrpc_method('telemeta.get_item_export_url') @@ -110,7 +111,7 @@ class ItemView(ItemBaseMixin): """Provide Item web UI methods""" def item_detail(self, request, public_id=None, marker_id=None, width=None, height=None, - template='telemeta/mediaitem_detail.html'): + template='telemeta/mediaitem_detail.html'): """Show the details of a given item""" # get item with one of its given marker_id @@ -128,12 +129,12 @@ class ItemView(ItemBaseMixin): title = ugettext('Item') + ' : ' + public_id + ' : ' + mess description = ugettext('Please login or contact the website administator to get a private access.') messages.error(request, title) - return render(request, 'telemeta/messages.html', {'description' : description}) + return render(request, 'telemeta/messages.html', {'description': description}) previous, next = self.item_previous_next(item) mime_type = item.mime_type - if mime_type and mime_type != 'none' : + if mime_type and mime_type != 'none': if 'quicktime' in mime_type: mime_type = 'video/mp4' @@ -151,20 +152,20 @@ class ItemView(ItemBaseMixin): format = item.format.get() return render(request, template, - {'item': item, 'export_formats': self.get_export_formats(), - 'visualizers': self.get_graphers(), 'auto_zoom': self.auto_zoom, - 'audio_export_enabled': self.export_enabled, - 'previous' : previous, 'next' : next, 'marker': marker_id, 'playlists' : playlists, - 'access': access, 'width': width, 'height': height, - 'related_media': related_media, 'mime_type': mime_type, 'last_revision': last_revision, - 'format': format, - }) + {'item': item, 'export_formats': self.get_export_formats(), + 'visualizers': self.get_graphers(), 'auto_zoom': self.auto_zoom, + 'audio_export_enabled': self.export_enabled, + 'previous': previous, 'next': next, 'marker': marker_id, 'playlists': playlists, + 'access': access, 'width': width, 'height': height, + 'related_media': related_media, 'mime_type': mime_type, 'last_revision': last_revision, + 'format': format, + }) def related_media_item_stream(self, request, item_public_id, media_id): item = get_object_or_404(MediaItem, code=item_public_id) media = get_object_or_404(MediaItemRelated, item=item, id=media_id) if media.file: - response = StreamingHttpResponse(stream_from_file(media.file.path), content_type=media.mime_type) + response = serve_media(media.file.path, content_type=media.mime_type) else: raise Http404 return response @@ -173,9 +174,7 @@ class ItemView(ItemBaseMixin): item = get_object_or_404(MediaItem, code=item_public_id) media = get_object_or_404(MediaItemRelated, item=item, id=media_id) if media.file: - filename = media.file.path.split(os.sep)[-1] - response = StreamingHttpResponse(stream_from_file(media.file.path), content_type=media.mime_type) - response['Content-Disposition'] = 'attachment; ' + 'filename=' + filename + response = serve_media(media.file.path, content_type=media.mime_type) else: raise Http404 return response @@ -192,8 +191,7 @@ class ItemView(ItemBaseMixin): return redirect('telemeta-item-edit', public_id) else: formset = MediaItemRelatedFormSet(instance=item) - return render(request, template, {'item': item, 'formset': formset,}) - + return render(request, template, {'item': item, 'formset': formset, }) @method_decorator(permission_required('telemeta.delete_mediaitem')) def item_delete(self, request, public_id): @@ -214,7 +212,7 @@ class ItemView(ItemBaseMixin): analyzers.append(analysis.to_dict()) mime_type = 'text/xml' response = HttpResponse(self.cache_data.get_analyzer_xml(analyzers), content_type=mime_type) - response['Content-Disposition'] = 'attachment; filename='+public_id+'.xml' + response['Content-Disposition'] = 'attachment; filename=' + public_id + '.xml' return response def item_visualize(self, request, public_id, grapher_id, width, height): @@ -268,7 +266,7 @@ class ItemView(ItemBaseMixin): decoder = timeside.core.get_processor('file_decoder')(source) graph = grapher(width=width, height=height) (decoder | graph).run() - graph.watermark('timeside', opacity=.6, margin=(5,5)) + graph.watermark('timeside', opacity=.6, margin=(5, 5)) f = open(path, 'w') graph.render(output=path) f.close() @@ -282,7 +280,7 @@ class ItemView(ItemBaseMixin): list = [] for encoder in self.encoders: list.append(encoder.file_extension()) - #FIXME: MP4 + # FIXME: MP4 list.append('mp4') return list @@ -293,26 +291,30 @@ class ItemView(ItemBaseMixin): public_access = get_item_access(item, request.user) if (not public_access == 'full' or not extension in settings.TELEMETA_STREAMING_FORMATS) and \ - not (request.user.has_perm('telemeta.can_play_all_items') or request.user.is_superuser): + not (request.user.has_perm('telemeta.can_play_all_items') or request.user.is_superuser): mess = ugettext('Access not allowed') title = 'Item file : ' + public_id + '.' + extension + ' : ' + mess description = ugettext('Please login or contact the website administator to get a private access.') messages.error(request, title) - return render(request, 'telemeta/messages.html', {'description' : description}) + return render(request, 'telemeta/messages.html', {'description': description}) - #FIXME: MP4 handling in TimeSide + # FIXME: MP4 handling in TimeSide if 'mp4' in extension: mime_type = 'video/mp4' video = item.file.path - response = StreamingHttpResponse(stream_from_file(video), mimetype = mime_type) - response['Content-Disposition'] = 'attachment' + response = serve_media(video, content_type=mime_type) + # response['Content-Disposition'] = 'attachment' + # TF : I don't know why empty attachment was set + # TODO: remove if useless return response if 'webm' in extension: mime_type = 'video/webm' video = item.file.path - response = StreamingHttpResponse(stream_from_file(video), mimetype = mime_type) - response['Content-Disposition'] = 'attachment' + response = serve_media(video, content_type=mime_type) + # response['Content-Disposition'] = 'attachment' + # TF : I don't know why empty attachment was set, + # TODO: remove if useless return response for encoder in self.encoders: @@ -324,7 +326,7 @@ class ItemView(ItemBaseMixin): mime_type = encoder.mime_type() file = public_id + '.' + encoder.file_extension() - source, _ = item.get_source() + source, source_type = item.get_source() flag = MediaItemTranscodingFlag.objects.filter(item=item, mime_type=mime_type) if not flag: @@ -339,33 +341,38 @@ class ItemView(ItemBaseMixin): mapping = DublinCoreToFormatMetadata(extension) metadata = mapping.get_metadata(dc_metadata) - if mime_type in format: + if mime_type in format and source_type == 'file': # source > stream if not extension in mapping.unavailable_extensions: proc = encoder(source, overwrite=True) proc.set_metadata(metadata) try: - #FIXME: should test if metadata writer is available + # FIXME: should test if metadata writer is available proc.write_metadata() except: pass - response = StreamingHttpResponse(stream_from_file(source), mimetype = mime_type) + response = serve_media(source, content_type=mime_type) else: media = self.cache_export.dir + os.sep + file if not self.cache_export.exists(file) or not flag.value: # source > encoder > stream decoder = timeside.core.get_processor('file_decoder')(source) - proc = encoder(media, streaming=True, overwrite=True) + proc = encoder(media, streaming=False, overwrite=True) if extension in mapping.unavailable_extensions: - metadata=None + metadata = None proc.set_metadata(metadata) self.cache_export.add_file(file) + pipe = decoder | proc + pipe.run() + flag.value = True + flag.save() + return HttpResponse('Plop') response = StreamingHttpResponse(stream_from_processor(decoder, proc, flag), content_type=mime_type) else: # cache > stream - response = StreamingHttpResponse(self.cache_export.read_stream_bin(file), content_type=mime_type) - response['Content-Disposition'] = 'attachment' + response = serve_media(media, content_type=mime_type, buffering=False) + return response def item_playlist(self, request, public_id, template, mimetype): @@ -389,7 +396,7 @@ class ItemView(ItemBaseMixin): return redirect('telemeta-item-edit', item.public_id) else: formset = PerformanceFormSet(instance=item) - return render(request, template, {'item': item, 'formset': formset,}) + return render(request, template, {'item': item, 'formset': formset, }) @method_decorator(permission_required('telemeta.change_mediaitem')) def item_keywords_edit(self, request, public_id, template): @@ -402,7 +409,7 @@ class ItemView(ItemBaseMixin): return redirect('telemeta-item-edit', item.public_id) else: formset = FormSet(instance=item) - return render(request, template, {'item': item, 'formset': formset,}) + return render(request, template, {'item': item, 'formset': formset, }) class ItemListView(ListView): @@ -420,12 +427,13 @@ class ItemListView(ListView): context['results_page'] = int(self.request.GET.get('results_page', 20)) return context + class ItemListViewFullAccess(ListView): model = MediaItem template_name = "telemeta/mediaitem_list.html" paginate_by = 20 - queryset = MediaItem.objects.enriched().filter(Q(collection__public_access="full")|Q(public_access="full")).sound().exclude(collection__public_access="none").order_by('code', 'old_code') + queryset = MediaItem.objects.enriched().filter(Q(collection__public_access="full") | Q(public_access="full")).sound().exclude(collection__public_access="none").order_by('code', 'old_code') def get_context_data(self, **kwargs): context = super(ItemListViewFullAccess, self).get_context_data(**kwargs) @@ -447,6 +455,7 @@ class ItemSoundListView(ItemListView): queryset = MediaItem.objects.sound().order_by('code', 'old_code') + class ItemInstrumentListView(ItemListView): template_name = "telemeta/media_item_instrument_list.html" @@ -457,25 +466,30 @@ class ItemInstrumentListView(ItemListView): def get_context_data(self, **kwargs): context = super(ItemInstrumentListView, self).get_context_data(**kwargs) - context['nom']=Instrument.objects.get(id=self.kwargs['value_id']).name - context['id']=self.kwargs['value_id'] + context['nom'] = Instrument.objects.get(id=self.kwargs['value_id']).name + context['id'] = self.kwargs['value_id'] return context + class ItemInstrumentPublishedListView(ItemInstrumentListView): def get_queryset(self): return super(ItemInstrumentPublishedListView, self).get_queryset().filter(collection__code__contains='_E_').order_by('code', 'old_code') + class ItemInstrumentUnpublishedListView(ItemInstrumentListView): def get_queryset(self): return super(ItemInstrumentUnpublishedListView, self).get_queryset().filter(collection__code__contains='_I_').order_by('code', 'old_code') + class ItemInstrumentSoundListView(ItemInstrumentListView): - def get_queryset(self): + + def get_queryset(self): return super(ItemInstrumentSoundListView, self).get_queryset().sound().order_by('code', 'old_code') + class ItemAliasListView(ItemListView): template_name = "telemeta/media_item_alias_list.html" @@ -486,25 +500,30 @@ class ItemAliasListView(ItemListView): def get_context_data(self, **kwargs): context = super(ItemAliasListView, self).get_context_data(**kwargs) - context['nom']=InstrumentAlias.objects.get(id=self.kwargs['value_id']).name - context['id']=self.kwargs['value_id'] + context['nom'] = InstrumentAlias.objects.get(id=self.kwargs['value_id']).name + context['id'] = self.kwargs['value_id'] return context + class ItemAliasPublishedListView(ItemAliasListView): def get_queryset(self): return super(ItemAliasPublishedListView, self).get_queryset().filter(collection__code__contains='_E_').order_by('code', 'old_code') + class ItemAliasUnpublishedListView(ItemAliasListView): def get_queryset(self): return super(ItemAliasUnpublishedListView, self).get_queryset().filter(collection__code__contains='_I_').order_by('code', 'old_code') + class ItemAliasSoundListView(ItemAliasListView): - def get_queryset(self): + + def get_queryset(self): return super(ItemAliasSoundListView, self).get_queryset().sound().order_by('code', 'old_code') + class ItemViewMixin(ItemBaseMixin): model = MediaItem @@ -555,7 +574,7 @@ class ItemEditView(ItemViewMixin, UpdateWithInlinesView): return super(ItemEditView, self).forms_valid(form, inlines) def get_success_url(self): - return reverse_lazy('telemeta-item-detail', kwargs={'public_id':self.code}) + return reverse_lazy('telemeta-item-detail', kwargs={'public_id': self.code}) def get_context_data(self, **kwargs): context = super(ItemEditView, self).get_context_data(**kwargs) @@ -563,7 +582,7 @@ class ItemEditView(ItemViewMixin, UpdateWithInlinesView): context['item'] = item context['access'] = get_item_access(item, self.request.user) context['previous'], context['next'] = self.item_previous_next(item) - #FIXME + # FIXME context['mime_type'] = 'audio/mp3' context['export_formats'] = self.get_export_formats() context['visualizers'] = self.get_graphers() @@ -601,7 +620,7 @@ class ItemAddView(ItemViewMixin, CreateWithInlinesView): return super(ItemAddView, self).forms_valid(form, inlines) def get_success_url(self): - return reverse_lazy('telemeta-item-detail', kwargs={'public_id':self.object.code}) + return reverse_lazy('telemeta-item-detail', kwargs={'public_id': self.object.code}) @method_decorator(permission_required('telemeta.add_mediaitem')) def dispatch(self, *args, **kwargs): @@ -637,7 +656,7 @@ class ItemCopyView(ItemAddView): return super(ItemCopyView, self).forms_valid(form, inlines) def get_success_url(self): - return reverse_lazy('telemeta-item-detail', kwargs={'public_id':self.object.code}) + return reverse_lazy('telemeta-item-detail', kwargs={'public_id': self.object.code}) def get_context_data(self, **kwargs): context = super(ItemCopyView, self).get_context_data(**kwargs) @@ -645,7 +664,7 @@ class ItemCopyView(ItemAddView): context['item'] = item context['access'] = get_item_access(item, self.request.user) context['previous'], context['next'] = self.item_previous_next(item) - #FIXME + # FIXME context['mime_type'] = 'audio/mp3' context['export_formats'] = self.get_export_formats() context['visualizers'] = self.get_graphers() @@ -700,14 +719,14 @@ class ItemDetailView(ItemViewMixin, DetailView): height = size.split('x')[1] image_file = '.'.join([item.public_id, self.default_grapher_id, size.replace('x', '_'), 'png']) path = self.cache_data.dir + os.sep + image_file - graph = default_grapher(width = int(width), height = int(height)) - graphers_sub.append({'graph' : graph, 'path': path}) + graph = default_grapher(width=int(width), height=int(height)) + graphers_sub.append({'graph': graph, 'path': path}) pipe = pipe | graph pipe.run() for grapher in graphers_sub: - grapher['graph'].watermark('timeside', opacity=.6, margin=(5,5)) + grapher['graph'].watermark('timeside', opacity=.6, margin=(5, 5)) f = open(grapher['path'], 'w') grapher['graph'].render(grapher['path']) f.close() @@ -715,10 +734,10 @@ class ItemDetailView(ItemViewMixin, DetailView): if os.path.exists(source): mime_type = mimetypes.guess_type(source)[0] analysis = MediaItemAnalysis(item=item, name='MIME type', - analyzer_id='mime_type', unit='', value=mime_type) + analyzer_id='mime_type', unit='', value=mime_type) analysis.save() analysis = MediaItemAnalysis(item=item, name='Size', - analyzer_id='size', unit='', value=item.size()) + analyzer_id='size', unit='', value=item.size()) analysis.save() analysis = MediaItemAnalysis(item=item, name='Channels', @@ -735,7 +754,7 @@ class ItemDetailView(ItemViewMixin, DetailView): analysis.save() analysis = MediaItemAnalysis(item=item, name='Duration', analyzer_id='duration', unit='s', - value=unicode(datetime.timedelta(0,decoder.input_duration))) + value=unicode(datetime.timedelta(0, decoder.input_duration))) analysis.save() for analyzer in analyzers_sub: @@ -745,7 +764,7 @@ class ItemDetailView(ItemViewMixin, DetailView): if value.shape[0] == 1: value = value[0] analysis = MediaItemAnalysis(item=item, name=result.name, - analyzer_id=result.id, unit=result.unit, value = unicode(value)) + analyzer_id=result.id, unit=result.unit, value=unicode(value)) analysis.save() # FIXME: parse tags on first load @@ -783,7 +802,7 @@ class ItemDetailView(ItemViewMixin, DetailView): self.item_analyze(item) - #FIXME: use mimetypes.guess_type + # FIXME: use mimetypes.guess_type if 'quicktime' in self.mime_type: self.mime_type = 'video/mp4' @@ -791,10 +810,10 @@ class ItemDetailView(ItemViewMixin, DetailView): rang = [] for i in range(len(playlists)): - for resource in playlists[i]['playlist'].resources.all(): - if int(resource.resource_id) == item.id: - rang.append(i) - break + for resource in playlists[i]['playlist'].resources.all(): + if int(resource.resource_id) == item.id: + rang.append(i) + break related_media = MediaItemRelated.objects.filter(item=item) check_related_media(related_media) revisions = Revision.objects.filter(element_type='item', element_id=item.id).order_by('-time') @@ -825,7 +844,7 @@ class ItemDetailView(ItemViewMixin, DetailView): context['format'] = item_format context['private_extra_types'] = private_extra_types.values() context['site'] = 'http://' + Site.objects.all()[0].name - context['rang_item_playlist']=rang + context['rang_item_playlist'] = rang # if ts_item: # context['ts_item_id'] = ts_item.pk # else: @@ -838,42 +857,42 @@ class DublinCoreToFormatMetadata(object): """a mapping class to get item DublinCore metadata dictionaries in various audio metadata format (MP3, OGG, etc...)""" - #FIXME: should be given by timeside + # FIXME: should be given by timeside unavailable_extensions = ['wav', 'aiff', 'aif', 'flac', 'webm'] metadata_mapping = { - 'mp3' : { - 'title': 'TIT2', #title2 - 'creator': 'TCOM', #composer - 'creator': 'TPE1', #lead - 'identifier': 'UFID', #unique ID - 'relation': 'TALB', #album - 'type': 'TCON', #genre - 'publisher': 'TPUB', #publisher - 'date': 'TDRC', #year -# 'coverage': 'COMM', #comment - }, - 'ogg': { - 'creator': 'artist', - 'relation': 'album', + 'mp3': { + 'title': 'TIT2', # title2 + 'creator': 'TCOM', # composer + 'creator': 'TPE1', # lead + 'identifier': 'UFID', # unique ID + 'relation': 'TALB', # album + 'type': 'TCON', # genre + 'publisher': 'TPUB', # publisher + 'date': 'TDRC', # year + # 'coverage': 'COMM', #comment + }, + 'ogg': { + 'creator': 'artist', + 'relation': 'album', 'all': 'all', - }, - 'flac': { - 'creator': 'artist', - 'relation': 'album', + }, + 'flac': { + 'creator': 'artist', + 'relation': 'album', 'all': 'all', - }, - 'wav': { - 'creator': 'artist', - 'relation': 'album', + }, + 'wav': { + 'creator': 'artist', + 'relation': 'album', 'all': 'all', - }, - 'webm': { - 'creator': 'artist', - 'relation': 'album', + }, + 'webm': { + 'creator': 'artist', + 'relation': 'album', 'all': 'all', - }, - } + }, + } def __init__(self, format): self.format = format @@ -889,7 +908,7 @@ class DublinCoreToFormatMetadata(object): if key == 'date': value = value.split(';')[0].split('=') if len(value) > 1: - value = value[1] + value = value[1] value = value.split('-')[0] else: value = value[0].split('-')[0] @@ -916,7 +935,7 @@ class ItemMarkerJsonView(View): data = '' response = HttpResponse(data, content_type='application/json') response['Content-Disposition'] = "attachment; filename=%s.%s" % \ - (item.code, 'json') + (item.code, 'json') return response diff --git a/telemeta/views/resource.py b/telemeta/views/resource.py index 0489f737..86f86a29 100644 --- a/telemeta/views/resource.py +++ b/telemeta/views/resource.py @@ -22,7 +22,8 @@ from telemeta.views.core import * -from telemeta.views.epub import * +from telemeta.views.core import serve_media +from telemeta.views.epub import BaseEpubMixin from django.utils.translation import ugettext_lazy as _ @@ -30,18 +31,18 @@ class ResourceView(object): """Provide Resource web UI methods""" types = {'corpus': - {'model': MediaCorpus, - 'form' : MediaCorpusForm, - 'related': MediaCorpusRelated, - 'parent': MediaFonds, - }, - 'fonds': - {'model': MediaFonds, - 'form' : MediaFondsForm, - 'related': MediaFondsRelated, - 'parent': None, - } - } + {'model': MediaCorpus, + 'form': MediaCorpusForm, + 'related': MediaCorpusRelated, + 'parent': MediaFonds, + }, + 'fonds': + {'model': MediaFonds, + 'form': MediaFondsForm, + 'related': MediaFondsRelated, + 'parent': None, + } + } def setup(self, type): self.model = self.types[type]['model'] @@ -69,8 +70,8 @@ class ResourceView(object): parents = [] return render(request, template, {'resource': resource, 'type': type, 'children': children, - 'related_media': related_media, 'parents': parents, 'playlists': playlists, - 'last_revision': last_revision }) + 'related_media': related_media, 'parents': parents, 'playlists': playlists, + 'last_revision': last_revision}) def edit(self, request, type, public_id, template='telemeta/resource_edit.html'): self.setup(type) @@ -86,7 +87,7 @@ class ResourceView(object): return redirect('telemeta-resource-detail', self.type, code) else: form = self.form(instance=resource) - return render(request, template, {'resource': resource, 'type': type, 'form': form,}) + return render(request, template, {'resource': resource, 'type': type, 'form': form, }) def add(self, request, type, template='telemeta/resource_add.html'): self.setup(type) @@ -102,7 +103,7 @@ class ResourceView(object): return redirect('telemeta-resource-detail', self.type, code) else: form = self.form(instance=resource) - return render(request, template, {'resource': resource, 'type': type, 'form': form,}) + return render(request, template, {'resource': resource, 'type': type, 'form': form, }) def copy(self, request, type, public_id, template='telemeta/resource_edit.html'): self.setup(type) @@ -119,7 +120,7 @@ class ResourceView(object): else: resource = self.model.objects.get(code=public_id) form = self.form(instance=resource) - return render(request, template, {'resource': resource, 'type': type, "form": form,}) + return render(request, template, {'resource': resource, 'type': type, "form": form, }) def playlist(self, request, type, public_id, template, mimetype): self.setup(type) @@ -139,14 +140,14 @@ class ResourceView(object): for revision in revisions: revision.delete() resource.delete() - return HttpResponseRedirect('/archives/'+self.type+'/') + return HttpResponseRedirect('/archives/' + self.type + '/') def related_stream(self, request, type, public_id, media_id): self.setup(type) resource = self.model.objects.get(code=public_id) media = self.related.objects.get(resource=resource, id=media_id) if media.file: - response = StreamingHttpResponse(stream_from_file(media.file.path), content_type=media.mime_type) + response = serve_media(media.file.path, content_type=media.mime_type) else: raise Http404 return response @@ -156,9 +157,7 @@ class ResourceView(object): resource = self.model.objects.get(code=public_id) media = self.related.objects.get(resource=resource, id=media_id) if media.file: - filename = media.file.path.split(os.sep)[-1] - response = StreamingHttpResponse(stream_from_file(media.file.path), content_type=media.mime_type) - response['Content-Disposition'] = 'attachment; ' + 'filename=' + filename + response = serve_media(media.file.path, content_type=media.mime_type) else: raise Http404 return response @@ -167,20 +166,20 @@ class ResourceView(object): class ResourceMixin(View): types = {'corpus': - {'model': MediaCorpus, - 'form' : MediaCorpusForm, - 'related': MediaCorpusRelated, - 'parent': MediaFonds, - 'inlines': [CorpusRelatedInline,] - }, - 'fonds': - {'model': MediaFonds, - 'form' : MediaFondsForm, - 'related': MediaFondsRelated, - 'parent': None, - 'inlines': [FondsRelatedInline,] - } - } + {'model': MediaCorpus, + 'form': MediaCorpusForm, + 'related': MediaCorpusRelated, + 'parent': MediaFonds, + 'inlines': [CorpusRelatedInline, ] + }, + 'fonds': + {'model': MediaFonds, + 'form': MediaFondsForm, + 'related': MediaFondsRelated, + 'parent': None, + 'inlines': [FondsRelatedInline, ] + } + } def setup(self, type): self.model = self.types[type]['model'] @@ -238,7 +237,7 @@ class ResourceSingleMixin(ResourceMixin): else: context['parents'] = [] return context - + class ResourceListView(ResourceMixin, ListView): @@ -276,7 +275,7 @@ class ResourceAddView(ResourceMixin, CreateView): return self def get_success_url(self): - return reverse_lazy('telemeta-resource-list', kwargs={'type':self.kwargs['type']}) + return reverse_lazy('telemeta-resource-list', kwargs={'type': self.kwargs['type']}) @method_decorator(permission_required('telemeta.add_mediacorpus')) @method_decorator(permission_required('telemeta.add_mediafonds')) @@ -292,7 +291,7 @@ class ResourceCopyView(ResourceSingleMixin, ResourceAddView): return model_to_dict(self.get_object()) def get_success_url(self): - return reverse_lazy('telemeta-resource-list', kwargs={'type':self.kwargs['type']}) + return reverse_lazy('telemeta-resource-list', kwargs={'type': self.kwargs['type']}) # return reverse_lazy('telemeta-resource-detail', kwargs={'type':self.kwargs['type'], 'public_id':self.kwargs['public_id']}) @method_decorator(permission_required('telemeta.add_mediacorpus')) @@ -306,7 +305,7 @@ class ResourceDeleteView(ResourceSingleMixin, DeleteView): template_name = 'telemeta/resource_confirm_delete.html' def get_success_url(self): - return reverse_lazy('telemeta-resource-list', kwargs={'type':self.kwargs['type']}) + return reverse_lazy('telemeta-resource-list', kwargs={'type': self.kwargs['type']}) @method_decorator(permission_required('telemeta.delete_mediacorpus')) @method_decorator(permission_required('telemeta.delete_mediafonds')) @@ -319,7 +318,7 @@ class ResourceEditView(ResourceSingleMixin, UpdateWithInlinesView): template_name = 'telemeta/resource_edit.html' def get_success_url(self): - return reverse_lazy('telemeta-resource-detail', kwargs={'type':self.kwargs['type'], 'public_id':self.kwargs['public_id']}) + return reverse_lazy('telemeta-resource-detail', kwargs={'type': self.kwargs['type'], 'public_id': self.kwargs['public_id']}) @method_decorator(permission_required('telemeta.change_mediacorpus')) @method_decorator(permission_required('telemeta.change_mediafonds'))