From f56385f0e4df0020d658937cabf4728cc3dc2fb5 Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Tue, 21 May 2024 14:41:17 +0200 Subject: [PATCH] On the fly species converter --- .../rootfs/etc/cont-init.d/71-newfeatures.sh | 8 +- birdnet-pi/rootfs/helpers/change_id/bird.svg | 22 - .../change_id/birdnet_changeidentification.sh | 117 --- birdnet-pi/rootfs/helpers/change_id/play.php | 705 -------------- birdnet-pi/rootfs/helpers/change_id/style.css | 894 ------------------ .../{convert_list => }/convert_list.php | 0 .../rootfs/helpers/convert_list/server.py | 342 ------- .../rootfs/helpers/convert_list/views.php | 457 --------- .../helpers/{convert_list => }/views.add | 0 9 files changed, 4 insertions(+), 2541 deletions(-) delete mode 100644 birdnet-pi/rootfs/helpers/change_id/bird.svg delete mode 100755 birdnet-pi/rootfs/helpers/change_id/birdnet_changeidentification.sh delete mode 100644 birdnet-pi/rootfs/helpers/change_id/play.php delete mode 100644 birdnet-pi/rootfs/helpers/change_id/style.css rename birdnet-pi/rootfs/helpers/{convert_list => }/convert_list.php (100%) delete mode 100644 birdnet-pi/rootfs/helpers/convert_list/server.py delete mode 100644 birdnet-pi/rootfs/helpers/convert_list/views.php rename birdnet-pi/rootfs/helpers/{convert_list => }/views.add (100%) diff --git a/birdnet-pi/rootfs/etc/cont-init.d/71-newfeatures.sh b/birdnet-pi/rootfs/etc/cont-init.d/71-newfeatures.sh index 343c88044..cfef4efaa 100755 --- a/birdnet-pi/rootfs/etc/cont-init.d/71-newfeatures.sh +++ b/birdnet-pi/rootfs/etc/cont-init.d/71-newfeatures.sh @@ -81,19 +81,19 @@ if bashio::config.true "SPECIES_CONVERTER"; then # Remove the extracted lines from the original file sed -i '/if(\$_GET\['\''view'\''\] == "File"){/,$d' "$HOME"/BirdNET-Pi/homepage/views.php # Add new text - cat "/helpers/convert_list/views.add" >> "$HOME"/BirdNET-Pi/homepage/views.php + cat "/helpers/views.add" >> "$HOME"/BirdNET-Pi/homepage/views.php cat "$HOME"/BirdNET-Pi/homepage/views.php.temp >> "$HOME"/BirdNET-Pi/homepage/views.php # Clean up: Remove the temporary file rm "$HOME"/BirdNET-Pi/homepage/views.php.temp fi - + # Add the converter script if [ ! -f "$HOME"/BirdNET-Pi/scripts/convert_list.php ]; then - mv -f /helpers/convert_list/convert_list.php "$HOME"/BirdNET-Pi/scripts/convert_list.php + mv -f /helpers/convert_list.php "$HOME"/BirdNET-Pi/scripts/convert_list.php chown pi:pi "$HOME"/BirdNET-Pi/scripts/convert_list.php chmod 664 "$HOME"/BirdNET-Pi/scripts/convert_list.php fi - + # Change server if ! grep -q "converted_entry" "$HOME"/BirdNET-Pi/scripts/server.py; then sed -i "/INTERPRETER, M_INTERPRETER, INCLUDE_LIST, EXCLUDE_LIST/c INTERPRETER, M_INTERPRETER, INCLUDE_LIST, EXCLUDE_LIST, CONVERT_LIST = (None, None, None, None, None)" "$HOME"/BirdNET-Pi/scripts/server.py diff --git a/birdnet-pi/rootfs/helpers/change_id/bird.svg b/birdnet-pi/rootfs/helpers/change_id/bird.svg deleted file mode 100644 index 2fa6ab0dc..000000000 --- a/birdnet-pi/rootfs/helpers/change_id/bird.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/birdnet-pi/rootfs/helpers/change_id/birdnet_changeidentification.sh b/birdnet-pi/rootfs/helpers/change_id/birdnet_changeidentification.sh deleted file mode 100755 index 9257c65f3..000000000 --- a/birdnet-pi/rootfs/helpers/change_id/birdnet_changeidentification.sh +++ /dev/null @@ -1,117 +0,0 @@ -#!/bin/bash - -# This scripts allows to change the identification of a Birdnet-pi detection - -################# -# SET VARIABLES # -################# - -HOME="/home/pi" -source /etc/birdnet/birdnet.conf &>/dev/null - -# Get arguments -OLDNAME="$1" #OLDNAME="Mésange_charbonnière-78-2024-05-02-birdnet-RTSP_1-18:14:08.mp3" -NEWNAME="$2" #NEWNAME="Lapinus atricapilla_Lapinu à tête noire" - -# Set log level -OUTPUT_TYPE="${3:-debug}" # Set 3rd argument to debug to have all outputs - -# Ask for user input if no arguments -if [ -z "$OLDNAME" ]; then read -r -p 'OLDNAME (finishing by mp3): ' OLDNAME; fi -if [ -z "$NEWNAME" ]; then read -r -p 'NEWNAME (sciname_commoname): ' NEWNAME; fi - -# Fixed values -LABELS_FILE="$HOME/BirdNET-Pi/model/labels.txt" -DB_FILE="$HOME/BirdNET-Pi/scripts/birds.db" -DETECTIONS_TABLE="detections" - -################### -# VALIDITY CHECKS # -################### - -# Check if files exist -if [ ! -f "$LABELS_FILE" ]; then echo "$LABELS_FILE doesn't exist, exiting" && exit 1; fi -if [ ! -f "$DB_FILE" ]; then echo "$DB_FILE doesn't exist, exiting" && exit 1; fi - -# Check if inputs are valid -if [[ "$1" != *".mp3" ]]; then - echo "The first argument should be a filename starting with the common name of the bird and finishing by mp3!" - echo "Instead, it is : $1" - exit 1 -elif [[ "$2" != *"_"* ]]; then - echo "The second argument should be in the format : \"scientific name_common name\"" - echo "Instead, it is : $2" - exit 1 -fi - -# Check if $NEWNAME is found in the file $LABELS_FILE -if ! grep -q "$NEWNAME" "$LABELS_FILE"; then - echo "Error: $NEWNAME not found in $LABELS_FILE" - exit 1 -fi - -# Check if the common name as the same as the first -OLDNAME_space="${OLDNAME//_/ }" -if [[ "${OLDNAME_space%%-*}" == "${NEWNAME#*_}" ]]; then - echo "Error: $OLDNAME has the same common name as $NEWNAME" - exit 1 -fi - -################## -# EXECUTE SCRIPT # -################## - -# Intro -[[ "$OUTPUT_TYPE" == "debug" ]] && echo "Starting to modify $OLDNAME to $NEWNAME" - -# Get the line where the column "File_Name" matches exactly $OLDNAME -IFS='|' read -r OLDNAME_sciname OLDNAME_comname OLDNAME_date < <(sqlite3 "$DB_FILE" "SELECT Sci_Name, Com_Name, Date FROM $DETECTIONS_TABLE WHERE File_Name = '$OLDNAME' LIMIT 1;") - -if [[ -z "$OLDNAME_sciname" ]]; then - echo "Error: No line matching $OLDNAME in $DB_FILE" - exit 1 -fi - -# Extract the part before the _ from $NEWNAME -NEWNAME_comname="${NEWNAME#*_}" -NEWNAME_sciname="${NEWNAME%%_*}" - -# Replace spaces with underscores -NEWNAME_comname2="${NEWNAME_comname// /_}" -OLDNAME_comname2="${OLDNAME_comname// /_}" - -# Replace OLDNAME_comname2 with NEWNAME_comname2 in OLDNAME -NEWNAME_filename="${OLDNAME//$OLDNAME_comname2/$NEWNAME_comname2}" - -[[ "$OUTPUT_TYPE" == "debug" ]] && echo "This script will change the identification $OLDNAME from $OLDNAME_comname to ${NEWNAME#*_}" - -######################## -# EXECUTE : MOVE FILES # -######################## - -# Check if the file exists -FILE_PATH="$HOME/BirdSongs/Extracted/By_Date/$OLDNAME_date/$OLDNAME_comname2/$OLDNAME" -if [[ -f $FILE_PATH ]]; then - # Ensure the new directory exists - NEW_DIR="$HOME/BirdSongs/Extracted/By_Date/$OLDNAME_date/$NEWNAME_comname2" - mkdir -p "$NEW_DIR" - - # Move and rename the file - mv "$FILE_PATH" "$NEW_DIR/$NEWNAME_filename" - mv "$FILE_PATH".png "$NEW_DIR/$NEWNAME_filename".png - - [[ "$OUTPUT_TYPE" == "debug" ]] && echo "Files moved!" -else - echo "Error: File $FILE_PATH does not exist" -fi - -################################### -# EXECUTE : UPDATE DATABASE FILES # -################################### - -# Update the database -sqlite3 "$DB_FILE" "UPDATE $DETECTIONS_TABLE SET Sci_Name = '$NEWNAME_sciname', Com_Name = '$NEWNAME_comname', File_Name = '$NEWNAME_filename' WHERE File_Name = '$OLDNAME';" - -[[ "$OUTPUT_TYPE" == "debug" ]] && echo "Database entry removed" - -[[ "$OUTPUT_TYPE" == "debug" ]] && echo "All done!" diff --git a/birdnet-pi/rootfs/helpers/change_id/play.php b/birdnet-pi/rootfs/helpers/change_id/play.php deleted file mode 100644 index 0c1827fe8..000000000 --- a/birdnet-pi/rootfs/helpers/change_id/play.php +++ /dev/null @@ -1,705 +0,0 @@ -busyTimeout(1000); - -if(isset($_GET['deletefile'])) { - ensure_authenticated('You must be authenticated to delete files.'); - if (preg_match('~^.*(\.\.\/).+$~', $_GET['deletefile'])) { - echo "Error"; - die(); - } - $db_writable = new SQLite3('./scripts/birds.db', SQLITE3_OPEN_READWRITE); - $db->busyTimeout(1000); - $statement1 = $db_writable->prepare('DELETE FROM detections WHERE File_Name = :file_name LIMIT 1'); - ensure_db_ok($statement1); - $statement1->bindValue(':file_name', explode("/", $_GET['deletefile'])[2]); - $file_pointer = $home."/BirdSongs/Extracted/By_Date/".$_GET['deletefile']; - if (!exec("sudo rm $file_pointer 2>&1 && sudo rm $file_pointer.png 2>&1", $output)) { - echo "OK"; - } else { - echo "Error - file deletion failed : " . implode(", ", $output) . "
"; - } - $result1 = $statement1->execute(); - if ($result1 === false || $db_writable->changes() === 0) { - echo "Error - database line deletion failed : " . $db_writable->lastErrorMsg(); - } - $db_writable->close(); - die(); -} - -if(isset($_GET['excludefile'])) { - ensure_authenticated('You must be authenticated to change the protection of files.'); - 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\n"); - } - 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(); - } -} - -if(isset($_GET['getlabels'])) { - $labels = file('/home/pi/BirdNET-Pi/model/labels.txt', FILE_IGNORE_NEW_LINES); - echo json_encode($labels); - die(); -} - -if(isset($_GET['changefile']) && isset($_GET['newname'])) { - ensure_authenticated('You must be authenticated to delete files.'); - if (preg_match('~^.*(\.\.\/).+$~', $_GET['changefile'])) { - echo "Error"; - die(); - } - $oldname = basename(urldecode($_GET['changefile'])); - $newname = urldecode($_GET['newname']); - if (!exec("$home/BirdNET-Pi/scripts/birdnet_changeidentification.sh \"$oldname\" \"$newname\" log_errors 2>&1", $output)) { - echo "OK"; - } else { - echo "Error : " . implode(", ", $output) . "
"; - } - die(); -} - -$shifted_path = $home."/BirdSongs/Extracted/By_Date/shifted/"; - -if(isset($_GET['shiftfile'])) { - ensure_authenticated('You cannot shift files for this installation'); - - $filename = $_GET['shiftfile']; - $pp = pathinfo($filename); - $dir = $pp['dirname']; - $fn = $pp['filename']; - $ext = $pp['extension']; - $pi = $home."/BirdSongs/Extracted/By_Date/"; - - if(isset($_GET['doshift'])) { - $freqshift_tool = $config['FREQSHIFT_TOOL']; - - if ($freqshift_tool == "ffmpeg") { - $cmd = "sudo /usr/bin/nohup /usr/bin/ffmpeg -y -i ".escapeshellarg($pi.$filename)." -af \"rubberband=pitch=".$config['FREQSHIFT_LO']."/".$config['FREQSHIFT_HI']."\" ".escapeshellarg($shifted_path.$filename).""; - shell_exec("sudo mkdir -p ".$shifted_path.$dir." && ".$cmd); - - } else if ($freqshift_tool == "sox") { - //linux.die.net/man/1/sox - $soxopt = "-q"; - $soxpitch = $config['FREQSHIFT_PITCH']; - $cmd = "sudo /usr/bin/nohup /usr/bin/sox ".escapeshellarg($pi.$filename)." ".escapeshellarg($shifted_path.$filename)." pitch ".$soxopt." ".$soxpitch; - shell_exec("sudo mkdir -p ".$shifted_path.$dir." && ".$cmd); - } - } else { - $cmd = "sudo rm -f " . escapeshellarg($shifted_path.$filename); - shell_exec($cmd); - } - - echo "OK"; - die(); -} - -if(isset($_GET['bydate'])){ - $statement = $db->prepare('SELECT DISTINCT(Date) FROM detections GROUP BY Date ORDER BY Date DESC'); - ensure_db_ok($statement); - $result = $statement->execute(); - $view = "bydate"; - - #Specific Date -} elseif(isset($_GET['date'])) { - $date = $_GET['date']; - session_start(); - $_SESSION['date'] = $date; - if(isset($_GET['sort']) && $_GET['sort'] == "occurrences") { - $statement = $db->prepare("SELECT DISTINCT(Com_Name) FROM detections WHERE Date == \"$date\" GROUP BY Com_Name ORDER BY COUNT(*) DESC"); - } else { - $statement = $db->prepare("SELECT DISTINCT(Com_Name) FROM detections WHERE Date == \"$date\" ORDER BY Com_Name"); - } - ensure_db_ok($statement); - $result = $statement->execute(); - $view = "date"; - - #By Species -} elseif(isset($_GET['byspecies'])) { - if(isset($_GET['sort']) && $_GET['sort'] == "occurrences") { - $statement = $db->prepare('SELECT DISTINCT(Com_Name) FROM detections GROUP BY Com_Name ORDER BY COUNT(*) DESC'); - } else { - $statement = $db->prepare('SELECT DISTINCT(Com_Name) FROM detections ORDER BY Com_Name ASC'); - } - session_start(); - ensure_db_ok($statement); - $result = $statement->execute(); - $view = "byspecies"; - - #Specific Species -} elseif(isset($_GET['species'])) { - $species = htmlspecialchars_decode($_GET['species'], ENT_QUOTES); - session_start(); - $_SESSION['species'] = $species; - $statement = $db->prepare("SELECT * FROM detections WHERE Com_Name == \"$species\" ORDER BY Com_Name"); - ensure_db_ok($statement); - $statement3 = $db->prepare("SELECT Date, Time, Sci_Name, MAX(Confidence), File_Name FROM detections WHERE Com_Name == \"$species\" ORDER BY Com_Name"); - ensure_db_ok($statement3); - $result = $statement->execute(); - $result3 = $statement3->execute(); - $view = "species"; -} else { - unset($_SESSION['species']); - unset($_SESSION['date']); - $view = "choose"; -} - -if (get_included_files()[0] === __FILE__) { - echo ' - - - - - '; -} - -?> - - - -
- -
-
- - - - -
-
-
- -
- - -fetchArray(SQLITE3_ASSOC)){ - $date = $results['Date']; - if(realpath($home."/BirdSongs/Extracted/By_Date/".$date) !== false){ - echo "";}} - - #By Species - } elseif($view == "byspecies") { - $birds = array(); - while($results=$result->fetchArray(SQLITE3_ASSOC)) - { - $name = $results['Com_Name']; - $birds[] = $name; - } - - if(count($birds) > 45) { - $num_cols = 3; - } else { - $num_cols = 1; - } - $num_rows = ceil(count($birds) / $num_cols); - - for ($row = 0; $row < $num_rows; $row++) { - echo ""; - - for ($col = 0; $col < $num_cols; $col++) { - $index = $row + $col * $num_rows; - - if ($index < count($birds)) { - ?> - - "; - } - } - - echo ""; - } - } elseif($view == "date") { - $birds = array(); -while($results=$result->fetchArray(SQLITE3_ASSOC)) -{ - $name = $results['Com_Name']; - $dir_name = str_replace("'", '', $name); - if(realpath($home."/BirdSongs/Extracted/By_Date/".$date."/".str_replace(" ", "_", $dir_name)) !== false){ - $birds[] = $name; - } -} - -if(count($birds) > 45) { - $num_cols = 3; -} else { - $num_cols = 1; -} -$num_rows = ceil(count($birds) / $num_cols); - -for ($row = 0; $row < $num_rows; $row++) { - echo ""; - - for ($col = 0; $col < $num_cols; $col++) { - $index = $row + $col * $num_rows; - - if ($index < count($birds)) { - ?> - - "; - } - } - - echo ""; -} - - #Choose - } else { - echo " - "; - } - - echo "
-
- -
- -
-
"; -} - -#Specific Species -if(isset($_GET['species'])){ ?> -
-
- - - - -
- type="checkbox" name="only_excluded" onChange="submit()"> - -
-
-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 { - 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"); - } -} -ensure_db_ok($statement2); -$result2 = $statement2->execute(); -$num_rows = 0; -while ($result2->fetchArray(SQLITE3_ASSOC)) { - $num_rows++; -} -$result2->reset(); // reset the pointer to the beginning of the result set -echo " - - - "; - $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']; - $filename_shifted = "/By_Date/shifted/".$date."/".$comname."/".$results['File_Name']; - $filename_png = $filename . ".png"; - $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'])) { - continue; - } - $iter++; - - if($num_rows < 100){ - $imageelem = ""; - } else { - $imageelem = ""; - } - - if($config["FULL_DISK"] == "purge") { - if(!in_array($filename_formatted, $disk_check_exclude_arr)) { - $imageicon = "images/unlock.svg"; - $title = "This file will be deleted when disk space needs to be freed (>95% usage)."; - $type = "add"; - } else { - $imageicon = "images/lock.svg"; - $title = "This file is excluded from being purged."; - $type = "del"; - } - - if(file_exists($shifted_path.$filename_formatted)) { - $shiftImageIcon = "images/unshift.svg"; - $shiftTitle = "This file has been shifted down in frequency."; - $shiftAction = "unshift"; - $filename = $filename_shifted; - } else { - $shiftImageIcon = "images/shift.svg"; - $shiftTitle = "This file is not shifted in frequency."; - $shiftAction = "shift"; - } - - echo " - - "; - } else { - echo " - - "; - } - - }if($iter == 0){ echo "";}echo "
$name
- - - - - $date $time
$confidence
- - ".$imageelem." -
$date $time
$confidence -
- ".$imageelem." -
No recordings were found.

They may have been deleted to make space for new recordings. You can prevent this from happening in the future by clicking the icon in the top right of a recording.
You can also modify this behavior globally under \"Full Disk Behavior\" here.
";} - - if(isset($_GET['filename'])){ - $name = $_GET['filename']; - $statement2 = $db->prepare("SELECT * FROM detections where File_name == \"$name\" ORDER BY Date DESC, Time DESC"); - ensure_db_ok($statement2); - $result2 = $statement2->execute(); - echo " - - - "; - 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']; - $filename_shifted = "/By_Date/shifted/".$date."/".$comname."/".$results['File_Name']; - $filename_png = $filename . ".png"; - $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"))); - } else { - $disk_check_exclude_arr = []; - } - - if($config["FULL_DISK"] == "purge") { - if(!in_array($filename_formatted, $disk_check_exclude_arr)) { - $imageicon = "images/unlock.svg"; - $title = "This file will be deleted when disk space needs to be freed (>95% usage)."; - $type = "add"; - } else { - $imageicon = "images/lock.svg"; - $title = "This file is excluded from being purged."; - $type = "del"; - } - - if(file_exists($shifted_path.$filename_formatted)) { - $shiftImageIcon = "images/unshift.svg"; - $shiftTitle = "This file has been shifted down in frequency."; - $shiftAction = "unshift"; - $filename = $filename_shifted; - } else { - $shiftImageIcon = "images/shift.svg"; - $shiftTitle = "This file is not shifted in frequency."; - $shiftAction = "shift"; - } - - echo " - - "; - } else { - echo " - - "; - } - - }echo "
$name
- - - - -$date $time
$confidence
- -
$date $time
$confidence -
-
";} - echo "
"; -if (get_included_files()[0] === __FILE__) { - echo ''; -} diff --git a/birdnet-pi/rootfs/helpers/change_id/style.css b/birdnet-pi/rootfs/helpers/change_id/style.css deleted file mode 100644 index 3274e2296..000000000 --- a/birdnet-pi/rootfs/helpers/change_id/style.css +++ /dev/null @@ -1,894 +0,0 @@ -@font-face { - font-family: 'Roboto Flex' ; - src: url('static/RobotoFlex-Regular.ttf') format('truetype'); -} - -* { - font-family: 'Roboto Flex', sans-serif; - box-sizing: border-box; - font-size: medium; -} - -a { - text-decoration: none; -} - -a:hover { - font-weight: bold; -} - -h3 { - text-align: center; - margin-bottom: 12px; -} - -iframe { - border: none; - height: 85%; - width: 100%; - position: fixed; -} - -body { - margin: 0; - background-color: rgb(119, 196, 135); -} - -table { - background-color: transparent; - border-collapse: collapse; - 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 { - padding: 10px; - vertical-align: top; - background-color: rgb(219, 255, 235); - font-weight: lighter; - text-align: center; -} - -th { - padding: 12px; - font-weight: bold; - height: auto; - text-align: center; -} - -audio, video{ - max-height: 100%; - max-width: 100%; -} - -label { - font-weight: bold; -} - -hr { - border-color:black; -} - -button { - background-color: transparent; - border: none; - color: black; - cursor: pointer; - transition:background-color 0.2s; -} - -.disabled{ - cursor: not-allowed; - pointer-events: none; - opacity:0.5; -} - -button:hover { - color: blue; -} - -.row { - display: flex; -} - -.centered { - text-align: center; - display: block; - width: auto; - margin-left: auto; - margin-right: auto; -} - -.banner { - height: 7%; - text-align: center; -} - -.banner h1 { - padding: .5em; - letter-spacing: 5px; -} - -.banner audio,.banner form { - float: right; - width: 120px; - margin-left: -120px; - margin-right: auto; -} - -.banner button { - padding-top: 8px; - font-weight: bold; - letter-spacing: 1px; -} - -.banner a { - text-decoration: none; - color: black; - font-size: x-large; -} - -.logo img { - position: absolute; - top: 0; - left: 0; - padding: 10px; -} - -.modal { - display: none; - position: fixed; - z-index: 1; - left: 0; - top: 0; - width: 100%; - height: 100%; - overflow: auto; - background-color: rgba(0,0,0,0.4); -} - -.modal-content { - background-color: #fefefe; - margin: 15% auto; - padding: 20px; - border: 1px solid #888; - width: 80%; - text-align: center; /* Center the content */ -} - -@media (max-width: 768px) { - .modal-content { - width: 95%; - margin: 10% auto; - } - #labelDropdown { - max-width: 100%; - overflow-x: auto; - } -} - -.topnav { - background-color: rgb(159, 226, 155); - display: flex; - flex: 65%; - width: 65%; - min-width: min-content; - justify-content: space-between; - margin-left: auto; - margin-right: auto; - margin-bottom: 15px; - box-shadow: 0px 0px 28px 1px rgba(0, 0, 0, 0.10) !important; - border-radius: 4px; -} - -.topimage { - width:175px; - display:initial !important; -} - -.topnav form { - margin: 0; - padding: 0; -} - -.topnav button { - background-color: transparent; - text-align: center; - padding: 14px 16px; - width: auto; - vertical-align: middle; -} - -.topnav button:hover { - background-color: rgb(219, 255, 235); - color: black; -} - -.topnav button.active { - background-color: #04AA6D; - color: white; -} - -.topnav .button-hover { - background-color: rgb(219, 255, 235) !important; - color: black; -} - -.topnav .icon { - display: none; -} - -.overview th { - background-color: rgb(219, 255, 235); - text-align: center; - padding: 12px; -} - -.overview td { - vertical-align: middle; -} - -.overview div img { - max-height: 100%; - display: flex; - justify-content: center; - margin-right: auto; - margin-left: auto; -} - -.overview .chart { - margin-top: 10px; -} -.overview-stats { - display: flex; - justify-content: center; -} - -.left-column { - flex: 10%; - padding-left: 10px; -} - -.right-column { - flex: 90%; - margin-right: 10%; -} - -.stats td { - vertical-align: middle; -} - -.stats table { - height: auto; -} - -.stats button:hover { - color: blue; -} - -.overview button, .center button{ - font-weight: bold; - color: blue; -} - -.history table,.history img { - width: auto; - margin-left: auto; - margin-right: auto; -} - -.views { - transition: opacity 0.3s; - -webkit-transition: opacity 0.3s; -} - -.views .centered button { - background-color: rgb(219, 255, 235); - padding: 12px; - transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1); - box-shadow:0px 3px 1px -2px rgb(0 0 0 / 20%), 0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%); -} - -.views .centered button:hover { - background-color: rgb(159, 226, 155); - color: black; - box-shadow:0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%); -} - -.settings { - padding: 12px; -} - -.settings h2 { - margin-top: 0px; -} - -.settings p { - margin-bottom: 0px; -} - -.settings h3 { - text-align: left; -} - -.settings button { - background-color: rgb(219, 255, 235); - padding: 12px; -} - -.settings button:hover { - background-color: rgb(159, 226, 155); - color: black; -} - -.float button { - margin-top: 6px; - margin-bottom: 6px; -} - -.customlabels,.customlabels2 { - float:left; -} - -.customlabels table { - height: 100%; -} - -.customlabels td,.customlabels2 td { - border:none; - background-color: transparent; - vertical-align: middle; -} - -.customlabels button,.customlabels2 button { - padding: 12px; - background-color: rgb(219, 255, 235); - - width: 100%; -} - -.column1, .column3 { - width: 45%; -} - -.smaller { - width: 100%; - display: none; - margin-left: auto; - margin-right: auto; -} - -.column2 { - text-align: center; - width: 10%; - height: 80%; -} - -.column4 { - text-align: justify; - width: 10%; - height: 50%; -} - -.column1 form,.column3 form { - width: 80%; - margin-left: auto; - margin-right: auto; -} - -.column1 select,.column3 select { - height: 80%; - width: 100%; -} - -.spectrogram { - width:50% -} - -.full { - width:100%; -} - -.logbutton, .navbuttons { - float: left; -} - -.systemcontrols form,.servicecontrols form { - /*text-align: center;*/ -} - -.servicecontrols button { - background-color: rgb(219, 255, 235); - padding: 12px; - width: 50%; -} - -.systemcontrols button { - background-color: rgb(219, 255, 235); - display: block; - padding: 12px; - width: 50%; - margin: 16px auto; -} - -.servicecontrols button { - width: 20%; -} - -.btn-group-center { - text-align:center; - /*align-content: center;*/ - margin: 16px auto; - position:relative; - /*display:inline-block;*/ -} - -.slider { - -webkit-appearance: none; - width: 33%; - height: 15px; - border-radius: 5px; - outline: none; - opacity: 0.7; - -webkit-transition: .2s; - transition: opacity .2s; -} - -.slider::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 25px; - height: 25px; - border-radius: 50%; - background: #04AA6D; - cursor: pointer; -} - -.slider::-moz-range-thumb { - width: 25px; - height: 25px; - border-radius: 50%; - background: #04AA6D; - cursor: pointer; -} - -#body::-webkit-scrollbar { - /*display:none*/ -} - -@media screen and (max-width: 1290px) { - .column1,.column2,.column3,.column4 { - height: 90% - } - .left-column { - display: none; - } - .right-column { - flex: 100%; - margin: 0; - } - img { - max-width: 100%; - } - .overview { - overflow-x: hidden; - } - .overview .right-column .chart img { - margin-left: 5%; - margin-right: auto; - margin-top: 10px; - } -} -@media screen and (max-width: 1000px) { - .customlabels form,.customlabels2 form { - width: 95%; - } - .column1, .column3 { - width: 50%; - height: 100%; - } - .column1 select,.column3 select { - height: 70%; - } - .column2,.column4 { - display: none; - } - .smaller{ - display: block; - } - .systemcontrols button,.servicecontrols button { - width: 60%; - padding: 12px; - background-color: rgb(219, 255, 235); - } - .topnav { - flex: 100%; - width: 100%; - } - .topnav button {display: none;} - .topnav button.icon { - padding: 0; - margin: 0; - display: block; - width: 100%; - margin-left: auto; - margin-right: auto; - } - .banner { - height: auto; - } - .banner img { - display: none; - } - .logo img { - display: block; - width: 60px; - height: 60px; - } - .topnav.responsive {position: relative;} - .topnav.responsive button { - display: block; - text-align: center; - } -} - -@media screen and (max-width: 800px) { - .column1, .column3 { - width: 100%; - } - .systemcontrols button,.servicecontrols button { - width: 80%; - padding: 12px; - background-color: rgb(219, 255, 235); - } - .stats img { - width: 100%; - margin-left:auto; - margin-right:auto; - } - .overview img { - width: 100% - } - .banner { - height: auto; - margin-left: 60px; - } - .banner img { - display: none; - } - .stream { - float: right; - display: block; - width: 100px; - } - .logo img { - display: block; - width: 60px; - height: 60px; - } - .play table,.overview table,.stats table { - width: 100%; - } - .topnav { - flex: 100%; - width: 100%; - flex-direction: column; - } - .topnav button { - font-size: large; - width: 100% - } - .topnav button {display: none;} - .topnav button.icon { - margin: 0; - padding: 0; - display: block; - width: 100%; - margin-left: auto; - margin-right: auto; - } - .topnav.responsive {position: relative;} - .topnav.responsive button { - display: block; - } - .left-column { - display: none; - } - .left { - display:none; - } -} - -.copyimage { - position:absolute; - top:7px; - right:7px; - width:25px !important; - height:25px !important; -} - -.copyimage-mobile { - width: 16px !important; - height: 16px !important; -} - -.relative { - position:relative; -} - -.sortbutton { - margin-top:10px; - font-size:x-large; - background:#dbffeb; - padding:5px; - box-shadow: 0px 0px 5px 2px rgba(0, 0, 0, 0.10); -} - -button.legacyview { - display: none; - color:gray; - margin:5px; - float:right; - z-index:100; - position:relative; - font-size:small; - background:#dbffeb; - padding:5px; - transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1); - box-shadow:0px 3px 1px -2px rgb(0 0 0 / 20%), 0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%); -} - -button.legacyview:hover { - box-shadow:0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%); -} - -button.loadmore { - margin-top:10px; - font-size:x-large; - background:#dbffeb; - padding:10px; - transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1); - box-shadow:0px 3px 1px -2px rgb(0 0 0 / 20%), 0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%); - -} - -button.loadmore:hover { - box-shadow:0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%); -} - -#searchterm { - transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1); -} - -#searchterm:hover { - box-shadow:0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%); -} - -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; - transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1); - box-shadow:0px 3px 1px -2px rgb(0 0 0 / 20%), 0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%); -} - -.centered form#views button:hover { - box-shadow:0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%); -} - -dl { - margin: 1em 0 0 1em; -} -dt { - float: left; - clear: left; - width: auto; - text-align: left; - font-weight: bold; - color: black; -} -dd::before { - content: ": "; -} - -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 { - transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1); - box-shadow:0px 3px 1px -2px rgb(0 0 0 / 20%), 0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%); - cursor:pointer; - height:95%; - position:absolute; - right:110%; - top:0px; - border-radius: 5px; - width:unset; -} - -.centered_image_container img.img1:hover{ - opacity:0.8; - box-shadow:0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%); -} - -#birdimage { - transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1); - box-shadow:0px 3px 1px -2px rgb(0 0 0 / 20%), 0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%); - cursor:pointer; - border-radius: 5px; -} - -#birdimage:hover { - opacity:0.8; - box-shadow:0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%); -} - -.centered_image_container * { - font-size:19px !important; -} - -.centered_image_container form { - margin-bottom:0px; -} - -.brbanner { - padding:15px; - background-color:rgb(159, 226, 155); - text-align:center; - font-size:large; -} - -#gain.centered { - margin-bottom:10px; -} - -.updatenumber { - margin-left:5px; - position:absolute; - display:inline-block; - background-color:#c8191a; - color:white; - width:20px; - line-height:20px; - border-radius:12px; - text-align:center; - font-size:small; -} - -form#views button .updatenumber { - position:initial; - margin-left:0px; -} - -#detections_table_overview table { - width:944px; -} - -#recent_detection_middle_td{ - width:33%; -} -@media screen and (max-width:500px) { - #recent_detection_middle_td{ - width:66%; - } -} -#recent_detection_middle_td img{ - width:unset !important; - height:75px; - float:left; -} - -.settingstable { - margin-left:unset; - margin-right:unset; -} -.settingstable td { - text-align:unset; -} -.settingstable textarea { - width:100%; - margin-top:10px; -} -.settingstable h2 { - font-size:x-large; -} -.plaintable { - box-shadow: unset; -} -.plaintable td { - padding: unset; -} - -.brbanner h1 { - margin:0px; - font-size: xx-large; -} - -.testbtn { - background:#77c487 !important; -} - -pre.bash { - background-color: black; - color: white; - font-size: medium ; - font-family: Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace; - width: 100%; - display: inline-block; -} -pre#timer.bash { - display:unset; - width:unset; -} - -#toolsbtn { - min-width: max-content; -} - -#showpassword { - cursor:pointer; - margin-left:2px; - height:5px; - line-height:5px; - padding:3px; - background-color:#9fe29b -} - -#newrtspstream{ - cursor: pointer; - margin-left: 2px; - height: 5px; - line-height: 5px; - padding: 3px; - background-color: #9fe29b; -} - -.exclude_species_list_option_highlight { - color: black; - background-color: rgb(119, 196, 135); - font-weight: bolder; -} - -#ddnewline::before { - content: none; -} diff --git a/birdnet-pi/rootfs/helpers/convert_list/convert_list.php b/birdnet-pi/rootfs/helpers/convert_list.php similarity index 100% rename from birdnet-pi/rootfs/helpers/convert_list/convert_list.php rename to birdnet-pi/rootfs/helpers/convert_list.php diff --git a/birdnet-pi/rootfs/helpers/convert_list/server.py b/birdnet-pi/rootfs/helpers/convert_list/server.py deleted file mode 100644 index df348913e..000000000 --- a/birdnet-pi/rootfs/helpers/convert_list/server.py +++ /dev/null @@ -1,342 +0,0 @@ -import datetime -import logging -import math -import operator -import os -import time - -import librosa -import numpy as np - -from utils.helpers import get_settings, Detection - -os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' -os.environ['CUDA_VISIBLE_DEVICES'] = '' - -try: - import tflite_runtime.interpreter as tflite -except BaseException: - from tensorflow import lite as tflite - -log = logging.getLogger(__name__) - - -userDir = os.path.expanduser('~') -INTERPRETER, M_INTERPRETER, INCLUDE_LIST, EXCLUDE_LIST, CONVERT_LIST, CONVERT_DICT = (None, None, None, None, None, None) -PREDICTED_SPECIES_LIST = [] -model, priv_thresh, sf_thresh = (None, None, None) - -mdata, mdata_params = (None, None) - - -def loadModel(): - - global INPUT_LAYER_INDEX - global OUTPUT_LAYER_INDEX - global MDATA_INPUT_INDEX - global CLASSES - - log.info('LOADING TF LITE MODEL...') - - # Load TFLite model and allocate tensors. - # model will either be BirdNET_GLOBAL_6K_V2.4_Model_FP16 (new) or BirdNET_6K_GLOBAL_MODEL (old) - modelpath = userDir + '/BirdNET-Pi/model/'+model+'.tflite' - myinterpreter = tflite.Interpreter(model_path=modelpath, num_threads=2) - myinterpreter.allocate_tensors() - - # Get input and output tensors. - input_details = myinterpreter.get_input_details() - output_details = myinterpreter.get_output_details() - - # Get input tensor index - INPUT_LAYER_INDEX = input_details[0]['index'] - if model == "BirdNET_6K_GLOBAL_MODEL": - MDATA_INPUT_INDEX = input_details[1]['index'] - OUTPUT_LAYER_INDEX = output_details[0]['index'] - - # Load labels - CLASSES = [] - labelspath = userDir + '/BirdNET-Pi/model/labels.txt' - with open(labelspath, 'r') as lfile: - for line in lfile.readlines(): - CLASSES.append(line.replace('\n', '')) - - log.info('LOADING DONE!') - - return myinterpreter - - -def loadMetaModel(): - - global M_INTERPRETER - global M_INPUT_LAYER_INDEX - global M_OUTPUT_LAYER_INDEX - - if get_settings().getint('DATA_MODEL_VERSION') == 2: - data_model = 'BirdNET_GLOBAL_6K_V2.4_MData_Model_V2_FP16.tflite' - else: - data_model = 'BirdNET_GLOBAL_6K_V2.4_MData_Model_FP16.tflite' - - # Load TFLite model and allocate tensors. - M_INTERPRETER = tflite.Interpreter(model_path=os.path.join(userDir, 'BirdNET-Pi/model', data_model)) - M_INTERPRETER.allocate_tensors() - - # Get input and output tensors. - input_details = M_INTERPRETER.get_input_details() - output_details = M_INTERPRETER.get_output_details() - - # Get input tensor index - M_INPUT_LAYER_INDEX = input_details[0]['index'] - M_OUTPUT_LAYER_INDEX = output_details[0]['index'] - - log.info("loaded META model") - - -def predictFilter(lat, lon, week): - - global M_INTERPRETER - - # Does interpreter exist? - if M_INTERPRETER is None: - loadMetaModel() - - # Prepare mdata as sample - sample = np.expand_dims(np.array([lat, lon, week], dtype='float32'), 0) - - # Run inference - M_INTERPRETER.set_tensor(M_INPUT_LAYER_INDEX, sample) - M_INTERPRETER.invoke() - - return M_INTERPRETER.get_tensor(M_OUTPUT_LAYER_INDEX)[0] - - -def explore(lat, lon, week): - - # Make filter prediction - l_filter = predictFilter(lat, lon, week) - - # Apply threshold - l_filter = np.where(l_filter >= float(sf_thresh), l_filter, 0) - - # Zip with labels - l_filter = list(zip(l_filter, CLASSES)) - - # Sort by filter value - l_filter = sorted(l_filter, key=lambda x: x[0], reverse=True) - - return l_filter - - -def predictSpeciesList(lat, lon, week): - - l_filter = explore(lat, lon, week) - for s in l_filter: - if s[0] >= float(sf_thresh): - # if there's a custom user-made include list, we only want to use the species in that - if (len(INCLUDE_LIST) == 0): - PREDICTED_SPECIES_LIST.append(s[1]) - - -def loadCustomSpeciesList(path): - - slist = [] - if os.path.isfile(path): - with open(path, 'r') as csfile: - for line in csfile.readlines(): - slist.append(line.replace('\r', '').replace('\n', '')) - - return slist - - -def splitSignal(sig, rate, overlap, seconds=3.0, minlen=1.5): - - # Split signal with overlap - sig_splits = [] - for i in range(0, len(sig), int((seconds - overlap) * rate)): - split = sig[i:i + int(seconds * rate)] - - # End of signal? - if len(split) < int(minlen * rate): - break - - # Signal chunk too short? Fill with zeros. - if len(split) < int(rate * seconds): - temp = np.zeros((int(rate * seconds))) - temp[:len(split)] = split - split = temp - - sig_splits.append(split) - - return sig_splits - - -def readAudioData(path, overlap, sample_rate=48000): - - log.info('READING AUDIO DATA...') - - # Open file with librosa (uses ffmpeg or libav) - sig, rate = librosa.load(path, sr=sample_rate, mono=True, res_type='kaiser_fast') - - # Split audio into 3-second chunks - chunks = splitSignal(sig, rate, overlap) - - log.info('READING DONE! READ %d CHUNKS.', len(chunks)) - - return chunks - - -def convertMetadata(m): - - # Convert week to cosine - if m[2] >= 1 and m[2] <= 48: - m[2] = math.cos(math.radians(m[2] * 7.5)) + 1 - else: - m[2] = -1 - - # Add binary mask - mask = np.ones((3,)) - if m[0] == -1 or m[1] == -1: - mask = np.zeros((3,)) - if m[2] == -1: - mask[2] = 0.0 - - return np.concatenate([m, mask]) - - -def custom_sigmoid(x, sensitivity=1.0): - return 1 / (1.0 + np.exp(-sensitivity * x)) - - -def predict(sample, sensitivity): - global INTERPRETER - # Make a prediction - INTERPRETER.set_tensor(INPUT_LAYER_INDEX, np.array(sample[0], dtype='float32')) - if model == "BirdNET_6K_GLOBAL_MODEL": - INTERPRETER.set_tensor(MDATA_INPUT_INDEX, np.array(sample[1], dtype='float32')) - INTERPRETER.invoke() - prediction = INTERPRETER.get_tensor(OUTPUT_LAYER_INDEX)[0] - - # Apply custom sigmoid - p_sigmoid = custom_sigmoid(prediction, sensitivity) - - # Get label and scores for pooled predictions - p_labels = dict(zip(CLASSES, p_sigmoid)) - - # Sort by score - p_sorted = sorted(p_labels.items(), key=operator.itemgetter(1), reverse=True) - - human_cutoff = max(10, int(len(p_sorted) * priv_thresh / 100.0)) - - log.debug("DATABASE SIZE: %d", len(p_sorted)) - log.debug("HUMAN-CUTOFF AT: %d", human_cutoff) - - for i in range(min(10, len(p_sorted))): - if p_sorted[i][0] == 'Human_Human': - with open(userDir + '/BirdNET-Pi/HUMAN.txt', 'a') as rfile: - rfile.write(str(datetime.datetime.now()) + str(p_sorted[i]) + ' ' + str(human_cutoff) + '\n') - - return p_sorted[:human_cutoff] - - -def analyzeAudioData(chunks, lat, lon, week, sens, overlap,): - global INTERPRETER - - sensitivity = max(0.5, min(1.0 - (sens - 1.0), 1.5)) - - detections = {} - start = time.time() - log.info('ANALYZING AUDIO...') - - if model == "BirdNET_GLOBAL_6K_V2.4_Model_FP16": - if len(PREDICTED_SPECIES_LIST) == 0 or len(INCLUDE_LIST) != 0: - predictSpeciesList(lat, lon, week) - - mdata = get_metadata(lat, lon, week) - - # Parse every chunk - pred_start = 0.0 - for c in chunks: - - # Prepare as input signal - sig = np.expand_dims(c, 0) - - # Make prediction - p = predict([sig, mdata], sensitivity) -# print("PPPPP",p) - HUMAN_DETECTED = False - - # Catch if Human is recognized - for x in range(len(p)): - if "Human" in p[x][0]: - HUMAN_DETECTED = True - - # Save result and timestamp - pred_end = pred_start + 3.0 - - # If human detected set all detections to human to make sure voices are not saved - if HUMAN_DETECTED is True: - p = [('Human_Human', 0.0)] * 10 - - detections[str(pred_start) + ';' + str(pred_end)] = p - - pred_start = pred_end - overlap - - log.info('DONE! Time %.2f SECONDS', time.time() - start) - return detections - - -def get_metadata(lat, lon, week): - global mdata, mdata_params - if mdata_params != [lat, lon, week]: - mdata_params = [lat, lon, week] - # Convert and prepare metadata - mdata = convertMetadata(np.array([lat, lon, week])) - mdata = np.expand_dims(mdata, 0) - - return mdata - - -def load_global_model(): - global INTERPRETER - global model, priv_thresh, sf_thresh - conf = get_settings() - model = conf['MODEL'] - priv_thresh = conf.getfloat('PRIVACY_THRESHOLD') - sf_thresh = conf.getfloat('SF_THRESH') - INTERPRETER = loadModel() - - -def run_analysis(file): - global INCLUDE_LIST, EXCLUDE_LIST, CONVERT_LIST, CONVERT_DICT - INCLUDE_LIST = loadCustomSpeciesList(os.path.expanduser("~/BirdNET-Pi/include_species_list.txt")) - EXCLUDE_LIST = loadCustomSpeciesList(os.path.expanduser("~/BirdNET-Pi/exclude_species_list.txt")) - CONVERT_LIST = loadCustomSpeciesList(os.path.expanduser("~/BirdNET-Pi/convert_species_list.txt")) - CONVERT_DICT = {row.split(';')[0]: row.split(';')[1] for row in CONVERT_LIST} - - conf = get_settings() - - # Read audio data & handle errors - try: - audio_data = readAudioData(file.file_name, conf.getfloat('OVERLAP')) - except (NameError, TypeError) as e: - log.error("Error with the following info: %s", e) - return [] - - # Process audio data and get detections - raw_detections = analyzeAudioData(audio_data, conf.getfloat('LATITUDE'), conf.getfloat('LONGITUDE'), file.week, - conf.getfloat('SENSITIVITY'), conf.getfloat('OVERLAP')) - confident_detections = [] - for time_slot, entries in raw_detections.items(): - log.info('%s-%s', time_slot, entries[0]) - for entry in entries: - if entry[1] >= conf.getfloat('CONFIDENCE'): - if entry[0] in CONVERT_DICT: - converted_entry = CONVERT_DICT.get(entry[0], entry[0]) - else : - converted_entry = entry[0] - if (converted_entry in INCLUDE_LIST or len(INCLUDE_LIST) == 0) and \ - (converted_entry not in EXCLUDE_LIST or len(EXCLUDE_LIST) == 0) and \ - (converted_entry in PREDICTED_SPECIES_LIST or len(PREDICTED_SPECIES_LIST) == 0): - d = Detection(time_slot.split(';')[0], time_slot.split(';')[1], converted_entry, entry[1]) - confident_detections.append(d) - return confident_detections diff --git a/birdnet-pi/rootfs/helpers/convert_list/views.php b/birdnet-pi/rootfs/helpers/convert_list/views.php deleted file mode 100644 index 359d1b9a8..000000000 --- a/birdnet-pi/rootfs/helpers/convert_list/views.php +++ /dev/null @@ -1,457 +0,0 @@ - $_SESSION['behind_time'] + 86400)) { - shell_exec("sudo -u".$user." git -C ".$home."/BirdNET-Pi fetch > /dev/null 2>/dev/null &"); - $str = trim(shell_exec("sudo -u".$user." git -C ".$home."/BirdNET-Pi status")); - if (preg_match("/behind '.*?' by (\d+) commit(s?)\b/", $str, $matches)) { - $num_commits_behind = $matches[1]; - } - if (preg_match('/\b(\d+)\b and \b(\d+)\b different commits each/', $str, $matches)) { - $num1 = (int) $matches[1]; - $num2 = (int) $matches[2]; - $num_commits_behind = $num1 + $num2; - } - if (stripos($str, "Your branch is up to date") !== false) { - $num_commits_behind = '0'; - } - $_SESSION['behind'] = $num_commits_behind; - $_SESSION['behind_time'] = time(); -} -if(isset($_SESSION['behind'])&&intval($_SESSION['behind']) >= 99) {?> - -WARNING: Your latitude and longitude are not set properly. Please do so now in Tools -> Settings."; -} -elseif ($config["LATITUDE"] == "0.000") { - echo "
WARNING: Your latitude is not set properly. Please do so now in Tools -> Settings.
"; -} -elseif ($config["LONGITUDE"] == "0.000") { - echo "
WARNING: Your longitude is not set properly. Please do so now in Tools -> Settings.
"; -} -?> - - - - - - BirdNET-Pi DB - - - -
-
- - - - - - - - - - -
-
- - - -
-";} - if($_GET['view'] == "System Controls"){ - ensure_authenticated(); - include('scripts/system_controls.php'); - } - if($_GET['view'] == "Services"){ - ensure_authenticated(); - include('scripts/service_controls.php'); - } - if($_GET['view'] == "Spectrogram"){include('spectrogram.php');} - if($_GET['view'] == "View Log"){echo "";} - if($_GET['view'] == "Overview"){include('overview.php');} - if($_GET['view'] == "Todays Detections"){include('todays_detections.php');} - if($_GET['view'] == "Kiosk"){$kiosk = true;include('todays_detections.php');} - if($_GET['view'] == "Species Stats"){include('stats.php');} - if($_GET['view'] == "Weekly Report"){include('weekly_report.php');} - if($_GET['view'] == "Streamlit"){echo "";} - if($_GET['view'] == "Daily Charts"){include('history.php');} - if($_GET['view'] == "Tools"){ - ensure_authenticated(); - $url = $_SERVER['SERVER_NAME']."/scripts/adminer.php"; - echo "
-
- - - - - - - - - - -
-
"; - } - if($_GET['view'] == "Recordings"){include('play.php');} - if($_GET['view'] == "Settings"){include('scripts/config.php');} - if($_GET['view'] == "Advanced"){include('scripts/advanced.php');} - if($_GET['view'] == "Included"){ - ensure_authenticated(); - if(isset($_GET['species']) && isset($_GET['add'])){ - $file = './scripts/include_species_list.txt'; - $str = file_get_contents("$file"); - $str = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $str); - file_put_contents("$file", "$str"); - if(isset($_GET['species'])){ - foreach ($_GET['species'] as $selectedOption) - file_put_contents("./scripts/include_species_list.txt", htmlspecialchars_decode($selectedOption, ENT_QUOTES)."\n", FILE_APPEND); - } - } elseif(isset($_GET['species']) && isset($_GET['del'])){ - $file = './scripts/include_species_list.txt'; - $str = file_get_contents("$file"); - $str = preg_replace('/^\h*\v+/m', '', $str); - file_put_contents("$file", "$str"); - foreach($_GET['species'] as $selectedOption) { - $content = file_get_contents("../BirdNET-Pi/include_species_list.txt"); - $newcontent = str_replace($selectedOption, "", "$content"); - $newcontent = str_replace(htmlspecialchars_decode($selectedOption, ENT_QUOTES), "", "$newcontent"); - file_put_contents("./scripts/include_species_list.txt", "$newcontent"); - } - $file = './scripts/include_species_list.txt'; - $str = file_get_contents("$file"); - $str = preg_replace('/^\h*\v+/m', '', $str); - file_put_contents("$file", "$str"); - } - include('./scripts/include_list.php'); - } - if($_GET['view'] == "Excluded"){ - ensure_authenticated(); - if(isset($_GET['species']) && isset($_GET['add'])){ - $file = './scripts/exclude_species_list.txt'; - $str = file_get_contents("$file"); - $str = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $str); - file_put_contents("$file", "$str"); - foreach ($_GET['species'] as $selectedOption) - file_put_contents("./scripts/exclude_species_list.txt", htmlspecialchars_decode($selectedOption, ENT_QUOTES)."\n", FILE_APPEND); - } elseif (isset($_GET['species']) && isset($_GET['del'])){ - $file = './scripts/exclude_species_list.txt'; - $str = file_get_contents("$file"); - $str = preg_replace('/^\h*\v+/m', '', $str); - file_put_contents("$file", "$str"); - foreach($_GET['species'] as $selectedOption) { - $content = file_get_contents("./scripts/exclude_species_list.txt"); - $newcontent = str_replace($selectedOption, "", "$content"); - $newcontent = str_replace(htmlspecialchars_decode($selectedOption, ENT_QUOTES), "", "$content"); - file_put_contents("./scripts/exclude_species_list.txt", "$newcontent"); - } - $file = './scripts/exclude_species_list.txt'; - $str = file_get_contents("$file"); - $str = preg_replace('/^\h*\v+/m', '', $str); - file_put_contents("$file", "$str"); - } - include('./scripts/exclude_list.php'); - } - if($_GET['view'] == "Converted"){ - ensure_authenticated(); - if(isset($_GET['species']) && isset($_GET['add'])){ - $file = './scripts/convert_species_list.txt'; - $str = file_get_contents("$file"); - $str = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $str); - file_put_contents("$file", "$str"); - // Write $_GET['species'] to the file - file_put_contents("./scripts/convert_species_list.txt", htmlspecialchars_decode($_GET['species'], ENT_QUOTES)."\n", FILE_APPEND); - } elseif (isset($_GET['species']) && isset($_GET['del'])){ - $file = './scripts/convert_species_list.txt'; - $str = file_get_contents("$file"); - $str = preg_replace('/^\h*\v+/m', '', $str); - file_put_contents("$file", "$str"); - foreach($_GET['species'] as $selectedOption) { - $content = file_get_contents("./scripts/convert_species_list.txt"); - $newcontent = str_replace($selectedOption, "", "$content"); - $newcontent = str_replace(htmlspecialchars_decode($selectedOption, ENT_QUOTES), "", "$content"); - file_put_contents("./scripts/convert_species_list.txt", "$newcontent"); - } - $file = './scripts/convert_species_list.txt'; - $str = file_get_contents("$file"); - $str = preg_replace('/^\h*\v+/m', '', $str); - file_put_contents("$file", "$str"); - } - include('./scripts/convert_list.php'); - } - if($_GET['view'] == "File"){ - echo ""; - } - if($_GET['view'] == "Webterm"){ - ensure_authenticated('You cannot access the web terminal'); - echo ""; - } -} elseif(isset($_GET['submit'])) { - ensure_authenticated(); - $allowedCommands = array('sudo systemctl stop livestream.service && sudo systemctl stop icecast2.service', - 'sudo systemctl restart livestream.service && sudo systemctl restart icecast2.service', - 'sudo systemctl disable --now livestream.service && sudo systemctl disable icecast2 && sudo systemctl stop icecast2.service', - 'sudo systemctl enable icecast2 && sudo systemctl start icecast2.service && sudo systemctl enable --now livestream.service', - 'sudo systemctl stop web_terminal.service', - 'sudo systemctl restart web_terminal.service', - 'sudo systemctl disable --now web_terminal.service', - 'sudo systemctl enable --now web_terminal.service', - 'sudo systemctl stop birdnet_log.service', - 'sudo systemctl restart birdnet_log.service', - 'sudo systemctl disable --now birdnet_log.service', - 'sudo systemctl enable --now birdnet_log.service', - 'sudo systemctl stop birdnet_analysis.service', - 'sudo systemctl restart birdnet_analysis.service', - 'sudo systemctl disable --now birdnet_analysis.service', - 'sudo systemctl enable --now birdnet_analysis.service', - 'sudo systemctl stop birdnet_stats.service', - 'sudo systemctl restart birdnet_stats.service', - 'sudo systemctl disable --now birdnet_stats.service', - 'sudo systemctl enable --now birdnet_stats.service', - 'sudo systemctl stop birdnet_recording.service', - 'sudo systemctl restart birdnet_recording.service', - 'sudo systemctl disable --now birdnet_recording.service', - 'sudo systemctl enable --now birdnet_recording.service', - 'sudo systemctl stop chart_viewer.service', - 'sudo systemctl restart chart_viewer.service', - 'sudo systemctl disable --now chart_viewer.service', - 'sudo systemctl enable --now chart_viewer.service', - 'sudo systemctl stop spectrogram_viewer.service', - 'sudo systemctl restart spectrogram_viewer.service', - 'sudo systemctl disable --now spectrogram_viewer.service', - 'sudo systemctl enable --now spectrogram_viewer.service', - 'sudo systemctl enable '.get_service_mount_name().' && sudo reboot', - 'sudo systemctl disable '.get_service_mount_name().' && sudo reboot', - 'stop_core_services.sh', - 'restart_services.sh', - 'sudo reboot', - 'update_birdnet.sh', - 'sudo shutdown now', - 'sudo clear_all_data.sh'); - $command = $_GET['submit']; - if(in_array($command,$allowedCommands)){ - if(isset($command)){ - $initcommand = $command; - if (strpos($command, "systemctl") !== false) { - //If there more than one command to execute, processes then separately - //currently only livestream service uses multiple commands to interact with the required services - if (strpos($command, " && ") !== false) { - $separate_commands = explode("&&", trim($command)); - $new_multiservice_status_command = ""; - foreach ($separate_commands as $indiv_service_command) { - //explode the string by " " space so we can get each individual component of the command - //and eventually the service name at the end - $separate_command_tmp = explode(" ", trim($indiv_service_command)); - //get the service names - $new_multiservice_status_command .= " " . trim(end($separate_command_tmp)); - } - - $service_names = $new_multiservice_status_command; - } else { - //only one service needs restarting so we only need to query the status of one service - $tmp = explode(" ", trim($command)); - $service_names = end($tmp); - } - - $command .= " & sleep 3;sudo systemctl status " . $service_names; - } - if($initcommand == "update_birdnet.sh") { - session_unset(); - } - $results = shell_exec("$command 2>&1"); - $results = str_replace("FAILURE", "FAILURE", $results); - $results = str_replace("failed", "failed",$results); - $results = str_replace("active (running)", "active (running)",$results); - $results = str_replace("Your branch is up to date", "Your branch is up to date",$results); - - $results = str_replace("(+)", "(+)",$results); - $results = str_replace("(-)", "(-)",$results); - - // split the input string into lines - $lines = explode("\n", $results); - - // iterate over each line - foreach ($lines as &$line) { - // check if the line matches the pattern - if (preg_match('/^(.+?)\s*\|\s*(\d+)\s*([\+\- ]+)(\d+)?$/', $line, $matches)) { - // extract the filename, count, and indicator letters - $filename = $matches[1]; - $count = $matches[2]; - $diff = $matches[3]; - $delta = $matches[4] ?? ''; - // determine the indicator letters - $diff_array = str_split($diff); - $indicators = array_map(function ($d) use ($delta) { - if ($d === '+') { - return "+"; - } elseif ($d === '-') { - return "-"; - } elseif ($d === ' ') { - if ($delta !== '') { - return 'A'; - } else { - return ' '; - } - } - }, $diff_array); - // modify the line with the new indicator letters - $line = sprintf('%-35s|%3d %s%s', $filename, $count, implode('', $indicators), $delta); - } - } - - // rejoin the modified lines into a string - $output = implode("\n", $lines); - $results = $output; - - // remove script tags (xss) - $results = preg_replace('#(.*?)#is', '', $results); - if(strlen($results) == 0) { - $results = "This command has no output."; - } - echo "
Output of command:`".$initcommand."`
$results
"; - } - } - ob_end_flush(); -} else {include('overview.php');} -?> - -
- diff --git a/birdnet-pi/rootfs/helpers/convert_list/views.add b/birdnet-pi/rootfs/helpers/views.add similarity index 100% rename from birdnet-pi/rootfs/helpers/convert_list/views.add rename to birdnet-pi/rootfs/helpers/views.add