Initial commit.
This commit is contained in:
commit
133238dfca
4 changed files with 190 additions and 0 deletions
15
.env.template
Normal file
15
.env.template
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Nextcloud Connection Details
|
||||
NC_URL="https://nextcloud.example.com"
|
||||
NC_USER="user"
|
||||
NC_PASS="password"
|
||||
|
||||
# Configuration File Paths
|
||||
NC_TEMP_DOWNLOAD="/tmp/temp_download.7z"
|
||||
# Temporary local extract path
|
||||
TEMP_EXTRACT_PATH="/tmp/temp_download"
|
||||
# Temporary local backup path
|
||||
TEMP_BACKUP_PATH="/temp/backup/path"
|
||||
# Plugin Configs default path
|
||||
PLUGIN_CONFIGS_PATH="/mnt/c/Users/[CHANGEME]/AppData/Roaming/XIVLauncher/pluginConfigs"
|
||||
# FFXIV Configs default path
|
||||
FFXIV_CONFIGS_PATH="/mnt/c/Users/[CHANGEME]/Documents/My Games/FINAL FANTASY XIV - A Realm Reborn"
|
||||
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
backup/*
|
||||
FINAL FANTASY XIV - A Realm Reborn/*
|
||||
pluginConfigs/*
|
||||
temp/*
|
||||
*.pyc
|
||||
.env
|
||||
2
requirements.txt
Normal file
2
requirements.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
nextcloud-api-wrapper==0.2.3
|
||||
python-dotenv==1.2.2
|
||||
167
restore_backup.py
Executable file
167
restore_backup.py
Executable file
|
|
@ -0,0 +1,167 @@
|
|||
#!/bin/env python3
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import argparse
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
from dotenv import load_dotenv
|
||||
from nextcloud import NextCloud
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
# --- CONFIGURATION FROM ENVIRONMENT ---
|
||||
NC_URL = os.getenv("NC_URL")
|
||||
NC_USER = os.getenv("NC_USER")
|
||||
NC_PASS = os.getenv("NC_PASS")
|
||||
TEMP_DOWNLOAD_NAME = os.getenv("NC_TEMP_DOWNLOAD", "/tmp/temp_download.7z")
|
||||
TEMP_EXTRACT_PATH = os.getenv("TEMP_EXTRACT_PATH")
|
||||
TEMP_BACKUP_PATH = os.getenv("TEMP_BACKUP_PATH")
|
||||
PLUGIN_CONFIG_PATH = os.getenv("PLUGIN_CONFIGS_PATH")
|
||||
FFXIV_CONFIGS_PATH = os.getenv("FFXIV_CONFIGS_PATH")
|
||||
# --------------------------------------
|
||||
|
||||
|
||||
def validate_environment():
|
||||
"""Validates that all necessary Nextcloud environment variables are set."""
|
||||
missing = []
|
||||
if not NC_URL:
|
||||
missing.append("NC_URL")
|
||||
if not NC_USER:
|
||||
missing.append("NC_USER")
|
||||
if not NC_PASS:
|
||||
missing.append("NC_PASS")
|
||||
if not NC_USER:
|
||||
missing.append("PLUGIN_CONFIGS_PATH")
|
||||
if not NC_PASS:
|
||||
missing.append("FFXIV_CONFIGS_PATH")
|
||||
|
||||
if missing:
|
||||
raise ValueError(
|
||||
f"Missing required environment variables in .env: {', '.join(missing)}"
|
||||
)
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
"""Defines and parses command-line arguments."""
|
||||
parser = argparse.ArgumentParser(description="""
|
||||
Download a .7z archive from Nextcloud, backup local files, and replace them.""")
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--remote",
|
||||
required=True,
|
||||
help="Path to the .7z archive file inside Nextcloud (e.g., 'backups/data.7z')",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def download_backup(remote_archive_path: str):
|
||||
"""Downloads the requested backup archive from Nextcloud"""
|
||||
# Connect to Nextcloud
|
||||
print("Connecting to Nextcloud...")
|
||||
nc = NextCloud(NC_URL, user=NC_USER, password=NC_PASS)
|
||||
|
||||
# Download 7z archive from Nextcloud
|
||||
print(f"Downloading '{remote_archive_path}' from Nextcloud...")
|
||||
try:
|
||||
downloaded_file = nc.get_file(remote_archive_path)
|
||||
downloaded_file.download(target=TEMP_DOWNLOAD_NAME)
|
||||
except Exception as e:
|
||||
print(f"Error downloading file: {e}")
|
||||
return
|
||||
|
||||
|
||||
def decompress_backup():
|
||||
"""Extract the files from the backup"""
|
||||
# Uncompress the downloaded .7z archive into target directory
|
||||
print(f"Extracting .7z files to:\n -> {TEMP_EXTRACT_PATH}")
|
||||
# 7z flags used:
|
||||
# 'x' = extract with full paths
|
||||
# '-o...' = target output directory (no space between -o and the path)
|
||||
# '-y' = assume Yes on all queries (overwrite prompts)
|
||||
cmd = ["7z", "x", TEMP_DOWNLOAD_NAME, f"-o{TEMP_EXTRACT_PATH}", "-y"]
|
||||
|
||||
try:
|
||||
# Run command and capture output; raises CalledProcessError if return code != 0
|
||||
subprocess.run(
|
||||
cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
|
||||
)
|
||||
print("Extraction completed successfully!")
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error: 7z extraction failed. {e}")
|
||||
return
|
||||
|
||||
finally:
|
||||
# Clean up downloaded archive file from configured path
|
||||
if os.path.exists(TEMP_DOWNLOAD_NAME):
|
||||
os.remove(TEMP_DOWNLOAD_NAME)
|
||||
|
||||
|
||||
def backup_and_restore(
|
||||
backup_config_path: str, restore_config_path: str, is_ffxiv_config: bool
|
||||
):
|
||||
"""Create backup of current config and restore downloaded backup"""
|
||||
# Create backup of FFXIV config directory if it exists and has content
|
||||
if os.path.exists(restore_config_path) and os.listdir(restore_config_path):
|
||||
print(
|
||||
"Creating safety backup of " + "ffxiv"
|
||||
if is_ffxiv_config
|
||||
else "plugins" + f" configs at:\n -> {backup_config_path}/game"
|
||||
)
|
||||
try:
|
||||
shutil.copytree(restore_config_path, f"{backup_config_path}/game")
|
||||
shutil.rmtree(restore_config_path)
|
||||
except Exception as e:
|
||||
print(f"Failed during ffxiv config backup creation phase: {e}")
|
||||
return
|
||||
|
||||
# Restore FFXIV config backup files
|
||||
try:
|
||||
print("Attempting to restore ffxiv config backups")
|
||||
shutil.copytree(
|
||||
f"{TEMP_EXTRACT_PATH}/{"game" if is_ffxiv_config else "plugins"}/",
|
||||
restore_config_path,
|
||||
dirs_exist_ok=True,
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Failed during FFXIV config restore phase: {e}")
|
||||
return
|
||||
|
||||
|
||||
def main():
|
||||
# Parse command-line inputs and validate env
|
||||
args = parse_arguments()
|
||||
remote_archive_path = args.remote
|
||||
|
||||
try:
|
||||
validate_environment()
|
||||
except ValueError as e:
|
||||
print(f"Configuration Error: {e}")
|
||||
return
|
||||
|
||||
download_backup(remote_archive_path)
|
||||
|
||||
# Generate a unique, timestamped backup folder name in the temp backup directory
|
||||
parent_dir = os.path.dirname(TEMP_BACKUP_PATH)
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||
backup_dir = os.path.join(parent_dir, f"backup_{timestamp}")
|
||||
os.makedirs(backup_dir, exist_ok=True)
|
||||
|
||||
decompress_backup()
|
||||
|
||||
backup_and_restore(backup_dir, FFXIV_CONFIGS_PATH, True)
|
||||
|
||||
backup_and_restore(backup_dir, PLUGIN_CONFIG_PATH, False)
|
||||
|
||||
# Remove temporary extract files
|
||||
try:
|
||||
shutil.rmtree(TEMP_EXTRACT_PATH)
|
||||
except Exception as e:
|
||||
print(f"Failed to remove temporary extracted backup files: {e}")
|
||||
return
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in a new issue