1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011 MediaGoblin contributors. See AUTHORS.
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.
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.
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/>.
20 from mediagoblin
import mg_globals
as mgg
21 from mediagoblin
.processing
import BadMediaFail
, \
22 create_pub_filepath
, THUMB_SIZE
, MEDIUM_SIZE
23 from mediagoblin
.media_types
.image
.EXIF
import process_file
24 from mediagoblin
.tools
.translate
import pass_to_ugettext
as _
26 def process_image(entry
):
28 Code to process an image
30 workbench
= mgg
.workbench_manager
.create_workbench()
31 # Conversions subdirectory to avoid collisions
32 conversions_subdir
= os
.path
.join(
33 workbench
.dir, 'conversions')
34 os
.mkdir(conversions_subdir
)
36 queued_filepath
= entry
.queued_media_file
37 queued_filename
= workbench
.localized_file(
38 mgg
.queue_store
, queued_filepath
,
41 filename_bits
= os
.path
.splitext(queued_filename
)
42 basename
= os
.path
.split(filename_bits
[0])[1]
43 extension
= filename_bits
[1].lower()
46 exif_tags
= extract_exif(queued_filename
)
47 gps_data
= get_gps_data(exif_tags
)
50 thumb
= Image
.open(queued_filename
)
54 thumb
= exif_fix_image_orientation(thumb
, exif_tags
)
56 thumb
.thumbnail(THUMB_SIZE
, Image
.ANTIALIAS
)
58 # Copy the thumb to the conversion subdir, then remotely.
59 thumb_filename
= 'thumbnail' + extension
60 thumb_filepath
= create_pub_filepath(entry
, thumb_filename
)
61 tmp_thumb_filename
= os
.path
.join(
62 conversions_subdir
, thumb_filename
)
63 with
file(tmp_thumb_filename
, 'w') as thumb_file
:
64 thumb
.save(thumb_file
)
65 mgg
.public_store
.copy_local_to_storage(
66 tmp_thumb_filename
, thumb_filepath
)
68 # If the size of the original file exceeds the specified size of a `medium`
69 # file, a `medium.jpg` files is created and later associated with the media
71 medium
= Image
.open(queued_filename
)
73 medium
= exif_fix_image_orientation(medium
, exif_tags
)
75 if medium
.size
[0] > MEDIUM_SIZE
[0] or medium
.size
[1] > MEDIUM_SIZE
[1]:
76 medium
.thumbnail(MEDIUM_SIZE
, Image
.ANTIALIAS
)
78 medium_filename
= 'medium' + extension
79 medium_filepath
= create_pub_filepath(entry
, medium_filename
)
80 tmp_medium_filename
= os
.path
.join(
81 conversions_subdir
, medium_filename
)
83 with
file(tmp_medium_filename
, 'w') as medium_file
:
84 medium
.save(medium_file
)
86 mgg
.public_store
.copy_local_to_storage(
87 tmp_medium_filename
, medium_filepath
)
89 # we have to re-read because unlike PIL, not everything reads
90 # things in string representation :)
91 queued_file
= file(queued_filename
, 'rb')
94 #create_pub_filepath(entry, queued_filepath[-1])
95 original_filepath
= create_pub_filepath(entry
, basename
+ extension
)
97 with mgg
.public_store
.get_file(original_filepath
, 'wb') \
99 original_file
.write(queued_file
.read())
101 # Remove queued media file from storage and database
102 mgg
.queue_store
.delete_file(queued_filepath
)
103 entry
.queued_media_file
= []
105 # Insert media file information into database
106 media_files_dict
= entry
.setdefault('media_files', {})
107 media_files_dict
['thumb'] = thumb_filepath
108 media_files_dict
['original'] = original_filepath
109 media_files_dict
['medium'] = medium_filepath
111 # Insert exif data into database
112 media_data
= entry
.setdefault('media_data', {})
113 media_data
['exif'] = clean_exif(exif_tags
)
114 media_data
['gps'] = gps_data
117 workbench
.destroy_self()
119 def exif_fix_image_orientation(im
, exif_tags
):
121 Translate any EXIF orientation to raw orientation
124 - REDUCES IMAGE QUALITY by recompressig it
130 if 'Image Orientation' in exif_tags
:
135 orientation
= exif_tags
['Image Orientation'].values
[0]
136 if orientation
in rotation_map
.keys():
138 rotation_map
[orientation
])
142 def extract_exif(filename
):
144 Returns EXIF tags found in file at ``filename``
149 image
= open(filename
)
150 exif_tags
= process_file(image
)
152 BadMediaFail(_('Could not read the image file.'))
156 def clean_exif(exif
):
157 # Clean the result from anything the database cannot handle
159 # Discard any JPEG thumbnail, for database compatibility
160 # and that I cannot see a case when we would use it.
161 # It takes up some space too.
163 'Thumbnail JPEGInterchangeFormatLength',
165 'Thumbnail JPEGInterchangeFormat']
169 for key
, value
in exif
.items():
170 if not key
in disabled_tags
:
171 clean_exif
[key
] = str(value
)
176 def get_gps_data(exif
):
178 Processes EXIF data returned by EXIF.py
180 if not 'Image GPSInfo' in exif
:
187 'latitude': exif
['GPS GPSLatitude'],
188 'longitude': exif
['GPS GPSLongitude']}
190 for key
, dat
in dms_data
.items():
193 float(v
[0].num
) / float(v
[0].den
) \
194 + (float(v
[1].num
) / float(v
[1].den
) / 60 )\
195 + (float(v
[2].num
) / float(v
[2].den
) / (60 * 60))
201 gps_data
['direction'] = (
203 float(d
.num
) / float(d
.den
)
204 )(exif
['GPS GPSImgDirection'].values
[0])
209 gps_data
['altitude'] = (
211 float(a
.num
) / float(a
.den
)
212 )(exif
['GPS GPSAltitude'].values
[0])
219 if __name__
== '__main__':
223 pp
= pprint
.PrettyPrinter()
225 result
= extract_exif(sys
.argv
[1])
226 gps
= get_gps_data(result
)