diff --git a/image_classifier/image_classifier.py b/image_classifier/image_classifier.py index f8657f2..85bf657 100755 --- a/image_classifier/image_classifier.py +++ b/image_classifier/image_classifier.py @@ -77,12 +77,14 @@ class image_classifier: self.process_file(entry.path) def count(self, iterable): + ''' Return the length of an iterable that can't be count with len()''' count = 0 for item in iterable: count += 1 return count def recursive_scandir(self, path, ignore_hidden_files=True): + ''' Recursively scan a directory for files''' files = [] try: for file in os.scandir(path): @@ -100,8 +102,8 @@ class image_classifier: self._log.warning(f"Permission denied accessing folder '{path}'") return files - def process_metadata(self, file): + ''' Process the metadata of an image file and store it in self.metadata''' self.metadata = pyexiv2.ImageMetadata(file) self.metadata.read() if 'Xmp.iptcExt.PersonInImage' in self.metadata.xmp_keys: @@ -109,6 +111,48 @@ class image_classifier: {self.metadata['Xmp.iptcExt.PersonInImage'].raw_value} \ (type: {type(self.metadata['Xmp.iptcExt.PersonInImage'].raw_value)})") + def get_file_date(self, file): + ''' Obtain the file date from EXIF metadata or the file name. Return None + if it's not accessible or 'unknown-time' if it can't determine the date''' + file_date = None + if not os.access(file, os.R_OK): + self._log.error(f"The file '{file}' is not readable.") + else: + file_date = "unknown-time" + if self.is_image(file): + if 'Exif.Photo.DateTimeOriginal' in self.metadata.exif_keys: + original_date = self.metadata['Exif.Photo.DateTimeOriginal'].value + self._log.debug(f"File creation time: {original_date} \ +(type: {type(original_date)})") + try: + file_date = original_date.strftime('%Y.%m.%d') + except Exception as error: + self._log.error(f"Failed to convert EXIF information about date '{original_date}'.") + file_date = 'unknown-time' + if file_date == 'unknown-time': + self._log.debug('Date not stored in EXIF metadata') + match = re.search( + r'(?P20[0-9]{2})[\-/\._]?(?P[0-1]?[0-9])[\-/\._]?(?P[0-3]?[0-9])', + filename + ) + if match: + file_date = f"{match.group('year')}.{match.group('month')}.\ +{match.group('day')}" + else: + match = re.search(r'(?P[0-3]?[0-9])[\-/\._]?\ +(?P[0-1]?[0-9])[\-/\._]?(?P20[0-9]{2})', filename) + if match: + file_date = f"{match.group('year')}.{match.group('month')}.\ +{match.group('day')}" + else: + self._log.warning(f"Date not found in file's name '{filename}'.") + else: + self._log.debug(f"The file '{file}' doesn't seem to be an image for PIL.") + folder = os.path.join(dirname, file_date, filename) + self._log.debug(f"Time based folder name section '{file_date}'") + return file_date + + def process_file(self, file): ''' Process a file, find faces, add EXIF information and move it to the folder of the day''' @@ -117,43 +161,18 @@ class image_classifier: dirname = os.path.dirname(os.path.realpath(file)) filename = os.path.basename(file) people = list() - if not os.access(file, os.R_OK): - self._log.error(f"The file '{file}' is not readable.") - else: + folder_date = self.get_file_date(file) + if folder_date: if self.is_image(file): self.process_metadata(file) + + if self.is_image(file): people = self.find_faces(file) if people: self._log.debug(f"Found {len(people)} known people in the image.") self._log.debug(json.dumps(people, indent=2)) self.append_people(file, people) - if 'Exif.Photo.DateTimeOriginal' in self.metadata.exif_keys: - original_date = self.metadata['Exif.Photo.DateTimeOriginal'].value - self._log.debug(f"File creation time: {original_date} \ -(type: {type(original_date)})") - try: - folder_date = original_date.strftime('%Y.%m.%d') - except Exception as error: - self._log.error(f"Failed to convert EXIF information about date '{original_date}'.") - folder_date = 'unknown-time' - if folder_date == 'unknown-time': - match = re.search( - r'(?P20[0-9]{2})[\-/\._]?(?P[0-1]?[0-9])[\-/\._]?(?P[0-3]?[0-9])', - filename - ) - if match: - folder_date = f"{match.group('year')}.{match.group('month')}.\ -{match.group('day')}" - else: - match = re.search(r'(?P[0-3]?[0-9])[\-/\._]?\ -(?P[0-1]?[0-9])[\-/\._]?(?P20[0-9]{2})', filename) - if match: - folder_date = f"{match.group('year')}.{match.group('month')}.\ -{match.group('day')}" - else: - self._log.debug(f"The file '{file}' doesn't seem to be an image for PIL.") - folder = os.path.join(dirname, folder_date, filename) - self._log.debug(f"Time based folder name section '{folder_date}'") + new_path = os.path.dirname(folder) if not os.path.exists(new_path): os.makedirs(new_path) @@ -194,6 +213,7 @@ class image_classifier: print(f" {key}: '{self.metadata[key].raw_value}'") def append_people(self, file, people): + '''Append people names to the metadata of an image file''' new_list = list() if 'Xmp.iptcExt.PersonInImage' in self.metadata.xmp_keys: for person in self.metadata['Xmp.iptcExt.PersonInImage'].raw_value: @@ -224,6 +244,7 @@ class image_classifier: return True def load_known_people(self): + '''Load faces of known people using the file names as person name''' known_people = list() self._log.debug(f"Looking for known faces in directory '{self.faces_directory}'...") if os.access(self.faces_directory, os.R_OK):