cleanup imports and syntax warnings

This commit is contained in:
j 2011-01-01 17:14:42 +05:30
parent 7fdae917cf
commit 2d5f924891
46 changed files with 452 additions and 517 deletions

View file

@ -2,49 +2,32 @@
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
from __future__ import division, with_statement from __future__ import division, with_statement
from datetime import datetime
import os.path
import math
import random
import re
import subprocess
import unicodedata
from glob import glob
from django.db import models from django.db import models
from django.db.models import Q
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.files.base import ContentFile
from django.conf import settings
from ox.django import fields
import ox
from ox.utils import json
from ox import stripTags
from ox.normalize import canonicalTitle, canonicalName
import utils import utils
class Layer(models.Model): class Layer(models.Model):
class Meta: class Meta:
ordering = ('position', ) ordering = ('position', )
name = models.CharField(null=True, max_length=255, unique=True) name = models.CharField(null=True, max_length=255, unique=True)
title = models.CharField(null=True, max_length=255) title = models.CharField(null=True, max_length=255)
#text, string, string from list(fixme), event, place, person #text, string, string from list(fixme), event, place, person
type = models.CharField(null=True, max_length=255) type = models.CharField(null=True, max_length=255)
position = models.IntegerField(default=0) position = models.IntegerField(default=0)
overlapping = models.BooleanField(default=True) overlapping = models.BooleanField(default=True)
enabled = models.BooleanField(default=True) enabled = models.BooleanField(default=True)
enabled = models.BooleanField(default=True) enabled = models.BooleanField(default=True)
public = models.BooleanField(default=True) #false=users only see there own bins public = models.BooleanField(default=True) #false=users only see there own bins
subtitle = models.BooleanField(default=True) #bis can be displayed as subtitle, only one bin subtitle = models.BooleanField(default=True) #bis can be displayed as subtitle, only one bin
find = models.BooleanField(default=True) find = models.BooleanField(default=True)
#words / item duration(wpm), total words, cuts per minute, cuts, number of annotations, number of annotations/duration #words / item duration(wpm), total words, cuts per minute, cuts, number of annotations, number of annotations/duration
sort = models.CharField(null=True, max_length=255) sort = models.CharField(null=True, max_length=255)
def properties(self): def properties(self):
@ -61,7 +44,9 @@ class Layer(models.Model):
def __unicode__(self): def __unicode__(self):
return self.title return self.title
class Annotation(models.Model): class Annotation(models.Model):
#FIXME: here having a item,start index would be good #FIXME: here having a item,start index would be good
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True) modified = models.DateTimeField(auto_now=True)
@ -77,9 +62,9 @@ class Annotation(models.Model):
def editable(self, user): def editable(self, user):
if user.is_authenticated(): if user.is_authenticated():
if obj.user == user.id or user.has_perm('0x.admin'): if self.user == user or user.has_perm('0x.admin'):
return True return True
if user.groups.filter(id__in=obj.groups.all()).count() > 0: if user.groups.filter(id__in=self.groups.all()).count() > 0:
return True return True
return False return False
@ -102,4 +87,3 @@ class Annotation(models.Model):
def __unicode__(self): def __unicode__(self):
return "%s/%s-%s" %(self.item, self.start, self.stop) return "%s/%s-%s" %(self.item, self.start, self.stop)

View file

@ -7,7 +7,9 @@ Replace these with more appropriate tests for your application.
from django.test import TestCase from django.test import TestCase
class SimpleTest(TestCase): class SimpleTest(TestCase):
def test_basic_addition(self): def test_basic_addition(self):
""" """
Tests that 1 + 1 always equals 2. Tests that 1 + 1 always equals 2.
@ -20,4 +22,3 @@ Another way to test that 1 + 1 is equal to 2.
>>> 1 + 1 == 2 >>> 1 + 1 == 2
True True
"""} """}

View file

@ -1,19 +1,22 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# ci:si:et:sw=4:sts=4:ts=4 # ci:si:et:sw=4:sts=4:ts=4
import re
import ox
def html_parser(text, nofollow=True): def html_parser(text, nofollow=True):
text = text.replace('<i>', '__i__').replace('</i>', '__/i__') text = text.replace('<i>', '__i__').replace('</i>', '__/i__')
text = text.replace('<b>', '__b__').replace('</b>', '__/b__') text = text.replace('<b>', '__b__').replace('</b>', '__/b__')
#truns links into wiki links, make sure to only take http links #truns links into wiki links, make sure to only take http links
text = re.sub('<a .*?href="(http.*?)".*?>(.*?)</a>', '[\\1 \\2]', text) text = re.sub('<a .*?href="(http.*?)".*?>(.*?)</a>', '[\\1 \\2]', text)
text = escape(text) text = ox.escape(text)
text = text.replace('__i__', '<i>').replace('__/i__', '</i>') text = text.replace('__i__', '<i>').replace('__/i__', '</i>')
text = text.replace('__b__', '<b>').replace('__/b__', '</b>') text = text.replace('__b__', '<b>').replace('__/b__', '</b>')
if nofollow: if nofollow:
nofollow_rel = ' rel="nofollow"' nofollow_rel = ' rel="nofollow"'
else: else:
nofollow_rel = '' nofollow_rel = ''
links = re.compile('(\[(http.*?) (.*?)\])').findall(text) links = re.compile('(\[(http.*?) (.*?)\])').findall(text)
for t, link, txt in links: for t, link, txt in links:
link = link.replace('http', '__LINK__').replace('.', '__DOT__') link = link.replace('http', '__LINK__').replace('.', '__DOT__')
@ -24,8 +27,8 @@ def html_parser(text, nofollow=True):
link = link.replace('http', '__LINK__').replace('.', '__DOT__') link = link.replace('http', '__LINK__').replace('.', '__DOT__')
ll = '<a href="%s"%s>%s</a>' % (link, nofollow_rel, link) ll = '<a href="%s"%s>%s</a>' % (link, nofollow_rel, link)
text = text.replace(t, ll) text = text.replace(t, ll)
text = urlize(text, nofollow=nofollow) text = ox.urlize(text, nofollow=nofollow)
#inpage links #inpage links
text = re.sub('\[(/.+?) (.+?)\]', '<a href="\\1">\\2</a>', text) text = re.sub('\[(/.+?) (.+?)\]', '<a href="\\1">\\2</a>', text)
@ -33,4 +36,3 @@ def html_parser(text, nofollow=True):
text = text.replace('__LINK__', 'http').replace('__DOT__', '.') text = text.replace('__LINK__', 'http').replace('__DOT__', '.')
text = text.replace("\n", '<br />') text = text.replace("\n", '<br />')
return text return text

View file

@ -1,27 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
from __future__ import division 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, redirect
from django.template import RequestContext
from django.conf import settings
from ox.utils import json from ox.utils import json
from ox.django.decorators import login_required_json from ox.django.decorators import login_required_json
from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response
from ox.django.http import HttpFileResponse
import ox
import models import models
from api.actions import actions from api.actions import actions
@ -48,6 +31,7 @@ def findAnnotation(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(findAnnotation) actions.register(findAnnotation)
@login_required_json @login_required_json
def addAnnotation(request): def addAnnotation(request):
''' '''
@ -67,18 +51,18 @@ def addAnnotation(request):
data = json.loads(request.POST['data']) data = json.loads(request.POST['data'])
for key in ('item', 'layer', 'start', 'end', 'value'): for key in ('item', 'layer', 'start', 'end', 'value'):
if key not in data: if key not in data:
return render_to_json_response(json_response(status=400, text='invalid data')) return render_to_json_response(json_response(status=400,
text='invalid data'))
item = get_object_or_404_json(models.Item, itemId=data['item']) item = get_object_or_404_json(models.Item, itemId=data['item'])
layer = get_object_or_404_json(models.Layer, layerId=data['layer']) layer = get_object_or_404_json(models.Layer, layerId=data['layer'])
annotation = models.Annotation( annotation = models.Annotation(
item=item, item=item,
layer=layer, layer=layer,
user=request.user, user=request.user,
start=float(data['start']), end=float(data['end']), start=float(data['start']), end=float(data['end']),
value=data['value'] value=data['value'])
)
annotation.save() annotation.save()
response = json_response() response = json_response()
response['data']['annotation'] = annotation.json() response['data']['annotation'] = annotation.json()
@ -88,6 +72,7 @@ def addAnnotation(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(addAnnotation) actions.register(addAnnotation)
@login_required_json @login_required_json
def removeAnnotation(request): def removeAnnotation(request):
''' '''
@ -103,6 +88,7 @@ def removeAnnotation(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(removeAnnotation) actions.register(removeAnnotation)
@login_required_json @login_required_json
def editAnnotation(request): def editAnnotation(request):
''' '''
@ -129,4 +115,3 @@ def editAnnotation(request):
response = json_response(status=501, text='not implemented') response = json_response(status=501, text='not implemented')
return render_to_json_response(response) return render_to_json_response(response)
actions.register(editAnnotation) actions.register(editAnnotation)

View file

@ -18,7 +18,7 @@ def autodiscover():
import_module('%s.views'%app) import_module('%s.views'%app)
except: except:
if module_has_submodule(mod, 'views'): if module_has_submodule(mod, 'views'):
raise raise
def trim(docstring): def trim(docstring):
@ -46,8 +46,11 @@ def trim(docstring):
# Return a single string: # Return a single string:
return '\n'.join(trimmed) return '\n'.join(trimmed)
class ApiActions(dict): class ApiActions(dict):
def __init__(self): def __init__(self):
def api(request): def api(request):
''' '''
returns list of all known api action returns list of all known api action
@ -56,7 +59,8 @@ class ApiActions(dict):
''' '''
actions = self.keys() actions = self.keys()
actions.sort() actions.sort()
return render_to_json_response(json_response({'actions': actions})) response = json_response({'actions': actions})
return render_to_json_response(response)
self['api'] = api self['api'] = api
def apidoc(request): def apidoc(request):

View file

@ -1,54 +1,32 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
from __future__ import division 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.shortcuts import render_to_response
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, redirect
from django.template import RequestContext from django.template import RequestContext
from django.conf import settings from django.conf import settings
from ox.utils import json from ox.django.shortcuts import render_to_json_response, json_response
from ox.django.decorators import login_required_json
from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response
from ox.django.http import HttpFileResponse
import ox
import models
from pandora.user.models import get_user_json from pandora.user.models import get_user_json
from pandora.archive.models import File
from pandora.archive import extract
from actions import actions from actions import actions
def api(request): def api(request):
if request.META['REQUEST_METHOD'] == "OPTIONS": if request.META['REQUEST_METHOD'] == "OPTIONS":
response = HttpResponse('') response = render_to_json_response({'status': {'code': 200,
response = render_to_json_response({'status': {'code': 200, 'text': 'use POST'}}) 'text': 'use POST'}})
response['Access-Control-Allow-Origin'] = '*' response['Access-Control-Allow-Origin'] = '*'
return response return response
if not 'action' in request.POST: if not 'action' in request.POST:
methods = actions.keys() methods = actions.keys()
api = [] api = []
for f in sorted(methods): for f in sorted(methods):
api.append({ api.append({'name': f,
'name': f, 'doc': actions.doc(f).replace('\n', '<br>\n')})
'doc': actions.doc(f).replace('\n', '<br>\n')
})
context = RequestContext(request, {'api': api, context = RequestContext(request, {'api': api,
'sitename': settings.SITENAME,}) 'sitename': settings.SITENAME})
return render_to_response('api.html', context) return render_to_response('api.html', context)
function = request.POST['action'] function = request.POST['action']
#FIXME: possible to do this in f #FIXME: possible to do this in f
@ -63,6 +41,7 @@ def api(request):
response['Access-Control-Allow-Origin'] = '*' response['Access-Control-Allow-Origin'] = '*'
return response return response
def hello(request): def hello(request):
''' '''
return {'status': {'code': int, 'text': string}, return {'status': {'code': int, 'text': string},
@ -73,15 +52,17 @@ def hello(request):
if request.user.is_authenticated(): if request.user.is_authenticated():
response['data']['user'] = get_user_json(request.user) response['data']['user'] = get_user_json(request.user)
else: else:
response['data']['user'] = {'name': 'Guest', 'group': 'guest', 'preferences': {}} response['data']['user'] = {'name': 'Guest',
'group': 'guest',
'preferences': {}}
return render_to_json_response(response) return render_to_json_response(response)
actions.register(hello) actions.register(hello)
def error(request): def error(request):
''' '''
this action is used to test api error codes, it should return a 503 error this action is used to test api error codes, it should return a 503 error
''' '''
success = error_is_success success = error_is_success
return render_to_json_response({}) return render_to_json_response({})
actions.register(error) actions.register(error)

View file

@ -5,6 +5,7 @@ from django.contrib import admin
import models import models
class PageAdmin(admin.ModelAdmin): class PageAdmin(admin.ModelAdmin):
search_fields = ['name', 'body'] search_fields = ['name', 'body']
@ -15,4 +16,3 @@ class SiteSettingsAdmin(admin.ModelAdmin):
search_fields = ['key', 'value'] search_fields = ['key', 'value']
admin.site.register(models.SiteSettings, SiteSettingsAdmin) admin.site.register(models.SiteSettings, SiteSettingsAdmin)

View file

@ -2,6 +2,7 @@
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
from django.db import models from django.db import models
class Page(models.Model): class Page(models.Model):
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True) modified = models.DateTimeField(auto_now=True)
@ -11,10 +12,10 @@ class Page(models.Model):
def __unicode__(self): def __unicode__(self):
return self.name return self.name
class SiteSettings(models.Model): class SiteSettings(models.Model):
key = models.CharField(max_length=1024, unique=True) key = models.CharField(max_length=1024, unique=True)
value = models.TextField(blank=True) value = models.TextField(blank=True)
def __unicode__(self): def __unicode__(self):
return self.key return self.key

View file

@ -7,7 +7,9 @@ Replace these with more appropriate tests for your application.
from django.test import TestCase from django.test import TestCase
class SimpleTest(TestCase): class SimpleTest(TestCase):
def test_basic_addition(self): def test_basic_addition(self):
""" """
Tests that 1 + 1 always equals 2. Tests that 1 + 1 always equals 2.
@ -20,4 +22,3 @@ Another way to test that 1 + 1 is equal to 2.
>>> 1 + 1 == 2 >>> 1 + 1 == 2
True True
"""} """}

View file

@ -1,31 +1,32 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404 from django.shortcuts import render_to_response
from django.template import RequestContext from django.template import RequestContext
from django.conf import settings from django.conf import settings
from ox.django.shortcuts import json_response, render_to_json_response, get_object_or_404_json from ox.django.shortcuts import json_response, render_to_json_response, get_object_or_404_json
from ox.utils import json
import models import models
from item.models import siteJson
from api.actions import actions from api.actions import actions
def intro(request): def intro(request):
context = RequestContext(request, {'settings':settings}) context = RequestContext(request, {'settings': settings})
return render_to_response('intro.html', context) return render_to_response('intro.html', context)
def index(request): def index(request):
context = RequestContext(request, {'settings':settings}) context = RequestContext(request, {'settings': settings})
if request.GET.get('_escaped_fragment_', None):
return html_snapshot(request)
return render_to_response('index.html', context) return render_to_response('index.html', context)
def timeline(request): def timeline(request):
context = RequestContext(request, {'settings':settings}) context = RequestContext(request, {'settings': settings})
return render_to_response('timeline.html', context) return render_to_response('timeline.html', context)
def getPage(request): def getPage(request):
data = json.loads(request.POST['data']) data = json.loads(request.POST['data'])
name = data['page'] name = data['page']
@ -34,6 +35,7 @@ def getPage(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(getPage) actions.register(getPage)
def site_json(request): def site_json(request):
''' '''
return render_to_json_response(siteJson()) return render_to_json_response(siteJson())
@ -41,6 +43,7 @@ def site_json(request):
siteSettings = {} siteSettings = {}
for s in models.SiteSettings.objects.all(): for s in models.SiteSettings.objects.all():
siteSettings[s.key] = s.value siteSettings[s.key] = s.value
context = RequestContext(request, {'settings':settings, 'siteSettings': siteSettings}) context = RequestContext(request, {'settings': settings,
return render_to_response('site.json', context, mimetype="application/javascript") 'siteSettings': siteSettings})
return render_to_response('site.json', context,
mimetype="application/javascript")

View file

@ -6,6 +6,7 @@ from django.contrib import admin
from forms import FileAdminForm, InstanceAdminForm from forms import FileAdminForm, InstanceAdminForm
import models import models
class FileAdmin(admin.ModelAdmin): class FileAdmin(admin.ModelAdmin):
search_fields = ['name', 'video_codec'] search_fields = ['name', 'video_codec']
list_display = ['available', 'is_main', '__unicode__', 'itemId'] list_display = ['available', 'is_main', '__unicode__', 'itemId']
@ -18,9 +19,9 @@ class FileAdmin(admin.ModelAdmin):
admin.site.register(models.File, FileAdmin) admin.site.register(models.File, FileAdmin)
class InstanceAdmin(admin.ModelAdmin): class InstanceAdmin(admin.ModelAdmin):
search_fields = ['name', 'folder', 'volume__name'] search_fields = ['name', 'folder', 'volume__name']
form = InstanceAdminForm form = InstanceAdminForm
admin.site.register(models.Instance, InstanceAdmin) admin.site.register(models.Instance, InstanceAdmin)

View file

@ -3,33 +3,31 @@
from __future__ import division, with_statement from __future__ import division, with_statement
import os import os
from os.path import abspath, join, dirname, exists from os.path import exists
import fractions import fractions
import subprocess import subprocess
import sys
import shutil
import tempfile import tempfile
import time import time
import re
import math import math
from glob import glob from glob import glob
import numpy as np import numpy as np
import Image import Image
import ox
from ox.utils import json
img_extension='jpg' img_extension='jpg'
FFMPEG2THEORA = 'ffmpeg2theora' FFMPEG2THEORA = 'ffmpeg2theora'
class AspectRatio(fractions.Fraction): class AspectRatio(fractions.Fraction):
def __new__(cls, numerator, denominator=None): def __new__(cls, numerator, denominator=None):
if not denominator: if not denominator:
ratio = map(int, numerator.split(':')) ratio = map(int, numerator.split(':'))
if len(ratio) == 1: ratio.append(1) if len(ratio) == 1:
ratio.append(1)
numerator = ratio[0] numerator = ratio[0]
denominator = ratio[1] denominator = ratio[1]
#if its close enough to the common aspect ratios rather use that #if its close enough to the common aspect ratios rather use that
@ -45,6 +43,7 @@ class AspectRatio(fractions.Fraction):
def ratio(self): def ratio(self):
return "%d:%d" % (self.numerator, self.denominator) return "%d:%d" % (self.numerator, self.denominator)
def stream(video, target, profile, info): def stream(video, target, profile, info):
if not os.path.exists(target): if not os.path.exists(target):
fdir = os.path.dirname(target) fdir = os.path.dirname(target)
@ -118,8 +117,8 @@ def stream(video, target, profile, info):
bpp = 0.17 bpp = 0.17
fps = AspectRatio(info['video'][0]['framerate']) fps = AspectRatio(info['video'][0]['framerate'])
width = int(dar * height) width = int(dar * height)
width += width % 2 width += width % 2
bitrate = height*width*fps*bpp/1000 bitrate = height*width*fps*bpp/1000
aspect = dar.ratio aspect = dar.ratio
@ -184,7 +183,7 @@ def stream(video, target, profile, info):
'-me_range', '16', '-me_range', '16',
'-g', '250', #FIXME: should this be related to fps? '-g', '250', #FIXME: should this be related to fps?
'-keyint_min', '25', #FIXME: should this be related to fps? '-keyint_min', '25', #FIXME: should this be related to fps?
'-sc_threshold','40', '-sc_threshold', '40',
'-i_qfactor', '0.71', '-i_qfactor', '0.71',
'-qmin', '10', '-qmax', '51', '-qmin', '10', '-qmax', '51',
'-qdiff', '4' '-qdiff', '4'
@ -200,19 +199,20 @@ def stream(video, target, profile, info):
if format == 'mp4': if format == 'mp4':
cmd += ["%s.mp4"%target] cmd += ["%s.mp4"%target]
else: else:
cmd += ['-f','webm', target] cmd += ['-f', 'webm', target]
print cmd print cmd
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT) p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
p.communicate() p.communicate()
if format == 'mp4': if format == 'mp4':
cmd = ['qt-faststart', "%s.mp4"%target, target] cmd = ['qt-faststart', "%s.mp4"%target, target]
print cmd print cmd
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT) p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
p.communicate() p.communicate()
os.unlink("%s.mp4"%target) os.unlink("%s.mp4"%target)
return True return True
def run_command(cmd, timeout=10): def run_command(cmd, timeout=10):
#print cmd #print cmd
p = subprocess.Popen(cmd, stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT) p = subprocess.Popen(cmd, stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
@ -226,6 +226,7 @@ def run_command(cmd, timeout=10):
killedpid, stat = os.waitpid(p.pid, os.WNOHANG) killedpid, stat = os.waitpid(p.pid, os.WNOHANG)
return p.returncode return p.returncode
def frame(videoFile, frame, position, width=128, redo=False): def frame(videoFile, frame, position, width=128, redo=False):
''' '''
params: params:
@ -243,6 +244,7 @@ def frame(videoFile, frame, position, width=128, redo=False):
cmd = ['oxframe', '-i', videoFile, '-o', frame, '-p', str(position), '-x', str(width)] cmd = ['oxframe', '-i', videoFile, '-o', frame, '-p', str(position), '-x', str(width)]
run_command(cmd) run_command(cmd)
def resize_image(image_source, image_output, width=None, size=None): def resize_image(image_source, image_output, width=None, size=None):
if exists(image_source): if exists(image_source):
source = Image.open(image_source).convert('RGB') source = Image.open(image_source).convert('RGB')
@ -257,7 +259,7 @@ def resize_image(image_source, image_output, width=None, size=None):
height = size height = size
width = int(height * (float(source_width) / source_height)) width = int(height * (float(source_width) / source_height))
width = width - width % 2 width = width - width % 2
else: else:
height = int(width / (float(source_width) / source_height)) height = int(width / (float(source_width) / source_height))
height = height - height % 2 height = height - height % 2
@ -269,12 +271,13 @@ def resize_image(image_source, image_output, width=None, size=None):
output = source.resize((width, height), resize_method) output = source.resize((width, height), resize_method)
output.save(image_output) output.save(image_output)
def timeline(video, prefix): def timeline(video, prefix):
cmd = ['oxtimeline', '-i', video, '-o', prefix] cmd = ['oxtimeline', '-i', video, '-o', prefix]
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.wait() p.wait()
#stats based on timeline images
def average_color(prefix): def average_color(prefix):
height = 64 height = 64
width = 1500 width = 1500
@ -294,10 +297,12 @@ def average_color(prefix):
color += p color += p
return list(map(float, color)) return list(map(float, color))
def get_distance(rgb0, rgb1): def get_distance(rgb0, rgb1):
dst = math.sqrt(pow(rgb0[0] - rgb1[0], 2) + pow(rgb0[0] - rgb1[0], 2) + pow(rgb0[0] - rgb1[0], 2)) dst = math.sqrt(pow(rgb0[0] - rgb1[0], 2) + pow(rgb0[0] - rgb1[0], 2) + pow(rgb0[0] - rgb1[0], 2))
return dst / math.sqrt(3 * pow(255, 2)) return dst / math.sqrt(3 * pow(255, 2))
def cuts(prefix): def cuts(prefix):
cuts = [] cuts = []
fps = 25 fps = 25
@ -324,6 +329,7 @@ def cuts(prefix):
cuts.append(frame / fps) cuts.append(frame / fps)
return cuts return cuts
def divide(num, by): def divide(num, by):
# >>> divide(100, 3) # >>> divide(100, 3)
# [33, 33, 34] # [33, 33, 34]
@ -334,9 +340,10 @@ def divide(num, by):
arr.append(div + (i > by - 1 - mod)) arr.append(div + (i > by - 1 - mod))
return arr return arr
def timeline_strip(item, cuts, info, prefix): def timeline_strip(item, cuts, info, prefix):
_debug = False _debug = False
duration = info['duration'] duration = info['duration']
video_height = info['video'][0]['height'] video_height = info['video'][0]['height']
video_width = info['video'][0]['width'] video_width = info['video'][0]['width']
video_ratio = video_width / video_height video_ratio = video_width / video_height
@ -392,6 +399,7 @@ def timeline_strip(item, cuts, info, prefix):
print 'writing', timeline_file print 'writing', timeline_file
timeline_image.save(timeline_file) timeline_image.save(timeline_file)
def chop(video, start, end): def chop(video, start, end):
t = end - start t = end - start
tmp = tempfile.mkdtemp() tmp = tempfile.mkdtemp()
@ -401,7 +409,7 @@ def chop(video, start, end):
'-y', '-y',
'-i', video, '-i', video,
'-ss', '%.3f'%start, '-ss', '%.3f'%start,
'-t','%.3f'%t, '-t', '%.3f'%t,
'-vcodec', 'copy', '-vcodec', 'copy',
'-acodec', 'copy', '-acodec', 'copy',
'-f', 'webm', '-f', 'webm',

View file

@ -1,4 +1,4 @@
from ajax_filtered_fields.forms import AjaxManyToManyField, ForeignKeyByLetter from ajax_filtered_fields.forms import ForeignKeyByLetter
from django.conf import settings from django.conf import settings
from django import forms from django import forms
@ -10,6 +10,8 @@ ajax_filtered_js = (
settings.STATIC_URL + 'js/jquery/jquery.js', settings.STATIC_URL + 'js/jquery/jquery.js',
settings.STATIC_URL + 'js/ajax_filtered_fields.js', settings.STATIC_URL + 'js/ajax_filtered_fields.js',
) )
class FileAdminForm(forms.ModelForm): class FileAdminForm(forms.ModelForm):
item = ForeignKeyByLetter(models.Item, field_name='itemId') item = ForeignKeyByLetter(models.Item, field_name='itemId')
@ -28,4 +30,3 @@ class InstanceAdminForm(forms.ModelForm):
class Media: class Media:
js = ajax_filtered_js js = ajax_filtered_js

View file

@ -1,31 +1,23 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
from __future__ import division from __future__ import division
from datetime import datetime
import os.path import os.path
import random
import re import re
import time import time
from django.db import models from django.db import models
from django.db.models import Q
from django.contrib.auth.models import User 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 django.conf import settings
from ox.django import fields from ox.django import fields
import ox import ox
from ox import stripTags from ox.normalize import canonicalTitle
from ox.normalize import canonicalTitle, canonicalName
from firefogg import Firefogg
import chardet import chardet
from item import utils from item import utils
from item.models import Item from item.models import Item
import extract
class File(models.Model): class File(models.Model):
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
@ -37,7 +29,7 @@ class File(models.Model):
item = models.ForeignKey(Item, related_name='files') item = models.ForeignKey(Item, related_name='files')
name = models.CharField(max_length=2048, default="") # canoncial path/file name = models.CharField(max_length=2048, default="") # canoncial path/file
sort_name = models.CharField(max_length=2048, default="") # sort path/file name sort_name = models.CharField(max_length=2048, default="") # sort name
part = models.CharField(default="", max_length=255) part = models.CharField(default="", max_length=255)
version = models.CharField(default="", max_length=255) # sort path/file name version = models.CharField(default="", max_length=255) # sort path/file name
@ -146,13 +138,14 @@ class File(models.Model):
return None return None
def srt(self): def srt(self):
def _detectEncoding(fp): def _detectEncoding(fp):
bomDict={ # bytepattern : name bomDict={ # bytepattern : name
(0x00, 0x00, 0xFE, 0xFF) : "utf_32_be", (0x00, 0x00, 0xFE, 0xFF): "utf_32_be",
(0xFF, 0xFE, 0x00, 0x00) : "utf_32_le", (0xFF, 0xFE, 0x00, 0x00): "utf_32_le",
(0xFE, 0xFF, None, None) : "utf_16_be", (0xFE, 0xFF, None, None): "utf_16_be",
(0xFF, 0xFE, None, None) : "utf_16_le", (0xFF, 0xFE, None, None): "utf_16_le",
(0xEF, 0xBB, 0xBF, None) : "utf_8", (0xEF, 0xBB, 0xBF, None): "utf_8",
} }
# go to beginning of file and get the first 4 bytes # go to beginning of file and get the first 4 bytes
@ -162,15 +155,15 @@ class File(models.Model):
# try bom detection using 4 bytes, 3 bytes, or 2 bytes # try bom detection using 4 bytes, 3 bytes, or 2 bytes
bomDetection = bomDict.get((byte1, byte2, byte3, byte4)) bomDetection = bomDict.get((byte1, byte2, byte3, byte4))
if not bomDetection : if not bomDetection:
bomDetection = bomDict.get((byte1, byte2, byte3, None)) bomDetection = bomDict.get((byte1, byte2, byte3, None))
if not bomDetection : if not bomDetection:
bomDetection = bomDict.get((byte1, byte2, None, None)) bomDetection = bomDict.get((byte1, byte2, None, None))
## if BOM detected, we're done :-) ## if BOM detected, we're done :-)
fp.seek(oldFP) fp.seek(oldFP)
if bomDetection : if bomDetection:
return bomDetection return bomDetection
encoding = 'latin-1' encoding = 'latin-1'
#more character detecting magick using http://chardet.feedparser.org/ #more character detecting magick using http://chardet.feedparser.org/
@ -221,7 +214,9 @@ class File(models.Model):
return True return True
return False return False
class Volume(models.Model): class Volume(models.Model):
class Meta: class Meta:
unique_together = ("user", "name") unique_together = ("user", "name")
@ -234,7 +229,9 @@ class Volume(models.Model):
def __unicode__(self): def __unicode__(self):
return u"%s's %s"% (self.user, self.name) return u"%s's %s"% (self.user, self.name)
class Instance(models.Model): class Instance(models.Model):
class Meta: class Meta:
unique_together = ("name", "folder", "volume") unique_together = ("name", "folder", "volume")
@ -258,12 +255,15 @@ class Instance(models.Model):
def itemId(self): def itemId(self):
return File.objects.get(oshash=self.oshash).itemId return File.objects.get(oshash=self.oshash).itemId
def frame_path(frame, name): def frame_path(frame, name):
ext = os.path.splitext(name)[-1] ext = os.path.splitext(name)[-1]
name = "%s%s" % (frame.position, ext) name = "%s%s" % (frame.position, ext)
return frame.file.path(name) return frame.file.path(name)
class Frame(models.Model): class Frame(models.Model):
class Meta: class Meta:
unique_together = ("file", "position") unique_together = ("file", "position")
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
@ -282,5 +282,3 @@ class Frame(models.Model):
def __unicode__(self): def __unicode__(self):
return u'%s at %s' % (self.file, self.position) return u'%s at %s' % (self.file, self.position)

View file

@ -1,17 +1,15 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
from datetime import timedelta
import os import os
import re
from celery.decorators import task, periodic_task from celery.decorators import task
from item.utils import oxid, parse_path from item.utils import parse_path
from item.models import get_item from item.models import get_item
import item.tasks
import models import models
@task(ignore_resulsts=True, queue='default') @task(ignore_resulsts=True, queue='default')
def update_files(user, volume, files): def update_files(user, volume, files):
user = models.User.objects.get(username=user) user = models.User.objects.get(username=user)
@ -33,9 +31,9 @@ def update_files(user, volume, files):
same_folder = models.Instance.objects.filter(folder=folder, volume=volume) same_folder = models.Instance.objects.filter(folder=folder, volume=volume)
if same_folder.count() > 0: if same_folder.count() > 0:
item = same_folder[0].file.item i = same_folder[0].file.item
else: else:
item = None i = None
path = os.path.join(folder, name) path = os.path.join(folder, name)
@ -48,7 +46,7 @@ def update_files(user, volume, files):
setattr(instance, key, f[key]) setattr(instance, key, f[key])
updated=True updated=True
if updated: if updated:
instance.save() instance.save()
else: else:
#look if oshash is known #look if oshash is known
file_objects = models.File.objects.filter(oshash=oshash) file_objects = models.File.objects.filter(oshash=oshash)
@ -56,13 +54,13 @@ def update_files(user, volume, files):
file_object = file_objects[0] file_object = file_objects[0]
#new oshash, add to database #new oshash, add to database
else: else:
if not item: if not i:
item_info = parse_path(folder) item_info = parse_path(folder)
item = get_item(item_info) i = get_item(item_info)
file_object = models.File() file_object = models.File()
file_object.oshash = oshash file_object.oshash = oshash
file_object.name = name file_object.name = name
file_object.item = item file_object.item = i
file_object.save() file_object.save()
instance = models.Instance() instance = models.Instance()
instance.volume = volume instance.volume = volume
@ -74,4 +72,3 @@ def update_files(user, volume, files):
#remove deleted files #remove deleted files
#FIXME: can this have any bad consequences? i.e. on the selction of used item files. #FIXME: can this have any bad consequences? i.e. on the selction of used item files.
models.Instance.objects.filter(volume=volume).exclude(file__oshash__in=all_files).delete() models.Instance.objects.filter(volume=volume).exclude(file__oshash__in=all_files).delete()

View file

@ -7,7 +7,9 @@ Replace these with more appropriate tests for your application.
from django.test import TestCase from django.test import TestCase
class SimpleTest(TestCase): class SimpleTest(TestCase):
def test_basic_addition(self): def test_basic_addition(self):
""" """
Tests that 1 + 1 always equals 2. Tests that 1 + 1 always equals 2.
@ -20,4 +22,3 @@ Another way to test that 1 + 1 is equal to 2.
>>> 1 + 1 == 2 >>> 1 + 1 == 2
True True
"""} """}

View file

@ -2,29 +2,18 @@
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
from __future__ import division from __future__ import division
import os.path import os.path
import re
from datetime import datetime from datetime import datetime
from urllib2 import unquote
import mimetypes
from django import forms from django import forms
from django.core.paginator import Paginator from django.shortcuts import get_object_or_404, redirect
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, redirect
from django.template import RequestContext
from django.conf import settings from django.conf import settings
from ox.utils import json from ox.utils import json
from ox.django.decorators import login_required_json from ox.django.decorators import login_required_json
from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response
from ox.django.http import HttpFileResponse
from ox.django.views import task_status from ox.django.views import task_status
import ox
from item.utils import oxid, parse_path from item.utils import parse_path
from item.models import get_item from item.models import get_item
import item.tasks import item.tasks
from api.actions import actions from api.actions import actions
@ -47,6 +36,7 @@ def removeVolume(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(removeVolume) actions.register(removeVolume)
@login_required_json @login_required_json
def update(request): def update(request):
''' '''
@ -111,12 +101,14 @@ def update(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(update) actions.register(update)
@login_required_json @login_required_json
def encodingProfile(request): def encodingProfile(request):
response = json_response({'profile': settings.VIDEO_PROFILE}) response = json_response({'profile': settings.VIDEO_PROFILE})
return render_to_json_response(response) return render_to_json_response(response)
actions.register(encodingProfile) actions.register(encodingProfile)
@login_required_json @login_required_json
def upload(request): def upload(request):
''' '''
@ -157,11 +149,13 @@ def upload(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(upload) actions.register(upload)
class VideoChunkForm(forms.Form): class VideoChunkForm(forms.Form):
chunk = forms.FileField() chunk = forms.FileField()
chunkId = forms.IntegerField(required=False) chunkId = forms.IntegerField(required=False)
done = forms.IntegerField(required=False) done = forms.IntegerField(required=False)
@login_required_json @login_required_json
def firefogg_upload(request): def firefogg_upload(request):
profile = request.GET['profile'] profile = request.GET['profile']
@ -189,7 +183,7 @@ def firefogg_upload(request):
#FIXME: this fails badly if rabbitmq goes down #FIXME: this fails badly if rabbitmq goes down
try: try:
t = item.tasks.update_streams.delay((f.item.itemId)) t = item.tasks.update_streams.delay((f.item.itemId))
data['resultUrl'] = t.task_id response['resultUrl'] = t.task_id
except: except:
pass pass
response['result'] = 1 response['result'] = 1
@ -213,6 +207,7 @@ def firefogg_upload(request):
response = json_response(status=400, text='this request requires POST') response = json_response(status=400, text='this request requires POST')
return render_to_json_response(response) return render_to_json_response(response)
@login_required_json @login_required_json
def taskStatus(request): def taskStatus(request):
#FIXME: should check if user has permissions to get status #FIXME: should check if user has permissions to get status
@ -223,6 +218,7 @@ def taskStatus(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(taskStatus) actions.register(taskStatus)
@login_required_json @login_required_json
def editFile(request): def editFile(request):
''' '''
@ -257,6 +253,7 @@ def editFile(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(editFile) actions.register(editFile)
def lookup_file(request, oshash): def lookup_file(request, oshash):
f = get_object_or_404(models.File, oshash=oshash) f = get_object_or_404(models.File, oshash=oshash)
return redirect(f.item.get_absolute_url()) return redirect(f.item.get_absolute_url())
@ -270,34 +267,34 @@ def fileInfo(request):
'data': {imdbId:string }} 'data': {imdbId:string }}
''' '''
if 'data' in request.POST: if 'data' in request.POST:
oshash = json.loads(request.POST['data']) oshash = json.loads(request.POST['data'])
elif 'oshash' in request.GET: elif 'oshash' in request.GET:
oshash = request.GET['oshash'] oshash = request.GET['oshash']
f = models.ItemFile.objects.get(oshash=oshash) f = models.ItemFile.objects.get(oshash=oshash)
response = {'data': f.json()} response = {'data': f.json()}
return render_to_json_response(response) return render_to_json_response(response)
actions.register(fileInfo) actions.register(fileInfo)
def subtitles(request): def subtitles(request):
''' '''
param data param data
oshash string oshash string
language string language string
subtitle string subtitle string
return return
if no language is provided: if no language is provided:
{data: {languages: array}} {data: {languages: array}}
if language is set: if language is set:
{data: {subtitle: string}} {data: {subtitle: string}}
if subtitle is set: if subtitle is set:
saves subtitle for given language saves subtitle for given language
''' '''
if 'data' in request.POST: if 'data' in request.POST:
data = json.loads(request.POST['data']) data = json.loads(request.POST['data'])
oshash = data['oshash'] oshash = data['oshash']
language = data.get('language', None) language = data.get('language', None)
srt = data.get('subtitle', None) srt = data.get('subtitle', None)
if srt: if srt:
user = request.user user = request.user
sub = models.Subtitles.objects.get_or_create(user, oshash, language) sub = models.Subtitles.objects.get_or_create(user, oshash, language)
sub.srt = srt sub.srt = srt
@ -307,8 +304,8 @@ def subtitles(request):
if language: if language:
q = models.Subtitles.objects.filter(item_file__oshash=oshash, language=language) q = models.Subtitles.objects.filter(item_file__oshash=oshash, language=language)
if q.count() > 0: if q.count() > 0:
response['data']['subtitle'] = q[0].srt response['data']['subtitle'] = q[0].srt
return render_to_json_response(response) return render_to_json_response(response)
l = models.Subtitles.objects.filter(item_file__oshash=oshash).values('language') l = models.Subtitles.objects.filter(item_file__oshash=oshash).values('language')
response['data']['languages'] = [f['language'] for f in l] response['data']['languages'] = [f['language'] for f in l]
return render_to_json_response(response) return render_to_json_response(response)

View file

@ -9,4 +9,3 @@ import models
class DateAdmin(admin.ModelAdmin): class DateAdmin(admin.ModelAdmin):
search_fields = ['name'] search_fields = ['name']
admin.site.register(models.Date, DateAdmin) admin.site.register(models.Date, DateAdmin)

View file

@ -1,15 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
import re
from ox.utils import json
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q, Manager from django.db.models import Q, Manager
import models
class DateManager(Manager): class DateManager(Manager):
def get_query_set(self): def get_query_set(self):
return super(DateManager, self).get_query_set() return super(DateManager, self).get_query_set()

View file

@ -3,13 +3,12 @@
from __future__ import division, with_statement from __future__ import division, with_statement
from django.db import models from django.db import models
from django.db.models import Q
from django.conf import settings
from ox.django import fields from ox.django import fields
import managers import managers
class Date(models.Model): class Date(models.Model):
''' '''
Dates are dates in time that can be once or recurring, Dates are dates in time that can be once or recurring,
@ -41,4 +40,3 @@ class Date(models.Model):
self.name_sort = self.name self.name_sort = self.name
self.name_find = self.name + '||'.join(self.aliases) self.name_find = self.name + '||'.join(self.aliases)
super(Date, self).save(*args, **kwargs) super(Date, self).save(*args, **kwargs)

View file

@ -7,7 +7,9 @@ Replace these with more appropriate tests for your application.
from django.test import TestCase from django.test import TestCase
class SimpleTest(TestCase): class SimpleTest(TestCase):
def test_basic_addition(self): def test_basic_addition(self):
""" """
Tests that 1 + 1 always equals 2. Tests that 1 + 1 always equals 2.
@ -20,4 +22,3 @@ Another way to test that 1 + 1 is equal to 2.
>>> 1 + 1 == 2 >>> 1 + 1 == 2
True True
"""} """}

View file

@ -1,32 +1,15 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
from __future__ import division 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, redirect
from django.template import RequestContext
from django.conf import settings
from ox.utils import json from ox.utils import json
from ox.django.decorators import login_required_json from ox.django.decorators import login_required_json
from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response
from ox.django.http import HttpFileResponse
import ox
import models import models
from api.actions import actions from api.actions import actions
@login_required_json @login_required_json
def addDate(request): def addDate(request):
data = json.loads(request.POST['data']) data = json.loads(request.POST['data'])
@ -39,6 +22,7 @@ def addDate(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(addDate) actions.register(addDate)
@login_required_json @login_required_json
def editDate(request): def editDate(request):
''' '''
@ -69,17 +53,19 @@ def editDate(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(editDate) actions.register(editDate)
@login_required_json @login_required_json
def removeDate(request): def removeDate(request):
response = json_response(status=501, text='not implemented') response = json_response(status=501, text='not implemented')
return render_to_json_response(response) return render_to_json_response(response)
actions.register(removeDate) actions.register(removeDate)
def findDate(request): def findDate(request):
''' '''
param data param data
{'query': query, 'sort': array, 'range': array} {'query': query, 'sort': array, 'range': array}
query: query object, more on query syntax at query: query object, more on query syntax at
https://wiki.0x2620.org/wiki/pandora/QuerySyntax https://wiki.0x2620.org/wiki/pandora/QuerySyntax
sort: array of key, operator dics sort: array of key, operator dics
@ -102,7 +88,7 @@ def findDate(request):
Positions Positions
param data param data
{'query': query, 'ids': []} {'query': query, 'ids': []}
query: query object, more on query syntax at query: query object, more on query syntax at
https://wiki.0x2620.org/wiki/pandora/QuerySyntax https://wiki.0x2620.org/wiki/pandora/QuerySyntax
ids: ids of dates for which positions are required ids: ids of dates for which positions are required
@ -111,8 +97,7 @@ Positions
response = json_response(status=200, text='ok') response = json_response(status=200, text='ok')
response['data']['places'] = [] response['data']['places'] = []
#FIXME: add coordinates to limit search #FIXME: add coordinates to limit search
for p in Dates.objects.find(data['query']): for p in models.Date.objects.find(data['query']):
response['data']['dates'].append(p.json()) response['data']['dates'].append(p.json())
return render_to_json_response(response) return render_to_json_response(response)
actions.register(findDate) actions.register(findDate)

View file

@ -5,6 +5,7 @@ from django.contrib import admin
import models import models
class ItemAdmin(admin.ModelAdmin): class ItemAdmin(admin.ModelAdmin):
search_fields = ['itemId', 'data', 'external_data'] search_fields = ['itemId', 'data', 'external_data']
list_display = ['available', 'itemId', '__unicode__'] list_display = ['available', 'itemId', '__unicode__']
@ -12,8 +13,7 @@ class ItemAdmin(admin.ModelAdmin):
admin.site.register(models.Item, ItemAdmin) admin.site.register(models.Item, ItemAdmin)
class PropertyAdmin(admin.ModelAdmin): class PropertyAdmin(admin.ModelAdmin):
search_fields = ['name', 'title'] search_fields = ['name', 'title']
admin.site.register(models.Property, PropertyAdmin) admin.site.register(models.Property, PropertyAdmin)

View file

@ -1,8 +1,5 @@
from ajax_filtered_fields.forms import AjaxManyToManyField, ForeignKeyByLetter from ajax_filtered_fields.forms import AjaxManyToManyField, ForeignKeyByLetter
from django.conf import settings from django.conf import settings
from django import forms
import models
ajax_filtered_js = ( ajax_filtered_js = (
settings.ADMIN_MEDIA_PREFIX + "js/SelectBox.js", settings.ADMIN_MEDIA_PREFIX + "js/SelectBox.js",

View file

@ -1,14 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
import re
from datetime import datetime from datetime import datetime
from urllib2 import unquote
from ox.utils import json
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q, Manager from django.db.models import Q, Manager
import models import models
@ -21,6 +16,7 @@ def keyType(key):
return "float" return "float"
return "string" return "string"
def parseCondition(condition): def parseCondition(condition):
''' '''
condition: { condition: {
@ -33,13 +29,15 @@ def parseCondition(condition):
operator: "!=" operator: "!="
} }
... ...
''' '''
k = condition.get('key', 'all') k = condition.get('key', 'all')
k = {'id': 'itemId'}.get(k, k) k = {'id': 'itemId'}.get(k, k)
if not k: k = 'all' if not k:
k = 'all'
v = condition['value'] v = condition['value']
op = condition.get('operator', None) op = condition.get('operator', None)
if not op: op = '~' if not op:
op = '~'
if op.startswith('!'): if op.startswith('!'):
op = op[1:] op = op[1:]
exclude = True exclude = True
@ -66,16 +64,17 @@ def parseCondition(condition):
k = str(k) k = str(k)
if exclude: if exclude:
if in_find and not k.startswith('itemId'): if in_find and not k.startswith('itemId'):
q = ~Q(**{'find__key':k, value_key:v}) q = ~Q(**{'find__key': k, value_key: v})
else: else:
q = ~Q(**{k:v}) q = ~Q(**{k: v})
else: else:
if in_find and not k.startswith('itemId'): if in_find and not k.startswith('itemId'):
q = Q(**{'find__key':k, value_key:v}) q = Q(**{'find__key': k, value_key: v})
else: else:
q = Q(**{k:v}) q = Q(**{k: v})
return q return q
else: #number or date else: #number or date
def parseDate(d): def parseDate(d):
while len(d) < 3: while len(d) < 3:
d.append(1) d.append(1)
@ -90,11 +89,11 @@ def parseCondition(condition):
if exclude: #!1960-1970 if exclude: #!1960-1970
k1 = 'value__lt' k1 = 'value__lt'
k2 = 'value__gte' k2 = 'value__gte'
return Q(**{'find__key': k, k1:v1})|Q(**{'find__key': k, k2:v2}) return Q(**{'find__key': k, k1: v1})|Q(**{'find__key': k, k2: v2})
else: #1960-1970 else: #1960-1970
k1 = 'value__gte' k1 = 'value__gte'
k2 = 'value__lt' k2 = 'value__lt'
return Q(**{'find__key': k, k1:v1})&Q(**{'find__key': k, k2:v2}) return Q(**{'find__key': k, k1: v1})&Q(**{'find__key': k, k2: v2})
else: else:
if keyType(k) == "date": if keyType(k) == "date":
v = parseDate(v.split('.')) v = parseDate(v.split('.'))
@ -113,9 +112,10 @@ def parseCondition(condition):
vk = str(vk) vk = str(vk)
if exclude: #!1960 if exclude: #!1960
return ~Q(**{'find__key': k, vk:v}) return ~Q(**{'find__key': k, vk: v})
else: #1960 else: #1960
return Q(**{'find__key': k, vk:v}) return Q(**{'find__key': k, vk: v})
def parseConditions(conditions, operator): def parseConditions(conditions, operator):
''' '''
@ -135,16 +135,18 @@ def parseConditions(conditions, operator):
} }
], ],
operator: "&" operator: "&"
''' '''
conn = [] conn = []
for condition in conditions: for condition in conditions:
if 'conditions' in condition: if 'conditions' in condition:
q = parseConditions(condition['conditions'], q = parseConditions(condition['conditions'],
condition.get('operator', '&')) condition.get('operator', '&'))
if q: conn.append(q) if q:
conn.append(q)
pass pass
else: else:
if condition.get('value', '') != '' or condition.get('operator', '') == '=': if condition.get('value', '') != '' or \
condition.get('operator', '') == '=':
conn.append(parseCondition(condition)) conn.append(parseCondition(condition))
if conn: if conn:
q = conn[0] q = conn[0]
@ -156,7 +158,9 @@ def parseConditions(conditions, operator):
return q return q
return None return None
class ItemManager(Manager): class ItemManager(Manager):
def get_query_set(self): def get_query_set(self):
return super(ItemManager, self).get_query_set() return super(ItemManager, self).get_query_set()
@ -165,13 +169,14 @@ class ItemManager(Manager):
l = l.split(":") l = l.split(":")
only_public = True only_public = True
if not user.is_anonymous(): if not user.is_anonymous():
if len(l) == 1: l = [request.user.username] + l if len(l) == 1:
if request.user.username == l[0]: l = [user.username] + l
if user.username == l[0]:
only_public = False only_public = False
if len(l) == 2: if len(l) == 2:
lqs = models.List.objects.filter(name=l[1], user__username=l[0]) lqs = models.List.objects.filter(name=l[1], user__username=l[0])
if only_public: if only_public:
lqs = qls.filter(public=True) lqs = lqs.filter(public=True)
if lqs.count() == 1: if lqs.count() == 1:
qs = qs.filter(listitem__list__id=lqs[0].id) qs = qs.filter(listitem__list__id=lqs[0].id)
return qs return qs
@ -196,7 +201,7 @@ class ItemManager(Manager):
], ],
operator: "&" operator: "&"
} }
''' '''
#join query with operator #join query with operator
qs = self.get_query_set() qs = self.get_query_set()
@ -212,4 +217,3 @@ class ItemManager(Manager):
l = data.get('list', 'all') l = data.get('list', 'all')
qs = self.filter_list(qs, l, user) qs = self.filter_list(qs, l, user)
return qs return qs

