summaryrefslogtreecommitdiffstats
path: root/roles
diff options
context:
space:
mode:
authorSacha Chua <sacha@sachachua.com>2022-10-30 10:06:47 -0400
committerSacha Chua <sacha@sachachua.com>2022-10-30 10:06:47 -0400
commitb8c97d14ed81871eef51ba7253982c45258ec538 (patch)
treeb9e6252349df072aca9b6ff66ca0cf000f7c9cc2 /roles
parenta1e9bd2ba2cabd37a298c4ed951dfe2344bd750f (diff)
downloademacsconf-ansible-b8c97d14ed81871eef51ba7253982c45258ec538.tar.xz
emacsconf-ansible-b8c97d14ed81871eef51ba7253982c45258ec538.zip
Add obs role
Diffstat (limited to '')
-rw-r--r--roles/obs/defaults/main.yml5
-rw-r--r--roles/obs/logo.pngbin0 -> 11774 bytes
-rw-r--r--roles/obs/tasks/firefox.yml36
-rw-r--r--roles/obs/tasks/main.yml79
-rw-r--r--roles/obs/tasks/mpv.yml38
-rw-r--r--roles/obs/tasks/obs-from-source.yml81
-rw-r--r--roles/obs/tasks/obs-setup.yml37
-rw-r--r--roles/obs/tasks/pulse.yml23
-rw-r--r--roles/obs/tasks/tigervnc.yml54
-rw-r--r--roles/obs/tasks/user.yml25
-rw-r--r--roles/obs/tasks/xorg.conf33
-rwxr-xr-xroles/obs/templates/firefox-track3
-rw-r--r--roles/obs/templates/i3-config206
-rwxr-xr-xroles/obs/templates/mpv-track5
-rw-r--r--roles/obs/templates/mpv.conf28
-rwxr-xr-xroles/obs/templates/obs-track3
-rw-r--r--roles/obs/templates/profile.ini49
-rwxr-xr-xroles/obs/templates/pulse142
-rw-r--r--roles/obs/templates/scenes.json407
-rwxr-xr-xroles/obs/templates/stream-desktop-with-ffmpeg.sh2
-rw-r--r--roles/obs/templates/vnc-track1
-rw-r--r--roles/obs/templates/xresources0
-rwxr-xr-xroles/obs/templates/xstartup3
-rwxr-xr-xroles/obs/templates/xstartup-track11
24 files changed, 1271 insertions, 0 deletions
diff --git a/roles/obs/defaults/main.yml b/roles/obs/defaults/main.yml
new file mode 100644
index 0000000..4038fa9
--- /dev/null
+++ b/roles/obs/defaults/main.yml
@@ -0,0 +1,5 @@
+ff_mcustom: content_type=video/webm cluster_time_limit=5100 cluster_size_limit=2M
+ff_vcustom: rt cpu-used=5 threads=2 error-resilient=1 crf=30 g=120 minrate=1.5M maxrate=1.5M
+ff_vbitrate: 1500
+ff_vgopsize: 120
+obs_profile_path: /home/{{ emacsconf_user }}/.config/obs-studio/basic/profiles
diff --git a/roles/obs/logo.png b/roles/obs/logo.png
new file mode 100644
index 0000000..cd62aac
--- /dev/null
+++ b/roles/obs/logo.png
Binary files differ
diff --git a/roles/obs/tasks/firefox.yml b/roles/obs/tasks/firefox.yml
new file mode 100644
index 0000000..d96e330
--- /dev/null
+++ b/roles/obs/tasks/firefox.yml
@@ -0,0 +1,36 @@
+- name: Add Firefox
+ apt:
+ name: firefox-esr
+ when: ansible_distribution == "Debian"
+- name: Add Firefox
+ apt:
+ name: firefox-esr
+ when: ansible_distribution == "Ubuntu"
+- name: Check if Firefox profiles exist
+ lineinfile:
+ dest: "{{ emacsconf_home }}/.mozilla/firefox/profiles.ini"
+ line: "Name={{ emacsconf_id }}-{{ item.id }}"
+ check_mode: yes
+ register: find_profiles
+ failed_when: no
+ loop: "{{ emacsconf_tracks }}"
+- name: Fail if there are missing profiles
+ fail:
+ msg: "Please use firefox -no-remote -CreateProfile {{ emacsconf_id }}-{{ item.item.id }} in an X session to create the profiles."
+ when: item.changed
+ loop: "{{ find_profiles.results }}"
+- name: Create scripts for tracks
+ template:
+ src: firefox-track
+ dest: "{{ emacsconf_home }}/bin/{{ item.id }}/-firefox"
+ owner: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
+ mode: 0775
+ loop: "{{ emacsconf_tracks }}"
+
+ # - debug:
+# var: find_profiles.results
+# - name: Create profile if it doesn't exist
+# shell: xinit firefox -no-remote -CreateProfile "{{ emacsconf_id }}-{{ item.item.id }}"
+# when: item.changed
+# loop: "{{ find_profiles.results }}"
diff --git a/roles/obs/tasks/main.yml b/roles/obs/tasks/main.yml
new file mode 100644
index 0000000..cb981b1
--- /dev/null
+++ b/roles/obs/tasks/main.yml
@@ -0,0 +1,79 @@
+- name: Load icecast vars
+ tags: wip
+ include_vars:
+ file: ../../stream/defaults/main.yml
+- name: Add repo
+ apt_repository:
+ repo: ppa:obsproject/obs-studio
+ when: ansible_distribution == "Ubuntu"
+- name: Add packages
+ apt:
+ name:
+ - xserver-xorg-video-dummy
+ - i3
+ - socat
+ - ffmpeg
+ - pulseaudio
+ - jackd2
+ - alsa-utils
+ - dbus-x11
+ - obs-studio
+ - xserver-xorg-dev
+ update_cache: yes
+- name: Set up user
+ include: user.yml
+- name: Set up track bins for addition to paths
+ file:
+ path: "{{ emacsconf_home }}/bin/{{ item.id }}"
+ state: directory
+ owner: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
+ loop: "{{ emacsconf_tracks }}"
+- name: Copy X11 config
+ template:
+ src: xorg.conf
+ dest: /etc/X11/xorg.conf
+- name: Set up VNC
+ include: tigervnc.yml
+ tags: vnc, wip
+- name: Set up MPV and MPVC
+ tags: mpv
+ include: mpv.yml
+- name: Set up OBS profiles and scenes
+ tags: obs-profile
+ include: obs-setup.yml
+- name: Add FFMPEG script for streaming
+ template:
+ src: stream-desktop-with-ffmpeg.sh
+ dest: /home/{{ emacsconf_user }}/bin/{{ item.id }}/stream-desktop-with-ffmpeg
+ mode: 0775
+ owner: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
+ loop: "{{ emacsconf_tracks }}"
+- name: Copy logo
+ copy:
+ src: logo.png
+ dest: "{{ emacsconf_home }}/logo.png"
+# - name: Start jack
+# shell: jackd -r -ddummy
+# async: 2592000
+- name: Set up pulse
+ include: pulse.yml
+ tags: pulse
+- name: Set up Firefox
+ tags: firefox
+ include: firefox.yml
+- name: Set up I3 directory
+ tags: wip
+ file:
+ path: "{{ emacsconf_home }}/.config/i3"
+ state: directory
+ owner: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
+- name: Set up I3
+ tags: wip
+ template:
+ src: i3-config
+ dest: "{{ emacsconf_home }}/.config/i3/config"
+ owner: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
diff --git a/roles/obs/tasks/mpv.yml b/roles/obs/tasks/mpv.yml
new file mode 100644
index 0000000..581472a
--- /dev/null
+++ b/roles/obs/tasks/mpv.yml
@@ -0,0 +1,38 @@
+- name: add apt-key
+ apt_key:
+ url: https://non-gnu.uvt.nl/debian/uvt_key.gpg
+ state: present
+- name: Add repo
+ apt_repository:
+ repo: deb https://non-gnu.uvt.nl/debian {{ ansible_distribution_release }} uvt
+- name: Install mpv
+ apt:
+ name: mpv=0.34.1+fruit.2
+- name: Create MPV profile directory
+ file:
+ path: "{{ emacsconf_home }}/.config/mpv"
+ state: directory
+ owner: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
+- name: Add MPV profile
+ template:
+ src: mpv.conf
+ dest: "{{ emacsconf_home }}/.config/mpv/mpv.conf"
+ owner: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
+- name: Download MPVC
+ git:
+ repo: https://github.com/lwilletts/mpvc.git
+ dest: /usr/src/mpvc
+- name: Install MPVC
+ make:
+ chdir: /usr/src/mpvc
+ target: install
+- name: Create mpv scripts for tracks
+ template:
+ src: mpv-track
+ dest: "{{ emacsconf_home }}/bin/{{ item.id }}/-mpv"
+ owner: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
+ mode: 0775
+ loop: "{{ emacsconf_tracks }}"
diff --git a/roles/obs/tasks/obs-from-source.yml b/roles/obs/tasks/obs-from-source.yml
new file mode 100644
index 0000000..915aeb2
--- /dev/null
+++ b/roles/obs/tasks/obs-from-source.yml
@@ -0,0 +1,81 @@
+- name: Install packages
+ package:
+ name:
+ - xrdp
+ - xfce4
+ - firefox-esr
+ - mpv
+ - ffmpeg
+ - cmake
+ - ninja-build
+ - pkg-config
+ - clang
+ - clang-format
+ - build-essential
+ - curl
+ - ccache
+ - libavcodec-dev
+ - libavdevice-dev
+ - libavfilter-dev
+ - libavformat-dev
+ - libavutil-dev
+ - libswresample-dev
+ - libswscale-dev
+ - libx264-dev
+ - libcurl4-openssl-dev
+ - libmbedtls-dev
+ - libgl1-mesa-dev
+ - libjansson-dev
+ - libluajit-5.1-dev
+ - python3-dev
+ - libx11-dev
+ - libxcb-randr0-dev
+ - libxcb-shm0-dev
+ - libxcb-xinerama0-dev
+ - libxcb-composite0-dev
+ - libxinerama-dev
+ - libxcb1-dev
+ - libx11-xcb-dev
+ - libxcb-xfixes0-dev
+ - swig
+ - libcmocka-dev
+ - libpci-dev
+ - libxss-dev
+ - libglvnd-dev
+ - libgles2-mesa
+ - libgles2-mesa-dev
+ - libwayland-dev
+ - libxkbcommon-dev
+ - qtbase5-dev
+ - qtbase5-private-dev
+ - libqt5svg5-dev
+ - qtwayland5
+ - libasound2-dev
+ - libfontconfig-dev
+ - libfreetype6-dev
+ - libjack-jackd2-dev
+ - libpulse-dev
+ - libsndio-dev
+ - libspeexdsp-dev
+ - libudev-dev
+ - libv4l-dev
+ - libva-dev
+ - libvlc-dev
+ - libdrm-dev
+- name: Install source
+ git:
+ repo: https://github.com/obsproject/obs-studio.git
+ dest: /usr/src/obs
+ recursive: true
+- name: Configure
+ shell: sudo cmake -S . -B build -G Ninja -DENABLE_PIPEWIRE=OFF -DENABLE_AJA=0 -DENABLE_BROWSER=OFF -DENABLE_NEW_MPEGTS_OUTPUT=OFF
+ args:
+ chdir: /usr/src/obs
+- name: Build
+ shell: sudo cmake --build build
+ args:
+ chdir: /usr/src/obs
+- name: Install
+ shell: sudo cmake --install build
+ args:
+ chdir: /usr/src/obs
diff --git a/roles/obs/tasks/obs-setup.yml b/roles/obs/tasks/obs-setup.yml
new file mode 100644
index 0000000..be82a8f
--- /dev/null
+++ b/roles/obs/tasks/obs-setup.yml
@@ -0,0 +1,37 @@
+- name: Create OBS scene directory
+ file:
+ path: "{{ emacsconf_home }}/.config/obs-studio/basic/scenes"
+ state: directory
+ owner: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
+- name: Create profile directories
+ file:
+ path: "{{ obs_profile_path }}/{{ item.name }}"
+ state: directory
+ owner: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
+ mode: 0775
+ loop: "{{ emacsconf_tracks }}"
+- name: Install OBS profiles
+ template:
+ src: profile.ini
+ dest: "{{ obs_profile_path }}/{{ item.name }}/basic.ini"
+ owner: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
+ force: no
+ mode: 0664
+ loop: "{{ emacsconf_tracks }}"
+- name: Install OBS scenes
+ template:
+ src: scenes.json
+ dest: "{{ emacsconf_home }}/.config/obs-studio/basic/scenes/{{ item.id }}.json"
+ force: no
+ loop: "{{ emacsconf_tracks }}"
+- name: Create OBS scripts for tracks
+ template:
+ src: obs-track
+ dest: "{{ emacsconf_home }}/bin/{{ item.id }}/-obs"
+ owner: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
+ mode: 0775
+ loop: "{{ emacsconf_tracks }}"
diff --git a/roles/obs/tasks/pulse.yml b/roles/obs/tasks/pulse.yml
new file mode 100644
index 0000000..1cb71c5
--- /dev/null
+++ b/roles/obs/tasks/pulse.yml
@@ -0,0 +1,23 @@
+- name: Create pulse directory
+ file:
+ state: directory
+ path: "{{ emacsconf_home }}/.config/pulse/"
+ owner: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
+- name: Set up pulse configuration
+ template:
+ src: pulse
+ owner: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
+ dest: "{{ emacsconf_home }}/.config/pulse/default.pa"
+ mode: 0755
+- name: Change ownership
+ file:
+ state: directory
+ path: "{{ emacsconf_home }}/.config/pulse/"
+ owner: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
+ recurse: true
+# - name: Start pulseaudio
+# become_user: "{{ emacsconf_user }}"
+# shell: pulseaudio --start
diff --git a/roles/obs/tasks/tigervnc.yml b/roles/obs/tasks/tigervnc.yml
new file mode 100644
index 0000000..539b70e
--- /dev/null
+++ b/roles/obs/tasks/tigervnc.yml
@@ -0,0 +1,54 @@
+- name: Add packages
+ apt:
+ name:
+ - tigervnc-standalone-server
+ - tigervnc-common
+- name: Create VNC dirs
+ file:
+ path: /home/{{ emacsconf_user }}/.vnc
+ state: directory
+ mode: 0755
+ owner: "{{ emacsconf_user }}"
+# https://github.com/sdarwin/Ansible-VNC/blob/master/tasks/main.yml
+- name: Set VNC password
+ become: "{{ emacsconf_user }}"
+ shell: |
+ set -o pipefail
+ printf "{{ vnc_password }}\n{{ vnc_password }}\n\n" | vncpasswd /home/{{ emacsconf_user }}/.vnc/passwd
+ args:
+ chdir: "/home/{{ emacsconf_user }}/.vnc"
+ creates: "/home/{{ emacsconf_user }}/.vnc/passwd"
+ executable: /bin/bash
+- name: Set up xstartup
+ template:
+ src: xstartup
+ dest: ~{{ emacsconf_user }}/.vnc/xstartup
+ mode: 0700
+- name: Set up xstartup for the tracks
+ template:
+ src: xstartup-track
+ dest: ~{{ emacsconf_user }}/.vnc/xstartup-{{ item.id }}
+ mode: 0700
+ loop: "{{ emacsconf_tracks }}"
+- name: Set up xstartup
+ template:
+ src: xresources
+ dest: ~{{ emacsconf_user }}/.Xresources
+ owner: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
+ mode: 0600
+- name: Set up VNC scripts
+ template:
+ src: vnc-track
+ dest: "{{ emacsconf_home }}/bin/{{ item.id }}/-vnc"
+ mode: 0755
+ owner: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
+ loop: "{{ emacsconf_tracks }}"
+- name: Set permissions and ownership
+ file:
+ path: "/home/{{ emacsconf_user }}/.vnc"
+ owner: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
+ mode: "u+rwX,g-rwx,o-rwx"
+ recurse: t
diff --git a/roles/obs/tasks/user.yml b/roles/obs/tasks/user.yml
new file mode 100644
index 0000000..c1493dd
--- /dev/null
+++ b/roles/obs/tasks/user.yml
@@ -0,0 +1,25 @@
+- name: Add group
+ group:
+ name: "{{ emacsconf_group }}"
+- name: Add user
+ user:
+ name: "{{ emacsconf_user }}"
+ group: "{{ emacsconf_group }}"
+# password: "{{ emacsconf_unix_password }}"
+- name: Create SSH folder
+ file:
+ path: "/home/{{ emacsconf_user }}/.ssh"
+ owner: "{{ emacsconf_user }}"
+ state: directory
+ mode: 700
+- name: Set up SSH key access
+ template:
+ src: authorized_keys
+ dest: "/home/{{ emacsconf_user }}/.ssh/authorized_keys"
+ force: no
+- name: Change ownership of SSH directory
+ file:
+ path: "/home/{{ emacsconf_user }}/.ssh"
+ recurse: true
+ owner: "{{ emacsconf_user }}"
+ mode: "u+rwX,g-rwx,o-rwx"
diff --git a/roles/obs/tasks/xorg.conf b/roles/obs/tasks/xorg.conf
new file mode 100644
index 0000000..1975093
--- /dev/null
+++ b/roles/obs/tasks/xorg.conf
@@ -0,0 +1,33 @@
+# This xorg configuration file is meant to be used
+# to start a dummy X11 server.
+# For details, please see:
+# https://www.xpra.org/xorg.conf
+
+# Here we setup a Virtual Display of 1920x1080 pixels
+
+Section "Device"
+ Identifier "Configured Video Device"
+ Driver "dummy"
+ #VideoRam 4096000
+ VideoRam 256000
+ #VideoRam 16384
+EndSection
+
+Section "Monitor"
+ Identifier "Configured Monitor"
+ HorizSync 5.0 - 1000.0
+ VertRefresh 5.0 - 200.0
+ Modeline "1280x720_60.00" 74.48 1280 1336 1472 1664 720 721 724 746 -HSync +Vsync
+EndSection
+
+Section "Screen"
+ Identifier "Default Screen"
+ Monitor "Configured Monitor"
+ Device "Configured Video Device"
+ DefaultDepth 24
+ SubSection "Display"
+ Viewport 0 0
+ Depth 24
+ Virtual 1280 720
+ EndSubSection
+EndSection \ No newline at end of file
diff --git a/roles/obs/templates/firefox-track b/roles/obs/templates/firefox-track
new file mode 100755
index 0000000..e3110ee
--- /dev/null
+++ b/roles/obs/templates/firefox-track
@@ -0,0 +1,3 @@
+#!/bin/bash
+# {{ ansible_managed }}
+firefox -P "{{ emacsconf_id }}-{{ item.id }}" $*
diff --git a/roles/obs/templates/i3-config b/roles/obs/templates/i3-config
new file mode 100644
index 0000000..ce22fd3
--- /dev/null
+++ b/roles/obs/templates/i3-config
@@ -0,0 +1,206 @@
+# i3 config file (v4)
+#
+# Please see https://i3wm.org/docs/userguide.html for a complete reference!
+#
+# This config file uses keycodes (bindsym) and was written for the QWERTY
+# layout.
+#
+# To get a config file with the same key positions, but for your current
+# layout, use the i3-config-wizard
+#
+
+# Font for window titles. Will also be used by the bar unless a different font
+# is used in the bar {} block below.
+font pango:monospace 8
+
+# This font is widely installed, provides lots of unicode glyphs, right-to-left
+# text rendering and scalability on retina/hidpi displays (thanks to pango).
+#font pango:DejaVu Sans Mono 8
+
+# The combination of xss-lock, nm-applet and pactl is a popular choice, so
+# they are included here as an example. Modify as you see fit.
+
+# xss-lock grabs a logind suspend inhibit lock and will use i3lock to lock the
+# screen before suspend. Use loginctl lock-session to lock your screen.
+exec --no-startup-id xss-lock --transfer-sleep-lock -- i3lock --nofork
+
+# NetworkManager is the most popular way to manage wireless networks on Linux,
+# and nm-applet is a desktop environment-independent system tray GUI for it.
+exec --no-startup-id nm-applet
+
+# Use pactl to adjust volume in PulseAudio.
+set $refresh_i3status killall -SIGUSR1 i3status
+bindsym XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ +10% && $refresh_i3status
+bindsym XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ -10% && $refresh_i3status
+bindsym XF86AudioMute exec --no-startup-id pactl set-sink-mute @DEFAULT_SINK@ toggle && $refresh_i3status
+bindsym XF86AudioMicMute exec --no-startup-id pactl set-source-mute @DEFAULT_SOURCE@ toggle && $refresh_i3status
+
+# use these keys for focus, movement, and resize directions when reaching for
+# the arrows is not convenient
+set $up l
+set $down k
+set $left j
+set $right semicolon
+
+# use Mouse+Mod1 to drag floating windows to their wanted position
+floating_modifier Mod1
+
+# start a terminal
+bindsym Mod1+Return exec i3-sensible-terminal
+
+# kill focused window
+bindsym Mod1+Shift+q kill
+
+# start dmenu (a program launcher)
+bindsym Mod1+d exec --no-startup-id dmenu_run
+# A more modern dmenu replacement is rofi:
+# bindsym Mod1+d exec "rofi -modi drun,run -show drun"
+# There also is i3-dmenu-desktop which only displays applications shipping a
+# .desktop file. It is a wrapper around dmenu, so you need that installed.
+# bindsym Mod1+d exec --no-startup-id i3-dmenu-desktop
+
+# change focus
+bindsym Mod1+$left focus left
+bindsym Mod1+$down focus down
+bindsym Mod1+$up focus up
+bindsym Mod1+$right focus right
+
+# alternatively, you can use the cursor keys:
+bindsym Mod1+Left focus left
+bindsym Mod1+Down focus down
+bindsym Mod1+Up focus up
+bindsym Mod1+Right focus right
+
+# move focused window
+bindsym Mod1+Shift+$left move left
+bindsym Mod1+Shift+$down move down
+bindsym Mod1+Shift+$up move up
+bindsym Mod1+Shift+$right move right
+
+# alternatively, you can use the cursor keys:
+bindsym Mod1+Shift+Left move left
+bindsym Mod1+Shift+Down move down
+bindsym Mod1+Shift+Up move up
+bindsym Mod1+Shift+Right move right
+
+# split in horizontal orientation
+bindsym Mod1+h split h
+
+# split in vertical orientation
+bindsym Mod1+v split v
+
+# enter fullscreen mode for the focused container
+bindsym Mod1+f fullscreen toggle
+
+# change container layout (stacked, tabbed, toggle split)
+bindsym Mod1+s layout stacking
+bindsym Mod1+w layout tabbed
+bindsym Mod1+e layout toggle split
+
+# toggle tiling / floating
+bindsym Mod1+Shift+space floating toggle
+
+# change focus between tiling / floating windows
+bindsym Mod1+space focus mode_toggle
+
+# focus the parent container
+bindsym Mod1+a focus parent
+
+# focus the child container
+#bindsym Mod1+d focus child
+
+# move the currently focused window to the scratchpad
+bindsym Mod1+Shift+minus move scratchpad
+
+# Show the next scratchpad window or hide the focused scratchpad window.
+# If there are multiple scratchpad windows, this command cycles through them.
+bindsym Mod1+minus scratchpad show
+
+# Define names for default workspaces for which we configure key bindings later on.
+# We use variables to avoid repeating the names in multiple places.
+set $ws1 "1"
+set $ws2 "2"
+set $ws3 "3"
+set $ws4 "4"
+set $ws5 "5"
+set $ws6 "6"
+set $ws7 "7"
+set $ws8 "8"
+set $ws9 "9"
+set $ws10 "10"
+
+# switch to workspace
+bindsym Mod1+1 workspace number $ws1
+bindsym Mod1+2 workspace number $ws2
+bindsym Mod1+3 workspace number $ws3
+bindsym Mod1+4 workspace number $ws4
+bindsym Mod1+5 workspace number $ws5
+bindsym Mod1+6 workspace number $ws6
+bindsym Mod1+7 workspace number $ws7
+bindsym Mod1+8 workspace number $ws8
+bindsym Mod1+9 workspace number $ws9
+bindsym Mod1+0 workspace number $ws10
+
+# move focused container to workspace
+bindsym Mod1+Shift+1 move container to workspace number $ws1
+bindsym Mod1+Shift+2 move container to workspace number $ws2
+bindsym Mod1+Shift+3 move container to workspace number $ws3
+bindsym Mod1+Shift+4 move container to workspace number $ws4
+bindsym Mod1+Shift+5 move container to workspace number $ws5
+bindsym Mod1+Shift+6 move container to workspace number $ws6
+bindsym Mod1+Shift+7 move container to workspace number $ws7
+bindsym Mod1+Shift+8 move container to workspace number $ws8
+bindsym Mod1+Shift+9 move container to workspace number $ws9
+bindsym Mod1+Shift+0 move container to workspace number $ws10
+
+# reload the configuration file
+bindsym Mod1+Shift+c reload
+# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
+bindsym Mod1+Shift+r restart
+# exit i3 (logs you out of your X session)
+bindsym Mod1+Shift+e exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -B 'Yes, exit i3' 'i3-msg exit'"
+
+# resize window (you can also use the mouse for that)
+mode "resize" {
+ # These bindings trigger as soon as you enter the resize mode
+
+ # Pressing left will shrink the window’s width.
+ # Pressing right will grow the window’s width.
+ # Pressing up will shrink the window’s height.
+ # Pressing down will grow the window’s height.
+ bindsym $left resize shrink width 10 px or 10 ppt
+ bindsym $down resize grow height 10 px or 10 ppt
+ bindsym $up resize shrink height 10 px or 10 ppt
+ bindsym $right resize grow width 10 px or 10 ppt
+
+ # same bindings, but for the arrow keys
+ bindsym Left resize shrink width 10 px or 10 ppt
+ bindsym Down resize grow height 10 px or 10 ppt
+ bindsym Up resize shrink height 10 px or 10 ppt
+ bindsym Right resize grow width 10 px or 10 ppt
+
+ # back to normal: Enter or Escape or Mod1+r
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+ bindsym Mod1+r mode "default"
+}
+
+bindsym Mod1+r mode "resize"
+
+# Start i3bar to display a workspace bar (plus the system information i3status
+# finds out, if available)
+bar {
+ status_command i3status
+}
+
+#######################################################################
+# automatically start i3-config-wizard to offer the user to create a
+# keysym-based config which used their favorite modifier (alt or windows)
+#
+# i3-config-wizard will not launch if there already is a config file
+# in ~/.config/i3/config (or $XDG_CONFIG_HOME/i3/config if set) or
+# ~/.i3/config.
+#
+# Please remove the following exec line:
+#######################################################################
+exec i3-config-wizard
diff --git a/roles/obs/templates/mpv-track b/roles/obs/templates/mpv-track
new file mode 100755
index 0000000..b34df70
--- /dev/null
+++ b/roles/obs/templates/mpv-track
@@ -0,0 +1,5 @@
+#!/bin/bash
+# Run MPV with the output set to the right sink and with an IPC socket
+# {{ ansible_managed }}
+
+mpv --input-ipc-server={{ emacsconf_home }}/mpv-socket-{{ emacsconf_id }}-{{ item.id }} --audio-device=pulse/{{ item.id }}-vid --profile={{ emacsconf_id }}-talks $*
diff --git a/roles/obs/templates/mpv.conf b/roles/obs/templates/mpv.conf
new file mode 100644
index 0000000..5c73681
--- /dev/null
+++ b/roles/obs/templates/mpv.conf
@@ -0,0 +1,28 @@
+# {{ ansible_managed }}
+# Requires mpv >= 0.34.0
+
+# Comment out the next line if you don't want this profile loaded by default.
+profile={{ emacsconf_id }}-talks
+# Then you can load it with `--profile={{ emacsconf_id }}-talks`
+
+[{{ emacsconf_id }}-talks]
+# Positioning
+video-zoom=-0.15
+video-pan-y=-0.055
+sub-use-margins=yes
+sub-scale-by-window=yes
+sub-pos=103
+sub-margin-x=150
+sub-margin-y=40
+# Style
+sub-font="Clear Sans Bold"
+sub-color="1/0.82/0"
+sub-blur=0.2
+sub-scale=0.9
+sub-font-size=40
+sub-border-size=0
+sub-border-color=0/1
+sub-shadow-color=0/1
+sub-shadow-offset=1.2
+sub-ass-force-style=Kerning=yes
+sub-ass-line-spacing=0
diff --git a/roles/obs/templates/obs-track b/roles/obs/templates/obs-track
new file mode 100755
index 0000000..5763d30
--- /dev/null
+++ b/roles/obs/templates/obs-track
@@ -0,0 +1,3 @@
+#!/bin/bash
+# {{ ansible_managed }}
+obs --profile "{{ emacsconf_id }}-{{ item.id }}" --collection "{{ item.id }}" -m
diff --git a/roles/obs/templates/profile.ini b/roles/obs/templates/profile.ini
new file mode 100644
index 0000000..ba09dec
--- /dev/null
+++ b/roles/obs/templates/profile.ini
@@ -0,0 +1,49 @@
+[General]
+Name={{ emacsconf_id }}-{{ item.id }}
+
+[Video]
+BaseCX={{ res_x }}
+BaseCY={{ res_y }}
+OutputCX={{ res_x }}
+OutputCY={{ res_y }}
+FPSType=1
+FPSCommon=10
+FPSInt=20
+
+[SimpleOutput]
+RecEncoder=x264
+RecQuality=Small
+
+[Output]
+Mode=Advanced
+
+[AdvOut]
+RescaleRes={{ res_x }}x{{ res_y }}
+TrackIndex=1
+RecType=FFmpeg
+RecRescaleRes={{ res_x }}x{{ res_y }}
+RecTracks=1
+FFOutputToFile=false
+FFFormat=webm
+FFFormatMimeType=video/webm
+FFRescaleRes={{ res_x }}x{{ res_y }}
+FFVEncoderId=139
+FFVEncoder=libvpx
+FFAEncoderId=86021
+FFAEncoder=libvorbis
+FFAudioTrack=1
+FFAudioMixes=1
+Pre22.1Settings=true
+FLVTrack=1
+FFURL=icecast://{{ icecast_emacsconf_user }}:{{ icecast_emacsconf_password }}@{{ icecast_hostname }}:{{ icecast_port }}/{{ emacsconf_id }}/{{ item.id }}.webm
+FFExtension=webm
+FFMCustom={{ ff_mcustom }}
+FFVBitrate={{ ff_vbitrate }}
+FFVGOPSize={{ ff_vgopsize }}
+FFIgnoreCompat=true
+FFVCustom={{ ff_vcustom }}
+FFABitrate=96
+VodTrackIndex=2
+
+[Twitch]
+AddonChoice=3
diff --git a/roles/obs/templates/pulse b/roles/obs/templates/pulse
new file mode 100755
index 0000000..cba7fc0
--- /dev/null
+++ b/roles/obs/templates/pulse
@@ -0,0 +1,142 @@
+#!/usr/bin/pulseaudio -nF
+#
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+
+# This startup script is used only if PulseAudio is started per-user
+# (i.e. not in system mode)
+
+.fail
+
+### Automatically restore the volume of streams and devices
+load-module module-device-restore
+load-module module-card-restore
+
+### Automatically augment property information from .desktop files
+### stored in /usr/share/application
+load-module module-augment-properties
+
+### Should be after module-*-restore but before module-*-detect
+load-module module-switch-on-port-available
+
+### Load audio drivers statically
+### (it's probably better to not load these drivers manually, but instead
+### use module-udev-detect -- see below -- for doing this automatically)
+#load-module module-alsa-sink
+#load-module module-alsa-source device=hw:1,0
+#load-module module-oss device="/dev/dsp" sink_name=output source_name=input
+#load-module module-oss-mmap device="/dev/dsp" sink_name=output source_name=input
+#load-module module-null-sink
+#load-module module-pipe-sink
+
+### Automatically load driver modules depending on the hardware available
+.ifexists module-udev-detect.so
+load-module module-udev-detect
+.else
+### Use the static hardware detection module (for systems that lack udev support)
+load-module module-detect
+.endif
+
+### Automatically connect sink and source if JACK server is present
+.ifexists module-jackdbus-detect.so
+.nofail
+load-module module-jackdbus-detect channels=2
+.fail
+.endif
+
+### Automatically load driver modules for Bluetooth hardware
+.ifexists module-bluetooth-policy.so
+load-module module-bluetooth-policy
+.endif
+
+.ifexists module-bluetooth-discover.so
+load-module module-bluetooth-discover
+.endif
+
+### Load several protocols
+.ifexists module-esound-protocol-unix.so
+load-module module-esound-protocol-unix
+.endif
+load-module module-native-protocol-unix
+
+### Network access (may be configured with paprefs, so leave this commented
+### here if you plan to use paprefs)
+#load-module module-esound-protocol-tcp
+#load-module module-native-protocol-tcp
+#load-module module-zeroconf-publish
+
+### Load the RTP receiver module (also configured via paprefs, see above)
+#load-module module-rtp-recv
+
+### Load the RTP sender module (also configured via paprefs, see above)
+#load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 sink_properties="device.description='RTP Multicast Sink'"
+#load-module module-rtp-send source=rtp.monitor
+
+### Load additional modules from GSettings. This can be configured with the paprefs tool.
+### Please keep in mind that the modules configured by paprefs might conflict with manually
+### loaded modules.
+.ifexists module-gsettings.so
+.nofail
+load-module module-gsettings
+.fail
+.endif
+
+
+### Automatically restore the default sink/source when changed by the user
+### during runtime
+### NOTE: This should be loaded as early as possible so that subsequent modules
+### that look up the default sink/source get the right value
+load-module module-default-device-restore
+
+### Make sure we always have a sink around, even if it is a null sink.
+load-module module-always-sink
+
+### Honour intended role device property
+load-module module-intended-roles
+
+### Automatically suspend sinks/sources that become idle for too long
+load-module module-suspend-on-idle
+
+### If autoexit on idle is enabled we want to make sure we only quit
+### when no local session needs us anymore.
+.ifexists module-console-kit.so
+load-module module-console-kit
+.endif
+.ifexists module-systemd-login.so
+load-module module-systemd-login
+.endif
+
+### Enable positioned event sounds
+load-module module-position-event-sounds
+
+### Cork music/video streams when a phone stream is active
+load-module module-role-cork
+
+### Modules to allow autoloading of filters (such as echo cancellation)
+### on demand. module-filter-heuristics tries to determine what filters
+### make sense, and module-filter-apply does the heavy-lifting of
+### loading modules and rerouting streams.
+load-module module-filter-heuristics
+load-module module-filter-apply
+
+### Make some devices default
+#set-default-sink output
+#set-default-source input
+
+{% for track in emacsconf_tracks %}
+load-module module-null-sink sink_name={{ track.id }}-vid sink_properties=device.description={{ track.id }}-vid
+load-module module-null-sink sink_name={{ track.id }}-qa sink_properties=device.description={{ track.id }}-qa
+{% endfor %}
+load-module module-stream-restore restore_device=false
diff --git a/roles/obs/templates/scenes.json b/roles/obs/templates/scenes.json
new file mode 100644
index 0000000..5e19f16
--- /dev/null
+++ b/roles/obs/templates/scenes.json
@@ -0,0 +1,407 @@
+{
+ "current_program_scene": "All track audio and screen",
+ "current_scene": "All track audio and screen",
+ "current_transition": "Fade",
+ "groups": [],
+ "modules": {
+ "auto-scene-switcher": {
+ "active": false,
+ "interval": 300,
+ "non_matching_scene": "",
+ "switch_if_not_matching": false,
+ "switches": []
+ },
+ "output-timer": {
+ "autoStartRecordTimer": false,
+ "autoStartStreamTimer": false,
+ "pauseRecordTimer": true,
+ "recordTimerHours": 0,
+ "recordTimerMinutes": 0,
+ "recordTimerSeconds": 30,
+ "streamTimerHours": 0,
+ "streamTimerMinutes": 0,
+ "streamTimerSeconds": 30
+ },
+ "scripts-tool": []
+ },
+ "name": "{{ item.id }}",
+ "preview_locked": false,
+ "quick_transitions": [
+ {
+ "duration": 300,
+ "fade_to_black": false,
+ "hotkeys": [],
+ "id": 1,
+ "name": "Cut"
+ },
+ {
+ "duration": 300,
+ "fade_to_black": false,
+ "hotkeys": [],
+ "id": 2,
+ "name": "Fade"
+ },
+ {
+ "duration": 300,
+ "fade_to_black": true,
+ "hotkeys": [],
+ "id": 3,
+ "name": "Fade"
+ }
+ ],
+ "saved_projectors": [],
+ "scaling_enabled": false,
+ "scaling_level": 0,
+ "scaling_off_x": 0,
+ "scaling_off_y": 0,
+ "scene_order": [
+ {
+ "name": "All track audio and screen"
+ },
+ {
+ "name": "Video audio and screen"
+ }
+ ],
+ "sources": [
+ {
+ "balance": 0.5,
+ "deinterlace_field_order": 0,
+ "deinterlace_mode": 0,
+ "enabled": true,
+ "flags": 0,
+ "hotkeys": {
+ "libobs.mute": [],
+ "libobs.push-to-mute": [],
+ "libobs.push-to-talk": [],
+ "libobs.unmute": []
+ },
+ "id": "pulse_output_capture",
+ "mixers": 255,
+ "monitoring_type": 0,
+ "muted": false,
+ "name": "{{ item.id }}-qa",
+ "prev_ver": 469762051,
+ "private_settings": {},
+ "push-to-mute": false,
+ "push-to-mute-delay": 0,
+ "push-to-talk": false,
+ "push-to-talk-delay": 0,
+ "settings": {
+ "device_id": "{{ item.id }}-qa.monitor"
+ },
+ "sync": 0,
+ "versioned_id": "pulse_output_capture",
+ "volume": 1
+ },
+ {
+ "balance": 0.5,
+ "deinterlace_field_order": 0,
+ "deinterlace_mode": 0,
+ "enabled": true,
+ "flags": 0,
+ "hotkeys": {
+ "OBSBasic.SelectScene": [],
+ "libobs.hide_scene_item.Screen Capture (XSHM)": [],
+ "libobs.hide_scene_item.{{ item.id }}-qa": [],
+ "libobs.hide_scene_item.{{ item.id }}-vid": [],
+ "libobs.show_scene_item.Screen Capture (XSHM)": [],
+ "libobs.show_scene_item.{{ item.id }}-qa": [],
+ "libobs.show_scene_item.{{ item.id }}-vid": []
+ },
+ "id": "scene",
+ "mixers": 0,
+ "monitoring_type": 0,
+ "muted": false,
+ "name": "All track audio and screen",
+ "prev_ver": 469762051,
+ "private_settings": {},
+ "push-to-mute": false,
+ "push-to-mute-delay": 0,
+ "push-to-talk": false,
+ "push-to-talk-delay": 0,
+ "settings": {
+ "custom_size": false,
+ "id_counter": 14,
+ "items": [
+ {
+ "align": 5,
+ "blend_method": "default",
+ "blend_type": "normal",
+ "bounds": {
+ "x": 0,
+ "y": 0
+ },
+ "bounds_align": 0,
+ "bounds_type": 0,
+ "crop_bottom": 0,
+ "crop_left": 0,
+ "crop_right": 0,
+ "crop_top": 0,
+ "group_item_backup": false,
+ "hide_transition": {
+ "duration": 0
+ },
+ "id": 1,
+ "locked": false,
+ "name": "Screen Capture (XSHM)",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "private_settings": {},
+ "rot": 0,
+ "scale": {
+ "x": 1,
+ "y": 1
+ },
+ "scale_filter": "disable",
+ "show_transition": {
+ "duration": 0
+ },
+ "visible": true
+ },
+ {
+ "align": 5,
+ "blend_method": "default",
+ "blend_type": "normal",
+ "bounds": {
+ "x": 0,
+ "y": 0
+ },
+ "bounds_align": 0,
+ "bounds_type": 0,
+ "crop_bottom": 0,
+ "crop_left": 0,
+ "crop_right": 0,
+ "crop_top": 0,
+ "group_item_backup": false,
+ "hide_transition": {
+ "duration": 0
+ },
+ "id": 12,
+ "locked": false,
+ "name": "{{ item.id }}-qa",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "private_settings": {},
+ "rot": 0,
+ "scale": {
+ "x": 1,
+ "y": 1
+ },
+ "scale_filter": "disable",
+ "show_transition": {
+ "duration": 0
+ },
+ "visible": true
+ },
+ {
+ "align": 5,
+ "blend_method": "default",
+ "blend_type": "normal",
+ "bounds": {
+ "x": 0,
+ "y": 0
+ },
+ "bounds_align": 0,
+ "bounds_type": 0,
+ "crop_bottom": 0,
+ "crop_left": 0,
+ "crop_right": 0,
+ "crop_top": 0,
+ "group_item_backup": false,
+ "hide_transition": {
+ "duration": 0
+ },
+ "id": 14,
+ "locked": false,
+ "name": "{{ item.id }}-vid",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "private_settings": {},
+ "rot": 0,
+ "scale": {
+ "x": 1,
+ "y": 1
+ },
+ "scale_filter": "disable",
+ "show_transition": {
+ "duration": 0
+ },
+ "visible": true
+ }
+ ]
+ },
+ "sync": 0,
+ "versioned_id": "scene",
+ "volume": 1
+ },
+ {
+ "balance": 0.5,
+ "deinterlace_field_order": 0,
+ "deinterlace_mode": 0,
+ "enabled": true,
+ "flags": 0,
+ "hotkeys": {},
+ "id": "xshm_input",
+ "mixers": 0,
+ "monitoring_type": 0,
+ "muted": false,
+ "name": "Screen Capture (XSHM)",
+ "prev_ver": 469762051,
+ "private_settings": {},
+ "push-to-mute": false,
+ "push-to-mute-delay": 0,
+ "push-to-talk": false,
+ "push-to-talk-delay": 0,
+ "settings": {},
+ "sync": 0,
+ "versioned_id": "xshm_input",
+ "volume": 1
+ },
+ {
+ "balance": 0.5,
+ "deinterlace_field_order": 0,
+ "deinterlace_mode": 0,
+ "enabled": true,
+ "flags": 0,
+ "hotkeys": {
+ "libobs.mute": [],
+ "libobs.push-to-mute": [],
+ "libobs.push-to-talk": [],
+ "libobs.unmute": []
+ },
+ "id": "pulse_output_capture",
+ "mixers": 255,
+ "monitoring_type": 0,
+ "muted": false,
+ "name": "{{ item.id }}-vid",
+ "prev_ver": 469762051,
+ "private_settings": {},
+ "push-to-mute": false,
+ "push-to-mute-delay": 0,
+ "push-to-talk": false,
+ "push-to-talk-delay": 0,
+ "settings": {
+ "device_id": "{{ item.id }}-vid.monitor"
+ },
+ "sync": 0,
+ "versioned_id": "pulse_output_capture",
+ "volume": 1
+ },
+ {
+ "balance": 0.5,
+ "deinterlace_field_order": 0,
+ "deinterlace_mode": 0,
+ "enabled": true,
+ "flags": 0,
+ "hotkeys": {
+ "OBSBasic.SelectScene": [],
+ "libobs.hide_scene_item.Screen Capture (XSHM)": [],
+ "libobs.hide_scene_item.{{ item.id }}-vid": [],
+ "libobs.show_scene_item.Screen Capture (XSHM)": [],
+ "libobs.show_scene_item.{{ item.id }}-vid": []
+ },
+ "id": "scene",
+ "mixers": 0,
+ "monitoring_type": 0,
+ "muted": false,
+ "name": "Video audio and screen",
+ "prev_ver": 469762051,
+ "private_settings": {},
+ "push-to-mute": false,
+ "push-to-mute-delay": 0,
+ "push-to-talk": false,
+ "push-to-talk-delay": 0,
+ "settings": {
+ "custom_size": false,
+ "id_counter": 5,
+ "items": [
+ {
+ "align": 5,
+ "blend_method": "default",
+ "blend_type": "normal",
+ "bounds": {
+ "x": 0,
+ "y": 0
+ },
+ "bounds_align": 0,
+ "bounds_type": 0,
+ "crop_bottom": 0,
+ "crop_left": 0,
+ "crop_right": 0,
+ "crop_top": 0,
+ "group_item_backup": false,
+ "hide_transition": {
+ "duration": 0
+ },
+ "id": 1,
+ "locked": false,
+ "name": "{{ item.id }}-vid",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "private_settings": {},
+ "rot": 0,
+ "scale": {
+ "x": 1,
+ "y": 1
+ },
+ "scale_filter": "disable",
+ "show_transition": {
+ "duration": 0
+ },
+ "visible": true
+ },
+ {
+ "align": 5,
+ "blend_method": "default",
+ "blend_type": "normal",
+ "bounds": {
+ "x": 0,
+ "y": 0
+ },
+ "bounds_align": 0,
+ "bounds_type": 0,
+ "crop_bottom": 0,
+ "crop_left": 0,
+ "crop_right": 0,
+ "crop_top": 0,
+ "group_item_backup": false,
+ "hide_transition": {
+ "duration": 0
+ },
+ "id": 5,
+ "locked": false,
+ "name": "Screen Capture (XSHM)",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "private_settings": {},
+ "rot": 0,
+ "scale": {
+ "x": 1,
+ "y": 1
+ },
+ "scale_filter": "disable",
+ "show_transition": {
+ "duration": 0
+ },
+ "visible": true
+ }
+ ]
+ },
+ "sync": 0,
+ "versioned_id": "scene",
+ "volume": 1
+ }
+ ],
+ "transition_duration": 300,
+ "transitions": []
+}
diff --git a/roles/obs/templates/stream-desktop-with-ffmpeg.sh b/roles/obs/templates/stream-desktop-with-ffmpeg.sh
new file mode 100755
index 0000000..273505b
--- /dev/null
+++ b/roles/obs/templates/stream-desktop-with-ffmpeg.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+while true; do ffmpeg -loglevel ${LOG:0} -ar 48000 -f alsa -channels 2 -sample_rate 48000 -i default -re -video_size {{ res_x }}x{{ res_y }} -framerate 25 -f x11grab -i :0.0 -cluster_size_limit 2M -cluster_time_limit 5100 -content_type video/webm -c:v libvpx -b:v 1M -crf 30 -g 125 -deadline good -threads 4 -f webm icecast://{{ icecast_emacsconf_user }}:{{icecast_emacsconf_password }}@live0.emacsconf.org:{{ icecast_port }}/{{ emacsconf_id }}/${1:{{ item.id }}}; sleep 5; done
diff --git a/roles/obs/templates/vnc-track b/roles/obs/templates/vnc-track
new file mode 100644
index 0000000..b987d8b
--- /dev/null
+++ b/roles/obs/templates/vnc-track
@@ -0,0 +1 @@
+vncserver :{{ item.vnc_id }} -geometry {{ res_x }}x{{ res_y }} -useold -xstartup {{ emacsconf_home }}/.vnc/xstartup-{{ item.id }} -desktop {{ emacsconf_id }}-{{ item.id }} \ No newline at end of file
diff --git a/roles/obs/templates/xresources b/roles/obs/templates/xresources
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/roles/obs/templates/xresources
diff --git a/roles/obs/templates/xstartup b/roles/obs/templates/xstartup
new file mode 100755
index 0000000..bc5c70f
--- /dev/null
+++ b/roles/obs/templates/xstartup
@@ -0,0 +1,3 @@
+#!/bin/bash
+xrdb $HOME/.Xresources
+exec startxfce4 \ No newline at end of file
diff --git a/roles/obs/templates/xstartup-track b/roles/obs/templates/xstartup-track
new file mode 100755
index 0000000..140f836
--- /dev/null
+++ b/roles/obs/templates/xstartup-track
@@ -0,0 +1,11 @@
+#!/bin/bash
+export MPV_SOCKET={{ emacsconf_home }}/{{ item.id }}-mpv-socket
+export TRACK={{ item.id }}
+export PATH="/usr/local/bin:/usr/bin:/bin:{{ emacsconf_home }}/bin/{{ item.id }}"
+xrdb $HOME/.Xresources
+pulseaudio --start
+pacmd set-default-sink {{ item.id }}-qa
+firefox file://data/emacsconf/{{ emacsconf_year }}/index-{{ item.id }}.html -P "{{ emacsconf_id }}-{{ item.id }}" &
+# $HOME/{{ item.id }}-obs &
+# $HOME/{{ item.id }}-mpv $HOME/logo.png &
+exec i3