diff --git a/.gitignore b/.gitignore index b02db1e..b89c231 100644 --- a/.gitignore +++ b/.gitignore @@ -223,4 +223,5 @@ __marimo__/ extracted/*.mp3 extracted/*.mkv extracted/*.wav -cache/ \ No newline at end of file +cache/ +.stream_state \ No newline at end of file diff --git a/extract.py b/extract.py index a889a8a..63f9297 100644 --- a/extract.py +++ b/extract.py @@ -493,33 +493,23 @@ for _file_idx, INPUT_FILE in enumerate(INPUT_FILES): print(f"-" * 50) print(f"All done. Files are in:", OUT_DIR) -# ─── Save selected audio stream indices to config.toml ─── -if len(_audio_stream_selections) >= 2 and CONFIG_PATH and os.path.exists(CONFIG_PATH): +# ─── Save selected audio stream indices to .stream_state (gitignored) ─── +if len(_audio_stream_selections) >= 2: try: - with open(CONFIG_PATH, "r") as f: - cfg_text = f.read() - # Update or add audio_stream_a and audio_stream_b in [extract] section - import re - def _cfg_set(key: str, val) -> str: - nonlocal cfg_text - pat = re.compile(rf'(^{key}\s*=\s*).*$') - replacement = f'{key} = {val}' - if pat.search(cfg_text): - cfg_text = pat.sub(replacement, cfg_text, count=1) - else: - # Insert before the next section header or at end of [extract] - ins_pat = re.compile(r'(\[audio\])') - if ins_pat.search(cfg_text): - cfg_text = ins_pat.sub(f'{replacement}\n\n\\1', cfg_text, count=1) - else: - cfg_text += f"\n{replacement}\n" - _cfg_set("audio_stream_a", _audio_stream_selections[0] - if _audio_stream_selections[0] is not None else "null") - _cfg_set("audio_stream_b", _audio_stream_selections[1] - if _audio_stream_selections[1] is not None else "null") - with open(CONFIG_PATH, "w") as f: - f.write(cfg_text) - print(f"[config] saved audio_stream_a={_audio_stream_selections[0]}, " - f"audio_stream_b={_audio_stream_selections[1]} → {CONFIG_PATH}") + import json + state_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".stream_state") + state = {} + if os.path.exists(state_path): + with open(state_path, "r") as f: + state = json.load(f) + # Record which files these selections belong to + state["file_a"] = INPUT_FILES[0] + state["file_b"] = INPUT_FILES[1] + for i, sel in enumerate(_audio_stream_selections): + key = f"audio_stream_{chr(ord('a') + i)}" + state[key] = sel if sel is not None else None + with open(state_path, "w") as f: + json.dump(state, f, indent=2) + print(f"[state] saved stream selection → {state_path}") except Exception as e: - print(f"[config] warn: could not save stream indices: {e}", file=sys.stderr) + print(f"[state] warn: could not save stream indices: {e}", file=sys.stderr) diff --git a/verify_sync.py b/verify_sync.py index c0a42fc..027643a 100644 --- a/verify_sync.py +++ b/verify_sync.py @@ -660,7 +660,6 @@ def main(): args = ap.parse_args() cfg = load_config(args.config) - ext = cfg.get("extract", {}) sync_cfg = cfg.get("sync", {}) audio_cfg = cfg.get("audio", {}) @@ -669,6 +668,17 @@ def main(): out_dir = args.out or sync_cfg.get("out_dir", "output") lag_sec = args.lag + # Load stream selection from .stream_state (created by extract.py) + _state_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".stream_state") + _state = {} + if os.path.exists(_state_path): + try: + import json + with open(_state_path, "r") as _sf: + _state = json.load(_sf) + except Exception: + pass + if args.mode == "full": synced = args.synced if not synced: @@ -691,11 +701,12 @@ def main(): # Determine which streams to extract from synced MKV # A's streams are first, B's delayed streams follow + ext = cfg.get("extract", {}) video_a = ext.get("file_a", "") a_streams_in = probe_audio_streams(video_a) if os.path.exists(video_a) else [] n_a = len(a_streams_in) - sel_a = ext.get("audio_stream_a", 0) or 0 - sel_b = ext.get("audio_stream_b", 0) or 0 + sel_a = _state.get("audio_stream_a", 0) or 0 + sel_b = _state.get("audio_stream_b", 0) or 0 sel_a = int(sel_a) sel_b = int(sel_b) # Output indices: A's selected stream stays at sel_a, B's becomes n_a + sel_b