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 -*-
|
||||
# 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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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):
|
||||
'''
|
||||
|
|
|
@ -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
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue