Enhance Avisynth video workflow and metadata tools
This commit is contained in:
+2
-37
@@ -24,7 +24,7 @@ from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
from typing import Iterable
|
||||
|
||||
from tools.console import pause_if_interactive, prompt_input
|
||||
from tools.console import pause_if_interactive, prompt_input, prompt_yes_no as ask_yes_no
|
||||
from tools.exiftool import require_exiftool, run_exiftool_json, run_exiftool_write
|
||||
from tools.filenames import (
|
||||
format_filename_stem,
|
||||
@@ -40,6 +40,7 @@ from tools.filesystem import (
|
||||
remove_empty_dirs,
|
||||
unique_existing_target,
|
||||
)
|
||||
from tools.timezones import parse_timezone_offset, timezone_to_string
|
||||
|
||||
|
||||
IMAGE_EXTS = {".jpg", ".jpeg", ".heic", ".arw"}
|
||||
@@ -96,7 +97,6 @@ DATETIME_RE = re.compile(
|
||||
TIME_ONLY_RE = re.compile(
|
||||
r"^(?P<h>\d{1,2})(?::?(?P<m>\d{2}))(?::?(?P<s>\d{2}))?$"
|
||||
)
|
||||
TZ_RE = re.compile(r"^(?P<sign>[+-])(?P<h>\d{1,2})(?::?(?P<m>\d{2}))?$")
|
||||
OFFSET_RE = re.compile(r"^([+-])(?:(\d+):)?(\d{1,2})(?::(\d{2}))?$")
|
||||
TOKEN_SHIFT_RE = re.compile(r"(\d+|[a-zA-Z]+)")
|
||||
ORDERED_TIMESTAMP_STEM_RE = re.compile(r"^(?P<timestamp>\d{8}_\d{6})-[1-9]\d*$")
|
||||
@@ -195,19 +195,6 @@ class UserChoices:
|
||||
apply_filename_timestamps: bool
|
||||
|
||||
|
||||
def ask_yes_no(prompt: str, default: bool) -> bool:
|
||||
suffix = "Y/n" if default else "y/N"
|
||||
while True:
|
||||
answer = prompt_input(f"{prompt} ({suffix}) ").strip().lower()
|
||||
if not answer:
|
||||
return default
|
||||
if answer in {"y", "yes"}:
|
||||
return True
|
||||
if answer in {"n", "no"}:
|
||||
return False
|
||||
print("Please answer y or n.")
|
||||
|
||||
|
||||
def ask_group_min_size() -> int:
|
||||
while True:
|
||||
answer = prompt_input(
|
||||
@@ -225,28 +212,6 @@ def ask_group_min_size() -> int:
|
||||
print("Please answer y, n, or an integer minimum group size.")
|
||||
|
||||
|
||||
def parse_timezone_offset(value: str) -> timezone:
|
||||
match = TZ_RE.match(value.strip())
|
||||
if not match:
|
||||
raise ValueError("timezone must look like +09:00 or -05:30")
|
||||
sign = 1 if match.group("sign") == "+" else -1
|
||||
hours = int(match.group("h"))
|
||||
minutes = int(match.group("m") or "0")
|
||||
if minutes not in {0, 15, 30, 45}:
|
||||
raise ValueError("timezone minutes must be 00, 15, 30, or 45")
|
||||
return timezone(sign * timedelta(hours=hours, minutes=minutes))
|
||||
|
||||
|
||||
def timezone_to_string(tz: timezone) -> str:
|
||||
offset = tz.utcoffset(None)
|
||||
if offset is None:
|
||||
return "+00:00"
|
||||
total_minutes = int(offset.total_seconds() // 60)
|
||||
sign = "+" if total_minutes >= 0 else "-"
|
||||
total_minutes = abs(total_minutes)
|
||||
return f"{sign}{total_minutes // 60:02d}:{total_minutes % 60:02d}"
|
||||
|
||||
|
||||
def parse_datetime(value: str, default_date: datetime | None = None) -> datetime:
|
||||
text = value.strip()
|
||||
match = DATETIME_RE.search(text)
|
||||
|
||||
Reference in New Issue
Block a user