Remove unused license preamble
[mygpo.git] / mygpo / web / logo.py
blob948fa500b2d4dd14d76e200db1a4bc2a8a21a5ac
1 import os.path
2 import io
3 from datetime import datetime
4 from glob import glob
5 import errno
6 import hashlib
7 import struct
9 from PIL import Image, ImageDraw
11 from django.core.urlresolvers import reverse
12 from django.conf import settings
13 from django.http import Http404, HttpResponse, HttpResponseNotFound
14 from django.views.generic.base import View
15 from django.utils.decorators import method_decorator
16 from django.views.decorators.http import last_modified
18 import logging
19 logger = logging.getLogger(__name__)
21 LOGO_DIR = os.path.join(settings.BASE_DIR, '..', 'htdocs', 'media', 'logo')
24 def _last_modified(request, size, prefix, filename):
26 target = os.path.join(LOGO_DIR, size, prefix, filename)
28 try:
29 return datetime.fromtimestamp(os.path.getmtime(target))
31 except OSError:
32 return None
35 class CoverArt(View):
37 @method_decorator(last_modified(_last_modified))
38 def get(self, request, size, prefix, filename):
40 size = int(size)
42 target = self.get_thumbnail(size, prefix, filename)
43 original = self.get_original(prefix, filename)
45 if os.path.exists(target):
46 return self.send_file(target)
48 if not os.path.exists(original):
49 raise Http404('Cover Art not available' + original)
51 target_dir = self.get_dir(target)
53 try:
54 im = Image.open(original)
55 if im.mode not in ('RGB', 'RGBA'):
56 im = im.convert('RGB')
57 except IOError:
58 raise Http404('Cannot open cover file')
60 try:
61 im.thumbnail((size, size), Image.ANTIALIAS)
62 resized = im
63 except (struct.error, IOError, IndexError) as ex:
64 # raised when trying to read an interlaced PNG;
65 logger.warn('Could not create thumbnail: %s', str(ex))
67 # we use the original instead
68 return self.send_file(original)
70 # If it's a RGBA image, composite it onto a white background for JPEG
71 if resized.mode == 'RGBA':
72 background = Image.new('RGB', resized.size)
73 draw = ImageDraw.Draw(background)
74 draw.rectangle((-1, -1, resized.size[0]+1, resized.size[1]+1),
75 fill=(255, 255, 255))
76 del draw
77 resized = Image.composite(resized, background, resized)
79 sio = io.BytesIO()
81 try:
82 resized.save(sio, 'JPEG', optimize=True, progression=True,
83 quality=80)
84 except IOError as ex:
85 return self.send_file(original)
87 s = sio.getvalue()
89 fp = open(target, 'wb')
90 fp.write(s)
91 fp.close()
93 return self.send_file(target)
95 # the length of the prefix is defined here and in web/urls.py
96 @staticmethod
97 def get_prefix(filename):
98 return filename[:3]
100 @staticmethod
101 def get_thumbnail(size, prefix, filename):
102 return os.path.join(LOGO_DIR, str(size), prefix, filename)
104 @staticmethod
105 def get_existing_thumbnails(prefix, filename):
106 files = glob(os.path.join(LOGO_DIR, '*', prefix, filename))
107 return [f for f in files if 'original' not in f]
109 @staticmethod
110 def get_original(prefix, filename):
111 return os.path.join(LOGO_DIR, 'original', prefix, filename)
113 @staticmethod
114 def get_dir(filename):
115 path = os.path.dirname(filename)
116 try:
117 os.makedirs(path)
119 except OSError as ose:
120 if ose.errno != errno.EEXIST:
121 raise
123 return path
125 def send_file(self, filename):
126 try:
127 f = open(filename, 'rb')
128 except IOError:
129 return HttpResponseNotFound()
131 resp = HttpResponse(content_type='image/jpeg')
132 resp.status_code = 200
133 resp.write(f.read())
134 return resp
137 def get_logo_url(podcast, size):
138 """ Return the logo URL for the podcast """
140 if podcast.logo_url:
141 filename = hashlib.sha1(podcast.logo_url.encode('utf-8')).hexdigest()
142 else:
143 filename = 'podcast-%d.png' % (hash(podcast.title) % 5, )
145 prefix = CoverArt.get_prefix(filename)
147 return reverse('logo', args=[size, prefix, filename])