gh-28 Add no-exif file for tests and add test cases
|
@ -23,8 +23,6 @@ Media class for general video operations
|
||||||
class Media(object):
|
class Media(object):
|
||||||
# class / static variable accessible through get_valid_extensions()
|
# class / static variable accessible through get_valid_extensions()
|
||||||
__name__ = 'Media'
|
__name__ = 'Media'
|
||||||
video_extensions = ('avi','m4v','mov','mp4','3gp')
|
|
||||||
photo_extensions = ('jpg', 'jpeg', 'nef', 'dng', 'gif')
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@param, source, string, The fully qualified path to the video file
|
@param, source, string, The fully qualified path to the video file
|
||||||
|
@ -88,41 +86,6 @@ class Media(object):
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
Get the date which the photo was taken.
|
|
||||||
The date value returned is defined by the min() of mtime and ctime.
|
|
||||||
|
|
||||||
@returns, time object or None for non-photo files or 0 timestamp
|
|
||||||
"""
|
|
||||||
def get_date_taken(self):
|
|
||||||
if(not self.is_valid()):
|
|
||||||
return None
|
|
||||||
|
|
||||||
source = self.source
|
|
||||||
seconds_since_epoch = min(os.path.getmtime(source), os.path.getctime(source))
|
|
||||||
# We need to parse a string from EXIF into a timestamp.
|
|
||||||
# EXIF DateTimeOriginal and EXIF DateTime are both stored in %Y:%m:%d %H:%M:%S format
|
|
||||||
# we use date.strptime -> .timetuple -> time.mktime to do the conversion in the local timezone
|
|
||||||
# EXIF DateTime is already stored as a timestamp
|
|
||||||
# Sourced from https://github.com/photo/frontend/blob/master/src/libraries/models/Photo.php#L500
|
|
||||||
exif = self.get_exif()
|
|
||||||
for key in self.exif_map['date_taken']:
|
|
||||||
try:
|
|
||||||
if(key in exif):
|
|
||||||
if(re.match('\d{4}(-|:)\d{2}(-|:)\d{2}', str(exif[key].value)) is not None):
|
|
||||||
seconds_since_epoch = time.mktime(exif[key].value.timetuple())
|
|
||||||
break;
|
|
||||||
except BaseException as e:
|
|
||||||
if(constants.debug == True):
|
|
||||||
print e
|
|
||||||
pass
|
|
||||||
|
|
||||||
if(seconds_since_epoch == 0):
|
|
||||||
return None
|
|
||||||
|
|
||||||
return time.gmtime(seconds_since_epoch)
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Read EXIF from a photo file.
|
Read EXIF from a photo file.
|
||||||
We store the result in a member variable so we can call get_exif() often without performance degredation
|
We store the result in a member variable so we can call get_exif() often without performance degredation
|
||||||
|
|
|
@ -86,7 +86,41 @@ class Photo(Media):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Check the file extension against valid file extensions as returned by get_valid_extensions()
|
Get the date which the photo was taken.
|
||||||
|
The date value returned is defined by the min() of mtime and ctime.
|
||||||
|
|
||||||
|
@returns, time object or None for non-photo files or 0 timestamp
|
||||||
|
"""
|
||||||
|
def get_date_taken(self):
|
||||||
|
if(not self.is_valid()):
|
||||||
|
return None
|
||||||
|
|
||||||
|
source = self.source
|
||||||
|
seconds_since_epoch = min(os.path.getmtime(source), os.path.getctime(source))
|
||||||
|
# We need to parse a string from EXIF into a timestamp.
|
||||||
|
# EXIF DateTimeOriginal and EXIF DateTime are both stored in %Y:%m:%d %H:%M:%S format
|
||||||
|
# we use date.strptime -> .timetuple -> time.mktime to do the conversion in the local timezone
|
||||||
|
# EXIF DateTime is already stored as a timestamp
|
||||||
|
# Sourced from https://github.com/photo/frontend/blob/master/src/libraries/models/Photo.php#L500
|
||||||
|
exif = self.get_exif()
|
||||||
|
for key in self.exif_map['date_taken']:
|
||||||
|
try:
|
||||||
|
if(key in exif):
|
||||||
|
if(re.match('\d{4}(-|:)\d{2}(-|:)\d{2}', str(exif[key].value)) is not None):
|
||||||
|
seconds_since_epoch = time.mktime(exif[key].value.timetuple())
|
||||||
|
break;
|
||||||
|
except BaseException as e:
|
||||||
|
if(constants.debug == True):
|
||||||
|
print e
|
||||||
|
pass
|
||||||
|
|
||||||
|
if(seconds_since_epoch == 0):
|
||||||
|
return None
|
||||||
|
|
||||||
|
return time.gmtime(seconds_since_epoch)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Check the file extension against valid file extensions as returned by self.extensions
|
||||||
|
|
||||||
@returns, boolean
|
@returns, boolean
|
||||||
"""
|
"""
|
||||||
|
@ -98,7 +132,6 @@ class Photo(Media):
|
||||||
if(imghdr.what(source) is None):
|
if(imghdr.what(source) is None):
|
||||||
return False;
|
return False;
|
||||||
|
|
||||||
# we can't use self.__get_extension else we'll endlessly recurse
|
|
||||||
return os.path.splitext(source)[1][1:].lower() in self.extensions
|
return os.path.splitext(source)[1][1:].lower() in self.extensions
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -174,4 +207,4 @@ class Photo(Media):
|
||||||
"""
|
"""
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_valid_extensions(Photo):
|
def get_valid_extensions(Photo):
|
||||||
return Media.photo_extensions
|
return Photo.extensions
|
||||||
|
|
After Width: | Height: | Size: 222 B |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 13 KiB |
|
@ -3,14 +3,10 @@
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import hashlib
|
import datetime
|
||||||
import random
|
|
||||||
import re
|
|
||||||
import shutil
|
import shutil
|
||||||
import string
|
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
import datetime
|
|
||||||
|
|
||||||
from nose.plugins.skip import SkipTest
|
from nose.plugins.skip import SkipTest
|
||||||
|
|
||||||
|
@ -25,7 +21,7 @@ os.environ['TZ'] = 'GMT'
|
||||||
|
|
||||||
def test_photo_extensions():
|
def test_photo_extensions():
|
||||||
photo = Photo()
|
photo = Photo()
|
||||||
extensions = photo.photo_extensions
|
extensions = photo.extensions
|
||||||
|
|
||||||
assert 'jpg' in extensions
|
assert 'jpg' in extensions
|
||||||
assert 'jpeg' in extensions
|
assert 'jpeg' in extensions
|
||||||
|
@ -75,6 +71,29 @@ def test_get_coordinate_longitude():
|
||||||
|
|
||||||
assert coordinate == -122.033383611, coordinate
|
assert coordinate == -122.033383611, coordinate
|
||||||
|
|
||||||
|
def test_get_coordinates_without_exif():
|
||||||
|
photo = Photo(helper.get_file('no-exif.jpg'))
|
||||||
|
latitude = photo.get_coordinate('latitude')
|
||||||
|
longitude = photo.get_coordinate('longitude')
|
||||||
|
|
||||||
|
assert latitude is None, latitude
|
||||||
|
assert longitude is None, longitude
|
||||||
|
|
||||||
|
def test_get_date_taken():
|
||||||
|
photo = Photo(helper.get_file('plain.jpg'))
|
||||||
|
date_taken = photo.get_date_taken()
|
||||||
|
|
||||||
|
assert date_taken == (2015, 12, 5, 0, 59, 26, 5, 339, 0), date_taken
|
||||||
|
|
||||||
|
def test_get_date_taken_without_exif():
|
||||||
|
source = helper.get_file('no-exif.jpg')
|
||||||
|
photo = Photo(source)
|
||||||
|
date_taken = photo.get_date_taken()
|
||||||
|
|
||||||
|
date_taken_from_file = time.gmtime(min(os.path.getmtime(source), os.path.getctime(source)))
|
||||||
|
|
||||||
|
assert date_taken == date_taken_from_file, date_taken
|
||||||
|
|
||||||
def test_is_valid():
|
def test_is_valid():
|
||||||
photo = Photo(helper.get_file('with-location.jpg'))
|
photo = Photo(helper.get_file('with-location.jpg'))
|
||||||
|
|
||||||
|
@ -141,10 +160,6 @@ def test_set_title():
|
||||||
photo = Photo(origin)
|
photo = Photo(origin)
|
||||||
origin_metadata = photo.get_metadata()
|
origin_metadata = photo.get_metadata()
|
||||||
|
|
||||||
# Verify that original photo has no location information
|
|
||||||
assert origin_metadata['latitude'] is None, origin_metadata['latitude']
|
|
||||||
assert origin_metadata['longitude'] is None, origin_metadata['longitude']
|
|
||||||
|
|
||||||
status = photo.set_title('my photo title')
|
status = photo.set_title('my photo title')
|
||||||
|
|
||||||
assert status == True, status
|
assert status == True, status
|
||||||
|
@ -166,10 +181,6 @@ def test_set_title_non_ascii():
|
||||||
photo = Photo(origin)
|
photo = Photo(origin)
|
||||||
origin_metadata = photo.get_metadata()
|
origin_metadata = photo.get_metadata()
|
||||||
|
|
||||||
# Verify that original photo has no location information
|
|
||||||
assert origin_metadata['latitude'] is None, origin_metadata['latitude']
|
|
||||||
assert origin_metadata['longitude'] is None, origin_metadata['longitude']
|
|
||||||
|
|
||||||
status = photo.set_title('形声字 / 形聲字')
|
status = photo.set_title('形声字 / 形聲字')
|
||||||
|
|
||||||
assert status == True, status
|
assert status == True, status
|
||||||
|
|