pandora/pandora/item/models.py

1341 lines
50 KiB
Python
Raw Normal View History

2009-06-08 16:08:59 +00:00
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
2010-08-07 14:31:20 +00:00
from __future__ import division, with_statement
2010-02-03 12:05:38 +00:00
from datetime import datetime
import math
2009-06-08 16:08:59 +00:00
import os.path
2011-08-05 15:50:18 +00:00
import re
2010-09-03 13:28:44 +00:00
import subprocess
from glob import glob
2011-04-18 18:50:31 +00:00
import shutil
2011-01-28 08:48:38 +00:00
import uuid
import unicodedata
2011-07-03 16:21:27 +00:00
from urllib import quote
2011-10-29 14:59:12 +00:00
from django.db import models, transaction
2011-11-11 17:45:46 +00:00
from django.db.models import Count, Q, Sum, Max
2009-12-31 15:04:32 +00:00
from django.core.files.base import ContentFile
from django.conf import settings
2011-01-21 09:31:49 +00:00
from django.contrib.auth.models import User, Group
2011-04-18 18:50:31 +00:00
from django.db.models.signals import pre_delete
2011-07-03 16:21:27 +00:00
from django.contrib.sites.models import Site
2010-07-07 22:46:41 +00:00
import ox
2011-01-01 11:44:42 +00:00
from ox.django import fields
import ox.web.imdb
2011-08-16 15:06:40 +00:00
import ox.image
2009-06-08 16:08:59 +00:00
2009-08-01 14:14:54 +00:00
import managers
2010-02-03 12:05:38 +00:00
import utils
import tasks
2011-08-20 17:53:26 +00:00
from .timelines import join_timelines
from data_api import external_data
2009-06-08 16:08:59 +00:00
2011-08-20 17:53:26 +00:00
from archive import extract
2011-11-02 14:06:34 +00:00
from annotation.models import Annotation
2011-11-02 14:27:02 +00:00
from clip.models import Clip
2011-08-23 17:39:34 +00:00
import archive.models
2011-01-01 11:44:42 +00:00
from person.models import get_name_sort
2011-10-11 11:29:05 +00:00
from title.models import get_title_sort
def get_id(info):
q = Item.objects.all()
for key in ('title', 'director', 'year'):
# 'episodeTitle', 'episodeDirector', 'episodeYear', 'season', 'episode'):
if key in info and info[key]:
k = 'find__key'
v = 'find__value'
if key in Item.facet_keys + ['title']:
k = 'facets__key'
v = 'facets__value'
if isinstance(info[key], list):
for value in info[key]:
q = q.filter(**{k: key, v: value})
else:
q = q.filter(**{k:key, v:info[key]})
if q.count() == 1:
return q[0].itemId
if settings.DATA_SERVICE:
r = external_data('getId', info)
if r['status']['code'] == 200:
2011-10-18 20:06:01 +00:00
imdbId = r['data']['id']
return imdbId
return None
def get_item(info, user=None, async=False):
2010-01-16 20:42:11 +00:00
'''
info dict with:
imdbId, title, director, year,
season, episode, episodeTitle, episodeDirector, episodeYear
2010-01-16 20:42:11 +00:00
'''
item_data = {
2011-10-17 19:43:42 +00:00
'title': info.get('title', ''),
'director': info.get('director', []),
'year': info.get('year', '')
}
for key in ('episodeTitle', 'episodeDirector', 'episodeYear',
'season', 'episode', 'seriesTitle'):
if key in info and info[key]:
item_data[key] = info[key]
2011-01-16 13:28:57 +00:00
if settings.USE_IMDB:
if 'imdbId' in info and info['imdbId']:
try:
2011-01-16 13:28:57 +00:00
item = Item.objects.get(itemId=info['imdbId'])
2010-09-23 16:01:48 +00:00
except Item.DoesNotExist:
2011-01-16 13:28:57 +00:00
item = Item(itemId=info['imdbId'])
if 'title' in info and 'director' in info:
item.external_data = item_data
2011-02-22 16:09:13 +00:00
item.user = user
2011-09-28 12:47:13 +00:00
item.oxdbId = item.itemId
2011-01-16 13:28:57 +00:00
item.save()
if async:
tasks.update_external.delay(item.itemId)
else:
item.update_external()
2011-01-16 13:28:57 +00:00
else:
itemId = get_id(info)
if itemId:
try:
item = Item.objects.get(itemId=itemId)
except Item.DoesNotExist:
info['imdbId'] = itemId
item = get_item(info)
return item
try:
item = Item.objects.get(itemId=info.get('oxdbId'))
except Item.DoesNotExist:
2011-07-05 14:28:22 +00:00
item = Item()
item.user = user
item.data = item_data
item.itemId = info.get('oxdbId', item.oxdb_id())
try:
existing_item = Item.objects.get(oxdbId=item.oxdb_id())
item = existing_item
except Item.DoesNotExist:
item.oxdbId = item.oxdb_id()
item.save()
2011-12-03 11:44:50 +00:00
tasks.update_poster.delay(item.itemId)
2011-01-16 13:28:57 +00:00
else:
2011-01-28 08:48:38 +00:00
qs = Item.objects.filter(find__key='title', find__value=info['title'])
if qs.count() == 1:
item = qs[0]
else:
item = Item()
item.data = item_data
2011-02-22 16:09:13 +00:00
item.user = user
2011-01-16 13:28:57 +00:00
item.save()
2011-12-03 11:44:50 +00:00
tasks.update_poster.delay(item.itemId)
2010-09-23 16:01:48 +00:00
return item
2010-01-16 20:42:11 +00:00
2010-09-23 16:01:48 +00:00
class Item(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
2009-06-08 16:08:59 +00:00
2011-02-22 16:09:13 +00:00
user = models.ForeignKey(User, null=True, related_name='items')
2011-02-23 11:51:32 +00:00
groups = models.ManyToManyField(Group, blank=True, related_name='items')
2011-01-21 09:31:49 +00:00
2011-06-27 13:39:35 +00:00
#while metadata is updated, files are set to rendered=False
rendered = models.BooleanField(default=False, db_index=True)
2011-09-16 17:17:49 +00:00
#should be set based on user
2011-12-27 06:54:49 +00:00
level = models.IntegerField(db_index=True)
2011-01-21 09:31:49 +00:00
2010-09-23 16:01:48 +00:00
itemId = models.CharField(max_length=128, unique=True, blank=True)
2011-04-05 10:49:58 +00:00
oxdbId = models.CharField(max_length=42, unique=True, blank=True, null=True)
2010-11-27 02:33:31 +00:00
external_data = fields.DictField(default={}, editable=False)
data = fields.DictField(default={}, editable=False)
json = fields.DictField(default={}, editable=False)
2011-01-22 10:14:30 +00:00
poster = models.ImageField(default=None, blank=True,
upload_to=lambda i, x: i.path("poster.jpg"))
2011-07-30 12:52:49 +00:00
poster_source = models.TextField(blank=True)
2010-11-27 02:33:31 +00:00
poster_height = models.IntegerField(default=0)
poster_width = models.IntegerField(default=0)
poster_frame = models.FloatField(default=-1)
2011-01-04 07:32:32 +00:00
2011-01-22 10:14:30 +00:00
icon = models.ImageField(default=None, blank=True,
upload_to=lambda i, x: i.path("icon.jpg"))
2011-01-04 07:32:32 +00:00
2011-12-15 11:21:21 +00:00
torrent = models.FileField(default=None, blank=True, max_length=1000,
2011-07-03 16:21:27 +00:00
upload_to=lambda i, x: i.path('torrent.torrent'))
2011-07-03 18:11:35 +00:00
stream_info = fields.DictField(default={}, editable=False)
2011-07-03 16:21:27 +00:00
2011-10-22 12:42:45 +00:00
notes = models.TextField(default='')
2010-11-27 02:33:31 +00:00
#stream related fields
stream_aspect = models.FloatField(default=4/3)
2010-09-23 16:01:48 +00:00
objects = managers.ItemManager()
2009-08-01 14:14:54 +00:00
def get(self, key, default=None):
2011-10-28 22:43:44 +00:00
if key == 'rightslevel':
return self.level
if self.data and key in self.data:
return self.data[key]
if self.external_data and key in self.external_data:
return self.external_data[key]
return default
2011-01-24 13:44:38 +00:00
def access(self, user):
2011-09-16 17:17:49 +00:00
if user.is_anonymous():
level = 'guest'
else:
level = user.get_profile().get_level()
allowed_level = settings.CONFIG['capabilities']['canSeeItem'][level]
2011-09-28 12:47:13 +00:00
if self.level <= allowed_level:
2011-01-24 13:44:38 +00:00
return True
elif user.is_authenticated() and \
2011-09-06 12:06:59 +00:00
(self.user == user or \
self.groups.filter(id__in=user.groups.all()).count() > 0):
return True
2011-01-24 13:44:38 +00:00
return False
def editable(self, user):
2011-06-06 18:38:16 +00:00
if user.is_anonymous():
return False
if user.get_profile().capability('canEditMetadata') == True or \
user.is_staff or \
2011-01-21 09:31:49 +00:00
self.user == user or \
self.groups.filter(id__in=user.groups.all()).count() > 0:
return True
return False
def edit(self, data):
#FIXME: how to map the keys to the right place to write them to?
2011-01-21 09:31:49 +00:00
if 'id' in data:
#FIXME: check if id is valid and exists and move/merge items accordingly
del data['id']
2011-02-25 12:12:56 +00:00
if 'groups' in data:
2011-01-21 09:31:49 +00:00
groups = data.pop('groups')
2012-01-13 09:47:18 +00:00
if isinstance(groups, list):
groups = filter(lambda g: g.strip(), groups)
self.groups.exclude(name__in=groups).delete()
current_groups = [g.name for g in self.groups.all()]
for g in filter(lambda g: g not in current_groups, groups):
group, created = Group.objects.get_or_create(name=g)
self.groups.add(group)
2012-01-15 15:05:37 +00:00
keys = [k['id'] for k in
filter(lambda i: i.get('description'), settings.CONFIG['itemKeys'])]
for k in keys:
key = '%sdescription' % k
if key in data:
value = data.get(k, self.get(k, ''))
d, created = Description.objects.get_or_create(key=k, value=value)
d.description = data.pop(key)
d.save()
2011-01-01 11:44:42 +00:00
for key in data:
2011-10-24 19:07:28 +00:00
self.data[key] = data[key]
2011-10-24 20:48:14 +00:00
return self.save()
2011-01-16 13:28:57 +00:00
def update_external(self):
if settings.DATA_SERVICE and not self.itemId.startswith('0x'):
response = external_data('getData', {'id': self.itemId})
if response['status']['code'] == 200:
self.external_data = response['data']
self.make_poster(True)
2011-10-28 18:27:54 +00:00
self.save()
2011-09-30 17:37:41 +00:00
def expand_connections(self):
c = self.get('connections')
if c:
2011-09-30 18:13:56 +00:00
for t in c.keys():
if c[t]:
if isinstance(c[t][0], basestring):
c[t]= [{'id': i, 'title': None} for i in c[t]]
ids = [i['id'] for i in c[t]]
known = {}
for l in Item.objects.filter(itemId__in=ids):
known[l.itemId] = l.get('title')
for i in c[t]:
if i['id'] in known:
i['item'] = i['id']
i['title'] = known[i['id']]
c[t]= filter(lambda x: x['title'], c[t])
if not c[t]:
del c[t]
return c
2011-09-30 17:37:41 +00:00
2009-06-08 16:08:59 +00:00
def __unicode__(self):
year = self.get('year')
if year:
2011-01-28 08:48:38 +00:00
return u'%s (%s)' % (self.get('title', 'Untitled'), self.get('year'))
return self.get('title', u'Untitled')
2010-09-14 14:10:37 +00:00
def get_absolute_url(self):
return '/%s' % self.itemId
2010-09-14 14:10:37 +00:00
2009-06-08 16:08:59 +00:00
def save(self, *args, **kwargs):
2011-10-24 20:48:14 +00:00
update_poster = False
2011-10-25 10:57:31 +00:00
update_ids = False
2011-01-28 08:48:38 +00:00
if not self.id:
2011-10-26 14:04:50 +00:00
if self.user:
self.level = settings.CONFIG['rightsLevel'][self.user.get_profile().get_level()]
2011-12-27 06:54:49 +00:00
else:
self.level = settings.CONFIG['rightsLevel']['member']
2011-01-28 08:48:38 +00:00
if not self.itemId:
self.itemId = str(uuid.uuid1())
super(Item, self).save(*args, **kwargs)
if not settings.USE_IMDB:
2011-12-18 09:35:49 +00:00
self.itemId = ox.toAZ(self.id)
#this does not work if another item without imdbid has the same metadata
2011-09-28 12:47:13 +00:00
oxdbId = self.oxdb_id()
if oxdbId:
if self.oxdbId != oxdbId:
q = Item.objects.filter(oxdbId=oxdbId).exclude(id=self.id)
if q.count() != 0:
2011-10-24 21:41:55 +00:00
if len(self.itemId) == 7:
self.oxdbId = None
q[0].merge_with(self, save=False)
else:
n = 1
key = 'episodeTitle' in self.data and 'episodeTitle' or 'title'
title = self.get(key, 'Untitled')
while q.count() != 0:
n += 1
self.data[key] = u'%s [%d]' % (title, n)
oxdbId = self.oxdb_id()
q = Item.objects.filter(oxdbId=oxdbId).exclude(id=self.id)
self.oxdbId = oxdbId
2011-10-24 20:48:14 +00:00
update_poster = True
2011-10-25 10:57:31 +00:00
update_ids = True
2011-07-05 14:28:22 +00:00
#id changed, what about existing item with new id?
if settings.USE_IMDB and len(self.itemId) != 7 and self.oxdbId != self.itemId:
self.itemId = self.oxdbId
#FIXME: move files to new id here
2011-11-22 16:49:38 +00:00
if settings.USE_IMDB and len(self.itemId) == 7:
for key in ('title', 'year', 'director', 'season', 'episode',
'seriesTitle', 'episodeTitle'):
if key in self.data:
del self.data[key]
2010-07-05 12:07:59 +00:00
2011-10-31 10:46:59 +00:00
if self.poster and os.path.exists(self.poster.path):
2010-09-06 20:31:12 +00:00
self.poster_height = self.poster.height
self.poster_width = self.poster.width
else:
self.poster_height = 128
self.poster_width = 80
self.update_find()
self.update_sort()
self.update_facets()
if not settings.USE_IMDB:
if self.poster_frame == -1 and self.sort.duration:
self.poster_frame = self.sort.duration/2
update_poster = True
2011-04-22 23:34:01 +00:00
if not self.get('runtime') and self.sort.duration:
self.data['runtime'] = self.sort.duration
self.update_sort()
self.json = self.get_json()
super(Item, self).save(*args, **kwargs)
2011-10-25 10:57:31 +00:00
if update_ids:
for c in self.clips.all(): c.save()
for a in self.annotations.all():
public_id = a.public_id.split('/')[1]
a.public_id = "%s/%s" % ( self.itemId, public_id)
a.save()
if update_poster:
2011-10-24 20:48:14 +00:00
return tasks.update_poster.delay(self.itemId)
2011-10-25 10:57:31 +00:00
2011-10-24 20:48:14 +00:00
return None
2009-06-08 16:08:59 +00:00
2011-04-18 18:50:31 +00:00
def delete_files(self):
2011-05-25 19:11:08 +00:00
path = os.path.join(settings.MEDIA_ROOT, self.path())
if os.path.exists(path):
shutil.rmtree(path)
2011-04-18 18:50:31 +00:00
2010-09-12 14:23:23 +00:00
def delete(self, *args, **kwargs):
2011-04-18 18:50:31 +00:00
self.delete_files()
2010-09-23 16:01:48 +00:00
super(Item, self).delete(*args, **kwargs)
2010-09-12 14:23:23 +00:00
def merge_with(self, other, save=True):
2010-09-12 14:23:23 +00:00
'''
move all related tables to other and delete self
'''
for l in self.lists.all():
2011-05-25 19:11:08 +00:00
l.remove(self)
2010-09-23 16:01:48 +00:00
if l.items.filter(id=other.id) == 0:
2011-05-25 19:11:08 +00:00
l.add(other)
2011-01-03 20:27:40 +00:00
#FIXME: should this really happen for annotations?
for a in self.annotations.all():
a.item = other
2011-10-29 14:12:28 +00:00
a.public_id = None
a.save()
2011-01-03 20:27:40 +00:00
2010-09-12 14:23:23 +00:00
if hasattr(self, 'files'):
for f in self.files.all():
2010-09-23 16:01:48 +00:00
f.item = other
2010-09-12 14:23:23 +00:00
f.save()
self.delete()
if save:
other.save()
#FIXME: update poster, stills and streams after this
2010-09-12 14:23:23 +00:00
2010-09-10 15:12:22 +00:00
def get_posters(self):
url = self.prefered_poster_url()
external_posters = self.external_data.get('posters', {})
services = external_posters.keys()
2011-06-06 18:38:16 +00:00
index = []
2011-07-30 13:51:14 +00:00
for service in settings.POSTER_PRECEDENCE:
if service in services:
2011-06-06 18:38:16 +00:00
index.append(service)
2011-07-30 13:51:14 +00:00
for service in services:
2011-06-06 18:38:16 +00:00
if service not in index:
index.append(service)
if settings.URL not in index:
index.append(settings.URL)
2011-07-30 13:51:14 +00:00
2011-06-06 18:58:51 +00:00
posters = []
2011-08-23 17:39:34 +00:00
poster = self.path('siteposter.jpg')
2011-06-06 18:58:51 +00:00
poster = os.path.abspath(os.path.join(settings.MEDIA_ROOT, poster))
if os.path.exists(poster):
posters.append({
2011-08-23 17:39:34 +00:00
'url': '/%s/siteposter.jpg' % self.itemId,
2011-07-26 17:22:23 +00:00
'width': 640,
'height': 1024,
2011-07-30 11:04:30 +00:00
'source': settings.URL,
2011-07-30 13:51:14 +00:00
'selected': url == None,
2011-06-06 18:38:16 +00:00
'index': index.index(settings.URL)
2011-06-06 18:58:51 +00:00
})
for service in external_posters:
p = external_posters[service][0]
p['source'] = service
p['selected'] = p['url'] == url
p['index'] = index.index(service)
posters.append(p)
2011-06-06 18:38:16 +00:00
posters.sort(key=lambda a: a['index'])
2010-09-10 15:12:22 +00:00
return posters
2011-06-06 18:58:51 +00:00
def get_frames(self):
frames = []
2011-08-04 17:53:43 +00:00
pframes = self.poster_frames()
if pframes:
2011-06-06 18:58:51 +00:00
pos = self.poster_frame
if pos < 0:
2011-08-04 17:55:41 +00:00
pos = int(len(pframes) / 2)
2011-06-06 18:58:51 +00:00
p = 0
2011-08-04 17:53:43 +00:00
for f in pframes:
2011-06-06 18:58:51 +00:00
frames.append({
'index': p,
'position': f['position'],
'selected': p == pos,
2011-08-06 18:00:15 +00:00
'url': '/%s/frameposter%d.jpg' %(self.itemId, p),
2011-06-06 18:58:51 +00:00
'height': f['height'],
'width': f['width']
})
p += 1
return frames
2010-09-10 15:12:22 +00:00
def get_stream(self):
2011-08-18 19:37:12 +00:00
for s in self.streams():
return s.json()
2010-09-10 15:12:22 +00:00
2011-02-11 10:21:25 +00:00
def get_layers(self, user=None):
2010-09-18 14:44:35 +00:00
layers = {}
2011-11-02 14:06:34 +00:00
for l in settings.CONFIG['layers']:
name = l['id']
ll = layers.setdefault(name, [])
qs = Annotation.objects.filter(layer=name, item=self).order_by(
'start', 'end', 'sortvalue')
2011-11-02 14:06:34 +00:00
if name == 'subtitles':
2011-10-20 09:11:14 +00:00
qs = qs.exclude(value='')
2011-11-02 14:06:34 +00:00
if l.get('private'):
2011-08-08 15:59:11 +00:00
if user and user.is_anonymous():
2011-02-11 10:21:25 +00:00
user = None
qs = qs.filter(user=user)
for a in qs.order_by('start'):
2012-01-12 19:32:54 +00:00
ll.append(a.json(user=user))
2010-09-18 14:44:35 +00:00
return layers
2011-07-30 12:23:06 +00:00
def get_json(self, keys=None):
i = {
2011-06-27 13:39:35 +00:00
'id': self.itemId,
2011-10-22 12:51:56 +00:00
'rendered': self.rendered,
2011-10-25 13:59:27 +00:00
'rightslevel': self.level
}
2011-12-27 06:54:49 +00:00
if self.user:
i['user'] = self.user.username
i.update(self.external_data)
i.update(self.data)
2011-09-06 12:06:59 +00:00
for k in settings.CONFIG['itemKeys']:
key = k['id']
2011-07-30 12:23:06 +00:00
if not keys or key in keys:
if key not in i:
value = self.get(key)
#also get values from sort table, i.e. numberof values
if not value and self.sort and hasattr(self.sort, key):
value = getattr(self.sort, key)
if value:
i[key] = value
2011-08-06 07:17:35 +00:00
if 'cast' in i and isinstance(i['cast'][0], basestring):
i['cast'] = [i['cast']]
2011-08-05 18:26:27 +00:00
if 'cast' in i and isinstance(i['cast'][0], list):
i['cast'] = map(lambda x: {'actor': x[0], 'character': x[1]}, i['cast'])
2011-08-05 15:50:18 +00:00
2011-09-30 17:37:41 +00:00
if 'connections' in i:
i['connections'] = self.expand_connections()
2011-08-19 12:20:30 +00:00
if not keys or 'posterRatio' in keys:
i['posterRatio'] = self.poster_width / self.poster_height
2011-08-06 13:34:56 +00:00
2011-09-29 13:05:34 +00:00
2011-08-19 12:20:30 +00:00
streams = self.streams()
i['durations'] = [s.duration for s in streams]
2011-08-18 19:37:12 +00:00
i['duration'] = sum(i['durations'])
2011-08-19 12:25:46 +00:00
i['parts'] = len(i['durations'])
if i['parts']:
2011-08-19 12:20:30 +00:00
i['videoRatio'] = streams[0].aspect_ratio
2011-10-28 23:17:16 +00:00
i['resolution'] = (streams[0].file.width, streams[0].file.height)
2011-08-06 13:34:56 +00:00
#only needed by admins
2011-06-06 18:58:51 +00:00
if keys and 'posters' in keys:
i['posters'] = self.get_posters()
2011-09-29 13:05:34 +00:00
frames = self.get_frames()
2011-06-06 18:58:51 +00:00
if keys and 'frames' in keys:
2011-09-29 13:05:34 +00:00
i['frames'] = frames
2011-09-30 01:20:47 +00:00
selected_frame = filter(lambda f: f['selected'], frames)
if selected_frame:
i['posterFrame'] = selected_frame[0]['position']
2011-09-29 13:05:34 +00:00
elif self.poster_frame != -1.0:
i['posterFrame'] = self.poster_frame
2012-01-15 15:05:37 +00:00
dkeys = [k['id'] for k in
filter(lambda i: i.get('description'), settings.CONFIG['itemKeys'])]
if keys:
dkeys = filter(lambda k: k in keys, dkeys)
for key in dkeys:
qs = Description.objects.filter(key=key, value=self.get(key, ''))
if qs.count() == 0:
i['%sdescription'%key] = ''
else:
i['%sdescription'%key] = qs[0].description
2011-07-30 12:23:06 +00:00
if keys:
info = {}
for key in keys:
if key in i:
info[key] = i[key]
return info
return i
2009-06-08 16:08:59 +00:00
2010-11-28 16:03:23 +00:00
def oxdb_id(self):
2011-01-28 08:48:38 +00:00
if not settings.USE_IMDB:
return self.itemId
2011-04-22 23:34:01 +00:00
if not self.get('title') and not self.get('director'):
2011-02-21 10:25:13 +00:00
return None
return ox.get_oxid(self.get('seriesTitle', self.get('title', '')),
self.get('director', []),
2011-10-24 19:33:03 +00:00
self.get('seriesYear', self.get('year', '')),
self.get('season', ''),
self.get('episode', ''),
self.get('episodeTitle', ''),
self.get('episodeDirector', []),
self.get('episodeYear', ''))
2010-09-10 15:12:22 +00:00
'''
Search related functions
'''
2011-01-01 11:44:42 +00:00
def update_find(self):
2011-01-01 11:44:42 +00:00
2010-11-06 16:14:00 +00:00
def save(key, value):
2011-09-04 21:56:22 +00:00
if value not in ('', None):
2011-10-24 20:48:14 +00:00
f, created = ItemFind.objects.get_or_create(item=self, key=key)
if isinstance(value, bool):
value = value and 'true' or 'false'
2011-01-16 13:28:57 +00:00
if isinstance(value, basestring):
value = value.strip()
f.value = value
2010-11-06 16:14:00 +00:00
f.save()
else:
2011-10-24 20:48:14 +00:00
ItemFind.objects.filter(item=self, key=key).delete()
2011-10-29 14:59:12 +00:00
with transaction.commit_on_success():
for key in settings.CONFIG['itemKeys']:
i = key['id']
if i == 'title':
save(i, u'\n'.join([self.get('title', 'Untitled'),
self.get('originalTitle', '')]))
elif i == 'rightslevel':
save(i, self.level)
elif i == 'filename':
save(i,
'\n'.join([f.path for f in self.files.all()]))
elif key['type'] == 'layer':
2012-01-12 19:32:54 +00:00
qs = Annotation.objects.filter(item=self)
if i == 'annotations':
qs = qs.filter(layer__in=Annotation.public_layers())
else:
qs = qs.filter(layer=i)
qs = qs.order_by('start')
save(i, u'\n'.join([l.findvalue for l in qs]))
2011-10-29 14:59:12 +00:00
elif i != '*' and i not in self.facet_keys:
value = self.get(i)
if isinstance(value, list):
value = u'\n'.join(value)
save(i, value)
for key in self.facet_keys:
if key == 'character':
values = self.get('cast', '')
if values:
values = filter(lambda x: x.strip(),
[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, [])
2011-10-20 08:43:33 +00:00
values = list(set(values))
2011-10-29 14:59:12 +00:00
else:
values = self.get(key, '')
if isinstance(values, list):
save(key, '\n'.join(values))
else:
save(key, values)
2011-01-05 13:06:09 +00:00
isSeries = self.get('series',
self.get('episodeTitle',
self.get('episode',
self.get('seriesTitle')))) != None
save('series', isSeries)
def update_sort(self):
try:
s = self.sort
2010-09-23 16:01:48 +00:00
except ItemSort.DoesNotExist:
s = ItemSort(item=self)
2009-08-16 12:23:29 +00:00
def sortNames(values):
2011-01-03 08:45:31 +00:00
sort_value = u''
if values:
2011-01-03 08:45:31 +00:00
sort_value = u'; '.join([get_name_sort(name) for name in values])
2009-08-16 12:23:29 +00:00
if not sort_value:
2011-01-03 08:45:31 +00:00
sort_value = u''
2009-08-16 12:23:29 +00:00
return sort_value
def set_value(s, name, value):
if not value:
value = None
2011-10-18 20:06:01 +00:00
if isinstance(value, basestring):
value = value.lower()
setattr(s, name, value)
base_keys = (
'aspectratio',
2011-10-20 08:43:33 +00:00
'bitrate',
'clips',
2011-11-10 19:52:26 +00:00
'created',
2011-10-20 08:43:33 +00:00
'cutsperminute',
'duration',
'hue',
2011-10-20 08:43:33 +00:00
'id',
'lightness',
2011-10-20 08:43:33 +00:00
'modified',
2011-10-18 20:06:01 +00:00
'numberofcuts',
'numberoffiles',
'parts',
2011-10-20 08:43:33 +00:00
'pixels',
2011-11-10 19:52:26 +00:00
'timesaccessed',
2011-11-11 17:45:46 +00:00
'accessed',
2011-10-20 08:43:33 +00:00
'resolution',
2011-11-10 21:22:58 +00:00
'width',
'height',
2011-10-25 13:59:27 +00:00
'rightslevel',
2011-10-20 08:43:33 +00:00
'saturation',
'size',
'volume',
'words',
'wordsperminute',
)
2011-09-06 12:06:59 +00:00
for key in filter(lambda k: 'columnWidth' in k, settings.CONFIG['itemKeys']):
name = key['id']
source = name
sort_type = key.get('sort', key['type'])
if 'value' in key:
if 'layer' in key['value']:
continue
source = key['value']['key']
2011-10-25 07:29:41 +00:00
sort_type = key['value'].get('type', sort_type)
2011-10-25 07:31:26 +00:00
if isinstance(sort_type, list):
sort_type = sort_type[0]
2011-01-05 13:06:09 +00:00
if name not in base_keys:
if sort_type == 'title':
2011-10-11 11:29:05 +00:00
value = get_title_sort(self.get(source, u'Untitled'))
2011-10-11 19:05:11 +00:00
value = utils.sort_title(value)[:955]
set_value(s, name, value)
elif sort_type == 'person':
2011-01-05 13:06:09 +00:00
value = sortNames(self.get(source, []))
2011-01-03 19:45:56 +00:00
value = utils.sort_string(value)[:955]
set_value(s, name, value)
elif sort_type == 'string':
2011-01-05 13:06:09 +00:00
value = self.get(source, u'')
if isinstance(value, list):
2011-01-03 08:45:31 +00:00
value = u','.join(value)
2011-01-03 19:45:56 +00:00
value = utils.sort_string(value)[:955]
set_value(s, name, value)
elif sort_type in ('length', 'integer', 'float'):
2011-01-05 13:06:09 +00:00
#can be length of strings or length of arrays, i.e. keywords
2011-04-22 23:34:01 +00:00
value = self.get(source)
if isinstance(value, list):
value = len(value)
set_value(s, name, value)
elif sort_type == 'words':
2011-04-22 23:34:01 +00:00
value = self.get(source)
if isinstance(value, list):
value = '\n'.join(value)
if value:
value = len(value.split(' '))
set_value(s, name, value)
elif sort_type == 'year':
2011-04-22 23:34:01 +00:00
value = self.get(source)
set_value(s, name, value)
elif sort_type == 'date':
2011-04-22 23:34:01 +00:00
value = self.get(source)
if isinstance(value, basestring):
value = datetime.strptime(value, '%Y-%m-%d')
set_value(s, name, value)
#sort keys based on database, these will always be available
2011-01-03 20:30:50 +00:00
s.itemId = self.itemId.replace('0x', 'xx')
s.modified = self.modified
2011-11-10 19:52:26 +00:00
s.created = self.created
2011-10-25 13:59:27 +00:00
s.rightslevel = self.level
2011-10-28 23:27:44 +00:00
s.aspectratio = self.get('aspectratio')
2011-10-20 08:43:33 +00:00
s.words = sum([len(a.value.split()) for a in self.annotations.exclude(value='')])
s.clips = self.clips.count()
2011-09-06 12:06:59 +00:00
videos = self.files.filter(selected=True, is_video=True)
2011-08-23 17:39:34 +00:00
if videos.count() > 0:
s.duration = sum([v.duration for v in videos])
2011-08-23 17:39:34 +00:00
v = videos[0]
s.resolution = v.width * v.height
2011-11-10 21:22:58 +00:00
s.width = v.width
s.height = v.height
2011-10-20 08:24:41 +00:00
if not s.aspectratio:
s.aspectratio = float(utils.parse_decimal(v.display_aspect_ratio))
s.pixels = sum([v.pixels for v in videos])
s.numberoffiles = self.files.all().count()
2011-08-23 17:39:34 +00:00
s.parts = videos.count()
s.size = sum([v.size for v in videos]) #FIXME: only size of movies?
2011-09-30 13:46:26 +00:00
if s.duration:
s.bitrate = s.size * 8 / s.duration
else:
s.bitrate = 0
s.volume = 0
2010-12-22 18:45:41 +00:00
else:
s.duration = None
s.resolution = None
s.bitrate = None
s.pixels = None
s.filename = None
s.files = None
s.size = None
s.volume = None
s.parts = 0
2011-10-23 11:57:52 +00:00
if 'color' in self.data and len(self.data['color']) == 3:
2011-06-27 13:39:35 +00:00
s.hue, s.saturation, s.lightness = self.data['color']
else:
s.hue = None
s.saturation = None
s.brighness = None
2011-10-18 20:06:01 +00:00
s.numberofcuts = len(self.data.get('cuts', []))
if s.duration:
2011-10-18 20:06:01 +00:00
s.cutsperminute = s.numberofcuts / (s.duration/60)
2011-09-06 12:06:59 +00:00
s.wordsperminute = s.words / (s.duration / 60)
2010-12-25 14:00:48 +00:00
else:
s.cutsperminute = None
2011-09-06 12:06:59 +00:00
s.wordsperminute = None
2011-11-10 19:52:26 +00:00
s.timesaccessed = self.accessed.aggregate(Sum('accessed'))['accessed__sum']
2011-11-10 20:00:57 +00:00
if not s.timesaccessed:
s.timesaccessed = 0
2011-11-11 17:45:46 +00:00
s.accessed = self.accessed.aggregate(Max('access'))['access__max']
2009-08-16 12:23:29 +00:00
s.save()
2011-10-20 08:49:01 +00:00
#update cached values in clips
2011-10-20 09:09:22 +00:00
self.clips.all().update(director=s.director, title=s.title)
2009-06-08 16:08:59 +00:00
def update_layer_facets(self):
filters = [f['id'] for f in settings.CONFIG['filters']]
for layer in settings.CONFIG['layers']:
if layer['id'] in filters:
key = layer['id']
current_values = [a['value']
for a in self.annotations.filter(layer=key).distinct().values('value')]
saved_values = [i.value for i in Facet.objects.filter(item=self, key=key)]
removed_values = filter(lambda i: i not in current_values, saved_values)
if removed_values:
Facet.objects.filter(item=self, key=key, value__in=removed_values).delete()
for value in current_values:
if value not in saved_values:
sortvalue = value
2011-12-26 19:21:20 +00:00
Facet.objects.get_or_create(item=self, key=key, value=value, sortvalue=sortvalue)
def update_facets(self):
2011-08-25 15:17:07 +00:00
for key in self.facet_keys + ['title']:
2011-01-03 17:47:20 +00:00
current_values = self.get(key, [])
2011-08-25 15:17:07 +00:00
if key == 'title':
2011-08-26 14:45:04 +00:00
if current_values:
current_values = [current_values]
else:
current_values = []
ot = self.get('originalTitle')
2011-08-25 15:41:14 +00:00
if ot:
current_values.append(ot)
2011-10-20 08:43:33 +00:00
elif key == 'character':
current_values = filter(lambda x: x.strip(),
[f['character'] for f in self.get('cast', [])])
2011-10-20 09:39:21 +00:00
current_values = [item for sublist in [x.split(' / ') for x in current_values]
for item in sublist]
2011-10-20 08:43:33 +00:00
elif key == 'name':
2011-08-09 10:03:06 +00:00
current_values = []
2011-10-20 08:43:33 +00:00
#FIXME: is there a better way to build name collection?
2011-08-09 10:03:06 +00:00
for k in map(lambda x: x['id'],
filter(lambda x: x.get('sort') == 'person',
2011-09-06 12:06:59 +00:00
settings.CONFIG['itemKeys'])):
2011-08-09 10:03:06 +00:00
current_values += self.get(k, [])
2011-01-03 17:47:20 +00:00
if not isinstance(current_values, list):
current_values = [unicode(current_values)]
current_values = list(set(current_values))
2010-09-23 16:01:48 +00:00
saved_values = [i.value for i in Facet.objects.filter(item=self, key=key)]
removed_values = filter(lambda i: i not in current_values, saved_values)
if removed_values:
2010-09-23 16:01:48 +00:00
Facet.objects.filter(item=self, key=key, value__in=removed_values).delete()
for value in current_values:
if value not in saved_values:
2011-10-29 23:32:11 +00:00
sortvalue = value
2011-10-30 00:50:38 +00:00
if key in self.person_keys + ['name']:
2011-10-29 23:32:11 +00:00
sortvalue = get_name_sort(value)
Facet.objects.get_or_create(item=self, key=key, value=value, sortvalue=sortvalue)
self.update_layer_facets()
2011-01-01 11:44:42 +00:00
2010-12-04 01:26:49 +00:00
def path(self, name=''):
2010-12-05 17:51:40 +00:00
h = self.itemId
2011-12-26 18:36:58 +00:00
h = (7-len(h))*'0' + h
2010-12-07 19:05:59 +00:00
return os.path.join('items', h[:2], h[2:4], h[4:6], h[6:], name)
2010-12-04 01:26:49 +00:00
2010-09-10 15:12:22 +00:00
'''
Video related functions
'''
2011-08-06 18:17:22 +00:00
def frame(self, position, height=128):
offset = 0
2011-08-18 19:37:12 +00:00
streams = self.streams()
for stream in streams:
if stream.duration + offset < position:
offset += stream.duration
else:
position = position - offset
height = min(height, stream.resolution)
path = os.path.join(settings.MEDIA_ROOT, stream.path(),
'frames', "%dp"%height, "%s.jpg"%position)
if not os.path.exists(path) and stream.video:
extract.frame(stream.video.path, path, position, height)
if not os.path.exists(path):
return None
return path
2010-09-10 15:12:22 +00:00
@property
def timeline_prefix(self):
2011-08-23 17:39:34 +00:00
videos = self.streams()
if len(videos) == 1:
return os.path.join(settings.MEDIA_ROOT, videos[0].path('timeline'))
2010-12-04 01:26:49 +00:00
return os.path.join(settings.MEDIA_ROOT, self.path(), 'timeline')
2010-09-10 15:12:22 +00:00
2011-08-23 17:39:34 +00:00
def get_files(self, user):
2011-10-18 20:06:01 +00:00
files = self.files.all().select_related()
if user.get_profile().get_level() != 'admin':
files = files.filter(instances__volume__user=user)
return [f.json() for f in files]
2011-08-23 17:39:34 +00:00
def users_with_files(self):
return User.objects.filter(
volumes__files__file__item=self
).order_by('-profile__level', 'date_joined').distinct()
def sets(self):
sets = []
for user in self.users_with_files():
files = self.files.filter(instances__volume__user=user, instances__ignore=False)
sets.append(files)
return sets
2011-08-23 17:39:34 +00:00
def update_wanted(self):
wanted = []
for s in self.sets():
if s.filter(selected=False).count() != 0:
wanted += [i.id for i in s]
else:
break
self.files.filter(id__in=wanted).update(wanted=True)
self.files.exclude(id__in=wanted).update(wanted=False)
2011-08-23 17:39:34 +00:00
def update_selected(self):
for s in self.sets():
if s.filter(Q(is_video=True)|Q(is_audio=True)).filter(available=False).count() == 0:
update = False
2011-10-18 20:06:01 +00:00
self.files.exclude(id__in=s).exclude(part=None).update(part=None)
deselect = self.files.filter(selected=True).exclude(id__in=s)
if deselect.count() > 0:
deselect.update(selected=False)
update = True
if s.filter(selected=False).count() > 0:
s.update(selected=True, wanted=False)
update = True
2011-10-18 20:06:01 +00:00
for f in s:
if f.get_part() != f.part:
f.save()
update = True
if update:
self.rendered = False
self.update_timeline()
break
def get_torrent(self, request):
if self.torrent:
self.torrent.seek(0)
data = ox.torrent.bdecode(self.torrent.read())
url = request.build_absolute_uri("%s/torrent/"%self.get_absolute_url())
data['url-list'] = ['%s%s' % (url, u.split('torrent/')[1]) for u in data['url-list']]
return ox.torrent.bencode(data)
2011-07-03 16:21:27 +00:00
def make_torrent(self):
2011-12-15 11:21:21 +00:00
streams = self.streams()
if streams.count() == 0:
return
2011-07-03 16:21:27 +00:00
base = self.path('torrent')
base = os.path.abspath(os.path.join(settings.MEDIA_ROOT, base))
if os.path.exists(base):
shutil.rmtree(base)
ox.makedirs(base)
2011-07-03 16:21:27 +00:00
2012-01-17 11:16:06 +00:00
filename = utils.safe_filename(self.get('title'))
2012-01-17 11:12:39 +00:00
base = self.path('torrent/%s' % filename)
2011-07-03 16:21:27 +00:00
base = os.path.abspath(os.path.join(settings.MEDIA_ROOT, base))
size = 0
duration = 0.0
2011-08-23 17:39:34 +00:00
if streams.count() == 1:
2011-07-03 16:21:27 +00:00
url = "%s/torrent/%s.webm" % (self.get_absolute_url(),
2012-01-17 11:12:39 +00:00
quote(filename.encode('utf-8')))
2011-07-03 16:21:27 +00:00
video = "%s.webm" % base
2011-08-23 17:39:34 +00:00
v = streams[0]
2011-07-03 16:21:27 +00:00
os.symlink(v.video.path, video)
2011-08-23 17:39:34 +00:00
size = v.video.size
duration = v.duration
2011-07-03 16:21:27 +00:00
else:
url = "%s/torrent/" % self.get_absolute_url()
part = 1
os.makedirs(base)
2011-08-23 17:39:34 +00:00
for v in streams:
2012-01-17 11:12:39 +00:00
video = "%s/%s.Part %d.webm" % (base, filename, part)
2011-07-03 16:21:27 +00:00
part += 1
os.symlink(v.video.path, video)
2011-08-23 17:39:34 +00:00
size += v.video.size
duration += v.duration
2011-07-03 16:21:27 +00:00
video = base
torrent = '%s.torrent' % base
2012-01-16 08:27:56 +00:00
url = "http://%s%s" % (settings.CONFIG['site']['url'], url)
2011-07-03 16:21:27 +00:00
meta = {
'target': torrent,
'url-list': url,
}
if duration:
meta['playtime'] = ox.formatDuration(duration*1000)[:-4]
#slightly bigger torrent file but better for streaming
piece_size_pow2 = 15 #1 mbps -> 32KB pieces
if size / duration >= 1000000:
piece_size_pow2 = 16 #2 mbps -> 64KB pieces
meta['piece_size_pow2'] = piece_size_pow2
ox.torrent.createTorrent(video, settings.TRACKER_URL, meta)
self.torrent.name = self.path('torrent/%s.torrent' % self.get('title'))
self.save()
2011-08-18 19:37:12 +00:00
def streams(self):
2011-08-27 10:54:39 +00:00
return archive.models.Stream.objects.filter(source=None, available=True,
file__item=self, file__is_video=True, file__selected=True).order_by('file__part')
2011-08-18 19:37:12 +00:00
2011-08-19 16:54:42 +00:00
def update_timeline(self, force=False):
2011-08-19 15:37:37 +00:00
streams = self.streams()
self.make_timeline()
2011-10-21 18:02:36 +00:00
if streams.count() == 1:
self.data['color'] = streams[0].color
self.data['cuts'] = streams[0].cuts
else:
#self.data['color'] = extract.average_color(self.timeline_prefix)
#self.data['cuts'] = extract.cuts(self.timeline_prefix)
self.data['cuts'] = []
offset = 0
color = [0, 0, 0]
n = streams.count()
for s in streams:
for c in s.cuts:
self.data['cuts'].append(c+offset)
2011-10-28 09:58:40 +00:00
color = map(lambda a,b: (a+b)/n, color,ox.image.getRGB(s.color))
2011-10-21 18:02:36 +00:00
offset += s.duration
self.data['color'] = ox.image.getHSL(color)
2011-08-19 15:37:37 +00:00
#extract.timeline_strip(self, self.data['cuts'], stream.info, self.timeline_prefix[:-8])
2011-08-20 10:06:18 +00:00
self.select_frame()
self.make_poster(True)
2011-08-19 15:37:37 +00:00
self.make_icon()
2011-09-06 12:06:59 +00:00
if settings.CONFIG['video']['download']:
2011-08-20 10:06:18 +00:00
self.make_torrent()
self.load_subtitles()
2011-10-23 11:57:52 +00:00
self.rendered = streams.count() > 0
2011-08-19 15:37:37 +00:00
self.save()
2010-09-10 15:12:22 +00:00
def delete_poster(self):
2010-09-13 15:25:37 +00:00
if self.poster:
path = self.poster.path
2011-10-25 08:51:30 +00:00
try:
2011-10-25 07:39:33 +00:00
self.poster.delete()
2011-10-25 08:51:30 +00:00
except:
2011-10-25 07:39:33 +00:00
self.poster.name = None
2011-06-06 18:38:16 +00:00
else:
poster= self.path('poster.jpg')
path = os.path.abspath(os.path.join(settings.MEDIA_ROOT, poster))
2011-08-23 17:39:34 +00:00
for f in glob(path.replace('.jpg', '*.jpg')):
2011-06-06 18:38:16 +00:00
os.unlink(f)
2011-12-29 19:13:03 +00:00
def save_poster(self, data):
self.poster.name = self.path('poster.jpg')
poster = self.poster.path
with open(poster, 'w') as f:
f.write(data)
def prefered_poster_url(self):
external_posters = self.external_data.get('posters', {})
2011-07-30 12:52:49 +00:00
service = self.poster_source
if service and service != settings.URL and service in external_posters:
return external_posters[service][0]['url']
2011-07-30 12:52:49 +00:00
if not service:
for service in settings.POSTER_PRECEDENCE:
if service in external_posters:
return external_posters[service][0]['url']
return None
2011-08-11 14:15:56 +00:00
def make_timeline(self):
2011-08-19 15:37:37 +00:00
streams = self.streams()
if streams.count() > 1:
2011-08-20 17:53:26 +00:00
timelines = [s.timeline_prefix for s in self.streams()]
join_timelines(timelines, self.timeline_prefix)
2011-08-11 14:15:56 +00:00
2010-12-01 00:00:33 +00:00
def make_poster(self, force=False):
2011-10-31 00:18:52 +00:00
ox.makedirs(os.path.join(settings.MEDIA_ROOT,self.path()))
2010-09-10 15:12:22 +00:00
if not self.poster or force:
2011-10-24 23:07:50 +00:00
self.delete_poster()
2011-10-24 20:04:14 +00:00
poster = self.make_siteposter()
2011-10-24 23:07:50 +00:00
url = self.prefered_poster_url()
if url:
data = ox.net.readUrl(url)
2011-12-29 19:13:03 +00:00
self.save_poster(data)
2011-10-24 23:07:50 +00:00
elif os.path.exists(poster):
2011-07-26 17:22:23 +00:00
with open(poster) as f:
2011-10-26 14:04:50 +00:00
data = f.read()
if data:
2011-12-29 19:13:03 +00:00
self.save_poster(data)
2011-10-24 20:04:14 +00:00
def make_siteposter(self):
2011-08-23 17:39:34 +00:00
poster = self.path('siteposter.jpg')
2011-07-26 17:22:23 +00:00
poster = os.path.abspath(os.path.join(settings.MEDIA_ROOT, poster))
frame = self.get_poster_frame_path()
timeline = '%s64p.png' % self.timeline_prefix
2011-07-26 17:22:23 +00:00
director = u', '.join(self.get('director', ['Unknown Director']))
cmd = [settings.ITEM_POSTER,
2011-12-03 11:16:13 +00:00
'-t', self.get('title', '').encode('utf-8'),
2011-07-26 17:22:23 +00:00
'-d', director.encode('utf-8'),
'-y', str(self.get('year', '')),
'-p', poster
]
if frame:
cmd += [
'-f', frame,
2011-08-04 13:50:13 +00:00
]
if os.path.exists(timeline):
cmd += [
2011-07-26 17:22:23 +00:00
'-l', timeline,
]
if settings.USE_IMDB:
if len(self.itemId) == 7:
2011-04-05 12:56:02 +00:00
cmd += ['-i', self.itemId]
2012-01-16 13:41:05 +00:00
oxdbId = self.oxdbId or self.oxdb_id() or self.itemId
2011-10-23 21:47:22 +00:00
cmd += ['-o', oxdbId]
2011-07-26 17:22:23 +00:00
else:
cmd += ['-i', self.itemId]
ox.makedirs(os.path.join(settings.MEDIA_ROOT,self.path()))
p = subprocess.Popen(cmd)
p.wait()
2011-08-23 17:39:34 +00:00
for f in glob(poster.replace('.jpg', '*.jpg')):
if f != poster:
os.unlink(f)
2011-07-26 17:22:23 +00:00
return poster
def poster_frames(self):
frames = []
2011-08-04 13:28:06 +00:00
offset = 0
for f in self.files.filter(selected=True, is_video=True):
2011-07-26 17:22:23 +00:00
for ff in f.frames.all():
2011-08-04 13:28:06 +00:00
frames.append({
'position': offset + ff.position,
'path': ff.frame.path,
'width': ff.frame.width,
'height': ff.frame.height
})
offset += f.duration
2011-07-26 17:22:23 +00:00
return frames
2010-09-03 13:28:44 +00:00
2011-08-16 15:06:40 +00:00
def select_frame(self):
frames = self.poster_frames()
if frames:
heat = [ox.image.getImageHeat(f['path']) for f in frames]
self.poster_frame = heat.index(max(heat))
2011-01-21 10:49:24 +00:00
def get_poster_frame_path(self):
2011-08-11 14:15:56 +00:00
frames = self.poster_frames()
2011-01-21 10:49:24 +00:00
if self.poster_frame >= 0:
2011-06-06 18:38:16 +00:00
if frames and len(frames) > int(self.poster_frame):
2011-08-04 13:28:06 +00:00
return frames[int(self.poster_frame)]['path']
2011-06-06 18:38:16 +00:00
else:
2011-09-06 12:06:59 +00:00
size = settings.CONFIG['video']['resolutions'][0]
2011-06-06 18:38:16 +00:00
return self.frame(self.poster_frame, size)
2011-01-21 10:49:24 +00:00
2011-07-26 17:22:23 +00:00
if frames:
2011-08-04 13:39:37 +00:00
return frames[int(len(frames)/2)]['path']
2011-01-21 10:49:24 +00:00
def make_icon(self):
frame = self.get_poster_frame_path()
2011-08-10 14:00:03 +00:00
icon = self.path('icon.jpg')
self.icon.name = icon
timeline = '%s64p.png' % self.timeline_prefix
2011-08-10 14:00:03 +00:00
cmd = [settings.ITEM_ICON,
'-i', self.icon.path
]
if os.path.exists(timeline):
cmd += ['-l', timeline]
2011-01-21 10:49:24 +00:00
if frame:
2011-08-10 14:00:03 +00:00
cmd += ['-f', frame]
p = subprocess.Popen(cmd)
p.wait()
2011-12-28 12:35:10 +00:00
#remove cached versions
2011-08-10 17:00:49 +00:00
icons = os.path.abspath(os.path.join(settings.MEDIA_ROOT, icon))
icons = glob(icons.replace('.jpg', '*.jpg'))
for f in filter(lambda p: not p.endswith('/icon.jpg'), icons):
2011-12-27 13:52:16 +00:00
try:
os.unlink(f)
except:
pass
2011-08-10 14:00:03 +00:00
return icon
2011-01-04 07:32:32 +00:00
2011-08-20 10:06:18 +00:00
def load_subtitles(self):
2011-12-15 11:21:21 +00:00
if not filter(lambda l: l['id'] == 'subtitles', settings.CONFIG['layers']):
return
2011-10-29 14:59:12 +00:00
with transaction.commit_on_success():
2011-11-02 14:06:34 +00:00
layer = 'subtitles'
2011-10-29 14:59:12 +00:00
Annotation.objects.filter(layer=layer,item=self).delete()
offset = 0
language = ''
subtitles = self.files.filter(selected=True, is_subtitle=True, available=True)
languages = [f.language for f in subtitles]
if languages:
if 'en' in languages:
language = 'en'
elif '' in languages:
language = ''
else:
language = languages[0]
#loop over all videos
for f in self.files.filter(Q(is_audio=True)|Q(is_video=True)) \
.filter(selected=True).order_by('part'):
subtitles_added = False
prefix = os.path.splitext(f.path)[0]
if f.instances.all().count() > 0:
user = f.instances.all()[0].volume.user
else:
#FIXME: allow annotations from no user instead?
user = User.objects.all().order_by('id')[0]
#if there is a subtitle with the same prefix, import
q = subtitles.filter(path__startswith=prefix,
language=language)
if q.count() == 1:
s = q[0]
for data in s.srt(offset):
subtitles_added = True
annotation = Annotation(
item=self,
layer=layer,
start=data['in'],
end=data['out'],
value=data['value'],
user=user
)
annotation.save()
#otherwise add empty 5 seconds annotation every minute
if not subtitles_added:
start = offset and int (offset / 60) * 60 + 60 or 0
for i in range(start,
int(offset + f.duration) - 5,
60):
annotation = Annotation(
item=self,
layer=layer,
start=i,
end=i + 5,
value='',
user=user
)
annotation.save()
offset += f.duration
2011-11-02 14:27:02 +00:00
#remove left over clips without annotations
Clip.objects.filter(item=self, annotations__id=None).delete()
2011-08-20 10:06:18 +00:00
self.update_find()
2012-01-02 17:08:19 +00:00
def srt(self, layer):
return ox.srt.encode([{
'in': a.start,
'out': a.end,
'value': a.value
} for a in self.annotations.filter(layer=layer).order_by('start', 'end', 'sortvalue')])
2012-01-02 17:08:19 +00:00
2011-04-18 18:50:31 +00:00
def delete_item(sender, **kwargs):
i = kwargs['instance']
i.delete_files()
pre_delete.connect(delete_item, sender=Item)
Item.facet_keys = []
2011-09-06 12:06:59 +00:00
for key in settings.CONFIG['itemKeys']:
if 'autocomplete' in key and not 'autocompleteSortKey' in key:
Item.facet_keys.append(key['id'])
Item.person_keys = []
2011-09-06 12:06:59 +00:00
for key in settings.CONFIG['itemKeys']:
if 'sort' in key and key['sort'] == 'person':
Item.person_keys.append(key['id'])
2011-01-01 11:44:42 +00:00
2010-09-23 16:01:48 +00:00
class ItemFind(models.Model):
2009-08-16 12:23:29 +00:00
"""
2010-11-06 16:14:00 +00:00
used to find items,
item.update_find populates this table
2010-11-06 16:14:00 +00:00
its used in manager.ItemManager
2009-08-16 12:23:29 +00:00
"""
2011-01-01 11:44:42 +00:00
2010-11-06 16:14:00 +00:00
class Meta:
unique_together = ("item", "key")
item = models.ForeignKey('Item', related_name='find', db_index=True)
key = models.CharField(max_length=200, db_index=True)
value = models.TextField(blank=True)
2009-06-08 16:08:59 +00:00
2011-01-15 14:22:29 +00:00
def __unicode__(self):
return u"%s=%s" % (self.key, self.value)
2011-01-05 13:06:09 +00:00
'''
ItemSort
2011-09-06 12:06:59 +00:00
table constructed based on info in settings.CONFIG['itemKeys']
2011-01-05 13:06:09 +00:00
'''
attrs = {
'__module__': 'item.models',
'item': models.OneToOneField('Item', related_name='sort', primary_key=True),
2011-09-05 16:33:47 +00:00
'duration': models.FloatField(null=True, blank=True, db_index=True),
2011-11-10 21:22:58 +00:00
'width': models.BigIntegerField(null=True, blank=True, db_index=True),
'height': models.BigIntegerField(null=True, blank=True, db_index=True),
2011-11-11 13:04:15 +00:00
'created': models.DateTimeField(null=True, blank=True, db_index=True),
}
2011-11-10 21:22:58 +00:00
for key in filter(lambda k: 'columnWidth' in k or k['type'] in ('integer', 'time', 'float', 'date', 'enum'), settings.CONFIG['itemKeys']):
name = key['id']
2011-01-03 20:30:50 +00:00
name = {'id': 'itemId'}.get(name, name)
sort_type = key.get('sort', key['type'])
if isinstance(sort_type, list):
sort_type = sort_type[0]
model = {
2011-01-24 13:44:38 +00:00
'char': (models.CharField, dict(null=True, max_length=1000, db_index=True)),
'year': (models.CharField, dict(null=True, max_length=4, db_index=True)),
'integer': (models.BigIntegerField, dict(null=True, blank=True, db_index=True)),
'float': (models.FloatField, dict(null=True, blank=True, db_index=True)),
'date': (models.DateTimeField, dict(null=True, blank=True, db_index=True))
}[{
'string': 'char',
'title': 'char',
'person': 'char',
'year': 'year',
'words': 'integer',
'length': 'integer',
'date': 'date',
'hue': 'float',
'time': 'integer',
2011-11-10 21:22:58 +00:00
'enum': 'integer',
}.get(sort_type, sort_type)]
attrs[name] = model[0](**model[1])
2011-01-01 11:44:42 +00:00
ItemSort = type('ItemSort', (models.Model,), attrs)
ItemSort.fields = [f.name for f in ItemSort._meta.fields]
2011-01-24 13:44:38 +00:00
class Access(models.Model):
class Meta:
unique_together = ("item", "user")
access = models.DateTimeField(auto_now=True)
item = models.ForeignKey(Item, related_name='accessed')
user = models.ForeignKey(User, null=True, related_name='accessed_items')
accessed = models.IntegerField(default=0)
def save(self, *args, **kwargs):
if not self.accessed:
self.accessed = 0
self.accessed += 1
super(Access, self).save(*args, **kwargs)
timesaccessed = Access.objects.filter(item=self.item).aggregate(Sum('accessed'))['accessed__sum']
2011-11-11 17:45:46 +00:00
ItemSort.objects.filter(item=self.item).update(timesaccessed=timesaccessed, accessed=self.access)
2011-01-24 13:44:38 +00:00
def __unicode__(self):
if self.user:
return u"%s/%s/%s" % (self.user, self.item, self.access)
return u"%s/%s" % (self.item, self.access)
class Facet(models.Model):
2011-01-05 13:06:09 +00:00
'''
used for keys that can have multiple values like people, languages etc.
does not perform to well if total number of items goes above 10k
this happens for keywords in 0xdb right now
'''
class Meta:
unique_together = ("item", "key", "value")
2010-09-23 16:01:48 +00:00
item = models.ForeignKey('Item', related_name='facets')
key = models.CharField(max_length=200, db_index=True)
2011-08-25 15:41:14 +00:00
value = models.CharField(max_length=1000, db_index=True)
2011-10-29 23:32:11 +00:00
sortvalue = models.CharField(max_length=1000, db_index=True)
2009-06-08 16:08:59 +00:00
2011-04-05 10:49:58 +00:00
def __unicode__(self):
return u"%s=%s" % (self.key, self.value)
def save(self, *args, **kwargs):
2011-10-29 23:32:11 +00:00
if not self.sortvalue:
self.sortvalue = utils.sort_string(self.value)
super(Facet, self).save(*args, **kwargs)
2009-08-16 12:23:29 +00:00
2012-01-15 15:05:37 +00:00
class Description(models.Model):
'''
shared itemkey descriptions
'''
class Meta:
unique_together = ("key", "value")
key = models.CharField(max_length=200, db_index=True)
value = models.CharField(max_length=1000, db_index=True)
description = models.TextField()