summary refs log tree commit diff
path: root/modules/sunshine.nix
blob: 6889e025445e7a0bf7c554b748affa919e7c5301 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
{
  me,
  lib,
  pkgs,
  auxiliaryPkgs,
  ...
}:

let
  inherit (lib)
    substring
    toUpper
    getExe
    ;

  # todo: move this to a utils package
  capitalize = str: toUpper (substring 0 1 str) + substring 1 (-1) str;

  user = "mel";

  # per machine sunshine settings
  perMachine = {
    # wolfram has an arc b570 gpu optimized for transcoding,
    # and is the primary streaming machine
    wolfram = {
      adapter_name = "/dev/dri/renderD128"; # primary card should be located here
      encoder = "vaapi"; # or "qsv" is quicksync is better supported
      av1_mode = 2;
    };

    # bismuth has a radeon rx 9070 xt, and is only
    # used when streaming from wolfram is impossible,
    # as it's my personal desktop machine.
    bismuth = {
      adapter_name = "/dev/dri/renderD128"; # primary card should be located here
      encoder = "amdvce";
      av1_mode = 0;
      hevc_mode = 2;
    };
  };
in
{
  services.sunshine = {
    enable = true;
    autoStart = true;
    capSysAdmin = true;
    openFirewall = true;

    settings = {
      sunshine_name = capitalize me.name;

      # no need for encryption since we are going through a secure network anyway
      lan_encryption_mode = 0;
      wan_encryption_mode = 0;
      origin_web_ui_allowed = "wan"; # allow access everywhere

      gamepad = "auto"; # to support ds5 and steam deck controllers
    }
    // perMachine.${me.name};

    applications = {
      env = {
        # give sunshine access to binaries (we can install stuff into .local/bin if we want)
        PATH = "$(PATH):/run/current-system/sw/bin:/etc/profiles/per-user/${user}/bin:$(HOME)/.local/bin";
      };

      apps =
        let
          command =
            name: cmd:
            getExe (
              pkgs.writeShellApplication {
                inherit name;
                runtimeInputs = with pkgs; [
                  steam
                  util-linux
                ];
                text = "sudo -u ${user} ${cmd}";
              }
            );
        in
        [
          # moondeck will call this app when connecting via moonlight, automatically
          {
            name = "MoonDeckStream";
            cmd = "${auxiliaryPkgs.moondeck-buddy}/bin/MoonDeckStream";
            image-path = "${auxiliaryPkgs.moondeck-buddy}/share/icons/hicolor/256x256/apps/moondeckbuddy.png";
            exclude-global-prep-cmd = "false";
            elevated = "false";
          }

          # other applications, called manually
          {
            name = "Desktop";
            image-path = "desktop.png";
            # empty commands will always simply open desktop
          }
          {
            name = "Steam";
            image-path = "steam.png";
            detached = [ (command "sunshine-open-steam-bp" "steam steam://open/bigpicture") ];
            prep-cmd = [
              {
                do = "";
                undo = command "sunshine-close-steam-bp" "steam steam://close/bigpicture"; # close up steam
              }
            ];
            # sunshine can't track the steam startup properly, so don't.
            auto-detach = "true";
            wait-all = "true";
            exit-timeout = "5";
          }
        ];
    };
  };

  # run the buddy app for the moondeck for a nicer integration of sunshine/moonlight
  # when using a steam deck!
  systemd.user.services.moondeck-buddy = {
    description = "MoonDeck Buddy";

    wantedBy = [ "graphical-session.target" ];
    partOf = [ "graphical-session.target" ];
    after = [
      "graphical-session.target"
      "network-online.target"
    ];

    serviceConfig = {
      ExecStart = "${getExe auxiliaryPkgs.moondeck-buddy}";

      # try to stay alive even if something moondeck-buddy depends on,
      # like steam, crashes.
      Restart = "on-failure";
      RestartSec = "5s";

      Environment = "QT_QPA_PLATFORM=wayland";
    };
  };

  networking.firewall.allowedTCPPorts = [ 59999 ]; # port for moondeck-buddy
}