This commit is contained in:
ge 2023-04-21 21:59:06 +03:00
commit 3a16af64d8
5 changed files with 257 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
dist/

48
README.md Normal file
View File

@ -0,0 +1,48 @@
mee
===
Minimalistic EXIF Editor.
MEE allows you to get, remove and change EXIF tags in images.
In addition to this, support for custom tags is implemented here. Custom tags are written in JSON format to the UserComment tag. MEE can operate with them in the same way as with ordinary tags. Be aware of the 64 kilobytes limit for the UserComment tag.
Custom tags entered there can only be interpreted manually or with MEE.
Installation
============
No prebuilt package provided. Build it by yourself.
1. Install `poetry`
2. Clone repo and run `poetry build`
3. Run `pip install ./dist/*.tar.gz`
Usage
=====
```
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.
```

146
mee.py Normal file
View File

@ -0,0 +1,146 @@
"""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()

43
poetry.lock generated Normal file
View File

@ -0,0 +1,43 @@
# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand.
[[package]]
name = "docopt"
version = "0.6.2"
description = "Pythonic argument parser, that will make you smile"
category = "main"
optional = false
python-versions = "*"
files = [
{file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"},
]
[[package]]
name = "exif"
version = "1.6.0"
description = "Read and modify image EXIF metadata using Python."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "exif-1.6.0-py3-none-any.whl", hash = "sha256:2d002b5740eb897da43de1e6a8f4ebacf5171b4d0b8bc0182c70aebde1be86d3"},
{file = "exif-1.6.0.tar.gz", hash = "sha256:36288d1ffc60030084a04c26f50cc32e19383a36cd234fcfa1fb1c6f698e1d36"},
]
[package.dependencies]
plum-py = ">=0.5.0,<2.0.0"
[[package]]
name = "plum-py"
version = "0.8.6"
description = "Pack/Unpack Memory."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
{file = "plum_py-0.8.6-py3-none-any.whl", hash = "sha256:c0ce6d03fc4bf09dbd838b520085e9dfb690d1604f3eb7a553fb6331c1155b78"},
]
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "1d8a15c0ec1aedf4cc7db64b156a95869a294604a356c1fc8be4e8d0e955a501"

19
pyproject.toml Normal file
View File

@ -0,0 +1,19 @@
[tool.poetry]
name = "mee"
version = "0.1.0"
description = "Minimalistic EXIF Editor."
authors = ["ge"]
license = "Unlicense"
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.10"
exif = "^1.6.0"
docopt = "^0.6.2"
[tool.poetry.scripts]
mee = 'mee:cli'
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"