Enhancement to allow simultaneous RTSP stream recording
Updated birdnet_recording.sh With a functional change around how ffmpeg is used to make use if it's functionality to accept multiple input streams and map each to a output file. Due to the original implementation generating a filename that contain the date/time streams would be writing to the same file - a small change was make each stream go to it's one file with unique filenames. Updated livestream.sh & spectrogram.php The Livestream service would fail if the RTSP stream setting was more than 1 stream (i.e comma separated list of streams) Made some improvements so streaming is possible from any of the URL's, The Spectrogram page received a new control and some Javascript that allows the user to change between streams, the setting gets saved and the livestream service restarted so it uses's the correct stream. This control is not visible if there are no RTSP streams configured By default the livestream service will stream the the first RTSP URL, if the setting is not set or invalid Updated advanced.php With a small GUI change around RTSP steam entry by providing a input field for each URL and a button to add more fields. In the background nothing changes as we use some Javascript to extract the valus entered and fill in the original rtsp_stream value with the correct comma separated string Updated server.py To work with the recording filename changes so that the correct filename is stored in the DB Updated update_birdnet_snippets.sh To support a new setting RTSP_STREAM_TO_LIVESTREAM on the Spectrogram page and in the livestream service
This commit is contained in:
+100
-4
@@ -76,6 +76,22 @@ if(isset($_GET['submit'])) {
|
||||
exec('sudo systemctl restart livestream.service');
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET["rtsp_stream_to_livestream"])) {
|
||||
$rtsp_stream_selected = trim($_GET["rtsp_stream_to_livestream"]);
|
||||
|
||||
//Setting exists already, see if the value changed
|
||||
if (strcmp($rtsp_stream_selected, $config['RTSP_STREAM_TO_LIVESTREAM']) !== 0) {
|
||||
$contents = preg_replace("/RTSP_STREAM_TO_LIVESTREAM=.*/", "RTSP_STREAM_TO_LIVESTREAM=\"$rtsp_stream_selected\"", $contents);
|
||||
$contents2 = preg_replace("/RTSP_STREAM_TO_LIVESTREAM=.*/", "RTSP_STREAM_TO_LIVESTREAM=\"$rtsp_stream_selected\"", $contents2);
|
||||
$fh = fopen("/etc/birdnet/birdnet.conf", "w");
|
||||
$fh2 = fopen("./scripts/thisrun.txt", "w");
|
||||
fwrite($fh, $contents);
|
||||
fwrite($fh2, $contents2);
|
||||
sleep(1);
|
||||
exec("sudo systemctl restart livestream.service");
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($_GET["overlap"])) {
|
||||
$overlap = $_GET["overlap"];
|
||||
@@ -269,6 +285,58 @@ if (file_exists('./scripts/thisrun.txt')) {
|
||||
output.innerHTML = this.value;
|
||||
document.getElementById("predictionCount").innerHTML = parseInt((this.value * <?php echo $count; ?>)/100);
|
||||
}
|
||||
|
||||
//Keep track of how many new input fields were added
|
||||
var number_of_new_rtsp_urls_added = 0;
|
||||
//Function to insert new input fields
|
||||
function addNewrtspInput() {
|
||||
//Find the placeholder input field
|
||||
var url_template_element = document.getElementById('rtsp_stream_url_placeholder');
|
||||
var new_url_input_template = url_template_element.cloneNode();
|
||||
var br_seperator = document.createElement("BR");
|
||||
|
||||
//Fix up the new element so it's visible, set the style so it's sligned correctly
|
||||
new_url_input_template.setAttribute("id", "rtsp_stream_url_new_" + number_of_new_rtsp_urls_added);
|
||||
new_url_input_template.setAttribute("name", "rtsp_stream_new_" + number_of_new_rtsp_urls_added);
|
||||
new_url_input_template.setAttribute("style", "margin-left: 107px");
|
||||
|
||||
//Insert the new input field before the button to add new urls
|
||||
var newrtspstream_button = document.getElementById('newrtspstream_button_container');
|
||||
//Insert the new input element before the newrtspstream button
|
||||
newrtspstream_button.parentNode.insertBefore(new_url_input_template, newrtspstream_button);
|
||||
//Add a separator before the button
|
||||
newrtspstream_button.parentNode.insertBefore(br_seperator, newrtspstream_button);
|
||||
|
||||
//Increment the counter
|
||||
number_of_new_rtsp_urls_added++;
|
||||
}
|
||||
|
||||
var rtsp_stream_string = "";
|
||||
var rtsp_stream_string_array = [];
|
||||
//Collect all the rtsp urls that have been set, concat them into a single string and set it into the rtsp_stream input field so it gets saved
|
||||
function collectrtspUrls() {
|
||||
//Reset the array and string so we don't get duplicates
|
||||
rtsp_stream_string = "";
|
||||
rtsp_stream_string_array = [];
|
||||
|
||||
//Get the inputs by name (which is similar across
|
||||
var existing_rtsp_stream_urls = document.querySelectorAll('[name^="rtsp_stream_"]');
|
||||
//Loop over the result and get the values
|
||||
for (let i = 0; i < existing_rtsp_stream_urls.length; i++) {
|
||||
//Only collect results that re not empty and add them to the array
|
||||
if (existing_rtsp_stream_urls[i].value !== 'undefined' && existing_rtsp_stream_urls[i].value !== "") {
|
||||
rtsp_stream_string_array.push(existing_rtsp_stream_urls[i].value.trim());
|
||||
}
|
||||
}
|
||||
|
||||
//if the array is not empty, then implode the array joining all the values by a comma
|
||||
if (rtsp_stream_string_array.length !== 0) {
|
||||
rtsp_stream_string = rtsp_stream_string_array.join(',');
|
||||
//Locate the hidden rtsp_stream input field that we'll populate with the full string which will get saved to the config file
|
||||
var rtsp_stream_input = document.querySelector('[name=rtsp_stream]');
|
||||
rtsp_stream_input.setAttribute('value',rtsp_stream_string);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<p>If a Human is predicted anywhere among the top <span id="predictionCount"><?php echo $newconfig['PRIVACY_THRESHOLD'] == 0 ? "threshold % of" : intval(($newconfig['PRIVACY_THRESHOLD'] * $count)/100); ?></span> predictions, the sample will be considered of human origin and no data will be collected. Start with 1% and move up as needed.</p>
|
||||
<label>Full Disk Behavior: </label>
|
||||
@@ -283,9 +351,37 @@ if (file_exists('./scripts/thisrun.txt')) {
|
||||
<label for="channels">Audio Channels: </label>
|
||||
<input name="channels" type="number" min="1" max="32" step="1" value="<?php print($newconfig['CHANNELS']);?>" required/><br>
|
||||
<p>Set Channels to the number of channels supported by your sound card. 32 max.</p>
|
||||
<label for="rtsp_stream">RTSP Stream: </label>
|
||||
<input name="rtsp_stream" type="url" value="<?php echo $newconfig['RTSP_STREAM'];?>"</input><br>
|
||||
<p>If you place an RTSP stream URL here, BirdNET-Pi will use that as its audio source.</p>
|
||||
<label id="rtsp_stream_input_label" for="rtsp_stream">RTSP Stream: </label>
|
||||
<input style="display: none;" name="rtsp_stream" type="url" value="">
|
||||
<input style="display: none;" id="rtsp_stream_url_placeholder" name="rtsp_stream_placeholder" type="url" size="60" value="">
|
||||
<?php
|
||||
//Print out the rtsp urls in their own input fields
|
||||
//Explode the stream into an array at the comma
|
||||
$rtsp_streams = explode(",", $newconfig['RTSP_STREAM']);
|
||||
//Print out existing streams
|
||||
foreach ($rtsp_streams as $stream_idx => $stream_url) {
|
||||
//For the first input keep the element mostly the same as the original but without styling to align it
|
||||
if ($stream_idx === 0) {
|
||||
?>
|
||||
<input id="rtsp_stream_url_0" name="rtsp_stream_0" type="url" size="60" value="<?php echo $stream_url; ?>">
|
||||
<br>
|
||||
<?php
|
||||
} else {
|
||||
//For every other input field, change the id to reflect the URL's index in the array
|
||||
?>
|
||||
<input id="rtsp_stream_url_<?php echo $stream_idx; ?>" name="rtsp_stream_<?php echo $stream_idx; ?>" type="url" size="60"
|
||||
value="<?php echo $stream_url; ?>"
|
||||
style="margin-left: 107px">
|
||||
<br>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
?>
|
||||
<div id="newrtspstream_button_container">
|
||||
<br>
|
||||
<span id="newrtspstream" style="margin-left: 107px" onclick="addNewrtspInput();">add</span><br>
|
||||
</div>
|
||||
<p>If you place an RTSP stream URL here, BirdNET-Pi will use that as its audio source. Multiple streams are allowed but may have a impact on rPi performance. Analyze ffmpeg CPU/Memory usage with <b>top</b> or <b>htop</b> if necessary.</p>
|
||||
<label for="recording_length">Recording Length: </label>
|
||||
<input name="recording_length" oninput="document.getElementsByName('extraction_length')[0].setAttribute('max', this.value);" type="number" min="3" max="60" step="1" value="<?php print($newconfig['RECORDING_LENGTH']);?>" required/><br>
|
||||
<p>Set Recording Length in seconds between 6 and 60. Multiples of 3 are recommended, as BirdNET analyzes in 3-second chunks.</p>
|
||||
@@ -389,7 +485,7 @@ foreach($formats as $format){
|
||||
</p>
|
||||
<br><br>
|
||||
<input type="hidden" name="view" value="Advanced">
|
||||
<button onclick="if(<?php print($newconfig['PRIVACY_THRESHOLD']);?> != document.getElementById('privacy_threshold').value){return confirm('This will take about 90 seconds.')}" type="submit" name="submit" value="advanced">
|
||||
<button onclick="if(<?php print($newconfig['PRIVACY_THRESHOLD']);?> != document.getElementById('privacy_threshold').value){return confirm('This will take about 90 seconds.')} collectrtspUrls();" type="submit" name="submit" value="advanced">
|
||||
<?php
|
||||
if(isset($_GET['submit'])){
|
||||
echo "Success!";
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Performs the recording from the specified RSTP stream or soundcard
|
||||
set -x
|
||||
source /etc/birdnet/birdnet.conf
|
||||
|
||||
@@ -6,10 +7,35 @@ source /etc/birdnet/birdnet.conf
|
||||
|
||||
if [ ! -z $RTSP_STREAM ];then
|
||||
[ -d $RECS_DIR/StreamData ] || mkdir -p $RECS_DIR/StreamData
|
||||
# Explode the RSPT steam setting into an array so we can count the number we have
|
||||
RSTP_STREAMS_EXPLODED_ARRAY=(${RTSP_STREAM//,/ })
|
||||
|
||||
while true;do
|
||||
for i in ${RTSP_STREAM//,/ };do
|
||||
ffmpeg -nostdin -i ${i} -t ${RECORDING_LENGTH} -vn -acodec pcm_s16le -ac 2 -ar 48000 file:${RECS_DIR}/StreamData/$(date "+%F")-birdnet-$(date "+%H:%M:%S").wav
|
||||
# Original loop
|
||||
# for i in ${RTSP_STREAM//,/ };do
|
||||
# ffmpeg -nostdin -i ${i} -t ${RECORDING_LENGTH} -vn -acodec pcm_s16le -ac 2 -ar 48000 file:${RECS_DIR}/StreamData/$(date "+%F")-birdnet-$(date "+%H:%M:%S").wav
|
||||
# done
|
||||
|
||||
# Initially start the count off at 1 - our very first stream
|
||||
RSTP_STREAMS_STARTED_COUNT=1
|
||||
FFMPEG_PARAMS=""
|
||||
|
||||
# Loop over the streams
|
||||
for i in "${RSTP_STREAMS_EXPLODED_ARRAY[@]}"
|
||||
do
|
||||
# Map id used to map input to output, this is 0 based in ffmpeg decrement
|
||||
MAP_ID=$((RSTP_STREAMS_STARTED_COUNT-1))
|
||||
# Build up the parameters to process the RSTP stream, including mapping for the output
|
||||
FFMPEG_PARAMS+="-vn -thread_queue_size 512 -i ${i} -map ${MAP_ID} -t ${RECORDING_LENGTH} -acodec pcm_s16le -ac 2 -ar 48000 file:${RECS_DIR}/StreamData/$(date "+%F")-birdnet-RSTP_${RSTP_STREAMS_STARTED_COUNT}-$(date "+%H:%M:%S").wav "
|
||||
# Increment counter
|
||||
((RSTP_STREAMS_STARTED_COUNT += 1))
|
||||
done
|
||||
|
||||
# Make sure were passing something valid to ffmpeg, ffmpeg will run interactive and control our look by waiting ${RECORDING_LENGTH} between loops
|
||||
if [ -n "$FFMPEG_PARAMS" ];then
|
||||
ffmpeg -nostdin $FFMPEG_PARAMS
|
||||
fi
|
||||
|
||||
done
|
||||
else
|
||||
if ! pulseaudio --check;then pulseaudio --start;fi
|
||||
|
||||
+18
-1
@@ -5,7 +5,24 @@ source /etc/birdnet/birdnet.conf
|
||||
if [ -z ${REC_CARD} ];then
|
||||
echo "Stream not supported"
|
||||
elif [[ ! -z ${RTSP_STREAM} ]];then
|
||||
ffmpeg -nostdin -loglevel 32 -ac ${CHANNELS} -i ${RTSP_STREAM} -acodec libmp3lame \
|
||||
# Explode the RSPT steam setting into an array so we can count the number we have
|
||||
RSTP_STREAMS_EXPLODED_ARRAY=(${RTSP_STREAM//,/ })
|
||||
|
||||
# If for some reason the RTSP_STREAM_TO_LIVESTREAM is not set, then init it to 0 to use the first stream
|
||||
if [[ -z ${RTSP_STREAM_TO_LIVESTREAM} ]];then
|
||||
RTSP_STREAM_TO_LIVESTREAM=0
|
||||
fi
|
||||
|
||||
# Get the RSTP stream at the specified array index
|
||||
SELECTED_RSTP_STREAM=${RSTP_STREAMS_EXPLODED_ARRAY[RTSP_STREAM_TO_LIVESTREAM]}
|
||||
|
||||
# If for some reason the RTSP stream url is null
|
||||
if [[ -z ${SELECTED_RSTP_STREAM} ]];then
|
||||
# Try select the first stream
|
||||
SELECTED_RSTP_STREAM=${RSTP_STREAMS_EXPLODED_ARRAY[0]}
|
||||
fi
|
||||
|
||||
ffmpeg -nostdin -loglevel 32 -ac ${CHANNELS} -i ${SELECTED_RSTP_STREAM} -acodec libmp3lame \
|
||||
-b:a 320k -ac ${CHANNELS} -content_type 'audio/mpeg' \
|
||||
-f mp3 icecast://source:${ICE_PWD}@localhost:8000/stream -re
|
||||
else
|
||||
|
||||
+18
-1
@@ -1,3 +1,4 @@
|
||||
import re
|
||||
from pathlib import Path
|
||||
from tzlocal import get_localzone
|
||||
import datetime
|
||||
@@ -403,8 +404,24 @@ def handle_client(conn, addr):
|
||||
full_file_name = args.i
|
||||
# print('FULL FILENAME: -' + full_file_name + '-')
|
||||
file_name = Path(full_file_name).stem
|
||||
|
||||
# Get the RSTP stream identifier from the filename if it exists
|
||||
rstp_ident_for_fn = ""
|
||||
rstp_ident = re.search("RSTP_[0-9]+-", file_name)
|
||||
if rstp_ident is not None:
|
||||
rstp_ident_for_fn = rstp_ident.group()
|
||||
|
||||
# Find and remove the identifier for the RSTP stream url it was from that is added when more than one
|
||||
# RSTP stream is recorded simultaneously, in order to make the filenames unique as filenames are all
|
||||
# generated at the same time
|
||||
file_name = re.sub("RSTP_[0-9]+-", "", file_name)
|
||||
|
||||
# Now we can read the date and time as normal
|
||||
# First portion of the filename contaning the date in Y m d
|
||||
file_date = file_name.split('-birdnet-')[0]
|
||||
# Second portion of the filename containing the time in H:M:S
|
||||
file_time = file_name.split('-birdnet-')[1]
|
||||
# Join the date and time together to get a complete string representing when the audio was recorded
|
||||
date_time_str = file_date + ' ' + file_time
|
||||
date_time_obj = datetime.datetime.strptime(date_time_str, '%Y-%m-%d %H:%M:%S')
|
||||
# print('Date:', date_time_obj.date())
|
||||
@@ -463,7 +480,7 @@ def handle_client(conn, addr):
|
||||
Overlap = str(args.overlap)
|
||||
Com_Name = Com_Name.replace("'", "")
|
||||
File_Name = Com_Name.replace(" ", "_") + '-' + Confidence + '-' + \
|
||||
Date.replace("/", "-") + '-birdnet-' + Time + audiofmt
|
||||
Date.replace("/", "-") + '-birdnet-' + rstp_ident_for_fn + Time + audiofmt
|
||||
|
||||
# Connect to SQLite Database
|
||||
for attempt_number in range(3):
|
||||
|
||||
+110
-5
@@ -1,13 +1,14 @@
|
||||
<?php
|
||||
error_reporting(E_ERROR);
|
||||
ini_set('display_errors',1);
|
||||
if(isset($_GET['ajax_csv'])) {
|
||||
|
||||
if (file_exists('./scripts/thisrun.txt')) {
|
||||
$config = parse_ini_file('./scripts/thisrun.txt');
|
||||
$config = parse_ini_file('./scripts/thisrun.txt');
|
||||
} elseif (file_exists('./scripts/firstrun.ini')) {
|
||||
$config = parse_ini_file('./scripts/firstrun.ini');
|
||||
$config = parse_ini_file('./scripts/firstrun.ini');
|
||||
}
|
||||
|
||||
if(isset($_GET['ajax_csv'])) {
|
||||
$RECS_DIR = $config["RECS_DIR"];
|
||||
|
||||
$user = shell_exec("awk -F: '/1000/{print $1}' /etc/passwd");
|
||||
@@ -39,6 +40,25 @@ if (($handle = fopen($RECS_DIR."/".date('F-Y')."/".date('d-l')."/".$newest_file.
|
||||
}
|
||||
die();
|
||||
}
|
||||
|
||||
$RSTP_Stream_Config = array();
|
||||
|
||||
//Load the birdnet config so we can read the RSTP setting
|
||||
// Valid config data
|
||||
if (is_array($config) && array_key_exists('RTSP_STREAM',$config)) {
|
||||
if (is_null($config['RTSP_STREAM']) === false && $config['RTSP_STREAM'] !== "") {
|
||||
$RSTP_Stream_Config_Data = explode(",", $config['RTSP_STREAM']);
|
||||
|
||||
//Process the stream further
|
||||
//we need to able to ID it (just do this by position), get the hostname to show in the dropdown box
|
||||
foreach ($RSTP_Stream_Config_Data as $stream_idx => $stream_url) {
|
||||
//$stream_idx is the array position of the the RSP stream URL, idx of 0 is the first, 1 - second etc
|
||||
$rstp_stream_url = parse_url($stream_url);
|
||||
$RSTP_Stream_Config[$stream_idx] = $rstp_stream_url['host'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
<script>
|
||||
// CREDITS: https://codepen.io/jakealbaugh/pen/jvQweW
|
||||
@@ -340,6 +360,46 @@ h1 {
|
||||
<img id="spectrogramimage" style="width:100%;height:100%;display:none" src="/spectrogram.png?nocache=<?php echo $time;?>">
|
||||
|
||||
<div class="centered">
|
||||
<?php
|
||||
if (isset($RSTP_Stream_Config) && !empty($RSTP_Stream_Config)) {
|
||||
?>
|
||||
<div style="display:inline" id="rstp_streams">
|
||||
<label>RTSP Stream: </label>
|
||||
<select id="rtsp_stream_select" name="RTSP Streams">
|
||||
<?php
|
||||
//The setting representing which livestream to stream is more than the number of RTSP streams available
|
||||
//maybe the list of streams has been modified
|
||||
//This isn't the ideal for this, but needed a way to fix this setting without calling the advanced setting page
|
||||
if (array_key_exists($config['RTSP_STREAM_TO_LIVESTREAM'], $RSTP_Stream_Config) === false) {
|
||||
$contents = file_get_contents('/etc/birdnet/birdnet.conf');
|
||||
$contents2 = file_get_contents('./scripts/thisrun.txt');
|
||||
$contents = preg_replace("/RTSP_STREAM_TO_LIVESTREAM=.*/", "RTSP_STREAM_TO_LIVESTREAM=\"0\"", $contents);
|
||||
$contents2 = preg_replace("/RTSP_STREAM_TO_LIVESTREAM=.*/", "RTSP_STREAM_TO_LIVESTREAM=\"0\"", $contents2);
|
||||
$fh = fopen("/etc/birdnet/birdnet.conf", "w");
|
||||
$fh2 = fopen("./scripts/thisrun.txt", "w");
|
||||
fwrite($fh, $contents);
|
||||
fwrite($fh2, $contents2);
|
||||
exec("sudo systemctl restart livestream.service");
|
||||
}
|
||||
|
||||
//Print out the dropdown list for the RTSP streams
|
||||
foreach ($RSTP_Stream_Config as $stream_id => $stream_host) {
|
||||
$isSelected = "";
|
||||
//Match up the selected value saved in config so we can preselect it
|
||||
if ($config['RTSP_STREAM_TO_LIVESTREAM'] == $stream_id) {
|
||||
$isSelected = 'selected="selected"';
|
||||
}
|
||||
//Create the select option
|
||||
echo "<option value=" . $stream_id . " $isSelected >" . $stream_host . "</option>";
|
||||
}
|
||||
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
—
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<div style="display:inline" id="gain" >
|
||||
<label>Gain: </label>
|
||||
<span class="slidecontainer">
|
||||
@@ -347,7 +407,7 @@ h1 {
|
||||
<span id="gain_value"></span>%
|
||||
</span>
|
||||
</div>
|
||||
—
|
||||
—
|
||||
<div style="display:inline" id="comp" >
|
||||
<label>Compression: </label>
|
||||
<input name="compression" type="checkbox" id="compression" disabled>
|
||||
@@ -355,10 +415,55 @@ h1 {
|
||||
</div>
|
||||
|
||||
<audio style="display:none" controls="" crossorigin="anonymous" id='player' preload="none"><source id="playersrc" src="/stream"></audio>
|
||||
<h1>Loading...</h1>
|
||||
<h1 id="loading-h1">Loading...</h1>
|
||||
<canvas></canvas>
|
||||
|
||||
<script>
|
||||
var rtsp_stream_select = document.getElementById("rtsp_stream_select");
|
||||
//When the dropdown selection is changed set the new value is settings, then restart the livestream service so it broadcasts newly selected RSTP stream
|
||||
rtsp_stream_select.onchange = function() {
|
||||
if (this.value !== 'undefined'){
|
||||
// Get the audio player element
|
||||
var audio_player = document.querySelector('audio#player');
|
||||
var central_controls_element = document.getElementsByClassName('centered')[0];
|
||||
|
||||
//Create the loading header again as a placeholder while we're waiting to reload the stream
|
||||
var h1_loading = document.createElement("H1");
|
||||
var h1_loading_text = document.createTextNode("Loading...");
|
||||
h1_loading.setAttribute("id","loading-h1");
|
||||
h1_loading.setAttribute("style","font-size:48px; font-weight: bolder; color: #FFF");
|
||||
h1_loading.appendChild(h1_loading_text);
|
||||
|
||||
// Create the XMLHttpRequest object.
|
||||
const xhr = new XMLHttpRequest();
|
||||
// Initialize the request
|
||||
xhr.open("GET", './views.php?rtsp_stream_to_livestream='+ this.value +'&view=Advanced&submit=advanced');
|
||||
// Send the request
|
||||
xhr.send();
|
||||
// Fired once the request completes successfully
|
||||
xhr.onload = function(e) {
|
||||
// Check if the request was a success
|
||||
if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
|
||||
// Restart the audio player in case it stopped working while the livestream service was restarted
|
||||
if(audio_player !== 'undefined'){
|
||||
central_controls_element.appendChild(h1_loading);
|
||||
//Wait 5 seconds before restarting the stream
|
||||
setTimeout(function () {
|
||||
audio_player.pause();
|
||||
audio_player.setAttribute('src', '/stream');
|
||||
audio_player.load();
|
||||
audio_player.play();
|
||||
|
||||
document.getElementById('loading-h1').remove()
|
||||
},
|
||||
10000
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var slider = document.getElementById("gain_input");
|
||||
var output = document.getElementById("gain_value");
|
||||
output.innerHTML = slider.value; // Display the default slider value
|
||||
|
||||
@@ -188,6 +188,10 @@ if ! grep APPRISE_ONLY_NOTIFY_SPECIES_NAMES /etc/birdnet/birdnet.conf &>/dev/nul
|
||||
sudo -u$USER echo "APPRISE_ONLY_NOTIFY_SPECIES_NAMES=\"\"" >> /etc/birdnet/birdnet.conf
|
||||
fi
|
||||
|
||||
if ! grep RTSP_STREAM_TO_LIVESTREAM /etc/birdnet/birdnet.conf &>/dev/null;then
|
||||
sudo -u$USER echo "RTSP_STREAM_TO_LIVESTREAM=\"0\"" >> /etc/birdnet/birdnet.conf
|
||||
fi
|
||||
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
restart_services.sh
|
||||
|
||||
Reference in New Issue
Block a user