subtitles

This commit is contained in:
j 2010-09-18 16:44:35 +02:00
parent c5b74c6f77
commit e64dd48ee2
4 changed files with 115 additions and 85 deletions

View file

@ -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

View file

@ -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:

View file

@ -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):
'''

View file

@ -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
});
});
});
}