1
0
mirror of https://github.com/darkzoul5/YoutubePlaylistSync.git synced 2026-07-03 04:23:59 +03:00

feat: add “fail fast” dependency check

This commit is contained in:
2026-05-15 20:07:53 +03:00
parent 1ea4352737
commit e4e342adf5
3 changed files with 30 additions and 2 deletions
+5 -1
View File
@@ -88,7 +88,11 @@ def main(argv: list[str] | None = None) -> int:
summary = ", ".join(f"{k}:{v}" for k, v in sorted(counts.items()))
print(f"Playlist {pid}: {len(actions)} actions → {summary}")
if args.apply and actions:
asyncio.run(executor.execute(actions, pl))
try:
asyncio.run(executor.execute(actions, pl))
except DependencyError as e:
print(f"ERROR: {e}")
return 2
db.set_playlist_last_sync(pid)
print(f"Applied actions for {pid}.")
+20
View File
@@ -12,6 +12,7 @@ from ..sync.reorder import safe_multi_rename
from ..database.db import Database
from ..utils.yt import extract_playlist_id
from ..events.event_bus import EventBus
from ..utils.deps import ensure_ffmpeg_available, ensure_yt_dlp_available
class ActionExecutor:
@@ -21,6 +22,8 @@ class ActionExecutor:
self.bus = event_bus
async def execute(self, actions: Iterable[SyncAction], playlist_cfg: dict) -> None:
self._preflight_dependencies(actions, playlist_cfg)
save_path = Path(playlist_cfg.get("save_path", "./downloads")).resolve()
mode = playlist_cfg.get("download_mode", "audio")
@@ -39,6 +42,23 @@ class ActionExecutor:
# Finally, perform downloads concurrently
await self._apply_downloads(actions, mode, audio_root, video_root, playlist_cfg)
def _preflight_dependencies(self, actions: Iterable[SyncAction], playlist_cfg: dict) -> None:
"""
Fail fast on core runtime dependencies before doing any filesystem work.
This keeps errors consistent regardless of entrypoint (CLI, bootstrap, tests, etc.).
"""
needs_download = any(a.type == SyncActionType.DOWNLOAD for a in actions)
if not needs_download:
return
# yt-dlp is required for any download job (Python API usage)
ensure_yt_dlp_available()
# ffmpeg/ffprobe are required for merges and audio extraction; check once up-front
ffmpeg_hint = playlist_cfg.get("ffmpeg_path", "ffmpeg")
ensure_ffmpeg_available(str(ffmpeg_hint) if ffmpeg_hint is not None else None)
async def _apply_renames(self, actions: Iterable[SyncAction], audio_root: Path, video_root: Path, playlist_cfg: dict) -> None:
playlist_id = extract_playlist_id(playlist_cfg.get("url", "")) or playlist_cfg.get("url", "")
audio_renames = []
+5 -1
View File
@@ -37,7 +37,11 @@ def bootstrap(db_path: Path | None = None) -> None:
print(f"Plan → {summary}")
# Execute
import asyncio
asyncio.run(executor.execute(actions, pl))
try:
asyncio.run(executor.execute(actions, pl))
except DependencyError as e:
print(f"ERROR: {e}")
continue
# Post summary (no DB readback yet)
pid = extract_playlist_id(pl.get('url', '')) or pl.get('url', '')
db.set_playlist_last_sync(pid)