2016-04-07 10:08:33 +02:00
|
|
|
"""
|
|
|
|
The base module provides a base :class:`Base` class for all objects that
|
|
|
|
are tracked by Elodie. The Base 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`, :class:`~elodie.media.video.Video`, and
|
|
|
|
:class:`~elodie.media.text.Text`)
|
|
|
|
are used to represent the actual files.
|
|
|
|
|
|
|
|
.. moduleauthor:: Jaisen Mathai <jaisen@jmathai.com>
|
|
|
|
"""
|
|
|
|
|
|
|
|
import mimetypes
|
|
|
|
import os
|
|
|
|
|
2016-08-22 08:10:45 +02:00
|
|
|
try: # Py3k compatibility
|
|
|
|
basestring
|
2016-03-12 20:09:28 +01:00
|
|
|
except NameError:
|
2016-08-22 08:10:45 +02:00
|
|
|
basestring = (bytes, str)
|
|
|
|
|
2016-04-07 10:08:33 +02:00
|
|
|
|
|
|
|
class Base(object):
|
|
|
|
|
|
|
|
"""The base class for all media objects.
|
|
|
|
|
|
|
|
:param str source: The fully qualified path to the video file.
|
|
|
|
"""
|
|
|
|
|
|
|
|
__name__ = 'Base'
|
|
|
|
|
2016-04-15 07:12:44 +02:00
|
|
|
extensions = ()
|
|
|
|
|
2016-04-07 10:08:33 +02:00
|
|
|
def __init__(self, source=None):
|
|
|
|
self.source = source
|
|
|
|
self.reset_cache()
|
|
|
|
|
|
|
|
def format_metadata(self, **kwargs):
|
|
|
|
"""Method to consistently return a populated metadata dictionary.
|
|
|
|
|
|
|
|
:returns: dict
|
|
|
|
"""
|
|
|
|
|
|
|
|
def get_album(self):
|
|
|
|
"""Base method for getting an album
|
|
|
|
|
|
|
|
:returns: None
|
|
|
|
"""
|
|
|
|
return None
|
|
|
|
|
|
|
|
def get_file_path(self):
|
|
|
|
"""Get the full path to the video.
|
|
|
|
|
|
|
|
:returns: string
|
|
|
|
"""
|
|
|
|
return self.source
|
|
|
|
|
|
|
|
def get_coordinate(self, type):
|
|
|
|
return None
|
|
|
|
|
|
|
|
def get_extension(self):
|
|
|
|
"""Get the file extension as a lowercased string.
|
|
|
|
|
|
|
|
:returns: string or None for a non-video
|
|
|
|
"""
|
|
|
|
if(not self.is_valid()):
|
|
|
|
return None
|
|
|
|
|
|
|
|
source = self.source
|
|
|
|
return os.path.splitext(source)[1][1:].lower()
|
|
|
|
|
|
|
|
def get_metadata(self, update_cache=False):
|
|
|
|
"""Get a dictionary of metadata for any file.
|
|
|
|
|
|
|
|
All keys will be present and have a value of None if not obtained.
|
|
|
|
|
|
|
|
:returns: dict or None for non-text files
|
|
|
|
"""
|
|
|
|
if(not self.is_valid()):
|
|
|
|
return None
|
|
|
|
|
2016-04-15 23:54:15 +02:00
|
|
|
if(isinstance(self.metadata, dict) and update_cache is False):
|
2016-04-07 10:08:33 +02:00
|
|
|
return self.metadata
|
|
|
|
|
|
|
|
source = self.source
|
|
|
|
|
|
|
|
self.metadata = {
|
|
|
|
'date_taken': self.get_date_taken(),
|
|
|
|
'latitude': self.get_coordinate('latitude'),
|
|
|
|
'longitude': self.get_coordinate('longitude'),
|
|
|
|
'album': self.get_album(),
|
|
|
|
'title': self.get_title(),
|
|
|
|
'mime_type': self.get_mimetype(),
|
|
|
|
'base_name': os.path.splitext(os.path.basename(source))[0],
|
|
|
|
'extension': self.get_extension(),
|
|
|
|
'directory_path': os.path.dirname(source)
|
|
|
|
}
|
|
|
|
|
|
|
|
return self.metadata
|
|
|
|
|
|
|
|
def get_mimetype(self):
|
|
|
|
"""Get the mimetype of the file.
|
|
|
|
|
|
|
|
:returns: str or None for a non-video
|
|
|
|
"""
|
|
|
|
if(not self.is_valid()):
|
|
|
|
return None
|
|
|
|
|
|
|
|
source = self.source
|
|
|
|
mimetype = mimetypes.guess_type(source)
|
|
|
|
if(mimetype is None):
|
|
|
|
return None
|
|
|
|
|
|
|
|
return mimetype[0]
|
|
|
|
|
|
|
|
def get_title(self):
|
|
|
|
"""Base method for getting the title of a file
|
|
|
|
|
|
|
|
:returns: None
|
|
|
|
"""
|
|
|
|
return None
|
|
|
|
|
|
|
|
def is_valid(self):
|
|
|
|
"""Check the file extension against valid file extensions.
|
|
|
|
|
|
|
|
The list of valid file extensions come from self.extensions.
|
|
|
|
|
|
|
|
:returns: bool
|
|
|
|
"""
|
|
|
|
source = self.source
|
|
|
|
return os.path.splitext(source)[1][1:].lower() in self.extensions
|
|
|
|
|
|
|
|
def reset_cache(self):
|
|
|
|
"""Resets any internal cache
|
|
|
|
"""
|
|
|
|
self.metadata = None
|
|
|
|
|
|
|
|
def set_album(self, name):
|
|
|
|
"""Base method for setting the album of a file
|
|
|
|
|
|
|
|
:returns: None
|
|
|
|
"""
|
|
|
|
return None
|
|
|
|
|
|
|
|
def set_album_from_folder(self):
|
|
|
|
"""Set the album attribute based on the leaf folder name
|
|
|
|
|
|
|
|
:returns: bool
|
|
|
|
"""
|
|
|
|
metadata = self.get_metadata()
|
|
|
|
|
|
|
|
# If this file has an album already set we do not overwrite EXIF
|
2016-04-15 07:12:44 +02:00
|
|
|
if(not isinstance(metadata, dict) or metadata['album'] is not None):
|
2016-04-07 10:08:33 +02:00
|
|
|
return False
|
|
|
|
|
|
|
|
folder = os.path.basename(metadata['directory_path'])
|
|
|
|
# If folder is empty we skip
|
|
|
|
if(len(folder) == 0):
|
|
|
|
return False
|
|
|
|
|
|
|
|
self.set_album(folder)
|
|
|
|
return True
|
|
|
|
|
|
|
|
def set_metadata_basename(self, new_basename):
|
|
|
|
"""Update the basename attribute in the metadata dict for this instance.
|
|
|
|
|
|
|
|
This is used for when we update the EXIF title of a media file. Since
|
|
|
|
that determines the name of a file if we update the title of a file
|
|
|
|
more than once it appends to the file name.
|
|
|
|
|
|
|
|
i.e. 2015-12-31_00-00-00-my-first-title-my-second-title.jpg
|
|
|
|
|
|
|
|
:param str new_basename: New basename of file (with the old title
|
|
|
|
removed).
|
|
|
|
"""
|
|
|
|
self.get_metadata()
|
|
|
|
self.metadata['base_name'] = new_basename
|
|
|
|
|
|
|
|
def set_metadata(self, **kwargs):
|
|
|
|
"""Method to manually update attributes in metadata.
|
|
|
|
|
|
|
|
:params dict kwargs: Named parameters to update.
|
|
|
|
"""
|
|
|
|
metadata = self.get_metadata()
|
|
|
|
for key in kwargs:
|
|
|
|
if(key in metadata):
|
|
|
|
self.metadata[key] = kwargs[key]
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_class_by_file(cls, _file, classes):
|
|
|
|
if not isinstance(_file, basestring) or not os.path.isfile(_file):
|
|
|
|
return None
|
|
|
|
|
|
|
|
extension = os.path.splitext(_file)[1][1:].lower()
|
|
|
|
|
2016-04-20 06:24:41 +02:00
|
|
|
if len(extension) > 0:
|
|
|
|
for i in classes:
|
|
|
|
if(extension in i.extensions):
|
|
|
|
return i(_file)
|
2016-04-07 10:08:33 +02:00
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_valid_extensions(cls):
|
|
|
|
"""Static method to access static extensions variable.
|
|
|
|
|
|
|
|
:returns: tuple(str)
|
|
|
|
"""
|
|
|
|
return cls.extensions
|