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:
|
||||
i['posterRatio'] = self.poster_width / self.poster_height
|
||||
|
||||
|
||||
streams = self.streams()
|
||||
i['durations'] = [s.duration for s in streams]
|
||||
i['duration'] = sum(i['durations'])
|
||||
|
@ -454,8 +455,16 @@ class Item(models.Model):
|
|||
#only needed by admins
|
||||
if keys and 'posters' in keys:
|
||||
i['posters'] = self.get_posters()
|
||||
|
||||
frames = self.get_frames()
|
||||
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:
|
||||
info = {}
|
||||
for key in keys:
|
||||
|
|
|
@ -10,11 +10,11 @@ import models
|
|||
def parseCondition(condition, user):
|
||||
'''
|
||||
'''
|
||||
print condition, user
|
||||
k = condition.get('key', 'name')
|
||||
k = {
|
||||
'user': 'user__username',
|
||||
'position': 'position__position',
|
||||
'posterFrames': 'poster_frames',
|
||||
}.get(k, k)
|
||||
if not k:
|
||||
k = 'name'
|
||||
|
@ -132,4 +132,4 @@ class ListManager(Manager):
|
|||
qs = qs.filter(Q(status='public') | Q(status='featured'))
|
||||
else:
|
||||
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
|
||||
import os
|
||||
import subprocess
|
||||
from glob import glob
|
||||
|
||||
from django.db import models
|
||||
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
|
||||
|
||||
|
||||
|
@ -31,10 +34,13 @@ class List(models.Model):
|
|||
icon = models.ImageField(default=None, blank=True,
|
||||
upload_to=lambda i, x: i.path("icon.jpg"))
|
||||
|
||||
poster_frames = TupleField(default=[], editable=False)
|
||||
|
||||
#is through table still required?
|
||||
items = models.ManyToManyField('item.Item', related_name='lists',
|
||||
through='ListItem')
|
||||
|
||||
items_sum = models.IntegerField(default=0)
|
||||
subscribed_users = models.ManyToManyField(User, related_name='subscribed_lists')
|
||||
|
||||
objects = managers.ListManager()
|
||||
|
@ -44,9 +50,10 @@ class List(models.Model):
|
|||
self.type = 'static'
|
||||
else:
|
||||
self.type = 'smart'
|
||||
self.items_sum = self.get_items_sum(self.user)
|
||||
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):
|
||||
return self.items.count()
|
||||
else:
|
||||
|
@ -78,11 +85,11 @@ class List(models.Model):
|
|||
|
||||
def json(self, keys=None, user=None):
|
||||
if not keys:
|
||||
keys=['id', 'name', 'user', 'type', 'query', 'status', 'subscribed']
|
||||
keys=['id', 'name', 'user', 'type', 'query', 'status', 'subscribed', 'posterFrames']
|
||||
response = {}
|
||||
for key in keys:
|
||||
if key == 'items':
|
||||
response[key] = self.get_number_of_items(user)
|
||||
response[key] = self.get_items_sum(user)
|
||||
elif key == 'id':
|
||||
response[key] = self.get_id()
|
||||
elif key == 'user':
|
||||
|
@ -90,30 +97,64 @@ class List(models.Model):
|
|||
elif key == 'query':
|
||||
if not self.query.get('static', False):
|
||||
response[key] = self.query
|
||||
elif key == 'subscribers':
|
||||
response[key] = self.subscribed_users.all().count()
|
||||
elif key == 'subscribed':
|
||||
if user and not user.is_anonymous():
|
||||
response[key] = self.subscribed_users.filter(id=user.id).exists()
|
||||
else:
|
||||
response[key] = getattr(self, key)
|
||||
response[key] = getattr(self, {
|
||||
'posterFrames': 'poster_frames'
|
||||
}.get(key, key))
|
||||
return response
|
||||
|
||||
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)
|
||||
|
||||
def make_icon(self):
|
||||
def update_icon(self):
|
||||
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
|
||||
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 = [
|
||||
'scripts/list_icon',
|
||||
settings.LIST_ICON,
|
||||
'-f', ','.join(frames),
|
||||
'-o', icon
|
||||
]
|
||||
p = subprocess.Popen(cmd)
|
||||
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):
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
# vi:si:et:sw=4:sts=4:ts=4
|
||||
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.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 models
|
||||
from api.actions import actions
|
||||
|
@ -25,10 +27,13 @@ def _order_query(qs, sort):
|
|||
if operator != '-':
|
||||
operator = ''
|
||||
key = {
|
||||
'subscribed': 'subscribed_users'
|
||||
'subscribed': 'subscribed_users',
|
||||
'items': 'items_sum'
|
||||
}.get(e['key'], e['key'])
|
||||
order = '%s%s' % (operator, key)
|
||||
order_by.append(order)
|
||||
if key == 'subscribers':
|
||||
qs = qs.annotate(subscribers=Sum('subscribed_users'))
|
||||
if order_by:
|
||||
qs = qs.order_by(*order_by)
|
||||
return qs
|
||||
|
@ -181,6 +186,13 @@ def addList(request):
|
|||
param data {
|
||||
name: value,
|
||||
}
|
||||
possible keys to create list:
|
||||
name
|
||||
description
|
||||
type
|
||||
query
|
||||
items
|
||||
|
||||
return {
|
||||
status: {'code': int, 'text': string},
|
||||
data: {
|
||||
|
@ -197,7 +209,7 @@ def addList(request):
|
|||
while not created:
|
||||
list, created = models.List.objects.get_or_create(name=name, user=request.user)
|
||||
num += 1
|
||||
name = data['name'] + ' (%d)' % num
|
||||
name = data['name'] + ' [%d]' % num
|
||||
|
||||
for key in data:
|
||||
if key == 'query' and not data['query']:
|
||||
|
@ -223,6 +235,10 @@ def addList(request):
|
|||
list.description = data['description']
|
||||
list.save()
|
||||
|
||||
if 'items' in data:
|
||||
for item in Item.objects.filter(itemId__in=data['items']):
|
||||
list.add(item)
|
||||
|
||||
if list.status == 'featured':
|
||||
pos, created = models.Position.objects.get_or_create(list=list,
|
||||
user=request.user, section='featured')
|
||||
|
@ -246,9 +262,11 @@ def editList(request):
|
|||
id: listId,
|
||||
key: value,
|
||||
}
|
||||
keys: name, status, query, position
|
||||
keys: name, status, query, position, posterFrames
|
||||
if you change status you have to provide position of list
|
||||
|
||||
posterFrames:
|
||||
array with objects that have item/position
|
||||
return {
|
||||
status: {'code': int, 'text': string},
|
||||
data: {
|
||||
|
@ -330,6 +348,9 @@ def editList(request):
|
|||
if list.status == 'private':
|
||||
pos.section = 'personal'
|
||||
pos.save()
|
||||
if 'posterFrames' in data:
|
||||
list.poster_frames = tuple(data['posterFrames'])
|
||||
list.update_icon()
|
||||
list.save()
|
||||
response['data'] = list.json(user=request.user)
|
||||
else:
|
||||
|
@ -462,3 +483,13 @@ def sortLists(request):
|
|||
response = json_response()
|
||||
return render_to_json_response(response)
|
||||
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
|
||||
import sys
|
||||
|
||||
|
||||
static_root = os.path.join(os.path.dirname(__file__), '..', '..', 'static')
|
||||
|
||||
def render_list_icon(frames, icon):
|
||||
icon_width = 256
|
||||
icon_height = 256
|
||||
icon_image = Image.new('RGBA', (icon_width, icon_height))
|
||||
frame_height = icon_height / 4
|
||||
frame_ratio = 4 / 3
|
||||
frame_height = int(icon_height / 2)
|
||||
frame_ratio = 1
|
||||
frame_width = int(round(frame_height * frame_ratio))
|
||||
for i, frame in enumerate(frames):
|
||||
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)
|
||||
top = int((frame_image.size[1] - frame_height) / 2)
|
||||
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))
|
||||
mask_image = Image.open(ox.path.join(static_root, 'png', 'icon.mask.png'))
|
||||
icon_image.paste(frame_image, (i % 2 * frame_width + (1 if i % 2 == 2 else 0),
|
||||
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))
|
||||
icon_image.putalpha(mask_image)
|
||||
icon_image.save(icon)
|
||||
|
@ -47,7 +47,7 @@ def render_list_icon(frames, icon):
|
|||
def main():
|
||||
parser = OptionParser()
|
||||
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()
|
||||
if options.icon == None:
|
||||
parser.print_help()
|
||||
|
@ -55,7 +55,7 @@ def main():
|
|||
|
||||
frames = options.frames.replace(', ', ',').split(',')
|
||||
|
||||
render_list_icon(frames, opt.icon)
|
||||
render_list_icon(frames, options.icon)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -28,6 +28,7 @@ urlpatterns = patterns('',
|
|||
(r'^file/(?P<oshash>.*)$', 'archive.views.lookup_file'),
|
||||
(r'^api/$', include('api.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'^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