From a2243fcfc745d04b01ec6edc3a8129f48faf522a Mon Sep 17 00:00:00 2001 From: frederik Date: Thu, 21 Apr 2022 19:36:23 +0200 Subject: [PATCH 1/7] consolidate duplicated code --- scripts/birdnet_analysis.sh | 245 ++++++++---------------------------- 1 file changed, 51 insertions(+), 194 deletions(-) diff --git a/scripts/birdnet_analysis.sh b/scripts/birdnet_analysis.sh index 3edfe2d..1fa8b98 100755 --- a/scripts/birdnet_analysis.sh +++ b/scripts/birdnet_analysis.sh @@ -93,30 +93,22 @@ run_analysis() { for i in "${files[@]}";do echo "${1}/${i}" > $HOME/BirdNET-Pi/analyzing_now.txt [ -z ${RECORDING_LENGTH} ] && RECORDING_LENGTH=15 - [ ${RECORDING_LENGTH} == "60" ] && RECORDING_LENGTH=01:00 FILE_LENGTH="$(ffmpeg -i ${1}/${i} 2>&1 | awk -F. '/Duration/ {print $1}' | cut -d':' -f3-4)" [ -z $FILE_LENGTH ] && sleep 1 && continue echo "RECORDING_LENGTH set to ${RECORDING_LENGTH}" - a=0 - if [ "${RECORDING_LENGTH}" == "01:00" ];then - until [ "$(ffmpeg -i ${1}/${i} 2>&1 | awk -F. '/Duration/ {print $1}' | cut -d':' -f3-4)" == "${RECORDING_LENGTH}" ];do - sleep 1 - [ $a -ge 60 ] && rm -f ${1}/${i} && break - a=$((a+1)) - done + if [ "${RECORDING_LENGTH}" == 60 ];then + RECORDING_LENGTH_MATCH="01:00" elif [ "${RECORDING_LENGTH}" -lt 10 ];then - until [ "$(ffmpeg -i ${1}/${i} 2>&1 | awk -F. '/Duration/ {print $1}' | cut -d':' -f3-4)" == "00:0${RECORDING_LENGTH}" ];do - sleep 1 - [ $a -ge ${RECORDING_LENGTH} ] && rm -f ${1}/${i} && break - a=$((a+1)) - done + RECORDING_LENGTH_MATCH="00:0${RECORDING_LENGTH}" else - until [ "$(ffmpeg -i ${1}/${i} 2>&1 | awk -F. '/Duration/ {print $1}' | cut -d':' -f3-4)" == "00:${RECORDING_LENGTH}" ];do - sleep 1 - [ $a -ge ${RECORDING_LENGTH} ] && rm -f ${1}/${i} && break - a=$((a+1)) - done + RECORDING_LENGTH_MATCH="00:${RECORDING_LENGTH}" fi + a=0 + until [ "$(ffmpeg -i ${1}/${i} 2>&1 | awk -F. '/Duration/ {print $1}' | cut -d':' -f3-4)" == "${RECORDING_LENGTH_MATCH}" ];do + sleep 1 + [ $a -ge ${RECORDING_LENGTH} ] && rm -f ${1}/${i} && break + a=$((a+1)) + done if ! grep 5050 <(netstat -tulpn 2>&1) &> /dev/null 2>&1;then echo "Waiting for socket" @@ -124,183 +116,48 @@ run_analysis() { sleep 1 done fi - if [ -f ${1}/${i} ] && [ ! -f ${INCLUDE_LIST} ] && [ ! -f ${EXCLUDE_LIST} ] && [ -z $BIRDWEATHER_ID ];then - echo "analyze.py \ ---i "${1}/${i}" \ ---o "${1}/${i}.csv" \ ---lat "${LATITUDE}" \ ---lon "${LONGITUDE}" \ ---week "${WEEK}" \ ---overlap "${OVERLAP}" \ ---sensitivity "${SENSITIVITY}" \ ---min_conf "${CONFIDENCE}"" - analyze.py \ - --i "${1}/${i}" \ - --o "${1}/${i}.csv" \ - --lat "${LATITUDE}" \ - --lon "${LONGITUDE}" \ - --week "${WEEK}" \ - --overlap "${OVERLAP}" \ - --sensitivity "${SENSITIVITY}" \ - --min_conf "${CONFIDENCE}" - elif [ -f ${1}/${i} ] && [ -f ${INCLUDELIST} ] && [ ! -f ${EXCLUDE_LIST} ] && [ -z $BIRDWEATHER_ID ];then - echo "analyze.py \ ---i "${1}/${i}" \ ---o "${1}/${i}.csv" \ ---lat "${LATITUDE}" \ ---lon "${LONGITUDE}" \ ---week "${WEEK}" \ ---overlap "${OVERLAP}" \ ---sensitivity "${SENSITIVITY}" \ ---min_conf "${CONFIDENCE}" \ ---include_list "${INCLUDE_LIST}"" - analyze.py \ - --i "${1}/${i}" \ - --o "${1}/${i}.csv" \ - --lat "${LATITUDE}" \ - --lon "${LONGITUDE}" \ - --week "${WEEK}" \ - --overlap "${OVERLAP}" \ - --sensitivity "${SENSITIVITY}" \ - --min_conf "${CONFIDENCE}" \ - --include_list "${INCLUDE_LIST}" - elif [ -f ${1}/${i} ] && [ ! -f ${INCLUDE_LIST} ] && [ -f ${EXCLUDE_LIST} ] && [ -z $BIRDWEATHER_ID ];then - echo "analyze.py \ ---i "${1}/${i}" \ ---o "${1}/${i}.csv" \ ---lat "${LATITUDE}" \ ---lon "${LONGITUDE}" \ ---week "${WEEK}" \ ---overlap "${OVERLAP}" \ ---sensitivity "${SENSITIVITY}" \ ---min_conf "${CONFIDENCE}" \ ---exclude_list "${EXCLUDE_LIST}"" - analyze.py \ - --i "${1}/${i}" \ - --o "${1}/${i}.csv" \ - --lat "${LATITUDE}" \ - --lon "${LONGITUDE}" \ - --week "${WEEK}" \ - --overlap "${OVERLAP}" \ - --sensitivity "${SENSITIVITY}" \ - --min_conf "${CONFIDENCE}" \ - --exclude_list "${EXCLUDE_LIST}" - elif [ -f ${1}/${i} ] && [ -f ${INCLUDE_LIST} ] && [ -f ${EXCLUDE_LIST} ] && [ -z $BIRDWEATHER_ID ];then - echo "analyze.py \ ---i "${1}/${i}" \ ---o "${1}/${i}.csv" \ ---lat "${LATITUDE}" \ ---lon "${LONGITUDE}" \ ---week "${WEEK}" \ ---overlap "${OVERLAP}" \ ---sensitivity "${SENSITIVITY}" \ ---min_conf "${CONFIDENCE}" \ ---include_list "${INCLUDE_LIST}" \ ---exclude_list "${EXCLUDE_LIST}"" - analyze.py \ - --i "${1}/${i}" \ - --o "${1}/${i}.csv" \ - --lat "${LATITUDE}" \ - --lon "${LONGITUDE}" \ - --week "${WEEK}" \ - --overlap "${OVERLAP}" \ - --sensitivity "${SENSITIVITY}" \ - --min_conf "${CONFIDENCE}" \ - --include_list "${INCLUDE_LIST}" \ - --exclude_list "${EXCLUDE_LIST}" - elif [ -f ${1}/${i} ] && [ ! -f ${INCLUDE_LIST} ] && [ ! -f ${EXCLUDE_LIST} ] && [ ! -z $BIRDWEATHER_ID ];then - echo "analyze.py \ ---i "${1}/${i}" \ ---o "${1}/${i}.csv" \ ---lat "${LATITUDE}" \ ---lon "${LONGITUDE}" \ ---week "${WEEK}" \ ---overlap "${OVERLAP}" \ ---sensitivity "${SENSITIVITY}" \ ---min_conf "${CONFIDENCE}" \ ---birdweather_id "IN_USE"" - analyze.py \ - --i "${1}/${i}" \ - --o "${1}/${i}.csv" \ - --lat "${LATITUDE}" \ - --lon "${LONGITUDE}" \ - --week "${WEEK}" \ - --overlap "${OVERLAP}" \ - --sensitivity "${SENSITIVITY}" \ - --min_conf "${CONFIDENCE}" \ - --birdweather_id "${BIRDWEATHER_ID}" - elif [ -f ${1}/${i} ] && [ -f ${INCLUDE_LIST} ] && [ ! -f ${EXCLUDE_LIST} ] && [ ! -z $BIRDWEATHER_ID ];then - echo "analyze.py \ ---i "${1}/${i}" \ ---o "${1}/${i}.csv" \ ---lat "${LATITUDE}" \ ---lon "${LONGITUDE}" \ ---week "${WEEK}" \ ---overlap "${OVERLAP}" \ ---sensitivity "${SENSITIVITY}" \ ---min_conf "${CONFIDENCE}" \ ---include_list "${INCLUDE_LIST}" \ ---birdweather_id "IN_USE"" - analyze.py \ - --i "${1}/${i}" \ - --o "${1}/${i}.csv" \ - --lat "${LATITUDE}" \ - --lon "${LONGITUDE}" \ - --week "${WEEK}" \ - --overlap "${OVERLAP}" \ - --sensitivity "${SENSITIVITY}" \ - --min_conf "${CONFIDENCE}" \ - --include_list "${INCLUDE_LIST}" \ - --birdweather_id "${BIRDWEATHER_ID}" - elif [ -f ${1}/${i} ] && [ ! -f ${INCLUDE_LIST} ] && [ -f ${EXCLUDE_LIST} ] && [ ! -z $BIRDWEATHER_ID ];then - echo "analyze.py \ ---i "${1}/${i}" \ ---o "${1}/${i}.csv" \ ---lat "${LATITUDE}" \ ---lon "${LONGITUDE}" \ ---week "${WEEK}" \ ---overlap "${OVERLAP}" \ ---sensitivity "${SENSITIVITY}" \ ---min_conf "${CONFIDENCE}" \ ---exclude_list "${EXCLUDE_LIST}" \ ---birdweather_id "IN_USE"" - analyze.py \ - --i "${1}/${i}" \ - --o "${1}/${i}.csv" \ - --lat "${LATITUDE}" \ - --lon "${LONGITUDE}" \ - --week "${WEEK}" \ - --overlap "${OVERLAP}" \ - --sensitivity "${SENSITIVITY}" \ - --min_conf "${CONFIDENCE}" \ - --exclude_list "${EXCLUDE_LIST}" \ - --birdweather_id "${BIRDWEATHER_ID}" - elif [ -f ${1}/${i} ] && [ -f ${INCLUDE_LIST} ] && [ -f ${EXCLUDE_LIST} ] && [ ! -z $BIRDWEATHER_ID ];then - echo "analyze.py \ ---i "${1}/${i}" \ ---o "${1}/${i}.csv" \ ---lat "${LATITUDE}" \ ---lon "${LONGITUDE}" \ ---week "${WEEK}" \ ---overlap "${OVERLAP}" \ ---sensitivity "${SENSITIVITY}" \ ---min_conf "${CONFIDENCE}" \ ---include_list "${INCLUDE_LIST}" \ ---exclude_list "${EXCLUDE_LIST}" \ ---birdweather_id "IN_USE"" - analyze.py \ - --i "${1}/${i}" \ - --o "${1}/${i}.csv" \ - --lat "${LATITUDE}" \ - --lon "${LONGITUDE}" \ - --week "${WEEK}" \ - --overlap "${OVERLAP}" \ - --sensitivity "${SENSITIVITY}" \ - --min_conf "${CONFIDENCE}" \ - --include_list "${INCLUDE_LIST}" \ - --exclude_list "${EXCLUDE_LIST}" \ - --birdweather_id "${BIRDWEATHER_ID}" + # prepare optional parameters for analyse.py + if [ -f ${INCLUDE_LIST} ]; then + INCLUDEPARAM="--include_list \"${INCLUDE_LIST}\"" + else + INCLUDEPARAM="" fi + if [ -f ${EXCLUDE_LIST} ]; then + EXCLUDEPARAM="--include_list \"${EXCLUDE_LIST}\"" + else + EXCLUDEPARAM="" + fi + if [ ! -z $BIRDWEATHER_ID ]; then + BIRDWEATHER_ID_PARAM="--birdweather_id \"${BIRDWEATHER_ID}\"" + BIRDWEATHER_ID_LOG="--birdweather_id \"IN_USE\"" + else + BIRDWEATHER_ID_PARAM="" + BIRDWEATHER_ID_LOG="" + fi + echo analyze.py \ +--i "${1}/${i}" \ +--o "${1}/${i}.csv" \ +--lat "${LATITUDE}" \ +--lon "${LONGITUDE}" \ +--week "${WEEK}" \ +--overlap "${OVERLAP}" \ +--sensitivity "${SENSITIVITY}" \ +--min_conf "${CONFIDENCE}" \ +${INCLUDEPARAM} \ +${EXCLUDEPARAM} \ +${BIRDWEATHER_ID_LOG} + analyze.py \ + --i "${1}/${i}" \ + --o "${1}/${i}.csv" \ + --lat "${LATITUDE}" \ + --lon "${LONGITUDE}" \ + --week "${WEEK}" \ + --overlap "${OVERLAP}" \ + --sensitivity "${SENSITIVITY}" \ + --min_conf "${CONFIDENCE}" \ + ${INCLUDEPARAM} \ + ${EXCLUDEPARAM} \ + ${BIRDWEATHER_ID_PARAM} done } From ba33976b28b5accd1d1b2862451c992e1cf6d21a Mon Sep 17 00:00:00 2001 From: frederik Date: Sat, 23 Apr 2022 18:36:41 +0200 Subject: [PATCH 2/7] avoid using ffmpeg: check existence of file in staid of checking for no length --- scripts/birdnet_analysis.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/birdnet_analysis.sh b/scripts/birdnet_analysis.sh index 1fa8b98..d456a0e 100755 --- a/scripts/birdnet_analysis.sh +++ b/scripts/birdnet_analysis.sh @@ -91,10 +91,9 @@ run_analysis() { fi for i in "${files[@]}";do + [ ! -f ${1}/${i} ] && continue echo "${1}/${i}" > $HOME/BirdNET-Pi/analyzing_now.txt [ -z ${RECORDING_LENGTH} ] && RECORDING_LENGTH=15 - FILE_LENGTH="$(ffmpeg -i ${1}/${i} 2>&1 | awk -F. '/Duration/ {print $1}' | cut -d':' -f3-4)" - [ -z $FILE_LENGTH ] && sleep 1 && continue echo "RECORDING_LENGTH set to ${RECORDING_LENGTH}" if [ "${RECORDING_LENGTH}" == 60 ];then RECORDING_LENGTH_MATCH="01:00" From be800cd51a1967b0b6069b1b8e6170627d914ba2 Mon Sep 17 00:00:00 2001 From: frederik Date: Sat, 23 Apr 2022 18:37:55 +0200 Subject: [PATCH 3/7] avoid using ffmpeg: just wait until the file is closed --- scripts/birdnet_analysis.sh | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/scripts/birdnet_analysis.sh b/scripts/birdnet_analysis.sh index d456a0e..a031245 100755 --- a/scripts/birdnet_analysis.sh +++ b/scripts/birdnet_analysis.sh @@ -95,15 +95,8 @@ run_analysis() { echo "${1}/${i}" > $HOME/BirdNET-Pi/analyzing_now.txt [ -z ${RECORDING_LENGTH} ] && RECORDING_LENGTH=15 echo "RECORDING_LENGTH set to ${RECORDING_LENGTH}" - if [ "${RECORDING_LENGTH}" == 60 ];then - RECORDING_LENGTH_MATCH="01:00" - elif [ "${RECORDING_LENGTH}" -lt 10 ];then - RECORDING_LENGTH_MATCH="00:0${RECORDING_LENGTH}" - else - RECORDING_LENGTH_MATCH="00:${RECORDING_LENGTH}" - fi a=0 - until [ "$(ffmpeg -i ${1}/${i} 2>&1 | awk -F. '/Duration/ {print $1}' | cut -d':' -f3-4)" == "${RECORDING_LENGTH_MATCH}" ];do + until [ -z "$(lsof -t ${1}/${i})" ];do sleep 1 [ $a -ge ${RECORDING_LENGTH} ] && rm -f ${1}/${i} && break a=$((a+1)) From c914c645a8525ed6df612a0644e3c6b54f2df725 Mon Sep 17 00:00:00 2001 From: frederik Date: Sat, 23 Apr 2022 18:40:50 +0200 Subject: [PATCH 4/7] avoid using ffmpeg: the loop is faster now, so slow down again --- scripts/birdnet_analysis.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/birdnet_analysis.sh b/scripts/birdnet_analysis.sh index a031245..a2e755f 100755 --- a/scripts/birdnet_analysis.sh +++ b/scripts/birdnet_analysis.sh @@ -97,9 +97,9 @@ run_analysis() { echo "RECORDING_LENGTH set to ${RECORDING_LENGTH}" a=0 until [ -z "$(lsof -t ${1}/${i})" ];do - sleep 1 + sleep 2 [ $a -ge ${RECORDING_LENGTH} ] && rm -f ${1}/${i} && break - a=$((a+1)) + a=$((a+2)) done if ! grep 5050 <(netstat -tulpn 2>&1) &> /dev/null 2>&1;then From eb8fdd2d8a9cabe93e967990d184ea08b275a62d Mon Sep 17 00:00:00 2001 From: mcguirepr89 Date: Thu, 5 May 2022 16:42:47 -0400 Subject: [PATCH 5/7] adjustments to the installer and updater to ensure `lsof` is installed --- scripts/install_services.sh | 2 +- scripts/most_recent.sh | 31 ++++++++++++++++++++++++++++++ scripts/update_birdnet.sh | 8 +------- scripts/update_birdnet_snippets.sh | 17 ++++++++++++++++ 4 files changed, 50 insertions(+), 8 deletions(-) create mode 100755 scripts/most_recent.sh create mode 100755 scripts/update_birdnet_snippets.sh diff --git a/scripts/install_services.sh b/scripts/install_services.sh index 7b486ae..b19e399 100755 --- a/scripts/install_services.sh +++ b/scripts/install_services.sh @@ -18,7 +18,7 @@ install_depends() { apt install -qqy caddy ftpd sqlite3 php-sqlite3 alsa-utils \ pulseaudio avahi-utils sox libsox-fmt-mp3 php php-fpm php-curl php-xml \ php-zip icecast2 swig ffmpeg wget unzip curl cmake make bc libjpeg-dev \ - zlib1g-dev python3-dev python3-pip python3-venv + zlib1g-dev python3-dev python3-pip python3-venv lsof } diff --git a/scripts/most_recent.sh b/scripts/most_recent.sh new file mode 100755 index 0000000..cf10249 --- /dev/null +++ b/scripts/most_recent.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +OLDIFS=$IFS +IFS=\| +most_recent_results=($(sqlite3 ~/BirdNET-Pi/scripts/birds.db \ + 'SELECT Com_Name, Time, Date FROM detections + ORDER BY Date DESC, Time DESC + LIMIT 1')) +today=$(date +%F) +yesterday=$(date --date="yesterday" +%F) +two_days_ago=$(date --date="2 days ago" +%F) + +echo -n The most recent detection was + +if [[ "${most_recent_results[0]}" =~ ^[AEIOU].* ]];then + echo -n " an ${most_recent_results[0]} at " +else + echo -n " a ${most_recent_results[0]} at " +fi +most_recent_results[1]=$(date --date="${most_recent_results[1]}" +%l:%M%p) +echo -n ${most_recent_results[1]} +if [[ ${most_recent_results[2]} == $today ]];then + echo " today." +elif [[ ${most_recent_results[2]} == $yesterday ]];then + echo " yesterday." +elif [[ ${most_recent_results[2]} == $two_days_ago ]];then + echo " two days ago." +else + echo " on ${most_recent_results[2]}." +fi + +IFS=$OLDIFS diff --git a/scripts/update_birdnet.sh b/scripts/update_birdnet.sh index a48e7d6..254efd1 100755 --- a/scripts/update_birdnet.sh +++ b/scripts/update_birdnet.sh @@ -11,10 +11,4 @@ sudo -u${USER} git -C $my_dir pull -f sudo systemctl daemon-reload sudo -u${USER} git -C $my_dir stash pop sudo ln -sf $my_dir/* /usr/local/bin/ -if ! grep python3 <(head -n1 $my_dir/analyze.py);then - echo "Ensure all python scripts use the virtual environment" - sed -si "1 i\\#\!$HOME/BirdNET-Pi/birdnet/bin/python3" $my_dir/*.py -fi -if ! grep PRIVACY_MODE /etc/birdnet/birdnet.conf;then - sudo -u${USER} echo "PRIVACY_MODE=off" >> /etc/birdnet/birdnet.conf -fi +sudo $my_dir/update_birdnet_snippets.sh diff --git a/scripts/update_birdnet_snippets.sh b/scripts/update_birdnet_snippets.sh new file mode 100755 index 0000000..5e63ff7 --- /dev/null +++ b/scripts/update_birdnet_snippets.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# Update BirdNET-Pi +source /etc/birdnet/birdnet.conf +trap 'exit 1' SIGINT SIGHUP +USER=$(awk -F: '/1000/ {print $1}' /etc/passwd) +HOME=$(awk -F: '/1000/ {print $6}' /etc/passwd) +my_dir=$HOME/BirdNET-Pi/scripts +if ! grep python3 <(head -n1 $my_dir/analyze.py);then + echo "Ensure all python scripts use the virtual environment" + sed -si "1 i\\#\!$HOME/BirdNET-Pi/birdnet/bin/python3" $my_dir/*.py +fi +if ! grep PRIVACY_MODE /etc/birdnet/birdnet.conf;then + sudo -u${USER} echo "PRIVACY_MODE=off" >> /etc/birdnet/birdnet.conf +fi +if ! which lsof &>/dev/null;then + sudo apt update && sudo apt -y install lsof +fi From 06041195c8a123570ee508e005eed4dd87ffe638 Mon Sep 17 00:00:00 2001 From: Patrick McGuire <60325264+mcguirepr89@users.noreply.github.com> Date: Thu, 5 May 2022 16:51:12 -0400 Subject: [PATCH 6/7] No need to see the grepping --- scripts/update_birdnet_snippets.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/update_birdnet_snippets.sh b/scripts/update_birdnet_snippets.sh index 5e63ff7..1f30143 100755 --- a/scripts/update_birdnet_snippets.sh +++ b/scripts/update_birdnet_snippets.sh @@ -5,11 +5,11 @@ trap 'exit 1' SIGINT SIGHUP USER=$(awk -F: '/1000/ {print $1}' /etc/passwd) HOME=$(awk -F: '/1000/ {print $6}' /etc/passwd) my_dir=$HOME/BirdNET-Pi/scripts -if ! grep python3 <(head -n1 $my_dir/analyze.py);then +if ! grep python3 <(head -n1 $my_dir/analyze.py) &>/dev/null;then echo "Ensure all python scripts use the virtual environment" sed -si "1 i\\#\!$HOME/BirdNET-Pi/birdnet/bin/python3" $my_dir/*.py fi -if ! grep PRIVACY_MODE /etc/birdnet/birdnet.conf;then +if ! grep PRIVACY_MODE /etc/birdnet/birdnet.conf &>/dev/null;then sudo -u${USER} echo "PRIVACY_MODE=off" >> /etc/birdnet/birdnet.conf fi if ! which lsof &>/dev/null;then From 32ca5a8f2c99d834ffd559975650bf7abe92d5d7 Mon Sep 17 00:00:00 2001 From: Patrick McGuire <60325264+mcguirepr89@users.noreply.github.com> Date: Thu, 5 May 2022 16:52:22 -0400 Subject: [PATCH 7/7] Update update_birdnet_snippets.sh --- scripts/update_birdnet_snippets.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/update_birdnet_snippets.sh b/scripts/update_birdnet_snippets.sh index 1f30143..3c45174 100755 --- a/scripts/update_birdnet_snippets.sh +++ b/scripts/update_birdnet_snippets.sh @@ -7,10 +7,10 @@ HOME=$(awk -F: '/1000/ {print $6}' /etc/passwd) my_dir=$HOME/BirdNET-Pi/scripts if ! grep python3 <(head -n1 $my_dir/analyze.py) &>/dev/null;then echo "Ensure all python scripts use the virtual environment" - sed -si "1 i\\#\!$HOME/BirdNET-Pi/birdnet/bin/python3" $my_dir/*.py + sudo -u$USER sed -si "1 i\\#\!$HOME/BirdNET-Pi/birdnet/bin/python3" $my_dir/*.py fi if ! grep PRIVACY_MODE /etc/birdnet/birdnet.conf &>/dev/null;then - sudo -u${USER} echo "PRIVACY_MODE=off" >> /etc/birdnet/birdnet.conf + sudo -u$USER echo "PRIVACY_MODE=off" >> /etc/birdnet/birdnet.conf fi if ! which lsof &>/dev/null;then sudo apt update && sudo apt -y install lsof