f
This commit is contained in:
@@ -603,8 +603,8 @@ def main() -> int:
|
||||
# If the current interpreter is the one inside the local venv, try to
|
||||
# run the uninstall via a Python outside the venv so files (including
|
||||
# the interpreter binary) can be removed on Windows.
|
||||
current_exe = Path(sys.executable).resolve()
|
||||
try:
|
||||
current_exe = Path(sys.executable).resolve()
|
||||
in_venv = str(current_exe).lower().startswith(str(vdir.resolve()).lower())
|
||||
except Exception:
|
||||
in_venv = False
|
||||
@@ -751,7 +751,6 @@ def main() -> int:
|
||||
if db_path.exists():
|
||||
try:
|
||||
import sqlite3
|
||||
import json
|
||||
with sqlite3.connect(str(db_path)) as conn:
|
||||
# We want to set store.hydrusnetwork.hydrus.<key>
|
||||
cur = conn.cursor()
|
||||
@@ -940,7 +939,7 @@ def main() -> int:
|
||||
try:
|
||||
# When piped, script_path is None. We don't want to use the detected repo_root
|
||||
# because that's just CWD.
|
||||
if script_path is not None:
|
||||
if script_path is not None and repo_root is not None:
|
||||
default_install = repo_root
|
||||
else:
|
||||
# When piped, default to home folder on POSIX, CWD on Windows
|
||||
@@ -964,6 +963,11 @@ def main() -> int:
|
||||
# Resolve while expanding user paths (~) and environment variables ($HOME)
|
||||
expanded = os.path.expandvars(os.path.expanduser(install_dir_raw))
|
||||
install_path = Path(expanded).resolve()
|
||||
|
||||
if install_path is None:
|
||||
print("Error: Could not determine installation path.", file=sys.stderr)
|
||||
return False
|
||||
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
return False
|
||||
|
||||
@@ -1143,153 +1147,166 @@ def main() -> int:
|
||||
|
||||
# If no specific action flag is passed and we're in a terminal (or we're being piped), show the menu
|
||||
if (sys.stdin.isatty() or sys.stdout.isatty() or script_path is None) and not args.quiet:
|
||||
sel = _interactive_menu()
|
||||
if sel == "install":
|
||||
if not _ensure_repo_available():
|
||||
return 1
|
||||
args.skip_deps = False
|
||||
args.install_editable = True
|
||||
args.no_playwright = False
|
||||
elif sel == "extras_hydrus":
|
||||
install_location = _prompt_hydrus_install_location()
|
||||
if install_location is None:
|
||||
return 0
|
||||
install_root, install_dest = install_location
|
||||
# Choice 2 is for installing HydrusNetwork standalone/independently.
|
||||
# We preferentially use the local script if already in a repo.
|
||||
hydrus_script = None
|
||||
temp_installer_path: Path | None = None
|
||||
temp_hydrus_repo: Path | None = None
|
||||
if is_in_repo and repo_root:
|
||||
hydrus_script = repo_root / "scripts" / "hydrusnetwork.py"
|
||||
|
||||
if not hydrus_script or not hydrus_script.exists():
|
||||
print("Downloading the Hydrus installation helper...")
|
||||
try:
|
||||
fd, path = tempfile.mkstemp(prefix="mm_hydrus_", suffix=".py")
|
||||
os.close(fd)
|
||||
helper_path = Path(path)
|
||||
if _download_hydrus_installer(helper_path):
|
||||
hydrus_script = helper_path
|
||||
temp_installer_path = helper_path
|
||||
else:
|
||||
helper_path.unlink(missing_ok=True)
|
||||
while True:
|
||||
sel = _interactive_menu()
|
||||
if sel == "install":
|
||||
if not _ensure_repo_available():
|
||||
return 1
|
||||
args.skip_deps = False
|
||||
args.install_editable = True
|
||||
args.no_playwright = False
|
||||
# Break the loop to proceed with the main installation steps below
|
||||
break
|
||||
elif sel == "extras_hydrus":
|
||||
install_location = _prompt_hydrus_install_location()
|
||||
if install_location is None:
|
||||
continue
|
||||
install_root, install_dest = install_location
|
||||
# Choice 2 is for installing HydrusNetwork standalone/independently.
|
||||
# We preferentially use the local script if already in a repo.
|
||||
hydrus_script = None
|
||||
temp_installer_path: Path | None = None
|
||||
temp_hydrus_repo: Path | None = None
|
||||
if is_in_repo and repo_root:
|
||||
hydrus_script = repo_root / "scripts" / "hydrusnetwork.py"
|
||||
|
||||
if not hydrus_script or not hydrus_script.exists():
|
||||
print("Downloading the Hydrus installation helper...")
|
||||
try:
|
||||
fd, path = tempfile.mkstemp(prefix="mm_hydrus_", suffix=".py")
|
||||
os.close(fd)
|
||||
helper_path = Path(path)
|
||||
if _download_hydrus_installer(helper_path):
|
||||
hydrus_script = helper_path
|
||||
temp_installer_path = helper_path
|
||||
else:
|
||||
helper_path.unlink(missing_ok=True)
|
||||
hydrus_script = None
|
||||
except Exception as e:
|
||||
print(f"Error setting up temporary installer: {e}")
|
||||
hydrus_script = None
|
||||
except Exception as e:
|
||||
print(f"Error setting up temporary installer: {e}")
|
||||
hydrus_script = None
|
||||
|
||||
if (not hydrus_script or not hydrus_script.exists()) and temp_hydrus_repo is None:
|
||||
print("Falling back to clone the Medios-Macina repository to obtain the helper script...")
|
||||
try:
|
||||
temp_mm_repo_dir = Path(tempfile.mkdtemp(prefix="mm_repo_"))
|
||||
if _clone_repo(REPO_URL, temp_mm_repo_dir, depth=1):
|
||||
hydrus_script = temp_mm_repo_dir / "scripts" / "hydrusnetwork.py"
|
||||
temp_hydrus_repo = temp_mm_repo_dir
|
||||
else:
|
||||
shutil.rmtree(temp_mm_repo_dir, ignore_errors=True)
|
||||
if (not hydrus_script or not hydrus_script.exists()) and temp_hydrus_repo is None:
|
||||
print("Falling back to clone the Medios-Macina repository to obtain the helper script...")
|
||||
try:
|
||||
temp_mm_repo_dir = Path(tempfile.mkdtemp(prefix="mm_repo_"))
|
||||
if _clone_repo(REPO_URL, temp_mm_repo_dir, depth=1):
|
||||
hydrus_script = temp_mm_repo_dir / "scripts" / "hydrusnetwork.py"
|
||||
temp_hydrus_repo = temp_mm_repo_dir
|
||||
else:
|
||||
shutil.rmtree(temp_mm_repo_dir, ignore_errors=True)
|
||||
hydrus_script = None
|
||||
except Exception as e:
|
||||
print(f"Error cloning Medios-Macina repo: {e}")
|
||||
hydrus_script = None
|
||||
except Exception as e:
|
||||
print(f"Error cloning Medios-Macina repo: {e}")
|
||||
hydrus_script = None
|
||||
|
||||
if hydrus_script and hydrus_script.exists():
|
||||
try:
|
||||
# Clear out project-venv related env vars to prevent auto-reexec
|
||||
env = os.environ.copy()
|
||||
env.pop("VIRTUAL_ENV", None)
|
||||
env.pop("PYTHONHOME", None)
|
||||
env.pop("PYTHONPATH", None)
|
||||
# We use sys.executable (the one running bootstrap.py) to run hydrusnetwork.py
|
||||
# This ensures it uses the same environment that started the bootstrap.
|
||||
# Pass sys.stdin to ensure the subprocess can talk to the terminal.
|
||||
subprocess.check_call(
|
||||
[
|
||||
sys.executable,
|
||||
str(hydrus_script),
|
||||
"--no-project-venv",
|
||||
"--root",
|
||||
str(install_root),
|
||||
"--dest-name",
|
||||
install_dest,
|
||||
],
|
||||
env=env,
|
||||
stdin=sys.stdin
|
||||
)
|
||||
except subprocess.CalledProcessError:
|
||||
print("\nHydrusNetwork setup exited with an error.")
|
||||
except Exception as e:
|
||||
print(f"\nFailed to run HydrusNetwork setup: {e}")
|
||||
finally:
|
||||
if temp_installer_path:
|
||||
temp_installer_path.unlink(missing_ok=True)
|
||||
if temp_hydrus_repo is not None:
|
||||
shutil.rmtree(temp_hydrus_repo, ignore_errors=True)
|
||||
else:
|
||||
print(f"\nError: {hydrus_script} not found.")
|
||||
return 0
|
||||
elif sel == "install_service":
|
||||
# Direct path input for the target repository
|
||||
print("\n[ SYSTEM SERVICE INSTALLATION ]")
|
||||
print("Enter the root directory of the Hydrus repository you want to run as a service.")
|
||||
print("This is the folder containing 'hydrus_client.py'.")
|
||||
|
||||
# Default to repo_root/hydrusnetwork if available, otherwise CWD
|
||||
default_path = repo_root / "hydrusnetwork" if repo_root else Path.cwd()
|
||||
sys.stdout.write(f"Repository Root [{default_path}]: ")
|
||||
sys.stdout.flush()
|
||||
|
||||
path_raw = sys.stdin.readline().strip()
|
||||
target_repo = Path(path_raw).resolve() if path_raw else default_path
|
||||
if hydrus_script and hydrus_script.exists():
|
||||
try:
|
||||
# Clear out project-venv related env vars to prevent auto-reexec
|
||||
env = os.environ.copy()
|
||||
env.pop("VIRTUAL_ENV", None)
|
||||
env.pop("PYTHONHOME", None)
|
||||
env.pop("PYTHONPATH", None)
|
||||
# We use sys.executable (the one running bootstrap.py) to run hydrusnetwork.py
|
||||
# This ensures it uses the same environment that started the bootstrap.
|
||||
# Pass sys.stdin to ensure the subprocess can talk to the terminal.
|
||||
subprocess.check_call(
|
||||
[
|
||||
sys.executable,
|
||||
str(hydrus_script),
|
||||
"--no-project-venv",
|
||||
"--root",
|
||||
str(install_root),
|
||||
"--dest-name",
|
||||
install_dest,
|
||||
],
|
||||
env=env,
|
||||
stdin=sys.stdin
|
||||
)
|
||||
# Update the main project's config with the new Hydrus path
|
||||
if is_in_repo and repo_root:
|
||||
_update_config_value(repo_root, "gitclone", str(Path(install_root) / install_dest))
|
||||
except subprocess.CalledProcessError:
|
||||
print("\nHydrusNetwork setup exited with an error.")
|
||||
except Exception as e:
|
||||
print(f"\nFailed to run HydrusNetwork setup: {e}")
|
||||
finally:
|
||||
if temp_installer_path:
|
||||
temp_installer_path.unlink(missing_ok=True)
|
||||
if temp_hydrus_repo is not None:
|
||||
shutil.rmtree(temp_hydrus_repo, ignore_errors=True)
|
||||
else:
|
||||
print(f"\nError: {hydrus_script} not found.")
|
||||
|
||||
print("\nHydrus installation task finished.")
|
||||
sys.stdout.write("Press Enter to return to menu...")
|
||||
sys.stdout.flush()
|
||||
sys.stdin.readline()
|
||||
continue
|
||||
elif sel == "install_service":
|
||||
# Direct path input for the target repository
|
||||
print("\n[ SYSTEM SERVICE INSTALLATION ]")
|
||||
print("Enter the root directory of the Hydrus repository you want to run as a service.")
|
||||
print("This is the folder containing 'hydrus_client.py'.")
|
||||
|
||||
# Default to repo_root/hydrusnetwork if available, otherwise CWD
|
||||
default_path = repo_root / "hydrusnetwork" if repo_root else Path.cwd()
|
||||
sys.stdout.write(f"Repository Root [{default_path}]: ")
|
||||
sys.stdout.flush()
|
||||
|
||||
path_raw = sys.stdin.readline().strip()
|
||||
target_repo = Path(path_raw).resolve() if path_raw else default_path
|
||||
|
||||
if not (target_repo / "hydrus_client.py").exists():
|
||||
print(f"\n[!] Error: 'hydrus_client.py' not found in: {target_repo}")
|
||||
print(" Please ensure you've entered the correct repository root.")
|
||||
if not (target_repo / "hydrus_client.py").exists():
|
||||
print(f"\n[!] Error: 'hydrus_client.py' not found in: {target_repo}")
|
||||
print(" Please ensure you've entered the correct repository root.")
|
||||
sys.stdout.write("\nPress Enter to return to menu...")
|
||||
sys.stdout.flush()
|
||||
sys.stdin.readline()
|
||||
continue
|
||||
|
||||
run_client_script = repo_root / "scripts" / "run_client.py" if repo_root else Path(__file__).parent / "run_client.py"
|
||||
|
||||
if run_client_script.exists():
|
||||
try:
|
||||
# We pass --repo-root explicitly to the target_repo provided by the user
|
||||
subprocess.check_call(
|
||||
[
|
||||
sys.executable,
|
||||
str(run_client_script),
|
||||
"--install-service",
|
||||
"--service-name", "hydrus-client",
|
||||
"--repo-root", str(target_repo),
|
||||
"--headless",
|
||||
"--pull"
|
||||
],
|
||||
stdin=sys.stdin
|
||||
)
|
||||
print("\nHydrus System service installed successfully.")
|
||||
except subprocess.CalledProcessError:
|
||||
print("\nService installation failed.")
|
||||
except Exception as e:
|
||||
print(f"\nError installing service: {e}")
|
||||
else:
|
||||
print(f"\nError: {run_client_script} not found.")
|
||||
|
||||
sys.stdout.write("\nPress Enter to return to menu...")
|
||||
sys.stdout.flush()
|
||||
sys.stdin.readline()
|
||||
return "menu"
|
||||
|
||||
run_client_script = repo_root / "scripts" / "run_client.py" if repo_root else Path(__file__).parent / "run_client.py"
|
||||
|
||||
if run_client_script.exists():
|
||||
try:
|
||||
# We pass --repo-root explicitly to the target_repo provided by the user
|
||||
subprocess.check_call(
|
||||
[
|
||||
sys.executable,
|
||||
str(run_client_script),
|
||||
"--install-service",
|
||||
"--service-name", "hydrus-client",
|
||||
"--repo-root", str(target_repo),
|
||||
"--headless",
|
||||
"--pull"
|
||||
],
|
||||
stdin=sys.stdin
|
||||
)
|
||||
print("\nHydrus System service installed successfully.")
|
||||
except subprocess.CalledProcessError:
|
||||
print("\nService installation failed.")
|
||||
except Exception as e:
|
||||
print(f"\nError installing service: {e}")
|
||||
else:
|
||||
print(f"\nError: {run_client_script} not found.")
|
||||
|
||||
sys.stdout.write("\nPress Enter to continue...")
|
||||
sys.stdout.flush()
|
||||
sys.stdin.readline()
|
||||
return "menu"
|
||||
elif sel == "uninstall":
|
||||
return _do_uninstall()
|
||||
elif sel == "delegate":
|
||||
rc = run_platform_bootstrap(repo_root)
|
||||
if rc != 0:
|
||||
return rc
|
||||
if not args.quiet:
|
||||
print("Platform bootstrap completed successfully.")
|
||||
return 0
|
||||
elif sel == 0:
|
||||
return 0
|
||||
continue
|
||||
elif sel == "uninstall":
|
||||
return _do_uninstall()
|
||||
elif sel == "delegate":
|
||||
rc = run_platform_bootstrap(repo_root)
|
||||
if rc != 0:
|
||||
return rc
|
||||
if not args.quiet:
|
||||
print("Platform bootstrap completed successfully.")
|
||||
return 0
|
||||
elif sel == 0:
|
||||
return 0
|
||||
elif sel == "menu":
|
||||
continue
|
||||
elif not args.no_delegate and script_path is not None:
|
||||
# Default non-interactive behavior: delegate to platform script
|
||||
rc = run_platform_bootstrap(repo_root)
|
||||
@@ -1742,7 +1759,14 @@ if (Test-Path (Join-Path $repo 'CLI.py')) {
|
||||
else:
|
||||
# POSIX
|
||||
# If running as root (id 0), prefer /usr/bin or /usr/local/bin which are standard on PATH
|
||||
if hasattr(os, "getuid") and os.getuid() == 0:
|
||||
is_root = False
|
||||
try:
|
||||
if platform.system().lower() != "windows" and os.getuid() == 0:
|
||||
is_root = True
|
||||
except (AttributeError, Exception):
|
||||
pass
|
||||
|
||||
if is_root:
|
||||
user_bin = Path("/usr/local/bin")
|
||||
if not os.access(user_bin, os.W_OK):
|
||||
user_bin = Path("/usr/bin")
|
||||
|
||||
@@ -192,34 +192,6 @@ def update_medios_config(hydrus_path: Path) -> bool:
|
||||
except Exception as e:
|
||||
logging.error("Failed to update medios.db: %s", e)
|
||||
|
||||
# Fallback to config.conf
|
||||
if not config_path.exists():
|
||||
logging.debug("MM config.conf not found at %s; skipping legacy auto-link.", config_path)
|
||||
return False
|
||||
|
||||
try:
|
||||
content = config_path.read_text(encoding="utf-8")
|
||||
key = "gitclone"
|
||||
value = hydrus_abs_path
|
||||
|
||||
# Pattern to replace existing gitclone in the hydrusnetwork section
|
||||
pattern = rf'^(\s*{re.escape(key)}\s*=\s*)(.*)$'
|
||||
if re.search(pattern, content, flags=re.MULTILINE):
|
||||
new_content = re.sub(pattern, rf'\1"{value}"', content, flags=re.MULTILINE)
|
||||
else:
|
||||
section_pattern = r'\[store=hydrusnetwork\]'
|
||||
if re.search(section_pattern, content):
|
||||
new_content = re.sub(section_pattern, f'[store=hydrusnetwork]\n{key}="{value}"', content, count=1)
|
||||
else:
|
||||
new_content = content + f'\n\n[store=hydrusnetwork]\nname="hydrus"\n{key}="{value}"'
|
||||
|
||||
if new_content != content:
|
||||
config_path.write_text(new_content, encoding="utf-8")
|
||||
logging.info("✅ Linked Hydrus installation in Medios-Macina config (gitclone=\"%s\")", value)
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.error("Failed to update config.conf: %s", e)
|
||||
return False
|
||||
return False
|
||||
|
||||
|
||||
@@ -1124,6 +1096,7 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
"show-in-file-manager": "showinfm",
|
||||
"opencv-python-headless": "cv2",
|
||||
"mpv": "mpv",
|
||||
"python-mpv": "mpv",
|
||||
"pyside6": "PySide6",
|
||||
"pyside6-essentials": "PySide6",
|
||||
"pyside6-addons": "PySide6",
|
||||
@@ -1140,6 +1113,11 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
except Exception:
|
||||
if mod == "mpv":
|
||||
# python-mpv requires system libmpv; failure is common on server/headless envs
|
||||
logging.info("Package '%s' is installed, but 'import %s' failed (likely missing system libmpv). This is usually non-critical.", pkg, mod)
|
||||
continue
|
||||
|
||||
logging.warning(
|
||||
"Package '%s' not importable inside venv (module %s)",
|
||||
pkg,
|
||||
@@ -1439,6 +1417,7 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
"show-in-file-manager": "showinfm",
|
||||
"opencv-python-headless": "cv2",
|
||||
"mpv": "mpv",
|
||||
"python-mpv": "mpv",
|
||||
"pyside6": "PySide6",
|
||||
"pyside6-essentials": "PySide6",
|
||||
"pyside6-addons": "PySide6",
|
||||
@@ -1476,6 +1455,11 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
except subprocess.CalledProcessError:
|
||||
if import_name == "mpv":
|
||||
# python-mpv requires system libmpv; failure is common on server/headless envs
|
||||
logging.info("Package '%s' is installed, but 'import %s' failed (likely missing system libmpv). This is usually non-critical.", pkg, import_name)
|
||||
continue
|
||||
|
||||
logging.warning(
|
||||
"Package '%s' appears installed but 'import %s' failed inside venv.",
|
||||
pkg,
|
||||
|
||||
Reference in New Issue
Block a user