diff --git a/elodie.py b/elodie.py index faa9977..623d6fa 100755 --- a/elodie.py +++ b/elodie.py @@ -10,6 +10,7 @@ from docopt import docopt from elodie import constants from elodie import geolocation from elodie.media.media import Media +from elodie.media.audio import Audio from elodie.media.photo import Photo from elodie.media.video import Video from elodie.filesystem import FileSystem @@ -34,7 +35,7 @@ FILESYSTEM = FileSystem() def import_file(_file, destination, album_from_folder): """Set file metadata and move it to destination. """ - media = Media.get_class_by_file(_file, (Photo, Video)) + media = Media.get_class_by_file(_file, [Audio, Photo, Video]) if not media: if constants.debug: print 'Not a supported file (%s)' % _file @@ -119,7 +120,7 @@ def _update(params): destination = os.path.expanduser(os.path.dirname(os.path.dirname( os.path.dirname(file_path)))) - media = Media.get_class_by_file(file_path, (Photo, Video)) + media = Media.get_class_by_file(file_path, [Audio, Photo, Video]) if not media: continue @@ -155,7 +156,7 @@ def _update(params): updated = True if updated: - updated_media = Media.get_class_by_file(file_path, (Photo, Video)) + updated_media = Media.get_class_by_file(file_path, [Audio, Photo, Video]) # See comments above on why we have to do this when titles # get updated. if remove_old_title_from_name and len(original_title) > 0: diff --git a/elodie/geolocation.py b/elodie/geolocation.py index 458b398..7318e99 100644 --- a/elodie/geolocation.py +++ b/elodie/geolocation.py @@ -92,7 +92,8 @@ def decimal_to_dms(decimal, signed=True): return ( pyexiv2.Rational(degrees, 1), pyexiv2.Rational(minutes, 1), - pyexiv2.Rational(subseconds_fraction.numerator, subseconds_fraction.denominator) # noqa + pyexiv2.Rational(subseconds_fraction.numerator, + subseconds_fraction.denominator) ) diff --git a/elodie/media/audio.py b/elodie/media/audio.py new file mode 100644 index 0000000..e373916 --- /dev/null +++ b/elodie/media/audio.py @@ -0,0 +1,21 @@ +""" +Author: Jaisen Mathai +Audio package that handles all audio operations +Inherits from Video package +""" + +from elodie import constants +from elodie import plist_parser +from media import Media +from video import Video + + +class Audio(Video): + __name__ = 'Audio' + extensions = ('m4a') + + """ + @param, source, string, The fully qualified path to the audio file + """ + def __init__(self, source=None): + super(Audio, self).__init__(source) diff --git a/elodie/media/video.py b/elodie/media/video.py index ea2d65e..f3d6ace 100644 --- a/elodie/media/video.py +++ b/elodie/media/video.py @@ -27,6 +27,7 @@ class Video(Media): """ @param, source, string, The fully qualified path to the video file + @param, Audio, class or none, The Audio class if being extendted by the Audio class """ def __init__(self, source=None): super(Video, self).__init__(source) @@ -375,7 +376,7 @@ class Video(Media): # Before we do anything destructive we confirm that the # file is in tact. - check_media = Video(temp_movie) + check_media = Media.get_class_by_file(temp_movie, [self.__class__]) check_metadata = check_media.get_metadata() if( ( diff --git a/elodie/tests/files/audio.m4a b/elodie/tests/files/audio.m4a new file mode 100644 index 0000000..c60a808 Binary files /dev/null and b/elodie/tests/files/audio.m4a differ diff --git a/elodie/tests/media/audio_test.py b/elodie/tests/media/audio_test.py new file mode 100644 index 0000000..52bee1d --- /dev/null +++ b/elodie/tests/media/audio_test.py @@ -0,0 +1,177 @@ +# -*- coding: utf-8 +# Project imports +import os +import sys + +import shutil +import tempfile +import time +import datetime + +from nose.plugins.skip import SkipTest + +sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))))) +sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))) + +import helper +from elodie.media.media import Media +from elodie.media.video import Video +from elodie.media.audio import Audio + +os.environ['TZ'] = 'GMT' + +def test_audio_extensions(): + audio = Audio() + extensions = audio.extensions + + assert 'm4a' in extensions + + valid_extensions = Audio.get_valid_extensions() + + assert extensions == valid_extensions, valid_extensions + +def test_get_coordinate(): + audio = Audio(helper.get_file('audio.m4a')) + coordinate = audio.get_coordinate() + + assert coordinate == 29.75893888888889, coordinate + +def test_get_coordinate_latitude(): + audio = Audio(helper.get_file('audio.m4a')) + coordinate = audio.get_coordinate('latitude') + + assert coordinate == 29.75893888888889, coordinate + +def test_get_coordinate_longitude(): + audio = Audio(helper.get_file('audio.m4a')) + coordinate = audio.get_coordinate('longitude') + + assert coordinate == -95.3677, coordinate + +def test_get_date_taken(): + raise SkipTest('gh-32 this test fails on travisci') + 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_exif(): + audio = Audio(helper.get_file('audio.m4a')) + exif = audio.get_exif() + + assert exif is not None, exif + +def test_is_valid(): + audio = Audio(helper.get_file('audio.m4a')) + + assert audio.is_valid() + +def test_is_not_valid(): + audio = Audio(helper.get_file('text.txt')) + + 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 + shutil.copyfile(helper.get_file('audio.m4a'), origin) + + audio = Audio(origin) + status = audio.set_date_taken(datetime.datetime(2013, 9, 30, 7, 6, 5)) + + assert status == True, status + + audio_new = Audio(origin) + metadata = audio_new.get_metadata() + + date_taken = metadata['date_taken'] + + shutil.rmtree(folder) + + 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') + + raise SkipTest('gh-31, precision is lost in conversion from decimal to dms') + temporary_folder, folder = helper.create_working_folder() + + origin = '%s/audio.m4a' % folder + shutil.copyfile(helper.get_file('audio.m4a'), origin) + + audio = Audio(origin) + origin_metadata = audio.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 = audio.set_location(11.1111111111, 99.9999999999) + + assert status == True, status + + audio_new = Audio(origin) + metadata = audio_new.get_metadata() + + shutil.rmtree(folder) + + # @TODO: understand why the decimal to degree conversion loses accuracy + assert metadata['latitude'] == 11.1111111111, metadata['latitude'] + assert 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/audio.m4a' % folder + shutil.copyfile(helper.get_file('audio.m4a'), origin) + + audio = Audio(origin) + origin_metadata = audio.get_metadata() + + status = audio.set_title('my audio title') + + assert status == True, status + + audio_new = Audio(origin) + metadata = audio_new.get_metadata() + + shutil.rmtree(folder) + + 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() + + origin = '%s/audio.m4a' % folder + shutil.copyfile(helper.get_file('audio.m4a'), origin) + + audio = Audio(origin) + origin_metadata = audio.get_metadata() + + status = audio.set_title('形声字 / 形聲字') + + assert status == True, status + + audio_new = Audio(origin) + metadata = audio_new.get_metadata() + + shutil.rmtree(folder) + + assert metadata['title'] == '形声字 / 形聲字', metadata['title'] + +def can_edit_exif(): + audio = Audio() + return audio.get_avmetareadwrite()