From a9a6e4b7048f998cf29a4c09f58580666aae2c3d Mon Sep 17 00:00:00 2001 From: Denis Zamataev Date: Wed, 26 Dec 2018 16:21:22 +0300 Subject: [PATCH 1/9] Implements prefer english names setting in config.ini which adds English as accepted header to MapQuest requests and results in the response location names being mostly in English not their native country language. --- Readme.md | 2 ++ config.ini-sample | 1 + elodie/geolocation.py | 24 +++++++++++++++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 10d5568b..6ee8c5a7 100644 --- a/Readme.md +++ b/Readme.md @@ -342,6 +342,8 @@ cp config.ini-sample ~/.elodie/config.ini # now you're ready to add your MapQuest key ``` +You may also prefer your locations to be named in English language, not the local language. Then just set the following key in `config.ini` like so: `prefer_english_names=True`. + ## Questions, comments or concerns? The best ways to provide feedback is by opening a [GitHub issue](https://github.com/jmathai/elodie/issues) or emailing me at [jaisen@jmathai.com](mailto:jaisen@jmathai.com). diff --git a/config.ini-sample b/config.ini-sample index 30098afb..bf729c31 100644 --- a/config.ini-sample +++ b/config.ini-sample @@ -1,2 +1,3 @@ [MapQuest] key=your-api-key-goes-here +prefer_english_names=False \ No newline at end of file diff --git a/elodie/geolocation.py b/elodie/geolocation.py index 2af91884..f5875d69 100644 --- a/elodie/geolocation.py +++ b/elodie/geolocation.py @@ -20,6 +20,7 @@ __KEY__ = None __DEFAULT_LOCATION__ = 'Unknown Location' +__PREFER_ENGLISH_NAMES__ = None def coordinates_by_name(name): @@ -114,6 +115,25 @@ def get_key(): __KEY__ = config['MapQuest']['key'] return __KEY__ +def get_prefer_english_names(): + global __PREFER_ENGLISH_NAMES__ + if __PREFER_ENGLISH_NAMES__ is not None: + return __PREFER_ENGLISH_NAMES__ + + config_file = '%s/config.ini' % constants.application_directory + if not path.exists(config_file): + return False + + config = load_config() + if('MapQuest' not in config): + return False + + if('prefer_english_names' not in config['MapQuest']): + return False + + s = config['MapQuest']['prefer_english_names'] + __PREFER_ENGLISH_NAMES__ = s in ['True', 'true', '1', 't', 'y', 'yes', 'Yes'] + return __PREFER_ENGLISH_NAMES__ def place_name(lat, lon): lookup_place_name_default = {'default': __DEFAULT_LOCATION__} @@ -167,6 +187,7 @@ def lookup(**kwargs): return None key = get_key() + prefer_english_names = get_prefer_english_names() if(key is None): return None @@ -181,7 +202,8 @@ def lookup(**kwargs): path, urllib.parse.urlencode(params) ) - r = requests.get(url) + headers = {'Accept-Language':'en-EN,en;q=0.8'} if prefer_english_names else {} + r = requests.get(url, headers=headers) return parse_result(r.json()) except requests.exceptions.RequestException as e: log.error(e) From 120b475ba15940403675de47f342c4ea8553e0e5 Mon Sep 17 00:00:00 2001 From: Denis Zamataev Date: Fri, 28 Dec 2018 13:18:15 +0300 Subject: [PATCH 2/9] apply suggested changes by @jmathai --- Readme.md | 2 +- elodie/geolocation.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Readme.md b/Readme.md index 6ee8c5a7..e76643f1 100644 --- a/Readme.md +++ b/Readme.md @@ -342,7 +342,7 @@ cp config.ini-sample ~/.elodie/config.ini # now you're ready to add your MapQuest key ``` -You may also prefer your locations to be named in English language, not the local language. Then just set the following key in `config.ini` like so: `prefer_english_names=True`. +If you're an english speaker then you will probably want to add `prefer_english_names=True` to the `[MapQuest]` section else you'll have cities named using the local language. ## Questions, comments or concerns? diff --git a/elodie/geolocation.py b/elodie/geolocation.py index f5875d69..31bbf59b 100644 --- a/elodie/geolocation.py +++ b/elodie/geolocation.py @@ -131,8 +131,7 @@ def get_prefer_english_names(): if('prefer_english_names' not in config['MapQuest']): return False - s = config['MapQuest']['prefer_english_names'] - __PREFER_ENGLISH_NAMES__ = s in ['True', 'true', '1', 't', 'y', 'yes', 'Yes'] + __PREFER_ENGLISH_NAMES__ = bool(config['MapQuest']['prefer_english_names']) return __PREFER_ENGLISH_NAMES__ def place_name(lat, lon): @@ -202,7 +201,9 @@ def lookup(**kwargs): path, urllib.parse.urlencode(params) ) - headers = {'Accept-Language':'en-EN,en;q=0.8'} if prefer_english_names else {} + headers = {} + if(prefer_english_names): + headers = {'Accept-Language':'en-EN,en;q=0.8'} r = requests.get(url, headers=headers) return parse_result(r.json()) except requests.exceptions.RequestException as e: From b94372b425a910c89e697a77882a1d8b2a224f94 Mon Sep 17 00:00:00 2001 From: Denis Zamataev Date: Fri, 28 Dec 2018 14:18:12 +0300 Subject: [PATCH 3/9] Implements separating media folders by command line argument. --- Readme.md | 2 ++ elodie.py | 5 ++++- elodie/constants.py | 3 +++ elodie/filesystem.py | 9 +++++++-- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Readme.md b/Readme.md index e76643f1..0f398941 100644 --- a/Readme.md +++ b/Readme.md @@ -282,6 +282,8 @@ You can, of course, ask me to change the location and time. I'll happily update ./elodie.py update --location="Las Vegas, NV" --time="2015-04-15" /where/i/want/my/photos/to/go/2015-09-Sep/Unknown\ Location/2015-09-27_01-41-38-_dsc8705.dng /where/i/want/my/photos/to/go/2015-09-Sep/Unknown\ Location/2015-09-27_01-41-38-_dsc8705.nef ``` +To put media according to it's type to a folder like 'Audio', 'Video' or 'Photo' at it's final destination add `--separate-media-folders`. + ## What about photos I take in the future? Organizing your existing photos is great. But I'd be lying if I said I was the only one who could help you with that. Unlike other programs I put the same effort into keeping your library organized into the future as I have in getting it organized in the first place. diff --git a/elodie.py b/elodie.py index 54c69ee6..ea571827 100755 --- a/elodie.py +++ b/elodie.py @@ -85,11 +85,14 @@ def import_file(_file, destination, album_from_folder, trash, allow_duplicates): help='Import the file even if it\'s already been imported.') @click.option('--debug', default=False, is_flag=True, help='Override the value in constants.py with True.') +@click.option('--separate-media-folders', default=False, is_flag=True, + help='Separate folders for audio and video at their final destination. Overrides the value in constants.py with True.') @click.argument('paths', nargs=-1, type=click.Path()) -def _import(destination, source, file, album_from_folder, trash, allow_duplicates, debug, paths): +def _import(destination, source, file, album_from_folder, trash, allow_duplicates, debug, separate_media_folders, paths): """Import files or directories by reading their EXIF and organizing them accordingly. """ constants.debug = debug + constants.separate_media_folders = separate_media_folders has_errors = False result = Result() diff --git a/elodie/constants.py b/elodie/constants.py index 7b4e6f9c..e7a895f5 100644 --- a/elodie/constants.py +++ b/elodie/constants.py @@ -28,3 +28,6 @@ # check python version, required in filesystem.py to trigger appropriate method python_version = version_info.major + +# Separate folders for audio and video at their final destination. +separate_media_folders = False \ No newline at end of file diff --git a/elodie/filesystem.py b/elodie/filesystem.py index 6c5d83e1..3028e5db 100644 --- a/elodie/filesystem.py +++ b/elodie/filesystem.py @@ -14,6 +14,7 @@ from elodie import compatability from elodie import geolocation from elodie import log +from elodie import constants from elodie.config import load_config from elodie.localstorage import Db from elodie.media.base import Base, get_all_subclasses @@ -215,7 +216,7 @@ def get_folder_path_definition(self): return self.cached_folder_path_definition - def get_folder_path(self, metadata): + def get_folder_path(self, metadata, media_name=None): """Given a media's metadata this function returns the folder path as a string. :param metadata dict: Metadata dictionary. @@ -258,6 +259,9 @@ def get_folder_path(self, metadata): elif part.startswith('"') and part.endswith('"'): path.append(part[1:-1]) + if (constants.separate_media_folders and media_name): + path.append(media_name) + return os.path.join(*path) def parse_mask_for_location(self, mask, location_parts, place_name): @@ -335,8 +339,9 @@ def process_file(self, _file, destination, media, **kwargs): media.set_original_name() metadata = media.get_metadata() + media_name = media.__name__ - directory_name = self.get_folder_path(metadata) + directory_name = self.get_folder_path(metadata, media_name) dest_directory = os.path.join(destination, directory_name) file_name = self.get_file_name(media) From 03d4a9ca0a0d6576ee7aee2bccae913183a2cc8e Mon Sep 17 00:00:00 2001 From: Denis Zamataev Date: Tue, 15 Jan 2019 12:40:43 +0300 Subject: [PATCH 4/9] update readme to include information about --separate-media-folders command line option --- Readme.md | 1 + elodie.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 0f398941..98c7442f 100644 --- a/Readme.md +++ b/Readme.md @@ -126,6 +126,7 @@ Options: --trash After copying files, move the old files to the trash. --allow-duplicates Import the file even if it's already been imported. + --separate-media-folders Put files in separate folders for photos, audio and video at their final destination. --help Show this message and exit. ``` diff --git a/elodie.py b/elodie.py index ea571827..8dd0130c 100755 --- a/elodie.py +++ b/elodie.py @@ -86,7 +86,7 @@ def import_file(_file, destination, album_from_folder, trash, allow_duplicates): @click.option('--debug', default=False, is_flag=True, help='Override the value in constants.py with True.') @click.option('--separate-media-folders', default=False, is_flag=True, - help='Separate folders for audio and video at their final destination. Overrides the value in constants.py with True.') + help='Put files in separate folders for photos, audio and video at their final destination. Overrides the value in constants.py with True.') @click.argument('paths', nargs=-1, type=click.Path()) def _import(destination, source, file, album_from_folder, trash, allow_duplicates, debug, separate_media_folders, paths): """Import files or directories by reading their EXIF and organizing them accordingly. From 580745941cc40350b6bdf3bcde0b875ca9371c38 Mon Sep 17 00:00:00 2001 From: Jaisen Mathai Date: Thu, 17 Jan 2019 21:34:43 -0800 Subject: [PATCH 5/9] Add tests for prefer_english_names --- elodie/tests/geolocation_test.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/elodie/tests/geolocation_test.py b/elodie/tests/geolocation_test.py index c97e757e..5b754361 100644 --- a/elodie/tests/geolocation_test.py +++ b/elodie/tests/geolocation_test.py @@ -112,6 +112,16 @@ def test_lookup_with_valid_key(): assert latLng['lat'] == 37.36883, latLng assert latLng['lng'] == -122.03635, latLng +@mock.patch('elodie.geolocation.__PREFER_ENGLISH_NAMES__', True) +def test_lookup_with_prefer_english_names_true(): + res = geolocation.lookup(lat=55.66333, lon=37.61583) + assert res['address']['city'] == 'Nagorny District', res + +@mock.patch('elodie.geolocation.__PREFER_ENGLISH_NAMES__', False) +def test_lookup_with_prefer_english_names_false(): + res = geolocation.lookup(lat=55.66333, lon=37.61583) + assert res['address']['city'] == u'\u041d\u0430\u0433\u043e\u0440\u043d\u044b\u0439 \u0440\u0430\u0439\u043e\u043d', res + @mock.patch('elodie.constants.location_db', '%s/location.json-cached' % gettempdir()) def test_place_name_deprecated_string_cached(): # See gh-160 for backwards compatability needed when a string is stored instead of a dict From 96a47c6dd21969070653c87d2f93758b695f9085 Mon Sep 17 00:00:00 2001 From: Jaisen Mathai Date: Thu, 17 Jan 2019 22:04:52 -0800 Subject: [PATCH 6/9] Revert "update readme to include information about --separate-media-folders command line option" This reverts commit 03d4a9ca0a0d6576ee7aee2bccae913183a2cc8e. --- Readme.md | 1 - elodie.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index 98c7442f..0f398941 100644 --- a/Readme.md +++ b/Readme.md @@ -126,7 +126,6 @@ Options: --trash After copying files, move the old files to the trash. --allow-duplicates Import the file even if it's already been imported. - --separate-media-folders Put files in separate folders for photos, audio and video at their final destination. --help Show this message and exit. ``` diff --git a/elodie.py b/elodie.py index 8dd0130c..ea571827 100755 --- a/elodie.py +++ b/elodie.py @@ -86,7 +86,7 @@ def import_file(_file, destination, album_from_folder, trash, allow_duplicates): @click.option('--debug', default=False, is_flag=True, help='Override the value in constants.py with True.') @click.option('--separate-media-folders', default=False, is_flag=True, - help='Put files in separate folders for photos, audio and video at their final destination. Overrides the value in constants.py with True.') + help='Separate folders for audio and video at their final destination. Overrides the value in constants.py with True.') @click.argument('paths', nargs=-1, type=click.Path()) def _import(destination, source, file, album_from_folder, trash, allow_duplicates, debug, separate_media_folders, paths): """Import files or directories by reading their EXIF and organizing them accordingly. From 0b737ad1f45af071f59a4ded11807928a09631fb Mon Sep 17 00:00:00 2001 From: Jaisen Mathai Date: Thu, 17 Jan 2019 22:05:16 -0800 Subject: [PATCH 7/9] Revert "Implements separating media folders by command line argument." This reverts commit b94372b425a910c89e697a77882a1d8b2a224f94. --- Readme.md | 2 -- elodie.py | 5 +---- elodie/constants.py | 3 --- elodie/filesystem.py | 9 ++------- 4 files changed, 3 insertions(+), 16 deletions(-) diff --git a/Readme.md b/Readme.md index 0f398941..e76643f1 100644 --- a/Readme.md +++ b/Readme.md @@ -282,8 +282,6 @@ You can, of course, ask me to change the location and time. I'll happily update ./elodie.py update --location="Las Vegas, NV" --time="2015-04-15" /where/i/want/my/photos/to/go/2015-09-Sep/Unknown\ Location/2015-09-27_01-41-38-_dsc8705.dng /where/i/want/my/photos/to/go/2015-09-Sep/Unknown\ Location/2015-09-27_01-41-38-_dsc8705.nef ``` -To put media according to it's type to a folder like 'Audio', 'Video' or 'Photo' at it's final destination add `--separate-media-folders`. - ## What about photos I take in the future? Organizing your existing photos is great. But I'd be lying if I said I was the only one who could help you with that. Unlike other programs I put the same effort into keeping your library organized into the future as I have in getting it organized in the first place. diff --git a/elodie.py b/elodie.py index ea571827..54c69ee6 100755 --- a/elodie.py +++ b/elodie.py @@ -85,14 +85,11 @@ def import_file(_file, destination, album_from_folder, trash, allow_duplicates): help='Import the file even if it\'s already been imported.') @click.option('--debug', default=False, is_flag=True, help='Override the value in constants.py with True.') -@click.option('--separate-media-folders', default=False, is_flag=True, - help='Separate folders for audio and video at their final destination. Overrides the value in constants.py with True.') @click.argument('paths', nargs=-1, type=click.Path()) -def _import(destination, source, file, album_from_folder, trash, allow_duplicates, debug, separate_media_folders, paths): +def _import(destination, source, file, album_from_folder, trash, allow_duplicates, debug, paths): """Import files or directories by reading their EXIF and organizing them accordingly. """ constants.debug = debug - constants.separate_media_folders = separate_media_folders has_errors = False result = Result() diff --git a/elodie/constants.py b/elodie/constants.py index e7a895f5..7b4e6f9c 100644 --- a/elodie/constants.py +++ b/elodie/constants.py @@ -28,6 +28,3 @@ # check python version, required in filesystem.py to trigger appropriate method python_version = version_info.major - -# Separate folders for audio and video at their final destination. -separate_media_folders = False \ No newline at end of file diff --git a/elodie/filesystem.py b/elodie/filesystem.py index 3028e5db..6c5d83e1 100644 --- a/elodie/filesystem.py +++ b/elodie/filesystem.py @@ -14,7 +14,6 @@ from elodie import compatability from elodie import geolocation from elodie import log -from elodie import constants from elodie.config import load_config from elodie.localstorage import Db from elodie.media.base import Base, get_all_subclasses @@ -216,7 +215,7 @@ def get_folder_path_definition(self): return self.cached_folder_path_definition - def get_folder_path(self, metadata, media_name=None): + def get_folder_path(self, metadata): """Given a media's metadata this function returns the folder path as a string. :param metadata dict: Metadata dictionary. @@ -259,9 +258,6 @@ def get_folder_path(self, metadata, media_name=None): elif part.startswith('"') and part.endswith('"'): path.append(part[1:-1]) - if (constants.separate_media_folders and media_name): - path.append(media_name) - return os.path.join(*path) def parse_mask_for_location(self, mask, location_parts, place_name): @@ -339,9 +335,8 @@ def process_file(self, _file, destination, media, **kwargs): media.set_original_name() metadata = media.get_metadata() - media_name = media.__name__ - directory_name = self.get_folder_path(metadata, media_name) + directory_name = self.get_folder_path(metadata) dest_directory = os.path.join(destination, directory_name) file_name = self.get_file_name(media) From 4142dab0a15d2d72f907d2984e29c590ec4b0ce6 Mon Sep 17 00:00:00 2001 From: Jaisen Mathai Date: Fri, 18 Jan 2019 16:48:32 -0800 Subject: [PATCH 8/9] Add support for a %custom placeholder for more complex folder names than %location or %date provide #279 (#283) --- .gitignore | 1 + Readme.md | 7 +++ elodie/filesystem.py | 86 +++++++++++++++++++++++---------- elodie/tests/filesystem_test.py | 48 ++++++++++++++++++ 4 files changed, 117 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index 192a5e2a..4ba6258f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ build/** **/*.dng **/*.nef **/*.rw2 +env/** diff --git a/Readme.md b/Readme.md index e76643f1..0ecc0009 100644 --- a/Readme.md +++ b/Readme.md @@ -194,6 +194,12 @@ month=%m year=%Y full_path=%year-%month/%location # -> 2015-12/Sunnyvale, California + +date=%Y +location=%city, %state +custom=%date %album +full_path=%location/%custom +# -> Sunnyvale, California/2015 Birthday Party ``` #### Using fallback folders @@ -239,6 +245,7 @@ In addition to my built-in and date placeholders you can combine them into a sin * `%location` can be used to combine multiple values of `%city`, `%state` and `%country`. For example, `location=%city, %state` would result in folder names like `Sunnyvale, California`. * `%date` can be used to combine multiple values from [the standard Python time directives](https://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior). For example, `date=%Y-%m` would result in folder names like `2015-12`. +* `%custom` can be used to combine multiple values from anything else. Think of it as a catch-all when `%location` and `%date` don't meet your needs. ### Reorganize by changing location and dates diff --git a/elodie/filesystem.py b/elodie/filesystem.py index 6c5d83e1..877cd5b4 100644 --- a/elodie/filesystem.py +++ b/elodie/filesystem.py @@ -218,7 +218,7 @@ def get_folder_path_definition(self): def get_folder_path(self, metadata): """Given a media's metadata this function returns the folder path as a string. - :param metadata dict: Metadata dictionary. + :param dict metadata: Metadata dictionary. :returns: str """ path_parts = self.get_folder_path_definition() @@ -232,33 +232,69 @@ def get_folder_path(self, metadata): # Unknown Location - when neither an album nor location exist for this_part in path_part: part, mask = this_part - if part in ('date', 'day', 'month', 'year'): - path.append( - time.strftime(mask, metadata['date_taken']) - ) + this_path = self.get_dynamic_path(part, mask, metadata) + if this_path: + path.append(this_path.strip()) + # We break as soon as we have a value to append + # Else we continue for fallbacks break - elif part in ('location', 'city', 'state', 'country'): - place_name = geolocation.place_name( - metadata['latitude'], - metadata['longitude'] - ) + return os.path.join(*path) - location_parts = re.findall('(%[^%]+)', mask) - parsed_folder_name = self.parse_mask_for_location( - mask, - location_parts, - place_name, - ) - path.append(parsed_folder_name) - break - elif part in ('album', 'camera_make', 'camera_model'): - if metadata[part]: - path.append(metadata[part]) - break - elif part.startswith('"') and part.endswith('"'): - path.append(part[1:-1]) + def get_dynamic_path(self, part, mask, metadata): + """Parse a specific folder's name given a mask and metadata. + + :param part: Name of the part as defined in the path (i.e. date from %date) + :param mask: Mask representing the template for the path (i.e. %city %state + :param metadata: Metadata dictionary. + :returns: str + """ + + # Each part has its own custom logic and we evaluate a single part and return + # the evaluated string. + if part in ('custom'): + custom_parts = re.findall('(%[a-z_]+)', mask) + folder = mask + for i in custom_parts: + folder = folder.replace( + i, + self.get_dynamic_path(i[1:], i, metadata) + ) + return folder + elif part in ('date'): + config = load_config() + # If Directory is in the config we assume full_path and its + # corresponding values (date, location) are also present + config_directory = self.default_folder_path_definition + if('Directory' in config): + config_directory = config['Directory'] + date_mask = '' + if 'date' in config_directory: + date_mask = config_directory['date'] + return time.strftime(date_mask, metadata['date_taken']) + elif part in ('day', 'month', 'year'): + return time.strftime(mask, metadata['date_taken']) + elif part in ('location', 'city', 'state', 'country'): + place_name = geolocation.place_name( + metadata['latitude'], + metadata['longitude'] + ) + + location_parts = re.findall('(%[^%]+)', mask) + parsed_folder_name = self.parse_mask_for_location( + mask, + location_parts, + place_name, + ) + return parsed_folder_name + elif part in ('album', 'camera_make', 'camera_model'): + if metadata[part]: + return metadata[part] + elif part.startswith('"') and part.endswith('"'): + # Fallback string + return part[1:-1] + + return '' - return os.path.join(*path) def parse_mask_for_location(self, mask, location_parts, place_name): """Takes a mask for a location and interpolates the actual place names. diff --git a/elodie/tests/filesystem_test.py b/elodie/tests/filesystem_test.py index ee289137..93d3dc06 100644 --- a/elodie/tests/filesystem_test.py +++ b/elodie/tests/filesystem_test.py @@ -279,6 +279,54 @@ def test_get_folder_path_with_int_in_config_component(): assert path == os.path.join('2015'), path +@mock.patch('elodie.config.config_file', '%s/config.ini-combined-date-and-album' % gettempdir()) +def test_get_folder_path_with_combined_date_and_album(): + # gh-239 + with open('%s/config.ini-combined-date-and-album' % gettempdir(), 'w') as f: + f.write(""" +[Directory] +date=%Y-%m-%b +custom=%date %album +full_path=%custom + """) + if hasattr(load_config, 'config'): + del load_config.config + filesystem = FileSystem() + media = Photo(helper.get_file('with-album.jpg')) + path = filesystem.get_folder_path(media.get_metadata()) + if hasattr(load_config, 'config'): + del load_config.config + + assert path == '2015-12-Dec Test Album', path + +@mock.patch('elodie.config.config_file', '%s/config.ini-combined-date-album-location-fallback' % gettempdir()) +def test_get_folder_path_with_album_and_location_fallback(): + # gh-279 + with open('%s/config.ini-combined-date-album-location-fallback' % gettempdir(), 'w') as f: + f.write(""" +[Directory] +date=%Y-%m-%b +custom=%album +full_path=%custom|%city + """) + if hasattr(load_config, 'config'): + del load_config.config + filesystem = FileSystem() + + # Test with no location + media = Photo(helper.get_file('plain.jpg')) + path_plain = filesystem.get_folder_path(media.get_metadata()) + + # Test with City + media = Photo(helper.get_file('with-location.jpg')) + path_city = filesystem.get_folder_path(media.get_metadata()) + if hasattr(load_config, 'config'): + del load_config.config + + assert path_plain == 'Unknown Location', path_plain + assert path_city == 'Sunnyvale', path_city + + def test_get_folder_path_with_int_in_source_path(): # gh-239 filesystem = FileSystem() From d95f917c3ae6372bd3783001d899c3d2197b372a Mon Sep 17 00:00:00 2001 From: Denis Zamataev Date: Mon, 4 Feb 2019 14:09:14 +0300 Subject: [PATCH 9/9] implements deleting duplicate files on import. --- elodie.py | 5 ++++- elodie/constants.py | 3 +++ elodie/filesystem.py | 29 +++++++++++++++++++++++++---- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/elodie.py b/elodie.py index 54c69ee6..e53831df 100755 --- a/elodie.py +++ b/elodie.py @@ -85,11 +85,14 @@ def import_file(_file, destination, album_from_folder, trash, allow_duplicates): help='Import the file even if it\'s already been imported.') @click.option('--debug', default=False, is_flag=True, help='Override the value in constants.py with True.') +@click.option('--delete-duplicates', default=False, is_flag=True, + help='If True, duplicate files will get deleted on import.') @click.argument('paths', nargs=-1, type=click.Path()) -def _import(destination, source, file, album_from_folder, trash, allow_duplicates, debug, paths): +def _import(destination, source, file, album_from_folder, trash, allow_duplicates, debug, delete_duplicates, paths): """Import files or directories by reading their EXIF and organizing them accordingly. """ constants.debug = debug + constants.delete_duplicates = delete_duplicates has_errors = False result = Result() diff --git a/elodie/constants.py b/elodie/constants.py index 7b4e6f9c..c410b5a6 100644 --- a/elodie/constants.py +++ b/elodie/constants.py @@ -8,6 +8,9 @@ #: If True, debug messages will be printed. debug = False +#: If True, duplicate files will get deleted on import. +delete_duplicates = False + #: Directory in which to store Elodie settings. application_directory = '{}/.elodie'.format(path.expanduser('~')) diff --git a/elodie/filesystem.py b/elodie/filesystem.py index 0a1d6ab0..264cca10 100644 --- a/elodie/filesystem.py +++ b/elodie/filesystem.py @@ -80,6 +80,20 @@ def delete_directory_if_empty(self, directory_path): return False + def delete_file(self, file_path): + """Delete a file safely but permanently. + """ + try: + if os.path.exists(file_path): + os.remove(file_path) + return True + else: + print("The file does not exist.") + except OSError: + pass + + return False + def get_all_files(self, path, extensions=None): """Recursively get all files which match a path and extension. @@ -508,10 +522,17 @@ def process_file(self, _file, destination, media, **kwargs): checksum_file = db.get_hash(checksum) if(allow_duplicate is False and checksum_file is not None): if(os.path.isfile(checksum_file)): - log.info('%s already exists at %s. Skipping...' % ( - _file, - checksum_file - )) + if constants.delete_duplicates: + log.info('%s already exists at %s. Deleting...' % ( + _file, + checksum_file + )) + self.delete_file(_file) + else: + log.info('%s already exists at %s. Skipping...' % ( + _file, + checksum_file + )) return else: log.info('%s matched checksum but file not found at %s. Importing again...' % ( # noqa