obs-studio
Set up and drive OBS Studio from a pi session with the pi-obs CLI - detect/install, provision obs-websocket as a managed service, switch scenes, bind cameras and displays, virtual camera, recording, s
SKILL.mdpackages/obs-studio-plugin/.pi/skills/obs-studio/SKILL.md
GitHubOBS Studio (pi-obs)
pi-obs is the CLI of this plugin. OBS is a managed service (obs for the local one,
obs-<host> for remote ones). The obs-websocket password lives only in the managed-services
secret store — you never see, print or pass it.
Rules (read first)
ask_userbefore every host-changing step:install,provision(incl.--update, config rotation, secret import),stop, and before ANY use of--yes. Say exactly what will run.--yesskips only the CLI's own prompt; the user's consent comes from you asking first.Offline verbs run directly (no secret, work while OBS is down or
blocked):pi-obs doctor,install,provision,launch,stop,preset list,wake-hook install|uninstall.Live verbs run through
service exec— always pass the id explicitly:pi-dashboard service ensure obs --json # starts OBS if needed; state must be healthy pi-dashboard service exec obs -- pi-obs <verb> --service obsNever put a password on a command line (
--passwordis refused). Never read~/.pi/dashboard/services-secrets.json.Prefer verbs over
call.call <Request>is the escape hatch; its JSON comes from stdin or--json-file, never argv. Removals, scene-collection/profile changes and stream settings are denied throughcallby design.Blank source or unhealthy service →
pi-obs doctorfirst, before any permission advice. A black frame alone is not a permission problem (a stale display id looks identical).After the virtual camera is first activated, tell the user to fully quit and reopen apps that list cameras (Meet, Zoom, Teams) — they cache the camera list at launch.
Stop stream / record / virtual camera before OBS exits. With any output active OBS opens an exit-confirm modal; an outside quit is cancelled (
osascript→User cancelled (-128)) and the modal can sit on an off-screen window, wedging OBS. Run stop as a live verb so it can stop them:pi-dashboard service exec obs -- pi-obs stop --service obs(it stops active outputs first and refuses withoutput-activeif one will not stop). Same before any quit/relaunch, e.g. aprovisionthat needs OBS closed. Websocket not provisioned yet →ask_userto stop them in OBS.Verify output state live, never from the log.
pi-obs status(or the OBS tray label) is the truth; aStarting Virtual Camera outputlog line does not mean the camera is on.
Flows
First setup — doctor → (not installed? ask_user, then install) → ask_user, then
provision → pi-dashboard service start obs → pi-dashboard service exec obs -- pi-obs doctor --live --service obs.
Remote OBS — password on stdin only:
printf '%s' "$PW" | pi-obs provision --remote ws://host:4455 --service obs-studio2 (warn the user it is
plain ws://, LAN or tunnel only).
Scenes — scene list|switch <name>|create <name>, preset apply webcam|desktop|desktop-webcam-pip|pip-bgremoval|subtitles
(idempotent by name; never changes existing inputs; reports created|existing|incomplete|skipped|plugin-missing).
Devices — devices list --kind camera|audio|display, input bind <input> --device <id>|--display <uuid>.
The OBS Virtual Camera is excluded and refused (feedback loop). Displays are macOS-only.
Screen capture frozen after sleep (macOS) — capture refresh [--input <name>] flips each screen capture to
another display and restores it exactly (OBS never restarts a capture that stopped during sleep). Permanent fix:
ask_user, then pi-obs wake-hook install --service obs --yes (LaunchAgent; refreshes after every wake; log
~/.pi/dashboard/plugins/obs-studio/wake-hook.log; wake-hook uninstall removes it).
Outputs — vcam on|off, record on|off, stream target <url> --key-stdin (key on stdin).
Text / filters / pixels — text set <input> <text>, filter add <input> bgremoval,
screenshot <source> --out file.jpg.
Diagnose
| Symptom | Do |
|---|---|
service blocked |
OBS shows its safe-mode prompt on the host. Run pi-obs doctor (works without a secret); the user answers the prompt. |
recovery-suspended |
Two early crashes in a row. After the user answered the prompt: pi-obs launch --reset-recovery. |
auth-failed |
Stored password rejected → ask_user, then pi-obs provision --update. |
| blank screen capture | doctor → stale-display-binding → devices list --kind display → input bind <input> --display <uuid>. |
| screen capture frozen / stale image after sleep | log mac-screencapture: Stream stopped with error 0. capture refresh (live). Recurs every sleep → offer wake-hook install (ask first). |
unsupported-request |
The connected OBS lacks that request; do not retry with call. |
| background not removed in vcam | call GetSourceFilterList on the scene's camera input; macOS plugin kind is mac_vision_filter (not background_removal) → ask_user, then call CreateSourceFilter. No screenshot right after (crash risk). See gotchas. |
service failed right after a crash |
safe-mode prompt pending; user picks Launch normally → service ensure obs again. |
pi-obs stop stops active outputs, then quits. It crashes OBS 32 during teardown (measured 3/3). It warns and asks; the next
service start recovers automatically. Never offer it as routine.
References: references/install.md, references/gotchas.md, references/presets.md,
references/permissions.md.