install.py (3896B)
1 #!/usr/bin/env python3 2 import getpass 3 import os 4 import secrets 5 import shutil 6 import subprocess 7 import sys 8 import tempfile 9 import venv 10 from pathlib import Path 11 12 SOURCE_ROOT = Path(__file__).resolve().parent 13 DEPLOY_ROOT = Path("/opt/simplefe") 14 VENV_DIR = DEPLOY_ROOT / ".venv" 15 VENV_PYTHON = VENV_DIR / "bin" / "python" 16 SIMPLEFE_TOKEN_KEY = "SIMPLEFE_TOKEN" 17 18 SYNC_EXCLUDES = [ 19 ".venv", 20 ".git", 21 ".env", 22 "__pycache__", 23 ".pytest_cache", 24 "*.egg-info", 25 ] 26 27 28 def sync_source(): 29 if SOURCE_ROOT == DEPLOY_ROOT: 30 return 31 print(f"Syncing source to {DEPLOY_ROOT} (requires sudo)...") 32 subprocess.run(["sudo", "mkdir", "-p", str(DEPLOY_ROOT)], check=True) 33 user = getpass.getuser() 34 subprocess.run( 35 ["sudo", "chown", "-R", f"{user}:{user}", str(DEPLOY_ROOT)], check=True 36 ) 37 shutil.copytree( 38 SOURCE_ROOT, 39 DEPLOY_ROOT, 40 dirs_exist_ok=True, 41 ignore=shutil.ignore_patterns(*SYNC_EXCLUDES), 42 ) 43 44 45 def create_venv(): 46 if VENV_DIR.exists(): 47 print(f"Virtual environment already exists at {VENV_DIR}, skipping.") 48 return 49 print(f"Creating virtual environment at {VENV_DIR}...") 50 venv.create(VENV_DIR, with_pip=True) 51 52 53 def install_package(): 54 print("Installing simplefe and its dependencies...") 55 subprocess.run( 56 [str(VENV_PYTHON), "-m", "pip", "install", "-e", str(DEPLOY_ROOT)], 57 check=True, 58 ) 59 60 61 def reexec_into_venv(): 62 if Path(sys.prefix) == VENV_DIR: 63 return 64 os.execv(str(VENV_PYTHON), [str(VENV_PYTHON), __file__, *sys.argv[1:]]) 65 66 67 def write_env_file(): 68 env_path = DEPLOY_ROOT / ".env" 69 if env_path.exists(): 70 if f"{SIMPLEFE_TOKEN_KEY}=" in env_path.read_text(): 71 print(f"{env_path} already has a {SIMPLEFE_TOKEN_KEY}, keeping it as-is.") 72 return 73 token = secrets.token_urlsafe(32) 74 with open(env_path, "a") as f: 75 f.write(f"{SIMPLEFE_TOKEN_KEY}={token}\n") 76 print(f"Added a freshly generated {SIMPLEFE_TOKEN_KEY} to {env_path}.") 77 return 78 79 token = secrets.token_urlsafe(32) 80 with open(env_path, "w") as f: 81 f.write(f"{SIMPLEFE_TOKEN_KEY}={token}\n") 82 83 print(f"Wrote {env_path} with a freshly generated {SIMPLEFE_TOKEN_KEY}.") 84 print( 85 "Once the service is running, visit /setup in your browser to add your " 86 "Jellyfin and Pushover credentials. Any ENCODE_*/DRIVE_PATH/OUTPUT_DIR/" 87 "OUTPUT_GROUP overrides still need to be added to .env by hand (see " 88 "settings.py for the full list and defaults)." 89 ) 90 91 92 SYSTEMD_UNIT_PATH = Path("/etc/systemd/system/simplefe.service") 93 94 UNIT_TEMPLATE = """\ 95 [Unit] 96 Description=simplefe 97 After=network.target 98 99 [Service] 100 Type=simple 101 User={user} 102 WorkingDirectory={project_root} 103 ExecStart={waitress} --listen=0.0.0.0:8000 --call simplefe.app:create_app 104 Restart=on-failure 105 106 [Install] 107 WantedBy=multi-user.target 108 """ 109 110 111 def install_systemd_unit(): 112 waitress = VENV_DIR / "bin" / "waitress-serve" 113 unit_content = UNIT_TEMPLATE.format( 114 user=getpass.getuser(), 115 project_root=DEPLOY_ROOT, 116 waitress=waitress, 117 ) 118 119 with tempfile.NamedTemporaryFile("w", suffix=".service", delete=False) as f: 120 f.write(unit_content) 121 tmp_path = f.name 122 123 print("Installing systemd service (requires sudo)...") 124 subprocess.run(["sudo", "cp", tmp_path, str(SYSTEMD_UNIT_PATH)], check=True) 125 Path(tmp_path).unlink() 126 subprocess.run(["sudo", "systemctl", "daemon-reload"], check=True) 127 subprocess.run(["sudo", "systemctl", "enable", "simplefe"], check=True) 128 subprocess.run(["sudo", "systemctl", "restart", "simplefe"], check=True) 129 print("simplefe service installed and started.") 130 131 132 def main(): 133 sync_source() 134 create_venv() 135 install_package() 136 reexec_into_venv() 137 138 write_env_file() 139 install_systemd_unit() 140 141 142 if __name__ == "__main__": 143 main()