Update data file date handling
This commit is contained in:
parent
630c7a0f74
commit
57aa88854b
2 changed files with 31 additions and 10 deletions
14
README.md
14
README.md
|
|
@ -82,15 +82,21 @@ Simplest format:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Paris Trip
|
Paris Trip
|
||||||
May 2024
|
|
||||||
Paris, France
|
Paris, France
|
||||||
|
May 2024
|
||||||
```
|
```
|
||||||
|
|
||||||
Those three lines mean:
|
Those three lines mean:
|
||||||
|
|
||||||
1. title
|
1. title
|
||||||
2. date
|
2. location
|
||||||
3. place
|
3. date
|
||||||
|
|
||||||
|
If the date line is omitted, movmaker uses the date from the first picture/video and displays it like:
|
||||||
|
|
||||||
|
```text
|
||||||
|
May 14th 2026
|
||||||
|
```
|
||||||
|
|
||||||
Key/value format also works:
|
Key/value format also works:
|
||||||
|
|
||||||
|
|
@ -100,7 +106,7 @@ date: May 2024
|
||||||
place: Paris, France
|
place: Paris, France
|
||||||
```
|
```
|
||||||
|
|
||||||
Optional per-file captions may be supported, but they are not required:
|
Optional per-file captions are supported:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
IMG_001.jpg: Eiffel Tower
|
IMG_001.jpg: Eiffel Tower
|
||||||
|
|
|
||||||
27
movmaker.py
27
movmaker.py
|
|
@ -223,6 +223,18 @@ def slugify(value: str) -> str:
|
||||||
return value or "movie"
|
return value or "movie"
|
||||||
|
|
||||||
|
|
||||||
|
def ordinal_day(day: int) -> str:
|
||||||
|
if 10 <= day % 100 <= 20:
|
||||||
|
suffix = "th"
|
||||||
|
else:
|
||||||
|
suffix = {1: "st", 2: "nd", 3: "rd"}.get(day % 10, "th")
|
||||||
|
return f"{day}{suffix}"
|
||||||
|
|
||||||
|
|
||||||
|
def display_date(dt: datetime) -> str:
|
||||||
|
return f"{dt.strftime('%B')} {ordinal_day(dt.day)} {dt.year}"
|
||||||
|
|
||||||
|
|
||||||
def default_output_path(first_media: Path, data: MovieData, out_dir: Path) -> Path:
|
def default_output_path(first_media: Path, data: MovieData, out_dir: Path) -> Path:
|
||||||
date = media_date(first_media).strftime("%Y%m%d")
|
date = media_date(first_media).strftime("%Y%m%d")
|
||||||
title = slugify(data.title or first_media.parent.name or "movie")
|
title = slugify(data.title or first_media.parent.name or "movie")
|
||||||
|
|
@ -292,13 +304,13 @@ def parse_data_file(path: Path | None) -> MovieData:
|
||||||
else:
|
else:
|
||||||
plain.append(line)
|
plain.append(line)
|
||||||
|
|
||||||
# Simple mode: first three non-key lines are title/date/place.
|
# Simple mode: first three non-key lines are title/location/date.
|
||||||
if plain:
|
if plain:
|
||||||
data.title = data.title or plain[0]
|
data.title = data.title or plain[0]
|
||||||
if len(plain) > 1:
|
if len(plain) > 1:
|
||||||
data.date = data.date or plain[1]
|
data.place = data.place or plain[1]
|
||||||
if len(plain) > 2:
|
if len(plain) > 2:
|
||||||
data.place = data.place or plain[2]
|
data.date = data.date or plain[2]
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
@ -400,6 +412,7 @@ def build_video(
|
||||||
title_label = "titled"
|
title_label = "titled"
|
||||||
title_font = str(Path.home() / ".local/share/fonts/google/BebasNeue-Regular.ttf")
|
title_font = str(Path.home() / ".local/share/fonts/google/BebasNeue-Regular.ttf")
|
||||||
stamp_font = str(Path.home() / ".local/share/fonts/google/Inter%5Bopsz,wght%5D.ttf")
|
stamp_font = str(Path.home() / ".local/share/fonts/google/Inter%5Bopsz,wght%5D.ttf")
|
||||||
|
stamp_bold_font = str(Path.home() / ".local/share/fonts/google/Inter%5Bopsz,wght%5D.ttf")
|
||||||
if title_lines:
|
if title_lines:
|
||||||
title_font_size = max(60, height // 10)
|
title_font_size = max(60, height // 10)
|
||||||
subtitle_font_size = max(40, height // 18)
|
subtitle_font_size = max(40, height // 18)
|
||||||
|
|
@ -438,7 +451,7 @@ def build_video(
|
||||||
filters.append(f"[{current}]copy[{title_label}]")
|
filters.append(f"[{current}]copy[{title_label}]")
|
||||||
|
|
||||||
caption_label = title_label
|
caption_label = title_label
|
||||||
caption_size = subtitle_font_size if title_lines else max(40, height // 18)
|
caption_size = (subtitle_font_size if title_lines else max(40, height // 18)) + 2
|
||||||
for idx, caption in enumerate(captions):
|
for idx, caption in enumerate(captions):
|
||||||
if not caption:
|
if not caption:
|
||||||
continue
|
continue
|
||||||
|
|
@ -463,13 +476,13 @@ def build_video(
|
||||||
) if caption_fade > 0 else "1"
|
) if caption_fade > 0 else "1"
|
||||||
next_label = f"caption{idx}"
|
next_label = f"caption{idx}"
|
||||||
filters.append(
|
filters.append(
|
||||||
f"[{caption_label}]drawtext=fontfile='{title_font}':text='{drawtext_escape(caption)}':x=(w-text_w)/2:y=h-text_h-62:"
|
f"[{caption_label}]drawtext=fontfile='{stamp_bold_font}':text='{drawtext_escape(caption)}':x=(w-text_w)/2:y=h-text_h-62:"
|
||||||
f"fontsize={caption_size}:fontcolor=white:borderw=1:bordercolor=black:"
|
f"fontsize={caption_size}:fontcolor=white:borderw=1:bordercolor=black:"
|
||||||
f"alpha='{caption_alpha}':enable='between(t,{start:.3f},{end:.3f})'[{next_label}]"
|
f"alpha='{caption_alpha}':enable='between(t,{start:.3f},{end:.3f})'[{next_label}]"
|
||||||
)
|
)
|
||||||
bold_label = f"caption{idx}b"
|
bold_label = f"caption{idx}b"
|
||||||
filters.append(
|
filters.append(
|
||||||
f"[{next_label}]drawtext=fontfile='{title_font}':text='{drawtext_escape(caption)}':x=(w-text_w)/2+1:y=h-text_h-62:"
|
f"[{next_label}]drawtext=fontfile='{stamp_bold_font}':text='{drawtext_escape(caption)}':x=(w-text_w)/2+1:y=h-text_h-62:"
|
||||||
f"fontsize={caption_size}:fontcolor=white:borderw=1:bordercolor=black:"
|
f"fontsize={caption_size}:fontcolor=white:borderw=1:bordercolor=black:"
|
||||||
f"alpha='{caption_alpha}':enable='between(t,{start:.3f},{end:.3f})'[{bold_label}]"
|
f"alpha='{caption_alpha}':enable='between(t,{start:.3f},{end:.3f})'[{bold_label}]"
|
||||||
)
|
)
|
||||||
|
|
@ -536,6 +549,8 @@ def main(argv: list[str] | None = None) -> int:
|
||||||
crf = 28
|
crf = 28
|
||||||
|
|
||||||
media, audio_files, data = scan_input(args.input, args.image_duration)
|
media, audio_files, data = scan_input(args.input, args.image_duration)
|
||||||
|
if not data.date:
|
||||||
|
data.date = display_date(media_date(media[0].path))
|
||||||
fade = min(args.fade, max(0.0, args.image_duration - 0.1))
|
fade = min(args.fade, max(0.0, args.image_duration - 0.1))
|
||||||
audio_fade = max(0.0, args.audio_fade)
|
audio_fade = max(0.0, args.audio_fade)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue