diff --git a/life-church/playbooks/thinkcenter-obs.yml b/life-church/playbooks/thinkcenter-obs.yml new file mode 100644 index 0000000..bba4c64 --- /dev/null +++ b/life-church/playbooks/thinkcenter-obs.yml @@ -0,0 +1,163 @@ +--- +# ============================================================================ +# 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') + 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] Install OBS Studio + virtual-cam + ffmpeg (retry on dpkg lock)" + ansible.builtin.apt: + name: + - obs-studio + - v4l2loopback-dkms + - v4l2loopback-utils + - ffmpeg + state: present + register: obs_install + until: obs_install is succeeded + retries: 3 + delay: 15 + + - name: "[vcam] Configure v4l2loopback module 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 (non-fatal if module build pending a reboot)" + community.general.modprobe: + name: v4l2loopback + state: present + register: vcam_load + failed_when: false + + - name: "[verify] OBS binary present + version" + ansible.builtin.command: obs --version + register: obs_ver + changed_when: false + failed_when: false + + - name: "[verify] Assert OBS installed" + ansible.builtin.assert: + that: "'obs-studio' in (obs_install.stdout | default('')) or obs_ver.rc == 0 or ('OBS' in (obs_ver.stdout | default('')) | string)" + fail_msg: "OBS Studio does not appear to be installed." + success_msg: "OBS Studio installed." + + - name: "[report] OBS summary" + ansible.builtin.debug: + msg: + - "===== thinkcenter OBS Studio =====" + - "packages changed this run : {{ obs_install.changed }}" + - "OBS version : {{ obs_ver.stdout | default('(query failed — check after login)') | 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."