[Web] fix shadowed variable name
[mygpo.git] / mygpo / web / logo.py
blobbc202d61b1c40689a0ffb99a4d6b570618aedff1
2 # This file is part of my.gpodder.org.
4 # my.gpodder.org is free software: you can redistribute it and/or modify it
5 # under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or (at your
7 # option) any later version.
9 # my.gpodder.org is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
12 # License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with my.gpodder.org. If not, see <http://www.gnu.org/licenses/>.
18 import os.path
19 import io
20 from datetime import datetime
21 from glob import glob
22 import errno
23 import hashlib
25 from PIL import Image, ImageDraw
27 from django.core.urlresolvers import reverse
28 from django.conf import settings
29 from django.http import Http404, HttpResponse, HttpResponseNotFound
30 from django.views.generic.base import View
31 from django.utils.decorators import method_decorator
32 from django.views.decorators.http import last_modified
34 import logging
35 logger = logging.getLogger(__name__)
37 LOGO_DIR = os.path.join(settings.BASE_DIR, '..', 'htdocs', 'media', 'logo')
40 def _last_modified(request, size, prefix, filename):
42 target = os.path.join(LOGO_DIR, size, prefix, filename)
44 try:
45 return datetime.fromtimestamp(os.path.getmtime(target))
47 except OSError:
48 return None
51 class CoverArt(View):
53 @method_decorator(last_modified(_last_modified))
54 def get(self, request, size, prefix, filename):
56 size = int(size)
58 target = self.get_thumbnail(size, prefix, filename)
59 original = self.get_original(prefix, filename)
61 if os.path.exists(target):
62 return self.send_file(target)
64 if not os.path.exists(original):
65 raise Http404('Cover Art not available' + original)
67 target_dir = self.get_dir(target)
69 try:
70 im = Image.open(original)
71 if im.mode not in ('RGB', 'RGBA'):
72 im = im.convert('RGB')
73 except IOError:
74 raise Http404('Cannot open cover file')
76 try:
77 im.thumbnail((size, size), Image.ANTIALIAS)
78 resized = im
79 except (IOError, IndexError) as ex:
80 # raised when trying to read an interlaced PNG;
81 logger.warn('Could not create thumbnail: %s', str(ex))
83 # we use the original instead
84 return self.send_file(original)
86 # If it's a RGBA image, composite it onto a white background for JPEG
87 if resized.mode == 'RGBA':
88 background = Image.new('RGB', resized.size)
89 draw = ImageDraw.Draw(background)
90 draw.rectangle((-1, -1, resized.size[0]+1, resized.size[1]+1),
91 fill=(255, 255, 255))
92 del draw
93 resized = Image.composite(resized, background, resized)
95 sio = io.StringIO()
97 try:
98 resized.save(sio, 'JPEG', optimize=True, progression=True,
99 quality=80)
100 except IOError as ex:
101 return self.send_file(original)
103 s = sio.getvalue()
105 fp = open(target, 'wb')
106 fp.write(s)
107 fp.close()
109 return self.send_file(target)
111 # the length of the prefix is defined here and in web/urls.py
112 @staticmethod
113 def get_prefix(filename):
114 return filename[:3]
116 @staticmethod
117 def get_thumbnail(size, prefix, filename):
118 return os.path.join(LOGO_DIR, str(size), prefix, filename)
120 @staticmethod
121 def get_existing_thumbnails(prefix, filename):
122 files = glob(os.path.join(LOGO_DIR, '*', prefix, filename))
123 return [f for f in files if 'original' not in f]
125 @staticmethod
126 def get_original(prefix, filename):
127 return os.path.join(LOGO_DIR, 'original', prefix, filename)
129 @staticmethod
130 def get_dir(filename):
131 path = os.path.dirname(filename)
132 try:
133 os.makedirs(path)
135 except OSError as ose:
136 if ose.errno != errno.EEXIST:
137 raise
139 return path
141 def send_file(self, filename):
142 try:
143 f = open(filename)
144 except IOError:
145 return HttpResponseNotFound()
147 resp = HttpResponse(content_type='image/jpeg')
148 resp.status_code = 200
149 resp.write(f.read())
150 return resp
153 def get_logo_url(podcast, size):
154 """ Return the logo URL for the podcast """
156 if podcast.logo_url:
157 filename = hashlib.sha1(podcast.logo_url.encode('utf-8')).hexdigest()
158 else:
159 filename = 'podcast-%d.png' % (hash(podcast.title) % 5, )
161 prefix = CoverArt.get_prefix(filename)
163 return reverse('logo', args=[size, prefix, filename])