refactor(autostart): abstract backend implementation for autostart

This commit is contained in:
2026-04-28 22:58:48 +01:00
parent 31e236dc46
commit 441dc02080
4 changed files with 89 additions and 82 deletions

View File

@@ -0,0 +1,15 @@
class AutostartBackend:
def __init__(self, program_path: str):
self.program_path = program_path
def enable_autostart(self):
raise NotImplementedError("enable_autostart not implemented for this platform")
def disable_autostart(self):
raise NotImplementedError("disable_autostart not implemented for this platform")
def start_daemon(self):
raise NotImplementedError("start_daemon not implemented for this platform")
def stop_daemon(self):
raise NotImplementedError("stop_daemon not implemented for this platform")

View File

@@ -1,14 +1,19 @@
import subprocess import subprocess
from pathlib import Path from pathlib import Path
from rewind.autostart.autostart_backend import AutostartBackend
def _run(cmd: list[str]): class LinuxBackend(AutostartBackend):
def __init__(self, program_path: str):
super().__init__(program_path)
def _run(self, cmd: list[str]):
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if result.returncode != 0: if result.returncode != 0:
raise RuntimeError(f"Command failed: {' '.join(cmd)}\n{result.stderr}") raise RuntimeError(f"Command failed: {' '.join(cmd)}\n{result.stderr}")
return result return result
def _create_systemd_service(program_path: str): def _create_systemd_service(self):
systemd_dir = Path.home() / ".config/systemd/user" systemd_dir = Path.home() / ".config/systemd/user"
systemd_dir.mkdir(parents=True, exist_ok=True) systemd_dir.mkdir(parents=True, exist_ok=True)
@@ -16,60 +21,60 @@ def _create_systemd_service(program_path: str):
daemon_service = systemd_dir / "rewind-daemon.service" daemon_service = systemd_dir / "rewind-daemon.service"
obs_service.write_text("""[Unit] obs_service.write_text("""[Unit]
Description=OBS Studio Description=OBS Studio
After=graphical-session.target After=graphical-session.target
Wants=graphical-session.target Wants=graphical-session.target
[Service] [Service]
# Ensure OBS sentinel files are cleaned up before starting the daemon # Ensure OBS sentinel files are cleaned up before starting the daemon
ExecStartPre=-/usr/bin/rm -rf %h/.config/obs-studio/.sentinel ExecStartPre=-/usr/bin/rm -rf %h/.config/obs-studio/.sentinel
ExecStart=obs --minimize-to-tray ExecStart=obs --minimize-to-tray
Restart=on-failure Restart=on-failure
RestartSec=3 RestartSec=3
[Install] [Install]
WantedBy=default.target WantedBy=default.target
""") """)
daemon_service.write_text(f"""[Unit] daemon_service.write_text(f"""[Unit]
Description=Rewind Daemon Description=Rewind Daemon
After=obs.service After=obs.service
Requires=obs.service Requires=obs.service
[Service] [Service]
ExecStart={program_path} ExecStart={self.program_path}
Restart=always Restart=always
RestartSec=5 RestartSec=5
Environment=PYTHONUNBUFFERED=1 Environment=PYTHONUNBUFFERED=1
[Install] [Install]
WantedBy=default.target WantedBy=default.target
""") """)
def enable_autostart_linux(program_path: str): def enable_autostart(self):
_create_systemd_service(program_path) self._create_systemd_service()
_run(["systemctl", "--user", "daemon-reload"]) self._run(["systemctl", "--user", "daemon-reload"])
_run(["systemctl", "--user", "enable", "obs.service"]) self._run(["systemctl", "--user", "enable", "obs.service"])
_run(["systemctl", "--user", "enable", "rewind-daemon.service"]) self._run(["systemctl", "--user", "enable", "rewind-daemon.service"])
print("Installed OBS + rewind-daemon via systemd") print("Installed OBS + rewind-daemon via systemd")
def disable_autostart_linux(program_path: str): def disable_autostart(self):
_create_systemd_service(program_path) self._create_systemd_service()
_run(["systemctl", "--user", "disable", "rewind-daemon.service"]) self._run(["systemctl", "--user", "disable", "rewind-daemon.service"])
_run(["systemctl", "--user", "disable", "obs.service"]) self._run(["systemctl", "--user", "disable", "obs.service"])
systemd_dir = Path.home() / ".config/systemd/user" systemd_dir = Path.home() / ".config/systemd/user"
(systemd_dir / "rewind-daemon.service").unlink(missing_ok=True) (systemd_dir / "rewind-daemon.service").unlink(missing_ok=True)
(systemd_dir / "obs.service").unlink(missing_ok=True) (systemd_dir / "obs.service").unlink(missing_ok=True)
_run(["systemctl", "--user", "daemon-reload"]) self._run(["systemctl", "--user", "daemon-reload"])
def start_daemon_linux(program_path: str): def start_daemon(self):
_create_systemd_service(program_path) self._create_systemd_service()
_run(["systemctl", "--user", "start", "rewind-daemon.service"]) self._run(["systemctl", "--user", "start", "rewind-daemon.service"])
def stop_daemon_linux(program_path: str): def stop_daemon(self):
_create_systemd_service(program_path) self._create_systemd_service()
_run(["systemctl", "--user", "stop", "rewind-daemon.service"]) self._run(["systemctl", "--user", "stop", "rewind-daemon.service"])
_run(["systemctl", "--user", "stop", "obs.service"]) self._run(["systemctl", "--user", "stop", "obs.service"])

