forked from 0x2620/pandora
list icons
This commit is contained in:
parent
d7a2833711
commit
64e7a534a7
6 changed files with 106 additions and 24 deletions
|
@ -444,6 +444,7 @@ class Item(models.Model):
|
||||||
if not keys or 'posterRatio' in keys:
|
if not keys or 'posterRatio' in keys:
|
||||||
i['posterRatio'] = self.poster_width / self.poster_height
|
i['posterRatio'] = self.poster_width / self.poster_height
|
||||||
|
|
||||||
|
|
||||||
streams = self.streams()
|
streams = self.streams()
|
||||||
i['durations'] = [s.duration for s in streams]
|
i['durations'] = [s.duration for s in streams]
|
||||||
i['duration'] = sum(i['durations'])
|
i['duration'] = sum(i['durations'])
|
||||||
|
@ -454,8 +455,16 @@ class Item(models.Model):
|
||||||
#only needed by admins
|
#only needed by admins
|
||||||
if keys and 'posters' in keys:
|
if keys and 'posters' in keys:
|
||||||
i['posters'] = self.get_posters()
|
i['posters'] = self.get_posters()
|
||||||
|
|
||||||
|
frames = self.get_frames()
|
||||||
if keys and 'frames' in keys:
|
if keys and 'frames' in keys:
|
||||||
i['frames'] = self.get_frames()
|
i['frames'] = frames
|
||||||
|
|
||||||
|
if frames:
|
||||||
|
i['posterFrame'] = filter(lambda f: f['selected'], frames)[0]['position']
|
||||||
|
elif self.poster_frame != -1.0:
|
||||||
|
i['posterFrame'] = self.poster_frame
|
||||||
|
|
||||||
if keys:
|
if keys:
|
||||||
info = {}
|
info = {}
|
||||||
for key in keys:
|
for key in keys:
|
||||||
|
|
|
@ -10,11 +10,11 @@ import models
|
||||||
def parseCondition(condition, user):
|
def parseCondition(condition, user):
|
||||||
'''
|
'''
|
||||||
'''
|
'''
|
||||||
print condition, user
|
|
||||||
k = condition.get('key', 'name')
|
k = condition.get('key', 'name')
|
||||||
k = {
|
k = {
|
||||||
'user': 'user__username',
|
'user': 'user__username',
|
||||||
'position': 'position__position',
|
'position': 'position__position',
|
||||||
|
'posterFrames': 'poster_frames',
|
||||||
}.get(k, k)
|
}.get(k, k)
|
||||||
if not k:
|
if not k:
|
||||||
k = 'name'
|
k = 'name'
|
||||||
|
@ -132,4 +132,4 @@ class ListManager(Manager):
|
||||||
qs = qs.filter(Q(status='public') | Q(status='featured'))
|
qs = qs.filter(Q(status='public') | Q(status='featured'))
|
||||||
else:
|
else:
|
||||||
qs = qs.filter(Q(status='public') | Q(status='featured') | Q(user=user))
|
qs = qs.filter(Q(status='public') | Q(status='featured') | Q(user=user))
|
||||||
return qs.distinct()
|
return qs
|
||||||
|
|
|
@ -3,13 +3,16 @@
|
||||||
from __future__ import division, with_statement
|
from __future__ import division, with_statement
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
from glob import glob
|
||||||
|
|
||||||
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.fields import DictField
|
from ox.django.fields import DictField, TupleField
|
||||||
|
|
||||||
|
|
||||||
|
from archive import extract
|
||||||
import managers
|
import managers
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,10 +34,13 @@ class List(models.Model):
|
||||||
icon = models.ImageField(default=None, blank=True,
|
icon = models.ImageField(default=None, blank=True,
|
||||||
upload_to=lambda i, x: i.path("icon.jpg"))
|
upload_to=lambda i, x: i.path("icon.jpg"))
|
||||||
|
|
||||||
|
poster_frames = TupleField(default=[], editable=False)
|
||||||
|
|
||||||
#is through table still required?
|
#is through table still required?
|
||||||
items = models.ManyToManyField('item.Item', related_name='lists',
|
items = models.ManyToManyField('item.Item', related_name='lists',
|
||||||
through='ListItem')
|
through='ListItem')
|
||||||
|
|
||||||
|
items_sum = models.IntegerField(default=0)
|
||||||
subscribed_users = models.ManyToManyField(User, related_name='subscribed_lists')
|
subscribed_users = models.ManyToManyField(User, related_name='subscribed_lists')
|
||||||
|
|
||||||
objects = managers.ListManager()
|
objects = managers.ListManager()
|
||||||
|
@ -44,9 +50,10 @@ class List(models.Model):
|
||||||
self.type = 'static'
|
self.type = 'static'
|
||||||
else:
|
else:
|
||||||
self.type = 'smart'
|
self.type = 'smart'
|
||||||
|
self.items_sum = self.get_items_sum(self.user)
|
||||||
super(List, self).save(*args, **kwargs)
|
super(List, self).save(*args, **kwargs)
|
||||||
|
|
||||||
def get_number_of_items(self, user=None):
|
def get_items_sum(self, user=None):
|
||||||
if self.query.get('static', False):
|
if self.query.get('static', False):
|
||||||
return self.items.count()
|
return self.items.count()
|
||||||
else:
|
else:
|
||||||
|
@ -78,11 +85,11 @@ class List(models.Model):
|
||||||
|
|
||||||
def json(self, keys=None, user=None):
|
def json(self, keys=None, user=None):
|
||||||
if not keys:
|
if not keys:
|
||||||
keys=['id', 'name', 'user', 'type', 'query', 'status', 'subscribed']
|
keys=['id', 'name', 'user', 'type', 'query', 'status', 'subscribed', 'posterFrames']
|
||||||
response = {}
|
response = {}
|
||||||
for key in keys:
|
for key in keys:
|
||||||
if key == 'items':
|
if key == 'items':
|
||||||
response[key] = self.get_number_of_items(user)
|
response[key] = self.get_items_sum(user)
|
||||||
elif key == 'id':
|
elif key == 'id':
|
||||||
response[key] = self.get_id()
|
response[key] = self.get_id()
|
||||||
elif key == 'user':
|
elif key == 'user':
|
||||||
|
@ -90,30 +97,64 @@ class List(models.Model):
|
||||||
elif key == 'query':
|
elif key == 'query':
|
||||||
if not self.query.get('static', False):
|
if not self.query.get('static', False):
|
||||||
response[key] = self.query
|
response[key] = self.query
|
||||||
|
elif key == 'subscribers':
|
||||||
|
response[key] = self.subscribed_users.all().count()
|
||||||
elif key == 'subscribed':
|
elif key == 'subscribed':
|
||||||
if user and not user.is_anonymous():
|
if user and not user.is_anonymous():
|
||||||
response[key] = self.subscribed_users.filter(id=user.id).exists()
|
response[key] = self.subscribed_users.filter(id=user.id).exists()
|
||||||
else:
|
else:
|
||||||
response[key] = getattr(self, key)
|
response[key] = getattr(self, {
|
||||||
|
'posterFrames': 'poster_frames'
|
||||||
|
}.get(key, key))
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def path(self, name=''):
|
def path(self, name=''):
|
||||||
h = self.get_id()
|
h = "%06d" % self.id
|
||||||
return os.path.join('lists', h[:2], h[2:4], h[4:6], h[6:], name)
|
return os.path.join('lists', h[:2], h[2:4], h[4:6], h[6:], name)
|
||||||
|
|
||||||
def make_icon(self):
|
def update_icon(self):
|
||||||
frames = []
|
frames = []
|
||||||
self.icon.name = self.path('icon.png')
|
for i in self.poster_frames:
|
||||||
|
qs = self.items.filter(itemId=i['item'])
|
||||||
|
if qs.count() > 0:
|
||||||
|
frame = qs[0].frame(i['position'])
|
||||||
|
if frame:
|
||||||
|
frames.append(frame)
|
||||||
|
self.icon.name = self.path('icon.jpg')
|
||||||
icon = self.icon.path
|
icon = self.icon.path
|
||||||
if frames:
|
if frames:
|
||||||
|
while len(frames) < 4:
|
||||||
|
frames += frames
|
||||||
|
folder = os.path.dirname(icon)
|
||||||
|
ox.makedirs(folder)
|
||||||
|
for f in glob("%s/icon*.jpg" % folder):
|
||||||
|
os.unlink(f)
|
||||||
cmd = [
|
cmd = [
|
||||||
'scripts/list_icon',
|
settings.LIST_ICON,
|
||||||
'-f', ','.join(frames),
|
'-f', ','.join(frames),
|
||||||
'-o', icon
|
'-o', icon
|
||||||
]
|
]
|
||||||
p = subprocess.Popen(cmd)
|
p = subprocess.Popen(cmd)
|
||||||
p.wait()
|
p.wait()
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
def get_icon(self, size=16):
|
||||||
|
path = self.path('icon%d.jpg' % size)
|
||||||
|
path = os.path.join(settings.MEDIA_ROOT, path)
|
||||||
|
if not os.path.exists(path):
|
||||||
|
folder = os.path.dirname(path)
|
||||||
|
ox.makedirs(folder)
|
||||||
|
if self.icon:
|
||||||
|
source = self.icon.path
|
||||||
|
max_size = min(self.icon.width, self.icon.height)
|
||||||
|
else:
|
||||||
|
source = os.path.join(settings.STATIC_ROOT, 'png/list256.png')
|
||||||
|
max_size = 256
|
||||||
|
if size < max_size:
|
||||||
|
extract.resize_image(source, path, size=size)
|
||||||
|
else:
|
||||||
|
path = source
|
||||||
|
return path
|
||||||
|
|
||||||
class ListItem(models.Model):
|
class ListItem(models.Model):
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
# 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.db.models import Max
|
from django.db.models import Max, Sum
|
||||||
|
from django.http import HttpResponseForbidden, Http404
|
||||||
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 models
|
import models
|
||||||
from api.actions import actions
|
from api.actions import actions
|
||||||
|
@ -25,10 +27,13 @@ def _order_query(qs, sort):
|
||||||
if operator != '-':
|
if operator != '-':
|
||||||
operator = ''
|
operator = ''
|
||||||
key = {
|
key = {
|
||||||
'subscribed': 'subscribed_users'
|
'subscribed': 'subscribed_users',
|
||||||
|
'items': 'items_sum'
|
||||||
}.get(e['key'], e['key'])
|
}.get(e['key'], e['key'])
|
||||||
order = '%s%s' % (operator, key)
|
order = '%s%s' % (operator, key)
|
||||||
order_by.append(order)
|
order_by.append(order)
|
||||||
|
if key == 'subscribers':
|
||||||
|
qs = qs.annotate(subscribers=Sum('subscribed_users'))
|
||||||
if order_by:
|
if order_by:
|
||||||
qs = qs.order_by(*order_by)
|
qs = qs.order_by(*order_by)
|
||||||
return qs
|
return qs
|
||||||
|
@ -181,6 +186,13 @@ def addList(request):
|
||||||
param data {
|
param data {
|
||||||
name: value,
|
name: value,
|
||||||
}
|
}
|
||||||
|
possible keys to create list:
|
||||||
|
name
|
||||||
|
description
|
||||||
|
type
|
||||||
|
query
|
||||||
|
items
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: {'code': int, 'text': string},
|
status: {'code': int, 'text': string},
|
||||||
data: {
|
data: {
|
||||||
|
@ -197,7 +209,7 @@ def addList(request):
|
||||||
while not created:
|
while not created:
|
||||||
list, created = models.List.objects.get_or_create(name=name, user=request.user)
|
list, created = models.List.objects.get_or_create(name=name, user=request.user)
|
||||||
num += 1
|
num += 1
|
||||||
name = data['name'] + ' (%d)' % num
|
name = data['name'] + ' [%d]' % num
|
||||||
|
|
||||||
for key in data:
|
for key in data:
|
||||||
if key == 'query' and not data['query']:
|
if key == 'query' and not data['query']:
|
||||||
|
@ -223,6 +235,10 @@ def addList(request):
|
||||||
list.description = data['description']
|
list.description = data['description']
|
||||||
list.save()
|
list.save()
|
||||||
|
|
||||||
|
if 'items' in data:
|
||||||
|
for item in Item.objects.filter(itemId__in=data['items']):
|
||||||
|
list.add(item)
|
||||||
|
|
||||||
if list.status == 'featured':
|
if list.status == 'featured':
|
||||||
pos, created = models.Position.objects.get_or_create(list=list,
|
pos, created = models.Position.objects.get_or_create(list=list,
|
||||||
user=request.user, section='featured')
|
user=request.user, section='featured')
|
||||||
|
@ -246,9 +262,11 @@ def editList(request):
|
||||||
id: listId,
|
id: listId,
|
||||||
key: value,
|
key: value,
|
||||||
}
|
}
|
||||||
keys: name, status, query, position
|
keys: name, status, query, position, posterFrames
|
||||||
if you change status you have to provide position of list
|
if you change status you have to provide position of list
|
||||||
|
|
||||||
|
posterFrames:
|
||||||
|
array with objects that have item/position
|
||||||
return {
|
return {
|
||||||
status: {'code': int, 'text': string},
|
status: {'code': int, 'text': string},
|
||||||
data: {
|
data: {
|
||||||
|
@ -330,6 +348,9 @@ def editList(request):
|
||||||
if list.status == 'private':
|
if list.status == 'private':
|
||||||
pos.section = 'personal'
|
pos.section = 'personal'
|
||||||
pos.save()
|
pos.save()
|
||||||
|
if 'posterFrames' in data:
|
||||||
|
list.poster_frames = tuple(data['posterFrames'])
|
||||||
|
list.update_icon()
|
||||||
list.save()
|
list.save()
|
||||||
response['data'] = list.json(user=request.user)
|
response['data'] = list.json(user=request.user)
|
||||||
else:
|
else:
|
||||||
|
@ -462,3 +483,13 @@ def sortLists(request):
|
||||||
response = json_response()
|
response = json_response()
|
||||||
return render_to_json_response(response)
|
return render_to_json_response(response)
|
||||||
actions.register(sortLists, cache=False)
|
actions.register(sortLists, cache=False)
|
||||||
|
|
||||||
|
|
||||||
|
def icon(request, id, size=16):
|
||||||
|
if not size:
|
||||||
|
size = 16
|
||||||
|
list = get_list_or_404_json(id)
|
||||||
|
icon = list.get_icon(int(size))
|
||||||
|
if icon:
|
||||||
|
return HttpFileResponse(icon, content_type='image/jpeg')
|
||||||
|
raise Http404
|
||||||
|
|
|
@ -16,15 +16,14 @@ from optparse import OptionParser
|
||||||
from ox.image import drawText, wrapText
|
from ox.image import drawText, wrapText
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
static_root = os.path.join(os.path.dirname(__file__), '..', '..', 'static')
|
static_root = os.path.join(os.path.dirname(__file__), '..', '..', 'static')
|
||||||
|
|
||||||
def render_list_icon(frames, icon):
|
def render_list_icon(frames, icon):
|
||||||
icon_width = 256
|
icon_width = 256
|
||||||
icon_height = 256
|
icon_height = 256
|
||||||
icon_image = Image.new('RGBA', (icon_width, icon_height))
|
icon_image = Image.new('RGBA', (icon_width, icon_height))
|
||||||
frame_height = icon_height / 4
|
frame_height = int(icon_height / 2)
|
||||||
frame_ratio = 4 / 3
|
frame_ratio = 1
|
||||||
frame_width = int(round(frame_height * frame_ratio))
|
frame_width = int(round(frame_height * frame_ratio))
|
||||||
for i, frame in enumerate(frames):
|
for i, frame in enumerate(frames):
|
||||||
frame_image = Image.open(frame)
|
frame_image = Image.open(frame)
|
||||||
|
@ -38,8 +37,9 @@ def render_list_icon(frames, icon):
|
||||||
frame_image = frame_image.resize((frame_width_, int(frame_width_ / frame_image_ratio)), Image.ANTIALIAS)
|
frame_image = frame_image.resize((frame_width_, int(frame_width_ / frame_image_ratio)), Image.ANTIALIAS)
|
||||||
top = int((frame_image.size[1] - frame_height) / 2)
|
top = int((frame_image.size[1] - frame_height) / 2)
|
||||||
frame_image = frame_image.crop((0, top, frame_width_, top + frame_height))
|
frame_image = frame_image.crop((0, top, frame_width_, top + frame_height))
|
||||||
icon_image.paste(frame_image, (i % 3 * frame_width + (1 if i % 2 == 2 else 0), int(i / 3) * frame_height))
|
icon_image.paste(frame_image, (i % 2 * frame_width + (1 if i % 2 == 2 else 0),
|
||||||
mask_image = Image.open(ox.path.join(static_root, 'png', 'icon.mask.png'))
|
int(i / 2) * frame_height))
|
||||||
|
mask_image = Image.open(os.path.join(static_root, 'png', 'iconMask.png'))
|
||||||
mask_image = mask_image.resize((icon_width, icon_height))
|
mask_image = mask_image.resize((icon_width, icon_height))
|
||||||
icon_image.putalpha(mask_image)
|
icon_image.putalpha(mask_image)
|
||||||
icon_image.save(icon)
|
icon_image.save(icon)
|
||||||
|
@ -47,7 +47,7 @@ def render_list_icon(frames, icon):
|
||||||
def main():
|
def main():
|
||||||
parser = OptionParser()
|
parser = OptionParser()
|
||||||
parser.add_option('-f', '--frames', dest='frames', help='Poster frames (image files to be read)', default='')
|
parser.add_option('-f', '--frames', dest='frames', help='Poster frames (image files to be read)', default='')
|
||||||
parser.add_option('-i', '--icon', dest='icon', help='Icon (image file to be written)')
|
parser.add_option('-o', '--icon', dest='icon', help='Icon (image file to be written)')
|
||||||
(options, args) = parser.parse_args()
|
(options, args) = parser.parse_args()
|
||||||
if options.icon == None:
|
if options.icon == None:
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
|
@ -55,7 +55,7 @@ def main():
|
||||||
|
|
||||||
frames = options.frames.replace(', ', ',').split(',')
|
frames = options.frames.replace(', ', ',').split(',')
|
||||||
|
|
||||||
render_list_icon(frames, opt.icon)
|
render_list_icon(frames, options.icon)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -28,6 +28,7 @@ urlpatterns = patterns('',
|
||||||
(r'^file/(?P<oshash>.*)$', 'archive.views.lookup_file'),
|
(r'^file/(?P<oshash>.*)$', 'archive.views.lookup_file'),
|
||||||
(r'^api/$', include('api.urls')),
|
(r'^api/$', include('api.urls')),
|
||||||
(r'', include('item.urls')),
|
(r'', include('item.urls')),
|
||||||
|
(r'^list/(?P<id>.*?)/icon(?P<size>\d*).jpg$', 'itemlist.views.icon'),
|
||||||
(r'^robots.txt$', serve_static_file, {'location': os.path.join(settings.STATIC_ROOT, 'robots.txt'), 'content_type': 'text/plain'}),
|
(r'^robots.txt$', serve_static_file, {'location': os.path.join(settings.STATIC_ROOT, 'robots.txt'), 'content_type': 'text/plain'}),
|
||||||
(r'^favicon.ico$', serve_static_file, {'location': os.path.join(settings.STATIC_ROOT, 'png/icon.16.png'), 'content_type': 'image/x-icon'}),
|
(r'^favicon.ico$', serve_static_file, {'location': os.path.join(settings.STATIC_ROOT, 'png/icon.16.png'), 'content_type': 'image/x-icon'}),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue