]> git.parisson.com Git - telemeta.git/commitdiff
Serve media file through nginx
authorThomas Fillon <thomas@parisson.com>
Tue, 4 Apr 2017 22:44:35 +0000 (00:44 +0200)
committerThomas Fillon <thomas@parisson.com>
Tue, 4 Apr 2017 22:44:35 +0000 (00:44 +0200)
telemeta/templates/telemeta/mediaitem_detail.html
telemeta/views/admin.py
telemeta/views/collection.py
telemeta/views/core.py
telemeta/views/epub.py
telemeta/views/item.py
telemeta/views/resource.py

index 7260817430cab51ddd035f16e24e7aef01dc718f..0385bc0e5bc4478abc3c0cc6d0a5baf1d0007c95 100644 (file)
@@ -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
index 051379e68b4d20ea67951c97d0731a9009843dbf..ec579eb69cf1648a700242786cc4d3db7096d7e6 100644 (file)
@@ -22,6 +22,8 @@
 
 
 from telemeta.views.core import *
+import telemeta.models
+
 
 class AdminView(object):
     """Provide Admin web UI methods"""
index daff446d43d52a1b37dbd8204f2241674adc3f83..d440221af4f44b5ddf3bea496ba001056b394fef 100644 (file)
@@ -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"
 
index 9b92fc3e46a893d2d7f03b57f04e2d76d43dd6c0..1677d1d8a851043a48a8a2ef3a4dc8d1b4833620 100644 (file)
@@ -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 <olivier@samalyse.com>
 #          Guillaume Pellerin <yomguy@parisson.com>
+#          Thomas Fillon <thomas@parisson.com>
 
 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
index db7e64a0a5a8da69363a28e531756e3244446b62..c2943b24877c3930bae2cda2313274f7de2226e6 100644 (file)
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright (C) 2015 Parisson SARL
+# Copyright (C) 2015-2017 Parisson SARL
 
 # This file is part of Telemeta.
 
 # Authors: Guillaume Pellerin <yomguy@parisson.com>
 
 
-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, {})
-
-
-
index c5e594079d0093f60fa8b86ef6899d06efce4236..0f87f2b35ab4dbb1a1161a1075733f11ae5f7f35 100644 (file)
@@ -21,8 +21,9 @@
 # Authors: Olivier Guilyardi <olivier@samalyse.com>
 #          Guillaume Pellerin <yomguy@parisson.com>
 
-
 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
 
 
index 0489f737dc709ffc8ad76e1dd2b8fef1c24996e2..86f86a299e3159536a894a8b8be82222cfb318ef 100644 (file)
@@ -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'))