From 48a1f64687fa55b44f7ea9f809500a31b398fc0f Mon Sep 17 00:00:00 2001 From: Fabrice Laporte Date: Tue, 29 Dec 2015 14:13:47 +0100 Subject: [PATCH 1/7] document config.ini expected location --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a99c18f..e97c467 100644 --- a/README.md +++ b/README.md @@ -188,4 +188,4 @@ If you have problems you can run the following commands which the fine folks at I use MapQuest to help me organize your photos by location. You'll need to sign up for a [free developer account](https://developer.mapquest.com/plan_purchase/steps/business_edition/business_edition_free) and get an API key. They give you 15,000 calls per month so I can't do any more than that unless you shell out some big bucks to them. Once I hit my limit the best I'll be able to do is *Unknown Location* until the following month. -Once you sign up you'll have to get an API key and copy it into a file named `config.ini` at the root of the repository. I've included a `config.ini-sample` file which you can copy to `config.ini`. +Once you sign up you'll have to get an API key and copy it into a file named `~/.elodie/config.ini`. I've included a `config.ini-sample` file which you can copy to `config.ini`. From 4676c154b6a14fd8389c2783e85747b3c6efb438 Mon Sep 17 00:00:00 2001 From: Fabrice Laporte Date: Wed, 30 Dec 2015 01:20:02 +0100 Subject: [PATCH 2/7] remove unused imports --- elodie.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/elodie.py b/elodie.py index e92dd9c..e4979f7 100755 --- a/elodie.py +++ b/elodie.py @@ -1,16 +1,12 @@ #!/usr/bin/env python import os -import pyexiv2 import re -import shutil import sys -import time from datetime import datetime from docopt import docopt -from elodie import arguments from elodie import constants from elodie import geolocation from elodie.media.photo import Media From 4ca2013572562c380374355b3fad6acf84ff70f9 Mon Sep 17 00:00:00 2001 From: Fabrice Laporte Date: Wed, 30 Dec 2015 01:23:38 +0100 Subject: [PATCH 3/7] refactor duplicated code the import code was duplicated in if/else branches and has been moved into a specific import_file() function --- elodie.py | 61 +++++++++++++++++++++++++------------------------------ 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/elodie.py b/elodie.py index e4979f7..84991ee 100755 --- a/elodie.py +++ b/elodie.py @@ -15,6 +15,7 @@ from elodie.media.video import Video from elodie.filesystem import FileSystem from elodie.localstorage import Db + def usage(): return """Usage: elodie.py import --source= --destination= [--album-from-folder] elodie.py import --file= --destination= [--album-from-folder] @@ -23,45 +24,39 @@ def usage(): -h --help show this """ + +def import_file(_file, destination): + media = Media.get_class_by_file(_file, (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 + return + + if media.__name__ == 'Video': + filesystem.set_date_from_path_video(media) + + if params['--album-from-folder']: + media.set_album_from_folder() + + dest_path = filesystem.process_file(_file, destination, + media, allowDuplicate=False, move=False) + if dest_path: + print '%s -> %s' % (_file, dest_path) + + def _import(params): destination = os.path.expanduser(params['--destination']) - if(params['--source'] is not None): + if params['--source']: source = os.path.expanduser(params['--source']) + files = filesystem.get_all_files(source, None) + elif params['--file']: + files = [os.path.expanduser(params['--file'])] - for current_file in filesystem.get_all_files(source, None): - media = Media.get_class_by_file(current_file, [Photo, Video]) - if(media is None): - continue + for current_file in files: + import_file(current_file, destination) - if(media.__name__ == 'Video'): - filesystem.set_date_from_path_video(media) - - if(params['--album-from-folder'] == True): - media.set_album_from_folder() - - dest_path = filesystem.process_file(current_file, destination, media, allowDuplicate=False, move=False) - if(dest_path is not None): - print '%s -> %s' % (current_file, dest_path) - elif(params['--file'] is not None): - current_file = os.path.expanduser(params['--file']) - media = Media.get_class_by_file(current_file, [Photo, Video]) - - if(media is None): - if(constants.debug == True): - print 'Not a supported file (%s)' % current_file - print '{"source":"%s", "error_msg":"Not a supported file"}' % current_file - return - - if(media.__name__ == 'Video'): - filesystem.set_date_from_path_video(media) - - if(params['--album-from-folder'] == True): - media.set_album_from_folder() - - dest_path = filesystem.process_file(current_file, destination, media, allowDuplicate=False, move=False) - if(dest_path is not None): - print '%s -> %s' % (current_file, dest_path) def _update(params): location_coords = None From 8a7725f68e48f54af77b0a6429f96c24688a5a24 Mon Sep 17 00:00:00 2001 From: Fabrice Laporte Date: Wed, 30 Dec 2015 01:30:53 +0100 Subject: [PATCH 4/7] pythonify elodie.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove C like parentheses around conditions. Replace “xxx is None” => “not x”. Use tuple when list content won’t change. Fix some others PEP8 warnings. --- elodie.py | 70 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/elodie.py b/elodie.py index 84991ee..8b35106 100755 --- a/elodie.py +++ b/elodie.py @@ -61,35 +61,39 @@ def _import(params): def _update(params): location_coords = None for file_path in params['INPUT']: - if(not os.path.exists(file_path)): - if(constants.debug == True): + 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 '{"source":"%s", "error_msg":"Could not find %s"}' % \ + (file_path, file_path) continue file_path = os.path.expanduser(file_path) - destination = os.path.expanduser(os.path.dirname(os.path.dirname(os.path.dirname(file_path)))) + 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]) - if(media is None): + media = Media.get_class_by_file(file_path, (Photo, Video)) + if not media: continue updated = False - if(params['--location'] is not None): - if(location_coords is None): - location_coords = geolocation.coordinates_by_name(params['--location']) + if params['--location']: + if not location_coords: + location_coords = geolocation.coordinates_by_name( + params['--location']) - if(location_coords is not None and 'latitude' in location_coords and 'longitude' in location_coords): - location_status = media.set_location(location_coords['latitude'], location_coords['longitude']) - if(location_status != True): - if(constants.debug == True): + if location_coords and 'latitude' in location_coords and \ + 'longitude' in location_coords: + location_status = media.set_location(location_coords[ + 'latitude'], location_coords['longitude']) + if not location_status: + if constants.debug: print 'Failed to update location' - print '{"source":"%s", "error_msg":"Failed to update location"}' % file_path + print '{"source":"%s","error_msg":"Failed to update location"}' % file_path sys.exit(1) updated = True - - if(params['--time'] is not None): + if params['--time']: time_string = params['--time'] time_format = '%Y-%m-%d %H:%M:%S' if(re.match('^\d{4}-\d{2}-\d{2}$', time_string)): @@ -106,40 +110,42 @@ def _update(params): media.set_date_taken(time) updated = True - if(params['--album'] is not None): + if params['--album']: media.set_album(params['--album']) updated = True # Updating a title can be problematic when doing it 2+ times on a file. - # You would end up with img_001.jpg -> img_001-first-title.jpg -> img_001-first-title-second-title.jpg. + # You would end up with img_001.jpg -> img_001-first-title.jpg -> + # img_001-first-title-second-title.jpg. # To resolve that we have to track the prior title (if there was one. - # Then we massage the updated_media's metadata['base_name'] to remove the old title. - # Since FileSystem.get_file_name() relies on base_name it will properly rename the file by updating the title - # instead of appending it. + # Then we massage the updated_media's metadata['base_name'] to remove + # the old title. + # Since FileSystem.get_file_name() relies on base_name it will properly + # rename the file by updating the title instead of appending it. remove_old_title_from_name = False - if(params['--title'] is not None): + if params['--title']: # We call get_metadata() to cache it before making any changes metadata = media.get_metadata() title_update_status = media.set_title(params['--title']) original_title = metadata['title'] - if(title_update_status and original_title is not None): + if title_update_status and original_title: # @TODO: We should move this to a shared method since FileSystem.get_file_name() does it too. original_title = re.sub('\W+', '-', original_title.lower()) original_base_name = metadata['base_name'] remove_old_title_from_name = True updated = True - - if(updated == True): - updated_media = Media.get_class_by_file(file_path, [Photo, Video]) + + if updated: + updated_media = Media.get_class_by_file(file_path, (Photo, Video)) # See comments above on why we have to do this when titles get updated. - if(remove_old_title_from_name is True and len(original_title) > 0): + if remove_old_title_from_name and len(original_title) > 0: updated_media.get_metadata() - updated_media.set_metadata_basename(original_base_name.replace('-%s' % original_title, '')) - - dest_path = filesystem.process_file(file_path, destination, updated_media, move=True, allowDuplicate=True) - if(constants.debug == True): - print '%s -> %s' % (file_path, dest_path) + updated_media.set_metadata_basename( + original_base_name.replace('-%s' % original_title, '')) + dest_path = filesystem.process_file(file_path, destination, + updated_media, move=True, allowDuplicate=True) + if constants.debug: 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)) From 5452aa9adb73022d94d9d48d83f52f0a8b3f46cd Mon Sep 17 00:00:00 2001 From: Fabrice Laporte Date: Wed, 30 Dec 2015 01:32:29 +0100 Subject: [PATCH 5/7] cleanup complex if/else conditions --- elodie.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/elodie.py b/elodie.py index 8b35106..74e920f 100755 --- a/elodie.py +++ b/elodie.py @@ -96,19 +96,18 @@ def _update(params): if params['--time']: time_string = params['--time'] time_format = '%Y-%m-%d %H:%M:%S' - if(re.match('^\d{4}-\d{2}-\d{2}$', time_string)): + if re.match('^\d{4}-\d{2}-\d{2}$', time_string): time_string = '%s 00:00:00' % time_string - if(re.match('^\d{4}-\d{2}-\d{2}$', time_string) is None and re.match('^\d{4}-\d{2}-\d{2} \d{2}:\d{2}\d{2}$', time_string)): - if(constants.debug == True): + elif re.match('^\d{4}-\d{2}-\d{2} \d{2}:\d{2}\d{2}$', time_string): + if constants.debug: print 'Invalid time format. Use YYYY-mm-dd hh:ii:ss or YYYY-mm-dd' print '{"source":"%s", "error_msg":"Invalid time format. Use YYYY-mm-dd hh:ii:ss or YYYY-mm-dd"}' % file_path sys.exit(1) - if(time_format is not None): - time = datetime.strptime(time_string, time_format) - media.set_date_taken(time) - updated = True + time = datetime.strptime(time_string, time_format) + media.set_date_taken(time) + updated = True if params['--album']: media.set_album(params['--album']) @@ -146,6 +145,7 @@ def _update(params): 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) # 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)) @@ -157,8 +157,8 @@ filesystem = FileSystem() if __name__ == '__main__': params = docopt(usage()) - if(params['import'] == True): + if params['import']: _import(params) - elif(params['update'] == True): + elif params['update']: _update(params) sys.exit(0) From 636b43fca4a65a79ba4c7ba50e4995817489cfad Mon Sep 17 00:00:00 2001 From: Fabrice Laporte Date: Wed, 30 Dec 2015 23:52:01 +0100 Subject: [PATCH 6/7] move some code out of _update() --- elodie.py | 69 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/elodie.py b/elodie.py index 74e920f..4e1488a 100755 --- a/elodie.py +++ b/elodie.py @@ -56,10 +56,45 @@ def _import(params): for current_file in files: import_file(current_file, destination) +def update_location(media, file_path, location_name): + """Update location exif metadata of media. + """ + location_coords = geolocation.coordinates_by_name(location_name) + + if location_coords and 'latitude' in location_coords and \ + 'longitude' in location_coords: + location_status = media.set_location(location_coords[ + '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"}') + sys.exit(1) + return True + + +def update_time(media, file_path, time_string): + """Update time exif metadata of media. + """ + time_format = '%Y-%m-%d %H:%M:%S' + if re.match(r'^\d{4}-\d{2}-\d{2}$', time_string): + time_string = '%s 00:00:00' % 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) + sys.exit(1) + + time = datetime.strptime(time_string, time_format) + media.set_date_taken(time) + return True def _update(params): - location_coords = None + """Update files. + """ for file_path in params['INPUT']: if not os.path.exists(file_path): if constants.debug: @@ -78,37 +113,9 @@ def _update(params): updated = False if params['--location']: - if not location_coords: - location_coords = geolocation.coordinates_by_name( - params['--location']) - - if location_coords and 'latitude' in location_coords and \ - 'longitude' in location_coords: - location_status = media.set_location(location_coords[ - 'latitude'], location_coords['longitude']) - if not location_status: - if constants.debug: - print 'Failed to update location' - print '{"source":"%s","error_msg":"Failed to update location"}' % file_path - sys.exit(1) - updated = True - + updated = update_location(media, file_path, params['--location']) if params['--time']: - time_string = params['--time'] - time_format = '%Y-%m-%d %H:%M:%S' - if re.match('^\d{4}-\d{2}-\d{2}$', time_string): - time_string = '%s 00:00:00' % time_string - - elif re.match('^\d{4}-\d{2}-\d{2} \d{2}:\d{2}\d{2}$', time_string): - if constants.debug: - print 'Invalid time format. Use YYYY-mm-dd hh:ii:ss or YYYY-mm-dd' - print '{"source":"%s", "error_msg":"Invalid time format. Use YYYY-mm-dd hh:ii:ss or YYYY-mm-dd"}' % file_path - sys.exit(1) - - time = datetime.strptime(time_string, time_format) - media.set_date_taken(time) - updated = True - + updated = update_time(media, file_path, params['--time']) if params['--album']: media.set_album(params['--album']) updated = True From 02bae17e953cce9769710c2d77f3219c4bc7ee95 Mon Sep 17 00:00:00 2001 From: Fabrice Laporte Date: Thu, 31 Dec 2015 00:29:13 +0100 Subject: [PATCH 7/7] fix PEP8 warnings --- elodie.py | 62 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/elodie.py b/elodie.py index 4e1488a..abe307c 100755 --- a/elodie.py +++ b/elodie.py @@ -17,15 +17,23 @@ from elodie.localstorage import Db def usage(): - return """Usage: elodie.py import --source= --destination= [--album-from-folder] + """Return usage message + """ + return """ +Usage: elodie.py import --source= --destination= [--album-from-folder] elodie.py import --file= --destination= [--album-from-folder] elodie.py update [--time=] [--location=] [--album=] [--title=] INPUT ... -h --help show this - """ +""" + +DB = Db() +FILESYSTEM = FileSystem() -def import_file(_file, destination): +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)) if not media: if constants.debug: @@ -34,28 +42,32 @@ def import_file(_file, destination): return if media.__name__ == 'Video': - filesystem.set_date_from_path_video(media) + FILESYSTEM.set_date_from_path_video(media) - if params['--album-from-folder']: + if album_from_folder: media.set_album_from_folder() - dest_path = filesystem.process_file(_file, destination, + dest_path = FILESYSTEM.process_file(_file, destination, media, allowDuplicate=False, move=False) if dest_path: print '%s -> %s' % (_file, dest_path) def _import(params): + """Import files. + """ destination = os.path.expanduser(params['--destination']) if params['--source']: source = os.path.expanduser(params['--source']) - files = filesystem.get_all_files(source, None) + files = FILESYSTEM.get_all_files(source, None) elif params['--file']: files = [os.path.expanduser(params['--file'])] for current_file in files: - import_file(current_file, destination) + import_file(current_file, destination, params['--album-from-folder']) + + def update_location(media, file_path, location_name): """Update location exif metadata of media. """ @@ -135,37 +147,47 @@ def _update(params): title_update_status = media.set_title(params['--title']) original_title = metadata['title'] if title_update_status and original_title: - # @TODO: We should move this to a shared method since FileSystem.get_file_name() does it too. - original_title = re.sub('\W+', '-', original_title.lower()) + # @TODO: We should move this to a shared method since + # FileSystem.get_file_name() does it too. + original_title = re.sub(r'\W+', '-', original_title.lower()) original_base_name = metadata['base_name'] remove_old_title_from_name = True updated = True if updated: updated_media = Media.get_class_by_file(file_path, (Photo, Video)) - # See comments above on why we have to do this when titles get updated. + # 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: updated_media.get_metadata() updated_media.set_metadata_basename( original_base_name.replace('-%s' % original_title, '')) - dest_path = filesystem.process_file(file_path, destination, + 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) - # 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)) - filesystem.delete_directory_if_empty(os.path.dirname(os.path.dirname(file_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)) + FILESYSTEM.delete_directory_if_empty( + os.path.dirname(os.path.dirname(file_path))) -db = Db() -filesystem = FileSystem() - -if __name__ == '__main__': +def main(argv=None): + """Main function call elodie subcommand on files. + """ + if argv is None: + argv = sys.argv params = docopt(usage()) if params['import']: _import(params) elif params['update']: _update(params) sys.exit(0) + + +if __name__ == '__main__': + sys.exit(main())