@@ -22,6 +22,7 @@ Check out birds from around the world
|
||||
- [Norman, Oklahoma, United States](https://normanok.birdnetpi.com)<br>
|
||||
- [Nijmegen, the Netherlands](https://nijmegen.birdnetpi.com)<br>
|
||||
- [Murrysville, Pennsylvania, United States](https://murrysvillepa.birdnetpi.com/)
|
||||
- [Berowra, New South Wales, Australia](https://berowra.birdnetpi.com/)
|
||||
|
||||
[Share your installation!!](https://github.com/mcguirepr89/BirdNET-Pi/wiki/Sharing-Your-BirdNET-Pi)
|
||||
|
||||
|
||||
@@ -62,6 +62,10 @@ APPRISE_NOTIFICATION_TITLE="New BirdNET-Pi Detection"
|
||||
APPRISE_NOTIFICATION_BODY="A \$sciname \$comname was just detected with a confidence of \$confidence"
|
||||
APPRISE_NOTIFY_EACH_DETECTION=false
|
||||
|
||||
#---------------------- Flickr Images API Configuration -----------------------#
|
||||
|
||||
FLICKR_API_KEY=
|
||||
|
||||
################################################################################
|
||||
#-------------------------------- Defaults ----------------------------------#
|
||||
################################################################################
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?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 330 330" style="enable-background:new 0 0 330 330;" xml:space="preserve">
|
||||
<g id="XMLID_504_">
|
||||
<path id="XMLID_505_" d="M65,330h200c8.284,0,15-6.716,15-15V145c0-8.284-6.716-15-15-15h-15V85c0-46.869-38.131-85-85-85
|
||||
S80,38.131,80,85v45H65c-8.284,0-15,6.716-15,15v170C50,323.284,56.716,330,65,330z M207.481,219.356l-42.5,42.5
|
||||
c-2.929,2.929-6.768,4.394-10.606,4.394s-7.678-1.465-10.606-4.394l-21.25-21.25c-5.858-5.858-5.858-15.354,0-21.213
|
||||
c5.857-5.858,15.355-5.858,21.213,0l10.644,10.643l31.894-31.893c5.857-5.858,15.355-5.858,21.213,0
|
||||
C213.34,204.002,213.34,213.498,207.481,219.356z M110,85c0-30.327,24.673-55,55-55s55,24.673,55,55v45H110V85z"/>
|
||||
</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.1 KiB |
@@ -0,0 +1,43 @@
|
||||
<?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 466.008 466.008" style="enable-background:new 0 0 466.008 466.008;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M233.004,0C104.224,0,0,104.212,0,233.004c0,128.781,104.212,233.004,233.004,233.004
|
||||
c128.782,0,233.004-104.212,233.004-233.004C466.008,104.222,361.796,0,233.004,0z M244.484,242.659l-63.512,75.511
|
||||
c-5.333,6.34-14.797,7.156-21.135,1.824c-6.34-5.333-7.157-14.795-1.824-21.135l59.991-71.325V58.028c0-8.284,6.716-15,15-15
|
||||
s15,6.716,15,15v174.976h0C248.004,236.536,246.757,239.956,244.484,242.659z"/>
|
||||
</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: 946 B |
@@ -0,0 +1,40 @@
|
||||
<?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 330 330" style="enable-background:new 0 0 330 330;" xml:space="preserve">
|
||||
<g id="XMLID_516_">
|
||||
<path id="XMLID_517_" d="M15,160c8.284,0,15-6.716,15-15V85c0-30.327,24.673-55,55-55c30.327,0,55,24.673,55,55v45h-25
|
||||
c-8.284,0-15,6.716-15,15v170c0,8.284,6.716,15,15,15h200c8.284,0,15-6.716,15-15V145c0-8.284-6.716-15-15-15H170V85
|
||||
c0-46.869-38.131-85-85-85S0,38.131,0,85v60C0,153.284,6.716,160,15,160z"/>
|
||||
</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: 829 B |
Binary file not shown.
+69
-11
@@ -1,6 +1,10 @@
|
||||
@font-face {
|
||||
font-family: 'Roboto Flex' ;
|
||||
src: url('static/RobotoFlex-Regular.ttf') format('truetype');
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: 'Arial', 'Gill Sans', 'Gill Sans MT',
|
||||
' Calibri', 'Trebuchet MS', 'sans-serif';
|
||||
font-family: 'Roboto Flex', sans-serif;
|
||||
box-sizing: border-box;
|
||||
font-size: medium;
|
||||
}
|
||||
@@ -37,6 +41,9 @@ table {
|
||||
border-spacing: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
box-shadow: 0px 0px 17px 1px rgba(0, 0, 0, 0.10);
|
||||
border-radius:3px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
td {
|
||||
@@ -44,7 +51,6 @@ td {
|
||||
vertical-align: top;
|
||||
background-color: rgb(219, 255, 235);
|
||||
font-weight: lighter;
|
||||
border: 2px solid black;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -132,6 +138,8 @@ button:hover {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 15px;
|
||||
box-shadow: 0px 0px 17px 1px rgba(0, 0, 0, 0.10); !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.topnav form {
|
||||
@@ -178,7 +186,6 @@ button:hover {
|
||||
|
||||
.overview th {
|
||||
background-color: rgb(219, 255, 235);
|
||||
border: 2px solid black;
|
||||
text-align: center;
|
||||
padding: 12px;
|
||||
}
|
||||
@@ -238,7 +245,6 @@ button:hover {
|
||||
}
|
||||
|
||||
.views .centered button {
|
||||
border: 2px solid black;
|
||||
background-color: rgb(219, 255, 235);
|
||||
padding: 12px;
|
||||
}
|
||||
@@ -257,7 +263,6 @@ button:hover {
|
||||
}
|
||||
|
||||
.settings button {
|
||||
border: 2px solid black;
|
||||
background-color: rgb(219, 255, 235);
|
||||
padding: 12px;
|
||||
}
|
||||
@@ -284,7 +289,7 @@ button:hover {
|
||||
.customlabels button,.customlabels2 button {
|
||||
padding: 12px;
|
||||
background-color: rgb(219, 255, 235);
|
||||
border: 2px solid black;
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -339,7 +344,7 @@ button:hover {
|
||||
}
|
||||
|
||||
.systemcontrols button,.servicecontrols button {
|
||||
border: 2px solid black;
|
||||
|
||||
background-color: rgb(219, 255, 235);
|
||||
padding: 12px;
|
||||
width: 50%;
|
||||
@@ -541,7 +546,7 @@ button:hover {
|
||||
font-size:x-large;
|
||||
background:#dbffeb;
|
||||
padding:5px;
|
||||
border: 2px solid black;
|
||||
|
||||
}
|
||||
button.legacyview {
|
||||
color:gray;
|
||||
@@ -552,12 +557,65 @@ button.legacyview {
|
||||
font-size:small;
|
||||
background:#dbffeb;
|
||||
padding:5px;
|
||||
border: 2px solid black;
|
||||
|
||||
}
|
||||
button.loadmore {
|
||||
margin-top:10px;
|
||||
font-size:x-large;
|
||||
background:#dbffeb;
|
||||
padding:10px;
|
||||
border: 2px solid black;
|
||||
|
||||
}
|
||||
|
||||
tr {
|
||||
background-color:#9fe29b;
|
||||
}
|
||||
.history.centered form {
|
||||
display:flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.history.centered input {
|
||||
margin-right:5px;
|
||||
border:0px;
|
||||
}
|
||||
.centered form#views button {
|
||||
box-shadow: 0px 0px 5px 2px rgba(0, 0, 0, 0.10);
|
||||
margin:2px;
|
||||
}
|
||||
input {
|
||||
box-shadow: 0px 0px 17px 1px rgba(0, 0, 0, 0.10);
|
||||
}
|
||||
dialog {
|
||||
border:none;
|
||||
}
|
||||
dialog::backdrop {
|
||||
background: repeating-linear-gradient(
|
||||
30deg,
|
||||
rgba(24, 194, 236, 0.2),
|
||||
rgba(24, 194, 236, 0.2) 1px,
|
||||
rgba(24, 194, 236, 0.3) 1px,
|
||||
rgba(24, 194, 236, 0.3) 20px
|
||||
);
|
||||
backdrop-filter: blur(1px)
|
||||
}
|
||||
.centered_image_container {
|
||||
font-size:19px !important;
|
||||
display:inline-block;
|
||||
position:relative;
|
||||
margin-bottom:3px;
|
||||
}
|
||||
.centered_image_container img.img1 {
|
||||
cursor:pointer;
|
||||
height:95%;
|
||||
position:absolute;
|
||||
right:110%;
|
||||
top:0px;
|
||||
border-radius: 5px;
|
||||
width:unset;
|
||||
}
|
||||
.centered_image_container * {
|
||||
font-size:19px !important;
|
||||
}
|
||||
.centered_image_container form {
|
||||
margin-bottom:0px;
|
||||
}
|
||||
+28
-6
@@ -47,6 +47,17 @@ body::-webkit-scrollbar {
|
||||
} else {
|
||||
topbuttons[0].classList.add("button-hover");
|
||||
}
|
||||
function copyOutput(elem) {
|
||||
elem.innerHTML = 'Copied!';
|
||||
const copyText = document.getElementsByTagName("pre")[0].textContent;
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.style.position = 'absolute';
|
||||
textArea.style.left = '-100%';
|
||||
textArea.textContent = copyText;
|
||||
document.body.append(textArea);
|
||||
textArea.select();
|
||||
document.execCommand("copy");
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="views">
|
||||
@@ -80,7 +91,7 @@ if(isset($_GET['view'])){
|
||||
if($submittedpwd == $caddypwd && $submitteduser == 'birdnet'){
|
||||
$url = $_SERVER['SERVER_NAME']."/scripts/adminer.php";
|
||||
echo "<div class=\"centered\">
|
||||
<form action=\"\" method=\"GET\" id=\"views\">
|
||||
<form action=\"\" method=\"GET\" id=\"views\">
|
||||
<button type=\"submit\" name=\"view\" value=\"Settings\" form=\"views\">Settings</button>
|
||||
<button type=\"submit\" name=\"view\" value=\"System Info\" form=\"views\">System Info</button>
|
||||
<button type=\"submit\" name=\"view\" value=\"System Controls\" form=\"views\">System Controls</button>
|
||||
@@ -90,8 +101,8 @@ if(isset($_GET['view'])){
|
||||
<button type=\"submit\" name=\"view\" value=\"Webterm\" form=\"views\">Web Terminal</button>
|
||||
<button type=\"submit\" name=\"view\" value=\"Included\" form=\"views\">Custom Species List</button>
|
||||
<button type=\"submit\" name=\"view\" value=\"Excluded\" form=\"views\">Excluded Species List</button>
|
||||
</form>
|
||||
</div>";
|
||||
</form>
|
||||
</div>";
|
||||
} else {
|
||||
header('WWW-Authenticate: Basic realm="My Realm"');
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
@@ -175,7 +186,7 @@ if(isset($_GET['view'])){
|
||||
$submitteduser = $_SERVER['PHP_AUTH_USER'];
|
||||
if($submittedpwd == $caddypwd && $submitteduser == 'birdnet'){
|
||||
#ACCESS THE WEB TERMINAL
|
||||
echo "<iframe src='/terminal'></iframe>";
|
||||
echo "<iframe src='/terminal'></iframe>";
|
||||
} else {
|
||||
header('WWW-Authenticate: Basic realm="My Realm"');
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
@@ -201,9 +212,20 @@ if(isset($_GET['view'])){
|
||||
$submitteduser = $_SERVER['PHP_AUTH_USER'];
|
||||
if($submittedpwd == $caddypwd && $submitteduser == 'birdnet'){
|
||||
$command = $_GET['submit'];
|
||||
if(isset($command)){
|
||||
if(isset($command)){
|
||||
$initcommand = $command;
|
||||
if (strpos($command, "systemctl") !== false) {
|
||||
$tmp = explode(" ",trim($command));
|
||||
$command .= "& sleep 3;sudo systemctl status ".end($tmp);
|
||||
}
|
||||
$results = shell_exec("$command 2>&1");
|
||||
echo "<pre>$results</pre>";
|
||||
$results = str_replace("FAILURE", "<span style='color:red'>FAILURE</span>", $results);
|
||||
$results = str_replace("failed", "<span style='color:red'>failed</span>",$results);
|
||||
$results = str_replace("active (running)", "<span style='color:green'><b>active (running)</b></span>",$results);
|
||||
if(strlen($results) == 0) {
|
||||
$results = "This command has no output.";
|
||||
}
|
||||
echo "<table style='min-width:70%;'><tr class='relative'><th>Output of command:`".$initcommand."`<button class='copyimage' style='right:40px' onclick='copyOutput(this);'>Copy</button></th></tr><tr><td><pre style='text-align:left'>$results</pre></td></tr></table>";
|
||||
} else {
|
||||
header('WWW-Authenticate: Basic realm="My Realm"');
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
|
||||
@@ -204,7 +204,7 @@ if (file_exists('./scripts/thisrun.txt')) {
|
||||
<input name="full_disk" type="radio" id="purge" value="purge" <?php if (strcmp($newconfig['FULL_DISK'], "purge") == 0) { echo "checked"; }?>>Purge</label>
|
||||
<label for="keep">
|
||||
<input name="full_disk" type="radio" id="keep" value="keep" <?php if (strcmp($newconfig['FULL_DISK'], "keep") == 0) { echo "checked"; }?>>Keep</label>
|
||||
<p>When the disk becomes full, you can choose to 'purge' old files to make room for new ones or 'keep' your data and stop all services instead.</p>
|
||||
<p>When the disk becomes full, you can choose to 'purge' old files to make room for new ones or 'keep' your data and stop all services instead.<br>Note: you can exclude specific files from 'purge' on the Recordings page.</p>
|
||||
<label for="rec_card">Audio Card: </label>
|
||||
<input name="rec_card" type="text" value="<?php print($newconfig['REC_CARD']);?>" required/><br>
|
||||
<p>Set Audio Card to 'default' to use PulseAudio (always recommended), or an ALSA recognized sound card device from the output of `aplay -L`.</p>
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#!/usr/bin/env bash
|
||||
journalctl --no-hostname -q -o short -fu birdnet_analysis -ubirdnet_server -uextraction | sed "s/$(date "+%b %d ")//g;s/${HOME//\//\\/}\///g"
|
||||
journalctl --no-hostname -q -o short -fu birdnet_analysis -ubirdnet_server -uextraction | sed "s/$(date "+%b %d ")//g;s/${HOME//\//\\/}\///g;/Line/d;/systemd/d;s/ .*\[.*\]: /---/"
|
||||
|
||||
+9
-1
@@ -10,6 +10,7 @@ $birdweather_id = $_GET["birdweather_id"];
|
||||
$apprise_input = $_GET['apprise_input'];
|
||||
$apprise_notification_title = $_GET['apprise_notification_title'];
|
||||
$apprise_notification_body = $_GET['apprise_notification_body'];
|
||||
$flickr_api_key = $_GET['flickr_api_key'];
|
||||
if(isset($_GET['apprise_notify_each_detection'])) {
|
||||
$apprise_notify_each_detection = 1;
|
||||
} else {
|
||||
@@ -45,6 +46,7 @@ $contents = preg_replace("/BIRDWEATHER_ID=.*/", "BIRDWEATHER_ID=$birdweather_id"
|
||||
$contents = preg_replace("/APPRISE_NOTIFICATION_TITLE=.*/", "APPRISE_NOTIFICATION_TITLE=\"$apprise_notification_title\"", $contents);
|
||||
$contents = preg_replace("/APPRISE_NOTIFICATION_BODY=.*/", "APPRISE_NOTIFICATION_BODY=\"$apprise_notification_body\"", $contents);
|
||||
$contents = preg_replace("/APPRISE_NOTIFY_EACH_DETECTION=.*/", "APPRISE_NOTIFY_EACH_DETECTION=$apprise_notify_each_detection", $contents);
|
||||
$contents = preg_replace("/FLICKR_API_KEY=.*/", "FLICKR_API_KEY=$flickr_api_key", $contents);
|
||||
|
||||
|
||||
$contents2 = file_get_contents("./scripts/thisrun.txt");
|
||||
@@ -54,6 +56,8 @@ $contents2 = preg_replace("/BIRDWEATHER_ID=.*/", "BIRDWEATHER_ID=$birdweather_id
|
||||
$contents2 = preg_replace("/APPRISE_NOTIFICATION_TITLE=.*/", "APPRISE_NOTIFICATION_TITLE=\"$apprise_notification_title\"", $contents2);
|
||||
$contents2 = preg_replace("/APPRISE_NOTIFICATION_BODY=.*/", "APPRISE_NOTIFICATION_BODY=\"$apprise_notification_body\"", $contents2);
|
||||
$contents2 = preg_replace("/APPRISE_NOTIFY_EACH_DETECTION=.*/", "APPRISE_NOTIFY_EACH_DETECTION=$apprise_notify_each_detection", $contents2);
|
||||
$contents2 = preg_replace("/FLICKR_API_KEY=.*/", "FLICKR_API_KEY=$flickr_api_key", $contents2);
|
||||
|
||||
|
||||
$fh = fopen("/etc/birdnet/birdnet.conf", "w");
|
||||
$fh2 = fopen("./scripts/thisrun.txt", "w");
|
||||
@@ -144,6 +148,10 @@ https://discordapp.com/api/webhooks/{WebhookID}/{WebhookToken}
|
||||
<label for="apprise_notify_each_species">Notify each new species</label><br>
|
||||
<input type="checkbox" name="apprise_notify_each_detection" <?php if($config['APPRISE_NOTIFY_EACH_DETECTION'] == 1 && filesize($home."/BirdNET-Pi/apprise.txt") != 0) { echo "checked"; };?> >
|
||||
<label for="apprise_notify_each_detection">Notify each new detection</label><br><br>
|
||||
<h3>Bird Photos from Flickr</h3>
|
||||
<label for="flickr_api_key">Flickr API Key: </label>
|
||||
<input name="flickr_api_key" type="text" value="<?php print($config['FLICKR_API_KEY']);?>"/><br>
|
||||
<p>Set your Flickr API key to enable the display of bird images next to detections. <a target="_blank" href="https://www.flickr.com/services/api/misc.api_keys.html">Get your free key here.</a></p>
|
||||
<h3>Localization</h3>
|
||||
<label for="language">Database Language: </label>
|
||||
<select name="language">
|
||||
@@ -206,7 +214,7 @@ https://discordapp.com/api/webhooks/{WebhookID}/{WebhookToken}
|
||||
?>
|
||||
<label for="appt">Select a Date and Time:</label><br>
|
||||
<span>If connected to the internet, retrieve time automatically?</span>
|
||||
<input type="checkbox" onchange='handleChange(this)' name="apprise_notify_each_detection" <?php echo $checkedvalue; ?> ><br>
|
||||
<input type="checkbox" onchange='handleChange(this)' <?php echo $checkedvalue; ?> ><br>
|
||||
<input onclick="this.showPicker()" type="date" id="date" name="date" value="<?php echo date('Y-m-d') ?>" <?php echo $disabledvalue; ?>>
|
||||
<input onclick="this.showPicker()" type="time" id="time" name="time" value="<?php echo date('H:i') ?>" <?php echo $disabledvalue; ?>>
|
||||
<br><br><br>
|
||||
|
||||
@@ -7,6 +7,8 @@ import matplotlib.pyplot as plt
|
||||
from matplotlib.colors import LogNorm
|
||||
from datetime import datetime
|
||||
import textwrap
|
||||
import matplotlib.font_manager as font_manager
|
||||
from matplotlib import rcParams
|
||||
|
||||
userDir = os.path.expanduser('~')
|
||||
conn = sqlite3.connect(userDir + '/BirdNET-Pi/scripts/birds.db')
|
||||
@@ -29,6 +31,14 @@ df['Hour of Day'] = [r.hour for r in df.Time]
|
||||
#Create separate dataframes for separate locations
|
||||
df_plt=df #Default to use the whole Dbase
|
||||
|
||||
# Add every font at the specified location
|
||||
font_dir = ['$HOME/BirdNET-Pi/homepage/static']
|
||||
for font in font_manager.findSystemFonts(font_dir):
|
||||
font_manager.fontManager.addfont(font)
|
||||
|
||||
# Set font family globally
|
||||
rcParams['font.family'] = 'Roboto Flex'
|
||||
|
||||
#Get todays readings
|
||||
now = datetime.now()
|
||||
df_plt_today = df_plt[df_plt['Date']==now.strftime("%Y-%m-%d")]
|
||||
|
||||
@@ -37,6 +37,7 @@ $totalcount = $result1->fetchArray(SQLITE3_ASSOC);
|
||||
<td><?php echo $totalcount['COUNT(*)'];?></td>
|
||||
</tr>
|
||||
</table>
|
||||
<br><br>
|
||||
<?php
|
||||
if (file_exists('./Charts/'.$chart)) {
|
||||
echo "<img src=\"/Charts/$chart?nocache=time()\" >";
|
||||
|
||||
@@ -75,6 +75,10 @@ APPRISE_NOTIFICATION_TITLE="New BirdNET-Pi Detection"
|
||||
APPRISE_NOTIFICATION_BODY="A \$sciname \$comname was just detected with a confidence of \$confidence"
|
||||
APPRISE_NOTIFY_EACH_DETECTION=false
|
||||
|
||||
#---------------------- Flickr Images API Configuration -----------------------#
|
||||
|
||||
FLICKR_API_KEY=
|
||||
|
||||
################################################################################
|
||||
#-------------------------------- Defaults ----------------------------------#
|
||||
################################################################################
|
||||
|
||||
+58
-12
@@ -8,6 +8,12 @@ if($db == False) {
|
||||
header("refresh: 0;");
|
||||
}
|
||||
|
||||
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['ajax_detections']) && $_GET['ajax_detections'] == "true" && isset($_GET['previous_detection_identifier'])) {
|
||||
|
||||
$statement4 = $db->prepare('SELECT Com_Name, Sci_Name, Date, Time, Confidence, File_Name FROM detections ORDER BY Date DESC, Time DESC LIMIT 5');
|
||||
@@ -16,6 +22,8 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" && isse
|
||||
header("refresh: 0;");
|
||||
}
|
||||
$result4 = $statement4->execute();
|
||||
$images = [];
|
||||
$iterations = 0;
|
||||
// hopefully one of the 5 most recent detections has an image that is valid, we'll use that one as the most recent detection until the newer ones get their images created
|
||||
while($mostrecent = $result4->fetchArray(SQLITE3_ASSOC)) {
|
||||
$comname = preg_replace('/ /', '_', $mostrecent['Com_Name']);
|
||||
@@ -37,6 +45,22 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" && isse
|
||||
// we've found our valid detection! ignore everything else from the database loop
|
||||
if(strpos($headers[0],'200')) {
|
||||
if($_GET['previous_detection_identifier'] == $filename) { die(); }
|
||||
|
||||
if (!empty($config["FLICKR_API_KEY"])) {
|
||||
// if we already searched flickr for this species before, use the previous image rather than doing an unneccesary api call
|
||||
$key = array_search($comname, array_column($images, 0));
|
||||
if($key !== false) {
|
||||
$image = $images[$key];
|
||||
} else {
|
||||
$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", $comname)."&license=2%2C3%2C4%2C5%2C6%2C9&sort=relevance&per_page=5&orientation=square,portrait&format=json&media=photos&nojsoncallback=1"), true)["photos"]["photo"][0];
|
||||
$modaltext = "https://flickr.com/photos/".$flickrjson["owner"]."/".$flickrjson["id"];
|
||||
$authorlink = "https://flickr.com/people/".$flickrjson["owner"];
|
||||
$imageurl = 'http://farm' .$flickrjson["farm"]. '.static.flickr.com/' .$flickrjson["server"]. '/' .$flickrjson["id"]. '_' .$flickrjson["secret"]. '.jpg';
|
||||
array_push($images, array($comname,$imageurl,$flickrjson["title"], $modaltext, $authorlink));
|
||||
$image = $images[count($images)-1];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
<style>
|
||||
.fade-in {
|
||||
@@ -60,13 +84,17 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" && isse
|
||||
<h3>Most Recent Detection: <span style="font-weight: normal;"><?php echo $mostrecent['Date']." ".$mostrecent['Time'];?></span></h3>
|
||||
<tr>
|
||||
<td class="relative"><a target="_blank" href="index.php?filename=<?php echo $mostrecent['File_Name']; ?>"><img class="copyimage" width="25" height="25" src="images/copy.png"></a>
|
||||
<form action="" method="GET">
|
||||
<input type="hidden" name="view" value="Species Stats">
|
||||
<button type="submit" name="species" value="<?php echo $mostrecent['Com_Name'];?>"><?php echo $mostrecent['Com_Name'];?></button></br>
|
||||
<a href="https://wikipedia.org/wiki/<?php echo $sciname;?>" target="_blank"/><i><?php echo $mostrecent['Sci_Name'];?></i></a>
|
||||
<br>Confidence: <?php echo $mostrecent['Confidence'];?><br>
|
||||
<video style="margin-top:10px" onplay='setLiveStreamVolume(0)' onended='setLiveStreamVolume(1)' onpause='setLiveStreamVolume(1)' controls poster="<?php echo $filename.".png";?>" preload="none" title="<?php echo $filename;?>"><source src="<?php echo $filename;?>"></video></td>
|
||||
</form>
|
||||
<div class="centered_image_container" style="margin-bottom: 0px !important;">
|
||||
<?php if(!empty($config["FLICKR_API_KEY"])) { ?>
|
||||
<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">
|
||||
<?php } ?>
|
||||
<form action="" method="GET">
|
||||
<input type="hidden" name="view" value="Species Stats">
|
||||
<button type="submit" name="species" value="<?php echo $mostrecent['Com_Name'];?>"><?php echo $mostrecent['Com_Name'];?></button></br>
|
||||
<a href="https://wikipedia.org/wiki/<?php echo $sciname;?>" target="_blank"/><i><?php echo $mostrecent['Sci_Name'];?></i></a>
|
||||
<br>Confidence: <?php echo $percent = round((float)round($mostrecent['Confidence'],2) * 100 ) . '%';?><br></div><br>
|
||||
<video style="margin-top:10px" onplay='setLiveStreamVolume(0)' onended='setLiveStreamVolume(1)' onpause='setLiveStreamVolume(1)' controls poster="<?php echo $filename.".png";?>" preload="none" title="<?php echo $filename;?>"><source src="<?php echo $filename;?>"></video></td>
|
||||
</form>
|
||||
</tr>
|
||||
</table> <?php break;
|
||||
}
|
||||
@@ -153,17 +181,35 @@ body::-webkit-scrollbar {
|
||||
</style>
|
||||
</head>
|
||||
<div class="overview">
|
||||
<dialog id="attribution-dialog">
|
||||
<h1 id="modalHeading"></h1>
|
||||
<p id="modalText"></p>
|
||||
<button onclick="hideDialog()">Close</button>
|
||||
</dialog>
|
||||
<script>
|
||||
var dialog = document.querySelector('dialog');
|
||||
dialogPolyfill.registerDialog(dialog);
|
||||
|
||||
function showDialog() {
|
||||
document.getElementById('attribution-dialog').showModal();
|
||||
}
|
||||
|
||||
function hideDialog() {
|
||||
document.getElementById('attribution-dialog').close();
|
||||
}
|
||||
|
||||
function setModalText(iter, title, text, authorlink, photolink) {
|
||||
document.getElementById('modalHeading').innerHTML = "Photo: \""+decodeURIComponent(title.replace("+"," "))+"\" Attribution";
|
||||
document.getElementById('modalText').innerHTML = "<div><img style='border-radius:5px' src='"+photolink+"'></div><br><div>Image link: <a target='_blank' href="+text+">"+text+"</a><br>Author link: <a target='_blank' href="+authorlink+">"+authorlink+"</a></div>";
|
||||
showDialog();
|
||||
}
|
||||
</script>
|
||||
<div class="overview-stats">
|
||||
<div class="left-column">
|
||||
</div>
|
||||
<div class="right-column">
|
||||
<div class="chart">
|
||||
<?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');
|
||||
}
|
||||
$refresh = $config['RECORDING_LENGTH'];
|
||||
$time = time();
|
||||
if (file_exists('./Charts/'.$chart)) {
|
||||
|
||||
+184
-30
@@ -9,18 +9,62 @@ if($db == False){
|
||||
header("refresh: 0;");
|
||||
}
|
||||
|
||||
#By Date
|
||||
if(isset($_GET['byfilename'])){
|
||||
$statement = $db->prepare('SELECT DISTINCT(Date) FROM detections GROUP BY Date');
|
||||
if($statement == False){
|
||||
echo "Database is busy";
|
||||
header("refresh: 0;");
|
||||
if (file_exists('./scripts/thisrun.txt')) {
|
||||
$config = parse_ini_file('./scripts/thisrun.txt');
|
||||
} elseif (file_exists('firstrun.ini')) {
|
||||
$config = parse_ini_file('firstrun.ini');
|
||||
}
|
||||
|
||||
$user = shell_exec("awk -F: '/1000/{print $1}' /etc/passwd");
|
||||
$home = shell_exec("awk -F: '/1000/{print $6}' /etc/passwd");
|
||||
$home = trim($home);
|
||||
|
||||
if(isset($_GET['excludefile'])) {
|
||||
if(isset($_SERVER['PHP_AUTH_USER'])) {
|
||||
$submittedpwd = $_SERVER['PHP_AUTH_PW'];
|
||||
$submitteduser = $_SERVER['PHP_AUTH_USER'];
|
||||
if($submittedpwd == $config['CADDY_PWD'] && $submitteduser == 'birdnet'){
|
||||
if(!file_exists($home."/BirdNET-Pi/scripts/disk_check_exclude.txt")) {
|
||||
file_put_contents($home."/BirdNET-Pi/scripts/disk_check_exclude.txt", "##start\n##end");
|
||||
}
|
||||
if(isset($_GET['exclude_add'])) {
|
||||
$myfile = fopen($home."/BirdNET-Pi/scripts/disk_check_exclude.txt", "a") or die("Unable to open file!");
|
||||
$txt = $_GET['excludefile'];
|
||||
fwrite($myfile, $txt."\n");
|
||||
fwrite($myfile, $txt.".png\n");
|
||||
fclose($myfile);
|
||||
echo "OK";
|
||||
die();
|
||||
} else {
|
||||
$lines = file($home."/BirdNET-Pi/scripts/disk_check_exclude.txt");
|
||||
$search = $_GET['excludefile'];
|
||||
|
||||
$result = '';
|
||||
foreach($lines as $line) {
|
||||
if(stripos($line, $search) === false && stripos($line, $search.".png") === false) {
|
||||
$result .= $line;
|
||||
}
|
||||
}
|
||||
file_put_contents($home."/BirdNET-Pi/scripts/disk_check_exclude.txt", $result);
|
||||
echo "OK";
|
||||
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;
|
||||
}
|
||||
$result = $statement->execute();
|
||||
$view = "bydate";
|
||||
#By Date
|
||||
}elseif(isset($_GET['bydate'])){
|
||||
$statement = $db->prepare('SELECT DISTINCT(Date) FROM detections GROUP BY Date');
|
||||
}
|
||||
|
||||
if(isset($_GET['bydate'])){
|
||||
$statement = $db->prepare('SELECT DISTINCT(Date) FROM detections GROUP BY Date ORDER BY Date DESC');
|
||||
if($statement == False){
|
||||
echo "Database is busy";
|
||||
header("refresh: 0;");
|
||||
@@ -79,7 +123,6 @@ if(isset($_GET['byfilename'])){
|
||||
session_unset();
|
||||
$view = "choose";
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<html>
|
||||
@@ -90,6 +133,31 @@ if(isset($_GET['byfilename'])){
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<script>
|
||||
function toggleLock(filename, type, elem) {
|
||||
const xhttp = new XMLHttpRequest();
|
||||
xhttp.onload = function() {
|
||||
if(this.responseText == "OK"){
|
||||
if(type == "add") {
|
||||
elem.setAttribute("src","images/lock.svg");
|
||||
elem.setAttribute("title", "This file is delete protected.");
|
||||
elem.setAttribute("onclick", elem.getAttribute("onclick").replace("add","del"));
|
||||
} else {
|
||||
elem.setAttribute("src","images/unlock.svg");
|
||||
elem.setAttribute("title", "This file is not delete protected.");
|
||||
elem.setAttribute("onclick", elem.getAttribute("onclick").replace("del","add"));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(type == "add") {
|
||||
xhttp.open("GET", "play.php?excludefile="+filename+"&exclude_add=true", true);
|
||||
} else {
|
||||
xhttp.open("GET", "play.php?excludefile="+filename+"&exclude_del=true", true);
|
||||
}
|
||||
xhttp.send();
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php
|
||||
#If no specific species
|
||||
if(!isset($_GET['species']) && !isset($_GET['filename'])){
|
||||
@@ -111,7 +179,6 @@ if(!isset($_GET['species']) && !isset($_GET['filename'])){
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<form action="" method="GET">
|
||||
@@ -121,13 +188,15 @@ if(!isset($_GET['species']) && !isset($_GET['filename'])){
|
||||
if($view == "bydate") {
|
||||
while($results=$result->fetchArray(SQLITE3_ASSOC)){
|
||||
$date = $results['Date'];
|
||||
if(realpath($home."/BirdSongs/Extracted/By_Date/".$date) !== false){
|
||||
echo "<td>
|
||||
<button action=\"submit\" name=\"date\" value=\"$date\">$date</button></td></tr>";}
|
||||
<button action=\"submit\" name=\"date\" value=\"$date\">$date</button></td></tr>";}}
|
||||
|
||||
#By Species
|
||||
} elseif($view == "byspecies") {
|
||||
while($results=$result->fetchArray(SQLITE3_ASSOC)){
|
||||
$name = $results['Com_Name'];
|
||||
|
||||
echo "<td>
|
||||
<button action=\"submit\" name=\"species\" value=\"$name\">$name</button></td></tr>";}
|
||||
|
||||
@@ -135,8 +204,11 @@ if(!isset($_GET['species']) && !isset($_GET['filename'])){
|
||||
} elseif($view == "date") {
|
||||
while($results=$result->fetchArray(SQLITE3_ASSOC)){
|
||||
$name = $results['Com_Name'];
|
||||
echo "<td>
|
||||
<button action=\"submit\" name=\"species\" value=\"$name\">$name</button></td></tr>";}
|
||||
if(realpath($home."/BirdSongs/Extracted/By_Date/".$date."/".str_replace(" ", "_",$name)) !== false){
|
||||
echo "<td>
|
||||
<button action=\"submit\" name=\"species\" value=\"$name\">$name</button></td></tr>";
|
||||
}
|
||||
}
|
||||
|
||||
#Choose
|
||||
} else {
|
||||
@@ -151,13 +223,42 @@ if(!isset($_GET['species']) && !isset($_GET['filename'])){
|
||||
}
|
||||
|
||||
#Specific Species
|
||||
if(isset($_GET['species'])){
|
||||
if(isset($_GET['species'])){ ?>
|
||||
<div style="width: auto;
|
||||
text-align: center">
|
||||
<form action="" method="GET">
|
||||
<input type="hidden" name="view" value="Recordings">
|
||||
<input type="hidden" name="species" value="<?php echo $_GET['species']; ?>">
|
||||
<button <?php if(!isset($_GET['sort']) || $_GET['sort'] == "date"){ echo "style='background:#9fe29b !important;'"; }?> class="sortbutton" type="submit" name="sort" value="date">
|
||||
<img width=35px src="images/sort_date.svg" alt="Sort by date">
|
||||
</button>
|
||||
<button <?php if(isset($_GET['sort']) && $_GET['sort'] == "confidence"){ echo "style='background:#9fe29b !important;'"; }?> class="sortbutton" type="submit" name="sort" value="confidence">
|
||||
<img src="images/sort_occ.svg" alt="Sort by confidence">
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
// add disk_check_exclude.txt lines into an array for grepping
|
||||
$fp = @fopen($home."/BirdNET-Pi/scripts/disk_check_exclude.txt", 'r');
|
||||
if ($fp) {
|
||||
$disk_check_exclude_arr = explode("\n", fread($fp, filesize($home."/BirdNET-Pi/scripts/disk_check_exclude.txt")));
|
||||
}
|
||||
|
||||
$name = $_GET['species'];
|
||||
if(isset($_SESSION['date'])) {
|
||||
$date = $_SESSION['date'];
|
||||
$statement2 = $db->prepare("SELECT * FROM detections where Com_Name == \"$name\" AND Date == \"$date\" ORDER BY Time DESC");
|
||||
if(isset($_GET['sort']) && $_GET['sort'] == "confidence") {
|
||||
$statement2 = $db->prepare("SELECT * FROM detections where Com_Name == \"$name\" AND Date == \"$date\" ORDER BY Confidence DESC");
|
||||
} else {
|
||||
$statement2 = $db->prepare("SELECT * FROM detections where Com_Name == \"$name\" AND Date == \"$date\" ORDER BY Time DESC");
|
||||
}
|
||||
} 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;");
|
||||
@@ -167,6 +268,7 @@ if(isset($_GET['species'])){
|
||||
<tr>
|
||||
<th>$name</th>
|
||||
</tr>";
|
||||
$iter=0;
|
||||
while($results=$result2->fetchArray(SQLITE3_ASSOC))
|
||||
{
|
||||
$comname = preg_replace('/ /', '_', $results['Com_Name']);
|
||||
@@ -176,13 +278,40 @@ if(isset($_GET['species'])){
|
||||
$sciname = preg_replace('/ /', '_', $results['Sci_Name']);
|
||||
$sci_name = $results['Sci_Name'];
|
||||
$time = $results['Time'];
|
||||
$confidence = $results['Confidence'];
|
||||
echo "<tr>
|
||||
<td class=\"relative\"><a target=\"_blank\" href=\"index.php?filename=".$results['File_Name']."\"><img class=\"copyimage\" width=25 src=\"images/copy.png\"></a>$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>";
|
||||
$confidence = round((float)round($results['Confidence'],2) * 100 ) . '%';
|
||||
$filename_formatted = $date."/".$comname."/".$results['File_Name'];
|
||||
|
||||
}echo "</table>";}
|
||||
// file was deleted by disk check, no need to show the detection in recordings
|
||||
if(!file_exists($home."/BirdSongs/Extracted/".$filename)) {
|
||||
continue;
|
||||
}
|
||||
$iter++;
|
||||
|
||||
if($config["FULL_DISK"] == "purge") {
|
||||
if(!in_array($filename_formatted, $disk_check_exclude_arr)) {
|
||||
$imageicon = "images/unlock.svg";
|
||||
$title = "This file is not delete protected.";
|
||||
$type = "add";
|
||||
} else {
|
||||
$imageicon = "images/lock.svg";
|
||||
$title = "This file is delete protected.";
|
||||
$type = "del";
|
||||
}
|
||||
|
||||
echo "<tr>
|
||||
<td class='relative'>$date $time<br>$confidence<br><img style='cursor:pointer' onclick='toggleLock(\"".$filename_formatted."\",\"".$type."\", this)' class=\"copyimage\" width=25 title=\"".$title."\" src=\"".$imageicon."\">
|
||||
<a href=\"$filename\"><img src=\"$filename.png\"></a>
|
||||
</td>
|
||||
</tr>";
|
||||
} else {
|
||||
echo "<tr>
|
||||
<td class='relative'>$date $time<br>$confidence<br>
|
||||
<a href=\"$filename\"><img src=\"$filename.png\"></a>
|
||||
</td>
|
||||
</tr>";
|
||||
}
|
||||
|
||||
}if($iter == 0){ echo "<tr><td><b>No recordings were found.</b><br><br><span style='font-size:small'>They may have been deleted to make space for new recordings. You can modify this setting for the future in Tools -> Settings -> Advanced Settings -> Full Disk Behavior.</small></td></tr>";}echo "</table>";}
|
||||
|
||||
if(isset($_GET['filename'])){
|
||||
$name = $_GET['filename'];
|
||||
@@ -205,11 +334,36 @@ if(isset($_GET['filename'])){
|
||||
$sciname = preg_replace('/ /', '_', $results['Sci_Name']);
|
||||
$sci_name = $results['Sci_Name'];
|
||||
$time = $results['Time'];
|
||||
$confidence = $results['Confidence'];
|
||||
echo "<tr>
|
||||
<td>$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>";
|
||||
$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 {
|
||||
$imageicon = "images/lock.svg";
|
||||
$title = "This file is delete protected.";
|
||||
$type = "del";
|
||||
}
|
||||
|
||||
echo "<tr>
|
||||
<td class=\"relative\"><img style='cursor:pointer' onclick='toggleLock(\"".$filename_formatted."\",\"".$type."\", this)' class=\"copyimage\" width=25 title=\"".$title."\" src=\"".$imageicon."\">$date $time<br>$confidence<br>
|
||||
<video onplay='setLiveStreamVolume(0)' onended='setLiveStreamVolume(1)' onpause='setLiveStreamVolume(1)' controls poster=\"$filename.png\" preload=\"none\" title=\"$filename\"><source src=\"$filename\"></video></td>
|
||||
</tr>";
|
||||
} else {
|
||||
echo "<tr>
|
||||
<td class=\"relative\">$date $time<br>$confidence<br>
|
||||
<video onplay='setLiveStreamVolume(0)' onended='setLiveStreamVolume(1)' onpause='setLiveStreamVolume(1)' controls poster=\"$filename.png\" preload=\"none\" title=\"$filename\"><source src=\"$filename\"></video></td>
|
||||
</tr>";
|
||||
}
|
||||
|
||||
}echo "</table>";}?>
|
||||
</div>
|
||||
|
||||
+83
-30
@@ -60,7 +60,8 @@ df2=df2.set_index('DateTime')
|
||||
# Date as slider
|
||||
Start_Date = pd.to_datetime(df2.index.min()).date()
|
||||
End_Date = pd.to_datetime(df2.index.max()).date()
|
||||
Date_Slider = st.slider('Date Range',
|
||||
cols1,cols2= st.columns((1,1))
|
||||
Date_Slider = cols1.slider('Date Range',
|
||||
min_value = Start_Date-timedelta(days=1),
|
||||
max_value = End_Date,
|
||||
value=(Start_Date,
|
||||
@@ -72,14 +73,29 @@ Date_Slider = st.slider('Date Range',
|
||||
filt = (df2.index >= pd.Timestamp(Date_Slider[0])) & (df2.index <= pd.Timestamp(Date_Slider[1]+timedelta(days=1)))
|
||||
df2 = df2[filt]
|
||||
|
||||
st.write('<style>div.row-widget.stRadio > div{flex-direction:row;justify-content: left;} </style>', unsafe_allow_html=True)
|
||||
st.write('<style>div.st-bf{flex-direction:column;} div.st-ag{font-weight:bold;padding-left:2px;}</style>', unsafe_allow_html=True)
|
||||
|
||||
resample_sel=cols2.radio("Select Resample Resolution - To downsample and make run faster select longer period, Daily provides a view on detections at 15 min intervals through the day", ('1 minute', '5 minutes', '10 minutes', 'Hourly', 'Daily'))
|
||||
|
||||
resample_times = {'1 minute':'1min',
|
||||
'5 minutes':'5min',
|
||||
'10 minutes':'10min',
|
||||
'Hourly':'1H',
|
||||
'Daily':'1D'
|
||||
}
|
||||
resample_time = resample_times[resample_sel]
|
||||
|
||||
df5=df2.resample(resample_time)['Com_Name'].aggregate('unique').explode()
|
||||
|
||||
#Create species count for selected date range
|
||||
|
||||
Specie_Count=df2['Com_Name'].value_counts()
|
||||
Specie_Count=df5.value_counts()
|
||||
|
||||
#Create species treemap
|
||||
|
||||
# Create Hourly Crosstab
|
||||
hourly=pd.crosstab(df2['Com_Name'],df2.index.hour, dropna=False)
|
||||
hourly=pd.crosstab(df5,df5.index.hour, dropna=False)
|
||||
|
||||
# Filter on species
|
||||
species = list(hourly.index)
|
||||
@@ -91,7 +107,7 @@ top_N = cols1.slider(
|
||||
value=min(10,len(Specie_Count))
|
||||
)
|
||||
|
||||
top_N_species = (df2['Com_Name'].value_counts()[:top_N])
|
||||
top_N_species = (df5.value_counts()[:top_N])
|
||||
|
||||
|
||||
specie = cols2.selectbox('Which bird would you like to explore for the dates '+str(Date_Slider[0])+' to '+str(Date_Slider[1])+'?', species,
|
||||
@@ -104,13 +120,13 @@ font_size=15
|
||||
#specie filter
|
||||
filt=df2['Com_Name']==specie
|
||||
|
||||
df_counts=df2[filt].resample('D').count()
|
||||
df_counts=sum(df5==specie)
|
||||
|
||||
fig = make_subplots(
|
||||
rows=3, cols =2,
|
||||
specs= [[{"type":"xy","rowspan":3}, {"type":"polar","rowspan":2}], [{"rowspan":1}, {"rowspan":1} ], [None, {"type":"xy","rowspan":1}]],
|
||||
subplot_titles=('<b>Top '+ str(top_N) + ' Species in Date Range '+str(Date_Slider[0])+' to '+str(Date_Slider[1])+'</b>',
|
||||
'Total Detect:'+str('{:,}'.format(sum(df_counts.Time)))+
|
||||
'Total Detect:'+str('{:,}'.format(df_counts))+
|
||||
' Confidence Max:'+str('{:.2f}%'.format(max(df2[df2['Com_Name']==specie]['Confidence'])*100))+
|
||||
' '+' Median:'+str('{:.2f}%'.format(np.median(df2[df2['Com_Name']==specie]['Confidence'])*100))
|
||||
)
|
||||
@@ -123,43 +139,80 @@ fig.add_trace(go.Bar(y=top_N_species.index, x=top_N_species, orientation='h'), r
|
||||
fig.update_layout(
|
||||
margin=dict(l=0, r=0, t=50, b=0),
|
||||
yaxis={'categoryorder':'total ascending'})
|
||||
|
||||
|
||||
# Set 360 degrees, 24 hours for polar plot
|
||||
theta = np.linspace(0.0, 360, 24, endpoint=False)
|
||||
|
||||
specie_filt= df5==specie
|
||||
df3=df5[specie_filt]
|
||||
|
||||
detections2= pd.crosstab(df3, df3.index.hour)
|
||||
|
||||
|
||||
|
||||
|
||||
d=pd.DataFrame(np.zeros((23,1))).squeeze()
|
||||
detections = hourly.loc[specie]
|
||||
detections=(d+detections).fillna(0)
|
||||
fig.add_trace(go.Barpolar(r = detections, theta=theta), row=1, col=2)
|
||||
|
||||
fig.update_layout(
|
||||
autosize=False,
|
||||
width = 1000,
|
||||
height = 500,
|
||||
showlegend=False,
|
||||
polar = dict(
|
||||
radialaxis = dict(
|
||||
tickfont_size = font_size,
|
||||
showticklabels = True,
|
||||
hoverformat = "#%{theta}: <br>Popularity: %{percent} </br> %{r}"
|
||||
|
||||
if resample_time != '1D':
|
||||
fig.add_trace(go.Barpolar(r = detections, theta=theta), row=1, col=2)
|
||||
fig.update_layout(
|
||||
autosize=False,
|
||||
width = 1000,
|
||||
height = 500,
|
||||
showlegend=False,
|
||||
polar = dict(
|
||||
radialaxis = dict(
|
||||
tickfont_size = font_size,
|
||||
showticklabels = False,
|
||||
hoverformat = "#%{theta}: <br>Popularity: %{percent} </br> %{r}"
|
||||
),
|
||||
angularaxis = dict(
|
||||
tickfont_size= font_size,
|
||||
rotation = -90,
|
||||
direction = 'clockwise',
|
||||
tickmode='array',
|
||||
tickvals=[0,15,35,45,60,75,90,105,120,135,150,165,180,195,210,225,240,255,270,285,300,315,330,345],
|
||||
ticktext=['12am','1am','2am','3am','4am','5am', '6am','7am','8am','9am','10am','11am','12pm','1pm','2pm','3pm','4pm','5pm', '6pm','7pm','8pm','9pm','10pm','11pm'],
|
||||
hoverformat = "#%{theta}: <br>Popularity: %{percent} </br> %{r}"
|
||||
),
|
||||
angularaxis = dict(
|
||||
tickfont_size= font_size,
|
||||
rotation = -90,
|
||||
direction = 'clockwise',
|
||||
tickmode='array',
|
||||
tickvals=[0,15,35,45,60,75,90,105,120,135,150,165,180,195,210,225,240,255,270,285,300,315,330,345],
|
||||
ticktext=['12am','1am','2am','3am','4am','5am', '6am','7am','8am','9am','10am','11am','12pm','1pm','2pm','3pm','4pm','5pm', '6pm','7pm','8pm','9pm','10pm','11pm'],
|
||||
hoverformat = "#%{theta}: <br>Popularity: %{percent} </br> %{r}"
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
||||
daily=pd.crosstab(df2['Com_Name'],df2.index.date, dropna=False)
|
||||
daily=pd.crosstab(df5,df5.index.date, dropna=False)
|
||||
|
||||
fig.add_trace(go.Bar(x=daily.columns, y=daily.loc[specie]), row=3, col=2)
|
||||
fig.add_trace(go.Bar(x=daily.columns, y=daily.loc[specie]), row=3, col=2)
|
||||
|
||||
else:
|
||||
fig = make_subplots(
|
||||
rows=1, cols =2,
|
||||
# specs= [[{"type":"xy","rowspan":1}],
|
||||
# [{"rowspan":1}],
|
||||
# ],
|
||||
# subplot_titles=('<b>Top '+ str(top_N) + ' Species in Date Range '+str(Date_Slider[0])+' to '+str(Date_Slider[1])+'</b>',
|
||||
# 'Total Detect:'+str('{:,}'.format(df_counts))+
|
||||
# ' Confidence Max:'+str('{:.2f}%'.format(max(df2[df2['Com_Name']==specie]['Confidence'])*100))+
|
||||
# ' '+' Median:'+str('{:.2f}%'.format(np.median(df2[df2['Com_Name']==specie]['Confidence'])*100))
|
||||
# )
|
||||
)
|
||||
df4=df2['Com_Name'][df2['Com_Name']==specie].resample('15min').count()
|
||||
df4.index=[df4.index.date, df4.index.time]
|
||||
day_hour_freq=df4.unstack().fillna(0)
|
||||
|
||||
fig_x = [d.strftime('%d-%m-%Y') for d in day_hour_freq.index.tolist()]
|
||||
fig_y = [h.strftime('%H:%M') for h in day_hour_freq.columns.tolist()]
|
||||
fig_z = day_hour_freq.values.transpose()
|
||||
fig_heatmap = go.Figure(data=go.Heatmap(x=fig_x,y=fig_y,z=fig_z))
|
||||
fig.add_trace(go.Bar(y=top_N_species.index, x=top_N_species, orientation='h'), row=1,col=1)
|
||||
fig.update_layout(
|
||||
margin=dict(l=0, r=0, t=50, b=0),
|
||||
yaxis={'categoryorder':'total ascending'})
|
||||
fig.add_trace(go.Heatmap(x=fig_x,y=fig_y,z=fig_z, autocolorscale = False, colorscale = 'blackbody'), row=1, col=2)
|
||||
# container=st.container()
|
||||
# config={'displayModelBar': False}
|
||||
st.plotly_chart(fig, use_container_width=True) #, config=config)
|
||||
|
||||
@@ -22,6 +22,6 @@ sudo systemctl restart "${i}"
|
||||
done
|
||||
until grep 5050 <(netstat -tulpn 2>&1);do
|
||||
sudo systemctl restart birdnet_server.service
|
||||
sleep 20
|
||||
sleep 30
|
||||
done
|
||||
sudo systemctl restart birdnet_analysis.service
|
||||
|
||||
+5
-5
@@ -261,7 +261,7 @@ def writeResultsToFile(detections, min_conf, path):
|
||||
def handle_client(conn, addr):
|
||||
global INCLUDE_LIST
|
||||
global EXCLUDE_LIST
|
||||
print(f"[NEW CONNECTION] {addr} connected.")
|
||||
#print(f"[NEW CONNECTION] {addr} connected.")
|
||||
|
||||
connected = True
|
||||
while connected:
|
||||
@@ -335,7 +335,7 @@ def handle_client(conn, addr):
|
||||
# Get Date/Time from filename in case Pi gets behind
|
||||
#now = datetime.now()
|
||||
full_file_name = args.i
|
||||
print('FULL FILENAME: -' + full_file_name + '-')
|
||||
#print('FULL FILENAME: -' + full_file_name + '-')
|
||||
file_name = Path(full_file_name).stem
|
||||
file_date = file_name.split('-birdnet-')[0]
|
||||
file_time = file_name.split('-birdnet-')[1]
|
||||
@@ -463,13 +463,13 @@ def start():
|
||||
global INTERPRETER, INCLUDE_LIST, EXCLUDE_LIST
|
||||
INTERPRETER = loadModel()
|
||||
server.listen()
|
||||
print(f"[LISTENING] Server is listening on {SERVER}")
|
||||
#print(f"[LISTENING] Server is listening on {SERVER}")
|
||||
while True:
|
||||
conn, addr = server.accept()
|
||||
thread = threading.Thread(target=handle_client, args=(conn, addr))
|
||||
thread.start()
|
||||
print(f"[ACTIVE CONNECTIONS] {threading.activeCount() - 1}")
|
||||
#print(f"[ACTIVE CONNECTIONS] {threading.activeCount() - 1}")
|
||||
|
||||
|
||||
print("[STARTING] server is starting...")
|
||||
#print("[STARTING] server is starting...")
|
||||
start()
|
||||
|
||||
+83
-14
@@ -1,16 +1,85 @@
|
||||
<?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');
|
||||
}
|
||||
$refresh = $config['RECORDING_LENGTH'];
|
||||
$time = time();
|
||||
echo "<img id=\"spectrogramimage\" style=\"width:100%;height:100%\" src=\"/spectrogram.png?nocache=$time\">";
|
||||
?>
|
||||
<script>
|
||||
// every $refresh seconds, this loop will run and refresh the spectrogram image
|
||||
window.setInterval(function(){
|
||||
document.getElementById("spectrogramimage").src = "/spectrogram.png?nocache="+Date.now();
|
||||
}, <?php echo $refresh; ?>*1000);
|
||||
// CREDITS: https://codepen.io/jakealbaugh/pen/jvQweW
|
||||
|
||||
// UPDATE: there is a problem in chrome with starting audio context
|
||||
// before a user gesture. This fixes it.
|
||||
var started = null;
|
||||
window.onload = function(){
|
||||
if (started) return;
|
||||
started = true;
|
||||
initialize();
|
||||
};
|
||||
|
||||
function initialize() {
|
||||
document.body.querySelector('h1').remove();
|
||||
const CVS = document.body.querySelector('canvas');
|
||||
const CTX = CVS.getContext('2d');
|
||||
const W = CVS.width = window.innerWidth;
|
||||
const H = CVS.height = window.innerHeight;
|
||||
|
||||
const ACTX = new AudioContext();
|
||||
const ANALYSER = ACTX.createAnalyser();
|
||||
|
||||
ANALYSER.fftSize = 2048;
|
||||
|
||||
process();
|
||||
|
||||
function process() {
|
||||
const SOURCE = ACTX.createMediaElementSource(document.getElementById('player'));
|
||||
SOURCE.connect(ANALYSER);
|
||||
SOURCE.connect(ACTX.destination)
|
||||
const DATA = new Uint8Array(ANALYSER.frequencyBinCount);
|
||||
const LEN = DATA.length;
|
||||
const h = H / LEN;
|
||||
const x = W - 1;
|
||||
CTX.fillStyle = 'hsl(280, 100%, 10%)';
|
||||
CTX.fillRect(0, 0, W, H);
|
||||
|
||||
loop();
|
||||
|
||||
function loop() {
|
||||
window.requestAnimationFrame(loop);
|
||||
let imgData = CTX.getImageData(1, 0, W - 1, H);
|
||||
CTX.fillRect(0, 0, W, H);
|
||||
CTX.putImageData(imgData, 0, 0);
|
||||
ANALYSER.getByteFrequencyData(DATA);
|
||||
for (let i = 0; i < LEN; i++) {
|
||||
let rat = DATA[i] / 128;
|
||||
let hue = Math.round((rat * 120) + 280 % 360);
|
||||
let sat = '100%';
|
||||
let lit = 10 + (70 * rat) + '%';
|
||||
CTX.beginPath();
|
||||
CTX.strokeStyle = `hsl(${hue}, ${sat}, ${lit})`;
|
||||
CTX.moveTo(x, H - (i * h));
|
||||
CTX.lineTo(x, H - (i * h + h));
|
||||
CTX.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<style>
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
height: 85%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<audio style="display:none" controls="" crossorigin="anonymous" id='player' autoplay=""><source src="/stream"></audio>
|
||||
<h1>Loading...</h1>
|
||||
<canvas></canvas>
|
||||
|
||||
|
||||
+54
-19
@@ -56,7 +56,18 @@ if(isset($_GET['species'])){
|
||||
$user = shell_exec("awk -F: '/1000/{print $1}' /etc/passwd");
|
||||
$home = shell_exec("awk -F: '/1000/{print $6}' /etc/passwd");
|
||||
$home = trim($home);
|
||||
file_put_contents($home."/BirdNET-Pi/scripts/disk_check_exclude.txt", "");
|
||||
if(!file_exists($home."/BirdNET-Pi/scripts/disk_check_exclude.txt")) {
|
||||
file_put_contents($home."/BirdNET-Pi/scripts/disk_check_exclude.txt", "##start\n##end");
|
||||
}
|
||||
function editfile( $sourcefile, $start='##start', $end='##end', $data=array() ){
|
||||
$lines=array_filter( file( $sourcefile , FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES ) );
|
||||
$output=array_merge(
|
||||
array_splice( $lines, 0, array_search( strtolower( $start ), array_map('strtolower', $lines ) ) + 1 ),
|
||||
$data,
|
||||
array_splice( $lines, array_search( strtolower( $end ), array_map('strtolower', $lines ) ) )
|
||||
);
|
||||
file_put_contents( $sourcefile, implode( PHP_EOL, $output ) );
|
||||
}
|
||||
?>
|
||||
|
||||
<html lang="en">
|
||||
@@ -106,17 +117,40 @@ $filename = "/By_Date/".$results['Date']."/".$comname."/".$results['File_Name'];
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<dialog id="attribution-dialog">
|
||||
<h1 id="modalHeading"></h1>
|
||||
<p id="modalText"></p>
|
||||
<button onclick="hideDialog()">Close</button>
|
||||
</dialog>
|
||||
<script>
|
||||
var dialog = document.querySelector('dialog');
|
||||
dialogPolyfill.registerDialog(dialog);
|
||||
|
||||
function showDialog() {
|
||||
document.getElementById('attribution-dialog').showModal();
|
||||
}
|
||||
|
||||
function hideDialog() {
|
||||
document.getElementById('attribution-dialog').close();
|
||||
}
|
||||
|
||||
function setModalText(iter, title, text, authorlink) {
|
||||
document.getElementById('modalHeading').innerHTML = "Photo "+iter+": \""+title+"\" Attribution";
|
||||
document.getElementById('modalText').innerHTML = "Image link: <a target='_blank' href="+text+">"+text+"</a><br>Author link: <a target='_blank' href="+authorlink+">"+authorlink+"</a>";
|
||||
showDialog();
|
||||
}
|
||||
</script>
|
||||
<div class="column center">
|
||||
<?php if(!isset($_GET['species'])){
|
||||
?><p class="centered">Choose a species to load images from Wikimedia Commons.</p>
|
||||
?><p class="centered">Choose a species to load images from Flickr.</p>
|
||||
<?php
|
||||
};?>
|
||||
<?php if(isset($_GET['species'])){
|
||||
$species = $_GET['species'];
|
||||
|
||||
$iter=0;
|
||||
while($results=$result3->fetchArray(SQLITE3_ASSOC)){
|
||||
$count = $results['COUNT(*)'];
|
||||
$maxconf = $results['MAX(Confidence)'];
|
||||
$maxconf = round((float)round($results['MAX(Confidence)'],2) * 100 ) . '%';
|
||||
$date = $results['Date'];
|
||||
$time = $results['Time'];
|
||||
$name = $results['Com_Name'];
|
||||
@@ -136,46 +170,47 @@ while($results=$result3->fetchArray(SQLITE3_ASSOC)){
|
||||
<video onplay='setLiveStreamVolume(0)' onended='setLiveStreamVolume(1)' onpause='setLiveStreamVolume(1)' controls poster=\"$filename.png\" title=\"$filename\"><source src=\"$filename\"></video></td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>Loading Images from <a href=\"https://commons.wikimedia.org/w/index.php?search=$linkname&title=Special:MediaSearch&go=Go&type=image\" target=\"_blank\">Wikimedia Commons</a></p>", '6096');
|
||||
<p>Loading Images from Flickr</p>", '6096');
|
||||
|
||||
echo "<script>document.getElementsByTagName(\"h3\")[0].scrollIntoView();</script>";
|
||||
|
||||
ob_flush();
|
||||
flush();
|
||||
$imagelink = "https://commons.wikimedia.org/w/index.php?search=$linkname&title=Special:MediaSearch&go=Go&type=image";
|
||||
$homepage = file_get_contents($imagelink);
|
||||
preg_match_all("{<img\\s*(.*?)src=('.*?'|\".*?\"|[^\\s]+)(.*?)\\s*/?>}ims", $homepage, $matches, PREG_SET_ORDER);
|
||||
foreach ($matches as $val) {
|
||||
$pos = strpos($val[2],"/");
|
||||
$link = substr($val[2],1,-1);
|
||||
if($pos !== 1 && strpos($link, "upload") == true && strpos($link, "CentralAutoLogin") == false)
|
||||
echo "<img src=\"$link\">";
|
||||
$flickrjson = json_decode(file_get_contents("https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=2158f6c5d66e89778bd0d340953f1bcf&text=\"".str_replace('_', '+', $comname)."\"&license=2%2C3%2C4%2C5%2C6%2C9&sort=relevance&per_page=15&format=json&nojsoncallback=1"), true)["photos"]["photo"];
|
||||
foreach ($flickrjson as $val) {
|
||||
$iter++;
|
||||
$modaltext = "https://flickr.com/photos/".$val["owner"]."/".$val["id"];
|
||||
$authorlink = "https://flickr.com/people/".$val["owner"];
|
||||
$imageurl = 'http://farm' .$val["farm"]. '.static.flickr.com/' .$val["server"]. '/' .$val["id"]. '_' .$val["secret"]. '.jpg';
|
||||
echo "<img style='vertical-align:top' src=\"$imageurl\"><span style='cursor:pointer;color:blue;text-decoration:underline;' onclick='setModalText(".$iter.",\"".$val["title"]."\",\"".$modaltext."\", \"".$authorlink."\")'>".$iter."</span>";
|
||||
}
|
||||
}}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<br><br><br>
|
||||
|
||||
<table>
|
||||
<?php
|
||||
$excludelines = [];
|
||||
while($results=$result->fetchArray(SQLITE3_ASSOC))
|
||||
{
|
||||
$comname = preg_replace('/ /', '_', $results['Com_Name']);
|
||||
$comname = preg_replace('/\'/', '', $comname);
|
||||
$filename = "/By_Date/".$results['Date']."/".$comname."/".$results['File_Name'];
|
||||
|
||||
$excludefile = fopen($home."/BirdNET-Pi/scripts/disk_check_exclude.txt", "a") or die("Unable to open file!");
|
||||
$txt = $results['Date']."/".$comname."/".$results['File_Name']."\n".$results['Date']."/".$comname."/".$results['File_Name'].".png\n";
|
||||
fwrite($excludefile, $txt);
|
||||
array_push($excludelines, $results['Date']."/".$comname."/".$results['File_Name']);
|
||||
array_push($excludelines, $results['Date']."/".$comname."/".$results['File_Name'].".png");
|
||||
?>
|
||||
<tr>
|
||||
<form action="" method="GET">
|
||||
<input type="hidden" name="sort" value="<?php if(isset($_GET['sort'])){echo $_GET['sort'];}?>">
|
||||
<td class="relative"><a target="_blank" href="index.php?filename=<?php echo $results['File_Name']; ?>"><img class="copyimage" width=25 src="images/copy.png"></a><input type="hidden" name="view" value="Species Stats">
|
||||
<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 $results['MAX(Confidence)'];?><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>
|
||||
</tr>
|
||||
<?php
|
||||
editfile($home."/BirdNET-Pi/scripts/disk_check_exclude.txt","##start","##end",$excludelines);
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
@@ -183,4 +218,4 @@ fwrite($excludefile, $txt);
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -67,7 +67,7 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" ) {
|
||||
$statement0 = $db->prepare('SELECT Time, Com_Name, Sci_Name, Confidence, File_Name FROM detections WHERE Date == Date(\'now\', \'localtime\') '.$searchquery.' ORDER BY Time DESC LIMIT '.(intval($_GET['display_limit'])-40).',40');
|
||||
} else {
|
||||
// legacy mode
|
||||
$statement0 = $db->prepare('SELECT Time, Com_Name, Sci_Name, Confidence, File_Name FROM detections WHERE Date == Date(\'now\', \'localtime\') '.$searchquery.' ORDER BY Time DESC'.$searchquery);
|
||||
$statement0 = $db->prepare('SELECT Time, Com_Name, Sci_Name, Confidence, File_Name FROM detections WHERE Date == Date(\'now\', \'localtime\') '.$searchquery.' ORDER BY Time DESC '.$searchquery);
|
||||
}
|
||||
if($statement0 == False){
|
||||
echo "Database is busy";
|
||||
@@ -77,7 +77,15 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" ) {
|
||||
|
||||
?> <table>
|
||||
<?php
|
||||
$images = [];
|
||||
$iterations = 0;
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
while($todaytable=$result0->fetchArray(SQLITE3_ASSOC))
|
||||
{
|
||||
$iterations++;
|
||||
@@ -86,13 +94,35 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" ) {
|
||||
$comname = preg_replace('/\'/', '_', $comname);
|
||||
$filename = "/By_Date/".date('Y-m-d')."/".$comname."/".$todaytable['File_Name'];
|
||||
$sciname = preg_replace('/ /', '_', $todaytable['Sci_Name']);
|
||||
|
||||
if (!empty($config["FLICKR_API_KEY"])) {
|
||||
// if we already searched flickr for this species before, use the previous image rather than doing an unneccesary api call
|
||||
$key = array_search($comname, array_column($images, 0));
|
||||
if($key !== false) {
|
||||
$image = $images[$key];
|
||||
} else {
|
||||
$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", $comname)."&license=2%2C3%2C4%2C5%2C6%2C9&sort=relevance&per_page=5&orientation=square,portrait&media=photos&format=json&nojsoncallback=1"), true)["photos"]["photo"][0];
|
||||
$modaltext = "https://flickr.com/photos/".$flickrjson["owner"]."/".$flickrjson["id"];
|
||||
$authorlink = "https://flickr.com/people/".$flickrjson["owner"];
|
||||
$imageurl = 'http://farm' .$flickrjson["farm"]. '.static.flickr.com/' .$flickrjson["server"]. '/' .$flickrjson["id"]. '_' .$flickrjson["secret"]. '.jpg';
|
||||
array_push($images, array($comname,$imageurl,$flickrjson["title"], $modaltext, $authorlink));
|
||||
$image = $images[count($images)-1];
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?php if(isset($_GET['display_limit']) && is_numeric($_GET['display_limit'])){ ?>
|
||||
<tr class="relative" id="<?php echo $iterations; ?>">
|
||||
<td class="relative"><a target="_blank" href="index.php?filename=<?php echo $todaytable['File_Name']; ?>"><img class="copyimage" width=25 src="images/copy.png"></a><?php echo $todaytable['Time'];?><br>
|
||||
<td class="relative"><a target="_blank" href="index.php?filename=<?php echo $todaytable['File_Name']; ?>"><img class="copyimage" width=25 src="images/copy.png"></a>
|
||||
|
||||
<div class="centered_image_container">
|
||||
<?php if(!empty($config["FLICKR_API_KEY"])) { ?>
|
||||
<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">
|
||||
<?php } ?>
|
||||
|
||||
<?php echo $todaytable['Time'];?><br>
|
||||
<b><a class="a2" href="https://allaboutbirds.org/guide/<?php echo $comname;?>" target="top"><?php echo $todaytable['Com_Name'];?></a></b><br>
|
||||
<a class="a2" href="https://wikipedia.org/wiki/<?php echo $sciname;?>" target="top"><i><?php echo $todaytable['Sci_Name'];?></i></a><br>
|
||||
<b>Confidence:</b> <?php echo $todaytable['Confidence'];?><br>
|
||||
<b>Confidence:</b> <?php echo round((float)round($todaytable['Confidence'],2) * 100 ) . '%';?><br></div><br>
|
||||
<video onplay='setLiveStreamVolume(0)' onended='setLiveStreamVolume(1)' onpause='setLiveStreamVolume(1)' controls poster="<?php echo $filename.".png";?>" preload="none" title="<?php echo $filename;?>"><source preload="none" src="<?php echo $filename;?>"></video>
|
||||
</td>
|
||||
<?php } else { //legacy mode ?>
|
||||
@@ -100,7 +130,7 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" ) {
|
||||
<td><?php echo $todaytable['Time'];?><br></td><td>
|
||||
<b><a class="a2" href="https://allaboutbirds.org/guide/<?php echo $comname;?>" target="top"><?php echo $todaytable['Com_Name'];?></a></b><br>
|
||||
<a class="a2" href="https://wikipedia.org/wiki/<?php echo $sciname;?>" target="top"><i><?php echo $todaytable['Sci_Name'];?></i></a><br></td>
|
||||
<td><b>Confidence:</b> <?php echo $todaytable['Confidence'];?><br></td>
|
||||
<td><b>Confidence:</b> <?php echo round((float)round($todaytable['Confidence'],2) * 100 ) . '%';?><br></td>
|
||||
<td style="min-width:180px"><audio controls preload="none" title="<?php echo $filename;?>"><source preload="none" src="<?php echo $filename;?>"></video>
|
||||
</td>
|
||||
<?php } ?>
|
||||
@@ -132,6 +162,29 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" ) {
|
||||
</style>
|
||||
</head>
|
||||
<div class="viewdb">
|
||||
<dialog id="attribution-dialog">
|
||||
<h1 id="modalHeading"></h1>
|
||||
<p id="modalText"></p>
|
||||
<button onclick="hideDialog()">Close</button>
|
||||
</dialog>
|
||||
<script>
|
||||
var dialog = document.querySelector('dialog');
|
||||
dialogPolyfill.registerDialog(dialog);
|
||||
|
||||
function showDialog() {
|
||||
document.getElementById('attribution-dialog').showModal();
|
||||
}
|
||||
|
||||
function hideDialog() {
|
||||
document.getElementById('attribution-dialog').close();
|
||||
}
|
||||
|
||||
function setModalText(iter, title, text, authorlink, photolink) {
|
||||
document.getElementById('modalHeading').innerHTML = "Photo: \""+decodeURIComponent(title.replace("+"," "))+"\" Attribution";
|
||||
document.getElementById('modalText').innerHTML = "<div><img style='border-radius:5px' src='"+photolink+"'></div><br><div>Image link: <a target='_blank' href="+text+">"+text+"</a><br>Author link: <a target='_blank' href="+authorlink+">"+authorlink+"</a></div>";
|
||||
showDialog();
|
||||
}
|
||||
</script>
|
||||
<h3>Number of Detections</h3>
|
||||
<table>
|
||||
<tr>
|
||||
@@ -158,7 +211,7 @@ if(isset($_GET['ajax_detections']) && $_GET['ajax_detections'] == "true" ) {
|
||||
|
||||
<h3>Today's Detections — <input autocomplete="off" size="11" type="text" placeholder="Search..." id="searchterm" name="searchterm"></h3>
|
||||
|
||||
<div style="padding-bottom:10px" id="detections_table"></div>
|
||||
<div style="padding-bottom:10px" id="detections_table"><h3>Loading...</h3></div>
|
||||
|
||||
<button onclick="switchViews(this);" class="legacyview">Legacy view</button>
|
||||
|
||||
|
||||
@@ -54,3 +54,7 @@ if grep bash $HOME/BirdNET-Pi/templates/web_terminal.service;then
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart web_terminal.service
|
||||
fi
|
||||
[ -L ~/BirdSongs/Extracted/static ] || ln -sf ~/BirdNET-Pi/homepage/static ~/BirdSongs/Extracted
|
||||
if ! grep FLICKR_API_KEY /etc/birdnet/birdnet.conf &>/dev/null;then
|
||||
sudo -u$USER echo "FLICKR_API_KEY=" >> /etc/birdnet/birdnet.conf
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user