290 lines
12 KiB
YAML
290 lines
12 KiB
YAML
---
|
|
# ============================================================================
|
|
# life-church/playbooks/thinkcenter-obs.yml
|
|
# Install OBS Studio on thinkcenter via the official OBS PPA (latest stable),
|
|
# plus v4l2loopback (virtual camera) + ffmpeg. thinkcenter ONLY. Idempotent.
|
|
# Native packages only (no flatpak/snap — matches the homelab audio rule).
|
|
# become password from Infisical /lc-hosts at runtime.
|
|
#
|
|
# Mint gotcha handled: `add-apt-repository` mis-detects Mint's codename ("zena")
|
|
# instead of the Ubuntu base ("noble"). We add the PPA source explicitly pinned
|
|
# to the Ubuntu base codename so apt can resolve it.
|
|
#
|
|
# UNDO:
|
|
# sudo apt remove --purge obs-studio v4l2loopback-dkms
|
|
# sudo add-apt-repository --remove ppa:obsproject/obs-studio
|
|
# (or delete /etc/apt/sources.list.d/obsproject-*.list + key)
|
|
# ============================================================================
|
|
|
|
# --- Play 1 (localhost): fetch thinkcenter sudo pass from Infisical ----------
|
|
- name: "thinkcenter OBS — resolve become password from Infisical"
|
|
hosts: localhost
|
|
gather_facts: false
|
|
tasks:
|
|
- name: "[resolve] Infisical creds from env"
|
|
ansible.builtin.set_fact:
|
|
infisical_client_id: "{{ lookup('env','INFISICAL_CLIENT_ID') }}"
|
|
infisical_client_secret: "{{ lookup('env','INFISICAL_CLIENT_SECRET') }}"
|
|
infisical_project_id: "{{ lookup('env','INFISICAL_PROJECT_ID') }}"
|
|
infisical_url: "{{ lookup('env','INFISICAL_API_URL') | default('http://192.168.122.41:8080', true) }}"
|
|
infisical_env_slug: "{{ lookup('env','INFISICAL_ENV_SLUG') | default('prod', true) }}"
|
|
|
|
- name: "[resolve] Assert Infisical creds present"
|
|
ansible.builtin.assert:
|
|
that:
|
|
- infisical_client_id | length > 0
|
|
- infisical_client_secret | length > 0
|
|
- infisical_project_id | length > 0
|
|
fail_msg: "Infisical creds missing from Semaphore environment 'infisical-dev'."
|
|
|
|
- name: "[infisical] Log in"
|
|
infisical.vault.login:
|
|
url: "{{ infisical_url }}"
|
|
auth_method: universal_auth
|
|
universal_auth_client_id: "{{ infisical_client_id }}"
|
|
universal_auth_client_secret: "{{ infisical_client_secret }}"
|
|
register: infisical_login
|
|
no_log: true
|
|
|
|
- name: "[infisical] Read /lc-hosts"
|
|
infisical.vault.read_secrets:
|
|
login_data: "{{ infisical_login.login_data }}"
|
|
project_id: "{{ infisical_project_id }}"
|
|
env_slug: "{{ infisical_env_slug }}"
|
|
path: "/lc-hosts"
|
|
as_dict: true
|
|
register: lc_secrets
|
|
no_log: true
|
|
|
|
- name: "[set] Stash thinkcenter sudo pass"
|
|
ansible.builtin.set_fact:
|
|
thinkcenter_become_pass: "{{ lc_secrets.secrets['THINKCENTER_SUDO_PASS'] }}"
|
|
no_log: true
|
|
|
|
# --- Play 2: OBS on thinkcenter ONLY -----------------------------------------
|
|
- name: "Install OBS Studio on thinkcenter"
|
|
hosts: thinkcenter
|
|
gather_facts: true
|
|
become: true
|
|
vars:
|
|
ansible_become_pass: "{{ hostvars['localhost'].thinkcenter_become_pass }}"
|
|
obs_ppa_codename: noble # Ubuntu base for Mint 22.3 (NOT 'zena')
|
|
v4l2_version: "0.15.4" # source build — distro dkms 0.12.7 fails on kernel 7.x
|
|
tasks:
|
|
- name: "[safety] Confirm we are on thinkcenter"
|
|
ansible.builtin.assert:
|
|
that: inventory_hostname == 'thinkcenter'
|
|
fail_msg: "Refusing to run — this play is scoped to thinkcenter only."
|
|
|
|
- name: "[safety] Confirm Ubuntu-family host (apt/PPA path)"
|
|
ansible.builtin.assert:
|
|
that: ansible_facts.os_family == 'Debian'
|
|
fail_msg: "This playbook targets Debian/Ubuntu (Mint). Host is {{ ansible_facts.os_family }}."
|
|
|
|
- name: "[repo] Ensure keyrings dir exists"
|
|
ansible.builtin.file:
|
|
path: /etc/apt/keyrings
|
|
state: directory
|
|
mode: "0755"
|
|
|
|
- name: "[repo] Fetch OBS PPA signing key into keyring (modern signed-by method)"
|
|
ansible.builtin.get_url:
|
|
url: "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xBC7345F522079769F5BBE987EFC71127F425E228"
|
|
dest: /etc/apt/keyrings/obsproject-obs-studio.asc
|
|
mode: "0644"
|
|
force: false
|
|
register: obs_key
|
|
until: obs_key is succeeded
|
|
retries: 3
|
|
delay: 10
|
|
|
|
- name: "[repo] Add OBS PPA source pinned to Ubuntu base codename (avoids Mint 'zena' bug)"
|
|
ansible.builtin.apt_repository:
|
|
repo: "deb [signed-by=/etc/apt/keyrings/obsproject-obs-studio.asc] https://ppa.launchpadcontent.net/obsproject/obs-studio/ubuntu {{ obs_ppa_codename }} main"
|
|
filename: obsproject-obs-studio
|
|
state: present
|
|
update_cache: false
|
|
register: obs_repo
|
|
|
|
- name: "[repo] Update apt cache (retry on transient failure)"
|
|
ansible.builtin.apt:
|
|
update_cache: true
|
|
register: apt_cache
|
|
until: apt_cache is succeeded
|
|
retries: 3
|
|
delay: 15
|
|
|
|
- name: "[apt] Repair any half-configured dpkg state before installing"
|
|
ansible.builtin.command: dpkg --configure -a
|
|
register: dpkg_repair
|
|
changed_when: "'Setting up' in (dpkg_repair.stdout | default(''))"
|
|
failed_when: false
|
|
|
|
- name: "[apt] Install OBS Studio + ffmpeg (core — must succeed; retry on lock)"
|
|
ansible.builtin.apt:
|
|
name:
|
|
- obs-studio
|
|
- ffmpeg
|
|
state: present
|
|
register: obs_install
|
|
until: obs_install is succeeded
|
|
retries: 3
|
|
delay: 15
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Virtual camera (v4l2loopback) — OPTIONAL / best-effort. The Ubuntu-packaged
|
|
# v4l2loopback-dkms (0.12.7) FAILS to build on kernel 7.x (upstream changed
|
|
# the v4l2_fh_add/del signature). So we DO NOT install that package. Instead
|
|
# we build v4l2loopback {{ v4l2_version }} from source, which supports the
|
|
# new kernel API. If anything here fails, we log and continue — OBS itself
|
|
# is already installed; only the "Start Virtual Camera" button is affected.
|
|
# -----------------------------------------------------------------------
|
|
- name: "[vcam] Ensure the broken distro dkms package is not installed"
|
|
ansible.builtin.apt:
|
|
name: v4l2loopback-dkms
|
|
state: absent
|
|
purge: true
|
|
register: vcam_dkms_removed
|
|
failed_when: false
|
|
|
|
- name: "[vcam] Install build prerequisites + v4l2loopback-utils (best-effort)"
|
|
ansible.builtin.apt:
|
|
name:
|
|
- build-essential
|
|
- "linux-headers-{{ ansible_kernel }}"
|
|
- v4l2loopback-utils
|
|
state: present
|
|
register: vcam_deps
|
|
failed_when: false
|
|
until: vcam_deps is succeeded
|
|
retries: 2
|
|
delay: 10
|
|
|
|
- name: "[vcam] Is a working v4l2loopback module already present for this kernel?"
|
|
ansible.builtin.command: "modinfo -k {{ ansible_kernel }} v4l2loopback"
|
|
register: vcam_modinfo
|
|
changed_when: false
|
|
failed_when: false
|
|
|
|
- name: "[vcam] Build + install v4l2loopback {{ v4l2_version }} from source (only if missing)"
|
|
when: vcam_modinfo.rc != 0
|
|
block:
|
|
- name: "[vcam] Fetch v4l2loopback source"
|
|
ansible.builtin.get_url:
|
|
url: "https://github.com/v4l2loopback/v4l2loopback/archive/refs/tags/v{{ v4l2_version }}.tar.gz"
|
|
dest: "/tmp/v4l2loopback-{{ v4l2_version }}.tar.gz"
|
|
mode: "0644"
|
|
register: v4l2_dl
|
|
until: v4l2_dl is succeeded
|
|
retries: 3
|
|
delay: 10
|
|
|
|
- name: "[vcam] Extract source"
|
|
ansible.builtin.unarchive:
|
|
src: "/tmp/v4l2loopback-{{ v4l2_version }}.tar.gz"
|
|
dest: /tmp
|
|
remote_src: true
|
|
|
|
- name: "[vcam] Build the kernel module (make)"
|
|
ansible.builtin.command:
|
|
cmd: make
|
|
chdir: "/tmp/v4l2loopback-{{ v4l2_version }}"
|
|
register: v4l2_make
|
|
changed_when: v4l2_make.rc == 0
|
|
|
|
- name: "[vcam] Install the module + utils (make install)"
|
|
ansible.builtin.command:
|
|
cmd: make install
|
|
chdir: "/tmp/v4l2loopback-{{ v4l2_version }}"
|
|
register: v4l2_makeinstall
|
|
changed_when: v4l2_makeinstall.rc == 0
|
|
|
|
- name: "[vcam] depmod so the new module is discoverable"
|
|
ansible.builtin.command: "depmod -a {{ ansible_kernel }}"
|
|
changed_when: true
|
|
rescue:
|
|
- name: "[vcam] Source build failed — record, do NOT fail the play"
|
|
ansible.builtin.debug:
|
|
msg: "v4l2loopback source build failed; OBS is installed but the Virtual Camera won't work until this is resolved. See /tmp/v4l2loopback-{{ v4l2_version }}."
|
|
|
|
- name: "[vcam] Configure v4l2loopback module options for the OBS virtual camera"
|
|
ansible.builtin.copy:
|
|
dest: /etc/modprobe.d/obs-v4l2loopback.conf
|
|
mode: "0644"
|
|
content: |
|
|
options v4l2loopback devices=1 video_nr=10 card_label="OBS Virtual Camera" exclusive_caps=1
|
|
register: vcam_conf
|
|
|
|
- name: "[vcam] Load v4l2loopback now (best-effort)"
|
|
community.general.modprobe:
|
|
name: v4l2loopback
|
|
state: present
|
|
register: vcam_load
|
|
failed_when: false
|
|
|
|
# --- Verify, with automatic repair + re-verify on failure ---------------
|
|
- name: "[verify] Verify OBS (auto-repair + re-verify if it fails)"
|
|
block:
|
|
- name: "[verify] Query obs-studio package status (dpkg, not the GUI binary)"
|
|
ansible.builtin.command: dpkg-query -W -f='${Status} ${Version}' obs-studio
|
|
register: obs_pkg
|
|
changed_when: false
|
|
failed_when: false
|
|
|
|
- name: "[verify] obs binary present on PATH"
|
|
ansible.builtin.command: which obs
|
|
register: obs_bin
|
|
changed_when: false
|
|
failed_when: false
|
|
|
|
- name: "[verify] Assert OBS installed"
|
|
ansible.builtin.assert:
|
|
that:
|
|
- "'install ok installed' in (obs_pkg.stdout | default(''))"
|
|
- obs_bin.rc == 0
|
|
fail_msg: "OBS verify failed. pkg='{{ obs_pkg.stdout | default('') }}', bin rc={{ obs_bin.rc }}"
|
|
success_msg: "OBS Studio installed."
|
|
rescue:
|
|
- name: "[reverify] Repair dpkg + reinstall OBS, then re-verify"
|
|
block:
|
|
- name: "[reverify] dpkg --configure -a"
|
|
ansible.builtin.command: dpkg --configure -a
|
|
failed_when: false
|
|
changed_when: false
|
|
|
|
- name: "[reverify] apt-get install -f (fix broken deps)"
|
|
ansible.builtin.command: apt-get -y -f install
|
|
failed_when: false
|
|
changed_when: false
|
|
|
|
- name: "[reverify] Reinstall OBS + ffmpeg"
|
|
ansible.builtin.apt:
|
|
name: [obs-studio, ffmpeg]
|
|
state: present
|
|
register: obs_reinstall
|
|
until: obs_reinstall is succeeded
|
|
retries: 3
|
|
delay: 15
|
|
|
|
- name: "[reverify] Re-query package + binary"
|
|
ansible.builtin.shell: "dpkg-query -W -f='${Status}' obs-studio; echo '|'; which obs || true"
|
|
register: obs_recheck
|
|
changed_when: false
|
|
|
|
- name: "[reverify] Assert OBS present after repair"
|
|
ansible.builtin.assert:
|
|
that:
|
|
- "'install ok installed' in obs_recheck.stdout"
|
|
- "'/obs' in obs_recheck.stdout"
|
|
fail_msg: "OBS still not installed after repair attempt: {{ obs_recheck.stdout }}"
|
|
success_msg: "OBS Studio installed (after auto-repair)."
|
|
|
|
- name: "[report] OBS summary"
|
|
ansible.builtin.debug:
|
|
msg:
|
|
- "===== thinkcenter OBS Studio ====="
|
|
- "packages changed this run : {{ obs_install.changed }}"
|
|
- "OBS package : {{ obs_pkg.stdout | default('n/a') | trim }}"
|
|
- "v4l2loopback module loaded: {{ 'yes' if vcam_load.rc | default(1) == 0 else 'not yet (loads on Start Virtual Camera / reboot)' }}"
|
|
- "NOTE: on Mint X11 use XSHM/XComposite capture sources; PipeWire capture is for Wayland."
|