mee/mee.py
2023-04-21 21:59:06 +03:00

147 lines
3.7 KiB
Python

"""Minimalistic EXIF Editor.
Commands overview:
ls, get, set, del operate with EXIF tags.
cget, cset, cdel operate with JSON in UserComment EXIF tag.
Usage:
mee ls <file>
mee get [-k <key>] <file>
mee set -k <key> -v <value> <file> [-o <file>]
mee del -k <key> <file> [-o <file>]
mee cget [-k <key>] <file>
mee cset -k <key> -v <value> <file> [-o <file>]
mee cdel -k <key> <file> [-o <file>]
mee (--help | --version)
Options:
-o, --output <file> output file. Same as input file by default.
-k, --key <key> set EXIF/JSON key.
-v, --value <value> EXIF/JSON key value.
--help print this message and exit.
--version print version and exit.
"""
__version__ = "0.1.0"
import re
import sys
import json
from tempfile import NamedTemporaryFile
from typing import Optional
from pathlib import Path
from docopt import docopt
from exif import Image
def open_image(infile: Path) -> Image:
with open(infile, "rb") as in_img:
return Image(in_img)
def commit_image(img: Image, infile: Path = None, outfile: Path = None) -> None:
if not outfile:
with NamedTemporaryFile(suffix="~") as out_tmp, open(infile, "wb") as out_img:
out_tmp.write(img.get_file())
out_tmp.seek(0)
out_img.write(out_tmp.read())
else:
with open(outfile, "wb") as out_img:
out_img.write(img.get_file())
def get_exif(key: str, infile: Path, echo: bool = True) -> None:
image = open_image(infile)
value = image.get(key, "n/a")
if echo:
print(f"{key}: {value}")
else:
return value
def ls_exif(infile: Path) -> None:
image = open_image(infile)
tags = image.list_all()
print(tags)
def set_exif(key: str, value: str, infile: Path, outfile: Path = None) -> None:
image = open_image(infile)
image.set(key, value)
commit_image(image, infile, outfile)
def del_exif(key: str, infile: Path, outfile: Path = None) -> None:
image = open_image(infile)
image.delete(key)
commit_image(image, infile, outfile)
def get_user_comment(infile: Path) -> dict:
user_comment = get_exif("user_comment", infile, echo=False)
try:
return json.loads(user_comment)
except (TypeError, json.JSONDecodeError):
return {}
def get_json(infile: Path, key: str = None) -> None:
user_comment = get_user_comment(infile)
if key:
try:
print(user_comment[key])
except KeyError as e:
sys.exit(f"No key: {e}")
else:
print(json.dumps(user_comment, indent=4, sort_keys=True))
def mod_json(key: str, value: str, infile: Path, outfile: Path = None, delete: bool = False) -> None:
user_comment = get_user_comment(infile)
if delete:
del user_comment[key]
else:
user_comment[key] = value
try:
del_exif("user_comment", infile, outfile)
except AttributeError:
pass
set_exif("user_comment", json.dumps(user_comment), infile, outfile)
def cli() -> None:
args = docopt(__doc__, version=__version__)
key = args["--key"]
value = args["--value"]
infile = args['<file>']
outfile = args['--output']
if args['ls']:
ls_exif(infile)
if args['get']:
if not key:
ls_exif(infile)
else:
get_exif(key, infile)
if args["set"]:
set_exif(key, value, infile, outfile)
if args['del']:
del_exif(key, infile, outfile)
if args['cget']:
get_json(infile, key)
if args["cset"]:
mod_json(key, value, infile, outfile)
if args['cdel']:
mod_json(key, None, infile, outfile, delete=True)
if __name__ == "__main__":
cli()