-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfpp_install.sh
More file actions
executable file
·351 lines (279 loc) · 9.44 KB
/
fpp_install.sh
File metadata and controls
executable file
·351 lines (279 loc) · 9.44 KB
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
#!/bin/bash
set -euo pipefail
PLUGIN_NAME="Announcement Assistant (Audio Ducking)"
PLUGIN_ID="AnnouncementAssistant"
# FPP Plugin Manager may pass these as args like: FPPDIR=/opt/fpp SRCDIR=... PLUGINDIR=...
FPPDIR="${FPPDIR:-}"
SRCDIR="${SRCDIR:-}"
PLUGINDIR="${PLUGINDIR:-}"
# Where FPP stores persistent config on real installs
CFG_DIR="/home/fpp/media/config"
CFG_FILE="${CFG_DIR}/announcementassistant.json"
# Defaults
APPLY_48K=1 # optional tweak; default ON
PIN_FPP_PULSE=1 # recommended; default ON
log() { echo "[$PLUGIN_ID] $*"; }
usage() {
cat <<EOF
Usage: sudo ./fpp_install.sh [options] [FPPDIR=/opt/fpp SRCDIR=... PLUGINDIR=...]
Options:
--no-48k Do NOT modify /etc/pulse/daemon.conf sample rate (default is to set 48k)
--force-48k Force set /etc/pulse/daemon.conf sample rate to 48k (same as default)
--no-pin-fpp Do NOT create /home/fpp/.config/pulse/client.conf (recommended to keep ON)
-h, --help Show this help
Notes:
- This installer runs a system-wide PulseAudio with socket: /run/pulse/native
- It validates the socket exists after restart (otherwise announcements will fail silently)
- FPP Plugin Manager may pass FPPDIR=/opt/fpp style arguments; these are accepted.
EOF
}
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--no-48k) APPLY_48K=0; shift ;;
--force-48k) APPLY_48K=1; shift ;;
--no-pin-fpp) PIN_FPP_PULSE=0; shift ;;
-h|--help) usage; exit 0 ;;
*=*)
# Accept KEY=VALUE args (FPP Plugin Manager uses these)
# Only export valid shell variable names to be safe.
key="${1%%=*}"
val="${1#*=}"
if [[ "$key" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then
export "$key=$val"
# Capture common FPP vars (optional; helpful for debugging)
[[ "$key" == "FPPDIR" ]] && FPPDIR="$val"
[[ "$key" == "SRCDIR" ]] && SRCDIR="$val"
[[ "$key" == "PLUGINDIR" ]] && PLUGINDIR="$val"
fi
shift
;;
*)
log "ERROR: Unknown option: $1"
usage
exit 1
;;
esac
done
}
need_root() {
if [[ "${EUID}" -ne 0 ]]; then
log "ERROR: fpp_install.sh must be run as root."
log "Tip: sudo ./fpp_install.sh"
exit 1
fi
}
ensure_dir() {
local d="$1"
[[ -d "$d" ]] || mkdir -p "$d"
}
install_pkgs_if_missing() {
local missing=0
local pkgs=(
pulseaudio
pulseaudio-utils
libasound2-plugins
alsa-utils
)
for p in "${pkgs[@]}"; do
if ! dpkg -s "$p" >/dev/null 2>&1; then
missing=1
break
fi
done
if [[ "$missing" -eq 1 ]]; then
log "Installing required packages (PulseAudio + ALSA pulse plugin)…"
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get install -y --no-install-recommends "${pkgs[@]}"
else
log "Required packages already installed."
fi
}
ensure_users_in_audio_group() {
if id -u pulse >/dev/null 2>&1; then
usermod -aG audio pulse || true
fi
if id -u fpp >/dev/null 2>&1; then
usermod -aG audio fpp || true
fi
}
install_pulse_system_pa() {
local pulse_dir="/etc/pulse"
local system_pa="${pulse_dir}/system.pa"
ensure_dir "$pulse_dir"
if [[ -f "$system_pa" && ! -f "${system_pa}.aa.bak" ]]; then
cp -a "$system_pa" "${system_pa}.aa.bak"
log "Backed up existing system.pa to system.pa.aa.bak"
fi
cat > "$system_pa" <<'EOF'
### Announcement Assistant system PulseAudio config
### Creates a local unix socket at /run/pulse/native for mixing/ducking use.
.nofail
# Local unix socket all local processes can connect to
# NOTE: Keep arguments minimal for compatibility across PulseAudio builds.
load-module module-native-protocol-unix auth-anonymous=1 socket=/run/pulse/native
# Detect ALSA devices
load-module module-udev-detect
# Always have a sink (prevents “no sink” edge cases)
load-module module-always-sink
# Nice defaults (safe if missing)
load-module module-stream-restore
load-module module-device-restore
load-module module-default-device-restore
EOF
# Upgrade-proofing: strip socket_mode if an older install left it behind.
sed -i -E 's/[[:space:]]+socket_mode=[0-9]+//g' "$system_pa"
chmod 644 "$system_pa"
log "Installed /etc/pulse/system.pa"
}
ensure_pulse_48k_daemon_conf() {
local pulse_dir="/etc/pulse"
local daemon_conf="${pulse_dir}/daemon.conf"
ensure_dir "$pulse_dir"
if [[ -f "$daemon_conf" && ! -f "${daemon_conf}.aa.bak" ]]; then
cp -a "$daemon_conf" "${daemon_conf}.aa.bak"
log "Backed up existing daemon.conf to daemon.conf.aa.bak"
fi
[[ -f "$daemon_conf" ]] || : > "$daemon_conf"
if grep -qE '^[[:space:]]*default-sample-rate[[:space:]]*=' "$daemon_conf"; then
sed -i -E 's|^[[:space:]]*default-sample-rate[[:space:]]*=.*|default-sample-rate = 48000|' "$daemon_conf"
else
echo 'default-sample-rate = 48000' >> "$daemon_conf"
fi
if grep -qE '^[[:space:]]*alternate-sample-rate[[:space:]]*=' "$daemon_conf"; then
sed -i -E 's|^[[:space:]]*alternate-sample-rate[[:space:]]*=.*|alternate-sample-rate = 48000|' "$daemon_conf"
else
echo 'alternate-sample-rate = 48000' >> "$daemon_conf"
fi
chmod 644 "$daemon_conf" || true
log "Ensured /etc/pulse/daemon.conf sample rate is 48000 Hz"
}
install_systemd_service_if_available() {
if ! command -v systemctl >/dev/null 2>&1; then
log "systemctl not found; skipping systemd service install."
return 0
fi
local svc="/etc/systemd/system/announcementassistant-pulse.service"
cat > "$svc" <<'EOF'
[Unit]
Description=Announcement Assistant - PulseAudio (system) for audio mixing/ducking
After=sound.target
[Service]
Type=simple
# /run is tmpfs; ensure pulse runtime dirs exist each boot with correct ownership
ExecStartPre=/usr/bin/install -d -o pulse -g pulse -m 0755 /run/pulse
ExecStartPre=/usr/bin/install -d -o pulse -g pulse -m 0700 /run/pulse/.config
ExecStartPre=/usr/bin/install -d -o pulse -g pulse -m 0700 /run/pulse/.config/pulse
ExecStart=/usr/bin/pulseaudio --system -nF /etc/pulse/system.pa --disallow-exit --exit-idle-time=-1 --log-target=journal
# Ensure local clients (fppd + plugin scripts) can connect to the socket
ExecStartPost=/bin/sh -c 'chmod 0666 /run/pulse/native || true'
Restart=on-failure
RestartSec=1
[Install]
WantedBy=multi-user.target
EOF
chmod 644 "$svc"
systemctl daemon-reload
systemctl enable announcementassistant-pulse.service
# Force a clean restart so updated system.pa takes effect and /run/pulse/native is created.
systemctl stop announcementassistant-pulse.service 2>/dev/null || true
pkill -u pulse pulseaudio 2>/dev/null || true
rm -rf /run/pulse
systemctl start announcementassistant-pulse.service
sleep 1
if [[ ! -S /run/pulse/native ]]; then
log "ERROR: Pulse socket /run/pulse/native was not created. Announcements will not work."
log "Last journal lines:"
journalctl -u announcementassistant-pulse.service -b --no-pager | tail -n 60 || true
exit 1
fi
log "Enabled and started announcementassistant-pulse.service"
}
pin_fpp_user_to_system_pulse() {
if [[ "$PIN_FPP_PULSE" -ne 1 ]]; then
log "Skipping fpp Pulse client pin (per --no-pin-fpp)"
return 0
fi
if ! id -u fpp >/dev/null 2>&1; then
return 0
fi
local d="/home/fpp/.config/pulse"
ensure_dir "$d"
cat > "${d}/client.conf" <<'EOF'
autospawn = no
default-server = unix:/run/pulse/native
EOF
chown -R fpp:fpp "/home/fpp/.config" 2>/dev/null || true
chmod 644 "${d}/client.conf" 2>/dev/null || true
pkill -u fpp pulseaudio 2>/dev/null || true
log "Pinned user 'fpp' Pulse client to system socket and disabled autospawn"
}
seed_default_config_if_missing() {
ensure_dir "$CFG_DIR"
if [[ ! -f "$CFG_FILE" ]]; then
cat > "$CFG_FILE" <<'EOF'
{
"duck": "25%",
"buttons": [
{ "label": "Announcement 1", "file": "" },
{ "label": "Announcement 2", "file": "" },
{ "label": "Announcement 3", "file": "" },
{ "label": "Announcement 4", "file": "" },
{ "label": "Announcement 5", "file": "" },
{ "label": "Announcement 6", "file": "" }
]
}
EOF
chown fpp:fpp "$CFG_FILE" 2>/dev/null || true
chmod 664 "$CFG_FILE" || true
log "Created default config: $CFG_FILE"
else
log "Config already exists: $CFG_FILE"
fi
chown fpp:fpp "$CFG_FILE" 2>/dev/null || true
chmod 664 "$CFG_FILE" 2>/dev/null || true
}
fix_plugin_script_perms() {
local here
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [[ -d "${here}/scripts" ]]; then
chmod 775 "${here}/scripts"/*.sh 2>/dev/null || true
fi
log "Ensured plugin script permissions."
}
post_install_notes() {
cat <<EOF
[$PLUGIN_ID] Install complete.
Next steps in FPP UI:
1) Set Audio Output Device to: pulse
2) Restart fppd
Notes:
- PulseAudio system socket: /run/pulse/native
- Announcement audio files should be placed in: /home/fpp/media/music
- 48kHz tweak: $([[ "$APPLY_48K" -eq 1 ]] && echo "ENABLED" || echo "DISABLED")
EOF
}
main() {
parse_args "$@"
need_root
log "Installing ${PLUGIN_NAME}…"
if [[ -n "${FPPDIR}" || -n "${SRCDIR}" || -n "${PLUGINDIR}" ]]; then
log "FPP installer context: FPPDIR=${FPPDIR:-<unset>} SRCDIR=${SRCDIR:-<unset>} PLUGINDIR=${PLUGINDIR:-<unset>}"
fi
install_pkgs_if_missing
ensure_users_in_audio_group
install_pulse_system_pa
if [[ "$APPLY_48K" -eq 1 ]]; then
ensure_pulse_48k_daemon_conf
else
log "Skipping 48kHz daemon.conf tweak (per --no-48k)"
fi
install_systemd_service_if_available
pin_fpp_user_to_system_pulse
seed_default_config_if_missing
fix_plugin_script_perms
post_install_notes
log "Done."
}
main "$@"