Align media ordering with web logic (read-only order.json, no filename sort fallback)
This commit is contained in:
parent
83f1dba8d1
commit
a7fea0a8e8
1 changed files with 23 additions and 26 deletions
49
movmaker.py
49
movmaker.py
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue