forked from 0x2620/pandora
serve frames and videos with X-Sendfile header
This commit is contained in:
parent
7c0e365a0a
commit
1354a1ddc5
6 changed files with 51 additions and 8 deletions
4
README
4
README
|
@ -30,3 +30,7 @@ Development:
|
||||||
we are using django, http://docs.djangoproject.com/en/dev/
|
we are using django, http://docs.djangoproject.com/en/dev/
|
||||||
communication between webserver and background tasks is done via rabbitmq
|
communication between webserver and background tasks is done via rabbitmq
|
||||||
http://github.com/ask/carrot/ for more info on the lib used to communicate
|
http://github.com/ask/carrot/ for more info on the lib used to communicate
|
||||||
|
|
||||||
|
|
||||||
|
Apache setup:
|
||||||
|
sudo apt-get install libapache2-mod-wsgi libapache2-mod-xsendfile
|
||||||
|
|
|
@ -8,16 +8,21 @@
|
||||||
Allow from all
|
Allow from all
|
||||||
</Directory>
|
</Directory>
|
||||||
|
|
||||||
Alias /.bzr /oxdb/.bzr
|
<Location />
|
||||||
Alias /static __PREFIX__/oxdb/static
|
XSendFile on
|
||||||
Alias /favicon.ico __PREFIX__/oxdb/static/favicon.ico
|
XSendFileAllowAbove on
|
||||||
Alias /media __PREFIX__/oxdb/media
|
</Location>
|
||||||
|
|
||||||
|
Alias /.bzr __PREFIX__/.bzr
|
||||||
|
Alias /static __PREFIX__/pandora/static
|
||||||
|
Alias /favicon.ico __PREFIX__/pandora/static/favicon.ico
|
||||||
|
Alias /media __PREFIX__/pandora/media
|
||||||
Alias /admin/media __PREFIX__/src/django/django/contrib/admin/media
|
Alias /admin/media __PREFIX__/src/django/django/contrib/admin/media
|
||||||
Alias /static/js/jquery.js __PREFIX__/src/django/django/contrib/admin/media/js/jquery.min.js
|
Alias /static/js/jquery.js __PREFIX__/src/django/django/contrib/admin/media/js/jquery.min.js
|
||||||
WSGIScriptAlias / __PREFIX__/wsgi/django.wsgi
|
WSGIScriptAlias / __PREFIX__/wsgi/django.wsgi
|
||||||
|
|
||||||
WSGIDaemonProcess oxdb user=oxdb group=oxdb threads=25 python-path=__PREFIX__/lib/python2.6/site-packages/
|
WSGIDaemonProcess pandora user=pandora group=pandora threads=25 python-path=__PREFIX__/lib/python2.6/site-packages/
|
||||||
WSGIProcessGroup oxdb
|
WSGIProcessGroup pandora
|
||||||
|
|
||||||
ServerSignature Off
|
ServerSignature Off
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
|
|
|
@ -10,6 +10,7 @@ from django.db.models import Q
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
from django.utils import simplejson as json
|
from django.utils import simplejson as json
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
from oxdjango import fields
|
from oxdjango import fields
|
||||||
import oxlib
|
import oxlib
|
||||||
|
@ -430,6 +431,11 @@ class Movie(models.Model):
|
||||||
self.get('series_title', ''), self.get('episode_title', ''),
|
self.get('series_title', ''), self.get('episode_title', ''),
|
||||||
self.get('season', ''), self.get('episode', ''))
|
self.get('season', ''), self.get('episode', ''))
|
||||||
|
|
||||||
|
def frame(self, position, width=128):
|
||||||
|
#FIXME: compute offset and so on
|
||||||
|
f = self.files.all()[0]
|
||||||
|
return f.frame(position, width)
|
||||||
|
|
||||||
def updateFind(self):
|
def updateFind(self):
|
||||||
try:
|
try:
|
||||||
f = self.find
|
f = self.find
|
||||||
|
@ -1155,7 +1161,9 @@ class File(models.Model):
|
||||||
def frame(self, position, width=128):
|
def frame(self, position, width=128):
|
||||||
videoFile = getattr(self, 'stream_%s'%settings.VIDEO_PROFILE).path
|
videoFile = getattr(self, 'stream_%s'%settings.VIDEO_PROFILE).path
|
||||||
frameFolder = os.path.join(os.path.dirname(videoFile), 'frames')
|
frameFolder = os.path.join(os.path.dirname(videoFile), 'frames')
|
||||||
extract.frame(videoFile, position, frameFolder, width)
|
if position<= self.duration:
|
||||||
|
return extract.frame(videoFile, position, frameFolder, width)
|
||||||
|
return None
|
||||||
|
|
||||||
def editable(self, user):
|
def editable(self, user):
|
||||||
'''
|
'''
|
||||||
|
|
|
@ -6,6 +6,8 @@ from django.conf.urls.defaults import *
|
||||||
|
|
||||||
urlpatterns = patterns("backend.views",
|
urlpatterns = patterns("backend.views",
|
||||||
(r'^upload/$', 'firefogg_upload'),
|
(r'^upload/$', 'firefogg_upload'),
|
||||||
|
(r'^frame/(?P<id>.*)/(?P<position>.*)\.(?P<size>\d+).jpg$', 'frame'),
|
||||||
|
(r'^stream/(?P<id>.*).(?P<quality>.*).ogv$', 'video'),
|
||||||
(r'^$', 'api'),
|
(r'^$', 'api'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# vi:si:et:sw=4:sts=4:ts=4
|
# vi:si:et:sw=4:sts=4:ts=4
|
||||||
|
from __future__ import division
|
||||||
import os.path
|
import os.path
|
||||||
import re
|
import re
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from urllib2 import unquote
|
from urllib2 import unquote
|
||||||
import json
|
import json
|
||||||
|
import mimetypes
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db.models import Q, Avg, Count
|
from django.db.models import Q, Avg, Count
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse, Http404
|
||||||
from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404
|
from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404
|
||||||
from django.template import RequestContext
|
from django.template import RequestContext
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -23,6 +25,8 @@ except ImportError:
|
||||||
|
|
||||||
from oxdjango.decorators import login_required_json
|
from oxdjango.decorators import login_required_json
|
||||||
from oxdjango.shortcuts import render_to_json_response, get_object_or_404_json, json_response
|
from oxdjango.shortcuts import render_to_json_response, get_object_or_404_json, json_response
|
||||||
|
from oxdjango.http import HttpFileResponse
|
||||||
|
import oxlib
|
||||||
|
|
||||||
import models
|
import models
|
||||||
import utils
|
import utils
|
||||||
|
@ -631,6 +635,24 @@ def api_subtitles(request):
|
||||||
response['data']['languages'] = [f['language'] for f in l]
|
response['data']['languages'] = [f['language'] for f in l]
|
||||||
return render_to_json_response(response)
|
return render_to_json_response(response)
|
||||||
|
|
||||||
|
def video(request, id, quality):
|
||||||
|
movie = get_object_or_404(models.Movie, movieId=id)
|
||||||
|
if quality not in settings.VIDEO_ENCODING:
|
||||||
|
raise Http404
|
||||||
|
stream = getattr(movie, 'stream_'+quality)
|
||||||
|
response = HttpFileResponse(stream.path, content_type='video/ogg')
|
||||||
|
#FIXME: movie needs duration field
|
||||||
|
#response['Content-Duration'] = movie.duration
|
||||||
|
return response
|
||||||
|
|
||||||
|
def frame(request, id, position, size):
|
||||||
|
movie = get_object_or_404(models.Movie, movieId=id)
|
||||||
|
position = oxlib.time2ms(position)/1000
|
||||||
|
frame = movie.frame(position, int(size))
|
||||||
|
if not frame:
|
||||||
|
raise Http404
|
||||||
|
return HttpFileResponse(frame, content_type='image/jpeg')
|
||||||
|
|
||||||
'''
|
'''
|
||||||
GET list
|
GET list
|
||||||
> {
|
> {
|
||||||
|
|
|
@ -13,6 +13,8 @@ DEBUG = True
|
||||||
TEMPLATE_DEBUG = DEBUG
|
TEMPLATE_DEBUG = DEBUG
|
||||||
JSON_DEBUG = False
|
JSON_DEBUG = False
|
||||||
|
|
||||||
|
XSENDFILE = False
|
||||||
|
|
||||||
ADMINS = (
|
ADMINS = (
|
||||||
('j', 'j@mailb.org'),
|
('j', 'j@mailb.org'),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue