from __future__ import annotations import json import os import shutil import tempfile import zipfile from pathlib import Path from typing import TYPE_CHECKING from .library import Mod, LibraryError if TYPE_CHECKING: from .manager import Manager def export_modpack(manager: Manager, dest_path: Path, profile_name: str) -> None: profile = manager.profiles.data.get(profile_name) if not profile: raise ValueError(f"Profile {profile_name} not found") with zipfile.ZipFile(dest_path, "w", zipfile.ZIP_DEFLATED) as zf: # Export profile metadata zf.writestr("modpack.json", json.dumps({"name": profile_name, "profile": profile}, indent=2)) # Export mods mods_to_export = set(profile["order"]) for mod_id in mods_to_export: mod = manager.library.get(mod_id) if not mod: continue # Write mod files mod_root = mod.root for dirpath, dirnames, filenames in os.walk(mod_root): for filename in filenames: file_path = Path(dirpath) / filename rel_path = file_path.relative_to(mod_root) zf.write(file_path, f"mods/{mod_id}/{rel_path}") def import_modpack(manager: Manager, source_path: Path, new_profile_name: str) -> list[str]: with zipfile.ZipFile(source_path, "r") as zf: try: with zf.open("modpack.json") as f: data = json.load(f) profile = data.get("profile") except KeyError: raise ValueError("Not a valid modpack: missing modpack.json") if not profile: raise ValueError("Invalid modpack.json") # Extract mods extracted_mods = [] for name in zf.namelist(): if name.startswith("mods/") and len(name.split("/")) > 2: parts = name.split("/") mod_id = parts[1] if mod_id not in manager.library.mods: # Not in library, we need to extract it dest_file = manager.paths.mods_dir / Path(*parts[1:]) dest_file.parent.mkdir(parents=True, exist_ok=True) if not name.endswith("/"): with zf.open(name) as source, open(dest_file, "wb") as target: shutil.copyfileobj(source, target) if mod_id not in extracted_mods: extracted_mods.append(mod_id) # Reload library to discover the new mods manager.reload_library() # Ensure profile name is unique final_name = new_profile_name n = 2 while final_name in manager.profiles.data: final_name = f"{new_profile_name} ({n})" n += 1 manager.profiles.data[final_name] = profile manager.profiles.switch(final_name) manager.profiles.save() return extracted_mods