From 3ef9955015c0ddc641046b6fb2935912183cdc97 Mon Sep 17 00:00:00 2001 From: Dylan De Faoite Date: Tue, 28 Apr 2026 22:35:03 +0100 Subject: [PATCH] feat(autostart): implement daemon management features in cli --- rewind/{autostart.py => autostart/linux.py} | 48 +++++++++----------- rewind/autostart/main.py | 49 +++++++++++++++++++++ rewind/cli.py | 31 ++++++++----- 3 files changed, 91 insertions(+), 37 deletions(-) rename rewind/{autostart.py => autostart/linux.py} (59%) create mode 100644 rewind/autostart/main.py diff --git a/rewind/autostart.py b/rewind/autostart/linux.py similarity index 59% rename from rewind/autostart.py rename to rewind/autostart/linux.py index bf9c554..ef4e281 100644 --- a/rewind/autostart.py +++ b/rewind/autostart/linux.py @@ -1,26 +1,14 @@ -#!/usr/bin/env python3 -import os -import platform -import shutil import subprocess from pathlib import Path -APP_NAME = "rewind-daemon" - -def _find_executable(): - path = shutil.which(APP_NAME) - if not path: - raise RuntimeError(f"{APP_NAME} not found in PATH") - return path - 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 -def _install_linux(program_path: str): +def _create_systemd_service(program_path: str): systemd_dir = Path.home() / ".config/systemd/user" systemd_dir.mkdir(parents=True, exist_ok=True) @@ -58,24 +46,30 @@ Environment=PYTHONUNBUFFERED=1 WantedBy=default.target """) +def enable_autostart_linux(program_path: str): + _create_systemd_service(program_path) _run(["systemctl", "--user", "daemon-reload"]) _run(["systemctl", "--user", "enable", "obs.service"]) _run(["systemctl", "--user", "enable", "rewind-daemon.service"]) - _run(["systemctl", "--user", "start", "obs.service"]) + print("Installed OBS + rewind-daemon via systemd") + +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"]) + + 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 start_daemon_linux(program_path: str): + _create_systemd_service(program_path) _run(["systemctl", "--user", "start", "rewind-daemon.service"]) - print("Installed and started OBS + rewind-daemon via systemd") - -def install(): - system = platform.system() - program_path = _find_executable() - - if system == "Linux": - _install_linux(program_path) - else: - raise RuntimeError(f"Autostart on OS {system} not compatible or not implemented") - -if __name__ == "__main__": - install() \ No newline at end of file +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 diff --git a/rewind/autostart/main.py b/rewind/autostart/main.py new file mode 100644 index 0000000..f019714 --- /dev/null +++ b/rewind/autostart/main.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +import platform +import shutil + +import rewind.autostart.linux as linux_autostart + +APP_NAME = "rewind-daemon" + +def _find_executable(): + path = shutil.which(APP_NAME) + if not path: + raise RuntimeError(f"{APP_NAME} not found in PATH") + return path + +def enable_autostart(): + system = platform.system() + program_path = _find_executable() + + if system == "Linux": + linux_autostart.enable_autostart_linux(program_path) + else: + raise RuntimeError(f"Autostart on OS {system} not compatible or not implemented") + +def disable_autostart(): + system = platform.system() + program_path = _find_executable() + + 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") + +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 diff --git a/rewind/cli.py b/rewind/cli.py index 7103c51..a12da56 100755 --- a/rewind/cli.py +++ b/rewind/cli.py @@ -3,7 +3,7 @@ import sys import argparse from rewind.core import clip, mark, save, print_markers -from rewind.autostart import install +from rewind.autostart.main import enable_autostart, disable_autostart, start_daemon, stop_daemon from rewind.config import Config def build_clip_parser(subparsers: argparse._SubParsersAction) -> None: @@ -43,12 +43,14 @@ def build_mark_parser(subparsers: argparse._SubParsersAction) -> None: def build_list_parser(subparsers: argparse._SubParsersAction) -> None: list_parser = subparsers.add_parser("list", help="List all markers in the recording") -def build_install_parser(subparsers: argparse._SubParsersAction) -> None: - subparsers.add_parser( - "install", - help="Enable background recording daemon", - description="Install and enable the rewind daemon using system autostart." - ) +def build_daemon_parser(subparsers: argparse._SubParsersAction) -> None: + daemon_parser = subparsers.add_parser("daemon", help="Control the rewind daemon") + daemon_sub = daemon_parser.add_subparsers(dest="daemon_command") + daemon_sub.add_parser("start", help="Start the daemon") + daemon_sub.add_parser("stop", help="Stop the daemon") + daemon_sub.add_parser("enable", help="Enable autostart for the daemon") + daemon_sub.add_parser("disable", help="Disable autostart for the daemon") + def build_config_parser(subparsers: argparse._SubParsersAction) -> None: config_parser = subparsers.add_parser("config", help="View or edit config values") @@ -73,7 +75,7 @@ def build_parser() -> argparse.ArgumentParser: build_mark_parser(sub) build_list_parser(sub) build_save_parser(sub) - build_install_parser(sub) + build_daemon_parser(sub) build_config_parser(sub) return parser @@ -92,8 +94,17 @@ def main(argv=None) -> int: print_markers() elif args.command == "save": save(args.start, args.end) - elif args.command == "install": - install() + elif args.command == "daemon": + if args.daemon_command == "start": + start_daemon() + elif args.daemon_command == "stop": + stop_daemon() + elif args.daemon_command == "enable": + enable_autostart() + elif args.daemon_command == "disable": + disable_autostart() + else: + parser.error("Unknown daemon command: expected 'start' or 'stop'") elif args.command == "config": if args.config_command == "set": cfg.set(args.key, args.value)