forked from 0x2620/pandora
merge
This commit is contained in:
commit
c6f11433f9
7 changed files with 159 additions and 111 deletions
|
@ -109,12 +109,13 @@ class Annotation(models.Model):
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
def set_public_id(self):
|
def set_public_id(self):
|
||||||
public_id = Annotation.objects.filter(item=self.item, id__lt=self.id).count()
|
if self.id:
|
||||||
self.public_id = "%s/%s" % (self.item.itemId, ox.to26(public_id))
|
public_id = Annotation.objects.filter(item=self.item, id__lt=self.id).count()
|
||||||
Annotation.objects.filter(id=self.id).update(public_id=self.public_id)
|
self.public_id = "%s/%s" % (self.item.itemId, ox.to26(public_id))
|
||||||
|
Annotation.objects.filter(id=self.id).update(public_id=self.public_id)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
set_public_id = not self.id
|
set_public_id = not self.id or not self.public_id
|
||||||
|
|
||||||
#no clip or update clip
|
#no clip or update clip
|
||||||
if not self.clip and not self.layer.private or \
|
if not self.clip and not self.layer.private or \
|
||||||
|
@ -160,5 +161,5 @@ class Annotation(models.Model):
|
||||||
return j
|
return j
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return u"%s/%s-%s" %(self.item, self.start, self.end)
|
return u"%s %s-%s" %(self.public_id, self.start, self.end)
|
||||||
|
|
||||||
|
|
|
@ -294,11 +294,12 @@ def average_color(prefix, start=0, end=0):
|
||||||
timelines = sorted(filter(lambda t: t!= '%s%sp.png'%(prefix,height), glob("%s%sp*.png"%(prefix, height))))
|
timelines = sorted(filter(lambda t: t!= '%s%sp.png'%(prefix,height), glob("%s%sp*.png"%(prefix, height))))
|
||||||
for image in timelines:
|
for image in timelines:
|
||||||
start_offset = 0
|
start_offset = 0
|
||||||
timeline = Image.open(image).convert('RGB')
|
if start and frames + 1500 < start:
|
||||||
frames += timeline.size[0]
|
frames += 1500
|
||||||
if start and frames < start:
|
|
||||||
continue
|
continue
|
||||||
elif start and frames > start > frames-timeline.size[0]:
|
timeline = Image.open(image)
|
||||||
|
frames += timeline.size[0]
|
||||||
|
if start and frames > start > frames-timeline.size[0]:
|
||||||
start_offset = start - (frames-timeline.size[0])
|
start_offset = start - (frames-timeline.size[0])
|
||||||
box = (start_offset, 0, timeline.size[0], height)
|
box = (start_offset, 0, timeline.size[0], height)
|
||||||
timeline = timeline.crop(box)
|
timeline = timeline.crop(box)
|
||||||
|
@ -307,7 +308,7 @@ def average_color(prefix, start=0, end=0):
|
||||||
box = (0, 0, end_offset, height)
|
box = (0, 0, end_offset, height)
|
||||||
timeline = timeline.crop(box)
|
timeline = timeline.crop(box)
|
||||||
|
|
||||||
p = np.asarray(timeline, dtype=np.float32)
|
p = np.asarray(timeline.convert('RGB'), dtype=np.float32)
|
||||||
p = np.sum(p, axis=0) / height #average color per frame
|
p = np.sum(p, axis=0) / height #average color per frame
|
||||||
pixels.append(p)
|
pixels.append(p)
|
||||||
|
|
||||||
|
|
37
pandora/item/management/commands/update_external.py
Normal file
37
pandora/item/management/commands/update_external.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vi:si:et:sw=4:sts=4:ts=4
|
||||||
|
from optparse import make_option
|
||||||
|
|
||||||
|
import os
|
||||||
|
from os.path import join, dirname, basename, splitext, exists
|
||||||
|
|
||||||
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
import monkey_patch.models
|
||||||
|
from ... import models
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
"""
|
||||||
|
rebuild sort/search cache for all items.
|
||||||
|
"""
|
||||||
|
help = 'listen to rabbitmq and execute encoding tasks.'
|
||||||
|
args = ''
|
||||||
|
option_list = BaseCommand.option_list + (
|
||||||
|
make_option('--all', action='store_true', dest='all',
|
||||||
|
default=False, help='update all items, otherwise oldes N'),
|
||||||
|
make_option('-n', '--items', action='store', dest='items', type=int,
|
||||||
|
default=30, help='number of items ot update'),
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle(self, **options):
|
||||||
|
offset = 0
|
||||||
|
chunk = options['all'] and 100 or options['items']
|
||||||
|
count = pos = models.Item.objects.count()
|
||||||
|
while options['all'] and offset <= count or offset < options['items']:
|
||||||
|
for i in models.Item.objects.all().order_by('modified')[offset:offset+chunk]:
|
||||||
|
print pos, i.itemId, i.modified
|
||||||
|
i.update_external()
|
||||||
|
pos -= 1
|
||||||
|
offset += chunk
|
|
@ -13,7 +13,7 @@ import uuid
|
||||||
import unicodedata
|
import unicodedata
|
||||||
from urllib import quote
|
from urllib import quote
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models, transaction
|
||||||
from django.db.models import Count, Q, Sum
|
from django.db.models import Count, Q, Sum
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -340,6 +340,8 @@ class Item(models.Model):
|
||||||
#FIXME: should this really happen for annotations?
|
#FIXME: should this really happen for annotations?
|
||||||
for a in self.annotations.all():
|
for a in self.annotations.all():
|
||||||
a.item = other
|
a.item = other
|
||||||
|
a.public_id = None
|
||||||
|
a.save()
|
||||||
|
|
||||||
if hasattr(self, 'files'):
|
if hasattr(self, 'files'):
|
||||||
for f in self.files.all():
|
for f in self.files.all():
|
||||||
|
@ -516,45 +518,46 @@ class Item(models.Model):
|
||||||
else:
|
else:
|
||||||
ItemFind.objects.filter(item=self, key=key).delete()
|
ItemFind.objects.filter(item=self, key=key).delete()
|
||||||
|
|
||||||
for key in settings.CONFIG['itemKeys']:
|
with transaction.commit_on_success():
|
||||||
i = key['id']
|
for key in settings.CONFIG['itemKeys']:
|
||||||
if i == 'title':
|
i = key['id']
|
||||||
save(i, u'\n'.join([self.get('title', 'Untitled'),
|
if i == 'title':
|
||||||
self.get('originalTitle', '')]))
|
save(i, u'\n'.join([self.get('title', 'Untitled'),
|
||||||
elif i == 'rightslevel':
|
self.get('originalTitle', '')]))
|
||||||
save(i, self.level)
|
elif i == 'rightslevel':
|
||||||
elif i == 'filename':
|
save(i, self.level)
|
||||||
save(i,
|
elif i == 'filename':
|
||||||
'\n'.join([f.path for f in self.files.all()]))
|
save(i,
|
||||||
elif key['type'] == 'layer':
|
'\n'.join([f.path for f in self.files.all()]))
|
||||||
qs = Annotation.objects.filter(layer__name=i, item=self).order_by('start')
|
elif key['type'] == 'layer':
|
||||||
save(i, '\n'.join([l.value for l in qs]))
|
qs = Annotation.objects.filter(layer__name=i, item=self).order_by('start')
|
||||||
elif i != '*' and i not in self.facet_keys:
|
save(i, '\n'.join([l.value for l in qs]))
|
||||||
value = self.get(i)
|
elif i != '*' and i not in self.facet_keys:
|
||||||
if isinstance(value, list):
|
value = self.get(i)
|
||||||
value = u'\n'.join(value)
|
if isinstance(value, list):
|
||||||
save(i, value)
|
value = u'\n'.join(value)
|
||||||
|
save(i, value)
|
||||||
|
|
||||||
for key in self.facet_keys:
|
for key in self.facet_keys:
|
||||||
if key == 'character':
|
if key == 'character':
|
||||||
values = self.get('cast', '')
|
values = self.get('cast', '')
|
||||||
if values:
|
if values:
|
||||||
values = filter(lambda x: x.strip(),
|
values = filter(lambda x: x.strip(),
|
||||||
[f['character'] for f in values])
|
[f['character'] for f in values])
|
||||||
|
values = list(set(values))
|
||||||
|
elif key == 'name':
|
||||||
|
values = []
|
||||||
|
for k in map(lambda x: x['id'],
|
||||||
|
filter(lambda x: x.get('sort') == 'person',
|
||||||
|
settings.CONFIG['itemKeys'])):
|
||||||
|
values += self.get(k, [])
|
||||||
values = list(set(values))
|
values = list(set(values))
|
||||||
elif key == 'name':
|
else:
|
||||||
values = []
|
values = self.get(key, '')
|
||||||
for k in map(lambda x: x['id'],
|
if isinstance(values, list):
|
||||||
filter(lambda x: x.get('sort') == 'person',
|
save(key, '\n'.join(values))
|
||||||
settings.CONFIG['itemKeys'])):
|
else:
|
||||||
values += self.get(k, [])
|
save(key, values)
|
||||||
values = list(set(values))
|
|
||||||
else:
|
|
||||||
values = self.get(key, '')
|
|
||||||
if isinstance(values, list):
|
|
||||||
save(key, '\n'.join(values))
|
|
||||||
else:
|
|
||||||
save(key, values)
|
|
||||||
|
|
||||||
def update_sort(self):
|
def update_sort(self):
|
||||||
try:
|
try:
|
||||||
|
@ -933,7 +936,7 @@ class Item(models.Model):
|
||||||
|
|
||||||
def make_timeline(self):
|
def make_timeline(self):
|
||||||
streams = self.streams()
|
streams = self.streams()
|
||||||
if len(streams) > 1:
|
if streams.count() > 1:
|
||||||
timelines = [s.timeline_prefix for s in self.streams()]
|
timelines = [s.timeline_prefix for s in self.streams()]
|
||||||
join_timelines(timelines, self.timeline_prefix)
|
join_timelines(timelines, self.timeline_prefix)
|
||||||
|
|
||||||
|
@ -1041,61 +1044,63 @@ class Item(models.Model):
|
||||||
return icon
|
return icon
|
||||||
|
|
||||||
def load_subtitles(self):
|
def load_subtitles(self):
|
||||||
layer = Layer.objects.get(name='subtitles')
|
with transaction.commit_on_success():
|
||||||
Annotation.objects.filter(layer=layer,item=self).delete()
|
layer = Layer.objects.get(name='subtitles')
|
||||||
offset = 0
|
Annotation.objects.filter(layer=layer,item=self).delete()
|
||||||
language = ''
|
offset = 0
|
||||||
subtitles = self.files.filter(selected=True, is_subtitle=True, available=True)
|
language = ''
|
||||||
languages = [f.language for f in subtitles]
|
subtitles = self.files.filter(selected=True, is_subtitle=True, available=True)
|
||||||
if languages:
|
languages = [f.language for f in subtitles]
|
||||||
if 'en' in languages:
|
if languages:
|
||||||
language = 'en'
|
if 'en' in languages:
|
||||||
elif '' in languages:
|
language = 'en'
|
||||||
language = ''
|
elif '' in languages:
|
||||||
else:
|
language = ''
|
||||||
language = languages[0]
|
else:
|
||||||
|
language = languages[0]
|
||||||
|
|
||||||
#loop over all videos
|
#loop over all videos
|
||||||
for f in self.files.filter(Q(is_audio=True)|Q(is_video=True)) \
|
for f in self.files.filter(Q(is_audio=True)|Q(is_video=True)) \
|
||||||
.filter(selected=True).order_by('part'):
|
.filter(selected=True).order_by('part'):
|
||||||
subtitles_added = False
|
subtitles_added = False
|
||||||
prefix = os.path.splitext(f.path)[0]
|
prefix = os.path.splitext(f.path)[0]
|
||||||
if f.instances.all().count() > 0:
|
if f.instances.all().count() > 0:
|
||||||
user = f.instances.all()[0].volume.user
|
user = f.instances.all()[0].volume.user
|
||||||
else:
|
else:
|
||||||
#FIXME: allow annotations from no user instead?
|
#FIXME: allow annotations from no user instead?
|
||||||
user = User.objects.all().order_by('id')[0]
|
user = User.objects.all().order_by('id')[0]
|
||||||
#if there is a subtitle with the same prefix, import
|
#if there is a subtitle with the same prefix, import
|
||||||
q = subtitles.filter(path__startswith=prefix,
|
q = subtitles.filter(path__startswith=prefix,
|
||||||
language=language)
|
language=language)
|
||||||
if q.count() == 1:
|
if q.count() == 1:
|
||||||
s = q[0]
|
s = q[0]
|
||||||
for data in s.srt(offset):
|
for data in s.srt(offset):
|
||||||
subtitles_added = True
|
subtitles_added = True
|
||||||
annotation = Annotation(
|
annotation = Annotation(
|
||||||
item=self,
|
item=self,
|
||||||
layer=layer,
|
layer=layer,
|
||||||
start=data['in'],
|
start=data['in'],
|
||||||
end=data['out'],
|
end=data['out'],
|
||||||
value=data['value'],
|
value=data['value'],
|
||||||
user=user
|
user=user
|
||||||
)
|
)
|
||||||
annotation.save()
|
annotation.save()
|
||||||
#otherwise add empty 5 seconds annotation every minute
|
#otherwise add empty 5 seconds annotation every minute
|
||||||
if not subtitles_added:
|
if not subtitles_added:
|
||||||
for i in range(int (offset / 60) * 60 + 60,
|
start = offset and int (offset / 60) * 60 + 60 or 0
|
||||||
int(offset + f.duration) - 5,
|
for i in range(start,
|
||||||
60):
|
int(offset + f.duration) - 5,
|
||||||
annotation = Annotation(
|
60):
|
||||||
item=self,
|
annotation = Annotation(
|
||||||
layer=layer,
|
item=self,
|
||||||
start=i,
|
layer=layer,
|
||||||
end=i + 5,
|
start=i,
|
||||||
value='',
|
end=i + 5,
|
||||||
user=user
|
value='',
|
||||||
)
|
user=user
|
||||||
annotation.save()
|
)
|
||||||
offset += f.duration
|
annotation.save()
|
||||||
|
offset += f.duration
|
||||||
self.update_find()
|
self.update_find()
|
||||||
|
|
||||||
def delete_item(sender, **kwargs):
|
def delete_item(sender, **kwargs):
|
||||||
|
|
|
@ -154,8 +154,9 @@ pandora.ui.clipList = function(videoRatio) {
|
||||||
poster: '/' + item + '/' + height + 'p' + points[0] + '.jpg',
|
poster: '/' + item + '/' + height + 'p' + points[0] + '.jpg',
|
||||||
rewind: true,
|
rewind: true,
|
||||||
video: partsAndPoints.parts.map(function(i) {
|
video: partsAndPoints.parts.map(function(i) {
|
||||||
return '/' + item + '/96p' + (i + 1)
|
var part = (i + 1),
|
||||||
+ '.' + pandora.user.videoFormat;
|
prefix = pandora.site.site.videoprefix.replace('PART', part);
|
||||||
|
return prefix + '/' + item + '/96p' + part + '.' + pandora.user.videoFormat;
|
||||||
}),
|
}),
|
||||||
width: width
|
width: width
|
||||||
})
|
})
|
||||||
|
|
|
@ -35,17 +35,19 @@ pandora.ui.clipPlayer = function() {
|
||||||
length = range[1] - range[0],
|
length = range[1] - range[0],
|
||||||
data = [];
|
data = [];
|
||||||
result.data.items.forEach(function(item, i) {
|
result.data.items.forEach(function(item, i) {
|
||||||
var id = item.id.split('/')[0]
|
var id = item.id.split('/')[0];
|
||||||
pandora.api.get({id: id, keys: ['durations']}, function(result) {
|
pandora.api.get({id: id, keys: ['durations']}, function(result) {
|
||||||
//Ox.print('API get item', id, 'result', result.data);
|
//Ox.print('API get item', id, 'result', result.data);
|
||||||
var points = [item['in'], item.out],
|
var points = [item['in'], item.out],
|
||||||
partsAndPoints = pandora.getVideoPartsAndPoints(result.data.durations, points);
|
partsAndPoints = pandora.getVideoPartsAndPoints(result.data.durations, points);
|
||||||
data[i] = {
|
data[i] = {
|
||||||
parts: partsAndPoints.parts.map(function(i) {
|
parts: partsAndPoints.parts.map(function(i) {
|
||||||
return '/' + id + '/96p' + (i + 1) + '.' + pandora.user.videoFormat;
|
var part = (i + 1),
|
||||||
|
prefix = pandora.site.site.videoprefix.replace('PART', part);
|
||||||
|
return prefix + '/' + id + '/96p' + part + '.' + pandora.user.videoFormat;
|
||||||
}),
|
}),
|
||||||
points: partsAndPoints.points
|
points: partsAndPoints.points
|
||||||
}
|
};
|
||||||
if (++counter == length) {
|
if (++counter == length) {
|
||||||
callback(data);
|
callback(data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,8 +91,9 @@ pandora.ui.itemClips = function(options) {
|
||||||
poster: '/' + self.options.id + '/' + self.height + 'p' + points[0] + '.jpg',
|
poster: '/' + self.options.id + '/' + self.height + 'p' + points[0] + '.jpg',
|
||||||
rewind: true,
|
rewind: true,
|
||||||
video: partsAndPoints.parts.map(function(i) {
|
video: partsAndPoints.parts.map(function(i) {
|
||||||
return '/' + self.options.id + '/96p' + (i + 1)
|
var part = (i + 1),
|
||||||
+ '.' + pandora.user.videoFormat;
|
prefix = pandora.site.site.videoprefix.replace('PART', part);
|
||||||
|
return prefix + '/' + self.options.id + '/96p' + part + '.' + pandora.user.videoFormat;
|
||||||
}),
|
}),
|
||||||
width: self.width
|
width: self.width
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue