pandora/pandora/item/views.py
2013-02-28 20:12:52 +00:00

1256 lines
44 KiB
Python

# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from __future__ import division
import os.path
import mimetypes
import random
from urlparse import urlparse
from urllib import quote
import time
import Image
from django.db.models import Count, Sum
from django.template import RequestContext
from django.http import HttpResponse, HttpResponseForbidden, Http404
from django.shortcuts import get_object_or_404, redirect, render_to_response
from django.conf import settings
from ox.utils import json, ET
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
import utils
import tasks
from archive.models import File, Stream
from archive import extract
from clip.models import Clip
from ox.django.api import actions
def _order_query(qs, sort, prefix='sort__'):
order_by = []
if len(sort) == 1:
key = utils.get_by_id(settings.CONFIG['itemKeys'], sort[0]['key'])
for s in key.get('additionalSort', settings.CONFIG.get('additionalSort', [])):
sort.append(s)
for e in sort:
operator = e['operator']
if operator != '-':
operator = ''
key = {
'id': 'itemId',
}.get(e['key'], e['key'])
key = "%s%s" % (prefix, key)
order = '%s%s' % (operator, key)
order_by.append(order)
if order_by:
qs = qs.order_by(*order_by, nulls_last=True)
return qs
def _order_by_group(query):
if 'sort' in query:
if len(query['sort']) == 1 and query['sort'][0]['key'] == 'items':
order_by = query['sort'][0]['operator'] == '-' and '-items' or 'items'
if query['group'] == "year":
secondary = query['sort'][0]['operator'] == '-' and '-sortvalue' or 'sortvalue'
order_by = (order_by, secondary)
elif query['group'] != "keyword":
order_by = (order_by, 'sortvalue')
else:
order_by = (order_by, 'value')
else:
order_by = query['sort'][0]['operator'] == '-' and '-sortvalue' or 'sortvalue'
order_by = (order_by, 'items')
else:
order_by = ('-sortvalue', 'items')
return order_by
def parse_query(data, user):
query = {}
query['range'] = [0, 100]
query['sort'] = [{'key':'title', 'operator':'+'}]
for key in ('sort', 'keys', 'group', 'range', 'position', 'positions'):
if key in data:
query[key] = data[key]
query['qs'] = models.Item.objects.find(data, user)
if 'clips' in data:
conditions = {'query': data['clips']['query']}
query['clip_qs'] = Clip.objects.find(conditions, user).order_by('start')
query['clip_filter'] = models.Clip.objects.filter_annotations(conditions, user)
query['clip_items'] = data['clips'].get('items', 5)
query['clip_keys'] = data['clips'].get('keys')
if not query['clip_keys']:
query['clip_keys'] = ['id', 'in', 'out', 'annotations']
#group by only allows sorting by name or number of itmes
return query
def find(request):
'''
Example:
find({
query:{
conditions:[{ key: '*', value: 'paris', operator: '='}],
operator:'&'
},
keys: ['title', 'id'],
range: [0, 10],
sort: [{key: 'title', operator: '+'}]
})
param data {
'query': query,
'sort': array,
'range': array
clipsQuery: ...
}
query: query object, more on query syntax at
https://wiki.0x2620.org/wiki/pandora/QuerySyntax
sort: array of key, operator dics
[
{
key: "year",
operator: "-"
},
{
key: "director",
operator: ""
}
]
range: result range, array [from, to]
keys: array of keys to return
group: group elements by, country, genre, director...
with keys, items is list of dicts with requested properties:
return {'status': {'code': int, 'text': string},
'data': {items: array}}
Groups
param data {
'query': query,
'key': string,
'group': string,
'range': array
clips: {}
}
query: query object, more on query syntax at
https://wiki.0x2620.org/wiki/pandora/QuerySyntax
range: result range, array [from, to]
keys: array of keys to return
group: group elements by, country, genre, director...
possible values for keys: name, items
with keys
items contains list of {'name': string, 'items': int}:
return {'status': {'code': int, 'text': string},
'data': {items: array}}
without keys: return number of items in given query
return {'status': {'code': int, 'text': string},
'data': {items: int}}
Positions
param data {
'query': query,
'positions': [],
'sort': array
}
query: query object, more on query syntax at
https://wiki.0x2620.org/wiki/pandora/QuerySyntax
positions: ids of items for which positions are required
return {
status: {...},
data: {
positions: {
id: position
}
}
}
'''
data = json.loads(request.POST['data'])
if settings.JSON_DEBUG:
print json.dumps(data, indent=2)
query = parse_query(data, request.user)
response = json_response({})
if 'group' in query:
response['data']['items'] = []
items = 'items'
item_qs = query['qs']
order_by = _order_by_group(query)
qs = models.Facet.objects.filter(key=query['group']).filter(item__id__in=item_qs)
qs = qs.values('value').annotate(items=Count('id')).order_by(*order_by)
if 'positions' in query:
response['data']['positions'] = {}
ids = [j['value'] for j in qs]
response['data']['positions'] = utils.get_positions(ids, query['positions'])
elif 'range' in data:
qs = qs[query['range'][0]:query['range'][1]]
response['data']['items'] = [{'name': i['value'], 'items': i[items]} for i in qs]
else:
response['data']['items'] = qs.count()
elif 'position' in query:
qs = _order_query(query['qs'], query['sort'])
ids = [j['itemId'] for j in qs.values('itemId')]
data['conditions'] = data['conditions'] + {
'value': query['position'],
'key': query['sort'][0]['key'],
'operator': '^'
}
query = parse_query(data, request.user)
qs = _order_query(query['qs'], query['sort'])
if qs.count() > 0:
response['data']['position'] = utils.get_positions(ids, [qs[0].itemId])[0]
elif 'positions' in query:
qs = _order_query(query['qs'], query['sort'])
ids = [j['itemId'] for j in qs.values('itemId')]
response['data']['positions'] = utils.get_positions(ids, query['positions'])
elif 'keys' in query:
response['data']['items'] = []
qs = _order_query(query['qs'], query['sort'])
_p = query['keys']
def get_clips(qs):
n = qs.count()
if n > query['clip_items']:
num = query['clip_items']
clips = []
step = int(n / (num + 1))
i = step
while i <= (n - step) and i < n and len(clips) < num:
clips.append(qs[i])
i += step
else:
clips = qs
return [c.json(query['clip_keys'], query['clip_filter']) for c in clips]
def only_p_sums(m):
r = {}
for p in _p:
if p == 'accessed':
r[p] = m.sort.accessed or ''
elif p == 'modified':
r[p] = m.sort.modified
elif p == 'timesaccessed':
r[p] = m.sort.timesaccessed
else:
r[p] = m.json.get(p, '')
if 'clip_qs' in query:
r['clips'] = get_clips(query['clip_qs'].filter(item=m))
return r
def only_p(m):
r = {}
if m:
m = json.loads(m, object_hook=ox.django.fields.from_json)
for p in _p:
r[p] = m.get(p, '')
if 'clip_qs' in query:
r['clips'] = get_clips(query['clip_qs'].filter(item__itemId=m['id']))
return r
qs = qs[query['range'][0]:query['range'][1]]
#response['data']['items'] = [m.get_json(_p) for m in qs]
if filter(lambda p: p in (
'accessed', 'modified', 'timesaccessed', 'viewed'
), _p):
qs = qs.select_related()
response['data']['items'] = [only_p_sums(m) for m in qs]
else:
response['data']['items'] = [only_p(m['json']) for m in qs.values('json')]
else: # otherwise stats
items = query['qs']
files = File.objects.filter(item__in=items).filter(size__gt=0)
r = files.aggregate(
Sum('duration'),
Sum('pixels'),
Sum('size')
)
totals = [i['id'] for i in settings.CONFIG['totals']]
if 'duration' in totals:
response['data']['duration'] = r['duration__sum']
if 'files' in totals:
response['data']['files'] = files.count()
if 'items' in totals:
response['data']['items'] = items.count()
if 'pixels' in totals:
response['data']['pixels'] = r['pixels__sum']
if 'runtime' in totals:
response['data']['runtime'] = items.aggregate(Sum('sort__runtime'))['sort__runtime__sum'] or 0
if 'size' in totals:
response['data']['size'] = r['size__sum']
for key in ('runtime', 'duration', 'pixels', 'size'):
if key in totals and response['data'][key] == None:
response['data'][key] = 0
return render_to_json_response(response)
actions.register(find)
def autocomplete(request):
'''
param data
key
value
operator '=', '==', '^', '$'
query
range
return
query can be an item query to limit results
'''
data = json.loads(request.POST['data'])
if not 'range' in data:
data['range'] = [0, 10]
op = data.get('operator', '=')
key = settings.CONFIG['keys'][data['key']]
order_by = key.get('autocompleteSortKey', False)
if order_by:
order_by = '-sort__%s' % order_by
else:
order_by = '-items'
sort_type = key.get('sortType', key.get('type', 'string'))
if sort_type == 'title':
qs = parse_query({'query': data.get('query', {})}, request.user)['qs']
if data['value']:
if op == '=':
qs = qs.filter(find__key=data['key'], find__value__icontains=data['value'])
elif op == '==':
qs = qs.filter(find__key=data['key'], find__value__iexact=data['value'])
elif op == '^':
qs = qs.filter(find__key=data['key'], find__value__istartswith=data['value'])
elif op == '$':
qs = qs.filter(find__key=data['key'], find__value__iendswith=data['value'])
qs = qs.order_by(order_by, nulls_last=True)
qs = qs[data['range'][0]:data['range'][1]]
response = json_response({})
response['data']['items'] = list(set([i.get(data['key']) for i in qs]))
else:
qs = models.Facet.objects.filter(key=data['key'])
if data['value']:
if op == '=':
qs = qs.filter(value__icontains=data['value'])
elif op == '==':
qs = qs.filter(value__iexact=data['value'])
elif op == '^':
qs = qs.filter(value__istartswith=data['value'])
elif op == '$':
qs = qs.filter(value__iendswith=data['value'])
if 'query' in data:
item_query = parse_query({'query': data.get('query', {})}, request.user)['qs']
qs = qs.filter(item__in=item_query)
qs = qs.values('value').annotate(items=Count('id'))
qs = qs.order_by(order_by)
qs = qs[data['range'][0]:data['range'][1]]
response = json_response({})
response['data']['items'] = [i['value'] for i in qs]
return render_to_json_response(response)
actions.register(autocomplete)
def findId(request):
'''
param data {
'id':
'title':
'director': []
'year': ...
}
'''
data = json.loads(request.POST['data'])
response = json_response({})
response['data']['items'] = []
if 'id' in data:
qs = models.Item.objects.filter(itemId=data['id'])
if qs.count() == 1:
response['data']['items'] = [
i.get_json(['title', 'director', 'year', 'id']) for i in qs
]
if not response['data']['items'] \
and settings.USE_IMDB \
and settings.DATA_SERVICE:
r = models.external_data('getId', data)
if r['status']['code'] == 200:
response['data']['items'] = [r['data']]
return render_to_json_response(response)
actions.register(findId)
def getMetadata(request):
'''
param data {
id: string,
keys: array
}
returns {
key: value,
..
}
'''
data = json.loads(request.POST['data'])
response = json_response({})
if settings.DATA_SERVICE:
'''
info = {}
for c in data['query']['conditions']:
info[c['key']] = c['value']
r = models.external_data('getId', info)
'''
r = models.external_data('getData', {'id': data['id']})
if r['status']['code'] == 200:
if 'keys' in data and data['keys']:
for key in data['keys']:
if key in r['data']:
response['data'][key] = r['data'][key]
else:
response['data'] = r['data']
return render_to_json_response(response)
actions.register(getMetadata)
def getIds(request):
'''
param data {
title: '',
director: [],
year: int
}
returns {
items: [{tite, director, year, originalTitle}, ...]
}
'''
data = json.loads(request.POST['data'])
response = json_response({})
if settings.DATA_SERVICE:
r = models.external_data('getIds', data)
if r['status']['code'] == 200:
response['data']['items'] = r['data']['items']
else:
response['data']['items']
return render_to_json_response(response)
actions.register(getIds)
def get(request):
'''
param data {
id: string
keys: array
}
return item array
'''
response = json_response({})
data = json.loads(request.POST['data'])
data['keys'] = data.get('keys', [])
item = get_object_or_404_json(models.Item, itemId=data['id'])
if item.access(request.user):
info = item.get_json(data['keys'])
if not data['keys'] or 'stream' in data['keys']:
info['stream'] = item.get_stream()
if data['keys'] and 'layers' in data['keys']:
info['layers'] = item.get_layers(request.user)
if data['keys'] and 'files' in data['keys']:
info['files'] = item.get_files(request.user)
if not data['keys'] or 'notes' in data['keys'] \
and request.user.get_profile().capability('canEditMetadata'):
info['notes'] = item.notes
if not data['keys'] or 'groups' in data['keys'] \
and request.user.get_profile().capability('canEditMetadata'):
info['groups'] = [g.name for g in item.groups.all()]
info['editable'] = item.editable(request.user)
response['data'] = info
else:
#response = json_response(status=403, text='permission denied')
response = json_response(status=404, text='not found')
return render_to_json_response(response)
actions.register(get)
@login_required_json
def add(request):
'''
param data {
}
return {
status: {'code': int, 'text': string},
data: {
id:
name:
...
}
}
'''
if not request.user.get_profile().capability('canAddItems'):
response = json_response(status=403, text='permissino denied')
else:
data = json.loads(request.POST['data'])
data['title'] = data.get('title', 'Untitled')
i = models.Item()
i.data['title'] = data['title']
i.user = request.user
p = i.save()
if p:
p.wait()
else:
i.make_poster(True)
response = json_response(status=200, text='created')
response['data'] = i.get_json()
return render_to_json_response(response)
actions.register(add, cache=False)
@login_required_json
def edit(request):
'''
param data {
id: string,
key: value,..
}
return {
status: {'code': int, 'text': string},
data: {}
}
'''
update_clips = False
data = json.loads(request.POST['data'])
item = get_object_or_404_json(models.Item, itemId=data['id'])
if item.editable(request.user):
item.log()
response = json_response(status=200, text='ok')
if 'notes' in data:
if request.user.get_profile().capability('canEditMetadata'):
item.notes = ox.sanitize_html(data['notes'])
del data['notes']
if 'rightslevel' in data:
item.level = int(data['rightslevel'])
del data['rightslevel']
if 'user' in data:
if request.user.get_profile().get_level() in ('admin', 'staff') and \
models.User.objects.filter(username=data['user']).exists():
new_user = models.User.objects.get(username=data['user'])
if new_user != item.user:
item.user = new_user
update_clips = True
del data['user']
r = item.edit(data)
if r:
r.wait()
if update_clips:
tasks.update_clips.delay(item.itemId)
response['data'] = item.get_json()
else:
response = json_response(status=403, text='permissino denied')
return render_to_json_response(response)
actions.register(edit, cache=False)
@login_required_json
def remove(request):
'''
param data {
id: string
}
return {'status': {'code': int, 'text': string}}
'''
response = json_response({})
data = json.loads(request.POST['data'])
item = get_object_or_404_json(models.Item, itemId=data['id'])
user = request.user
if user.get_profile().capability('canRemoveItems') == True or \
user.is_staff or \
item.user == user or \
item.groups.filter(id__in=user.groups.all()).count() > 0:
item.log()
#FIXME: is this cascading enough or do we end up with orphan files etc.
item.delete()
response = json_response(status=200, text='removed')
else:
response = json_response(status=403, text='permission denied')
return render_to_json_response(response)
actions.register(remove, cache=False)
'''
Poster API
'''
def setPosterFrame(request): #parse path and return info
'''
param data {
id: itemId,
position: float
}
return {
status: {'code': int, 'text': string},
data: {
}
}
'''
data = json.loads(request.POST['data'])
item = get_object_or_404_json(models.Item, itemId=data['id'])
if item.editable(request.user):
item.poster_frame = data['position']
item.save()
tasks.update_poster(item.itemId)
response = json_response()
else:
response = json_response(status=403, text='permissino denied')
return render_to_json_response(response)
actions.register(setPosterFrame, cache=False)
def setPoster(request): #parse path and return info
'''
param data {
id: itemId,
source: string
}
return {
status: {'code': int, 'text': string},
data: {
poster: {url,width,height}
}
}
'''
data = json.loads(request.POST['data'])
item = get_object_or_404_json(models.Item, itemId=data['id'])
response = json_response()
if item.editable(request.user):
valid_sources = [p['source'] for p in item.get_posters()]
if data['source'] in valid_sources:
item.poster_source = data['source']
if item.poster:
item.poster.delete()
item.save()
tasks.update_poster(item.itemId)
response = json_response()
response['data']['posterAspect'] = item.poster_width/item.poster_height
else:
response = json_response(status=403, text='invalid poster url')
else:
response = json_response(status=403, text='permission denied')
return render_to_json_response(response)
actions.register(setPoster, cache=False)
def updateExternalData(request):
'''
param data {
id: itemId,
}
return {
status: {'code': int, 'text': string},
data: {
poster: {url,width,height}
}
}
'''
data = json.loads(request.POST['data'])
item = get_object_or_404_json(models.Item, itemId=data['id'])
response = json_response()
if item.editable(request.user):
item.update_external()
else:
response = json_response(status=403, text='permission denied')
return render_to_json_response(response)
actions.register(updateExternalData, cache=False)
def lookup(request):
'''
param data {
title: string,
director: [string],
year: string,
id: string
}
return {
status: {'code': int, 'text': string},
data: {
title: string,
director: [string],
year: string,
id: string
}
}
'''
data = json.loads(request.POST['data'])
if 'id' in data:
i = models.Item.objects.get(itemId=data['id'])
r = {'id': i.itemId}
for key in ('title', 'director', 'year'):
r[key] = i.get(key)
response = json_response(r)
else:
response = json_response(status=404, text='not found')
return render_to_json_response(response)
actions.register(lookup)
def getImdbId(request):
'''
param data {
title: string,
director: string,
year: string
}
return {
status: {'code': int, 'text': string},
data: {
imdbId:string
}
}
'''
data = json.loads(request.POST['data'])
imdbId = ox.web.imdb.getImdbId(data['title'], data['director'], timeout=-1)
if imdbId:
response = json_response({'imdbId': imdbId})
else:
response = json_response(status=404, text='not found')
return render_to_json_response(response)
actions.register(getImdbId)
'''
media delivery
'''
def frame(request, id, size, position=None):
item = get_object_or_404(models.Item, itemId=id)
if not item.access(request.user):
return HttpResponseForbidden()
frame = None
if not position:
frames = item.poster_frames()
if frames:
position = item.poster_frame
if position == -1 or position > len(frames):
position = int(len(frames)/2)
position = frames[int(position)]['position']
elif item.poster_frame == -1 and item.sort.duration:
position = item.sort.duration/2
else:
position = item.poster_frame
else:
position = float(position.replace(',', '.'))
if not frame:
frame = item.frame(position, int(size))
if not frame:
frame = os.path.join(settings.STATIC_ROOT, 'jpg/list256.jpg')
#raise Http404
return HttpFileResponse(frame, content_type='image/jpeg')
def poster_frame(request, id, position):
item = get_object_or_404(models.Item, itemId=id)
if not item.access(request.user):
return HttpResponseForbidden()
position = int(position)
frames = item.poster_frames()
if frames and len(frames) > position:
frame = frames[position]['path']
return HttpFileResponse(frame, content_type='image/jpeg')
raise Http404
def image_to_response(image, size=None):
if size:
size = int(size)
path = image.path.replace('.jpg', '.%d.jpg'%size)
if not os.path.exists(path):
image_size = max(image.width, image.height)
if size > image_size:
path = image.path
else:
extract.resize_image(image.path, path, size=size)
else:
path = image.path
return HttpFileResponse(path, content_type='image/jpeg')
def siteposter(request, id, size=None):
item = get_object_or_404(models.Item, itemId=id)
if not item.access(request.user):
return HttpResponseForbidden()
poster = item.path('siteposter.jpg')
poster = os.path.abspath(os.path.join(settings.MEDIA_ROOT, poster))
if size:
size = int(size)
image = Image.open(poster)
image_size = max(image.size)
if size < image_size:
path = poster.replace('.jpg', '.%d.jpg'%size)
extract.resize_image(poster, path, size=size)
poster = path
return HttpFileResponse(poster, content_type='image/jpeg')
def poster(request, id, size=None):
item = get_object_or_404(models.Item, itemId=id)
if not item.access(request.user):
return HttpResponseForbidden()
if not item.poster:
poster_path = os.path.join(settings.MEDIA_ROOT, item.path('poster.jpg'))
if os.path.exists(poster_path):
item.poster.name = item.path('poster.jpg')
item.poster_height = item.poster.height
item.poster_width = item.poster.width
models.Item.objects.filter(pk=item.id).update(
poster=item.poster.name,
poster_height=item.poster_height,
poster_width=item.poster_width,
icon=item.icon.name,
json=item.get_json()
)
if item.poster:
return image_to_response(item.poster, size)
else:
poster_path = os.path.join(settings.STATIC_ROOT, 'jpg/poster.jpg')
response = HttpFileResponse(poster_path, content_type='image/jpeg')
response['Cache-Control'] = 'no-cache'
return response
def icon(request, id, size=None):
item = get_object_or_404(models.Item, itemId=id)
if not item.access(request.user):
return HttpResponseForbidden()
if item.icon:
return image_to_response(item.icon, size)
else:
poster_path = os.path.join(settings.STATIC_ROOT, 'jpg/poster.jpg')
response = HttpFileResponse(poster_path, content_type='image/jpeg')
response['Cache-Control'] = 'no-cache'
return response
def timeline(request, id, size, position=-1, format='jpg', mode=None):
item = get_object_or_404(models.Item, itemId=id)
if not item.access(request.user):
return HttpResponseForbidden()
if not mode:
mode = 'antialias'
modes = [t['id'] for t in settings.CONFIG['timelines']]
if mode not in modes:
raise Http404
modes.pop(modes.index(mode))
prefix = os.path.join(item.timeline_prefix, 'timeline')
def timeline():
timeline = '%s%s%sp' % (prefix, mode, size)
if position > -1:
timeline += '%d' % int(position)
return timeline + '.jpg'
path = timeline()
while modes and not os.path.exists(path):
mode = modes.pop(0)
path = timeline()
return HttpFileResponse(path, content_type='image/jpeg')
def torrent(request, id, filename=None):
item = get_object_or_404(models.Item, itemId=id)
if not item.access(request.user):
return HttpResponseForbidden()
if not item.torrent:
raise Http404
if not filename or filename.endswith('.torrent'):
response = HttpResponse(item.get_torrent(request),
content_type='application/x-bittorrent')
filename = utils.safe_filename("%s.torrent" % item.get('title'))
response['Content-Disposition'] = "attachment; filename*=UTF-8''%s" % quote(filename.encode('utf-8'))
return response
while filename.startswith('/'):
filename = filename[1:]
filename = filename.replace('/../', '/')
filename = item.path('torrent/%s' % filename)
filename = os.path.abspath(os.path.join(settings.MEDIA_ROOT, filename))
response = HttpFileResponse(filename)
response['Content-Disposition'] = "attachment; filename*=UTF-8''%s" % \
quote(os.path.basename(filename.encode('utf-8')))
return response
def video(request, id, resolution, format, index=None):
item = get_object_or_404(models.Item, itemId=id)
if not item.access(request.user):
return HttpResponseForbidden()
if index:
index = int(index) - 1
else:
index = 0
#streams = Stream.object.filter(file__item__itemId=item.itemId,
# file__selected=True, file__part=index,
# resolution=resolution, format=format)
#if streams.count() != 1:
# reise Http404
streams = Stream.objects.filter(file__item__itemId=item.itemId,
resolution=resolution, format=format).order_by('file__part', 'file__sort_path')
if index + 1 > streams.count():
raise Http404
stream = streams[index]
if not stream.available or not stream.video:
raise Http404
path = stream.video.path
#server side cutting
#FIXME: this needs to join segments if needed
t = request.GET.get('t')
if t:
def parse_timestamp(s):
if ':' in s:
s = ox.time2ms(s) / 1000
return float(s)
t = map(parse_timestamp, t.split(','))
ext = '.%s' % format
content_type = mimetypes.guess_type(path)[0]
if len(t) == 2 and t[1] > t[0] and stream.info['duration']>=t[1]:
response = HttpResponse(extract.chop(path, t[0], t[1]), content_type=content_type)
filename = u"Clip of %s - %s-%s - %s %s%s" % (
item.get('title'),
ox.format_duration(t[0] * 1000).replace(':', '.')[:-4],
ox.format_duration(t[1] * 1000).replace(':', '.')[:-4],
settings.SITENAME,
item.itemId,
ext
)
response['Content-Disposition'] = "attachment; filename*=UTF-8''%s" % quote(filename.encode('utf-8'))
return response
else:
filename = "%s - %s %s%s" % (
item.get('title'),
settings.SITENAME,
item.itemId,
ext
)
response = HttpFileResponse(path, content_type=content_type)
response['Content-Disposition'] = "attachment; filename*=UTF-8''%s" % quote(filename.encode('utf-8'))
return response
if not settings.XSENDFILE and not settings.XACCELREDIRECT:
return redirect(stream.video.url)
response = HttpFileResponse(path)
response['Cache-Control'] = 'public'
return response
def srt(request, id, layer, index=None):
item = get_object_or_404(models.Item, itemId=id)
if not item.access(request.user):
response = HttpResponseForbidden()
else:
response = HttpResponse()
filename = u"%s.srt" % item.get('title')
response['Content-Disposition'] = "attachment; filename*=UTF-8''%s" % quote(filename.encode('utf-8'))
response['Content-Type'] = 'text/x-srt'
response.write(item.srt(layer))
return response
def random_annotation(request):
n = models.Item.objects.all().count()
pos = random.randint(0, n)
item = models.Item.objects.all()[pos]
n = item.annotations.all().count()
pos = random.randint(0, n)
clip = item.annotations.all()[pos]
return redirect('/%s'% clip.public_id)
def atom_xml(request):
add_updated = True
feed = ET.Element("feed")
feed.attrib['xmlns'] = 'http://www.w3.org/2005/Atom'
feed.attrib['xmlns:media'] = 'http://search.yahoo.com/mrss/'
feed.attrib['xml:lang'] = 'en'
title = ET.SubElement(feed, "title")
title.text = settings.SITENAME
title.attrib['type'] = 'text'
link = ET.SubElement(feed, "link")
link.attrib['rel'] = 'self'
link.attrib['type'] = 'application/atom+xml'
atom_link = request.build_absolute_uri('/atom.xml')
link.attrib['href'] = atom_link
'''
rights = ET.SubElement(feed, 'rights')
rights.attrib['type'] = 'text'
rights.text = "PGL"
'''
el = ET.SubElement(feed, 'id')
el.text = atom_link
level = settings.CONFIG['capabilities']['canSeeItem']['guest']
if not request.user.is_anonymous():
level = request.user.get_profile().level
for item in models.Item.objects.filter(level__lte=level, rendered=True).order_by('-created')[:7]:
if add_updated:
updated = ET.SubElement(feed, "updated")
updated.text = item.modified.strftime("%Y-%m-%dT%H:%M:%SZ")
add_updated = False
page_link = request.build_absolute_uri('/%s' % item.itemId)
entry = ET.Element("entry")
title = ET.SubElement(entry, "title")
title.text = item.get('title')
link = ET.SubElement(entry, "link")
link.attrib['rel'] = 'alternate'
link.attrib['href'] = "%s/info" % page_link
updated = ET.SubElement(entry, "updated")
updated.text = item.modified.strftime("%Y-%m-%dT%H:%M:%SZ")
published = ET.SubElement(entry, "published")
published.text = item.created.strftime("%Y-%m-%dT%H:%M:%SZ")
el = ET.SubElement(entry, "id")
el.text = page_link
if item.get('director'):
el = ET.SubElement(entry, "author")
name = ET.SubElement(el, "name")
name.text = u', '.join(item.get('director'))
elif item.user:
el = ET.SubElement(entry, "author")
name = ET.SubElement(el, "name")
name.text = item.user.username
for topic in item.get('topics', []):
el = ET.SubElement(entry, "category")
el.attrib['term'] = topic
'''
el = ET.SubElement(entry, "rights")
el.text = "PGL"
el = ET.SubElement(entry, "link")
el.attrib['rel'] = "license"
el.attrib['type'] = "text/html"
el.attrib['href'] = item.licenseUrl
'''
'''
el = ET.SubElement(entry, "contributor")
name = ET.SubElement(el, "name")
name.text = item.user.username
'''
description = item.get('description', item.get('summary'))
if description:
content = ET.SubElement(entry, "content")
content.attrib['type'] = 'html'
content.text = description
format = ET.SubElement(entry, "format")
format.attrib['xmlns'] = 'http://transmission.cc/FileFormat'
stream = item.streams().filter(source=None).order_by('-id')[0]
for key in ('size', 'duration', 'video_codec',
'framerate', 'width', 'height',
'audio_codec', 'samplerate', 'channels'):
value = stream.info.get(key)
if not value and stream.info.get('video'):
value = stream.info['video'][0].get({
'video_codec': 'codec'
}.get(key, key))
if not value and stream.info.get('audio'):
value = stream.info['audio'][0].get({
'audio_codec': 'codec'
}.get(key, key))
if value and value != -1:
el = ET.SubElement(format, key)
el.text = unicode(value)
el = ET.SubElement(format, 'pixel_aspect_ratio')
el.text = u"1:1"
if settings.CONFIG['video'].get('download'):
if item.torrent:
el = ET.SubElement(entry, "link")
el.attrib['rel'] = 'enclosure'
el.attrib['type'] = 'application/x-bittorrent'
el.attrib['href'] = '%s/torrent/' % page_link
el.attrib['length'] = '%s' % ox.get_torrent_size(item.torrent.path)
#FIXME: loop over streams
#for s in item.streams().filter(resolution=max(settings.CONFIG['video']['resolutions'])):
for s in item.streams().filter(source=None):
el = ET.SubElement(entry, "link")
el.attrib['rel'] = 'enclosure'
el.attrib['type'] = 'video/%s' % s.format
el.attrib['href'] = '%s/%sp.%s' % (page_link, s.resolution, s.format)
el.attrib['length'] = '%s'%s.video.size
el = ET.SubElement(entry, "media:thumbnail")
thumbheight = 96
thumbwidth = int(thumbheight * item.stream_aspect)
thumbwidth -= thumbwidth % 2
el.attrib['url'] = '%s/%sp.jpg' % (page_link, thumbheight)
el.attrib['width'] = str(thumbwidth)
el.attrib['height'] = str(thumbheight)
feed.append(entry)
return HttpResponse(
'<?xml version="1.0" encoding="utf-8" ?>\n' + ET.tostring(feed),
'application/atom+xml'
)
def oembed(request):
format = request.GET.get('format', 'json')
maxwidth = request.GET.get('maxwidth', 640)
maxheight = request.GET.get('maxheight', 480)
url = request.GET['url']
parts = urlparse(url).path.split('/')
itemId = parts[1]
#fixme: embed should reflect actuall url
item = get_object_or_404_json(models.Item, itemId=itemId)
embed_url = request.build_absolute_uri('/%s/embed' % item.itemId)
oembed = {}
oembed['version'] = '1.0'
oembed['type'] = 'video'
oembed['provider_name'] = settings.SITENAME
oembed['provider_url'] = request.build_absolute_uri('/')
oembed['title'] = item.get('title')
#oembed['author_name'] = item.get('director')
#oembed['author_url'] = ??
height = 96
width = 128
if maxheight > height or height > maxheight:
height = maxheight
if maxwidth > width or width > maxwidth:
width = maxwidth
oembed['html'] = '<iframe width="%s" height="%s" src="%s" frameborder="0" allowfullscreen></iframe>' % (width, height, embed_url)
oembed['width'] = width
oembed['height'] = height
thumbheight = 96
thumbwidth = int(thumbheight * item.stream_aspect)
thumbwidth -= thumbwidth % 2
oembed['thumbnail_height'] = thumbheight
oembed['thumbnail_width'] = thumbwidth
oembed['thumbnail_url'] = request.build_absolute_uri('/%s/%sp.jpg' % (item.itemId, thumbheight))
if format == 'xml':
oxml = ET.Element('oembed')
for key in oembed:
e = ET.SubElement(oxml, key)
e.text = unicode(oembed[key])
return HttpResponse(
'<?xml version="1.0" encoding="utf-8" standalone="yes"?>\n' + ET.tostring(oxml),
'application/xml'
)
return HttpResponse(json.dumps(oembed, indent=2), 'application/json')
def sitemap_xml(request):
sitemap = os.path.abspath(os.path.join(settings.MEDIA_ROOT, 'sitemap.xml'))
if not os.path.exists(sitemap):
tasks.update_sitemap(request.build_absolute_uri('/'))
elif time.mktime(time.localtime()) - os.stat(sitemap).st_ctime > 24*60*60:
tasks.update_sitemap.delay(request.build_absolute_uri('/'))
response = HttpFileResponse(sitemap)
response['Content-Type'] = 'application/xml'
return response
def item_json(request, id):
level = settings.CONFIG['capabilities']['canSeeItem']['guest']
if not request.user.is_anonymous():
level = request.user.get_profile().level
qs = models.Item.objects.filter(itemId=id, level__lte=level)
if qs.count() == 0:
response = json_response(status=404, text='not found')
else:
item = qs[0]
response = item.get_json()
response['layers'] = item.get_layers(request.user)
return render_to_json_response(response)
def item_xml(request, id):
level = settings.CONFIG['capabilities']['canSeeItem']['guest']
if not request.user.is_anonymous():
level = request.user.get_profile().level
qs = models.Item.objects.filter(itemId=id, level__lte=level)
if qs.count() == 0:
response = json_response(status=404, text='not found')
response = render_to_json_response(response)
else:
item = qs[0]
j = item.get_json()
j['layers'] = item.get_layers(request.user)
if 'resolution' in j:
j['resolution'] = {'width': j['resolution'][0], 'height':j['resolution'][1]}
def xmltree(root, key, data):
if isinstance(data, list) or \
isinstance(data, tuple):
e = ET.SubElement(root, key)
for value in data:
xmltree(e, key, value)
elif isinstance(data, dict):
for k in data:
if data[k]:
xmltree(root, k, data[k])
else:
e = ET.SubElement(root, key)
e.text = unicode(data)
oxml = ET.Element('item')
xmltree(oxml, 'item', j)
response = HttpResponse(
'<?xml version="1.0" encoding="utf-8" standalone="yes"?>\n' + ET.tostring(oxml),
'application/xml'
)
return response
def item(request, id):
id = id.split('/')[0]
template = 'index.html'
level = settings.CONFIG['capabilities']['canSeeItem']['guest']
if not request.user.is_anonymous():
level = request.user.get_profile().level
qs = models.Item.objects.filter(itemId=id, level__lte=level)
if qs.count() == 0:
context = RequestContext(request, {
'base_url': request.build_absolute_uri('/'),
'settings': settings
})
else:
item = qs[0]
template = 'item.html'
keys = [
'year',
'director',
'topic',
'summary'
]
data = []
for key in keys:
value = item.get(key)
if value:
if isinstance(value, list):
value = value = u', '.join([unicode(v) for v in value])
data.append({'key': key.capitalize(), 'value': value})
clips = []
clip = {'in': 0, 'annotations': []}
#logged in users should have javascript. not adding annotations makes load faster
if request.user.is_anonymous():
for a in item.annotations.filter(
layer__in=models.Annotation.public_layers()).order_by('start', 'end', 'sortvalue'):
if clip['in'] < a.start:
if clip['annotations']:
clip['annotations'] = '<br />\n'.join(clip['annotations'])
clips.append(clip)
clip = {'in': a.start, 'annotations': []}
clip['annotations'].append(a.value)
ctx = {
'current_url': request.build_absolute_uri(request.get_full_path()),
'base_url': request.build_absolute_uri('/'),
'url': request.build_absolute_uri('/%s' % id),
'id': id,
'settings': settings,
'data': data,
'clips': clips,
'icon': settings.CONFIG['user']['ui']['icons'] == 'frames' and 'icon' or 'poster',
}
for key in ('title', 'description', 'keywords'):
value = item.get({
'description': 'summary',
'keywords': 'topic' in keys and 'topic' or 'keywords'
}.get(key, key))
if isinstance(value, list):
value = value = ', '.join(value)
if value:
ctx[key] = ox.strip_tags(value)
context = RequestContext(request, ctx)
return render_to_response(template, context)