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"> <h1 align="center">
BirdNET-Pi BirdNET-Pi
</h1> </h1>
@@ -18,15 +20,21 @@ Check out birds from around the world
- [BirdWeather](https://app.birdweather.com)<br> - [BirdWeather](https://app.birdweather.com)<br>
- [Johannesburg, South Africa](https://joburg.birdnetpi.com)<br> - [Johannesburg, South Africa](https://joburg.birdnetpi.com)<br>
- [Öringe, Tyresö, Sweden](https://tyreso.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) - [Murrysville, Pennsylvania, United States](https://murrysvillepa.birdnetpi.com)
- [Berowra, New South Wales, Australia](https://berowra.birdnetpi.com) - [Berowra, New South Wales, Australia](https://berowra.birdnetpi.com)
- [Fairview, Tennessee, United States](https://fairviewtennessee.birdnetpi.com) - [Fairview, Tennessee, United States](https://fairviewtennessee.birdnetpi.com)
- [Dundas, Ontario, Canada](https://dundasontario.birdnetpi.com) - [Dundas, Ontario, Canada](https://dundasontario.birdnetpi.com)
- [Bungendore, New South Wales, Australia](https://bungendorensw.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) [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 . . . Currently listening in these countries . . . that I know of . . .
- The United States - The United States
@@ -53,6 +61,7 @@ Currently listening in these countries . . . that I know of . . .
- Ireland - Ireland
- Denmark - Denmark
- Costa Rica - Costa Rica
- The Philippines
## Features ## Features
* 24/7 recording and BirdNET-Lite analysis * 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 * A USB Microphone or Sound Card
## Installation ## 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). [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: 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 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 ## Access
The BirdNET-Pi can be accessed from any web browser on the same network: The BirdNET-Pi can be accessed from any web browser on the same network:
- http://birdnetpi.local - http://birdnetpi.local
@@ -113,7 +127,8 @@ Use the web interface and go to "Tools" > "System Controls" > "Update." If you e
``` ```
## Troubleshooting and Ideas ## 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 ## Sharing
Please join a Discussion!! and please join [BirdWeather!!](https://app.birdweather.com) 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/) - [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 - <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/) - [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: ### 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_NOTIFICATION_BODY="A \$sciname \$comname was just detected with a confidence of \$confidence"
APPRISE_NOTIFY_EACH_DETECTION=0 APPRISE_NOTIFY_EACH_DETECTION=0
APPRISE_NOTIFY_NEW_SPECIES=0 APPRISE_NOTIFY_NEW_SPECIES=0
APPRISE_WEEKLY_REPORT=1
#---------------------- Flickr Images API Configuration -----------------------# #---------------------- Flickr Images API Configuration -----------------------#
## If FLICKR_API_KEY is set, the web interface will try and display bird images ## If FLICKR_API_KEY is set, the web interface will try and display bird images
@@ -158,6 +159,12 @@ AUDIOFMT=mp3
DATABASE_LANG=en 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 ## These are just for debugging
LAST_RUN= LAST_RUN=
THIS_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 display:none
} }
</style> </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" /> <link rel="stylesheet" type="text/css" href="static/dialog-polyfill.css" />
<body> <body>
<div class="banner"> <div class="banner">
+22
View File
@@ -261,6 +261,11 @@ button:hover {
margin-right: auto; margin-right: auto;
} }
.views {
transition: opacity 0.3s;
-webkit-transition: opacity 0.3s;
}
.views .centered button { .views .centered button {
background-color: rgb(219, 255, 235); background-color: rgb(219, 255, 235);
padding: 12px; padding: 12px;
@@ -790,3 +795,20 @@ pre.bash {
width: 100%; width: 100%;
display: inline-block; 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); $home = trim($home);
if(!isset($_SESSION['behind'])) { if(!isset($_SESSION['behind'])) {
$fetch = shell_exec("sudo -u".$user." git -C ".$home."/BirdNET-Pi fetch 2>&1"); $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) {?> if(isset($_SESSION['behind'])&&intval($_SESSION['behind']) >= 99) {?>
<style> <style>
.updatenumber { .updatenumber {
@@ -16,8 +14,14 @@ if(!isset($_SESSION['behind'])) {
} }
</style> </style>
<?php }} <?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> <style>
body::-webkit-scrollbar { body::-webkit-scrollbar {
display:none display:none
@@ -49,13 +53,24 @@ body::-webkit-scrollbar {
<form action="" method="GET" id="views"> <form action="" method="GET" id="views">
<button type="submit" name="view" value="View Log" form="views">View Log</button> <button type="submit" name="view" value="View Log" form="views">View Log</button>
</form> </form>
<form action="" method="GET" id="views"> <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){ $updatediv = ' <div class="updatenumber">'.$_SESSION["behind"].'</div>'; } else { $updatediv = ""; } echo $updatediv; ?></button> <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> </form>
<button href="javascript:void(0);" class="icon" onclick="myFunction()"><img src="images/menu.png"></button> <button href="javascript:void(0);" class="icon" onclick="myFunction()"><img src="images/menu.png"></button>
</div> </div>
<script> <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']"); var topbuttons = document.querySelectorAll("button[form='views']");
if(window.location.search.substr(1) != '') { if(window.location.search.substr(1) != '') {
for (var i = 0; i < topbuttons.length; i++) { 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'] == "Overview"){include('overview.php');}
if($_GET['view'] == "Today's Detections"){include('todays_detections.php');} if($_GET['view'] == "Today's Detections"){include('todays_detections.php');}
if($_GET['view'] == "Species Stats"){include('stats.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'] == "Streamlit"){echo "<iframe src=\"/stats\"></iframe>";}
if($_GET['view'] == "Daily Charts"){include('history.php');} if($_GET['view'] == "Daily Charts"){include('history.php');}
if($_GET['view'] == "Tools"){ 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']; $caddypwd = $config['CADDY_PWD'];
if (!isset($_SERVER['PHP_AUTH_USER'])) { if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"'); 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 fi
branch=main 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 $HOME/BirdNET-Pi/scripts/install_birdnet.sh
if [ ${PIPESTATUS[0]} -eq 0 ];then if [ ${PIPESTATUS[0]} -eq 0 ];then
+18 -4
View File
@@ -1,7 +1,6 @@
<?php <?php
ini_set('display_errors', 1); ini_set('display_errors', 1);
ini_set('display_startup_errors', 1); error_reporting(E_ERROR);
error_reporting(E_ALL);
if (file_exists('./scripts/thisrun.txt')) { if (file_exists('./scripts/thisrun.txt')) {
$config = parse_ini_file('./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); $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"); $fh = fopen('/etc/birdnet/birdnet.conf', "w");
$fh2 = fopen("./scripts/thisrun.txt", "w"); $fh2 = fopen("./scripts/thisrun.txt", "w");
@@ -164,7 +173,10 @@ if(isset($_GET['submit'])) {
fwrite($fh2, $contents2); 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; $count = $count_labels;
?> ?>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
@@ -233,13 +245,15 @@ foreach($formats as $format){
<h3>BirdNET-Pi Password</h3> <h3>BirdNET-Pi Password</h3>
<p>This password will protect your "Tools" page and "Live Audio" stream.</p> <p>This password will protect your "Tools" page and "Live Audio" stream.</p>
<label for="caddy_pwd">Password: </label> <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> <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><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> <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> <label for="birdnetpi_url">BirdNET-Pi URL: </label>
<input style="width:40ch;" name="birdnetpi_url" type="url" value="<?php print($newconfig['BIRDNETPI_URL']);?>" /><br> <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> <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> <h3>BirdNET-Lite Settings</h3>
<label for="overlap">Overlap: </label> <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> <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_PARAM=""
BIRDWEATHER_ID_LOG="" BIRDWEATHER_ID_LOG=""
fi fi
echo $PYTHON_VIRTUAL_ENV "$DIR\analyze.py" \ echo $PYTHON_VIRTUAL_ENV "$DIR/analyze.py" \
--i "${1}/${i}" \ --i "${1}/${i}" \
--o "${1}/${i}.csv" \ --o "${1}/${i}.csv" \
--lat "${LATITUDE}" \ --lat $(echo "${LATITUDE}" | awk '{print int($1+0.5)}').XX \
--lon "${LONGITUDE}" \ --lon $(echo "${LONGITUDE}" | awk '{print int($1+0.5)}').XX \
--week "${WEEK}" \ --week "${WEEK}" \
--overlap "${OVERLAP}" \ --overlap "${OVERLAP}" \
--sensitivity "${SENSITIVITY}" \ --sensitivity "${SENSITIVITY}" \
@@ -168,7 +168,7 @@ until grep 5050 <(netstat -tulpn 2>&1) &> /dev/null 2>&1;do
sleep 1 sleep 1
done 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 find $RECS_DIR -maxdepth 1 -name '*wav' -type f -size 0 -delete
run_birdnet "${RECS_DIR}/StreamData" run_birdnet "${RECS_DIR}/StreamData"
fi fi
+5
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
source /etc/birdnet/birdnet.conf source /etc/birdnet/birdnet.conf
set -x
cd "${PROCESSED}" || exit 1 cd "${PROCESSED}" || exit 1
empties=($(find ${PROCESSED} -size 57c)) empties=($(find ${PROCESSED} -size 57c))
@@ -11,3 +12,7 @@ done
if [[ "$(find ${PROCESSED} | wc -l)" -ge 100 ]];then if [[ "$(find ${PROCESSED} | wc -l)" -ge 100 ]];then
ls -1t . | tail -n +100 | xargs -r rm -vv ls -1t . | tail -n +100 | xargs -r rm -vv
fi 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/stats.php ${EXTRACTED}
sudo -u ${USER} ln -fs $my_dir/todays_detections.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/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 $my_dir/homepage/images/favicon.ico ${EXTRACTED}
sudo -u ${USER} ln -fs ${HOME}/phpsysinfo ${EXTRACTED} sudo -u ${USER} ln -fs ${HOME}/phpsysinfo ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname $my_dir)/templates/phpsysinfo.ini ${HOME}/phpsysinfo/ 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 { } else {
$apprise_notify_new_species_each_day = 0; $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)) { if(isset($timezone)) {
shell_exec("sudo timedatectl set-timezone ".$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_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=.*/", "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_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("/FLICKR_API_KEY=.*/", "FLICKR_API_KEY=$flickr_api_key", $contents);
$contents = preg_replace("/DATABASE_LANG=.*/", "DATABASE_LANG=$language", $contents); $contents = preg_replace("/DATABASE_LANG=.*/", "DATABASE_LANG=$language", $contents);
$contents = preg_replace("/FLICKR_FILTER_EMAIL=.*/", "FLICKR_FILTER_EMAIL=$flickr_filter_email", $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_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=.*/", "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_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("/FLICKR_API_KEY=.*/", "FLICKR_API_KEY=$flickr_api_key", $contents2);
$contents2 = preg_replace("/DATABASE_LANG=.*/", "DATABASE_LANG=$language", $contents2); $contents2 = preg_replace("/DATABASE_LANG=.*/", "DATABASE_LANG=$language", $contents2);
$contents2 = preg_replace("/FLICKR_FILTER_EMAIL=.*/", "FLICKR_FILTER_EMAIL=$flickr_filter_email", $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; $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("\$sciname", $sciname, $title);
$title = str_replace("\$comname", $comname, $title); $title = str_replace("\$comname", $comname, $title);
$title = str_replace("\$confidence", $confidence, $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("\$cutoff", $cutoff, $title);
$title = str_replace("\$sens", $sens, $title); $title = str_replace("\$sens", $sens, $title);
$title = str_replace("\$overlap", $overlap, $title); $title = str_replace("\$overlap", $overlap, $title);
$title = str_replace("\$flickrimage", $exampleimage, $title);
$body = str_replace("\$sciname", $sciname, $body); $body = str_replace("\$sciname", $sciname, $body);
$body = str_replace("\$comname", $comname, $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("\$cutoff", $cutoff, $body);
$body = str_replace("\$sens", $sens, $body); $body = str_replace("\$sens", $sens, $body);
$body = str_replace("\$overlap", $overlap, $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(); die();
} }
@@ -320,6 +338,8 @@ https://discordapp.com/api/webhooks/{WebhookID}/{WebhookToken}
<dd>Sigmoid Sensitivity set in "Advanced Settings"</dd> <dd>Sigmoid Sensitivity set in "Advanced Settings"</dd>
<dt>$overlap</dt> <dt>$overlap</dt>
<dd>Overlap set in "Advanced Settings"</dd> <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> </dl>
<p>Use the variables defined above to customize your notification title and body.</p> <p>Use the variables defined above to customize your notification title and body.</p>
<label for="apprise_notification_title">Notification Title: </label> <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"; };?> > <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> <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"; };?> > <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"; };?> > <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> <button type="button" class="testbtn" onclick="sendTestNotification(this)">Send Test Notification</button><br>
<span id="testsuccessmsg"></span> <span id="testsuccessmsg"></span>
+112 -3
View File
@@ -1,7 +1,13 @@
<?php <?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL); 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'])){ if(isset($_GET['date'])){
$theDate = $_GET['date']; $theDate = $_GET['date'];
@@ -18,15 +24,118 @@ $statement1 = $db->prepare("SELECT COUNT(*) FROM detections
$result1 = $statement1->execute(); $result1 = $statement1->execute();
$totalcount = $result1->fetchArray(SQLITE3_ASSOC); $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> <head>
<style> <style>
#attribution-dialog p {
display:inline;
}
#attribution-dialog {
text-align: left;
}
</style> </style>
</head> </head>
<body> <body>
<div class="history centered"> <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"> <form action="" method="GET">
<input type="date" name="date" value="<?php echo $theDate;?>"> <input type="date" name="date" value="<?php echo $theDate;?>">
<button type="submit" name="view" value="Daily Charts">Submit Date</button> <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> <td><?php echo $totalcount['COUNT(*)'];?></td>
</tr> </tr>
</table> </table>
<br><br> <?php // <br><button type="button" onclick="showDialog()">Export as CSV for eBird</button><br><br> ?>
<?php <?php
if (file_exists('./Charts/'.$chart)) { if (file_exists('./Charts/'.$chart)) {
echo "<img src=\"/Charts/$chart?nocache=time()\" >"; 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_NOTIFICATION_BODY="A \$sciname \$comname was just detected with a confidence of \$confidence"
APPRISE_NOTIFY_EACH_DETECTION=0 APPRISE_NOTIFY_EACH_DETECTION=0
APPRISE_NOTIFY_NEW_SPECIES=0 APPRISE_NOTIFY_NEW_SPECIES=0
APPRISE_WEEKLY_REPORT=1
#---------------------- Flickr Images API Configuration -----------------------# #---------------------- Flickr Images API Configuration -----------------------#
## If FLICKR_API_KEY is set, the web interface will try and display bird images ## 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 is the language used for the bird species database
DATABASE_LANG=en 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 ## These are just for debugging
LAST_RUN= LAST_RUN=
THIS_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/stats.php ${EXTRACTED}
sudo -u ${USER} ln -fs $my_dir/scripts/todays_detections.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/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 $my_dir/homepage/images/favicon.ico ${EXTRACTED}
sudo -u ${USER} ln -fs ${HOME}/phpsysinfo ${EXTRACTED} sudo -u ${USER} ln -fs ${HOME}/phpsysinfo ${EXTRACTED}
sudo -u ${USER} ln -fs $my_dir/templates/phpsysinfo.ini ${HOME}/phpsysinfo/ sudo -u ${USER} ln -fs $my_dir/templates/phpsysinfo.ini ${HOME}/phpsysinfo/
@@ -274,7 +275,7 @@ Restart=on-failure
RestartSec=5 RestartSec=5
Type=simple Type=simple
User=${USER} 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] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
@@ -379,6 +380,8 @@ config_icecast() {
for i in "${passwords[@]}";do for i in "${passwords[@]}";do
sed -i "s/<${i}password>.*<\/${i}password>/<${i}password>${ICE_PWD}<\/${i}password>/g" /etc/icecast2/icecast.xml sed -i "s/<${i}password>.*<\/${i}password>/<${i}password>${ICE_PWD}<\/${i}password>/g" /etc/icecast2/icecast.xml
done 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 systemctl enable icecast2.service
} }
@@ -406,6 +409,10 @@ install_cleanup_cron() {
sed "s/\$USER/$USER/g" $my_dir/templates/cleanup.cron >> /etc/crontab 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_things() {
chown -R $USER:$USER $HOME/Bird* chown -R $USER:$USER $HOME/Bird*
} }
@@ -431,6 +438,7 @@ install_services() {
install_phpsysinfo install_phpsysinfo
install_livestream_service install_livestream_service
install_cleanup_cron install_cleanup_cron
install_weekly_cron
create_necessary_dirs create_necessary_dirs
generate_BirdDB generate_BirdDB
+13 -6
View File
@@ -1,6 +1,6 @@
<?php <?php
error_reporting(0); error_reporting(E_ERROR);
ini_set('display_errors', 0); ini_set('display_errors',1);
ini_set('session.gc_maxlifetime', 7200); ini_set('session.gc_maxlifetime', 7200);
session_set_cookie_params(7200); session_set_cookie_params(7200);
session_start(); session_start();
@@ -50,12 +50,15 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" && isse
$comname = preg_replace('/\'/', '', $comname); $comname = preg_replace('/\'/', '', $comname);
$filename = "/By_Date/".$mostrecent['Date']."/".$comname."/".$mostrecent['File_Name']; $filename = "/By_Date/".$mostrecent['Date']."/".$comname."/".$mostrecent['File_Name'];
$args = "&license=2%2C3%2C4%2C5%2C6%2C9&orientation=square,portrait"; $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\ // 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(file_exists($home."/BirdSongs/Extracted".$filename.".png")){
if($_GET['previous_detection_identifier'] == $filename) { die(); } if($_GET['previous_detection_identifier'] == $filename) { die(); }
if($_GET['only_name'] == "true") { echo $comname.",".$filename;die(); } if($_GET['only_name'] == "true") { echo $comname.",".$filename;die(); }
$iterations++;
if (!empty($config["FLICKR_API_KEY"])) { if (!empty($config["FLICKR_API_KEY"])) {
if(!empty($config["FLICKR_FILTER_EMAIL"])) { 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"]; $_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']; $args = "&user_id=".$_SESSION['FLICKR_FILTER_EMAIL'];
$comnameprefix = "";
} else { } else {
if(isset($_SESSION["FLICKR_FILTER_EMAIL"])) { if(isset($_SESSION["FLICKR_FILTER_EMAIL"])) {
unset($_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"]; $modaltext = "https://flickr.com/photos/".$flickrjson["owner"]."/".$flickrjson["id"];
$authorlink = "https://flickr.com/people/".$flickrjson["owner"]; $authorlink = "https://flickr.com/people/".$flickrjson["owner"];
$imageurl = 'https://farm' .$flickrjson["farm"]. '.static.flickr.com/' .$flickrjson["server"]. '/' .$flickrjson["id"]. '_' .$flickrjson["secret"]. '.jpg'; $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'; ?>"> <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> <h3>Most Recent Detection: <span style="font-weight: normal;"><?php echo $mostrecent['Date']." ".$mostrecent['Time'];?></span></h3>
<tr> <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;"> <div class="centered_image_container" style="margin-bottom: 0px !important;">
<?php if(!empty($config["FLICKR_API_KEY"]) && strlen($image[2]) > 0) { ?> <?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"> <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; </table> <?php break;
} }
} }
if($iterations == 0) {
echo "<h3>No Detections For Today.</h3>";
}
die(); die();
} }
@@ -284,7 +291,7 @@ function loadDetectionIfNewExists(previous_detection_identifier=undefined) {
const xhttp = new XMLHttpRequest(); const xhttp = new XMLHttpRequest();
xhttp.onload = function() { xhttp.onload = function() {
// if there's a new detection that needs to be updated to the page // 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; document.getElementById("most_recent_detection").innerHTML = this.responseText;
// only going to load left chart & 5 most recents if there's a new detection // only going to load left chart & 5 most recents if there's a new detection
@@ -309,7 +316,7 @@ function loadLeftChart() {
function refreshTopTen() { function refreshTopTen() {
const xhttp = new XMLHttpRequest(); const xhttp = new XMLHttpRequest();
xhttp.onload = function() { 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(); document.getElementById("chart").src = "/Charts/"+this.responseText+"?nocache="+Date.now();
} }
} }
+202 -143
View File
@@ -1,5 +1,6 @@
<?php <?php
error_reporting(E_ERROR); error_reporting(E_ERROR);
ini_set('display_errors',1);
$db = new SQLite3('./scripts/birds.db', SQLITE3_OPEN_CREATE | SQLITE3_OPEN_READWRITE); $db = new SQLite3('./scripts/birds.db', SQLITE3_OPEN_CREATE | SQLITE3_OPEN_READWRITE);
if($db == False){ 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 = shell_exec("awk -F: '/1000/{print $6}' /etc/passwd");
$home = trim($home); $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($_GET['excludefile'])) {
if(isset($_SERVER['PHP_AUTH_USER'])) { if(isset($_SERVER['PHP_AUTH_USER'])) {
$submittedpwd = $_SERVER['PHP_AUTH_PW']; $submittedpwd = $_SERVER['PHP_AUTH_PW'];
@@ -27,21 +63,21 @@ if(isset($_GET['excludefile'])) {
} }
if(isset($_GET['exclude_add'])) { if(isset($_GET['exclude_add'])) {
$myfile = fopen($home."/BirdNET-Pi/scripts/disk_check_exclude.txt", "a") or die("Unable to open file!"); $myfile = fopen($home."/BirdNET-Pi/scripts/disk_check_exclude.txt", "a") or die("Unable to open file!");
$txt = $_GET['excludefile']; $txt = $_GET['excludefile'];
fwrite($myfile, $txt."\n"); fwrite($myfile, $txt."\n");
fwrite($myfile, $txt.".png\n"); fwrite($myfile, $txt.".png\n");
fclose($myfile); fclose($myfile);
echo "OK"; echo "OK";
die(); die();
} else { } else {
$lines = file($home."/BirdNET-Pi/scripts/disk_check_exclude.txt"); $lines = file($home."/BirdNET-Pi/scripts/disk_check_exclude.txt");
$search = $_GET['excludefile']; $search = $_GET['excludefile'];
$result = ''; $result = '';
foreach($lines as $line) { foreach($lines as $line) {
if(stripos($line, $search) === false && stripos($line, $search.".png") === false) { if(stripos($line, $search) === false && stripos($line, $search.".png") === false) {
$result .= $line; $result .= $line;
} }
} }
file_put_contents($home."/BirdNET-Pi/scripts/disk_check_exclude.txt", $result); file_put_contents($home."/BirdNET-Pi/scripts/disk_check_exclude.txt", $result);
echo "OK"; echo "OK";
@@ -90,7 +126,7 @@ if(isset($_GET['bydate'])){
#By Species #By Species
} elseif(isset($_GET['byspecies'])) { } elseif(isset($_GET['byspecies'])) {
if(isset($_GET['sort']) && $_GET['sort'] == "occurrences") { 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 { } else {
$statement = $db->prepare('SELECT DISTINCT(Com_Name) FROM detections ORDER BY Com_Name ASC'); $statement = $db->prepare('SELECT DISTINCT(Com_Name) FROM detections ORDER BY Com_Name ASC');
} }
@@ -132,18 +168,37 @@ if(isset($_GET['bydate'])){
</head> </head>
<script> <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) { function toggleLock(filename, type, elem) {
const xhttp = new XMLHttpRequest(); const xhttp = new XMLHttpRequest();
xhttp.onload = function() { xhttp.onload = function() {
if(this.responseText == "OK"){ if(this.responseText == "OK"){
if(type == "add") { if(type == "add") {
elem.setAttribute("src","images/lock.svg"); elem.setAttribute("src","images/lock.svg");
elem.setAttribute("title", "This file is delete protected."); elem.setAttribute("title", "This file is excluded from being purged.");
elem.setAttribute("onclick", elem.getAttribute("onclick").replace("add","del")); elem.setAttribute("onclick", elem.getAttribute("onclick").replace("add","del"));
} else { } else {
elem.setAttribute("src","images/unlock.svg"); elem.setAttribute("src","images/unlock.svg");
elem.setAttribute("title", "This file is not delete protected."); elem.setAttribute("title", "This file is not excluded from being purged.");
elem.setAttribute("onclick", elem.getAttribute("onclick").replace("del","add")); 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)){ while($results=$result->fetchArray(SQLITE3_ASSOC)){
$date = $results['Date']; $date = $results['Date'];
if(realpath($home."/BirdSongs/Extracted/By_Date/".$date) !== false){ if(realpath($home."/BirdSongs/Extracted/By_Date/".$date) !== false){
echo "<td> echo "<td>
<button action=\"submit\" name=\"date\" value=\"$date\">$date</button></td></tr>";}} <button action=\"submit\" name=\"date\" value=\"$date\">".($date == date('Y-m-d') ? "Today" : $date)."</button></td></tr>";}}
#By Species #By Species
} elseif($view == "byspecies") { } elseif($view == "byspecies") {
while($results=$result->fetchArray(SQLITE3_ASSOC)){ while($results=$result->fetchArray(SQLITE3_ASSOC)){
$name = $results['Com_Name']; $name = $results['Com_Name'];
echo "<td> echo "<td>
<button action=\"submit\" name=\"species\" value=\"$name\">$name</button></td></tr>";} <button action=\"submit\" name=\"species\" value=\"$name\">$name</button></td></tr>";}
#Specific Date #Specific Date
} elseif($view == "date") { } elseif($view == "date") {
while($results=$result->fetchArray(SQLITE3_ASSOC)){ while($results=$result->fetchArray(SQLITE3_ASSOC)){
$name = $results['Com_Name']; $name = $results['Com_Name'];
if(realpath($home."/BirdSongs/Extracted/By_Date/".$date."/".str_replace(" ", "_",$name)) !== false){ if(realpath($home."/BirdSongs/Extracted/By_Date/".$date."/".str_replace(" ", "_",$name)) !== false){
echo "<td> echo "<td>
<button action=\"submit\" name=\"species\" value=\"$name\">$name</button></td></tr>"; <button action=\"submit\" name=\"species\" value=\"$name\">$name</button></td></tr>";
} }
} }
#Choose #Choose
} else { } else {
echo "<td> echo "<td>
<button action=\"submit\" name=\"byspecies\" value=\"byspecies\">By Species</button></td></tr> <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> echo "</form>
</tr> </tr>
</table>"; </table>";
} }
#Specific Species #Specific Species
@@ -227,142 +282,146 @@ if(isset($_GET['species'])){ ?>
<form action="" method="GET"> <form action="" method="GET">
<input type="hidden" name="view" value="Recordings"> <input type="hidden" name="view" value="Recordings">
<input type="hidden" name="species" value="<?php echo $_GET['species']; ?>"> <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"> <img width=35px src="images/sort_date.svg" title="Sort by date" alt="Sort by date">
</button> </button>
<button <?php if(isset($_GET['sort']) && $_GET['sort'] == "confidence"){ echo "style='background:#9fe29b !important;'"; }?> class="sortbutton" type="submit" name="sort" value="confidence"> <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"> <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> </form>
</div> </div>
<?php <?php
// add disk_check_exclude.txt lines into an array for grepping // add disk_check_exclude.txt lines into an array for grepping
$fp = @fopen($home."/BirdNET-Pi/scripts/disk_check_exclude.txt", 'r'); $fp = @fopen($home."/BirdNET-Pi/scripts/disk_check_exclude.txt", 'r');
if ($fp) { if ($fp) {
$disk_check_exclude_arr = explode("\n", fread($fp, filesize($home."/BirdNET-Pi/scripts/disk_check_exclude.txt"))); $disk_check_exclude_arr = explode("\n", fread($fp, filesize($home."/BirdNET-Pi/scripts/disk_check_exclude.txt")));
} }
$name = $_GET['species']; $name = $_GET['species'];
if(isset($_SESSION['date'])) { if(isset($_SESSION['date'])) {
$date = $_SESSION['date']; $date = $_SESSION['date'];
if(isset($_GET['sort']) && $_GET['sort'] == "confidence") { if(isset($_GET['sort']) && $_GET['sort'] == "confidence") {
$statement2 = $db->prepare("SELECT * FROM detections where Com_Name == \"$name\" AND Date == \"$date\" ORDER BY Confidence DESC"); $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");
}
} else { } else {
if(isset($_GET['sort']) && $_GET['sort'] == "confidence") { $statement2 = $db->prepare("SELECT * FROM detections where Com_Name == \"$name\" AND Date == \"$date\" ORDER BY Time DESC");
$statement2 = $db->prepare("SELECT * FROM detections where Com_Name == \"$name\" ORDER BY Confidence DESC"); }
} else { } else {
$statement2 = $db->prepare("SELECT * FROM detections where Com_Name == \"$name\" ORDER BY Date DESC, Time DESC"); 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(!in_array($filename_formatted, $disk_check_exclude_arr) && isset($_GET['only_excluded'])) {
if($statement2 == False){ continue;
echo "Database is busy"; }
header("refresh: 0;"); $iter++;
}
$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($config["FULL_DISK"] == "purge") {
if(!file_exists($home."/BirdSongs/Extracted/".$filename)) { if(!in_array($filename_formatted, $disk_check_exclude_arr)) {
continue; $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") { echo "<tr>
if(!in_array($filename_formatted, $disk_check_exclude_arr)) { <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>
$imageicon = "images/unlock.svg"; <video onplay='setLiveStreamVolume(0)' onended='setLiveStreamVolume(1)' onpause='setLiveStreamVolume(1)' controls poster=\"$filename.png\" preload=\"none\" title=\"$filename\"><source src=\"$filename\"></video></td>
$title = "This file is not delete protected."; </tr>";
$type = "add"; } else {
} else { echo "<tr>
$imageicon = "images/lock.svg"; <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>
$title = "This file is delete protected."; <video onplay='setLiveStreamVolume(0)' onended='setLiveStreamVolume(1)' onpause='setLiveStreamVolume(1)' controls poster=\"$filename.png\" preload=\"none\" title=\"$filename\"><source src=\"$filename\"></video></td>
$type = "del"; </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> if($config["FULL_DISK"] == "purge") {
<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."\"> if(!in_array($filename_formatted, $disk_check_exclude_arr)) {
<a href=\"$filename\"><img src=\"$filename.png\"></a> $imageicon = "images/unlock.svg";
</td> $title = "This file is not excluded from being purged.";
</tr>"; $type = "add";
} else { } else {
echo "<tr> $imageicon = "images/lock.svg";
<td class='relative'>$date $time<br>$confidence<br> $title = "This file is excluded from being purged.";
<a href=\"$filename\"><img src=\"$filename.png\"></a> $type = "del";
</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>";} 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>
if(isset($_GET['filename'])){ <video onplay='setLiveStreamVolume(0)' onended='setLiveStreamVolume(1)' onpause='setLiveStreamVolume(1)' controls poster=\"$filename.png\" preload=\"none\" title=\"$filename\"><source src=\"$filename\"></video></td>
$name = $_GET['filename']; </tr>";
$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";
} else { } else {
$imageicon = "images/lock.svg"; echo "<tr>
$title = "This file is delete protected."; <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>
$type = "del"; <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> }echo "</table>";}?>
<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>";}?>
</div> </div>
</html> </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_server.service
sudo systemctl stop birdnet_recording.service sudo systemctl stop birdnet_recording.service
services=(icecast2.service services=(chart_viewer.service
spectrogram_viewer.service
icecast2.service
extraction.service extraction.service
birdnet_recording.service birdnet_recording.service
birdnet_log.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> <html>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<br> <br>
<br>
<div class="servicecontrols"> <div class="servicecontrols">
<form action="" method="GET"> <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 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 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 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> <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>
<form action="" method="GET"> <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 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 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 disable --now web_terminal.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now web_terminal.service">Enable</button> <button type="submit" name="submit" value="sudo systemctl enable --now web_terminal.service">Enable</button>
</form> </form>
<form action="" method="GET"> <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 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 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 disable --now birdnet_log.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now birdnet_log.service">Enable</button> <button type="submit" name="submit" value="sudo systemctl enable --now birdnet_log.service">Enable</button>
</form> </form>
<form action="" method="GET"> <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 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 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 disable --now extraction.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now extraction.service">Enable</button> <button type="submit" name="submit" value="sudo systemctl enable --now extraction.service">Enable</button>
</form> </form>
<form action="" method="GET"> <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 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 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 disable --now birdnet_server.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now birdnet_server.service">Enable</button> <button type="submit" name="submit" value="sudo systemctl enable --now birdnet_server.service">Enable</button>
</form> </form>
<form action="" method="GET"> <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 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 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 disable --now birdnet_analysis.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now birdnet_analysis.service">Enable</button> <button type="submit" name="submit" value="sudo systemctl enable --now birdnet_analysis.service">Enable</button>
</form> </form>
<form action="" method="GET"> <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 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 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 disable --now birdnet_stats.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now birdnet_stats.service">Enable</button> <button type="submit" name="submit" value="sudo systemctl enable --now birdnet_stats.service">Enable</button>
</form> </form>
<form action="" method="GET"> <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 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 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 disable --now birdnet_recording.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now birdnet_recording.service">Enable</button> <button type="submit" name="submit" value="sudo systemctl enable --now birdnet_recording.service">Enable</button>
</form> </form>
<form action="" method="GET"> <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 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 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 disable --now chart_viewer.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now chart_viewer.service">Enable</button> <button type="submit" name="submit" value="sudo systemctl enable --now chart_viewer.service">Enable</button>
</form> </form>
<form action="" method="GET"> <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 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 restart spectrogram_viewer.service">Restart</button>
<button type="submit" name="submit" value="sudo systemctl disable --now spectrogram_viewer.service">Disable</button> <button type="submit" name="submit" value="sudo systemctl disable --now spectrogram_viewer.service">Disable</button>
@@ -75,8 +95,8 @@
</form> </form>
<form action="" method="GET"> <form action="" method="GET">
<button type="submit" name="submit" value="stop_core_services.sh">Stop Core Services</button> <button type="submit" name="submit" value="stop_core_services.sh">Stop Core Services</button>
</form> </form>
<form action="" method="GET"> <form action="" method="GET">
<button type="submit" name="submit" value="restart_services.sh">Restart Core Services</button> <button type="submit" name="submit" value="restart_services.sh">Restart Core Services</button>
</form> </form>
</div> </div>
+3 -1
View File
@@ -1,4 +1,6 @@
<?php <?php
error_reporting(E_ERROR);
ini_set('display_errors',1);
if(isset($_GET['ajax_csv'])) { if(isset($_GET['ajax_csv'])) {
if (file_exists('./scripts/thisrun.txt')) { if (file_exists('./scripts/thisrun.txt')) {
@@ -66,7 +68,7 @@ window.onload = function(){
}; };
// if user agent includes iPhone or Mac use legacy mode // 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.getElementById("spectrogramimage").style.display="";
document.body.querySelector('canvas').remove(); document.body.querySelector('canvas').remove();
document.getElementById('player').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']; $filename = "/By_Date/".$date."/".$comname."/".$results['File_Name'];
echo str_pad("<h3>$species</h3> echo str_pad("<h3>$species</h3>
<table><tr> <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>Occurrences: </b>$count<br>
<b>Max Confidence: </b>$maxconf<br> <b>Max Confidence: </b>$maxconf<br>
<b>Best Recording: </b>$date $time<br> <b>Best Recording: </b>$date $time<br>
@@ -223,7 +223,7 @@ array_push($excludelines, $results['Date']."/".$comname."/".$results['File_Name'
<tr> <tr>
<form action="" method="GET"> <form action="" method="GET">
<input type="hidden" name="sort" value="<?php if(isset($_GET['sort'])){echo $_GET['sort'];}?>"> <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> <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>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> <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 = shell_exec("awk -F: '/1000/{print $6}' /etc/passwd");
$home = trim($home); $home = trim($home);
$fetch = shell_exec("sudo -u".$user." git -C ".$home."/BirdNET-Pi fetch 2>&1"); $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> ?><html>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<br> <br>
<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"> <div class="systemcontrols">
<form action="" method="GET"> <form action="" method="GET">
<button type="submit" name="submit" value="sudo reboot" onclick="return confirm('Are you sure you want to reboot?')">Reboot</button> <button type="submit" name="submit" value="sudo reboot" onclick="return confirm('Are you sure you want to reboot?')">Reboot</button>
</form> </form>
<form action="" method="GET"> <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>
<form action="" method="GET"> <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> <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); ini_set('session.gc_maxlifetime', 7200);
session_set_cookie_params(7200); session_set_cookie_params(7200);
session_start(); session_start();
error_reporting(0); error_reporting(E_ERROR);
ini_set('display_errors', 0); ini_set('display_errors',1);
$db = new SQLite3('./scripts/birds.db', SQLITE3_OPEN_CREATE | SQLITE3_OPEN_READWRITE); $db = new SQLite3('./scripts/birds.db', SQLITE3_OPEN_CREATE | SQLITE3_OPEN_READWRITE);
if($db == False){ 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']; $filename = "/By_Date/".date('Y-m-d')."/".$comname."/".$todaytable['File_Name'];
$sciname = preg_replace('/ /', '_', $todaytable['Sci_Name']); $sciname = preg_replace('/ /', '_', $todaytable['Sci_Name']);
$args = "&license=2%2C3%2C4%2C5%2C6%2C9&orientation=square,portrait"; $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']))) { 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"]; $_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']; $args = "&user_id=".$_SESSION['FLICKR_FILTER_EMAIL'];
$comnameprefix = "";
} else { } else {
if(isset($_SESSION["FLICKR_FILTER_EMAIL"])) { if(isset($_SESSION["FLICKR_FILTER_EMAIL"])) {
unset($_SESSION["FLICKR_FILTER_EMAIL"]); unset($_SESSION["FLICKR_FILTER_EMAIL"]);
@@ -151,7 +153,7 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" ) {
break; 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"]; $modaltext = "https://flickr.com/photos/".$flickrjson["owner"]."/".$flickrjson["id"];
$authorlink = "https://flickr.com/people/".$flickrjson["owner"]; $authorlink = "https://flickr.com/people/".$flickrjson["owner"];
$imageurl = 'https://farm' .$flickrjson["farm"]. '.static.flickr.com/' .$flickrjson["server"]. '/' .$flickrjson["id"]. '_' .$flickrjson["secret"]. '.jpg'; $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'])){ ?> <?php if(isset($_GET['display_limit']) && is_numeric($_GET['display_limit'])){ ?>
<tr class="relative" id="<?php echo $iterations; ?>"> <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"> <div class="centered_image_container">
<?php if(!empty($config["FLICKR_API_KEY"]) && strlen($image[2]) > 0) { ?> <?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> </table>
<?php <?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 // 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'])) { ?> if($iterations >= 40 && isset($_GET['display_limit']) && is_numeric($_GET['display_limit'])) { ?>
<center> <center>
+25
View File
@@ -6,6 +6,10 @@ USER=$(awk -F: '/1000/ {print $1}' /etc/passwd)
HOME=$(awk -F: '/1000/ {print $6}' /etc/passwd) HOME=$(awk -F: '/1000/ {print $6}' /etc/passwd)
my_dir=$HOME/BirdNET-Pi/scripts 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 if ! grep PRIVACY_THRESHOLD /etc/birdnet/birdnet.conf &>/dev/null;then
sudo -u$USER echo "PRIVACY_THRESHOLD=0" >> /etc/birdnet/birdnet.conf sudo -u$USER echo "PRIVACY_THRESHOLD=0" >> /etc/birdnet/birdnet.conf
git -C $HOME/BirdNET-Pi rm $my_dir/privacy_server.py 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 $HOME/BirdNET-Pi/birdnet/bin/pip3 install pytest==7.1.2 pytest-mock==3.7.0
fi 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 sudo systemctl daemon-reload
restart_services.sh restart_services.sh
+41 -8
View File
@@ -3,21 +3,29 @@ import os
import socket import socket
import sqlite3 import sqlite3
from datetime import datetime from datetime import datetime
import requests
userDir = os.path.expanduser('~') userDir = os.path.expanduser('~')
APPRISE_CONFIG = userDir + '/BirdNET-Pi/apprise.txt' APPRISE_CONFIG = userDir + '/BirdNET-Pi/apprise.txt'
DB_PATH = userDir + '/BirdNET-Pi/scripts/birds.db' DB_PATH = userDir + '/BirdNET-Pi/scripts/birds.db'
def notify(body, title): def notify(body, title, attached=""):
apobj = apprise.Apprise() apobj = apprise.Apprise()
config = apprise.AppriseConfig() config = apprise.AppriseConfig()
config.add(APPRISE_CONFIG) config.add(APPRISE_CONFIG)
apobj.add(config) apobj.add(config)
apobj.notify( if attached != "":
body=body, apobj.notify(
title=title, 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): 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" websiteurl = "http://"+socket.gethostname()+".local"
listenurl = websiteurl+"?filename="+path 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": if settings_dict.get('APPRISE_NOTIFY_EACH_DETECTION') == "1":
notify_body = body.replace("$sciname", sciName)\ notify_body = body.replace("$sciname", sciName)\
@@ -50,6 +77,7 @@ def sendAppriseNotifications(species, confidence, path, date, time, week, latitu
.replace("$longitude", longitude)\ .replace("$longitude", longitude)\
.replace("$cutoff", cutoff)\ .replace("$cutoff", cutoff)\
.replace("$sens", sens)\ .replace("$sens", sens)\
.replace("$flickrimage", image_url if "{" in body else "")\
.replace("$overlap", overlap) .replace("$overlap", overlap)
notify_title = title.replace("$sciname", sciName)\ notify_title = title.replace("$sciname", sciName)\
.replace("$comname", comName)\ .replace("$comname", comName)\
@@ -62,8 +90,9 @@ def sendAppriseNotifications(species, confidence, path, date, time, week, latitu
.replace("$longitude", longitude)\ .replace("$longitude", longitude)\
.replace("$cutoff", cutoff)\ .replace("$cutoff", cutoff)\
.replace("$sens", sens)\ .replace("$sens", sens)\
.replace("$flickrimage", image_url if "{" in body else "")\
.replace("$overlap", overlap) .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. 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": 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("$longitude", longitude)\
.replace("$cutoff", cutoff)\ .replace("$cutoff", cutoff)\
.replace("$sens", sens)\ .replace("$sens", sens)\
.replace("$flickrimage", image_url if "{" in body else "")\
.replace("$overlap", overlap)\ .replace("$overlap", overlap)\
+ " (first time today)" + " (first time today)"
notify_title = title.replace("$sciname", sciName)\ notify_title = title.replace("$sciname", sciName)\
@@ -103,9 +133,10 @@ def sendAppriseNotifications(species, confidence, path, date, time, week, latitu
.replace("$longitude", longitude)\ .replace("$longitude", longitude)\
.replace("$cutoff", cutoff)\ .replace("$cutoff", cutoff)\
.replace("$sens", sens)\ .replace("$sens", sens)\
.replace("$flickrimage", image_url if "{" in body else "")\
.replace("$overlap", overlap)\ .replace("$overlap", overlap)\
+ " (first time today)" + " (first time today)"
notify(notify_body, notify_title) notify(notify_body, notify_title, image_url)
con.close() con.close()
except sqlite3.Error as e: except sqlite3.Error as e:
print(e) print(e)
@@ -135,6 +166,7 @@ def sendAppriseNotifications(species, confidence, path, date, time, week, latitu
.replace("$longitude", longitude)\ .replace("$longitude", longitude)\
.replace("$cutoff", cutoff)\ .replace("$cutoff", cutoff)\
.replace("$sens", sens)\ .replace("$sens", sens)\
.replace("$flickrimage", image_url if "{" in body else "")\
.replace("$overlap", overlap)\ .replace("$overlap", overlap)\
+ " (only seen " + str(int(numberDetections)) + " times in last 7d)" + " (only seen " + str(int(numberDetections)) + " times in last 7d)"
notify_title = title.replace("$sciname", sciName)\ notify_title = title.replace("$sciname", sciName)\
@@ -148,9 +180,10 @@ def sendAppriseNotifications(species, confidence, path, date, time, week, latitu
.replace("$longitude", longitude)\ .replace("$longitude", longitude)\
.replace("$cutoff", cutoff)\ .replace("$cutoff", cutoff)\
.replace("$sens", sens)\ .replace("$sens", sens)\
.replace("$flickrimage", image_url if "{" in body else "")\
.replace("$overlap", overlap)\ .replace("$overlap", overlap)\
+ " (only seen " + str(int(numberDetections)) + " times in last 7d)" + " (only seen " + str(int(numberDetections)) + " times in last 7d)"
notify(notify_body, notify_title) notify(notify_body, notify_title, image_url)
con.close() con.close()
except sqlite3.Error: except sqlite3.Error:
print("Database busy") 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