Add EXIF support for video files

This commit is contained in:
Jaisen Mathai 2015-10-12 00:37:57 -07:00
parent 96f8c3efcf
commit 7408a860d9
4 changed files with 63 additions and 9 deletions

View File

@ -3,4 +3,6 @@ pip install exifread
pip install LatLon pip install LatLon
pip install requests pip install requests
brew install exiftool
Need config.ini for reverse lookup Need config.ini for reverse lookup

View File

@ -104,7 +104,7 @@ class Photo(Media):
return self.exif return self.exif
""" """
Get latitude of photo from EXIF Get latitude or longitude of photo from EXIF
@returns, float or None if not present in EXIF or a non-photo file @returns, float or None if not present in EXIF or a non-photo file
""" """

View File

@ -4,7 +4,10 @@ Video package that handles all video operations
""" """
# load modules # load modules
from distutils.spawn import find_executable
from sys import argv from sys import argv
from datetime import datetime
import mimetypes import mimetypes
import os import os
import re import re
@ -26,6 +29,34 @@ class Video(Media):
def __init__(self, source=None): def __init__(self, source=None):
super(Video, self).__init__(source) super(Video, self).__init__(source)
"""
Get latitude or longitude of photo from EXIF
@returns, time object or None for non-video files or 0 timestamp
"""
def get_coordinate(self, type='latitude'):
exif_data = self.get_exif()
if(exif_data is None):
return None
coords = re.findall('(GPS %s +: .+)' % type.capitalize(), exif_data)
if(coords is None or len(coords) == 0):
return None
coord_string = coords[0]
coordinate = re.findall('([0-9.]+)', coord_string)
direction = re.search('[NSEW]$', coord_string)
if(coordinate is None or direction is None):
return None
direction = direction.group(0)
decimal_degrees = float(coordinate[0]) + float(coordinate[1])/60 + float(coordinate[2])/3600
if(direction == 'S' or direction == 'W'):
decimal_degrees = decimal_degrees * -1
return decimal_degrees
""" """
Get the date which the video was taken. Get the date which the video was taken.
The date value returned is defined by the min() of mtime and ctime. The date value returned is defined by the min() of mtime and ctime.
@ -38,6 +69,17 @@ class Video(Media):
source = self.source source = self.source
seconds_since_epoch = min(os.path.getmtime(source), os.path.getctime(source)) seconds_since_epoch = min(os.path.getmtime(source), os.path.getctime(source))
# We need to parse a string from EXIF into a timestamp.
# we use date.strptime -> .timetuple -> time.mktime to do the conversion in the local timezone
exif_data = self.get_exif()
date = re.search('Media Create Date +: +(.+)', exif_data)
if(date is not None):
date_string = date.group(1)
try:
seconds_since_epoch = time.mktime(datetime.strptime(date_string, '%Y:%m:%d %H:%M:%S').timetuple())
except:
pass
if(seconds_since_epoch == 0): if(seconds_since_epoch == 0):
return None return None
@ -61,6 +103,21 @@ class Video(Media):
return re.search('(\d{2}:\d{2}.\d{2})', key).group(1).replace('.', ':') return re.search('(\d{2}:\d{2}.\d{2})', key).group(1).replace('.', ':')
return None return None
"""
Get exif data from video file.
Not all video files have exif and this currently relies on the CLI exiftool program
@returns, string or None if exiftool is not found
"""
def get_exif(self):
exiftool = find_executable('exiftool')
if(exiftool is None):
return None
source = self.source
process_output = subprocess.Popen(['%s %s ' % (exiftool, source)], stdout=subprocess.PIPE, shell=True)
return process_output.stdout.read()
""" """
Get a dictionary of metadata for a video. Get a dictionary of metadata for a video.
All keys will be present and have a value of None if not obtained. All keys will be present and have a value of None if not obtained.
@ -74,6 +131,8 @@ class Video(Media):
source = self.source source = self.source
metadata = { metadata = {
"date_taken": self.get_date_taken(), "date_taken": self.get_date_taken(),
"latitude": self.get_coordinate('latitude'),
"longitude": self.get_coordinate('longitude'),
"length": self.get_duration(), "length": self.get_duration(),
"mime_type": self.get_mimetype(), "mime_type": self.get_mimetype(),
"base_name": os.path.splitext(os.path.basename(source))[0], "base_name": os.path.splitext(os.path.basename(source))[0],

View File

@ -25,14 +25,7 @@ def process_file(_file, destination, media):
metadata = media.get_metadata() metadata = media.get_metadata()
if(type(media).__name__ == 'Video'):
directory_name = filesystem.get_folder_path(date=metadata['date_taken'])
elif(type(media).__name__ == 'Photo'):
directory_name = filesystem.get_folder_path(date=metadata['date_taken'], latitude=metadata['latitude'], longitude=metadata['longitude']) directory_name = filesystem.get_folder_path(date=metadata['date_taken'], latitude=metadata['latitude'], longitude=metadata['longitude'])
else:
print 'Invalid media type'
sys.exit(2)
dest_directory = '%s/%s' % (destination, directory_name) dest_directory = '%s/%s' % (destination, directory_name)
# TODO remove the day prefix of the file that was there prior to the crawl # TODO remove the day prefix of the file that was there prior to the crawl