Completed
Push — master ( 362b1b...4cd91e )
by Jaisen
26s
created

Media.get_camera_make()   B

Complexity

Conditions 5

Duplication

Lines 0
Ratio 0 %

Size

Total Lines 18

Importance

Changes 0
Metric Value
cc 5
dl 0
loc 18
rs 8.5454
c 0
b 0
f 0
1
"""
2
The media module provides a base :class:`Media` class for media objects that
3
are tracked by Elodie. The Media class provides some base functionality used
4
by all the media types, but isn't itself used to represent anything. Its
5
sub-classes (:class:`~elodie.media.audio.Audio`,
6
:class:`~elodie.media.photo.Photo`, and :class:`~elodie.media.video.Video`)
7
are used to represent the actual files.
8
9
.. moduleauthor:: Jaisen Mathai <[email protected]>
10
"""
11
from __future__ import print_function
12
13
import os
14
15
# load modules
16
from elodie import constants
17
from elodie.dependencies import get_exiftool
18
from elodie.external.pyexiftool import ExifTool
19
from elodie.media.base import Base
20
21
22
class Media(Base):
23
24
    """The base class for all media objects.
25
26
    :param str source: The fully qualified path to the video file.
27
    """
28
29
    __name__ = 'Media'
30
31
    d_coordinates = {
32
        'latitude': 'latitude_ref',
33
        'longitude': 'longitude_ref'
34
    }
35
36
    def __init__(self, source=None):
37
        super(Media, self).__init__(source)
38
        self.exif_map = {
39
            'date_taken': [
40
                'EXIF:DateTimeOriginal',
41
                'EXIF:CreateDate',
42
                'EXIF:ModifyDate'
43
            ]
44
        }
45
        self.camera_make_keys = ['EXIF:Make', 'QuickTime:Make']
46
        self.camera_model_keys = ['EXIF:Model', 'QuickTime:Model']
47
        self.album_keys = ['XMP-xmpDM:Album', 'XMP:Album']
48
        self.title_key = 'XMP:Title'
49
        self.latitude_keys = ['EXIF:GPSLatitude']
50
        self.longitude_keys = ['EXIF:GPSLongitude']
51
        self.latitude_ref_key = 'EXIF:GPSLatitudeRef'
52
        self.longitude_ref_key = 'EXIF:GPSLongitudeRef'
53
        self.original_name_key = 'XMP:OriginalFileName'
54
        self.set_gps_ref = True
55
        self.exiftool_addedargs = [
56
            '-overwrite_original',
57
            u'-config',
58
            u'"{}"'.format(constants.exiftool_config)
59
        ]
60
61
    def get_album(self):
62
        """Get album from EXIF
63
64
        :returns: None or string
65
        """
66
        if(not self.is_valid()):
67
            return None
68
69
        exiftool_attributes = self.get_exiftool_attributes()
70
        if exiftool_attributes is None:
71
            return None
72
73
        for album_key in self.album_keys:
74
            if album_key in exiftool_attributes:
75
                return exiftool_attributes[album_key]
76
77
        return None
78
79
    def get_coordinate(self, type='latitude'):
80
        """Get latitude or longitude of media from EXIF
81
82
        :param str type: Type of coordinate to get. Either "latitude" or
83
            "longitude".
84
        :returns: float or None if not present in EXIF or a non-photo file
85
        """
86
87
        exif = self.get_exiftool_attributes()
88
        if not exif:
89
            return None
90
91
        # The lat/lon _keys array has an order of precedence.
92
        # The first key is writable and we will give the writable
93
        #   key precence when reading.
94
        direction_multiplier = 1.0
95
        for key in self.latitude_keys + self.longitude_keys:
96
            if key not in exif:
97
                continue
98
99
            # Cast coordinate to a float due to a bug in exiftool's
100
            #   -json output format.
101
            # https://github.com/jmathai/elodie/issues/171
102
            # http://u88.n24.queensu.ca/exiftool/forum/index.php/topic,7952.0.html  # noqa
103
            this_coordinate = float(exif[key])
104
105
            # TODO: verify that we need to check ref key
106
            #   when self.set_gps_ref != True
107
            if type == 'latitude' and key in self.latitude_keys:
108
                if self.latitude_ref_key in exif and \
109
                        exif[self.latitude_ref_key] == 'S':
110
                    direction_multiplier = -1.0
111
                return this_coordinate * direction_multiplier
112
            elif type == 'longitude' and key in self.longitude_keys:
113
                if self.longitude_ref_key in exif and \
114
                        exif[self.longitude_ref_key] == 'W':
115
                    direction_multiplier = -1.0
116
                return this_coordinate * direction_multiplier
117
118
        return None
119
120
    def get_exiftool_attributes(self):
121
        """Get attributes for the media object from exiftool.
122
123
        :returns: dict, or False if exiftool was not available.
124
        """
125
        source = self.source
126
        exiftool = get_exiftool()
127
        if(exiftool is None):
128
            return False
129
130
        with ExifTool(addedargs=self.exiftool_addedargs) as et:
131
            metadata = et.get_metadata(source)
132
            if not metadata:
