diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 1a6d8599e7e..1376bbb6045 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -933,6 +933,7 @@ ./services/wayland/cage.nix ./services/video/epgstation/default.nix ./services/video/mirakurun.nix + ./services/video/replay-sorcery.nix ./services/web-apps/atlassian/confluence.nix ./services/web-apps/atlassian/crowd.nix ./services/web-apps/atlassian/jira.nix diff --git a/nixos/modules/services/video/replay-sorcery.nix b/nixos/modules/services/video/replay-sorcery.nix new file mode 100644 index 00000000000..d78e782c796 --- /dev/null +++ b/nixos/modules/services/video/replay-sorcery.nix @@ -0,0 +1,70 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.replay-sorcery; + configFile = generators.toKeyValue {} cfg.settings; +in +{ + options = with types; { + services.replay-sorcery = { + enable = mkEnableOption "the ReplaySorcery service for instant-replays"; + + enableSysAdminCapability = mkEnableOption '' + the system admin capability to support hardware accelerated + video capture. This is equivalent to running ReplaySorcery as + root, so use with caution''; + + autoStart = mkOption { + type = bool; + default = false; + description = "Automatically start ReplaySorcery when graphical-session.target starts."; + }; + + settings = mkOption { + type = attrsOf (oneOf [ str int ]); + default = {}; + description = "System-wide configuration for ReplaySorcery (/etc/replay-sorcery.conf)."; + example = literalExample '' + { + videoInput = "hwaccel"; # requires `services.replay-sorcery.enableSysAdminCapability = true` + videoFramerate = 60; + } + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment = { + systemPackages = [ pkgs.replay-sorcery ]; + etc."replay-sorcery.conf".text = configFile; + }; + + security.wrappers = mkIf cfg.enableSysAdminCapability { + replay-sorcery = { + source = "${pkgs.replay-sorcery}/bin/replay-sorcery"; + capabilities = "cap_sys_admin+ep"; + }; + }; + + systemd = { + packages = [ pkgs.replay-sorcery ]; + user.services.replay-sorcery = { + wantedBy = mkIf cfg.autoStart [ "graphical-session.target" ]; + partOf = mkIf cfg.autoStart [ "graphical-session.target" ]; + serviceConfig = { + ExecStart = mkIf cfg.enableSysAdminCapability [ + "" # Tell systemd to clear the existing ExecStart list, to prevent appending to it. + "${config.security.wrapperDir}/replay-sorcery" + ]; + }; + }; + }; + }; + + meta = { + maintainers = with maintainers; [ kira-bruneau ]; + }; +} diff --git a/pkgs/tools/video/replay-sorcery/default.nix b/pkgs/tools/video/replay-sorcery/default.nix new file mode 100644 index 00000000000..8f362c3beb9 --- /dev/null +++ b/pkgs/tools/video/replay-sorcery/default.nix @@ -0,0 +1,62 @@ +{ lib +, stdenv +, fetchFromGitHub +, substituteAll +, cmake +, pkg-config +, ffmpeg +, libX11 +, drmSupport ? true, libdrm +, notifySupport ? true, libnotify +, pulseaudioSupport ? true, libpulseaudio +}: + +stdenv.mkDerivation rec { + pname = "replay-sorcery"; + version = "0.5.0"; + + src = fetchFromGitHub { + owner = "matanui159"; + repo = "ReplaySorcery"; + rev = version; + fetchSubmodules = true; + sha256 = "sha256-HPkSOwfwcg4jLUzKfqdXgLu7mgD5H4wb9d2BrqWQeHc="; + }; + + patches = [ + # Use global config generated by NixOS (/etc/replay-sorcery.conf) + # instead of $out/etc/replay-sorcery.conf. + ./fix-global-config.patch + ] ++ lib.optional notifySupport (substituteAll { + # Patch in libnotify if support is enabled. Can't use makeWrapper + # since it would break the security wrapper in the NixOS module. + src = ./hardcode-libnotify.patch; + inherit libnotify; + }); + + nativeBuildInputs = [ + cmake + pkg-config + ]; + + buildInputs = [ ffmpeg libX11 ] + ++ lib.optional drmSupport libdrm + ++ lib.optional pulseaudioSupport libpulseaudio; + + cmakeFlags = [ + "-DRS_SYSTEMD_DIR=${placeholder "out"}/lib/systemd/user" + + # SETUID & SETGID permissions required for hardware accelerated + # video capture can't be set during the build. Use the NixOS + # module if you want hardware accelerated video capture. + "-DRS_SETID=OFF" + ]; + + meta = with lib; { + description = "An open-source, instant-replay solution for Linux"; + homepage = "https://github.com/matanui159/ReplaySorcery"; + license = licenses.gpl3Plus; + maintainers = with maintainers; [ kira-bruneau ]; + platforms = platforms.linux; + }; +} diff --git a/pkgs/tools/video/replay-sorcery/fix-global-config.patch b/pkgs/tools/video/replay-sorcery/fix-global-config.patch new file mode 100644 index 00000000000..4f30e2875cf --- /dev/null +++ b/pkgs/tools/video/replay-sorcery/fix-global-config.patch @@ -0,0 +1,13 @@ +diff --git a/src/rsbuild.h.in b/src/rsbuild.h.in +index ff0a0f6..5529556 100644 +--- a/src/rsbuild.h.in ++++ b/src/rsbuild.h.in +@@ -20,7 +20,7 @@ + #ifndef RS_BUILD_H + #define RS_BUILD_H + +-#define RS_BUILD_GLOBAL_CONFIG "@CMAKE_INSTALL_PREFIX@/etc/replay-sorcery.conf" ++#define RS_BUILD_GLOBAL_CONFIG "/etc/replay-sorcery.conf" + #define RS_BUILD_LOCAL_CONFIG "%s/.config/replay-sorcery.conf" + + #cmakedefine RS_BUILD_PTHREAD_FOUND diff --git a/pkgs/tools/video/replay-sorcery/hardcode-libnotify.patch b/pkgs/tools/video/replay-sorcery/hardcode-libnotify.patch new file mode 100644 index 00000000000..8ff42bec92f --- /dev/null +++ b/pkgs/tools/video/replay-sorcery/hardcode-libnotify.patch @@ -0,0 +1,25 @@ +diff --git a/src/config.c b/src/config.c +index 3af7455..12cb6b5 100644 +--- a/src/config.c ++++ b/src/config.c +@@ -129,7 +129,7 @@ static const AVOption configOptions[] = { + CONFIG_CONST(alt, RS_CONFIG_KEYMOD_ALT, keyMods), + CONFIG_CONST(super, RS_CONFIG_KEYMOD_SUPER, keyMods), + CONFIG_STRING(outputFile, "~/Videos/ReplaySorcery_%F_%H-%M-%S.mp4"), +- CONFIG_STRING(outputCommand, "notify-send " RS_NAME " \"Saved replay as %s\""), ++ CONFIG_STRING(outputCommand, "@libnotify@/bin/notify-send " RS_NAME " \"Saved replay as %s\""), + {NULL}}; + + static const AVClass configClass = { +diff --git a/sys/replay-sorcery.conf b/sys/replay-sorcery.conf +index 5cd0aa6..c600e7f 100644 +--- a/sys/replay-sorcery.conf ++++ b/sys/replay-sorcery.conf +@@ -123,5 +123,5 @@ outputFile = ~/Videos/ReplaySorcery_%F_%H-%M-%S.mp4 + + # A command to run when a video is successfully saved + # Possible values: a printf formatted command +-# Default value: notify-send ReplaySorcery "Saved replay as %s" +-outputCommand = notify-send ReplaySorcery "Saved replay as %s" ++# Default value: @libnotify@/bin/notify-send ReplaySorcery "Saved replay as %s" ++outputCommand = @libnotify@/bin/notify-send ReplaySorcery "Saved replay as %s" diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 8132d590cc6..aa642484b2d 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -3333,6 +3333,8 @@ in razergenie = libsForQt5.callPackage ../applications/misc/razergenie { }; + replay-sorcery = callPackage ../tools/video/replay-sorcery { }; + ring-daemon = callPackage ../applications/networking/instant-messengers/ring-daemon { }; ripasso-cursive = callPackage ../tools/security/ripasso/cursive.nix {