Merge branch 'mcguirepr89:main' into main

This commit is contained in:
Phill Price
2022-09-28 10:13:59 +01:00
committed by GitHub
29 changed files with 918 additions and 215 deletions
+21 -5
View File
@@ -1,3 +1,5 @@
<h1 align="center"><a href="https://github.com/mcguirepr89/BirdNET-Pi/blob/main/LICENSE">Review the license!!</a></h1>
<h1 align="center">You may not use BirdNET-Pi to develop a commercial product!!!!</h1>
<h1 align="center">
BirdNET-Pi
</h1>
@@ -18,15 +20,21 @@ Check out birds from around the world
- [BirdWeather](https://app.birdweather.com)<br>
- [Johannesburg, South Africa](https://joburg.birdnetpi.com)<br>
- [Öringe, Tyresö, Sweden](https://tyreso.birdnetpi.com)<br>
- [Norman, Oklahoma, United States](https://normanok.birdnetpi.com)<br>
- [Nijmegen, the Netherlands](https://nijmegen.birdnetpi.com)<br>
- [Murrysville, Pennsylvania, United States](https://murrysvillepa.birdnetpi.com)
- [Berowra, New South Wales, Australia](https://berowra.birdnetpi.com)
- [Fairview, Tennessee, United States](https://fairviewtennessee.birdnetpi.com)
- [Dundas, Ontario, Canada](https://dundasontario.birdnetpi.com)
- [Bungendore, New South Wales, Australia](https://bungendorensw.birdnetpi.com)
- [Rivers Bend, Ohio, United States](https://riversbendoh.birdnetpi.com)
- [Vienna, Virginia, United States](https://viennava.birdnetpi.com)
- [Grevenbroich, Elsen, Germany](https://grevenbroich-elsen.birdnetpi.com)
- [Occoquan, Virginia, United States](https://occoquanva.birdnetpi.com)
- [Westmoreland, Pennsylvania, United States](https://westmorelandbnc.birdnetpi.com)
- [Latrobe, Pennsylvania, United States](https://stvincentcollege.birdnetpi.com)
- [Cambridge, Massachusetts, United States](https://cambridgema.birdnetpi.com)
[Share your installation!!](https://github.com/mcguirepr89/BirdNET-Pi/wiki/Sharing-Your-BirdNET-Pi)
Have a public installation not in the list above? Let me know!! I'd be happy to add it.
Currently listening in these countries . . . that I know of . . .
- The United States
@@ -53,6 +61,7 @@ Currently listening in these countries . . . that I know of . . .
- Ireland
- Denmark
- Costa Rica
- The Philippines
## Features
* 24/7 recording and BirdNET-Lite analysis
@@ -76,8 +85,13 @@ Currently listening in these countries . . . that I know of . . .
* A USB Microphone or Sound Card
## Installation
**IMPORTANT:** Not yet tested on [the newest RaspiOS image](https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2022-09-07/)
[A comprehensive installation guide is available here](https://github.com/mcguirepr89/BirdNET-Pi/wiki/Installation-Guide).
[Raspberry Pi 0W2 installation guide available here](https://github.com/mcguirepr89/BirdNET-Pi/wiki/RPi0W2-Installation-Guide)
Please note that installing BirdNET-Pi on top of other servers is not supported. If this is something that you require, please open a discussion for your idea and inquire about how to contribute to development.
[Raspberry Pi 3B[+] and 0W2 installation guide available here](https://github.com/mcguirepr89/BirdNET-Pi/wiki/RPi0W2-Installation-Guide)
The system can be installed with:
```
@@ -85,7 +99,7 @@ curl -s https://raw.githubusercontent.com/mcguirepr89/BirdNET-Pi/main/newinstall
```
The installer takes care of any and all necessary updates, so you can run that as the very first command upon the first boot, if you'd like.
The installation creates a log in `$HOME/installation$(date "+%F").log`.
The installation creates a log in `$HOME/installation-$(date "+%F").txt`.
## Access
The BirdNET-Pi can be accessed from any web browser on the same network:
- http://birdnetpi.local
@@ -113,7 +127,8 @@ Use the web interface and go to "Tools" > "System Controls" > "Update." If you e
```
## Troubleshooting and Ideas
Having trouble or have an idea? Submit an issue for trouble and a discussion for ideas.
*Hint: A lot of weird problems can be solved by simply restarting the core services. Do this from the web interface "Tools" > "Services" > "Restart Core Services"
Having trouble or have an idea? *Submit an issue for trouble* and a *discussion for ideas*. Please do *not* submit an issue as a discussion -- the issue tracker solicits information that is needed for anyone to help -- discussions are *not for issues*.
## Sharing
Please join a Discussion!! and please join [BirdWeather!!](https://app.birdweather.com)
@@ -126,6 +141,7 @@ I hope that if you find BirdNET-Pi has been worth your time, you will share your
- [PixCams Build Guide](https://pixcams.com/building-a-birdnet-pi-real-time-acoustic-bird-id-station/)
- <ins>[Core-Electronics](https://core-electronics.com.au/projects/bird-calls-raspberry-pi)</ins> Build Article
- [RaspberryPi.com Blog Post](https://www.raspberrypi.com/news/classify-birds-acoustically-with-birdnet-pi/)
- [MagPi Issue 119 Showcase Article](https://magpi.raspberrypi.com/issues/119/pdf)
### Internationalization:
+7
View File
@@ -62,6 +62,7 @@ APPRISE_NOTIFICATION_TITLE="New BirdNET-Pi Detection"
APPRISE_NOTIFICATION_BODY="A \$sciname \$comname was just detected with a confidence of \$confidence"
APPRISE_NOTIFY_EACH_DETECTION=0
APPRISE_NOTIFY_NEW_SPECIES=0
APPRISE_WEEKLY_REPORT=1
#---------------------- Flickr Images API Configuration -----------------------#
## If FLICKR_API_KEY is set, the web interface will try and display bird images
@@ -158,6 +159,12 @@ AUDIOFMT=mp3
DATABASE_LANG=en
## SILENCE_UPDATE_INDICATOR is for quieting the display of how many commits
## your installation is behind by, relative to the Github repo. This number
## appears next to "Tools" when you're 50 or more commits behind.
SILENCE_UPDATE_INDICATOR=0
## These are just for debugging
LAST_RUN=
THIS_RUN=
+48
View File
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 457.503 457.503" style="enable-background:new 0 0 457.503 457.503;" xml:space="preserve">
<g>
<g>
<path d="M381.575,57.067h-90.231C288.404,25.111,261.461,0,228.752,0C196.043,0,169.1,25.111,166.16,57.067H75.929
c-26.667,0-48.362,21.695-48.362,48.362c0,26.018,20.655,47.292,46.427,48.313v246.694c0,31.467,25.6,57.067,57.067,57.067
h195.381c31.467,0,57.067-25.6,57.067-57.067V153.741c25.772-1.02,46.427-22.294,46.427-48.313
C429.936,78.761,408.242,57.067,381.575,57.067z M165.841,376.817c0,8.013-6.496,14.509-14.508,14.509
c-8.013,0-14.508-6.496-14.508-14.509V186.113c0-8.013,6.496-14.508,14.508-14.508c8.013,0,14.508,6.496,14.508,14.508V376.817z
M243.26,376.817c0,8.013-6.496,14.509-14.508,14.509c-8.013,0-14.508-6.496-14.508-14.509V186.113
c0-8.013,6.496-14.508,14.508-14.508c8.013,0,14.508,6.496,14.508,14.508V376.817z M320.679,376.817
c0,8.013-6.496,14.509-14.508,14.509c-8.013,0-14.509-6.496-14.509-14.509V186.113c0-8.013,6.496-14.508,14.509-14.508
s14.508,6.496,14.508,14.508V376.817z"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+1 -1
View File
@@ -5,7 +5,7 @@ body::-webkit-scrollbar {
display:none
}
</style>
<link rel="stylesheet" href="style.css?v=6.22.22">
<link rel="stylesheet" href="style.css?v=8.05.22">
<link rel="stylesheet" type="text/css" href="static/dialog-polyfill.css" />
<body>
<div class="banner">
+22
View File
@@ -261,6 +261,11 @@ button:hover {
margin-right: auto;
}
.views {
transition: opacity 0.3s;
-webkit-transition: opacity 0.3s;
}
.views .centered button {
background-color: rgb(219, 255, 235);
padding: 12px;
@@ -790,3 +795,20 @@ pre.bash {
width: 100%;
display: inline-block;
}
pre#timer.bash {
display:unset;
width:unset;
}
#toolsbtn {
min-width: max-content;
}
#showpassword {
cursor:pointer;
margin-left:2px;
height:5px;
line-height:5px;
padding:3px;
background-color:#9fe29b
}
+22 -11
View File
@@ -6,9 +6,7 @@ $home = shell_exec("awk -F: '/1000/{print $6}' /etc/passwd");
$home = trim($home);
if(!isset($_SESSION['behind'])) {
$fetch = shell_exec("sudo -u".$user." git -C ".$home."/BirdNET-Pi fetch 2>&1");
if(strlen($fetch) > 0) {
$_SESSION['behind'] = trim(shell_exec("sudo -u".$user." git -C ".$home."/BirdNET-Pi status | sed -n '2 p' | cut -d ' ' -f 7"));
}
$_SESSION['behind'] = trim(shell_exec("sudo -u".$user." git -C ".$home."/BirdNET-Pi status | sed -n '2 p' | cut -d ' ' -f 7"));
if(isset($_SESSION['behind'])&&intval($_SESSION['behind']) >= 99) {?>
<style>
.updatenumber {
@@ -16,8 +14,14 @@ if(!isset($_SESSION['behind'])) {
}
</style>
<?php }}
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');
}
?>
<link rel="stylesheet" href="style.css?v=6.22.22">
<link rel="stylesheet" href="style.css?v=8.05.22">
<style>
body::-webkit-scrollbar {
display:none
@@ -49,13 +53,24 @@ body::-webkit-scrollbar {
<form action="" method="GET" id="views">
<button type="submit" name="view" value="View Log" form="views">View Log</button>
</form>
<form action="" method="GET" id="views">
<button type="submit" name="view" value="Tools" form="views">Tools<?php if(isset($_SESSION['behind']) && intval($_SESSION['behind']) >= 50){ $updatediv = ' <div class="updatenumber">'.$_SESSION["behind"].'</div>'; } else { $updatediv = ""; } echo $updatediv; ?></button>
<form action="" id="toolsbtn" method="GET" id="views">
<button type="submit" name="view" value="Tools" form="views">Tools<?php if(isset($_SESSION['behind']) && intval($_SESSION['behind']) >= 50 && ($config['SILENCE_UPDATE_INDICATOR'] != 1)){ $updatediv = ' <div class="updatenumber">'.$_SESSION["behind"].'</div>'; } else { $updatediv = ""; } echo $updatediv; ?></button>
</form>
<button href="javascript:void(0);" class="icon" onclick="myFunction()"><img src="images/menu.png"></button>
</div>
<script>
window.onload = function() {
var elements = document.querySelectorAll("button[name=view]");
var setViewsOpacity = function() {
document.getElementsByClassName("views")[0].style.opacity = "0.5";
};
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener('click', setViewsOpacity, false);
}
};
var topbuttons = document.querySelectorAll("button[form='views']");
if(window.location.search.substr(1) != '') {
for (var i = 0; i < topbuttons.length; i++) {
@@ -90,14 +105,10 @@ if(isset($_GET['view'])){
if($_GET['view'] == "Overview"){include('overview.php');}
if($_GET['view'] == "Today's Detections"){include('todays_detections.php');}
if($_GET['view'] == "Species Stats"){include('stats.php');}
if($_GET['view'] == "Weekly Report"){include('weekly_report.php');}
if($_GET['view'] == "Streamlit"){echo "<iframe src=\"/stats\"></iframe>";}
if($_GET['view'] == "Daily Charts"){include('history.php');}
if($_GET['view'] == "Tools"){
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"');
Binary file not shown.
+1 -1
View File
@@ -24,7 +24,7 @@ if [[ ! -z $PACKAGES_MISSING ]] ; then
fi
branch=main
git clone -b $branch https://github.com/mcguirepr89/BirdNET-Pi.git ${HOME}/BirdNET-Pi &&
git clone -b $branch --depth=1 https://github.com/mcguirepr89/BirdNET-Pi.git ${HOME}/BirdNET-Pi &&
$HOME/BirdNET-Pi/scripts/install_birdnet.sh
if [ ${PIPESTATUS[0]} -eq 0 ];then
+18 -4
View File
@@ -1,7 +1,6 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
error_reporting(E_ERROR);
if (file_exists('./scripts/thisrun.txt')) {
$config = parse_ini_file('./scripts/thisrun.txt');
@@ -157,6 +156,16 @@ if(isset($_GET['submit'])) {
$contents2 = preg_replace("/AUDIOFMT=.*/", "AUDIOFMT=$audiofmt", $contents2);
}
}
if(isset($_GET["silence_update_indicator"])) {
$silence_update_indicator = 1;
if(strcmp($silence_update_indicator,$config['SILENCE_UPDATE_INDICATOR']) !== 0) {
$contents = preg_replace("/SILENCE_UPDATE_INDICATOR=.*/", "SILENCE_UPDATE_INDICATOR=$silence_update_indicator", $contents);
$contents2 = preg_replace("/SILENCE_UPDATE_INDICATOR=.*/", "SILENCE_UPDATE_INDICATOR=$silence_update_indicator", $contents2);
}
} else {
$contents = preg_replace("/SILENCE_UPDATE_INDICATOR=.*/", "SILENCE_UPDATE_INDICATOR=0", $contents);
$contents2 = preg_replace("/SILENCE_UPDATE_INDICATOR=.*/", "SILENCE_UPDATE_INDICATOR=0", $contents2);
}
$fh = fopen('/etc/birdnet/birdnet.conf', "w");
$fh2 = fopen("./scripts/thisrun.txt", "w");
@@ -164,7 +173,10 @@ if(isset($_GET['submit'])) {
fwrite($fh2, $contents2);
}
$count_labels = count(file("./scripts/labels.txt"));
$user = trim(shell_exec("awk -F: '/1000/{print $1}' /etc/passwd"));
$home = trim(shell_exec("awk -F: '/1000/{print $6}' /etc/passwd"));
$count_labels = count(file($home."/BirdNET-Pi/model/labels.txt"));
$count = $count_labels;
?>
<meta name="viewport" content="width=device-width, initial-scale=1">
@@ -233,13 +245,15 @@ foreach($formats as $format){
<h3>BirdNET-Pi Password</h3>
<p>This password will protect your "Tools" page and "Live Audio" stream.</p>
<label for="caddy_pwd">Password: </label>
<input style="width:40ch" name="caddy_pwd" type="text" value="<?php print($newconfig['CADDY_PWD']);?>" /><br>
<input style="width:40ch" name="caddy_pwd" id="caddy_pwd" type="password" value="<?php print($newconfig['CADDY_PWD']);?>" /><span id="showpassword" onmouseover="document.getElementById('caddy_pwd').type='text';" onmouseout="document.getElementById('caddy_pwd').type='password';">show</span><br>
<h3>Custom URL</h3>
<p><a href="mailto:mcguirepr89@gmail.com?subject=Request%20BirdNET-Pi%20Subdomain&body=<?php include('birdnetpi_request.php'); ?>" target="_blank">Email Me</a> if you would like a BirdNETPi.com subdomain. This would be, <i>https://YourLocation.birdnetpi.com</i></p>
<p>When you update the URL below, the web server will reload, so be sure to wait at least 30 seconds and then go to your new URL.</p>
<label for="birdnetpi_url">BirdNET-Pi URL: </label>
<input style="width:40ch;" name="birdnetpi_url" type="url" value="<?php print($newconfig['BIRDNETPI_URL']);?>" /><br>
<p>The BirdNET-Pi URL is how the main page will be reached. If you want your installation to respond to an IP address, place that here, but be sure to indicate "<i>http://</i>".<br>Example for IP: <i>http://192.168.0.109</i><br>Example if you own your own domain: <i>https://virginia.birdnetpi.com</i></p>
<label for="silence_update_indicator">Silence Update Indicator: </label>
<input type="checkbox" name="silence_update_indicator" <?php if($newconfig['SILENCE_UPDATE_INDICATOR'] == 1) { echo "checked"; };?> ><br>
<h3>BirdNET-Lite Settings</h3>
<label for="overlap">Overlap: </label>
<input name="overlap" type="number" min="0.0" max="2.9" step="0.1" value="<?php print($newconfig['OVERLAP']);?>" required/><br>
+4 -4
View File
@@ -128,11 +128,11 @@ run_analysis() {
BIRDWEATHER_ID_PARAM=""
BIRDWEATHER_ID_LOG=""
fi
echo $PYTHON_VIRTUAL_ENV "$DIR\analyze.py" \
echo $PYTHON_VIRTUAL_ENV "$DIR/analyze.py" \
--i "${1}/${i}" \
--o "${1}/${i}.csv" \
--lat "${LATITUDE}" \
--lon "${LONGITUDE}" \
--lat $(echo "${LATITUDE}" | awk '{print int($1+0.5)}').XX \
--lon $(echo "${LONGITUDE}" | awk '{print int($1+0.5)}').XX \
--week "${WEEK}" \
--overlap "${OVERLAP}" \
--sensitivity "${SENSITIVITY}" \
@@ -168,7 +168,7 @@ until grep 5050 <(netstat -tulpn 2>&1) &> /dev/null 2>&1;do
sleep 1
done
if [ $(find ${RECS_DIR}/StreamData -maxdepth 1 -name '*wav' | wc -l) -gt 0 ];then
if [ $(find ${RECS_DIR}/StreamData -maxdepth 1 -name '*wav' 2>/dev/null| wc -l) -gt 0 ];then
find $RECS_DIR -maxdepth 1 -name '*wav' -type f -size 0 -delete
run_birdnet "${RECS_DIR}/StreamData"
fi
+5
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
source /etc/birdnet/birdnet.conf
set -x
cd "${PROCESSED}" || exit 1
empties=($(find ${PROCESSED} -size 57c))
@@ -11,3 +12,7 @@ done
if [[ "$(find ${PROCESSED} | wc -l)" -ge 100 ]];then
ls -1t . | tail -n +100 | xargs -r rm -vv
fi
#accumulated_files=$(find $RECS_DIR -path $PROCESSED -prune -o -path $EXTRACTED -prune -o -type f -print | wc -l)
#[ $accumulated_files -ge 10 ] && stop_core_services.sh
#echo "$(date "+%b %e %I:%M:%S") Stopped Core Services -- It looks like analysis stopped. Check raw recordings in $RECS_DIR and check the birdnet_analysis.service and birdnet_server.service \"journalctl -eu birdnet_analysis -u birdnet_server\"" | sudo tee -a /var/log/syslog
+1
View File
@@ -34,6 +34,7 @@ sudo -u ${USER} ln -fs $my_dir/overview.php ${EXTRACTED}
sudo -u ${USER} ln -fs $my_dir/stats.php ${EXTRACTED}
sudo -u ${USER} ln -fs $my_dir/todays_detections.php ${EXTRACTED}
sudo -u ${USER} ln -fs $my_dir/history.php ${EXTRACTED}
sudo -u ${USER} ln -fs $my_dir/weekly_report.php ${EXTRACTED}
sudo -u ${USER} ln -fs $my_dir/homepage/images/favicon.ico ${EXTRACTED}
sudo -u ${USER} ln -fs ${HOME}/phpsysinfo ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname $my_dir)/templates/phpsysinfo.ini ${HOME}/phpsysinfo/
+25 -3
View File
@@ -46,6 +46,11 @@ if(isset($_GET["latitude"])){
} else {
$apprise_notify_new_species_each_day = 0;
}
if(isset($_GET['apprise_weekly_report'])) {
$apprise_weekly_report = 1;
} else {
$apprise_weekly_report = 0;
}
if(isset($timezone)) {
shell_exec("sudo timedatectl set-timezone ".$timezone);
@@ -101,6 +106,7 @@ if(isset($_GET["latitude"])){
$contents = preg_replace("/APPRISE_NOTIFY_EACH_DETECTION=.*/", "APPRISE_NOTIFY_EACH_DETECTION=$apprise_notify_each_detection", $contents);
$contents = preg_replace("/APPRISE_NOTIFY_NEW_SPECIES=.*/", "APPRISE_NOTIFY_NEW_SPECIES=$apprise_notify_new_species", $contents);
$contents = preg_replace("/APPRISE_NOTIFY_NEW_SPECIES_EACH_DAY=.*/", "APPRISE_NOTIFY_NEW_SPECIES_EACH_DAY=$apprise_notify_new_species_each_day", $contents);
$contents = preg_replace("/APPRISE_WEEKLY_REPORT=.*/", "APPRISE_WEEKLY_REPORT=$apprise_weekly_report", $contents);
$contents = preg_replace("/FLICKR_API_KEY=.*/", "FLICKR_API_KEY=$flickr_api_key", $contents);
$contents = preg_replace("/DATABASE_LANG=.*/", "DATABASE_LANG=$language", $contents);
$contents = preg_replace("/FLICKR_FILTER_EMAIL=.*/", "FLICKR_FILTER_EMAIL=$flickr_filter_email", $contents);
@@ -114,6 +120,7 @@ if(isset($_GET["latitude"])){
$contents2 = preg_replace("/APPRISE_NOTIFY_EACH_DETECTION=.*/", "APPRISE_NOTIFY_EACH_DETECTION=$apprise_notify_each_detection", $contents2);
$contents2 = preg_replace("/APPRISE_NOTIFY_NEW_SPECIES=.*/", "APPRISE_NOTIFY_NEW_SPECIES=$apprise_notify_new_species", $contents2);
$contents2 = preg_replace("/APPRISE_NOTIFY_NEW_SPECIES_EACH_DAY=.*/", "APPRISE_NOTIFY_NEW_SPECIES_EACH_DAY=$apprise_notify_new_species_each_day", $contents2);
$contents2 = preg_replace("/APPRISE_WEEKLY_REPORT=.*/", "APPRISE_WEEKLY_REPORT=$apprise_weekly_report", $contents2);
$contents2 = preg_replace("/FLICKR_API_KEY=.*/", "FLICKR_API_KEY=$flickr_api_key", $contents2);
$contents2 = preg_replace("/DATABASE_LANG=.*/", "DATABASE_LANG=$language", $contents2);
$contents2 = preg_replace("/FLICKR_FILTER_EMAIL=.*/", "FLICKR_FILTER_EMAIL=$flickr_filter_email", $contents2);
@@ -180,6 +187,15 @@ if(isset($_GET['sendtest']) && $_GET['sendtest'] == "true") {
$filename = "http://birdnetpi.local/"."?filename=".$filename;
}
$attach="";
$exampleimage = "https://live.staticflickr.com/7430/27545810581_8bfa8289a3_c.jpg";
if (strpos($body, '$flickrimage') !== false) {
$attach = "--attach ".$exampleimage;
}
if (strpos($body, '{') === false) {
$exampleimage = "";
}
$title = str_replace("\$sciname", $sciname, $title);
$title = str_replace("\$comname", $comname, $title);
$title = str_replace("\$confidence", $confidence, $title);
@@ -192,6 +208,7 @@ if(isset($_GET['sendtest']) && $_GET['sendtest'] == "true") {
$title = str_replace("\$cutoff", $cutoff, $title);
$title = str_replace("\$sens", $sens, $title);
$title = str_replace("\$overlap", $overlap, $title);
$title = str_replace("\$flickrimage", $exampleimage, $title);
$body = str_replace("\$sciname", $sciname, $body);
$body = str_replace("\$comname", $comname, $body);
@@ -205,8 +222,9 @@ if(isset($_GET['sendtest']) && $_GET['sendtest'] == "true") {
$body = str_replace("\$cutoff", $cutoff, $body);
$body = str_replace("\$sens", $sens, $body);
$body = str_replace("\$overlap", $overlap, $body);
$body = str_replace("\$flickrimage", $exampleimage, $body);
echo "<pre class=\"bash\">".shell_exec($home."/BirdNET-Pi/birdnet/bin/apprise -vv -t '".$title."' -b '".$body."' ".$cf." ")."</pre>";
echo "<pre class=\"bash\">".shell_exec($home."/BirdNET-Pi/birdnet/bin/apprise -vv -t '".$title."' -b '".$body."' ".$attach." ".$cf." ")."</pre>";
die();
}
@@ -320,6 +338,8 @@ https://discordapp.com/api/webhooks/{WebhookID}/{WebhookToken}
<dd>Sigmoid Sensitivity set in "Advanced Settings"</dd>
<dt>$overlap</dt>
<dd>Overlap set in "Advanced Settings"</dd>
<dt>$flickrimage</dt>
<dd>A preview image of the detected species from Flickr. Set your API key below.</dd>
</dl>
<p>Use the variables defined above to customize your notification title and body.</p>
<label for="apprise_notification_title">Notification Title: </label>
@@ -329,9 +349,11 @@ https://discordapp.com/api/webhooks/{WebhookID}/{WebhookToken}
<input type="checkbox" name="apprise_notify_new_species" <?php if($config['APPRISE_NOTIFY_NEW_SPECIES'] == 1 && filesize($home."/BirdNET-Pi/apprise.txt") != 0) { echo "checked"; };?> >
<label for="apprise_notify_new_species">Notify each new infrequent species detection (<5 visits per week)</label><br>
<input type="checkbox" name="apprise_notify_new_species_each_day" <?php if($config['APPRISE_NOTIFY_NEW_SPECIES_EACH_DAY'] == 1 && filesize($home."/BirdNET-Pi/apprise.txt") != 0) { echo "checked"; };?> >
<label for="apprise_notify_new_species_each_day">Notify each new species detection of the day</label><br>
<label for="apprise_notify_new_species_each_day">Notify each species first detection of the day</label><br>
<input type="checkbox" name="apprise_notify_each_detection" <?php if($config['APPRISE_NOTIFY_EACH_DETECTION'] == 1 && filesize($home."/BirdNET-Pi/apprise.txt") != 0) { echo "checked"; };?> >
<label for="apprise_notify_each_detection">Notify each new detection</label><br><br>
<label for="apprise_weekly_report">Notify each new detection</label><br>
<input type="checkbox" name="apprise_weekly_report" <?php if($config['APPRISE_WEEKLY_REPORT'] == 1 && filesize($home."/BirdNET-Pi/apprise.txt") != 0) { echo "checked"; };?> >
<label for="apprise_weekly_report">Send weekly report</label><br><br>
<button type="button" class="testbtn" onclick="sendTestNotification(this)">Send Test Notification</button><br>
<span id="testsuccessmsg"></span>
+112 -3
View File
@@ -1,7 +1,13 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
ini_set('display_errors',1);
ini_set('display_startup_errors',1);
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');
}
if(isset($_GET['date'])){
$theDate = $_GET['date'];
@@ -18,15 +24,118 @@ $statement1 = $db->prepare("SELECT COUNT(*) FROM detections
$result1 = $statement1->execute();
$totalcount = $result1->fetchArray(SQLITE3_ASSOC);
if(isset($_GET['blocation']) ) {
header("Content-type: text/csv");
header("Content-Disposition: attachment; filename=result_file.csv");
header("Pragma: no-cache");
header("Expires: 0");
$user = trim(shell_exec("awk -F: '/1000/{print $1}' /etc/passwd"));
$home = trim(shell_exec("awk -F: '/1000/{print $6}' /etc/passwd"));
//$sunrise = date_sunrise(time(), SUNFUNCS_RET_TIMESTAMP, $config["LATITUDE"], $config["LONGITUDE"]);
//$sunset = date_sunset(time(), SUNFUNCS_RET_TIMESTAMP, $config["LATITUDE"], $config["LONGITUDE"]);
$list = array ();
//$hrsinday = intval(($sunset-$sunrise)/60/60);
$hrsinday = 24;
for($i=0;$i<$hrsinday;$i++) {
$starttime = strtotime("12 AM") + (3600*$i);
$statement1 = $db->prepare("SELECT DISTINCT(Com_Name), COUNT(*) FROM detections WHERE Date == \"$theDate\" AND Time > '".date("H:i", $starttime)."' AND Time < '".date("H:i",$starttime + 3600)."' AND Confidence > 0.75 GROUP By Com_Name ORDER BY COUNT(*) DESC");
if($statement1 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result1 = $statement1->execute();
$detections = [];
while($detection=$result1->fetchArray(SQLITE3_ASSOC))
{
$detections[$detection["Com_Name"]] = $detection["COUNT(*)"];
}
foreach($detections as $com_name=>$scount)
{
array_push($list, array($com_name,'','','1','',$_GET['blocation'],$config["LATITUDE"],$config["LONGITUDE"],date("m/d/Y", strtotime($theDate)), date("H:i", $starttime), $_GET['state'], $_GET['country'], $_GET['protocol'], $_GET['num_observers'], '60', 'Y', $_GET['dist_traveled'],'',$_GET['notes'] ) );
}
}
$output = fopen("php://output", "w");
foreach ($list as $row) {
fputcsv($output, $row);
}
fclose($output);
die();
}
?>
<head>
<style>
#attribution-dialog p {
display:inline;
}
#attribution-dialog {
text-align: left;
}
</style>
</head>
<body>
<div class="history centered">
<dialog id="attribution-dialog">
<p style="display:none" id="filename"></p>
<h1 id="modalHeading">Enter Checklist Information</h1>
<p id="modalText">Location Name:</p> <input placeholder="My house" id="blocation"><br><br>
<p id="modalText">State (2 letter code):</p> <input maxlength="3" id="state"><br><br>
<p id="modalText">Country (2 letter code):</p> <input maxlength="2" id="country"><br><br>
<p id="modalText">Protocol:</p> <select id="protocol">
<option value="casual">casual</option>
<option value="stationary">stationary</option>
<option value="traveling">traveling</option>
<option value="area">area</option>
</select>
<br><br>
<p id="modalText">Number of observers:</p> <input type="number" id="num_observers"><br><br>
<p id="modalText">Distance traveled (miles):</p> <input type="number" id="dist_traveled"><br><br>
<p id="modalText">Notes:</p> <input id="notes"><br><br>
<button onclick="submitID()">Submit</button>
</dialog>
<script>
var dialog = document.querySelector('dialog');
dialogPolyfill.registerDialog(dialog);
function showDialog() {
document.getElementById('attribution-dialog').showModal();
}
function closeDialog() {
document.getElementById('attribution-dialog').close();
}
function submitID() {
blocation = document.getElementById("blocation").value;
state = document.getElementById("state").value;
country = document.getElementById("country").value;
protocol = document.getElementById("protocol").value;
num_observers = document.getElementById("num_observers").value;
dist_traveled = document.getElementById("dist_traveled").value;
notes = document.getElementById("notes").value;
window.open("history.php?blocation="+blocation+"&state="+state+"&country="+country+"&protocol="+protocol+"&num_observers="+num_observers+"&dist_traveled="+dist_traveled+"&notes="+notes+"&date="+"<?php echo $theDate; ?>");
document.getElementById('attribution-dialog').innerHTML = "<h3>Success!</h3><p>Your checklist will start downloading momentarily.<br><br>Refer to <a target='_blank' href='https://ebird.org/content/eBirdCommon/docs/ebird_import_data_process.pdf'>this guide</a> for information on how to import it in eBird. The checklist file format is: 'eBird Record Format (Extended)'.<br><br><span style='font-size:small'>Note: Only detections with a confidence > 0.75 were included, and entries have been limited to 1 detection per hour per species, to comply with eBird's data quality guidelines.<br>It's always good practice to manually verify your checklist before submitting, especially for nocturnal hours.</span></p><br><br><button onclick=\"closeDialog()\">Close</button>";
}
</script>
<form action="" method="GET">
<input type="date" name="date" value="<?php echo $theDate;?>">
<button type="submit" name="view" value="Daily Charts">Submit Date</button>
@@ -37,7 +146,7 @@ $totalcount = $result1->fetchArray(SQLITE3_ASSOC);
<td><?php echo $totalcount['COUNT(*)'];?></td>
</tr>
</table>
<br><br>
<?php // <br><button type="button" onclick="showDialog()">Export as CSV for eBird</button><br><br> ?>
<?php
if (file_exists('./Charts/'.$chart)) {
echo "<img src=\"/Charts/$chart?nocache=time()\" >";
+7
View File
@@ -75,6 +75,7 @@ APPRISE_NOTIFICATION_TITLE="New BirdNET-Pi Detection"
APPRISE_NOTIFICATION_BODY="A \$sciname \$comname was just detected with a confidence of \$confidence"
APPRISE_NOTIFY_EACH_DETECTION=0
APPRISE_NOTIFY_NEW_SPECIES=0
APPRISE_WEEKLY_REPORT=1
#---------------------- Flickr Images API Configuration -----------------------#
## If FLICKR_API_KEY is set, the web interface will try and display bird images
@@ -170,6 +171,12 @@ AUDIOFMT=mp3
## DATABASE_LANG is the language used for the bird species database
DATABASE_LANG=en
## SILENCE_UPDATE_INDICATOR is for quieting the display of how many commits
## your installation is behind by, relative to the Github repo. This number
## appears next to "Tools" when you're 50 or more commits behind.
SILENCE_UPDATE_INDICATOR=0
## These are just for debugging
LAST_RUN=
THIS_RUN=
+9 -1
View File
@@ -111,6 +111,7 @@ create_necessary_dirs() {
sudo -u ${USER} ln -fs $my_dir/scripts/stats.php ${EXTRACTED}
sudo -u ${USER} ln -fs $my_dir/scripts/todays_detections.php ${EXTRACTED}
sudo -u ${USER} ln -fs $my_dir/scripts/history.php ${EXTRACTED}
sudo -u ${USER} ln -fs $my_dir/scripts/weekly_report.php ${EXTRACTED}
sudo -u ${USER} ln -fs $my_dir/homepage/images/favicon.ico ${EXTRACTED}
sudo -u ${USER} ln -fs ${HOME}/phpsysinfo ${EXTRACTED}
sudo -u ${USER} ln -fs $my_dir/templates/phpsysinfo.ini ${HOME}/phpsysinfo/
@@ -274,7 +275,7 @@ Restart=on-failure
RestartSec=5
Type=simple
User=${USER}
ExecStart=$HOME/BirdNET-Pi/birdnet/bin/streamlit run $HOME/BirdNET-Pi/scripts/plotly_streamlit.py --server.address localhost --server.baseUrlPath "/stats"
ExecStart=$HOME/BirdNET-Pi/birdnet/bin/streamlit run $HOME/BirdNET-Pi/scripts/plotly_streamlit.py --browser.gatherUsageStats false --server.address localhost --server.baseUrlPath "/stats"
[Install]
WantedBy=multi-user.target
@@ -379,6 +380,8 @@ config_icecast() {
for i in "${passwords[@]}";do
sed -i "s/<${i}password>.*<\/${i}password>/<${i}password>${ICE_PWD}<\/${i}password>/g" /etc/icecast2/icecast.xml
done
sed -i 's|<!-- <bind-address>.*|<bind-address>127.0.0.1</bind-address>|;s|<!-- <shoutcast-mount>.*|<shoutcast-mount>/stream</shoutcast-mount>|'
systemctl enable icecast2.service
}
@@ -406,6 +409,10 @@ install_cleanup_cron() {
sed "s/\$USER/$USER/g" $my_dir/templates/cleanup.cron >> /etc/crontab
}
install_weekly_cron() {
sed "s/\$USER/$USER/g" $my_dir/templates/weekly_report.cron >> /etc/crontab
}
chown_things() {
chown -R $USER:$USER $HOME/Bird*
}
@@ -431,6 +438,7 @@ install_services() {
install_phpsysinfo
install_livestream_service
install_cleanup_cron
install_weekly_cron
create_necessary_dirs
generate_BirdDB
+13 -6
View File
@@ -1,6 +1,6 @@
<?php
error_reporting(0);
ini_set('display_errors', 0);
error_reporting(E_ERROR);
ini_set('display_errors',1);
ini_set('session.gc_maxlifetime', 7200);
session_set_cookie_params(7200);
session_start();
@@ -50,12 +50,15 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" && isse
$comname = preg_replace('/\'/', '', $comname);
$filename = "/By_Date/".$mostrecent['Date']."/".$comname."/".$mostrecent['File_Name'];
$args = "&license=2%2C3%2C4%2C5%2C6%2C9&orientation=square,portrait";
$comnameprefix = "%20bird";
// check to make sure the image actually exists, sometimes it takes a minute to be created\
if(file_exists($home."/BirdSongs/Extracted".$filename.".png")){
if($_GET['previous_detection_identifier'] == $filename) { die(); }
if($_GET['only_name'] == "true") { echo $comname.",".$filename;die(); }
$iterations++;
if (!empty($config["FLICKR_API_KEY"])) {
if(!empty($config["FLICKR_FILTER_EMAIL"])) {
@@ -64,6 +67,7 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" && isse
$_SESSION['FLICKR_FILTER_EMAIL'] = json_decode(file_get_contents("https://www.flickr.com/services/rest/?method=flickr.people.findByEmail&api_key=".$config["FLICKR_API_KEY"]."&find_email=".$config["FLICKR_FILTER_EMAIL"]."&format=json&nojsoncallback=1"), true)["user"]["nsid"];
}
$args = "&user_id=".$_SESSION['FLICKR_FILTER_EMAIL'];
$comnameprefix = "";
} else {
if(isset($_SESSION["FLICKR_FILTER_EMAIL"])) {
unset($_SESSION["FLICKR_FILTER_EMAIL"]);
@@ -89,7 +93,7 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" && isse
}
}
$flickrjson = json_decode(file_get_contents("https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=".$config["FLICKR_API_KEY"]."&text=\"".str_replace(" ", "%20", $engname)."\"&sort=relevance".$args."&per_page=5&media=photos&format=json&nojsoncallback=1"), true)["photos"]["photo"][0];
$flickrjson = json_decode(file_get_contents("https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=".$config["FLICKR_API_KEY"]."&text=".str_replace(" ", "%20", $engname).$comnameprefix."&sort=relevance".$args."&per_page=5&media=photos&format=json&nojsoncallback=1"), true)["photos"]["photo"][0];
$modaltext = "https://flickr.com/photos/".$flickrjson["owner"]."/".$flickrjson["id"];
$authorlink = "https://flickr.com/people/".$flickrjson["owner"];
$imageurl = 'https://farm' .$flickrjson["farm"]. '.static.flickr.com/' .$flickrjson["server"]. '/' .$flickrjson["id"]. '_' .$flickrjson["secret"]. '.jpg';
@@ -120,7 +124,7 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" && isse
<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>
<td class="relative"><a target="_blank" href="index.php?filename=<?php echo $mostrecent['File_Name']; ?>"><img class="copyimage" title="Open in new tab" width="25" height="25" src="images/copy.png"></a>
<div class="centered_image_container" style="margin-bottom: 0px !important;">
<?php if(!empty($config["FLICKR_API_KEY"]) && strlen($image[2]) > 0) { ?>
<img onclick='setModalText(<?php echo $iterations; ?>,"<?php echo urlencode($image[2]); ?>", "<?php echo $image[3]; ?>", "<?php echo $image[4]; ?>", "<?php echo $image[1]; ?>")' src="<?php echo $image[1]; ?>" class="img1">
@@ -136,6 +140,9 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" && isse
</table> <?php break;
}
}
if($iterations == 0) {
echo "<h3>No Detections For Today.</h3>";
}
die();
}
@@ -284,7 +291,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 && !this.responseText.includes("Database is busy")) {
if(this.responseText.length > 0 && !this.responseText.includes("Database is busy") && !this.responseText.includes("No Detections") || previous_detection_identifier == undefined) {
document.getElementById("most_recent_detection").innerHTML = this.responseText;
// only going to load left chart & 5 most recents if there's a new detection
@@ -309,7 +316,7 @@ function loadLeftChart() {
function refreshTopTen() {
const xhttp = new XMLHttpRequest();
xhttp.onload = function() {
if(this.responseText.length > 0 && !this.responseText.includes("Database is busy")) {
if(this.responseText.length > 0 && !this.responseText.includes("Database is busy") && !this.responseText.includes("No Detections") || previous_detection_identifier == undefined) {
document.getElementById("chart").src = "/Charts/"+this.responseText+"?nocache="+Date.now();
}
}
+202 -143
View File
@@ -1,5 +1,6 @@
<?php
error_reporting(E_ERROR);
ini_set('display_errors',1);
$db = new SQLite3('./scripts/birds.db', SQLITE3_OPEN_CREATE | SQLITE3_OPEN_READWRITE);
if($db == False){
@@ -17,6 +18,41 @@ $user = shell_exec("awk -F: '/1000/{print $1}' /etc/passwd");
$home = shell_exec("awk -F: '/1000/{print $6}' /etc/passwd");
$home = trim($home);
if(isset($_GET['deletefile'])) {
if(isset($_SERVER['PHP_AUTH_USER'])) {
$submittedpwd = $_SERVER['PHP_AUTH_PW'];
$submitteduser = $_SERVER['PHP_AUTH_USER'];
if($submittedpwd == $config['CADDY_PWD'] && $submitteduser == 'birdnet'){
$statement1 = $db->prepare('DELETE FROM detections WHERE File_Name = "'.explode("/",$_GET['deletefile'])[2].'" LIMIT 1');
if($statement1 == False){
echo "Error";
header("refresh: 0;");
} else {
$file_pointer = $home."/BirdSongs/Extracted/By_Date/".$_GET['deletefile'];
if (!exec("sudo rm $file_pointer && sudo rm $file_pointer.png")) {
echo "OK";
} else {
echo "Error";
}
}
$result1 = $statement1->execute();
die();
} else {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'You must be authenticated to change the protection of files.';
exit;
}
} else {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'You must be authenticated to change the protection of files.';
exit;
}
}
if(isset($_GET['excludefile'])) {
if(isset($_SERVER['PHP_AUTH_USER'])) {
$submittedpwd = $_SERVER['PHP_AUTH_PW'];
@@ -27,21 +63,21 @@ if(isset($_GET['excludefile'])) {
}
if(isset($_GET['exclude_add'])) {
$myfile = fopen($home."/BirdNET-Pi/scripts/disk_check_exclude.txt", "a") or die("Unable to open file!");
$txt = $_GET['excludefile'];
fwrite($myfile, $txt."\n");
fwrite($myfile, $txt.".png\n");
fclose($myfile);
echo "OK";
die();
$txt = $_GET['excludefile'];
fwrite($myfile, $txt."\n");
fwrite($myfile, $txt.".png\n");
fclose($myfile);
echo "OK";
die();
} else {
$lines = file($home."/BirdNET-Pi/scripts/disk_check_exclude.txt");
$search = $_GET['excludefile'];
$result = '';
foreach($lines as $line) {
if(stripos($line, $search) === false && stripos($line, $search.".png") === false) {
$result .= $line;
}
if(stripos($line, $search) === false && stripos($line, $search.".png") === false) {
$result .= $line;
}
}
file_put_contents($home."/BirdNET-Pi/scripts/disk_check_exclude.txt", $result);
echo "OK";
@@ -90,7 +126,7 @@ if(isset($_GET['bydate'])){
#By Species
} elseif(isset($_GET['byspecies'])) {
if(isset($_GET['sort']) && $_GET['sort'] == "occurrences") {
$statement = $db->prepare('SELECT DISTINCT(Com_Name) FROM detections GROUP BY Com_Name ORDER BY COUNT(*) DESC');
$statement = $db->prepare('SELECT DISTINCT(Com_Name) FROM detections GROUP BY Com_Name ORDER BY COUNT(*) DESC');
} else {
$statement = $db->prepare('SELECT DISTINCT(Com_Name) FROM detections ORDER BY Com_Name ASC');
}
@@ -132,18 +168,37 @@ if(isset($_GET['bydate'])){
</head>
<script>
function deleteDetection(filename,copylink=false) {
if (confirm("Are you sure you want to delete this detection from the database?") == true) {
const xhttp = new XMLHttpRequest();
xhttp.onload = function() {
if(this.responseText == "OK"){
if(copylink == true) {
window.top.close();
} else {
location.reload();
}
} else {
alert("Database busy.")
}
}
xhttp.open("GET", "play.php?deletefile="+filename, true);
xhttp.send();
}
}
function toggleLock(filename, type, elem) {
const xhttp = new XMLHttpRequest();
xhttp.onload = function() {
if(this.responseText == "OK"){
if(type == "add") {
elem.setAttribute("src","images/lock.svg");
elem.setAttribute("title", "This file is delete protected.");
elem.setAttribute("onclick", elem.getAttribute("onclick").replace("add","del"));
elem.setAttribute("src","images/lock.svg");
elem.setAttribute("title", "This file is excluded from being purged.");
elem.setAttribute("onclick", elem.getAttribute("onclick").replace("add","del"));
} else {
elem.setAttribute("src","images/unlock.svg");
elem.setAttribute("title", "This file is not delete protected.");
elem.setAttribute("onclick", elem.getAttribute("onclick").replace("del","add"));
elem.setAttribute("src","images/unlock.svg");
elem.setAttribute("title", "This file is not excluded from being purged.");
elem.setAttribute("onclick", elem.getAttribute("onclick").replace("del","add"));
}
}
}
@@ -187,28 +242,28 @@ if(!isset($_GET['species']) && !isset($_GET['filename'])){
while($results=$result->fetchArray(SQLITE3_ASSOC)){
$date = $results['Date'];
if(realpath($home."/BirdSongs/Extracted/By_Date/".$date) !== false){
echo "<td>
<button action=\"submit\" name=\"date\" value=\"$date\">$date</button></td></tr>";}}
echo "<td>
<button action=\"submit\" name=\"date\" value=\"$date\">".($date == date('Y-m-d') ? "Today" : $date)."</button></td></tr>";}}
#By Species
#By Species
} elseif($view == "byspecies") {
while($results=$result->fetchArray(SQLITE3_ASSOC)){
$name = $results['Com_Name'];
echo "<td>
<button action=\"submit\" name=\"species\" value=\"$name\">$name</button></td></tr>";}
#Specific Date
#Specific Date
} elseif($view == "date") {
while($results=$result->fetchArray(SQLITE3_ASSOC)){
$name = $results['Com_Name'];
if(realpath($home."/BirdSongs/Extracted/By_Date/".$date."/".str_replace(" ", "_",$name)) !== false){
echo "<td>
<button action=\"submit\" name=\"species\" value=\"$name\">$name</button></td></tr>";
echo "<td>
<button action=\"submit\" name=\"species\" value=\"$name\">$name</button></td></tr>";
}
}
#Choose
#Choose
} else {
echo "<td>
<button action=\"submit\" name=\"byspecies\" value=\"byspecies\">By Species</button></td></tr>
@@ -216,8 +271,8 @@ if(!isset($_GET['species']) && !isset($_GET['filename'])){
}
echo "</form>
</tr>
</table>";
</tr>
</table>";
}
#Specific Species
@@ -227,142 +282,146 @@ if(isset($_GET['species'])){ ?>
<form action="" method="GET">
<input type="hidden" name="view" value="Recordings">
<input type="hidden" name="species" value="<?php echo $_GET['species']; ?>">
<button <?php if(!isset($_GET['sort']) || $_GET['sort'] == "date"){ echo "style='background:#9fe29b !important;'"; }?> class="sortbutton" type="submit" name="sort" value="date">
<input type="hidden" name="sort" value="<?php echo $_GET['sort']; ?>">
<button <?php if(!isset($_GET['sort']) || $_GET['sort'] == "" || $_GET['sort'] == "date"){ echo "style='background:#9fe29b !important;'"; }?> class="sortbutton" type="submit" name="sort" value="date">
<img width=35px src="images/sort_date.svg" title="Sort by date" alt="Sort by date">
</button>
<button <?php if(isset($_GET['sort']) && $_GET['sort'] == "confidence"){ echo "style='background:#9fe29b !important;'"; }?> class="sortbutton" type="submit" name="sort" value="confidence">
<img src="images/sort_occ.svg" title="Sort by confidence" alt="Sort by confidence">
</button>
</button><br>
<input style="margin-top:10px" <?php if(isset($_GET['only_excluded'])){ echo "checked"; }?> type="checkbox" name="only_excluded" onChange="submit()">
<label for="onlyverified">Only Show Purge Excluded</label>
</form>
</div>
<?php
// add disk_check_exclude.txt lines into an array for grepping
$fp = @fopen($home."/BirdNET-Pi/scripts/disk_check_exclude.txt", 'r');
if ($fp) {
$disk_check_exclude_arr = explode("\n", fread($fp, filesize($home."/BirdNET-Pi/scripts/disk_check_exclude.txt")));
}
$name = $_GET['species'];
if(isset($_SESSION['date'])) {
$date = $_SESSION['date'];
if(isset($_GET['sort']) && $_GET['sort'] == "confidence") {
$statement2 = $db->prepare("SELECT * FROM detections where Com_Name == \"$name\" AND Date == \"$date\" ORDER BY Confidence DESC");
} else {
$statement2 = $db->prepare("SELECT * FROM detections where Com_Name == \"$name\" AND Date == \"$date\" ORDER BY Time DESC");
}
if ($fp) {
$disk_check_exclude_arr = explode("\n", fread($fp, filesize($home."/BirdNET-Pi/scripts/disk_check_exclude.txt")));
}
$name = $_GET['species'];
if(isset($_SESSION['date'])) {
$date = $_SESSION['date'];
if(isset($_GET['sort']) && $_GET['sort'] == "confidence") {
$statement2 = $db->prepare("SELECT * FROM detections where Com_Name == \"$name\" AND Date == \"$date\" ORDER BY Confidence DESC");
} else {
if(isset($_GET['sort']) && $_GET['sort'] == "confidence") {
$statement2 = $db->prepare("SELECT * FROM detections where Com_Name == \"$name\" ORDER BY Confidence DESC");
} else {
$statement2 = $db->prepare("SELECT * FROM detections where Com_Name == \"$name\" ORDER BY Date DESC, Time DESC");
$statement2 = $db->prepare("SELECT * FROM detections where Com_Name == \"$name\" AND Date == \"$date\" ORDER BY Time DESC");
}
} else {
if(isset($_GET['sort']) && $_GET['sort'] == "confidence") {
$statement2 = $db->prepare("SELECT * FROM detections where Com_Name == \"$name\" ORDER BY Confidence DESC");
} else {
$statement2 = $db->prepare("SELECT * FROM detections where Com_Name == \"$name\" ORDER BY Date DESC, Time DESC");
}
}
if($statement2 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result2 = $statement2->execute();
echo "<table>
<tr>
<th>$name</th>
</tr>";
$iter=0;
while($results=$result2->fetchArray(SQLITE3_ASSOC))
{
$comname = preg_replace('/ /', '_', $results['Com_Name']);
$comname = preg_replace('/\'/', '', $comname);
$date = $results['Date'];
$filename = "/By_Date/".$date."/".$comname."/".$results['File_Name'];
$sciname = preg_replace('/ /', '_', $results['Sci_Name']);
$sci_name = $results['Sci_Name'];
$time = $results['Time'];
$confidence = round((float)round($results['Confidence'],2) * 100 ) . '%';
$filename_formatted = $date."/".$comname."/".$results['File_Name'];
// file was deleted by disk check, no need to show the detection in recordings
if(!file_exists($home."/BirdSongs/Extracted/".$filename)) {
continue;
}
}
if($statement2 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result2 = $statement2->execute();
echo "<table>
<tr>
<th>$name</th>
</tr>";
$iter=0;
while($results=$result2->fetchArray(SQLITE3_ASSOC))
{
$comname = preg_replace('/ /', '_', $results['Com_Name']);
$comname = preg_replace('/\'/', '', $comname);
$date = $results['Date'];
$filename = "/By_Date/".$date."/".$comname."/".$results['File_Name'];
$sciname = preg_replace('/ /', '_', $results['Sci_Name']);
$sci_name = $results['Sci_Name'];
$time = $results['Time'];
$confidence = round((float)round($results['Confidence'],2) * 100 ) . '%';
$filename_formatted = $date."/".$comname."/".$results['File_Name'];
if(!in_array($filename_formatted, $disk_check_exclude_arr) && isset($_GET['only_excluded'])) {
continue;
}
$iter++;
// file was deleted by disk check, no need to show the detection in recordings
if(!file_exists($home."/BirdSongs/Extracted/".$filename)) {
continue;
if($config["FULL_DISK"] == "purge") {
if(!in_array($filename_formatted, $disk_check_exclude_arr)) {
$imageicon = "images/unlock.svg";
$title = "This file is not excluded from being purged.";
$type = "add";
} else {
$imageicon = "images/lock.svg";
$title = "This file is excluded from being purged.";
$type = "del";
}
$iter++;
if($config["FULL_DISK"] == "purge") {
if(!in_array($filename_formatted, $disk_check_exclude_arr)) {
$imageicon = "images/unlock.svg";
$title = "This file is not delete protected.";
$type = "add";
} else {
$imageicon = "images/lock.svg";
$title = "This file is delete protected.";
$type = "del";
echo "<tr>
<td class=\"relative\"><img style='cursor:pointer;right:45px' src='images/delete.svg' onclick='deleteDetection(\"".$filename_formatted."\")' class=\"copyimage\" width=25 title='Delete Detection'> <img style='cursor:pointer' onclick='toggleLock(\"".$filename_formatted."\",\"".$type."\", this)' class=\"copyimage\" width=25 title=\"".$title."\" src=\"".$imageicon."\">$date $time<br>$confidence<br>
<video onplay='setLiveStreamVolume(0)' onended='setLiveStreamVolume(1)' onpause='setLiveStreamVolume(1)' controls poster=\"$filename.png\" preload=\"none\" title=\"$filename\"><source src=\"$filename\"></video></td>
</tr>";
} else {
echo "<tr>
<td class=\"relative\">$date $time<br>$confidence<img style='cursor:pointer' src='images/delete.svg' onclick='deleteDetection(\"".$filename_formatted."\")' class=\"copyimage\" width=25 title='Delete Detection'><br>
<video onplay='setLiveStreamVolume(0)' onended='setLiveStreamVolume(1)' onpause='setLiveStreamVolume(1)' controls poster=\"$filename.png\" preload=\"none\" title=\"$filename\"><source src=\"$filename\"></video></td>
</tr>";
}
}if($iter == 0){ echo "<tr><td><b>No recordings were found.</b><br><br><span style='font-size:medium'>They may have been deleted to make space for new recordings. You can prevent this from happening in the future by clicking the <img src='images/unlock.svg' style='width:20px'> icon in the top right of a recording.<br>You can also modify this behavior globally under \"Full Disk Behavior\" <a href='views.php?view=Advanced'>here.</a></span></td></tr>";}echo "</table>";}
if(isset($_GET['filename'])){
$name = $_GET['filename'];
$statement2 = $db->prepare("SELECT * FROM detections where File_name == \"$name\" ORDER BY Date DESC, Time DESC");
if($statement2 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result2 = $statement2->execute();
echo "<table>
<tr>
<th>$name</th>
</tr>";
while($results=$result2->fetchArray(SQLITE3_ASSOC))
{
$comname = preg_replace('/ /', '_', $results['Com_Name']);
$comname = preg_replace('/\'/', '', $comname);
$date = $results['Date'];
$filename = "/By_Date/".$date."/".$comname."/".$results['File_Name'];
$sciname = preg_replace('/ /', '_', $results['Sci_Name']);
$sci_name = $results['Sci_Name'];
$time = $results['Time'];
$confidence = round((float)round($results['Confidence'],2) * 100 ) . '%';
$filename_formatted = $date."/".$comname."/".$results['File_Name'];
// add disk_check_exclude.txt lines into an array for grepping
$fp = @fopen($home."/BirdNET-Pi/scripts/disk_check_exclude.txt", 'r');
if ($fp) {
$disk_check_exclude_arr = explode("\n", fread($fp, filesize($home."/BirdNET-Pi/scripts/disk_check_exclude.txt")));
}
echo "<tr>
<td class='relative'>$date $time<br>$confidence<br><img style='cursor:pointer' onclick='toggleLock(\"".$filename_formatted."\",\"".$type."\", this)' class=\"copyimage\" width=25 title=\"".$title."\" src=\"".$imageicon."\">
<a href=\"$filename\"><img src=\"$filename.png\"></a>
</td>
</tr>";
} else {
echo "<tr>
<td class='relative'>$date $time<br>$confidence<br>
<a href=\"$filename\"><img src=\"$filename.png\"></a>
</td>
</tr>";
}
if($config["FULL_DISK"] == "purge") {
if(!in_array($filename_formatted, $disk_check_exclude_arr)) {
$imageicon = "images/unlock.svg";
$title = "This file is not excluded from being purged.";
$type = "add";
} else {
$imageicon = "images/lock.svg";
$title = "This file is excluded from being purged.";
$type = "del";
}
}if($iter == 0){ echo "<tr><td><b>No recordings were found.</b><br><br><span style='font-size:medium'>They may have been deleted to make space for new recordings. You can prevent this from happening in the future by clicking the <img src='images/unlock.svg' style='width:20px'> icon in the top right of a recording.<br>You can also modify this behavior globally under \"Full Disk Behavior\" <a href='views.php?view=Advanced'>here.</a></span></td></tr>";}echo "</table>";}
if(isset($_GET['filename'])){
$name = $_GET['filename'];
$statement2 = $db->prepare("SELECT * FROM detections where File_name == \"$name\" ORDER BY Date DESC, Time DESC");
if($statement2 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result2 = $statement2->execute();
echo "<table>
<tr>
<th>$name</th>
</tr>";
while($results=$result2->fetchArray(SQLITE3_ASSOC))
{
$comname = preg_replace('/ /', '_', $results['Com_Name']);
$comname = preg_replace('/\'/', '', $comname);
$date = $results['Date'];
$filename = "/By_Date/".$date."/".$comname."/".$results['File_Name'];
$sciname = preg_replace('/ /', '_', $results['Sci_Name']);
$sci_name = $results['Sci_Name'];
$time = $results['Time'];
$confidence = round((float)round($results['Confidence'],2) * 100 ) . '%';
$filename_formatted = $date."/".$comname."/".$results['File_Name'];
// add disk_check_exclude.txt lines into an array for grepping
$fp = @fopen($home."/BirdNET-Pi/scripts/disk_check_exclude.txt", 'r');
if ($fp) {
$disk_check_exclude_arr = explode("\n", fread($fp, filesize($home."/BirdNET-Pi/scripts/disk_check_exclude.txt")));
}
if($config["FULL_DISK"] == "purge") {
if(!in_array($filename_formatted, $disk_check_exclude_arr)) {
$imageicon = "images/unlock.svg";
$title = "This file is not delete protected.";
$type = "add";
echo "<tr>
<td class=\"relative\"><img style='cursor:pointer;right:45px' src='images/delete.svg' onclick='deleteDetection(\"".$filename_formatted."\", true)' class=\"copyimage\" width=25 title='Delete Detection'> <img style='cursor:pointer' onclick='toggleLock(\"".$filename_formatted."\",\"".$type."\", this)' class=\"copyimage\" width=25 title=\"".$title."\" src=\"".$imageicon."\">$date $time<br>$confidence<br>
<video onplay='setLiveStreamVolume(0)' onended='setLiveStreamVolume(1)' onpause='setLiveStreamVolume(1)' controls poster=\"$filename.png\" preload=\"none\" title=\"$filename\"><source src=\"$filename\"></video></td>
</tr>";
} else {
$imageicon = "images/lock.svg";
$title = "This file is delete protected.";
$type = "del";
echo "<tr>
<td class=\"relative\">$date $time<br>$confidence<img style='cursor:pointer' src='images/delete.svg' onclick='deleteDetection(\"".$filename_formatted."\", true)' class=\"copyimage\" width=25 title='Delete Detection'><br>
<video onplay='setLiveStreamVolume(0)' onended='setLiveStreamVolume(1)' onpause='setLiveStreamVolume(1)' controls poster=\"$filename.png\" preload=\"none\" title=\"$filename\"><source src=\"$filename\"></video></td>
</tr>";
}
echo "<tr>
<td class=\"relative\"><img style='cursor:pointer' onclick='toggleLock(\"".$filename_formatted."\",\"".$type."\", this)' class=\"copyimage\" width=25 title=\"".$title."\" src=\"".$imageicon."\">$date $time<br>$confidence<br>
<video onplay='setLiveStreamVolume(0)' onended='setLiveStreamVolume(1)' onpause='setLiveStreamVolume(1)' controls poster=\"$filename.png\" preload=\"none\" title=\"$filename\"><source src=\"$filename\"></video></td>
</tr>";
} else {
echo "<tr>
<td class=\"relative\">$date $time<br>$confidence<br>
<video onplay='setLiveStreamVolume(0)' onended='setLiveStreamVolume(1)' onpause='setLiveStreamVolume(1)' controls poster=\"$filename.png\" preload=\"none\" title=\"$filename\"><source src=\"$filename\"></video></td>
</tr>";
}
}echo "</table>";}?>
}echo "</table>";}?>
</div>
</html>
+3 -1
View File
@@ -8,7 +8,9 @@ my_dir=$HOME/BirdNET-Pi/scripts
sudo systemctl stop birdnet_server.service
sudo systemctl stop birdnet_recording.service
services=(icecast2.service
services=(chart_viewer.service
spectrogram_viewer.service
icecast2.service
extraction.service
birdnet_recording.service
birdnet_log.service)
+33 -13
View File
@@ -1,73 +1,93 @@
<?php
function service_status($name) {
$user = shell_exec("awk -F: '/1000/{print $1}' /etc/passwd");
$home = shell_exec("awk -F: '/1000/{print $6}' /etc/passwd");
$home = trim($home);
if($name == "birdnet_server.service") {
$filesinproc=trim(shell_exec("ls ".$home."/BirdSongs/Processed | wc -l"));
if($filesinproc > 200) {
echo "<span style='color:#fc6603'>(stalled - backlog of ".$filesinproc." files in ~/BirdSongs/Processed/)</span>";
return;
}
}
$op = shell_exec("sudo systemctl status ".$name." | grep Active | grep ' active\| activating\|running\|waiting\|start'");
if(strlen($op) > 0) {
echo "<span style='color:green'>(active)</span>";
} else {
echo "<span style='color:#fc6603'>(inactive)</span>";
}
}
?>
<html>
<meta name="viewport" content="width=device-width, initial-scale=1">
<br>
<br>
<div class="servicecontrols">
<form action="" method="GET">
<h3>Live Audio Stream</h3>
<h3>Live Audio Stream <?php echo service_status("livestream.service");?></h3>
<button type="submit" name="submit" value="sudo systemctl stop livestream.service && sudo /etc/init.d/icecast2 stop">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart livestream.service && sudo /etc/init.d/icecast2 restart">Restart </button>
<button type="submit" name="submit" value="sudo systemctl disable --now livestream.service && sudo systemctl disable icecast2 && sudo /etc/init.d/icecast2 stop">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable icecast2 && sudo /etc/init.d/icecast2 start && sudo systemctl enable --now livestream.service">Enable</button>
</form>
<form action="" method="GET">
<h3>Web Terminal</h3>
<h3>Web Terminal <?php echo service_status("web_terminal.service");?></h3>
<button type="submit" name="submit" value="sudo systemctl stop web_terminal.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart web_terminal.service">Restart </button>
<button type="submit" name="submit" value="sudo systemctl disable --now web_terminal.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now web_terminal.service">Enable</button>
</form>
<form action="" method="GET">
<h3>BirdNET Log</h3>
<h3>BirdNET Log <?php echo service_status("birdnet_log.service");?></h3>
<button type="submit" name="submit" value="sudo systemctl stop birdnet_log.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart birdnet_log.service">Restart </button>
<button type="submit" name="submit" value="sudo systemctl disable --now birdnet_log.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now birdnet_log.service">Enable</button>
</form>
<form action="" method="GET">
<h3>Extraction Service</h3>
<h3>Extraction Service <?php echo service_status("extraction.service");?></h3>
<button type="submit" name="submit" value="sudo systemctl stop extraction.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart extraction.service">Restart </button>
<button type="submit" name="submit" value="sudo systemctl disable --now extraction.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now extraction.service">Enable</button>
</form>
<form action="" method="GET">
<h3>BirdNET Analysis Server</h3>
<h3>BirdNET Analysis Server <?php echo service_status("birdnet_server.service");?></h3>
<button type="submit" name="submit" value="sudo systemctl stop birdnet_server.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart birdnet_server.service">Restart</button>
<button type="submit" name="submit" value="sudo systemctl disable --now birdnet_server.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now birdnet_server.service">Enable</button>
</form>
<form action="" method="GET">
<h3>BirdNET Analysis Client</h3>
<h3>BirdNET Analysis Client <?php echo service_status("birdnet_analysis.service");?></h3>
<button type="submit" name="submit" value="sudo systemctl stop birdnet_analysis.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart birdnet_analysis.service">Restart</button>
<button type="submit" name="submit" value="sudo systemctl disable --now birdnet_analysis.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now birdnet_analysis.service">Enable</button>
</form>
<form action="" method="GET">
<h3>Streamlit Statistics</h3>
<h3>Streamlit Statistics <?php echo service_status("birdnet_stats.service");?></h3>
<button type="submit" name="submit" value="sudo systemctl stop birdnet_stats.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart birdnet_stats.service">Restart</button>
<button type="submit" name="submit" value="sudo systemctl disable --now birdnet_stats.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now birdnet_stats.service">Enable</button>
</form>
<form action="" method="GET">
<h3>Recording Service</h3>
<h3>Recording Service <?php echo service_status("birdnet_recording.service");?></h3>
<button type="submit" name="submit" value="sudo systemctl stop birdnet_recording.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart birdnet_recording.service">Restart</button>
<button type="submit" name="submit" value="sudo systemctl disable --now birdnet_recording.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now birdnet_recording.service">Enable</button>
</form>
<form action="" method="GET">
<h3>Chart Viewer</h3>
<h3>Chart Viewer <?php echo service_status("chart_viewer.service");?></h3>
<button type="submit" name="submit" value="sudo systemctl stop chart_viewer.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart chart_viewer.service">Restart</button>
<button type="submit" name="submit" value="sudo systemctl disable --now chart_viewer.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now chart_viewer.service">Enable</button>
</form>
<form action="" method="GET">
<h3>Spectrogram Viewer</h3>
<h3>Spectrogram Viewer <?php echo service_status("spectrogram_viewer.service");?></h3>
<button type="submit" name="submit" value="sudo systemctl stop spectrogram_viewer.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart spectrogram_viewer.service">Restart</button>
<button type="submit" name="submit" value="sudo systemctl disable --now spectrogram_viewer.service">Disable</button>
@@ -75,8 +95,8 @@
</form>
<form action="" method="GET">
<button type="submit" name="submit" value="stop_core_services.sh">Stop Core Services</button>
</form>
</form>
<form action="" method="GET">
<button type="submit" name="submit" value="restart_services.sh">Restart Core Services</button>
</form>
</form>
</div>
+3 -1
View File
@@ -1,4 +1,6 @@
<?php
error_reporting(E_ERROR);
ini_set('display_errors',1);
if(isset($_GET['ajax_csv'])) {
if (file_exists('./scripts/thisrun.txt')) {
@@ -66,7 +68,7 @@ window.onload = function(){
};
// if user agent includes iPhone or Mac use legacy mode
if(window.navigator.userAgent.includes("iPhone") || window.navigator.userAgent.includes("Mac") || legacy == true) {
if(((window.navigator.userAgent.includes("iPhone") || window.navigator.userAgent.includes("Mac")) && !window.navigator.userAgent.includes("Chrome")) || legacy == true) {
document.getElementById("spectrogramimage").style.display="";
document.body.querySelector('canvas').remove();
document.getElementById('player').remove();
+2 -2
View File
@@ -155,7 +155,7 @@ while($results=$result3->fetchArray(SQLITE3_ASSOC)){
$filename = "/By_Date/".$date."/".$comname."/".$results['File_Name'];
echo str_pad("<h3>$species</h3>
<table><tr>
<td class=\"relative\"><a target=\"_blank\" href=\"index.php?filename=".$results['File_Name']."\"><img class=\"copyimage\" width=25 src=\"images/copy.png\"></a> <a href=\"https://wikipedia.org/wiki/$dbsciname\" target=\"top\"/><i>$sciname</i></a><br>
<td class=\"relative\"><a target=\"_blank\" href=\"index.php?filename=".$results['File_Name']."\"><img title=\"Open in new tab\" class=\"copyimage\" width=25 src=\"images/copy.png\"></a> <a href=\"https://wikipedia.org/wiki/$dbsciname\" target=\"top\"/><i>$sciname</i></a><br>
<b>Occurrences: </b>$count<br>
<b>Max Confidence: </b>$maxconf<br>
<b>Best Recording: </b>$date $time<br>
@@ -223,7 +223,7 @@ array_push($excludelines, $results['Date']."/".$comname."/".$results['File_Name'
<tr>
<form action="" method="GET">
<input type="hidden" name="sort" value="<?php if(isset($_GET['sort'])){echo $_GET['sort'];}?>">
<td class="relative"><a target="_blank" href="index.php?filename=<?php echo $results['File_Name']; ?>"><img class="copyimage" width=25 src="images/copy.png"></a><input type="hidden" name="view" value="Species Stats">
<td class="relative"><a target="_blank" href="index.php?filename=<?php echo $results['File_Name']; ?>"><img title="Open in new tab" class="copyimage" width=25 src="images/copy.png"></a><input type="hidden" name="view" value="Species Stats">
<button type="submit" name="species" value="<?php echo $results['Com_Name'];?>"><?php echo $results['Com_Name'];?></button><br><b>Occurrences:</b> <?php echo $results['COUNT(*)'];?><br>
<b>Max Confidence:</b> <?php echo $percent = round((float)round($results['MAX(Confidence)'],2) * 100 ) . '%';?><br>
<b>Best Recording:</b> <?php echo $results['Date']." ".$results['Time'];?><br><video 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;?>" type="audio/mp3"></video></td>
+13 -4
View File
@@ -5,19 +5,28 @@ $user = trim($user);
$home = shell_exec("awk -F: '/1000/{print $6}' /etc/passwd");
$home = trim($home);
$fetch = shell_exec("sudo -u".$user." git -C ".$home."/BirdNET-Pi fetch 2>&1");
if(strlen($fetch) > 0) {
$_SESSION['behind'] = trim(shell_exec("sudo -u".$user." git -C ".$home."/BirdNET-Pi status | sed -n '2 p' | cut -d ' ' -f 7"));
}
$_SESSION['behind'] = trim(shell_exec("sudo -u".$user." git -C ".$home."/BirdNET-Pi status | sed -n '2 p' | cut -d ' ' -f 7"));
?><html>
<meta name="viewport" content="width=device-width, initial-scale=1">
<br>
<br>
<script>
var seconds = 0;
function update() {
if(confirm('Are you sure you want to update?')) {
setInterval(function(){ seconds += 1; document.getElementById('updatebtn').innerHTML = "Updating: <pre id='timer' class='bash'>"+new Date(seconds * 1000).toISOString().substring(14, 19)+"</span>"; }, 1000);
return true;
} else {
return false;
}
}
</script>
<div class="systemcontrols">
<form action="" method="GET">
<button type="submit" name="submit" value="sudo reboot" onclick="return confirm('Are you sure you want to reboot?')">Reboot</button>
</form>
<form action="" method="GET">
<button type="submit" name="submit" value="update_birdnet.sh" onclick="return confirm('Are you sure you want to update?')">Update <?php if(isset($_SESSION['behind']) && $_SESSION['behind'] != "0" && $_SESSION['behind'] != "with"){?><div class="updatenumber"><?php echo $_SESSION['behind']; ?></div><?php } ?></button>
<button type="submit" name="submit" id="updatebtn" value="update_birdnet.sh" onclick="update();">Update <?php if(isset($_SESSION['behind']) && $_SESSION['behind'] != "0" && $_SESSION['behind'] != "with"){?><div class="updatenumber"><?php echo $_SESSION['behind']; ?></div><?php } ?></button>
</form>
<form action="" method="GET">
<button type="submit" name="submit" value="sudo shutdown now" onclick="return confirm('Are you sure you want to shutdown?')">Shutdown</button>
+10 -4
View File
@@ -2,8 +2,8 @@
ini_set('session.gc_maxlifetime', 7200);
session_set_cookie_params(7200);
session_start();
error_reporting(0);
ini_set('display_errors', 0);
error_reporting(E_ERROR);
ini_set('display_errors',1);
$db = new SQLite3('./scripts/birds.db', SQLITE3_OPEN_CREATE | SQLITE3_OPEN_READWRITE);
if($db == False){
@@ -119,6 +119,7 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" ) {
$filename = "/By_Date/".date('Y-m-d')."/".$comname."/".$todaytable['File_Name'];
$sciname = preg_replace('/ /', '_', $todaytable['Sci_Name']);
$args = "&license=2%2C3%2C4%2C5%2C6%2C9&orientation=square,portrait";
$comnameprefix = "%20bird";
if (!empty($config["FLICKR_API_KEY"]) && (isset($_GET['display_limit']) || isset($_GET['hard_limit']))) {
@@ -128,6 +129,7 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" ) {
$_SESSION['FLICKR_FILTER_EMAIL'] = json_decode(file_get_contents("https://www.flickr.com/services/rest/?method=flickr.people.findByEmail&api_key=".$config["FLICKR_API_KEY"]."&find_email=".$config["FLICKR_FILTER_EMAIL"]."&format=json&nojsoncallback=1"), true)["user"]["nsid"];
}
$args = "&user_id=".$_SESSION['FLICKR_FILTER_EMAIL'];
$comnameprefix = "";
} else {
if(isset($_SESSION["FLICKR_FILTER_EMAIL"])) {
unset($_SESSION["FLICKR_FILTER_EMAIL"]);
@@ -151,7 +153,7 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" ) {
break;
}
}
$flickrjson = json_decode(file_get_contents("https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=".$config["FLICKR_API_KEY"]."&text=\"".str_replace(" ", "%20", $engname)."\"&sort=relevance".$args."&per_page=5&media=photos&format=json&nojsoncallback=1"), true)["photos"]["photo"][0];
$flickrjson = json_decode(file_get_contents("https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=".$config["FLICKR_API_KEY"]."&text=".str_replace(" ", "%20", $engname).$comnameprefix."&sort=relevance".$args."&per_page=5&media=photos&format=json&nojsoncallback=1"), true)["photos"]["photo"][0];
$modaltext = "https://flickr.com/photos/".$flickrjson["owner"]."/".$flickrjson["id"];
$authorlink = "https://flickr.com/people/".$flickrjson["owner"];
$imageurl = 'https://farm' .$flickrjson["farm"]. '.static.flickr.com/' .$flickrjson["server"]. '/' .$flickrjson["id"]. '_' .$flickrjson["secret"]. '.jpg';
@@ -162,7 +164,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 class="relative"><a target="_blank" href="index.php?filename=<?php echo $todaytable['File_Name']; ?>"><img class="copyimage" width=25 src="images/copy.png"></a>
<td class="relative"><a target="_blank" href="index.php?filename=<?php echo $todaytable['File_Name']; ?>"><img class="copyimage" title="Open in new tab" width=25 src="images/copy.png"></a>
<div class="centered_image_container">
<?php if(!empty($config["FLICKR_API_KEY"]) && strlen($image[2]) > 0) { ?>
@@ -199,6 +201,10 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" ) {
</table>
<?php
if($iterations == 0) {
echo "<h3>No Detections For Today.</h3>";
}
// 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>
+25
View File
@@ -6,6 +6,10 @@ USER=$(awk -F: '/1000/ {print $1}' /etc/passwd)
HOME=$(awk -F: '/1000/ {print $6}' /etc/passwd)
my_dir=$HOME/BirdNET-Pi/scripts
# Sets proper permissions and ownership
sudo -E chown -R $USER:$USER $HOME/*
sudo chmod -R g+wr $HOME/*
if ! grep PRIVACY_THRESHOLD /etc/birdnet/birdnet.conf &>/dev/null;then
sudo -u$USER echo "PRIVACY_THRESHOLD=0" >> /etc/birdnet/birdnet.conf
git -C $HOME/BirdNET-Pi rm $my_dir/privacy_server.py
@@ -94,6 +98,27 @@ if [[ "$pytest_installation_status" = "not installed" ]];then
$HOME/BirdNET-Pi/birdnet/bin/pip3 install pytest==7.1.2 pytest-mock==3.7.0
fi
[ -L ~/BirdSongs/Extracted/weekly_report.php ] || ln -sf ~/BirdNET-Pi/scripts/weekly_report.php ~/BirdSongs/Extracted
if ! grep weekly_report /etc/crontab &>/dev/null;then
sed "s/\$USER/$USER/g" $HOME/BirdNET-Pi/templates/weekly_report.cron | sudo tee -a /etc/crontab
fi
if ! grep APPRISE_WEEKLY_REPORT /etc/birdnet/birdnet.conf &>/dev/null;then
sudo -u$USER echo "APPRISE_WEEKLY_REPORT=1" >> /etc/birdnet/birdnet.conf
fi
if ! grep SILENCE_UPDATE_INDICATOR /etc/birdnet/birdnet.conf &>/dev/null;then
sudo -u$USER echo "SILENCE_UPDATE_INDICATOR=0" >> /etc/birdnet/birdnet.conf
fi
if ! grep '\-\-browser.gatherUsageStats false' $HOME/BirdNET-Pi/templates/birdnet_stats.service &>/dev/null ;then
sudo -E sed -i "s|ExecStart=.*|ExecStart=$HOME/BirdNET-Pi/birdnet/bin/streamlit run $HOME/BirdNET-Pi/scripts/plotly_streamlit.py --browser.gatherUsageStats false --server.address localhost --server.baseUrlPath \"/stats\"|" $HOME/BirdNET-Pi/templates/birdnet_stats.service
sudo systemctl daemon-reload && restart_services.sh
fi
# Make IceCast2 a little more secure
sudo sed -i 's|<!-- <bind-address>.*|<bind-address>127.0.0.1</bind-address>|;s|<!-- <shoutcast-mount>.*|<shoutcast-mount>/stream</shoutcast-mount>|' /etc/icecast2/icecast.xml
sudo systemctl restart icecast2
sudo systemctl daemon-reload
restart_services.sh
+41 -8
View File
@@ -3,21 +3,29 @@ import os
import socket
import sqlite3
from datetime import datetime
import requests
userDir = os.path.expanduser('~')
APPRISE_CONFIG = userDir + '/BirdNET-Pi/apprise.txt'
DB_PATH = userDir + '/BirdNET-Pi/scripts/birds.db'
def notify(body, title):
def notify(body, title, attached=""):
apobj = apprise.Apprise()
config = apprise.AppriseConfig()
config.add(APPRISE_CONFIG)
apobj.add(config)
apobj.notify(
body=body,
title=title,
)
if attached != "":
apobj.notify(
body=body,
title=title,
attach=attached,
)
else:
apobj.notify(
body=body,
title=title,
)
def sendAppriseNotifications(species, confidence, path, date, time, week, latitude, longitude, cutoff, sens, overlap, settings_dict, db_path=DB_PATH):
@@ -37,6 +45,25 @@ def sendAppriseNotifications(species, confidence, path, date, time, week, latitu
websiteurl = "http://"+socket.gethostname()+".local"
listenurl = websiteurl+"?filename="+path
image_url = ""
flickr_images = {}
if len(settings_dict.get('FLICKR_API_KEY')) > 0 and "$flickrimage" in body:
if not comName in flickr_images:
try:
# TODO: Make this work with non-english comnames. Implement the "// convert sci name to English name" logic from overview.php here
url = 'https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key='+str(settings_dict.get('FLICKR_API_KEY'))+'&text='+str(comName)+' bird&sort=relevance&per_page=5&media=photos&format=json&license=2%2C3%2C4%2C5%2C6%2C9&nojsoncallback=1'
resp = requests.get(url=url)
data = resp.json()["photos"]["photo"][0]
image_url = 'https://farm'+str(data["farm"])+'.static.flickr.com/'+str(data["server"])+'/'+str(data["id"])+'_'+str(data["secret"])+'_n.jpg'
flickr_images[comName] = image_url
except Exception as e:
print("FLICKR API ERROR: "+str(e))
image_url = ""
else:
image_url = flickr_images[comName]
if settings_dict.get('APPRISE_NOTIFY_EACH_DETECTION') == "1":
notify_body = body.replace("$sciname", sciName)\
@@ -50,6 +77,7 @@ def sendAppriseNotifications(species, confidence, path, date, time, week, latitu
.replace("$longitude", longitude)\
.replace("$cutoff", cutoff)\
.replace("$sens", sens)\
.replace("$flickrimage", image_url if "{" in body else "")\
.replace("$overlap", overlap)
notify_title = title.replace("$sciname", sciName)\
.replace("$comname", comName)\
@@ -62,8 +90,9 @@ def sendAppriseNotifications(species, confidence, path, date, time, week, latitu
.replace("$longitude", longitude)\
.replace("$cutoff", cutoff)\
.replace("$sens", sens)\
.replace("$flickrimage", image_url if "{" in body else "")\
.replace("$overlap", overlap)
notify(notify_body, notify_title)
notify(notify_body, notify_title, image_url)
APPRISE_NOTIFICATION_NEW_SPECIES_DAILY_COUNT_LIMIT = 1 # Notifies the first N per day.
if settings_dict.get('APPRISE_NOTIFY_NEW_SPECIES_EACH_DAY') == "1":
@@ -90,6 +119,7 @@ def sendAppriseNotifications(species, confidence, path, date, time, week, latitu
.replace("$longitude", longitude)\
.replace("$cutoff", cutoff)\
.replace("$sens", sens)\
.replace("$flickrimage", image_url if "{" in body else "")\
.replace("$overlap", overlap)\
+ " (first time today)"
notify_title = title.replace("$sciname", sciName)\
@@ -103,9 +133,10 @@ def sendAppriseNotifications(species, confidence, path, date, time, week, latitu
.replace("$longitude", longitude)\
.replace("$cutoff", cutoff)\
.replace("$sens", sens)\
.replace("$flickrimage", image_url if "{" in body else "")\
.replace("$overlap", overlap)\
+ " (first time today)"
notify(notify_body, notify_title)
notify(notify_body, notify_title, image_url)
con.close()
except sqlite3.Error as e:
print(e)
@@ -135,6 +166,7 @@ def sendAppriseNotifications(species, confidence, path, date, time, week, latitu
.replace("$longitude", longitude)\
.replace("$cutoff", cutoff)\
.replace("$sens", sens)\
.replace("$flickrimage", image_url if "{" in body else "")\
.replace("$overlap", overlap)\
+ " (only seen " + str(int(numberDetections)) + " times in last 7d)"
notify_title = title.replace("$sciname", sciName)\
@@ -148,9 +180,10 @@ def sendAppriseNotifications(species, confidence, path, date, time, week, latitu
.replace("$longitude", longitude)\
.replace("$cutoff", cutoff)\
.replace("$sens", sens)\
.replace("$flickrimage", image_url if "{" in body else "")\
.replace("$overlap", overlap)\
+ " (only seen " + str(int(numberDetections)) + " times in last 7d)"
notify(notify_body, notify_title)
notify(notify_body, notify_title, image_url)
con.close()
except sqlite3.Error:
print("Database busy")
+259
View File
@@ -0,0 +1,259 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
$startdate = strtotime('last sunday') - (7*86400);
$enddate = strtotime('last sunday') - (1*86400);
$debug = false;
if(isset($_GET['ascii'])) {
$db = new SQLite3('./scripts/birds.db', SQLITE3_OPEN_CREATE | SQLITE3_OPEN_READWRITE);
if($db == False){
echo "Database is busy";
header("refresh: 0;");
}
$statement1 = $db->prepare('SELECT DISTINCT(Com_Name), COUNT(*) FROM detections WHERE Date BETWEEN "'.date("Y-m-d",$startdate).'" AND "'.date("Y-m-d",$enddate).'" GROUP By Com_Name ORDER BY COUNT(*) DESC');
if($statement1 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result1 = $statement1->execute();
$statement4 = $db->prepare('SELECT DISTINCT(Com_Name), COUNT(*) FROM detections WHERE Date BETWEEN "'.date("Y-m-d",$startdate).'" AND "'.date("Y-m-d",$enddate).'"');
if($statement4 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result4 = $statement4->execute();
$totalcount = $result4->fetchArray(SQLITE3_ASSOC)['COUNT(*)'];
$statement5 = $db->prepare('SELECT DISTINCT(Com_Name), COUNT(*) FROM detections WHERE Date BETWEEN "'.date("Y-m-d",$startdate- (7*86400)).'" AND "'.date("Y-m-d",$enddate- (7*86400)).'"');
if($statement5 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result5 = $statement5->execute();
$priortotalcount = $result5->fetchArray(SQLITE3_ASSOC)['COUNT(*)'];
$statement6 = $db->prepare('SELECT COUNT(DISTINCT(Com_Name)) FROM detections WHERE Date BETWEEN "'.date("Y-m-d",$startdate).'" AND "'.date("Y-m-d",$enddate).'"');
if($statement6 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result6 = $statement6->execute();
$totalspeciestally = $result6->fetchArray(SQLITE3_ASSOC)['COUNT(DISTINCT(Com_Name))'];
$statement7 = $db->prepare('SELECT COUNT(DISTINCT(Com_Name)) FROM detections WHERE Date BETWEEN "'.date("Y-m-d",$startdate- (7*86400)).'" AND "'.date("Y-m-d",$enddate- (7*86400)).'"');
if($statement7 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result7= $statement7->execute();
$priortotalspeciestally = $result7->fetchArray(SQLITE3_ASSOC)['COUNT(DISTINCT(Com_Name))'];
$percentagedifftotal = round((1 - $priortotalcount / $totalcount) * 100);
if($percentagedifftotal > 0) {
$percentagedifftotal = "<span style='color:green;font-size:small'>+".$percentagedifftotal."%</span>";
} else {
$percentagedifftotal = "<span style='color:red;font-size:small'>-".abs($percentagedifftotal)."%</span>";
}
$percentagedifftotaldistinctspecies = round((1 - $priortotalspeciestally / $totalspeciestally) * 100);
if($percentagedifftotaldistinctspecies > 0) {
$percentagedifftotaldistinctspecies = "<span style='color:green;font-size:small'>+".$percentagedifftotaldistinctspecies."%</span>";
} else {
$percentagedifftotaldistinctspecies = "<span style='color:red;font-size:small'>-".abs($percentagedifftotaldistinctspecies)."%</span>";
}
$detections = [];
$i = 0;
while($detection=$result1->fetchArray(SQLITE3_ASSOC))
{
$detections[$detection["Com_Name"]] = $detection["COUNT(*)"];
}
echo "# BirdNET-Pi: Week ".date('W', $enddate)." Report\n";
echo "Total Detections: <b>".$totalcount."</b> (".$percentagedifftotal.")<br>";
echo "Unique Species Detected: <b>".$totalspeciestally."</b> (".$percentagedifftotaldistinctspecies.")<br><br>";
echo "= <b>Top 10 Species</b> =<br>";
$i = 0;
foreach($detections as $com_name=>$scount)
{
$i++;
if($i <= 10) {
$statement2 = $db->prepare('SELECT COUNT(*) FROM detections WHERE Com_Name == "'.$com_name.'" AND Date BETWEEN "'.date("Y-m-d",$startdate - (7*86400)).'" AND "'.date("Y-m-d",$enddate - (7*86400)).'"');
if($statement2 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result2 = $statement2->execute();
$totalcount = $result2->fetchArray(SQLITE3_ASSOC);
$priorweekcount = $totalcount['COUNT(*)'];
$percentagediff = round((1 - $priorweekcount / $scount) * 100);
if($percentagediff > 0) {
$percentagediff = "<span style='color:green;font-size:small'>+".$percentagediff."%</span>";
} else {
$percentagediff = "<span style='color:red;font-size:small'>-".abs($percentagediff)."%</span>";
}
echo $com_name." - ".$scount." (".$percentagediff.")<br>";
}
}
echo "<br>= <b>Species Detected for the First Time</b> =<br>";
$newspeciescount=0;
foreach($detections as $com_name=>$scount)
{
$statement3 = $db->prepare('SELECT COUNT(*) FROM detections WHERE Com_Name == "'.$com_name.'" AND Date NOT BETWEEN "'.date("Y-m-d",$startdate).'" AND "'.date("Y-m-d",$enddate).'"');
if($statement3 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result3 = $statement3->execute();
$totalcount = $result3->fetchArray(SQLITE3_ASSOC);
$nonthisweekcount = $totalcount['COUNT(*)'];
if($nonthisweekcount == 0) {
$newspeciescount++;
echo $com_name." - ".$scount."<br>";
}
}
if($newspeciescount == 0) {
echo "No new species were seen this week.";
}
echo "<hr><small>* data from ".date('Y-m-d', $startdate)."".date('Y-m-d',$enddate).".</small><br>";
echo '<small>* percentages are calculated relative to week '.(date('W', $enddate) - 1).'.</small>';
die();
}
?>
<div class="brbanner"> <?php
echo "<h1>Week ".date('W', $enddate)." Report</h1>".date('F jS, Y',$startdate)." — ".date('F jS, Y',$enddate)."<br>";
?></div><?php
$db = new SQLite3('./scripts/birds.db', SQLITE3_OPEN_CREATE | SQLITE3_OPEN_READWRITE);
if($db == False){
echo "Database is busy";
header("refresh: 0;");
}
if($debug == false){
$statement1 = $db->prepare('SELECT DISTINCT(Com_Name), COUNT(*) FROM detections WHERE Date BETWEEN "'.date("Y-m-d",$startdate).'" AND "'.date("Y-m-d",$enddate).'" GROUP By Com_Name ORDER BY COUNT(*) DESC');
} else {
$statement1 = $db->prepare('SELECT DISTINCT(Com_Name), COUNT(*) FROM detections WHERE Date BETWEEN "'.date("Y-m-d",$startdate).'" AND "'.date("Y-m-d",$enddate).'" GROUP By Com_Name ORDER BY COUNT(*) ASC');
}
if($statement1 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result1 = $statement1->execute();
$detections = [];
$i = 0;
while($detection=$result1->fetchArray(SQLITE3_ASSOC))
{
if($debug == true){
if($i > 10) {
break;
}
}
$i++;
$detections[$detection["Com_Name"]] = $detection["COUNT(*)"];
}
?>
<br>
<?php // TODO: fix the box shadows, maybe make them a bit smaller on the tr ?>
<table align="center" style="box-shadow:unset"><tr><td style="background-color:#77c487">
<table>
<thead>
<tr>
<th><?php echo "Top 10 Species: <br>"; ?></th>
</tr>
</thead>
<tbody>
<?php
$i = 0;
foreach($detections as $com_name=>$scount)
{
$i++;
if($i <= 10) {
$statement2 = $db->prepare('SELECT COUNT(*) FROM detections WHERE Com_Name == "'.$com_name.'" AND Date BETWEEN "'.date("Y-m-d",$startdate - (7*86400)).'" AND "'.date("Y-m-d",$enddate - (7*86400)).'"');
if($statement2 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result2 = $statement2->execute();
$totalcount = $result2->fetchArray(SQLITE3_ASSOC);
$priorweekcount = $totalcount['COUNT(*)'];
$percentagediff = round((1 - $priorweekcount / $scount) * 100);
if($percentagediff > 0) {
$percentagediff = "<span style='color:green;font-size:small'>+".$percentagediff."%</span>";
} else {
$percentagediff = "<span style='color:red;font-size:small'>-".abs($percentagediff)."%</span>";
}
echo "<tr><td>".$com_name."<br><small style=\"font-size:small\">".$scount." (".$percentagediff.")</small><br></td></tr>";
}
}
?>
</tbody>
</table>
</td><td style="background-color:#77c487">
<table >
<thead>
<tr>
<th><?php echo "Species Detected for the First Time: <br>"; ?></th>
</tr>
</thead>
<tbody>
<?php
$newspeciescount=0;
foreach($detections as $com_name=>$scount)
{
$statement3 = $db->prepare('SELECT COUNT(*) FROM detections WHERE Com_Name == "'.$com_name.'" AND Date NOT BETWEEN "'.date("Y-m-d",$startdate).'" AND "'.date("Y-m-d",$enddate).'"');
if($statement3 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result3 = $statement3->execute();
$totalcount = $result3->fetchArray(SQLITE3_ASSOC);
$nonthisweekcount = $totalcount['COUNT(*)'];
if($nonthisweekcount == 0) {
$newspeciescount++;
echo "<tr><td>".$com_name."<br><small style=\"font-size:small\">".$scount."</small><br></td></tr>";
}
}
if($newspeciescount == 0) {
echo "<tr><td>No new species were seen this week.</td></tr>";
}
?>
</tbody>
</table>
</td></tr></table>
<br>
<div style="text-align:center">
<hr><small style="font-size:small">* percentages are calculated relative to week <?php echo date('W', $enddate) - 1; ?></small>
</div>
+9
View File
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
source /etc/birdnet/birdnet.conf
if [ ${APPRISE_WEEKLY_REPORT} == 1 ];then
NOTIFICATION=$(curl 'localhost/views.php?view=Weekly%20Report&ascii=true')
NOTIFICATION=${NOTIFICATION#*#}
firstLine=`echo "${NOTIFICATION}" | head -1`
NOTIFICATION=`echo "${NOTIFICATION}" | tail -n +2`
$HOME/BirdNET-Pi/birdnet/bin/apprise -vv -t "${firstLine}" -b "${NOTIFICATION}" --input-format=html --config=$HOME/BirdNET-Pi/apprise.txt
fi
+2
View File
@@ -0,0 +1,2 @@
#birdnet
0 1 * * 1 $USER /usr/local/bin/weekly_report.sh >/dev/null 2>&1