Refactoring Media class
This commit is contained in:
		
							parent
							
								
									ad14604648
								
							
						
					
					
						commit
						a693e6018a
					
				@ -15,7 +15,7 @@ from pathlib import Path, PurePath
 | 
				
			|||||||
import inquirer
 | 
					import inquirer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from ordigi.database import Sqlite
 | 
					from ordigi.database import Sqlite
 | 
				
			||||||
from ordigi.media import Media
 | 
					from ordigi.media import Media, Medias
 | 
				
			||||||
from ordigi.images import Image, Images
 | 
					from ordigi.images import Image, Images
 | 
				
			||||||
from ordigi import request
 | 
					from ordigi import request
 | 
				
			||||||
from ordigi.summary import Summary
 | 
					from ordigi.summary import Summary
 | 
				
			||||||
@ -440,98 +440,6 @@ class Paths:
 | 
				
			|||||||
        return self.paths_list
 | 
					        return self.paths_list
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Medias:
 | 
					 | 
				
			||||||
    """Get media data in collection or source path"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(
 | 
					 | 
				
			||||||
        self,
 | 
					 | 
				
			||||||
        paths,
 | 
					 | 
				
			||||||
        root,
 | 
					 | 
				
			||||||
        album_from_folder=False,
 | 
					 | 
				
			||||||
        cache=False,
 | 
					 | 
				
			||||||
        db=None,
 | 
					 | 
				
			||||||
        interactive=False,
 | 
					 | 
				
			||||||
        ignore_tags=None,
 | 
					 | 
				
			||||||
        logger=logging.getLogger(),
 | 
					 | 
				
			||||||
        use_date_filename=False,
 | 
					 | 
				
			||||||
        use_file_dates=False,
 | 
					 | 
				
			||||||
    ):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Modules
 | 
					 | 
				
			||||||
        self.db = db
 | 
					 | 
				
			||||||
        self.paths = paths
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Arguments
 | 
					 | 
				
			||||||
        self.root = root
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Options
 | 
					 | 
				
			||||||
        self.cache = cache
 | 
					 | 
				
			||||||
        self.album_from_folder = album_from_folder
 | 
					 | 
				
			||||||
        self.ignore_tags = ignore_tags
 | 
					 | 
				
			||||||
        self.interactive = interactive
 | 
					 | 
				
			||||||
        self.logger = logger.getChild(self.__class__.__name__)
 | 
					 | 
				
			||||||
        self.use_date_filename = use_date_filename
 | 
					 | 
				
			||||||
        self.use_file_dates = use_file_dates
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Attributes
 | 
					 | 
				
			||||||
        # List to store medias datas
 | 
					 | 
				
			||||||
        self.datas = []
 | 
					 | 
				
			||||||
        self.theme = request.load_theme()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_media(self, file_path, src_dir, loc=None):
 | 
					 | 
				
			||||||
        media = Media(
 | 
					 | 
				
			||||||
            file_path,
 | 
					 | 
				
			||||||
            src_dir,
 | 
					 | 
				
			||||||
            self.album_from_folder,
 | 
					 | 
				
			||||||
            self.ignore_tags,
 | 
					 | 
				
			||||||
            self.interactive,
 | 
					 | 
				
			||||||
            self.logger,
 | 
					 | 
				
			||||||
            self.use_date_filename,
 | 
					 | 
				
			||||||
            self.use_file_dates,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        media.get_metadata(self.root, loc, self.db.sqlite, self.cache)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return media
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_medias(self, src_dirs, imp=False, loc=None):
 | 
					 | 
				
			||||||
        """Get medias data"""
 | 
					 | 
				
			||||||
        for src_dir in src_dirs:
 | 
					 | 
				
			||||||
            src_dir = self.paths.check(src_dir)
 | 
					 | 
				
			||||||
            paths = self.paths.get_paths_list(src_dir)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Get medias and src_dirs
 | 
					 | 
				
			||||||
            for src_path in paths:
 | 
					 | 
				
			||||||
                if self.root not in src_path.parents:
 | 
					 | 
				
			||||||
                    if not imp:
 | 
					 | 
				
			||||||
                        self.logger.error(f"""{src_path} not in {self.root}
 | 
					 | 
				
			||||||
                                collection, use `ordigi import`""")
 | 
					 | 
				
			||||||
                        sys.exit(1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                # Get file metadata
 | 
					 | 
				
			||||||
                media = self.get_media(src_path, src_dir, loc)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                yield media
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def update_exif_data(self, media):
 | 
					 | 
				
			||||||
        updated = False
 | 
					 | 
				
			||||||
        if self.album_from_folder:
 | 
					 | 
				
			||||||
            media.set_album_from_folder()
 | 
					 | 
				
			||||||
            updated = True
 | 
					 | 
				
			||||||
        if media.metadata['original_name'] in (False, ''):
 | 
					 | 
				
			||||||
            media.set_value('original_name', media.metadata['filename'])
 | 
					 | 
				
			||||||
            updated = True
 | 
					 | 
				
			||||||
        if self.album_from_folder:
 | 
					 | 
				
			||||||
            album = media.metadata['album']
 | 
					 | 
				
			||||||
            if album and album != '':
 | 
					 | 
				
			||||||
                media.set_value('album', album)
 | 
					 | 
				
			||||||
                updated = True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if updated:
 | 
					 | 
				
			||||||
            return True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class SortMedias:
 | 
					class SortMedias:
 | 
				
			||||||
    """Sort medias in collection"""
 | 
					    """Sort medias in collection"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -576,25 +484,24 @@ class SortMedias:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return True
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _record_file(self, src_path, dest_path, media, imp=False):
 | 
					    def _record_file(self, src_path, dest_path, metadata, imp=False):
 | 
				
			||||||
        """Check file and record the file to db"""
 | 
					        """Check file and record the file to db"""
 | 
				
			||||||
        # Check if file remain the same
 | 
					        # Check if file remain the same
 | 
				
			||||||
        checksum = media.metadata['checksum']
 | 
					        checksum = metadata['checksum']
 | 
				
			||||||
        if not self._checkcomp(dest_path, checksum):
 | 
					        if not self._checkcomp(dest_path, checksum):
 | 
				
			||||||
            self.logger.error(f'Files {src_path} and {dest_path} are not identical')
 | 
					            self.logger.error(f'Files {src_path} and {dest_path} are not identical')
 | 
				
			||||||
            self.summary.append('check', False, src_path, dest_path)
 | 
					            self.summary.append('check', False, src_path, dest_path)
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # change media file_path to dest_path
 | 
					        # change media file_path to dest_path
 | 
				
			||||||
        media.file_path = dest_path
 | 
					 | 
				
			||||||
        if not self.dry_run:
 | 
					        if not self.dry_run:
 | 
				
			||||||
            updated = self.medias.update_exif_data(media)
 | 
					            updated = self.medias.update_exif_data(metadata)
 | 
				
			||||||
            if updated:
 | 
					            if updated:
 | 
				
			||||||
                checksum = utils.checksum(dest_path)
 | 
					                checksum = utils.checksum(dest_path)
 | 
				
			||||||
                media.metadata['checksum'] = checksum
 | 
					                metadata['checksum'] = checksum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not self.dry_run:
 | 
					        if not self.dry_run:
 | 
				
			||||||
            self.db.add_file_data(media.metadata)
 | 
					            self.db.add_file_data(metadata)
 | 
				
			||||||
            if imp != 'copy' and self.root in src_path.parents:
 | 
					            if imp != 'copy' and self.root in src_path.parents:
 | 
				
			||||||
                self.db.sqlite.delete_filepath(str(src_path.relative_to(self.root)))
 | 
					                self.db.sqlite.delete_filepath(str(src_path.relative_to(self.root)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -613,7 +520,7 @@ class SortMedias:
 | 
				
			|||||||
                self.summary.append('sort', False, src_path, dest_path)
 | 
					                self.summary.append('sort', False, src_path, dest_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def sort_file(self, src_path, dest_path, media, imp=False):
 | 
					    def sort_file(self, src_path, dest_path, metadata, imp=False):
 | 
				
			||||||
        """Sort file and register it to db"""
 | 
					        """Sort file and register it to db"""
 | 
				
			||||||
        if imp == 'copy':
 | 
					        if imp == 'copy':
 | 
				
			||||||
            self.fileio.copy(src_path, dest_path)
 | 
					            self.fileio.copy(src_path, dest_path)
 | 
				
			||||||
@ -622,7 +529,7 @@ class SortMedias:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if self.db:
 | 
					        if self.db:
 | 
				
			||||||
            result = self._record_file(
 | 
					            result = self._record_file(
 | 
				
			||||||
                src_path, dest_path, media, imp=imp
 | 
					                src_path, dest_path, metadata, imp=imp
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            result = True
 | 
					            result = True
 | 
				
			||||||
@ -637,8 +544,8 @@ class SortMedias:
 | 
				
			|||||||
        :param Path: A fully qualified path of the to create.
 | 
					        :param Path: A fully qualified path of the to create.
 | 
				
			||||||
        :returns: bool
 | 
					        :returns: bool
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        for media in self.medias.datas:
 | 
					        for file_path, metadata in self.medias.datas.items():
 | 
				
			||||||
            relpath = os.path.dirname(media.metadata['file_path'])
 | 
					            relpath = os.path.dirname(metadata['file_path'])
 | 
				
			||||||
            directory_path = self.root / relpath
 | 
					            directory_path = self.root / relpath
 | 
				
			||||||
            parts = directory_path.relative_to(self.root).parts
 | 
					            parts = directory_path.relative_to(self.root).parts
 | 
				
			||||||
            for i, _ in enumerate(parts):
 | 
					            for i, _ in enumerate(parts):
 | 
				
			||||||
@ -661,10 +568,9 @@ class SortMedias:
 | 
				
			|||||||
                    self.logger.warning(f'Renaming {dir_path} to {file_path}')
 | 
					                    self.logger.warning(f'Renaming {dir_path} to {file_path}')
 | 
				
			||||||
                    if not self.dry_run:
 | 
					                    if not self.dry_run:
 | 
				
			||||||
                        shutil.move(dir_path, file_path)
 | 
					                        shutil.move(dir_path, file_path)
 | 
				
			||||||
                    for med in self.medias.datas:
 | 
					                    metadata = self.medias.datas[dir_path]
 | 
				
			||||||
                        if med.file_path == dir_path:
 | 
					                    self.medias.datas[file_path] = metadata
 | 
				
			||||||
                            med.file_path = file_path
 | 
					                    del(self.medias.datas[dir_path])
 | 
				
			||||||
                            break
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if not self.dry_run:
 | 
					            if not self.dry_run:
 | 
				
			||||||
                directory_path.mkdir(parents=True, exist_ok=True)
 | 
					                directory_path.mkdir(parents=True, exist_ok=True)
 | 
				
			||||||
@ -706,7 +612,7 @@ class SortMedias:
 | 
				
			|||||||
    def _solve_conflicts(self, conflicts, remove_duplicates):
 | 
					    def _solve_conflicts(self, conflicts, remove_duplicates):
 | 
				
			||||||
        unresolved_conflicts = []
 | 
					        unresolved_conflicts = []
 | 
				
			||||||
        while conflicts != []:
 | 
					        while conflicts != []:
 | 
				
			||||||
            src_path, dest_path, media = conflicts.pop()
 | 
					            src_path, dest_path, metadata = conflicts.pop()
 | 
				
			||||||
            # Check for conflict status again in case is has changed
 | 
					            # Check for conflict status again in case is has changed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            conflict = self.check_conflicts(src_path, dest_path, remove_duplicates)
 | 
					            conflict = self.check_conflicts(src_path, dest_path, remove_duplicates)
 | 
				
			||||||
@ -726,12 +632,12 @@ class SortMedias:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if conflict == 1:
 | 
					            if conflict == 1:
 | 
				
			||||||
                # i = 100:
 | 
					                # i = 100:
 | 
				
			||||||
                unresolved_conflicts.append((src_path, dest_path, media))
 | 
					                unresolved_conflicts.append((src_path, dest_path, metadata))
 | 
				
			||||||
                self.logger.error(f"Too many appends for {dest_path}")
 | 
					                self.logger.error(f"Too many appends for {dest_path}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            media.metadata['file_path'] = os.path.relpath(dest_path, self.root)
 | 
					            metadata['file_path'] = os.path.relpath(dest_path, self.root)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            yield (src_path, dest_path, media), conflict
 | 
					            yield (src_path, dest_path, metadata), conflict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def sort_medias(self, imp=False, remove_duplicates=False):
 | 
					    def sort_medias(self, imp=False, remove_duplicates=False):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -741,19 +647,18 @@ class SortMedias:
 | 
				
			|||||||
        self._create_directories()
 | 
					        self._create_directories()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        conflicts = []
 | 
					        conflicts = []
 | 
				
			||||||
        for media in self.medias.datas:
 | 
					        for src_path, metadata in self.medias.datas.items():
 | 
				
			||||||
            src_path = media.file_path
 | 
					            dest_path = self.root / metadata['file_path']
 | 
				
			||||||
            dest_path = self.root / media.metadata['file_path']
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            conflict = self.check_conflicts(src_path, dest_path, remove_duplicates)
 | 
					            conflict = self.check_conflicts(src_path, dest_path, remove_duplicates)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if not conflict:
 | 
					            if not conflict:
 | 
				
			||||||
                self.sort_file(
 | 
					                self.sort_file(
 | 
				
			||||||
                    src_path, dest_path, media, imp=imp
 | 
					                    src_path, dest_path, metadata, imp=imp
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
            elif conflict == 1:
 | 
					            elif conflict == 1:
 | 
				
			||||||
                # There is conflict and file are different
 | 
					                # There is conflict and file are different
 | 
				
			||||||
                conflicts.append((src_path, dest_path, media))
 | 
					                conflicts.append((src_path, dest_path, metadata))
 | 
				
			||||||
            elif conflict == 3:
 | 
					            elif conflict == 3:
 | 
				
			||||||
                # Same file checksum
 | 
					                # Same file checksum
 | 
				
			||||||
                if imp == 'move':
 | 
					                if imp == 'move':
 | 
				
			||||||
@ -766,10 +671,10 @@ class SortMedias:
 | 
				
			|||||||
            for files_data, conflict in self._solve_conflicts(conflicts,
 | 
					            for files_data, conflict in self._solve_conflicts(conflicts,
 | 
				
			||||||
                remove_duplicates):
 | 
					                remove_duplicates):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                src_path, dest_path, media = files_data
 | 
					                src_path, dest_path, metadata = files_data
 | 
				
			||||||
                if not conflict:
 | 
					                if not conflict:
 | 
				
			||||||
                    self.sort_file(
 | 
					                    self.sort_file(
 | 
				
			||||||
                        src_path, dest_path, media, imp=imp
 | 
					                        src_path, dest_path, metadata, imp=imp
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
                elif conflict == 1:
 | 
					                elif conflict == 1:
 | 
				
			||||||
                    # There is unresolved conflict
 | 
					                    # There is unresolved conflict
 | 
				
			||||||
@ -787,6 +692,7 @@ class SortMedias:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class Collection(SortMedias):
 | 
					class Collection(SortMedias):
 | 
				
			||||||
    """Class of the media collection."""
 | 
					    """Class of the media collection."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(
 | 
					    def __init__(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
        root,
 | 
					        root,
 | 
				
			||||||
@ -1047,7 +953,8 @@ class Collection(SortMedias):
 | 
				
			|||||||
            media.metadata['file_path'] = fpath.get_path(media.metadata)
 | 
					            media.metadata['file_path'] = fpath.get_path(media.metadata)
 | 
				
			||||||
            subdirs.add(media.file_path.parent)
 | 
					            subdirs.add(media.file_path.parent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.medias.datas.append(copy(media))
 | 
					            src_path = media.file_path
 | 
				
			||||||
 | 
					            self.medias.datas[src_path] = copy(media.metadata)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Sort files and solve conflicts
 | 
					        # Sort files and solve conflicts
 | 
				
			||||||
        self.summary = self.sort_medias(imp, remove_duplicates)
 | 
					        self.summary = self.sort_medias(imp, remove_duplicates)
 | 
				
			||||||
@ -1099,7 +1006,8 @@ class Collection(SortMedias):
 | 
				
			|||||||
                dedup_path.append(''.join(filtered_items))
 | 
					                dedup_path.append(''.join(filtered_items))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            media.metadata['file_path'] = os.path.join(*dedup_path)
 | 
					            media.metadata['file_path'] = os.path.join(*dedup_path)
 | 
				
			||||||
            self.medias.datas.append(copy(media))
 | 
					            src_path = media.file_path
 | 
				
			||||||
 | 
					            self.medias.datas[src_path] = copy(media.metadata)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Sort files and solve conflicts
 | 
					        # Sort files and solve conflicts
 | 
				
			||||||
        self.sort_medias(remove_duplicates=remove_duplicates)
 | 
					        self.sort_medias(remove_duplicates=remove_duplicates)
 | 
				
			||||||
@ -1120,9 +1028,10 @@ class Collection(SortMedias):
 | 
				
			|||||||
            self.paths.paths_list.append(img_path)
 | 
					            self.paths.paths_list.append(img_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            media = self.medias.get_media(img_path, path)
 | 
					            media = self.medias.get_media(img_path, path)
 | 
				
			||||||
            relpath = os.path.join(directory_name, image.img_path.name)
 | 
					            relpath = os.path.join(directory_name, img_path.name)
 | 
				
			||||||
            media.metadata['file_path'] = relpath
 | 
					            media.metadata['file_path'] = relpath
 | 
				
			||||||
            self.medias.datas.append(copy(media))
 | 
					            file_path = media.file_path
 | 
				
			||||||
 | 
					            self.medias.datas[file_path] = copy(media.metadata)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.medias.datas:
 | 
					        if self.medias.datas:
 | 
				
			||||||
            # Found similar images to image
 | 
					            # Found similar images to image
 | 
				
			||||||
@ -1130,7 +1039,8 @@ class Collection(SortMedias):
 | 
				
			|||||||
            media = self.medias.get_media(image.img_path, path)
 | 
					            media = self.medias.get_media(image.img_path, path)
 | 
				
			||||||
            relpath = os.path.join(directory_name, image.img_path.name)
 | 
					            relpath = os.path.join(directory_name, image.img_path.name)
 | 
				
			||||||
            media.metadata['file_path'] = relpath
 | 
					            media.metadata['file_path'] = relpath
 | 
				
			||||||
            self.medias.datas.insert(0, copy(media))
 | 
					            file_path = media.file_path
 | 
				
			||||||
 | 
					            self.medias.datas[file_path] = copy(media.metadata)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return True
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1146,6 +1056,8 @@ class Collection(SortMedias):
 | 
				
			|||||||
        images = Images(images_paths, logger=self.logger)
 | 
					        images = Images(images_paths, logger=self.logger)
 | 
				
			||||||
        nb_row_ini = self.db.sqlite.len('metadata')
 | 
					        nb_row_ini = self.db.sqlite.len('metadata')
 | 
				
			||||||
        for image in images_paths:
 | 
					        for image in images_paths:
 | 
				
			||||||
 | 
					            # Clear datas in every loops
 | 
				
			||||||
 | 
					            self.medias.datas = {}
 | 
				
			||||||
            similar_images = self._find_similar_images(
 | 
					            similar_images = self._find_similar_images(
 | 
				
			||||||
                image, images, path, dest_dir, similarity
 | 
					                image, images, path, dest_dir, similarity
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										285
									
								
								ordigi/media.py
									
									
									
									
									
								
							
							
						
						
									
										285
									
								
								ordigi/media.py
									
									
									
									
									
								
							@ -16,10 +16,185 @@ from ordigi import utils
 | 
				
			|||||||
from ordigi import request
 | 
					from ordigi import request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Metadata:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, ignore_tags=None):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Options
 | 
				
			||||||
 | 
					        if ignore_tags is None:
 | 
				
			||||||
 | 
					            ignore_tags = set()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.exif_metadata = []
 | 
				
			||||||
 | 
					        self.metadata = {}
 | 
				
			||||||
 | 
					        # self.datas = {}
 | 
				
			||||||
 | 
					        self.ignore_tags = ignore_tags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Attributes
 | 
				
			||||||
 | 
					        self.tags_keys = self.get_tags()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_tags(self) -> dict:
 | 
				
			||||||
 | 
					        """Get exif tags groups in dict"""
 | 
				
			||||||
 | 
					        tags_keys = {}
 | 
				
			||||||
 | 
					        tags_keys['date_original'] = [
 | 
				
			||||||
 | 
					            'EXIF:DateTimeOriginal',
 | 
				
			||||||
 | 
					            'H264:DateTimeOriginal',
 | 
				
			||||||
 | 
					            'QuickTime:ContentCreateDate',
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        tags_keys['date_created'] = [
 | 
				
			||||||
 | 
					            'EXIF:CreateDate',
 | 
				
			||||||
 | 
					            'QuickTime:CreationDate',
 | 
				
			||||||
 | 
					            'QuickTime:CreateDate',
 | 
				
			||||||
 | 
					            'QuickTime:CreationDate-und-US',
 | 
				
			||||||
 | 
					            'QuickTime:MediaCreateDate',
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        tags_keys['date_modified'] = ['File:FileModifyDate', 'QuickTime:ModifyDate']
 | 
				
			||||||
 | 
					        tags_keys['camera_make'] = ['EXIF:Make', 'QuickTime:Make']
 | 
				
			||||||
 | 
					        tags_keys['camera_model'] = ['EXIF:Model', 'QuickTime:Model']
 | 
				
			||||||
 | 
					        tags_keys['album'] = ['XMP-xmpDM:Album', 'XMP:Album']
 | 
				
			||||||
 | 
					        tags_keys['title'] = ['XMP:Title', 'XMP:DisplayName']
 | 
				
			||||||
 | 
					        tags_keys['latitude'] = [
 | 
				
			||||||
 | 
					            'EXIF:GPSLatitude',
 | 
				
			||||||
 | 
					            'XMP:GPSLatitude',
 | 
				
			||||||
 | 
					            # 'QuickTime:GPSLatitude',
 | 
				
			||||||
 | 
					            'Composite:GPSLatitude',
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        tags_keys['longitude'] = [
 | 
				
			||||||
 | 
					            'EXIF:GPSLongitude',
 | 
				
			||||||
 | 
					            'XMP:GPSLongitude',
 | 
				
			||||||
 | 
					            # 'QuickTime:GPSLongitude',
 | 
				
			||||||
 | 
					            'Composite:GPSLongitude',
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        tags_keys['latitude_ref'] = ['EXIF:GPSLatitudeRef']
 | 
				
			||||||
 | 
					        tags_keys['longitude_ref'] = ['EXIF:GPSLongitudeRef']
 | 
				
			||||||
 | 
					        tags_keys['original_name'] = ['XMP:OriginalFileName']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Remove ignored tag from list
 | 
				
			||||||
 | 
					        for tag_regex in self.ignore_tags:
 | 
				
			||||||
 | 
					            for key, tags in tags_keys.items():
 | 
				
			||||||
 | 
					                for i, tag in enumerate(tags):
 | 
				
			||||||
 | 
					                    if re.match(tag_regex, tag):
 | 
				
			||||||
 | 
					                        del tags_keys[key][i]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return tags_keys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _del_ignored_tags(self):
 | 
				
			||||||
 | 
					        for tag_regex in self.ignore_tags:
 | 
				
			||||||
 | 
					            ignored_tags = set()
 | 
				
			||||||
 | 
					            for tag in self.exif_metadata:
 | 
				
			||||||
 | 
					                if re.search(tag_regex, tag) is not None:
 | 
				
			||||||
 | 
					                    ignored_tags.add(tag)
 | 
				
			||||||
 | 
					            for ignored_tag in ignored_tags:
 | 
				
			||||||
 | 
					                del self.exif_metadata[ignored_tag]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class WriteExif(Metadata):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(
 | 
				
			||||||
 | 
					            self,
 | 
				
			||||||
 | 
					            file_path,
 | 
				
			||||||
 | 
					            metadata,
 | 
				
			||||||
 | 
					            exif_metadata=None,
 | 
				
			||||||
 | 
					            ignore_tags=None,
 | 
				
			||||||
 | 
					            logger=logging.getLogger(),
 | 
				
			||||||
 | 
					            ):
 | 
				
			||||||
 | 
					        super().__init__(ignore_tags)
 | 
				
			||||||
 | 
					        self.file_path = file_path
 | 
				
			||||||
 | 
					        self.metadata = metadata
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not exif_metadata:
 | 
				
			||||||
 | 
					            exif_metadata = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.exif_metadata = exif_metadata
 | 
				
			||||||
 | 
					        self.logger = logger.getChild(self.__class__.__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def set_value(self, tag, value):
 | 
				
			||||||
 | 
					        """Set value of a tag.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :returns: value (str)
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return ExifTool(self.file_path, logger=self.logger).setvalue(tag, value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def set_key_values(self, key, value):
 | 
				
			||||||
 | 
					        """Set tags values for given key"""
 | 
				
			||||||
 | 
					        status = True
 | 
				
			||||||
 | 
					        if self.exif_metadata is None:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for tag in self.tags_keys[key]:
 | 
				
			||||||
 | 
					            if tag in self.exif_metadata:
 | 
				
			||||||
 | 
					                if not self.set_value(tag, value):
 | 
				
			||||||
 | 
					                    status = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def set_date_media(self, time):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Set the date/time a photo was taken.
 | 
				
			||||||
 | 
					        :param datetime time: datetime object of when the photo was taken
 | 
				
			||||||
 | 
					        :returns: bool
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if time is None:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        formatted_time = time.strftime('%Y:%m:%d %H:%M:%S')
 | 
				
			||||||
 | 
					        status = self.set_value('date_original', formatted_time)
 | 
				
			||||||
 | 
					        if status == False:
 | 
				
			||||||
 | 
					            # exif attribute date_original d'ont exist
 | 
				
			||||||
 | 
					            status = self.set_value('date_created', formatted_time)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def set_coordinates(self, latitude, longitude):
 | 
				
			||||||
 | 
					        status = []
 | 
				
			||||||
 | 
					        if self.metadata['latitude_ref']:
 | 
				
			||||||
 | 
					            latitude = abs(latitude)
 | 
				
			||||||
 | 
					            if latitude > 0:
 | 
				
			||||||
 | 
					                status.append(self.set_value('latitude_ref', 'N'))
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                status.append(self.set_value('latitude_ref', 'S'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        status.append(self.set_value('latitude', latitude))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.metadata['longitude_ref']:
 | 
				
			||||||
 | 
					            longitude = abs(longitude)
 | 
				
			||||||
 | 
					            if longitude > 0:
 | 
				
			||||||
 | 
					                status.append(self.set_value('latitude_ref', 'E'))
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                status.append(self.set_value('longitude_ref', 'W'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        status.append(self.set_value('longitude', longitude))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if all(status):
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def set_album_from_folder(self):
 | 
				
			||||||
 | 
					        """Set the album attribute based on the leaf folder name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :returns: bool
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return self.set_value('album', self.file_path.parent.name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ReadExif(Metadata):
 | 
				
			||||||
 | 
					    def __init__(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        file_path,
 | 
				
			||||||
 | 
					        src_dir,
 | 
				
			||||||
 | 
					        album_from_folder=False,
 | 
				
			||||||
 | 
					        ignore_tags=None,
 | 
				
			||||||
 | 
					        interactive=False,
 | 
				
			||||||
 | 
					        logger=logging.getLogger(),
 | 
				
			||||||
 | 
					        use_date_filename=False,
 | 
				
			||||||
 | 
					        use_file_dates=False,
 | 
				
			||||||
 | 
					    ):
 | 
				
			||||||
 | 
					        super().__init__(ignore_tags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Media:
 | 
					class Media:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    The media class for all media objects.
 | 
					    Extract matadatas from exiftool and sort them to dict structure
 | 
				
			||||||
    The fully qualified path to the media file.
 | 
					 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    d_coordinates = {'latitude': 'latitude_ref', 'longitude': 'longitude_ref'}
 | 
					    d_coordinates = {'latitude': 'latitude_ref', 'longitude': 'longitude_ref'}
 | 
				
			||||||
@ -128,8 +303,8 @@ class Media:
 | 
				
			|||||||
        return mimetype[0]
 | 
					        return mimetype[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _get_key_values(self, key):
 | 
					    def _get_key_values(self, key):
 | 
				
			||||||
        """Get the first value of a tag set
 | 
					        """
 | 
				
			||||||
 | 
					        Get the first value of a tag set
 | 
				
			||||||
        :returns: str or None if no exif tag
 | 
					        :returns: str or None if no exif tag
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if self.exif_metadata is None:
 | 
					        if self.exif_metadata is None:
 | 
				
			||||||
@ -140,8 +315,8 @@ class Media:
 | 
				
			|||||||
                yield self.exif_metadata[tag]
 | 
					                yield self.exif_metadata[tag]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_value(self, tag):
 | 
					    def get_value(self, tag):
 | 
				
			||||||
        """Get given value from EXIF.
 | 
					        """
 | 
				
			||||||
 | 
					        Get given value from EXIF.
 | 
				
			||||||
        :returns: str or None
 | 
					        :returns: str or None
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if self.exif_metadata is None:
 | 
					        if self.exif_metadata is None:
 | 
				
			||||||
@ -152,7 +327,7 @@ class Media:
 | 
				
			|||||||
        return self.exif_metadata[tag]
 | 
					        return self.exif_metadata[tag]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_date_format(self, value):
 | 
					    def get_date_format(self, value):
 | 
				
			||||||
        """Formate date attribute.
 | 
					        """Formatting date attribute.
 | 
				
			||||||
        :returns: datetime object or None
 | 
					        :returns: datetime object or None
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        # We need to parse a string to datetime format.
 | 
					        # We need to parse a string to datetime format.
 | 
				
			||||||
@ -587,3 +762,99 @@ class Media:
 | 
				
			|||||||
        return self.set_value('album', self.file_path.parent.name)
 | 
					        return self.set_value('album', self.file_path.parent.name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Medias:
 | 
				
			||||||
 | 
					    """Get media data in collection or source path"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        paths,
 | 
				
			||||||
 | 
					        root,
 | 
				
			||||||
 | 
					        album_from_folder=False,
 | 
				
			||||||
 | 
					        cache=False,
 | 
				
			||||||
 | 
					        db=None,
 | 
				
			||||||
 | 
					        interactive=False,
 | 
				
			||||||
 | 
					        ignore_tags=None,
 | 
				
			||||||
 | 
					        logger=logging.getLogger(),
 | 
				
			||||||
 | 
					        use_date_filename=False,
 | 
				
			||||||
 | 
					        use_file_dates=False,
 | 
				
			||||||
 | 
					    ):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Modules
 | 
				
			||||||
 | 
					        self.db = db
 | 
				
			||||||
 | 
					        self.paths = paths
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Arguments
 | 
				
			||||||
 | 
					        self.root = root
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Options
 | 
				
			||||||
 | 
					        self.cache = cache
 | 
				
			||||||
 | 
					        self.album_from_folder = album_from_folder
 | 
				
			||||||
 | 
					        self.ignore_tags = ignore_tags
 | 
				
			||||||
 | 
					        self.interactive = interactive
 | 
				
			||||||
 | 
					        self.logger = logger.getChild(self.__class__.__name__)
 | 
				
			||||||
 | 
					        self.use_date_filename = use_date_filename
 | 
				
			||||||
 | 
					        self.use_file_dates = use_file_dates
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Attributes
 | 
				
			||||||
 | 
					        # List to store medias datas
 | 
				
			||||||
 | 
					        self.datas = {}
 | 
				
			||||||
 | 
					        self.theme = request.load_theme()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_media(self, file_path, src_dir, loc=None):
 | 
				
			||||||
 | 
					        media = Media(
 | 
				
			||||||
 | 
					            file_path,
 | 
				
			||||||
 | 
					            src_dir,
 | 
				
			||||||
 | 
					            self.album_from_folder,
 | 
				
			||||||
 | 
					            self.ignore_tags,
 | 
				
			||||||
 | 
					            self.interactive,
 | 
				
			||||||
 | 
					            self.logger,
 | 
				
			||||||
 | 
					            self.use_date_filename,
 | 
				
			||||||
 | 
					            self.use_file_dates,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        media.get_metadata(self.root, loc, self.db.sqlite, self.cache)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return media
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_medias(self, src_dirs, imp=False, loc=None):
 | 
				
			||||||
 | 
					        """Get medias data"""
 | 
				
			||||||
 | 
					        for src_dir in src_dirs:
 | 
				
			||||||
 | 
					            src_dir = self.paths.check(src_dir)
 | 
				
			||||||
 | 
					            paths = self.paths.get_paths_list(src_dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # Get medias and src_dirs
 | 
				
			||||||
 | 
					            for src_path in paths:
 | 
				
			||||||
 | 
					                if self.root not in src_path.parents:
 | 
				
			||||||
 | 
					                    if not imp:
 | 
				
			||||||
 | 
					                        self.logger.error(f"""{src_path} not in {self.root}
 | 
				
			||||||
 | 
					                                collection, use `ordigi import`""")
 | 
				
			||||||
 | 
					                        sys.exit(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # Get file metadata
 | 
				
			||||||
 | 
					                media = self.get_media(src_path, src_dir, loc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                yield media
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update_exif_data(self, metadata):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        file_path = self.root / metadata['file_path']
 | 
				
			||||||
 | 
					        exif = WriteExif(file_path, metadata, self.ignore_tags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        updated = False
 | 
				
			||||||
 | 
					        if self.album_from_folder:
 | 
				
			||||||
 | 
					            exif.set_album_from_folder()
 | 
				
			||||||
 | 
					            updated = True
 | 
				
			||||||
 | 
					        if metadata['original_name'] in (False, ''):
 | 
				
			||||||
 | 
					            exif.set_value('original_name', metadata['filename'])
 | 
				
			||||||
 | 
					            updated = True
 | 
				
			||||||
 | 
					        if self.album_from_folder:
 | 
				
			||||||
 | 
					            album = metadata['album']
 | 
				
			||||||
 | 
					            if album and album != '':
 | 
				
			||||||
 | 
					                exif.set_value('album', album)
 | 
				
			||||||
 | 
					                updated = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if updated:
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -206,13 +206,14 @@ class TestCollection:
 | 
				
			|||||||
            # copy mode
 | 
					            # copy mode
 | 
				
			||||||
            src_path = Path(self.src_path, 'test_exif', 'photo.png')
 | 
					            src_path = Path(self.src_path, 'test_exif', 'photo.png')
 | 
				
			||||||
            media = Media(src_path, self.src_path)
 | 
					            media = Media(src_path, self.src_path)
 | 
				
			||||||
            metadata = media.get_metadata(tmp_path)
 | 
					            media.get_metadata(tmp_path)
 | 
				
			||||||
            name = 'photo_' + str(imp) + '.png'
 | 
					            name = 'photo_' + str(imp) + '.png'
 | 
				
			||||||
            media.metadata['file_path'] = name
 | 
					            media.metadata['file_path'] = name
 | 
				
			||||||
            dest_path = Path(tmp_path, name)
 | 
					            dest_path = Path(tmp_path, name)
 | 
				
			||||||
            src_checksum = utils.checksum(src_path)
 | 
					            src_checksum = utils.checksum(src_path)
 | 
				
			||||||
            summary = collection.sort_file(src_path, dest_path, media,
 | 
					            summary = collection.sort_file(
 | 
				
			||||||
                    imp=imp)
 | 
					                src_path, dest_path, media.metadata, imp=imp
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            assert not summary.errors
 | 
					            assert not summary.errors
 | 
				
			||||||
            # Ensure files remain the same
 | 
					            # Ensure files remain the same
 | 
				
			||||||
            assert collection._checkcomp(dest_path, src_checksum)
 | 
					            assert collection._checkcomp(dest_path, src_checksum)
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user