Fix edit metadata

This commit is contained in:
Cédric Leporcq 2022-04-17 22:05:13 +02:00
parent 5f0237a48f
commit a4746cbf60
5 changed files with 179 additions and 69 deletions

View File

@ -346,6 +346,87 @@ def _compare(**kwargs):
sys.exit(1) sys.exit(1)
@cli.command('edit')
@add_options(_logger_options)
@add_options(_filter_options)
@click.option(
'--key',
'-k',
default=None,
multiple=True,
# TODO Allow edit all values
required=True,
help="Select exif tags groups to edit",
)
@click.argument('subdirs', required=False, nargs=-1, type=click.Path())
@click.argument('path', required=True, nargs=1, type=click.Path())
def _edit(**kwargs):
"""Edit EXIF metadata in files or directories"""
log_level = log.get_level(kwargs['verbose'])
log.console(LOG, level=log_level)
paths, root = _get_paths(kwargs['subdirs'], kwargs['path'])
collection = Collection(
root,
{
'cache': True,
'ignore_tags': kwargs['ignore_tags'],
'exclude': kwargs['exclude'],
'extensions': kwargs['ext'],
'glob': kwargs['glob'],
}
)
location = False
keys = set()
for key in kwargs['key']:
if key not in (
'album',
'camera_make',
'camera_model',
'coordinates',
'date_original',
'date_created',
'date_modified',
'latitude',
'longitude',
'latitude_ref',
'longitude_ref',
'original_name',
'title',
):
LOG.error(f"key '{key}' is not valid")
sys.exit(1)
if key in (
'latitude',
'longitude',
'latitude_ref',
'longitude_ref',
):
location = True
if key == 'coordinates':
keys.update(['latitude', 'longitude'])
else:
keys.add(key)
if location:
loc = _cli_get_location(collection)
else:
loc = None
summary = collection.edit_metadata(paths, keys, loc, overwrite=True)
if log_level < 30:
summary.print()
if summary.errors:
sys.exit(1)
@cli.command('init') @cli.command('init')
@add_options(_logger_options) @add_options(_logger_options)
@click.argument('path', required=True, nargs=1, type=click.Path()) @click.argument('path', required=True, nargs=1, type=click.Path())
@ -366,6 +447,9 @@ def _init(**kwargs):
if log_level < 30: if log_level < 30:
summary.print() summary.print()
if summary.errors:
sys.exit(1)
@cli.command('import') @cli.command('import')
@add_options(_logger_options) @add_options(_logger_options)

View File

@ -543,6 +543,7 @@ class SortMedias:
def sort_file(self, src_path, dest_path, metadata, 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)
else: else:
@ -1130,69 +1131,50 @@ class Collection(SortMedias):
return self.summary return self.summary
def edit_metadata(self, path, key, loc=None, overwrite=False): def edit_metadata(self, paths, keys, loc=None, overwrite=False):
"""Fill metadata and exif data for given key""" """Edit metadata and exif data for given key"""
self._init_check_db() self._init_check_db()
if key in ( for file_path, media in self.medias.get_medias_datas(paths, loc=loc):
'latitude',
'longitude',
'latitude_ref',
'longitude_ref',
):
print(f"Set {key} alone is not allowed")
return None
if overwrite:
print(f"Edit {key} values:")
else:
print(f"Fill empty {key} values:")
paths = self.paths.get_paths_list(path)
for file_path in paths:
media = self.medias.get_media(file_path, self.root)
media.get_metadata(
self.root, loc, self.db.sqlite, False
)
media.metadata['file_path'] = os.path.relpath(file_path, self.root) media.metadata['file_path'] = os.path.relpath(file_path, self.root)
print() for key in keys:
value = media.metadata[key] print()
if overwrite or not value: value = media.metadata[key]
print(f"FILE: '{file_path}'") if overwrite or not value:
if overwrite: print(f"FILE: '{file_path}'")
print(f"{key}: '{value}'") if overwrite:
if overwrite or not value: print(f"{key}: '{value}'")
# Prompt value for given key for file_path if overwrite or not value:
# utils.open_file() # Prompt value for given key for file_path
prompt = [ prompt = [
inquirer.Text('value', message=key), inquirer.Text('value', message=key),
] ]
answer = inquirer.prompt(prompt, theme=self.theme) answer = inquirer.prompt(prompt, theme=self.theme)
# Validate value # answer = {'value': '03-12-2021 08:12:35'}
if key in ('date_original', 'date_created', 'date_modified'): # Validate value
# Check date format if key in ('date_original', 'date_created', 'date_modified'):
value = media.get_date_format(answer['value']) # Check date format
else: value = media.get_date_format(answer['value'])
if not answer[key].isalnum(): else:
print("Invalid entry, use alphanumeric chars") if not answer[key].isalnum():
value = inquirer.prompt(prompt, theme=self.theme) print("Invalid entry, use alphanumeric chars")
value = inquirer.prompt(prompt, theme=self.theme)
result = False result = False
if value: if value:
media.metadata[key] = value media.metadata[key] = value
# Update database # Update database
self.db.add_file_data(media.metadata) self.db.add_file_data(media.metadata)
# Update exif data # Update exif data
exif = WriteExif( exif = WriteExif(
file_path, file_path,
media.metadata, media.metadata,
ignore_tags=self.opt['Exif']['ignore_tags'], ignore_tags=self.opt['Exif']['ignore_tags'],
) )
result = exif.set_key_values(key, value) result = exif.set_key_values(key, value)
if result: if result:
self.summary.append('update', True, file_path) self.summary.append('update', True, file_path)
else: else:
self.summary.append('update', False, file_path) self.summary.append('update', False, file_path)
return self.summary return self.summary

