Update config and settings etc
This commit is contained in:
@@ -6,7 +6,6 @@ import datetime
|
||||
import toml
|
||||
from alive_progress import alive_bar
|
||||
|
||||
|
||||
CONFIG_PATH = os.path.expanduser("~/.config/emulsion/config.toml")
|
||||
|
||||
|
||||
@@ -27,8 +26,15 @@ def parse_args(config):
|
||||
nargs='*',
|
||||
help='Image files to process (e.g. *.jpg *.tif).'
|
||||
)
|
||||
|
||||
# Configurable fields
|
||||
parser.add_argument('--author', default=None, help='Name of the photographer.')
|
||||
parser.add_argument('--lab', default=None, help='Name of the lab to store in XMP:DevelopedBy.')
|
||||
parser.add_argument('--lab', default=None, help='Name of the lab who developed the film.')
|
||||
parser.add_argument('--make', default=None, help='Camera make (stored in EXIF:Make).')
|
||||
parser.add_argument('--model', default=None, help='Camera model (stored in EXIF:Model).')
|
||||
parser.add_argument('--film', default=None, help='Film stock (stored in EXIF:UserComment and XMP:Description).')
|
||||
|
||||
# Time settings
|
||||
parser.add_argument('--base-date', default=None, help='Base date or date/time (e.g. 2023-04-10 or 2023-04-10 12:00:00).')
|
||||
parser.add_argument('--time-increment', type=int, default=None, help='Time increment in seconds between images.')
|
||||
parser.add_argument('--dry-run', action='store_true', help='Show what would be changed without modifying files.')
|
||||
@@ -36,10 +42,22 @@ def parse_args(config):
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Merge from config
|
||||
if args.author is None and 'author' in config:
|
||||
args.author = config['author']
|
||||
|
||||
if args.lab is None and 'lab' in config:
|
||||
args.lab = config['lab']
|
||||
|
||||
if args.make is None and 'make' in config:
|
||||
args.make = config['make']
|
||||
|
||||
if args.model is None and 'model' in config:
|
||||
args.model = config['model']
|
||||
|
||||
if args.film is None and 'film' in config:
|
||||
args.film = config['film']
|
||||
|
||||
if args.time_increment is None and 'time_increment' in config:
|
||||
args.time_increment = config['time_increment']
|
||||
|
||||
@@ -47,6 +65,10 @@ def parse_args(config):
|
||||
|
||||
|
||||
def prompt_for_config(args):
|
||||
"""
|
||||
Prompt for config-only fields before creating a config file.
|
||||
(Base date is ephemeral, not stored in config.)
|
||||
"""
|
||||
try:
|
||||
if not args.author:
|
||||
args.author = input("Photographer's name (Author)? ").strip()
|
||||
@@ -55,6 +77,18 @@ def prompt_for_config(args):
|
||||
resp = input("Lab name (optional, enter to skip)? ").strip()
|
||||
args.lab = resp if resp else ""
|
||||
|
||||
if args.make is None:
|
||||
resp = input("Camera make (optional, enter to skip)? ").strip()
|
||||
args.make = resp if resp else ""
|
||||
|
||||
if args.model is None:
|
||||
resp = input("Camera model (optional, enter to skip)? ").strip()
|
||||
args.model = resp if resp else ""
|
||||
|
||||
if args.film is None:
|
||||
resp = input("Film stock (optional, enter to skip)? ").strip()
|
||||
args.film = resp if resp else ""
|
||||
|
||||
if not args.time_increment:
|
||||
dflt = "60"
|
||||
resp = input(f"Time increment in seconds [{dflt}]: ").strip()
|
||||
@@ -66,6 +100,10 @@ def prompt_for_config(args):
|
||||
|
||||
|
||||
def prompt_if_missing(args):
|
||||
"""
|
||||
Prompt for ephemeral fields like base_date if missing,
|
||||
and also fill in other fields if user didn't supply them.
|
||||
"""
|
||||
try:
|
||||
if not args.author:
|
||||
args.author = input("Photographer's name (Author)? ").strip()
|
||||
@@ -74,8 +112,20 @@ def prompt_if_missing(args):
|
||||
resp = input("Lab name (optional, enter to skip)? ").strip()
|
||||
args.lab = resp if resp else ""
|
||||
|
||||
if args.make is None:
|
||||
resp = input("Camera make (optional, enter to skip)? ").strip()
|
||||
args.make = resp if resp else ""
|
||||
|
||||
if args.model is None:
|
||||
resp = input("Camera model (optional, enter to skip)? ").strip()
|
||||
args.model = resp if resp else ""
|
||||
|
||||
if args.film is None:
|
||||
resp = input("Film stock (optional, enter to skip)? ").strip()
|
||||
args.film = resp if resp else ""
|
||||
|
||||
if not args.base_date:
|
||||
dflt = "2023-01-01"
|
||||
dflt = datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
resp = input(f"Base date/time for first image [{dflt}]: ").strip()
|
||||
args.base_date = resp if resp else dflt
|
||||
|
||||
@@ -97,28 +147,56 @@ def parse_user_date(dt_str):
|
||||
return datetime.datetime.strptime(dt_str, "%Y-%m-%d")
|
||||
|
||||
|
||||
def build_exiftool_cmd(file_path, author, lab, timestamp, dry_run=False):
|
||||
def build_exiftool_cmd(file_path, author, lab, make, model, film, timestamp, dry_run=False):
|
||||
"""
|
||||
Use standard EXIF fields:
|
||||
- EXIF:Make (args.make)
|
||||
- EXIF:Model (args.model)
|
||||
- EXIF:UserComment (args.film)
|
||||
Also store film in XMP:Description for better compatibility.
|
||||
"""
|
||||
current_year = datetime.datetime.now().year
|
||||
cmd = [
|
||||
"exiftool",
|
||||
"-overwrite_original",
|
||||
|
||||
# Photographer info
|
||||
f"-Artist={author}",
|
||||
f"-Creator={author}",
|
||||
f"-By-line={author}",
|
||||
f"-Credit={author}",
|
||||
f"-CopyrightNotice={author}",
|
||||
f"-Copyright={author}",
|
||||
f"-CopyrightNotice=© {current_year} {author}",
|
||||
f"-Copyright=© {current_year} {author}",
|
||||
|
||||
# Timestamps
|
||||
f"-DateTimeOriginal={timestamp}",
|
||||
|
||||
# Clear out some lab fields
|
||||
"-WebStatement=",
|
||||
"-CreatorWorkURL=",
|
||||
f"-DateTimeOriginal={timestamp}"
|
||||
"-CreatorWorkURL="
|
||||
]
|
||||
|
||||
# Lab in XMP:DevelopedBy
|
||||
if lab:
|
||||
cmd.append(f"-XMP:DevelopedBy={lab}")
|
||||
|
||||
# If user gave a make, store it in EXIF:Make
|
||||
if make:
|
||||
cmd.append(f"-Make={make}")
|
||||
|
||||
# If user gave a model, store it in EXIF:Model
|
||||
if model:
|
||||
cmd.append(f"-Model={model}")
|
||||
|
||||
# If user gave a film stock, store it in EXIF:UserComment AND XMP:Description
|
||||
if film:
|
||||
cmd.append(f"-UserComment={film}")
|
||||
cmd.append(f"-XMP:Description={film}")
|
||||
|
||||
cmd.append(file_path)
|
||||
|
||||
if dry_run:
|
||||
# Return a string for printing only
|
||||
return " ".join(cmd)
|
||||
|
||||
return cmd
|
||||
|
||||
|
||||
@@ -130,9 +208,21 @@ def create_config_file(args):
|
||||
defaults = {
|
||||
"author": args.author or "Your Name",
|
||||
"lab": args.lab or "",
|
||||
"make": args.make or "",
|
||||
"model": args.model or "",
|
||||
"film": args.film or "",
|
||||
"time_increment": args.time_increment if args.time_increment else 60
|
||||
}
|
||||
|
||||
# Remove empty values so user is prompted next time if they left something blank
|
||||
keys_to_remove = []
|
||||
for k, v in defaults.items():
|
||||
if isinstance(v, str) and not v.strip():
|
||||
keys_to_remove.append(k)
|
||||
|
||||
for k in keys_to_remove:
|
||||
del defaults[k]
|
||||
|
||||
os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True)
|
||||
|
||||
with open(CONFIG_PATH, "w", encoding="utf-8") as f:
|
||||
@@ -183,6 +273,9 @@ def main():
|
||||
file_path=f,
|
||||
author=args.author,
|
||||
lab=args.lab,
|
||||
make=args.make,
|
||||
model=args.model,
|
||||
film=args.film,
|
||||
timestamp=timestamp_str,
|
||||
dry_run=args.dry_run
|
||||
)
|
||||
@@ -194,8 +287,8 @@ def main():
|
||||
subprocess.run(
|
||||
cmd,
|
||||
check=True,
|
||||
stdout=subprocess.DEVNULL, # Hide exiftool normal output
|
||||
stderr=subprocess.DEVNULL # Hide exiftool error output
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL
|
||||
)
|
||||
bar.text(f"Updated {f} => {timestamp_str}")
|
||||
except subprocess.CalledProcessError as e:
|
||||
|
||||
Reference in New Issue
Block a user