From e64dd48ee25bbf7293f59abbe79a62e6a45cb48f Mon Sep 17 00:00:00 2001 From: j <0x006A@0x2620.org> Date: Sat, 18 Sep 2010 16:44:35 +0200 Subject: [PATCH] subtitles --- pandora/archive/models.py | 61 ++++++++++++++++++++++++++ pandora/backend/models.py | 10 +++++ pandora/backend/views.py | 44 ++----------------- pandora/static/js/pandora.js | 85 +++++++++++++++++------------------- 4 files changed, 115 insertions(+), 85 deletions(-) diff --git a/pandora/archive/models.py b/pandora/archive/models.py index 21ef4862f..7633e089b 100644 --- a/pandora/archive/models.py +++ b/pandora/archive/models.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 +from __future__ import division from datetime import datetime import os.path import random @@ -19,6 +20,7 @@ import ox from ox import stripTags from ox.normalize import canonicalTitle, canonicalName from firefogg import Firefogg +import chardet from backend import utils from pandora.backend.models import Movie @@ -149,6 +151,65 @@ class File(models.Model): return self.data.read() return None + def srt(self): + def _detectEncoding(fp): + bomDict={ # bytepattern : name + (0x00, 0x00, 0xFE, 0xFF) : "utf_32_be", + (0xFF, 0xFE, 0x00, 0x00) : "utf_32_le", + (0xFE, 0xFF, None, None) : "utf_16_be", + (0xFF, 0xFE, None, None) : "utf_16_le", + (0xEF, 0xBB, 0xBF, None) : "utf_8", + } + + # go to beginning of file and get the first 4 bytes + oldFP = fp.tell() + fp.seek(0) + (byte1, byte2, byte3, byte4) = tuple(map(ord, fp.read(4))) + + # try bom detection using 4 bytes, 3 bytes, or 2 bytes + bomDetection = bomDict.get((byte1, byte2, byte3, byte4)) + if not bomDetection : + bomDetection = bomDict.get((byte1, byte2, byte3, None)) + if not bomDetection : + bomDetection = bomDict.get((byte1, byte2, None, None)) + + ## if BOM detected, we're done :-) + fp.seek(oldFP) + if bomDetection : + return bomDetection + + encoding = 'latin-1' + #more character detecting magick using http://chardet.feedparser.org/ + fp.seek(0) + rawdata = fp.read() + encoding = chardet.detect(rawdata)['encoding'] + fp.seek(oldFP) + return encoding + + def parseTime(t): + return ox.time2ms(t.replace(',', '.')) / 1000 + + srt = [] + + f = open(self.data.path) + encoding = _detectEncoding(f) + data = f.read() + f.close() + try: + data = unicode(data, encoding) + except: + try: + data = unicode(data, 'latin-1') + except: + print "failed to detect encoding, giving up" + return srt + + srts = re.compile('(\d\d:\d\d:\d\d[,.]\d\d\d)\s*-->\s*(\d\d:\d\d:\d\d[,.]\d\d\d)\s*(.+?)\n\n', re.DOTALL) + for s in srts.findall(data): + _s = {'in': parseTime(s[0]), 'out': parseTime(s[1]), 'text': s[2].strip()} + srt.append(_s) + return srt + def editable(self, user): #FIXME: check that user has instance of this file return True diff --git a/pandora/backend/models.py b/pandora/backend/models.py index 6af279cd6..4b62ea12b 100644 --- a/pandora/backend/models.py +++ b/pandora/backend/models.py @@ -270,6 +270,16 @@ class Movie(models.Model): stream['profiles'] = list(set(map(lambda s: int(os.path.splitext(s['profile'])[0][:-1]), self.streams.all().values('profile')))) return stream + def get_layers(self): + layers = {} + layers['cuts'] = self.metadata.get('cuts', {}) + + layers['subtitles'] = {} + qs = self.files.filter(is_subtitle=True, is_main=True, available=True) + if qs.count()>0: + layers['subtitles'] = qs[0].srt() + return layers + def get_json(self, fields=None): movie = {} for key in self._public_fields: diff --git a/pandora/backend/views.py b/pandora/backend/views.py index adf3418a0..899cc15ab 100644 --- a/pandora/backend/views.py +++ b/pandora/backend/views.py @@ -279,7 +279,10 @@ def api_getItem(request): itemId = json.loads(request.POST['data']) item = get_object_or_404_json(models.Movie, movieId=itemId) #FIXME: check permissions - response['data'] = {'item': item.get_json()} + info = item.get_json() + info['stream'] = item.get_stream() + info['layers'] = item.get_layers() + response['data'] = {'item': info} return render_to_json_response(response) @login_required_json @@ -316,45 +319,6 @@ def api_removeItem(request): response = json_response(status=403, text='permissino denied') return render_to_json_response(response) -def api_info(request): - ''' - param data - string id - - return {'status': {'code': int, 'text': string}, 'data': {'info': {}}} - ''' - response = json_response({}) - itemId = json.loads(request.POST['data']) - item = get_object_or_404_json(models.Movie, movieId=itemId) - response['data'] = {'info': movie.get_stream()} - return render_to_json_response(response) - -def api_subtitles(request): - ''' - param data - string id - - return {'status': {'code': int, 'text': string}, 'data': {'subtitles': {}}} - ''' - response = json_response({}) - itemId = json.loads(request.POST['data']) - item = get_object_or_404_json(models.Movie, movieId=itemId) - response['data'] = {'subtitles': movie.metadata.get('subtitles', {})} - return render_to_json_response(response) - -def api_cuts(request): - ''' - param data - string id - - return {'status': {'code': int, 'text': string}, 'data': {'cuts': {}}} - ''' - response = json_response({}) - itemId = json.loads(request.POST['data']) - item = get_object_or_404_json(models.Movie, movieId=itemId) - response['data'] = {'cuts': movie.metadata.get('cuts', {})} - return render_to_json_response(response) - @login_required_json def api_addLayer(request): ''' diff --git a/pandora/static/js/pandora.js b/pandora/static/js/pandora.js index 0040970f5..a0ed1a72a 100755 --- a/pandora/static/js/pandora.js +++ b/pandora/static/js/pandora.js @@ -518,57 +518,52 @@ app.constructItem = function(id, view) { app.$ui.contentPanel.resize(0, 80); app.$ui.groupsOuterPanel.empty(); if (view == 'timeline') { - // fixme: this should be one app request, not three getJSONs - $.getJSON('/' + id + '/data/video.json', function(data) { - var video = data; - video.height = 96; - video.width = parseInt(video.height * video.aspectRatio / 2) * 2; - video.url = video.baseUrl + '/' + video.height + 'p.' + ($.support.video.webm ? 'webm' : 'mp4'); - $.getJSON('/' + id + '/data/subtitles.json', function(data) { - var subtitles = data; - subtitles = [{ + app.api.getItem(id, function(result) { + item_debug = result.data.item; + var video = result.data.item.stream, + cuts = result.data.item.layers.cuts || {}, + subtitles = result.data.item.layers.subtitles || [{ 'in': 5, 'out': 10, 'text': 'This subtitle is just a test...' }]; - $.getJSON('/' + id + '/data/cuts.json', function(data) { - var cuts = data; - $item = new Ox.VideoEditor({ - cuts: cuts, - duration: video.duration, - find: '', - frameURL: function(position) { - return '/' + id + '/frame/' + video.width.toString() + '/' + position.toString() + '.jpg' - }, - id: 'editor', - largeTimeline: true, - matches: [], - points: [0, 0], - position: 0, - posterFrame: parseInt(video.duration / 2), - subtitles: subtitles, - videoHeight: video.height, - videoId: id, - videoWidth: video.width, - videoSize: 'small', - videoURL: video.url, - width: app.$document.width() - app.$ui.leftPanel.width() - 1 - 256 - 1 - }); - app.$ui.contentPanel.replace(1, $item); - app.$ui.rightPanel - /*.unbindEvent('resize')*/ - .bindEvent('resize', function(event, data) { - Ox.print('seems to work', data) - $item.options({ - width: data - 256 - 1 - }); - }); - app.$window.resize(function() { - $item.options({ - width: app.$document.width() - app.$ui.leftPanel.width() - 1 - 256 - 1 - }); + video.height = 96; + video.width = parseInt(video.height * video.aspectRatio / 2) * 2; + video.url = video.baseUrl + '/' + video.height + 'p.' + ($.support.video.webm ? 'webm' : 'mp4'); + $item = new Ox.VideoEditor({ + cuts: cuts, + duration: video.duration, + find: '', + frameURL: function(position) { + return '/' + id + '/frame/' + video.width.toString() + '/' + position.toString() + '.jpg' + }, + id: 'editor', + largeTimeline: true, + matches: [], + points: [0, 0], + position: 0, + posterFrame: parseInt(video.duration / 2), + subtitles: subtitles, + videoHeight: video.height, + videoId: id, + videoWidth: video.width, + videoSize: 'small', + videoURL: video.url, + width: app.$document.width() - app.$ui.leftPanel.width() - 1 - 256 - 1 + }); + app.$ui.contentPanel.replace(1, $item); + app.$ui.rightPanel + /*.unbindEvent('resize')*/ + .bindEvent('resize', function(event, data) { + Ox.print('seems to work', data) + $item.options({ + width: data - 256 - 1 }); }); + app.$window.resize(function() { + $item.options({ + width: app.$document.width() - app.$ui.leftPanel.width() - 1 - 256 - 1 + }); }); }); }