cablegates/pandora/backend/models.py

1229 lines
42 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-02-03 12:05:38 +00:00
from datetime import datetime
2009-06-08 16:08:59 +00:00
import os.path
import random
2010-02-03 12:05:38 +00:00
import re
2009-06-08 16:08:59 +00:00
from django.db import models
from django.db.models import Q
from django.contrib.auth.models import User
2009-12-31 15:04:32 +00:00
from django.core.files.base import ContentFile
from django.utils import simplejson as json
from django.conf import settings
2010-02-03 12:05:38 +00:00
from oxdjango import fields
2010-07-07 22:46:41 +00:00
import ox
from ox import stripTags
from ox.normalize import canonicalTitle, canonicalName
2010-02-06 08:28:35 +00:00
from firefogg import Firefogg
2009-06-08 16:08:59 +00:00
2009-08-01 14:14:54 +00:00
import managers
2010-01-16 20:42:11 +00:00
import load
2010-02-03 12:05:38 +00:00
import utils
2010-02-16 10:13:44 +00:00
import extract
2009-06-08 16:08:59 +00:00
class MovieImdb(models.Model):
2009-06-08 16:08:59 +00:00
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
imdbId = models.CharField(max_length=7, unique=True)
2009-06-08 16:08:59 +00:00
title = models.CharField(max_length=1000)
year = models.CharField(max_length=4)
runtime = models.IntegerField(null=True, blank=True)
release_date = models.DateField(null=True, blank=True)
tagline = models.TextField(blank=True)
plot = models.TextField(blank=True)
plot_outline = models.TextField(blank=True)
rating = models.FloatField(null=True, blank=True)
votes = models.IntegerField(null=True, blank=True)
budget = models.IntegerField(null=True, blank=True)
gross = models.IntegerField(null=True, blank=True)
profit = models.IntegerField(null=True, blank=True)
series_imdb = models.CharField(max_length=7, default='')
2009-12-31 15:04:32 +00:00
series_title = models.CharField(max_length=1000, blank=True, default='')
episode_title = models.CharField(max_length=1000, blank=True, default='')
season = models.IntegerField(default=-1)
episode = models.IntegerField(default=-1)
2009-12-31 15:04:32 +00:00
def __unicode__(self):
return u"%s (%s)" % (self.title, self.imdbId)
class MovieOxdb(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
2009-06-08 16:08:59 +00:00
title = models.CharField(max_length=1000)
year = models.CharField(max_length=4)
runtime = models.IntegerField(null=True, blank=True)
release_date = models.DateField(null=True, blank=True)
2009-06-08 16:08:59 +00:00
tagline = models.TextField(blank=True)
plot = models.TextField(blank=True)
plot_outline = models.TextField(blank=True)
rating = models.FloatField(null=True, blank=True)
2009-06-08 16:08:59 +00:00
votes = models.IntegerField(null=True, blank=True)
budget = models.IntegerField(null=True, blank=True)
gross = models.IntegerField(null=True, blank=True)
profit = models.IntegerField(null=True, blank=True)
series_imdb = models.CharField(max_length=7, default='')
series_title = models.TextField(blank=True, default='')
episode_title = models.TextField(blank=True, default='')
season = models.IntegerField(default=-1)
episode = models.IntegerField(default=-1)
2009-12-31 15:04:32 +00:00
def __unicode__(self):
return u"%s (%s)" % (self.title, self.year)
2010-01-16 20:42:11 +00:00
class MovieExtra(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
title = models.CharField(max_length=1000)
description = models.TextField(blank=True)
contributor = models.CharField(max_length=1000)
rights_level = models.IntegerField(default=-1)
2010-01-16 20:42:11 +00:00
def getMovie(info):
'''
info dict with:
imdbId, title, director, episode_title, season, series
'''
2010-01-22 23:57:06 +00:00
if 'imdbId' in info and info['imdbId']:
2010-01-16 20:42:11 +00:00
try:
2010-01-22 23:57:06 +00:00
movie = Movie.byImdbId(info['imdbId'])
except Movie.DoesNotExist:
movie = load.loadIMDb(info['imdbId'])
2010-01-16 20:42:11 +00:00
else:
2010-01-22 23:57:06 +00:00
q = Movie.objects.filter(oxdb__title=info['title'])
2010-01-16 20:42:11 +00:00
if q.count() > 1:
2010-01-22 23:57:06 +00:00
print "FIXME: check more than title here!!"
2010-01-16 20:42:11 +00:00
movie = q[0]
else:
2010-01-22 23:57:06 +00:00
print info
2010-01-16 20:42:11 +00:00
movie = newMovie(info['title'], info['director'], '')
updated = False
for key in ('episode_title', 'season', 'year'):
if key in info:
setattr(movie.oxdb, key, info[key])
updated = True
if updated:
2010-01-16 21:04:38 +00:00
movie.save()
2010-01-16 20:42:11 +00:00
return movie
def newMovie(title, director, year):
movie = Movie()
oxdb = MovieOxdb()
oxdb.save()
movie.oxdb = oxdb
movie.oxdb.title = title
movie.oxdb.year = str(year)
movie.oxdb.save()
movie.oxdbId = "__init__%s" % random.randint(0, 100000)
movie.save()
movie.oxdbId = movie.oxid()
2010-01-25 09:20:21 +00:00
print title, director, year
print movie.oxdbId
print movie.movieId
movie.save()
return movie
2009-12-31 15:04:32 +00:00
def movie_path(f, size):
name = "%s.%s" % (size, 'ogv')
url_hash = f.movieId
return os.path.join('movie', url_hash[:2], url_hash[2:4], url_hash[4:6], name)
2010-07-05 12:07:59 +00:00
def poster_path(f):
name = "%s.%s" % (f.movieId, 'jpg')
url_hash = f.movieId
return os.path.join('poster', url_hash[:2], url_hash[2:4], url_hash[4:6], name)
class Movie(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
2010-01-16 20:42:11 +00:00
published = models.DateTimeField(default=datetime.now, editable=False)
2009-06-08 16:08:59 +00:00
#only movies that have metadata from files are available,
#this is indicated by setting available to True
2009-12-31 15:04:32 +00:00
available = models.BooleanField(default=False, db_index=True)
movieId = models.CharField(max_length=128, unique=True, blank=True)
oxdbId = models.CharField(max_length=42, unique=True, blank=True)
imdb = models.OneToOneField('MovieImdb', null=True, related_name='movie')
oxdb = models.OneToOneField('MovieOxdb', null=True, related_name='movie')
2010-01-22 22:56:33 +00:00
extra = models.OneToOneField('MovieExtra', null=True, related_name='movie')
2009-06-08 16:08:59 +00:00
2009-08-01 14:14:54 +00:00
objects = managers.MovieManager()
def get(self, key, default=None):
2010-01-16 20:42:11 +00:00
if self.extra and getattr(self.extra, key):
return getattr(self.extra, key)
if self.oxdb and getattr(self.oxdb, key):
return getattr(self.oxdb, key)
if self.imdb:
return getattr(self.imdb, key)
return default
def editable(self, user):
#FIXME: make permissions work
return False
def edit(self, data):
#FIXME: how to map the keys to the right place to write them to?
for key in data:
if key != 'id':
setattr(self.oxdb, key, data[key])
self.oxdb.save()
self.save()
def _manual(self, qs, f='manual'):
if qs.filter(**{f:True}).count() > 0:
return qs.exclude(**{f:False})
return qs.exclude(**{f:True})
2009-06-08 16:08:59 +00:00
def directors(self):
qs = self.people.filter(cast__role='directors').order_by('cast__position')
return self._manual(qs, 'cast__manual')
2009-06-08 16:08:59 +00:00
def writers(self):
qs = self.people.filter(cast__role='writers').order_by('cast__position')
return self._manual(qs, 'cast__manual')
2009-06-08 16:08:59 +00:00
def editors(self):
qs = self.people.filter(cast__role='editors').order_by('cast__position')
return self._manual(qs, 'cast__manual')
2009-06-08 16:08:59 +00:00
def producers(self):
qs = self.people.filter(cast__role='producers').order_by('cast__position')
return self._manual(qs, 'cast__manual')
2009-06-08 16:08:59 +00:00
def cinematographers(self):
qs = self.people.filter(cast__role='cinematographers').order_by('cast__position')
return self._manual(qs, 'cast__manual')
2009-06-08 16:08:59 +00:00
def cast(self):
cast = []
qs = Cast.objects.filter(movie=self, role='cast').order_by('position')
qs = self._manual(qs)
for c in qs:
2009-06-08 16:08:59 +00:00
cast.append((c.person.name, c.character))
return tuple(cast)
def alternative_titles(self):
return self._manual(self.alternative_titles_all)
def genres(self):
return self._manual(self.genres_all)
def keywords(self):
return self._manual(self.keywords_all)
def countries(self):
2009-12-31 15:04:32 +00:00
return self._manual(self.countries_all, 'moviecountry__manual').order_by('moviecountry__position')
def languages(self):
2009-12-31 15:04:32 +00:00
return self._manual(self.languages_all, 'movielanguage__manual').order_by('movielanguage__position')
def trivia(self):
return self._manual(self.trivia_all)
def locations(self):
return self._manual(self.locations_all)
def connections(self):
return self._manual(self.connections_all)
2009-06-08 16:08:59 +00:00
2009-08-16 12:23:29 +00:00
def connections_json(self):
connections = {}
for connection in self.connections():
2009-08-16 12:23:29 +00:00
if connection.relation not in connections:
connections[connection.relation] = []
connections[connection.relation].append(connection.object.movieId)
return connections
def reviews(self):
q = self.reviews_all.filter(manual=True)
if q.count() > 0:
return q
2009-06-08 16:08:59 +00:00
whitelist = ReviewWhitelist.objects.all()
q = Q(id=-1)
for w in whitelist:
q = q | Q(url__contains=w.url)
return self.reviews_all.filter(q).filter(manual=False)
2009-06-08 16:08:59 +00:00
2009-12-31 15:04:32 +00:00
json = fields.DictField(default={}, editable=False)
2009-08-16 12:23:29 +00:00
#FIXME: use data.0xdb.org
2009-12-31 15:04:32 +00:00
'''
2009-06-08 16:08:59 +00:00
tpb_id = models.CharField(max_length=128, blank=True)
kg_id = models.CharField(max_length=128, blank=True)
open_subtitle_id = models.IntegerField(null=True, blank=True)
wikipedia_url = models.TextField(blank=True)
2009-12-31 15:04:32 +00:00
#FIXME: use data.0xdb.org/posters for that
2009-06-08 16:08:59 +00:00
#what of this is still required?
still_pos = models.IntegerField(null=True, blank=True)
poster = models.TextField(blank=True)
posters_disabled = models.TextField(blank=True)
posters_available = models.TextField(blank=True)
2010-07-05 12:07:59 +00:00
poster = models.ImageField(default=None, blank=True, upload_to=poster_path)
2009-12-31 15:04:32 +00:00
'''
2010-07-05 08:30:17 +00:00
poster_height = models.IntegerField(default=0)
poster_width = models.IntegerField(default=0)
2009-06-08 16:08:59 +00:00
2009-12-31 15:04:32 +00:00
#stream related fields
'''
'''
2010-02-08 10:26:25 +00:00
stream_low = models.FileField(default=None, blank=True, upload_to=lambda f, x: movie_path(f, 'low'))
stream_mid = models.FileField(default=None, blank=True, upload_to=lambda f, x: movie_path(f, 'mid'))
stream_high = models.FileField(default=None, blank=True, upload_to=lambda f, x: movie_path(f, 'high'))
2009-12-31 15:04:32 +00:00
#FIXME: is this still required? should this not be aspect ratio? depends on stream???
2009-06-08 16:08:59 +00:00
scene_height = models.IntegerField(null=True, blank=True)
def __unicode__(self):
return u'%s (%s)' % (self.get('title'), self.get('year'))
2009-06-08 16:08:59 +00:00
def save(self, *args, **kwargs):
if not self.oxdb:
oxdb = MovieOxdb()
oxdb.save()
self.oxdb = oxdb
if self.imdb:
mid = self.imdb.imdbId
2009-06-08 16:08:59 +00:00
else:
mid = self.oxdbId
self.movieId = mid
2009-12-31 15:04:32 +00:00
if self.id:
self.json = self.get_json()
2010-07-05 12:07:59 +00:00
2009-06-08 16:08:59 +00:00
super(Movie, self).save(*args, **kwargs)
2009-08-16 12:23:29 +00:00
self.updateFind()
self.updateSort()
2009-06-08 16:08:59 +00:00
_public_fields = {
'movieId': 'id',
'title': 'title',
'year': 'year',
'runtime': 'runtime',
'release_date': 'release_date',
'countries': 'country',
'directors': 'director',
'languages': 'language',
'genres': 'genre',
'keywords': 'keyword',
2009-06-08 16:08:59 +00:00
'cast': 'cast',
'series_title': 'series_title',
'episode_title': 'episode_title',
'season': 'season',
'episode': 'episode',
'reviews': 'reviews',
2009-08-01 14:14:54 +00:00
'trivia': 'trivia',
'rating': 'rating',
'votes': 'votes',
2009-08-16 12:23:29 +00:00
'alternative_titles': 'alternative_titles',
2010-07-05 08:30:17 +00:00
'connections_json': 'connections',
2010-07-05 08:38:53 +00:00
'poster_width': 'posterWidth',
'poster_height': 'posterHeight'
2009-06-08 16:08:59 +00:00
}
2009-12-31 15:04:32 +00:00
def get_json(self, fields=None):
2009-06-08 16:08:59 +00:00
movie = {}
for key in self._public_fields:
pub_key = self._public_fields.get(key, key)
if not fields or pub_key in fields:
if hasattr(self, key):
value = getattr(self, key)
else:
value = self.get(key)
2009-12-31 15:04:32 +00:00
if key in ('directors', 'writers', 'editors', 'cinematographers', 'producers',
'reviews', 'countries', 'languages',
'keywords', 'genres', 'trivia', 'alternative_titles'):
2009-06-08 16:08:59 +00:00
movie[pub_key] = tuple([v.json() for v in value()])
elif callable(value):
movie[pub_key] = value()
2009-06-08 16:08:59 +00:00
else:
movie[pub_key] = value
if fields:
for f in fields:
if f.endswith('.length') and f[:-7] in ('cast', 'genre', 'trivia'):
2010-07-05 12:07:59 +00:00
movie[f] = getattr(self.sort, f[:-7])
2009-06-08 16:08:59 +00:00
return movie
2009-08-16 12:23:29 +00:00
def fields(self):
fields = {}
for f in self._meta.fields:
if f.name in self._public_fields:
fields[f.name] = {}
fields[f.name]['order'] = 'desc'
fields[f.name]['type'] = type(f)
return fields
fields = classmethod(fields)
2009-06-08 16:08:59 +00:00
#Class functions to get Movies by ID, right now movieId, imdbId and oxdbId
#FIXME: this should go into a manager
def byMovieId(self, movieId):
if len(movieId) == 7:
return self.byImdbId(movieId)
return self.byOxdbId(movieId)
byMovieId = classmethod(byMovieId)
def byImdbId(self, imdbId):
return self.objects.get(imdb__imdbId=imdbId)
2009-06-08 16:08:59 +00:00
byImdbId = classmethod(byImdbId)
def byOxdbId(self, oxdbId):
2009-08-16 12:23:29 +00:00
return self.objects.get(oxdbId=oxdbId)
2009-06-08 16:08:59 +00:00
byOxdbId = classmethod(byOxdbId)
def oxid(self):
2009-08-16 12:23:29 +00:00
directors = ','.join([d.name for d in self.directors()])
return utils.oxid(self.get('title', ''), directors, self.get('year', ''),
self.get('series_title', ''), self.get('episode_title', ''),
self.get('season', ''), self.get('episode', ''))
2009-08-16 12:23:29 +00:00
def frame(self, position, width=128):
#FIXME: compute offset and so on
f = self.files.all()[0]
return f.frame(position, width)
2009-08-16 12:23:29 +00:00
def updateFind(self):
try:
f = self.find
except MovieFind.DoesNotExist:
f = MovieFind(movie=self)
2010-02-22 09:25:29 +00:00
f.title = self.get('title')
#FIXME: filter us/int title
#f.title += ' '.join([t.title for t in self.alternative_titles()])
2010-07-05 08:21:09 +00:00
f.director = '|%s|'%'|'.join([i.name for i in self.directors()])
f.country = '|%s|'%'|'.join([i.name for i in self.countries()])
f.year = self.get('year', '')
2010-07-05 08:21:09 +00:00
f.language = '|%s|'%'|'.join([i.name for i in self.languages()])
f.writer = '|%s|'%'|'.join([i.name for i in self.writers()])
f.producer = '|%s|'%'|'.join([i.name for i in self.producers()])
f.editor = '|%s|'%'|'.join([i.name for i in self.editors()])
f.cinematographer = '|%s|'%'|'.join([i.name for i in self.cinematographers()])
2009-08-16 12:23:29 +00:00
f.cast = ' '.join(['%s %s' % i for i in self.cast()])
2010-07-05 08:21:09 +00:00
f.genre = '|%s|'%'|'.join([i.name for i in self.genres()])
f.keywords = '|%s|'%'|'.join([i.name for i in self.keywords()])
f.summary = self.get('plot', '') + self.get('plot_outline', '')
f.trivia = ' '.join([i.trivia for i in self.trivia()])
2010-07-05 08:21:09 +00:00
f.location = '|%s|'%'|'.join([i.name for i in self.locations()])
#FIXME: collate filenames
#f.filename = self.filename
f.all = ' '.join(filter(None, [f.title, f.director, f.country, f.year, f.language,
2009-08-16 12:23:29 +00:00
f.writer, f.producer, f.editor, f.cinematographer,
f.cast, f.genre, f.keywords, f.summary, f.trivia,
f.location, f.filename]))
2009-08-16 12:23:29 +00:00
f.save()
def updateSort(self):
try:
s = self.sort
except MovieSort.DoesNotExist:
s = MovieSort(movie=self)
2009-08-16 12:23:29 +00:00
def sortName(value):
sort_value = '~'
if value:
sort_value = stripTags(value).split(',')
sort_value = '; '.join([canonicalName(name.strip()) for name in sort_value])
sort_value = sort_value.replace(u'\xc5k', 'A')
if not sort_value:
sort_value = '~'
return sort_value
#title
title = canonicalTitle(self.get('title'))
2009-08-16 12:23:29 +00:00
title = re.sub(u'[\'!¿¡,\.;\-"\:\*\[\]]', '', title)
title = title.replace(u'Æ', 'Ae')
#pad numbered titles
numbers = re.compile('^(\d+)').findall(title)
if numbers:
padded = '%010d' % int(numbers[0])
title = re.sub('^(\d+)', padded, title)
s.title = title.strip()
directors = ','.join([i.name for i in self.directors()])
s.director = sortName(directors)
s.country = ','.join([i.name for i in self.countries()])
s.year = self.get('year', '')
2009-08-16 12:23:29 +00:00
names = ','.join([i.name for i in self.producers()])
s.producer = sortName(names)
names = ','.join([i.name for i in self.writers()])
s.writer = sortName(names)
names = ','.join([i.name for i in self.editors()])
s.editor = sortName(names)
names = ','.join([i.name for i in self.cinematographers()])
s.cinematographer = sortName(names)
s.language = ','.join([i.name for i in self.languages()])
s.country = ','.join([i.name for i in self.countries()])
s.runtime = self.get('runtime', 0)
2009-08-16 12:23:29 +00:00
s.keywords = self.keywords().count()
s.genre = self.genres().count()
2009-08-16 12:23:29 +00:00
s.cast = len(self.cast())
s.summary = len(self.get('plot', '').split())
s.trivia = self.trivia().count()
s.connections = self.connections().count()
2009-08-16 12:23:29 +00:00
s.movieId = self.movieId
s.rating = self.get('rating', -1)
s.votes = self.get('votes', -1)
2009-08-16 12:23:29 +00:00
# data from related subtitles
s.scenes = 0 #FIXME
s.words = 0 #FIXME
s.wpm = 0 #FIXME
s.risk = 0 #FIXME
# data from related files
s.duration = 0 #FIXME
s.resolution = 0 #FIXME
s.aspectratio = 0 #FIXME
s.bitrate = 0 #FIXME
s.pixels = 0 #FIXME
s.filename = 0 #FIXME
s.files = 0 #FIXME
s.size = 0 #FIXME
s.save()
2009-06-08 16:08:59 +00:00
class MovieFind(models.Model):
2009-08-16 12:23:29 +00:00
"""
used to search movies, all search values are in here
"""
movie = models.OneToOneField('Movie', related_name='find', primary_key=True)
2009-06-08 16:08:59 +00:00
2009-08-16 12:23:29 +00:00
all = models.TextField(blank=True)
2009-12-31 15:04:32 +00:00
title = models.TextField(blank=True)
director = models.TextField(blank=True, default='')
country = models.TextField(blank=True, default='')
2009-06-08 16:08:59 +00:00
year = models.CharField(max_length=4)
language = models.TextField(blank=True, default='')
writer = models.TextField(blank=True, default='')
producer = models.TextField(blank=True, default='')
editor = models.TextField(blank=True, default='')
cinematographer = models.TextField(blank=True, default='')
cast = models.TextField(blank=True, default='')
2009-06-08 16:08:59 +00:00
#person
genre = models.TextField(blank=True)
keywords = models.TextField(blank=True)
summary = models.TextField(blank=True)
trivia = models.TextField(blank=True)
locations = models.TextField(blank=True, default='')
2009-06-08 16:08:59 +00:00
#only for own files or as admin?
filename = models.TextField(blank=True, default='')
2009-06-08 16:08:59 +00:00
_private_fields = ('id', 'movie')
2009-12-31 15:04:32 +00:00
#return available find fields
#FIXME: should return mapping name -> verbose_name
def fields(self):
fields = []
2009-06-08 16:08:59 +00:00
for f in self._meta.fields:
if f.name not in self._private_fields:
2009-12-31 15:04:32 +00:00
name = f.verbose_name
name = name[0].capitalize() + name[1:]
fields.append(name)
return tuple(fields)
fields = classmethod(fields)
2009-06-08 16:08:59 +00:00
class MovieSort(models.Model):
2009-08-16 12:23:29 +00:00
"""
used to sort movies, all sort values are in here
"""
movie = models.OneToOneField('Movie', related_name='sort', primary_key=True)
2009-06-08 16:08:59 +00:00
2010-04-20 10:10:39 +00:00
title = models.CharField(max_length=1000, db_index=True)
director = models.TextField(blank=True, db_index=True)
country = models.TextField(blank=True, db_index=True)
year = models.CharField(max_length=4, db_index=True)
producer = models.TextField(blank=True, db_index=True)
writer = models.TextField(blank=True, db_index=True)
editor = models.TextField(blank=True, db_index=True)
cinematographer = models.TextField(blank=True, db_index=True)
language = models.TextField(blank=True, db_index=True)
runtime = models.IntegerField(blank=True, null=True, db_index=True)
keywords = models.IntegerField(blank=True, db_index=True)
genre = models.TextField(blank=True, db_index=True)
cast = models.IntegerField(blank=True, db_index=True)
summary = models.IntegerField(blank=True, db_index=True)
trivia = models.IntegerField(blank=True, db_index=True)
connections = models.IntegerField(blank=True, db_index=True)
rating = models.FloatField(blank=True, db_index=True)
votes = models.IntegerField(blank=True, db_index=True)
scenes = models.IntegerField(blank=True, db_index=True)
words = models.IntegerField(null=True, blank=True, db_index=True)
wpm = models.IntegerField('Words per Minute', null=True, blank=True, db_index=True)
risk = models.IntegerField(null=True, blank=True, db_index=True)
movieId = models.CharField('ID', max_length=128, blank=True, db_index=True)
duration = models.FloatField(default=-1, db_index=True)
resolution = models.IntegerField(blank=True, db_index=True)
aspectratio = models.IntegerField('Aspect Ratio', blank=True, db_index=True)
bitrate = models.IntegerField(blank=True, db_index=True)
pixels = models.BigIntegerField(blank=True, db_index=True)
filename = models.IntegerField(blank=True, db_index=True)
files = models.IntegerField(blank=True, db_index=True)
size = models.BigIntegerField(blank=True, db_index=True)
2009-06-08 16:08:59 +00:00
_private_fields = ('id', 'movie')
2009-12-31 15:04:32 +00:00
#return available sort fields
#FIXME: should return mapping name -> verbose_name
def fields(self):
fields = []
2009-06-08 16:08:59 +00:00
for f in self._meta.fields:
if f.name not in self._private_fields:
2009-12-31 15:04:32 +00:00
name = f.verbose_name
name = name[0].capitalize() + name[1:]
fields.append(name)
return tuple(fields)
fields = classmethod(fields)
2009-06-08 16:08:59 +00:00
class AlternativeTitle(models.Model):
movie = models.ForeignKey(Movie, related_name='alternative_titles_all')
2009-06-08 16:08:59 +00:00
title = models.TextField()
2009-08-16 12:23:29 +00:00
type = models.CharField(max_length=1000)
manual = models.BooleanField(default=False)
2009-06-08 16:08:59 +00:00
class Meta:
ordering = ('title', )
def __unicode__(self):
return self.title
2009-08-16 12:23:29 +00:00
def json(self):
return (self.title, self.type)
2009-06-08 16:08:59 +00:00
class Person(models.Model):
name = models.CharField(max_length=200)
imdbId = models.CharField(max_length=7, blank=True)
name_sort = models.CharField(max_length=200)
movies = models.ManyToManyField(Movie, related_name='people', through='Cast')
class Meta:
ordering = ('name_sort', )
def __unicode__(self):
return self.name
def save(self, *args, **kwargs):
if not self.name_sort:
2010-07-07 22:46:41 +00:00
self.name_sort = ox.normalize.canonicalName(self.name)
2009-06-08 16:08:59 +00:00
super(Person, self).save(*args, **kwargs)
def get_or_create(model, name, imdbId=None):
if imdbId:
q = model.objects.filter(name=name, imdbId=imdbId)
else:
2009-08-01 14:14:54 +00:00
q = model.objects.all().filter(name=name)
2009-06-08 16:08:59 +00:00
if q.count() > 0:
o = q[0]
else:
o = model.objects.create(name=name)
if imdbId:
o.imdbId = imdbId
o.save()
return o
get_or_create = classmethod(get_or_create)
def json(self):
return self.name
class Cast(models.Model):
2009-12-31 15:04:32 +00:00
movie = models.ForeignKey(Movie, related_name='cast_relation')
2009-06-08 16:08:59 +00:00
person = models.ForeignKey(Person)
role = models.CharField(max_length=200)
2009-12-31 15:04:32 +00:00
character = models.CharField(max_length=1000, blank=True)
2009-06-08 16:08:59 +00:00
position = models.IntegerField()
manual = models.BooleanField(default=False)
2009-06-08 16:08:59 +00:00
class Meta:
ordering = ('position', 'person__name_sort')
def __unicode__(self):
return "%s <> %s" % (self.person, self.movie)
def link(self, movie, person, role, character, position, manual=False):
2009-06-08 16:08:59 +00:00
q = self.objects.filter(movie=movie, person=person, role=role, character=character)
if q.count() > 0:
link = q[0]
link.position = position
link.manual = manual
2009-06-08 16:08:59 +00:00
link.save()
else:
link = self()
link.movie=movie
link.person=person
link.role=role
link.character=character
link.position = position
link.manual = manual
2009-06-08 16:08:59 +00:00
link.save()
return link
link = classmethod(link)
def json(self):
return (self.person.json(), self.character)
class Country(models.Model):
2009-08-01 14:14:54 +00:00
name = models.CharField(max_length=200, unique=True)
movies = models.ManyToManyField(Movie, related_name='countries_all', through='MovieCountry')
2009-06-08 16:08:59 +00:00
class Meta:
2009-08-01 14:14:54 +00:00
#!! adding this to ordering, breaks:
# models.Country.objects.values("name").annotate(movies=Count('movies'))
#'moviecountry__position',
ordering = ('name', )
2009-06-08 16:08:59 +00:00
def __unicode__(self):
return self.name
def json(self):
return self.name
class MovieCountry(models.Model):
movie = models.ForeignKey(Movie)
country = models.ForeignKey(Country)
position = models.IntegerField()
manual = models.BooleanField(default=False)
2009-06-08 16:08:59 +00:00
class Meta:
ordering = ('position', 'country')
def __unicode__(self):
return "%s <> %s" % (self.country, self.movie)
def link(self, movie, country, position):
q = self.objects.filter(movie=movie, country=country)
if q.count() > 0:
link = q[0]
link.position = position
link.save()
else:
link = self()
link.movie=movie
link.country=country
link.position=position
link.save()
return link
link = classmethod(link)
class Language(models.Model):
2009-08-01 14:14:54 +00:00
name = models.CharField(max_length=200, unique=True)
movies = models.ManyToManyField(Movie, related_name='languages_all', through="MovieLanguage")
2009-06-08 16:08:59 +00:00
class Meta:
ordering = ('name', )
def __unicode__(self):
return self.name
def json(self):
return self.name
class MovieLanguage(models.Model):
movie = models.ForeignKey(Movie)
language = models.ForeignKey(Language)
position = models.IntegerField()
manual = models.BooleanField(default=False)
2009-06-08 16:08:59 +00:00
class Meta:
ordering = ('position', 'language')
def __unicode__(self):
return self.language.name
def link(self, movie, language, position):
q = self.objects.filter(movie=movie, language=language)
if q.count() > 0:
link = q[0]
link.position = position
link.save()
else:
link = self()
link.movie=movie
link.language=language
link.position=position
link.save()
return link
link = classmethod(link)
class Keyword(models.Model):
2009-08-01 14:14:54 +00:00
name = models.CharField(max_length=200, unique=True)
manual = models.BooleanField(default=False)
movies = models.ManyToManyField(Movie, related_name='keywords_all')
2009-06-08 16:08:59 +00:00
class Meta:
ordering = ('name', )
def __unicode__(self):
return self.name
def json(self):
return self.name
class Genre(models.Model):
2009-08-01 14:14:54 +00:00
name = models.CharField(max_length=200, unique=True)
manual = models.BooleanField(default=False)
movies = models.ManyToManyField(Movie, related_name='genres_all')
2009-06-08 16:08:59 +00:00
class Meta:
ordering = ('name', )
def __unicode__(self):
return self.name
def json(self):
return self.name
class Location(models.Model):
2009-08-01 14:14:54 +00:00
name = models.CharField(max_length=200, unique=True)
manual = models.BooleanField(default=False)
movies = models.ManyToManyField(Movie, related_name='locations_all')
2009-06-08 16:08:59 +00:00
#fixme: geo data
2009-08-16 12:23:29 +00:00
lat_sw = models.FloatField(default=0)
lng_sw = models.FloatField(default=0)
lat_ne = models.FloatField(default=0)
lng_ne = models.FloatField(default=0)
lat_center = models.FloatField(default=0)
lng_center = models.FloatField(default=0)
area = models.FloatField(default=-1)
2009-06-08 16:08:59 +00:00
class Meta:
ordering = ('name', )
def __unicode__(self):
return self.name
def json(self):
return self.name
class Trivia(models.Model):
trivia = models.TextField()
manual = models.BooleanField(default=False)
2009-06-08 16:08:59 +00:00
position = models.IntegerField()
movie = models.ForeignKey(Movie, related_name='trivia_all')
2009-06-08 16:08:59 +00:00
class Meta:
ordering = ('position', )
def __unicode__(self):
return self.trivia
def json(self):
2009-08-16 12:23:29 +00:00
trivia = self.trivia
2010-07-07 22:46:41 +00:00
trivia = ox.fixAmpersands(trivia)
2009-12-31 15:04:32 +00:00
trivia = re.sub('<a name="#tr\d{7}"></a> ', '', trivia)
2009-08-16 12:23:29 +00:00
trivia = re.sub('<a href="(/name/nm.*?)">(.*?)</a>', '<a href="/?f=name&amp;q=\\2">\\2</a>', trivia)
trivia = re.sub('<a href="/title/tt(.*?)/">(.*?)</a>', '<a href="/\\1">\\2</a>', trivia)
return trivia
class Connection(models.Model):
subject = models.ForeignKey(Movie, related_name='connections_all')
2009-08-16 12:23:29 +00:00
relation = models.CharField(max_length=512)
object = models.ForeignKey(Movie)
manual = models.BooleanField(default=False)
2009-08-16 12:23:29 +00:00
def get_or_create(model, subject, relation, object, reverse=True, manual=False):
2009-08-16 12:23:29 +00:00
q = model.objects.filter(subject=subject, relation=relation, object=object)
if q.count() > 0:
o = q[0]
else:
o = model.objects.create(subject=subject, relation=relation, object=object, manual=manual)
2009-08-16 12:23:29 +00:00
o.save()
if reverse:
_map = {
'Edited into': 'Edited from',
'Features': 'Featured in',
'Follows': 'Followed by',
'References': 'Referenced in',
'Remake of': 'Remade as',
'Spin off from': 'Spin off',
'Spoofs': 'Spoofed in',
'Version of': 'Version of',
2009-12-31 15:04:32 +00:00
'Alternate language version of': 'Alternate language version of',
2009-08-16 12:23:29 +00:00
}
if relation in _map.values():
for k in _map:
if _map[k] == relation:
reverse_relation = k
else:
reverse_relation = _map[relation]
o2 = model.get_or_create(object, reverse_relation, subject, reverse=False)
return o
get_or_create = classmethod(get_or_create)
def __unicode__(self):
return '%s %s %s' % (self.subject, self.relation, self.object)
2009-06-08 16:08:59 +00:00
class Review(models.Model):
movie = models.ForeignKey('Movie', related_name='reviews_all')
title = models.CharField(blank=True, max_length=2048)
url = models.CharField(blank=True, max_length=2048)
manual = models.BooleanField(default=False)
def __unicode__(self):
return self.title
def get_or_create(self, movie, url):
q = self.objects.filter(movie=movie, url=url)
if q.count() > 0:
o = q[0]
else:
o = self.objects.create(movie=movie, url=url)
o.save()
return o
get_or_create = classmethod(get_or_create)
def name(self):
for w in ReviewWhitelist.objects.all():
if w.url in self.url:
return w.name
return self.title
def json(self):
return (self.name(), self.url)
class ReviewWhitelist(models.Model):
name = models.CharField(max_length=255, unique=True)
url = models.CharField(max_length=255, unique=True)
class List(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
user = models.ForeignKey(User)
name = models.CharField(max_length=255, unique=True)
public = models.BooleanField(default=False)
movies = models.ManyToManyField(Movie, related_name='lists', through='ListItem')
def add(self, movie):
q = self.movies.filter(id=movie.id)
if q.count() == 0:
l = ListItem()
l.list = self
l.movie = movie
l.save()
def remove(self, movie):
self.ListItem.objects.all().filter(movie=movie, list=self).delete()
def __unicode__(self):
return u'%s (%s)' % (self.title, unicode(self.user))
def editable(self, user):
#FIXME: make permissions work
2010-02-06 08:24:39 +00:00
if self.user == user or user.has_perm('Ox.admin'):
return True
return False
class ListItem(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
list = models.ForeignKey(List)
movie = models.ForeignKey(Movie)
def __unicode__(self):
return u'%s in %s' % (unicode(self.movie), unicode(self.list))
def stream_path(f, size):
name = "%s.%s" % (size, 'ogv')
url_hash = f.oshash
2010-02-06 08:24:39 +00:00
return os.path.join(url_hash[:2], url_hash[2:4], url_hash[4:6], url_hash, name)
2010-02-06 08:24:39 +00:00
def timeline_path(f):
name = "timeline.png"
2009-12-31 15:04:32 +00:00
url_hash = f.oshash
2010-02-06 08:24:39 +00:00
return os.path.join(url_hash[:2], url_hash[2:4], url_hash[4:6], url_hash, name)
def frame_path(f):
2010-07-07 22:46:41 +00:00
position = ox.formatDuration(f.position*1000).replace(':', '.')
2010-02-06 08:24:39 +00:00
name = "%s.%s" % (position, 'png')
url_hash = f.file.oshash
return os.path.join(url_hash[:2], url_hash[2:4], url_hash[4:6], url_hash, 'frames', name)
2009-12-31 15:04:32 +00:00
FILE_TYPES = (
(0, 'unknown'),
(1, 'video'),
(2, 'audio'),
(3, 'subtitle'),
)
2009-08-16 12:23:29 +00:00
class File(models.Model):
2009-06-08 16:08:59 +00:00
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
2010-01-16 20:42:11 +00:00
published = models.DateTimeField(default=datetime.now, editable=False)
2009-12-31 15:04:32 +00:00
verified = models.BooleanField(default=False)
2009-06-08 16:08:59 +00:00
2009-08-01 14:14:54 +00:00
oshash = models.CharField(blank=True, unique=True, max_length=16)
2009-12-31 15:04:32 +00:00
sha1 = models.CharField(blank=True, null=True, unique=True, max_length=40)
md5 = models.CharField(blank=True, null=True, unique=True, max_length=32)
2009-06-08 16:08:59 +00:00
2009-12-31 15:04:32 +00:00
movie = models.ForeignKey(Movie, related_name="files", default=None, null=True)
2009-06-08 16:08:59 +00:00
2009-12-31 15:04:32 +00:00
type = models.IntegerField(default=0, choices=FILE_TYPES)
info = fields.DictField(default={})
#FIXME: why do i need those in the db? could just have them in info
2009-12-31 15:04:32 +00:00
path = models.CharField(blank=True, max_length=2048)
size = models.BigIntegerField(default=-1)
2009-06-08 16:08:59 +00:00
duration = models.FloatField(default=-1)
2009-06-08 16:08:59 +00:00
video_codec = models.CharField(blank=True, max_length=256)
pixel_format = models.CharField(blank=True, max_length=256)
width = models.IntegerField(default=-1)
height = models.IntegerField(default=-1)
pixel_aspect_ratio = models.CharField(blank=True, max_length=256)
display_aspect_ratio = models.CharField(blank=True, max_length=256)
framerate = models.CharField(blank=True, max_length=256)
audio_codec = models.CharField(blank=True, max_length=256)
samplerate = models.IntegerField(default=-1)
channels = models.IntegerField(default=-1)
#computed values
bpp = models.FloatField(default=-1)
2009-12-31 15:04:32 +00:00
pixels = models.BigIntegerField(default=0)
2009-06-08 16:08:59 +00:00
2009-08-16 12:23:29 +00:00
part = models.IntegerField(default=0)
2009-12-31 15:04:32 +00:00
needs_data = models.BooleanField(default=True)
#stream related fields
available = models.BooleanField(default=False)
2010-02-08 10:26:25 +00:00
stream_low = models.FileField(default=None, upload_to=lambda f, x: stream_path(f, 'low'))
stream_mid = models.FileField(default=None, upload_to=lambda f, x: stream_path(f, 'mid'))
stream_high = models.FileField(default=None, upload_to=lambda f, x: stream_path(f, 'high'))
def timeline_base_url(self):
2010-02-08 10:26:25 +00:00
return '%s/timeline' % os.path.dirname(self.stream_low.url)
2009-12-31 15:04:32 +00:00
def save_chunk(self, chunk, name='video.ogv'):
if not self.available:
2010-02-08 10:26:25 +00:00
#FIXME: this should use stream_low or stream_high depending on configuration
video = getattr(self, 'stream_%s'%settings.VIDEO_PROFILE)
if not video:
2010-02-03 11:59:11 +00:00
video.save(name, chunk)
self.save()
else:
f = open(video.path, 'a')
2010-02-03 11:59:11 +00:00
f.write(chunk.read())
f.close()
return True
print "somehing failed, not sure what?", self.available
return False
objects = managers.FileManager()
2009-06-08 16:08:59 +00:00
def __unicode__(self):
2009-12-31 15:04:32 +00:00
return "%s (%s)" % (self.path, self.oshash)
def update(self, data=None):
"""
only add, do not overwrite keys in file
"""
if data and not self.info:
self.info = data
_video_map = {
'codec': 'video_codec',
}
if 'video' in self.info and self.info['video']:
for key in ('codec', 'pixel_format', 'width', 'height',
'pixel_aspect_ratio', 'display_aspect_ratio', 'framerate'):
if key in self.info['video'][0]:
setattr(self, _video_map.get(key, key), self.info['video'][0][key])
_audio_map = {
'codec': 'audio_codec',
}
if 'audio' in self.info and self.info['audio']:
for key in ('codec', 'samplerate', 'channels'):
if key in self.info['audio'][0]:
setattr(self, _audio_map.get(key, key), self.info['audio'][0][key])
for key in ('duration', 'size', 'sha1', 'md5'):
if key in self.info:
setattr(self, key, self.info[key])
#detect and assign type based on extension or detected video track
if os.path.splitext(self.info['path'])[-1] in ('.rar', '.sub', '.idx'):
self.type = 0
elif 'video' in self.info:
self.type = 1
elif os.path.splitext(self.info['path'])[-1] in ('.mp3', '.oga'):
self.type = 2
elif os.path.splitext(self.info['path'])[-1] in ('.srt', ):
self.type = 3
#FIXME: this should be computed and not submitted path
self.path = self.info['path']
self.save()
2010-01-16 20:42:11 +00:00
def findMovie(self):
info = utils.parsePath(self.path)
self.movie = getMovie(info)
self.save()
2010-02-06 08:24:39 +00:00
def extract_timeline(self):
2010-02-08 10:26:25 +00:00
if self.stream_high:
video = self.stream_high.path
elif self.stream_mid:
video = self.stream_mid.path
elif self.stream_low:
video = self.stream_low.path
2010-02-06 08:24:39 +00:00
else:
return False
prefix = os.path.join(os.path.dirname(video), 'timeline')
cmd = ['oxtimeline', '-i', video, '-o', prefix]
p = subprocess.Popen(cmd)
p.wait()
return p.returncode == 0
def extract_video(self):
ogg = Firefogg()
2010-02-16 10:13:44 +00:00
source = None
profiles = []
2010-02-08 10:26:25 +00:00
if self.stream_high:
2010-02-16 10:13:44 +00:00
source = self.stream_high
profiles = ['low', 'mid']
2010-02-08 10:26:25 +00:00
elif self.stream_mid:
2010-02-16 10:13:44 +00:00
source = self.stream_mid
profiles = ['low', ]
for profile in profiles:
output = getattr(self, 'stream_%s'%profile)
output.name = stream_path(self, profile)
output.save()
ogg.encode(source.path, output.path, settings.VIDEO_ENCODING[profile])
2010-01-22 23:25:48 +00:00
def extract(self):
#FIXME: do stuff, like create timeline or create smaller videos etc
2010-02-06 08:24:39 +00:00
self.extract_video()
self.extract_timeline()
2010-01-22 23:25:48 +00:00
return
2010-02-16 10:13:44 +00:00
def frame(self, position, width=128):
videoFile = getattr(self, 'stream_%s'%settings.VIDEO_PROFILE).path
frameFolder = os.path.join(os.path.dirname(videoFile), 'frames')
if position<= self.duration:
return extract.frame(videoFile, position, frameFolder, width)
return None
2010-02-16 10:13:44 +00:00
def editable(self, user):
2010-02-06 08:24:39 +00:00
'''
#FIXME: this should use a queryset!!!
archives = []
for a in self.archive_files.all():
archives.append(a.archive)
users = []
for a in archives:
users += a.users.all()
return user in users
'''
return self.archive_files.filter(archive__users__id=user.id).count() > 0
2010-02-06 08:24:39 +00:00
class Frame(models.Model):
2009-12-31 15:04:32 +00:00
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
2010-02-06 08:24:39 +00:00
file = models.ForeignKey(File, related_name="frames")
2009-12-31 15:04:32 +00:00
position = models.FloatField()
2010-02-06 08:24:39 +00:00
frame = models.ImageField(default=None, null=True, upload_to=lambda f, x: frame_path(f))
#FIXME: frame path should be renamed on save to match current position
2009-12-31 15:04:32 +00:00
def __unicode__(self):
return '%s at %s' % (self.file, self.position)
2009-06-08 16:08:59 +00:00
2010-02-06 08:24:39 +00:00
class Layer(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
user = models.ForeignKey(User)
movie = models.ForeignKey(Movie)
#seconds
time_in = models.FloatField(default=-1)
time_out = models.FloatField(default=-1)
type = models.CharField(blank=True, max_length=255)
value = models.TextField()
#FIXME: relational layers, Locations, clips etc
#location = models.ForeignKey('Location', default=None)
def editable(self, user):
2010-02-06 08:24:39 +00:00
if user.is_authenticated():
if obj.user == user.id or user.has_perm('0x.admin'):
return True
if user.groups.filter(id__in=obj.groups.all()).count() > 0:
return True
return False
2009-08-16 12:23:29 +00:00
class Archive(models.Model):
2009-06-08 16:08:59 +00:00
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
2009-09-07 15:43:42 +00:00
name = models.CharField(max_length=255, unique=True)
public = models.BooleanField(default=False)
2009-08-16 12:23:29 +00:00
users = models.ManyToManyField(User, related_name='archives')
2009-12-31 15:04:32 +00:00
def __unicode__(self):
return '%s' % (self.name)
2009-08-16 12:23:29 +00:00
def editable(self, user):
return self.users.filter(id=user.id).count() > 0
2009-08-16 12:23:29 +00:00
class ArchiveFile(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
archive = models.ForeignKey(Archive, related_name='files')
2010-02-06 08:24:39 +00:00
file = models.ForeignKey(File, related_name='archive_files')
2009-06-08 16:08:59 +00:00
path = models.CharField(blank=True, max_length=2048)
2009-08-16 12:23:29 +00:00
objects = managers.ArchiveFileManager()
2009-12-31 15:04:32 +00:00
2009-08-16 12:23:29 +00:00
def update(self, data):
"""
only add, do not overwrite keys in file
2009-08-16 12:23:29 +00:00
"""
2009-12-31 15:04:32 +00:00
self.file.update(data)
2009-08-16 12:23:29 +00:00
self.path = data.get('path', '')
self.save()
def get_or_create(model, archive, oshash):
try:
f = model.objects.by_oshash(oshash=oshash)
except model.DoesNotExist:
file, created = File.objects.get_or_create(oshash)
if created:
file.save()
2009-12-31 15:04:32 +00:00
f = model.objects.create(archive=archive, file=file)
2009-08-16 12:23:29 +00:00
f.save()
return f
get_or_create = classmethod(get_or_create)
2009-06-08 16:08:59 +00:00
def __unicode__(self):
2009-12-31 15:04:32 +00:00
return '%s (%s)' % (self.path, unicode(self.archive))
2009-06-08 16:08:59 +00:00
2010-02-06 08:24:39 +00:00
def editable(self, user):
return self.archive.editable(user)
2009-08-01 14:14:54 +00:00
2010-02-03 11:59:11 +00:00
class Collection(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
2010-02-06 08:24:39 +00:00
users = models.ManyToManyField(User, related_name='collections')
2010-02-03 11:59:11 +00:00
name = models.CharField(blank=True, max_length=2048)
subdomain = models.CharField(unique=True, max_length=2048)
movies = models.ForeignKey(Movie)
2010-02-06 08:24:39 +00:00
def editable(self, user):
return self.users.filter(id=user.id).count() > 0