From bf0131cf2738b8ea4467d79cc65e80252b95d78d Mon Sep 17 00:00:00 2001 From: jaredb7 Date: Sun, 7 May 2023 11:59:50 +1000 Subject: [PATCH 01/29] Query Consolidate Code for API Preparations All SQL Queries were moved into their respective functions and parameterized, there were some slight logic changes in the front end pages to accommodate data being returned differently but existing functionality is maintained A common function is used to execute the queries and looks after binding any parameterized values that have been supplied. Other code for deleting / protecting detection's, frequency shift, Flickr API call etc have also been moved into common.php to be used later by the API Two new functions getDirectory and getFilePath were introduced in aims and make it easier to relocate files. This will be worked into all existing php files to replace hardcoded paths --- scripts/common.php | 1053 +++++++++++++++++++++++++++++++++ scripts/history.php | 34 +- scripts/overview.php | 255 ++++---- scripts/play.php | 262 ++++---- scripts/stats.php | 158 ++--- scripts/todays_detections.php | 278 ++------- scripts/weekly_report.php | 140 ++--- 7 files changed, 1479 insertions(+), 701 deletions(-) create mode 100644 scripts/common.php diff --git a/scripts/common.php b/scripts/common.php new file mode 100644 index 0000000..b1cfe92 --- /dev/null +++ b/scripts/common.php @@ -0,0 +1,1053 @@ +getMessage()); + } + } + + return $DB_CONN; +} + +/** + * Disconnects the database + * + * @return void + */ +function disconnect_from_birdsdb() +{ + global $DB_CONN; + + if ($DB_CONN != null) { + return $DB_CONN->close(); + } +} + +/** + * Executes the supplied query and returns all results + * + * @param $query string The query to execute + * @param $bind_params string Any values that should be bound into the query + * @param $fetchAllRecords string Any values that should be bound into the query + * @param $fetchMode string Controls how result data is returned, default is @->default('SQLITE3_ASSOC') Associative Array; + * @return array + */ +function db_execute_query($query, $bind_params = [], $fetchAllRecords = false, $fetchMode = SQLITE3_ASSOC) +{ + global $DB_CONN, $api_incl; + $success = false; + $message = ''; + $data_to_return = null; + + //Connect to the DB + connect_to_birdsdb(); + try { + $stmt = $DB_CONN->prepare($query); + // + if (!empty($bind_params)) { + //Loop over the bind values and add them + foreach ($bind_params as $bind_key => $bind_value) { + $stmt->bindValue($bind_key, $bind_value); + } + } + + if ($stmt == False) { + //get caller's function name + $caller_func_name = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function']; + $error_msg = "$caller_func_name => db_execute_custom_query:: birds.db database is busy or a query error occurred"; + // + $success = false; + $message = $error_msg; + $data_to_return = null; + + //Log the error message + birdnet_error_log($error_msg); + } else { + $result = $stmt->execute(); + //Initial result collection + $resultArray = $result->fetchArray($fetchMode); + + if ($fetchAllRecords) { + $multiArray = array(); //array to store all rows + //Loop over the results to collect them all + while ($resultArray !== false) { + array_push($multiArray, $resultArray); //insert all rows to $multiArray + $resultArray = $result->fetchArray($fetchMode); + } + unset($resultArray); //unset temporary variable + + $data_to_return = $multiArray; + } else { + $data_to_return = $resultArray; + } + + // + $success = true; + $message = 'Ok'; + } + } catch (Exception $sql_exec) { + $caller_func_name = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function']; + $error_msg = "$caller_func_name => db_execute_custom_query:: Exception occurred while executing query - " . $sql_exec->getMessage(); + // + $success = false; + $message = $error_msg; + $data_to_return = null; + + birdnet_error_log($error_msg); + } + + return array('success' => $success, 'message' => $message, 'data' => $data_to_return); +} + +/** + * Returns a count of detections in the database + * @return array + */ +function getDetectionCountAll() +{ + return db_execute_query('SELECT COUNT(*) FROM detections'); +} + +/** + * Returns today's count of detections in the database + * @return array + */ +function getDetectionCountToday() +{ + return db_execute_query('SELECT COUNT(*) FROM detections WHERE Date == DATE(\'now\', \'localtime\')'); +} + +/** + * Returns a count of detections in the past hour + * @return array + */ +function getDetectionCountLastHour() +{ + return db_execute_query('SELECT COUNT(*) FROM detections WHERE Date == Date(\'now\', \'localtime\') AND TIME >= TIME(\'now\', \'localtime\', \'-1 hour\')'); +} + +/** + * Returns the most recent detection + * @return array|string + */ +function getMostRecentDetection($limit = 1) +{ + return db_execute_query('SELECT Com_Name, Sci_Name, Date, Time, Confidence, File_Name FROM detections ORDER BY Date DESC, Time DESC LIMIT :limit', [':limit' => $limit], true); +} + +/** + * Returns a talley for todays detected species + * @return array + */ +function getSpeciesTalley($range = "today", $start_date = null, $end_date = null) +{ + $range = strtolower($range); + + if ($range == "today") { + $result = db_execute_query('SELECT COUNT(DISTINCT(Com_Name)) FROM detections WHERE Date == Date(\'now\', \'localtime\')'); + } else if ($range == "custom") { + $result = db_execute_query('SELECT COUNT(DISTINCT(Com_Name)) FROM detections WHERE Date == :start_date', [':start_date' => $start_date], false); + } else if ($range == "range") { + $result = db_execute_query('SELECT COUNT(DISTINCT(Com_Name)) FROM detections WHERE Date BETWEEN :start_date AND :end_date', [':start_date' => $start_date, ':end_date' => $end_date]); + } + + return $result; +} + +/** + * Returns a species talley from ALL detections + * @return array + */ +function getAllSpeciesTalley() +{ + return db_execute_query('SELECT COUNT(DISTINCT(Com_Name)) FROM detections'); +} + +/** + * Returns the number of detections on the specified date + * + * @param $date string Date to count detections for + * @return array + */ +function getDetectionCountByDate($date, $date_range = false) +{ + return db_execute_query("SELECT COUNT(*) FROM detections WHERE Date == :date", [':date' => $date], false); +} + +/** + * Returns detections and counts for the specified date and start & finish times + * + * @param $date string Date to count detections for + * @param $starttime string Search for detections after this time + * @param $endtime string And detections up to this time + * @return array + */ +function getDetectionBreakdownByTime($date, $starttime, $endtime) +{ + return db_execute_query('SELECT DISTINCT(Com_Name), COUNT(*) FROM detections WHERE Date == :date AND Time > :start_time AND Time < :end_time AND Confidence > 0.75 GROUP By Com_Name ORDER BY COUNT(*) DESC', + [ + ':date' => $date, + ':start_time' => $starttime, + ':end_time' => $endtime + ], + true); +} + + +/** + * Returns the detection count for a specified bird + * @param $birdName string Species Name to get stats for + * @return array + */ +function getBirdDetectionStats($birdName) +{ + //Cleanup the bird/species name + $birdName = str_replace("_", " ", $birdName); + + $birdDetections = db_execute_query('SELECT Date, COUNT(*) AS Detections FROM detections WHERE Com_Name = :com_name AND Date BETWEEN DATE("now", "-30 days") AND DATE("now") GROUP BY Date', [':com_name' => $birdName], true); + + // Fetch the result set as an associative array + $data = array(); + foreach ($birdDetections as $birdDetection) { + $data[$birdDetection['Date']] = $birdDetection['Detections']; + } + + // Create an array of all dates in the last 14 days + $last14Days = array(); + for ($i = 0; $i < 31; $i++) { + $last14Days[] = date('Y-m-d', strtotime("-$i days")); + } + + // Merge the data array with the last14Days array + $data = array_merge(array_fill_keys($last14Days, 0), $data); + + // Sort the data by date in ascending order + ksort($data); + + // Convert the data to an array of objects + $data = array_map(function ($date, $count) { + return array('date' => $date, 'count' => $count); + }, array_keys($data), $data); + + // Return the data as JSON - overwrite the result data + $birdDetections['data'] = json_encode($data); + + return $birdDetections; +} + +/** + * Returns detection info for the specified + * @param $birdName string Species name to get detection info on + * @param $date string OPTIONAL - The date on which to list species on + * @param $sort string OPTIONAL - Whether to sort the species by confidence value (so species ranked by detection accuracy/confidence) + * @return array|string + */ +function getSpeciesDetectionInfo($birdName, $date = null, $sort = null) +{ + //If no date is supplied, then list a unique list of detection dates in the DB, sorted descending + if ($date == null) { + //No date set so automatically search by date and time if no sort value is set + if (isset($sort) && $sort == "confidence") { + $detectionInfo = db_execute_query("SELECT * FROM detections where Com_Name == :birdname ORDER BY Confidence DESC", [':birdname' => $birdName], true); + } else { + $detectionInfo = db_execute_query("SELECT * FROM detections where Com_Name == :birdname ORDER BY Date DESC, Time DESC", [':birdname' => $birdName], true); + } + } else { + //Date set so use that to filter the results depending on the sort value + if (isset($sort) && $sort == "confidence") { + $detectionInfo = db_execute_query("SELECT * FROM detections where Com_Name == :birdname AND Date == :date ORDER BY Confidence DESC", [':birdname' => $birdName, ':date' => $date], true); + } else { + $detectionInfo = db_execute_query("SELECT * FROM detections where Com_Name == :birdname AND Date == :date ORDER BY Time DESC", [':birdname' => $birdName, ':date' => $date], true); + } + } + + return $detectionInfo; +} + +/** + * Returns the detections for a specified filename + * + * @param $filename string The filename on which to get detections for + * @return array|string + */ +function getDetectionsByFilename($filename) +{ + return db_execute_query("SELECT * FROM detections where File_name == :filename ORDER BY Date DESC, Time DESC", [':filename' => $filename], true); +} + +/** + * Returns a list of specie names on a specified date if supplied, else lists valid dates which can be passed again for specific list of species on that date + * + * @param $date string OPTIONAL - The date on which to list species on + * @param $sort string OPTIONAL - Whether to sort the species by occurrence (so species ranked by number of detections) + * @return array|string + */ +function getDetectionsByDate($date = null, $sort = null) +{ + + //If no date is supplied, then list a unique list of species in the DB + if ($date == null) { + $detectionsByDateResult = db_execute_query('SELECT DISTINCT(Date) FROM detections GROUP BY Date ORDER BY Date DESC', null, true); + } else { + //Else a date was supplied, first check the sort order if any + if (isset($sort) && $sort == "occurrences") { + $detectionsByDateResult = db_execute_query("SELECT DISTINCT(Com_Name) FROM detections WHERE Date == :date GROUP BY Com_Name ORDER BY COUNT(*) DESC", [':date' => $date], true); + } else { + $detectionsByDateResult = db_execute_query("SELECT DISTINCT(Com_Name) FROM detections WHERE Date == :date ORDER BY Com_Name", [':date' => $date], true); + } + } + + return $detectionsByDateResult; +} + +/** + * Returns a list detections for a species if supplied, else lists all detected species by name + * + * @param $species_name string OPTIONAL - List detections for this specific species + * @param $sort string OPTIONAL - Whether to sort the species by occurrence (so species ranked by number of detections) + * @return array + */ +function getDetectionsBySpecies($species_name = null, $sort = null) +{ + //If no species is is supplied, then list all species + if (!isset($species_name)) { + if (isset($sort) && $sort == "occurrences") { + //Sort by occurrences + $speciesDetections = db_execute_query('SELECT DISTINCT(Com_Name) FROM detections GROUP BY Com_Name ORDER BY COUNT(*) DESC', null, true); + } else { + //Don't sort by occurrences + $speciesDetections = db_execute_query('SELECT DISTINCT(Com_Name) FROM detections ORDER BY Com_Name ASC', null, true); + } + } else { + //Else a species name was supplied, first check the sort order if any + $speciesDetections = db_execute_query("SELECT * FROM detections WHERE Com_Name == :species_name ORDER BY Com_Name", [':species_name' => $species_name], true); + //Also get the highest confidence record for this species + $speciesDetections_MaxConf = db_execute_query("SELECT Date, Time, Sci_Name, MAX(Confidence), File_Name FROM detections WHERE Com_Name == :species_name ORDER BY Com_Name", [':species_name' => $species_name], true); + } + + //Rearrange data + $newReturnData = []; + $newReturnData['species'] = $speciesDetections; + //Check to see if we have to get max confidence results for the species also + if (isset($speciesDetections_MaxConf)) { + $newReturnData['species_MaxConf'] = $speciesDetections_MaxConf; + } + + $speciesDetections['data'] = $newReturnData; + + return $speciesDetections; +} + +/** + * Returns a list of species either ordered alphabetically (default) or ordered by the number of detections the species has + * + * @param $sort string OPTIONAL - Sort result alphabetically (supply null) or by number of occurrences (supply "occurrences" + * @return array + */ +function getSpeciesBestRecordingList($sort = null) +{ + if (isset($sort) && $sort == "occurrences") { + //Sort by occurrences + $speciesBestRecording = db_execute_query('SELECT Date, Time, File_Name, Com_Name, COUNT(*), MAX(Confidence) FROM detections GROUP BY Com_Name ORDER BY COUNT(*) DESC', null, true); + + } else { + //Don't sort by occurrences, sort by alphabetical + $speciesBestRecording = db_execute_query('SELECT Date, Time, File_Name, Com_Name, COUNT(*), MAX(Confidence) FROM detections GROUP BY Com_Name ORDER BY Com_Name ASC', null, true); + } + + return $speciesBestRecording; +} + +/** + * Returns a list of best recordings for a supplied species + * + * @param $species_name string Name of the species + * @return array + */ +function getBestRecordingsForSpecies($species_name) +{ + return db_execute_query("SELECT Com_Name, Sci_Name, COUNT(*), MAX(Confidence), File_Name, Date, Time from detections WHERE Com_Name = :species_name", [':species_name' => $species_name], true); +} + +/** + * Get a list of todays detections + * + * @param $display_limit string Number of results to return + * @param $search_term string OPTIONAL: Return results that match the supplied term + * @param $hard_limit string OPTIONAL: Return a fix number of results + * @return array + */ +function getTodaysDetections($display_limit, $search_term = null, $hard_limit = null) +{ + $bind_params = []; + + if (isset($search_term)) { + if (strtolower(explode(" ", $search_term)[0]) == "not") { + $not = "NOT "; + $operator = "AND"; + $search_term = str_replace("not ", "", $search_term); + $search_term = str_replace("NOT ", "", $search_term); + } else { + $not = ""; + $operator = "OR"; + } + $searchquery = "AND (Com_name " . $not . "LIKE :search_term " . + $operator . " Sci_name " . $not . "LIKE :search_term " . + $operator . " Confidence " . $not . "LIKE :search_term " . + $operator . " File_Name " . $not . "LIKE :search_term " . + $operator . " Time " . $not . "LIKE :search_term)"; + + $bind_params = [':search_term' => '%' . $search_term . '%']; + } else { + $searchquery = ""; + } + + if (isset($display_limit) && is_numeric($display_limit)) { + $bind_params[':display_limit'] = (intval($display_limit) - 40); + $result = db_execute_query('SELECT Date, Time, Com_Name, Sci_Name, Confidence, File_Name FROM detections WHERE Date == Date(\'now\', \'localtime\') ' . $searchquery . ' ORDER BY Time DESC LIMIT :display_limit,40', $bind_params, true); + } else { + // legacy mode + if (isset($hard_limit) && is_numeric($hard_limit)) { + $bind_params[':hard_limit'] = $hard_limit; + $result = db_execute_query('SELECT Date, Time, Com_Name, Sci_Name, Confidence, File_Name FROM detections WHERE Date == Date(\'now\', \'localtime\') ' . $searchquery . ' ORDER BY Time DESC LIMIT :hard_limit', $bind_params, true); + } else { + $result = db_execute_query('SELECT Date, Time, Com_Name, Sci_Name, Confidence, File_Name FROM detections WHERE Date == Date(\'now\', \'localtime\') ' . $searchquery . ' ORDER BY Time DESC', $bind_params, true); + } + } + + return $result; +} + +/** + * Returns the species talley for last week and the week prior + * + * @return array[] + */ +function getWeeklyReportSpeciesTalley() +{ + $last_week_dates = getLastWeekDates(); + $startdate = $last_week_dates['start_date']; + $enddate = $last_week_dates['end_date']; + + + $totalspeciestally = getSpeciesTalley('range', date("Y-m-d", $startdate), date("Y-m-d", $enddate)); + $priortotalspeciestally = getSpeciesTalley('range', date("Y-m-d", $startdate - (7 * 86400)), date("Y-m-d", $enddate - (7 * 86400))); + + return ['totalspeciestally' => $totalspeciestally, 'priortotalspeciestally' => $priortotalspeciestally]; +} + +/** + * Returns the species talley for last week and the week prior + * + * @return array[] + */ +function getWeeklyReportSpeciesDetectionCounts($detections_asc = false) +{ + $last_week_dates = getLastWeekDates(); + $startdate = $last_week_dates['start_date']; + $enddate = $last_week_dates['end_date']; + + + $sort_order = $detections_asc ? 'ASC' : 'DESC'; + + $detections = db_execute_query('SELECT DISTINCT(Com_Name), COUNT(*) FROM detections WHERE Date BETWEEN :start_date AND :end_date GROUP By Com_Name ORDER BY COUNT(*) ' . $sort_order, [':start_date' => date("Y-m-d", $startdate), ':end_date' => date("Y-m-d", $enddate)], true); + $totalcount = db_execute_query('SELECT DISTINCT(Com_Name), COUNT(*) FROM detections WHERE Date BETWEEN :start_date AND :end_date', [':start_date' => date("Y-m-d", $startdate), ':end_date' => date("Y-m-d", $enddate)], true); + $priortotalcount = db_execute_query('SELECT DISTINCT(Com_Name), COUNT(*) FROM detections WHERE Date BETWEEN :start_date AND :end_date', [':start_date' => date("Y-m-d", date("Y-m-d", $startdate - (7 * 86400))), ':end_date' => date("Y-m-d", date("Y-m-d", $enddate - (7 * 86400)))], true); + + return ['detections' => $detections, 'totalcount' => $totalcount, 'priortotalcount' => $priortotalcount]; +} + +/** + * Returns last weeks detection count for the specified species + * + * @param string $species_name Species to get last weeks detection count for + * @param bool $this_week Whether to find detections in or outside last week + * @return array + */ +function getWeeklyReportSpeciesDetection($species_name, $this_week = true) +{ + $last_week_dates = getLastWeekDates(); + $startdate = $last_week_dates['start_date']; + $enddate = $last_week_dates['end_date']; + + if ($this_week) { + $result = db_execute_query('SELECT COUNT(*) FROM detections WHERE Com_Name == :species_name AND Date BETWEEN :start_date AND :end_date', [':species_name' => $species_name, ':start_date' => date("Y-m-d", $startdate - (7 * 86400)), ':end_date' => date("Y-m-d", $enddate - (7 * 86400))], false); + } else { + $result = db_execute_query('SELECT COUNT(*) FROM detections WHERE Com_Name == :species_name AND Date NOT BETWEEN :start_date AND :end_date', [':species_name' => $species_name, ':start_date' => date("Y-m-d", $startdate), ':end_date' => date("Y-m-d", $enddate)], false); + } + + return $result; +} + +/** + * Deletes a specified detection by filename + * + * @param $filename string The filename of the detection e.g 2023-04-25/Pacific_Koel/Pacific_Koel-76-2023-04-25-birdnet-RTSP_2-16:24:05.mp3 + * @return array + */ +function deleteDetection($filename) +{ + global $DB_CONN, $api_incl; + $success = false; + $message = ''; + $data_to_return = null; + + $filename_exploded = explode("/", $filename); + $actual_filename = $filename_exploded[2]; + + + //If the detection was successfully deleted, remove the mp3 and png spectrogram + $file_pointer = getDirectory('home') . "/BirdSongs/Extracted/By_Date/" . $filename; + if (!exec("sudo rm $file_pointer && sudo rm $file_pointer.png")) { + $message = "OK"; + $statement = db_execute_query('DELETE FROM detections WHERE File_Name = :filename LIMIT 1', [':filename' => $actual_filename]); + } else { + $message = "Error"; + } + + // + $success = true; + //Message set above + $data_to_return = $statement; + + return array('success' => $success, 'message' => $message, 'data' => $data_to_return); +} + +/** + * Protects the specified file from deletion + * + * @param $mode string Set the mode to add (protect) to remove (unprotect) the specified filepath from protection + * @param $filename_to_protect string The filename of the detection to protect e.g 2023-04-25/Pacific_Koel/Pacific_Koel-76-2023-04-25-birdnet-RTSP_2-16:24:05.mp3 + * @return array + */ +function protectDetectionFromDeletion($mode, $filename_to_protect) +{ + global $api_incl; + $success = false; + $message = ''; + $data_to_return = null; + + $mode = strtolower($mode); + + $scripts_dir = getDirectory('scripts'); + $disk_check_exclude_file = $scripts_dir . "/disk_check_exclude.txt"; + + //Initially create the file if it doesn't exist + if (!file_exists($disk_check_exclude_file)) { + file_put_contents($disk_check_exclude_file, "##start\n##end\n"); + } + + if ($mode == "protect") { + // load the data and delete the line from the array which is ##end, + // so that all excluded files sit between the ##start and ##end tags + $lines = file($disk_check_exclude_file); + $last = sizeof($lines) - 1; + unset($lines[$last]); + + //Open the file and truncate contents + if (($myfile = fopen($disk_check_exclude_file, "w")) !== false) { + $txt = $filename_to_protect; + //Write all existing lines + fwrite($myfile, implode("", $lines)); + //Write out the lines for the file were trying to exclude + fwrite($myfile, $txt . "\n"); + fwrite($myfile, $txt . ".png\n"); + //Insert the end tag again to make it the last line + fwrite($myfile, "##end\n"); + + fclose($myfile); + // + $success = true; + $message = "OK"; + } else { + $success = false; + $message = "Unable to open file! " . $disk_check_exclude_file; + } + } else if ($mode == "unprotect") { + $lines = file($disk_check_exclude_file); + $search = $filename_to_protect; + + $result = ''; + foreach ($lines as $line) { + if (stripos($line, $search) === false && stripos($line, $search . ".png") === false) { + $result .= $line; + } + } + if (file_put_contents($disk_check_exclude_file, $result) !== false) { + $success = true; + $message = "OK"; + } else { + $success = true; + $message = "Failed writing contents after removing file from protection, back to file $disk_check_exclude_file"; + } + } + + return array('success' => $success, 'message' => $message, 'data' => $data_to_return); +} + +/** + * Frequency shifts the specified audio file to aid listening for hearing impaired people + * + * @param $filename + * @param $performShift + * @return array + */ +function frequencyShiftDetectionAudio($filename, $performShift = null) +{ + global $config, $api_incl; + $success = false; + $message = ''; + $data_to_return = null; + + $shifted_path = getDirectory('shifted_audio') . '/'; + + $pp = pathinfo($filename); + $dir = $pp['dirname']; + $fn = $pp['filename']; + $ext = $pp['extension']; + $pi = getDirectory('extracted_bydate') . '/'; + + if (isset($performShift) && $performShift == true) { + $freqshift_tool = $config['FREQSHIFT_TOOL']; + + if ($freqshift_tool == "ffmpeg") { + $cmd = "sudo /usr/bin/nohup /usr/bin/ffmpeg -y -i \"" . $pi . $filename . "\" -af \"rubberband=pitch=" . $config['FREQSHIFT_LO'] . "/" . $config['FREQSHIFT_HI'] . "\" \"" . $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 \"" . $pi . $filename . "\" \"" . $shifted_path . $filename . "\" pitch " . $soxopt . " " . $soxpitch; + shell_exec("sudo mkdir -p " . $shifted_path . $dir . " && " . $cmd); + } + } else { + $cmd = "sudo rm -f " . $shifted_path . $filename; + shell_exec($cmd); + } + + // + $success = true; + $message = "OK"; + + return array('success' => $success, 'message' => $message, 'data' => $data_to_return); +} + +/** + * Adds the supplied Flickr Image Id to the list of blacklisted images + * + * @param $imageID + * @return array + */ +function blacklistFlickrImage($imageID) +{ + $success = false; + $message = ''; + $data_to_return = null; + + $scripts_dir = getDirectory('scripts'); + + //Append the Flickr Image ID the blacklist file + if (($file_handle = fopen($scripts_dir . "/blacklisted_images.txt", 'a+')) !== false) { + fwrite($file_handle, $imageID . "\n"); + fclose($file_handle); + // + $success = true; + $message = "Ok"; + } else { + $success = false; + $message = "Failed"; + } + + return array('success' => $success, 'message' => $message, 'data' => $data_to_return); +} + +/** + * Finds and returns a flicker image for the supplied detection, detection data must have the following columns Com_Name, Sci_Name, Date, Time, Confidence, File_Name + * getMostRecentDetections + * + * @param $detection_data + * @param bool $getAll + * @return string[][] + */ +function getFlickrImage($detection_data, $getAll = false) +{ + global $config; + + $success = false; + $message = ''; + $data_to_return = null; + + $flickr_data_to_return = array( + 'Com_Name' => 'N/A', + 'Sci_Name' => 'N/A', + 'Date' => 'N/A', + 'Time' => 'N/A', + 'Confidence' => '0', + 'File_Name' => 'N/A', + 'Com_Name_clean' => 'N/A', + 'Sci_Name_clean' => 'N/A', + 'photos' => null, + 'filename_path' => 'N/A', + 'filename_formatted' => 'N/A' + ); + $foundImage = false; + + if (isset($detection_data)) { + $extracted_dir = getDirectory('extracted'); + $home_dir = getDirectory('home'); + + //Setup out flickr image cache in the session + if (!isset($_SESSION['images'])) { + $_SESSION['images'] = []; + } + + //Cleanup the Birds common and scientic name + $comname_clean = preg_replace('/ /', '_', $detection_data['Com_Name']); + $sciname_clean = preg_replace('/ /', '_', $detection_data['Sci_Name']); + $comname_clean = preg_replace('/\'/', '', $comname_clean); + $filename_path = "/By_Date/" . $detection_data['Date'] . "/" . $comname_clean . "/" . $detection_data['File_Name']; + $filename_formatted = $detection_data['Date'] . "/" . $comname_clean . "/" . $detection_data['File_Name']; + $args = "&license=2%2C3%2C4%2C5%2C6%2C9&orientation=square,portrait"; + $comnameprefix = "%20bird"; + + if (!empty($config["FLICKR_API_KEY"])) { + + if (!empty($config["FLICKR_FILTER_EMAIL"])) { + if (!isset($_SESSION["FLICKR_FILTER_EMAIL"])) { + unset($_SESSION['images']); + $_SESSION['FLICKR_FILTER_EMAIL'] = json_decode(file_get_contents("https://www.flickr.com/services/rest/?method=flickr.people.findByEmail&api_key=" . $config["FLICKR_API_KEY"] . "&find_email=" . $config["FLICKR_FILTER_EMAIL"] . "&format=json&nojsoncallback=1"), true)["user"]["nsid"]; + } + $args = "&user_id=" . $_SESSION['FLICKR_FILTER_EMAIL']; + $comnameprefix = ""; + } else { + if (isset($_SESSION["FLICKR_FILTER_EMAIL"])) { + unset($_SESSION["FLICKR_FILTER_EMAIL"]); + unset($_SESSION['images']); + } + } + + + // if we already searched flickr for this species before, use the previous image rather than doing an unneccesary api call + $key = array_search($comname_clean, array_column($_SESSION['images'], 0)); + if ($key !== false && !$getAll) { + $flickr_image_data = $_SESSION['images'][$key]; + + $foundImage = true; + //Extract data out of the image data, either in our $_SESSION var already is recently pushed (new data) + $flickr_data_to_return['photos'][0]['image_url'] = $flickr_image_data[1]; + $flickr_data_to_return['photos'][0]['photo_title'] = $flickr_image_data[2]; + $flickr_data_to_return['photos'][0]['modal_text'] = $flickr_image_data[3]; + $flickr_data_to_return['photos'][0]['author_link'] = $flickr_image_data[4]; + $flickr_data_to_return['photos'][0]['license_url'] = $flickr_image_data[5]; + } else { + // Get license information if we haven't already + if (empty($licenses_urls)) { + $licenses_url = "https://api.flickr.com/services/rest/?method=flickr.photos.licenses.getInfo&api_key=" . $config["FLICKR_API_KEY"] . "&format=json&nojsoncallback=1"; + $licenses_response = file_get_contents($licenses_url); + $licenses_data = json_decode($licenses_response, true)["licenses"]["license"]; + foreach ($licenses_data as $license) { + $license_id = $license["id"]; + $license_name = $license["name"]; + $license_url = $license["url"]; + $licenses_urls[$license_id] = $license_url; + } + } + + // only open the file once per script execution + if (!isset($lines)) { + $lines = file($home_dir . "/BirdNET-Pi/model/labels_flickr.txt"); + } + // convert sci name to English name + foreach ($lines as $line) { + if (strpos($line, $detection_data['Sci_Name']) !== false) { + $sci_name_as_english_name = trim(explode("_", $line)[1]); + break; + } + } + + // Read the blacklisted image ids from the file into an array + $blacklisted_ids = array_map('trim', file($home_dir . "/BirdNET-Pi/scripts/blacklisted_images.txt")); + + // Make the API call + $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", $sci_name_as_english_name) . $comnameprefix . "&sort=relevance" . $args . "&per_page=5&media=photos&format=json&nojsoncallback=1"), true)["photos"]["photo"]; + + // Find the first photo that is not blacklisted or is not the specific blacklisted id + $photo = null; + foreach ($flickrjson as $flickrphoto) { + if ($flickrphoto["id"] !== "4892923285" && !in_array($flickrphoto["id"], $blacklisted_ids)) { + $photo = $flickrphoto; + + $license_url = "https://api.flickr.com/services/rest/?method=flickr.photos.getInfo&api_key=" . $config["FLICKR_API_KEY"] . "&photo_id=" . $photo["id"] . "&format=json&nojsoncallback=1"; + $license_response = file_get_contents($license_url); + $license_info = json_decode($license_response, true)["photo"]["license"]; + $license_url = $licenses_urls[$license_info]; + + $modaltext = "https://flickr.com/photos/" . $photo["owner"] . "/" . $photo["id"]; + $authorlink = "https://flickr.com/people/" . $photo["owner"]; + $imageurl = 'https://farm' . $photo["farm"] . '.static.flickr.com/' . $photo["server"] . '/' . $photo["id"] . '_' . $photo["secret"] . '.jpg'; + + if (!$getAll) { + //Get single image then break the loop + array_push($_SESSION['images'], array($comname_clean, $imageurl, $photo["title"], $modaltext, $authorlink, $license_url)); + $flickr_image_data = $_SESSION['images'][count($_SESSION['images']) - 1]; + + //Extract data out of the image data, either in our $_SESSION var already is recently pushed (new data) + $flickr_data_to_return['photos'][0]['image_url'] = $flickr_image_data[1]; + $flickr_data_to_return['photos'][0]['photo_title'] = $flickr_image_data[2]; + $flickr_data_to_return['photos'][0]['modal_text'] = $flickr_image_data[3]; + $flickr_data_to_return['photos'][0]['author_link'] = $flickr_image_data[4]; + $flickr_data_to_return['photos'][0]['license_url'] = $flickr_image_data[5]; + + //Break out of the loop + break; + } else { + //Get all images + //Extract data out of the image data, either in our $_SESSION var already is recently pushed (new data) + $flickr_data_to_return['photos'][(int)$flickrphoto["id"]]['image_url'] = $imageurl; + $flickr_data_to_return['photos'][(int)$flickrphoto["id"]]['photo_title'] = $photo["title"]; + $flickr_data_to_return['photos'][(int)$flickrphoto["id"]]['modal_text'] = $modaltext; + $flickr_data_to_return['photos'][(int)$flickrphoto["id"]]['author_link'] = $authorlink; + $flickr_data_to_return['photos'][(int)$flickrphoto["id"]]['license_url'] = $license_url; + } + } + } + + $foundImage = true; + $success = true; + $message = 'Flickr image data successfully downloaded'; + } + + //Fill out all other common data + $flickr_data_to_return['Com_Name'] = $detection_data['Com_Name']; + $flickr_data_to_return['Sci_Name'] = $detection_data['Sci_Name']; + $flickr_data_to_return['Date'] = $detection_data['Date']; + $flickr_data_to_return['Time'] = $detection_data['Time']; + $flickr_data_to_return['Confidence'] = array_key_exists('Confidence', $detection_data) ? $detection_data['Confidence'] : 0; + $flickr_data_to_return['File_Name'] = $detection_data['File_Name']; + + $flickr_data_to_return['Com_Name_clean'] = $comname_clean; + $flickr_data_to_return['Sci_Name_clean'] = $sciname_clean; + + $flickr_data_to_return['filename_path'] = $filename_path; + $flickr_data_to_return['filename_formatted'] = $filename_formatted; + } + } + + return array('success' => $success, 'message' => $message, 'image_found' => $foundImage, 'data' => $flickr_data_to_return); +} + + +/** + * Returns the filename of todays species chart for the main page + * + * @return string + */ +function getChartString() +{ + $myDate = date('Y-m-d'); + $chart = "Combo-$myDate.png"; + return $chart; +} + +/** + * Returns the start and end dates for last week + * + * @return string[] + */ +function getLastWeekDates() +{ + $startdate = strtotime('last sunday') - (7 * 86400); + $enddate = strtotime('last sunday') - (1 * 86400); + + return ['start_date' => $startdate, 'end_date' => $enddate]; +} + +/** + * Directory Path Helper, returns a full directory path for a supplied directory name e.g home, processed, extracted + * + * @param string $dir The directory to obtain a path for + * @return string + */ +function getDirectory($dir) +{ + global $config, $home; + $dir = strtolower($dir); + + if ($dir == "home") { + return $home; + } else if ($dir == "birdnet-pi" || $dir == "birdnet_pi") { + return getDirectory('home') . '/BirdNET-Pi'; + } else if ($dir == "recs_dir" || $dir == "recordings_dir") { + $recs_dir_setting = $config['RECS_DIR']; + return str_replace('$HOME', getDirectory('home'), $recs_dir_setting); + } else if ($dir == "processed") { + $processed_dir_setting = $config['PROCESSED']; + return getDirectory('recs_dir') . str_replace('${RECS_DIR}', '', $processed_dir_setting); + } else if ($dir == "extracted") { + $extracted_dir_setting = $config['EXTRACTED']; + return getDirectory('recs_dir') . str_replace('${RECS_DIR}', '', $extracted_dir_setting); + } elseif ($dir == "extracted_bydate") { + return getDirectory('extracted') . '/By_Date'; + } elseif ($dir == "shifted_audio") { + return getDirectory('home') . '/BirdSongs/Extracted/By_Date/shifted'; + } elseif ($dir == "database") { + // NOT USED + return getDirectory('birdnet_pi') . '/database'; + } elseif ($dir == "config") { + // NOT USED + return getDirectory('birdnet_pi') . '/config'; + } elseif ($dir == "models" || $dir == "model") { + return getDirectory('birdnet_pi') . '/model'; + } elseif ($dir == "scripts") { + return getDirectory('birdnet_pi') . '/scripts'; + } elseif ($dir == "templates") { + return getDirectory('birdnet_pi') . '/templates'; + } elseif ($dir == "web" || $dir == "www") { + return getDirectory('birdnet_pi') . '/homepage'; + } + + return ""; +} + +/** + * Returns the full filepath for the specified filename + * + * @param $filename + * @return string + */ +function getFilePath($filename) +{ + if ($filename == "analyzing_now.txt") { + return getDirectory('birdnet_pi') . "/analyzing_now.txt"; + } else if ($filename == "apprise.txt") { + return getDirectory('birdnet_pi') . "/apprise.txt"; + } else if ($filename == "birdnet.conf") { + return getDirectory('birdnet_pi') . "/birdnet.conf"; + } else if ($filename == "BirdDB.txt") { + return getDirectory('birdnet_pi') . "/BirdDB.txt"; + } else if ($filename == "birds.db") { + return getDirectory('scripts') . "/birds.db"; + } else if ($filename == "blacklisted_images.txt") { + return getDirectory('scripts') . "/blacklisted_images.txt"; + } else if ($filename == "disk_check_exclude.txt") { + return getDirectory('scripts') . "/disk_check_exclude.txt"; + } else if ($filename == "exclude_species_list.txt") { + return getDirectory('home') . "/exclude_species_list.txt"; + } else if ($filename == "firstrun.ini") { + return getDirectory('home') . "/firstrun.ini"; + } else if ($filename == "HUMAN.txt") { + return getDirectory('birdnet_pi') . "/HUMAN.txt"; + } else if ($filename == "include_species_list.txt") { + return getDirectory('birdnet_pi') . "/include_species_list.txt"; + } else if ($filename == "labels.txt") { + return getDirectory('model') . "/labels.txt"; + } else if ($filename == "labels_flickr.txt") { + return getDirectory('model') . "/labels_flickr.txt"; + } else if ($filename == "labels_l18n.zip") { + return getDirectory('model') . "/labels_l18n.zip"; + } else if ($filename == "labels_lang.txt") { + return getDirectory('model') . "/labels_lang.txt"; + } else if ($filename == "labels_nm.zip") { + return getDirectory('model') . "/labels_nm.zip"; + } else if ($filename == "lastrun.txt") { + return getDirectory('scripts') . "/lastrun.txt"; + } else if ($filename == "thisrun.txt") { + return getDirectory('scripts') . "/thisrun.txt"; + } + + return ""; +} + +/** + * Logging helper to accept a message and process it accordingly + * @param $message + * @param $level + * @return void + */ +function birdnet_error_log($message, $level = 'error') +{ + $logfile_path = "./scripts/web-ui-error.log"; + + if (!file_exists($logfile_path)) { + touch($logfile_path); + } + + error_log($message . "\r\n", 3, $logfile_path); +} + + +register_shutdown_function('disconnect_from_birdsdb'); \ No newline at end of file diff --git a/scripts/history.php b/scripts/history.php index a37cbe0..27f4448 100644 --- a/scripts/history.php +++ b/scripts/history.php @@ -1,13 +1,5 @@ prepare("SELECT COUNT(*) FROM detections - WHERE Date == \"$theDate\""); -$result1 = $statement1->execute(); -$totalcount = $result1->fetchArray(SQLITE3_ASSOC); +$statement1 = getDetectionCountByDate($theDate); +if($statement1['success'] == False){ + echo $statement1['message']; + header("refresh: 0;"); +} if(isset($_GET['blocation']) ) { @@ -45,16 +36,17 @@ if(isset($_GET['blocation']) ) { $hrsinday = 24; for($i=0;$i<$hrsinday;$i++) { $starttime = strtotime("12 AM") + (3600*$i); + $endtime = date("H:i",$starttime + 3600); - $statement1 = $db->prepare("SELECT DISTINCT(Com_Name), COUNT(*) FROM detections WHERE Date == \"$theDate\" AND Time > '".date("H:i", $starttime)."' AND Time < '".date("H:i",$starttime + 3600)."' AND Confidence > 0.75 GROUP By Com_Name ORDER BY COUNT(*) DESC"); - if($statement1 == False){ - echo "Database is busy"; - header("refresh: 0;"); + $statement1 = getDetectionBreakdownByTime($theDate,$starttime,$endtime); + if($statement1['success'] == False){ + echo $statement1['message']; + header("refresh: 0;"); } - $result1 = $statement1->execute(); + $result1 = $statement1['data']; $detections = []; - while($detection=$result1->fetchArray(SQLITE3_ASSOC)) + foreach ($result1 as $detection) { $detections[$detection["Com_Name"]] = $detection["COUNT(*)"]; } diff --git a/scripts/overview.php b/scripts/overview.php index 6b9f52f..848a025 100644 --- a/scripts/overview.php +++ b/scripts/overview.php @@ -1,36 +1,39 @@ prepare('SELECT COUNT(*) FROM detections WHERE Date == DATE(\'now\', \'localtime\')'); -if($statement2 == False) { - echo "Database is busy"; - header("refresh: 0;"); +if($todaycount_data['success'] == False){ + echo $todaycount_data['message']; + header("refresh: 0;"); } -$result2 = $statement2->execute(); -$todaycount = $result2->fetchArray(SQLITE3_ASSOC); +$todaycount = $todaycount_data['data']; if(isset($_GET['custom_image'])){ if(isset($config["CUSTOM_IMAGE"])) { @@ -53,11 +56,9 @@ if(isset($_GET['blacklistimage'])) { if($submittedpwd == $config['CADDY_PWD'] && $submitteduser == 'birdnet'){ $imageid = $_GET['blacklistimage']; - $file_handle = fopen($home."/BirdNET-Pi/scripts/blacklisted_images.txt", 'a+'); - fwrite($file_handle, $imageid . "\n"); - fclose($file_handle); + $result = blacklistFlickrImage($imageid); unset($_SESSION['images']); - die("OK"); + die($result['message']); } else { header('WWW-Authenticate: Basic realm="My Realm"'); header('HTTP/1.0 401 Unauthorized'); @@ -74,116 +75,59 @@ if(isset($_GET['blacklistimage'])) { } if(isset($_GET['fetch_chart_string']) && $_GET['fetch_chart_string'] == "true") { - $myDate = date('Y-m-d'); - $chart = "Combo-$myDate.png"; + $chart = getChartString(); echo $chart; die(); } 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 15'); - if($statement4 == False) { - echo "Database is busy"; - header("refresh: 0;"); - } - $result4 = $statement4->execute(); - if(!isset($_SESSION['images'])) { - $_SESSION['images'] = []; - } - $iterations = 0; - $lines; - $licenses_urls = array(); + $result4 = getMostRecentDetection(15); + + if ($result4['success'] == False) { + echo $result4['message']; + header("refresh: 0;"); + } + $result4 = $result4['data']; + + $iterations = 0; + $processed_detections = 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']); + foreach ($result4 as $mostrecent){ + $iterations++; + $comname = preg_replace('/ /', '_', $mostrecent['Com_Name']); $sciname = preg_replace('/ /', '_', $mostrecent['Sci_Name']); $comname = preg_replace('/\'/', '', $comname); $filename = "/By_Date/".$mostrecent['Date']."/".$comname."/".$mostrecent['File_Name']; - $args = "&license=2%2C3%2C4%2C5%2C6%2C9&orientation=square,portrait"; - $comnameprefix = "%20bird"; - - // check to make sure the image actually exists, sometimes it takes a minute to be created\ - if(file_exists($home."/BirdSongs/Extracted".$filename.".png")){ - if($_GET['previous_detection_identifier'] == $filename) { die(); } - if($_GET['only_name'] == "true") { echo $comname.",".$filename;die(); } + // check to make sure the image actually exists, sometimes it takes a minute to be created\ + if (file_exists(getDirectory('extracted') . "/" . $filename . ".png")) { + if ($_GET['previous_detection_identifier'] == $filename) { + die(); + } + if ($_GET['only_name'] == "true") { + echo $comname . "," . $filename; + die(); + } - $iterations++; + $processed_detections++; - if (!empty($config["FLICKR_API_KEY"])) { + //Get the flickr image for this detection + $flickr_Image = getFlickrImage($mostrecent); - if(!empty($config["FLICKR_FILTER_EMAIL"])) { - if(!isset($_SESSION["FLICKR_FILTER_EMAIL"])) { - unset($_SESSION['images']); - $_SESSION['FLICKR_FILTER_EMAIL'] = json_decode(file_get_contents("https://www.flickr.com/services/rest/?method=flickr.people.findByEmail&api_key=".$config["FLICKR_API_KEY"]."&find_email=".$config["FLICKR_FILTER_EMAIL"]."&format=json&nojsoncallback=1"), true)["user"]["nsid"]; - } - $args = "&user_id=".$_SESSION['FLICKR_FILTER_EMAIL']; - $comnameprefix = ""; - } else { - if(isset($_SESSION["FLICKR_FILTER_EMAIL"])) { - unset($_SESSION["FLICKR_FILTER_EMAIL"]); - unset($_SESSION['images']); - } - } - - - // 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($_SESSION['images'], 0)); - if($key !== false) { - $image = $_SESSION['images'][$key]; - } else { - // Get license information if we haven't already - if (empty($licenses_urls)) { - $licenses_url = "https://api.flickr.com/services/rest/?method=flickr.photos.licenses.getInfo&api_key=".$config["FLICKR_API_KEY"]."&format=json&nojsoncallback=1"; - $licenses_response = file_get_contents($licenses_url); - $licenses_data = json_decode($licenses_response, true)["licenses"]["license"]; - foreach ($licenses_data as $license) { - $license_id = $license["id"]; - $license_name = $license["name"]; - $license_url = $license["url"]; - $licenses_urls[$license_id] = $license_url; - } - } - - // only open the file once per script execution - if(!isset($lines)) { - $lines = file($home."/BirdNET-Pi/model/labels_flickr.txt"); - } - // convert sci name to English name - foreach($lines as $line){ - if(strpos($line, $mostrecent['Sci_Name']) !== false){ - $engname = trim(explode("_", $line)[1]); - break; - } - } - - // Read the blacklisted image ids from the file into an array - $blacklisted_ids = array_map('trim', file($home."/BirdNET-Pi/scripts/blacklisted_images.txt")); - - // Make the API call - $flickrjson = json_decode(file_get_contents("https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=".$config["FLICKR_API_KEY"]."&text=".str_replace(" ", "%20", $engname).$comnameprefix."&sort=relevance".$args."&per_page=5&media=photos&format=json&nojsoncallback=1"), true)["photos"]["photo"]; - - // Find the first photo that is not blacklisted or is not the specific blacklisted id - $photo = null; - foreach ($flickrjson as $flickrphoto) { - if ($flickrphoto["id"] !== "4892923285" && !in_array($flickrphoto["id"], $blacklisted_ids)) { - $photo = $flickrphoto; - break; - } - } - - $license_url = "https://api.flickr.com/services/rest/?method=flickr.photos.getInfo&api_key=".$config["FLICKR_API_KEY"]."&photo_id=".$photo["id"]."&format=json&nojsoncallback=1"; - $license_response = file_get_contents($license_url); - $license_info = json_decode($license_response, true)["photo"]["license"]; - $license_url = $licenses_urls[$license_info]; - - $modaltext = "https://flickr.com/photos/".$photo["owner"]."/".$photo["id"]; - $authorlink = "https://flickr.com/people/".$photo["owner"]; - $imageurl = 'https://farm' .$photo["farm"]. '.static.flickr.com/' .$photo["server"]. '/' .$photo["id"]. '_' .$photo["secret"]. '.jpg'; - array_push($_SESSION['images'], array($comname,$imageurl,$photo["title"], $modaltext, $authorlink, $license_url)); - $image = $_SESSION['images'][count($_SESSION['images'])-1]; - } - } + if ($flickr_Image['image_found']) { + //Remap the data from returned data into an array that is referenced in our HTML + //to save have to rewrite or adjust it + $flickr_Image_data = $flickr_Image['data']; + $image = [ + 0 => $flickr_Image_data['Com_Name_clean'], + 1 => $flickr_Image_data['photos'][0]['image_url'], + 2 => $flickr_Image_data['photos'][0]['photo_title'], + 3 => $flickr_Image_data['photos'][0]['modal_text'], + 4 => $flickr_Image_data['photos'][0]['author_link'], + 5 => $flickr_Image_data['photos'][0]['license_url'] + ]; + } ?> WARNING: Your latitude and longitude are not set properly. Please do so now in Tools -> Settings."; @@ -187,25 +154,25 @@ if(isset($_GET['view'])){ if($_GET['view'] == "Advanced"){include('scripts/advanced.php');} if($_GET['view'] == "Included"){ if(isset($_GET['species']) && isset($_GET['add'])){ - $file = './scripts/include_species_list.txt'; + $file = getFilePath('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", $selectedOption."\n", FILE_APPEND); + file_put_contents( getFilePath('include_species_list.txt'), $selectedOption."\n", FILE_APPEND); } } elseif(isset($_GET['species']) && isset($_GET['del'])){ - $file = './scripts/include_species_list.txt'; + $file = getFilePath('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"); + $content = file_get_contents( getFilePath('include_species_list.txt')); $newcontent = str_replace($selectedOption, "", "$content"); - file_put_contents("./scripts/include_species_list.txt", "$newcontent"); + file_put_contents( getFilePath('include_species_list.txt'), "$newcontent"); } - $file = './scripts/include_species_list.txt'; + $file = getFilePath('include_species_list.txt'); $str = file_get_contents("$file"); $str = preg_replace('/^\h*\v+/m', '', $str); file_put_contents("$file", "$str"); @@ -214,23 +181,23 @@ if(isset($_GET['view'])){ } if($_GET['view'] == "Excluded"){ if(isset($_GET['species']) && isset($_GET['add'])){ - $file = './scripts/exclude_species_list.txt'; + $file = getFilePath('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", $selectedOption."\n", FILE_APPEND); + file_put_contents(getFilePath('exclude_species_list.txt'), $selectedOption."\n", FILE_APPEND); } elseif (isset($_GET['species']) && isset($_GET['del'])){ - $file = './scripts/exclude_species_list.txt'; + $file = getFilePath('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"); + $content = file_get_contents(getFilePath('exclude_species_list.txt')); $newcontent = str_replace($selectedOption, "", "$content"); - file_put_contents("./scripts/exclude_species_list.txt", "$newcontent"); + file_put_contents(getFilePath('exclude_species_list.txt'), "$newcontent"); } - $file = './scripts/exclude_species_list.txt'; + $file = getFilePath('exclude_species_list.txt'); $str = file_get_contents("$file"); $str = preg_replace('/^\h*\v+/m', '', $str); file_put_contents("$file", "$str"); @@ -241,11 +208,7 @@ if(isset($_GET['view'])){ echo ""; } if($_GET['view'] == "Webterm"){ - 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'); - } + parseConfig(); $caddypwd = $config['CADDY_PWD']; if (!isset($_SERVER['PHP_AUTH_USER'])) { header('WWW-Authenticate: Basic realm="My Realm"'); @@ -267,11 +230,7 @@ if(isset($_GET['view'])){ } } } elseif(isset($_GET['submit'])) { - 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'); - } + parseConfig(); $caddypwd = $config['CADDY_PWD']; if (!isset($_SERVER['PHP_AUTH_USER'])) { header('WWW-Authenticate: Basic realm="My Realm"'); @@ -281,48 +240,48 @@ if(isset($_GET['view'])){ } else { $submittedpwd = $_SERVER['PHP_AUTH_PW']; $submitteduser = $_SERVER['PHP_AUTH_USER']; - $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 extraction.service', - 'sudo systemctl restart extraction.service', - 'sudo systemctl disable --now extraction.service', - 'sudo systemctl enable --now extraction.service', - 'sudo systemctl stop birdnet_server.service', - 'sudo systemctl restart birdnet_server.service', - 'sudo systemctl disable --now birdnet_server.service', - 'sudo systemctl enable --now birdnet_server.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', - 'stop_core_services.sh', - 'restart_services.sh', + $allowedCommands = array('service stop livestream.service && service stop icecast2.service', + 'service restart livestream.service && service restart icecast2.service', + 'service disable livestream.service && service disable icecast2 && service stop icecast2.service', + 'service enable icecast2 && service start icecast2.service && service enable livestream.service', + 'service stop web_terminal.service', + 'service restart web_terminal.service', + 'service disable web_terminal.service', + 'service enable web_terminal.service', + 'service stop birdnet_log.service', + 'service restart birdnet_log.service', + 'service disable birdnet_log.service', + 'service enable birdnet_log.service', + 'service stop extraction.service', + 'service restart extraction.service', + 'service disable extraction.service', + 'service enable extraction.service', + 'service stop birdnet_server.service', + 'service restart birdnet_server.service', + 'service disable birdnet_server.service', + 'service enable birdnet_server.service', + 'service stop birdnet_analysis.service', + 'service restart birdnet_analysis.service', + 'service disable birdnet_analysis.service', + 'service enable birdnet_analysis.service', + 'service stop birdnet_stats.service', + 'service restart birdnet_stats.service', + 'service disable birdnet_stats.service', + 'service enable birdnet_stats.service', + 'service stop birdnet_recording.service', + 'service restart birdnet_recording.service', + 'service disable birdnet_recording.service', + 'service enable birdnet_recording.service', + 'service stop chart_viewer.service', + 'service restart chart_viewer.service', + 'service disable chart_viewer.service', + 'service enable chart_viewer.service', + 'service stop spectrogram_viewer.service', + 'service restart spectrogram_viewer.service', + 'service disable spectrogram_viewer.service', + 'service enable spectrogram_viewer.service', + 'system stop core.services', + 'system restart core.services', 'sudo reboot', 'update_birdnet.sh', 'sudo shutdown now', @@ -331,33 +290,43 @@ if(isset($_GET['view'])){ if($submittedpwd == $caddypwd && $submitteduser == 'birdnet' && in_array($command,$allowedCommands)){ if(isset($command)){ $initcommand = $command; - if (strpos($command, "systemctl") !== false) { + $results = ""; + //Process the system commands differently + if (strpos($command, "system") !== false) { + $results = serviceMaintenance($command); + //clear the command so we skip the next bits and go straight to output processing + $command = ''; + } + if (strpos($command, "service") !== 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) { + //Action the command + serviceMaintenance($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 + //get the service names so we can poll th status $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 + serviceMaintenance($command); + //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; + //Build up the command that will query the service status + $command = "sleep 3;sudo systemctl status " . $service_names; } if($initcommand == "update_birdnet.sh") { unset($_SESSION['behind']); } - $results = shell_exec("$command 2>&1"); + $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); diff --git a/scripts/advanced.php b/scripts/advanced.php index ae46ac3..dd03e67 100644 --- a/scripts/advanced.php +++ b/scripts/advanced.php @@ -1,11 +1,8 @@ /dev/null 2>&1 &'); - } + setSetting('CADDY_PWD', "\"$caddy_pwd\"",'update_caddyfile'); } if(isset($_GET["ice_pwd"])) { $ice_pwd = $_GET["ice_pwd"]; - if(strcmp($ice_pwd,$config['ICE_PWD']) !== 0) { - $contents = preg_replace("/ICE_PWD=.*/", "ICE_PWD=$ice_pwd", $contents); - $contents2 = preg_replace("/ICE_PWD=.*/", "ICE_PWD=$ice_pwd", $contents2); - } + setSetting('ICE_PWD', $ice_pwd); } if(isset($_GET["birdnetpi_url"])) { $birdnetpi_url = $_GET["birdnetpi_url"]; // remove trailing slash to prevent conf from becoming broken $birdnetpi_url = rtrim($birdnetpi_url, '/'); - if(strcmp($birdnetpi_url,$config['BIRDNETPI_URL']) !== 0) { - $contents = preg_replace("/BIRDNETPI_URL=.*/", "BIRDNETPI_URL=$birdnetpi_url", $contents); - $contents2 = preg_replace("/BIRDNETPI_URL=.*/", "BIRDNETPI_URL=$birdnetpi_url", $contents2); - $fh = fopen('/etc/birdnet/birdnet.conf', "w"); - $fh2 = fopen("./scripts/thisrun.txt", "w"); - fwrite($fh, $contents); - fwrite($fh2, $contents2); - exec('sudo /usr/local/bin/update_caddyfile.sh > /dev/null 2>&1 &'); - } + setSetting('BIRDNETPI_URL', $birdnetpi_url,'update_caddyfile'); } if(isset($_GET["rtsp_stream"])) { $rtsp_stream = str_replace("\r\n", ",", $_GET["rtsp_stream"]); - if(strcmp($rtsp_stream,$config['RTSP_STREAM']) !== 0) { - $contents = preg_replace("/RTSP_STREAM=.*/", "RTSP_STREAM=\"$rtsp_stream\"", $contents); - $contents2 = preg_replace("/RTSP_STREAM=.*/", "RTSP_STREAM=\"$rtsp_stream\"", $contents2); - $fh = fopen('/etc/birdnet/birdnet.conf', "w"); - $fh2 = fopen("./scripts/thisrun.txt", "w"); - fwrite($fh, $contents); - fwrite($fh2, $contents2); - exec('sudo systemctl restart birdnet_recording.service'); - exec('sudo systemctl restart livestream.service'); - } + setSetting('RTSP_STREAM', "\"$rtsp_stream\"", ['restart birdnet_recording', 'restart livestream']); } if (isset($_GET["rtsp_stream_to_livestream"])) { $rtsp_stream_selected = trim($_GET["rtsp_stream_to_livestream"]); - - //Setting exists already, see if the value changed - if (strcmp($rtsp_stream_selected, $config['RTSP_STREAM_TO_LIVESTREAM']) !== 0) { - $contents = preg_replace("/RTSP_STREAM_TO_LIVESTREAM=.*/", "RTSP_STREAM_TO_LIVESTREAM=\"$rtsp_stream_selected\"", $contents); - $contents2 = preg_replace("/RTSP_STREAM_TO_LIVESTREAM=.*/", "RTSP_STREAM_TO_LIVESTREAM=\"$rtsp_stream_selected\"", $contents2); - $fh = fopen("/etc/birdnet/birdnet.conf", "w"); - $fh2 = fopen("./scripts/thisrun.txt", "w"); - fwrite($fh, $contents); - fwrite($fh2, $contents2); - sleep(1); - exec("sudo systemctl restart livestream.service"); - } + setSetting('RTSP_STREAM_TO_LIVESTREAM', "\"$rtsp_stream_selected\"", 'restart livestream'); } if(isset($_GET["overlap"])) { $overlap = $_GET["overlap"]; - if(strcmp($overlap,$config['OVERLAP']) !== 0) { - $contents = preg_replace("/OVERLAP=.*/", "OVERLAP=$overlap", $contents); - $contents2 = preg_replace("/OVERLAP=.*/", "OVERLAP=$overlap", $contents2); - } + setSetting('OVERLAP', $overlap); } if(isset($_GET["confidence"])) { $confidence = $_GET["confidence"]; - if(strcmp($confidence,$config['CONFIDENCE']) !== 0) { - $contents = preg_replace("/CONFIDENCE=.*/", "CONFIDENCE=$confidence", $contents); - $contents2 = preg_replace("/CONFIDENCE=.*/", "CONFIDENCE=$confidence", $contents2); - } + setSetting('CONFIDENCE', $confidence); } if(isset($_GET["sensitivity"])) { $sensitivity = $_GET["sensitivity"]; - if(strcmp($sensitivity,$config['SENSITIVITY']) !== 0) { - $contents = preg_replace("/SENSITIVITY=.*/", "SENSITIVITY=$sensitivity", $contents); - $contents2 = preg_replace("/SENSITIVITY=.*/", "SENSITIVITY=$sensitivity", $contents2); - } + setSetting('SENSITIVITY', $sensitivity); } if(isset($_GET["freqshift_hi"]) && is_numeric($_GET['freqshift_hi'])) { $freqshift_hi = $_GET["freqshift_hi"]; - if(strcmp($freqshift_hi,$config['FREQSHIFT_HI']) !== 0) { - $contents = preg_replace("/FREQSHIFT_HI=.*/", "FREQSHIFT_HI=$freqshift_hi", $contents); - $contents2 = preg_replace("/FREQSHIFT_HI=.*/", "FREQSHIFT_HI=$freqshift_hi", $contents2); - } + setSetting('FREQSHIFT_HI', $freqshift_hi); } if(isset($_GET["freqshift_lo"]) && is_numeric($_GET['freqshift_lo'])) { $freqshift_lo = $_GET["freqshift_lo"]; - if(strcmp($freqshift_lo,$config['FREQSHIFT_LO']) !== 0) { - $contents = preg_replace("/FREQSHIFT_LO=.*/", "FREQSHIFT_LO=$freqshift_lo", $contents); - $contents2 = preg_replace("/FREQSHIFT_LO=.*/", "FREQSHIFT_LO=$freqshift_lo", $contents2); - } + setSetting('FREQSHIFT_HI', $freqshift_hi); } if(isset($_GET["freqshift_pitch"]) && is_numeric($_GET['freqshift_pitch'])) { $freqshift_pitch = $_GET["freqshift_pitch"]; - if(strcmp($freqshift_pitch,$config['FREQSHIFT_PITCH']) !== 0) { - $contents = preg_replace("/FREQSHIFT_PITCH=.*/", "FREQSHIFT_PITCH=$freqshift_pitch", $contents); - $contents2 = preg_replace("/FREQSHIFT_PITCH=.*/", "FREQSHIFT_PITCH=$freqshift_pitch", $contents2); - } + setSetting('FREQSHIFT_PITCH', $freqshift_pitch); } if(isset($_GET["freqshift_tool"])) { $freqshift_tool = $_GET["freqshift_tool"]; - if(strcmp($freqshift_tool,$config['FREQSHIFT_TOOL']) !== 0) { - $contents = preg_replace("/FREQSHIFT_TOOL=.*/", "FREQSHIFT_TOOL=$freqshift_tool", $contents); - $contents2 = preg_replace("/FREQSHIFT_TOOL=.*/", "FREQSHIFT_TOOL=$freqshift_tool", $contents2); - } + setSetting('FREQSHIFT_TOOL', $freqshift_tool); } if(isset($_GET["full_disk"])) { $full_disk = $_GET["full_disk"]; - if(strcmp($full_disk,$config['FULL_DISK']) !== 0) { - $contents = preg_replace("/FULL_DISK=.*/", "FULL_DISK=$full_disk", $contents); - $contents2 = preg_replace("/FULL_DISK=.*/", "FULL_DISK=$full_disk", $contents2); - } + setSetting('FULL_DISK', $full_disk); } if(isset($_GET["privacy_threshold"])) { $privacy_threshold = $_GET["privacy_threshold"]; - if(strcmp($privacy_threshold,$config['PRIVACY_THRESHOLD']) !== 0) { - $contents = preg_replace("/PRIVACY_THRESHOLD=.*/", "PRIVACY_THRESHOLD=$privacy_threshold", $contents); - $contents2 = preg_replace("/PRIVACY_THRESHOLD=.*/", "PRIVACY_THRESHOLD=$privacy_threshold", $contents2); - exec('restart_services.sh'); - } + setSetting('PRIVACY_THRESHOLD', $privacy_threshold,'restart_services'); } if(isset($_GET["rec_card"])) { - $rec_card = $_GET["rec_card"]; - if(strcmp($rec_card,$config['REC_CARD']) !== 0) { - $contents = preg_replace("/REC_CARD=.*/", "REC_CARD=\"$rec_card\"", $contents); - $contents2 = preg_replace("/REC_CARD=.*/", "REC_CARD=\"$rec_card\"", $contents2); - } + $rec_card = trim($_GET["rec_card"]); + setSetting('REC_CARD', "\"$rec_card\""); } if(isset($_GET["channels"])) { $channels = $_GET["channels"]; - if(strcmp($channels,$config['CHANNELS']) !== 0) { - $contents = preg_replace("/CHANNELS=.*/", "CHANNELS=$channels", $contents); - $contents2 = preg_replace("/CHANNELS=.*/", "CHANNELS=$channels", $contents2); - } + setSetting('CHANNELS', $channels); } if(isset($_GET["recording_length"])) { $recording_length = $_GET["recording_length"]; - if(strcmp($recording_length,$config['RECORDING_LENGTH']) !== 0) { - $contents = preg_replace("/RECORDING_LENGTH=.*/", "RECORDING_LENGTH=$recording_length", $contents); - $contents2 = preg_replace("/RECORDING_LENGTH=.*/", "RECORDING_LENGTH=$recording_length", $contents2); - } + setSetting('RECORDING_LENGTH', $recording_length); } if(isset($_GET["extraction_length"])) { $extraction_length = $_GET["extraction_length"]; - if(strcmp($extraction_length,$config['EXTRACTION_LENGTH']) !== 0) { - $contents = preg_replace("/EXTRACTION_LENGTH=.*/", "EXTRACTION_LENGTH=$extraction_length", $contents); - $contents2 = preg_replace("/EXTRACTION_LENGTH=.*/", "EXTRACTION_LENGTH=$extraction_length", $contents2); - } + setSetting('EXTRACTION_LENGTH', $extraction_length); } if(isset($_GET["audiofmt"])) { $audiofmt = $_GET["audiofmt"]; - if(strcmp($audiofmt,$config['AUDIOFMT']) !== 0) { - $contents = preg_replace("/AUDIOFMT=.*/", "AUDIOFMT=$audiofmt", $contents); - $contents2 = preg_replace("/AUDIOFMT=.*/", "AUDIOFMT=$audiofmt", $contents2); - } + setSetting('AUDIOFMT', $audiofmt); } if(isset($_GET["silence_update_indicator"])) { $silence_update_indicator = 1; - if(strcmp($silence_update_indicator,$config['SILENCE_UPDATE_INDICATOR']) !== 0) { - $contents = preg_replace("/SILENCE_UPDATE_INDICATOR=.*/", "SILENCE_UPDATE_INDICATOR=$silence_update_indicator", $contents); - $contents2 = preg_replace("/SILENCE_UPDATE_INDICATOR=.*/", "SILENCE_UPDATE_INDICATOR=$silence_update_indicator", $contents2); - } + setSetting('SILENCE_UPDATE_INDICATOR', $silence_update_indicator); } else { - $contents = preg_replace("/SILENCE_UPDATE_INDICATOR=.*/", "SILENCE_UPDATE_INDICATOR=0", $contents); - $contents2 = preg_replace("/SILENCE_UPDATE_INDICATOR=.*/", "SILENCE_UPDATE_INDICATOR=0", $contents2); + setSetting('SILENCE_UPDATE_INDICATOR', 0); } if(isset($_GET["raw_spectrogram"])) { $raw_spectrogram = 1; - if(strcmp($RAW_SPECTROGRAM,$config['RAW_SPECTROGRAM']) !== 0) { - $contents = preg_replace("/RAW_SPECTROGRAM=.*/", "RAW_SPECTROGRAM=$raw_spectrogram", $contents); - $contents2 = preg_replace("/RAW_SPECTROGRAM=.*/", "RAW_SPECTROGRAM=$raw_spectrogram", $contents2); - } + setSetting('RAW_SPECTROGRAM', $raw_spectrogram); } else { - $contents = preg_replace("/RAW_SPECTROGRAM=.*/", "RAW_SPECTROGRAM=0", $contents); - $contents2 = preg_replace("/RAW_SPECTROGRAM=.*/", "RAW_SPECTROGRAM=0", $contents2); + setSetting('RAW_SPECTROGRAM', 0); } if(isset($_GET["custom_image"])) { $custom_image = $_GET["custom_image"]; - if(strcmp($custom_image,$config['CUSTOM_IMAGE']) !== 0) { - $contents = preg_replace("/CUSTOM_IMAGE=.*/", "CUSTOM_IMAGE=$custom_image", $contents); - $contents2 = preg_replace("/CUSTOM_IMAGE=.*/", "CUSTOM_IMAGE=$custom_image", $contents2); - } + + setSetting('CUSTOM_IMAGE', $custom_image); } if(isset($_GET["custom_image_label"])) { $custom_image_label = $_GET["custom_image_label"]; - if(strcmp($custom_image_label,$config['CUSTOM_IMAGE_TITLE']) !== 0) { - $contents = preg_replace("/CUSTOM_IMAGE_TITLE=.*/", "CUSTOM_IMAGE_TITLE=\"$custom_image_label\"", $contents); - $contents2 = preg_replace("/CUSTOM_IMAGE_TITLE=.*/", "CUSTOM_IMAGE_TITLE=\"$custom_image_label\"", $contents2); - } + + setSetting('CUSTOM_IMAGE_TITLE', "\"$custom_image_label\""); } if (isset($_GET["LogLevel_BirdnetRecordingService"])) { $birdnet_recording_service_log_level = trim($_GET["LogLevel_BirdnetRecordingService"]); - //If setting exists change it's value - if (array_key_exists('LogLevel_BirdnetRecordingService', $config)) { - //Setting exists already, see if the value changed - if (strcmp($birdnet_recording_service_log_level, $config['LogLevel_BirdnetRecordingService']) !== 0) { - $contents = preg_replace("/LogLevel_BirdnetRecordingService=.*/", "LogLevel_BirdnetRecordingService=\"$birdnet_recording_service_log_level\"", $contents); - $contents2 = preg_replace("/LogLevel_BirdnetRecordingService=.*/", "LogLevel_BirdnetRecordingService=\"$birdnet_recording_service_log_level\"", $contents2); - $fh = fopen("/etc/birdnet/birdnet.conf", "w"); - $fh2 = fopen("./scripts/thisrun.txt", "w"); - fwrite($fh, $contents); - fwrite($fh2, $contents2); - sleep(1); - exec("sudo systemctl restart birdnet_recording.service"); - } - } else { - //Create the setting in the setting file - same as what update_birdnet_snippets.sh does but will take the users selected log level as the value - shell_exec('sudo echo "LogLevel_BirdnetRecordingService=\"' . $birdnet_recording_service_log_level . '\"" >> /etc/birdnet/birdnet.conf'); - //also update this run txt file - shell_exec('sudo echo "LogLevel_BirdnetRecordingService=\"' . $birdnet_recording_service_log_level . '\"" >> ./scripts/thisrun.txt'); - - //Reload the config files as we've changed the contents, we need to make sure the contents of the existing variables reflects contents of the config file - sleep(1); - $contents = file_get_contents('/etc/birdnet/birdnet.conf'); - $contents2 = file_get_contents('./scripts/thisrun.txt'); - - exec("sudo systemctl restart birdnet_recording.service"); - } + setSetting('LogLevel_BirdnetRecordingService', "\"$birdnet_recording_service_log_level\"",'restart birdnet_recording'); } if (isset($_GET["LogLevel_SpectrogramViewerService"])) { $spectrogram_viewer_service_log_level = trim($_GET["LogLevel_SpectrogramViewerService"]); - //If setting exists change it's value - if (array_key_exists('LogLevel_SpectrogramViewerService', $config)) { - //Setting exists already, see if the value changed - if (strcmp($spectrogram_viewer_service_log_level, $config['LogLevel_SpectrogramViewerService']) !== 0) { - $contents = preg_replace("/LogLevel_SpectrogramViewerService=.*/", "LogLevel_SpectrogramViewerService=\"$spectrogram_viewer_service_log_level\"", $contents); - $contents2 = preg_replace("/LogLevel_SpectrogramViewerService=.*/", "LogLevel_SpectrogramViewerService=\"$spectrogram_viewer_service_log_level\"", $contents2); - $fh = fopen("/etc/birdnet/birdnet.conf", "w"); - $fh2 = fopen("./scripts/thisrun.txt", "w"); - fwrite($fh, $contents); - fwrite($fh2, $contents2); - sleep(1); - exec("sudo systemctl restart spectrogram_viewer.service"); - } - } else { - //Create the setting in the setting file - same as what update_birdnet_snippets.sh does but will take the users selected log level as the value - shell_exec('sudo echo "LogLevel_SpectrogramViewerService=\"' . $spectrogram_viewer_service_log_level . '\"" >> /etc/birdnet/birdnet.conf'); - //also update this run txt file - shell_exec('sudo echo "LogLevel_SpectrogramViewerService=\"' . $spectrogram_viewer_service_log_level . '\"" >> ./scripts/thisrun.txt'); - - //Reload the config files as we've changed the contents, we need to make sure the contents of the existing variables reflects contents of the config file - sleep(1); - $contents = file_get_contents('/etc/birdnet/birdnet.conf'); - $contents2 = file_get_contents('./scripts/thisrun.txt'); - - exec("sudo systemctl restart spectrogram_viewer.service"); - } + setSetting('LogLevel_SpectrogramViewerService', "\"$spectrogram_viewer_service_log_level\"",'restart spectrogram_viewer'); } if (isset($_GET["LogLevel_LiveAudioStreamService"])) { $livestream_audio_service_log_level = trim($_GET["LogLevel_LiveAudioStreamService"]); - //If setting exists change it's value - if (array_key_exists('LogLevel_LiveAudioStreamService', $config)) { - //Setting exists already, see if the value changed - if (strcmp($livestream_audio_service_log_level, $config['LogLevel_LiveAudioStreamService']) !== 0) { - $contents = preg_replace("/LogLevel_LiveAudioStreamService=.*/", "LogLevel_LiveAudioStreamService=\"$livestream_audio_service_log_level\"", $contents); - $contents2 = preg_replace("/LogLevel_LiveAudioStreamService=.*/", "LogLevel_LiveAudioStreamService=\"$livestream_audio_service_log_level\"", $contents2); - //Write the settings to the config files, so we can restart the relevant services - $fh = fopen("/etc/birdnet/birdnet.conf", "w"); - $fh2 = fopen("./scripts/thisrun.txt", "w"); - fwrite($fh, $contents); - fwrite($fh2, $contents2); - sleep(1); - exec("sudo systemctl restart livestream.service && sudo systemctl restart icecast2.service"); - } - } else { - //Create the setting in the setting file - same as what update_birdnet_snippets.sh does but will take the users selected log level as the value - shell_exec('sudo echo "LogLevel_LiveAudioStreamService=\"' . $livestream_audio_service_log_level . '\"" >> /etc/birdnet/birdnet.conf'); - //also update this run txt file - shell_exec('sudo echo "LogLevel_LiveAudioStreamService=\"' . $livestream_audio_service_log_level . '\"" >> ./scripts/thisrun.txt'); - - //Reload the config files as we've changed the contents, we need to make sure the contents of the existing variables reflects contents of the config file - sleep(1); - $contents = file_get_contents('/etc/birdnet/birdnet.conf'); - $contents2 = file_get_contents('./scripts/thisrun.txt'); - - exec("sudo systemctl restart livestream.service && sudo systemctl restart icecast2.service"); - } + setSetting('LogLevel_LiveAudioStreamService', "\"$livestream_audio_service_log_level\"",['restart livestream','restart icecast2']); } - - //Finally write the data out. some sections do this themselves in order to have the new settings ready for the services that will be restarted - //but will doubly ensure the settings are saved after any modification - $fh = fopen('/etc/birdnet/birdnet.conf', "w"); - $fh2 = fopen("./scripts/thisrun.txt", "w"); - fwrite($fh, $contents); - fwrite($fh2, $contents2); } -$user = trim(shell_exec("awk -F: '/1000/{print $1}' /etc/passwd")); -$home = trim(shell_exec("awk -F: '/1000/{print $6}' /etc/passwd")); - -$count_labels = count(file($home."/BirdNET-Pi/model/labels.txt")); -$count = $count_labels; +$count = getLabelsCount(); ?> @@ -47,7 +44,7 @@ $eachlines = file($filename, FILE_IGNORE_NEW_LINES);
' />
- > + >
- > + >
- > + >
- > + >

