Refactor shared script helpers
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Copy meaningful metadata from one file to another.
|
||||
|
||||
Run without files to type source/destination paths, or drag exactly two files
|
||||
onto the script and choose which is the source.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from tools.console import pause_if_interactive, prompt_input
|
||||
from tools.exiftool import require_exiftool
|
||||
|
||||
|
||||
EXCLUDED_COPY_TAGS = [
|
||||
"--File:all",
|
||||
"--ExifTool:all",
|
||||
"--Composite:all",
|
||||
"--SourceFile",
|
||||
"--Directory",
|
||||
"--FileName",
|
||||
"--FileSize",
|
||||
"--FileModifyDate",
|
||||
"--FileAccessDate",
|
||||
"--FileInodeChangeDate",
|
||||
"--FilePermissions",
|
||||
"--FileType",
|
||||
"--FileTypeExtension",
|
||||
"--MIMEType",
|
||||
"--ImageWidth",
|
||||
"--ImageHeight",
|
||||
"--ExifImageWidth",
|
||||
"--ExifImageHeight",
|
||||
"--PixelXDimension",
|
||||
"--PixelYDimension",
|
||||
"--RelatedImageWidth",
|
||||
"--RelatedImageHeight",
|
||||
"--ImageSize",
|
||||
"--Megapixels",
|
||||
"--XResolution",
|
||||
"--YResolution",
|
||||
"--ResolutionUnit",
|
||||
]
|
||||
|
||||
|
||||
def prompt_path(prompt: str) -> Path:
|
||||
while True:
|
||||
value = prompt_input(prompt).strip().strip('"')
|
||||
path = Path(value).expanduser()
|
||||
if path.is_file():
|
||||
return path.resolve()
|
||||
print("File not found. Enter a single file path.")
|
||||
|
||||
|
||||
def choose_from_two(path_a: Path, path_b: Path) -> tuple[Path, Path] | None:
|
||||
print("Choose metadata source:")
|
||||
print(f"1: {path_a}")
|
||||
print(f"2: {path_b}")
|
||||
while True:
|
||||
answer = prompt_input("Source file [1/2, Enter=1]: ").strip()
|
||||
if answer == "" or answer == "1":
|
||||
return path_a, path_b
|
||||
if answer == "2":
|
||||
return path_b, path_a
|
||||
print("Please choose 1 or 2.")
|
||||
|
||||
|
||||
def resolve_files(argv: list[str]) -> tuple[Path, Path] | None:
|
||||
args = argv[1:]
|
||||
if not args:
|
||||
source = prompt_path("Source file: ")
|
||||
destination = prompt_path("Destination file: ")
|
||||
return source, destination
|
||||
|
||||
if len(args) != 2:
|
||||
print("Error: provide exactly two files, or run without files to type paths.")
|
||||
return None
|
||||
|
||||
paths = [Path(arg).expanduser().resolve() for arg in args]
|
||||
missing = [path for path in paths if not path.is_file()]
|
||||
if missing:
|
||||
for path in missing:
|
||||
print(f"File not found: {path}")
|
||||
return None
|
||||
|
||||
return choose_from_two(paths[0], paths[1])
|
||||
|
||||
|
||||
def copy_metadata(exiftool: str, source: Path, destination: Path) -> int:
|
||||
print(f"Source: {source}")
|
||||
print(f"Destination: {destination}", flush=True)
|
||||
command = [
|
||||
exiftool,
|
||||
"-overwrite_original",
|
||||
"-TagsFromFile",
|
||||
str(source),
|
||||
"-all:all",
|
||||
*EXCLUDED_COPY_TAGS,
|
||||
"-charset",
|
||||
"filename=UTF8",
|
||||
str(destination),
|
||||
]
|
||||
return subprocess.run(command).returncode
|
||||
|
||||
|
||||
def run(argv: list[str]) -> int:
|
||||
try:
|
||||
exiftool = require_exiftool()
|
||||
except RuntimeError as exc:
|
||||
print(exc)
|
||||
return 1
|
||||
|
||||
try:
|
||||
resolved = resolve_files(argv)
|
||||
if resolved is None:
|
||||
return 1
|
||||
except RuntimeError as exc:
|
||||
print(exc)
|
||||
return 1
|
||||
|
||||
source, destination = resolved
|
||||
if source == destination:
|
||||
print("Source and destination must be different files.")
|
||||
return 1
|
||||
|
||||
return_code = copy_metadata(exiftool, source, destination)
|
||||
if return_code == 0:
|
||||
print("Metadata copy complete.")
|
||||
return return_code
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
try:
|
||||
return run(argv)
|
||||
finally:
|
||||
pause_if_interactive()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main(sys.argv))
|
||||
Reference in New Issue
Block a user