From 9defcd3c9f8a2218fcca3842f45b714ceb3ea54c Mon Sep 17 00:00:00 2001 From: j <0x006A@0x2620.org> Date: Mon, 12 Jul 2010 16:56:14 +0200 Subject: [PATCH] - simplify metadata db structure - better sort values if field is empty - switch to python-ox, faster imdb import - move files backend into own app --- pandora/archive/__init__.py | 0 pandora/archive/admin.py | 25 + pandora/archive/migrations/0001_initial.py | 240 ++++++ pandora/archive/migrations/__init__.py | 0 pandora/archive/models.py | 182 +++++ pandora/archive/tests.py | 23 + pandora/archive/views.py | 164 ++++ pandora/backend/admin.py | 24 +- pandora/backend/forms.py | 4 +- pandora/backend/managers.py | 6 +- pandora/backend/migrations/0003_archive.py | 647 +++++++++++++++ pandora/backend/models.py | 898 ++++----------------- pandora/backend/tasks.py | 11 +- pandora/backend/utils.py | 29 +- pandora/backend/views.py | 153 +--- pandora/settings.py | 1 + 16 files changed, 1493 insertions(+), 914 deletions(-) create mode 100644 pandora/archive/__init__.py create mode 100644 pandora/archive/admin.py create mode 100644 pandora/archive/migrations/0001_initial.py create mode 100644 pandora/archive/migrations/__init__.py create mode 100644 pandora/archive/models.py create mode 100644 pandora/archive/tests.py create mode 100644 pandora/archive/views.py create mode 100644 pandora/backend/migrations/0003_archive.py diff --git a/pandora/archive/__init__.py b/pandora/archive/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pandora/archive/admin.py b/pandora/archive/admin.py new file mode 100644 index 000000000..1cebf187f --- /dev/null +++ b/pandora/archive/admin.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# vi:si:et:sw=4:sts=4:ts=4 + +from django.contrib import admin + +#from forms import FileAdminForm, MovieAdminForm, ArchiveFileAdminForm +import models + +class FileAdmin(admin.ModelAdmin): + search_fields = ['path', 'video_codec'] + + #form = FileAdminForm + +admin.site.register(models.File, FileAdmin) + +class FileInstanceAdmin(admin.ModelAdmin): + search_fields = ['path', 'archive__name'] + #form = ArchiveFileAdminForm + +admin.site.register(models.FileInstance, FileInstanceAdmin) + +class ArchiveAdmin(admin.ModelAdmin): + search_fields = ['name'] +admin.site.register(models.Archive, ArchiveAdmin) + diff --git a/pandora/archive/migrations/0001_initial.py b/pandora/archive/migrations/0001_initial.py new file mode 100644 index 000000000..653749f3d --- /dev/null +++ b/pandora/archive/migrations/0001_initial.py @@ -0,0 +1,240 @@ +# -*- coding: utf-8 -*- + +from south.db import db +from django.db import models +from archive.models import * + +class Migration: + + def forwards(self, orm): + + # Adding model 'Volume' + db.create_table('archive_volume', ( + ('id', orm['archive.Volume:id']), + ('start', orm['archive.Volume:start']), + ('end', orm['archive.Volume:end']), + ('name', orm['archive.Volume:name']), + )) + db.send_create_signal('archive', ['Volume']) + + # Adding model 'Frame' + db.create_table('archive_frame', ( + ('id', orm['archive.Frame:id']), + ('created', orm['archive.Frame:created']), + ('modified', orm['archive.Frame:modified']), + ('file', orm['archive.Frame:file']), + ('position', orm['archive.Frame:position']), + ('frame', orm['archive.Frame:frame']), + )) + db.send_create_signal('archive', ['Frame']) + + # Adding model 'Archive' + db.create_table('archive_archive', ( + ('id', orm['archive.Archive:id']), + ('created', orm['archive.Archive:created']), + ('modified', orm['archive.Archive:modified']), + ('published', orm['archive.Archive:published']), + ('name', orm['archive.Archive:name']), + ('user', orm['archive.Archive:user']), + )) + db.send_create_signal('archive', ['Archive']) + + # Adding model 'File' + db.create_table('archive_file', ( + ('id', orm['archive.File:id']), + ('created', orm['archive.File:created']), + ('modified', orm['archive.File:modified']), + ('verified', orm['archive.File:verified']), + ('oshash', orm['archive.File:oshash']), + ('movie', orm['archive.File:movie']), + ('name', orm['archive.File:name']), + ('sort_name', orm['archive.File:sort_name']), + ('part', orm['archive.File:part']), + ('version', orm['archive.File:version']), + ('language', orm['archive.File:language']), + ('season', orm['archive.File:season']), + ('episode', orm['archive.File:episode']), + ('size', orm['archive.File:size']), + ('duration', orm['archive.File:duration']), + ('info', orm['archive.File:info']), + ('video_codec', orm['archive.File:video_codec']), + ('pixel_format', orm['archive.File:pixel_format']), + ('display_aspect_ratio', orm['archive.File:display_aspect_ratio']), + ('width', orm['archive.File:width']), + ('height', orm['archive.File:height']), + ('framerate', orm['archive.File:framerate']), + ('audio_codec', orm['archive.File:audio_codec']), + ('channels', orm['archive.File:channels']), + ('samplerate', orm['archive.File:samplerate']), + ('bits_per_pixel', orm['archive.File:bits_per_pixel']), + ('pixels', orm['archive.File:pixels']), + ('is_audio', orm['archive.File:is_audio']), + ('is_video', orm['archive.File:is_video']), + ('is_extra', orm['archive.File:is_extra']), + ('is_main', orm['archive.File:is_main']), + ('is_subtitle', orm['archive.File:is_subtitle']), + ('is_version', orm['archive.File:is_version']), + )) + db.send_create_signal('archive', ['File']) + + # Adding model 'FileInstance' + db.create_table('archive_fileinstance', ( + ('id', orm['archive.FileInstance:id']), + ('created', orm['archive.FileInstance:created']), + ('modified', orm['archive.FileInstance:modified']), + ('published', orm['archive.FileInstance:published']), + ('accessed', orm['archive.FileInstance:accessed']), + ('path', orm['archive.FileInstance:path']), + ('folder', orm['archive.FileInstance:folder']), + ('file', orm['archive.FileInstance:file']), + ('archive', orm['archive.FileInstance:archive']), + )) + db.send_create_signal('archive', ['FileInstance']) + + + + def backwards(self, orm): + + # Deleting model 'Volume' + db.delete_table('archive_volume') + + # Deleting model 'Frame' + db.delete_table('archive_frame') + + # Deleting model 'Archive' + db.delete_table('archive_archive') + + # Deleting model 'File' + db.delete_table('archive_file') + + # Deleting model 'FileInstance' + db.delete_table('archive_fileinstance') + + + + models = { + 'archive.archive': { + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'published': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'owned_archives'", 'to': "orm['auth.User']"}), + 'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'}), + 'volumes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['archive.Volume']", 'symmetrical': 'False'}) + }, + 'archive.file': { + 'audio_codec': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'bits_per_pixel': ('django.db.models.fields.FloatField', [], {'default': '-1'}), + 'channels': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'display_aspect_ratio': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'duration': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'episode': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), + 'framerate': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'height': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'info': ('fields.DictField', [], {'default': '{}'}), + 'is_audio': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_extra': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_main': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_subtitle': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_version': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_video': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'language': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '8'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'movie': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'files'", 'to': "orm['backend.Movie']"}), + 'name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '2048'}), + 'oshash': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'part': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), + 'pixel_format': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'pixels': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + 'samplerate': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'season': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), + 'size': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + 'sort_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '2048'}), + 'verified': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), + 'video_codec': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'width': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'archive.fileinstance': { + 'accessed': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'archive': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'files'", 'to': "orm['archive.Archive']"}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'file': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'instances'", 'to': "orm['archive.File']"}), + 'folder': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'max_length': '2048'}), + 'published': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}) + }, + 'archive.frame': { + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'file': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'frames'", 'to': "orm['archive.File']"}), + 'frame': ('django.db.models.fields.files.ImageField', [], {'default': 'None', 'max_length': '100', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'position': ('django.db.models.fields.FloatField', [], {}) + }, + 'archive.volume': { + 'end': ('django.db.models.fields.CharField', [], {'max_length': '1'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'start': ('django.db.models.fields.CharField', [], {'max_length': '1'}) + }, + 'auth.group': { + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)"}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'backend.movie': { + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'imdb': ('fields.DictField', [], {'default': '{}', 'editable': 'False'}), + 'json': ('fields.DictField', [], {'default': '{}', 'editable': 'False'}), + 'metadata': ('fields.DictField', [], {'default': '{}', 'editable': 'False'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'movieId': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'blank': 'True'}), + 'oxdbId': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '42', 'blank': 'True'}), + 'poster_height': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'poster_width': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'published': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'scene_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'stream_high': ('django.db.models.fields.files.FileField', [], {'default': 'None', 'max_length': '100', 'blank': 'True'}), + 'stream_low': ('django.db.models.fields.files.FileField', [], {'default': 'None', 'max_length': '100', 'blank': 'True'}), + 'stream_mid': ('django.db.models.fields.files.FileField', [], {'default': 'None', 'max_length': '100', 'blank': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['archive'] diff --git a/pandora/archive/migrations/__init__.py b/pandora/archive/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pandora/archive/models.py b/pandora/archive/models.py new file mode 100644 index 000000000..bba62bb4b --- /dev/null +++ b/pandora/archive/models.py @@ -0,0 +1,182 @@ +# -*- coding: utf-8 -*- +# vi:si:et:sw=4:sts=4:ts=4 +from datetime import datetime +import os.path +import random +import re +from decimal import Decimal + +from django.db import models +from django.db.models import Q +from django.contrib.auth.models import User +from django.core.files.base import ContentFile +from django.utils import simplejson as json +from django.conf import settings + +from oxdjango import fields +import ox +from ox import stripTags +from ox.normalize import canonicalTitle, canonicalName +from firefogg import Firefogg + +from backend import utils +from backend import extract +from pandora.backend.models import Movie + +def parse_decimal(string): + string = string.replace(':', '/') + if '/' not in string: + string = '%s/1' % string + d = string.split('/') + return Decimal(d[0]) / Decimal(d[1]) + +#ARCHIVE stuff +class Volume(models.Model): + start = models.CharField(max_length=1) + end = models.CharField(max_length=1) + name = models.CharField(max_length=255) + +class Archive(models.Model): + created = models.DateTimeField(auto_now_add=True) + modified = models.DateTimeField(auto_now=True) + published = models.DateTimeField(default=datetime.now, editable=False) + + name = models.CharField(max_length=255) + user = models.ForeignKey(User, related_name='owned_archives') + + users = models.ManyToManyField(User, related_name='archives') + volumes = models.ManyToManyField(Volume, related_name='archives') + + def editable(self, user): + return self.users.filter(username=user.username).count() > 0 + +class File(models.Model): + created = models.DateTimeField(auto_now_add=True) + modified = models.DateTimeField(auto_now=True) + + verified = models.BooleanField(default = False) + + oshash = models.CharField(max_length=16) + movie = models.ForeignKey(Movie, related_name='files') + + name = models.CharField(max_length=2048, default="") # canoncial path/file + sort_name = models.CharField(max_length=2048, default="") # sort path/file name + + part = models.CharField(default="", max_length=255) + version = models.CharField(default="", max_length=255) # sort path/file name + language = models.CharField(default="", max_length=8) + + season = models.IntegerField(default = -1) + episode = models.IntegerField(default = -1) + + size = models.BigIntegerField(default = 0) + duration = models.IntegerField(default = 0) + + info = fields.DictField(default={}) + + video_codec = models.CharField(max_length=255) + pixel_format = models.CharField(max_length=255) + display_aspect_ratio = models.CharField(max_length=255) + width = models.IntegerField(default = 0) + height = models.IntegerField(default = 0) + framerate = models.CharField(max_length=255) + + audio_codec = models.CharField(max_length=255) + channels = models.IntegerField(default = 0) + samplerate = models.IntegerField(default = 0) + + bits_per_pixel = models.FloatField(default=-1) + pixels = models.BigIntegerField(default=0) + + is_audio = models.BooleanField(default = False) + is_video = models.BooleanField(default = False) + is_extra = models.BooleanField(default = False) + is_main = models.BooleanField(default = False) + is_subtitle = models.BooleanField(default = False) + is_version = models.BooleanField(default = False) + + def __unicode__(self): + return self.name + + def save(self, *args, **kwargs): + if self.name and not self.sort_name: + self.sort_name = canonicalTitle(self.name) + if self.info: + for key in ('duration', 'size'): + setattr(self, key, self.info.get(key, 0)) + + if 'video' in self.info and self.info['video']: + self.video_codec = self.info['video'][0]['codec'] + self.width = self.info['video'][0]['width'] + self.height = self.info['video'][0]['height'] + self.framerate = self.info['video'][0]['framerate'] + if 'display_aspect_ratio' in self.info['video'][0]: + self.display_aspect_ratio = self.info['video'][0]['display_aspect_ratio'] + else: + self.display_aspect_ratio = "%s:%s" % (self.width, self.height) + self.is_video = True + self.is_audio = False + else: + self.is_video = False + if 'audio' in self.info and self.info['audio']: + self.audio_codec = self.info['audio'][0]['codec'] + self.samplerate = self.info['audio'][0]['samplerate'] + self.channels = self.info['audio'][0]['channels'] + + if not self.is_video: + self.is_audio = True + else: + self.is_audio = False + + if self.framerate: + self.pixels = int(self.width * self.height * float(parse_decimal(self.framerate)) * self.duration) + + if not self.is_audio and not self.is_video and self.name.endswith('.srt'): + self.is_subtitle = True + + if self.name and self.name.startswith('Extra/'): + self.is_extra = True + self.is_main = False + else: + self.is_extra = False + self.is_main = True + + super(File, self).save(*args, **kwargs) + + def json(self): + r = {} + for k in self: + r[k] = unicode(self[k]) + return r + +class FileInstance(models.Model): + created = models.DateTimeField(auto_now_add=True) + modified = models.DateTimeField(auto_now=True) + published = models.DateTimeField(default=datetime.now, editable=False) + accessed = models.DateTimeField(default=datetime.now, editable=False) + + path = models.CharField(max_length=2048) + folder = models.CharField(max_length=255) + + file = models.ForeignKey(File, related_name='instances') + archive = models.ForeignKey(Archive, related_name='files') + + def __unicode__(self): + return u'%s <%s> in %s'% (self.path, self.oshash, self.archive.name) + + @property + def movieId(self): + return File.objects.get(oshash=self.oshash).movieId + +class Frame(models.Model): + created = models.DateTimeField(auto_now_add=True) + modified = models.DateTimeField(auto_now=True) + file = models.ForeignKey(File, related_name="frames") + position = models.FloatField() + 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 + + def __unicode__(self): + return '%s at %s' % (self.file, self.position) + diff --git a/pandora/archive/tests.py b/pandora/archive/tests.py new file mode 100644 index 000000000..2247054b3 --- /dev/null +++ b/pandora/archive/tests.py @@ -0,0 +1,23 @@ +""" +This file demonstrates two different styles of tests (one doctest and one +unittest). These will both pass when you run "manage.py test". + +Replace these with more appropriate tests for your application. +""" + +from django.test import TestCase + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.failUnlessEqual(1 + 1, 2) + +__test__ = {"doctest": """ +Another way to test that 1 + 1 is equal to 2. + +>>> 1 + 1 == 2 +True +"""} + diff --git a/pandora/archive/views.py b/pandora/archive/views.py new file mode 100644 index 000000000..c3d8fd9a4 --- /dev/null +++ b/pandora/archive/views.py @@ -0,0 +1,164 @@ +# -*- coding: utf-8 -*- +# vi:si:et:sw=4:sts=4:ts=4 +from __future__ import division +import os.path +import re +from datetime import datetime +from urllib2 import unquote +import mimetypes + +from django import forms +from django.core.paginator import Paginator +from django.contrib.auth.decorators import login_required +from django.contrib.auth.models import User +from django.db.models import Q, Avg, Count, Sum +from django.http import HttpResponse, Http404 +from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404 +from django.template import RequestContext +from django.conf import settings + +try: + import simplejson as json +except ImportError: + from django.utils import simplejson as json + +from oxdjango.decorators import login_required_json +from oxdjango.shortcuts import render_to_json_response, get_object_or_404_json, json_response +from oxdjango.http import HttpFileResponse +import ox + +import models + +from backend.utils import oxid, parsePath +import backend.models + + + +#@login_required_json +def api_update(request): + ''' + param data + {archive: string, files: json} + return {'status': {'code': int, 'text': string}, + 'data': {info: object, rename: object}} + ''' + data = json.loads(request.POST['data']) + archive = data['archive'] + folder = data['folder'] + files = data['files'] + needs_data = [] + rename = [] + archive, created = models.Archive.objects.get_or_create(name=archive, user=request.user) + if archive.editable(request.user): + print 'editing' + same_folder = models.FileInstance.objects.filter(folder=folder) + if same_folder.count() > 0: + movie = same_folder[0].file.movie + else: + movie = None + for filename in files: + data = files[filename] + oshash = data['oshash'] + path = os.path.join(folder, filename) + + instance = models.FileInstance.objects.filter(file__oshash=oshash) + if instance.count()>0: + instance = instance[0] + if path != instance.path: #file was movied + instance.path = path + instance.folder = folder + f.save() + print "file movied, so other shit" + else: + #look if oshash is known + f = models.File.objects.filter(oshash=oshash) + if f.count() > 0: + f = f[0] + instance = models.FileInstance() + instance.file = f + instance.path=data['path'] + instance.folder=folder + instance.save() + movie = f.movie + #new oshash, add to database + else: + if not movie: + movie_info = parsePath(folder) + movie = backend.models.getMovie(movie_info) + f = models.File() + f.oshash = oshash + f.info = data + del f.info['oshash'] + f.name = filename + f.movie = movie + f.save() + instance = models.FileInstance() + instance.archive = archive + instance.file = f + instance.path = path + instance.folder = folder + instance.save() + + response = json_response({'info': needs_data, 'rename': rename}) + else: + response = json_response(status=403, text='permission denied') + return render_to_json_response(response) + +@login_required_json +def api_addArchive(request): + ''' + ARCHIVE API NEEDS CLEANUP + param data + {name: string} + return {'status': {'code': int, 'text': string}, + 'data': {}} + ''' + data = json.loads(request.POST['data']) + try: + archive = models.Archive.objects.get(name=data['name']) + response = {'status': {'code': 401, 'text': 'archive with this name exists'}} + except models.Archive.DoesNotExist: + archive = models.Archive(name=data['name']) + archive.user = request.user + archive.save() + archive.users.add(request.user) + response = json_response({}) + response['status']['text'] = 'archive created' + return render_to_json_response(response) + +@login_required_json +def api_editArchive(request): + ''' + ARCHIVE API NEEDS CLEANUP + param data + {id: string, key: value,..} + return {'status': {'code': int, 'text': string}, + 'data': {}} + ''' + data = json.loads(request.POST['data']) + item = get_object_or_404_json(models.Archive, name=data['name']) + if item.editable(request.user): + response = json_response(status=501, text='not implemented') + item.edit(data) + else: + response = json_response(status=403, text='permission denied') + return render_to_json_response(response) + +@login_required_json +def api_removeArchive(request): + ''' + ARCHIVE API NEEDS CLEANUP + param data + string id + + return {'status': {'code': int, 'text': string}} + ''' + response = json_response({}) + itemId = json.loads(request.POST['data']) + item = get_object_or_404_json(models.Archive, movieId=itemId) + if item.editable(request.user): + response = json_response(status=501, text='not implemented') + else: + response = json_response(status=403, text='permission denied') + return render_to_json_response(response) + diff --git a/pandora/backend/admin.py b/pandora/backend/admin.py index fa1d2f088..e9193531d 100644 --- a/pandora/backend/admin.py +++ b/pandora/backend/admin.py @@ -3,41 +3,27 @@ from django.contrib import admin -from forms import FileAdminForm, MovieAdminForm, ArchiveFileAdminForm +#from forms import FileAdminForm, MovieAdminForm, ArchiveFileAdminForm import models + +''' #class MovieImdbAdmin(admin.ModelAdmin): # search_fields = ['imdbId', 'title'] #admin.site.register(models.MovieImdb, MovieImdbAdmin) - class MovieImdbInline(admin.StackedInline): model = models.MovieImdb class MovieOxdbInline(admin.StackedInline): model = models.MovieOxdb +''' class MovieAdmin(admin.ModelAdmin): search_fields = ['movieId', 'imdb__title', 'oxdb__title'] - form = MovieAdminForm + #form = MovieAdminForm #inlines = [MovieImdbInline, MovieOxdbInline] admin.site.register(models.Movie, MovieAdmin) -class FileAdmin(admin.ModelAdmin): - search_fields = ['path', 'video_codec'] - - form = FileAdminForm - -admin.site.register(models.File, FileAdmin) - -class ArchiveFileAdmin(admin.ModelAdmin): - search_fields = ['path', 'archive__name'] - form = ArchiveFileAdminForm - -admin.site.register(models.ArchiveFile, ArchiveFileAdmin) - -class ArchiveAdmin(admin.ModelAdmin): - search_fields = ['name'] -admin.site.register(models.Archive, ArchiveAdmin) diff --git a/pandora/backend/forms.py b/pandora/backend/forms.py index 15cc3d208..dd2c55095 100644 --- a/pandora/backend/forms.py +++ b/pandora/backend/forms.py @@ -10,7 +10,7 @@ ajax_filtered_js = ( settings.STATIC_URL + 'js/jquery/jquery.js', settings.STATIC_URL + 'js/ajax_filtered_fields.js', ) - +""" class FileAdminForm(forms.ModelForm): movie = ForeignKeyByLetter(models.Movie, field_name='imdb__title') @@ -39,4 +39,4 @@ class MovieAdminForm(forms.ModelForm): class Media: js = ajax_filtered_js - +""" diff --git a/pandora/backend/managers.py b/pandora/backend/managers.py index 7dff3d822..bc563701a 100644 --- a/pandora/backend/managers.py +++ b/pandora/backend/managers.py @@ -46,7 +46,7 @@ class MovieManager(Manager): query: { conditions: [ { - value: "war"" + value: "war" } { key: "year", @@ -59,10 +59,10 @@ class MovieManager(Manager): operator: "^" } ], - operator: "&" + operator: "," } ''' - query_operator = data['query'].get('operator', '&') + query_operator = data['query'].get('operator', ',') conditions = [] for condition in data['query']['conditions']: k = condition.get('key', 'all') diff --git a/pandora/backend/migrations/0003_archive.py b/pandora/backend/migrations/0003_archive.py new file mode 100644 index 000000000..fa9079d51 --- /dev/null +++ b/pandora/backend/migrations/0003_archive.py @@ -0,0 +1,647 @@ +# -*- coding: utf-8 -*- + +from south.db import db +from django.db import models +from backend.models import * + +class Migration: + + def forwards(self, orm): + + # Deleting unique_together for [imdb] on movie. + db.delete_unique('backend_movie', ['imdb_id']) + + # Adding model 'Facet' + db.create_table('backend_facet', ( + ('id', orm['backend.facet:id']), + ('movie', orm['backend.facet:movie']), + ('key', orm['backend.facet:key']), + ('value', orm['backend.facet:value']), + ('value_sort', orm['backend.facet:value_sort']), + )) + db.send_create_signal('backend', ['Facet']) + + # Adding field 'Movie.metadata' + db.add_column('backend_movie', 'metadata', orm['backend.movie:metadata']) + + # Adding field 'MovieSort.director_desc' + db.add_column('backend_moviesort', 'director_desc', orm['backend.moviesort:director_desc']) + + # Adding field 'MovieSort.writer_desc' + db.add_column('backend_moviesort', 'writer_desc', orm['backend.moviesort:writer_desc']) + + # Adding field 'MovieSort.country_desc' + db.add_column('backend_moviesort', 'country_desc', orm['backend.moviesort:country_desc']) + + # Adding field 'MovieFind.location' + db.add_column('backend_moviefind', 'location', orm['backend.moviefind:location']) + + # Adding field 'MovieFind.actor' + db.add_column('backend_moviefind', 'actor', orm['backend.moviefind:actor']) + + # Adding field 'MovieSort.producer_desc' + db.add_column('backend_moviesort', 'producer_desc', orm['backend.moviesort:producer_desc']) + + # Adding field 'MovieSort.cinematographer_desc' + db.add_column('backend_moviesort', 'cinematographer_desc', orm['backend.moviesort:cinematographer_desc']) + + # Adding field 'MovieSort.title_desc' + db.add_column('backend_moviesort', 'title_desc', orm['backend.moviesort:title_desc']) + + # Adding field 'MovieSort.language_desc' + db.add_column('backend_moviesort', 'language_desc', orm['backend.moviesort:language_desc']) + + # Adding field 'MovieSort.year_desc' + db.add_column('backend_moviesort', 'year_desc', orm['backend.moviesort:year_desc']) + + # Adding field 'MovieFind.character' + db.add_column('backend_moviefind', 'character', orm['backend.moviefind:character']) + + # Adding field 'MovieSort.editor_desc' + db.add_column('backend_moviesort', 'editor_desc', orm['backend.moviesort:editor_desc']) + + # Adding field 'MovieFind.keyword' + db.add_column('backend_moviefind', 'keyword', orm['backend.moviefind:keyword']) + + # Deleting field 'Movie.extra' + db.delete_column('backend_movie', 'extra_id') + + # Deleting field 'MovieFind.cast' + db.delete_column('backend_moviefind', 'cast') + + # Dropping ManyToManyField 'Person.movies' + db.delete_table('backend_person_movies') + + # Deleting field 'Movie.oxdb' + db.delete_column('backend_movie', 'oxdb_id') + + # Deleting field 'MovieFind.keywords' + db.delete_column('backend_moviefind', 'keywords') + + # Deleting field 'MovieFind.locations' + db.delete_column('backend_moviefind', 'locations') + + # Deleting model 'movieoxdb' + db.delete_table('backend_movieoxdb') + + # Deleting model 'review' + db.delete_table('backend_review') + + # Deleting model 'archive' + db.delete_table('backend_archive') + + # Deleting model 'alternativetitle' + db.delete_table('backend_alternativetitle') + + # Deleting model 'moviecountry' + db.delete_table('backend_moviecountry') + + # Deleting model 'trivia' + db.delete_table('backend_trivia') + + # Deleting model 'connection' + db.delete_table('backend_connection') + + # Deleting model 'cast' + db.delete_table('backend_cast') + + # Deleting model 'frame' + db.delete_table('backend_frame') + + # Deleting model 'movielanguage' + db.delete_table('backend_movielanguage') + + # Deleting model 'file' + db.delete_table('backend_file') + + # Deleting model 'movieimdb' + db.delete_table('backend_movieimdb') + + # Deleting model 'country' + db.delete_table('backend_country') + + # Deleting model 'archivefile' + db.delete_table('backend_archivefile') + + # Deleting model 'movieextra' + db.delete_table('backend_movieextra') + + # Deleting model 'language' + db.delete_table('backend_language') + + # Deleting model 'keyword' + db.delete_table('backend_keyword') + + # Deleting model 'genre' + db.delete_table('backend_genre') + + # Changing field 'Movie.imdb' + # (to signature: fields.DictField(default={}, editable=False)) + db.alter_column('backend_movie', 'imdb', orm['backend.movie:imdb']) + + + + def backwards(self, orm): + + # Deleting model 'Facet' + db.delete_table('backend_facet') + + # Deleting field 'Movie.metadata' + db.delete_column('backend_movie', 'metadata') + + # Deleting field 'MovieSort.director_desc' + db.delete_column('backend_moviesort', 'director_desc') + + # Deleting field 'MovieSort.writer_desc' + db.delete_column('backend_moviesort', 'writer_desc') + + # Deleting field 'MovieSort.country_desc' + db.delete_column('backend_moviesort', 'country_desc') + + # Deleting field 'MovieFind.location' + db.delete_column('backend_moviefind', 'location') + + # Deleting field 'MovieFind.actor' + db.delete_column('backend_moviefind', 'actor') + + # Deleting field 'MovieSort.producer_desc' + db.delete_column('backend_moviesort', 'producer_desc') + + # Deleting field 'MovieSort.cinematographer_desc' + db.delete_column('backend_moviesort', 'cinematographer_desc') + + # Deleting field 'MovieSort.title_desc' + db.delete_column('backend_moviesort', 'title_desc') + + # Deleting field 'MovieSort.language_desc' + db.delete_column('backend_moviesort', 'language_desc') + + # Deleting field 'MovieSort.year_desc' + db.delete_column('backend_moviesort', 'year_desc') + + # Deleting field 'MovieFind.character' + db.delete_column('backend_moviefind', 'character') + + # Deleting field 'MovieSort.editor_desc' + db.delete_column('backend_moviesort', 'editor_desc') + + # Deleting field 'MovieFind.keyword' + db.delete_column('backend_moviefind', 'keyword') + + # Adding field 'Movie.extra' + db.add_column('backend_movie', 'extra', orm['backend.movie:extra']) + + # Adding field 'MovieFind.cast' + db.add_column('backend_moviefind', 'cast', orm['backend.moviefind:cast']) + + # Adding ManyToManyField 'Person.movies' + db.create_table('backend_person_movies', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('person', models.ForeignKey(orm.Person, null=False)), + ('movie', models.ForeignKey(orm.movie, null=False)) + )) + + # Adding field 'Movie.oxdb' + db.add_column('backend_movie', 'oxdb', orm['backend.movie:oxdb']) + + # Adding field 'MovieFind.keywords' + db.add_column('backend_moviefind', 'keywords', orm['backend.moviefind:keywords']) + + # Adding field 'MovieFind.locations' + db.add_column('backend_moviefind', 'locations', orm['backend.moviefind:locations']) + + # Adding model 'movieoxdb' + db.create_table('backend_movieoxdb', ( + ('series_title', orm['backend.moviefind:series_title']), + ('gross', orm['backend.moviefind:gross']), + ('votes', orm['backend.moviefind:votes']), + ('episode', orm['backend.moviefind:episode']), + ('created', orm['backend.moviefind:created']), + ('profit', orm['backend.moviefind:profit']), + ('season', orm['backend.moviefind:season']), + ('plot', orm['backend.moviefind:plot']), + ('rating', orm['backend.moviefind:rating']), + ('year', orm['backend.moviefind:year']), + ('budget', orm['backend.moviefind:budget']), + ('modified', orm['backend.moviefind:modified']), + ('episode_title', orm['backend.moviefind:episode_title']), + ('series_imdb', orm['backend.moviefind:series_imdb']), + ('tagline', orm['backend.moviefind:tagline']), + ('title', orm['backend.moviefind:title']), + ('runtime', orm['backend.moviefind:runtime']), + ('release_date', orm['backend.moviefind:release_date']), + ('id', orm['backend.moviefind:id']), + ('plot_outline', orm['backend.moviefind:plot_outline']), + )) + db.send_create_signal('backend', ['movieoxdb']) + + # Adding model 'review' + db.create_table('backend_review', ( + ('title', orm['backend.moviefind:title']), + ('url', orm['backend.moviefind:url']), + ('movie', orm['backend.moviefind:movie']), + ('manual', orm['backend.moviefind:manual']), + ('id', orm['backend.moviefind:id']), + )) + db.send_create_signal('backend', ['review']) + + # Adding model 'archive' + db.create_table('backend_archive', ( + ('users', orm['backend.moviefind:users']), + ('created', orm['backend.moviefind:created']), + ('modified', orm['backend.moviefind:modified']), + ('id', orm['backend.moviefind:id']), + ('public', orm['backend.moviefind:public']), + ('name', orm['backend.moviefind:name']), + )) + db.send_create_signal('backend', ['archive']) + + # Adding model 'alternativetitle' + db.create_table('backend_alternativetitle', ( + ('title', orm['backend.moviefind:title']), + ('movie', orm['backend.moviefind:movie']), + ('manual', orm['backend.moviefind:manual']), + ('type', orm['backend.moviefind:type']), + ('id', orm['backend.moviefind:id']), + )) + db.send_create_signal('backend', ['alternativetitle']) + + # Adding model 'moviecountry' + db.create_table('backend_moviecountry', ( + ('country', orm['backend.moviefind:country']), + ('manual', orm['backend.moviefind:manual']), + ('movie', orm['backend.moviefind:movie']), + ('position', orm['backend.moviefind:position']), + ('id', orm['backend.moviefind:id']), + )) + db.send_create_signal('backend', ['moviecountry']) + + # Adding model 'trivia' + db.create_table('backend_trivia', ( + ('movie', orm['backend.moviefind:movie']), + ('manual', orm['backend.moviefind:manual']), + ('position', orm['backend.moviefind:position']), + ('trivia', orm['backend.moviefind:trivia']), + ('id', orm['backend.moviefind:id']), + )) + db.send_create_signal('backend', ['trivia']) + + # Adding model 'connection' + db.create_table('backend_connection', ( + ('object', orm['backend.moviefind:object']), + ('manual', orm['backend.moviefind:manual']), + ('relation', orm['backend.moviefind:relation']), + ('id', orm['backend.moviefind:id']), + ('subject', orm['backend.moviefind:subject']), + )) + db.send_create_signal('backend', ['connection']) + + # Adding model 'cast' + db.create_table('backend_cast', ( + ('character', orm['backend.moviefind:character']), + ('manual', orm['backend.moviefind:manual']), + ('person', orm['backend.moviefind:person']), + ('role', orm['backend.moviefind:role']), + ('movie', orm['backend.moviefind:movie']), + ('position', orm['backend.moviefind:position']), + ('id', orm['backend.moviefind:id']), + )) + db.send_create_signal('backend', ['cast']) + + # Adding model 'frame' + db.create_table('backend_frame', ( + ('created', orm['backend.moviefind:created']), + ('frame', orm['backend.moviefind:frame']), + ('modified', orm['backend.moviefind:modified']), + ('file', orm['backend.moviefind:file']), + ('position', orm['backend.moviefind:position']), + ('id', orm['backend.moviefind:id']), + )) + db.send_create_signal('backend', ['frame']) + + # Adding model 'movielanguage' + db.create_table('backend_movielanguage', ( + ('language', orm['backend.moviefind:language']), + ('movie', orm['backend.moviefind:movie']), + ('manual', orm['backend.moviefind:manual']), + ('position', orm['backend.moviefind:position']), + ('id', orm['backend.moviefind:id']), + )) + db.send_create_signal('backend', ['movielanguage']) + + # Adding model 'file' + db.create_table('backend_file', ( + ('available', orm['backend.moviefind:available']), + ('needs_data', orm['backend.moviefind:needs_data']), + ('pixel_aspect_ratio', orm['backend.moviefind:pixel_aspect_ratio']), + ('stream_high', orm['backend.moviefind:stream_high']), + ('pixel_format', orm['backend.moviefind:pixel_format']), + ('oshash', orm['backend.moviefind:oshash']), + ('stream_low', orm['backend.moviefind:stream_low']), + ('height', orm['backend.moviefind:height']), + ('channels', orm['backend.moviefind:channels']), + ('part', orm['backend.moviefind:part']), + ('display_aspect_ratio', orm['backend.moviefind:display_aspect_ratio']), + ('audio_codec', orm['backend.moviefind:audio_codec']), + ('duration', orm['backend.moviefind:duration']), + ('path', orm['backend.moviefind:path']), + ('samplerate', orm['backend.moviefind:samplerate']), + ('id', orm['backend.moviefind:id']), + ('md5', orm['backend.moviefind:md5']), + ('info', orm['backend.moviefind:info']), + ('sha1', orm['backend.moviefind:sha1']), + ('verified', orm['backend.moviefind:verified']), + ('created', orm['backend.moviefind:created']), + ('movie', orm['backend.moviefind:movie']), + ('framerate', orm['backend.moviefind:framerate']), + ('modified', orm['backend.moviefind:modified']), + ('pixels', orm['backend.moviefind:pixels']), + ('bpp', orm['backend.moviefind:bpp']), + ('stream_mid', orm['backend.moviefind:stream_mid']), + ('published', orm['backend.moviefind:published']), + ('video_codec', orm['backend.moviefind:video_codec']), + ('size', orm['backend.moviefind:size']), + ('type', orm['backend.moviefind:type']), + ('width', orm['backend.moviefind:width']), + )) + db.send_create_signal('backend', ['file']) + + # Adding model 'movieimdb' + db.create_table('backend_movieimdb', ( + ('rating', orm['backend.moviefind:rating']), + ('modified', orm['backend.moviefind:modified']), + ('year', orm['backend.moviefind:year']), + ('id', orm['backend.moviefind:id']), + ('gross', orm['backend.moviefind:gross']), + ('votes', orm['backend.moviefind:votes']), + ('title', orm['backend.moviefind:title']), + ('profit', orm['backend.moviefind:profit']), + ('tagline', orm['backend.moviefind:tagline']), + ('season', orm['backend.moviefind:season']), + ('plot', orm['backend.moviefind:plot']), + ('imdbId', orm['backend.moviefind:imdbId']), + ('series_imdb', orm['backend.moviefind:series_imdb']), + ('series_title', orm['backend.moviefind:series_title']), + ('episode', orm['backend.moviefind:episode']), + ('created', orm['backend.moviefind:created']), + ('release_date', orm['backend.moviefind:release_date']), + ('budget', orm['backend.moviefind:budget']), + ('episode_title', orm['backend.moviefind:episode_title']), + ('runtime', orm['backend.moviefind:runtime']), + ('plot_outline', orm['backend.moviefind:plot_outline']), + )) + db.send_create_signal('backend', ['movieimdb']) + + # Adding model 'country' + db.create_table('backend_country', ( + ('movies', orm['backend.moviefind:movies']), + ('id', orm['backend.moviefind:id']), + ('name', orm['backend.moviefind:name']), + )) + db.send_create_signal('backend', ['country']) + + # Adding model 'archivefile' + db.create_table('backend_archivefile', ( + ('created', orm['backend.moviefind:created']), + ('modified', orm['backend.moviefind:modified']), + ('archive', orm['backend.moviefind:archive']), + ('file', orm['backend.moviefind:file']), + ('path', orm['backend.moviefind:path']), + ('id', orm['backend.moviefind:id']), + )) + db.send_create_signal('backend', ['archivefile']) + + # Adding model 'movieextra' + db.create_table('backend_movieextra', ( + ('rights_level', orm['backend.moviefind:rights_level']), + ('description', orm['backend.moviefind:description']), + ('created', orm['backend.moviefind:created']), + ('title', orm['backend.moviefind:title']), + ('modified', orm['backend.moviefind:modified']), + ('contributor', orm['backend.moviefind:contributor']), + ('id', orm['backend.moviefind:id']), + )) + db.send_create_signal('backend', ['movieextra']) + + # Adding model 'language' + db.create_table('backend_language', ( + ('movies', orm['backend.moviefind:movies']), + ('id', orm['backend.moviefind:id']), + ('name', orm['backend.moviefind:name']), + )) + db.send_create_signal('backend', ['language']) + + # Adding model 'keyword' + db.create_table('backend_keyword', ( + ('movies', orm['backend.moviefind:movies']), + ('manual', orm['backend.moviefind:manual']), + ('id', orm['backend.moviefind:id']), + ('name', orm['backend.moviefind:name']), + )) + db.send_create_signal('backend', ['keyword']) + + # Adding model 'genre' + db.create_table('backend_genre', ( + ('movies', orm['backend.moviefind:movies']), + ('manual', orm['backend.moviefind:manual']), + ('id', orm['backend.moviefind:id']), + ('name', orm['backend.moviefind:name']), + )) + db.send_create_signal('backend', ['genre']) + + # Changing field 'Movie.imdb' + # (to signature: django.db.models.fields.related.OneToOneField(unique=True, null=True, to=orm['backend.MovieImdb'])) + db.alter_column('backend_movie', 'imdb', orm['backend.movie:imdb']) + + # Creating unique_together for [imdb] on movie. + db.create_unique('backend_movie', ['imdb_id']) + + + + models = { + 'auth.group': { + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)"}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'backend.collection': { + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'movies': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['backend.Movie']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'blank': 'True'}), + 'subdomain': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '2048'}), + 'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'}) + }, + 'backend.facet': { + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '200', 'db_index': 'True'}), + 'movie': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'facets'", 'to': "orm['backend.Movie']"}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'value_sort': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'backend.layer': { + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'movie': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['backend.Movie']"}), + 'time_in': ('django.db.models.fields.FloatField', [], {'default': '-1'}), + 'time_out': ('django.db.models.fields.FloatField', [], {'default': '-1'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'value': ('django.db.models.fields.TextField', [], {}) + }, + 'backend.list': { + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'movies': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['backend.Movie']", 'symmetrical': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'backend.listitem': { + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'list': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['backend.List']"}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'movie': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['backend.Movie']"}) + }, + 'backend.location': { + 'area': ('django.db.models.fields.FloatField', [], {'default': '-1'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'lat_center': ('django.db.models.fields.FloatField', [], {'default': '0'}), + 'lat_ne': ('django.db.models.fields.FloatField', [], {'default': '0'}), + 'lat_sw': ('django.db.models.fields.FloatField', [], {'default': '0'}), + 'lng_center': ('django.db.models.fields.FloatField', [], {'default': '0'}), + 'lng_ne': ('django.db.models.fields.FloatField', [], {'default': '0'}), + 'lng_sw': ('django.db.models.fields.FloatField', [], {'default': '0'}), + 'manual': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'movies': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['backend.Movie']", 'symmetrical': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}) + }, + 'backend.movie': { + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'imdb': ('fields.DictField', [], {'default': '{}', 'editable': 'False'}), + 'json': ('fields.DictField', [], {'default': '{}', 'editable': 'False'}), + 'metadata': ('fields.DictField', [], {'default': '{}', 'editable': 'False'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'movieId': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'blank': 'True'}), + 'oxdbId': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '42', 'blank': 'True'}), + 'poster_height': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'poster_width': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'published': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'scene_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'stream_high': ('django.db.models.fields.files.FileField', [], {'default': 'None', 'max_length': '100', 'blank': 'True'}), + 'stream_low': ('django.db.models.fields.files.FileField', [], {'default': 'None', 'max_length': '100', 'blank': 'True'}), + 'stream_mid': ('django.db.models.fields.files.FileField', [], {'default': 'None', 'max_length': '100', 'blank': 'True'}) + }, + 'backend.moviefind': { + 'actor': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'all': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'character': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'cinematographer': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'country': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'director': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'editor': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'filename': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'genre': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'keyword': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'language': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'location': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'movie': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'find'", 'unique': 'True', 'primary_key': 'True', 'to': "orm['backend.Movie']"}), + 'producer': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'trivia': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'writer': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'year': ('django.db.models.fields.CharField', [], {'max_length': '4'}) + }, + 'backend.moviesort': { + 'aspectratio': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'blank': 'True'}), + 'bitrate': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'blank': 'True'}), + 'cast': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'blank': 'True'}), + 'cinematographer': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'blank': 'True'}), + 'cinematographer_desc': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'blank': 'True'}), + 'connections': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'blank': 'True'}), + 'country': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'blank': 'True'}), + 'country_desc': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'blank': 'True'}), + 'director': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'blank': 'True'}), + 'director_desc': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'blank': 'True'}), + 'duration': ('django.db.models.fields.FloatField', [], {'default': '-1', 'db_index': 'True'}), + 'editor': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'blank': 'True'}), + 'editor_desc': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'blank': 'True'}), + 'filename': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'blank': 'True'}), + 'files': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'blank': 'True'}), + 'genre': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'blank': 'True'}), + 'keywords': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'blank': 'True'}), + 'language': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'blank': 'True'}), + 'language_desc': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'blank': 'True'}), + 'movie': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'sort'", 'unique': 'True', 'primary_key': 'True', 'to': "orm['backend.Movie']"}), + 'movieId': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'blank': 'True'}), + 'pixels': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'blank': 'True'}), + 'producer': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'blank': 'True'}), + 'producer_desc': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'blank': 'True'}), + 'rating': ('django.db.models.fields.FloatField', [], {'db_index': 'True', 'blank': 'True'}), + 'resolution': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'blank': 'True'}), + 'risk': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'runtime': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'scenes': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'blank': 'True'}), + 'size': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'blank': 'True'}), + 'summary': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'db_index': 'True'}), + 'title_desc': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'db_index': 'True'}), + 'trivia': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'blank': 'True'}), + 'votes': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'blank': 'True'}), + 'words': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'wpm': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'writer': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'blank': 'True'}), + 'writer_desc': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'blank': 'True'}), + 'year': ('django.db.models.fields.CharField', [], {'max_length': '4', 'db_index': 'True'}), + 'year_desc': ('django.db.models.fields.CharField', [], {'max_length': '4', 'db_index': 'True'}) + }, + 'backend.person': { + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'imdbId': ('django.db.models.fields.CharField', [], {'max_length': '7', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'name_sort': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'backend.reviewwhitelist': { + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'url': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['backend'] diff --git a/pandora/backend/models.py b/pandora/backend/models.py index 46b950f74..dd9801fbd 100644 --- a/pandora/backend/models.py +++ b/pandora/backend/models.py @@ -23,71 +23,6 @@ import load import utils import extract -class MovieImdb(models.Model): - created = models.DateTimeField(auto_now_add=True) - modified = models.DateTimeField(auto_now=True) - - imdbId = models.CharField(max_length=7, unique=True) - 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='') - 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) - - 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) - - 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='') - series_title = models.TextField(blank=True, default='') - episode_title = models.TextField(blank=True, default='') - season = models.IntegerField(default=-1) - episode = models.IntegerField(default=-1) - - def __unicode__(self): - return u"%s (%s)" % (self.title, self.year) - -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) def getMovie(info): ''' @@ -96,44 +31,42 @@ def getMovie(info): ''' if 'imdbId' in info and info['imdbId']: try: - movie = Movie.byImdbId(info['imdbId']) + movie = Movie.objects.get(movieId=info['imdbId']) except Movie.DoesNotExist: - movie = load.loadIMDb(info['imdbId']) + movie = Movie(movieId=info['imdbId']) + if 'title' in info and 'directors' in info: + movie.imdb = { + 'title': info['title'], + 'directors': info['directors'], + 'year': info.get('year', '') + } + #FIXME: this should be done async + #movie.save() + #tasks.updateImdb.delay(movie.movieId) + movie.updateImdb() else: - q = Movie.objects.filter(oxdb__title=info['title']) + q = Movie.objects.filter(find__title=info['title']) if q.count() > 1: - print "FIXME: check more than title here!!" + print "FIXME: check more than title here!!?" movie = q[0] else: - print info - 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: + try: + movie = Movie.objects.get(movieId=info['oxdbId']) + except Movie.DoesNotExist: + movie = Movie() + movie.metadata = { + 'title': info['title'], + 'directors': info['directors'], + 'year': info.get('year', '') + } + movie.movieId = info['oxdbId'] + + for key in ('episode_title', 'series_title', 'season', 'episode'): + if key in info: + movie.metadata[key] = info[key] movie.save() 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() - print title, director, year - print movie.oxdbId - print movie.movieId - - movie.save() - return movie - def movie_path(f, size): name = "%s.%s" % (size, 'ogv') url_hash = f.movieId @@ -144,7 +77,6 @@ def poster_path(f): 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) @@ -157,19 +89,13 @@ class Movie(models.Model): 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') - extra = models.OneToOneField('MovieExtra', null=True, related_name='movie') - objects = managers.MovieManager() def get(self, key, default=None): - 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) + if self.metadata and key in self.metadata: + return self.metadata[key] + if self.imdb and key in self.imdb: + return self.imdb[key] return default def editable(self, user): @@ -180,77 +106,30 @@ class Movie(models.Model): #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]) + setattr(self.metadata, 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}) - - def directors(self): - qs = self.people.filter(cast__role='directors').order_by('cast__position') - return self._manual(qs, 'cast__manual') - def writers(self): - qs = self.people.filter(cast__role='writers').order_by('cast__position') - return self._manual(qs, 'cast__manual') - def editors(self): - qs = self.people.filter(cast__role='editors').order_by('cast__position') - return self._manual(qs, 'cast__manual') - def producers(self): - qs = self.people.filter(cast__role='producers').order_by('cast__position') - return self._manual(qs, 'cast__manual') - def cinematographers(self): - qs = self.people.filter(cast__role='cinematographers').order_by('cast__position') - return self._manual(qs, 'cast__manual') - - def cast(self): - cast = [] - qs = Cast.objects.filter(movie=self, role='cast').order_by('position') - qs = self._manual(qs) - for c in qs: - 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): - return self._manual(self.countries_all, 'moviecountry__manual').order_by('moviecountry__position') - def languages(self): - 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) - - def connections_json(self): - connections = {} - for connection in self.connections(): - 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 - 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) + reviews = self.get('reviews', []) + whitelist = [w for w in ReviewWhitelist.objects.all()] + _reviews = {} + for r in reviews: + for w in whitelist: + if w.url in r[0]: + _reviews[w.title] = r[0] + return _reviews + imdb = fields.DictField(default={}, editable=False) + metadata = fields.DictField(default={}, editable=False) json = fields.DictField(default={}, editable=False) + def updateImdb(self): + if len(self.movieId) == 7: + self.imdb = ox.web.imdb.Imdb(self.movieId) + self.save() + #FIXME: use data.0xdb.org ''' tpb_id = models.CharField(max_length=128, blank=True) @@ -282,22 +161,14 @@ class Movie(models.Model): return u'%s (%s)' % (self.get('title'), self.get('year')) def save(self, *args, **kwargs): - if not self.oxdb: - oxdb = MovieOxdb() - oxdb.save() - self.oxdb = oxdb - if self.imdb: - mid = self.imdb.imdbId - else: - mid = self.oxdbId - self.movieId = mid - - if self.id: - self.json = self.get_json() + self.json = self.get_json() + if not self.oxdbId: + self.oxdbId = self.oxid() super(Movie, self).save(*args, **kwargs) self.updateFind() self.updateSort() + self.updateFacets() _public_fields = { 'movieId': 'id', @@ -335,11 +206,7 @@ class Movie(models.Model): value = getattr(self, key) else: value = self.get(key) - if key in ('directors', 'writers', 'editors', 'cinematographers', 'producers', - 'reviews', 'countries', 'languages', - 'keywords', 'genres', 'trivia', 'alternative_titles'): - movie[pub_key] = tuple([v.json() for v in value()]) - elif callable(value): + if callable(value): movie[pub_key] = value() else: movie[pub_key] = value @@ -359,26 +226,9 @@ class Movie(models.Model): return fields fields = classmethod(fields) - #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) - byImdbId = classmethod(byImdbId) - - def byOxdbId(self, oxdbId): - return self.objects.get(oxdbId=oxdbId) - byOxdbId = classmethod(byOxdbId) - def oxid(self): - 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', ''), + return utils.oxid(self.get('title', ''), self.get('directors', []), str(self.get('year', '')), + self.get('series title', ''), self.get('episode title', ''), self.get('season', ''), self.get('episode', '')) def frame(self, position, width=128): @@ -395,25 +245,28 @@ class Movie(models.Model): f.title = self.get('title') #FIXME: filter us/int title #f.title += ' '.join([t.title for t in self.alternative_titles()]) - f.director = '|%s|'%'|'.join([i.name for i in self.directors()]) - f.country = '|%s|'%'|'.join([i.name for i in self.countries()]) + f.director = '|%s|'%'|'.join(self.get('directors', [])) + f.country = '|%s|'%'|'.join(self.get('countries', [])) f.year = self.get('year', '') - 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()]) - f.cast = ' '.join(['%s %s' % i for i in self.cast()]) - f.genre = '|%s|'%'|'.join([i.name for i in self.genres()]) - f.keywords = '|%s|'%'|'.join([i.name for i in self.keywords()]) + for key in ('language', 'writer', 'producer', 'editor', 'cinematographer'): + setattr(f, key, '|%s|'%'|'.join(self.get('%ss'%key, []))) + + f.actor = '|%s|'%'|'.join([i[0] for i in self.get('actor', [])]) + f.character = '|%s|'%'|'.join([stripTagsl(i[1]) for i in self.get('actor', [])]) + + f.genre = '|%s|'%'|'.join(self.get('genres', [])) + f.keyword = '|%s|'%'|'.join(self.get('keywords', [])) f.summary = self.get('plot', '') + self.get('plot_outline', '') - f.trivia = ' '.join([i.trivia for i in self.trivia()]) - f.location = '|%s|'%'|'.join([i.name for i in self.locations()]) + f.trivia = ' '.join(self.get('trivia', [])) + f.location = '|%s|'%'|'.join(self.get('filming_locations', [])) + + f.dialog = 'fixme' + #FIXME: collate filenames #f.filename = self.filename - f.all = ' '.join(filter(None, [f.title, f.director, f.country, f.year, f.language, + f.all = ' '.join(filter(None, [f.title, f.director, f.country, str(f.year), f.language, f.writer, f.producer, f.editor, f.cinematographer, - f.cast, f.genre, f.keywords, f.summary, f.trivia, + f.actor, f.character, f.genre, f.keyword, f.summary, f.trivia, f.location, f.filename])) f.save() @@ -423,14 +276,12 @@ class Movie(models.Model): except MovieSort.DoesNotExist: s = MovieSort(movie=self) - 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') + def sortNames(values): + sort_value = '' + if values: + sort_value = '; '.join([getPersonSort(name) for name in values]) if not sort_value: - sort_value = '~' + sort_value = '' return sort_value #title @@ -445,31 +296,22 @@ class Movie(models.Model): 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.country = ','.join(self.get('countries', [])) s.year = self.get('year', '') - 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) + for key in ('director', 'writer', 'producer', 'editor', 'cinematographer'): + setattr(s, key, sortNames(self.get('%ss'%key, []))) - s.language = ','.join([i.name for i in self.languages()]) - s.country = ','.join([i.name for i in self.countries()]) + s.language = ','.join(self.get('languages', [])) + s.country = ','.join(self.get('countries', [])) s.runtime = self.get('runtime', 0) - s.keywords = self.keywords().count() - s.genre = self.genres().count() - s.cast = len(self.cast()) + s.keywords = len(self.get('keywords', [])) + s.genre = len(self.get('genres', [])) + s.cast = len(self.get('cast', [])) s.summary = len(self.get('plot', '').split()) - s.trivia = self.trivia().count() - s.connections = self.connections().count() + s.trivia = len(self.get('trivia', [])) + s.connections = len(self.get('connections', [])) s.movieId = self.movieId s.rating = self.get('rating', -1) s.votes = self.get('votes', -1) @@ -488,8 +330,38 @@ class Movie(models.Model): s.filename = 0 #FIXME s.files = 0 #FIXME s.size = 0 #FIXME + + for key in ('title', 'director', 'writer', 'producer', 'editor', 'cinematographer', 'language', 'country'): + setattr(s, '%s_desc'%key, getattr(s, key)) + if not getattr(s, key): + setattr(s, key, u'zzzzzzzzzzzzzzzzzzzzzzzzz') + if not s.year: + s.year_desc = ''; + s.year = '9999'; s.save() + def updateFacets(self): + #"year", is extra is it? + #FIXME: what to do with Unkown Director, Year, Country etc. + def plural(term): + return { + 'country': 'countries', + }.get(term, term + 's') + for key in ("director", "country", "language", "genre"): + current_values = self.get(plural(key), []) + saved_values = [i.value for i in Facet.objects.filter(movie=self, key=key)] + removed_values = filter(lambda x: x not in current_values, saved_values) + if removed_values: + Facet.objects.filter(movie=self, key=key, value__in=removed_values).delete() + for value in current_values: + if value not in saved_values: + value_sort = value + if key in ('director', ): + value_sort = getPersonSort(value) + f = Facet(key=key, value=value, value_sort=value_sort) + f.movie = self + f.save() + class MovieFind(models.Model): """ used to search movies, all search values are in here @@ -506,14 +378,15 @@ class MovieFind(models.Model): producer = models.TextField(blank=True, default='') editor = models.TextField(blank=True, default='') cinematographer = models.TextField(blank=True, default='') - cast = models.TextField(blank=True, default='') + actor = models.TextField(blank=True, default='') + character = models.TextField(blank=True, default='') #person genre = models.TextField(blank=True) - keywords = models.TextField(blank=True) + keyword = models.TextField(blank=True) summary = models.TextField(blank=True) trivia = models.TextField(blank=True) - locations = models.TextField(blank=True, default='') + location = models.TextField(blank=True, default='') #only for own files or as admin? filename = models.TextField(blank=True, default='') @@ -575,6 +448,20 @@ class MovieSort(models.Model): files = models.IntegerField(blank=True, db_index=True) size = models.BigIntegerField(blank=True, db_index=True) + #required to move empty values to the bottom for both asc and desc sort + title_desc = models.CharField(max_length=1000, db_index=True) + director_desc = models.TextField(blank=True, db_index=True) + country_desc = models.TextField(blank=True, db_index=True) + year_desc = models.CharField(max_length=4, db_index=True) + + producer_desc = models.TextField(blank=True, db_index=True) + writer_desc = models.TextField(blank=True, db_index=True) + editor_desc = models.TextField(blank=True, db_index=True) + cinematographer_desc = models.TextField(blank=True, db_index=True) + + language_desc = models.TextField(blank=True, db_index=True) + + _private_fields = ('id', 'movie') #return available sort fields #FIXME: should return mapping name -> verbose_name @@ -588,27 +475,26 @@ class MovieSort(models.Model): return tuple(fields) fields = classmethod(fields) -class AlternativeTitle(models.Model): - movie = models.ForeignKey(Movie, related_name='alternative_titles_all') - title = models.TextField() - type = models.CharField(max_length=1000) - manual = models.BooleanField(default=False) +class Facet(models.Model): + movie = models.ForeignKey('Movie', related_name='facets') + key = models.CharField(max_length=200, db_index=True) + value = models.CharField(max_length=200) + value_sort = models.CharField(max_length=200) - class Meta: - ordering = ('title', ) - - def __unicode__(self): - return self.title - - def json(self): - return (self.title, self.type) + def save(self, *args, **kwargs): + if not self.value_sort: + self.value_sort = self.value + super(Facet, self).save(*args, **kwargs) +def getPersonSort(name): + person, created = Person.objects.get_or_create(name=name) + name_sort = person.name_sort.replace(u'\xc5k', 'A') + return name_sort 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', ) @@ -639,153 +525,6 @@ class Person(models.Model): def json(self): return self.name -class Cast(models.Model): - movie = models.ForeignKey(Movie, related_name='cast_relation') - person = models.ForeignKey(Person) - role = models.CharField(max_length=200) - character = models.CharField(max_length=1000, blank=True) - position = models.IntegerField() - manual = models.BooleanField(default=False) - - 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): - 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 - link.save() - else: - link = self() - link.movie=movie - link.person=person - link.role=role - link.character=character - link.position = position - link.manual = manual - link.save() - return link - link = classmethod(link) - - def json(self): - return (self.person.json(), self.character) - -class Country(models.Model): - name = models.CharField(max_length=200, unique=True) - movies = models.ManyToManyField(Movie, related_name='countries_all', through='MovieCountry') - - class Meta: - #!! adding this to ordering, breaks: - # models.Country.objects.values("name").annotate(movies=Count('movies')) - #'moviecountry__position', - ordering = ('name', ) - - 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) - - 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): - name = models.CharField(max_length=200, unique=True) - movies = models.ManyToManyField(Movie, related_name='languages_all', through="MovieLanguage") - - 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) - - 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): - name = models.CharField(max_length=200, unique=True) - manual = models.BooleanField(default=False) - movies = models.ManyToManyField(Movie, related_name='keywords_all') - - class Meta: - ordering = ('name', ) - - def __unicode__(self): - return self.name - - def json(self): - return self.name - -class Genre(models.Model): - name = models.CharField(max_length=200, unique=True) - manual = models.BooleanField(default=False) - movies = models.ManyToManyField(Movie, related_name='genres_all') - - class Meta: - ordering = ('name', ) - - def __unicode__(self): - return self.name - - def json(self): - return self.name - class Location(models.Model): name = models.CharField(max_length=200, unique=True) manual = models.BooleanField(default=False) @@ -809,92 +548,6 @@ class Location(models.Model): def json(self): return self.name -class Trivia(models.Model): - trivia = models.TextField() - manual = models.BooleanField(default=False) - position = models.IntegerField() - movie = models.ForeignKey(Movie, related_name='trivia_all') - - class Meta: - ordering = ('position', ) - - def __unicode__(self): - return self.trivia - - def json(self): - trivia = self.trivia - trivia = ox.fixAmpersands(trivia) - trivia = re.sub(' ', '', trivia) - trivia = re.sub('(.*?)', '\\2', trivia) - trivia = re.sub('(.*?)', '\\2', trivia) - return trivia - -class Connection(models.Model): - subject = models.ForeignKey(Movie, related_name='connections_all') - relation = models.CharField(max_length=512) - object = models.ForeignKey(Movie) - manual = models.BooleanField(default=False) - - def get_or_create(model, subject, relation, object, reverse=True, manual=False): - 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) - 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', - 'Alternate language version of': 'Alternate language version of', - } - 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) - -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) @@ -936,213 +589,6 @@ class ListItem(models.Model): 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 - return os.path.join(url_hash[:2], url_hash[2:4], url_hash[4:6], url_hash, name) - -def timeline_path(f): - name = "timeline.png" - url_hash = f.oshash - return os.path.join(url_hash[:2], url_hash[2:4], url_hash[4:6], url_hash, name) - -def frame_path(f): - position = ox.formatDuration(f.position*1000).replace(':', '.') - 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) - -FILE_TYPES = ( - (0, 'unknown'), - (1, 'video'), - (2, 'audio'), - (3, 'subtitle'), -) - -class File(models.Model): - created = models.DateTimeField(auto_now_add=True) - modified = models.DateTimeField(auto_now=True) - published = models.DateTimeField(default=datetime.now, editable=False) - - verified = models.BooleanField(default=False) - - oshash = models.CharField(blank=True, unique=True, max_length=16) - sha1 = models.CharField(blank=True, null=True, unique=True, max_length=40) - md5 = models.CharField(blank=True, null=True, unique=True, max_length=32) - - movie = models.ForeignKey(Movie, related_name="files", default=None, null=True) - - 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 - path = models.CharField(blank=True, max_length=2048) - size = models.BigIntegerField(default=-1) - duration = models.FloatField(default=-1) - - 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) - pixels = models.BigIntegerField(default=0) - - part = models.IntegerField(default=0) - - needs_data = models.BooleanField(default=True) - - #stream related fields - available = models.BooleanField(default=False) - 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): - return '%s/timeline' % os.path.dirname(self.stream_low.url) - - def save_chunk(self, chunk, name='video.ogv'): - if not self.available: - #FIXME: this should use stream_low or stream_high depending on configuration - video = getattr(self, 'stream_%s'%settings.VIDEO_PROFILE) - if not video: - video.save(name, chunk) - self.save() - else: - f = open(video.path, 'a') - f.write(chunk.read()) - f.close() - return True - print "somehing failed, not sure what?", self.available - return False - - objects = managers.FileManager() - - def __unicode__(self): - 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() - - def findMovie(self): - info = utils.parsePath(self.path) - self.movie = getMovie(info) - self.save() - - def extract_timeline(self): - 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 - 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() - source = None - profiles = [] - if self.stream_high: - source = self.stream_high - profiles = ['low', 'mid'] - elif self.stream_mid: - 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]) - - def extract(self): - #FIXME: do stuff, like create timeline or create smaller videos etc - self.extract_video() - self.extract_timeline() - return - - 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 - - def editable(self, user): - ''' - #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 - -class Frame(models.Model): - created = models.DateTimeField(auto_now_add=True) - modified = models.DateTimeField(auto_now=True) - file = models.ForeignKey(File, related_name="frames") - position = models.FloatField() - 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 - - def __unicode__(self): - return '%s at %s' % (self.file, self.position) - - class Layer(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) @@ -1167,54 +613,6 @@ class Layer(models.Model): return True return False -class Archive(models.Model): - created = models.DateTimeField(auto_now_add=True) - modified = models.DateTimeField(auto_now=True) - name = models.CharField(max_length=255, unique=True) - public = models.BooleanField(default=False) - users = models.ManyToManyField(User, related_name='archives') - - def __unicode__(self): - return '%s' % (self.name) - - def editable(self, user): - return self.users.filter(id=user.id).count() > 0 - - -class ArchiveFile(models.Model): - created = models.DateTimeField(auto_now_add=True) - modified = models.DateTimeField(auto_now=True) - archive = models.ForeignKey(Archive, related_name='files') - file = models.ForeignKey(File, related_name='archive_files') - path = models.CharField(blank=True, max_length=2048) - - objects = managers.ArchiveFileManager() - - def update(self, data): - """ - only add, do not overwrite keys in file - """ - self.file.update(data) - 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() - f = model.objects.create(archive=archive, file=file) - f.save() - return f - get_or_create = classmethod(get_or_create) - - def __unicode__(self): - return '%s (%s)' % (self.path, unicode(self.archive)) - - def editable(self, user): - return self.archive.editable(user) class Collection(models.Model): created = models.DateTimeField(auto_now_add=True) diff --git a/pandora/backend/tasks.py b/pandora/backend/tasks.py index bc7129009..cf2b93568 100644 --- a/pandora/backend/tasks.py +++ b/pandora/backend/tasks.py @@ -12,16 +12,17 @@ import models def cronjob(**kwargs): print "do some cleanup stuff once a day" -@task(ignore_resulsts=True) -def loadIMDb(imdbId): - load.loadIMDb(imdbId) +@task(ignore_resulsts=True, queue='default') +def updateImdb(imdbId): + movie = models.Movie.objects.get(movieId=imdbId) + movie.updateImdb() @task(ignore_resulsts=True) def findMovie(fileId): f = models.File.objects.get(pk=fileId) f.findMovie() -@task(ignore_resulsts=True, exchange="encoding") +@task(ignore_resulsts=True, queue="encoding") def extractData(fileId): ''' update file stuff @@ -30,7 +31,7 @@ def extractData(fileId): f = models.File.objects.get(pk=fileId) f.extract() -@task(ignore_resulsts=True, exchange="encoding") +@task(ignore_resulsts=True, queue="encoding") def updateMovie(movidId): ''' update movie diff --git a/pandora/backend/utils.py b/pandora/backend/utils.py index 84347f7d1..59063a85f 100644 --- a/pandora/backend/utils.py +++ b/pandora/backend/utils.py @@ -12,7 +12,8 @@ import ox import ox.iso from ox.normalize import normalizeName -def oxid(title, director, year='', seriesTitle='', episodeTitle='', season=0, episode=0): +def oxid(title, directors, year='', seriesTitle='', episodeTitle='', season=0, episode=0): + director = ', '.join(directors) oxid_value = u"\n".join([title, director, year]) oxid = hashlib.sha1(oxid_value.encode('utf-8')).hexdigest() if seriesTitle: @@ -22,15 +23,19 @@ def oxid(title, director, year='', seriesTitle='', episodeTitle='', season=0, ep oxid += hashlib.sha1(oxid_value.encode('utf-8')).hexdigest()[:20] return u"0x" + oxid -def oxdb_director(director): +def oxdb_directors(director): director = os.path.basename(os.path.dirname(director)) if director.endswith('_'): director = "%s." % director[:-1] - director = ", ".join([normalizeName(d) for d in director.split('; ')]) - director = director.replace('Series', '') - director = director.replace('Unknown Director', '') - director = director.replace('Various Directors', '') - return director + directors = [normalizeName(d) for d in director.split('; ')] + def cleanup(director): + director = director.strip() + director = director.replace('Series', '') + director = director.replace('Unknown Director', '') + director = director.replace('Various Directors', '') + return director + directors = filter(None, [cleanup(d) for d in directors]) + return directors def oxdb_title(_title, searchTitle = False): ''' @@ -117,11 +122,15 @@ def parsePath(path): search_title = oxdb_title(path, True) r = {} r['title'] = oxdb_title(path) - r['director'] = oxdb_director(path) + r['directors'] = oxdb_directors(path) r['episode_title'] = oxdb_episode_title(path) r['season'], r['episode'] = oxdb_season_episode(path) - r['series'] = oxdb_series_title(path) + r['series_title'] = oxdb_series_title(path) r['part'] = oxdb_part(path) - r['imdbId'] = ox.web.imdb.guess(search_title, r['director'], timeout=-1) + r['imdbId'] = ox.web.imdb.guess(search_title, ', '.join(r['directors']), timeout=-1) + r['oxdbId'] = oxid(r['title'], r['directors'], + seriesTitle=r['series_title'], + episodeTitle=r['episode_title'], + season=r['season'], episode=r['episode']) return r diff --git a/pandora/backend/views.py b/pandora/backend/views.py index 539574d55..f9aabfdb3 100644 --- a/pandora/backend/views.py +++ b/pandora/backend/views.py @@ -33,8 +33,10 @@ import tasks from oxuser.models import getUserJSON from oxuser.views import api_login, api_logout, api_register, api_contact, api_recover, api_preferences +from archive.views import api_update, api_addArchive, api_editArchive, api_removeArchive + +from archive.models import File - def api(request): if request.META['REQUEST_METHOD'] == "OPTIONS": response = HttpResponse('') @@ -78,10 +80,18 @@ def api_error(request): def _order_query(qs, sort, prefix='sort__'): order_by = [] + if len(sort) == 1: + if sort[0]['key'] in ('title', 'director'): + sort.append({'operator': '-', 'key': 'year'}) + if sort[0]['key'] in ('year', ): + sort.append({'operator': '+', 'key': 'director'}) for e in sort: operator = e['operator'] if operator != '-': operator = '' key = {'id': 'movieId'}.get(e['key'], e['key']) + #FIXME: this should be a property of models.MovieSort!!! + if operator=='-' and key in ('title', 'director', 'writer', 'producer', 'editor', 'cinematographer', 'language', 'country', 'year'): + key = '%s_desc' % key order = '%s%s%s' % (operator, prefix, key) order_by.append(order) if order_by: @@ -180,29 +190,23 @@ Positions query['sort'] = [{'key': 'name', 'operator':'+'}] #FIXME: also filter lists here response['data']['items'] = [] - name = 'name' items = 'movies' movie_qs = query['qs'] - if query['group'] == "director": - qs = models.Cast.objects.filter(role='directors').filter(movie__id__in=movie_qs).values('person__name').annotate(movies=Count('person__id')).order_by() - name = 'person__name' - elif query['group'] == "country": - qs = models.Country.objects.filter(movies__id__in=movie_qs).values('name').annotate(movies=Count('id')) - elif query['group'] == "genre": - qs = models.Genre.objects.filter(movies__id__in=movie_qs).values('name').annotate(movies=Count('id')) - elif query['group'] == "language": - qs = models.Language.objects.filter(movies__id__in=movie_qs).values('name').annotate(movies=Count('id')) - elif query['group'] == "year": + if query['group'] == "year": qs = models.MovieSort.objects.filter(movie__id__in=movie_qs).values('year').annotate(movies=Count('year')) name='year' + name_sort='year' + else: + qs = models.Facet.objects.filter(key=query['group']).filter(movie__id__in=movie_qs) + qs = qs.values('value').annotate(movies=Count('id')).order_by() + name = 'value' + name_sort = 'value_sort' + #replace normalized items/name sort with actual db value for i in range(0, len(query['sort'])): if query['sort'][i]['key'] == 'name': - if query['group'] in ('director', ): - query['sort'][i]['key'] = 'person__name_sort' - else: - query['sort'][i]['key'] = name - if query['sort'][i]['key'] == 'items': + query['sort'][i]['key'] = name_sort + elif query['sort'][i]['key'] == 'items': query['sort'][i]['key'] = items qs = _order_query(qs, query['sort'], prefix='') if 'ids' in query: @@ -235,26 +239,25 @@ Positions for p in _p: r[p] = m.get(p, '') return r - qs = qs[query['range'][0]:query['range'][1]] response['data']['items'] = [only_p(m['json']) for m in qs.values('json')] else: # otherwise stats #movies = models.Movie.objects.filter(available=True) movies = query['qs'] - files = models.File.objects.all().filter(movie__in=movies) + files = File.objects.all().filter(movie__in=movies) r = files.aggregate( - Count('duration'), - Count('pixels'), - Count('size') + Sum('duration'), + Sum('pixels'), + Sum('size') ) - response['data']['duration'] = r['duration__count'] + response['data']['duration'] = r['duration__sum'] response['data']['files'] = files.count() response['data']['items'] = movies.count() - response['data']['pixels'] = r['pixels__count'] + response['data']['pixels'] = r['pixels__sum'] response['data']['runtime'] = movies.aggregate(Sum('sort__runtime'))['sort__runtime__sum'] if response['data']['runtime'] == None: response['data']['runtime'] = 1337 - response['data']['size'] = r['size__count'] + response['data']['size'] = r['size__sum'] return render_to_json_response(response) def api_getItem(request): @@ -401,106 +404,6 @@ def api_removeList(request): response = json_response(status=501, text='not implemented') return render_to_json_response(response) -@login_required_json -def api_addArchive(request): - ''' - ARCHIVE API NEEDS CLEANUP - param data - {name: string} - return {'status': {'code': int, 'text': string}, - 'data': {}} - ''' - data = json.loads(request.POST['data']) - try: - archive = models.Archive.objects.get(name=data['name']) - response = {'status': {'code': 401, 'text': 'archive with this name exists'}} - except models.Archive.DoesNotExist: - archive = models.Archive(name=data['name']) - archive.save() - archive.users.add(request.user) - response = json_response({}) - response['status']['text'] = 'archive created' - return render_to_json_response(response) - -@login_required_json -def api_editArchive(request): - ''' - ARCHIVE API NEEDS CLEANUP - param data - {id: string, key: value,..} - return {'status': {'code': int, 'text': string}, - 'data': {}} - ''' - data = json.loads(request.POST['data']) - item = get_object_or_404_json(models.Archive, name=data['name']) - if item.editable(request.user): - response = json_response(status=501, text='not implemented') - item.edit(data) - else: - response = json_response(status=403, text='permission denied') - return render_to_json_response(response) - -@login_required_json -def api_removeArchive(request): - ''' - ARCHIVE API NEEDS CLEANUP - param data - string id - - return {'status': {'code': int, 'text': string}} - ''' - response = json_response({}) - itemId = json.loads(request.POST['data']) - item = get_object_or_404_json(models.Archive, movieId=itemId) - if item.editable(request.user): - response = json_response(status=501, text='not implemented') - else: - response = json_response(status=403, text='permission denied') - return render_to_json_response(response) - - - -#@login_required_json -def api_update(request): - ''' - param data - {archive: string, files: json} - return {'status': {'code': int, 'text': string}, - 'data': {info: object, rename: object}} - ''' - data = json.loads(request.POST['data']) - archive = data['archive'] - files = data['files'] - archive = get_object_or_404_json(models.Archive, name=archive) - if archive.editable(request.user): - needs_data = [] - rename = {} - for oshash in files: - data = files[oshash] - q = models.ArchiveFile.objects.filter(archive=archive, file__oshash=oshash) - if q.count() == 0: - #print "adding file", oshash, data['path'] - f = models.ArchiveFile.get_or_create(archive, oshash) - f.update(data) - if not f.file.movie: - task.findMovie(f.file.id) - #FIXME: only add if it was not in File - else: - f = q[0] - if data['path'] != f.path: - f.path = data['path'] - f.save() - if f.file.needs_data: - needs_data.append(oshash) - if f.path != f.file.path: - rename[oshash] = f.file.path - #print "processed files for", archive.name - #remove all files not in files.keys() from ArchiveFile - response = json_response({'info': needs_data, 'rename': rename}) - else: - response = json_response(status=403, text='permission denied') - return render_to_json_response(response) - def api_encodingSettings(request): ''' returns Firefogg encoding settings as specified by site diff --git a/pandora/settings.py b/pandora/settings.py index d08766eb5..b034bd980 100644 --- a/pandora/settings.py +++ b/pandora/settings.py @@ -112,6 +112,7 @@ INSTALLED_APPS = ( 'app', 'backend', + 'archive', 'oxuser', 'torrent', )