From fba5070bc2bbd21a38c373b70da69ecfdd718e14 Mon Sep 17 00:00:00 2001 From: j <0x006A@0x2620.org> Date: Tue, 24 Aug 2010 19:16:33 +0200 Subject: [PATCH] uploads, transcodes, /ra --- pandora/app/views.py | 4 +++ pandora/archive/models.py | 60 +++++++++++++++++++++++++++++++++--- pandora/archive/views.py | 41 ++++++++++++++---------- pandora/backend/extract.py | 2 ++ pandora/backend/models.py | 22 +++++++------ pandora/backend/utils.py | 6 ++++ pandora/settings.py | 20 +++++++----- pandora/templates/intro.html | 10 ++++++ pandora/urls.py | 3 +- 9 files changed, 128 insertions(+), 40 deletions(-) create mode 100644 pandora/templates/intro.html diff --git a/pandora/app/views.py b/pandora/app/views.py index 6af3e285..5fd48b2f 100644 --- a/pandora/app/views.py +++ b/pandora/app/views.py @@ -8,6 +8,10 @@ from oxdjango.shortcuts import json_response, render_to_json_response, get_objec import models +def intro(request): + context = RequestContext(request, {'settings':settings}) + return render_to_response('intro.html', context) + def index(request): context = RequestContext(request, {'settings':settings}) return render_to_response('index.html', context) diff --git a/pandora/archive/models.py b/pandora/archive/models.py index f23af64a..050ba0ea 100644 --- a/pandora/archive/models.py +++ b/pandora/archive/models.py @@ -24,6 +24,9 @@ from backend import utils from backend import extract from pandora.backend.models import Movie +import extract + + def parse_decimal(string): string = string.replace(':', '/') if '/' not in string: @@ -133,6 +136,11 @@ class File(models.Model): r[k] = unicode(self[k]) return r + def contents(self): + if self.contents_set.count() > 0: + return self.contents_set.all()[0].data + return None + class Volume(models.Model): class Meta: unique_together = ("user", "name") @@ -171,8 +179,8 @@ class FileInstance(models.Model): return File.objects.get(oshash=self.oshash).movieId def frame_path(f, name): - ext = os.path.splitext(name) - name = "%s.%s" % (f.position, ext) + ext = os.path.splitext(name)[-1] + name = "%s%s" % (f.position, ext) h = f.file.oshash return os.path.join('frame', h[:2], h[2:4], h[4:6], name) @@ -188,7 +196,7 @@ class Frame(models.Model): #FIXME: frame path should be renamed on save to match current position def __unicode__(self): - return '%s at %s' % (self.file, self.position) + return u'%s at %s' % (self.file, self.position) def stream_path(f): h = f.file.oshash @@ -204,10 +212,43 @@ class Stream(models.Model): source = models.ForeignKey('Stream', related_name='derivatives', default=None, null=True) available = models.BooleanField(default=False) + def __unicode__(self): + return self.video + def extract_derivates(self): - #here based on settings derivates like smaller versions or other formats would be created + if settings.VIDEO_H264: + profile = self.profile.replace('.webm', '.mp4') + if Stream.objects.filter(profile=profile, source=self).count() == 0: + derivate = Stream(file=self.file, source=self, profile=profile) + derivate.video.name = self.video.name.replace(self.profile, profile) + derivate.encode() + + for p in settings.VIDEO_DERIVATIVES: + profile = p + '.webm' + target = self.video.path.replace(self.profile, profile) + if Stream.objects.filter(profile=profile, source=self).count() == 0: + derivate = Stream(file=self.file, source=self, profile=profile) + derivate.video.name = self.video.name.replace(self.profile, profile) + derivate.encode() + + if settings.VIDEO_H264: + profile = p + '.mp4' + if Stream.objects.filter(profile=profile, source=self).count() == 0: + derivate = Stream(file=self.file, source=self, profile=profile) + derivate.video.name = self.video.name.replace(self.profile, profile) + derivate.encode() return True + def encode(self): + if self.source: + video = self.source.video.path + target = self.video.path + profile = self.profile + info = self.file.info + if extract.stream(video, target, profile, info): + self.available=True + self.save() + def editable(self, user): #FIXME: possibly needs user setting for stream return True @@ -230,4 +271,15 @@ class Stream(models.Model): self.file.save() super(Stream, self).save(*args, **kwargs) +class FileContents(models.Model): + created = models.DateTimeField(auto_now_add=True) + modified = models.DateTimeField(auto_now=True) + file = models.ForeignKey(File, related_name="contents_set") + data = models.TextField(default=u'') + + def save(self, *args, **kwargs): + if self.data and not self.file.available: + self.file.available = True + self.file.save() + super(FileContents, self).save(*args, **kwargs) diff --git a/pandora/archive/views.py b/pandora/archive/views.py index 25896b96..c063db57 100644 --- a/pandora/archive/views.py +++ b/pandora/archive/views.py @@ -159,28 +159,37 @@ def api_update(request): @login_required_json -#FIXME: is this part of the api or does it have to be outside due to multipart? def api_upload(request): ''' - multipart - param data - oshash: string - frame: [] //multipart frames + oshash: string + frame: [] //multipart frames + file: [] //multipart file + return {'status': {'code': int, 'text': string}, 'data': {info: object, rename: object}} ''' user = request.user f = get_object_or_404(models.File, oshash=request.POST['oshash']) - if f.frames.count() == 0 and 'frame' in request.FILES: - for frame in request.FILES['frame']: - name = frame.name - position = float(os.path.splitext(name)[0]) - fr = models.Frame(file=f, position=position) - fr.save() - fr.frame.save(frame, name) - response = json_response({}) - else: - response = json_response(status=403, text='permissino denied') + if 'frame' in request.FILES: + if f.frames.count() == 0: + for frame in request.FILES.getlist('frame'): + name = frame.name + #float required? + position = float(os.path.splitext(name)[0]) + fr = models.Frame(file=f, position=position) + fr.save() + fr.frame.save(name, frame) + response = json_response({}) + else: + response = json_response(status=403, text='permissino denied') + if 'file' in request.FILES: + if f.contents.count() == 0: + contents = models.FileContents(file=f) + contents.data = request.FILES['file'].read() + contents.save() + response = json_response({}) + else: + response = json_response(status=403, text='permissino denied') return render_to_json_response(response) class VideoChunkForm(forms.Form): @@ -198,7 +207,6 @@ def firefogg_upload(request): #post next chunk if 'chunk' in request.FILES and oshash: - print "all chunk now" stream = get_object_or_404(models.Stream, file__oshash=oshash, profile=profile) form = VideoChunkForm(request.POST, request.FILES) @@ -233,7 +241,6 @@ def firefogg_upload(request): 'result': 1 } return render_to_json_response(response) - print request.GET, request.POST response = json_response(status=400, text='this request requires POST') return render_to_json_response(response) diff --git a/pandora/backend/extract.py b/pandora/backend/extract.py index 5ea3418a..58f6fd92 100644 --- a/pandora/backend/extract.py +++ b/pandora/backend/extract.py @@ -73,3 +73,5 @@ def resize_image(image_source, image_output, width): resize_method = Image.BICUBIC output = source.resize((width, height), resize_method) output.save(image_output) + + diff --git a/pandora/backend/models.py b/pandora/backend/models.py index 847c15a3..1a06dce1 100644 --- a/pandora/backend/models.py +++ b/pandora/backend/models.py @@ -25,11 +25,6 @@ import load import utils import extract -def plural_key(term): - return { - 'country': 'countries', - }.get(term, term + 's') - def getMovie(info): ''' @@ -210,6 +205,7 @@ class Movie(models.Model): else: movie[pub_key] = value movie['poster'] = self.get_poster() + if fields: for f in fields: if f.endswith('.length') and f[:-7] in ('cast', 'genre', 'trivia'): @@ -231,6 +227,13 @@ class Movie(models.Model): self.get('series title', ''), self.get('episode title', ''), self.get('season', ''), self.get('episode', '')) + def streams(self): + streams = [] + for f in self.files.filter(is_main=True, available=True): + for s in f.streams.all(): + streams.append(s.video.url) + return streams + def frame(self, position, width=128): #FIXME: compute offset and so on f = self.files.all()[0] @@ -254,7 +257,7 @@ class Movie(models.Model): elif key == 'character': values = [i[1] for i in self.get('actor', [])] else: - values = self.get(plural_key(key), []) + values = self.get(utils.plural_key(key), []) setattr(f, key, '|%s|'%'|'.join(values)) f.summary = self.get('plot', '') + self.get('plot_outline', '') @@ -303,10 +306,10 @@ class Movie(models.Model): s.year = self.get('year', '') for key in self.person_keys: - setattr(s, key, sortNames(self.get(plural_key(key), []))) + setattr(s, key, sortNames(self.get(utils.plural_key(key), []))) for key in ('language', 'country'): - setattr(s, key, ','.join(self.get(plural_key(key), []))) + setattr(s, key, ','.join(self.get(utils.plural_key(key), []))) s.runtime = self.get('runtime', 0) @@ -351,7 +354,7 @@ class Movie(models.Model): elif key == 'character': current_values = [i[1] for i in self.get('actor', [])] else: - current_values = self.get(plural_key(key), []) + current_values = self.get(utils.plural_key(key), []) saved_values = [i.value for i in Facet.objects.filter(movie=self, key=key)] removed_values = filter(lambda x: x not in current_values, saved_values) if removed_values: @@ -625,7 +628,6 @@ class Layer(models.Model): return True return False - class Collection(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) diff --git a/pandora/backend/utils.py b/pandora/backend/utils.py index da398418..5a55af3d 100644 --- a/pandora/backend/utils.py +++ b/pandora/backend/utils.py @@ -12,6 +12,12 @@ import ox import ox.iso from ox.normalize import normalizeName, normalizeTitle + +def plural_key(term): + return { + 'country': 'countries', + }.get(term, term + 's') + def oxid(title, directors, year='', seriesTitle='', episodeTitle='', season=0, episode=0): director = ', '.join(directors) oxid_value = u"\n".join([title, director, year]) diff --git a/pandora/settings.py b/pandora/settings.py index d77084c6..c8b5af73 100644 --- a/pandora/settings.py +++ b/pandora/settings.py @@ -120,14 +120,18 @@ INSTALLED_APPS = ( AUTH_PROFILE_MODULE = 'oxuser.UserProfile' #Video encoding settings -VIDEO_PROFILE = 'low' # possible values low, mid or high -VIDEO_ENCODING = { - 'low': {'height': 96, 'videoBitrate': 180, 'softTarget': True, - 'samplerate': 44100, 'audioQuality': -1, 'channels': 1, 'noUpscaling': True}, - 'mid': {'maxSize': 320, 'videoBitrate': 500, - 'samplerate': 44100, 'audioQuality': 0, 'channels': 1, 'noUpscaling': True}, - 'high': {'profile': 'padma'} -} +#available profiles: 96p, 270p, 360p, 480p, 720p, 1080p + +VIDEO_PROFILE = '96p' +VIDEO_DERIVATIVES = [] +VIDEO_H264 = True + + +#Pad.ma +#VIDEO_PROFILE = '480p' +#VIDEO_DERIVATIVES = ['96p', '270p', '360p'] +#VIDEO_H264 = False + TRANSMISSON_HOST='localhost' TRANSMISSON_PORT=9091 diff --git a/pandora/templates/intro.html b/pandora/templates/intro.html new file mode 100644 index 00000000..b13c1378 --- /dev/null +++ b/pandora/templates/intro.html @@ -0,0 +1,10 @@ + + + + pan.do/ra + + + + this is a pan.do/ra development preview, click here to continue + + diff --git a/pandora/urls.py b/pandora/urls.py index 45b6493e..3c219a7c 100644 --- a/pandora/urls.py +++ b/pandora/urls.py @@ -13,7 +13,8 @@ urlpatterns = patterns('', (r'^api/', include('backend.urls')), (r'^api/upload/$', 'archive.views.firefogg_upload'), (r'^site.js$', 'app.views.site_js'), - (r'^$', 'app.views.index'), + (r'^$', 'app.views.intro'), + (r'^ra$', 'app.views.index'), (r'^r/(?P.*)$', 'oxuser.views.recover'), # Uncomment the admin/doc line below and add 'django.contrib.admindocs'