Merge branch 'master' into improve-tests

This commit is contained in:
Jaisen Mathai 2016-09-04 21:45:41 -07:00
commit ab77938408
27 changed files with 146 additions and 229 deletions

View File

@ -1,3 +1,4 @@
[report]
omit =
*/tests/*
*/external/*

View File

@ -1,3 +1,3 @@
#!/usr/bin/env bash
flake8 elodie --exclude=tests && nosetests -w elodie/tests
flake8 elodie --exclude=tests,external && nosetests -w elodie/tests

View File

@ -2,20 +2,29 @@ language: python
dist: trusty
python:
- "2.7"
- "3.4"
virtualenv:
system_site_packages: true
before_install:
- "sudo apt-get update -qq"
- "sudo apt-get install python-dev python-pip libimage-exiftool-perl -y"
install:
- "sudo pip install -r elodie/tests/requirements.txt"
- "sudo pip install coveralls"
- "pip install -r elodie/tests/requirements.txt"
- "pip install coveralls"
# command to run tests
# test mapquest key
before_script:
- "mkdir ~/.elodie"
- "sed 's/your-api-key-goes-here/x8wQLqGhW7qK3sFpjYtVTogVtoMK0S8s/g' config.ini-sample > ~/.elodie/config.ini"
# Get exiftool 10.20 installed
- "export ELODIE_DIR=${PWD}"
- "wget http://www.sno.phy.queensu.ca/~phil/exiftool/Image-ExifTool-10.20.tar.gz"
- "gzip -dc Image-ExifTool-10.20.tar.gz | tar -xf -"
- "cd Image-ExifTool-10.20"
- "perl Makefile.PL"
- "sudo make install"
- "cd ${ELODIE_DIR}"
after_success:
- "coveralls"
script:
script:
- "nosetests --with-coverage --cover-package=elodie -w elodie/tests"

View File

@ -11,7 +11,7 @@ Getting started takes just a few minutes.
Elodie relies on the great [ExifTool library by Phil Harvey](http://www.sno.phy.queensu.ca/~phil/exiftool/). You'll need to install it for your platform.
Some features for video files will only work with newer versions of ExifTool and have been tested on version 10.15 or higher. Check your version by typing `exiftool -ver` and see the [manual installation instructions for ExifTool](http://www.sno.phy.queensu.ca/~phil/exiftool/install.html#Unix) if needed.
Some features for video files will only work with newer versions of ExifTool and have been tested on version 10.20 or higher. Check your version by typing `exiftool -ver` and see the [manual installation instructions for ExifTool](http://www.sno.phy.queensu.ca/~phil/exiftool/install.html#Unix) if needed.
```
# OSX (uses homebrew, http://brew.sh/)
@ -98,7 +98,7 @@ Here's an example of a very asynchronous setup.
* Periodically recategorize photos by fixing their location or date or by adding them to an album.
* Have a Synology at home set to automatically sync down from Dropbox/Google Drive.
This setup means you can quickly get photos off your or anyone's phone and know that they'll be organized and backed up in 3 locations by the time they're ready to view them.
This setup means you can quickly get photos off your or anyone's phone and know that they'll be organized and backed up in 3 locations by the time you're ready to view them.
<p align="center"><img src ="creative/workflow-simplified-white-bg.png" /></p>
@ -189,7 +189,7 @@ Organizing your existing photos is great. But I'd be lying if I said I was the o
In order to sort new photos that I haven't already organized I need someone to tell me about them. There's no single way to do this. You could use inotify, cron, Automator or my favorite app - Hazel; it doesn't matter.
If you'd like to let me know of a specific photo or group of photos to add to your library you would run one of the following command. Use fully qualified paths for everything since you won't be running this manually.
If you'd like to let me know of a specific photo or group of photos to add to your library you would run one of the following commands. Use fully qualified paths for everything since you won't be running this manually.
```
# I can import a single file into your library.
@ -219,13 +219,13 @@ When I organize photos I look at the embedded metadata. Here are the details of
| Dimension | Fields | Notes |
|---|---|---|
| Date Taken (photo) | EXIF:DateTimeOriginal,EXIF:DateTime, EXIF:DateTimeDigitized, file created, file modified | |
| Date Taken (photo) | EXIF:DateTimeOriginal, EXIF:CreateDate, EXIF:ModifyDate, file created, file modified | |
| Date Taken (video, audio) | QuickTime:CreationDate, QuickTime:CreationDate-und-US, QuickTime:MediaCreateDate, file created, file modified | |
| Location (photo) | EXIF:GPSLatitude/EXIF:GPSLatitudeRef, EXIF:GPSLongitude/EXIF:GPSLongitudeRef | |
| Location (video, audio) | XMP:GPSLatitude, Composite:GPSLatitude, XMP:GPSLongitude, Composite:GPSLongitude | Composite tags are read-only |
| Title (photo) | XMP:Title | |
| Title (video, audio) | XMP:DisplayName | |
| Album | XMP:Album | User defined tag in `configs/ExifTool_config` |
| Album | XMP-xmpDM:Album, XMP:Album | XMP:Album is user defined in `configs/ExifTool_config` for backwards compatability |
## Using OpenStreetMap data from MapQuest

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python
from __future__ import print_function
import os
import re
import sys
@ -35,16 +36,16 @@ def import_file(_file, destination, album_from_folder, trash):
"""
if not os.path.exists(_file):
if constants.debug:
print 'Could not find %s' % _file
print '{"source":"%s", "error_msg":"Could not find %s"}' % \
(_file, _file)
print('Could not find %s' % _file)
print('{"source":"%s", "error_msg":"Could not find %s"}' % \
(_file, _file))
return
media = Media.get_class_by_file(_file, [Text, Audio, Photo, Video])
if not media:
if constants.debug:
print 'Not a supported file (%s)' % _file
print '{"source":"%s", "error_msg":"Not a supported file"}' % _file
print('Not a supported file (%s)' % _file)
print('{"source":"%s", "error_msg":"Not a supported file"}' % _file)
return
if media.__name__ == 'Video':
@ -56,7 +57,7 @@ def import_file(_file, destination, album_from_folder, trash):
dest_path = FILESYSTEM.process_file(_file, destination,
media, allowDuplicate=False, move=False)
if dest_path:
print '%s -> %s' % (_file, dest_path)
print('%s -> %s' % (_file, dest_path))
if trash:
send2trash(_file)
@ -109,9 +110,9 @@ def update_location(media, file_path, location_name):
'latitude'], location_coords['longitude'])
if not location_status:
if constants.debug:
print 'Failed to update location'
print ('{"source":"%s",' % file_path,
'"error_msg":"Failed to update location"}')
print('Failed to update location')
print(('{"source":"%s",' % file_path,
'"error_msg":"Failed to update location"}'))
sys.exit(1)
return True
@ -125,8 +126,8 @@ def update_time(media, file_path, time_string):
elif re.match(r'^\d{4}-\d{2}-\d{2} \d{2}:\d{2}\d{2}$', time_string):
msg = ('Invalid time format. Use YYYY-mm-dd hh:ii:ss or YYYY-mm-dd')
if constants.debug:
print msg
print '{"source":"%s", "error_msg":"%s"}' % (file_path, msg)
print(msg)
print('{"source":"%s", "error_msg":"%s"}' % (file_path, msg))
sys.exit(1)
time = datetime.strptime(time_string, time_format)
@ -150,9 +151,9 @@ def _update(album, location, time, title, files):
for file_path in files:
if not os.path.exists(file_path):
if constants.debug:
print 'Could not find %s' % file_path
print '{"source":"%s", "error_msg":"Could not find %s"}' % \
(file_path, file_path)
print('Could not find %s' % file_path)
print('{"source":"%s", "error_msg":"Could not find %s"}' % \
(file_path, file_path))
continue
file_path = os.path.expanduser(file_path)
@ -209,9 +210,9 @@ def _update(album, location, time, title, files):
dest_path = FILESYSTEM.process_file(file_path, destination,
updated_media, move=True, allowDuplicate=True)
if constants.debug:
print u'%s -> %s' % (file_path, dest_path)
print '{"source":"%s", "destination":"%s"}' % (file_path,
dest_path)
print(u'%s -> %s' % (file_path, dest_path))
print('{"source":"%s", "destination":"%s"}' % (file_path,
dest_path))
# If the folder we moved the file out of or its parent are empty
# we delete it.
FILESYSTEM.delete_directory_if_empty(os.path.dirname(file_path))

View File

@ -1,6 +1,7 @@
"""
Command line argument parsing for helper scripts.
"""
from __future__ import print_function
import getopt
import sys
@ -20,13 +21,13 @@ def parse(argv, options, long_options, usage):
try:
opts, args = getopt.getopt(argv, options, long_options)
except getopt.GetoptError:
print usage
print(usage)
sys.exit(2)
return_arguments = {}
for opt, arg in opts:
if opt == '-h':
print usage
print(usage)
sys.exit()
else:
return_arguments[sub('^-+', '', opt)] = arg

View File

@ -2,6 +2,7 @@
Helpers for checking for an interacting with external dependencies. These are
things that Elodie requires, but aren't installed automatically for the user.
"""
from __future__ import print_function
import os
import sys
@ -43,7 +44,7 @@ def verify_dependencies():
"""
exiftool = get_exiftool()
if exiftool is None:
print >>sys.stderr, EXIFTOOL_ERROR
print(EXIFTOOL_ERROR, file=sys.stderr)
return False
return True

View File

@ -273,8 +273,7 @@ class ExifTool(object):
"""
if not self.running:
raise ValueError("ExifTool instance not running.")
cmd_txt = b"\n".join(params + (b"-execute\n",))
self._process.stdin.write(cmd_txt.encode("utf-8"))
self._process.stdin.write(b"\n".join(params + (b"-execute\n",)))
self._process.stdin.flush()
output = b""
fd = self._process.stdout.fileno()
@ -403,11 +402,13 @@ class ExifTool(object):
"an iterable of strings")
params = []
params_utf8 = []
for tag, value in tags.items():
params.append(u'-%s=%s' % (tag, value))
params.extend(filenames)
return self.execute(*params)
params_utf8 = [x.encode('utf-8') for x in params]
return self.execute(*params_utf8)
def set_tags(self, tags, filename):
"""Writes the values of the specified tags for the given file.

View File

@ -3,6 +3,8 @@ General file system methods.
.. moduleauthor:: Jaisen Mathai <jaisen@jmathai.com>
"""
from __future__ import print_function
from builtins import object
import os
import re
@ -176,7 +178,7 @@ class FileSystem(object):
allow_duplicate = kwargs['allowDuplicate']
if(not media.is_valid()):
print '%s is not a valid media file. Skipping...' % _file
print('%s is not a valid media file. Skipping...' % _file)
return
metadata = media.get_metadata()
@ -191,17 +193,17 @@ class FileSystem(object):
checksum = db.checksum(_file)
if(checksum is None):
if(constants.debug is True):
print 'Could not get checksum for %s. Skipping...' % _file
print('Could not get checksum for %s. Skipping...' % _file)
return
# If duplicates are not allowed and this hash exists in the db then we
# return
if(allow_duplicate is False and db.check_hash(checksum) is True):
if(constants.debug is True):
print '%s already exists at %s. Skipping...' % (
print('%s already exists at %s. Skipping...' % (
_file,
db.get_hash(checksum)
)
))
return
self.create_directory(dest_directory)

View File

@ -1,32 +1,23 @@
"""Look up geolocation information for media objects."""
from __future__ import print_function
from __future__ import division
from future import standard_library
from past.utils import old_div
from os import path
from ConfigParser import ConfigParser
import fractions
from configparser import ConfigParser
standard_library.install_aliases() # noqa
import requests
import urllib
import urllib.request
import urllib.parse
import urllib.error
from elodie import constants
from elodie.localstorage import Db
class Fraction(fractions.Fraction):
"""Only create Fractions from floats.
Should be compatible with Python 2.6, though untested.
>>> Fraction(0.3)
Fraction(3, 10)
>>> Fraction(1.1)
Fraction(11, 10)
"""
def __new__(cls, value, ignore=None):
return fractions.Fraction.from_float(value).limit_denominator(99999)
def coordinates_by_name(name):
# Try to get cached location first
db = Db()
@ -76,11 +67,11 @@ def coordinates_by_name(name):
def decimal_to_dms(decimal):
decimal = float(decimal)
decimal_abs = abs(decimal)
minutes,seconds = divmod(decimal_abs*3600,60)
degrees,minutes = divmod(minutes,60)
minutes, seconds = divmod(decimal_abs*3600, 60)
degrees, minutes = divmod(minutes, 60)
degrees = degrees
sign = 1 if decimal >= 0 else -1
return (degrees,minutes,seconds, sign)
return (degrees, minutes, seconds, sign)
def dms_to_decimal(degrees, minutes, seconds, direction=' '):
@ -88,7 +79,8 @@ def dms_to_decimal(degrees, minutes, seconds, direction=' '):
if(direction[0] in 'WSws'):
sign = -1
return (
float(degrees) + float(minutes) / 60 + float(seconds) / 3600
float(degrees) + old_div(float(minutes), 60) +
old_div(float(seconds), 3600)
) * sign
@ -159,17 +151,17 @@ def reverse_lookup(lat, lon):
headers = {"Accept-Language": constants.accepted_language}
r = requests.get(
'http://open.mapquestapi.com/nominatim/v1/reverse.php?%s' %
urllib.urlencode(params), headers=headers
urllib.parse.urlencode(params), headers=headers
)
return r.json()
except requests.exceptions.RequestException as e:
if(constants.debug is True):
print e
print(e)
return None
except ValueError as e:
if(constants.debug is True):
print r.text
print e
print(r.text)
print(e)
return None
@ -182,18 +174,18 @@ def lookup(name):
try:
params = {'format': 'json', 'key': key, 'location': name}
if(constants.debug is True):
print 'http://open.mapquestapi.com/geocoding/v1/address?%s' % urllib.urlencode(params) # noqa
print('http://open.mapquestapi.com/geocoding/v1/address?%s' % urllib.parse.urlencode(params)) # noqa
r = requests.get(
'http://open.mapquestapi.com/geocoding/v1/address?%s' %
urllib.urlencode(params)
urllib.parse.urlencode(params)
)
return r.json()
except requests.exceptions.RequestException as e:
if(constants.debug is True):
print e
print(e)
return None
except ValueError as e:
if(constants.debug is True):
print r.text
print e
print(r.text)
print(e)
return None

View File

@ -1,6 +1,8 @@
"""
Methods for interacting with information Elodie caches about stored media.
"""
from builtins import map
from builtins import object
import hashlib
import json
@ -141,17 +143,17 @@ class Db(object):
the given latitude and longitude.
:returns: str, or None if a matching location couldn't be found.
"""
last_d = sys.maxint
last_d = sys.maxsize
name = None
for data in self.location_db:
# As threshold is quite small use simple math
# From http://stackoverflow.com/questions/15736995/how-can-i-quickly-estimate-the-distance-between-two-latitude-longitude-points # noqa
# convert decimal degrees to radians
lon1, lat1, lon2, lat2 = map(
lon1, lat1, lon2, lat2 = list(map(
radians,
[longitude, latitude, data['long'], data['lat']]
)
))
r = 6371000 # radius of the earth in m
x = (lon2 - lon1) * cos(0.5 * (lat2 + lat1))

View File

@ -5,8 +5,9 @@ class.
.. moduleauthor:: Jaisen Mathai <jaisen@jmathai.com>
"""
from __future__ import absolute_import
from video import Video
from .video import Video
class Audio(Video):

View File

@ -13,6 +13,11 @@ are used to represent the actual files.
import mimetypes
import os
try: # Py3k compatibility
basestring
except NameError:
basestring = (bytes, str)
class Base(object):

View File

@ -8,6 +8,7 @@ are used to represent the actual files.
.. moduleauthor:: Jaisen Mathai <jaisen@jmathai.com>
"""
from __future__ import print_function
# load modules
from elodie import constants
@ -15,10 +16,6 @@ from elodie.dependencies import get_exiftool
from elodie.external.pyexiftool import ExifTool
from elodie.media.base import Base
import os
import re
import subprocess
class Media(Base):
@ -39,11 +36,11 @@ class Media(Base):
self.exif_map = {
'date_taken': [
'EXIF:DateTimeOriginal',
'EXIF:DateTime',
'EXIF:DateTimeDigitized'
'EXIF:CreateDate',
'EXIF:ModifyDate'
]
}
self.album_key = 'XMP:Album'
self.album_keys = ['XMP-xmpDM:Album', 'XMP:Album']
self.title_key = 'XMP:Title'
self.latitude_keys = ['EXIF:GPSLatitude']
self.longitude_keys = ['EXIF:GPSLongitude']
@ -68,10 +65,11 @@ class Media(Base):
if exiftool_attributes is None:
return None
if self.album_key not in exiftool_attributes:
return None
for album_key in self.album_keys:
if album_key in exiftool_attributes:
return exiftool_attributes[album_key]
return exiftool_attributes[self.album_key]
return None
def get_coordinate(self, type='latitude'):
"""Get latitude or longitude of media from EXIF
@ -92,12 +90,16 @@ class Media(Base):
for key in self.latitude_keys + self.longitude_keys:
# TODO: verify that we need to check ref key
# when self.set_gps_ref != True
if type == 'latitude' and key in self.latitude_keys and key in exif:
if self.latitude_ref_key in exif and exif[self.latitude_ref_key] == 'S': #noqa
if type == 'latitude' and key in self.latitude_keys and \
key in exif:
if self.latitude_ref_key in exif and \
exif[self.latitude_ref_key] == 'S':
direction_multiplier = -1
return exif[key] * direction_multiplier
elif type == 'longitude' and key in self.longitude_keys and key in exif: #noqa
if self.longitude_ref_key in exif and exif[self.longitude_ref_key] == 'W': #noqa
elif type == 'longitude' and key in self.longitude_keys and \
key in exif:
if self.longitude_ref_key in exif and \
exif[self.longitude_ref_key] == 'W':
direction_multiplier = -1
return exif[key] * direction_multiplier
@ -153,9 +155,7 @@ class Media(Base):
if(not self.is_valid()):
return None
source = self.source
tags = {self.album_key: album}
tags = {self.album_keys[0]: album}
status = self.__set_tags(tags)
self.reset_cache()
@ -170,8 +170,6 @@ class Media(Base):
if(time is None):
return False
source = self.source
tags = {}
formatted_time = time.strftime('%Y:%m:%d %H:%M:%S')
for key in self.exif_map['date_taken']:
@ -185,8 +183,6 @@ class Media(Base):
if(not self.is_valid()):
return None
source = self.source
# 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.
@ -222,8 +218,6 @@ class Media(Base):
if(title is None):
return None
source = self.source
tags = {self.title_key: title}
status = self.__set_tags(tags)
self.reset_cache()

View File

@ -4,6 +4,8 @@ image objects (JPG, DNG, etc.).
.. moduleauthor:: Jaisen Mathai <jaisen@jmathai.com>
"""
from __future__ import print_function
from __future__ import absolute_import
import imghdr
import os
@ -15,9 +17,7 @@ from re import compile
from elodie import constants
from elodie import geolocation
from elodie.external.pyexiftool import ExifTool
from media import Media
from .media import Media
class Photo(Media):
@ -69,7 +69,7 @@ class Photo(Media):
"""
if(not self.is_valid()):
return None
source = self.source
seconds_since_epoch = min(os.path.getmtime(source), os.path.getctime(source)) # noqa
@ -80,7 +80,7 @@ class Photo(Media):
# 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 split on a space and then r':|-' -> convert to int -> .timetuple()
# we split on a space and then r':|-' -> convert to int -> .timetuple()
# 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 # noqa
@ -97,7 +97,7 @@ class Photo(Media):
break
except BaseException as e:
if(constants.debug is True):
print e
print(e)
pass
if(seconds_since_epoch == 0):

View File

@ -123,7 +123,7 @@ class Text(Base):
self.metadata_line = parsed_json
except ValueError:
if(constants.debug is True):
print 'Could not parse JSON from first line: %s' % first_line
print('Could not parse JSON from first line: %s' % first_line)
pass
def write_metadata(self, **kwargs):

View File

@ -4,23 +4,19 @@ objects (AVI, MOV, etc.).
.. moduleauthor:: Jaisen Mathai <jaisen@jmathai.com>
"""
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
# load modules
from distutils.spawn import find_executable
import tempfile
from datetime import datetime
import os
import re
import shutil
import subprocess
import time
from elodie import constants
from elodie import plist_parser
from elodie.dependencies import get_exiftool
from media import Base
from media import Media
from .media import Media
class Video(Media):
@ -45,12 +41,12 @@ class Video(Media):
self.title_key = 'XMP:DisplayName'
self.latitude_keys = [
'XMP:GPSLatitude',
#'QuickTime:GPSLatitude',
# 'QuickTime:GPSLatitude',
'Composite:GPSLatitude'
]
self.longitude_keys = [
'XMP:GPSLongitude',
#'QuickTime:GPSLongitude',
# 'QuickTime:GPSLongitude',
'Composite:GPSLongitude'
]
self.latitude_ref_key = 'EXIF:GPSLatitudeRef'
@ -81,7 +77,7 @@ class Video(Media):
"""
if(not self.is_valid()):
return None
source = self.source
seconds_since_epoch = min(os.path.getmtime(source), os.path.getctime(source)) # noqa
@ -107,7 +103,7 @@ class Video(Media):
if date_offset is not None:
offset_parts = date_offset[1:].split(':')
offset_seconds = int(offset_parts[0]) * 3600
offset_seconds = offset_seconds + int(offset_parts[1]) * 60 #noqa
offset_seconds = offset_seconds + int(offset_parts[1]) * 60 # noqa
if date_offset[0] == '-':
seconds_since_epoch - offset_seconds
elif date_offset[0] == '+':

View File

@ -1,44 +0,0 @@
"""
Parse OS X plists.
.. moduleauthor:: Jaisen Mathai <jaisen@jmathai.com>
"""
# load modules
from os import path
import plistlib
class Plist(object):
"""Parse and interact with a plist file.
This class wraps the `plistlib module`_ from the standard library.
.. _plistlib module: https://docs.python.org/3/library/plistlib.html
:param str source: Source to read the plist from.
"""
def __init__(self, source):
if not path.isfile(source):
raise IOError('Could not load plist file %s' % source)
self.source = source
self.plist = plistlib.readPlist(self.source)
def update_key(self, key, value):
"""Update a value in the plist.
:param str key: Key to modify.
:param value: New value.
"""
self.plist[key] = value
def write_file(self, destination):
"""Save the plist.
:param destination: Write the plist here.
:type destination: str or file object
"""
plistlib.writePlist(self.plist, destination)

View File

@ -87,9 +87,6 @@ def test_import_file_video():
assert helper.path_tz_fix(os.path.join('2015-01-Jan','California','2015-01-19_12-45-11-video.mov')) in dest_path, dest_path
def test_update_location_on_audio():
if not can_edit_exif():
raise SkipTest('avmetareadwrite executable not found')
temporary_folder, folder = helper.create_working_folder()
temporary_folder_destination, folder_destination = helper.create_working_folder()
@ -165,9 +162,6 @@ def test_update_location_on_text():
assert helper.isclose(metadata_processed['longitude'], -122.03635), metadata_processed['longitude']
def test_update_location_on_video():
if not can_edit_exif():
raise SkipTest('avmetareadwrite executable not found')
temporary_folder, folder = helper.create_working_folder()
temporary_folder_destination, folder_destination = helper.create_working_folder()
@ -193,9 +187,6 @@ def test_update_location_on_video():
assert helper.isclose(metadata_processed['longitude'], -122.03635), metadata_processed['longitude']
def test_update_time_on_audio():
if not can_edit_exif():
raise SkipTest('avmetareadwrite executable not found')
temporary_folder, folder = helper.create_working_folder()
temporary_folder_destination, folder_destination = helper.create_working_folder()
@ -268,9 +259,6 @@ def test_update_time_on_text():
assert metadata_processed['date_taken'] == helper.time_convert((2000, 1, 1, 12, 0, 0, 5, 1, 0)), metadata_processed['date_taken']
def test_update_time_on_video():
if not can_edit_exif():
raise SkipTest('avmetareadwrite executable not found')
temporary_folder, folder = helper.create_working_folder()
temporary_folder_destination, folder_destination = helper.create_working_folder()
@ -303,7 +291,3 @@ def restore_hash_db():
hash_db = '{}-test'.format(constants.hash_db)
if os.path.isfile(hash_db):
os.rename(hash_db, hash_db.replace('-test', ''))
def can_edit_exif():
video = Video()
return video.get_avmetareadwrite()

View File

@ -1,3 +1,4 @@
from __future__ import absolute_import
# Project imports
import os
import re
@ -11,7 +12,7 @@ import mock
sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))))
import helper
from . import helper
from elodie.filesystem import FileSystem
from elodie.media.media import Media
from elodie.media.photo import Photo
@ -342,9 +343,6 @@ def test_process_file_with_album_and_title_and_location():
# gh-89 (setting album then title reverts album)
def test_process_video_with_album_then_title():
if not can_edit_exif():
raise SkipTest('avmetareadwrite executable not found')
filesystem = FileSystem()
temporary_folder, folder = helper.create_working_folder()
@ -366,7 +364,3 @@ def test_process_video_with_album_then_title():
assert origin_checksum is not None, origin_checksum
assert origin_checksum != destination_checksum, destination_checksum
assert helper.path_tz_fix(os.path.join('2015-01-Jan','test_album','2015-01-19_12-45-11-movie-test_title.mov')) in destination, destination
def can_edit_exif():
video = Video()
return video.get_avmetareadwrite()

View File

@ -1,3 +1,7 @@
from __future__ import absolute_import
from __future__ import division
from builtins import range
from past.utils import old_div
# Project imports
import os
import random
@ -6,7 +10,7 @@ import sys
sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))))
import helper
from . import helper
from elodie import geolocation
os.environ['TZ'] = 'GMT'
@ -18,7 +22,7 @@ def test_decimal_to_dms():
target_decimal_value = random.uniform(0.0, 180.0)
if(x % 2 == 1):
target_decimal_value = target_decimal_value * -1
dms = geolocation.decimal_to_dms(target_decimal_value)
check_value = (dms[0] + dms[1] / 60 + dms[2] / 3600) * dms[3]
@ -34,7 +38,7 @@ def test_dms_string_latitude():
target_decimal_value = random.uniform(0.0, 180.0)
if(x % 2 == 1):
target_decimal_value = target_decimal_value * -1
dms = geolocation.decimal_to_dms(target_decimal_value)
dms_string = geolocation.dms_string(target_decimal_value, 'latitude')
@ -49,7 +53,7 @@ def test_dms_string_longitude():
target_decimal_value = random.uniform(0.0, 180.0)
if(x % 2 == 1):
target_decimal_value = target_decimal_value * -1
dms = geolocation.decimal_to_dms(target_decimal_value)
dms_string = geolocation.dms_string(target_decimal_value, 'longitude')

View File

@ -1,3 +1,7 @@
from __future__ import division
from __future__ import unicode_literals
from builtins import range
from past.utils import old_div
import hashlib
import os
import random
@ -12,7 +16,7 @@ from datetime import timedelta
def checksum(file_path, blocksize=65536):
hasher = hashlib.sha256()
with open(file_path, 'r') as f:
with open(file_path, 'rb') as f:
buf = f.read(blocksize)
while len(buf) > 0:
@ -73,7 +77,7 @@ def random_decimal():
def random_coordinate(coordinate, precision):
# Here we add to the decimal section of the coordinate by a given precision
return coordinate + ((10.0 / (10.0**precision)) * random_decimal())
return coordinate + ((old_div(10.0, (10.0**precision))) * random_decimal())
def temp_dir():
return tempfile.gettempdir()
@ -91,8 +95,8 @@ def is_windows():
def path_tz_fix(file_name):
if is_windows():
# Calculate the offset between UTC and local time
tz_shift = (datetime.fromtimestamp(0) -
datetime.utcfromtimestamp(0)).seconds/3600
tz_shift = old_div((datetime.fromtimestamp(0) -
datetime.utcfromtimestamp(0)).seconds,3600)
# replace timestamp in file_name
m = re.search('(\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2})',file_name)
t_date = datetime.fromtimestamp(time.mktime(time.strptime(m.group(0), '%Y-%m-%d_%H-%M-%S')))

View File

@ -1,10 +1,12 @@
from __future__ import print_function
from __future__ import absolute_import
# Project imports
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))))
import helper
from . import helper
from elodie.localstorage import Db
from elodie import constants
@ -154,10 +156,10 @@ def test_get_location_name_within_threshold():
latitude, longitude, name = helper.get_test_location()
db.add_location(latitude, longitude, name)
print latitude
print(latitude)
new_latitude = helper.random_coordinate(latitude, 4)
new_longitude = helper.random_coordinate(longitude, 4)
print new_latitude
print(new_latitude)
# 10 miles
retrieved_name = db.get_location_name(new_latitude, new_longitude, 1600*10)

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8
# Project imports
from __future__ import print_function
import os
import sys
@ -56,7 +57,6 @@ def test_get_date_taken():
audio = Audio(helper.get_file('audio.m4a'))
date_taken = audio.get_date_taken()
print '%r' % date_taken
assert date_taken == (2016, 1, 4, 5, 24, 15, 0, 19, 0), date_taken
def test_get_exiftool_attributes():
@ -77,9 +77,6 @@ def test_is_not_valid():
assert not audio.is_valid()
def test_set_date_taken():
if not can_edit_exif():
raise SkipTest('avmetareadwrite executable not found')
temporary_folder, folder = helper.create_working_folder()
origin = '%s/audio.m4a' % folder
@ -100,9 +97,6 @@ def test_set_date_taken():
assert date_taken == (2013, 9, 30, 7, 6, 5, 0, 273, 0), metadata['date_taken']
def test_set_location():
if not can_edit_exif():
raise SkipTest('avmetareadwrite executable not found')
temporary_folder, folder = helper.create_working_folder()
origin = '%s/audio.m4a' % folder
@ -129,9 +123,6 @@ def test_set_location():
assert helper.isclose(metadata['longitude'], 99.9999999999), metadata['longitude']
def test_set_location_minus():
if not can_edit_exif():
raise SkipTest('avmetareadwrite executable not found')
temporary_folder, folder = helper.create_working_folder()
origin = '%s/audio.m4a' % folder
@ -158,9 +149,6 @@ def test_set_location_minus():
assert helper.isclose(metadata['longitude'], -99.999999), metadata['longitude']
def test_set_title():
if not can_edit_exif():
raise SkipTest('avmetareadwrite executable not found')
temporary_folder, folder = helper.create_working_folder()
origin = '%s/audio.m4a' % folder
@ -181,9 +169,6 @@ def test_set_title():
assert metadata['title'] == 'my audio title', metadata['title']
def test_set_title_non_ascii():
if not can_edit_exif():
raise SkipTest('avmetareadwrite executable not found')
raise SkipTest('gh-27, non-ascii characters')
temporary_folder, folder = helper.create_working_folder()
@ -203,7 +188,3 @@ def test_set_title_non_ascii():
shutil.rmtree(folder)
assert metadata['title'] == '形声字 / 形聲字', metadata['title']
def can_edit_exif():
audio = Audio()
return audio.get_avmetareadwrite()

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8
# Project imports
from __future__ import unicode_literals
import os
import sys

View File

@ -101,9 +101,6 @@ def test_set_album():
assert metadata_new['album'] == 'Test Album', metadata_new['album']
def test_set_date_taken():
if not can_edit_exif():
raise SkipTest('avmetareadwrite executable not found')
temporary_folder, folder = helper.create_working_folder()
origin = '%s/video.mov' % folder
@ -124,9 +121,6 @@ def test_set_date_taken():
assert date_taken == (2013, 9, 30, 7, 6, 5, 0, 273, 0), metadata['date_taken']
def test_set_location():
if not can_edit_exif():
raise SkipTest('avmetareadwrite executable not found')
temporary_folder, folder = helper.create_working_folder()
origin = '%s/video.mov' % folder
@ -153,9 +147,6 @@ def test_set_location():
assert helper.isclose(metadata['longitude'], 99.9999999999), metadata['longitude']
def test_set_title():
if not can_edit_exif():
raise SkipTest('avmetareadwrite executable not found')
temporary_folder, folder = helper.create_working_folder()
origin = '%s/video.mov' % folder
@ -176,9 +167,6 @@ def test_set_title():
assert metadata['title'] == 'my video title', metadata['title']
def test_set_title_non_ascii():
if not can_edit_exif():
raise SkipTest('avmetareadwrite executable not found')
raise SkipTest('gh-27, non-ascii characters')
temporary_folder, folder = helper.create_working_folder()
@ -198,7 +186,3 @@ def test_set_title_non_ascii():
shutil.rmtree(folder)
assert metadata['title'] == '形声字 / 形聲字', metadata['title']
def can_edit_exif():
video = Video()
return video.get_avmetareadwrite()

View File

@ -1,3 +1,4 @@
click>=6.2,<7.0
requests>=2.9.1,<3.0
send2trash>=1.3.0,<2.0
future