ordigi/elodie/media/media.py

245 lines
7.2 KiB
Python
Raw Normal View History

2015-10-07 08:47:51 +02:00
"""
The media module provides a base :class:`Media` class for media objects that
2016-01-08 23:49:06 +01:00
are tracked by Elodie. The Media class provides some base functionality used
by all the media types, but isn't itself used to represent anything. Its
sub-classes (:class:`~elodie.media.audio.Audio`,
:class:`~elodie.media.photo.Photo`, and :class:`~elodie.media.video.Video`)
are used to represent the actual files.
.. moduleauthor:: Jaisen Mathai <jaisen@jmathai.com>
2015-10-07 08:47:51 +02:00
"""
2016-03-12 20:09:28 +01:00
from __future__ import print_function
2015-10-07 08:47:51 +02:00
# load modules
from elodie import constants
from elodie.dependencies import get_exiftool
from elodie.external.pyexiftool import ExifTool
from elodie.media.base import Base
class Media(Base):
2016-01-08 23:49:06 +01:00
"""The base class for all media objects.
:param str source: The fully qualified path to the video file.
2015-10-07 08:47:51 +02:00
"""
2016-01-08 23:49:06 +01:00
__name__ = 'Media'
2016-02-12 20:22:26 +01:00
d_coordinates = {
'latitude': 'latitude_ref',
'longitude': 'longitude_ref'
}
2016-02-12 20:22:26 +01:00
2015-10-07 08:47:51 +02:00
def __init__(self, source=None):
super(Media, self).__init__(source)
self.exif_map = {
'date_taken': [
'EXIF:DateTimeOriginal',
'EXIF:CreateDate',
'EXIF:ModifyDate'
]
}
self.album_keys = ['XMP-xmpDM:Album', 'XMP:Album']
self.title_key = 'XMP:Title'
self.latitude_keys = ['EXIF:GPSLatitude']
self.longitude_keys = ['EXIF:GPSLongitude']
self.latitude_ref_key = 'EXIF:GPSLatitudeRef'
self.longitude_ref_key = 'EXIF:GPSLongitudeRef'
self.set_gps_ref = True
self.exiftool_addedargs = [
'-overwrite_original',
u'-config',
u'"{}"'.format(constants.exiftool_config)
]
def get_album(self):
2016-01-08 23:49:06 +01:00
"""Get album from EXIF
:returns: None or string
"""
if(not self.is_valid()):
return None
exiftool_attributes = self.get_exiftool_attributes()
if exiftool_attributes is None:
return None
for album_key in self.album_keys:
if album_key in exiftool_attributes:
return exiftool_attributes[album_key]
2015-10-07 08:47:51 +02:00
return None
2016-01-08 23:49:06 +01:00
def get_coordinate(self, type='latitude'):
"""Get latitude or longitude of media from EXIF
2016-01-08 23:49:06 +01:00
:param str type: Type of coordinate to get. Either "latitude" or
"longitude".
:returns: float or None if not present in EXIF or a non-photo file
2016-01-08 23:49:06 +01:00
"""
exif = self.get_exiftool_attributes()
if not exif:
return None
# The lat/lon _keys array has an order of precedence.
# The first key is writable and we will give the writable
# key precence when reading.
direction_multiplier = 1.0
for key in self.latitude_keys + self.longitude_keys:
if key not in exif:
continue
# Cast coordinate to a float due to a bug in exiftool's
# -json output format.
# https://github.com/jmathai/elodie/issues/171
# http://u88.n24.queensu.ca/exiftool/forum/index.php/topic,7952.0.html #noqa
this_coordinate = float(exif[key])
# TODO: verify that we need to check ref key
# when self.set_gps_ref != True
if type == 'latitude' and key in self.latitude_keys:
2016-06-24 06:31:58 +02:00
if self.latitude_ref_key in exif and \
exif[self.latitude_ref_key] == 'S':
direction_multiplier = -1.0
return this_coordinate * direction_multiplier
elif type == 'longitude' and key in self.longitude_keys:
2016-06-24 06:31:58 +02:00
if self.longitude_ref_key in exif and \
exif[self.longitude_ref_key] == 'W':
direction_multiplier = -1.0
return this_coordinate * direction_multiplier
return None
def get_exiftool_attributes(self):
2016-01-08 23:49:06 +01:00
"""Get attributes for the media object from exiftool.
:returns: dict, or False if exiftool was not available.
"""
source = self.source
exiftool = get_exiftool()
if(exiftool is None):
return False
with ExifTool(addedargs=self.exiftool_addedargs) as et:
metadata = et.get_metadata(source)
if not metadata:
return False
return metadata
def get_title(self):
2016-01-08 23:49:06 +01:00
"""Get the title for a photo of video
:returns: str or None if no title is set or not a valid media type
"""
if(not self.is_valid()):
return None
exiftool_attributes = self.get_exiftool_attributes()
if exiftool_attributes is None:
return None
if(self.title_key not in exiftool_attributes):
return None
return exiftool_attributes[self.title_key]
def reset_cache(self):
"""Resets any internal cache
"""
self.exiftool_attributes = None
super(Media, self).reset_cache()
def set_album(self, album):
2016-01-08 23:49:06 +01:00
"""Set album for a photo
:param str name: Name of album
:returns: bool
"""
if(not self.is_valid()):
return None
tags = {self.album_keys[0]: album}
status = self.__set_tags(tags)
self.reset_cache()
return status
def set_date_taken(self, time):
"""Set the date/time a photo was taken.
:param datetime time: datetime object of when the photo was taken
:returns: bool
"""
if(time is None):
return False
tags = {}
formatted_time = time.strftime('%Y:%m:%d %H:%M:%S')
for key in self.exif_map['date_taken']:
tags[key] = formatted_time
status = self.__set_tags(tags)
self.reset_cache()
return status
def set_location(self, latitude, longitude):
if(not self.is_valid()):
return None
# The lat/lon _keys array has an order of precedence.
# The first key is writable and we will give the writable
# key precence when reading.
tags = {
self.latitude_keys[0]: latitude,
self.longitude_keys[0]: longitude,
}
# If self.set_gps_ref == True then it means we are writing an EXIF
# GPS tag which requires us to set the reference key.
# That's because the lat/lon are absolute values.
if self.set_gps_ref:
if latitude < 0:
tags[self.latitude_ref_key] = 'S'
if longitude < 0:
tags[self.longitude_ref_key] = 'W'
status = self.__set_tags(tags)
self.reset_cache()
return status
def set_title(self, title):
"""Set title for a photo.
:param str title: Title of the photo.
:returns: bool
"""
if(not self.is_valid()):
return None
if(title is None):
return None
tags = {self.title_key: title}
status = self.__set_tags(tags)
self.reset_cache()
return status
def __set_tags(self, tags):
if(not self.is_valid()):
return None
source = self.source
status = ''
with ExifTool(addedargs=self.exiftool_addedargs) as et:
status = et.set_tags(tags, source)
return status != ''