use symlinks

This commit is contained in:
Alexandre
2025-03-21 07:39:30 +01:00
parent c941a85210
commit 57798c92b5
19 changed files with 850 additions and 465 deletions

View File

@@ -1,6 +1,16 @@
#!/usr/bin/with-contenv bashio
# shellcheck shell=bash
# Maximum file size in bytes (50MB)
MAX_SIZE=$((50 * 1024 * 1024))
# Function to check if a file is a valid WAV
is_valid_wav() {
local file="$1"
# Check if the file contains a valid WAV header
file "$file" | grep -qE 'WAVE audio'
}
if [ -d "$HOME"/BirdSongs/StreamData ]; then
bashio::log.fatal "Container stopping, saving temporary files."
@@ -18,16 +28,22 @@ if [ -d "$HOME"/BirdSongs/StreamData ]; then
# Wait for both services to stop
wait
# Check if there are files in StreamData and move them to /data/StreamData
# Create the destination directory
mkdir -p /data/StreamData
if [ "$(ls -A "$HOME"/BirdSongs/StreamData)" ]; then
if mv -v "$HOME"/BirdSongs/StreamData/* /data/StreamData/; then
bashio::log.info "Files successfully moved to /data/StreamData."
# Move only valid WAV files under 50MB
shopt -s nullglob # Prevent errors if no files match
for file in "$HOME"/BirdSongs/StreamData/*.wav; do
if [ -f "$file" ] && [ "$(stat --format="%s" "$file")" -lt "$MAX_SIZE" ] && is_valid_wav "$file"; then
if mv -v "$file" /data/StreamData/; then
bashio::log.info "Moved valid WAV file: $(basename "$file")"
else
bashio::log.error "Failed to move: $(basename "$file")"
fi
else
bashio::log.error "Failed to move files to /data/StreamData."
exit 1
bashio::log.warning "Skipping invalid or large file: $(basename "$file")"
fi
fi
done
bashio::log.info "... files safe, allowing container to stop."
else

View File

@@ -2,6 +2,17 @@
# shellcheck shell=bash
set -e
##################
# ALLOW RESTARTS #
##################
if [[ "${BASH_SOURCE[0]}" == /etc/cont-init.d/* ]]; then
mkdir -p /etc/scripts-init
sed -i "s|/etc/cont-init.d|/etc/scripts-init|g" /ha_entrypoint.sh
sed -i "/ rm/d" /ha_entrypoint.sh
cp "${BASH_SOURCE[0]}" /etc/scripts-init/
fi
###############
# SET /CONFIG #
###############
@@ -18,8 +29,6 @@ for file in "${DEFAULT_FILES[@]}"; do
done
touch /config/include_species_list.txt # Ensure this is always created
touch "$HOME/BirdNET-Pi/scripts/common.php"
# Set BirdSongs folder location from configuration if specified
BIRDSONGS_FOLDER="/config/BirdSongs"
if bashio::config.has_value "BIRDSONGS_FOLDER"; then
@@ -53,6 +62,14 @@ echo "... setting permissions for user pi"
chown -R pi:pi /config /etc/birdnet "$BIRDSONGS_FOLDER" /tmp
chmod -R 755 /config /etc/birdnet "$BIRDSONGS_FOLDER" /tmp
# Backup default birdnet.conf for sanity check
cp "$HOME/BirdNET-Pi/birdnet.conf" "$HOME/BirdNET-Pi/birdnet.bak"
# Create default birdnet.conf if not existing
if [ ! -f /config/birdnet.conf ]; then
cp -f "$HOME/BirdNET-Pi/birdnet.conf" /config/
fi
# Create default birds.db
if [ ! -f /config/birds.db ]; then
echo "... creating initial db"
@@ -65,12 +82,6 @@ elif [ "$(stat -c%s /config/birds.db)" -lt 10240 ]; then
cp "$HOME/BirdNET-Pi/scripts/birds.db" /config/
fi
# Backup default birdnet.conf for sanity check
if [ ! -f "$HOME/BirdNET-Pi/birdnet.conf" ]; then
ln -s /etc/birdnet/birdnet.conf "$HOME/BirdNET-Pi/birdnet.conf"
fi
cp "$HOME/BirdNET-Pi/birdnet.conf" "$HOME/BirdNET-Pi/birdnet.bak"
# Symlink configuration files
echo "... creating symlinks for configuration files"
CONFIG_FILES=("$HOME/BirdNET-Pi/birdnet.conf" "$HOME/BirdNET-Pi/scripts/whitelist_species_list.txt" "$HOME/BirdNET-Pi/blacklisted_images.txt" "$HOME/BirdNET-Pi/scripts/birds.db" "$HOME/BirdNET-Pi/BirdDB.txt" "$HOME/BirdNET-Pi/scripts/disk_check_exclude.txt" "$HOME/BirdNET-Pi/apprise.txt" "$HOME/BirdNET-Pi/exclude_species_list.txt" "$HOME/BirdNET-Pi/include_species_list.txt" "$HOME/BirdNET-Pi/IdentifiedSoFar.txt" "$HOME/BirdNET-Pi/scripts/confirmed_species_list.txt")
@@ -84,12 +95,6 @@ for file in "${CONFIG_FILES[@]}"; do
sudo -u pi ln -fs "/config/$filename" "/etc/birdnet/$filename"
done
# thisrun
cp /config/birdnet.conf "$HOME/BirdNET-Pi/scripts/thisrun.txt"
cp /config/birdnet.conf "$HOME/BirdNET-Pi/scripts/lastrun.txt"
chown pi:pi "$HOME/BirdNET-Pi/scripts/thisrun.txt"
chown pi:pi "$HOME/BirdNET-Pi/scripts/lastrun.txt"
# Symlink BirdSongs folders
for folder in By_Date Charts; do
echo "... creating symlink for $BIRDSONGS_FOLDER/$folder"
@@ -102,9 +107,9 @@ echo "... checking and setting permissions"
chmod -R 755 /config/*
chmod 777 /config
# Create Matplotlib configuration directory
# Create folder for matplotlib
echo "... setting up Matplotlabdir"
MPLCONFIGDIR="${MPLCONFIGDIR:-$HOME/.config/matplotlib}"
mkdir -p "$MPLCONFIGDIR"
chown pi:pi "$MPLCONFIGDIR"
chmod 777 "$MPLCONFIGDIR"
mkdir -p "$HOME"/.cache/matplotlib
chown -R "pi:pi" "$HOME"/.cache/matplotlib
chmod 777 "$HOME"/.cache/matplotlib

View File

@@ -2,22 +2,38 @@
# shellcheck shell=bash
set -e
if [ -d /data/StreamData ]; then
bashio::log.fatal "Container was stopped while files were still being analyzed."
##################
# ALLOW RESTARTS #
##################
# Check if there are .wav files in /data/StreamData
if find /data/StreamData -type f -name "*.wav" | grep -q .; then
bashio::log.warning "Restoring .wav files from /data/StreamData to $HOME/BirdSongs/StreamData."
if [[ "${BASH_SOURCE[0]}" == /etc/cont-init.d/* ]]; then
mkdir -p /etc/scripts-init
sed -i "s|/etc/cont-init.d|/etc/scripts-init|g" /ha_entrypoint.sh
sed -i "/ rm/d" /ha_entrypoint.sh
cp "${BASH_SOURCE[0]}" /etc/scripts-init/
fi
######################
# RESTORE STREAMDATA #
######################
if [ -d /config/TemporaryFiles ]; then
# Check if there are .wav files in /config/TemporaryFiles
if find /config/TemporaryFiles -type f -name "*.wav" | grep -q .; then
bashio::log.warning "Container was stopped while files were still being analyzed."
echo "... restoring .wav files from /config/TemporaryFiles to $HOME/BirdSongs/StreamData."
# Create the destination directory if it does not exist
mkdir -p "$HOME"/BirdSongs/StreamData
# Count the number of .wav files to be moved
file_count=$(find /data/StreamData -type f -name "*.wav" | wc -l)
file_count=$(find /config/TemporaryFiles -type f -name "*.wav" | wc -l)
echo "... found $file_count .wav files to restore."
# Move the .wav files using `mv` to avoid double log entries
mv -v /data/StreamData/*.wav "$HOME"/BirdSongs/StreamData/
mv -v /config/TemporaryFiles/*.wav "$HOME"/BirdSongs/StreamData/
rm -r /config/TemporaryFiles
# Update permissions only if files were moved successfully
if [ "$file_count" -gt 0 ]; then
@@ -30,7 +46,7 @@ if [ -d /data/StreamData ]; then
fi
# Clean up the source folder if it is empty
if [ -z "$(ls -A /data/StreamData)" ]; then
rm -r /data/StreamData
if [ -z "$(ls -A /config/TemporaryFiles)" ]; then
rm -r /config/TemporaryFiles
fi
fi

View File

@@ -2,6 +2,17 @@
# shellcheck shell=bash
set -e
##################
# ALLOW RESTARTS #
##################
if [[ "${BASH_SOURCE[0]}" == /etc/cont-init.d/* ]]; then
mkdir -p /etc/scripts-init
sed -i "s|/etc/cont-init.d|/etc/scripts-init|g" /ha_entrypoint.sh
sed -i "/ rm/d" /ha_entrypoint.sh
cp "${BASH_SOURCE[0]}" /etc/scripts-init/
fi
######################
# CHECK BIRDNET.CONF #
######################
@@ -48,17 +59,17 @@ fi
bashio::log.info "Performing potential updates"
# Adapt update_birdnet_snippets
sed -i "/USER=/a USER=\"$USER\"" "$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh
sed -i "/HOME=/a HOME=\"$HOME\"" "$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh
sed -i "/chown/d" "$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh
sed -i "/chmod/d" "$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh
sed -i "s|systemctl list-unit-files|false \&\& echo|g" "$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh # Avoid systemctl
sed -i "/systemctl /d" "$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh # Avoid systemctl
sed -i "/install_tmp_mount/d" "$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh # Use HA tmp
sed -i "/find /d" "$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh # Not useful
sed -i "/set -x/d" "$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh # Not useful
sed -i "/restart_services/d" "$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh # Not useful
sed -i "s|/etc/birdnet/birdnet.conf|/config/birdnet.conf|g" "$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh
sed -i "/restart_services/d" "$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh
sed -i "/set -x/d" "$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh
sed -i "s|systemctl list-unit-files|false \&\& echo|g" "$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh
sed -i "/systemctl /d" "$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh
sed -i "/find /d" "$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh
sed -i "/update_caddyfile/c echo \"yes\"" "$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh # Avoid systemctl
# Execute update_birdnet_snippets
export RECS_DIR="$HOME/BirdSongs"
export EXTRACTED="$HOME/BirdSongs/Extracted"
chmod +x "$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh
"$HOME"/BirdNET-Pi/scripts/update_birdnet_snippets.sh

View File

@@ -0,0 +1,84 @@
#!/command/with-contenv bashio
# shellcheck shell=bash
set -e
##################
# ALLOW RESTARTS #
##################
if [[ "${BASH_SOURCE[0]}" == /etc/cont-init.d/* ]]; then
mkdir -p /etc/scripts-init
sed -i "s|/etc/cont-init.d|/etc/scripts-init|g" /ha_entrypoint.sh
sed -i "/ rm/d" /ha_entrypoint.sh
cp "${BASH_SOURCE[0]}" /etc/scripts-init/
fi
############
# SET MQTT #
############
# Function to perform common setup steps
common_steps () {
# Attempt to connect to the MQTT broker
TOPIC="birdnet"
if mosquitto_pub -h "$MQTT_HOST" -p "$MQTT_PORT" -t "$TOPIC" -m "test" -u "$MQTT_USER" -P "$MQTT_PASS" -q 1 -d --will-topic "$TOPIC" --will-payload "Disconnected" --will-qos 1 --will-retain > /dev/null 2>&1; then
# Adapt script with MQTT settings
sed -i "s|%%mqtt_server%%|$MQTT_HOST|g" /helpers/birdnet_to_mqtt.py
sed -i "s|%%mqtt_port%%|$MQTT_PORT|g" /helpers/birdnet_to_mqtt.py
sed -i "s|%%mqtt_user%%|$MQTT_USER|g" /helpers/birdnet_to_mqtt.py
sed -i "s|%%mqtt_pass%%|$MQTT_PASS|g" /helpers/birdnet_to_mqtt.py
# Copy script to the appropriate directory
cp /helpers/birdnet_to_mqtt.py "$HOME"/BirdNET-Pi/scripts/utils/birdnet_to_mqtt.py
chown pi:pi "$HOME"/BirdNET-Pi/scripts/utils/birdnet_to_mqtt.py
chmod +x "$HOME"/BirdNET-Pi/scripts/utils/birdnet_to_mqtt.py
# Add hooks to the main analysis script
sed -i "/load_global_model, run_analysis/a from utils.birdnet_to_mqtt import automatic_mqtt_publish" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i '/write_to_db(/a\ automatic_mqtt_publish(file, detection, os.path.basename(detection.file_name_extr))' "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
else
bashio::log.fatal "MQTT connection failed, it will not be configured"
fi
}
# Check if MQTT service is available and not disabled
if bashio::services.available 'mqtt' && ! bashio::config.true 'MQTT_DISABLED'; then
bashio::log.green "---"
bashio::log.blue "MQTT addon is active on your system! Birdnet-pi is now automatically configured to send its output to MQTT"
bashio::log.blue "MQTT user : $(bashio::services "mqtt" "username")"
bashio::log.blue "MQTT password : $(bashio::services "mqtt" "password")"
bashio::log.blue "MQTT broker : tcp://$(bashio::services "mqtt" "host"):$(bashio::services "mqtt" "port")"
bashio::log.green "---"
bashio::log.blue "Data will be posted to the topic : 'birdnet'"
bashio::log.blue "Json data : {'Date', 'Time', 'ScientificName', 'CommonName', 'Confidence', 'SpeciesCode', 'ClipName', 'url'}"
bashio::log.blue "---"
# Apply MQTT settings
MQTT_HOST="$(bashio::services "mqtt" "host")"
MQTT_PORT="$(bashio::services "mqtt" "port")"
MQTT_USER="$(bashio::services "mqtt" "username")"
MQTT_PASS="$(bashio::services "mqtt" "password")"
# Perform common setup steps
common_steps
# Check if manual MQTT configuration is provided
elif bashio::config.has_value "MQTT_HOST_manual" && bashio::config.has_value "MQTT_PORT_manual"; then
bashio::log.green "---"
bashio::log.blue "MQTT is manually configured in the addon options"
bashio::log.blue "Birdnet-pi is now automatically configured to send its output to MQTT"
bashio::log.green "---"
bashio::log.blue "Data will be posted to the topic : 'birdnet'"
bashio::log.blue "Json data : {'Date', 'Time', 'ScientificName', 'CommonName', 'Confidence', 'SpeciesCode', 'ClipName', 'url'}"
bashio::log.blue "---"
# Apply manual MQTT settings
MQTT_HOST="$(bashio::config "MQTT_HOST_manual")"
MQTT_PORT="$(bashio::config "MQTT_PORT_manual")"
MQTT_USER="$(bashio::config "MQTT_USER_manual")"
MQTT_PASS="$(bashio::config "MQTT_PASSWORD_manual")"
# Perform common setup steps
common_steps
fi

View File

@@ -0,0 +1,66 @@
#!/command/with-contenv bashio
# shellcheck shell=bash
set -e
##################
# ALLOW RESTARTS #
##################
if [[ "${BASH_SOURCE[0]}" == /etc/cont-init.d/* ]]; then
mkdir -p /etc/scripts-init
sed -i "s|/etc/cont-init.d|/etc/scripts-init|g" /ha_entrypoint.sh
sed -i "/ rm/d" /ha_entrypoint.sh
cp "${BASH_SOURCE[0]}" /etc/scripts-init/
fi
################
# ADD FEATURES #
################
bashio::log.info "Adding optional features"
# Enable the Processed folder
#############################
if bashio::config.true "PROCESSED_FOLDER_ENABLED" && ! grep -q "processed_size" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py; then
echo "... Enabling the Processed folder : the last 15 wav files will be stored there"
# Adapt config.php
sed -i "/GET\[\"info_site\"\]/a\ \$processed_size = \$_GET\[\"processed_size\"\];" "$HOME"/BirdNET-Pi/scripts/config.php
sed -i "/\$contents = file_get_contents/a\ \$contents = preg_replace\(\"/PROCESSED_SIZE=\.\*/\", \"PROCESSED_SIZE=\$processed_size\", \$contents\);" "$HOME"/BirdNET-Pi/scripts/config.php
sed -i "/\"success\"/i <table class=\"settingstable\"><tr><td>" "$HOME"/BirdNET-Pi/scripts/config.php
sed -i "/\"success\"/i <h2>Processed folder management </h2>" "$HOME"/BirdNET-Pi/scripts/config.php
sed -i "/\"success\"/i <label for=\"processed_size\">Amount of files to keep after analysis :</label>" "$HOME"/BirdNET-Pi/scripts/config.php
sed -i "/\"success\"/i <input name=\"processed_size\" type=\"number\" style=\"width:6em;\" max=\"90\" min=\"0\" step=\"1\" value=\"<\?php print(\$config\['PROCESSED_SIZE'\]);?>\"/>" "$HOME"/BirdNET-Pi/scripts/config.php
sed -i "/\"success\"/i </td></tr><tr><td>" "$HOME"/BirdNET-Pi/scripts/config.php
sed -i "/\"success\"/i Processed is the directory where the formerly 'Analyzed' files are moved after extractions, mostly for troubleshooting purposes.<br>" "$HOME"/BirdNET-Pi/scripts/config.php
sed -i "/\"success\"/i This value defines the maximum amount of files that are kept before replacement with new files.<br>" "$HOME"/BirdNET-Pi/scripts/config.php
sed -i "/\"success\"/i </td></tr></table>" "$HOME"/BirdNET-Pi/scripts/config.php
sed -i "/\"success\"/i\ <br>" "$HOME"/BirdNET-Pi/scripts/config.php
# Adapt birdnet_analysis.py - move_to_processed
sed -i "/log.info('handle_reporting_queue done')/a\ os.remove(files.pop(0))" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i "/log.info('handle_reporting_queue done')/a\ while len(files) > processed_size:" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i "/log.info('handle_reporting_queue done')/a\ files.sort(key=os.path.getmtime)" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i "/log.info('handle_reporting_queue done')/a\ files = glob.glob(os.path.join(processed_dir, '*'))" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i "/log.info('handle_reporting_queue done')/a\ os.rename(file_name, os.path.join(processed_dir, os.path.basename(file_name)))" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i "/log.info('handle_reporting_queue done')/a\ processed_dir = os.path.join(get_settings()['RECS_DIR'], 'Processed')" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i "/log.info('handle_reporting_queue done')/a\def move_to_processed(file_name, processed_size):" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i "/log.info('handle_reporting_queue done')/a\ " "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
# Adapt birdnet_analysis.py - get_processed_size
sed -i "/log.info('handle_reporting_queue done')/a\ return 0" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i "/log.info('handle_reporting_queue done')/a\ except (ValueError, TypeError):" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i "/log.info('handle_reporting_queue done')/a\ return processed_size if isinstance(processed_size, int) else 0" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i "/log.info('handle_reporting_queue done')/a\ processed_size = get_settings().getint('PROCESSED_SIZE')" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i "/log.info('handle_reporting_queue done')/a\ try:" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i "/log.info('handle_reporting_queue done')/a\def get_processed_size():" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i "/log.info('handle_reporting_queue done')/a\ " "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
# Modify calls
sed -i "/from subprocess import CalledProcessError/a\import glob" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i "/from subprocess import CalledProcessError/a\import time" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
# Modify main code
sed -i "/os.remove(file.file_name)/i\ processed_size = get_processed_size()" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i "/os.remove(file.file_name)/i\ if processed_size > 0:" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i "/os.remove(file.file_name)/i\ move_to_processed(file.file_name, processed_size)" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i "/os.remove(file.file_name)/i\ else:" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
sed -i "/os.remove(file.file_name)/c\ os.remove(file.file_name)" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py
fi || true

View File

@@ -1,7 +1,18 @@
#!/command/with-contenv bashio
# shellcheck shell=bash
# shellcheck shell=bash disable=SC2016
set -e
##################
# ALLOW RESTARTS #
##################
if [[ "${BASH_SOURCE[0]}" == /etc/cont-init.d/* ]]; then
mkdir -p /etc/scripts-init
sed -i "s|/etc/cont-init.d|/etc/scripts-init|g" /ha_entrypoint.sh
sed -i "/ rm/d" /ha_entrypoint.sh
cp "${BASH_SOURCE[0]}" /etc/scripts-init/
fi
################
# MODIFY WEBUI #
################
@@ -14,74 +25,115 @@ sed -i '/>System Controls/d' "$HOME/BirdNET-Pi/homepage/views.php"
# Remove Ram drive option from webui
echo "... removing Ram drive from webui as it is handled from HA"
sed -i '/Ram drive/{n;s/center"/center" style="display: none;"/;}' "$HOME/BirdNET-Pi/scripts/service_controls.php"
sed -i '/Ram drive/d' "$HOME/BirdNET-Pi/scripts/service_controls.php"
if grep -q "Ram drive" "$HOME/BirdNET-Pi/scripts/service_controls.php"; then
sed -i '/Ram drive/{n;s/center"/center" style="display: none;"/;}' "$HOME/BirdNET-Pi/scripts/service_controls.php"
sed -i '/Ram drive/d' "$HOME/BirdNET-Pi/scripts/service_controls.php"
fi
# Correct services to start as user pi
echo "... updating services to start as user pi"
while IFS= read -r file; do
if [[ "$(basename "$file")" != "birdnet_log.service" ]]; then
sed -i "s|ExecStart=|ExecStart=/usr/bin/sudo -u pi |g" "$file"
fi
done < <(find "$HOME/BirdNET-Pi/templates/" -name "b*.service" -print)
if ! grep -q "/usr/bin/sudo" "$HOME/BirdNET-Pi/templates/birdnet_log.service"; then
while IFS= read -r file; do
if [[ "$(basename "$file")" != "birdnet_log.service" ]]; then
sed -i "s|ExecStart=|ExecStart=/usr/bin/sudo -u pi |g" "$file"
fi
done < <(find "$HOME/BirdNET-Pi/templates/" -name "birdnet*.service" -print)
fi
# Send services log to container logs
echo "... redirecting services logs to container logs"
while IFS= read -r file; do
sed -i "/Service/a StandardError=append:/proc/1/fd/1" "$file"
sed -i "/Service/a StandardOutput=append:/proc/1/fd/1" "$file"
done < <(find "$HOME/BirdNET-Pi/templates/" -name "b*.service" -print)
sed -i "/StandardError/d" "$file"
sed -i "/StandardOutput/d" "$file"
sed -i "/\[Service/a StandardError=append:/proc/1/fd/1" "$file"
sed -i "/\[Service/a StandardOutput=append:/proc/1/fd/1" "$file"
done < <(find "$HOME/BirdNET-Pi/templates/" -name "*.service" -print)
# Avoid preselection in include and exclude lists
echo "... disabling preselecting options in include and exclude lists"
sed -i "s|option selected|option disabled|g" "$HOME/BirdNET-Pi/scripts/include_list.php"
sed -i "s|option selected|option disabled|g" "$HOME/BirdNET-Pi/scripts/exclude_list.php"
# Preencode API key
if ! grep -q "221160312" "$HOME/BirdNET-Pi/scripts/common.php"; then
sed -i "/return \$_SESSION\['my_config'\];/i\ \ \ \ if (isset(\$_SESSION\['my_config'\]) \&\& empty(\$_SESSION\['my_config'\]\['FLICKR_API_KEY'\])) {\n\ \ \ \ \ \ \ \ \$_SESSION\['my_config'\]\['FLICKR_API_KEY'\] = \"221160312e1c22\";\n\ \ \ \ }" "$HOME"/BirdNET-Pi/scripts/common.php
sed -i "s|e1c22|e1c22ec60ecf336951b0e77|g" "$HOME"/BirdNET-Pi/scripts/common.php
fi
# Correct log services to show /proc/1/fd/1
echo "... redirecting birdnet_log service output to /logs"
sed -i "/User=pi/d" "$HOME/BirdNET-Pi/templates/birdnet_log.service"
sed -i "s|birdnet_log.sh|cat /proc/1/fd/1|g" "$HOME/BirdNET-Pi/templates/birdnet_log.service"
# Correct backup script
echo "... correct backup script"
sed -i "/PHP_SERVICE=/c PHP_SERVICE=\$(systemctl list-unit-files -t service --no-pager | grep 'php' | grep 'fpm' | awk '{print \$1}')" "$HOME/BirdNET-Pi/scripts/backup_data.sh"
# Caddyfile modifications
echo "... modifying Caddyfile configurations"
caddy fmt --overwrite /etc/caddy/Caddyfile
#Change port to leave 80 free for certificate requests
sed -i "s|http://|http://:8081|g" /etc/caddy/Caddyfile
sed -i "s|http://|http://:8081|g" "$HOME/BirdNET-Pi/scripts/update_caddyfile.sh"
if [ -f /etc/caddy/Caddyfile.original ]; then
rm /etc/caddy/Caddyfile.original
if ! grep -q "http://:8081" /etc/caddy/Caddyfile; then
sed -i "s|http://|http://:8081|g" /etc/caddy/Caddyfile
sed -i "s|http://|http://:8081|g" "$HOME/BirdNET-Pi/scripts/update_caddyfile.sh"
if [ -f /etc/caddy/Caddyfile.original ]; then
rm /etc/caddy/Caddyfile.original
fi
fi
# Correct webui paths
echo "... correcting webui paths"
sed -i "s|/stats|/stats/|g" "$HOME/BirdNET-Pi/homepage/views.php"
sed -i "s|/log|/log/|g" "$HOME/BirdNET-Pi/homepage/views.php"
if ! grep -q "/stats/" "$HOME/BirdNET-Pi/homepage/views.php"; then
sed -i "s|/stats|/stats/|g" "$HOME/BirdNET-Pi/homepage/views.php"
sed -i "s|/log|/log/|g" "$HOME/BirdNET-Pi/homepage/views.php"
fi
# Check if port 80 is correctly configured
if [ -n "$(bashio::addon.port 80)" ] && [ "$(bashio::addon.port 80)" != 80 ]; then
if [ -n "$(bashio::addon.port "80")" ] && [ "$(bashio::addon.port "80")" != 80 ]; then
bashio::log.fatal "The port 80 is enabled, but should still be 80 if you want automatic SSL certificates generation to work."
fi
# Correct systemctl path
echo "... updating systemctl path"
mv /helpers/systemctl3.py /bin/systemctl
chmod a+x /bin/systemctl
#echo "... updating systemctl path"
#if [[ -f /helpers/systemctl3.py ]]; then
# mv /helpers/systemctl3.py /bin/systemctl
# chmod a+x /bin/systemctl
#fi
# Improve streamlit cache
#echo "... add streamlit cache"
#sed -i "/def get_data/i \\@st\.cache_resource\(\)" "$HOME/BirdNET-Pi/scripts/plotly_streamlit.py"
# Allow reverse proxy for streamlit
echo "... allow reverse proxy for streamlit"
sed -i "s|plotly_streamlit.py --browser.gatherUsageStats|plotly_streamlit.py --server.enableXsrfProtection=false --server.enableCORS=false --browser.gatherUsageStats|g" "$HOME/BirdNET-Pi/templates/birdnet_stats.service"
# Clean saved mp3 files
echo ".. add highpass and lowpass to sox extracts"
sed -i "s|f'={stop}']|f'={stop}', 'highpass', '250', 'lowpass', '15000']|g" "$HOME/BirdNET-Pi/scripts/utils/reporting.py"
sed -i '/sox.*-V1/s/spectrogram/highpass 250 spectrogram/' "$HOME/BirdNET-Pi/scripts/spectrogram.sh"
# Correct timedatectl path
echo "updating timedatectl path"
mv /helpers/timedatectl /usr/bin/timedatectl
chown pi:pi /usr/bin/timedatectl
chmod a+x /usr/bin/timedatectl
if [[ -f /helpers/timedatectl ]]; then
mv /helpers/timedatectl /usr/bin/timedatectl
chown pi:pi /usr/bin/timedatectl
chmod a+x /usr/bin/timedatectl
fi
# Correct timezone showing in config.php
# shellcheck disable=SC2016
echo "... updating timezone in config.php"
sed -i -e '/<option disabled selected>/s/selected//' \
-e '/\$current_timezone = trim(shell_exec("timedatectl show --value --property=Timezone"));/d' \
-e "/\$date = new DateTime('now');/i \$current_timezone = trim(shell_exec(\"timedatectl show --value --property=Timezone\"));" \
-e "/\$date = new DateTime('now');/i date_default_timezone_set(\$current_timezone);" "$HOME/BirdNET-Pi/scripts/config.php"
# Use only first user
echo "... correcting for multiple users"
for file in $(grep -rl "/1000/{print" "$HOME"/BirdNET-Pi/scripts); do
sed -i "s|'/1000/{print \$1}'|'/1000/{print \$1; exit}'|" "$file"
sed -i "s|'/1000/{print \$6}'|'/1000/{print \$6; exit}'|" "$file"
done
# Correct language labels according to birdnet.conf
echo "... adapting labels according to birdnet.conf"
if export "$(grep "^DATABASE_LANG" /config/birdnet.conf)"; then
bashio::log.info "Setting language to ${DATABASE_LANG:-en}"
"$HOME/BirdNET-Pi/scripts/install_language_label_nm.sh" -l "${DATABASE_LANG:-}" &>/dev/null || bashio::log.warning "Failed to update language labels"
else
bashio::log.warning "DATABASE_LANG not found in configuration. Using default labels."
fi

View File

@@ -2,6 +2,17 @@
# shellcheck shell=bash
set -e
##################
# ALLOW RESTARTS #
##################
if [[ "${BASH_SOURCE[0]}" == /etc/cont-init.d/* ]]; then
mkdir -p /etc/scripts-init
sed -i "s|/etc/cont-init.d|/etc/scripts-init|g" /ha_entrypoint.sh
sed -i "/ rm/d" /ha_entrypoint.sh
cp "${BASH_SOURCE[0]}" /etc/scripts-init/
fi
#################
# NGINX SETTING #
#################
@@ -12,8 +23,9 @@ ingress_interface=$(bashio::addon.ip_address)
ingress_entry=$(bashio::addon.ingress_entry)
# Quits if ingress is not active
if [ -z "$ingress_entry" ]; then
bashio::log.warning "Ingress entry is not set, exiting configuration."
if [[ "$ingress_entry" != "/api"* ]]; then
bashio::log.info "Ingress entry is not set, exiting configuration."
sed -i "1a sleep infinity" /custom-services.d/02-nginx.sh
exit 0
fi
@@ -31,14 +43,20 @@ else
exit 1
fi
# Disable log
sed -i "/View Log/d" "$HOME/BirdNET-Pi/homepage/views.php"
echo "... ensuring restricted area access"
echo "${ingress_entry}" > /ingress_url
# Modify PHP file safely
for php_file in config.php play.php advanced.php overview.php; do
sed -i "s|if (\!isset(\$_SERVER\['PHP_AUTH_USER'\])) {|if (\!isset(\$_SERVER\['PHP_AUTH_USER'\]) \&\& strpos(\$_SERVER\['HTTP_REFERER'\], '/api/hassio_ingress') == false) {|g" "$HOME/BirdNET-Pi/scripts/$php_file"
sed -i "s+if(\$submittedpwd == \$caddypwd \&\& \$submitteduser == 'birdnet')+if((\$submittedpwd == \$caddypwd \&\& \$submitteduser == 'birdnet') || (strpos(\$_SERVER['HTTP_REFERER'], '/api/hassio_ingress') !== false \&\& strpos(\$_SERVER['HTTP_REFERER'], trim(file_get_contents('/ingress_url'))) !== false)+g" "$HOME/BirdNET-Pi/scripts/$php_file"
done
php_file="$HOME/BirdNET-Pi/scripts/common.php"
if [ -f "$php_file" ]; then
sed -i "/function is_authenticated/a if (strpos(\$_SERVER['HTTP_REFERER'], '/api/hassio_ingress') !== false && strpos(\$_SERVER['HTTP_REFERER'], trim(file_get_contents('/ingress_url'))) !== false) { \$ret = true; return \$ret; }" "$php_file"
else
bashio::log.error "PHP file not found: $php_file"
exit 1
fi
echo "... adapting Caddyfile for ingress"
chmod +x /helpers/caddy_ingress.sh

View File

@@ -2,6 +2,17 @@
# shellcheck shell=bash
set -e
##################
# ALLOW RESTARTS #
##################
if [[ "${BASH_SOURCE[0]}" == /etc/cont-init.d/* ]]; then
mkdir -p /etc/scripts-init
sed -i "s|/etc/cont-init.d|/etc/scripts-init|g" /ha_entrypoint.sh
sed -i "/ rm/d" /ha_entrypoint.sh
cp "${BASH_SOURCE[0]}" /etc/scripts-init/
fi
###############
# SSL SETTING #
###############

View File

@@ -0,0 +1,37 @@
#!/command/with-contenv bashio
# shellcheck shell=bash disable=SC1091
set -e
##################
# ALLOW RESTARTS #
##################
if [[ "${BASH_SOURCE[0]}" == /etc/cont-init.d/* ]]; then
mkdir -p /etc/scripts-init
sed -i "s|/etc/cont-init.d|/etc/scripts-init|g" /ha_entrypoint.sh
sed -i "/ rm/d" /ha_entrypoint.sh
cp "${BASH_SOURCE[0]}" /etc/scripts-init/
fi
######################
# INSTALL TENSORFLOW #
######################
# Check if the CPU supports AVX2
if [[ "$(uname -m)" = "x86_64" ]]; then
if lscpu | grep -q "Flags"; then
if ! lscpu | grep -q "avx2"; then
bashio::log.warning "NON SUPPORTED CPU DETECTED"
bashio::log.warning "Your cpu doesn't support avx2, the analyzer service will likely won't work"
bashio::log.warning "Trying to install tensorflow instead of tflite_runtime instead. This might take some time (up to 5 minutes)."
bashio::log.warning "You could try also Birdnet-Go which should supports your cpu"
source /home/pi/BirdNET-Pi/birdnet/bin/activate
mkdir -p /home/pi/.cache/pip || true &>/dev/null
chmod 777 /home/pi/.cache/pip || true &>/dev/null
pip3 uninstall -y tflite_runtime
pip install --upgrade packaging==23.2
pip3 install --upgrade --force-reinstall "https://github.com/snowzach/tensorflow-multiarch/releases/download/v2.16.1/tensorflow-2.16.1-cp311-cp311-linux_x86_64.whl"
deactivate
fi
fi
fi

View File

@@ -1,18 +1,32 @@
#!/command/with-contenv bashio
# shellcheck shell=bash
set -e
set -eu
##################
# ALLOW RESTARTS #
##################
if [[ "${BASH_SOURCE[0]}" == /etc/cont-init.d/* ]]; then
mkdir -p /etc/scripts-init
sed -i "s|/etc/cont-init.d|/etc/scripts-init|g" /ha_entrypoint.sh
sed -i "/ rm/d" /ha_entrypoint.sh
cp "${BASH_SOURCE[0]}" /etc/scripts-init/
fi
##############
# SET SYSTEM #
##############
# Set password
bashio::log.info "Setting password for the user pi"
echo "pi:$(bashio::config "pi_password")" | chpasswd
if bashio::config.has_value "pi_password"; then
echo "pi:$(bashio::config "pi_password")" | chpasswd
fi
bashio::log.info "Password set successfully for user pi."
bashio::log.info "Setting timezone :"
# Use timezone defined in add-on options if available
bashio::log.info "Setting timezone :"
if bashio::config.has_value 'TZ'; then
TZ_VALUE="$(bashio::config 'TZ')"
if timedatectl set-timezone "$TZ_VALUE"; then
@@ -38,7 +52,11 @@ else
else
bashio::log.fatal "Couldn't set automatic timezone! Please set a manual one from the options."
fi
fi
fi || true
# Fix timezone as per installer
CURRENT_TIMEZONE="$(timedatectl show --value --property=Timezone)"
[ -f /etc/timezone ] && echo "$CURRENT_TIMEZONE" | sudo tee /etc/timezone > /dev/null
bashio::log.info "Starting system services"
@@ -53,11 +71,12 @@ chmod +x "$HOME/BirdNET-Pi/scripts/restart_services.sh" >/dev/null
"$HOME/BirdNET-Pi/scripts/restart_services.sh" >/dev/null
# Start livestream services if enabled in configuration
if bashio::config.true LIVESTREAM_BOOT_ENABLED; then
if bashio::config.true "LIVESTREAM_BOOT_ENABLED"; then
echo "... starting livestream services"
systemctl enable icecast2 >/dev/null
systemctl start icecast2.service >/dev/null
systemctl enable --now livestream.service >/dev/null
fi
bashio::log.info "Setup complete."
# Start
bashio::log.info "✅ Setup complete."

View File

@@ -1,4 +1,4 @@
server {
server {
listen %%interface%%:%%port%% default_server;
include /etc/nginx/includes/server_params.conf;
include /etc/nginx/includes/proxy_params.conf;
@@ -6,21 +6,6 @@
proxy_buffering off;
auth_basic_user_file /home/pi/.htpasswd;
location /log {
# Proxy pass
proxy_pass http://localhost:8082;
}
location /stats {
# Proxy pass
proxy_pass http://localhost:8082;
}
location /terminal {
# Proxy pass
proxy_pass http://localhost:8082;
}
location / {
# Proxy pass
proxy_pass http://localhost:8082;
@@ -30,6 +15,12 @@
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Adjust any Location headers in backend redirects
absolute_redirect off;
proxy_redirect /stats %%ingress_entry%%/stats;
proxy_redirect /log %%ingress_entry%%/log;
proxy_redirect /terminal %%ingress_entry%%/terminal;
# Correct base_url
proxy_set_header Accept-Encoding "";
sub_filter_once off;
@@ -37,11 +28,14 @@
sub_filter /spectrogram %%ingress_entry%%/spectrogram;
sub_filter /By_Date/ %%ingress_entry%%/By_Date/;
sub_filter /Charts/ %%ingress_entry%%/Charts/;
sub_filter /stats/ %%ingress_entry%%/stats/;
sub_filter /log/ %%ingress_entry%%/log/;
sub_filter /terminal/ %%ingress_entry%%/terminal/;
sub_filter "url = '/" "url = '%%ingress_entry%%/";
sub_filter /todays %%ingress_entry%%/todays;
sub_filter href=\"/ href=\"%%ingress_entry%%/;
sub_filter src=\"/ src=\"%%ingress_entry%%/;
sub_filter hx-get=\"/ hx-get=\"%%ingress_entry%%/;
sub_filter action=\"/ action=\"%%ingress_entry%%/;
}
}