View file

@ -4,26 +4,18 @@ from __future__ import division, with_statement
from datetime import datetime from datetime import datetime
import os.path import os.path
import math
import random
import re
import subprocess import subprocess
import unicodedata
from glob import glob from glob import glob
from django.db import models 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.core.files.base import ContentFile
from django.utils import simplejson as json from django.utils import simplejson as json
from django.conf import settings from django.conf import settings
from ox.django import fields
import ox import ox
from ox import stripTags from ox.django import fields
from ox.normalize import canonicalTitle
import ox.web.imdb import ox.web.imdb
from ox.normalize import canonicalTitle, canonicalName
from firefogg import Firefogg
import managers import managers
import utils import utils
@ -31,23 +23,23 @@ import tasks
from archive import extract from archive import extract
from annotaion.models import Annotation, Layer from annotaion.models import Annotation, Layer
from person.models import get_name_sort, Person from person.models import get_name_sort
def siteJson(): def siteJson():
r = {} r = {}
r['findKeys'] = [{"id": "all", "title": "All"}] r['findKeys'] = [{"id": "all", "title": "All"}]
for p in Property.objects.all(): for p in Property.objects.all():
if p.find: if p.find:
title = p.title title = p.title
if not title: if not title:
title = p.name.capitalize() title = p.name.capitalize()
f = {"id": p.name, "title": title} f = {"id": p.name, "title": title}
f['autocomplete'] = p.autocomplete f['autocomplete'] = p.autocomplete
r['findKeys'].append(f) r['findKeys'].append(f)
r['groups'] = [p.name for p in Property.objects.filter(group=True)] r['groups'] = [p.name for p in Property.objects.filter(group=True)]
r['layers'] = [l.json() for l in Layer.objects.all()] r['layers'] = [l.json() for l in Layer.objects.all()]
r['itemViews'] = [ r['itemViews'] = [
{"id": "info", "title": "Info"}, {"id": "info", "title": "Info"},
@ -81,28 +73,28 @@ def siteJson():
{"id": "public", "title": "Public Lists"}, {"id": "public", "title": "Public Lists"},
{"id": "featured", "title": "Featured Lists"} {"id": "featured", "title": "Featured Lists"}
] ]
r['sortKeys'] = [] r['sortKeys'] = []
for p in Property.objects.exclude(sort=''): for p in Property.objects.exclude(sort=''):
title = p.title title = p.title
if not title: if not title:
title = p.name.capitalize() title = p.name.capitalize()
f = { f = {
"id": p.name, "id": p.name,
"title": title, "title": title,
"operator": p.operator, "operator": p.operator,
"align": p.align, "align": p.align,
"width": p.width, "width": p.width,
} }
if not p.removable: if not p.removable:
f['removable'] = False f['removable'] = False
r['sortKeys'].append(f) r['sortKeys'].append(f)
r['sortKeys'].append([{"id": "id", "title": "ID", "operator": "", "align": "left", "width": 90}]) r['sortKeys'].append([{"id": "id", "title": "ID", "operator": "", "align": "left", "width": 90}])
r['totals'] = [{"id": "items"}] r['totals'] = [{"id": "items"}]
for p in Property.objects.filter(totals=True): for p in Property.objects.filter(totals=True):
f = {'id': p.name, 'admin': p.admin} f = {'id': p.name, 'admin': p.admin}
r['totals'].append(f) r['totals'].append(f)
#FIXME: defaults should also be populated from properties #FIXME: defaults should also be populated from properties
r["user"] = { r["user"] = {
@ -127,7 +119,8 @@ def siteJson():
}, },
"username": "" "username": ""
} }
return r return r
def get_item(info): def get_item(info):
''' '''
@ -176,28 +169,30 @@ def get_item(info):
item.save() item.save()
return item return item
class Property(models.Model): class Property(models.Model):
class Meta: class Meta:
ordering = ('position', ) ordering = ('position', )
verbose_name_plural = "Properties" verbose_name_plural = "Properties"
name = models.CharField(null=True, max_length=255, unique=True) name = models.CharField(null=True, max_length=255, unique=True)
title = models.CharField(null=True, max_length=255, blank=True) title = models.CharField(null=True, max_length=255, blank=True)
#text, string, string from list(fixme), event, place, person #text, string, string from list(fixme), event, place, person
type = models.CharField(null=True, max_length=255) type = models.CharField(null=True, max_length=255)
array = models.BooleanField(default=False) array = models.BooleanField(default=False)
position = models.IntegerField(default=0) position = models.IntegerField(default=0)
width = models.IntegerField(default=180) width = models.IntegerField(default=180)
align = models.CharField(null=True, max_length=255, default='left') align = models.CharField(null=True, max_length=255, default='left')
operator = models.CharField(null=True, max_length=5, default='', blank=True) operator = models.CharField(null=True, max_length=5, default='', blank=True)
default = models.BooleanField('Enabled by default', default=False) default = models.BooleanField('Enabled by default', default=False)
removable = models.BooleanField(default=True) removable = models.BooleanField(default=True)
#sort values: title, string, integer, float, date #sort values: title, string, integer, float, date
sort = models.CharField(null=True, max_length=255, blank=True) sort = models.CharField(null=True, max_length=255, blank=True)
find = models.BooleanField(default=False) find = models.BooleanField(default=False)
autocomplete = models.BooleanField(default=False) autocomplete = models.BooleanField(default=False)
group = models.BooleanField(default=False) group = models.BooleanField(default=False)
totals = models.BooleanField(default=False) totals = models.BooleanField(default=False)
admin = models.BooleanField(default=False) admin = models.BooleanField(default=False)
@ -207,19 +202,20 @@ class Property(models.Model):
return self.title return self.title
return self.name return self.name
def json(self): def json(self):
j = {} j = {}
for key in ('type', 'sort', 'title', 'array', 'totals', 'admin'): for key in ('type', 'sort', 'title', 'array', 'totals', 'admin'):
value = getattr(self, key) value = getattr(self, key)
if value: if value:
j[key] = value j[key] = value
return j return j
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if not self.title: if not self.title:
self.title = self.name.capitalize() self.title = self.name.capitalize()
super(Property, self).save(*args, **kwargs) super(Property, self).save(*args, **kwargs)
class Item(models.Model): class Item(models.Model):
person_keys = ('director', 'writer', 'producer', 'editor', 'cinematographer', 'actor', 'character') person_keys = ('director', 'writer', 'producer', 'editor', 'cinematographer', 'actor', 'character')
facet_keys = person_keys + ('country', 'language', 'genre', 'keyword') facet_keys = person_keys + ('country', 'language', 'genre', 'keyword')
@ -228,7 +224,7 @@ class Item(models.Model):
published = models.DateTimeField(default=datetime.now, editable=False) published = models.DateTimeField(default=datetime.now, editable=False)
#only items that have data from files are available, #only items that have data from files are available,
#this is indicated by setting available to True #this is indicated by setting available to True
available = models.BooleanField(default=False, db_index=True) available = models.BooleanField(default=False, db_index=True)
itemId = models.CharField(max_length=128, unique=True, blank=True) itemId = models.CharField(max_length=128, unique=True, blank=True)
oxdbId = models.CharField(max_length=42, unique=True, blank=True) oxdbId = models.CharField(max_length=42, unique=True, blank=True)
@ -258,9 +254,9 @@ class Item(models.Model):
def edit(self, data): def edit(self, data):
#FIXME: how to map the keys to the right place to write them to? #FIXME: how to map the keys to the right place to write them to?
for key in data: for key in data:
if key != 'id': if key != 'id':
setattr(self.data, key, data[key]) setattr(self.data, key, data[key])
self.oxdb.save() self.oxdb.save()
self.save() self.save()
@ -339,11 +335,11 @@ class Item(models.Model):
#FIXME: this should not be used #FIXME: this should not be used
_public_fields = { _public_fields = {
'itemId': 'id', 'itemId': 'id',
'title': 'title', 'title': 'title',
'year': 'year', 'year': 'year',
'runtime': 'runtime', 'runtime': 'runtime',
'release_date': 'release_date', 'release_date': 'release_date',
'countries': 'country', 'countries': 'country',
'directors': 'director', 'directors': 'director',
@ -402,7 +398,7 @@ class Item(models.Model):
s = self.streams.all()[0] s = self.streams.all()[0]
if s.video and s.info: if s.video and s.info:
stream['duration'] = s.info['duration'] stream['duration'] = s.info['duration']
if 'video' in s.info and s.info['video']: if 'video' in s.info and s.info['video']:
stream['aspectRatio'] = s.info['video'][0]['width'] / s.info['video'][0]['height'] stream['aspectRatio'] = s.info['video'][0]['width'] / s.info['video'][0]['height']
if settings.XSENDFILE or settings.XACCELREDIRECT: if settings.XSENDFILE or settings.XACCELREDIRECT:
stream['baseUrl'] = '/%s' % self.itemId stream['baseUrl'] = '/%s' % self.itemId
@ -414,7 +410,7 @@ class Item(models.Model):
def get_layers(self): def get_layers(self):
layers = {} layers = {}
layers['cuts'] = self.data.get('cuts', {}) layers['cuts'] = self.data.get('cuts', {})
layers['subtitles'] = {} layers['subtitles'] = {}
#FIXME: subtitles should be stored in Annotation #FIXME: subtitles should be stored in Annotation
qs = self.files.filter(is_subtitle=True, is_main=True, available=True) qs = self.files.filter(is_subtitle=True, is_main=True, available=True)
@ -468,7 +464,9 @@ class Item(models.Model):
''' '''
Search related functions Search related functions
''' '''
def update_find(self): def update_find(self):
def save(key, value): def save(key, value):
f, created = ItemFind.objects.get_or_create(item=self, key=key) f, created = ItemFind.objects.get_or_create(item=self, key=key)
if value not in ('', '||'): if value not in ('', '||'):
@ -478,7 +476,7 @@ class Item(models.Model):
f.delete() f.delete()
save('title', '\n'.join([self.get('title'), self.get('original_title', '')])) save('title', '\n'.join([self.get('title'), self.get('original_title', '')]))
#FIXME: filter us/int title #FIXME: filter us/int title
#f.title += ' '.join([t.title for t in self.alternative_titles()]) #f.title += ' '.join([t.title for t in self.alternative_titles()])
@ -537,17 +535,17 @@ class Item(models.Model):
for key in ('keywords', 'genres', 'cast', 'summary', 'trivia', 'connections'): for key in ('keywords', 'genres', 'cast', 'summary', 'trivia', 'connections'):
setattr(s, key, len(self.get(key, ''))) setattr(s, key, len(self.get(key, '')))
s.itemId = self.itemId.replace('0x', 'xx') s.itemId = self.itemId.replace('0x', 'xx')
s.rating = self.get('rating', -1) s.rating = self.get('rating', -1)
s.votes = self.get('votes', -1) s.votes = self.get('votes', -1)
# data from related subtitles # data from related subtitles
s.scenes = 0 #FIXME s.scenes = 0 #FIXME
s.dialog = 0 #FIXME s.dialog = 0 #FIXME
s.words = 0 #FIXME s.words = 0 #FIXME
s.wpm = 0 #FIXME s.wpm = 0 #FIXME
s.risk = 0 #FIXME s.risk = 0 #FIXME
# data from related files # data from related files
videos = self.main_videos() videos = self.main_videos()
if len(videos) > 0: if len(videos) > 0:
@ -574,7 +572,7 @@ class Item(models.Model):
s.color = int(sum(self.data.get('color', []))) s.color = int(sum(self.data.get('color', [])))
s.saturation = 0 #FIXME s.saturation = 0 #FIXME
s.brightness = 0 #FIXME s.brightness = 0 #FIXME
s.cuts = len(self.data.get('cuts', [])) s.cuts = len(self.data.get('cuts', []))
if s.duration: if s.duration:
s.cutsperminute = s.cuts / (s.duration/60) s.cutsperminute = s.cuts / (s.duration/60)
@ -585,13 +583,13 @@ class Item(models.Model):
if not getattr(s, key): if not getattr(s, key):
setattr(s, key, u'zzzzzzzzzzzzzzzzzzzzzzzzz') setattr(s, key, u'zzzzzzzzzzzzzzzzzzzzzzzzz')
if not s.year: if not s.year:
s.year_desc = ''; s.year_desc = ''
s.year = '9999'; s.year = '9999'
#FIXME: also deal with number based rows like genre, keywords etc #FIXME: also deal with number based rows like genre, keywords etc
s.save() s.save()
def update_facets(self): def update_facets(self):
#FIXME: what to do with Unkown Director, Year, Country etc. #FIXME: what to do with Unkown Director, Year, Country etc.
for key in self.facet_keys: for key in self.facet_keys:
if key == 'actor': if key == 'actor':
current_values = [i[0] for i in self.get('actor', [])] current_values = [i[0] for i in self.get('actor', [])]
@ -616,7 +614,7 @@ class Item(models.Model):
f, created = Facet.objects.get_or_create(key='year', value=year, value_sort=year, item=self) f, created = Facet.objects.get_or_create(key='year', value=year, value_sort=year, item=self)
else: else:
Facet.objects.filter(item=self, key='year').delete() Facet.objects.filter(item=self, key='year').delete()
def path(self, name=''): def path(self, name=''):
h = self.itemId h = self.itemId
return os.path.join('items', h[:2], h[2:4], h[4:6], h[6:], name) return os.path.join('items', h[:2], h[2:4], h[4:6], h[6:], name)
@ -624,6 +622,7 @@ class Item(models.Model):
''' '''
Video related functions Video related functions
''' '''
def frame(self, position, width=128): def frame(self, position, width=128):
stream = self.streams.filter(profile=settings.VIDEO_PROFILE+'.webm')[0] stream = self.streams.filter(profile=settings.VIDEO_PROFILE+'.webm')[0]
path = os.path.join(settings.MEDIA_ROOT, self.path(), 'frames', "%d"%width, "%s.jpg"%position) path = os.path.join(settings.MEDIA_ROOT, self.path(), 'frames', "%d"%width, "%s.jpg"%position)
@ -647,14 +646,14 @@ class Item(models.Model):
first.width == v.width and first.height == v.height: first.width == v.width and first.height == v.height:
return True return True
return False return False
videos = filter(check, videos) videos = filter(check, videos)
return videos return videos
def update_streams(self): def update_streams(self):
files = {} files = {}
for f in self.main_videos(): for f in self.main_videos():
files[utils.sort_title(f.name)] = f.video.path files[utils.sort_title(f.name)] = f.video.path
#FIXME: how to detect if something changed? #FIXME: how to detect if something changed?
if files: if files:
stream, created = Stream.objects.get_or_create(item=self, profile='%s.webm' % settings.VIDEO_PROFILE) stream, created = Stream.objects.get_or_create(item=self, profile='%s.webm' % settings.VIDEO_PROFILE)
@ -693,6 +692,7 @@ class Item(models.Model):
''' '''
Poster related functions Poster related functions
''' '''
def update_poster_urls(self): def update_poster_urls(self):
_current = {} _current = {}
for s in settings.POSTER_SERVICES: for s in settings.POSTER_SERVICES:
@ -781,12 +781,14 @@ class Item(models.Model):
p.wait() p.wait()
return posters.keys() return posters.keys()
class ItemFind(models.Model): class ItemFind(models.Model):
""" """
used to find items, used to find items,
item.update_find populates this table item.update_find populates this table
its used in manager.ItemManager its used in manager.ItemManager
""" """
class Meta: class Meta:
unique_together = ("item", "key") unique_together = ("item", "key")
@ -794,8 +796,9 @@ class ItemFind(models.Model):
key = models.CharField(max_length=200, db_index=True) key = models.CharField(max_length=200, db_index=True)
value = models.TextField(blank=True) value = models.TextField(blank=True)
#FIXME: make sort based on site.json
class ItemSort(models.Model): class ItemSort(models.Model):
#FIXME: make sort based on site.json
""" """
used to sort items, all sort values are in here used to sort items, all sort values are in here
""" """
@ -872,6 +875,7 @@ class ItemSort(models.Model):
return tuple(fields) return tuple(fields)
fields = classmethod(fields) fields = classmethod(fields)
class Facet(models.Model): class Facet(models.Model):
item = models.ForeignKey('Item', related_name='facets') item = models.ForeignKey('Item', related_name='facets')
key = models.CharField(max_length=200, db_index=True) key = models.CharField(max_length=200, db_index=True)
@ -883,7 +887,9 @@ class Facet(models.Model):
self.value_sort = self.value self.value_sort = self.value
super(Facet, self).save(*args, **kwargs) super(Facet, self).save(*args, **kwargs)
class Stream(models.Model): class Stream(models.Model):
class Meta: class Meta:
unique_together = ("item", "profile") unique_together = ("item", "profile")
@ -899,7 +905,7 @@ class Stream(models.Model):
def path(self): def path(self):
return self.item.path(self.profile) return self.item.path(self.profile)
def extract_derivatives(self): def extract_derivatives(self):
if settings.VIDEO_H264: if settings.VIDEO_H264:
profile = self.profile.replace('.webm', '.mp4') profile = self.profile.replace('.webm', '.mp4')
@ -945,7 +951,9 @@ class Stream(models.Model):
self.info = ox.avinfo(self.video.path) self.info = ox.avinfo(self.video.path)
super(Stream, self).save(*args, **kwargs) super(Stream, self).save(*args, **kwargs)
class PosterUrl(models.Model): class PosterUrl(models.Model):
class Meta: class Meta:
unique_together = ("item", "service", "url") unique_together = ("item", "service", "url")
ordering = ('-height', ) ordering = ('-height', )
@ -958,4 +966,3 @@ class PosterUrl(models.Model):
def __unicode__(self): def __unicode__(self):
return u'%s %s %dx%d' % (unicode(self.item), self.service, self.width, self.height) return u'%s %s %dx%d' % (unicode(self.item), self.service, self.width, self.height)

View file

@ -11,16 +11,19 @@ import models
def cronjob(**kwargs): def cronjob(**kwargs):
print "do some cleanup stuff once a day" print "do some cleanup stuff once a day"
@task(ignore_resulsts=True, queue='default') @task(ignore_resulsts=True, queue='default')
def update_poster(itemId): def update_poster(itemId):
item = models.Item.objects.get(itemId=itemId) item = models.Item.objects.get(itemId=itemId)
item.make_poster(True) item.make_poster(True)
@task(ignore_resulsts=True, queue='default') @task(ignore_resulsts=True, queue='default')
def update_imdb(itemId): def update_imdb(itemId):
item = models.Item.objects.get(itemId=itemId) item = models.Item.objects.get(itemId=itemId)
item.update_imdb() item.update_imdb()
@task(queue="encoding") @task(queue="encoding")
def update_streams(itemId): def update_streams(itemId):
''' '''

View file

@ -7,7 +7,9 @@ Replace these with more appropriate tests for your application.
from django.test import TestCase from django.test import TestCase
class SimpleTest(TestCase): class SimpleTest(TestCase):
def test_basic_addition(self): def test_basic_addition(self):
""" """
Tests that 1 + 1 always equals 2. Tests that 1 + 1 always equals 2.
@ -20,4 +22,3 @@ Another way to test that 1 + 1 is equal to 2.
>>> 1 + 1 == 2 >>> 1 + 1 == 2
True True
"""} """}

View file

@ -13,4 +13,3 @@ urlpatterns = patterns("item.views",
(r'^(?P<id>[A-Z0-9].*)/poster\.jpg$', 'poster'), (r'^(?P<id>[A-Z0-9].*)/poster\.jpg$', 'poster'),
(r'^(?P<id>[A-Z0-9].*)/timelines/(?P<timeline>.+)\.(?P<size>\d+)\.(?P<position>\d+)\.png$', 'timeline'), (r'^(?P<id>[A-Z0-9].*)/timelines/(?P<timeline>.+)\.(?P<size>\d+)\.(?P<position>\d+)\.png$', 'timeline'),
) )

