Completed
Push8eaba9...3fc43c
passed — Build
created

Photo.get_date_taken()   D

↳ Parent: Photo

Complexity

Conditions 8

Duplication

Lines 0
Ratio 0 %

Size

Total Lines 43

Importance

Changes 3
Bugs 0 Features 1
Metric Value
cc 8
c 3
b 0
f 1
dl 0
loc 43
rs 4
1
"""
2
The photo module contains the :class:`Photo` class, which is used to track
3
image objects (JPG, DNG, etc.).
4
5
.. moduleauthor:: Jaisen Mathai <[email protected]>
6
"""
7
from __future__ import print_function
8
from __future__ import absolute_import
9
10
import imghdr
11
import os
12
import re
13
import time
14
from datetime import datetime
15
from re import compile
16
17
18
from elodie import log
19
from .media import Media
20
21
22
class Photo(Media):
23
24
    """A photo object.
25
26
    :param str source: The fully qualified path to the photo file
27
    """
28
29
    __name__ = 'Photo'
30
31
    #: Valid extensions for photo files.
32
    extensions = ('arw', 'cr2', 'dng', 'gif', 'jpeg', 'jpg', 'nef', 'rw2')
33
34
    def __init__(self, source=None):
35
        super(Photo, self).__init__(source)
36
37
        # We only want to parse EXIF once so we store it here
38
        self.exif = None
39
40
    def get_date_taken(self):
41
        """Get the date which the photo was taken.
42
43
        The date value returned is defined by the min() of mtime and ctime.
44
45
        :returns: time object or None for non-photo files or 0 timestamp
46
        """
47
        if(not self.is_valid()):
48
            return None
49
50
        source = self.source
51
        seconds_since_epoch = min(os.path.getmtime(source), os.path.getctime(source))  # noqa
52
53
        exif = self.get_exiftool_attributes()
54
        if not exif:
55
            return seconds_since_epoch
56
57
        # We need to parse a string from EXIF into a timestamp.
58
        # EXIF DateTimeOriginal and EXIF DateTime are both stored
59
        #   in %Y:%m:%d %H:%M:%S format
60
        # we split on a space and then r':|-' -> convert to int -> .timetuple()
61
        #   the conversion in the local timezone
62
        # EXIF DateTime is already stored as a timestamp
63
        # Sourced from https://github.com/photo/frontend/blob/master/src/libraries/models/Photo.php#L500  # noqa
64
        for key in self.exif_map['date_taken']:
65
            try:
66
                if(key in exif):
67
                    if(re.match('\d{4}(-|:)\d{2}(-|:)\d{2}', exif[key]) is not None):  # noqa
68
                        dt, tm = exif[key].split(' ')
69
                        dt_list = compile(r'-|:').split(dt)
70
                        dt_list = dt_list + compile(r'-|:').split(tm)
71
                        dt_list = map(int, dt_list)
72
                        time_tuple = datetime(*dt_list).timetuple()
73
                        seconds_since_epoch = time.mktime(time_tuple)
74
                        break
75
            except BaseException as e:
76
                log.error(e)
77
                pass
78
79
        if(seconds_since_epoch == 0):
80
            return None
81
82
        return time.gmtime(seconds_since_epoch)
83
84
    def is_valid(self):
85
        """Check the file extension against valid file extensions.
86
87
        The list of valid file extensions come from self.extensions. This
88
        also checks whether the file is an image.
89
90
        :returns: bool
91
        """
92
        source = self.source
93
94
        # gh-4 This checks if the source file is an image.
95
        # It doesn't validate against the list of supported types.
96
        if(imghdr.what(source) is None):
97
            return False
98
99
        return os.path.splitext(source)[1][1:].lower() in self.extensions
100