test candidate for full operation

This commit is contained in:
2025-08-24 21:57:15 -07:00
parent cc24088bbe
commit 0a5a7cc32c
9 changed files with 113 additions and 107 deletions

View File

@ -1,13 +1,17 @@
I need one more component here, the service manager service site This is an ansible role intended to be ran as a supplement to my server-base playbook upon request
I'll use what I built for the carputer, and add to it Some considerations of this are the expectations of docker and samba already being installed and configured
Things to add:
- Duration selection grid This ansible task automatically builds out a matt-cloud debian puck to be a VCR Ripping device
- Duration Submission Selection API There is a hardware requirement of USB RCA Capture device, and this device must have its settings configured in defauts/main.yaml
- Cron job + helper script
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:
- ffmpeg service to combine the video and audio of the USB capture device into a rtmp stream
- mediamtx to automatically save rtmp streams to a mp4 file
- a docker apache php container for the control site
- a python API service to control the ffmpeg streaming service
- 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
I will update the API to maintain the selection state and return it from the API
I will write a selection grid that updates color depending on selction
It will also be a styled radio button states 0-3 for 6ht, 2hr, 1hr, and 30min
I will write a helper cron script that runs every second that will stop the service when the API says so
The API will have a function that will return the expected state of the service
When the helper script sees it not match, it will stop the service

View File

@ -16,7 +16,7 @@ video_device: "/dev/video0"
audio_device: "hw:0,0" audio_device: "hw:0,0"
mediamtx_version: "amd64" mediamtx_architecture: "amd64"
lsusb_device_ID: "534d:0021" lsusb_device_ID: "534d:0021"
@ -28,6 +28,8 @@ service_name: "VCR Streamer"
container_name: "service_control" container_name: "service_control"
mediamtx_local_version: "deadbeef_test"
mount_sd: true mount_sd: true
format_sd: false format_sd: false
@ -39,6 +41,7 @@ arm_arch: false
refresh_special: true refresh_special: true
streamer_packages: streamer_packages:
- gdisk
- ffmpeg - ffmpeg
- alsa-utils - alsa-utils
- alsa-oss - alsa-oss

View File

