Add BeatMods parsing and userdata restore
This commit is contained in:
@@ -19,7 +19,7 @@ from .planner import create_plan
|
||||
from .scanner import scan_instance
|
||||
from .state import load_installed_state
|
||||
from .updates import check_updates
|
||||
from .userdata import sync_windows_data_repo
|
||||
from .userdata import restore_windows_data_repo, sync_windows_data_repo
|
||||
|
||||
|
||||
def _json(data: Any) -> None:
|
||||
@@ -246,6 +246,16 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
backup.add_argument("--appdata-path", help="Override Beat Saber Windows AppData path")
|
||||
backup.add_argument("--no-appdata", action="store_true", help="Only copy UserData")
|
||||
|
||||
restore = subcommands.add_parser(
|
||||
"restore-userdata",
|
||||
help="Restore UserData and AppData from the backups repo into an instance",
|
||||
parents=[_common_parent()],
|
||||
)
|
||||
restore.add_argument("--instance", required=True)
|
||||
restore.add_argument("--backup-root", default="../backups/beat-saber", help="Backup directory")
|
||||
restore.add_argument("--appdata-path", help="Override Beat Saber AppData destination path")
|
||||
restore.add_argument("--no-appdata", action="store_true", help="Only restore UserData")
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
@@ -300,7 +310,8 @@ def _run_menu(inst_roots: list[Path], st_root: Path, input_func: Callable[[str],
|
||||
("bootstrap-check", "Check BSIPA bootstrap", "Verifies recorded bootstrap state and the latest BSIPA log evidence."),
|
||||
("plan", "Create install plan", "Writes a dry-run JSON plan for locked plugin files before anything is applied."),
|
||||
("apply", "Apply a plan by path", "Installs exactly the file changes from a previously generated plan JSON."),
|
||||
("backup-userdata", "Back up UserData", "Creates a timestamped archive of UserData before risky changes."),
|
||||
("backup-userdata", "Back up UserData", "Copies UserData and AppData into the adjacent backups repo."),
|
||||
("restore-userdata", "Restore UserData", "Restores UserData and AppData from the backups repo into an instance."),
|
||||
("change", "Choose another version", "Returns to the Beat Saber version picker."),
|
||||
]
|
||||
|
||||
@@ -574,6 +585,27 @@ def run(argv: list[str] | None = None) -> int:
|
||||
print(f" {item['source']} -> {item['destination']}")
|
||||
return 0
|
||||
|
||||
if args.command == "restore-userdata":
|
||||
instance = get_instance(inst_roots, args.instance)
|
||||
root = repo_root()
|
||||
backup_root = Path(args.backup_root).expanduser()
|
||||
if not backup_root.is_absolute():
|
||||
backup_root = (root / backup_root).resolve()
|
||||
result = restore_windows_data_repo(
|
||||
instance=args.instance,
|
||||
instance_path=instance.path,
|
||||
backup_root=backup_root,
|
||||
appdata_path=Path(args.appdata_path).expanduser() if args.appdata_path else None,
|
||||
include_appdata=not args.no_appdata,
|
||||
)
|
||||
print(f"Backup root: {result['backupRoot']}")
|
||||
for item in result["restored"]:
|
||||
print(f"{item['label']}: {item['fileCount']} files")
|
||||
print(f" {item['source']} -> {item['destination']}")
|
||||
if item["snapshot"]:
|
||||
print(f" previous: {item['snapshot']}")
|
||||
return 0
|
||||
|
||||
except Exception as exc:
|
||||
print(f"error: {exc}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
Reference in New Issue
Block a user