updating references to old repo to new repo
This commit is contained in:
+11
-129
@@ -1,129 +1,11 @@
|
|||||||
# Byte-compiled / optimized / DLL files
|
*.pyc
|
||||||
__pycache__/
|
*.flac
|
||||||
*.py[cod]
|
model/BirdNET_Soundscape_Model.pkl
|
||||||
*$py.class
|
.vscode
|
||||||
|
datasets/
|
||||||
# C extensions
|
birdnet.conf
|
||||||
*.so
|
miniforge
|
||||||
|
scripts/install_miniforge.sh
|
||||||
# Distribution / packaging
|
IdentifiedSoFar.txt
|
||||||
.Python
|
IdentifiedSoFar.txt.bak
|
||||||
build/
|
Birders_Guide_Installer_Configuration.txt
|
||||||
develop-eggs/
|
|
||||||
dist/
|
|
||||||
downloads/
|
|
||||||
eggs/
|
|
||||||
.eggs/
|
|
||||||
lib/
|
|
||||||
lib64/
|
|
||||||
parts/
|
|
||||||
sdist/
|
|
||||||
var/
|
|
||||||
wheels/
|
|
||||||
pip-wheel-metadata/
|
|
||||||
share/python-wheels/
|
|
||||||
*.egg-info/
|
|
||||||
.installed.cfg
|
|
||||||
*.egg
|
|
||||||
MANIFEST
|
|
||||||
|
|
||||||
# PyInstaller
|
|
||||||
# Usually these files are written by a python script from a template
|
|
||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
||||||
*.manifest
|
|
||||||
*.spec
|
|
||||||
|
|
||||||
# Installer logs
|
|
||||||
pip-log.txt
|
|
||||||
pip-delete-this-directory.txt
|
|
||||||
|
|
||||||
# Unit test / coverage reports
|
|
||||||
htmlcov/
|
|
||||||
.tox/
|
|
||||||
.nox/
|
|
||||||
.coverage
|
|
||||||
.coverage.*
|
|
||||||
.cache
|
|
||||||
nosetests.xml
|
|
||||||
coverage.xml
|
|
||||||
*.cover
|
|
||||||
*.py,cover
|
|
||||||
.hypothesis/
|
|
||||||
.pytest_cache/
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
*.mo
|
|
||||||
*.pot
|
|
||||||
|
|
||||||
# Django stuff:
|
|
||||||
*.log
|
|
||||||
local_settings.py
|
|
||||||
db.sqlite3
|
|
||||||
db.sqlite3-journal
|
|
||||||
|
|
||||||
# Flask stuff:
|
|
||||||
instance/
|
|
||||||
.webassets-cache
|
|
||||||
|
|
||||||
# Scrapy stuff:
|
|
||||||
.scrapy
|
|
||||||
|
|
||||||
# Sphinx documentation
|
|
||||||
docs/_build/
|
|
||||||
|
|
||||||
# PyBuilder
|
|
||||||
target/
|
|
||||||
|
|
||||||
# Jupyter Notebook
|
|
||||||
.ipynb_checkpoints
|
|
||||||
|
|
||||||
# IPython
|
|
||||||
profile_default/
|
|
||||||
ipython_config.py
|
|
||||||
|
|
||||||
# pyenv
|
|
||||||
.python-version
|
|
||||||
|
|
||||||
# pipenv
|
|
||||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
||||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
||||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
||||||
# install all needed dependencies.
|
|
||||||
#Pipfile.lock
|
|
||||||
|
|
||||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
||||||
__pypackages__/
|
|
||||||
|
|
||||||
# Celery stuff
|
|
||||||
celerybeat-schedule
|
|
||||||
celerybeat.pid
|
|
||||||
|
|
||||||
# SageMath parsed files
|
|
||||||
*.sage.py
|
|
||||||
|
|
||||||
# Environments
|
|
||||||
.env
|
|
||||||
.venv
|
|
||||||
env/
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
env.bak/
|
|
||||||
venv.bak/
|
|
||||||
|
|
||||||
# Spyder project settings
|
|
||||||
.spyderproject
|
|
||||||
.spyproject
|
|
||||||
|
|
||||||
# Rope project settings
|
|
||||||
.ropeproject
|
|
||||||
|
|
||||||
# mkdocs documentation
|
|
||||||
/site
|
|
||||||
|
|
||||||
# mypy
|
|
||||||
.mypy_cache/
|
|
||||||
.dmypy.json
|
|
||||||
dmypy.json
|
|
||||||
|
|
||||||
# Pyre type checker
|
|
||||||
.pyre/
|
|
||||||
|
|||||||
Executable
+423
@@ -0,0 +1,423 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
my_dir=${HOME}/BirdNET-Lite
|
||||||
|
trap '${my_dir}/scripts/dump_logs.sh && exit' EXIT SIGHUP SIGINT
|
||||||
|
|
||||||
|
|
||||||
|
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_zram_swap() {
|
||||||
|
echo
|
||||||
|
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 &> /dev/null
|
||||||
|
[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
|
||||||
|
echo "Installing stage 2 installation script now."
|
||||||
|
cd ~
|
||||||
|
curl -s -O "https://raw.githubusercontent.com/mcguirepr89/BirdNET-Lite/rpi4/Birders_Guide_Installer.sh"
|
||||||
|
chmod +x Birders_Guide_Installer.sh
|
||||||
|
cat << EOF | sudo tee /etc/systemd/user/birdnet-system-installer.service &> /dev/null
|
||||||
|
[Unit]
|
||||||
|
Description=A BirdNET-Lite Installation Script Service
|
||||||
|
After=graphical.target network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=3s
|
||||||
|
ExecStart=lxterminal -e /home/pi/Birders_Guide_Installer.sh
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
EOF
|
||||||
|
systemctl --user enable birdnet-system-installer.service
|
||||||
|
echo
|
||||||
|
echo "Stage 1 complete"
|
||||||
|
touch ${HOME}/stage_1_complete
|
||||||
|
echo
|
||||||
|
echo "Rebooting the system in 5 seconds"
|
||||||
|
sleep 5
|
||||||
|
sudo reboot
|
||||||
|
}
|
||||||
|
|
||||||
|
stage_1() {
|
||||||
|
echo
|
||||||
|
echo "Beginning Stage 1"
|
||||||
|
echo
|
||||||
|
echo "Ensuring the system is up-to-date."
|
||||||
|
sudo apt -qq update
|
||||||
|
sudo apt -qqy full-upgrade
|
||||||
|
sudo apt -y autoremove --purge
|
||||||
|
echo "System Updated!"
|
||||||
|
if ! which git &> /dev/null; then
|
||||||
|
echo "Installing git"
|
||||||
|
sudo apt install -qqy git
|
||||||
|
fi
|
||||||
|
ZRAM="$(swapon --show=SIZE,NAME | awk -FG '!/SIZE/ && /zram/ {print $1}')"
|
||||||
|
[ ! -z ${ZRAM} ] || ZRAM=0
|
||||||
|
if [ ${ZRAM} -lt 4 ];then
|
||||||
|
install_zram_swap
|
||||||
|
else
|
||||||
|
echo "Stage 1 complete"
|
||||||
|
stage_2
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
stage_2() {
|
||||||
|
echo
|
||||||
|
echo "Beginning stage 2"
|
||||||
|
echo
|
||||||
|
echo "Checking for an internet connection to continue . . ."
|
||||||
|
until ping -c 1 google.com &> /dev/null; do
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
echo "Connected!"
|
||||||
|
echo
|
||||||
|
if [ ! -d ${my_dir} ];then
|
||||||
|
cd ~ || exit 1
|
||||||
|
echo "Cloning the BirdNET-Lite repository in your home directory"
|
||||||
|
git clone https://github.com/mcguirepr89/BirdNET-Lite.git !/BirdNET-Lite
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f ${my_dir}/Birders_Guide_Installer_Configuration.txt ];then
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
echo "Follow the instructions to fill out the LATITUDE and LONGITUDE variables
|
||||||
|
and set the passwords for the live audio stream. Save the file after editing
|
||||||
|
and then close the Mouse Pad editing window to continue."
|
||||||
|
mousepad ${my_dir}/Birders_Guide_Installer_Configuration.txt &> /dev/null
|
||||||
|
while pgrep mouse &> /dev/null;do
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
source ${my_dir}/Birders_Guide_Installer_Configuration.txt || exit 1
|
||||||
|
else
|
||||||
|
echo "Something went wrong. I can't find the configuration file."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z ${LATITUDE} ] || [ -z ${LONGITUDE} ] || [ -z ${CADDY_PWD} ] || [ -z ${ICE_PWD} ];then
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
echo "It looks like you haven't filled out the Birders_Guide_Installer_Configuration.txt file
|
||||||
|
completely.
|
||||||
|
|
||||||
|
Open that file to edit it. (Go to the folder icon in the top left and look for the \"BirdNET-Lite\"
|
||||||
|
folder and double-click the file called \"Birders_Guide_Installer_Configuration.txt\"
|
||||||
|
Enter the latitude and longitude of where the BirdNET-Lite will be.
|
||||||
|
You can find this information at https://maps.google.com
|
||||||
|
|
||||||
|
Find your location on the map and right click to find your coordinates.
|
||||||
|
After you have filled out the configuration file, you can re-run this script. Just do the exact
|
||||||
|
same things you did to start this (copying and pasting from the Wiki) to try again.
|
||||||
|
Press Enter to close this window.
|
||||||
|
Good luck!"
|
||||||
|
read
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Installing the BirdNET-Lite configuration file."
|
||||||
|
[ -f ${my_dir}/soundcard_params.txt ] || touch ${my_dir}/soundcard_params.txt
|
||||||
|
SOUND_PARAMS="${HOME}/BirdNET-Lite/soundcard_params.txt"
|
||||||
|
SOUND_CARD="$(sudo -u pi aplay -L \
|
||||||
|
| grep -e '^hw' \
|
||||||
|
| cut -d, -f1 \
|
||||||
|
| grep -ve 'vc4' -e 'Head' -e 'PCH' \
|
||||||
|
| uniq)"
|
||||||
|
script -c "arecord -D ${SOUND_CARD} --dump-hw-params" -a ${SOUND_PARAMS} &> /dev/null
|
||||||
|
install_birdnet_config || exit 1
|
||||||
|
echo "Installing the BirdNET-Lite"
|
||||||
|
if ${my_dir}/scripts/install_birdnet.sh << EOF ; then
|
||||||
|
|
||||||
|
n
|
||||||
|
EOF
|
||||||
|
echo "The next time you power on the raspberry pi, all of the services will start up automatically.
|
||||||
|
|
||||||
|
The installation has finished. Press Enter to close this window."
|
||||||
|
read
|
||||||
|
else
|
||||||
|
echo "Something went wrong during installation. Open a github issue or email mcguirepr89@gmail.com"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
install_birdnet_config() {
|
||||||
|
cat << EOF > ${my_dir}/birdnet.conf
|
||||||
|
################################################################################
|
||||||
|
# Configuration settings for BirdNET as a service #
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
#___________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=pi
|
||||||
|
|
||||||
|
## 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=${HOME}/BirdSongs
|
||||||
|
|
||||||
|
## 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=y
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
#----------------------------- 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=y
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
#----------------- 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_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_USER is the user SSH will use to connect to the REMOTE_HOST.
|
||||||
|
|
||||||
|
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=
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
#----------------------- 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=http://raspberrypi.local
|
||||||
|
|
||||||
|
## 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=y
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
#-------------------------------- 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=$(awk '/CHANN/ { print $2 }' ${SOUND_PARAMS} | sed 's/\r$//')
|
||||||
|
|
||||||
|
# Don't 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=${my_dir}/miniforge/envs/birdnet
|
||||||
|
EOF
|
||||||
|
[ -d /etc/birdnet ] || sudo mkdir /etc/birdnet
|
||||||
|
sudo ln -sf ${my_dir}/birdnet.conf /etc/birdnet/birdnet.conf
|
||||||
|
}
|
||||||
|
echo "
|
||||||
|
Welcome to the Birders Guide Installer script!
|
||||||
|
|
||||||
|
The installer runs in two stages:
|
||||||
|
Stage 1 configures and enables the zRAM kernel module and allocates 4G
|
||||||
|
to its swapping size if needed. This will trigger a reboot.
|
||||||
|
Stage 1 also ensures the system is up to date.
|
||||||
|
Stage 2 guides you through configuring the essentials and installs the full BirdNET-Lite."
|
||||||
|
|
||||||
|
|
||||||
|
if [ ! -f ${HOME}/stage_1_complete ] ;then
|
||||||
|
stage_1
|
||||||
|
else
|
||||||
|
stage_2
|
||||||
|
if [ -f ${HOME}/Birders_Guide_Installer.sh ];then
|
||||||
|
rm ${HOME}/Birders_Guide_Installer.sh
|
||||||
|
fi
|
||||||
|
rm ${HOME}/stage_1_complete
|
||||||
|
${HOME}/scripts/dump_logs.sh
|
||||||
|
systemctl --user disable --now birdnet-system-installer.service
|
||||||
|
sudo rm -f /etc/systemd/user/birdnet-system-installer.service
|
||||||
|
fi
|
||||||
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# Birders Guide Installer Configuration File
|
||||||
|
#####NOTE: the '#' sign needs to precede the lines where they are currently
|
||||||
|
##### DO NOT DELETE ANY OF THEM!!!
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
# Fill this out in order to install BirdNET-Lite
|
||||||
|
#
|
||||||
|
# 1. To find this information, you can go to https://maps.google.com
|
||||||
|
# 1a. Find the location on the map where you will be putting the BirdNET-Lite
|
||||||
|
# 1b. Right-click the location to view the latitude and longitude and click them to
|
||||||
|
# copy them to your clip-board.
|
||||||
|
# 2. Enter the latitude and longitude here. Be certain not to mix them up and
|
||||||
|
# only go to the thousandth's decimal place. The first number is the latitude,
|
||||||
|
# and the second is longitude. See the example below.
|
||||||
|
# 3. Set a password that you will use to access your live audio stream. This is the
|
||||||
|
# "CADDY_PWD" field. You can set this to anything, but keep it alpha-numeric.
|
||||||
|
# You will probably also want to write this one down if you ever plan to listen
|
||||||
|
# to the live stream.
|
||||||
|
# 4. Set a password that that IceCast2 will use to authenticate the stream source.
|
||||||
|
# You can set this to anything also, but keep it alpha-numeric. You will never use
|
||||||
|
# this again, so just set it to your favorite word.
|
||||||
|
# 5. FILE SAVE!!! Don't forget to save this file after editing.
|
||||||
|
# 6. Close this window to continue the installation.
|
||||||
|
# Example: these coordinates would indicate the Eiffel Tower in Paris, France.
|
||||||
|
#LATITUDE=48.858
|
||||||
|
#LONGITUDE=2.294
|
||||||
|
LATITUDE=
|
||||||
|
LONGITUDE=
|
||||||
|
CADDY_PWD=
|
||||||
|
ICE_PWD=
|
||||||
|
|
||||||
|
# This section can be left alone. If you setup a Pushed.co
|
||||||
|
# mobile application, you can enter the App secret and secret
|
||||||
|
# key in order to enable phone notifications for new species
|
||||||
|
# detections.
|
||||||
|
PUSHED_APP_SECRET=
|
||||||
|
PUSHED_APP_KEY=
|
||||||
@@ -1,110 +1,112 @@
|
|||||||
# BirdNET-Lite
|
# BirdNET-Lite for arm64/aarch64 (Raspberry Pi 4)
|
||||||
TFLite version of BirdNET. Bird sound recognition for more than 6,000 species worldwide.
|
### Built on https://github.com/kahst/BirdNET -- checkout the Wiki at [BirdNETWiki.pmcgui.xyz](https://birdnetwiki.pmcgui.xyz)
|
||||||
|
|
||||||
Center for Conservation Bioacoustics, Cornell Lab of Ornithology, Cornell University
|
This project offers an installation script for BirdNET as a systemd service on arm64 (aarch64) Debian-based operating systems, namely RaspiOS. The installation script offers to walk the user through setting up the '*birdnet.conf*' main configuration file interactively, or can read from an existing '*birdnet.conf*'. A variety of configurations can be attained through this installation script.
|
||||||
|
|
||||||
Go to https://birdnet.cornell.edu to learn more about the project.
|
BirdNET-Lite can be configured with the following optional services:
|
||||||
|
- A 24/7 recording script that can be easily configured to use any available sound card
|
||||||
|
- An extraction service that extracts the audio selections identified by BirdNET by date and species
|
||||||
|
- A Caddy instance that serves the extracted files and live audio stream (icecast2) (requires dsnoop capable mic)
|
||||||
|
- A species list updating and notification script supporting mobile notifications via Pushed.co (sorry, Android users, Pushed.co doesn't seem to work for you)
|
||||||
|
- NoMachine remote desktop software (for personal use only)
|
||||||
|
|
||||||
Want to use BirdNET to analyze a large dataset? Don't hesitate to contact us: ccb-birdnet@cornell.edu
|
An installation one-liner is available [HERE](https://birdnetwiki.pmcgui.xyz/wiki/Birder%27s_Guide_to_BirdNET-Lite#Install_BirdNET-Lite) for RaspiOS-ARM64 meeting the prequisites below. It installs all services listed above.
|
||||||
|
- Prerequisites:
|
||||||
|
- An updated RaspiOS for AArch64 that has locale, WiFi, time-zone, and pi user password set. A guide is available [here](https://birdnetwiki.pmcgui.xyz/wiki/Birder%27s_Guide_to_BirdNET-Lite#Install_the_base_operating_system_.28OS.29). 64GB SD card for best performance.
|
||||||
|
- A USB microphone (dsnoop capable to enable live audio stream).
|
||||||
|
- Running the installer from within the Raspberry Pi's desktop environment (i.e., not over SSH -- for SSH installations, see installation options 2 & 3)
|
||||||
|
|
||||||
# Setup (Ubuntu 18.04)
|
|
||||||
|
|
||||||
TFLite for x86 platforms comes with the standard Tensorflow package. If you are on a different platform, you need to install a dedicated version of TFLite (e.g., a pre-compiled version for Raspberry Pi). See Raspberry Pi 4B installation instructions below.
|
## What the installation does
|
||||||
|
1. Looks for a *'birdnet.conf'* file in the *BirdNET-Lite* main directory
|
||||||
|
1. If a *'birdnet.conf'* file exists and is filled out properly, the installation is nearly
|
||||||
|
non-interactive and builds the system based off of the services configured in the *'birdnet.conf'* file
|
||||||
|
1. If the installer cannot find a *'birdnet.conf'* file, the installation is interactive and will
|
||||||
|
walk the user through creating the '*birdnet.conf'* file interactively.
|
||||||
|
1. Installs the following system dependencies:
|
||||||
|
- ffmpeg
|
||||||
|
- libblas-dev
|
||||||
|
- liblapack-dev
|
||||||
|
- caddy (for web access to extractions)
|
||||||
|
- icecast2 (live audio stream)
|
||||||
|
- alsa-utils (for recording)
|
||||||
|
- sshfs (to mount remote sound file directories)
|
||||||
|
1. Installs BirdNET-Lite scripts in */usr/local/bin*
|
||||||
|
1. Installs all selected services based on '*birdnet.conf*'
|
||||||
|
1. Installs *miniforge* for the aarch64 architecture using the current release from https://github.com/conda-forge/miniforge
|
||||||
|
1. Builds BirdNET in miniforge's *'birdnet'* virtual environment
|
||||||
|
1. Enables (but does not start) the services
|
||||||
|
|
||||||
## x86
|
## What you should know before any installation
|
||||||
We need to setup TF2.3+ for BirdNET. First, we install Python 3 and pip:
|
1. The licensing information for the software that is used (see [LICENSE](https://raw.githubusercontent.com/mcguirepr89/BirdNET-Lite/BirdNET-Lite-for-raspi4/LICENSE)).
|
||||||
|
1. The **latitude** and **longitude** where the bird recordings take place. Google maps is an easy way to find these (right-clicking the location).
|
||||||
|
1. In order for the live audio stream to work at the same time as the birdnet_recording.service, the microphone needs to be dsnoop capable. If you are wondering whether your mic supports creating the dsnoop device, you can use `aplay -L | awk -F, '/dsn/ {print $1}' | grep -ve 'vc4' -e 'Head' -e 'PCH' | uniq` to check. (No output means your microphone does not support creating a dsnoop device and therefore cannot also provide an audio stream while recording. The birdnet_recording.service, however, should not be affected by this and the installation one-liner can still be used. The live stream link simply will not work.)
|
||||||
|
|
||||||
```
|
## What you should know for a manual installation
|
||||||
sudo apt-get update
|
1. The **local directory** where the recordings should be found on your local computer. BirdNET-Lite supports setting up a systemd.mount for automounting remote directories. So for instance, if the actual recordings live on RemoteHost's `/home/user/recordings` directory, but you would like them to be found on your device at `/home/me/BirdNET-recordings`, then `/home/me/BirdNET-recordings` will be your answer to that question.
|
||||||
sudo apt-get install python3-dev python3-pip
|
1. If mounting the recordings directory from a remote host, you need to know the **remote hostname, username, and password** to connect to it via SSH, as well as the **absolute path of the recordings on the remote host**.
|
||||||
sudo pip3 install --upgrade pip
|
1. If you are using a special microphone or have multiple sound cards and would like to specify which to use for recording, you can edit the `~/BirdNET-Lite/birdnet.conf` file before the installation and set the **REC_CARD** variable to the sound card of your choice. Copy your desired sound card line from the output of `aplay -L | awk -F, '/^dsn:/ { print $1 }'`(prefered), or `aplay -L | awk -F, '/^hw:/ { print $1 }'`(if prefered is not available).
|
||||||
```
|
1. If you would like to take advantage of Caddy's automatic handling of SSL certificates to be able to host a public website where your friends can hear your bird sounds, forward ports 80 and 443 to the host you want to serve the files. You may also want to purchase a domain name.
|
||||||
|
- *Note: If you're just keeping this on your local network, **be sure to set your extraction URL to something 'http://'** (on RaspiOS, I recommend 'http://raspberrypi.local') to disable Caddy's automatic HTTPS. Alternatively, you may edit the `/etc/caddy/Caddyfile` after installation and add the `tls internal` directive to the site block to have Caddy issue a self-signed certificate for an HTTPS connection.*
|
||||||
|
1. If you would like to take advantage of BirdNET-Lite's ability to send New Species mobile notifications, you can easily setup a Pushed.co notification app (see the #TODOs at the bottom for more info). After setting up your application, make note of your **App Key** and **App Secret** -- you will need these to enable mobile notifications for new species.
|
||||||
|
- *Note for Android users: it seems that the Pushed.co Mobile App does not work for Android devices, which is a huge bummer. If anyone knows of an Android alternative, or if anyone might be able to come up with a home-spun notification system, please let me know.*
|
||||||
|
|
||||||
Then, we can install Tensorflow with:
|
## How to install
|
||||||
|
#### Option 1 (Recommended) -- Install All Services
|
||||||
|
1. In the terminal run: `curl -s https://raw.githubusercontent.com/mcguirepr89/BirdNET-Lite/BirdNET-Lite-for-raspi4/Birders_Guide_Installer.sh | bash`
|
||||||
|
|
||||||
```
|
##### Options 2 & 3 require you setup 4GB of swapping. That step is included in the directions below.
|
||||||
sudo pip3 install tensorflow
|
#### Option 2 -- Pre-fill birdnet.conf
|
||||||
```
|
1. In the terminal run `git clone https://github.com/mcguirepr89/BirdNET-Lite.git ~/BirdNET-Lite`
|
||||||
|
1. You can copy the included *'birdnet.conf-defaults'* template to create and configure the BirdNET-Lite
|
||||||
|
to your needs before running the installer. Issue `cp ~/BirdNET-Lite/birdnet.conf-defaults ~/BirdNET-Lite/birdnet.conf`.
|
||||||
|
Edit the new *'birdnet.conf'* file to suit your needs and save it.
|
||||||
|
If you choose this method, the installation will be (nearly) non-interactive.
|
||||||
|
1. Setup zRAM swapping. Run `~/BirdNET-Lite/scripts/install_zram_service.sh && sudo reboot`
|
||||||
|
1. After the reboot, run `~/BirdNET-Lite/scripts/install_birdnet.sh`
|
||||||
|
#### Option 3 -- Interactive Installation
|
||||||
|
1. In the terminal run `git clone https://github.com/mcguirepr89/BirdNET-Lite.git ~/BirdNET-Lite`
|
||||||
|
1. Setup zRAM swapping. Run `~/BirdNET-Lite/scripts/install_zram_service.sh && sudo reboot`
|
||||||
|
1. After the reboot, run `~/BirdNET-Lite/scripts/install_birdnet.sh`
|
||||||
|
1. Follow the installation prompts to configure the BirdNET-Lite to your needs.
|
||||||
|
- Note: The installation should be run as a regular user. If run on an OS other than RaspiOS, be sure the regular user is in the sudoers file or the sudo group.
|
||||||
|
|
||||||
TFLite on x86 platform currently only supports CPUs.
|
## Access your BirdNET-Lite
|
||||||
|
If you configured BirdNET-Lite with the Caddy webserver, you can access the extractions locally at
|
||||||
|
|
||||||
Note: Make sure to set `CUDA_VISIBLE_DEVICES=""` in your environment variables. Or set `os.environ['CUDA_VISIBLE_DEVICES'] = ''` at the top of your Python script.
|
- http://birdnetsystem.local
|
||||||
|
|
||||||
In this example, we use Librosa to open audio files. Install Librosa with:
|
You can also view the log output for the <code>birdnet_analysis.service</code> and <code>extraction.service</code> at
|
||||||
|
|
||||||
```
|
- http://birdlog.local
|
||||||
sudo pip3 install librosa
|
- http://extractionlog.local
|
||||||
sudo apt-get install ffmpeg
|
|
||||||
```
|
|
||||||
|
|
||||||
You can use any other audio lib if you like, or pass raw audio signals to the model.
|
and the BirdNET-Lite Statistics Report at
|
||||||
|
- http://birdstats.local
|
||||||
|
|
||||||
If you don't use Librosa, make sure to install NumPy:
|
If you opt to also install NoMachine alongside the BirdNET-Lite, you can also access BirdNET-Lite
|
||||||
|
remotely following the address information that can be found on the NoMachine's server information page.
|
||||||
|
|
||||||
```
|
## Examples
|
||||||
sudo pip3 install numpy
|
These are examples of my personal instance of the BirdNET-Lite on a Raspberry Pi 4B.
|
||||||
```
|
- https://birdsounds.pmcgui.xyz -- My BirdNET-Lite Extractions page
|
||||||
|
- https://birdlog.pmcgui.xyz -- My 'birdlog' birdnet_analysis.service log
|
||||||
|
- https://extraction.pmcgui.xyz -- My 'extractionlog' extraction.service log
|
||||||
|
- https://birdstats.pmcgui.xyz -- My 'birdstats' BirdNET-Lite Report
|
||||||
|
|
||||||
Note: BirdNET expects 3-second chunks of raw audio data, sampled at 48 kHz.
|
## How to reconfigure the system
|
||||||
|
At any time, you can completely reconfigure the system to select or remove features. To reconfigure the system, simply run the included "reconfigure_birdnet.sh" script (as the regular user) and follow the prompts to create a new birdnet.conf file and install new services: `~/BirdNET-Lite/scripts/reconfigure_birdnet.sh`
|
||||||
|
|
||||||
## Raspberry Pi 4B running AArch64 OS
|
## How to uninstall BirdNET-Lite
|
||||||
These steps install BirdNET-Lite on a Raspberry Pi 4B running an AArch64 OS using pre-built TFLite binaries.
|
To remove BirdNET-Lite, run the included '*uninstall.sh*' script as the regular user.
|
||||||
1. Install dependencies<br>`sudo apt install swig libjpeg-dev zlib1g-dev python3-dev unzip wget python3-pip curl git cmake make`
|
1. Issue `/usr/local/bin/uninstall.sh && cd ~ && rm -drf BirdNET-Lite`
|
||||||
1. Update pip, whell, and setuptools:<br>`sudo pip3 install --upgrade pip wheel setuptools`
|
|
||||||
1. Fetch pre-built binaries:
|
|
||||||
1. `curl -sc /tmp/cookie "https://drive.google.com/uc?export=download&id=1dlEbugFDJXs-YDBCUC6WjADVtIttWxZA" > /dev/null`
|
|
||||||
1. `CODE="$(awk '/_warning_/ {print $NF}' /tmp/cookie)"`
|
|
||||||
1. `curl -Lb /tmp/cookie "https://drive.google.com/uc?export=download&confirm=${CODE}&id=1dlEbugFDJXs-YDBCUC6WjADVtIttWxZA" -o tflite_runtime-2.6.0-cp37-none-linux_aarch64.whl`
|
|
||||||
1. `sudo pip3 install --upgrade tflite_runtime-2.6.0-cp37-none-linux_aarch64.whl`
|
|
||||||
1. `sudo pip3 install librosa`
|
|
||||||
1. `sudo apt-get install ffmpeg`
|
|
||||||
1. `git clone https://github.com/kahst/BirdNET-Lite.git`
|
|
||||||
1. `cd BirdNET-Lite/`<br>and test:
|
|
||||||
1. `python3 analyze.py --i 'example/XC558716 - Soundscape.mp3' --lat 35.4244 --lon -120.7463 --week 18`
|
|
||||||
|
|
||||||
# Usage
|
## Troubleshooting
|
||||||
|
**General** -- At anytime, you can run the included `~/BirdNET-Lite/dump_logs.sh` script to create a compressed tar ball of system logs that may provide a helpful overview of the system services. In addition, you can upload it in a new issue along with a description of what you are experiencing. dump_logs.sh scrubs password information, but does retain LATITUDE and LONGITUDE information. If at all concerned with privacy, you're welcome to send them to me via email at mailto:mcguirepr89@gmail.com.
|
||||||
|
|
||||||
You can run BirdNET via the command line. You can add a few parameters that affect the output.
|
**Audio** -- If you have problems with the _bridnet_recording.service_ or _livestream.service_, try setting the REC_CARD setting in the _birdnet.conf_ file to `REC_CARD=default` and the CHANNELS variable to `CHANNELS=2`. This works for two very different microphones I have, so it may work for you. If it does, please let me know, as I may change the code as a result. Also, during installation, a file is created called `~/BirdNET-Lite/soundcard_params.txt` that may provide helpful information for customized settings.
|
||||||
|
|
||||||
The input parameters include:
|
**Installation** -- The installer _should_ always create a compressed set of system logs whether it succeeds or fails. Its location is `~/BirdNET-Lite/logs.tar.gz`. Take a look through there or feel free to create a new issue and upload it along with a description of what you are experiencing.
|
||||||
|
|
||||||
```
|
|
||||||
--i, Path to input file.
|
|
||||||
--o, Path to output file. Defaults to result.csv.
|
|
||||||
--lat, Recording location latitude. Set -1 to ignore.
|
|
||||||
--lon, Recording location longitude. Set -1 to ignore.
|
|
||||||
--week, Week of the year when the recording was made. Values in [1, 48] (4 weeks per month). Set -1 to ignore.
|
|
||||||
--overlap, Overlap in seconds between extracted spectrograms. Values in [0.0, 2.9]. Defaults tp 0.0.
|
|
||||||
--sensitivity, Detection sensitivity; Higher values result in higher sensitivity. Values in [0.5, 1.5]. Defaults to 1.0.
|
|
||||||
--min_conf, Minimum confidence threshold. Values in [0.01, 0.99]. Defaults to 0.1.
|
|
||||||
--custom_list, Path to text file containing a list of species. Not used if not provided.
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: A custom species list needs to contain one species label per line. Take a look at the `model/label.txt` for the correct species label. Only labels from this text file are valid. You can find an example of a valid custom list in the 'example' folder.
|
|
||||||
|
|
||||||
Here are two example commands to run this BirdNET version:
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
python3 analyze.py --i 'example/XC558716 - Soundscape.mp3' --lat 35.4244 --lon -120.7463 --week 18
|
|
||||||
|
|
||||||
python3 analyze.py --i 'example/XC563936 - Soundscape.mp3' --lat 47.6766 --lon -122.294 --week 11 --overlap 1.5 --min_conf 0.25 --sensitivity 1.25 --custom_list 'example/custom_species_list.txt'
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: Please make sure to provide lat, lon, and week. BirdNET will work without these values, but the results might be less reliable.
|
|
||||||
|
|
||||||
The results of the anlysis will be stored in a result file in CSV format. All confidence values are raw prediction scores and should be post-processed to eliminate occasional false-positive results.
|
|
||||||
|
|
||||||
# Contact us
|
|
||||||
|
|
||||||
Please don't hesitate to contact us if you have any issues with the code or if you have any other remarks or questions.
|
|
||||||
|
|
||||||
Our e-mail address: ccb-birdnet@cornell.edu
|
|
||||||
|
|
||||||
We are always open for a collaboration with you.
|
|
||||||
|
|
||||||
# Funding
|
|
||||||
|
|
||||||
This project is supported by Jake Holshuh (Cornell class of ’69). The Arthur Vining Davis Foundations also kindly support our efforts.
|
|
||||||
|
|
||||||
|
### TODO & Notes:
|
||||||
|
1. I ought to add the steps to setup a Pushed.co application for the mobile notifications feature. Here is a link for now https://pushed.co/quick-start-guide
|
||||||
|
|||||||
@@ -0,0 +1,224 @@
|
|||||||
|
################################################################################
|
||||||
|
# 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=
|
||||||
|
|
||||||
|
## 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=
|
||||||
|
|
||||||
|
## 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=
|
||||||
|
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=
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
#----------------------------- 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=
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
#----------------- 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_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_USER is the user SSH will use to connect to the REMOTE_HOST.
|
||||||
|
|
||||||
|
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=
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
#----------------------- 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=
|
||||||
|
|
||||||
|
## 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=
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
#------------------------- 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=
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
#------------------- 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_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=
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
#-------------------------------- Defaults ----------------------------------#
|
||||||
|
#______The seven 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 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
|
||||||
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
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
${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://birdterminal.local {
|
||||||
|
reverse_proxy localhost:9111
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
${EXTRACTIONS_URL} {
|
||||||
|
root * ${EXTRACTED}
|
||||||
|
file_server browse
|
||||||
|
basicauth ${PROCESSED}* {
|
||||||
|
birdnet ${HASHWORD}
|
||||||
|
}
|
||||||
|
basicauth /stream {
|
||||||
|
birdnet ${HASHWORD}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http://birdnetsystem.local {
|
||||||
|
root * ${EXTRACTED}
|
||||||
|
file_server browse
|
||||||
|
basicauth ${PROCESSED}* {
|
||||||
|
birdnet ${HASHWORD}
|
||||||
|
}
|
||||||
|
basicauth /stream {
|
||||||
|
birdnet ${HASHWORD}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http://birdlog.local {
|
||||||
|
reverse_proxy localhost:8080
|
||||||
|
}
|
||||||
|
http://extractionlog.local {
|
||||||
|
reverse_proxy localhost:8888
|
||||||
|
}
|
||||||
|
http://birdterminal.local {
|
||||||
|
reverse_proxy localhost:9111
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[Unit]
|
||||||
|
After=network.target network-online.target ${SYSTEMD_MOUNT}
|
||||||
|
Requires=network-online.target ${SYSTEMD_MOUNT}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
[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
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[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
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=BirdNET Analysis
|
||||||
|
Requires=${SYSTEMD_MOUNT}
|
||||||
|
After=network-online.target ${SYSTEMD_MOUNT}
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
Type=simple
|
||||||
|
RestartSec=3
|
||||||
|
User=${BIRDNET_USER}
|
||||||
|
ExecStart=/usr/local/bin/birdnet_analysis.sh
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=BirdNET Analysis Log
|
||||||
|
[Service]
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=3
|
||||||
|
Type=simple
|
||||||
|
User=${BIRDNET_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
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=BirdNET Recording
|
||||||
|
[Service]
|
||||||
|
Environment=XDG_RUNTIME_DIR=/run/user/1000
|
||||||
|
Restart=always
|
||||||
|
Type=simple
|
||||||
|
RestartSec=3
|
||||||
|
User=${BIRDNET_USER}
|
||||||
|
ExecStart=/usr/local/bin/birdnet_recording.sh
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=BirdNET Statistics Log
|
||||||
|
[Service]
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=3
|
||||||
|
Type=simple
|
||||||
|
User=${BIRDNET_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
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
[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
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#birdnet
|
||||||
|
0 1 * * * /usr/local/bin/cleanup.sh &> /dev/null
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=BirdNET BirdSound Extraction
|
||||||
|
[Service]
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=3
|
||||||
|
Type=simple
|
||||||
|
User=${BIRDNET_USER}
|
||||||
|
ExecStart=/usr/local/bin/extract_new_birdsounds.sh
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=BirdNET BirdSound Extraction Timer
|
||||||
|
Requires=extraction.service
|
||||||
|
[Timer]
|
||||||
|
Unit=extraction.service
|
||||||
|
OnCalendar=*:0/10
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=BirdNET Extraction Log
|
||||||
|
[Service]
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=3
|
||||||
|
Type=simple
|
||||||
|
User=${BIRDNET_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
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
preferences {
|
||||||
|
font_size = 16
|
||||||
|
foreground_color = "rgb(0, 0, 0)"
|
||||||
|
background_color = "rgb(119, 196, 135)"
|
||||||
|
}
|
||||||
@@ -0,0 +1,445 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>BirdNET-Lite</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<style>
|
||||||
|
* { padding: 0; margin: 0; }
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-rendering: optimizespeed;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #006ed3;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover,
|
||||||
|
h1 a:hover {
|
||||||
|
color: #319cff;
|
||||||
|
}
|
||||||
|
|
||||||
|
header,
|
||||||
|
#summary {
|
||||||
|
padding-left: 5%;
|
||||||
|
padding-right: 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
th:first-child,
|
||||||
|
td:first-child {
|
||||||
|
width: 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
th:last-child,
|
||||||
|
td:last-child {
|
||||||
|
width: 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
padding-top: 25px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: normal;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow-x: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 a {
|
||||||
|
color: #000;
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 a:first-child {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta {
|
||||||
|
font-size: 12px;
|
||||||
|
font-family: Verdana, sans-serif;
|
||||||
|
border-bottom: 1px solid #9C9C9C;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-item {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filter {
|
||||||
|
padding: 4px;
|
||||||
|
border: 1px solid #CCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
border-bottom: 1px dashed #dadada;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:hover {
|
||||||
|
background-color: #ffffec;
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
text-align: left;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
padding-top: 15px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
font-size: 16px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
th a {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
th svg {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:nth-child(2) {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:nth-child(3),
|
||||||
|
th:nth-child(3) {
|
||||||
|
padding: 0 20px 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th:nth-child(4),
|
||||||
|
td:nth-child(4) {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:nth-child(2) svg {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
td .name,
|
||||||
|
td .goup {
|
||||||
|
margin-left: 1.75em;
|
||||||
|
word-break: break-all;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon.sort {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
position: relative;
|
||||||
|
top: .2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon.sort .top {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon.sort .bottom {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -1px;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
padding: 40px 20px;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.hideable {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:nth-child(2) {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
th:nth-child(3),
|
||||||
|
td:nth-child(3) {
|
||||||
|
padding-right: 5%;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 a {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filter {
|
||||||
|
max-width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body {
|
||||||
|
background-color: #101010;
|
||||||
|
color: #dddddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background-color: #151515;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:hover {
|
||||||
|
background-color: #252525;
|
||||||
|
}
|
||||||
|
|
||||||
|
header a,
|
||||||
|
th a {
|
||||||
|
color: #dddddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #5796d1;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover,
|
||||||
|
h1 a:hover {
|
||||||
|
color: #62b2fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
border-bottom: 1px dashed rgba(255, 255, 255, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
#up-arrow,
|
||||||
|
#down-arrow {
|
||||||
|
fill: #dddddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filter {
|
||||||
|
background-color: #151515;
|
||||||
|
color: #ffffff;
|
||||||
|
border: 1px solid #212121;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta {
|
||||||
|
border-bottom: 1px solid #212121
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body onload='initFilter()'>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="0" width="0" style="position: absolute;">
|
||||||
|
<defs>
|
||||||
|
<!-- Folder -->
|
||||||
|
<g id="folder" fill-rule="nonzero" fill="none">
|
||||||
|
<path d="M285.22 37.55h-142.6L110.9 0H31.7C14.25 0 0 16.9 0 37.55v75.1h316.92V75.1c0-20.65-14.26-37.55-31.7-37.55z" fill="#FFA000"/>
|
||||||
|
<path d="M285.22 36H31.7C14.25 36 0 50.28 0 67.74v158.7c0 17.47 14.26 31.75 31.7 31.75H285.2c17.44 0 31.7-14.3 31.7-31.75V67.75c0-17.47-14.26-31.75-31.7-31.75z" fill="#FFCA28"/>
|
||||||
|
</g>
|
||||||
|
<g id="folder-shortcut" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="folder-shortcut-group" fill-rule="nonzero">
|
||||||
|
<g id="folder-shortcut-shape">
|
||||||
|
<path d="M285.224876,37.5486902 L142.612438,37.5486902 L110.920785,0 L31.6916529,0 C14.2612438,0 0,16.8969106 0,37.5486902 L0,112.646071 L316.916529,112.646071 L316.916529,75.0973805 C316.916529,54.4456008 302.655285,37.5486902 285.224876,37.5486902 Z" id="Shape" fill="#FFA000"></path>
|
||||||
|
<path d="M285.224876,36 L31.6916529,36 C14.2612438,36 0,50.2838568 0,67.7419039 L0,226.451424 C0,243.909471 14.2612438,258.193328 31.6916529,258.193328 L285.224876,258.193328 C302.655285,258.193328 316.916529,243.909471 316.916529,226.451424 L316.916529,67.7419039 C316.916529,50.2838568 302.655285,36 285.224876,36 Z" id="Shape" fill="#FFCA28"></path>
|
||||||
|
</g>
|
||||||
|
<path d="M126.154134,250.559184 C126.850974,251.883673 127.300549,253.006122 127.772602,254.106122 C128.469442,255.206122 128.919016,256.104082 129.638335,257.002041 C130.559962,258.326531 131.728855,259 133.100057,259 C134.493737,259 135.415364,258.55102 136.112204,257.67551 C136.809044,257.002041 137.258619,255.902041 137.258619,254.577551 C137.258619,253.904082 137.258619,252.804082 137.033832,251.457143 C136.786566,249.908163 136.561779,249.032653 136.561779,248.583673 C136.089726,242.814286 135.864939,237.920408 135.864939,233.273469 C135.864939,225.057143 136.786566,217.514286 138.180246,210.846939 C139.798713,204.202041 141.889234,198.634694 144.429328,193.763265 C147.216689,188.869388 150.678411,184.873469 154.836973,181.326531 C158.995535,177.779592 163.626149,174.883673 168.481552,172.661224 C173.336954,170.438776 179.113983,168.665306 185.587852,167.340816 C192.061722,166.218367 198.760378,165.342857 205.481514,164.669388 C212.18017,164.220408 219.598146,163.995918 228.162535,163.995918 L246.055591,163.995918 L246.055591,195.514286 C246.055591,197.736735 246.752431,199.510204 248.370899,201.059184 C250.214153,202.608163 252.079886,203.506122 254.372715,203.506122 C256.463236,203.506122 258.531277,202.608163 260.172223,201.059184 L326.102289,137.797959 C327.720757,136.24898 328.642384,134.47551 328.642384,132.253061 C328.642384,130.030612 327.720757,128.257143 326.102289,126.708163 L260.172223,63.4469388 C258.553756,61.8979592 256.463236,61 254.395194,61 C252.079886,61 250.236632,61.8979592 248.393377,63.4469388 C246.77491,64.9959184 246.07807,66.7693878 246.07807,68.9918367 L246.07807,100.510204 L228.162535,100.510204 C166.863084,100.510204 129.166282,117.167347 115.274437,150.459184 C110.666301,161.54898 108.350993,175.310204 108.350993,191.742857 C108.350993,205.279592 113.903236,223.912245 124.760454,247.438776 C125.00772,248.112245 125.457294,249.010204 126.154134,250.559184 Z" id="Shape" fill="#FFFFFF" transform="translate(218.496689, 160.000000) scale(-1, 1) translate(-218.496689, -160.000000) "></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- File -->
|
||||||
|
<g id="file" stroke="#000" stroke-width="25" fill="#FFF" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M13 24.12v274.76c0 6.16 5.87 11.12 13.17 11.12H239c7.3 0 13.17-4.96 13.17-11.12V136.15S132.6 13 128.37 13H26.17C18.87 13 13 17.96 13 24.12z"/>
|
||||||
|
<path d="M129.37 13L129 113.9c0 10.58 7.26 19.1 16.27 19.1H249L129.37 13z"/>
|
||||||
|
</g>
|
||||||
|
<g id="file-shortcut" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="file-shortcut-group" transform="translate(13.000000, 13.000000)">
|
||||||
|
<g id="file-shortcut-shape" stroke="#000000" stroke-width="25" fill="#FFFFFF" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M0,11.1214886 L0,285.878477 C0,292.039924 5.87498876,296.999983 13.1728373,296.999983 L225.997983,296.999983 C233.295974,296.999983 239.17082,292.039942 239.17082,285.878477 L239.17082,123.145388 C239.17082,123.145388 119.58541,2.84217094e-14 115.369423,2.84217094e-14 L13.1728576,2.84217094e-14 C5.87500907,-1.71479982e-05 0,4.96022995 0,11.1214886 Z" id="rect1171"></path>
|
||||||
|
<path d="M116.37005,0 L116,100.904964 C116,111.483663 123.258008,120 132.273377,120 L236,120 L116.37005,0 L116.37005,0 Z" id="rect1794"></path>
|
||||||
|
</g>
|
||||||
|
<path d="M47.803141,294.093878 C48.4999811,295.177551 48.9495553,296.095918 49.4216083,296.995918 C50.1184484,297.895918 50.5680227,298.630612 51.2873415,299.365306 C52.2089688,300.44898 53.3778619,301 54.7490634,301 C56.1427436,301 57.0643709,300.632653 57.761211,299.916327 C58.4580511,299.365306 58.9076254,298.465306 58.9076254,297.381633 C58.9076254,296.830612 58.9076254,295.930612 58.6828382,294.828571 C58.4355724,293.561224 58.2107852,292.844898 58.2107852,292.477551 C57.7387323,287.757143 57.5139451,283.753061 57.5139451,279.95102 C57.5139451,273.228571 58.4355724,267.057143 59.8292526,261.602041 C61.44772,256.165306 63.5382403,251.610204 66.0783349,247.62449 C68.8656954,243.620408 72.3274172,240.35102 76.4859792,237.44898 C80.6445412,234.546939 85.2751561,232.177551 90.1305582,230.359184 C94.9859603,228.540816 100.76299,227.089796 107.236859,226.006122 C113.710728,225.087755 120.409385,224.371429 127.13052,223.820408 C133.829177,223.453061 141.247152,223.269388 149.811542,223.269388 L167.704598,223.269388 L167.704598,249.057143 C167.704598,250.87551 168.401438,252.326531 170.019905,253.593878 C171.86316,254.861224 173.728893,255.595918 176.021722,255.595918 C178.112242,255.595918 180.180284,254.861224 181.82123,253.593878 L247.751296,201.834694 C249.369763,200.567347 250.291391,199.116327 250.291391,197.297959 C250.291391,195.479592 249.369763,194.028571 247.751296,192.761224 L181.82123,141.002041 C180.202763,139.734694 178.112242,139 176.044201,139 C173.728893,139 171.885639,139.734694 170.042384,141.002041 C168.423917,142.269388 167.727077,143.720408 167.727077,145.538776 L167.727077,171.326531 L149.811542,171.326531 C88.5120908,171.326531 50.8152886,184.955102 36.9234437,212.193878 C32.3153075,221.267347 30,232.526531 30,245.971429 C30,257.046939 35.5522422,272.291837 46.4094607,291.540816 C46.6567266,292.091837 47.1063009,292.826531 47.803141,294.093878 Z" id="Shape-Copy" fill="#000000" fill-rule="nonzero" transform="translate(140.145695, 220.000000) scale(-1, 1) translate(-140.145695, -220.000000) "></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Up arrow -->
|
||||||
|
<g id="up-arrow" transform="translate(-279.22 -208.12)">
|
||||||
|
<path transform="matrix(.22413 0 0 .12089 335.67 164.35)" stroke-width="0" d="m-194.17 412.01h-28.827-28.827l14.414-24.965 14.414-24.965 14.414 24.965z"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Down arrow -->
|
||||||
|
<g id="down-arrow" transform="translate(-279.22 -208.12)">
|
||||||
|
<path transform="matrix(.22413 0 0 -.12089 335.67 257.93)" stroke-width="0" d="m-194.17 412.01h-28.827-28.827l14.414-24.965 14.414-24.965 14.414 24.965z"/>
|
||||||
|
</g>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<h1>
|
||||||
|
<a href="">BirdNET-Lite Extractions</a>
|
||||||
|
</h1>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<div class="meta">
|
||||||
|
<div id="summary">
|
||||||
|
<span class="meta-item"><b>2</b> directories</span>
|
||||||
|
<span class="meta-item"><b>2</b> files</span>
|
||||||
|
<span class="meta-item"><input type="text" placeholder="filter" id="filter" onkeyup='filter()'></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="listing">
|
||||||
|
<table aria-describedby="summary">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th>
|
||||||
|
<a href="?sort=namedirfirst&order=desc" class="icon"><svg width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#up-arrow"></use></svg></a>
|
||||||
|
<a href="?sort=name&order=asc">Name</a>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<a href="?sort=size&order=asc">Size</a>
|
||||||
|
</th>
|
||||||
|
<th class="hideable">
|
||||||
|
<a href="?sort=time&order=asc">Modified</a>
|
||||||
|
</th>
|
||||||
|
<th class="hideable"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="file">
|
||||||
|
<td></td>
|
||||||
|
<td>
|
||||||
|
<a href="./By_Date/">
|
||||||
|
<svg width="1.5em" height="1em" version="1.1" viewBox="0 0 317 259"><use xlink:href="#folder"></use></svg>
|
||||||
|
<span class="name">By_Date</span>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td data-order="-1">—</td>
|
||||||
|
<td class="hideable"><time datetime="2021-09-01T21:40:53Z">09/01/2021 09:40:53 PM +00:00</time></td>
|
||||||
|
<td class="hideable"></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="file">
|
||||||
|
<td></td>
|
||||||
|
<td>
|
||||||
|
<a href="./By_Species/">
|
||||||
|
<svg width="1.5em" height="1em" version="1.1" viewBox="0 0 317 259"><use xlink:href="#folder"></use></svg>
|
||||||
|
<span class="name">By_Species</span>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td data-order="-1">—</td>
|
||||||
|
<td class="hideable"><time datetime="2021-09-01T21:40:48Z">09/01/2021 09:40:48 PM +00:00</time></td>
|
||||||
|
<td class="hideable"></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="file">
|
||||||
|
<td></td>
|
||||||
|
<td>
|
||||||
|
<a href="./Processed">
|
||||||
|
<svg width="1.5em" height="1em" version="1.1" viewBox="0 0 265 323"><use xlink:href="#file-shortcut"></use></svg>
|
||||||
|
<span class="name">Processed</span>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td data-order="28">28 B</td>
|
||||||
|
<td class="hideable"><time datetime="2021-09-01T21:40:14Z">09/01/2021 09:40:14 PM +00:00</time></td>
|
||||||
|
<td class="hideable"></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="file">
|
||||||
|
<td></td>
|
||||||
|
<td>
|
||||||
|
<a href="./stream">
|
||||||
|
<svg width="1.5em" height="1em" version="1.1" viewBox="0 0 265 323"><use xlink:href="#file"></use></svg>
|
||||||
|
<span class="name">stream</span>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td data-order="0">0 B</td>
|
||||||
|
<td class="hideable"><time datetime="2021-09-01T21:30:42Z">09/01/2021 09:30:42 PM +00:00</time></td>
|
||||||
|
<td class="hideable"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
Served with <a rel="noopener noreferrer" href="https://caddyserver.com">Caddy</a>
|
||||||
|
</footer>
|
||||||
|
<script>
|
||||||
|
var filterEl = document.getElementById('filter');
|
||||||
|
filterEl.focus();
|
||||||
|
|
||||||
|
function initFilter() {
|
||||||
|
if (!filterEl.value) {
|
||||||
|
var filterParam = new URL(window.location.href).searchParams.get('filter');
|
||||||
|
if (filterParam) {
|
||||||
|
filterEl.value = filterParam;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filter();
|
||||||
|
}
|
||||||
|
|
||||||
|
function filter() {
|
||||||
|
var q = filterEl.value.trim().toLowerCase();
|
||||||
|
var elems = document.querySelectorAll('tr.file');
|
||||||
|
elems.forEach(function(el) {
|
||||||
|
if (!q) {
|
||||||
|
el.style.display = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var nameEl = el.querySelector('.name');
|
||||||
|
var nameVal = nameEl.textContent.trim().toLowerCase();
|
||||||
|
if (nameVal.indexOf(q) !== -1) {
|
||||||
|
el.style.display = '';
|
||||||
|
} else {
|
||||||
|
el.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function localizeDatetime(e, index, ar) {
|
||||||
|
if (e.textContent === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var d = new Date(e.getAttribute('datetime'));
|
||||||
|
if (isNaN(d)) {
|
||||||
|
d = new Date(e.textContent);
|
||||||
|
if (isNaN(d)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.textContent = d.toLocaleString([], {day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
||||||
|
}
|
||||||
|
var timeList = Array.prototype.slice.call(document.getElementsByTagName("time"));
|
||||||
|
timeList.forEach(localizeDatetime);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=BirdNET-Lite Live Stream
|
||||||
|
[Service]
|
||||||
|
Environment=XDG_RUNTIME_DIR=/run/user/1000
|
||||||
|
Restart=always
|
||||||
|
Type=simple
|
||||||
|
RestartSec=3
|
||||||
|
User=${BIRDNET_USER}
|
||||||
|
ExecStart=ffmpeg -loglevel 52 -ac 2 -f alsa -i ${REC_CARD} -acodec libmp3lame -b:a 320k -ac 2 -content_type 'audio/mpeg' -f mp3 icecast://source:${ICE_PWD}@localhost:8000/stream -re
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#birdnet
|
||||||
|
*/5 * * * * /usr/local/bin/species_notifier.sh &> /dev/null
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# This is the tmux.conf
|
||||||
|
set-option -g base-index 1
|
||||||
|
set-option -g pane-base-index 1
|
||||||
|
set-option -g allow-rename off
|
||||||
|
set-option -g clock-mode-style 12
|
||||||
|
set-option -g clock-mode-colour brightgreen
|
||||||
|
set-option -g window-status-current-style bg=brightgreen,fg=black
|
||||||
|
set-option -g window-status-separator '| |'
|
||||||
|
set-window-option -g mode-keys vi
|
||||||
|
|
||||||
|
|
||||||
|
set-option -g status-interval 5
|
||||||
|
set-option -g status 2
|
||||||
|
set-option -g status-format[1] '#[bg=cyan,fg=black]#[align=left][BirdNET-Lite] #[bg=cyan]#[align=centre]#(vcgencmd measure_temp) #(vcgencmd get_throttled)#[align=right]#(~/BirdNET-Lite/scripts/disk_usage.sh)%l:%M%p'
|
||||||
|
set-option -g status-style bg=magenta,fg=cyan
|
||||||
|
set-option -g status-justify centre
|
||||||
|
set-option -g status-left-length 50
|
||||||
|
set-option -g status-left '#[fg=brightyellow]arm clock #(vcgencmd measure_clock arm)'
|
||||||
|
set-option -g status-right '#[fg=brightyellow]%A, %B %e#(~/BirdNET-Lite/scripts/pretty_date.sh)'
|
||||||
|
|
||||||
|
bind r source-file /etc/tmux.conf \; display "Reloaded!"
|
||||||
|
|
||||||
|
bind -n M-1 select-window -t 1
|
||||||
|
bind -n M-2 select-window -t 2
|
||||||
|
bind -n M-3 select-window -t 3
|
||||||
|
bind -n M-4 select-window -t 4
|
||||||
|
bind -n M-5 select-window -t 5
|
||||||
|
bind -n M-6 select-window -t 6
|
||||||
|
bind -n M-7 select-window -t 7
|
||||||
|
bind -n M-8 select-window -t 8
|
||||||
|
bind -n M-TAB next
|
||||||
Reference in New Issue
Block a user