Merge branch 'main' into notifications
This commit is contained in:
+1
-1
@@ -10,4 +10,4 @@ liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
custom: https://thesatanictemple.com/pages/fundraiser#donate
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<h1 align="center">
|
||||
BirdNET-Pi <img src="https://img.shields.io/badge/Version-0.13-pink" />
|
||||
BirdNET-Pi
|
||||
</h1>
|
||||
<p align="center">
|
||||
A realtime acoustic bird classification system for the Raspberry Pi 4B
|
||||
@@ -37,6 +37,8 @@ Currently listening in these countries . . . that I know of . . .
|
||||
- Italy
|
||||
- Finland
|
||||
- Australia
|
||||
- Canada
|
||||
- Switzerland
|
||||
|
||||
## Features
|
||||
* 24/7 recording and BirdNET-Lite analysis
|
||||
@@ -141,3 +143,6 @@ Current database languages include the list below:
|
||||
| Swedish | 264 | 4.15% |
|
||||
| Thai | 5580 | 87.71% |
|
||||
| Ukrainian | 646 | 10.15% |
|
||||
|
||||
## :thinking:
|
||||
Are you a lucky ducky with an extra Raspberry Pi 4B lying around? [Here's an idea!](https://foldingathome.org/alternative-downloads)
|
||||
|
||||
@@ -48,6 +48,13 @@ ICE_PWD=birdnetpi
|
||||
|
||||
BIRDNETPI_URL=
|
||||
|
||||
#---------------------------- RTSP Stream URL -------------------------------#
|
||||
|
||||
## If RTSP_STREAM is set, the system will use the RTSP stream as its audio
|
||||
## source instead of recording its own audio. If this variable is kept empty,
|
||||
## BirdNET-Pi will default to recording its own audio.
|
||||
|
||||
RTSP_STREAM=
|
||||
|
||||
#------------------- Mobile Notifications via Pushed.co ---------------------#
|
||||
#____________The two variables below enable mobile notifications_______________#
|
||||
|
||||
@@ -513,3 +513,21 @@ button:hover {
|
||||
padding:5px;
|
||||
border: 2px solid black;
|
||||
}
|
||||
button.legacyview {
|
||||
color:gray;
|
||||
margin:5px;
|
||||
float:right;
|
||||
z-index:100;
|
||||
position:relative;
|
||||
font-size:small;
|
||||
background:#dbffeb;
|
||||
padding:5px;
|
||||
border: 2px solid black;
|
||||
}
|
||||
button.loadmore {
|
||||
margin-top:10px;
|
||||
font-size:x-large;
|
||||
background:#dbffeb;
|
||||
padding:10px;
|
||||
border: 2px solid black;
|
||||
}
|
||||
|
||||
+26
-4
@@ -185,10 +185,32 @@ if(isset($_GET['view'])){
|
||||
}
|
||||
}
|
||||
} elseif(isset($_GET['submit'])) {
|
||||
$command = $_GET['submit'];
|
||||
if(isset($command)){
|
||||
$results = shell_exec("$command 2>&1");
|
||||
echo "<pre>$results</pre>";
|
||||
if (file_exists('./scripts/thisrun.txt')) {
|
||||
$config = parse_ini_file('./scripts/thisrun.txt');
|
||||
} elseif (file_exists('./scripts/firstrun.ini')) {
|
||||
$config = parse_ini_file('./scripts/firstrun.ini');
|
||||
}
|
||||
$caddypwd = $config['CADDY_PWD'];
|
||||
if (!isset($_SERVER['PHP_AUTH_USER'])) {
|
||||
header('WWW-Authenticate: Basic realm="My Realm"');
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
echo 'You cannot access the web terminal';
|
||||
exit;
|
||||
} else {
|
||||
$submittedpwd = $_SERVER['PHP_AUTH_PW'];
|
||||
$submitteduser = $_SERVER['PHP_AUTH_USER'];
|
||||
if($submittedpwd == $caddypwd && $submitteduser == 'birdnet'){
|
||||
$command = $_GET['submit'];
|
||||
if(isset($command)){
|
||||
$results = shell_exec("$command 2>&1");
|
||||
echo "<pre>$results</pre>";
|
||||
} else {
|
||||
header('WWW-Authenticate: Basic realm="My Realm"');
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
echo 'You cannot access the web terminal';
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
ob_end_flush();
|
||||
} else {include('overview.php');}
|
||||
|
||||
Regular → Executable
+10
-3
@@ -6,11 +6,18 @@ USER=$USER
|
||||
export HOME=$HOME
|
||||
export USER=$USER
|
||||
|
||||
branch=main
|
||||
if ! which git &> /dev/null;then
|
||||
PACKAGES_MISSING=
|
||||
for cmd in git jq ; do
|
||||
if ! which $cmd &> /dev/null;then
|
||||
PACKAGES_MISSING="${PACKAGES_MISSING} $cmd"
|
||||
fi
|
||||
done
|
||||
if [[ ! -z $PACKAGES_MISSING ]] ; then
|
||||
sudo apt update
|
||||
sudo apt -y install git
|
||||
sudo apt -y install $PACKAGES_MISSING
|
||||
fi
|
||||
|
||||
branch=main
|
||||
git clone -b $branch https://github.com/mcguirepr89/BirdNET-Pi.git ${HOME}/BirdNET-Pi &&
|
||||
|
||||
$HOME/BirdNET-Pi/scripts/install_birdnet.sh
|
||||
|
||||
+22
-6
@@ -64,6 +64,19 @@ if(isset($_GET['submit'])) {
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($_GET["rtsp_stream"])) {
|
||||
$rtsp_stream = str_replace("\r\n", ",", $_GET["rtsp_stream"]);
|
||||
if(strcmp($rtsp_stream,$config['RTSP_STREAM']) !== 0) {
|
||||
$contents = preg_replace("/RTSP_STREAM=.*/", "RTSP_STREAM=$rtsp_stream", $contents);
|
||||
$contents2 = preg_replace("/RTSP_STREAM=.*/", "RTSP_STREAM=$rtsp_stream", $contents2);
|
||||
$fh = fopen('/etc/birdnet/birdnet.conf', "w");
|
||||
$fh2 = fopen("./scripts/thisrun.txt", "w");
|
||||
fwrite($fh, $contents);
|
||||
fwrite($fh2, $contents2);
|
||||
exec('sudo systemctl restart birdnet_recording.service');
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($_GET["overlap"])) {
|
||||
$overlap = $_GET["overlap"];
|
||||
if(strcmp($overlap,$config['OVERLAP']) !== 0) {
|
||||
@@ -108,14 +121,14 @@ if(isset($_GET['submit'])) {
|
||||
$contents2 = preg_replace("/PRIVACY_MODE=.*/", "PRIVACY_MODE=$privacy_mode", $contents2);
|
||||
if(strcmp($privacy_mode,"on") == 0) {
|
||||
exec('sudo sed -i \'s/\/usr\/local\/bin\/server.py/\/usr\/local\/bin\/privacy_server.py/g\' ../../BirdNET-Pi/templates/birdnet_server.service');
|
||||
exec('sudo systemctl daemon-reload');
|
||||
exec('restart_services.sh');
|
||||
header('Location: /log');
|
||||
exec('sudo systemctl daemon-reload');
|
||||
exec('restart_services.sh');
|
||||
header('Location: /log');
|
||||
} elseif(strcmp($privacy_mode,"off") == 0) {
|
||||
exec('sudo sed -i \'s/\/usr\/local\/bin\/privacy_server.py/\/usr\/local\/bin\/server.py/g\' ../../BirdNET-Pi/templates/birdnet_server.service');
|
||||
exec('sudo systemctl daemon-reload');
|
||||
exec('restart_services.sh');
|
||||
header('Location: /log');
|
||||
exec('sudo systemctl daemon-reload');
|
||||
exec('restart_services.sh');
|
||||
header('Location: /log');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,6 +214,9 @@ if (file_exists('./scripts/thisrun.txt')) {
|
||||
<label for="channels">Audio Channels: </label>
|
||||
<input name="channels" type="number" min="1" max="32" step="1" value="<?php print($newconfig['CHANNELS']);?>" required/><br>
|
||||
<p>Set Channels to the number of channels supported by your sound card. 32 max.</p>
|
||||
<label for="rtsp_stream">RTSP Stream: </label>
|
||||
<input name="rtsp_stream" type="url" value="<?php echo $newconfig['RTSP_STREAM'];?>"</input><br>
|
||||
<p>If you place an RTSP stream URL here, BirdNET-Pi will use that as its audio source.</p>
|
||||
<label for="recording_length">Recording Length: </label>
|
||||
<input name="recording_length" oninput="document.getElementsByName('extraction_length')[0].setAttribute('max', this.value);" type="number" min="3" max="60" step="1" value="<?php print($newconfig['RECORDING_LENGTH']);?>" required/><br>
|
||||
<p>Set Recording Length in seconds between 6 and 60. Multiples of 3 are recommended, as BirdNET analyzes in 3-second chunks.</p>
|
||||
|
||||
+50
-198
@@ -91,32 +91,16 @@ run_analysis() {
|
||||
fi
|
||||
|
||||
for i in "${files[@]}";do
|
||||
[ ! -f ${1}/${i} ] && continue
|
||||
echo "${1}/${i}" > $HOME/BirdNET-Pi/analyzing_now.txt
|
||||
[ -z ${RECORDING_LENGTH} ] && RECORDING_LENGTH=15
|
||||
[ ${RECORDING_LENGTH} == "60" ] && RECORDING_LENGTH=01:00
|
||||
FILE_LENGTH="$(ffmpeg -i ${1}/${i} 2>&1 | awk -F. '/Duration/ {print $1}' | cut -d':' -f3-4)"
|
||||
[ -z $FILE_LENGTH ] && sleep 1 && continue
|
||||
echo "RECORDING_LENGTH set to ${RECORDING_LENGTH}"
|
||||
a=0
|
||||
if [ "${RECORDING_LENGTH}" == "01:00" ];then
|
||||
until [ "$(ffmpeg -i ${1}/${i} 2>&1 | awk -F. '/Duration/ {print $1}' | cut -d':' -f3-4)" == "${RECORDING_LENGTH}" ];do
|
||||
sleep 1
|
||||
[ $a -ge 60 ] && rm -f ${1}/${i} && break
|
||||
a=$((a+1))
|
||||
done
|
||||
elif [ "${RECORDING_LENGTH}" -lt 10 ];then
|
||||
until [ "$(ffmpeg -i ${1}/${i} 2>&1 | awk -F. '/Duration/ {print $1}' | cut -d':' -f3-4)" == "00:0${RECORDING_LENGTH}" ];do
|
||||
sleep 1
|
||||
[ $a -ge ${RECORDING_LENGTH} ] && rm -f ${1}/${i} && break
|
||||
a=$((a+1))
|
||||
done
|
||||
else
|
||||
until [ "$(ffmpeg -i ${1}/${i} 2>&1 | awk -F. '/Duration/ {print $1}' | cut -d':' -f3-4)" == "00:${RECORDING_LENGTH}" ];do
|
||||
sleep 1
|
||||
[ $a -ge ${RECORDING_LENGTH} ] && rm -f ${1}/${i} && break
|
||||
a=$((a+1))
|
||||
done
|
||||
fi
|
||||
until [ -z "$(lsof -t ${1}/${i})" ];do
|
||||
sleep 2
|
||||
[ $a -ge ${RECORDING_LENGTH} ] && rm -f ${1}/${i} && break
|
||||
a=$((a+2))
|
||||
done
|
||||
|
||||
if ! grep 5050 <(netstat -tulpn 2>&1) &> /dev/null 2>&1;then
|
||||
echo "Waiting for socket"
|
||||
@@ -124,183 +108,48 @@ run_analysis() {
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
if [ -f ${1}/${i} ] && [ ! -f ${INCLUDE_LIST} ] && [ ! -f ${EXCLUDE_LIST} ] && [ -z $BIRDWEATHER_ID ];then
|
||||
echo "analyze.py \
|
||||
--i "${1}/${i}" \
|
||||
--o "${1}/${i}.csv" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--sensitivity "${SENSITIVITY}" \
|
||||
--min_conf "${CONFIDENCE}""
|
||||
analyze.py \
|
||||
--i "${1}/${i}" \
|
||||
--o "${1}/${i}.csv" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--sensitivity "${SENSITIVITY}" \
|
||||
--min_conf "${CONFIDENCE}"
|
||||
elif [ -f ${1}/${i} ] && [ -f ${INCLUDELIST} ] && [ ! -f ${EXCLUDE_LIST} ] && [ -z $BIRDWEATHER_ID ];then
|
||||
echo "analyze.py \
|
||||
--i "${1}/${i}" \
|
||||
--o "${1}/${i}.csv" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--sensitivity "${SENSITIVITY}" \
|
||||
--min_conf "${CONFIDENCE}" \
|
||||
--include_list "${INCLUDE_LIST}""
|
||||
analyze.py \
|
||||
--i "${1}/${i}" \
|
||||
--o "${1}/${i}.csv" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--sensitivity "${SENSITIVITY}" \
|
||||
--min_conf "${CONFIDENCE}" \
|
||||
--include_list "${INCLUDE_LIST}"
|
||||
elif [ -f ${1}/${i} ] && [ ! -f ${INCLUDE_LIST} ] && [ -f ${EXCLUDE_LIST} ] && [ -z $BIRDWEATHER_ID ];then
|
||||
echo "analyze.py \
|
||||
--i "${1}/${i}" \
|
||||
--o "${1}/${i}.csv" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--sensitivity "${SENSITIVITY}" \
|
||||
--min_conf "${CONFIDENCE}" \
|
||||
--exclude_list "${EXCLUDE_LIST}""
|
||||
analyze.py \
|
||||
--i "${1}/${i}" \
|
||||
--o "${1}/${i}.csv" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--sensitivity "${SENSITIVITY}" \
|
||||
--min_conf "${CONFIDENCE}" \
|
||||
--exclude_list "${EXCLUDE_LIST}"
|
||||
elif [ -f ${1}/${i} ] && [ -f ${INCLUDE_LIST} ] && [ -f ${EXCLUDE_LIST} ] && [ -z $BIRDWEATHER_ID ];then
|
||||
echo "analyze.py \
|
||||
--i "${1}/${i}" \
|
||||
--o "${1}/${i}.csv" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--sensitivity "${SENSITIVITY}" \
|
||||
--min_conf "${CONFIDENCE}" \
|
||||
--include_list "${INCLUDE_LIST}" \
|
||||
--exclude_list "${EXCLUDE_LIST}""
|
||||
analyze.py \
|
||||
--i "${1}/${i}" \
|
||||
--o "${1}/${i}.csv" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--sensitivity "${SENSITIVITY}" \
|
||||
--min_conf "${CONFIDENCE}" \
|
||||
--include_list "${INCLUDE_LIST}" \
|
||||
--exclude_list "${EXCLUDE_LIST}"
|
||||
elif [ -f ${1}/${i} ] && [ ! -f ${INCLUDE_LIST} ] && [ ! -f ${EXCLUDE_LIST} ] && [ ! -z $BIRDWEATHER_ID ];then
|
||||
echo "analyze.py \
|
||||
--i "${1}/${i}" \
|
||||
--o "${1}/${i}.csv" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--sensitivity "${SENSITIVITY}" \
|
||||
--min_conf "${CONFIDENCE}" \
|
||||
--birdweather_id "IN_USE""
|
||||
analyze.py \
|
||||
--i "${1}/${i}" \
|
||||
--o "${1}/${i}.csv" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--sensitivity "${SENSITIVITY}" \
|
||||
--min_conf "${CONFIDENCE}" \
|
||||
--birdweather_id "${BIRDWEATHER_ID}"
|
||||
elif [ -f ${1}/${i} ] && [ -f ${INCLUDE_LIST} ] && [ ! -f ${EXCLUDE_LIST} ] && [ ! -z $BIRDWEATHER_ID ];then
|
||||
echo "analyze.py \
|
||||
--i "${1}/${i}" \
|
||||
--o "${1}/${i}.csv" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--sensitivity "${SENSITIVITY}" \
|
||||
--min_conf "${CONFIDENCE}" \
|
||||
--include_list "${INCLUDE_LIST}" \
|
||||
--birdweather_id "IN_USE""
|
||||
analyze.py \
|
||||
--i "${1}/${i}" \
|
||||
--o "${1}/${i}.csv" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--sensitivity "${SENSITIVITY}" \
|
||||
--min_conf "${CONFIDENCE}" \
|
||||
--include_list "${INCLUDE_LIST}" \
|
||||
--birdweather_id "${BIRDWEATHER_ID}"
|
||||
elif [ -f ${1}/${i} ] && [ ! -f ${INCLUDE_LIST} ] && [ -f ${EXCLUDE_LIST} ] && [ ! -z $BIRDWEATHER_ID ];then
|
||||
echo "analyze.py \
|
||||
--i "${1}/${i}" \
|
||||
--o "${1}/${i}.csv" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--sensitivity "${SENSITIVITY}" \
|
||||
--min_conf "${CONFIDENCE}" \
|
||||
--exclude_list "${EXCLUDE_LIST}" \
|
||||
--birdweather_id "IN_USE""
|
||||
analyze.py \
|
||||
--i "${1}/${i}" \
|
||||
--o "${1}/${i}.csv" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--sensitivity "${SENSITIVITY}" \
|
||||
--min_conf "${CONFIDENCE}" \
|
||||
--exclude_list "${EXCLUDE_LIST}" \
|
||||
--birdweather_id "${BIRDWEATHER_ID}"
|
||||
elif [ -f ${1}/${i} ] && [ -f ${INCLUDE_LIST} ] && [ -f ${EXCLUDE_LIST} ] && [ ! -z $BIRDWEATHER_ID ];then
|
||||
echo "analyze.py \
|
||||
--i "${1}/${i}" \
|
||||
--o "${1}/${i}.csv" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--sensitivity "${SENSITIVITY}" \
|
||||
--min_conf "${CONFIDENCE}" \
|
||||
--include_list "${INCLUDE_LIST}" \
|
||||
--exclude_list "${EXCLUDE_LIST}" \
|
||||
--birdweather_id "IN_USE""
|
||||
analyze.py \
|
||||
--i "${1}/${i}" \
|
||||
--o "${1}/${i}.csv" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--sensitivity "${SENSITIVITY}" \
|
||||
--min_conf "${CONFIDENCE}" \
|
||||
--include_list "${INCLUDE_LIST}" \
|
||||
--exclude_list "${EXCLUDE_LIST}" \
|
||||
--birdweather_id "${BIRDWEATHER_ID}"
|
||||
# prepare optional parameters for analyse.py
|
||||
if [ -f ${INCLUDE_LIST} ]; then
|
||||
INCLUDEPARAM="--include_list \"${INCLUDE_LIST}\""
|
||||
else
|
||||
INCLUDEPARAM=""
|
||||
fi
|
||||
if [ -f ${EXCLUDE_LIST} ]; then
|
||||
EXCLUDEPARAM="--include_list \"${EXCLUDE_LIST}\""
|
||||
else
|
||||
EXCLUDEPARAM=""
|
||||
fi
|
||||
if [ ! -z $BIRDWEATHER_ID ]; then
|
||||
BIRDWEATHER_ID_PARAM="--birdweather_id \"${BIRDWEATHER_ID}\""
|
||||
BIRDWEATHER_ID_LOG="--birdweather_id \"IN_USE\""
|
||||
else
|
||||
BIRDWEATHER_ID_PARAM=""
|
||||
BIRDWEATHER_ID_LOG=""
|
||||
fi
|
||||
echo analyze.py \
|
||||
--i "${1}/${i}" \
|
||||
--o "${1}/${i}.csv" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--sensitivity "${SENSITIVITY}" \
|
||||
--min_conf "${CONFIDENCE}" \
|
||||
${INCLUDEPARAM} \
|
||||
${EXCLUDEPARAM} \
|
||||
${BIRDWEATHER_ID_LOG}
|
||||
analyze.py \
|
||||
--i "${1}/${i}" \
|
||||
--o "${1}/${i}.csv" \
|
||||
--lat "${LATITUDE}" \
|
||||
--lon "${LONGITUDE}" \
|
||||
--week "${WEEK}" \
|
||||
--overlap "${OVERLAP}" \
|
||||
--sensitivity "${SENSITIVITY}" \
|
||||
--min_conf "${CONFIDENCE}" \
|
||||
${INCLUDEPARAM} \
|
||||
${EXCLUDEPARAM} \
|
||||
${BIRDWEATHER_ID_PARAM}
|
||||
done
|
||||
}
|
||||
|
||||
@@ -318,13 +167,16 @@ until grep 5050 <(netstat -tulpn 2>&1) &> /dev/null 2>&1;do
|
||||
done
|
||||
|
||||
if [ $(find ${RECS_DIR} -maxdepth 1 -name '*wav' | wc -l) -gt 0 ];then
|
||||
find $RECS_DIR -maxdepth 1 -name '*wav' -type f -size 0 -delete
|
||||
run_birdnet "${RECS_DIR}"
|
||||
fi
|
||||
|
||||
YESTERDAY="$RECS_DIR/$(date --date="yesterday" "+%B-%Y/%d-%A")"
|
||||
TODAY="$RECS_DIR/$(date "+%B-%Y/%d-%A")"
|
||||
if [ $(find ${YESTERDAY} -name '*wav' 2>/dev/null | wc -l) -gt 0 ];then
|
||||
find $YESTERDAY -name '*wav' -type f -size 0 -delete
|
||||
run_birdnet "${YESTERDAY}"
|
||||
elif [ $(find ${TODAY} -name '*wav' | wc -l) -gt 0 ];then
|
||||
find $TODAY -name '*wav' -type f -size 0 -delete
|
||||
run_birdnet "${TODAY}"
|
||||
fi
|
||||
|
||||
@@ -2,29 +2,29 @@
|
||||
set -x
|
||||
source /etc/birdnet/birdnet.conf
|
||||
|
||||
if [ "${TIMESTAMP_FORMAT}" == "12" ];then
|
||||
STAMP="%I:%M:%S%P"
|
||||
else
|
||||
STAMP="%H:%M:%S"
|
||||
fi
|
||||
|
||||
[ -z $RECORDING_LENGTH ] && RECORDING_LENGTH=15
|
||||
|
||||
if ! pulseaudio --check;then pulseaudio --start;fi
|
||||
|
||||
if pgrep arecord &> /dev/null ;then
|
||||
echo "Recording"
|
||||
else
|
||||
until grep 5050 <(netstat -tulpn 2>&1);do
|
||||
sleep 1
|
||||
if [ ! -z $RTSP_STREAM ];then
|
||||
while true;do
|
||||
for i in ${RTSP_STREAM//,/ };do
|
||||
ffmpeg -i ${i} -t ${RECORDING_LENGTH} -vn -acodec pcm_s16le -ac 2 -ar 48000 file:${RECS_DIR}/$(date "+%F")-birdnet-$(date "+%H:%M:%S").wav
|
||||
done
|
||||
done
|
||||
if [ -z ${REC_CARD} ];then
|
||||
|
||||
arecord -f S16_LE -c${CHANNELS} -r48000 -t wav --max-file-time ${RECORDING_LENGTH}\
|
||||
--use-strftime ${RECS_DIR}/%B-%Y/%d-%A/%F-birdnet-${STAMP}.wav
|
||||
else
|
||||
if ! pulseaudio --check;then pulseaudio --start;fi
|
||||
if pgrep arecord &> /dev/null ;then
|
||||
echo "Recording"
|
||||
else
|
||||
arecord -f S16_LE -c${CHANNELS} -r48000 -t wav --max-file-time ${RECORDING_LENGTH}\
|
||||
-D "${REC_CARD}" --use-strftime \
|
||||
${RECS_DIR}/%B-%Y/%d-%A/%F-birdnet-${STAMP}.wav
|
||||
until grep 5050 <(netstat -tulpn 2>&1);do
|
||||
sleep 1
|
||||
done
|
||||
if [ -z ${REC_CARD} ];then
|
||||
arecord -f S16_LE -c${CHANNELS} -r48000 -t wav --max-file-time ${RECORDING_LENGTH}\
|
||||
--use-strftime ${RECS_DIR}/%B-%Y/%d-%A/%F-birdnet-%H:%M:%S.wav
|
||||
else
|
||||
arecord -f S16_LE -c${CHANNELS} -r48000 -t wav --max-file-time ${RECORDING_LENGTH}\
|
||||
-D "${REC_CARD}" --use-strftime \
|
||||
${RECS_DIR}/%B-%Y/%d-%A/%F-birdnet-%H:%M:%S.wav
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -13,9 +13,9 @@ source /etc/birdnet/birdnet.conf
|
||||
|
||||
# Set Variables
|
||||
TMPFILE=$(mktemp)
|
||||
ANALYZED=${RECS_DIR}/*/*Analyzed
|
||||
# SCAN_DIRS are all directories marked "Analyzed"
|
||||
SCAN_DIRS=($(find ${ANALYZED} -type d 2>/dev/null | sort ))
|
||||
#ANALYZED=${RECS_DIR}/*/*Analyzed
|
||||
#SCAN_DIRS are all directories marked "Analyzed"
|
||||
SCAN_DIRS=($(find $HOME -type d -name '*Analyzed' 2>/dev/null | sort ))
|
||||
|
||||
for h in "${SCAN_DIRS[@]}";do
|
||||
# The TMPFILE is created from each .csv file BirdNET creates
|
||||
@@ -26,25 +26,18 @@ for h in "${SCAN_DIRS[@]}";do
|
||||
# Field 4: Common name
|
||||
# Field 5: Confidence
|
||||
|
||||
# Removes old directories
|
||||
#if echo "${h}" | grep $(date --date="yesterday" "+%A") &> /dev/null;then
|
||||
# echo "Removing old directories"
|
||||
# rm -drf "${h}"
|
||||
# rm -drf "$(echo ${h} | cut -d'-' -f1-3)"
|
||||
# continue
|
||||
#fi
|
||||
|
||||
# Iterates over each "Analyzed" directory
|
||||
for i in $(find ${h} -name '*csv' 2>/dev/null | sort );do
|
||||
# Iterates over each '.csv' file found in each "Analyzed" directory
|
||||
# to create the TMPFILE
|
||||
echo "${i}" | cut -d'/' -f7 >> ${TMPFILE}
|
||||
echo "$(basename ${i})" >> ${TMPFILE}
|
||||
sort -k1n -t\; "${i}" | awk '!/Start/{print}' >> ${TMPFILE}
|
||||
done
|
||||
|
||||
# The extraction reads each line of the TMPFILE and sets the variables ffmpeg
|
||||
# will use.
|
||||
while read -r line;do
|
||||
echo "Line = $line"
|
||||
DATE="$(echo "${line}" \
|
||||
| awk -F- '/birdnet/{print $1"-"$2"-"$3}')"
|
||||
if [ ! -z ${DATE} ];then
|
||||
@@ -142,7 +135,14 @@ for h in "${SCAN_DIRS[@]}";do
|
||||
# next extraction.
|
||||
[[ -d "${PROCESSED}" ]] || mkdir "${PROCESSED}"
|
||||
#echo "Moving processed files to ${PROCESSED}"
|
||||
mv ${h}/* ${PROCESSED} &> /dev/null || continue
|
||||
mv ${h}/* ${PROCESSED} &> /dev/null || true
|
||||
|
||||
# Removes old directories
|
||||
if echo "${h}" | grep $(date --date="-2 day" "+%A") &> /dev/null;then
|
||||
echo "Removing old directories"
|
||||
rm -drf "${h}"
|
||||
rm -drf "$(echo ${h} | cut -d'-' -f1-3)"
|
||||
fi
|
||||
done
|
||||
|
||||
#echo "Linking Processed files to "${EXTRACTED}/Processed" web directory"
|
||||
|
||||
@@ -19,8 +19,8 @@ install_config() {
|
||||
## TO BE CHANGED TO STATIC VALUES
|
||||
## Please only go to 4 decimal places. Example:43.3984
|
||||
|
||||
LATITUDE=$(curl -s4 ifconfig.co/json | awk '/lat/ {print $2}' | tr -d ',')
|
||||
LONGITUDE=$(curl -s4 ifconfig.co/json | awk '/lon/ {print $2}' | tr -d ',')
|
||||
LATITUDE=$(curl -s4 ifconfig.co/json | jq .latitude)
|
||||
LONGITUDE=$(curl -s4 ifconfig.co/json | jq .longitude)
|
||||
|
||||
#--------------------- BirdWeather Station Information -----------------------#
|
||||
#_____________The variable below can be set to have your BirdNET-Pi____________#
|
||||
@@ -61,6 +61,13 @@ ICE_PWD=birdnetpi
|
||||
|
||||
BIRDNETPI_URL=
|
||||
|
||||
#---------------------------- RTSP Stream URL -------------------------------#
|
||||
|
||||
## If RTSP_STREAM is set, the system will use the RTSP stream as its audio
|
||||
## source instead of recording its own audio. If this variable is kept empty,
|
||||
## BirdNET-Pi will default to recording its own audio.
|
||||
|
||||
RTSP_STREAM=
|
||||
|
||||
#------------------- Mobile Notifications via Pushed.co ---------------------#
|
||||
#____________The two variables below enable mobile notifications_______________#
|
||||
|
||||
@@ -18,7 +18,7 @@ install_depends() {
|
||||
apt install -qqy caddy ftpd sqlite3 php-sqlite3 alsa-utils \
|
||||
pulseaudio avahi-utils sox libsox-fmt-mp3 php php-fpm php-curl php-xml \
|
||||
php-zip icecast2 swig ffmpeg wget unzip curl cmake make bc libjpeg-dev \
|
||||
zlib1g-dev python3-dev python3-pip python3-venv
|
||||
zlib1g-dev python3-dev python3-pip python3-venv lsof
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,8 +4,12 @@ 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 \
|
||||
elif [[ ! -z ${RTSP_STREAM} ]];then
|
||||
ffmpeg -loglevel 52 -ac ${CHANNELS} -i ${RTSP_STREAM} -acodec libmp3lame \
|
||||
-b:a 320k -ac ${CHANNELS} -content_type 'audio/mpeg' \
|
||||
-f mp3 icecast://source:${ICE_PWD}@localhost:8000/stream -re
|
||||
fi
|
||||
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
+31
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
OLDIFS=$IFS
|
||||
IFS=\|
|
||||
most_recent_results=($(sqlite3 ~/BirdNET-Pi/scripts/birds.db \
|
||||
'SELECT Com_Name, Time, Date FROM detections
|
||||
ORDER BY Date DESC, Time DESC
|
||||
LIMIT 1'))
|
||||
today=$(date +%F)
|
||||
yesterday=$(date --date="yesterday" +%F)
|
||||
two_days_ago=$(date --date="2 days ago" +%F)
|
||||
|
||||
echo -n The most recent detection was
|
||||
|
||||
if [[ "${most_recent_results[0]}" =~ ^[AEIOU].* ]];then
|
||||
echo -n " an ${most_recent_results[0]} at "
|
||||
else
|
||||
echo -n " a ${most_recent_results[0]} at "
|
||||
fi
|
||||
most_recent_results[1]=$(date --date="${most_recent_results[1]}" +%l:%M%p)
|
||||
echo -n ${most_recent_results[1]}
|
||||
if [[ ${most_recent_results[2]} == $today ]];then
|
||||
echo " today."
|
||||
elif [[ ${most_recent_results[2]} == $yesterday ]];then
|
||||
echo " yesterday."
|
||||
elif [[ ${most_recent_results[2]} == $two_days_ago ]];then
|
||||
echo " two days ago."
|
||||
else
|
||||
echo " on ${most_recent_results[2]}."
|
||||
fi
|
||||
|
||||
IFS=$OLDIFS
|
||||
+51
-50
@@ -10,65 +10,66 @@ if($db == False) {
|
||||
|
||||
if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" && isset($_GET['previous_detection_identifier'])) {
|
||||
|
||||
$statement4 = $db->prepare('SELECT Com_Name, Sci_Name, Date, Time, Confidence, File_Name FROM detections ORDER BY Date DESC, Time DESC LIMIT 1');
|
||||
$statement4 = $db->prepare('SELECT Com_Name, Sci_Name, Date, Time, Confidence, File_Name FROM detections ORDER BY Date DESC, Time DESC LIMIT 5');
|
||||
if($statement4 == False) {
|
||||
echo "Database is busy";
|
||||
header("refresh: 0;");
|
||||
}
|
||||
$result4 = $statement4->execute();
|
||||
$mostrecent = $result4->fetchArray(SQLITE3_ASSOC);
|
||||
$comname = preg_replace('/ /', '_', $mostrecent['Com_Name']);
|
||||
$sciname = preg_replace('/ /', '_', $mostrecent['Sci_Name']);
|
||||
$comname = preg_replace('/\'/', '', $comname);
|
||||
$filename = "/By_Date/".$mostrecent['Date']."/".$comname."/".$mostrecent['File_Name'];
|
||||
// hopefully one of the 5 most recent detections has an image that is valid, we'll use that one as the most recent detection until the newer ones get their images created
|
||||
while($mostrecent = $result4->fetchArray(SQLITE3_ASSOC)) {
|
||||
$comname = preg_replace('/ /', '_', $mostrecent['Com_Name']);
|
||||
$sciname = preg_replace('/ /', '_', $mostrecent['Sci_Name']);
|
||||
$comname = preg_replace('/\'/', '', $comname);
|
||||
$filename = "/By_Date/".$mostrecent['Date']."/".$comname."/".$mostrecent['File_Name'];
|
||||
|
||||
|
||||
if($_GET['previous_detection_identifier'] != $filename || $_GET['previous_detection_identifier'] == "undefined") {
|
||||
// check to make sure the image actually exists, sometimes it takes a minute to be created
|
||||
if (isset($_SERVER['HTTPS']) &&
|
||||
($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) ||
|
||||
isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
|
||||
$_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
|
||||
$protocol = 'https://';
|
||||
}
|
||||
else {
|
||||
$protocol = 'http://';
|
||||
}
|
||||
$headers = @get_headers($protocol.$_SERVER['HTTP_HOST'].$filename.".png");
|
||||
if(strpos($headers[0],'200')) {
|
||||
?>
|
||||
<style>
|
||||
.fade-in {
|
||||
opacity: 1;
|
||||
animation-name: fadeInOpacity;
|
||||
animation-iteration-count: 1;
|
||||
animation-timing-function: ease-in;
|
||||
animation-duration: 1s;
|
||||
// check to make sure the image actually exists, sometimes it takes a minute to be created
|
||||
if (isset($_SERVER['HTTPS']) &&
|
||||
($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) ||
|
||||
isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
|
||||
$_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
|
||||
$protocol = 'https://';
|
||||
}
|
||||
|
||||
@keyframes fadeInOpacity {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
else {
|
||||
$protocol = 'http://';
|
||||
}
|
||||
$headers = @get_headers($protocol.$_SERVER['HTTP_HOST'].$filename.".png");
|
||||
// we've found our valid detection! ignore everything else from the database loop
|
||||
if(strpos($headers[0],'200')) {
|
||||
if($_GET['previous_detection_identifier'] == $filename) { die(); }
|
||||
?>
|
||||
<style>
|
||||
.fade-in {
|
||||
opacity: 1;
|
||||
animation-name: fadeInOpacity;
|
||||
animation-iteration-count: 1;
|
||||
animation-timing-function: ease-in;
|
||||
animation-duration: 1s;
|
||||
}
|
||||
|
||||
@keyframes fadeInOpacity {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<table class="<?php echo ($_GET['previous_detection_identifier'] == 'undefined') ? '' : 'fade-in'; ?>">
|
||||
<h3>Most Recent Detection: <span style="font-weight: normal;"><?php echo $mostrecent['Date']." ".$mostrecent['Time'];?></span></h3>
|
||||
<tr>
|
||||
<td class="relative"><a target="_blank" href="index.php?filename=<?php echo $mostrecent['File_Name']; ?>"><img class="copyimage" width="25" height="25" src="images/copy.png"></a>
|
||||
<form action="" method="GET">
|
||||
<input type="hidden" name="view" value="Species Stats">
|
||||
<button type="submit" name="species" value="<?php echo $mostrecent['Com_Name'];?>"><?php echo $mostrecent['Com_Name'];?></button></br>
|
||||
<a href="https://wikipedia.org/wiki/<?php echo $sciname;?>" target="_blank"/><i><?php echo $mostrecent['Sci_Name'];?></i></a>
|
||||
<br>Confidence: <?php echo $mostrecent['Confidence'];?><br>
|
||||
<video style="margin-top:10px" onplay='setLiveStreamVolume(0)' onended='setLiveStreamVolume(1)' onpause='setLiveStreamVolume(1)' controls poster="<?php echo $filename.".png";?>" preload="none" title="<?php echo $filename;?>"><source src="<?php echo $filename;?>"></video></td>
|
||||
</form>
|
||||
</tr>
|
||||
</table> <?php break;
|
||||
}
|
||||
</style>
|
||||
<table class="<?php echo ($_GET['previous_detection_identifier'] == 'undefined') ? '' : 'fade-in'; ?>">
|
||||
<h3>Most Recent Detection: <span style="font-weight: normal;"><?php echo $mostrecent['Date']." ".$mostrecent['Time'];?></span></h3>
|
||||
<tr>
|
||||
<td class="relative"><a target="_blank" href="index.php?filename=<?php echo $mostrecent['File_Name']; ?>"><img class="copyimage" width="25" height="25" src="images/copy.png"></a>
|
||||
<form action="" method="GET">
|
||||
<input type="hidden" name="view" value="Species Stats">
|
||||
<button type="submit" name="species" value="<?php echo $mostrecent['Com_Name'];?>"><?php echo $mostrecent['Com_Name'];?></button></br>
|
||||
<a href="https://wikipedia.org/wiki/<?php echo $sciname;?>" target="_blank"/><i><?php echo $mostrecent['Sci_Name'];?></i></a>
|
||||
<br>Confidence: <?php echo $mostrecent['Confidence'];?><br>
|
||||
<video style="margin-top:10px" onplay='setLiveStreamVolume(0)' onended='setLiveStreamVolume(1)' onpause='setLiveStreamVolume(1)' controls poster="<?php echo $filename.".png";?>" preload="none" title="<?php echo $filename;?>"><source src="<?php echo $filename;?>"></video></td>
|
||||
</form>
|
||||
</tr>
|
||||
</table> <?php
|
||||
}
|
||||
}
|
||||
die();
|
||||
}
|
||||
@@ -191,7 +192,7 @@ function loadDetectionIfNewExists(previous_detection_identifier=undefined) {
|
||||
const xhttp = new XMLHttpRequest();
|
||||
xhttp.onload = function() {
|
||||
// if there's a new detection that needs to be updated to the page
|
||||
if(this.responseText.length > 0) {
|
||||
if(this.responseText.length > 0 && !this.responseText.includes("Database is busy.")) {
|
||||
document.getElementById("most_recent_detection").innerHTML = this.responseText;
|
||||
|
||||
// only going to load left chart if there's a new detection
|
||||
|
||||
@@ -58,11 +58,16 @@ $result6 = $statement6->execute();
|
||||
$totalspeciestally = $result6->fetchArray(SQLITE3_ASSOC);
|
||||
|
||||
if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" ) {
|
||||
if(isset($_GET['searchterm'])) {
|
||||
$searchquery = "AND (Com_name LIKE '%".$_GET['searchterm']."%' OR Sci_name LIKE '%".$_GET['searchterm']."%' OR Confidence LIKE '%".$_GET['searchterm']."%' OR File_Name LIKE '%".$_GET['searchterm']."%' OR Time LIKE '%".$_GET['searchterm']."%')";
|
||||
} else {
|
||||
$searchquery = "";
|
||||
}
|
||||
if(isset($_GET['display_limit']) && is_numeric($_GET['display_limit'])){
|
||||
$statement0 = $db->prepare('SELECT Time, Com_Name, Sci_Name, Confidence, File_Name FROM detections WHERE Date == Date(\'now\', \'localtime\') ORDER BY Time DESC LIMIT '.(intval($_GET['display_limit'])-40).',40');
|
||||
$statement0 = $db->prepare('SELECT Time, Com_Name, Sci_Name, Confidence, File_Name FROM detections WHERE Date == Date(\'now\', \'localtime\') '.$searchquery.' ORDER BY Time DESC LIMIT '.(intval($_GET['display_limit'])-40).',40');
|
||||
} else {
|
||||
// legacy mode
|
||||
$statement0 = $db->prepare('SELECT Time, Com_Name, Sci_Name, Confidence, File_Name FROM detections WHERE Date == Date(\'now\', \'localtime\') ORDER BY Time DESC ');
|
||||
$statement0 = $db->prepare('SELECT Time, Com_Name, Sci_Name, Confidence, File_Name FROM detections WHERE Date == Date(\'now\', \'localtime\') '.$searchquery.' ORDER BY Time DESC'.$searchquery);
|
||||
}
|
||||
if($statement0 == False){
|
||||
echo "Database is busy";
|
||||
@@ -84,7 +89,7 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" ) {
|
||||
?>
|
||||
<?php if(isset($_GET['display_limit']) && is_numeric($_GET['display_limit'])){ ?>
|
||||
<tr class="relative" id="<?php echo $iterations; ?>">
|
||||
<td><a target="_blank" href="index.php?filename=<?php echo $todaytable['File_Name']; ?>"><img class="copyimage" width=25 src="images/copy.png"></a><?php echo $todaytable['Time'];?><br>
|
||||
<td class="relative"><a target="_blank" href="index.php?filename=<?php echo $todaytable['File_Name']; ?>"><img class="copyimage" width=25 src="images/copy.png"></a><?php echo $todaytable['Time'];?><br>
|
||||
<b><a class="a2" href="https://allaboutbirds.org/guide/<?php echo $comname;?>" target="top"><?php echo $todaytable['Com_Name'];?></a></b><br>
|
||||
<a class="a2" href="https://wikipedia.org/wiki/<?php echo $sciname;?>" target="top"><i><?php echo $todaytable['Sci_Name'];?></i></a><br>
|
||||
<b>Confidence:</b> <?php echo $todaytable['Confidence'];?><br>
|
||||
@@ -107,7 +112,7 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" ) {
|
||||
// don't show the button if there's no more detections to be displayed, we're at the end of the list
|
||||
if($iterations >= 40 && isset($_GET['display_limit']) && is_numeric($_GET['display_limit'])) { ?>
|
||||
<center>
|
||||
<button style="margin-top:10px;font-size:x-large;background:#dbffeb;padding:10px;border: 2px solid black;" onclick="loadDetections(<?php echo $_GET['display_limit'] + 40; ?>, this);" value="Today's Detections">Load 40 More...</button>
|
||||
<button class="loadmore" onclick="loadDetections(<?php echo $_GET['display_limit'] + 40; ?>, this);" value="Today's Detections">Load 40 More...</button>
|
||||
</center>
|
||||
<?php }
|
||||
|
||||
@@ -151,15 +156,37 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" ) {
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3>Today's Detections</h3>
|
||||
<h3>Today's Detections — <input autocomplete="off" size="11" type="text" placeholder="Search..." id="searchterm" name="searchterm"></h3>
|
||||
|
||||
<div style="padding-bottom:10px" id="detections_table"></div>
|
||||
|
||||
<button onclick="switchViews(this);" style="color:gray;margin:5px;float:right;z-index:100;position:relative;font-size:small;background:#dbffeb;padding:5px;border: 2px solid black;">Legacy view</button>
|
||||
<button onclick="switchViews(this);" class="legacyview">Legacy view</button>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
var timer = '';
|
||||
searchterm = "";
|
||||
|
||||
document.getElementById("searchterm").onkeydown = (function(e) {
|
||||
if (e.key === "Enter") {
|
||||
clearTimeout(timer);
|
||||
searchDetections(document.getElementById("searchterm").value);
|
||||
document.getElementById("searchterm").blur();
|
||||
} else {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(function() {
|
||||
searchDetections(document.getElementById("searchterm").value);
|
||||
|
||||
setTimeout(function() {
|
||||
// search auto submitted and now the user is probably scrolling, get the keyboard out of the way & prevent browser from jumping to the top when a video is played
|
||||
document.getElementById("searchterm").blur();
|
||||
}, 2000);
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
function switchViews(element) {
|
||||
document.getElementById("detections_table").innerHTML = "";
|
||||
if(element.innerHTML == "Legacy view") {
|
||||
@@ -170,17 +197,28 @@ function switchViews(element) {
|
||||
loadDetections(40);
|
||||
}
|
||||
}
|
||||
function searchDetections(searchvalue) {
|
||||
document.getElementById("detections_table").innerHTML = "<h3>Loading...</h3>";
|
||||
searchterm = searchvalue;
|
||||
loadDetections(40,undefined);
|
||||
}
|
||||
function loadDetections(detections_limit, element=undefined) {
|
||||
const xhttp = new XMLHttpRequest();
|
||||
xhttp.onload = function() {
|
||||
if(typeof element !== "undefined")
|
||||
{
|
||||
element.remove();
|
||||
document.getElementById("detections_table").innerHTML+= this.responseText;
|
||||
} else {
|
||||
document.getElementById("detections_table").innerHTML= this.responseText;
|
||||
}
|
||||
document.getElementById("detections_table").innerHTML+= this.responseText;
|
||||
|
||||
}
|
||||
xhttp.open("GET", "todays_detections.php?ajax_detections=true&display_limit="+detections_limit, true);
|
||||
if(searchterm !== ""){
|
||||
xhttp.open("GET", "todays_detections.php?ajax_detections=true&display_limit="+detections_limit+"&searchterm="+searchterm, true);
|
||||
} else {
|
||||
xhttp.open("GET", "todays_detections.php?ajax_detections=true&display_limit="+detections_limit, true);
|
||||
}
|
||||
xhttp.send();
|
||||
}
|
||||
window.addEventListener("load", function(){
|
||||
|
||||
Executable → Regular
+4
-11
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Update BirdNET-Pi
|
||||
# Update BirdNET-Pi's Git Repo
|
||||
source /etc/birdnet/birdnet.conf
|
||||
trap 'exit 1' SIGINT SIGHUP
|
||||
USER=$(awk -F: '/1000/ {print $1}' /etc/passwd)
|
||||
@@ -11,14 +11,7 @@ sudo -u${USER} git -C $my_dir pull -f
|
||||
sudo systemctl daemon-reload
|
||||
sudo -u${USER} git -C $my_dir stash pop
|
||||
sudo ln -sf $my_dir/* /usr/local/bin/
|
||||
if ! grep python3 <(head -n1 $my_dir/analyze.py);then
|
||||
echo "Ensure all python scripts use the virtual environment"
|
||||
sed -si "1 i\\#\!$HOME/BirdNET-Pi/birdnet/bin/python3" $my_dir/*.py
|
||||
fi
|
||||
if ! grep PRIVACY_MODE /etc/birdnet/birdnet.conf;then
|
||||
sudo -u${USER} echo "PRIVACY_MODE=off" >> /etc/birdnet/birdnet.conf
|
||||
fi
|
||||
if ! grep NOTIFY_RUN_CHANNEL_ID /etc/birdnet/birdnet.conf;then
|
||||
sudo -u${USER} echo "NOTIFY_RUN_CHANNEL_ID=" >> /etc/birdnet/birdnet.conf
|
||||
fi
|
||||
|
||||
# The script below handles changes to the host system
|
||||
# Any additions to the updater should be placed in that file.
|
||||
sudo $my_dir/update_birdnet_snippets.sh
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
# Update BirdNET-Pi
|
||||
source /etc/birdnet/birdnet.conf
|
||||
trap 'exit 1' SIGINT SIGHUP
|
||||
USER=$(awk -F: '/1000/ {print $1}' /etc/passwd)
|
||||
HOME=$(awk -F: '/1000/ {print $6}' /etc/passwd)
|
||||
my_dir=$HOME/BirdNET-Pi/scripts
|
||||
if ! grep python3 <(head -n1 $my_dir/analyze.py) &>/dev/null;then
|
||||
echo "Ensure all python scripts use the virtual environment"
|
||||
sudo -u$USER sed -si "1 i\\#\!$HOME/BirdNET-Pi/birdnet/bin/python3" $my_dir/*.py
|
||||
fi
|
||||
if ! grep PRIVACY_MODE /etc/birdnet/birdnet.conf &>/dev/null;then
|
||||
sudo -u$USER echo "PRIVACY_MODE=off" >> /etc/birdnet/birdnet.conf
|
||||
fi
|
||||
if ! which lsof &>/dev/null;then
|
||||
sudo apt update && sudo apt -y install lsof
|
||||
fi
|
||||
Reference in New Issue
Block a user