@ -272,7 +272,7 @@ function set_duration($duration) {
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API Data Display</title> <title>VCR Rip Controller</title>
<link rel="stylesheet" href="styles.css"> <link rel="stylesheet" href="styles.css">
</head> </head>

View File

@ -1,8 +1,5 @@
--- ---
# - name: Video Capture - Configure Owncast
# include_tasks: owncast.yaml
- name: Video Capture - Check arch if needed - name: Video Capture - Check arch if needed
when: refresh_special | bool when: refresh_special | bool
block: block:
@ -15,25 +12,26 @@
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 - Install Packages
# when: not refresh_special | bool when: not refresh_special | bool
# apt: apt:
# name: name:
# - "{{ streamer_packages_items }}" - "{{ streamer_packages_items }}"
# state: present state: present
# loop: "{{ streamer_packages }}" loop: "{{ streamer_packages }}"
# loop_control: loop_control:
# loop_var: streamer_packages_items loop_var: streamer_packages_items
#
#- name: Video Capture - SD Card Handler - name: Video Capture - SD Card Handler
# when: mount_sd | bool when: mount_sd | bool
# include_tasks: sd_handler.yaml include_tasks: sd_handler.yaml
#
#- name: Video Capture - Configure MediaMTX - name: Video Capture - Configure MediaMTX
# include_tasks: mediamtx.yaml when: not refresh_special | bool
# include_tasks: mediamtx.yaml
#- name: Video Capture - Configure Streaming
# include_tasks: streamer.yaml - name: Video Capture - Configure Streaming
include_tasks: streamer.yaml
- name: Video Capture - Configure service control - name: Video Capture - Configure service control
include_tasks: service_control.yaml include_tasks: service_control.yaml

View File

@ -1,10 +1,6 @@
--- ---
# mediamtx might automatically make video files # mediamtx automatically makes video files from rtmp streams
# newest release URL:
# curl -sL https://api.github.com/repos/bluenviron/mediamtx/releases/latest | \
# grep browser_download_url | grep linux_amd64 | cut -d\" -f 4
- name: MediaMTX - stop mediamtx_service if running - name: MediaMTX - stop mediamtx_service if running
systemd: systemd:
@ -24,22 +20,21 @@
- name: MediaMTX - check for arm - name: MediaMTX - check for arm
when: '"arm" in cpu_architecture' when: '"arm" in cpu_architecture'
set_fact: set_fact:
mediamtx_version: "arm64" mediamtx_architecture: "arm64"
- name: MediaMTX - get current release URL - name: MediaMTX - get current release URL
shell: | shell: |
curl -sL https://api.github.com/repos/bluenviron/mediamtx/releases/latest | \ curl -sL https://api.github.com/repos/bluenviron/mediamtx/releases/latest | \
grep browser_download_url | grep linux_{{ mediamtx_version }} | cut -d\" -f 4 grep browser_download_url | grep linux_{{ mediamtx_architecture }} | cut -d\" -f 4
register: mediamtx_latest_url register: mediamtx_latest_url
- debug: - debug:
msg: "URL To Download: {{ mediamtx_latest_url.stdout_lines[0] }}" msg: "Current Release URL: {{ mediamtx_latest_url.stdout_lines[0] }}"
- name: MediaMTX - get current release archive - name: MediaMTX - get current release archive
shell: "curl -s -o {{ mediamtx_working_folder }}/mediamtx.tar.gz -L {{ mediamtx_latest_url.stdout_lines[0] }}" shell: "curl -s -o {{ mediamtx_working_folder }}/mediamtx.tar.gz -L {{ mediamtx_latest_url.stdout_lines[0] }}"
- name: MediaMTX - extract archive - name: MediaMTX - extract archive
unarchive: unarchive:
# src: "/var/jenkins_home/ansible-files/programs/mediamtx_v1.14.0_linux_amd64.tar.gz"
src: "{{ mediamtx_working_folder }}/mediamtx.tar.gz" src: "{{ mediamtx_working_folder }}/mediamtx.tar.gz"
dest: "{{ mediamtx_working_folder }}" dest: "{{ mediamtx_working_folder }}"
mode: '0755' mode: '0755'
@ -53,25 +48,17 @@
loop: "{{ mediamtx_configs }}" loop: "{{ mediamtx_configs }}"
loop_control: loop_control:
loop_var: mediamtx_configs_item loop_var: mediamtx_configs_item
# - name: MediaMTX - config - enable recording
# lineinfile:
# path: "{{ mediamtx_working_folder }}/mediamtx.yml"
# search_string: 'record'
# line: ' record: yes'
#
- name: MediaMTX - create service file - name: MediaMTX - create service file
template: template:
src: mediamtx_service.service.j2 src: mediamtx_service.service.j2
dest: "/etc/systemd/system/mediamtx_service.service" dest: "/etc/systemd/system/mediamtx_service.service"
mode: 0644 mode: 0644
# daemon reload
- name: MediaMTX - daemon reload - name: MediaMTX - daemon reload
systemd: systemd:
daemon_reload: yes daemon_reload: yes
# Enable and start
- name: MediaMTX - enable and start mediamtx_service - name: MediaMTX - enable and start mediamtx_service
systemd: systemd:
name: mediamtx_service.service name: mediamtx_service.service

View File

@ -1,7 +1,7 @@
--- ---
# when ran, this will look for an SD card # when ran, this will look for an SD card
# optionally format it # optionally format it, be careful with that, it WILL destroy things
# map it to working_storage # map it to working_storage
# can only run on arm64 # can only run on arm64
# '"arm64" in cpu_architecture' # '"arm64" in cpu_architecture'
@ -20,20 +20,13 @@
when: '"arm64" in cpu_architecture' when: '"arm64" in cpu_architecture'
set_fact: set_fact:
arm_arch: true arm_arch: true
format_sd: false
- name: SD Handler - Checks have passed - name: SD Handler - Checks have passed
when: sd_unmounted and arm_arch | bool when: sd_unmounted and arm_arch | bool
block: block:
- name: SD Handler - format SD card - name: SD Handler - Get SD Device
when: format_sd | bool
block:
- name: dummy task
debug:
msg: "nothing to see here, move along"
- name: SD Handler - find SD card
block: block:
- name: SD Handler - get boot device - name: SD Handler - get boot device
@ -44,6 +37,34 @@
shell: "lsblk -o NAME,SIZE --nodeps | grep -v -e loop -e {{ boot_device.stdout_lines[0] }} -e NAME | cut -d ' ' -f 1" 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 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 - 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" 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 register: sd_card_uuid_output
@ -98,5 +119,37 @@
- debug: - debug:
msg: "{{ sd_test_output.stdout_lines }}" msg: "{{ sd_test_output.stdout_lines }}"
- 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/smb.conf"
search_string: "/etc/smb/smb.conf.d/vcr_rip.conf"
line: "/etc/smb/smb.conf.d/vcr_rip.conf"
insertafter: 'map to guest'
- name: SD Handler - restart smbd
service:
name: smbd
state: restarted
enabled: yes
... ...

View File

@ -1,7 +1,8 @@
--- ---
############################################### ###############################################
# lifted directly from my carputer playbook # lifted from my carputer playbook
# modified to work here
############################################### ###############################################
############################################### ###############################################
@ -122,17 +123,9 @@
owner: root owner: root
group: root group: root
#- name: service_control_website - template index.php
# template:
# src: index-service_control.php.j2
# dest: "{{ service_control_web_folder }}/html/index.php"
# mode: 0644
############################################### ###############################################
# Start service_control_website # Start service_control_website
############################################### ###############################################
# https://unix.stackexchange.com/questions/265704/start-stop-a-systemd-service-at-specific-times
# i can create several conflicting services with various timeouts
- name: start service_control_website - name: start service_control_website
when: not refresh_special | bool when: not refresh_special | bool
@ -157,6 +150,4 @@
msg="{{ docker_output.stdout_lines }}" msg="{{ docker_output.stdout_lines }}"
msg="{{ docker_output.stderr_lines }}" msg="{{ docker_output.stderr_lines }}"
... ...

View File

@ -1,10 +1,9 @@
--- ---
# video stream with ustreamer
# audio stream probably just a device
# looks like ffmpeg can do it # looks like ffmpeg can do it
# Create service Folder # Create service Folder
- name: video_capture - streamer - create streaming_working_folder folder - name: "video_capture - streamer - create {{ streaming_working_folder }} folder"
file: file:
path: "{{ streaming_working_folder }}" path: "{{ streaming_working_folder }}"
state: directory state: directory
@ -62,7 +61,6 @@
dest: /etc/systemd/system/stream_service.service dest: /etc/systemd/system/stream_service.service
mode: 0644 mode: 0644
# daemon reload
- name: video_capture - streamer - daemon reload - name: video_capture - streamer - daemon reload
systemd: systemd:
daemon_reload: yes daemon_reload: yes

View File

@ -13,16 +13,13 @@ def start_service():
try: try:
# Run the command using subprocess.run() # Run the command using subprocess.run()
process = subprocess.Popen(command, shell=True) process = subprocess.Popen(command, shell=True)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
return {"Error": e.strip('\n')} return {"Error": e.strip('\n')}
command = "systemctl status {{ service_control_name }} | grep Active | cut -d ':' -f 2- | cut -b 2-" command = "systemctl status {{ service_control_name }} | grep Active | cut -d ':' -f 2- | cut -b 2-"
try: try:
# Run the command using subprocess.run() # Run the command using subprocess.run()
result = subprocess.run(command, shell=True, check=True, capture_output=True, text=True) result = subprocess.run(command, shell=True, check=True, capture_output=True, text=True)
return {"Message": result.stdout.strip('\n')} return {"Message": result.stdout.strip('\n')}
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
return {"Error": e.strip('\n')} return {"Error": e.strip('\n')}
@ -31,7 +28,6 @@ def stop_service():
try: try:
# Run the command using subprocess.run() # Run the command using subprocess.run()
process = subprocess.Popen(command, shell=True) process = subprocess.Popen(command, shell=True)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
return {"Error": e.strip('\n')} return {"Error": e.strip('\n')}
@ -52,7 +48,6 @@ def service_status():
status = subprocess.run(command, shell=True, check=True, capture_output=True, text=True) status = subprocess.run(command, shell=True, check=True, capture_output=True, text=True)
if status.stdout.strip('\n') == "active": if status.stdout.strip('\n') == "active":
elapsed_time = check_time_elapsed(result.stdout.strip('\n')) elapsed_time = check_time_elapsed(result.stdout.strip('\n'))
times_up(elapsed_time)
else: else:
elapsed_time = -1 elapsed_time = -1
return {"Elapsed_Time": elapsed_time, "Service Name": "{{ service_control_name }}", "Message": result.stdout.strip('\n'), "Status": status.stdout.strip('\n')} return {"Elapsed_Time": elapsed_time, "Service Name": "{{ service_control_name }}", "Message": result.stdout.strip('\n'), "Status": status.stdout.strip('\n')}
@ -71,7 +66,6 @@ def save_duration(new_duration):
seconds = 7200 seconds = 7200
if new_duration == "3": if new_duration == "3":
seconds = 21600 seconds = 21600
info = { info = {
"duration": new_duration, "duration": new_duration,
"seconds": seconds "seconds": seconds
@ -91,40 +85,18 @@ def get_duration():
def check_time_elapsed(time_str): def check_time_elapsed(time_str):
# Extract the time part from the string # Extract the time part from the string
datetime_extract = time_str.split("since ")[1].split(";")[0] datetime_extract = time_str.split("since ")[1].split(";")[0]
try: try:
time_format = "%a %Y-%m-%d %H:%M:%S %Z" time_format = "%a %Y-%m-%d %H:%M:%S %Z"
given_time = datetime.strptime(datetime_extract, time_format) given_time = datetime.strptime(datetime_extract, time_format)
except ValueError as e: except ValueError as e:
raise ValueError(f"Error parsing the time string: {datetime_extract}" + str(e)) raise ValueError(f"Error parsing the time string: {datetime_extract}" + str(e))
# Get the current time # Get the current time
now = datetime.now() now = datetime.now()
# Calculate the difference in seconds # Calculate the difference in seconds
delta = now - given_time delta = now - given_time
total_seconds = int(delta.total_seconds()) total_seconds = int(delta.total_seconds())
return total_seconds return total_seconds
def times_up(time_elapsed):
duration_json = get_duration()
stored_duration = duration_json["duration"]
if stored_duration == 0:
if time_elapsed > 1800:
stop_service()
elif stored_duration == 1:
if time_elapsed > 3600:
stop_service()
elif stored_duration == 2:
if time_elapsed > 7200:
stop_service()
elif stored_duration == 3:
if time_elapsed > 21600:
stop_service()
@app.route('/start', methods=['GET']) @app.route('/start', methods=['GET'])
def start(): def start():
try: try: