40f52bbf256c306090de76ece034551ab4a36abd
[mygpo.git] / mygpo / web / logo.py
blob40f52bbf256c306090de76ece034551ab4a36abd
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 StringIO
20 from datetime import datetime
21 from glob import glob
22 import errno
24 import Image
25 import ImageDraw
27 from django.conf import settings
28 from django.http import Http404, HttpResponse
29 from django.views.generic.base import View
30 from django.utils.decorators import method_decorator
31 from django.views.decorators.http import last_modified
34 LOGO_DIR = os.path.join(settings.BASE_DIR, '..', 'htdocs', 'media', 'logo')
37 def _last_modified(request, size, prefix, filename):
39 target = os.path.join(LOGO_DIR, size, prefix, filename)
41 try:
42 return datetime.fromtimestamp(os.path.getmtime(target))
44 except OSError:
45 return None
48 class CoverArt(View):
50 @method_decorator(last_modified(_last_modified))
51 def get(self, request, size, prefix, filename):
53 size = int(size)
55 target = self.get_thumbnail(size, prefix, filename)
56 original = self.get_original(prefix, filename)
58 if os.path.exists(target):
59 return self.send_file(target)
61 if not os.path.exists(original):
62 raise Http404('Cover Art not available' + original)
64 target_dir = self.get_dir(target)
66 try:
67 im = Image.open(original)
68 if im.mode not in ('RGB', 'RGBA'):
69 im = im.convert('RGB')
70 except IOError:
71 raise Http404('Cannot open cover file')
73 try:
74 im.thumbnail((size, size), Image.ANTIALIAS)
75 resized = im
76 except IOError as ex:
77 print ex
78 # raised when trying to read an interlaced PNG;
79 # we use the original instead
80 return self.send_file(original)
82 # If it's a RGBA image, composite it onto a white background for JPEG
83 if resized.mode == 'RGBA':
84 background = Image.new('RGB', resized.size)
85 draw = ImageDraw.Draw(background)
86 draw.rectangle((-1, -1, resized.size[0]+1, resized.size[1]+1),
87 fill=(255, 255, 255))
88 del draw
89 resized = Image.composite(resized, background, resized)
91 io = StringIO.StringIO()
93 try:
94 resized.save(io, 'JPEG', optimize=True, progression=True,
95 quality=80)
96 except IOError as ex:
97 return self.send_file(original)
99 s = io.getvalue()
101 fp = open(target, 'wb')
102 fp.write(s)
103 fp.close()
105 return self.send_file(target)
107 # the length of the prefix is defined here and in web/urls.py
108 @staticmethod
109 def get_prefix(filename):
110 return filename[:3]
112 @staticmethod
113 def get_thumbnail(size, prefix, filename):
114 return os.path.join(LOGO_DIR, str(size), prefix, filename)
116 @staticmethod
117 def get_existing_thumbnails(prefix, filename):
118 files = glob(os.path.join(LOGO_DIR, '*', prefix, filename))
119 return filter(lambda f: 'original' not in f, files)
121 @staticmethod
122 def get_original(prefix, filename):
123 return os.path.join(LOGO_DIR, 'original', prefix, filename)
125 @staticmethod
126 def get_dir(filename):
127 path = os.path.dirname(filename)
128 try:
129 os.makedirs(path)
131 except OSError as ose:
132 if ose.errno != errno.EEXIST:
133 raise
135 return path
137 def send_file(self, filename):
138 resp = HttpResponse(content_type='image/jpeg')
139 resp.status_code = 200
140 f = open(filename)
141 resp.write(f.read())
142 return resp