diff --git a/ordigi/collection.py b/ordigi/collection.py index 18aae67..2499ec7 100644 --- a/ordigi/collection.py +++ b/ordigi/collection.py @@ -777,10 +777,10 @@ class Collection(SortMedias): def init(self, loc): """Init collection db""" for file_path in self.get_collection_files(): - media = self.medias.get_media(file_path, self.root, loc) - media.metadata['file_path'] = os.path.relpath(file_path, self.root) + metadata = self.medias.get_metadata(file_path, self.root, loc) + metadata['file_path'] = os.path.relpath(file_path, self.root) - self.db.add_file_data(media.metadata) + self.db.add_file_data(metadata) self.summary.append('update', file_path) return self.summary @@ -832,22 +832,22 @@ class Collection(SortMedias): relpath = os.path.relpath(file_path, self.root) # If file not in database if relpath not in db_paths: - media = self.medias.get_media(file_path, self.root, loc) - media.metadata['file_path'] = relpath + metadata = self.medias.get_metadata(file_path, self.root, loc) + metadata['file_path'] = relpath # Check if file checksum is in invalid rows row = [] for row in invalid_db_rows: - if row['Checksum'] == media.metadata['checksum']: + if row['Checksum'] == metadata['checksum']: # file have been moved without registering to db - media.metadata['src_path'] = row['SrcPath'] + metadata['src_path'] = row['SrcPath'] # Check if row FilePath is a subpath of relpath if relpath.startswith(row['FilePath']): path = os.path.relpath(relpath, row['FilePath']) - media.metadata['subdirs'] = row['Subdirs'] + path - media.metadata['Filename'] = row['Filename'] + metadata['subdirs'] = row['Subdirs'] + path + metadata['Filename'] = row['Filename'] break # set row attribute to the file - self.db.add_file_data(media.metadata) + self.db.add_file_data(metadata) self.summary.append('update', file_path) # Finally delete invalid rows @@ -947,14 +947,13 @@ class Collection(SortMedias): # Get medias data subdirs = set() - for media in self.medias.get_medias(src_dirs, imp=imp, loc=loc): + for src_path, metadata in self.medias.get_metadatas(src_dirs, imp=imp, loc=loc): # Get the destination path according to metadata fpath = FPath(path_format, self.day_begins, self.logger) - media.metadata['file_path'] = fpath.get_path(media.metadata) - subdirs.add(media.file_path.parent) + metadata['file_path'] = fpath.get_path(metadata) + subdirs.add(src_path.parent) - src_path = media.file_path - self.medias.datas[src_path] = copy(media.metadata) + self.medias.datas[src_path] = copy(metadata) # Sort files and solve conflicts self.summary = self.sort_medias(imp, remove_duplicates) @@ -990,9 +989,8 @@ class Collection(SortMedias): dedup_regex = [date_num3, date_num2, default] # Get medias data - for media in self.medias.get_medias(paths): + for src_path, metadata in self.medias.get_metadatas(paths): # Deduplicate the path - src_path = media.file_path path_parts = src_path.relative_to(self.root).parts dedup_path = [] for path_part in path_parts: @@ -1005,9 +1003,8 @@ class Collection(SortMedias): dedup_path.append(''.join(filtered_items)) - media.metadata['file_path'] = os.path.join(*dedup_path) - src_path = media.file_path - self.medias.datas[src_path] = copy(media.metadata) + metadata['file_path'] = os.path.join(*dedup_path) + self.medias.datas[src_path] = copy(metadata) # Sort files and solve conflicts self.sort_medias(remove_duplicates=remove_duplicates) @@ -1027,20 +1024,18 @@ class Collection(SortMedias): for img_path in images.find_similar(image, similarity): self.paths.paths_list.append(img_path) - media = self.medias.get_media(img_path, path) + metadata = self.medias.get_metadata(img_path, path) relpath = os.path.join(directory_name, img_path.name) - media.metadata['file_path'] = relpath - file_path = media.file_path - self.medias.datas[file_path] = copy(media.metadata) + metadata['file_path'] = relpath + self.medias.datas[img_path] = copy(metadata) if self.medias.datas: # Found similar images to image self.paths.paths_list.append(image.img_path) - media = self.medias.get_media(image.img_path, path) + metadata = self.medias.get_metadata(image.img_path, path) relpath = os.path.join(directory_name, image.img_path.name) - media.metadata['file_path'] = relpath - file_path = media.file_path - self.medias.datas[file_path] = copy(media.metadata) + metadata['file_path'] = relpath + self.medias.datas[image.img_path] = copy(metadata) return True diff --git a/ordigi/media.py b/ordigi/media.py index 149f77a..37c440a 100644 --- a/ordigi/media.py +++ b/ordigi/media.py @@ -69,6 +69,7 @@ class ExifMetadata: class ReadExif(ExifMetadata): + """Read exif metadata to file""" def __init__( self, @@ -94,7 +95,7 @@ class ReadExif(ExifMetadata): return ExifToolCaching(self.file_path, logger=self.logger).asdict() - def _get_key_values(self, key): + def get_key_values(self, key): """ Get the first value of a tag set :returns: str or None if no exif tag @@ -106,20 +107,43 @@ class ReadExif(ExifMetadata): if tag in self.exif_metadata: yield self.exif_metadata[tag] - def get_value(self, tag): + def get_coordinates(self, key, value): + """Get latitude or longitude value + + :param str key: Type of coordinate to get. Either "latitude" or + "longitude". + :returns: float or None """ - Get given value from EXIF. - :returns: str or None - """ - if self.exif_metadata is None: - return None - if tag not in self.exif_metadata: + if value is None: return None - return self.exif_metadata[tag] + if isinstance(value, str) and len(value) == 0: + # If exiftool GPS output is empty, the data returned will be a str + # with 0 length. + # https://github.com/jmathai/elodie/issues/354 + return None + + # Cast coordinate to a float due to a bug in exiftool's + # -json output format. + # https://github.com/jmathai/elodie/issues/171 + # http://u88.n24.queensu.ca/exiftool/forum/index.php/topic,7952.0.html # noqa + this_coordinate = float(value) + + direction_multiplier = 1.0 + # when self.set_gps_ref != True + if key == 'latitude': + if 'EXIF:GPSLatitudeRef' in self.exif_metadata: + if self.exif_metadata['EXIF:GPSLatitudeRef'] == 'S': + direction_multiplier = -1.0 + elif key == 'longitude': + if 'EXIF:GPSLongitudeRef' in self.exif_metadata: + if self.exif_metadata['EXIF:GPSLongitudeRef'] == 'W': + direction_multiplier = -1.0 + return this_coordinate * direction_multiplier class WriteExif(ExifMetadata): + """Write exif metadata to file""" def __init__( self, @@ -140,6 +164,7 @@ class WriteExif(ExifMetadata): :returns: value (str) """ + # TODO overwrite mode check if fail return ExifTool(self.file_path, logger=self.logger).setvalue(tag, value) def set_key_values(self, key, value): @@ -190,17 +215,17 @@ class WriteExif(ExifMetadata): if all(status): return True - else: - return False + + return False def set_album_from_folder(self): """Set the album attribute based on the leaf folder name :returns: bool """ + # TODO use tag key return self.set_value('Album', self.file_path.parent.name) - class Media(ReadExif): """ Extract matadatas from exiftool and sort them to dict structure @@ -230,61 +255,25 @@ class Media(ReadExif): self.album_from_folder = album_from_folder self.interactive = interactive self.logger = logger.getChild(self.__class__.__name__) + self.metadata = None self.use_date_filename = use_date_filename self.use_file_dates = use_file_dates self.theme = request.load_theme() - # get self.metadata - self.get_metadata(self.file_path) def get_mimetype(self): """ Get the mimetype of the file. :returns: str or None """ + # TODO add to metadata mimetype = mimetypes.guess_type(self.file_path) if mimetype is None: return None return mimetype[0] - def get_coordinates(self, key, value): - """Get latitude or longitude value - - :param str key: Type of coordinate to get. Either "latitude" or - "longitude". - :returns: float or None - """ - if value is None: - return None - - if isinstance(value, str) and len(value) == 0: - # If exiftool GPS output is empty, the data returned will be a str - # with 0 length. - # https://github.com/jmathai/elodie/issues/354 - return None - - # Cast coordinate to a float due to a bug in exiftool's - # -json output format. - # https://github.com/jmathai/elodie/issues/171 - # http://u88.n24.queensu.ca/exiftool/forum/index.php/topic,7952.0.html # noqa - this_coordinate = float(value) - - direction_multiplier = 1.0 - # when self.set_gps_ref != True - if key == 'latitude': - if 'EXIF:GPSLatitudeRef' in self.exif_metadata: - if self.exif_metadata['EXIF:GPSLatitudeRef'] == 'S': - direction_multiplier = -1.0 - elif key == 'longitude': - if 'EXIF:GPSLongitudeRef' in self.exif_metadata: - if self.exif_metadata['EXIF:GPSLongitudeRef'] == 'W': - direction_multiplier = -1.0 - return this_coordinate * direction_multiplier - - return None - def get_date_format(self, value): """ Formatting date attribute. @@ -311,11 +300,12 @@ class Media(ReadExif): choices_list = [ inquirer.List( 'date_list', - message=f"Choice appropriate original date", + message="Choice appropriate original date", choices=choices, default=default, ), ] + # import ipdb; ipdb.set_trace() answers = inquirer.prompt(choices_list, theme=self.theme) if not answers['date_list']: @@ -324,8 +314,8 @@ class Media(ReadExif): ] answers = inquirer.prompt(prompt, theme=self.theme) return self.get_date_format(answers['date_custom']) - else: - return answers['date_list'] + + return answers['date_list'] def get_date_media(self): ''' @@ -384,17 +374,19 @@ class Media(ReadExif): return date_filename - elif self.use_file_dates: + if self.use_file_dates: if date_created: self.logger.warning( f"use date created:{date_created} for {self.file_path}" ) return date_created - elif date_modified: + + if date_modified: self.logger.warning( f"use date modified:{date_modified} for {self.file_path}" ) return date_modified + elif self.interactive: choices = [] if date_filename: @@ -441,7 +433,7 @@ class Media(ReadExif): for key in self.tags_keys: formated_data = None - for value in self._get_key_values(key): + for value in self.get_key_values(key): if 'date' in key: formated_data = self.get_date_format(value) elif key in ('latitude', 'longitude'): @@ -485,7 +477,8 @@ class Media(ReadExif): return db.get_metadata_data(relpath, 'LocationId') def _check_file(self, db, root): - # Check if file_path is a subpath of root + """Check if file_path is a subpath of root""" + if str(self.file_path).startswith(str(root)): relpath = os.path.relpath(self.file_path, root) db_checksum = db.get_checksum(relpath) @@ -506,7 +499,7 @@ class Media(ReadExif): return relpath, db_checksum - return + return None, None def _set_location_metadata(self, location_id, db, loc=None): @@ -653,8 +646,8 @@ class Medias: self.datas = {} self.theme = request.load_theme() - def get_media(self, file_path, src_dir, loc=None): - return Media( + def get_media(self, file_path, src_dir): + media = Media( file_path, src_dir, self.album_from_folder, @@ -665,7 +658,16 @@ class Medias: self.use_file_dates, ) - def get_medias(self, src_dirs, imp=False, loc=None): + return media + + def get_metadata(self, file_path, src_dir, loc=None): + media = self.get_media(file_path, src_dir) + media.get_metadata(self.root, loc, self.db.sqlite, + self.cache) + + return media.metadata + + def get_metadatas(self, src_dirs, imp=False, loc=None): """Get medias data""" for src_dir in src_dirs: src_dir = self.paths.check(src_dir) @@ -680,9 +682,9 @@ class Medias: sys.exit(1) # Get file metadata - media = self.get_media(src_path, src_dir, loc) + metadata = self.get_metadata(src_path, src_dir, loc) - yield media + yield src_path, metadata def update_exif_data(self, metadata): @@ -711,5 +713,3 @@ class Medias: return True return False - - diff --git a/tests/test_collection.py b/tests/test_collection.py index 1434bdf..9be33b6 100644 --- a/tests/test_collection.py +++ b/tests/test_collection.py @@ -1,4 +1,3 @@ -# TODO to be removed later from datetime import datetime import shutil import sqlite3 @@ -12,7 +11,7 @@ from ordigi.collection import Collection, FPath, Paths from ordigi.exiftool import ExifToolCaching, exiftool_is_running, terminate_exiftool from ordigi.geolocation import GeoLocation from ordigi import log -from ordigi.media import Media +from ordigi.media import Media, ReadExif from ordigi import utils from .conftest import randomize_files, randomize_db from ordigi.summary import Summary @@ -65,12 +64,12 @@ class TestFPath: exif_data = ExifToolCaching(str(file_path)).asdict() loc = GeoLocation() - metadata = media.metadata + media.get_metadata(self.src_path, loc) for item, regex in items.items(): for mask in masks: matched = re.search(regex, mask) if matched: - part = fpath.get_part(item, mask[1:-1], metadata) + part = fpath.get_part(item, mask[1:-1], media.metadata) # check if part is correct assert isinstance(part, str), file_path if item == 'basename': @@ -157,7 +156,7 @@ class TestCollection: for file_path in paths: if '.db' not in str(file_path): media = Media(file_path, tmp_path, album_from_folder=True) - for value in media._get_key_values('album'): + for value in ReadExif(file_path).get_key_values('album'): assert value != '' or None collection = Collection(tmp_path, album_from_folder=True) @@ -206,6 +205,7 @@ class TestCollection: # copy mode src_path = Path(self.src_path, 'test_exif', 'photo.png') media = Media(src_path, self.src_path) + media.get_metadata(tmp_path) name = 'photo_' + str(imp) + '.png' media.metadata['file_path'] = name dest_path = Path(tmp_path, name) diff --git a/tests/test_media.py b/tests/test_media.py index f4f3b98..25beea9 100644 --- a/tests/test_media.py +++ b/tests/test_media.py @@ -52,7 +52,7 @@ class TestMedia: assert value is None if key == 'album': - for album in media._get_key_values('album'): + for album in media.get_key_values('album'): if album is not None and album != '': assert value == album break @@ -76,6 +76,7 @@ class TestMedia: exif_data = ExifToolCaching(str(file_path)).asdict() media = Media(file_path, self.src_path, use_date_filename=True, use_file_dates=True) + media.get_metadata(self.src_path) date_media = media.get_date_media() date_filename = None