View file

@ -2,17 +2,15 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
# #
import errno
from decimal import Decimal from decimal import Decimal
import os import os
import sys
import re import re
import hashlib import hashlib
import unicodedata import unicodedata
import ox import ox
import ox.iso import ox.iso
from ox.normalize import normalizeName, normalizeTitle, canonicalTitle from ox.normalize import normalizeName, normalizeTitle
def parse_decimal(string): def parse_decimal(string):
@ -22,11 +20,13 @@ def parse_decimal(string):
d = string.split('/') d = string.split('/')
return Decimal(d[0]) / Decimal(d[1]) return Decimal(d[0]) / Decimal(d[1])
def plural_key(term): def plural_key(term):
return { return {
'country': 'countries', 'country': 'countries',
}.get(term, term + 's') }.get(term, term + 's')
def oxid(title, directors, year='', seriesTitle='', episodeTitle='', season=0, episode=0): def oxid(title, directors, year='', seriesTitle='', episodeTitle='', season=0, episode=0):
director = ', '.join(directors) director = ', '.join(directors)
oxid_value = u"\n".join([title, director, year]) oxid_value = u"\n".join([title, director, year])
@ -38,6 +38,7 @@ def oxid(title, directors, year='', seriesTitle='', episodeTitle='', season=0, e
oxid += hashlib.sha1(oxid_value.encode('utf-8')).hexdigest()[:20] oxid += hashlib.sha1(oxid_value.encode('utf-8')).hexdigest()[:20]
return u"0x" + oxid return u"0x" + oxid
def oxdb_id(title, directors=[], year='', season='', episode='', episode_title='', episode_directors=[], episode_year=''): def oxdb_id(title, directors=[], year='', season='', episode='', episode_title='', episode_directors=[], episode_year=''):
# new id function, will replace oxid() # new id function, will replace oxid()
def get_hash(string): def get_hash(string):
@ -51,11 +52,13 @@ def oxdb_id(title, directors=[], year='', season='', episode='', episode_title='
get_hash('\n'.join([str(episode), episode_director, episode_title, str(episode_year)]))[:8] get_hash('\n'.join([str(episode), episode_director, episode_title, str(episode_year)]))[:8]
return u'0x' + oxdb_id return u'0x' + oxdb_id
def oxdb_directors(director): def oxdb_directors(director):
director = os.path.basename(os.path.dirname(director)) director = os.path.basename(os.path.dirname(director))
if director.endswith('_'): if director.endswith('_'):
director = "%s." % director[:-1] director = "%s." % director[:-1]
directors = [normalizeName(d) for d in director.split('; ')] directors = [normalizeName(d) for d in director.split('; ')]
def cleanup(director): def cleanup(director):
director = director.strip() director = director.strip()
director = director.replace('Series', '') director = director.replace('Series', '')
@ -65,6 +68,7 @@ def oxdb_directors(director):
directors = filter(None, [cleanup(d) for d in directors]) directors = filter(None, [cleanup(d) for d in directors])
return directors return directors
def oxdb_title(_title, searchTitle = False): def oxdb_title(_title, searchTitle = False):
''' '''
normalize filename to get item title normalize filename to get item title
@ -83,7 +87,7 @@ def oxdb_title(_title, searchTitle = False):
else: else:
stitle = _title.split('.')[-2] stitle = _title.split('.')[-2]
if stitle.startswith('Episode '): if stitle.startswith('Episode '):
stitle = '' stitle = ''
if searchTitle: if searchTitle:
title = '"%s" %s' % (title, stitle) title = '"%s" %s' % (title, stitle)
else: else:
@ -98,9 +102,11 @@ def oxdb_title(_title, searchTitle = False):
title = normalizeTitle(title) title = normalizeTitle(title)
return title return title
def oxdb_year(data): def oxdb_year(data):
return ox.findRe(data, '\.(\d{4})\.') return ox.findRe(data, '\.(\d{4})\.')
def oxdb_series_title(path): def oxdb_series_title(path):
seriesTitle = u'' seriesTitle = u''
if path.startswith('Series'): if path.startswith('Series'):
@ -111,6 +117,7 @@ def oxdb_series_title(path):
seriesTitle = t.split(" (S")[0] seriesTitle = t.split(" (S")[0]
return seriesTitle return seriesTitle
def oxdb_episode_title(path): def oxdb_episode_title(path):
episodeTitle = u'' episodeTitle = u''
ep = re.compile('.Episode \d+?\.(.*?)\.[a-zA-Z]').findall(path) ep = re.compile('.Episode \d+?\.(.*?)\.[a-zA-Z]').findall(path)
@ -118,6 +125,7 @@ def oxdb_episode_title(path):
episodeTitle = ep[0] episodeTitle = ep[0]
return episodeTitle return episodeTitle
def oxdb_season_episode(path): def oxdb_season_episode(path):
season = 0 season = 0
episode = 0 episode = 0
@ -137,6 +145,7 @@ def oxdb_season_episode(path):
episode = int(se[0][1]) episode = int(se[0][1])
return (season, episode) return (season, episode)
def oxdb_part(path): def oxdb_part(path):
part = 1 part = 1
path = path.lower() path = path.lower()
@ -149,6 +158,7 @@ def oxdb_part(path):
part = p[0] part = p[0]
return part return part
def parse_path(path): def parse_path(path):
''' '''
expects path in the form expects path in the form
@ -178,6 +188,7 @@ def parse_path(path):
episode_year='') episode_year='')
return r return r
def sort_title(title): def sort_title(title):
#title #title
title = re.sub(u'[\'!¿¡,\.;\-"\:\*\[\]]', '', title) title = re.sub(u'[\'!¿¡,\.;\-"\:\*\[\]]', '', title)
@ -185,9 +196,8 @@ def sort_title(title):
#title = title.replace(u'Æ', 'Ae') #title = title.replace(u'Æ', 'Ae')
if isinstance(title, str): if isinstance(title, str):
title = unicode(title) title = unicode(title)
title = unicodedata.normalize('NFKD',title) title = unicodedata.normalize('NFKD', title)
#pad numbered titles #pad numbered titles
title = re.sub('(\d+)', lambda x: '%010d' % int(x.group(0)), title) title = re.sub('(\d+)', lambda x: '%010d' % int(x.group(0)), title)
return title.strip() return title.strip()