133
                return False
134
135
        return metadata
136
137
    def get_camera_make(self):
138
        """Get the camera make stored in EXIF.
139
140
        :returns: str
141
        """
142
        if(not self.is_valid()):
143
            return None
144
145
        exiftool_attributes = self.get_exiftool_attributes()
146
147
        if exiftool_attributes is None:
148
            return None
149
150
        for camera_make_key in self.camera_make_keys:
151
            if camera_make_key in exiftool_attributes:
152
                return exiftool_attributes[camera_make_key]
153
154
        return None
155
156
    def get_camera_model(self):
157
        """Get the camera make stored in EXIF.
158
159
        :returns: str
160
        """
161
        if(not self.is_valid()):
162
            return None
163
164
        exiftool_attributes = self.get_exiftool_attributes()
165
166
        if exiftool_attributes is None:
167
            return None
168
169
        for camera_model_key in self.camera_model_keys:
170
            if camera_model_key in exiftool_attributes:
171
                return exiftool_attributes[camera_model_key]
172
173
        return None
174
175
    def get_original_name(self):
176
        """Get the original name stored in EXIF.
177
178
        :returns: str
179
        """
180
        if(not self.is_valid()):
181
            return None
182
183
        exiftool_attributes = self.get_exiftool_attributes()
184
185
        if exiftool_attributes is None:
186
            return None
187
188
        if(self.original_name_key not in exiftool_attributes):
189
            return None
190
191
        return exiftool_attributes[self.original_name_key]
192
193
    def get_title(self):
194
        """Get the title for a photo of video
195
196
        :returns: str or None if no title is set or not a valid media type
197
        """
198
        if(not self.is_valid()):
199
            return None
200
201
        exiftool_attributes = self.get_exiftool_attributes()
202
203
        if exiftool_attributes is None:
204
            return None
205
206
        if(self.title_key not in exiftool_attributes):
207
            return None
208
209
        return exiftool_attributes[self.title_key]
210
211
    def reset_cache(self):
212
        """Resets any internal cache
213
        """
214
        self.exiftool_attributes = None
215
        super(Media, self).reset_cache()
216
217
    def set_album(self, album):
218
        """Set album for a photo
219
220
        :param str name: Name of album
221
        :returns: bool
222
        """
223
        if(not self.is_valid()):
224
            return None
225
226
        tags = {self.album_keys[0]: album}
227
        status = self.__set_tags(tags)
228
        self.reset_cache()
229
230
        return status
231
232
    def set_date_taken(self, time):
233
        """Set the date/time a photo was taken.
234
235
        :param datetime time: datetime object of when the photo was taken
236
        :returns: bool
237
        """
238
        if(time is None):
239
            return False
240
241
        tags = {}
242
        formatted_time = time.strftime('%Y:%m:%d %H:%M:%S')
243
        for key in self.exif_map['date_taken']:
244
            tags[key] = formatted_time
245
246
        status = self.__set_tags(tags)
247
        self.reset_cache()
248
        return status
249
250
    def set_location(self, latitude, longitude):
251
        if(not self.is_valid()):
252
            return None
253
254
        # The lat/lon _keys array has an order of precedence.
255
        # The first key is writable and we will give the writable
256
        #   key precence when reading.
257
        tags = {
258
            self.latitude_keys[0]: latitude,
259
            self.longitude_keys[0]: longitude,
260
        }
261
262
        # If self.set_gps_ref == True then it means we are writing an EXIF
263
        #   GPS tag which requires us to set the reference key.
264
        # That's because the lat/lon are absolute values.
265
        if self.set_gps_ref:
266
            if latitude < 0:
267
                tags[self.latitude_ref_key] = 'S'
268
269
            if longitude < 0:
270
                tags[self.longitude_ref_key] = 'W'
271
272
        status = self.__set_tags(tags)
273
        self.reset_cache()
274
275
        return status
276
277
    def set_original_name(self, name=None):
278
        """Sets the original name EXIF tag if not already set.
279
280
        :returns: True, False, None
281
        """
282
        if(not self.is_valid()):
283
            return None
284
285
        # If EXIF original name tag is set then we return.
286
        if self.get_original_name() is not None:
287
            return None
288
289
        source = self.source
290
291
        if not name:
292
            name = os.path.basename(source)
293
294
        tags = {self.original_name_key: name}
295
        status = self.__set_tags(tags)
296
        self.reset_cache()
297
        return status
298
299
    def set_title(self, title):
300
        """Set title for a photo.
301
302
        :param str title: Title of the photo.
303
        :returns: bool
304
        """
305
        if(not self.is_valid()):
306
            return None
307
308
        if(title is None):
309
            return None
310
311
        tags = {self.title_key: title}
312
        status = self.__set_tags(tags)
313
        self.reset_cache()
314
315
        return status
316
317
    def __set_tags(self, tags):
318
        if(not self.is_valid()):
319
            return None
320
321
        source = self.source
322
323
        status = ''
324
        with ExifTool(addedargs=self.exiftool_addedargs) as et:
325
            status = et.set_tags(tags, source)
326
327
        return status != ''
328