Refactor shared script helpers

This commit is contained in:
ajp_anton
2026-06-08 02:55:31 +00:00
parent ae05bc9710
commit b6024d8b65
10 changed files with 855 additions and 356 deletions
+144
View File
@@ -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))