View file

@ -2,19 +2,10 @@
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
from __future__ import division from __future__ import division
import os.path import os.path
import re
from datetime import datetime
from urllib2 import unquote
import mimetypes
from django import forms from django.db.models import Count, Sum
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.http import HttpResponse, Http404
from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.template import RequestContext
from django.conf import settings from django.conf import settings
from ox.utils import json from ox.utils import json
@ -28,13 +19,13 @@ import models
import utils import utils
import tasks import tasks
from user.models import get_user_json
from archive.models import File from archive.models import File
from archive import extract from archive import extract
from api.actions import actions from api.actions import actions
def _order_query(qs, sort, prefix='sort__'): def _order_query(qs, sort, prefix='sort__'):
order_by = [] order_by = []
if len(sort) == 1: if len(sort) == 1:
@ -44,7 +35,8 @@ def _order_query(qs, sort, prefix='sort__'):
sort.append({'operator': '+', 'key': 'director'}) sort.append({'operator': '+', 'key': 'director'})
for e in sort: for e in sort:
operator = e['operator'] operator = e['operator']
if operator != '-': operator = '' if operator != '-':
operator = ''
key = {'id': 'itemId'}.get(e['key'], e['key']) key = {'id': 'itemId'}.get(e['key'], e['key'])
#FIXME: this should be a property of models.ItemSort!!! #FIXME: this should be a property of models.ItemSort!!!
if operator=='-' and key in ('title', 'director', 'writer', 'producer', 'editor', 'cinematographer', 'language', 'country', 'year'): if operator=='-' and key in ('title', 'director', 'writer', 'producer', 'editor', 'cinematographer', 'language', 'country', 'year'):
@ -82,7 +74,7 @@ def find(request):
'sort': array, 'sort': array,
'range': array 'range': array
} }
query: query object, more on query syntax at query: query object, more on query syntax at
https://wiki.0x2620.org/wiki/pandora/QuerySyntax https://wiki.0x2620.org/wiki/pandora/QuerySyntax
sort: array of key, operator dics sort: array of key, operator dics
@ -111,7 +103,7 @@ Groups
'group': string, 'group': string,
'range': array 'range': array
} }
query: query object, more on query syntax at query: query object, more on query syntax at
https://wiki.0x2620.org/wiki/pandora/QuerySyntax https://wiki.0x2620.org/wiki/pandora/QuerySyntax
range: result range, array [from, to] range: result range, array [from, to]
@ -134,7 +126,7 @@ Positions
'query': query, 'query': query,
'ids': [] 'ids': []
} }
query: query object, more on query syntax at query: query object, more on query syntax at
https://wiki.0x2620.org/wiki/pandora/QuerySyntax https://wiki.0x2620.org/wiki/pandora/QuerySyntax
ids: ids of items for which positions are required ids: ids of items for which positions are required
@ -143,7 +135,7 @@ Positions
if settings.JSON_DEBUG: if settings.JSON_DEBUG:
print json.dumps(data, indent=2) print json.dumps(data, indent=2)
query = _parse_query(data, request.user) query = _parse_query(data, request.user)
response = json_response({}) response = json_response({})
if 'group' in query: if 'group' in query:
if 'sort' in query: if 'sort' in query:
@ -183,7 +175,7 @@ Positions
elif 'ids' in query: elif 'ids' in query:
#FIXME: this does not scale for larger results #FIXME: this does not scale for larger results
qs = _order_query(query['qs'], query['sort']) qs = _order_query(query['qs'], query['sort'])
response['data']['positions'] = {} response['data']['positions'] = {}
ids = [j['itemId'] for j in qs.values('itemId')] ids = [j['itemId'] for j in qs.values('itemId')]
response['data']['positions'] = _get_positions(ids, query['ids']) response['data']['positions'] = _get_positions(ids, query['ids'])
@ -371,7 +363,8 @@ def getImdbId(request):
} }
} }
''' '''
imdbId = ox.web.imdb.guess(search_title, r['director'], timeout=-1) data = json.loads(request.POST['data'])
imdbId = ox.web.imdb.getImdbId(data['title'], data['director'], timeout=-1)
if imdbId: if imdbId:
response = json_response({'imdbId': imdbId}) response = json_response({'imdbId': imdbId})
else: else:
@ -406,7 +399,8 @@ def poster(request, id, size=None):
else: else:
poster_path = item.poster.path poster_path = item.poster.path
else: else:
if not size: size='large' if not size:
size='large'
return redirect('http://0xdb.org/%s/poster.%s.jpg' % (item.itemId, size)) return redirect('http://0xdb.org/%s/poster.%s.jpg' % (item.itemId, size))
poster_path = os.path.join(settings.STATIC_ROOT, 'png/posterDark.48.png') poster_path = os.path.join(settings.STATIC_ROOT, 'png/posterDark.48.png')
return HttpFileResponse(poster_path, content_type='image/jpeg') return HttpFileResponse(poster_path, content_type='image/jpeg')

