Normalize timestamp group folder names

This commit is contained in:
ajp_anton
2026-06-04 06:09:49 +00:00
parent 739b443bd2
commit 7ce356d299
2 changed files with 43 additions and 7 deletions
+14 -2
View File
@@ -12,11 +12,23 @@ Files are sorted by their current path and moved into one folder named
from __future__ import annotations from __future__ import annotations
import os import os
import re
import shutil import shutil
import sys import sys
from pathlib import Path from pathlib import Path
ORDERED_TIMESTAMP_STEM_RE = re.compile(r"^(?P<timestamp>\d{8}_\d{6})-[1-9]\d*$")
def group_stem(path: Path) -> str:
stem = path.stem
match = ORDERED_TIMESTAMP_STEM_RE.match(stem)
if match:
return match.group("timestamp")
return stem
def unique_path(path: Path) -> Path: def unique_path(path: Path) -> Path:
if not path.exists(): if not path.exists():
return path return path
@@ -87,8 +99,8 @@ def group_items(items: list[Path]) -> Path | None:
return None return None
sorted_items = sorted(items, key=lambda item: item.name.lower()) sorted_items = sorted(items, key=lambda item: item.name.lower())
first = sorted_items[0].stem first = group_stem(sorted_items[0])
last = sorted_items[-1].stem last = group_stem(sorted_items[-1])
target_dir = unique_path(working_dir_for_items(sorted_items) / f"{first}-{last}") target_dir = unique_path(working_dir_for_items(sorted_items) / f"{first}-{last}")
target_dir.mkdir(parents=True) target_dir.mkdir(parents=True)
+29 -5
View File
@@ -84,6 +84,7 @@ TIME_ONLY_RE = re.compile(
TZ_RE = re.compile(r"^(?P<sign>[+-])(?P<h>\d{1,2})(?::?(?P<m>\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}))?$") OFFSET_RE = re.compile(r"^([+-])(?:(\d+):)?(\d{1,2})(?::(\d{2}))?$")
TOKEN_SHIFT_RE = re.compile(r"(\d+|[a-zA-Z]+)") 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*$")
@dataclass @dataclass
@@ -917,6 +918,14 @@ def normalized_extension(path: Path) -> str:
return path.suffix.lower() return path.suffix.lower()
def group_stem_from_name(name: str) -> str:
stem = Path(name).stem
match = ORDERED_TIMESTAMP_STEM_RE.match(stem)
if match:
return match.group("timestamp")
return stem
def plan_names(records: list[MediaRecord], choices: UserChoices) -> None: def plan_names(records: list[MediaRecord], choices: UserChoices) -> None:
if not choices.rename_files: if not choices.rename_files:
for record in records: for record in records:
@@ -981,12 +990,27 @@ def assign_groups(records: list[MediaRecord], groups: list[list[MediaRecord]]) -
record.group_id = index record.group_id = index
def assign_group_names(groups: list[list[MediaRecord]]) -> None: def unique_group_name(base_name: str, working_dir: Path, used_names: set[str]) -> str:
if base_name not in used_names and not (working_dir / base_name).exists():
used_names.add(base_name)
return base_name
for index in range(1, 10000):
candidate = f"{base_name}-{index}"
if candidate not in used_names and not (working_dir / candidate).exists():
used_names.add(candidate)
return candidate
raise RuntimeError(f"Could not find an available group directory name for {base_name}")
def assign_group_names(groups: list[list[MediaRecord]], working_dir: Path) -> None:
used_names: set[str] = set()
for group in groups: for group in groups:
ordered = sorted(group, key=photo_sort_key) ordered = sorted(group, key=photo_sort_key)
first = Path(ordered[0].final_name or ordered[0].path.name).stem first = group_stem_from_name(ordered[0].final_name or ordered[0].path.name)
last = Path(ordered[-1].final_name or ordered[-1].path.name).stem last = group_stem_from_name(ordered[-1].final_name or ordered[-1].path.name)
group_name = f"{first}-{last}" group_name = unique_group_name(f"{first}-{last}", working_dir, used_names)
for record in ordered: for record in ordered:
record.group_name = group_name record.group_name = group_name
@@ -1165,7 +1189,7 @@ def prepare_plan(records: list[MediaRecord], choices: UserChoices) -> list[list[
plan_names(records, choices) plan_names(records, choices)
if groups: if groups:
assign_group_names(groups) assign_group_names(groups, choices.working_dir)
plan_targets(records, choices) plan_targets(records, choices)
return groups return groups