updating references to old repo to new repo

This commit is contained in:
Patrick McGuire
2021-09-28 14:48:25 -04:00
parent 7d31737f03
commit bc2d3df898
42 changed files with 3078 additions and 217 deletions
+81
View File
@@ -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
+16
View File
@@ -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
+37
View File
@@ -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
+6
View File
@@ -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 {} +
+3
View File
@@ -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"}'
+62
View File
@@ -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"
+158
View File
@@ -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)"
+123
View File
@@ -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
+412
View File
@@ -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
+441
View File
@@ -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
+76
View File
@@ -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
+26
View File
@@ -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."
+11
View File
@@ -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
+14
View File
@@ -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"
+7
View File
@@ -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."
+31
View File
@@ -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
+89
View File
@@ -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."
+25
View File
@@ -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