View File

@@ -2,7 +2,7 @@
import platform import platform
import shutil import shutil
import rewind.autostart.linux as linux_autostart from rewind.autostart.linux import LinuxBackend
APP_NAME = "rewind-daemon" APP_NAME = "rewind-daemon"
@@ -12,38 +12,26 @@ def _find_executable():
raise RuntimeError(f"{APP_NAME} not found in PATH") raise RuntimeError(f"{APP_NAME} not found in PATH")
return path return path
def enable_autostart(): def _get_backend():
system = platform.system() system = platform.system()
program_path = _find_executable() program_path = _find_executable()
if system == "Linux": if system == "Linux":
linux_autostart.enable_autostart_linux(program_path) return LinuxBackend(program_path)
else: else:
raise RuntimeError(f"Autostart on OS {system} not compatible or not implemented") raise RuntimeError(f"Autostart on OS {system} not compatible or not implemented")
def enable_autostart():
backend = _get_backend()
backend.enable_autostart()
def disable_autostart(): def disable_autostart():
system = platform.system() backend = _get_backend()
program_path = _find_executable() backend.disable_autostart()
if system == "Linux":
linux_autostart.disable_autostart_linux(program_path)
else:
raise RuntimeError(f"Autostart on OS {system} not compatible or not implemented")
def start_daemon(): def start_daemon():
system = platform.system() backend = _get_backend()
program_path = _find_executable() backend.start_daemon()
if system == "Linux":
linux_autostart.start_daemon_linux(program_path)
else:
raise RuntimeError(f"Autostart on OS {system} not compatible or not implemented")
def stop_daemon(): def stop_daemon():
system = platform.system() backend = _get_backend()
program_path = _find_executable() backend.stop_daemon()
if system == "Linux":
linux_autostart.stop_daemon_linux(program_path)
else:
raise RuntimeError(f"Autostart on OS {system} not compatible or not implemented")

View File

@@ -16,7 +16,6 @@ from rewind.state import add_file_to_state, create_state_file_if_needed, cleanup
running = True running = True
LOG_FILE = get_state_dir() / "daemon.log" LOG_FILE = get_state_dir() / "daemon.log"
INTERVAL = 10 INTERVAL = 10
SENTINEL_FILE = os.path.expanduser("~/.config/obs-studio/.sentinel")
OBS_MAX_RETRIES = 5 OBS_MAX_RETRIES = 5
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)