update readme
This commit is contained in:
48
README.md
48
README.md
@ -1,21 +1,47 @@
|
|||||||
This is an ansible role intended to be ran as a supplement to my server-base playbook upon request
|
This is an ansible role intended to be ran as the final step in the corresponding playbook to do what it says on the tin
|
||||||
|
|
||||||
Some considerations of this are the expectations of docker and samba already being installed and configured
|
If this is not ran as a part of the cosmos playbook, then some considerations here are the expectations of docker and samba already being installed and configured
|
||||||
|
|
||||||
|
This ansible task automatically builds out a matt-cloud debian system to be a VCR Ripping device
|
||||||
|
|
||||||
This ansible task automatically builds out a matt-cloud debian puck to be a VCR Ripping device
|
There is a hardware requirement of USB RCA Capture device, and this device must be configured in defauts/main.yaml
|
||||||
|
|
||||||
There is a hardware requirement of USB RCA Capture device, and this device must have its settings configured in defauts/main.yaml
|
There is an option to install a basic GUI and then display the feed in a browser kiosk
|
||||||
|
|
||||||
|
This platform requires too much processing power to be installed on weak systems. The RK3528A 4-core SBC loads up to 6 when this runs on it. Perhaps a future version of this will have a detector so that when it's installed on a wimpy ARM SBC it will do nothing except save the file; no MediaMTX or Jellyfin. Not important right now.
|
||||||
|
|
||||||
This process uses stages of different software to accomplish the goal of making capturing a VHS tape as automatic as possible
|
This process uses stages of different software to accomplish the goal of making capturing a VHS tape as automatic as possible
|
||||||
|
|
||||||
Most of the layers include:
|
The various pertinent services live at these ports:
|
||||||
- ffmpeg service to combine the video and audio of the USB capture device into a rtmp stream
|
- Control Panel at port 8081
|
||||||
- mediamtx to automatically save rtmp streams to a mp4 file
|
- Local Read-Only File Index at 8080
|
||||||
- a docker apache php container for the control site
|
- Preview livestream at 8888
|
||||||
- a python API service to control the ffmpeg streaming service
|
- Jellyfin at 8096
|
||||||
- a small helper script to monitor the elapsed time and stop streaming after the selected time
|
|
||||||
- a playbook to mount and format a sd card when using ARM based puck systems
|
Overview of layers:
|
||||||
|
- ffmpeg service to combine the video and audio of the USB capture device into a RTMP stream and mp4 file
|
||||||
|
- The RTMP stream is so the capture can be live-previewed
|
||||||
|
- This feed is pointed at the MediaMTX service
|
||||||
|
- Mediamtx to monitor current capture
|
||||||
|
- This service will only be used for its ability to view a RTMP stream live in a web browser
|
||||||
|
- Docker container with apache+php for the control site
|
||||||
|
- This runs the PHP site that controls the ffmpeg capture service
|
||||||
|
- Python API service to control the ffmpeg streaming service
|
||||||
|
- This Python API is used by the PHP site for controlling the service
|
||||||
|
- It also has the duration variable storage control API here
|
||||||
|
- This is stored locally in a JSON file
|
||||||
|
- Small helper script to monitor the elapsed time and stop streaming after the selected time
|
||||||
|
- This script kills the capture service after the duration has been reached
|
||||||
|
- It does this by reading the JSON file managed by the Python API
|
||||||
|
- Playbook to mount and format additional storage when present
|
||||||
|
- When a secondary storage is detected it will be mapped at the media storage path
|
||||||
|
- If there is a blank storage attached, it will be formatted so BE CAREFUL
|
||||||
|
- If there is no secondary storage, the videos will be stored on the root path
|
||||||
|
- Playbook to install the GUI when certain hardware is detected
|
||||||
|
- Right now it will just be a 2nd Gen MS Surface I have
|
||||||
|
- It will be identified by the "System Info" dmidecode data
|
||||||
|
- There is a variable in the defaults file for a list of these strings
|
||||||
|
|
||||||
|
Some additional notes on the storage handling task working_folder.yaml. This will mount additional local storage if present. It expects there to be a single additional storage device and will work with an SD Card or an NVMe. It will map if a valid GPT ext4 volume is present, and it will create a new ext4 volume if there are no volumes present on this storage. It should just fail in edge cases which is fine. It would be a bad idea to run this playbook on an inappropriate endpoint.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -10,10 +10,16 @@ streaming_working_folder: "/opt/cosmos/streamer"
|
|||||||
|
|
||||||
mediamtx_working_folder: "/opt/cosmos/mediamtx"
|
mediamtx_working_folder: "/opt/cosmos/mediamtx"
|
||||||
|
|
||||||
|
jellyfin_working_folder: "/opt/cosmos/jellyfin"
|
||||||
|
|
||||||
|
jellyfin_port: "8096"
|
||||||
|
|
||||||
service_control_folder: "/opt/cosmos/streamer/service_control"
|
service_control_folder: "/opt/cosmos/streamer/service_control"
|
||||||
|
|
||||||
video_device: "/dev/video0"
|
video_device: "/dev/video0"
|
||||||
|
|
||||||
|
v4l2_id_string: "AV TO USB2.0"
|
||||||
|
|
||||||
audio_device: "hw:0,0"
|
audio_device: "hw:0,0"
|
||||||
|
|
||||||
mediamtx_architecture: "amd64"
|
mediamtx_architecture: "amd64"
|
||||||
@ -30,9 +36,11 @@ container_name: "service_control"
|
|||||||
|
|
||||||
mediamtx_local_version: "deadbeef_test"
|
mediamtx_local_version: "deadbeef_test"
|
||||||
|
|
||||||
mount_sd: true
|
storage_unmounted: true
|
||||||
|
|
||||||
format_sd: false
|
mount_storge: true
|
||||||
|
|
||||||
|
format_storage: false
|
||||||
|
|
||||||
sd_unmounted: true
|
sd_unmounted: true
|
||||||
|
|
||||||
@ -40,22 +48,34 @@ arm_arch: false
|
|||||||
|
|
||||||
refresh_special: true
|
refresh_special: true
|
||||||
|
|
||||||
|
kiosk_models:
|
||||||
|
- "Surface Pro 2"
|
||||||
|
|
||||||
|
install_kodi: false
|
||||||
|
|
||||||
|
local_username: "cosmos"
|
||||||
|
|
||||||
|
chrome_website: "http://localhost:8888/stream"
|
||||||
|
|
||||||
|
fixed_size: "--start-fullscreen"
|
||||||
|
|
||||||
streamer_packages:
|
streamer_packages:
|
||||||
- gdisk
|
- gdisk
|
||||||
- ffmpeg
|
- ffmpeg
|
||||||
- alsa-utils
|
- alsa-utils
|
||||||
- alsa-oss
|
# - alsa-oss
|
||||||
- alsa-firmware-loaders
|
- alsa-firmware-loaders
|
||||||
- apulse
|
- apulse
|
||||||
- libasound2-plugins
|
- libasound2-plugins
|
||||||
- v4l-utils
|
- v4l-utils
|
||||||
- usbutils
|
- usbutils
|
||||||
- python3-venv
|
- python3-venv
|
||||||
|
- python3-setuptools
|
||||||
|
|
||||||
# media_mtx variables
|
# media_mtx variables
|
||||||
mediamtx_configs:
|
mediamtx_configs:
|
||||||
- search_string: 'record:'
|
- search_string: 'record:'
|
||||||
line: ' record: yes'
|
line: ' record: no'
|
||||||
- search_string: 'recordPath'
|
- search_string: 'recordPath'
|
||||||
line: ' recordPath: {{ recording_capture_folder }}/%path/%Y-%m-%d_%H-%M/%H-%M-%S-%f'
|
line: ' recordPath: {{ recording_capture_folder }}/%path/%Y-%m-%d_%H-%M/%H-%M-%S-%f'
|
||||||
- search_string: 'recordSegmentDuration'
|
- search_string: 'recordSegmentDuration'
|
||||||
|
|||||||
BIN
files/jellyfin.tar.gz
Normal file
BIN
files/jellyfin.tar.gz
Normal file
Binary file not shown.
37
tasks/jellyfin.yaml
Normal file
37
tasks/jellyfin.yaml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
###############################################
|
||||||
|
# Install Jellyfin
|
||||||
|
###############################################
|
||||||
|
|
||||||
|
- name: jellyfin - create folder
|
||||||
|
file:
|
||||||
|
path: "{{ jellyfin_working_folder }}"
|
||||||
|
state: directory
|
||||||
|
mode: '0755'
|
||||||
|
|
||||||
|
# Extract jellyfin configs
|
||||||
|
- name: jellyfin - Extract jellyfin.tar.gz
|
||||||
|
unarchive:
|
||||||
|
src: jellyfin.tar.gz
|
||||||
|
dest: "/opt/cosmos"
|
||||||
|
owner: "1000"
|
||||||
|
group: "1000"
|
||||||
|
mode: 0755
|
||||||
|
|
||||||
|
- name: jellyfin - Copy config
|
||||||
|
template:
|
||||||
|
src: docker-compose-jellyfin.yaml
|
||||||
|
dest: "{{ jellyfin_working_folder }}/docker-compose.yaml"
|
||||||
|
mode: 0644
|
||||||
|
|
||||||
|
- name: "jellyfin - Start container at {{ jellyfin_port }}"
|
||||||
|
shell: "docker-compose -f {{ jellyfin_working_folder }}/docker-compose.yaml up -d"
|
||||||
|
register: jellyfin_output
|
||||||
|
- debug:
|
||||||
|
msg:
|
||||||
|
- "Docker compose output:"
|
||||||
|
- "{{ jellyfin_output.stdout_lines }}"
|
||||||
|
- "{{ jellyfin_output.stderr_lines }}"
|
||||||
|
|
||||||
|
...
|
||||||
43
tasks/kiosk.yaml
Normal file
43
tasks/kiosk.yaml
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- name: Kiosk - get Product Name
|
||||||
|
shell: "dmidecode | grep -A3 'System Info' | grep Product | cut -d: -f2 | cut -b2-"
|
||||||
|
register: prod_name_output
|
||||||
|
|
||||||
|
|
||||||
|
- name: Kiosk - Check for approved platform
|
||||||
|
set_fact:
|
||||||
|
install_sddm: "{{ prod_name_output.stdout_lines[0] in kiosk_models }}"
|
||||||
|
|
||||||
|
|
||||||
|
- name: Kiosk - Proceed with local kiosk install
|
||||||
|
when: install_sddm | bool
|
||||||
|
block:
|
||||||
|
|
||||||
|
# install SDDM + Plasma using Kodi role
|
||||||
|
- name: Kiosk - Install SDDM + KDE
|
||||||
|
include_role:
|
||||||
|
- kodi
|
||||||
|
|
||||||
|
# Install Chrome
|
||||||
|
- name: Install chrome
|
||||||
|
block:
|
||||||
|
- name: prereqs - Chrome - Check if installed
|
||||||
|
command: dpkg -l google-chrome-stable
|
||||||
|
register: chrome_installed
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: prereqs - Chrome - Set chrome_present variable
|
||||||
|
set_fact:
|
||||||
|
chrome_present: "{{ chrome_installed.rc == 0 }}"
|
||||||
|
|
||||||
|
- name: prereqs - Install Chrome
|
||||||
|
include_tasks: /var/jenkins_home/ansible/roles/install_apps/tasks/chrome.yaml
|
||||||
|
when: not chrome_present | bool
|
||||||
|
|
||||||
|
# set up chrome kiosk using lldp_scan role
|
||||||
|
- name: Kiosk - Install Chrome Kiosk Service
|
||||||
|
include_role:
|
||||||
|
- lldp_scan
|
||||||
|
|
||||||
|
...
|
||||||
@ -12,8 +12,11 @@
|
|||||||
set_fact:
|
set_fact:
|
||||||
cpu_architecture: "{{ cpu_architecture_output.stdout_lines[0] }}"
|
cpu_architecture: "{{ cpu_architecture_output.stdout_lines[0] }}"
|
||||||
|
|
||||||
- name: Video Capture - Install Packages
|
- name: Video Capture - Run Once Block
|
||||||
when: not refresh_special | bool
|
when: not refresh_special | bool
|
||||||
|
block:
|
||||||
|
|
||||||
|
- name: Video Capture - Install Packages
|
||||||
apt:
|
apt:
|
||||||
name:
|
name:
|
||||||
- "{{ streamer_packages_items }}"
|
- "{{ streamer_packages_items }}"
|
||||||
@ -22,14 +25,16 @@
|
|||||||
loop_control:
|
loop_control:
|
||||||
loop_var: streamer_packages_items
|
loop_var: streamer_packages_items
|
||||||
|
|
||||||
- name: Video Capture - SD Card Handler
|
- name: Video Capture - Working Folder Handler
|
||||||
when: mount_sd | bool
|
include_tasks: working_folder.yaml
|
||||||
include_tasks: sd_handler.yaml
|
|
||||||
|
|
||||||
- name: Video Capture - Configure MediaMTX
|
- name: Video Capture - Configure MediaMTX
|
||||||
when: not refresh_special | bool
|
|
||||||
include_tasks: mediamtx.yaml
|
include_tasks: mediamtx.yaml
|
||||||
|
|
||||||
|
- name: Video Capture - Configure Jellyfin
|
||||||
|
when: '"amd64" in cpu_architecture'
|
||||||
|
include_tasks: jellyfin.yaml
|
||||||
|
|
||||||
- name: Video Capture - Configure Streaming
|
- name: Video Capture - Configure Streaming
|
||||||
include_tasks: streamer.yaml
|
include_tasks: streamer.yaml
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
# mediamtx automatically makes video files from rtmp streams
|
# mediamtx is for monitoring the capture
|
||||||
|
|
||||||
- name: MediaMTX - stop mediamtx_service if running
|
- name: MediaMTX - stop mediamtx_service if running
|
||||||
systemd:
|
systemd:
|
||||||
@ -40,6 +40,7 @@
|
|||||||
mode: '0755'
|
mode: '0755'
|
||||||
remote_src: yes
|
remote_src: yes
|
||||||
|
|
||||||
|
# this loops over a bunch of vars and modifies the config file
|
||||||
- name: MediaMTX - update configs
|
- name: MediaMTX - update configs
|
||||||
lineinfile:
|
lineinfile:
|
||||||
path: "{{ mediamtx_working_folder }}/mediamtx.yml"
|
path: "{{ mediamtx_working_folder }}/mediamtx.yml"
|
||||||
|
|||||||
@ -1,215 +0,0 @@
|
|||||||
---
|
|
||||||
|
|
||||||
# when ran, this will look for an SD card
|
|
||||||
# optionally format it, be careful with that, it WILL destroy things
|
|
||||||
# map it to working_storage
|
|
||||||
# can only run on arm64
|
|
||||||
# '"arm64" in cpu_architecture'
|
|
||||||
|
|
||||||
|
|
||||||
- name: Video Capture - Check for SD
|
|
||||||
shell: "df"
|
|
||||||
register: df_check_output
|
|
||||||
|
|
||||||
- name: Video Capture - Set sd_unmounted
|
|
||||||
when: working_storage in df_check_output.stdout
|
|
||||||
set_fact:
|
|
||||||
sd_unmounted: false
|
|
||||||
|
|
||||||
- name: Video Capture - Set arm_arch
|
|
||||||
when: '"arm64" in cpu_architecture'
|
|
||||||
set_fact:
|
|
||||||
arm_arch: true
|
|
||||||
format_sd: false
|
|
||||||
|
|
||||||
- name: SD Handler - Checks have passed
|
|
||||||
when: sd_unmounted and arm_arch | bool
|
|
||||||
block:
|
|
||||||
|
|
||||||
- name: SD Handler - Get SD Device
|
|
||||||
block:
|
|
||||||
|
|
||||||
- name: SD Handler - get boot device
|
|
||||||
shell: "blkid | grep '\"boot\"' | cut -d: -f 1 | cut -d/ -f 3 | cut -dp -f 1"
|
|
||||||
register: boot_device
|
|
||||||
|
|
||||||
- name: SD Handler - get sd card device
|
|
||||||
shell: "lsblk -o NAME,SIZE --nodeps | grep -v -e loop -e {{ boot_device.stdout_lines[0] }} -e NAME | cut -d ' ' -f 1"
|
|
||||||
register: sd_card_device
|
|
||||||
|
|
||||||
- name: SD Handler - format SD card
|
|
||||||
when: format_sd | bool
|
|
||||||
block:
|
|
||||||
|
|
||||||
- name: SD Handler - clear existing partition table
|
|
||||||
shell: 'sgdisk --zap-all "/dev/{{ sd_card_device.stdout_lines[0] }}"'
|
|
||||||
|
|
||||||
- name: SD Handler - create new partition table
|
|
||||||
shell: 'sgdisk --new=0:0:0 "/dev/{{ sd_card_device.stdout_lines[0] }}"'
|
|
||||||
|
|
||||||
- name: SD Handler - create new volume table
|
|
||||||
shell: 'sgdisk --typecode=1:8300 --change-name=1:"Linux filesystem" "/dev/{{ sd_card_device.stdout_lines[0] }}"'
|
|
||||||
|
|
||||||
- name: SD Handler - find new volume
|
|
||||||
shell: 'blkid | grep {{ sd_card_device.stdout_lines[0] }} | cut -d: -f1'
|
|
||||||
register: new_sd_volume
|
|
||||||
|
|
||||||
- name: SD Handler - format new volume
|
|
||||||
shell: 'mkfs.ext4 {{ new_sd_volume.stdout_lines[0] }}'
|
|
||||||
register: format_output
|
|
||||||
|
|
||||||
- name: SD Handler - show format stdout
|
|
||||||
debug:
|
|
||||||
msg: "{{ format_output.stdout_lines }}"
|
|
||||||
|
|
||||||
- name: SD Handler - find SD card volume
|
|
||||||
block:
|
|
||||||
|
|
||||||
- name: SD Handler - get sd card uuid
|
|
||||||
shell: "blkid | grep {{ sd_card_device.stdout_lines[0] }} | awk '{for (i=1; i<=NF; i++) print $i}' | grep UUID | grep -v PART | cut -d '\"' -f 2"
|
|
||||||
register: sd_card_uuid_output
|
|
||||||
|
|
||||||
- name: SD Handler - set uuid variable
|
|
||||||
set_fact:
|
|
||||||
sd_card_uuid: "{{ sd_card_uuid_output.stdout_lines[0] }}"
|
|
||||||
|
|
||||||
- debug:
|
|
||||||
msg: "UUID Found: {{ sd_card_uuid }}"
|
|
||||||
|
|
||||||
- name: SD Handler - mount sd card
|
|
||||||
block:
|
|
||||||
|
|
||||||
- name: SD Handler - check folder
|
|
||||||
file:
|
|
||||||
path: "{{ working_storage }}"
|
|
||||||
state: directory
|
|
||||||
mode: '0755'
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
|
|
||||||
- name: SD Handler - Generate fstab entry
|
|
||||||
set_fact:
|
|
||||||
fstab_line_sd: "UUID={{ sd_card_uuid }} {{ working_storage }} ext4 errors=remount-ro 0 1"
|
|
||||||
|
|
||||||
- name: SD Handler - add fstab entry
|
|
||||||
lineinfile:
|
|
||||||
path: "/etc/fstab"
|
|
||||||
search_string: "{{ sd_card_uuid }}"
|
|
||||||
line: "{{ fstab_line_sd }}"
|
|
||||||
|
|
||||||
- debug:
|
|
||||||
msg: |
|
|
||||||
fstab entry:
|
|
||||||
{{ fstab_line_sd }}
|
|
||||||
|
|
||||||
- name: SD Handler - daemon reload
|
|
||||||
systemd:
|
|
||||||
daemon_reload: yes
|
|
||||||
|
|
||||||
- name: SD Handler - Mount it
|
|
||||||
shell: mount -a
|
|
||||||
|
|
||||||
- name: SD Handler - validate this
|
|
||||||
block:
|
|
||||||
|
|
||||||
- name: SD Handler - check for new mount point
|
|
||||||
shell: "df -h | grep -e Size -e {{ working_storage }}"
|
|
||||||
register: sd_test_output
|
|
||||||
|
|
||||||
- debug:
|
|
||||||
msg: "{{ sd_test_output.stdout_lines }}"
|
|
||||||
|
|
||||||
- name: SD Handler - samba and index
|
|
||||||
block:
|
|
||||||
|
|
||||||
- name: SD Handler - Share with samba
|
|
||||||
block:
|
|
||||||
|
|
||||||
- name: SD Handler - Create config file
|
|
||||||
copy:
|
|
||||||
dest: "/etc/samba/smb.conf.d/vcr_rip.conf"
|
|
||||||
content: |
|
|
||||||
[vcr_rip]
|
|
||||||
path = {{ working_storage }}
|
|
||||||
writable = yes
|
|
||||||
read only = no
|
|
||||||
only guest = yes
|
|
||||||
public = yes
|
|
||||||
guest ok = yes
|
|
||||||
guest only = yes
|
|
||||||
guest account = nobody
|
|
||||||
browsable = yes
|
|
||||||
mode: 0644
|
|
||||||
|
|
||||||
- name: SD Handler - Include in smb.conf
|
|
||||||
lineinfile:
|
|
||||||
path: "/etc/samba/smb.conf"
|
|
||||||
search_string: "/etc/samba/smb.conf.d/vcr_rip.conf"
|
|
||||||
line: "include = /etc/samba/smb.conf.d/vcr_rip.conf"
|
|
||||||
insertbefore: '[global]'
|
|
||||||
|
|
||||||
- name: SD Handler - restart smbd
|
|
||||||
service:
|
|
||||||
name: smbd
|
|
||||||
state: restarted
|
|
||||||
enabled: yes
|
|
||||||
|
|
||||||
- name: SD Handler - Apache Index
|
|
||||||
block:
|
|
||||||
|
|
||||||
###############################################
|
|
||||||
# Clone and build apache-index
|
|
||||||
###############################################
|
|
||||||
|
|
||||||
- name: SD Handler - - apache-index - Ensure the destination directory exists
|
|
||||||
file:
|
|
||||||
path: /opt/cosmos/apache-index # Replace with your desired directory on the target machine
|
|
||||||
state: directory
|
|
||||||
mode: '0755'
|
|
||||||
|
|
||||||
- name: SD Handler - - apache-index - Clone the Git repository
|
|
||||||
git:
|
|
||||||
repo: 'https://gitea.matt-cloud.com/matt/apache-index.git' # Replace with your repository URL
|
|
||||||
dest: '/opt/cosmos/apache-index' # Replace with your desired directory on the target machine
|
|
||||||
version: 'main' # Replace with the branch, tag, or commit you want to clone
|
|
||||||
force: yes
|
|
||||||
|
|
||||||
- name: SD Handler - - apache-index - Build image
|
|
||||||
docker_image:
|
|
||||||
name: apache-index # Name of the SD Handler - image
|
|
||||||
source: build
|
|
||||||
build:
|
|
||||||
path: /opt/cosmos/apache-index # Path to the directory containing your Dockerfile
|
|
||||||
state: present
|
|
||||||
tag: latest
|
|
||||||
register: build_index_output
|
|
||||||
- name: SD Handler - - apache-index - Image changed
|
|
||||||
debug: msg="{{ build_index_output.changed }}"
|
|
||||||
- name: SD Handler - - apache-index - Image failed
|
|
||||||
debug: msg="{{ build_index_output.failed }}"
|
|
||||||
- name: SD Handler - - apache-index - Image layers
|
|
||||||
debug: msg="{{ build_index_output.image.RootFS.Layers }}"
|
|
||||||
|
|
||||||
###############################################
|
|
||||||
# Install local-index 00
|
|
||||||
###############################################
|
|
||||||
|
|
||||||
- name: SD Handler - - local-index - create folder
|
|
||||||
file:
|
|
||||||
path: "{{ streaming_working_folder }}/local-index"
|
|
||||||
state: directory
|
|
||||||
mode: '0755'
|
|
||||||
|
|
||||||
- name: SD Handler - - local-index - Copy config
|
|
||||||
template:
|
|
||||||
src: docker-compose-local-index.yaml
|
|
||||||
dest: "{{ streaming_working_folder }}/local-index/docker-compose.yaml"
|
|
||||||
mode: 0644
|
|
||||||
|
|
||||||
- name: SD Handler - - local-index - Start container at 172.17.0.1:8080
|
|
||||||
shell: "docker-compose -f {{ streaming_working_folder }}/local-index/docker-compose.yaml up -d"
|
|
||||||
register: local_index_output
|
|
||||||
- debug: |
|
|
||||||
msg="{{ local_index_output.stdout_lines }}"
|
|
||||||
msg="{{ local_index_output.stderr_lines }}"
|
|
||||||
...
|
|
||||||
@ -38,9 +38,9 @@
|
|||||||
copy:
|
copy:
|
||||||
dest: "{{ service_control_folder }}/requirements.txt"
|
dest: "{{ service_control_folder }}/requirements.txt"
|
||||||
content: |
|
content: |
|
||||||
|
requests
|
||||||
Flask==2.1.0
|
Flask==2.1.0
|
||||||
pytz
|
pytz
|
||||||
requests
|
|
||||||
lxml
|
lxml
|
||||||
Werkzeug==2.0
|
Werkzeug==2.0
|
||||||
mode: 0644
|
mode: 0644
|
||||||
@ -61,6 +61,7 @@
|
|||||||
dest: /etc/systemd/system/service_control.service
|
dest: /etc/systemd/system/service_control.service
|
||||||
mode: 0644
|
mode: 0644
|
||||||
|
|
||||||
|
# this is the service that stops the capture service
|
||||||
- name: video_capture - service timeout helper files
|
- name: video_capture - service timeout helper files
|
||||||
block:
|
block:
|
||||||
|
|
||||||
@ -96,7 +97,7 @@
|
|||||||
|
|
||||||
|
|
||||||
###############################################
|
###############################################
|
||||||
# This part sets up serice control website
|
# This part sets up service control website
|
||||||
###############################################
|
###############################################
|
||||||
|
|
||||||
- name: service control web interface
|
- name: service control web interface
|
||||||
|
|||||||
@ -34,9 +34,10 @@
|
|||||||
audio_device: "hw:{{ sound_ID_0.stdout_lines[0] }},{{ sound_ID_1.stdout_lines[0] }}"
|
audio_device: "hw:{{ sound_ID_0.stdout_lines[0] }},{{ sound_ID_1.stdout_lines[0] }}"
|
||||||
|
|
||||||
# same with video, the lsusb ID is 534d:0021
|
# same with video, the lsusb ID is 534d:0021
|
||||||
|
# v4l2-ctl shows it as "AV TO USB2.0"
|
||||||
|
|
||||||
- name: video_capture - get video device
|
- name: video_capture - get video device
|
||||||
shell: "v4l2-ctl --list-devices -z {{ lsusb_device_ID }} | grep video | head -n 1 | awk '{print $1}'"
|
shell: "v4l2-ctl --list-devices 2> /dev/null | grep -A3 '{{ v4l2_id_string }}' | grep video | head -n 1 | awk '{print $1}'"
|
||||||
register: video_ID_0
|
register: video_ID_0
|
||||||
|
|
||||||
- name: video_capture - set video_device
|
- name: video_capture - set video_device
|
||||||
|
|||||||
254
tasks/working_folder.yaml
Normal file
254
tasks/working_folder.yaml
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
---
|
||||||
|
# this is for prepping the working folder, including mounting the storage if present and requested
|
||||||
|
|
||||||
|
# this folder needs to exist regardless of if there's storage to mount
|
||||||
|
- name: Working Folder - check folder
|
||||||
|
file:
|
||||||
|
path: "{{ working_storage }}"
|
||||||
|
state: directory
|
||||||
|
mode: '0755'
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
|
||||||
|
- name: Working Folder - Check for Storage
|
||||||
|
shell: "df"
|
||||||
|
register: df_check_output
|
||||||
|
|
||||||
|
- name: Working Folder - Set storage_unmounted
|
||||||
|
when: working_storage in df_check_output.stdout
|
||||||
|
set_fact:
|
||||||
|
storage_unmounted: false
|
||||||
|
|
||||||
|
- name: Storage Handler Block
|
||||||
|
when: storage_unmounted and mount_storge | bool
|
||||||
|
block:
|
||||||
|
|
||||||
|
# find the device, architecture dependent
|
||||||
|
# the arm64 is intended for the friendlyelec devices
|
||||||
|
# which only have one sd slot, but still be careful
|
||||||
|
- name: Storage Handler - Get Storage Device arm64
|
||||||
|
when: '"arm64" in cpu_architecture'
|
||||||
|
block:
|
||||||
|
|
||||||
|
- name: Storage Handler - get boot device
|
||||||
|
shell: "blkid | grep '\"boot\"' | cut -d: -f 1 | cut -d/ -f 3 | cut -dp -f 1"
|
||||||
|
register: boot_device_arm64
|
||||||
|
- debug:
|
||||||
|
msg: "Boot device: {{ boot_device_arm64.stdout_lines[0] }}"
|
||||||
|
|
||||||
|
# this will choose the first storage device that it finds that isn't the boot device
|
||||||
|
# be careful and don't run this on an inappropriate host
|
||||||
|
- name: Storage Handler - Get Storage Device amd64
|
||||||
|
when: '"amd64" in cpu_architecture'
|
||||||
|
block:
|
||||||
|
|
||||||
|
- name: Storage Handler - get boot UUID
|
||||||
|
shell: 'cat /etc/fstab | grep " / " | grep UUID | cut -d= -f2 | cut -d " " -f 1'
|
||||||
|
register: boot_uuid
|
||||||
|
- debug:
|
||||||
|
msg: "Boot device UUID: {{ boot_uuid.stdout_lines[0] }}"
|
||||||
|
|
||||||
|
- name: Storage Handler - get boot device
|
||||||
|
shell: "blkid | grep {{ boot_uuid.stdout_lines[0] }} | cut -d: -f 1 | cut -d/ -f 3 | cut -dp -f 1 | cut -b 1-3"
|
||||||
|
register: boot_device_amd64
|
||||||
|
- debug:
|
||||||
|
msg: "Boot device: {{ boot_device_amd64.stdout_lines[0] }}"
|
||||||
|
|
||||||
|
- name: set boot_device variable
|
||||||
|
set_fact:
|
||||||
|
boot_device: "{{ boot_device_arm64.stdout_lines[0] | default(boot_device_amd64.stdout_lines[0]) }}"
|
||||||
|
|
||||||
|
- name: Storage Handler - get storage device
|
||||||
|
shell: "lsblk -o NAME,SIZE --nodeps | grep -v -e loop -e {{ boot_device }} -e NAME | cut -d ' ' -f 1"
|
||||||
|
register: storage_device_output
|
||||||
|
|
||||||
|
- name: Storage Handler - set uuid variable
|
||||||
|
set_fact:
|
||||||
|
storage_device: "{{ storage_device_output.stdout_lines[0] }}"
|
||||||
|
- debug:
|
||||||
|
msg: "Storage Device Found: {{ storage_device }}"
|
||||||
|
|
||||||
|
- name: Storage Handler - check for volumes
|
||||||
|
block:
|
||||||
|
|
||||||
|
- name: Storage Handler - Check for volumes on storage device
|
||||||
|
shell: 'lsblk | grep {{ storage_device }} | grep -v disk || true'
|
||||||
|
register: volume_check
|
||||||
|
|
||||||
|
- name: Storage Handler - Set format_storage variable
|
||||||
|
when: not storage_device in volume_check.stdout
|
||||||
|
set_fact:
|
||||||
|
format_storage: true
|
||||||
|
|
||||||
|
- name: Storage Handler - Test if format
|
||||||
|
when: format_storage
|
||||||
|
debug:
|
||||||
|
msg: "Looks like it's clobberin' time: {{ storage_device }} will be wiped and prepped"
|
||||||
|
|
||||||
|
- name: Storage Handler - Test if no format
|
||||||
|
when: not format_storage | bool
|
||||||
|
debug:
|
||||||
|
msg:
|
||||||
|
- "Looks like there's at least one volume there, the storage is safe:"
|
||||||
|
- "{{ volume_check.stdout_lines }}"
|
||||||
|
|
||||||
|
# this little block will format the storage found above
|
||||||
|
# it will be skipped unless you do something about it
|
||||||
|
- name: Storage Handler - format storage
|
||||||
|
# when: false
|
||||||
|
when: format_storage | bool
|
||||||
|
block:
|
||||||
|
|
||||||
|
- name: Storage Handler - clear existing partition table
|
||||||
|
shell: 'sgdisk --zap-all "/dev/{{ storage_device }}"'
|
||||||
|
|
||||||
|
- name: Storage Handler - create new partition table
|
||||||
|
shell: 'sgdisk --new=0:0:0 "/dev/{{ storage_device }}"'
|
||||||
|
|
||||||
|
- name: Storage Handler - create new volume table
|
||||||
|
shell: 'sgdisk --typecode=1:8300 --change-name=1:"Linux filesystem" "/dev/{{ storage_device }}"'
|
||||||
|
|
||||||
|
- name: Storage Handler - find new volume
|
||||||
|
shell: 'blkid | grep {{ storage_device }} | cut -d: -f1'
|
||||||
|
register: new_sd_volume
|
||||||
|
|
||||||
|
- name: Storage Handler - format new volume
|
||||||
|
shell: 'mkfs.ext4 {{ new_sd_volume.stdout_lines[0] }}'
|
||||||
|
register: format_output
|
||||||
|
|
||||||
|
- name: Storage Handler - show format stdout
|
||||||
|
debug:
|
||||||
|
msg: "{{ format_output.stdout_lines }}"
|
||||||
|
|
||||||
|
- name: Storage Handler - find storage volume
|
||||||
|
block:
|
||||||
|
|
||||||
|
- name: Storage Handler - get storage uuid
|
||||||
|
shell: "blkid | grep {{ storage_device }} | awk '{for (i=1; i<=NF; i++) print $i}' | grep UUID | grep -v PART | cut -d '\"' -f 2"
|
||||||
|
register: storage_uuid_output
|
||||||
|
|
||||||
|
- name: Storage Handler - set uuid variable
|
||||||
|
set_fact:
|
||||||
|
storage_uuid: "{{ storage_uuid_output.stdout_lines[0] }}"
|
||||||
|
|
||||||
|
- name: Storage Handler - Generate fstab entry
|
||||||
|
set_fact:
|
||||||
|
fstab_line_storage: "UUID={{ storage_uuid }} {{ working_storage }} ext4 errors=remount-ro 0 1"
|
||||||
|
|
||||||
|
- debug:
|
||||||
|
msg:
|
||||||
|
- "UUID Found:"
|
||||||
|
- "{{ storage_uuid }}"
|
||||||
|
- "fstab entry:"
|
||||||
|
- "{{ fstab_line_storage }}"
|
||||||
|
|
||||||
|
- name: Storage Handler - mount storage
|
||||||
|
block:
|
||||||
|
|
||||||
|
- name: Storage Handler - add fstab entry
|
||||||
|
lineinfile:
|
||||||
|
path: "/etc/fstab"
|
||||||
|
search_string: "{{ storage_uuid }}"
|
||||||
|
line: "{{ fstab_line_storage }}"
|
||||||
|
|
||||||
|
- name: Storage Handler - daemon reload
|
||||||
|
systemd:
|
||||||
|
daemon_reload: yes
|
||||||
|
|
||||||
|
- name: Storage Handler - Mount it
|
||||||
|
shell: mount -a
|
||||||
|
|
||||||
|
- name: Storage Handler - check for new mount point
|
||||||
|
shell: "df -h | grep -e Size -e {{ working_storage }}"
|
||||||
|
register: storage_output
|
||||||
|
|
||||||
|
- debug:
|
||||||
|
msg:
|
||||||
|
- "Storage handler drive mount result:"
|
||||||
|
- "{{ storage_output.stdout_lines }}"
|
||||||
|
|
||||||
|
###############################################
|
||||||
|
# this shares out the working folder with samba
|
||||||
|
# and builds an apache index site
|
||||||
|
###############################################
|
||||||
|
- name: Working Folder - samba and index
|
||||||
|
block:
|
||||||
|
|
||||||
|
- name: Working Folder - Share with samba
|
||||||
|
block:
|
||||||
|
|
||||||
|
- name: Working Folder - Create smb config file
|
||||||
|
template:
|
||||||
|
src: vcr_rip.conf
|
||||||
|
dest: "/etc/samba/smb.conf.d/vcr_rip.conf"
|
||||||
|
mode: 0644
|
||||||
|
|
||||||
|
- name: Working Folder - Include config in smb.conf
|
||||||
|
lineinfile:
|
||||||
|
path: "/etc/samba/smb.conf"
|
||||||
|
search_string: "/etc/samba/smb.conf.d/vcr_rip.conf"
|
||||||
|
line: "include = /etc/samba/smb.conf.d/vcr_rip.conf"
|
||||||
|
insertbefore: '[global]'
|
||||||
|
|
||||||
|
- name: Working Folder - restart smbd
|
||||||
|
service:
|
||||||
|
name: smbd
|
||||||
|
state: restarted
|
||||||
|
enabled: yes
|
||||||
|
|
||||||
|
- name: Working Folder - Apache Index
|
||||||
|
block:
|
||||||
|
|
||||||
|
# Clone and build apache-index
|
||||||
|
- name: Working Folder - apache-index - Ensure the destination directory exists
|
||||||
|
file:
|
||||||
|
path: /opt/cosmos/apache-index # Replace with your desired directory on the target machine
|
||||||
|
state: directory
|
||||||
|
mode: '0755'
|
||||||
|
|
||||||
|
- name: Working Folder - apache-index - Clone the Git repository
|
||||||
|
git:
|
||||||
|
repo: 'https://gitea.matt-cloud.com/matt/apache-index.git' # Replace with your repository URL
|
||||||
|
dest: '/opt/cosmos/apache-index' # Replace with your desired directory on the target machine
|
||||||
|
version: 'main' # Replace with the branch, tag, or commit you want to clone
|
||||||
|
force: yes
|
||||||
|
|
||||||
|
- name: Working Folder - apache-index - Build image
|
||||||
|
docker_image:
|
||||||
|
name: apache-index # Name of the Storage Handler - image
|
||||||
|
source: build
|
||||||
|
build:
|
||||||
|
path: /opt/cosmos/apache-index # Path to the directory containing your Dockerfile
|
||||||
|
state: present
|
||||||
|
tag: latest
|
||||||
|
register: build_index_output
|
||||||
|
|
||||||
|
- name: Storage Handler - apache-index results
|
||||||
|
debug:
|
||||||
|
msg:
|
||||||
|
- "{{ build_index_output.changed }}"
|
||||||
|
- "{{ build_index_output.failed }}"
|
||||||
|
- "{{ build_index_output.image.RootFS.Layers }}"
|
||||||
|
|
||||||
|
# start local-index
|
||||||
|
- name: Working Folder - local-index - create folder
|
||||||
|
file:
|
||||||
|
path: "{{ streaming_working_folder }}/local-index"
|
||||||
|
state: directory
|
||||||
|
mode: '0755'
|
||||||
|
|
||||||
|
- name: Working Folder - local-index - Copy config
|
||||||
|
template:
|
||||||
|
src: docker-compose-local-index.yaml
|
||||||
|
dest: "{{ streaming_working_folder }}/local-index/docker-compose.yaml"
|
||||||
|
mode: 0644
|
||||||
|
|
||||||
|
- name: Working Folder - local-index - Start container at 172.17.0.1:8080
|
||||||
|
shell: "docker-compose -f {{ streaming_working_folder }}/local-index/docker-compose.yaml up -d"
|
||||||
|
register: local_index_output
|
||||||
|
- debug:
|
||||||
|
msg:
|
||||||
|
- "local-index docker compose output"
|
||||||
|
- "{{ local_index_output.stdout_lines }}"
|
||||||
|
- "{{ local_index_output.stderr_lines }}"
|
||||||
|
...
|
||||||
17
templates/docker-compose-jellyfin.yaml
Normal file
17
templates/docker-compose-jellyfin.yaml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
services:
|
||||||
|
jellyfin:
|
||||||
|
image: lscr.io/linuxserver/jellyfin:latest
|
||||||
|
container_name: jellyfin
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Etc/UTC
|
||||||
|
volumes:
|
||||||
|
- ./config:/config
|
||||||
|
- {{ recording_capture_folder }}:/data/recordings:ro
|
||||||
|
ports:
|
||||||
|
- {{ jellyfin_port }}:8096
|
||||||
|
restart: always
|
||||||
|
network_mode: bridge
|
||||||
|
...
|
||||||
@ -4,11 +4,12 @@ After=network.target
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
|
KillSignal=SIGINT
|
||||||
WorkingDirectory={{ streaming_working_folder }}
|
WorkingDirectory={{ streaming_working_folder }}
|
||||||
ExecStart={{ streaming_working_folder }}/stream_service.sh
|
ExecStart={{ streaming_working_folder }}/stream_service.sh
|
||||||
Restart=always
|
|
||||||
User=root
|
User=root
|
||||||
Group=root
|
Group=root
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
ffmpeg -hwaccel cuda -hwaccel_output_format cuda \
|
DATE=$(date +'%Y%m%d-%H%M%S')
|
||||||
-f alsa -ac 2 -i {{ audio_device }} -thread_queue_size 64 \
|
|
||||||
|
ffmpeg \
|
||||||
|
-f alsa -i {{ audio_device }} -thread_queue_size 64 \
|
||||||
-f v4l2 -framerate 30 -video_size 640x480 -input_format yuyv422 -i {{ video_device }} \
|
-f v4l2 -framerate 30 -video_size 640x480 -input_format yuyv422 -i {{ video_device }} \
|
||||||
-c:v libx264 -preset ultrafast -tune zerolatency \
|
-c:v libx264 -preset fast -crf 23 -c:a aac -b:a 192k -tune zerolatency \
|
||||||
-vf "format=yuv420p" -g 60 -c:a aac -b:a 128k -ar 44100 \
|
-f rtsp rtsp://localhost:8554/stream \
|
||||||
-f flv rtmp://0.0.0.0:1935/stream
|
-f mp4 {{ recording_capture_folder }}/$DATE.mp4
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
10
templates/vcr_rip.conf
Normal file
10
templates/vcr_rip.conf
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[vcr_rip]
|
||||||
|
path = {{ working_storage }}
|
||||||
|
writable = yes
|
||||||
|
read only = no
|
||||||
|
only guest = yes
|
||||||
|
public = yes
|
||||||
|
guest ok = yes
|
||||||
|
guest only = yes
|
||||||
|
guest account = nobody
|
||||||
|
browsable = yes
|
||||||
Reference in New Issue
Block a user