Add EXIF support for video files
This commit is contained in:
parent
96f8c3efcf
commit
7408a860d9
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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],
|
||||||
|
|
|
@ -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'], latitude=metadata['latitude'], longitude=metadata['longitude'])
|
||||||
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'])
|
|
||||||
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
|
||||||
|
|
Loading…
Reference in New Issue