updating references to old repo to new repo
This commit is contained in:
Executable
+81
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env bash
|
||||
# Runs BirdNET in virtual environment
|
||||
#set -x
|
||||
source /etc/birdnet/birdnet.conf
|
||||
DAYS=(
|
||||
"2 days ago"
|
||||
"yesterday"
|
||||
"today"
|
||||
)
|
||||
|
||||
# Create an array of the day's audio files
|
||||
# Uses 1st argument:
|
||||
# - {DIRECTORY}
|
||||
get_files() {
|
||||
echo "Starting get_files() for ${1}"
|
||||
files=($( find ${1} -maxdepth 1 -name '*wav' \
|
||||
| sort \
|
||||
| awk -F "/" '{print $NF}' ))
|
||||
[ -n "${files[1]}" ] && echo "Files loaded"
|
||||
}
|
||||
|
||||
# Move all files that have been analyzed already into newly created "Analyzed"
|
||||
# directory
|
||||
# Uses 1st argument:
|
||||
# - {DIRECTORY}
|
||||
move_analyzed() {
|
||||
echo "Starting move_analyzed() for ${1}"
|
||||
for i in "${files[@]}";do
|
||||
j="$(echo "${i}" | cut -d'.' -f1).BirdNET.selections.txt"
|
||||
if [ -f "${1}/${j}" ];then
|
||||
if [ ! -d "${1}-Analyzed" ];then
|
||||
mkdir -vvvvvvvp "${1}-Analyzed" && echo "'Analyzed' directory created"
|
||||
fi
|
||||
echo "Moving analyzed files to new directory"
|
||||
mv -vv "${1}/${i}" "${1}-Analyzed/"
|
||||
mv -vv "${1}/${j}" "${1}-Analyzed/"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Run BirdNET analysis on the remaining WAVE files for the day
|
||||
# Uses 1st and 2nd arguments:
|
||||
# - {DIRECTORY}
|
||||
# - {"today", "yesterday", "2 days ago",...}
|
||||
run_analysis() {
|
||||
echo "Starting run_analysis() for ${1}"
|
||||
WEEK=$(date --date="${2}" +"%U")
|
||||
cd ${HOME}/BirdNET-Lite || exit 1
|
||||
"${VENV}"/bin/python analyze.py \
|
||||
--i "${1}" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--min_conf "${CONFIDENCE}"
|
||||
}
|
||||
|
||||
# The three main functions
|
||||
# Requires 2 arguments:
|
||||
# - {DIRECTORY}
|
||||
# - {"today", "yesterday", "2 days ago",...}
|
||||
run_birdnet() {
|
||||
echo "Starting run_birdnet() in \"${1}\" for \""${2}"\""
|
||||
sleep 1
|
||||
get_files "${1}"
|
||||
sleep 1
|
||||
move_analyzed "${1}"
|
||||
sleep 1
|
||||
run_analysis "${1}" "${2}"
|
||||
}
|
||||
|
||||
if [ $(find ${RECS_DIR} -maxdepth 1 -name '*wav' | wc -l) -gt 0 ];then
|
||||
run_birdnet "${RECS_DIR}" "today"
|
||||
fi
|
||||
|
||||
for i in ${!DAYS[@]};do
|
||||
DIRECTORY="$RECS_DIR/$(date --date="${DAYS[$i]}" "+%B-%Y/%d-%A")"
|
||||
if [ $(find ${DIRECTORY} -name '*wav' | wc -l) -gt 0 ];then
|
||||
run_birdnet "${DIRECTORY}" "${DAYS[$i]}"
|
||||
fi
|
||||
done
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
set -x
|
||||
source /etc/birdnet/birdnet.conf
|
||||
|
||||
if pgrep arecord &> /dev/null ;then
|
||||
echo "Recording"
|
||||
else
|
||||
if [ -z ${REC_CARD} ];then
|
||||
arecord -f S16_LE -c${CHANNELS} -r48000 -t wav --max-file-time 60 \
|
||||
--use-strftime ${RECS_DIR}/%B-%Y/%d-%A/%F-birdnet-%I:%M%P.wav
|
||||
else
|
||||
arecord -f S16_LE -c${CHANNELS} -r48000 -t wav --max-file-time 60 \
|
||||
-D "${REC_CARD}" --use-strftime \
|
||||
${RECS_DIR}/%B-%Y/%d-%A/%F-birdnet-%I:%M%P.wav
|
||||
fi
|
||||
fi
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
# BirdNET Stats Page
|
||||
trap 'setterm --cursor on' EXIT
|
||||
source /etc/birdnet/birdnet.conf
|
||||
setterm --cursor off
|
||||
|
||||
while true;do
|
||||
cat << "EOF"
|
||||
.+"+.+"+.+"+.+"+.+"+.+"+.+"+.+"+.+"+.+"+.+"+.+"+.+"+.+"+.
|
||||
( _ ____ __ _ )
|
||||
) |_)o.__||\ ||_ |__(_ __|_ _ ._ _ |_) _ ._ _ .__|_ (
|
||||
( |_)||(_|| \||_ | __)\/_> |_(/_| | | | \(/_|_)(_)| |_ )
|
||||
) / | (
|
||||
"+.+"+.+"+.+"+.+"+.+"+.+"+.+"+.+"+.+"+.+"+.+"+.+"+.+"+.+"
|
||||
EOF
|
||||
if [ "$(find ${EXTRACTED} -name '*.wav' | wc -l)" -ge 1 ];then
|
||||
a=$( find "${EXTRACTED}" -name '*.wav' \
|
||||
| awk -F "/" '{print $NF}' \
|
||||
| cut -d'-' -f1 \
|
||||
| sort -n \
|
||||
| tail -n1 )
|
||||
else
|
||||
a=0
|
||||
fi
|
||||
echo
|
||||
SOFAR=$(wc -l ${IDFILE}| cut -d' ' -f1)
|
||||
echo " -$a detections so far"
|
||||
echo
|
||||
echo " -$SOFAR species identified so far"
|
||||
while read -r line;do
|
||||
echo " + $line"
|
||||
done < ${IDFILE}
|
||||
echo
|
||||
echo -n "Listening since "${INSTALL_DATE}""
|
||||
sleep 180
|
||||
clear
|
||||
done
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
source /etc/birdnet/birdnet.conf
|
||||
|
||||
cd "${PROCESSED}" || exit 1
|
||||
FIND_DATE=*$(date --date="2 days ago" "+%F")*
|
||||
find . -name "${FIND_DATE}" -exec rm -rfv {} +
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
# Disk Usage Indicator for TMUX
|
||||
df -h / | awk '/\// {print "Disk Size="$2", Used="$3", Available="$4", "$5" of disk used"}'
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env bash
|
||||
# A comprehensive log dumper
|
||||
# set -x # Uncomment to debug
|
||||
source /etc/birdnet/birdnet.conf &> /dev/null
|
||||
LOG_DIR="${HOME}/BirdNET-Lite/logs"
|
||||
SERVICES=(avahi-alias@.service
|
||||
birdnet_analysis.service
|
||||
birdnet_log.service
|
||||
birdnet_recording.service
|
||||
birdstats.service
|
||||
birdterminal.service
|
||||
caddy.service
|
||||
extraction_log.service
|
||||
extraction.service
|
||||
extraction.timer
|
||||
icecast2.service
|
||||
livestream.service
|
||||
${SYSTEMD_MOUNT})
|
||||
|
||||
# Create logs directory
|
||||
[ -d ${LOG_DIR} ] || mkdir ${LOG_DIR}
|
||||
|
||||
# Create services logs
|
||||
for i in "${SERVICES[@]}";do
|
||||
if [ -L /etc/systemd/system/multi-user.target.wants/${i} ];then
|
||||
journalctl -u ${i} -n 100 --no-pager > ${LOG_DIR}/${i}.log
|
||||
cp -L /etc/systemd/system/multi-user.target.wants/${i} ${LOG_DIR}/${i}
|
||||
fi
|
||||
done
|
||||
|
||||
# Create password-removed birdnet.conf
|
||||
sed -e '/PWD=/d' ${HOME}/BirdNET-Lite/birdnet.conf > ${LOG_DIR}/birdnet.conf
|
||||
|
||||
# Create password-removed Caddyfile
|
||||
if [ -f /etc/caddy/Caddyfile ];then
|
||||
sed -e '/basicauth/,+2d' /etc/caddy/Caddyfile > ${LOG_DIR}/Caddyfile
|
||||
fi
|
||||
|
||||
# Get sound card specs
|
||||
SOUND_CARD="$(aplay -L \
|
||||
| awk -F, '/^hw:/ {print $1}' \
|
||||
| grep -ve 'vc4' -e 'Head' -e 'PCH' \
|
||||
| uniq)"
|
||||
echo "SOUND_CARD=${SOUND_CARD}" > ${LOG_DIR}/soundcard
|
||||
script -c "arecord -D ${SOUND_CARD} --dump-hw-params" -a ${LOG_DIR}/soundcard &> /dev/null
|
||||
|
||||
# Get system info
|
||||
CALLS=("df -h" "free -h" "ifconfig" "find ${RECS_DIR}")
|
||||
|
||||
for i in "${CALLS[@]}";do
|
||||
${i} >> ${LOG_DIR}/sysinfo
|
||||
echo "
|
||||
===============================================================================
|
||||
===============================================================================
|
||||
|
||||
" >> ${LOG_DIR}/sysinfo
|
||||
done
|
||||
|
||||
# TAR the logs into a ball
|
||||
tar --remove-files -cvpzf ${HOME}/BirdNET-Lite/logs.tar.gz ${LOG_DIR} &> /dev/null
|
||||
# Finished
|
||||
echo "Your compressed logs are located at ${HOME}/BirdNET-Lite/logs.tar.gz"
|
||||
Executable
+158
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env bash
|
||||
# Exit when any command fails
|
||||
#set -x
|
||||
set -e
|
||||
# Keep track of the last executed command
|
||||
trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG
|
||||
# Echo an error message before exiting
|
||||
trap 'echo "\"${last_command}\" command exited with code $?."' EXIT
|
||||
# Remove temporary file
|
||||
trap 'rm -f $TMPFILE' EXIT
|
||||
|
||||
source /etc/birdnet/birdnet.conf
|
||||
|
||||
# Set Variables
|
||||
TMPFILE=$(mktemp)
|
||||
# SCAN_DIRS are all directories marked "Analyzed"
|
||||
SCAN_DIRS=($(find ${ANALYZED} -type d | sort ))
|
||||
|
||||
# This sets our while loop integer iterator 'a' -- it first checks whether
|
||||
# there are any extractions, and if so, this instance of the extraction will
|
||||
# start the 'a' value where the most recent instance left off.
|
||||
# Ex: If the last extraction file is 189-%date%species.wav, 'a' will be 190.
|
||||
# Else, 'a' starts at 1
|
||||
if [ "$(find ${EXTRACTED} -name '*.wav' | wc -l)" -ge 1 ];then
|
||||
a=$(( $( find "${EXTRACTED}" -name '*.wav' \
|
||||
| awk -F "/" '{print $NF}' \
|
||||
| cut -d'-' -f1 \
|
||||
| sort -n \
|
||||
| tail -n1 ) + 1 ))
|
||||
else
|
||||
a=1
|
||||
fi
|
||||
|
||||
echo "Starting numbering at ${a}"
|
||||
|
||||
for h in "${SCAN_DIRS[@]}";do
|
||||
echo "Creating the TMPFILE"
|
||||
# The TMPFILE is created from each "Selection" txt file BirdNET creates
|
||||
# within each "Analyzed" directory
|
||||
# Field 1: Original WAVE file name
|
||||
# Field 2: Extraction start time in seconds
|
||||
# Field 3: Extraction end time in seconds
|
||||
# Field 4: New WAVE file name to use
|
||||
# Field 5: The species name
|
||||
# Iterates over each "Analyzed" directory
|
||||
for i in $(find ${h} -name '*txt' | sort );do
|
||||
# Iterates over each '.txt' file found in each "Analyzed" directory
|
||||
# to create the TMPFILE
|
||||
sort -k 6n "$i" \
|
||||
| awk '/Spect/ {print}' \
|
||||
>> $TMPFILE
|
||||
done
|
||||
|
||||
# The extraction reads each line of the TMPFILE and sets the variables ffmpeg
|
||||
# will use.
|
||||
while read -r line;do
|
||||
a=$a
|
||||
DATE="$(echo "${line}" \
|
||||
| awk '{print $5}' \
|
||||
| awk -F- '{print $1"-"$2"-"$3}')"
|
||||
OLDFILE="$(echo "${line}" | awk '{print $5}')"
|
||||
START="$(echo "${line}" | awk '{print $6}')"
|
||||
END="$(echo "${line}" | awk '{print $7}')"
|
||||
SPECIES=""$(echo ${line//\'} \
|
||||
| awk '{for(i=11;i<=NF;++i)printf $i""FS ; print ""}' \
|
||||
| cut -d'0' -f1 \
|
||||
| xargs)""
|
||||
NEWFILE="${SPECIES// /_}-${OLDFILE}"
|
||||
NEWSPECIES_BYDATE="${EXTRACTED}/By_Date/${DATE}/${SPECIES// /_}"
|
||||
NEWSPECIES_BYSPEC="${EXTRACTED}/By_Species/${SPECIES// /_}"
|
||||
|
||||
# If the extracted file already exists, increment the 'a' variable once
|
||||
# but move onto the next line of the TMPFILE for extraction.
|
||||
if [[ -f "${NEWSPECIES_BYDATE}/${a}-${NEWFILE}" ]];then
|
||||
echo "Extraction exists. Moving on"
|
||||
a=$((a+1))
|
||||
continue
|
||||
fi
|
||||
|
||||
|
||||
echo "Checking for ${h}/${OLDFILE}"
|
||||
# Before extracting the "Selection," the script checks to be sure the
|
||||
# original WAVE file still exists.
|
||||
[[ -f "${h}/${OLDFILE}" ]] || continue
|
||||
|
||||
|
||||
echo "Checking for ${NEWSPECIES_BYDATE}"
|
||||
# If a directory does not already exist for the species (by date),
|
||||
# it is created
|
||||
[[ -d "${NEWSPECIES_BYDATE}" ]] || mkdir -p "${NEWSPECIES_BYDATE}"
|
||||
|
||||
|
||||
echo "Checking for ${NEWSPECIES_BYSPEC}"
|
||||
# If a directory does not already exist for the species (by-species),
|
||||
# it is created.
|
||||
[[ -d "${NEWSPECIES_BYSPEC}" ]] || mkdir -p "${NEWSPECIES_BYSPEC}"
|
||||
|
||||
|
||||
# If there are already 20 extracted entries for a given species
|
||||
# for today, remove the oldest file and create the new one.
|
||||
if [[ "$(find ${NEWSPECIES_BYDATE} | wc -l)" -ge 21 ]];then
|
||||
echo "20 ${SPECIES}s, already! Removing the oldest by-date and making a new one"
|
||||
cd ${NEWSPECIES_BYDATE} || exit 1
|
||||
ls -1t . | tail -n +21 | xargs -r rm -vv
|
||||
fi
|
||||
|
||||
echo "Extracting audio . . . "
|
||||
# If the above tests have passed, then the extraction happens.
|
||||
# After creating the extracted files by-date, and a directory tree
|
||||
# structured by-species, symbolic links are made to populate the new
|
||||
# directory.
|
||||
|
||||
ffmpeg -hide_banner -loglevel error -nostdin -i "${h}/${OLDFILE}" \
|
||||
-acodec copy -ss "${START}" -to "${END}"\
|
||||
"${NEWSPECIES_BYDATE}/${a}-${NEWFILE}"
|
||||
if [[ "$(find ${NEWSPECIES_BYSPEC} | wc -l)" -ge 21 ]];then
|
||||
echo "20 ${SPECIES}s, already! Removing the oldest by-species and making a new one"
|
||||
cd ${NEWSPECIES_BYSPEC} || exit 1
|
||||
ls -1t . | tail -n +21 | xargs -r rm -vv
|
||||
ln -fs "${NEWSPECIES_BYDATE}/${a}-${NEWFILE}"\
|
||||
"${NEWSPECIES_BYSPEC}/${a}-${NEWFILE}"
|
||||
echo "Success! New extraction for ${SPECIES}"
|
||||
else
|
||||
ln -fs "${NEWSPECIES_BYDATE}/${a}-${NEWFILE}"\
|
||||
"${NEWSPECIES_BYSPEC}/${a}-${NEWFILE}"
|
||||
fi
|
||||
|
||||
|
||||
# Finally, 'a' is incremented by one to allow multiple extractions per
|
||||
# species per minute.
|
||||
a=$((a + 1))
|
||||
|
||||
done < "${TMPFILE}"
|
||||
|
||||
echo -e "\n\n\nFINISHED!!! Processed extractions for ${h}"
|
||||
# Once each line of the TMPFILE has been processed, the TMPFILE is emptied
|
||||
# for the next iteration of the for loop.
|
||||
>"${TMPFILE}"
|
||||
|
||||
# Rename files that have been processed so that they are not processed on the
|
||||
# next extraction.
|
||||
[[ -d "${PROCESSED}" ]] || mkdir "${PROCESSED}"
|
||||
echo "Moving processed files to ${PROCESSED}"
|
||||
mv -v ${h}/* ${PROCESSED} || continue
|
||||
done
|
||||
|
||||
echo "Linking Processed files to "${EXTRACTED}/Processed" web directory"
|
||||
# After all audio extractions have taken place, a directory is created to house
|
||||
# the original WAVE and .txt files used for this extraction processs.
|
||||
if [[ ! -L ${EXTRACTED}/Processed ]] || [[ ! -e ${EXTRACTED}/Processed ]];then
|
||||
ln -sf ${PROCESSED} ${EXTRACTED}/Processed
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# That's all!
|
||||
echo "Finished -- the extracted sections are in:
|
||||
$(find -L ${EXTRACTED} -maxdepth 1)"
|
||||
Executable
+123
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env bash
|
||||
# Install BirdNET script
|
||||
#set -x # debugging
|
||||
set -e # exit installation if anything fails
|
||||
my_dir=$(realpath $(dirname $0))
|
||||
trap '${my_dir}/dump_logs.sh && echo -e "\n\nExiting the installation. Goodbye!" && exit 1' SIGINT
|
||||
cd $my_dir || exit 1
|
||||
|
||||
if [ "$(uname -m)" != "aarch64" ];then
|
||||
echo "BirdNET-Lite requires a 64-bit OS.
|
||||
It looks like your operating system is using $(uname -m),
|
||||
but would need to be aarch64.
|
||||
Please take a look at https://birdnetwiki.pmcgui.xyz for more
|
||||
information"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#Install/Configure /etc/birdnet/birdnet.conf
|
||||
./install_config.sh || exit 1
|
||||
sudo ./install_services.sh || exit 1
|
||||
source /etc/birdnet/birdnet.conf
|
||||
|
||||
CODE="$(awk '/_warning_/ {print $NF}' /tmp/cookie)"
|
||||
TFLITE_URL="https://drive.google.com/uc?export=download&id=1dlEbugFDJXs-YDBCUC6WjADVtIttWxZA"
|
||||
TF_COOKIE="https://drive.google.com/uc?export=download&confirm=${CODE}&id=1dlEbugFDJXs-YDBCUC6WjADVtIttWxZA"
|
||||
APT_DEPS=(swig ffmpeg wget unzip curl cmake make)
|
||||
LIBS_MODULES=(libjpeg-dev zlib1g-dev python3-dev python3-pip)
|
||||
|
||||
install_deps() {
|
||||
echo " Checking dependencies"
|
||||
sudo apt update &> /dev/null
|
||||
for i in "${LIBS_MODULES[@]}";do
|
||||
if [ $(apt list --installed 2>/dev/null | grep "$i" | wc -l) -le 0 ];then
|
||||
echo " Installing $i"
|
||||
sudo apt -y install ${i} &> /dev/null
|
||||
else
|
||||
echo " $i is installed!"
|
||||
fi
|
||||
done
|
||||
|
||||
for i in "${APT_DEPS[@]}";do
|
||||
if ! which $i &>/dev/null ;then
|
||||
echo " Installing $i"
|
||||
sudo apt -y install ${i} &> /dev/null
|
||||
else
|
||||
echo " $i is installed!"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
install_birdnet() {
|
||||
set -e
|
||||
cd ~/BirdNET-Lite || exit 1
|
||||
echo "Upgrading pip, whell, and setuptools"
|
||||
sudo pip3 install --upgrade pip wheel setuptools
|
||||
echo "Fetching the TFLite pre-built binaries"
|
||||
curl -sc /tmp/cookie ${TFLITE_URL} > /dev/null
|
||||
curl -Lb /tmp/cookie ${TF_COOKIE} -o tflite_runtime-2.6.0-cp37-none-linux_aarch64.whl
|
||||
echo "Installing the new TFLite bin wheel"
|
||||
sudo pip3 install --upgrade tflite_runtime-2.6.0-cp37-none-linux_aarch64.whl
|
||||
echo "Installing colorama==0.4.4"
|
||||
sudo pip3 install colorama==0.4.4
|
||||
echo "Installing librosa"
|
||||
sudo pip3 install librosa
|
||||
}
|
||||
|
||||
echo "
|
||||
This script will do the following:
|
||||
#1: Install the following BirdNET system dependencies:
|
||||
- ffmpeg
|
||||
- swig
|
||||
- libjpeg-dev
|
||||
- zlib1g-dev
|
||||
- python3-dev
|
||||
- curl
|
||||
- cmake
|
||||
- make
|
||||
- wget
|
||||
#2: Copies the systemd .service and .mount files and enables those chosen
|
||||
#3: Adds cron environments and jobs chosen"
|
||||
|
||||
echo
|
||||
read -sp "\
|
||||
Be sure you have read the software license before installing. This is
|
||||
available in the BirdNET-Lite directory as "LICENSE"
|
||||
If you DO NOT want to install BirdNET and the birdnet_analysis.service,
|
||||
press Ctrl+C to cancel. If you DO wish to install BirdNET and the
|
||||
birdnet_analysis.service, press ENTER to continue with the installation."
|
||||
echo
|
||||
echo
|
||||
|
||||
[ -d ${RECS_DIR} ] || mkdir -p ${RECS_DIR} &> /dev/null
|
||||
|
||||
install_deps
|
||||
if [ ! -d ${VENV} ];then
|
||||
install_birdnet
|
||||
fi
|
||||
|
||||
echo " BirdNet is installed!!
|
||||
|
||||
To start the service manually, issue:
|
||||
'sudo systemctl start birdnet_analysis'
|
||||
To monitor the service logs, issue:
|
||||
'journalctl -fu birdnet_analysis'
|
||||
To stop the service manually, issue:
|
||||
'sudo systemctl stop birdnet_analysis'
|
||||
To stop and disable the service, issue:
|
||||
'sudo systemctl disable --now birdnet_analysis.service'
|
||||
|
||||
Visit
|
||||
http://birdnetsystem.local to see your extractions,
|
||||
http://birdlog.local to see the log output of the birdnet_analysis.service,
|
||||
http://extractionlog.local to see the log output of the extraction.service, and
|
||||
http://birdstats.local to see the BirdNET-Lite Report"
|
||||
echo
|
||||
read -n1 -p " Would you like to run the birdnet_analysis.service now?" YN
|
||||
echo
|
||||
case $YN in
|
||||
[Yy] ) sudo systemctl start birdnet_analysis.service \
|
||||
&& journalctl -fu birdnet_analysis;;
|
||||
* ) echo " Thanks for installing BirdNET-Lite!!
|
||||
I hope it was helpful!";;
|
||||
esac
|
||||
Executable
+412
@@ -0,0 +1,412 @@
|
||||
#!/usr/bin/env bash
|
||||
# Creates and installs the /etc/birdnet/birdnet.conf file
|
||||
#set -x # Uncomment to enable debugging
|
||||
set -e
|
||||
trap 'exit 1' SIGINT SIGHUP
|
||||
|
||||
my_dir=$(realpath $(dirname $0))
|
||||
BIRDNET_CONF="$(dirname ${my_dir})/birdnet.conf"
|
||||
|
||||
get_RECS_DIR() {
|
||||
read -p "What is the full path to your recordings directory (locally)? " RECS_DIR
|
||||
}
|
||||
|
||||
get_LATITUDE() {
|
||||
read -p "What is the latitude where the recordings were made? " LATITUDE
|
||||
}
|
||||
|
||||
get_LONGITUDE() {
|
||||
read -p "What is the longitude where the recordings were made? " LONGITUDE
|
||||
}
|
||||
|
||||
get_DO_EXTRACTIONS() {
|
||||
while true; do
|
||||
read -n1 -p "Do you want this device to perform the extractions? " DO_EXTRACTIONS
|
||||
echo
|
||||
case $DO_EXTRACTIONS in
|
||||
[Yy] ) break;;
|
||||
[Nn] ) break;;
|
||||
* ) echo "You must answer with Yes or No (y or n)";;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
get_DO_RECORDING() {
|
||||
while true; do
|
||||
read -n1 -p "Is this device also doing the recording? " DO_RECORDING
|
||||
echo
|
||||
case $DO_RECORDING in
|
||||
[Yy] ) break;;
|
||||
[Nn] ) break;;
|
||||
* ) echo "You must answer with Yes or No (y or n)";;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
get_REMOTE() {
|
||||
while true; do
|
||||
read -n1 -p "Are the recordings mounted on a remote file system?" REMOTE
|
||||
echo
|
||||
case $REMOTE in
|
||||
[Yy] )
|
||||
read -p "What is the remote hostname or IP address for the recorder? " REMOTE_HOST
|
||||
read -p "Who is the remote user? " REMOTE_USER
|
||||
read -p "What is the absolute path of the recordings directory on the remote host? " REMOTE_RECS_DIR
|
||||
break;;
|
||||
[Nn] ) break;;
|
||||
* ) echo "Please answer Yes or No (y or n)";;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
get_EXTRACTIONS_URL() {
|
||||
while true;do
|
||||
read -n1 -p "Would you like to access the extractions via a web browser?
|
||||
|
||||
*Note: It is recommended, (but not required), that you run the web
|
||||
server on the same host that does the extractions. If the extraction
|
||||
service and web server are on different hosts, the \"By_Species\" and
|
||||
\"Processed\" symbolic links won't work. The \"By-Date\" extractions,
|
||||
however, will work as expected." CADDY_SERVICE
|
||||
echo
|
||||
case $CADDY_SERVICE in
|
||||
[Yy] ) read -p "What URL would you like to publish the extractions to?
|
||||
*Note: Set this to http://localhost if you do not want to make the
|
||||
extractions publically available: " EXTRACTIONS_URL
|
||||
get_CADDY_PWD
|
||||
get_ICE_PWD
|
||||
break;;
|
||||
[Nn] ) EXTRACTIONS_URL= CADDY_PWD= ICE_PWD=;break;;
|
||||
* ) echo "Please answer Yes or No";;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
get_CADDY_PWD() {
|
||||
if [ -z ${CADDY_PWD} ]; then
|
||||
while true; do
|
||||
read -p "Please set a password to protect your data: " CADDY_PWD
|
||||
case $CADDY_PWD in
|
||||
"" ) echo "The password cannot be empty. Please try again.";;
|
||||
* ) break;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
get_ICE_PWD() {
|
||||
if [ ! -z ${CADDY_PWD} ] && [[ ${DO_RECORDING} =~ [Yy] ]];then
|
||||
while true; do
|
||||
read -n1 -p "Would you like to enable the live audio streaming service?" LIVE_STREAM
|
||||
echo
|
||||
case $LIVE_STREAM in
|
||||
[Yy] )
|
||||
read -p "Please set the icecast password. Use only alphanumeric characters." ICE_PWD
|
||||
echo
|
||||
case ${ICE_PWD} in
|
||||
"" ) echo "The password cannot be empty. Please try again.";;
|
||||
*) break;;
|
||||
esac
|
||||
break;;
|
||||
[Nn] ) break;;
|
||||
* ) echo "You must answer Yes or No (y or n).";;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
get_PUSHED() {
|
||||
while true; do
|
||||
read -n1 -p "Do you have a free App key to receive mobile notifications via Pushed.co?" YN
|
||||
echo
|
||||
case $YN in
|
||||
[Yy] ) read -p "Enter your Pushed.co App Key: " PUSHED_APP_KEY
|
||||
read -p "Enter your Pushed.co App Key Secret: " PUSHED_APP_SECRET
|
||||
break;;
|
||||
[Nn] ) PUSHED_APP_KEY=
|
||||
PUSHED_APP_SECRET=
|
||||
break;;
|
||||
* ) echo "A simple Yea or Nay will do";;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
get_INSTALL_NOMACHINE() {
|
||||
while true; do
|
||||
read -n1 -p "Would you like to also install NoMachine for remote desktop access?" INSTALL_NOMACHINE
|
||||
echo
|
||||
case $INSTALL_NOMACHINE in
|
||||
[Yy] ) break;;
|
||||
[Nn] ) break;;
|
||||
* ) echo "You must answer with Yes or No (y or n)";;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
get_CHANNELS() {
|
||||
[ -f $(dirname ${my_dir})/soundcard_params.txt ] || touch $(dirname ${my_dir})/soundcard_params.txt
|
||||
SOUND_PARAMS=$(dirname ${my_dir})/soundcard_params.txt
|
||||
SOUND_CARD="$(sudo -u ${USER} aplay -L \
|
||||
| awk -F, '/^hw:/ {print $1}' \
|
||||
| grep -ve 'vc4' -e 'Head' -e 'PCH' \
|
||||
| uniq)"
|
||||
script -c "arecord -D ${SOUND_CARD} --dump-hw-params" -a "${SOUND_PARAMS}" &> /dev/null
|
||||
|
||||
CHANNELS=$(awk '/CHANN/ { print $2 }' "${SOUND_PARAMS}" | sed 's/\r$//')
|
||||
echo "Number of channels available: ${CHANNELS}"
|
||||
}
|
||||
|
||||
|
||||
configure() {
|
||||
get_RECS_DIR
|
||||
get_LATITUDE
|
||||
get_LONGITUDE
|
||||
get_DO_EXTRACTIONS
|
||||
get_DO_RECORDING
|
||||
get_REMOTE
|
||||
get_EXTRACTIONS_URL
|
||||
get_PUSHED
|
||||
get_INSTALL_NOMACHINE
|
||||
get_CHANNELS
|
||||
}
|
||||
|
||||
install_birdnet_conf() {
|
||||
cat << EOF > $(dirname ${my_dir})/birdnet.conf
|
||||
################################################################################
|
||||
# Configuration settings for BirdNET as a service #
|
||||
################################################################################
|
||||
INSTALL_DATE="$(date "+%D")"
|
||||
#___________The four variables below are the only that are required.___________#
|
||||
|
||||
## BIRDNET_USER should be the non-root user systemd should use to execute each
|
||||
## service.
|
||||
|
||||
BIRDNET_USER=${USER}
|
||||
|
||||
## RECS_DIR is the location birdnet_analysis.service will look for the data-set
|
||||
## it needs to analyze. Be sure this directory is readable and writable for
|
||||
## the BIRDNET_USER. If you are going to be accessing a remote data-set, you
|
||||
## still need to set this, as this will be where the remote directory gets
|
||||
## mounted locally. See REMOTE_RECS_DIR below for mounting remote data-sets.
|
||||
|
||||
RECS_DIR=${RECS_DIR}
|
||||
|
||||
## LATITUDE and LONGITUDE are self-explanatroy. Find them easily at
|
||||
## maps.google.com. Only go to the thousanths place for these variables
|
||||
## Example: these coordinates would indicate the Eiffel Tower in Paris, France.
|
||||
## LATITUDE=48.858
|
||||
## LONGITUDE=2.294
|
||||
|
||||
LATITUDE="${LATITUDE}"
|
||||
LONGITUDE="${LONGITUDE}"
|
||||
|
||||
################################################################################
|
||||
#------------------------------ Extraction Service ---------------------------#
|
||||
|
||||
# Keep this EMPTY if you do not want this device to perform the extractions #
|
||||
|
||||
## DO_EXTRACTIONS is simply a setting for enabling the extraction.service.
|
||||
## Set this to Y or y to enable extractions.
|
||||
|
||||
DO_EXTRACTIONS=${DO_EXTRACTIONS}
|
||||
|
||||
################################################################################
|
||||
#----------------------------- Recording Service ----------------------------#
|
||||
|
||||
# Keep this EMPTY if you do not want this device to perform the recording. #
|
||||
|
||||
## DO_RECORDING is simply a setting for enabling the 24/7 birdnet_recording.service.
|
||||
## Set this to Y or y to enable recording.
|
||||
|
||||
DO_RECORDING=${DO_RECORDING}
|
||||
|
||||
################################################################################
|
||||
#----------------- Mounting a remote directory with systemd -----------------#
|
||||
#_______________The four variables below can be set to enable a_______________#
|
||||
#___________________systemd.mount for analysis, extraction,____________________#
|
||||
#______________________________or file-serving_________________________________#
|
||||
|
||||
# Leave these settings EMPTY if your data-set is local. #
|
||||
|
||||
## REMOTE is simply a setting for enabling the systemd.mount to use a remote
|
||||
## filesystem for the data storage and service.
|
||||
## Set this to Y or y to enable the systemd.mount.
|
||||
|
||||
REMOTE=${REMOTE}
|
||||
|
||||
## REMOTE_HOST is the IP address, hostname, or domain name SSH should use to
|
||||
## connect for FUSE to mount its remote directories locally.
|
||||
|
||||
REMOTE_HOST=${REMOTE_HOST}
|
||||
|
||||
## REMOTE_USER is the user SSH will use to connect to the REMOTE_HOST.
|
||||
|
||||
REMOTE_USER=${REMOTE_USER}
|
||||
|
||||
## REMOTE_RECS_DIR is the directory on the REMOTE_HOST which contains the
|
||||
## data-set SSHFS should mount to this system for local access. This is NOT the
|
||||
## directory where you will access the data on this machine. See RECS_DIR for
|
||||
## that.
|
||||
|
||||
REMOTE_RECS_DIR=${REMOTE_RECS_DIR}
|
||||
|
||||
################################################################################
|
||||
#----------------------- Web-hosting/Caddy File-server -----------------------#
|
||||
#__________The two variables below can be set to enable web access_____________#
|
||||
#____________to your data,(e.g., extractions, raw data, live___________________#
|
||||
#______________audio stream, BirdNET.selection.txt files)______________________#
|
||||
|
||||
# Leave these EMPTY if you do not want to enable web access #
|
||||
|
||||
## EXTRACTIONS_URL is the URL where the extractions, data-set, and live-stream
|
||||
## will be web-hosted. If you do not own a domain, or would just prefer to keep
|
||||
## BirdNET-Lite on your local network, you can set this to http://localhost.
|
||||
## Setting this (even to http://localhost) will also allow you to enable the
|
||||
## GoTTY web logging features below.
|
||||
|
||||
EXTRACTIONS_URL=${EXTRACTIONS_URL}
|
||||
|
||||
## CADDY_PWD is the plaintext password (that will be hashed) and used to access
|
||||
## the "Processed" directory and live audio stream. This MUST be set if you
|
||||
## choose to enable this feature.
|
||||
|
||||
CADDY_PWD=${CADDY_PWD}
|
||||
|
||||
################################################################################
|
||||
#------------------------- Live Audio Stream --------------------------------#
|
||||
#_____________The variable below configures/enables the live___________________#
|
||||
#_____________________________audio stream.____________________________________#
|
||||
|
||||
# Keep this EMPTY if you do not wish to enable the live stream #
|
||||
# or if this device is not doing the recording #
|
||||
|
||||
## ICE_PWD is the password that icecast2 will use to authenticate ffmpeg as a
|
||||
## trusted source for the stream. You will never need to enter this manually
|
||||
## anywhere other than here.
|
||||
|
||||
ICE_PWD=${ICE_PWD}
|
||||
|
||||
################################################################################
|
||||
#------------------- Mobile Notifications via Pushed.co ---------------------#
|
||||
#____________The two variables below enable mobile notifications_______________#
|
||||
#_____________See https://pushed.co/quick-start-guide to get___________________#
|
||||
#_________________________these values for your app.___________________________#
|
||||
|
||||
# Keep these EMPTY if haven't setup a Pushed.co App yet. #
|
||||
|
||||
## Pushed.co App Key and App Secret
|
||||
|
||||
PUSHED_APP_KEY=${PUSHED_APP_KEY}
|
||||
PUSHED_APP_SECRET=${PUSHED_APP_SECRET}
|
||||
|
||||
################################################################################
|
||||
#------------------------------- NoMachine ----------------------------------#
|
||||
#_____________The variable below can be set include NoMachine__________________#
|
||||
#_________________remote desktop software to be installed._____________________#
|
||||
|
||||
# Keep this EMPTY if you do not want to install NoMachine. #
|
||||
|
||||
## INSTALL_NOMACHINE is simply a setting that can be enabled to install
|
||||
## NoMachine alongside the BirdNET-Lite for remote desktop access. This in-
|
||||
## staller assumes personal use. Please reference the LICENSE file included
|
||||
## in this repository for more information.
|
||||
## Set this to Y or y to install NoMachine alongside the BirdNET-Lite
|
||||
|
||||
INSTALL_NOMACHINE=${INSTALL_NOMACHINE}
|
||||
|
||||
################################################################################
|
||||
#-------------------------------- Defaults ----------------------------------#
|
||||
#________The six variables below are default settings that you (probably)______#
|
||||
#__________________don't need to change at all, but can._______________________#
|
||||
|
||||
## REC_CARD is the sound card you would want the birdnet_recording.service to
|
||||
## use. This setting is irrelevant if you are not planning on doing data
|
||||
## collection via recording on this machine. The command substitution below
|
||||
## looks for a USB microphone's dsnoop alsa device. The dsnoop device lets
|
||||
## birdnet_recording.service and livestream.service share the raw audio stream
|
||||
## from the microphone. If you would like to use a different microphone than
|
||||
## what this produces, or if your microphone does not support creating a
|
||||
## dsnoop device, you can set this explicitly from a list of the available
|
||||
## devices from the output of running 'aplay -L'
|
||||
|
||||
REC_CARD="\$(sudo -u pi aplay -L \
|
||||
| grep dsnoop \
|
||||
| cut -d, -f1 \
|
||||
| grep -ve 'vc4' -e 'Head' -e 'PCH' \
|
||||
| uniq)"
|
||||
|
||||
## PROCESSED is the directory where the formerly 'Analyzed' files are moved
|
||||
## after extractions have been made from them. This includes both WAVE and
|
||||
## BirdNET.selection.txt files.
|
||||
|
||||
PROCESSED=${RECS_DIR}/Processed
|
||||
|
||||
## EXTRACTED is the directory where the extracted audio selections are moved.
|
||||
|
||||
EXTRACTED=${RECS_DIR}/Extracted
|
||||
|
||||
## IDFILE is the file that keeps a complete list of every spececies that
|
||||
## BirdNET has identified from your data-set. It is persistent across
|
||||
## data-sets, so would need to be whiped clean through deleting or renaming
|
||||
## it. A backup is automatically made from this variable each time it is
|
||||
## updated (structure: ${IDFILE}.bak), and would also need to be removed
|
||||
## or renamed to start a new file between data-sets. Alternately, you can
|
||||
## change this variable between data-sets to preserve records of disparate
|
||||
## data-sets according to name.
|
||||
|
||||
IDFILE=${HOME}/BirdNET-Lite/IdentifiedSoFar.txt
|
||||
|
||||
## OVERLAP is the value in seconds which BirdNET should use when analyzing
|
||||
## the data. The values must be between 0.0-2.9.
|
||||
|
||||
OVERLAP="0.0"
|
||||
|
||||
## CONFIDENCE is the minimum confidence level from 0.0-1.0 BirdNET's analysis
|
||||
## should reach before creating an entry in the BirdNET.selection.txt file.
|
||||
## Don't set this to 1.0 or you won't have any results.
|
||||
|
||||
CONFIDENCE="0.7"
|
||||
|
||||
################################################################################
|
||||
#------------------------------ Auto-Generated ------------------------------#
|
||||
#_____________________The variables below are auto-generated___________________#
|
||||
#______________________________during installation_____________________________#
|
||||
|
||||
## CHANNELS holds the variabel that corresponds to the number of channels the
|
||||
## sound card supports.
|
||||
|
||||
CHANNELS=${CHANNELS}
|
||||
|
||||
# Don't touch the three below
|
||||
|
||||
## ANALYZED is where the extraction.service looks for audio and
|
||||
## BirdNET.selection.txt files after they have been processed by the
|
||||
## birdnet_analysis.service. This is NOT where the analyzed files are moved --
|
||||
## analyzed files are always created within the same directory
|
||||
## birdnet_analysis.service finds them.
|
||||
|
||||
ANALYZED=${RECS_DIR}/*/*Analyzed
|
||||
|
||||
## SYSTEMD_MOUNT is created from the RECS_DIR variable to comply with systemd
|
||||
## mount naming requirements.
|
||||
|
||||
SYSTEMD_MOUNT=$(echo ${RECS_DIR#/} | tr / -).mount
|
||||
|
||||
## VENV is the virtual environment where the the BirdNET python build is found,
|
||||
## i.e, VENV is the virtual environment miniforge built for BirdNET.
|
||||
|
||||
VENV=$(dirname ${my_dir})/miniforge/envs/birdnet
|
||||
EOF
|
||||
[ -d /etc/birdnet ] || sudo mkdir /etc/birdnet
|
||||
sudo ln -sf $(dirname ${my_dir})/birdnet.conf /etc/birdnet/birdnet.conf
|
||||
}
|
||||
|
||||
# Checks for a birdnet.conf file in the BirdNET-Lite directory for a
|
||||
# non-interactive installation. Otherwise,the installation is interactive.
|
||||
if [ -f ${BIRDNET_CONF} ];then
|
||||
source ${BIRDNET_CONF}
|
||||
install_birdnet_conf
|
||||
else
|
||||
configure
|
||||
install_birdnet_conf
|
||||
fi
|
||||
Executable
+441
@@ -0,0 +1,441 @@
|
||||
#!/usr/bin/env bash
|
||||
# This installs the services that have been selected
|
||||
#set -x # Uncomment to enable debugging
|
||||
trap 'rm -f ${TMPFILE}' EXIT
|
||||
trap 'exit 1' SIGINT SIGHUP
|
||||
my_dir=$(realpath $(dirname $0))
|
||||
TMPFILE=$(mktemp)
|
||||
nomachine_url="https://download.nomachine.com/download/7.6/Arm/nomachine_7.6.2_3_arm64.deb"
|
||||
gotty_url="https://github.com/yudai/gotty/releases/download/v1.0.1/gotty_linux_arm.tar.gz"
|
||||
CONFIG_FILE="$(dirname ${my_dir})/birdnet.conf"
|
||||
|
||||
install_scripts() {
|
||||
echo "Installing BirdNET-Lite scripts to /usr/local/bin"
|
||||
ln -sf ${my_dir}/* /usr/local/bin/
|
||||
}
|
||||
|
||||
install_birdnet_analysis() {
|
||||
echo "Installing the birdnet_analysis.service"
|
||||
cat << EOF > /etc/systemd/system/birdnet_analysis.service
|
||||
[Unit]
|
||||
Description=BirdNET Analysis
|
||||
[Service]
|
||||
Restart=always
|
||||
RuntimeMaxSec=10800
|
||||
Type=simple
|
||||
RestartSec=3
|
||||
User=${USER}
|
||||
ExecStart=/usr/local/bin/birdnet_analysis.sh
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable birdnet_analysis.service
|
||||
}
|
||||
|
||||
install_extraction_service() {
|
||||
echo "Installing the extraction.service and extraction.timer"
|
||||
cat << EOF > /etc/systemd/system/extraction.service
|
||||
[Unit]
|
||||
Description=BirdNET BirdSound Extraction
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
Type=simple
|
||||
User=${USER}
|
||||
ExecStart=/usr/local/bin/extract_new_birdsounds.sh
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
cat << EOF > /etc/systemd/system/extraction.timer
|
||||
[Unit]
|
||||
Description=BirdNET BirdSound Extraction Timer
|
||||
Requires=extraction.service
|
||||
|
||||
[Timer]
|
||||
Unit=extraction.service
|
||||
OnCalendar=*:0/10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable extraction.timer
|
||||
systemctl enable extraction.service
|
||||
echo "Adding the species_updater.cron"
|
||||
if ! crontab -u ${USER} -l &> /dev/null;then
|
||||
crontab -u ${USER} $(dirname ${my_dir})/templates/species_updater.cron &> /dev/null
|
||||
else
|
||||
crontab -u ${USER} -l > ${TMPFILE}
|
||||
cat $(dirname ${my_dir})/templates/species_updater.cron >> ${TMPFILE}
|
||||
crontab -u ${USER} "${TMPFILE}" &> /dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
create_necessary_dirs() {
|
||||
echo "Creating necessary directories"
|
||||
[ -d ${EXTRACTED} ] || sudo -u ${USER} mkdir -p ${EXTRACTED}
|
||||
[ -d ${EXTRACTED}/By_Date ] || sudo -u ${USER} mkdir -p ${EXTRACTED}/By_Date
|
||||
[ -d ${EXTRACTED}/By_Species ] || sudo -u ${USER} mkdir -p ${EXTRACTED}/By_Species
|
||||
[ -d ${PROCESSED} ] || sudo -u ${USER} mkdir -p ${PROCESSED}
|
||||
}
|
||||
|
||||
install_alsa() {
|
||||
echo "Checking for alsa-utils"
|
||||
if which arecord &> /dev/null ;then
|
||||
echo "alsa-utils installed"
|
||||
else
|
||||
echo "Installing alsa-utils"
|
||||
apt -qqq update
|
||||
apt install -qqy alsa-utils
|
||||
echo "alsa-utils installed"
|
||||
fi
|
||||
}
|
||||
|
||||
install_recording_service() {
|
||||
echo "Installing birdnet_recording.service"
|
||||
cat << EOF > /etc/systemd/system/birdnet_recording.service
|
||||
[Unit]
|
||||
Description=BirdNET Recording
|
||||
|
||||
[Service]
|
||||
Environment=XDG_RUNTIME_DIR=/run/user/1000
|
||||
Restart=always
|
||||
Type=simple
|
||||
RestartSec=3
|
||||
User=${USER}
|
||||
ExecStart=/usr/local/bin/birdnet_recording.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable birdnet_recording.service
|
||||
}
|
||||
|
||||
|
||||
install_sshfs() {
|
||||
echo "Checking for SSHFS to mount remote filesystem"
|
||||
if ! which sshfs &> /dev/null ;then
|
||||
echo "Installing SSHFS"
|
||||
apt -qqq update
|
||||
apt install -qqqy sshfs
|
||||
fi
|
||||
}
|
||||
|
||||
setup_sshkeys() {
|
||||
echo "Setting up SSH keys for SSHFS"
|
||||
echo "Adding remote host key to ${HOME}/.ssh/known_hosts"
|
||||
ssh-keyscan -H ${REMOTE_HOST} >> ${HOME}/.ssh/known_hosts
|
||||
chown ${USER}:${USER} ${HOME}/.ssh/known_hosts &> /dev/null
|
||||
if [ ! -f ${HOME}/.ssh/id_ed25519.pub ];then
|
||||
echo "Creating a new key"
|
||||
ssh-keygen -t ed25519 -f ${HOME}/.ssh/id_ed25519 -P ""
|
||||
fi
|
||||
chown -R ${USER}:${USER} ${HOME}/.ssh/ &> /dev/null
|
||||
echo "Copying public key to ${REMOTE_HOST}"
|
||||
ssh-copy-id ${REMOTE_USER}@${REMOTE_HOST}
|
||||
}
|
||||
|
||||
install_systemd_mount() {
|
||||
echo "Installing systemd.mount"
|
||||
cat << EOF > /etc/systemd/system/${SYSTEMD_MOUNT}
|
||||
[Unit]
|
||||
Description=Mount remote fs with sshfs
|
||||
DefaultDependencies=no
|
||||
Conflicts=umount.target
|
||||
After=network-online.target
|
||||
Before=umount.target
|
||||
Wants=network-online.target
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
[Mount]
|
||||
What=${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_RECS_DIR}
|
||||
Where=${RECS_DIR}
|
||||
Type=fuse.sshfs
|
||||
Options=delay_connect,_netdev,allow_other,IdentityFile=${HOME}/.ssh/id_ed25519,reconnect,ServerAliveInterval=30,ServerAliveCountMax=5,x-systemd.automount,uid=1000,gid=1000
|
||||
TimeoutSec=60
|
||||
EOF
|
||||
}
|
||||
|
||||
install_caddy() {
|
||||
if ! which caddy &> /dev/null ;then
|
||||
echo "Installing Caddy"
|
||||
curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/caddy/stable/setup.deb.sh' \
|
||||
| sudo -E bash
|
||||
apt -qq update
|
||||
apt install -qqy caddy
|
||||
systemctl enable --now caddy
|
||||
else
|
||||
echo "Caddy is installed"
|
||||
systemctl enable --now caddy
|
||||
fi
|
||||
}
|
||||
|
||||
install_Caddyfile() {
|
||||
echo "Installing the Caddyfile"
|
||||
[ -d /etc/caddy ] || mkdir /etc/caddy
|
||||
cp $(dirname ${my_dir})/templates/index.html ${EXTRACTED}/
|
||||
if [ -f /etc/caddy/Caddyfile ];then
|
||||
cp /etc/caddy/Caddyfile{,.original}
|
||||
fi
|
||||
HASHWORD=$(caddy hash-password -plaintext ${CADDY_PWD})
|
||||
cat << EOF > /etc/caddy/Caddyfile
|
||||
${EXTRACTIONS_URL} {
|
||||
root * ${EXTRACTED}
|
||||
file_server browse
|
||||
basicauth /Processed* {
|
||||
birdnet ${HASHWORD}
|
||||
}
|
||||
basicauth /stream {
|
||||
birdnet ${HASHWORD}
|
||||
}
|
||||
reverse_proxy /stream localhost:8000
|
||||
}
|
||||
|
||||
http://birdnetsystem.local {
|
||||
root * ${EXTRACTED}
|
||||
file_server browse
|
||||
basicauth /Processed* {
|
||||
birdnet ${HASHWORD}
|
||||
}
|
||||
basicauth /stream {
|
||||
birdnet ${HASHWORD}
|
||||
}
|
||||
reverse_proxy /stream localhost:8000
|
||||
}
|
||||
|
||||
http://birdlog.local {
|
||||
reverse_proxy localhost:8080
|
||||
}
|
||||
|
||||
http://extractionlog.local {
|
||||
reverse_proxy localhost:8888
|
||||
}
|
||||
|
||||
http://birdstats.local {
|
||||
reverse_proxy localhost:9090
|
||||
}
|
||||
EOF
|
||||
systemctl reload caddy
|
||||
}
|
||||
|
||||
install_avahi_aliases() {
|
||||
echo "Installing Avahi Services"
|
||||
if ! which avahi-publish &> /dev/null; then
|
||||
echo "Installing avahi-utils"
|
||||
apt install -y avahi-utils &> /dev/null
|
||||
fi
|
||||
echo "Installing avahi-alias service"
|
||||
cat << 'EOF' > /etc/systemd/system/avahi-alias@.service
|
||||
[Unit]
|
||||
Description=Publish %I as alias for %H.local via mdns
|
||||
After=network.target network-online.target
|
||||
Requires=network-online.target
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
Type=simple
|
||||
ExecStart=/bin/bash -c "/usr/bin/avahi-publish -a -R %I $(avahi-resolve -4 -n %H.local | cut -f 2)"
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable --now avahi-alias@birdnetsystem.local.service
|
||||
systemctl enable --now avahi-alias@birdlog.local.service
|
||||
systemctl enable --now avahi-alias@extractionlog.local.service
|
||||
systemctl enable --now avahi-alias@birdstats.local.service
|
||||
}
|
||||
|
||||
install_gotty_logs() {
|
||||
echo "Installing GoTTY logging"
|
||||
if ! which gotty &> /dev/null;then
|
||||
echo "Installing GoTTY binary"
|
||||
wget -c ${gotty_url} -O - | tar -xz -C /usr/local/bin/
|
||||
fi
|
||||
sudo -u ${USER} ln -sf $(dirname ${my_dir})/templates/gotty \
|
||||
${HOME}/.gotty
|
||||
echo "Installing the birdnet_log.service"
|
||||
cat << EOF > /etc/systemd/system/birdnet_log.service
|
||||
[Unit]
|
||||
Description=BirdNET Analysis Log
|
||||
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
Type=simple
|
||||
User=${USER}
|
||||
Environment=TERM=xterm-256color
|
||||
ExecStart=/usr/local/bin/gotty -p 8080 --title-format "BirdNET-Lite Log" journalctl -fu birdnet_analysis.service
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable --now birdnet_log.service
|
||||
echo "Installing the extraction_log.service"
|
||||
cat << EOF > /etc/systemd/system/extraction_log.service
|
||||
[Unit]
|
||||
Description=BirdNET Extraction Log
|
||||
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
Type=simple
|
||||
User=${USER}
|
||||
Environment=TERM=xterm-256color
|
||||
ExecStart=/usr/local/bin/gotty -p 8888 --title-format "Extractions Log" journalctl -fu extraction.service
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable --now extraction_log.service
|
||||
echo "Installing the birdstats.service"
|
||||
cat << EOF > /etc/systemd/system/birdstats.service
|
||||
[Unit]
|
||||
Description=BirdNET Statistics Log
|
||||
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
Type=simple
|
||||
User=${USER}
|
||||
Environment=TERM=xterm-256color
|
||||
ExecStart=/usr/local/bin/gotty -p 9090 --title-format "BirdNET-Lite Statistics" /usr/local/bin/birdnet_stats.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable --now birdstats.service
|
||||
}
|
||||
|
||||
|
||||
install_icecast() {
|
||||
if ! which icecast2;then
|
||||
echo "Installing IceCast2"
|
||||
apt -qq update
|
||||
echo "icecast2 icecast2/icecast-setup boolean false" | debconf-set-selections
|
||||
apt install -qqy icecast2
|
||||
config_icecast
|
||||
systemctl enable --now icecast2
|
||||
/etc/init.d/icecast2 start
|
||||
else
|
||||
echo "Icecast2 is installed"
|
||||
config_icecast
|
||||
systemctl enable --now icecast2
|
||||
/etc/init.d/icecast2 start
|
||||
fi
|
||||
}
|
||||
|
||||
config_icecast() {
|
||||
if [ -f /etc/icecast2/icecast.xml ];then
|
||||
cp /etc/icecast2/icecast.xml{,.prebirdnetsystem}
|
||||
fi
|
||||
sed -i 's/>admin</>birdnet</g' /etc/icecast2/icecast.xml
|
||||
passwords=("source-" "relay-" "admin-" "master-" "")
|
||||
for i in "${passwords[@]}";do
|
||||
sed -i "s/<${i}password>.*<\/${i}password>/<${i}password>${ICE_PWD}<\/${i}password>/g" /etc/icecast2/icecast.xml
|
||||
done
|
||||
}
|
||||
|
||||
install_livestream_service() {
|
||||
echo "Installing Live Stream service"
|
||||
cat << EOF > /etc/systemd/system/livestream.service
|
||||
[Unit]
|
||||
Description=BirdNET-Lite Live Stream
|
||||
|
||||
[Service]
|
||||
Environment=XDG_RUNTIME_DIR=/run/user/1000
|
||||
Restart=always
|
||||
Type=simple
|
||||
RestartSec=3
|
||||
User=${USER}
|
||||
ExecStart=/usr/local/bin/livestream.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable --now livestream.service
|
||||
}
|
||||
|
||||
install_nomachine() {
|
||||
echo "Installing NoMachine"
|
||||
cd ~
|
||||
curl -s -O "${nomachine_url}"
|
||||
apt install -y ${HOME}/nomachine_7.6.2_3_arm64.deb
|
||||
rm -f ${HOME}/nomachine_7.6.2_3_arm64.deb
|
||||
}
|
||||
|
||||
install_systemd_overrides() {
|
||||
for i in caddy birdnet_analysis extraction birdnet_recording;do
|
||||
if [ -f /etc/systemd/system/${i}.service ];then
|
||||
[ -d /etc/systemd/system/${i}.d ] || mkdir /etc/systemd/system/${i}.d
|
||||
echo "Installing the systemd overrides.conf for the ${i}.service"
|
||||
cat << EOF > /etc/systemd/system/${i}.d/overrides.conf
|
||||
[Unit]
|
||||
After=network.target network-online.target ${SYSTEMD_MOUNT}
|
||||
Requires=network-online.target ${SYSTEMD_MOUNT}
|
||||
EOF
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
install_cleanup_cron() {
|
||||
echo "Installing the cleanup.cron"
|
||||
if ! crontab -u ${USER} -l &> /dev/null;then
|
||||
crontab -u ${USER} $(dirname ${my_dir})/templates/cleanup.cron &> /dev/null
|
||||
else
|
||||
crontab -u ${USER} -l > ${TMPFILE}
|
||||
cat $(dirname ${my_dir})/templates/cleanup.cron >> ${TMPFILE}
|
||||
crontab -u ${USER} "${TMPFILE}" &> /dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
install_selected_services() {
|
||||
install_scripts
|
||||
install_birdnet_analysis
|
||||
|
||||
if [[ "${DO_EXTRACTIONS}" =~ [Yy] ]];then
|
||||
install_extraction_service
|
||||
create_necessary_dirs
|
||||
fi
|
||||
|
||||
if [[ "${DO_RECORDING}" =~ [Yy] ]];then
|
||||
install_alsa
|
||||
install_recording_service
|
||||
fi
|
||||
|
||||
if [[ "${REMOTE}" =~ [Yy] ]];then
|
||||
install_sshfs
|
||||
setup_sshkeys
|
||||
install_systemd_mount
|
||||
fi
|
||||
|
||||
if [ ! -z "${EXTRACTIONS_URL}" ];then
|
||||
install_caddy
|
||||
install_Caddyfile
|
||||
install_avahi_aliases
|
||||
install_gotty_logs
|
||||
fi
|
||||
|
||||
if [ ! -z "${ICE_PWD}" ];then
|
||||
install_icecast
|
||||
install_livestream_service
|
||||
fi
|
||||
|
||||
if [ ! -z "${INSTALL_NOMACHINE}" ];then
|
||||
install_nomachine
|
||||
fi
|
||||
|
||||
if [[ "${REMOTE}" =~ [Yy] ]];then
|
||||
install_systemd_overrides
|
||||
fi
|
||||
|
||||
install_cleanup_cron
|
||||
}
|
||||
|
||||
if [ -f ${CONFIG_FILE} ];then
|
||||
source ${CONFIG_FILE}
|
||||
USER=${BIRDNET_USER}
|
||||
HOME="$(getent passwd ${BIRDNET_USER} | cut -d: -f6)"
|
||||
install_selected_services
|
||||
else
|
||||
echo "Unable to find a configuration file. Please make sure that $CONFIG_FILE exists."
|
||||
fi
|
||||
Executable
+76
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env bash
|
||||
# This script installs a web-based terminal @ http://birdterminal.local
|
||||
# ONLY run this additional script if you trust everyone on your
|
||||
# local network completely as the credentials are sent WITHOUT
|
||||
# any SSL/TLS encryption. For a secure remote connection to your
|
||||
# BirdNET-Lite command line, consider enabling SSH on the
|
||||
# Raspberry Pi and using another Linux machine or an SSH
|
||||
# client software, or you can alternately add the 'tls internal'
|
||||
# directive to the Caddyfile to add a self-signed certificate for TLS/SSL
|
||||
# encryption. For remote desktop access, NoMachine can be installed.
|
||||
source /etc/birdnet/birdnet.conf
|
||||
my_dir=$(realpath $(dirname $0))
|
||||
|
||||
# Install tmux from version control
|
||||
install_tmux() {
|
||||
DEPENDS=(
|
||||
automake
|
||||
autoconf
|
||||
libevent-2*
|
||||
libevent-dev
|
||||
ncurses-bin
|
||||
ncurses-base
|
||||
ncurses-term
|
||||
libncurses-dev
|
||||
build-essential
|
||||
bison
|
||||
pkg-config
|
||||
gcc
|
||||
)
|
||||
|
||||
if which tmux &>/dev/null; then
|
||||
echo "tmux is installed"
|
||||
else
|
||||
|
||||
sudo apt update && sudo apt -y install "${DEPENDS[@]}"
|
||||
|
||||
cd ${HOME} && git clone https://github.com/tmux/tmux.git
|
||||
cd tmux
|
||||
sh autogen.sh
|
||||
./configure && make && sudo make install
|
||||
cd && rm -drf ./tmux
|
||||
sudo ln -sf "$(dirname ${my_dir})/templates/tmux.conf" /etc/tmux.conf
|
||||
fi
|
||||
}
|
||||
|
||||
install_web_terminal() {
|
||||
cat << EOF | sudo tee /etc/systemd/system/birdterminal.service
|
||||
[Unit]
|
||||
Description=A BirdNET-Lite Web Terminal
|
||||
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
Type=simple
|
||||
User=${BIRDNET_USER}
|
||||
Environment=TERM=xterm-256color
|
||||
ExecStart=/usr/local/bin/gotty -w --title-format "Login!" -p 9111 tmux new -A -s Login sudo bash -c login
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
HASHWORD="$(caddy hash-password -plaintext "${CADDY_PWD}")"
|
||||
cat << EOF | sudo tee -a /etc/caddy/Caddyfile
|
||||
http://birdterminal.local {
|
||||
reverse_proxy localhost:9111
|
||||
basicauth {
|
||||
birdnet "${HASHWORD}"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
sudo systemctl enable --now birdterminal.service
|
||||
sudo systemctl enable --now avahi-alias@birdterminal.local.service
|
||||
sudo systemctl restart caddy
|
||||
}
|
||||
install_tmux
|
||||
install_web_terminal
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
echo "Configuring zram.service"
|
||||
sudo touch /etc/modules-load.d/zram.conf
|
||||
echo 'zram' | sudo tee /etc/modules-load.d/zram.conf
|
||||
sudo touch /etc/modprobe.d/zram.conf
|
||||
echo 'options zram num_devices=1' | sudo tee /etc/modprobe.d/zram.conf
|
||||
sudo touch /etc/udev/rules.d/99-zram.rules
|
||||
echo 'KERNEL=="zram0", ATTR{disksize}="4G",TAG+="systemd"' \
|
||||
| sudo tee /etc/udev/rules.d/99-zram.rules
|
||||
sudo touch /etc/systemd/system/zram.service
|
||||
echo "Installing zram.service"
|
||||
cat << EOF | sudo tee /etc/systemd/system/zram.service
|
||||
[Unit]
|
||||
Description=Swap with zram
|
||||
After=multi-user.target
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=true
|
||||
ExecStartPre=/sbin/mkswap /dev/zram0
|
||||
ExecStart=/sbin/swapon /dev/zram0
|
||||
ExecStop=/sbin/swapoff /dev/zram0
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
sudo systemctl enable zram
|
||||
echo "You'll need to reboot for this to take effect."
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
# Live Audio Stream Service Script
|
||||
source /etc/birdnet/birdnet.conf
|
||||
|
||||
if [ -z ${REC_CARD} ];then
|
||||
echo "Stream not supported"
|
||||
else
|
||||
ffmpeg -loglevel 52 -ac ${CHANNELS} -f alsa -i ${REC_CARD} -acodec libmp3lame \
|
||||
-b:a 320k -ac ${CHANNELS} -content_type 'audio/mpeg' \
|
||||
-f mp3 icecast://source:${ICE_PWD}@localhost:8000/stream -re
|
||||
fi
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# Pretty date suffixes
|
||||
TODAY="$(date +%e)"
|
||||
|
||||
if [[ $TODAY == ' 1' ]] || [[ $TODAY == 21 ]] || [[ $TODAY == 31 ]]; then
|
||||
SUFFIX="st"
|
||||
elif [[ $TODAY == ' 2' ]] || [[ $TODAY == 22 ]];then
|
||||
SUFFIX="nd"
|
||||
elif [[ $TODAY == ' 3' ]] || [[ $TODAY == 23 ]];then
|
||||
SUFFIX="rd"
|
||||
else
|
||||
SUFFIX="th"
|
||||
fi
|
||||
echo "$SUFFIX"
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# Reconfigure the BirdNET-Lite
|
||||
source /etc/birdnet/birdnet.conf
|
||||
uninstall.sh
|
||||
${HOME}/BirdNET-Lite/scripts/install_config.sh
|
||||
sudo ${HOME}/BirdNET-Lite/scripts/install_services.sh
|
||||
echo "BirdNET-Lite has now been reconfigured."
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
# Sends a notification if a new species is detected
|
||||
# set -x
|
||||
trap 'rm -f $TMPFILE' SIGINT SIGHUP EXIT
|
||||
|
||||
source /etc/birdnet/birdnet.conf
|
||||
|
||||
TMPFILE=$(mktemp)
|
||||
|
||||
[ -f ${IDFILE} ] || touch ${IDFILE}
|
||||
cat "${IDFILE}" > "${TMPFILE}"
|
||||
|
||||
/usr/local/bin/update_species.sh &> /dev/null
|
||||
|
||||
if ! diff "${IDFILE}" "${TMPFILE}"; then
|
||||
SPECIES=("$(diff "${IDFILE}" "${TMPFILE}" \
|
||||
| awk '/</ {print $2" "$3}')")
|
||||
|
||||
NOTIFICATION="New Species Detected: ${SPECIES[@]}"
|
||||
|
||||
sudo systemctl restart birdnet_analysis && sleep 30
|
||||
sudo systemctl start extraction
|
||||
if [ ! -z ${PUSHED_APP_KEY} ];then
|
||||
curl -X POST -s \
|
||||
--form-string "app_key=${PUSHED_APP_KEY}" \
|
||||
--form-string "app_secret=${PUSHED_APP_SECRET}" \
|
||||
--form-string "target_type=app" \
|
||||
--form-string "content=${NOTIFICATION}" \
|
||||
https://api.pushed.co/1/push
|
||||
fi
|
||||
fi
|
||||
Executable
+89
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env bash
|
||||
# Uninstall script to remove everything
|
||||
# set -x # Uncomment to debug
|
||||
trap 'rm -f ${TMPFILE}' EXIT
|
||||
source /etc/birdnet/birdnet.conf &> /dev/null
|
||||
SCRIPTS=(/usr/local/bin/birdnet_analysis.sh
|
||||
/usr/local/bin/birdnet_recording.sh
|
||||
/usr/local/bin/birdnet_stats.sh
|
||||
/usr/local/bin/cleanup.sh
|
||||
/usr/local/bin/disk_usage.sh
|
||||
/usr/local/bin/dump_logs.sh
|
||||
/usr/local/bin/extract_new_birdsounds.sh
|
||||
/usr/local/bin/install_birdnet.sh
|
||||
/usr/local/bin/install_config.sh
|
||||
/usr/local/bin/install_services.sh
|
||||
/usr/local/bin/install_tmux_services.sh
|
||||
/usr/local/bin/install_zram_service.sh
|
||||
/usr/local/bin/livestream.sh
|
||||
/usr/local/bin/pretty_date.sh
|
||||
/usr/local/bin/reconfigure_birdnet.sh
|
||||
/usr/local/bin/species_notifier.sh
|
||||
/usr/local/bin/uninstall.sh
|
||||
/usr/local/bin/update_species.sh
|
||||
${HOME}/.gotty)
|
||||
|
||||
|
||||
SERVICES=(avahi-alias@birdlog.local.service
|
||||
avahi-alias@birdnetsystem.local.service
|
||||
avahi-alias@birdstats.local.service
|
||||
avahi-alias@extractionlog.local.service
|
||||
avahi-alias@birdterminal.local.service
|
||||
birdnet_analysis.d
|
||||
birdnet_analysis.service
|
||||
birdnet_log.service
|
||||
birdnet_recording.d
|
||||
birdnet_recording.service
|
||||
birdstats.service
|
||||
birdterminal.service
|
||||
caddy.d
|
||||
caddy.service
|
||||
extraction_log.service
|
||||
extraction.d
|
||||
extraction.service
|
||||
extraction.timer
|
||||
livestream.service
|
||||
${SYSTEMD_MOUNT})
|
||||
|
||||
remove_services() {
|
||||
for i in "${SERVICES[@]}"; do
|
||||
if [ -L /etc/systemd/system/multi-user.target.wants/"${i}" ];then
|
||||
sudo systemctl disable --now "${i}"
|
||||
fi
|
||||
if [ -f /etc/systemd/system/"${i}" ];then
|
||||
sudo rm /etc/systemd/system/"${i}"
|
||||
fi
|
||||
if [ -d /etc/systemd/system/"${i}" ];then
|
||||
sudo rm -drf /etc/systemd/system/"${i}"
|
||||
fi
|
||||
done
|
||||
remove_icecast
|
||||
remove_crons
|
||||
}
|
||||
|
||||
remove_crons() {
|
||||
TMPFILE=$(mktemp)
|
||||
crontab -l | sed -e '/birdnet/,+1d' > "${TMPFILE}"
|
||||
crontab "${TMPFILE}"
|
||||
}
|
||||
|
||||
remove_icecast() {
|
||||
if [ -f /etc/init.d/icecast2 ];then
|
||||
sudo /etc/init.d/icecast2 stop
|
||||
sudo systemctl disable --now icecast2
|
||||
fi
|
||||
}
|
||||
|
||||
remove_scripts() {
|
||||
for i in "${SCRIPTS[@]}";do
|
||||
if [ -L "${i}" ];then
|
||||
sudo rm -v "${i}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
remove_services
|
||||
remove_scripts
|
||||
if [ -d /etc/birdnet ];then sudo rm -drf /etc/birdnet;fi
|
||||
if [ -f ${HOME}/BirdNET-Lite/birdnet.conf ];then sudo rm -f ${HOME}/BirdNET-Lite/birdnet.conf;fi
|
||||
echo "Uninstall finished. Remove this directory with 'rm -drfv' to finish."
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
# Update the species list
|
||||
# set -x
|
||||
trap 'rm -f "$TMPFILE"' SIGINT SIGTERM EXIT
|
||||
|
||||
source /etc/birdnet/birdnet.conf
|
||||
|
||||
TMPFILE=$(mktemp) || exit 1
|
||||
|
||||
[ -f ${IDFILE} ] || touch ${IDFILE}
|
||||
|
||||
IDFILEBAKUP="${IDFILE}.bak"
|
||||
|
||||
if [ $(find ${ANALYZED} -name '*txt' | wc -l) -ge 1 ];then
|
||||
sort $(find ${ANALYZED} -name '*txt') \
|
||||
| awk '/Spect/ {for(i=11;i<=NF;++i)printf $i""FS ; print ""}' \
|
||||
| cut -d'0' -f1 \
|
||||
| sort -u > "$TMPFILE"
|
||||
cat "$IDFILE" >> "$TMPFILE"
|
||||
cp "$IDFILE" "$IDFILEBAKUP"
|
||||
sort -u "$TMPFILE" > "$IDFILE"
|
||||
cat "$IDFILE"
|
||||
else
|
||||
cat "$IDFILE"
|
||||
fi
|
||||
Reference in New Issue
Block a user