Refactoring media class (3)

This commit is contained in:
Cédric Leporcq 2021-11-11 16:24:37 +01:00
parent 8fd65fda34
commit 26845cf56b
4 changed files with 95 additions and 99 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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