Fixing loudness issue
This commit is contained in:
+100
-19
@@ -372,10 +372,11 @@ def cmd_import(project_path: Path, force: bool, verbose: bool) -> int:
|
||||
shared_assets_dir = _find_shared_assets(project_path)
|
||||
if shared_assets_dir:
|
||||
_import_shared_assets(shared_assets_dir, verbose)
|
||||
_import_shared_audio(shared_assets_dir, project_path, config, verbose)
|
||||
_sync_shared_videos_to_local(project_path, config, shared_assets_dir, verbose)
|
||||
|
||||
# Probe and cache audio file durations into audio.json
|
||||
_probe_audio_durations(project_path, config, force, verbose)
|
||||
_probe_audio_durations(project_path, config, force, verbose, shared_assets_dir)
|
||||
|
||||
# Probe and cache video metadata (duration, has_audio) into videos.json
|
||||
_probe_video_metadata(project_path, config, shared_assets_dir, force, verbose)
|
||||
@@ -384,8 +385,71 @@ def cmd_import(project_path: Path, force: bool, verbose: bool) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def _import_shared_audio(
|
||||
shared_assets_dir: Path,
|
||||
project_path: Path,
|
||||
config,
|
||||
verbose: bool,
|
||||
) -> None:
|
||||
"""Import audio files from shared_assets/media/audio into the project's audio.json."""
|
||||
audio_extensions = {".mp3", ".wav", ".aac", ".m4a", ".ogg", ".flac"}
|
||||
shared_audio_dir = shared_assets_dir / "media" / "audio"
|
||||
|
||||
if not shared_audio_dir.exists():
|
||||
if verbose:
|
||||
print(f" No shared audio dir found at {shared_audio_dir}")
|
||||
return
|
||||
|
||||
audio_files = sorted(
|
||||
f
|
||||
for f in shared_audio_dir.iterdir()
|
||||
if f.is_file()
|
||||
and f.suffix.lower() in audio_extensions
|
||||
and not f.name.startswith(".")
|
||||
)
|
||||
|
||||
if not audio_files:
|
||||
if verbose:
|
||||
print(f" No audio files found in {shared_audio_dir}")
|
||||
return
|
||||
|
||||
# Resolve project audio.json path
|
||||
if config and config.audio_path:
|
||||
audio_json_path = project_path / config.audio_path
|
||||
else:
|
||||
audio_json_path = project_path / "media" / "audio" / "audio.json"
|
||||
|
||||
audio_json_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
existing: dict = _read_json(audio_json_path) if audio_json_path.exists() else {}
|
||||
|
||||
added = 0
|
||||
for f in audio_files:
|
||||
audio_id = f.stem
|
||||
if audio_id in existing:
|
||||
if verbose:
|
||||
print(f" Skipping {audio_id} (already in audio.json)")
|
||||
continue
|
||||
existing[audio_id] = {
|
||||
"file": f.name,
|
||||
"is_shared": True,
|
||||
"volume": 1.0,
|
||||
}
|
||||
added += 1
|
||||
if verbose:
|
||||
print(f" Added shared audio: {audio_id}")
|
||||
|
||||
if added > 0:
|
||||
with open(audio_json_path, "w", encoding="utf-8") as fh:
|
||||
json.dump(existing, fh, indent=2)
|
||||
print(f" Updated {audio_json_path.relative_to(project_path)} (+{added} shared audio files)")
|
||||
else:
|
||||
if verbose:
|
||||
print(f" No new shared audio files to add")
|
||||
|
||||
|
||||
def _probe_audio_durations(
|
||||
project_path: Path, config, force: bool, verbose: bool
|
||||
project_path: Path, config, force: bool, verbose: bool,
|
||||
shared_assets_dir: Optional[Path] = None,
|
||||
) -> None:
|
||||
"""Probe and cache audio file durations into audio.json.
|
||||
|
||||
@@ -413,7 +477,10 @@ def _probe_audio_durations(
|
||||
if verbose:
|
||||
print(f" Audio '{audio_id}': cached ({audio_data['duration']:.1f}s)")
|
||||
continue
|
||||
audio_path = audio_dir / audio_data["file"]
|
||||
if audio_data.get("is_shared") and shared_assets_dir:
|
||||
audio_path = shared_assets_dir / "media" / "audio" / audio_data["file"]
|
||||
else:
|
||||
audio_path = audio_dir / audio_data["file"]
|
||||
if not audio_path.exists():
|
||||
if verbose:
|
||||
print(f" Audio '{audio_id}': file not found, skipping")
|
||||
@@ -1060,8 +1127,16 @@ _TASKS_VIDEO_PREFIXES = {
|
||||
"video:": 6,
|
||||
"vft:": 4,
|
||||
"vfb:": 4,
|
||||
"vf2t:": 5,
|
||||
"vf2b:": 5,
|
||||
"vst:": 4,
|
||||
"vsb:": 4,
|
||||
"vftp:": 5,
|
||||
"vfbp:": 5,
|
||||
"vf2tp:": 6,
|
||||
"vf2bp:": 6,
|
||||
"vstp:": 5,
|
||||
"vsbp:": 5,
|
||||
"narration:": 10,
|
||||
}
|
||||
|
||||
@@ -1993,6 +2068,14 @@ def cmd_stitch(
|
||||
print(f"\n Combined narration exists: {stitch_output.name}")
|
||||
print(" (use --force to regenerate)")
|
||||
else:
|
||||
# Extract loudnorm config from talkinghead filter so stitch uses
|
||||
# per-project settings instead of hardcoded defaults.
|
||||
_loudnorm_cfg = None
|
||||
if config and config.default_filters:
|
||||
for _f in (config.default_filters.get("talkinghead") or []):
|
||||
if isinstance(_f, dict) and _f.get("type") == "audio_normalize":
|
||||
_loudnorm_cfg = _f
|
||||
break
|
||||
stitch_narration_segments(
|
||||
narration_dir,
|
||||
segment_ids,
|
||||
@@ -2000,6 +2083,7 @@ def cmd_stitch(
|
||||
stitch_output,
|
||||
verbose=verbose,
|
||||
default_end_trim=config.default_end_trim if config else 0.0,
|
||||
loudnorm_config=_loudnorm_cfg,
|
||||
)
|
||||
# Run import videos again, because at this point narration_combined might have been created.
|
||||
_import_videos(videos_dir, config, verbose)
|
||||
@@ -2127,14 +2211,10 @@ def _print_render_plan_details(plan, marker_timings, slides: dict) -> None:
|
||||
marker_id.startswith(p)
|
||||
for p in (
|
||||
"video:",
|
||||
"vft:",
|
||||
"vfb:",
|
||||
"vst:",
|
||||
"vsb:",
|
||||
"vft:",
|
||||
"vfbp:",
|
||||
"vstp:",
|
||||
"vsbp:",
|
||||
"vft:", "vfb:", "vf2t:", "vf2b:",
|
||||
"vst:", "vsb:",
|
||||
"vftp:", "vfbp:", "vf2tp:", "vf2bp:",
|
||||
"vstp:", "vsbp:",
|
||||
)
|
||||
):
|
||||
aligned_count += 1
|
||||
@@ -2142,14 +2222,10 @@ def _print_render_plan_details(plan, marker_timings, slides: dict) -> None:
|
||||
len(p)
|
||||
for p in (
|
||||
"video:",
|
||||
"vft:",
|
||||
"vfb:",
|
||||
"vst:",
|
||||
"vsb:",
|
||||
"vft:",
|
||||
"vfbp:",
|
||||
"vstp:",
|
||||
"vsbp:",
|
||||
"vft:", "vfb:", "vf2t:", "vf2b:",
|
||||
"vst:", "vsb:",
|
||||
"vftp:", "vfbp:", "vf2tp:", "vf2bp:",
|
||||
"vstp:", "vsbp:",
|
||||
)
|
||||
if marker_id.startswith(p)
|
||||
)
|
||||
@@ -3066,6 +3142,11 @@ _RSYNC_EXCLUDES = [
|
||||
"media/narration/processed/",
|
||||
"media/narration/processed/**",
|
||||
"media/videos/narration_combined.mov",
|
||||
# Low-res preview files (generated locally, not synced)
|
||||
"media/narration/low/",
|
||||
"media/narration/low/**",
|
||||
"media/videos/low/",
|
||||
"media/videos/low/**",
|
||||
# Chunk scratch directories
|
||||
"**/chunks/",
|
||||
"**/chunks/**",
|
||||
|
||||
Reference in New Issue
Block a user