Align media ordering with web logic (read-only order.json, no filename sort fallback)

This commit is contained in:
hbrain 2026-05-31 11:31:15 +00:00
parent 83f1dba8d1
commit a7fea0a8e8

View file

@ -504,31 +504,33 @@ def media_embedded_date(path: Path) -> datetime | None:
return dt return dt
except Exception: except Exception:
pass pass
return ffprobe_creation_date(path) return None
if ext in VIDEO_EXTS:
return ffprobe_creation_date(path)
return None
def parse_date_from_filename(path: Path) -> datetime | None: def parse_date_from_filename(path: Path) -> datetime | None:
# Look for 8 digits (YYYYMMDD) followed by optional separator and 6 digits (HHMMSS) # Match exactly YYYYMMDD[_-]HHMMSS as a standalone numeric chunk.
m = re.search(r'(\d{8})[_-]?(\d{6})?', path.stem) m = re.search(r'(?:^|[^0-9])(\d{8})[_-]?(\d{6})(?:[^0-9]|$)', path.name)
if m: if not m:
try: return None
date_part = m.group(1) full_ts = f"{m.group(1)}{m.group(2)}"
time_part = m.group(2) if m.group(2) else '000000' try:
full_ts = date_part + time_part return datetime.strptime(full_ts, "%Y%m%d%H%M%S")
return datetime.strptime(full_ts, '%Y%m%d%H%M%S') except ValueError:
except ValueError: return None
return None
return None
def media_date(path: Path) -> datetime: def media_date(path: Path) -> datetime:
# 1. Try embedded metadata # 1) Embedded metadata (EXIF/ffprobe)
dt = media_embedded_date(path) dt = media_embedded_date(path)
# 2. Try filename parsing for videos # 2) Filename parsing
if dt is None and path.suffix.lower() in VIDEO_EXTS: if dt is None:
dt = parse_date_from_filename(path) dt = parse_date_from_filename(path)
# 3. Fallback to mtime # 3) Filesystem datetime fallback
if dt is None: if dt is None:
dt = datetime.fromtimestamp(path.stat().st_mtime) dt = datetime.fromtimestamp(path.stat().st_mtime)
@ -616,13 +618,6 @@ def default_output_path(first_media: Path, data: MovieData, out_dir: Path) -> Pa
return out_dir / f"{date}_{title}.mp4" return out_dir / f"{date}_{title}.mp4"
def natural_key(path: Path) -> list[object]:
# Simple natural-ish sort: IMG_2 before IMG_10.
import re
parts = re.split(r"(\d+)", path.name.lower())
return [int(p) if p.isdigit() else p for p in parts]
def scan_input(input_dir: Path, image_duration: float) -> tuple[list[MediaItem], list[Path], MovieData]: def scan_input(input_dir: Path, image_duration: float) -> tuple[list[MediaItem], list[Path], MovieData]:
"""Detect media/audio/data files inside the input directory and build metadata.""" """Detect media/audio/data files inside the input directory and build metadata."""
if not input_dir.is_dir(): if not input_dir.is_dir():
@ -662,7 +657,9 @@ def scan_input(input_dir: Path, image_duration: float) -> tuple[list[MediaItem],
sorted_media.extend(media_map.values()) sorted_media.extend(media_map.values())
media = sorted_media media = sorted_media
else: else:
media.sort(key=lambda item: (media_date(item.path), natural_key(item.path))) # Datetime-only ordering (no filename-order fallback).
# Python sort is stable, so equal datetimes keep discovery order.
media.sort(key=lambda item: media_date(item.path))
return media, audio, parse_data_file(data_file) return media, audio, parse_data_file(data_file)