ordigi/elodie/media/video.py

157 lines
4.7 KiB
Python
Raw Normal View History

"""
Author: Jaisen Mathai <jaisen@jmathai.com>
Video package that handles all video operations
"""
2015-10-02 09:20:27 +02:00
# load modules
2015-10-12 09:37:57 +02:00
from distutils.spawn import find_executable
2015-10-02 09:20:27 +02:00
from sys import argv
2015-10-12 09:37:57 +02:00
from datetime import datetime
2015-10-02 09:20:27 +02:00
import mimetypes
import os
import re
import subprocess
import time
2015-10-07 08:47:51 +02:00
from elodie.media.media import Media
2015-10-02 09:20:27 +02:00
"""
Video class for general video operations
"""
2015-10-07 08:47:51 +02:00
class Video(Media):
# class / static variable accessible through get_valid_extensions()
__valid_extensions = ('avi','m4v','mov','mp4','3gp')
"""
@param, source, string, The fully qualified path to the video file
"""
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)
2015-10-02 09:20:27 +02:00
2015-10-12 09:37:57 +02:00
"""
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.
The date value returned is defined by the min() of mtime and ctime.
2015-10-02 09:20:27 +02:00
@returns, time object or None for non-video files or 0 timestamp
"""
2015-10-07 08:47:51 +02:00
def get_date_taken(self):
2015-10-02 09:20:27 +02:00
if(not self.is_valid()):
return None
source = self.source
seconds_since_epoch = min(os.path.getmtime(source), os.path.getctime(source))
2015-10-12 09:37:57 +02:00
# 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
2015-10-02 09:20:27 +02:00
if(seconds_since_epoch == 0):
return None
return time.gmtime(seconds_since_epoch)
"""
Get the duration of a video in seconds.
Uses ffmpeg/ffprobe
@returns, string or None for a non-video file
"""
2015-10-07 08:47:51 +02:00
def get_duration(self):
2015-10-02 09:20:27 +02:00
if(not self.is_valid()):
return None
source = self.source
result = subprocess.Popen(['ffprobe', source],
2015-10-02 09:20:27 +02:00
stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
for key in result.stdout.readlines():
if 'Duration' in key:
return re.search('(\d{2}:\d{2}.\d{2})', key).group(1).replace('.', ':')
return None
2015-10-02 09:20:27 +02:00
2015-10-12 09:37:57 +02:00
"""
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()
2015-10-07 10:48:01 +02:00
"""
Get a dictionary of metadata for a video.
All keys will be present and have a value of None if not obtained.
@returns, dictionary or None for non-video files
"""
def get_metadata(self):
if(not self.is_valid()):
return None
source = self.source
metadata = {
"date_taken": self.get_date_taken(),
2015-10-12 09:37:57 +02:00
"latitude": self.get_coordinate('latitude'),
"longitude": self.get_coordinate('longitude'),
2015-10-07 10:48:01 +02:00
"length": self.get_duration(),
"mime_type": self.get_mimetype(),
"base_name": os.path.splitext(os.path.basename(source))[0],
"extension": self.get_extension()
}
return metadata
"""
Static method to access static __valid_extensions variable.
@returns, tuple
"""
@classmethod
def get_valid_extensions(Video):
return Video.__valid_extensions
2015-10-02 09:20:27 +02:00
class Transcode(object):
# Constructor takes a video object as it's parameter
def __init__(self, video=None):
self.video = video