import os import posixpath import time def _resolve_local_target(remote_path: str, target: str | None) -> str: if target: return target basename = posixpath.basename(remote_path.rstrip("/")) if not basename: raise ValueError("Kann keinen lokalen Dateinamen aus dem Remote-Pfad ableiten. Bitte Zielpfad angeben.") return basename def execute(conn, source: str, target: str | None = None): local_path = _resolve_local_target(source, target) os.makedirs(os.path.dirname(local_path) or ".", exist_ok=True) last_print = 0.0 start_time = time.monotonic() def _progress(_chunk_len: int, received: int, expected: int | None): nonlocal last_print now = time.monotonic() if now - last_print < 0.2: return last_print = now elapsed = max(now - start_time, 1e-6) speed_kb_s = (received / 1024.0) / elapsed if expected is not None and expected > 0: percent = (received * 100.0) / expected remaining = max(expected - received, 0) eta_sec = (remaining / 1024.0) / speed_kb_s if speed_kb_s > 0 else 0.0 eta_str = f"{int(eta_sec // 60):02d}:{int(eta_sec % 60):02d}" print( f"\r⬇️ {received}/{expected} B ({percent:5.1f}%) | {speed_kb_s:6.1f} KB/s | ETA {eta_str}", end="", flush=True, ) else: print(f"\r⬇️ {received} B | {speed_kb_s:6.1f} KB/s", end="", flush=True) data = conn.get_file_data(source, progress_callback=_progress) if len(data) > 0: print() with open(local_path, "wb") as f: f.write(data) total_duration = max(time.monotonic() - start_time, 1e-6) avg_speed_kb_s = (len(data) / 1024.0) / total_duration print(f"✅ Heruntergeladen: '{source}' -> '{local_path}' ({len(data)} B, {avg_speed_kb_s:.1f} KB/s)") return { "source": source, "target": local_path, "size": len(data), }