Commit | Line | Data |
---|---|---|
93bdab9d | 1 | # GNU MediaGoblin -- federated, autonomous media hosting |
cf29e8a8 | 2 | # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. |
93bdab9d JW |
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 | ||
17 | import Image | |
8e5f9746 | 18 | import os |
92f129b5 | 19 | import logging |
93bdab9d | 20 | |
93bdab9d | 21 | from mediagoblin import mg_globals as mgg |
45ab3e07 | 22 | from mediagoblin.decorators import get_workbench |
4535f759 | 23 | from mediagoblin.processing import BadMediaFail, \ |
deea3f66 | 24 | create_pub_filepath, FilenameBuilder |
a180ca26 | 25 | from mediagoblin.tools.exif import exif_fix_image_orientation, \ |
0f8221dc B |
26 | extract_exif, clean_exif, get_gps_data, get_useful, \ |
27 | exif_image_needs_rotation | |
93bdab9d | 28 | |
92f129b5 JW |
29 | _log = logging.getLogger(__name__) |
30 | ||
deea3f66 | 31 | |
ab35ad46 BS |
32 | def resize_image(entry, filename, new_path, exif_tags, workdir, new_size, |
33 | size_limits=(0, 0)): | |
c72d661b JW |
34 | """ |
35 | Store a resized version of an image and return its pathname. | |
063670e9 BS |
36 | |
37 | Arguments: | |
38 | entry -- the entry for the image to resize | |
39 | filename -- the filename of the original image being resized | |
ab35ad46 | 40 | new_path -- public file path for the new resized image |
063670e9 BS |
41 | exif_tags -- EXIF data for the original image |
42 | workdir -- directory path for storing converted image files | |
43 | new_size -- 2-tuple size for the resized image | |
063670e9 BS |
44 | """ |
45 | try: | |
46 | resized = Image.open(filename) | |
47 | except IOError: | |
48 | raise BadMediaFail() | |
49 | resized = exif_fix_image_orientation(resized, exif_tags) # Fix orientation | |
deea3f66 | 50 | resized.thumbnail(new_size, Image.ANTIALIAS) |
063670e9 | 51 | |
063670e9 | 52 | # Copy the new file to the conversion subdir, then remotely. |
ab35ad46 | 53 | tmp_resized_filename = os.path.join(workdir, new_path[-1]) |
063670e9 BS |
54 | with file(tmp_resized_filename, 'w') as resized_file: |
55 | resized.save(resized_file) | |
ab35ad46 | 56 | mgg.public_store.copy_local_to_storage(tmp_resized_filename, new_path) |
063670e9 | 57 | |
deea3f66 | 58 | |
92f129b5 JW |
59 | SUPPORTED_FILETYPES = ['png', 'gif', 'jpg', 'jpeg'] |
60 | ||
c56d4b55 | 61 | |
ec4261a4 | 62 | def sniff_handler(media_file, **kw): |
e2caf574 | 63 | if kw.get('media') is not None: # That's a double negative! |
92f129b5 JW |
64 | name, ext = os.path.splitext(kw['media'].filename) |
65 | clean_ext = ext[1:].lower() # Strip the . from ext and make lowercase | |
66 | ||
92f129b5 JW |
67 | if clean_ext in SUPPORTED_FILETYPES: |
68 | _log.info('Found file extension in supported filetypes') | |
69 | return True | |
70 | else: | |
10085b77 | 71 | _log.debug('Media present, extension not found in {0}'.format( |
92f129b5 JW |
72 | SUPPORTED_FILETYPES)) |
73 | else: | |
74 | _log.warning('Need additional information (keyword argument \'media\')' | |
75 | ' to be able to handle sniffing') | |
76 | ||
ec4261a4 JW |
77 | return False |
78 | ||
c56d4b55 | 79 | |
45ab3e07 SS |
80 | @get_workbench |
81 | def process_image(entry, workbench=None): | |
82 | """Code to process an image. Will be run by celery. | |
83 | ||
84 | A Workbench() represents a local tempory dir. It is automatically | |
85 | cleaned up when this function exits. | |
93bdab9d | 86 | """ |
8e5f9746 JW |
87 | # Conversions subdirectory to avoid collisions |
88 | conversions_subdir = os.path.join( | |
89 | workbench.dir, 'conversions') | |
90 | os.mkdir(conversions_subdir) | |
8545cfc9 | 91 | queued_filepath = entry.queued_media_file |
93bdab9d JW |
92 | queued_filename = workbench.localized_file( |
93 | mgg.queue_store, queued_filepath, | |
94 | 'source') | |
28f364bd | 95 | name_builder = FilenameBuilder(queued_filename) |
8e5f9746 | 96 | |
e8e444a8 JW |
97 | # EXIF extraction |
98 | exif_tags = extract_exif(queued_filename) | |
99 | gps_data = get_gps_data(exif_tags) | |
100 | ||
063670e9 | 101 | # Always create a small thumbnail |
ab35ad46 | 102 | thumb_filepath = create_pub_filepath( |
28f364bd | 103 | entry, name_builder.fill('{basename}.thumbnail{ext}')) |
ab35ad46 | 104 | resize_image(entry, queued_filename, thumb_filepath, |
deea3f66 JW |
105 | exif_tags, conversions_subdir, |
106 | (mgg.global_config['media:thumb']['max_width'], | |
107 | mgg.global_config['media:thumb']['max_height'])) | |
93bdab9d JW |
108 | |
109 | # If the size of the original file exceeds the specified size of a `medium` | |
063670e9 | 110 | # file, a `.medium.jpg` files is created and later associated with the media |
93bdab9d JW |
111 | # entry. |
112 | medium = Image.open(queued_filename) | |
c56d4b55 | 113 | if medium.size[0] > mgg.global_config['media:medium']['max_width'] \ |
deea3f66 JW |
114 | or medium.size[1] > mgg.global_config['media:medium']['max_height'] \ |
115 | or exif_image_needs_rotation(exif_tags): | |
c0516158 CAW |
116 | medium_filepath = create_pub_filepath( |
117 | entry, name_builder.fill('{basename}.medium{ext}')) | |
118 | resize_image( | |
119 | entry, queued_filename, medium_filepath, | |
deea3f66 | 120 | exif_tags, conversions_subdir, |
c56d4b55 | 121 | (mgg.global_config['media:medium']['max_width'], |
deea3f66 | 122 | mgg.global_config['media:medium']['max_height'])) |
0f8221dc B |
123 | else: |
124 | medium_filepath = None | |
93bdab9d | 125 | |
5018a355 SS |
126 | # Copy our queued local workbench to its final destination |
127 | original_filepath = create_pub_filepath( | |
deea3f66 | 128 | entry, name_builder.fill('{basename}{ext}')) |
5018a355 | 129 | mgg.public_store.copy_local_to_storage(queued_filename, original_filepath) |
93bdab9d | 130 | |
e8e444a8 | 131 | # Remove queued media file from storage and database |
93bdab9d | 132 | mgg.queue_store.delete_file(queued_filepath) |
8545cfc9 | 133 | entry.queued_media_file = [] |
e8e444a8 JW |
134 | |
135 | # Insert media file information into database | |
93bdab9d | 136 | media_files_dict = entry.setdefault('media_files', {}) |
5bd0adeb BS |
137 | media_files_dict[u'thumb'] = thumb_filepath |
138 | media_files_dict[u'original'] = original_filepath | |
0f8221dc | 139 | if medium_filepath: |
5bd0adeb | 140 | media_files_dict[u'medium'] = medium_filepath |
e8e444a8 JW |
141 | |
142 | # Insert exif data into database | |
763ef5b7 | 143 | exif_all = clean_exif(exif_tags) |
763ef5b7 E |
144 | |
145 | if len(exif_all): | |
146 | entry.media_data_init(exif_all=exif_all) | |
ea200c32 E |
147 | |
148 | if len(gps_data): | |
149 | for key in list(gps_data.keys()): | |
150 | gps_data['gps_' + key] = gps_data.pop(key) | |
151 | entry.media_data_init(**gps_data) | |
93bdab9d | 152 | |
e8e444a8 | 153 | |
e8e444a8 JW |
154 | if __name__ == '__main__': |
155 | import sys | |
156 | import pprint | |
157 | ||
158 | pp = pprint.PrettyPrinter() | |
159 | ||
160 | result = extract_exif(sys.argv[1]) | |
161 | gps = get_gps_data(result) | |
a180ca26 JW |
162 | clean = clean_exif(result) |
163 | useful = get_useful(clean) | |
e8e444a8 | 164 | |
e8e444a8 | 165 | print pp.pprint( |
a180ca26 | 166 | clean) |