use setuptools instead since it can use MANIFEST.in; also remove silly VERSION discov...
[mediagoblin-stock:mediagoblin-hidden_original.git] / mediagoblin_hidden_original / __init__.py
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2014 MediaGoblin contributors.  See AUTHORS.
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it 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
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 from __future__ import unicode_literals
17
18 try:
19     from PIL import Image
20 except ImportError:
21     import Image
22
23 import logging
24 from mediagoblin import mg_globals as mgg
25 from mediagoblin.processing import (BadMediaFail, store_public)
26 import uuid
27
28 PIL_FILTERS = {
29     'NEAREST': Image.NEAREST,
30     'BILINEAR': Image.BILINEAR,
31     'BICUBIC': Image.BICUBIC,
32     'ANTIALIAS': Image.ANTIALIAS}
33 # TODO: want to say this instead:
34 #from mediagoblin.media_types.image import PIL_FILTERS
35
36 __VERSION__ = '0.1.3+' # releases get numbers, post release a "+" appended
37 _log = logging.getLogger(__name__)
38
39 def setup_plugin():
40     _log.info('Hidden Original plugin set up!')
41
42 def stash_original(processor, quality=None, filter=None):
43     """This function creates a secret filename for storing the original
44     media file and saves that path with the key "hidden_original",
45     then downsizes the file (processor.process_filename) that
46     eventually gets stored as "original".
47
48     The `processor' argument is one of the CommonImageProcessors of
49     media_types.image
50
51     Note that since we downsize to the medium size, a separate
52     'medium' entry will not be created in the regular image process
53     function ('medium' versions are only created if the original is
54     larger than the medium size).
55
56     """
57     if u"hidden_original" in processor.entry.media_files \
58        and mgg.public_store.file_exists(
59            processor.entry.media_files[u"hidden_original"]):
60         # This should happen if we're called from Resizer instead of
61         # InitialProcessor
62         return
63
64     # Stash away the actual original under the key "hidden_original".
65     secret_filename = "%s-%s" % (uuid.uuid4(),
66                                  processor.name_builder.fill('{basename}{ext}'))
67     # uuid4() as in get_unique_filepath, good enough for our purposes.
68     store_public(processor.entry,
69                  u"hidden_original",
70                  processor.process_filename,
71                  secret_filename)
72
73     # Downsize the faux original to the "medium" size:
74     new_size = (mgg.global_config['media:' + 'medium']['max_width'],
75                 mgg.global_config['media:' + 'medium']['max_height'])
76
77     # TODO: The following overlaps a bit with generate_thumb,
78     # resize_image and resize_tool, but those functions do way too
79     # much; e.g. storing metadata and the file itself.
80     if not quality:
81         quality = processor.image_config['quality']
82     if not filter:
83         filter = processor.image_config['resize_filter']
84     try:
85         im = Image.open(processor.process_filename)
86     except IOError:
87         raise BadMediaFail()
88     if im.size[0] > new_size[0] or im.size[1] > new_size[1]:
89         try:
90             resize_filter = PIL_FILTERS[filter.upper()]
91         except KeyError:
92             raise Exception('Filter "{0}" not found, choose one of {1}'.format(
93                 unicode(filter),
94                 u', '.join(PIL_FILTERS.keys())))
95
96         im.thumbnail(new_size, resize_filter)
97
98         # Overwrite the process_filename file with our downsized version:
99         with file(processor.process_filename, 'w') as resized_original:
100             im.save(resized_original, quality=quality)
101         # This will later get used as the "original" in copy_original
102
103     # else:
104     #     A small file, just keep the original as is.
105
106
107 hooks = {
108     'setup': setup_plugin,
109     'imageprocessor_preprocess': stash_original
110     }