View file

@ -2,29 +2,12 @@
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
from __future__ import division, with_statement from __future__ import division, with_statement
from datetime import datetime
import os.path
import math
import random
import re
import subprocess
import unicodedata
from glob import glob
from django.db import models from django.db import models
from django.db.models import Q
from django.contrib.auth.models import User 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 ox.django import fields
import ox
from ox import stripTags
from ox.normalize import canonicalTitle, canonicalName
class List(models.Model): class List(models.Model):
class Meta: class Meta:
unique_together = ("user", "name") unique_together = ("user", "name")
@ -33,7 +16,8 @@ class List(models.Model):
user = models.ForeignKey(User) user = models.ForeignKey(User)
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
public = models.BooleanField(default=False) public = models.BooleanField(default=False)
items = models.ManyToManyField('item.Item', related_name='lists', through='ListItem') items = models.ManyToManyField('item.Item', related_name='lists',
through='ListItem')
def add(self, item): def add(self, item):
q = self.items.filter(id=item.id) q = self.items.filter(id=item.id)
@ -55,6 +39,7 @@ class List(models.Model):
return True return True
return False return False
class ListItem(models.Model): class ListItem(models.Model):
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True) modified = models.DateTimeField(auto_now=True)
@ -63,4 +48,3 @@ class ListItem(models.Model):
def __unicode__(self): def __unicode__(self):
return u'%s in %s' % (self.item, self.list) return u'%s in %s' % (self.item, self.list)

View file

@ -7,7 +7,9 @@ Replace these with more appropriate tests for your application.
from django.test import TestCase from django.test import TestCase
class SimpleTest(TestCase): class SimpleTest(TestCase):
def test_basic_addition(self): def test_basic_addition(self):
""" """
Tests that 1 + 1 always equals 2. Tests that 1 + 1 always equals 2.
@ -20,4 +22,3 @@ Another way to test that 1 + 1 is equal to 2.
>>> 1 + 1 == 2 >>> 1 + 1 == 2
True True
"""} """}

View file

@ -1,28 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
from __future__ import division 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, redirect
from django.template import RequestContext
from django.conf import settings
from ox.utils import json from ox.utils import json
from ox.django.decorators import login_required_json from ox.django.decorators import login_required_json
from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response
from ox.django.http import HttpFileResponse
import ox
import models
from api.actions import actions from api.actions import actions
@ -48,12 +32,13 @@ def addListItem(request):
response = json_response(status=403, text='not allowed') response = json_response(status=403, text='not allowed')
elif 'query' in data: elif 'query' in data:
response = json_response(status=501, text='not implemented') response = json_response(status=501, text='not implemented')
else: else:
response = json_response(status=501, text='not implemented') response = json_response(status=501, text='not implemented')
return render_to_json_response(response) return render_to_json_response(response)
actions.register(addListItem) actions.register(addListItem)
@login_required_json @login_required_json
def removeListItem(request): def removeListItem(request):
''' '''
@ -76,12 +61,13 @@ def removeListItem(request):
response = json_response(status=403, text='not allowed') response = json_response(status=403, text='not allowed')
elif 'query' in data: elif 'query' in data:
response = json_response(status=501, text='not implemented') response = json_response(status=501, text='not implemented')
else: else:
response = json_response(status=501, text='not implemented') response = json_response(status=501, text='not implemented')
return render_to_json_response(response) return render_to_json_response(response)
actions.register(removeListItem) actions.register(removeListItem)
@login_required_json @login_required_json
def addList(request): def addList(request):
''' '''
@ -97,12 +83,11 @@ def addList(request):
response = json_response(status=200, text='created') response = json_response(status=200, text='created')
else: else:
response = json_response(status=200, text='list already exists') response = json_response(status=200, text='list already exists')
response['data']['errors'] = { response['data']['errors'] = {'name': 'List already exists'}
'name': 'List already exists'
}
return render_to_json_response(response) return render_to_json_response(response)
actions.register(addList) actions.register(addList)
@login_required_json @login_required_json
def editList(request): def editList(request):
''' '''
@ -124,6 +109,8 @@ def editList(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(editList) actions.register(editList)
@login_required_json
def removeList(request): def removeList(request):
''' '''
param data param data
@ -139,4 +126,3 @@ def removeList(request):
response = json_response(status=403, text='not allowed') response = json_response(status=403, text='not allowed')
return render_to_json_response(response) return render_to_json_response(response)
actions.register(removeList) actions.register(removeList)

View file

@ -2,26 +2,12 @@
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
from __future__ import division, with_statement from __future__ import division, with_statement
from datetime import datetime
import os.path
import math
import random
import re
import subprocess
import unicodedata import unicodedata
from glob import glob
from django.db import models 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 ox.django import fields from ox.django import fields
import ox import ox
from ox import stripTags
from ox.normalize import canonicalTitle, canonicalName
def get_name_sort(name): def get_name_sort(name):
@ -29,6 +15,7 @@ def get_name_sort(name):
name_sort = unicodedata.normalize('NFKD', person.name_sort) name_sort = unicodedata.normalize('NFKD', person.name_sort)
return name_sort return name_sort
class Person(models.Model): class Person(models.Model):
name = models.CharField(max_length=200) name = models.CharField(max_length=200)
name_sort = models.CharField(max_length=200) name_sort = models.CharField(max_length=200)
@ -67,4 +54,3 @@ class Person(models.Model):
def json(self): def json(self):
return self.name return self.name

View file

@ -7,7 +7,9 @@ Replace these with more appropriate tests for your application.
from django.test import TestCase from django.test import TestCase
class SimpleTest(TestCase): class SimpleTest(TestCase):
def test_basic_addition(self): def test_basic_addition(self):
""" """
Tests that 1 + 1 always equals 2. Tests that 1 + 1 always equals 2.
@ -20,4 +22,3 @@ Another way to test that 1 + 1 is equal to 2.
>>> 1 + 1 == 2 >>> 1 + 1 == 2
True True
"""} """}

View file

@ -9,5 +9,3 @@ import models
class PlaceAdmin(admin.ModelAdmin): class PlaceAdmin(admin.ModelAdmin):
search_fields = ['name'] search_fields = ['name']
admin.site.register(models.Place, PlaceAdmin) admin.site.register(models.Place, PlaceAdmin)

View file

@ -1,28 +1,23 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
import re
from ox.utils import json
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q, Manager from django.db.models import Q, Manager
import models
class PlaceManager(Manager): class PlaceManager(Manager):
def get_query_set(self): def get_query_set(self):
return super(PlaceManager, self).get_query_set() return super(PlaceManager, self).get_query_set()
def find(self, q='', f="globe", sw_lat=-180.0, sw_lng=-180.0, ne_lat=180.0, ne_lng=180.0): def find(self, q='', f="globe", sw_lat=-180.0, sw_lng=-180.0, ne_lat=180.0, ne_lng=180.0):
qs = self.get_query_set() qs = self.get_query_set()
qs = qs.filter(Q( qs = qs.filter(Q(
Q(Q(sw_lat__gt=sw_lat)|Q(sw_lat__lt=ne_lat)|Q(sw_lng__gt=sw_lng)|Q(sw_lng__lt=ne_lng)) & Q(Q(sw_lat__gt=sw_lat)|Q(sw_lat__lt=ne_lat)|Q(sw_lng__gt=sw_lng)|Q(sw_lng__lt=ne_lng)) &
Q(Q(sw_lat__gt=sw_lat)|Q(sw_lat__lt=ne_lat)|Q(sw_lng__lt=ne_lng)|Q(ne_lng__gt=ne_lng)) & Q(Q(sw_lat__gt=sw_lat)|Q(sw_lat__lt=ne_lat)|Q(sw_lng__lt=ne_lng)|Q(ne_lng__gt=ne_lng)) &
Q(Q(ne_lat__gt=sw_lat)|Q(ne_lat__lt=ne_lat)|Q(sw_lng__gt=sw_lng)|Q(sw_lng__lt=ne_lng)) & Q(Q(ne_lat__gt=sw_lat)|Q(ne_lat__lt=ne_lat)|Q(sw_lng__gt=sw_lng)|Q(sw_lng__lt=ne_lng)) &
Q(Q(ne_lat__gt=sw_lat)|Q(ne_lat__lt=ne_lat)|Q(ne_lng__gt=sw_lng)|Q(ne_lng__lt=ne_lng)) Q(Q(ne_lat__gt=sw_lat)|Q(ne_lat__lt=ne_lat)|Q(ne_lng__gt=sw_lng)|Q(ne_lng__lt=ne_lng))
)) ))
if q: if q:
qs = qs.filter(name_find__icontains, q) qs = qs.filter(name_find__icontains=q)
return qs return qs
''' '''
#only return locations that have layers of videos visible to current user #only return locations that have layers of videos visible to current user

View file

@ -3,13 +3,13 @@
from __future__ import division, with_statement from __future__ import division, with_statement
from django.db import models from django.db import models
from django.db.models import Q
from django.conf import settings
import ox
from ox.django import fields from ox.django import fields
import managers import managers
class Place(models.Model): class Place(models.Model):
''' '''
Places are named locations, they should have geographical information attached to them. Places are named locations, they should have geographical information attached to them.
@ -60,7 +60,6 @@ class Place(models.Model):
self.lng_center = ox.location.center(self.lng_sw, self.lng_ne) self.lng_center = ox.location.center(self.lng_sw, self.lng_ne)
#update area #update area
self.area = location.area(self.lat_sw, self.lng_sw, self.lat_ne, self.lng_ne) self.area = ox.location.area(self.lat_sw, self.lng_sw, self.lat_ne, self.lng_ne)
super(Place, self).save(*args, **kwargs) super(Place, self).save(*args, **kwargs)

View file

@ -7,7 +7,9 @@ Replace these with more appropriate tests for your application.
from django.test import TestCase from django.test import TestCase
class SimpleTest(TestCase): class SimpleTest(TestCase):
def test_basic_addition(self): def test_basic_addition(self):
""" """
Tests that 1 + 1 always equals 2. Tests that 1 + 1 always equals 2.
@ -20,4 +22,3 @@ Another way to test that 1 + 1 is equal to 2.
>>> 1 + 1 == 2 >>> 1 + 1 == 2
True True
"""} """}

View file