View File

@ -665,16 +665,20 @@ class Medias:
return media return media
def get_metadata(self, file_path, src_dir, loc=None): def get_media_data(self, file_path, src_dir, loc=None):
media = self.get_media(file_path, src_dir) media = self.get_media(file_path, src_dir)
media.get_metadata( media.get_metadata(
self.root, loc, self.db.sqlite, self.exif_opt['cache'] self.root, loc, self.db.sqlite, self.exif_opt['cache']
) )
return media.metadata return media
def get_metadatas(self, src_dirs, imp=False, loc=None): def get_metadata(self, src_path, src_dir, loc=None):
"""Get medias data""" """Get metadata"""
return self.get_media_data(src_path, src_dir, loc).metadata
def get_paths(self, src_dirs, imp=False):
"""Get paths"""
for src_dir in src_dirs: for src_dir in src_dirs:
src_dir = self.paths.check(src_dir) src_dir = self.paths.check(src_dir)
paths = self.paths.get_paths_list(src_dir) paths = self.paths.get_paths_list(src_dir)
@ -687,10 +691,23 @@ class Medias:
collection, use `ordigi import`""") collection, use `ordigi import`""")
sys.exit(1) sys.exit(1)
# Get file metadata yield src_dir, src_path
metadata = self.get_metadata(src_path, src_dir, loc)
yield src_path, metadata def get_medias_datas(self, src_dirs, imp=False, loc=None):
"""Get medias datas"""
for src_dir, src_path in self.get_paths(src_dirs, imp=imp):
# Get file metadata
media = self.get_media_data(src_path, src_dir, loc)
yield src_path, media
def get_metadatas(self, src_dirs, imp=False, loc=None):
"""Get medias data"""
for src_dir, src_path in self.get_paths(src_dirs, imp=imp):
# Get file metadata
metadata = self.get_metadata(src_path, src_dir, loc)
yield src_path, metadata
def update_exif_data(self, metadata): def update_exif_data(self, metadata):

View File

@ -2,6 +2,7 @@ import shutil
from click.testing import CliRunner from click.testing import CliRunner
from pathlib import Path from pathlib import Path
import pytest import pytest
import inquirer
from ordigi import cli from ordigi import cli
@ -62,6 +63,7 @@ class TestOrdigi:
cli._check, cli._check,
cli._clean, cli._clean,
cli._compare, cli._compare,
cli._edit,
cli._import, cli._import,
cli._init, cli._init,
cli._sort, cli._sort,
@ -76,6 +78,32 @@ class TestOrdigi:
self.assert_cli(cli._clone, ['not_exist'], state=2) self.assert_cli(cli._clone, ['not_exist'], state=2)
def test_edit(self, monkeypatch):
bool_options = ()
arg_options = (
*self.logger_options,
*self.filter_options,
)
def mockreturn(prompt, theme):
return {'value': '03-12-2021 08:12:35'}
monkeypatch.setattr(inquirer, 'prompt', mockreturn)
args = (
'--key',
'date_original',
str(self.src_path.joinpath('test_exif/photo.png')),
str(self.src_path),
)
self.assert_cli(cli._edit, args)
# self.assert_options(cli._edit, bool_options, arg_options, args)
# self.assert_all_options(cli._edit, bool_options, arg_options, args)
def test_sort(self): def test_sort(self):
bool_options = ( bool_options = (
# '--interactive', # '--interactive',

View File

@ -251,7 +251,6 @@ class TestCollection:
# Summary is created and there is no errors # Summary is created and there is no errors
assert not summary.errors assert not summary.errors
# @pytest.mark.skip()
def test_edit_metadata(self, tmp_path, monkeypatch): def test_edit_metadata(self, tmp_path, monkeypatch):
path = tmp_path / 'collection' path = tmp_path / 'collection'
shutil.copytree(self.src_path, path) shutil.copytree(self.src_path, path)
@ -263,7 +262,7 @@ class TestCollection:
monkeypatch.setattr(inquirer, 'prompt', mockreturn) monkeypatch.setattr(inquirer, 'prompt', mockreturn)
collection.edit_metadata(path, 'date_original', overwrite=True) collection.edit_metadata({path}, {'date_original'}, overwrite=True)
# check if db value is set # check if db value is set
date = collection.db.sqlite.get_metadata_data('test_exif/photo.rw2', date = collection.db.sqlite.get_metadata_data('test_exif/photo.rw2',
'DateOriginal') 'DateOriginal')