1
|
|
|
""" |
2
|
|
|
The video module contains the :class:`Video` class, which represents video |
3
|
|
|
objects (AVI, MOV, etc.). |
4
|
|
|
|
5
|
|
|
.. moduleauthor:: Jaisen Mathai <[email protected]> |
6
|
|
|
""" |
7
|
|
|
from __future__ import print_function |
8
|
|
|
from __future__ import absolute_import |
9
|
|
|
from __future__ import division |
10
|
|
|
|
11
|
|
|
# load modules |
12
|
|
|
from datetime import datetime |
13
|
|
|
|
14
|
|
|
import os |
15
|
|
|
import re |
16
|
|
|
import time |
17
|
|
|
|
18
|
|
|
from .media import Media |
19
|
|
|
|
20
|
|
|
|
21
|
|
|
class Video(Media): |
22
|
|
|
|
23
|
|
|
"""A video object. |
24
|
|
|
|
25
|
|
|
:param str source: The fully qualified path to the video file. |
26
|
|
|
""" |
27
|
|
|
|
28
|
|
|
__name__ = 'Video' |
29
|
|
|
|
30
|
|
|
#: Valid extensions for video files. |
31
|
|
|
extensions = ('avi', 'm4v', 'mov', 'mp4', 'mpg', 'mpeg', '3gp', 'mts') |
32
|
|
|
|
33
|
|
|
def __init__(self, source=None): |
34
|
|
|
super(Video, self).__init__(source) |
35
|
|
|
self.exif_map['date_taken'] = [ |
36
|
|
|
'QuickTime:CreationDate', |
37
|
|
|
'QuickTime:CreateDate', |
38
|
|
|
'QuickTime:CreationDate-und-US', |
39
|
|
|
'QuickTime:MediaCreateDate', |
40
|
|
|
'H264:DateTimeOriginal' |
41
|
|
|
] |
42
|
|
|
self.title_key = 'XMP:DisplayName' |
43
|
|
|
self.latitude_keys = [ |
44
|
|
|
'XMP:GPSLatitude', |
45
|
|
|
# 'QuickTime:GPSLatitude', |
46
|
|
|
'Composite:GPSLatitude' |
47
|
|
|
] |
48
|
|
|
self.longitude_keys = [ |
49
|
|
|
'XMP:GPSLongitude', |
50
|
|
|
# 'QuickTime:GPSLongitude', |
51
|
|
|
'Composite:GPSLongitude' |
52
|
|
|
] |
53
|
|
|
self.latitude_ref_key = 'EXIF:GPSLatitudeRef' |
54
|
|
|
self.longitude_ref_key = 'EXIF:GPSLongitudeRef' |
55
|
|
|
self.set_gps_ref = False |
56
|
|
|
|
57
|
|
|
def get_date_taken(self): |
58
|
|
|
"""Get the date which the photo was taken. |
59
|
|
|
|
60
|
|
|
The date value returned is defined by the min() of mtime and ctime. |
61
|
|
|
|
62
|
|
|
:returns: time object or None for non-photo files or 0 timestamp |
63
|
|
|
""" |
64
|
|
|
if(not self.is_valid()): |
65
|
|
|
return None |
66
|
|
|
|
67
|
|
|
source = self.source |
68
|
|
|
seconds_since_epoch = min(os.path.getmtime(source), os.path.getctime(source)) # noqa |
69
|
|
|
|
70
|
|
|
exif = self.get_exiftool_attributes() |
71
|
|
|
for date_key in self.exif_map['date_taken']: |
72
|
|
|
if date_key in exif: |
73
|
|
|
# Example date strings we want to parse |
74
|
|
|
# 2015:01:19 12:45:11-08:00 |
75
|
|
|
# 2013:09:30 07:06:05 |
76
|
|
|
date = re.search('([0-9: ]+)([-+][0-9:]+)?', exif[date_key]) |
77
|
|
|
if(date is not None): |
78
|
|
|
date_string = date.group(1) |
79
|
|
|
date_offset = date.group(2) |
80
|
|
|
try: |
81
|
|
|
exif_seconds_since_epoch = time.mktime( |
82
|
|
|
datetime.strptime( |
83
|
|
|
date_string, |
84
|
|
|
'%Y:%m:%d %H:%M:%S' |
85
|
|
|
).timetuple() |
86
|
|
|
) |
87
|
|
|
if(exif_seconds_since_epoch < seconds_since_epoch): |
88
|
|
|
seconds_since_epoch = exif_seconds_since_epoch |
89
|
|
|
if date_offset is not None: |
90
|
|
|
offset_parts = date_offset[1:].split(':') |
91
|
|
|
offset_seconds = int(offset_parts[0]) * 3600 |
92
|
|
|
offset_seconds = offset_seconds + int(offset_parts[1]) * 60 # noqa |
93
|
|
|
if date_offset[0] == '-': |
94
|
|
|
seconds_since_epoch - offset_seconds |
95
|
|
|
elif date_offset[0] == '+': |
96
|
|
|
seconds_since_epoch + offset_seconds |
97
|
|
|
except: |
98
|
|
|
pass |
99
|
|
|
|
100
|
|
|
if(seconds_since_epoch == 0): |
101
|
|
|
return None |
102
|
|
|
|
103
|
|
|
return time.gmtime(seconds_since_epoch) |
104
|
|
|
|