@ -1,37 +1,19 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
from __future__ import division 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, redirect
from django.template import RequestContext
from django.conf import settings
from ox.utils import json from ox.utils import json
from ox.django.decorators import login_required_json from ox.django.decorators import login_required_json
from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response
from ox.django.http import HttpFileResponse
import ox
import models import models
from api.actions import actions from api.actions import actions
'''
fixme, require admin
'''
@login_required_json @login_required_json
def addPlace(request): def addPlace(request):
#FIXME: require admin
''' '''
param data param data
{ {
@ -56,6 +38,7 @@ def addPlace(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(addPlace) actions.register(addPlace)
@login_required_json @login_required_json
def editPlace(request): def editPlace(request):
''' '''
@ -86,17 +69,19 @@ def editPlace(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(editPlace) actions.register(editPlace)
@login_required_json @login_required_json
def removePlace(request): def removePlace(request):
response = json_response(status=501, text='not implemented') response = json_response(status=501, text='not implemented')
return render_to_json_response(response) return render_to_json_response(response)
actions.register(removePlace) actions.register(removePlace)
def findPlace(request): def findPlace(request):
''' '''
param data param data
{'query': query, 'sort': array, 'range': array, 'area': array} {'query': query, 'sort': array, 'range': array, 'area': array}
query: query object, more on query syntax at query: query object, more on query syntax at
https://wiki.0x2620.org/wiki/pandora/QuerySyntax https://wiki.0x2620.org/wiki/pandora/QuerySyntax
sort: array of key, operator dics sort: array of key, operator dics
@ -120,7 +105,7 @@ def findPlace(request):
Positions Positions
param data param data
{'query': query, 'ids': []} {'query': query, 'ids': []}
query: query object, more on query syntax at query: query object, more on query syntax at
https://wiki.0x2620.org/wiki/pandora/QuerySyntax https://wiki.0x2620.org/wiki/pandora/QuerySyntax
ids: ids of places for which positions are required ids: ids of places for which positions are required
@ -129,8 +114,7 @@ Positions
response = json_response(status=200, text='ok') response = json_response(status=200, text='ok')
response['data']['places'] = [] response['data']['places'] = []
#FIXME: add coordinates to limit search #FIXME: add coordinates to limit search
for p in Places.objects.find(data['query']): for p in models.Place.objects.find(data['query']):
response['data']['places'].append(p.json()) response['data']['places'].append(p.json())
return render_to_json_response(response) return render_to_json_response(response)
actions.register(findPlace) actions.register(findPlace)

View file

@ -5,11 +5,6 @@ from datetime import datetime
from django.db import models from django.db import models
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.conf import settings
import ox
from ox.django import fields
from ox.utils import json
class News(models.Model): class News(models.Model):
@ -29,6 +24,7 @@ class News(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
return '/text/%s' % self.slug return '/text/%s' % self.slug
class Text(models.Model): class Text(models.Model):
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True) modified = models.DateTimeField(auto_now=True)
@ -46,6 +42,7 @@ class Text(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
return '/text/%s' % self.slug return '/text/%s' % self.slug
class Image(models.Model): class Image(models.Model):
image = models.ImageField(upload_to='text/image') image = models.ImageField(upload_to='text/image')
caption = models.CharField(max_length=255, default="") caption = models.CharField(max_length=255, default="")
@ -53,10 +50,10 @@ class Image(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
return self.image.url return self.image.url
class Attachment(models.Model): class Attachment(models.Model):
file = models.FileField(upload_to='text/attachment') file = models.FileField(upload_to='text/attachment')
caption = models.CharField(max_length=255, default="") caption = models.CharField(max_length=255, default="")
def get_absolute_url(self): def get_absolute_url(self):
return self.file.url return self.file.url

View file

@ -2,23 +2,15 @@
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
from __future__ import division from __future__ import division
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, redirect
from django.template import RequestContext
from django.conf import settings
from ox.utils import json from ox.utils import json
from ox.django.decorators import login_required_json from ox.django.decorators import login_required_json
from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response
from ox.django.http import HttpFileResponse
import ox
import models import models
from api.actions import actions from api.actions import actions
def getNews(request): def getNews(request):
''' '''
param data param data
@ -33,6 +25,23 @@ def getNews(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(getNews) actions.register(getNews)
@login_required_json
def editNews(request):
'''
param data
string id
return page
'''
response = json_response({})
itemId = json.loads(request.POST['data'])
item = get_object_or_404_json(models.Text, pk=itemId)
response['data']['page'] = item.html()
return render_to_json_response(response)
actions.register(editNews)
def findNews(request): def findNews(request):
''' '''
''' '''
@ -40,6 +49,7 @@ def findNews(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(findNews) actions.register(findNews)
def getText(request): def getText(request):
''' '''
param data param data
@ -54,10 +64,26 @@ def getText(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(getText) actions.register(getText)
@login_required_json
def editText(request):
'''
param data
string id
return page
'''
response = json_response({})
itemId = json.loads(request.POST['data'])
item = get_object_or_404_json(models.Text, pk=itemId)
response['data']['page'] = item.html()
return render_to_json_response(response)
actions.register(editText)
def findText(request): def findText(request):
''' '''
''' '''
response = json_response({}) response = json_response({})
return render_to_json_response(response) return render_to_json_response(response)
actions.register(findText) actions.register(findText)

View file

@ -10,12 +10,14 @@ from django.conf import settings
import ox.torrent import ox.torrent
import transmissionrpc import transmissionrpc
def connect(): def connect():
return transmissionrpc.Client(settings.TRANSMISSON_HOST, return transmissionrpc.Client(settings.TRANSMISSON_HOST,
port=settings.TRANSMISSON_PORT, port=settings.TRANSMISSON_PORT,
user=settings.TRANSMISSON_USER, user=settings.TRANSMISSON_USER,
password=settings.TRANSMISSON_PASSWORD) password=settings.TRANSMISSON_PASSWORD)
def remove(info_hash): def remove(info_hash):
if settings.DEBUG: if settings.DEBUG:
print 'remove', info_hash print 'remove', info_hash
@ -24,10 +26,11 @@ def remove(info_hash):
tc = connect() tc = connect()
tc.remove(info_hash.lower()) tc.remove(info_hash.lower())
except: except:
if DEBUG: if settings.DEBUG:
import traceback import traceback
traceback.print_exc() traceback.print_exc()
def add(torrent_file): def add(torrent_file):
download_dir = os.path.dirname(torrent_file) download_dir = os.path.dirname(torrent_file)
with open(torrent_file) as f: with open(torrent_file) as f:
@ -42,6 +45,7 @@ def add(torrent_file):
import traceback import traceback
traceback.print_exc() traceback.print_exc()
def is_seeding(info_hash): def is_seeding(info_hash):
info_hash = info_hash.lower() info_hash = info_hash.lower()
try: try:
@ -56,6 +60,7 @@ def is_seeding(info_hash):
return True return True
return False return False
def start_daemon(): def start_daemon():
try: try:
tc = connect() tc = connect()
@ -71,4 +76,3 @@ def start_daemon():
'-w', settings.MEDIA_ROOT, '-w', settings.MEDIA_ROOT,
]) ])
time.sleep(1) time.sleep(1)

View file

@ -5,24 +5,25 @@ from datetime import datetime
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import models from django.db import models
from django.db.models import signals
from django.dispatch import dispatcher
from django.conf import settings from django.conf import settings
from ox.utils import json from ox.utils import json
class UserProfile(models.Model): class UserProfile(models.Model):
reset_token = models.TextField(blank=True, null=True, unique=True) reset_token = models.TextField(blank=True, null=True, unique=True)
user = models.ForeignKey(User, unique=True) user = models.ForeignKey(User, unique=True)
files_updated = models.DateTimeField(default=datetime.now) files_updated = models.DateTimeField(default=datetime.now)
newsletter = models.BooleanField(default=True) newsletter = models.BooleanField(default=True)
def user_post_save(sender, instance, **kwargs): def user_post_save(sender, instance, **kwargs):
profile, new = UserProfile.objects.get_or_create(user=instance) profile, new = UserProfile.objects.get_or_create(user=instance)
models.signals.post_save.connect(user_post_save, sender=User) models.signals.post_save.connect(user_post_save, sender=User)
class Preference(models.Model): class Preference(models.Model):
user = models.ForeignKey(User, related_name='preferences') user = models.ForeignKey(User, related_name='preferences')
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
@ -33,18 +34,20 @@ class Preference(models.Model):
def __unicode__(self): def __unicode__(self):
return u"%s/%s=%s" % (self.user, self.key, self.value) return u"%s/%s=%s" % (self.user, self.key, self.value)
def get_user_json(user): def get_user_json(user):
json = {} result = {}
for key in ('username', ): for key in ('username', ):
json[key] = getattr(user, key) result[key] = getattr(user, key)
json['group'] = 'user' result['group'] = 'user'
if user.is_staff: if user.is_staff:
json['group'] = 'admin' result['group'] = 'admin'
elif user.has_perm('0x.vip'): #FIXME: permissions elif user.has_perm('0x.vip'): #FIXME: permissions
json['group'] = 'vip' result['group'] = 'vip'
json['preferences'] = get_preferences(user) result['preferences'] = get_preferences(user)
json['ui'] = get_ui(user) result['ui'] = get_ui(user)
return json return result
def get_ui(user): def get_ui(user):
with open(os.path.join(settings.PROJECT_ROOT, 'templates', 'site.json')) as f: with open(os.path.join(settings.PROJECT_ROOT, 'templates', 'site.json')) as f:
@ -70,6 +73,7 @@ def get_ui(user):
} }
''' '''
def get_preferences(user): def get_preferences(user):
prefs = {} prefs = {}
for p in Preference.objects.filter(user=user): for p in Preference.objects.filter(user=user):
@ -77,6 +81,7 @@ def get_preferences(user):
prefs['email'] = user.email prefs['email'] = user.email
return prefs return prefs
def get_preference(user, key, value=None): def get_preference(user, key, value=None):
if key in ('email', ): if key in ('email', ):
value = getattr(user, key) value = getattr(user, key)
@ -86,6 +91,7 @@ def get_preference(user, key, value=None):
value = json.loads(q[0].value) value = json.loads(q[0].value)
return value return value
def set_preference(user, key, value): def set_preference(user, key, value):
if key in ('email', ): if key in ('email', ):
setattr(user, key, value) setattr(user, key, value)

View file

@ -7,8 +7,7 @@ random.seed()
from django import forms from django import forms
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login, logout from django.contrib.auth import authenticate, login, logout
from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404, redirect from django.template import RequestContext, loader
from django.template import RequestContext, loader, Context
from django.utils import simplejson as json from django.utils import simplejson as json
from django.conf import settings from django.conf import settings
from django.core.mail import send_mail, BadHeaderError from django.core.mail import send_mail, BadHeaderError
@ -26,13 +25,14 @@ class LoginForm(forms.Form):
username = forms.TextInput() username = forms.TextInput()
password = forms.TextInput() password = forms.TextInput()
def api_login(request): def api_login(request):
''' '''
param data { param data {
username: 'username', username: 'username',
password: 'password' password: 'password'
} }
return { return {
status: {'code': 200, 'text': 'ok'} status: {'code': 200, 'text': 'ok'}
data: { data: {
@ -81,11 +81,12 @@ def api_login(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(api_login, 'login') actions.register(api_login, 'login')
def api_logout(request): def api_logout(request):
''' '''
param data { param data {
} }
return { return {
status: {'code': int, 'text': string} status: {'code': int, 'text': string}
data: { data: {
@ -105,11 +106,13 @@ def api_logout(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(api_logout, 'logout') actions.register(api_logout, 'logout')
class RegisterForm(forms.Form): class RegisterForm(forms.Form):
username = forms.TextInput() username = forms.TextInput()
password = forms.TextInput() password = forms.TextInput()
email = forms.TextInput() email = forms.TextInput()
def register(request): def register(request):
''' '''
param data { param data {
@ -117,7 +120,7 @@ def register(request):
password: 'password', password: 'password',
email: 'emailaddress' email: 'emailaddress'
} }
return { return {
status: {'code': int, 'text': string} status: {'code': int, 'text': string}
data: { data: {
@ -172,13 +175,14 @@ def register(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(register) actions.register(register)
def resetPassword(request): def resetPassword(request):
''' '''
param data { param data {
token: reset token token: reset token
password: new password password: new password
} }
return { return {
status: {'code': int, 'text': string} status: {'code': int, 'text': string}
data: { data: {
@ -226,13 +230,14 @@ def resetPassword(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(resetPassword) actions.register(resetPassword)
def requestToken(request): def requestToken(request):
''' '''
param data { param data {
username: username, username: username,
email: email email: email
} }
return { return {
status: {'code': int, 'text': string} status: {'code': int, 'text': string}
data: { data: {
@ -291,13 +296,14 @@ def requestToken(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(requestToken) actions.register(requestToken)
def findUser(request): def findUser(request):
''' '''
param data { param data {
key: "username", key: "username",
value: "foo", operator: "=" value: "foo", operator: "="
} }
return { return {
'status': {'code': int, 'text': string} 'status': {'code': int, 'text': string}
'data': { 'data': {
@ -315,18 +321,20 @@ def findUser(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(findUser) actions.register(findUser)
class ContactForm(forms.Form): class ContactForm(forms.Form):
email = forms.EmailField() email = forms.EmailField()
subject = forms.TextInput() subject = forms.TextInput()
message = forms.TextInput() message = forms.TextInput()
def contact(request): def contact(request):
''' '''
param data { param data {
'email': string, 'email': string,
'message': string 'message': string
} }
return { return {
'status': {'code': int, 'text': string} 'status': {'code': int, 'text': string}
} }
@ -353,6 +361,7 @@ def contact(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(contact) actions.register(contact)
@login_required_json @login_required_json
def preferences(request): def preferences(request):
''' '''
@ -391,4 +400,3 @@ def preferences(request):
models.set_preference(request.user, key, data[key]) models.set_preference(request.user, key, data[key])
return render_to_json_response(response) return render_to_json_response(response)
actions.register(preferences) actions.register(preferences)