From 441dc02080ad0857579dc6a3668da7b0e70170ac Mon Sep 17 00:00:00 2001 From: Dylan De Faoite Date: Tue, 28 Apr 2026 22:58:48 +0100 Subject: [PATCH] refactor(autostart): abstract backend implementation for autostart --- rewind/autostart/autostart_backend.py | 15 ++++ rewind/autostart/linux.py | 117 ++++++++++++++------------ rewind/autostart/main.py | 38 +++------ rewind/daemon.py | 1 - 4 files changed, 89 insertions(+), 82 deletions(-) create mode 100644 rewind/autostart/autostart_backend.py diff --git a/rewind/autostart/autostart_backend.py b/rewind/autostart/autostart_backend.py new file mode 100644 index 0000000..4017554 --- /dev/null +++ b/rewind/autostart/autostart_backend.py @@ -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") \ No newline at end of file diff --git a/rewind/autostart/linux.py b/rewind/autostart/linux.py index ef4e281..c6ed542 100644 --- a/rewind/autostart/linux.py +++ b/rewind/autostart/linux.py @@ -1,75 +1,80 @@ import subprocess from pathlib import Path +from rewind.autostart.autostart_backend import AutostartBackend -def _run(cmd: list[str]): - result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - if result.returncode != 0: - raise RuntimeError(f"Command failed: {' '.join(cmd)}\n{result.stderr}") - return result +class LinuxBackend(AutostartBackend): + def __init__(self, program_path: str): + super().__init__(program_path) -def _create_systemd_service(program_path: str): - systemd_dir = Path.home() / ".config/systemd/user" - systemd_dir.mkdir(parents=True, exist_ok=True) + def _run(self, cmd: list[str]): + result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + if result.returncode != 0: + raise RuntimeError(f"Command failed: {' '.join(cmd)}\n{result.stderr}") + return result - obs_service = systemd_dir / "obs.service" - daemon_service = systemd_dir / "rewind-daemon.service" + def _create_systemd_service(self): + systemd_dir = Path.home() / ".config/systemd/user" + systemd_dir.mkdir(parents=True, exist_ok=True) - obs_service.write_text("""[Unit] -Description=OBS Studio -After=graphical-session.target -Wants=graphical-session.target + obs_service = systemd_dir / "obs.service" + daemon_service = systemd_dir / "rewind-daemon.service" -[Service] -# Ensure OBS sentinel files are cleaned up before starting the daemon -ExecStartPre=-/usr/bin/rm -rf %h/.config/obs-studio/.sentinel -ExecStart=obs --minimize-to-tray -Restart=on-failure -RestartSec=3 + obs_service.write_text("""[Unit] + Description=OBS Studio + After=graphical-session.target + Wants=graphical-session.target -[Install] -WantedBy=default.target -""") + [Service] + # Ensure OBS sentinel files are cleaned up before starting the daemon + ExecStartPre=-/usr/bin/rm -rf %h/.config/obs-studio/.sentinel + ExecStart=obs --minimize-to-tray + Restart=on-failure + RestartSec=3 - daemon_service.write_text(f"""[Unit] -Description=Rewind Daemon -After=obs.service -Requires=obs.service + [Install] + WantedBy=default.target + """) -[Service] -ExecStart={program_path} -Restart=always -RestartSec=5 -Environment=PYTHONUNBUFFERED=1 + daemon_service.write_text(f"""[Unit] + Description=Rewind Daemon + After=obs.service + Requires=obs.service -[Install] -WantedBy=default.target -""") + [Service] + ExecStart={self.program_path} + Restart=always + RestartSec=5 + Environment=PYTHONUNBUFFERED=1 -def enable_autostart_linux(program_path: str): - _create_systemd_service(program_path) - _run(["systemctl", "--user", "daemon-reload"]) + [Install] + WantedBy=default.target + """) - _run(["systemctl", "--user", "enable", "obs.service"]) - _run(["systemctl", "--user", "enable", "rewind-daemon.service"]) + def enable_autostart(self): + self._create_systemd_service() + self._run(["systemctl", "--user", "daemon-reload"]) - print("Installed OBS + rewind-daemon via systemd") + self._run(["systemctl", "--user", "enable", "obs.service"]) + self._run(["systemctl", "--user", "enable", "rewind-daemon.service"]) -def disable_autostart_linux(program_path: str): - _create_systemd_service(program_path) - _run(["systemctl", "--user", "disable", "rewind-daemon.service"]) - _run(["systemctl", "--user", "disable", "obs.service"]) + print("Installed OBS + rewind-daemon via systemd") - systemd_dir = Path.home() / ".config/systemd/user" - (systemd_dir / "rewind-daemon.service").unlink(missing_ok=True) - (systemd_dir / "obs.service").unlink(missing_ok=True) - _run(["systemctl", "--user", "daemon-reload"]) + def disable_autostart(self): + self._create_systemd_service() + self._run(["systemctl", "--user", "disable", "rewind-daemon.service"]) + self._run(["systemctl", "--user", "disable", "obs.service"]) -def start_daemon_linux(program_path: str): - _create_systemd_service(program_path) - _run(["systemctl", "--user", "start", "rewind-daemon.service"]) + systemd_dir = Path.home() / ".config/systemd/user" + (systemd_dir / "rewind-daemon.service").unlink(missing_ok=True) + (systemd_dir / "obs.service").unlink(missing_ok=True) + self._run(["systemctl", "--user", "daemon-reload"]) -def stop_daemon_linux(program_path: str): - _create_systemd_service(program_path) - _run(["systemctl", "--user", "stop", "rewind-daemon.service"]) - _run(["systemctl", "--user", "stop", "obs.service"]) \ No newline at end of file + def start_daemon(self): + self._create_systemd_service() + self._run(["systemctl", "--user", "start", "rewind-daemon.service"]) + + def stop_daemon(self): + self._create_systemd_service() + self._run(["systemctl", "--user", "stop", "rewind-daemon.service"]) + self._run(["systemctl", "--user", "stop", "obs.service"]) \ No newline at end of file diff --git a/rewind/autostart/main.py b/rewind/autostart/main.py index f019714..80fa310 100644 --- a/rewind/autostart/main.py +++ b/rewind/autostart/main.py @@ -2,7 +2,7 @@ import platform import shutil -import rewind.autostart.linux as linux_autostart +from rewind.autostart.linux import LinuxBackend APP_NAME = "rewind-daemon" @@ -12,38 +12,26 @@ def _find_executable(): raise RuntimeError(f"{APP_NAME} not found in PATH") return path -def enable_autostart(): +def _get_backend(): system = platform.system() program_path = _find_executable() - if system == "Linux": - linux_autostart.enable_autostart_linux(program_path) + return LinuxBackend(program_path) else: raise RuntimeError(f"Autostart on OS {system} not compatible or not implemented") + +def enable_autostart(): + backend = _get_backend() + backend.enable_autostart() def disable_autostart(): - system = platform.system() - program_path = _find_executable() + backend = _get_backend() + 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(): - system = platform.system() - program_path = _find_executable() - - if system == "Linux": - linux_autostart.start_daemon_linux(program_path) - else: - raise RuntimeError(f"Autostart on OS {system} not compatible or not implemented") + backend = _get_backend() + backend.start_daemon() def stop_daemon(): - system = platform.system() - program_path = _find_executable() - - if system == "Linux": - linux_autostart.stop_daemon_linux(program_path) - else: - raise RuntimeError(f"Autostart on OS {system} not compatible or not implemented") \ No newline at end of file + backend = _get_backend() + backend.stop_daemon() \ No newline at end of file diff --git a/rewind/daemon.py b/rewind/daemon.py index 5488dc6..48b08e4 100755 --- a/rewind/daemon.py +++ b/rewind/daemon.py @@ -16,7 +16,6 @@ from rewind.state import add_file_to_state, create_state_file_if_needed, cleanup running = True LOG_FILE = get_state_dir() / "daemon.log" INTERVAL = 10 -SENTINEL_FILE = os.path.expanduser("~/.config/obs-studio/.sentinel") OBS_MAX_RETRIES = 5 logger = logging.getLogger(__name__)