From 6af59a2908165e4765e0da76365e809148b4a0e2 Mon Sep 17 00:00:00 2001 From: jaredb7 Date: Wed, 10 May 2023 23:56:23 +1000 Subject: [PATCH 08/29] Fix caddyfile not updating due to command execution being intercepted by auth prompt We always want this command to execute regardless and rebuild the caddy config accordingly. --- scripts/advanced.php | 7 +++++-- scripts/common.php | 13 ++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/scripts/advanced.php b/scripts/advanced.php index 85cdbe5..751ccb8 100644 --- a/scripts/advanced.php +++ b/scripts/advanced.php @@ -25,7 +25,9 @@ if (!isset($_SERVER['PHP_AUTH_USER'])) { if(isset($_GET['submit'])) { if(isset($_GET["caddy_pwd"])) { $caddy_pwd = $_GET["caddy_pwd"]; - saveSetting('CADDY_PWD', "\"$caddy_pwd\"",'update_caddyfile'); + saveSetting('CADDY_PWD', "\"$caddy_pwd\""); + //Make sure the caddy file is set directly + update_caddyfile(); } if(isset($_GET["ice_pwd"])) { @@ -37,7 +39,8 @@ if(isset($_GET['submit'])) { $birdnetpi_url = $_GET["birdnetpi_url"]; // remove trailing slash to prevent conf from becoming broken $birdnetpi_url = rtrim($birdnetpi_url, '/'); - saveSetting('BIRDNETPI_URL', $birdnetpi_url,'update_caddyfile'); + saveSetting('BIRDNETPI_URL', $birdnetpi_url); + update_caddyfile(); } if(isset($_GET["rtsp_stream"])) { diff --git a/scripts/common.php b/scripts/common.php index d239edb..96c533d 100644 --- a/scripts/common.php +++ b/scripts/common.php @@ -1338,9 +1338,6 @@ function executeSysCommand($command_type, $extra_data_to_pass = null) $command = "sudo -u $user " . getFilePath('python3') . ' ' . getFilePath('species.py') . " --threshold " . escapeshellcmd($extra_data_to_pass) . " 2>&1"; $result = shell_exec($command); // - } else if ($command_type == "update_caddyfile") { - $result = exec("sudo /usr/local/bin/update_caddyfile.sh > /dev/null 2>&1 &"); - // } else if ($command_type == "update_birdnet") { $result = shell_exec("update_birdnet.sh"); // @@ -1358,6 +1355,16 @@ function executeSysCommand($command_type, $extra_data_to_pass = null) return $result; } +/** + * Updates the caddy configuration, only ever called when BirdNET-Pi Password or Site URL is set + * + * @return void + */ +function update_caddyfile() +{ + exec("sudo /usr/local/bin/update_caddyfile.sh > /dev/null 2>&1 &"); +} + /** * Performs tasks such as stop, restart, disable and enable on the supplied service * input command must contain the service name and must contain a keyword that describes the action From 66863f2e6ba3cfa5a04f9910c88104b232da6ee7 Mon Sep 17 00:00:00 2001 From: jaredb7 Date: Fri, 12 May 2023 23:09:15 +1000 Subject: [PATCH 09/29] Save password last to avoid auth check preventing saving other settings Because saveSetting() checks each time to see that the user has authenticated, if the password is changed first then very next setting will cause a auth prompt (as the password changed) and if cancelled then the other setting won't save. Putting it last will allow us to save all the other setting and then change the password, then the next attempt to execute a action or access a page that required auth will prompt the user the the password. --- scripts/advanced.php | 37 ++++++++++++++++++++++--------------- scripts/common.php | 30 +++++++----------------------- 2 files changed, 29 insertions(+), 38 deletions(-) diff --git a/scripts/advanced.php b/scripts/advanced.php index 751ccb8..9132cc6 100644 --- a/scripts/advanced.php +++ b/scripts/advanced.php @@ -23,26 +23,11 @@ if (!isset($_SERVER['PHP_AUTH_USER'])) { } if(isset($_GET['submit'])) { - if(isset($_GET["caddy_pwd"])) { - $caddy_pwd = $_GET["caddy_pwd"]; - saveSetting('CADDY_PWD', "\"$caddy_pwd\""); - //Make sure the caddy file is set directly - update_caddyfile(); - } - if(isset($_GET["ice_pwd"])) { $ice_pwd = $_GET["ice_pwd"]; saveSetting('ICE_PWD', $ice_pwd); } - if(isset($_GET["birdnetpi_url"])) { - $birdnetpi_url = $_GET["birdnetpi_url"]; - // remove trailing slash to prevent conf from becoming broken - $birdnetpi_url = rtrim($birdnetpi_url, '/'); - saveSetting('BIRDNETPI_URL', $birdnetpi_url); - update_caddyfile(); - } - if(isset($_GET["rtsp_stream"])) { $rtsp_stream = str_replace("\r\n", ",", $_GET["rtsp_stream"]); saveSetting('RTSP_STREAM', "\"$rtsp_stream\"", ['restart birdnet_recording', 'restart livestream']); @@ -165,6 +150,28 @@ if(isset($_GET['submit'])) { saveSetting('LogLevel_LiveAudioStreamService', "\"$livestream_audio_service_log_level\"",['restart livestream','restart icecast2']); } + + if(isset($_GET["birdnetpi_url"])) { + $birdnetpi_url = $_GET["birdnetpi_url"]; + // remove trailing slash to prevent conf from becoming broken + $birdnetpi_url = rtrim($birdnetpi_url, '/'); + saveSetting('BIRDNETPI_URL', $birdnetpi_url); + update_caddyfile(); + } + + if(isset($_GET["caddy_pwd"])) { + $caddy_pwd_value = ""; + $caddy_pwd = $_GET["caddy_pwd"]; + //If the BirdNET-Pi Password is empty, return a empty string instead of a string that was surrounded by double quotes + //else return the supplied password + //so in the ini file ['CADDY_PWD']= , instead of ['CADDY_PWD']="" (which still prompts for auth but password is required) + //this returns the password setting to it's default state when no password is set + $caddy_pwd_value = !empty($caddy_pwd) ? "\"$caddy_pwd\"" : ""; + + saveSetting('CADDY_PWD', "$caddy_pwd_value"); + //Make sure the caddy file is set directly + update_caddyfile(); + } } $count = getLabelsCount(); diff --git a/scripts/common.php b/scripts/common.php index 96c533d..805a2bb 100644 --- a/scripts/common.php +++ b/scripts/common.php @@ -614,9 +614,6 @@ function deleteDetection($filename) //Check authentication before going any further //the front end would have done this check already and such should pass authenticateUser(); - if (!userIsAuthenticated()) { - return []; - } $filename_exploded = explode("/", $filename); $actual_filename = $filename_exploded[2]; @@ -728,9 +725,6 @@ function frequencyShiftDetectionAudio($filename, $performShift = null) //Check authentication before going any further //the front end would have done this check already and such should pass authenticateUser(); - if (!userIsAuthenticated()) { - return []; - } $shifted_path = getDirectory('shifted_audio') . '/'; @@ -1176,9 +1170,6 @@ function saveSetting($setting_name, $setting_value, $post_save_command = null) //Check authentication before going any further //the front end would have done this check already and such should pass authenticateUser(); - if(!userIsAuthenticated()){ - return; - } //Setting exists already, see if the value changed if (array_key_exists($setting_name, $config)) { @@ -1285,9 +1276,6 @@ function executeSysCommand($command_type, $extra_data_to_pass = null) //Check authentication before going any further //the front end would have done this check already and such should pass authenticateUser(); - if(!userIsAuthenticated()){ - return ""; - } $command_type = strtolower($command_type); $result = null; @@ -1382,9 +1370,6 @@ function serviceMaintenance($command) //Check authentication before going any further //the front end would have done this check already and such should pass authenticateUser(); - if(!userIsAuthenticated()){ - return ""; - } ///e.g $command = 'service stop livestream.service', match the service name ////// BIRDNET LOG SERVICE ////// @@ -1575,15 +1560,14 @@ function syslog_shell_exec($cmd, $sudo_user = null) //Check authentication before going any further //the front end would have done this check already and such should pass authenticateUser(); - if(userIsAuthenticated()){ - if ($sudo_user) { - $cmd = "sudo -u $sudo_user $cmd"; - } - $output = shell_exec($cmd); - if (strlen($output) > 0) { - syslog(LOG_INFO, $output); - } + if ($sudo_user) { + $cmd = "sudo -u $sudo_user $cmd"; + } + $output = shell_exec($cmd); + + if (strlen($output) > 0) { + syslog(LOG_INFO, $output); } return $output; From a80d39d7b6f26001af9660e0524331ab1647ab60 Mon Sep 17 00:00:00 2001 From: jaredb7 Date: Sat, 13 May 2023 01:07:19 +1000 Subject: [PATCH 10/29] Replace all authentication code with a function that does the auth for us Makes things a little tidier and easier to change in the future, a custom message can be passed to authenticateUser() and this is output if the authentication fails. Added protection around Include and Exclude species pages as these still accessible & data can still be written to include and exclude species.txt even with a password. --- homepage/index.php | 22 ++--------- homepage/views.php | 77 +++++++++++--------------------------- scripts/advanced.php | 18 +-------- scripts/common.php | 19 ++++++---- scripts/config.php | 17 +-------- scripts/overview.php | 27 +++----------- scripts/play.php | 88 +++++++++++--------------------------------- 7 files changed, 66 insertions(+), 202 deletions(-) diff --git a/homepage/index.php b/homepage/index.php index bfbd6fa..e4e51b1 100644 --- a/homepage/index.php +++ b/homepage/index.php @@ -29,28 +29,14 @@ echo "

$site_name

"; - } else { - header('WWW-Authenticate: Basic realm="My Realm"'); - header('HTTP/1.0 401 Unauthorized'); - echo 'You cannot listen to the live audio stream'; - exit; - } - } + } } else { echo "
diff --git a/homepage/views.php b/homepage/views.php index 543f109..235676f 100644 --- a/homepage/views.php +++ b/homepage/views.php @@ -118,20 +118,11 @@ if(isset($_GET['view'])){ if($_GET['view'] == "Streamlit"){echo "";} if($_GET['view'] == "Daily Charts"){include('history.php');} if($_GET['view'] == "Tools"){ - parseConfig(); //Authenticate before proceeding - $caddypwd = $config['CADDY_PWD']; - if (!isset($_SERVER['PHP_AUTH_USER'])) { - header('WWW-Authenticate: Basic realm="My Realm"'); - header('HTTP/1.0 401 Unauthorized'); - echo '
You cannot edit the settings for this installation
'; - exit; - } else { - $submittedpwd = $_SERVER['PHP_AUTH_PW']; - $submitteduser = $_SERVER['PHP_AUTH_USER']; - if($submittedpwd == $caddypwd && $submitteduser == 'birdnet'){ - $url = $_SERVER['SERVER_NAME']."/scripts/adminer.php"; - echo "
+ $user_is_authenticated = authenticateUser(); + if ($user_is_authenticated) { + $url = $_SERVER['SERVER_NAME'] . "/scripts/adminer.php"; + echo "
@@ -144,19 +135,14 @@ if(isset($_GET['view'])){
"; - } else { - header('WWW-Authenticate: Basic realm="My Realm"'); - header('HTTP/1.0 401 Unauthorized'); - echo '
You cannot edit the settings for this installation
'; - exit; - } - } + } } 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"){ - if(isset($_GET['species']) && isset($_GET['add'])){ + $user_is_authenticated = authenticateUser('You cannot modify the system'); + if(isset($_GET['species']) && isset($_GET['add']) && $user_is_authenticated){ $file = getFilePath('include_species_list.txt'); $str = file_get_contents("$file"); $str = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $str); @@ -183,7 +169,8 @@ if(isset($_GET['view'])){ include('./scripts/include_list.php'); } if($_GET['view'] == "Excluded"){ - if(isset($_GET['species']) && isset($_GET['add'])){ + $user_is_authenticated = authenticateUser('You cannot modify the system'); + if(isset($_GET['species']) && isset($_GET['add']) && $user_is_authenticated){ $file = getFilePath('exclude_species_list.txt'); $str = file_get_contents("$file"); $str = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $str); @@ -211,40 +198,17 @@ if(isset($_GET['view'])){ echo ""; } if($_GET['view'] == "Webterm"){ - parseConfig(); - //Authenticate before proceeding - $caddypwd = $config['CADDY_PWD']; - if (!isset($_SERVER['PHP_AUTH_USER'])) { - header('WWW-Authenticate: Basic realm="My Realm"'); - header('HTTP/1.0 401 Unauthorized'); - echo '
You cannot access the web terminal
'; - exit; - } else { - $submittedpwd = $_SERVER['PHP_AUTH_PW']; - $submitteduser = $_SERVER['PHP_AUTH_USER']; - if($submittedpwd == $caddypwd && $submitteduser == 'birdnet'){ + //Authenticate before proceeding + $user_is_authenticated = authenticateUser("You cannot access the web terminal"); + if ($user_is_authenticated) { #ACCESS THE WEB TERMINAL echo ""; - } else { - header('WWW-Authenticate: Basic realm="My Realm"'); - header('HTTP/1.0 401 Unauthorized'); - echo '
You cannot access the web terminal
'; - exit; - } - } + } } } elseif(isset($_GET['submit'])) { - parseConfig(); //Authenticate before proceeding - $caddypwd = $config['CADDY_PWD']; - if (!isset($_SERVER['PHP_AUTH_USER'])) { - header('WWW-Authenticate: Basic realm="My Realm"'); - header('HTTP/1.0 401 Unauthorized'); - echo 'You cannot access the web terminal'; - exit; - } else { - $submittedpwd = $_SERVER['PHP_AUTH_PW']; - $submitteduser = $_SERVER['PHP_AUTH_USER']; + $user_is_authenticated = authenticateUser('You cannot modify the system'); + $allowedCommands = array('service stop livestream.service && service stop icecast2.service', 'service restart livestream.service && service restart icecast2.service', 'service disable livestream.service && service disable icecast2 && service stop icecast2.service', @@ -292,7 +256,7 @@ if(isset($_GET['view'])){ 'sudo shutdown now', 'sudo clear_all_data.sh'); $command = $_GET['submit']; - if($submittedpwd == $caddypwd && $submitteduser == 'birdnet' && in_array($command,$allowedCommands)){ + if($user_is_authenticated && in_array($command,$allowedCommands)){ if(isset($command)){ $initcommand = $command; $results = ""; @@ -383,15 +347,16 @@ if(isset($_GET['view'])){ } echo "
Output of command:`".$initcommand."`
$results
"; } else { - header('WWW-Authenticate: Basic realm="My Realm"'); + header('WWW-Authenticate: Basic realm="BirdNET-Pi"'); header('HTTP/1.0 401 Unauthorized'); - echo 'You cannot access the web terminal'; + echo 'You cannot modify the system'; exit; } } - } ob_end_flush(); -} else {include('overview.php');} +} else { + include('overview.php'); +} ?> "; } - updateAppriseConfig($apprise_input); + $fh = fopen("/etc/birdnet/birdnet.conf", "w"); + $fh2 = fopen("./scripts/thisrun.txt", "w"); + fwrite($fh, $contents); + fwrite($fh2, $contents2); - serviceMaintenance('restart core.services'); + if(isset($apprise_input)){ + $user = shell_exec("awk -F: '/1000/{print $1}' /etc/passwd"); + $home = shell_exec("awk -F: '/1000/{print $6}' /etc/passwd"); + $home = trim($home); + + $appriseconfig = fopen($home."/BirdNET-Pi/apprise.txt", "w"); + fwrite($appriseconfig, $apprise_input); + } + + syslog(LOG_INFO, "Restarting Services"); + shell_exec("sudo restart_services.sh"); } if(isset($_GET['sendtest']) && $_GET['sendtest'] == "true") { + $db = new SQLite3('./birds.db', SQLITE3_OPEN_CREATE | SQLITE3_OPEN_READWRITE); + + $user = shell_exec("awk -F: '/1000/{print $1}' /etc/passwd"); + $home = shell_exec("awk -F: '/1000/{print $6}' /etc/passwd"); + $home = trim($home); + + if (file_exists('./thisrun.txt')) { + $config = parse_ini_file('./thisrun.txt'); + } elseif (file_exists('./firstrun.ini')) { + $config = parse_ini_file('./firstrun.ini'); + } + $cf = explode("\n",$_GET['apprise_config']); $cf = "'".implode("' '", $cf)."'"; - $result0 = getMostRecentDetectionToday(); - foreach ($result0['data'] as $todaytable) + $statement0 = $db->prepare('SELECT * FROM detections WHERE Date == DATE(\'now\', \'localtime\') ORDER BY TIME DESC LIMIT 1'); + $result0 = $statement0->execute(); + while($todaytable=$result0->fetchArray(SQLITE3_ASSOC)) { $sciname = $todaytable['Sci_Name']; $comname = $todaytable['Com_Name']; @@ -204,7 +310,7 @@ if(isset($_GET['sendtest']) && $_GET['sendtest'] == "true") { $body = str_replace("\$overlap", $overlap, $body); $body = str_replace("\$flickrimage", $exampleimage, $body); - echo "
" . executeSysCommand('appraise_notification', ['title' => $title, 'body' => $body, 'attach' => $attach, 'cf' => $cf]) . "
"; + echo "
".shell_exec($home."/BirdNET-Pi/birdnet/bin/apprise -vv --plugin-path ".$home."/.apprise/plugins "." -t '".escapeshellcmd($title)."' -b '".escapeshellcmd($body)."' ".$attach." ".$cf." ")."
"; die(); } @@ -218,10 +324,11 @@ if(isset($_GET['sendtest']) && $_GET['sendtest'] == "true") {

Basic Settings


+ @@ -253,6 +360,7 @@ function sendTestNotification(e) { ' />
- > + >
- > + >
- > + >
- > + >

@@ -467,6 +575,39 @@ https://discordapp.com/api/webhooks/{WebhookID}/{WebhookToken} prepare("SELECT COUNT(*) FROM detections + WHERE Date == \"$theDate\""); +$result1 = $statement1->execute(); +$totalcount = $result1->fetchArray(SQLITE3_ASSOC); if(isset($_GET['blocation']) ) { @@ -27,6 +32,10 @@ if(isset($_GET['blocation']) ) { header("Expires: 0"); + $user = trim(shell_exec("awk -F: '/1000/{print $1}' /etc/passwd")); + $home = trim(shell_exec("awk -F: '/1000/{print $6}' /etc/passwd")); + + //$sunrise = date_sunrise(time(), SUNFUNCS_RET_TIMESTAMP, $config["LATITUDE"], $config["LONGITUDE"]); //$sunset = date_sunset(time(), SUNFUNCS_RET_TIMESTAMP, $config["LATITUDE"], $config["LONGITUDE"]); @@ -36,17 +45,16 @@ if(isset($_GET['blocation']) ) { $hrsinday = 24; for($i=0;$i<$hrsinday;$i++) { $starttime = strtotime("12 AM") + (3600*$i); - $endtime = date("H:i",$starttime + 3600); - $statement1 = getDetectionBreakdownByTime($theDate,$starttime,$endtime); - if($statement1['success'] == False){ - echo $statement1['message']; - header("refresh: 0;"); + $statement1 = $db->prepare("SELECT DISTINCT(Com_Name), COUNT(*) FROM detections WHERE Date == \"$theDate\" AND Time > '".date("H:i", $starttime)."' AND Time < '".date("H:i",$starttime + 3600)."' AND Confidence > 0.75 GROUP By Com_Name ORDER BY COUNT(*) DESC"); + if($statement1 == False){ + echo "Database is busy"; + header("refresh: 0;"); } - $result1 = $statement1['data']; + $result1 = $statement1->execute(); $detections = []; - foreach ($result1 as $detection) + while($detection=$result1->fetchArray(SQLITE3_ASSOC)) { $detections[$detection["Com_Name"]] = $detection["COUNT(*)"]; } diff --git a/scripts/include_list.php b/scripts/include_list.php index ac78990..ad663be 100644 --- a/scripts/include_list.php +++ b/scripts/include_list.php @@ -3,7 +3,10 @@ @@ -44,7 +47,7 @@ $eachlines = file($filename, FILE_IGNORE_NEW_LINES); [more info]
- + From 88ccda11f387ce8f9cb4a87635f8208d2c4c5342 Mon Sep 17 00:00:00 2001 From: ehpersonal38 <103586016+ehpersonal38@users.noreply.github.com> Date: Thu, 25 May 2023 15:54:54 -0400 Subject: [PATCH 24/29] fix the things --- homepage/views.php | 4 +++- scripts/config.php | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/homepage/views.php b/homepage/views.php index 1b2cb42..0578f5e 100644 --- a/homepage/views.php +++ b/homepage/views.php @@ -66,6 +66,7 @@ elseif ($config["LONGITUDE"] == "0.000") { echo "
WARNING: Your longitude is not set properly. Please do so now in Tools -> Settings.
"; } ?> + - Date: Sun, 28 May 2023 13:39:55 -0400 Subject: [PATCH 26/29] resolve #934 --- scripts/config.php | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/scripts/config.php b/scripts/config.php index 942f309..d16f6f4 100644 --- a/scripts/config.php +++ b/scripts/config.php @@ -2,14 +2,14 @@ error_reporting(E_ERROR); ini_set('display_errors',1); -if (file_exists('./scripts/thisrun.txt')) { - $config = parse_ini_file('./scripts/thisrun.txt'); -} elseif (file_exists('./scripts/firstrun.ini')) { - $config = parse_ini_file('./scripts/firstrun.ini'); -} $user = shell_exec("awk -F: '/1000/{print $1}' /etc/passwd"); $home = shell_exec("awk -F: '/1000/{print $6}' /etc/passwd"); $home = trim($home); +if (file_exists($home.'/BirdNET-Pi/scripts/thisrun.txt')) { + $config = parse_ini_file($home.'/BirdNET-Pi/scripts/thisrun.txt'); +} elseif (file_exists($home.'/BirdNET-Pi/scripts/thisrun.ini')) { + $config = parse_ini_file($home.'/BirdNET-Pi/scripts/thisrun.ini'); +} if (file_exists($home."/BirdNET-Pi/apprise.txt")) { $apprise_config = file_get_contents($home."/BirdNET-Pi/apprise.txt"); } else { @@ -136,11 +136,10 @@ if(isset($_GET["latitude"])){ } } - // Update Language settings only if a change is requested - if (file_exists('./scripts/thisrun.txt')) { - $lang_config = parse_ini_file('./scripts/thisrun.txt'); - } elseif (file_exists('./scripts/firstrun.ini')) { - $lang_config = parse_ini_file('./scripts/firstrun.ini'); + if (file_exists($home.'/BirdNET-Pi/scripts/thisrun.txt')) { + $lang_config = parse_ini_file($home.'/BirdNET-Pi/scripts/thisrun.txt'); + } elseif (file_exists($home.'/BirdNET-Pi/scripts/thisrun.ini')) { + $lang_config = parse_ini_file($home.'/BirdNET-Pi/scripts/thisrun.ini'); } if ($model != $lang_config['MODEL'] || $language != $lang_config['DATABASE_LANG']){ @@ -183,7 +182,7 @@ if(isset($_GET["latitude"])){ $contents = preg_replace("/APPRISE_ONLY_NOTIFY_SPECIES_NAMES=.*/", "APPRISE_ONLY_NOTIFY_SPECIES_NAMES=\"$only_notify_species_names\"", $contents); $contents = preg_replace("/APPRISE_ONLY_NOTIFY_SPECIES_NAMES_2=.*/", "APPRISE_ONLY_NOTIFY_SPECIES_NAMES_2=\"$only_notify_species_names_2\"", $contents); - $contents2 = file_get_contents("./scripts/thisrun.txt"); + $contents2 = file_get_contents($home."/BirdNET-Pi/scripts/thisrun.txt"); $contents2 = preg_replace("/SITE_NAME=.*/", "SITE_NAME=\"$site_name\"", $contents2); $contents2 = preg_replace("/LATITUDE=.*/", "LATITUDE=$latitude", $contents2); $contents2 = preg_replace("/LONGITUDE=.*/", "LONGITUDE=$longitude", $contents2); @@ -213,7 +212,7 @@ if(isset($_GET["latitude"])){ } $fh = fopen("/etc/birdnet/birdnet.conf", "w"); - $fh2 = fopen("./scripts/thisrun.txt", "w"); + $fh2 = fopen($home."/BirdNET-Pi/scripts/thisrun.txt", "w"); fwrite($fh, $contents); fwrite($fh2, $contents2); @@ -231,18 +230,17 @@ if(isset($_GET["latitude"])){ } if(isset($_GET['sendtest']) && $_GET['sendtest'] == "true") { - $db = new SQLite3('./birds.db', SQLITE3_OPEN_CREATE | SQLITE3_OPEN_READWRITE); - $user = shell_exec("awk -F: '/1000/{print $1}' /etc/passwd"); $home = shell_exec("awk -F: '/1000/{print $6}' /etc/passwd"); $home = trim($home); - - if (file_exists('./thisrun.txt')) { - $config = parse_ini_file('./thisrun.txt'); - } elseif (file_exists('./firstrun.ini')) { - $config = parse_ini_file('./firstrun.ini'); + if (file_exists($home.'/BirdNET-Pi/scripts/thisrun.txt')) { + $config = parse_ini_file($home.'/BirdNET-Pi/scripts/thisrun.txt'); + } elseif (file_exists($home.'/BirdNET-Pi/scripts/thisrun.ini')) { + $config = parse_ini_file($home.'/BirdNET-Pi/scripts/thisrun.ini'); } + $db = new SQLite3($home."/BirdNET-Pi/scripts/birds.db", SQLITE3_OPEN_CREATE | SQLITE3_OPEN_READWRITE); + $cf = explode("\n",$_GET['apprise_config']); $cf = "'".implode("' '", $cf)."'"; @@ -497,7 +495,7 @@ function runProcess() {

BirdWeather


-

BirdWeather.com is a weather map for bird sounds. Stations around the world supply audio and video streams to BirdWeather where they are then analyzed by BirdNET and compared to eBird Grid data. BirdWeather catalogues the bird audio and spectrogram visualizations so that you can listen to, view, and read about birds throughout the world. Email Tim to request a BirdWeather ID

+

BirdWeather.com is a weather map for bird sounds. Stations around the world supply audio and video streams to BirdWeather where they are then analyzed by BirdNET and compared to eBird Grid data. BirdWeather catalogues the bird audio and spectrogram visualizations so that you can listen to, view, and read about birds throughout the world. Email Tim to request a BirdWeather ID


Notifications

From f050a60cc15978b686603622d5e2dca8ef95ba40 Mon Sep 17 00:00:00 2001 From: ehpersonal38 <103586016+ehpersonal38@users.noreply.github.com> Date: Sun, 28 May 2023 13:40:16 -0400 Subject: [PATCH 27/29] Revert "fix the things" This reverts commit 88ccda11f387ce8f9cb4a87635f8208d2c4c5342. --- homepage/views.php | 4 +--- scripts/config.php | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/homepage/views.php b/homepage/views.php index 735fbad..6784ab1 100644 --- a/homepage/views.php +++ b/homepage/views.php @@ -67,7 +67,6 @@ elseif ($config["LONGITUDE"] == "0.000") { echo "
WARNING: Your longitude is not set properly. Please do so now in Tools -> Settings.
"; } ?> -