subtitles
This commit is contained in:
parent
c5b74c6f77
commit
e64dd48ee2
4 changed files with 115 additions and 85 deletions
|
@ -1,5 +1,6 @@
|
||||||
# -*- 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
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import os.path
|
import os.path
|
||||||
import random
|
import random
|
||||||
|
@ -19,6 +20,7 @@ import ox
|
||||||
from ox import stripTags
|
from ox import stripTags
|
||||||
from ox.normalize import canonicalTitle, canonicalName
|
from ox.normalize import canonicalTitle, canonicalName
|
||||||
from firefogg import Firefogg
|
from firefogg import Firefogg
|
||||||
|
import chardet
|
||||||
|
|
||||||
from backend import utils
|
from backend import utils
|
||||||
from pandora.backend.models import Movie
|
from pandora.backend.models import Movie
|
||||||
|
@ -149,6 +151,65 @@ class File(models.Model):
|
||||||
return self.data.read()
|
return self.data.read()
|
||||||
return None
|
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):
|
def editable(self, user):
|
||||||
#FIXME: check that user has instance of this file
|
#FIXME: check that user has instance of this file
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -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'))))
|
stream['profiles'] = list(set(map(lambda s: int(os.path.splitext(s['profile'])[0][:-1]), self.streams.all().values('profile'))))
|
||||||
return stream
|
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):
|
def get_json(self, fields=None):
|
||||||
movie = {}
|
movie = {}
|
||||||
for key in self._public_fields:
|
for key in self._public_fields:
|
||||||
|
|
|
@ -279,7 +279,10 @@ def api_getItem(request):
|
||||||
itemId = json.loads(request.POST['data'])
|
itemId = json.loads(request.POST['data'])
|
||||||
item = get_object_or_404_json(models.Movie, movieId=itemId)
|
item = get_object_or_404_json(models.Movie, movieId=itemId)
|
||||||
#FIXME: check permissions
|
#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)
|
return render_to_json_response(response)
|
||||||
|
|
||||||
@login_required_json
|
@login_required_json
|
||||||
|
@ -316,45 +319,6 @@ def api_removeItem(request):
|
||||||
response = json_response(status=403, text='permissino denied')
|
response = json_response(status=403, text='permissino denied')
|
||||||
return render_to_json_response(response)
|
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
|
@login_required_json
|
||||||
def api_addLayer(request):
|
def api_addLayer(request):
|
||||||
'''
|
'''
|
||||||
|
|
|
@ -518,57 +518,52 @@ app.constructItem = function(id, view) {
|
||||||
app.$ui.contentPanel.resize(0, 80);
|
app.$ui.contentPanel.resize(0, 80);
|
||||||
app.$ui.groupsOuterPanel.empty();
|
app.$ui.groupsOuterPanel.empty();
|
||||||
if (view == 'timeline') {
|
if (view == 'timeline') {
|
||||||
// fixme: this should be one app request, not three getJSONs
|
app.api.getItem(id, function(result) {
|
||||||
$.getJSON('/' + id + '/data/video.json', function(data) {
|
item_debug = result.data.item;
|
||||||
var video = data;
|
var video = result.data.item.stream,
|
||||||
video.height = 96;
|
cuts = result.data.item.layers.cuts || {},
|
||||||
video.width = parseInt(video.height * video.aspectRatio / 2) * 2;
|
subtitles = result.data.item.layers.subtitles || [{
|
||||||
video.url = video.baseUrl + '/' + video.height + 'p.' + ($.support.video.webm ? 'webm' : 'mp4');
|
|
||||||
$.getJSON('/' + id + '/data/subtitles.json', function(data) {
|
|
||||||
var subtitles = data;
|
|
||||||
subtitles = [{
|
|
||||||
'in': 5,
|
'in': 5,
|
||||||
'out': 10,
|
'out': 10,
|
||||||
'text': 'This subtitle is just a test...'
|
'text': 'This subtitle is just a test...'
|
||||||
}];
|
}];
|
||||||
$.getJSON('/' + id + '/data/cuts.json', function(data) {
|
video.height = 96;
|
||||||
var cuts = data;
|
video.width = parseInt(video.height * video.aspectRatio / 2) * 2;
|
||||||
$item = new Ox.VideoEditor({
|
video.url = video.baseUrl + '/' + video.height + 'p.' + ($.support.video.webm ? 'webm' : 'mp4');
|
||||||
cuts: cuts,
|
$item = new Ox.VideoEditor({
|
||||||
duration: video.duration,
|
cuts: cuts,
|
||||||
find: '',
|
duration: video.duration,
|
||||||
frameURL: function(position) {
|
find: '',
|
||||||
return '/' + id + '/frame/' + video.width.toString() + '/' + position.toString() + '.jpg'
|
frameURL: function(position) {
|
||||||
},
|
return '/' + id + '/frame/' + video.width.toString() + '/' + position.toString() + '.jpg'
|
||||||
id: 'editor',
|
},
|
||||||
largeTimeline: true,
|
id: 'editor',
|
||||||
matches: [],
|
largeTimeline: true,
|
||||||
points: [0, 0],
|
matches: [],
|
||||||
position: 0,
|
points: [0, 0],
|
||||||
posterFrame: parseInt(video.duration / 2),
|
position: 0,
|
||||||
subtitles: subtitles,
|
posterFrame: parseInt(video.duration / 2),
|
||||||
videoHeight: video.height,
|
subtitles: subtitles,
|
||||||
videoId: id,
|
videoHeight: video.height,
|
||||||
videoWidth: video.width,
|
videoId: id,
|
||||||
videoSize: 'small',
|
videoWidth: video.width,
|
||||||
videoURL: video.url,
|
videoSize: 'small',
|
||||||
width: app.$document.width() - app.$ui.leftPanel.width() - 1 - 256 - 1
|
videoURL: video.url,
|
||||||
});
|
width: app.$document.width() - app.$ui.leftPanel.width() - 1 - 256 - 1
|
||||||
app.$ui.contentPanel.replace(1, $item);
|
});
|
||||||
app.$ui.rightPanel
|
app.$ui.contentPanel.replace(1, $item);
|
||||||
/*.unbindEvent('resize')*/
|
app.$ui.rightPanel
|
||||||
.bindEvent('resize', function(event, data) {
|
/*.unbindEvent('resize')*/
|
||||||
Ox.print('seems to work', data)
|
.bindEvent('resize', function(event, data) {
|
||||||
$item.options({
|
Ox.print('seems to work', data)
|
||||||
width: data - 256 - 1
|
$item.options({
|
||||||
});
|
width: data - 256 - 1
|
||||||
});
|
|
||||||
app.$window.resize(function() {
|
|
||||||
$item.options({
|
|
||||||
width: app.$document.width() - app.$ui.leftPanel.width() - 1 - 256 - 1
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
app.$window.resize(function() {
|
||||||
|
$item.options({
|
||||||
|
width: app.$document.width() - app.$ui.leftPanel.width() - 1 - 256 - 1
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue