2015-10-05 08:36:06 +02:00
|
|
|
"""
|
2016-01-08 23:49:06 +01:00
|
|
|
The video module contains the :class:`Video` class, which represents video
|
|
|
|
objects (AVI, MOV, etc.).
|
|
|
|
|
|
|
|
.. moduleauthor:: Jaisen Mathai <jaisen@jmathai.com>
|
2015-10-05 08:36:06 +02:00
|
|
|
"""
|
2015-10-02 09:20:27 +02:00
|
|
|
|
2015-10-05 08:36:06 +02:00
|
|
|
# load modules
|
2015-10-12 09:37:57 +02:00
|
|
|
from distutils.spawn import find_executable
|
2015-10-20 10:17:09 +02:00
|
|
|
import tempfile
|
2015-10-12 09:37:57 +02:00
|
|
|
from datetime import datetime
|
|
|
|
|
2015-10-02 09:20:27 +02:00
|
|
|
import os
|
|
|
|
import re
|
2015-10-20 10:17:09 +02:00
|
|
|
import shutil
|
2015-10-02 09:20:27 +02:00
|
|
|
import subprocess
|
|
|
|
import time
|
|
|
|
|
2015-10-28 08:19:21 +01:00
|
|
|
from elodie import constants
|
2015-10-22 03:40:50 +02:00
|
|
|
from elodie import plist_parser
|
2016-01-08 01:45:55 +01:00
|
|
|
from elodie.dependencies import get_exiftool
|
2016-04-07 10:08:33 +02:00
|
|
|
from media import Base
|
2015-10-20 10:17:09 +02:00
|
|
|
from media import Media
|
2015-10-02 09:20:27 +02:00
|
|
|
|
2016-01-02 08:23:06 +01:00
|
|
|
|
2015-10-07 08:47:51 +02:00
|
|
|
class Video(Media):
|
2016-01-08 23:49:06 +01:00
|
|
|
|
|
|
|
"""A video object.
|
|
|
|
|
|
|
|
:param str source: The fully qualified path to the video file.
|
|
|
|
"""
|
|
|
|
|
2015-11-19 11:31:32 +01:00
|
|
|
__name__ = 'Video'
|
2016-01-08 23:49:06 +01:00
|
|
|
|
|
|
|
#: Valid extensions for video files.
|
2016-02-14 18:41:13 +01:00
|
|
|
extensions = ('avi', 'm4v', 'mov', 'mp4', 'mpg', 'mpeg', '3gp')
|
2015-10-05 08:10:46 +02:00
|
|
|
|
2015-10-02 09:20:27 +02:00
|
|
|
def __init__(self, source=None):
|
2015-10-07 08:47:51 +02:00
|
|
|
super(Video, self).__init__(source)
|
2016-06-21 20:19:40 +02:00
|
|
|
self.exif_map['date_taken'] = [
|
|
|
|
'QuickTime:CreationDate',
|
|
|
|
'QuickTime:CreationDate-und-US',
|
|
|
|
'QuickTime:MediaCreateDate'
|
|
|
|
]
|
|
|
|
self.title_key = 'XMP:DisplayName'
|
|
|
|
self.latitude_keys = [
|
|
|
|
'XMP:GPSLatitude',
|
|
|
|
#'QuickTime:GPSLatitude',
|
|
|
|
'Composite:GPSLatitude'
|
|
|
|
]
|
|
|
|
self.longitude_keys = [
|
|
|
|
'XMP:GPSLongitude',
|
|
|
|
#'QuickTime:GPSLongitude',
|
|
|
|
'Composite:GPSLongitude'
|
|
|
|
]
|
|
|
|
self.latitude_ref_key = 'EXIF:GPSLatitudeRef'
|
|
|
|
self.longitude_ref_key = 'EXIF:GPSLongitudeRef'
|
|
|
|
self.set_gps_ref = False
|
2015-10-02 09:20:27 +02:00
|
|
|
|
2015-11-02 11:11:53 +01:00
|
|
|
def get_avmetareadwrite(self):
|
2016-01-08 23:49:06 +01:00
|
|
|
"""Get path to executable avmetareadwrite binary.
|
|
|
|
|
|
|
|
We wrap this since we call it in a few places and we do a fallback.
|
|
|
|
|
|
|
|
:returns: None or string
|
|
|
|
"""
|
2015-11-02 11:11:53 +01:00
|
|
|
avmetareadwrite = find_executable('avmetareadwrite')
|
|
|
|
if(avmetareadwrite is None):
|
|
|
|
avmetareadwrite = '/usr/bin/avmetareadwrite'
|
2016-01-02 08:23:06 +01:00
|
|
|
if(not os.path.isfile(avmetareadwrite) or not os.access(avmetareadwrite, os.X_OK)): # noqa
|
2015-11-02 11:11:53 +01:00
|
|
|
return None
|
|
|
|
|
|
|
|
return avmetareadwrite
|
|
|
|
|
2015-10-20 10:17:09 +02:00
|
|
|
def get_date_taken(self):
|
2016-06-21 20:19:40 +02:00
|
|
|
"""Get the date which the photo was taken.
|
2016-01-08 23:49:06 +01:00
|
|
|
|
|
|
|
The date value returned is defined by the min() of mtime and ctime.
|
|
|
|
|
2016-06-21 20:19:40 +02:00
|
|
|
:returns: time object or None for non-photo files or 0 timestamp
|
2016-01-08 23:49:06 +01:00
|
|
|
"""
|
2015-10-20 10:17:09 +02:00
|
|
|
if(not self.is_valid()):
|
|
|
|
return None
|
2016-06-21 20:19:40 +02:00
|
|
|
|
2015-10-20 10:17:09 +02:00
|
|
|
source = self.source
|
2016-01-02 08:23:06 +01:00
|
|
|
seconds_since_epoch = min(os.path.getmtime(source), os.path.getctime(source)) # noqa
|
2016-06-21 20:19:40 +02:00
|
|
|
|
|
|
|
exif = self.get_exiftool_attributes()
|
|
|
|
for date_key in self.exif_map['date_taken']:
|
|
|
|
if date_key in exif:
|
|
|
|
# Example date strings we want to parse
|
|
|
|
# 2015:01:19 12:45:11-08:00
|
|
|
|
# 2013:09:30 07:06:05
|
|
|
|
date = re.search('([0-9: ]+)([-+][0-9:]+)?', exif[date_key])
|
|
|
|
if(date is not None):
|
|
|
|
date_string = date.group(1)
|
|
|
|
date_offset = date.group(2)
|
|
|
|
try:
|
|
|
|
exif_seconds_since_epoch = time.mktime(
|
|
|
|
datetime.strptime(
|
|
|
|
date_string,
|
|
|
|
'%Y:%m:%d %H:%M:%S'
|
|
|
|
).timetuple()
|
|
|
|
)
|
|
|
|
if(exif_seconds_since_epoch < seconds_since_epoch):
|
|
|
|
seconds_since_epoch = exif_seconds_since_epoch
|
|
|
|
if date_offset is not None:
|
|
|
|
offset_parts = date_offset[1:].split(':')
|
|
|
|
offset_seconds = int(offset_parts[0]) * 3600
|
|
|
|
offset_seconds = offset_seconds + int(offset_parts[1]) * 60 #noqa
|
|
|
|
if date_offset[0] == '-':
|
|
|
|
seconds_since_epoch - offset_seconds
|
|
|
|
elif date_offset[0] == '+':
|
|
|
|
seconds_since_epoch + offset_seconds
|
|
|
|
except:
|
|
|
|
pass
|
2015-10-20 10:17:09 +02:00
|
|
|
|
|
|
|
if(seconds_since_epoch == 0):
|
|
|
|
return None
|
|
|
|
|
|
|
|
return time.gmtime(seconds_since_epoch)
|