Files
malkuthroandClaude Opus 4.8 447465ecd8 refactor(nodes): remove superseded LTX Director extra nodes + harden registry (#198) (#199)
* refactor(nodes): remove superseded LTX Director extra nodes + harden registry (#198)

Remove the two experimental nodes layered on the WhatDreamsCost fork — KoolookAudioTranscriptTimeline (k_audio_timeline.py) and KoolookTimelineEditor (k_timeline_editor.py) — plus the transcribe_audio_timeline.py CLI and its test. The audio-transcript to Director-timing job now lives inside the fork via the Director's audio_transcript_json input, so the standalone nodes are redundant. Also drops the orphaned [audio]/faster-whisper extra and updates packaging, dev-sync, web, and docs references.

Harden the node registry against the #183 failure: __init__.py now imports each node group independently and installs the /koolook/presets/* routes regardless of node-import results, so one missing or broken module degrades to 'that node absent' instead of zeroing every node and 404'ing the sidebar disk routes. A new test keeps every root k_*.py listed in dev-sync's RUNTIME_PATHS.

Closes #198.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(nodes): ship loader gates via dev-sync-audio; generalize RUNTIME_PATHS guard (#198)

Address the K-Flame review on #199:

MEDIUM — the #198 refactor made koolook_versioning.py a single registration gate, but dev-sync-audio shipped the new __init__.py without it (and the k_*.py-glob guard test couldn't see it). Add koolook_versioning.py AND koolook_install_guard.py (whose absolute-import fallback raises uncaught) to AUDIO_PATHS so a scoped sync can never leave the loader pointing at a module the target lacks.

Generalize the guard test: parse __init__.py's actual load-time import targets (relative imports + _merge_node_group calls) and assert each is in RUNTIME_PATHS, instead of globbing k_*.py which missed the non-k_ gates. Add an AUDIO_PATHS gate-coverage test.

LOW — CHANGELOG now records the [audio]/faster-whisper + transcribe_audio_timeline.py removal as an external-contract break.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 19:47:25 +02:00

100 lines
3.1 KiB
Python

"""Safety coverage for scripts/sync_to_dev_audio.py."""
from __future__ import annotations
import importlib.util
from pathlib import Path
import pytest
_SCRIPT = Path(__file__).resolve().parents[2] / "scripts" / "sync_to_dev_audio.py"
_spec = importlib.util.spec_from_file_location("sync_to_dev_audio", _SCRIPT)
assert _spec is not None and _spec.loader is not None
sync_to_dev_audio = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(sync_to_dev_audio)
def _stale_path(root: Path) -> Path:
return root / "web" / "whatdreamscost_koolook_v1_3_2"
def test_remove_stale_paths_removes_directory(tmp_path: Path) -> None:
stale = _stale_path(tmp_path)
stale.mkdir(parents=True)
(stale / "ltx_director.js").write_text("old extension\n", encoding="utf-8")
removed = sync_to_dev_audio.remove_stale_paths(
tmp_path,
dry_run=False,
verbose=False,
)
assert removed == 1
assert not stale.exists()
def test_remove_stale_paths_dry_run_preserves_directory(tmp_path: Path) -> None:
stale = _stale_path(tmp_path)
stale.mkdir(parents=True)
removed = sync_to_dev_audio.remove_stale_paths(
tmp_path,
dry_run=True,
verbose=False,
)
assert removed == 1
assert stale.exists()
def test_remove_stale_paths_idempotent_when_absent(tmp_path: Path) -> None:
removed = sync_to_dev_audio.remove_stale_paths(
tmp_path,
dry_run=False,
verbose=False,
)
assert removed == 0
def test_remove_stale_paths_unlinks_symlink_without_touching_target(tmp_path: Path) -> None:
current = tmp_path / "web" / "whatdreamscost_koolook"
current.mkdir(parents=True)
keep = current / "ltx_director.js"
keep.write_text("current extension\n", encoding="utf-8")
stale = _stale_path(tmp_path)
try:
stale.symlink_to(current, target_is_directory=True)
except OSError as exc:
pytest.skip(f"symlink creation unavailable: {exc}")
removed = sync_to_dev_audio.remove_stale_paths(
tmp_path,
dry_run=False,
verbose=False,
)
assert removed == 1
assert not stale.exists()
assert keep.read_text(encoding="utf-8") == "current extension\n"
def test_target_is_repo_root_detects_source_repo() -> None:
assert sync_to_dev_audio.target_is_repo_root(sync_to_dev_audio._dev.REPO_ROOT)
def test_audio_paths_ship_init_load_gates() -> None:
"""dev-sync-audio ships ``__init__.py``, so it must also ship the modules
``__init__`` imports at load before any node group. Those gates are not
per-group-guarded: a missing ``koolook_install_guard`` makes the absolute
import fallback raise uncaught (plugin dead), and a missing
``koolook_versioning`` makes the context probe mislabel its ImportError as
a non-Comfy context and register nothing (#198 / #183).
"""
audio_paths = set(sync_to_dev_audio.AUDIO_PATHS)
required = {"__init__.py", "koolook_install_guard.py", "koolook_versioning.py"}
missing = sorted(required - audio_paths)
assert not missing, (
f"dev-sync-audio ships __init__.py but not its load-time gate(s): {missing}"
)