diff --git a/gui.py b/gui.py
index 569c8ac0..9e4e0732 100644
--- a/gui.py
+++ b/gui.py
@@ -7,7 +7,6 @@
sys.stderr = sys.stdout = open("logs.txt", "w")
import multiprocessing
-from multiprocessing import freeze_support
from pathlib import Path
import gradio as gr
@@ -20,7 +19,6 @@
import species
import utils
from train import trainModel
-import webbrowser
_WINDOW: webview.Window
OUTPUT_TYPE_MAP = {
@@ -364,7 +362,7 @@ def runAnalysis(
# Combine results?
if not cfg.OUTPUT_FILE is None:
- print("Combining results into {}...".format(cfg.OUTPUT_FILE), end='', flush=True)
+ print(f"Combining results into {cfg.OUTPUT_FILE}...", end="", flush=True)
analyze.combineResults(cfg.OUTPUT_PATH, cfg.OUTPUT_FILE)
print("done!", flush=True)
@@ -434,8 +432,8 @@ def select_subdirectories():
labels = []
for folder in subdirs:
- labels_in_folder = folder.split(',')
-
+ labels_in_folder = folder.split(",")
+
for label in labels_in_folder:
if not label in labels:
labels.append(label)
@@ -455,6 +453,7 @@ def select_file(filetypes=()):
The selected file or None of the dialog was canceled.
"""
files = _WINDOW.create_file_dialog(webview.OPEN_DIALOG, file_types=filetypes)
+
return files[0] if files else None
@@ -472,7 +471,7 @@ def format_seconds(secs: float):
hours, secs = divmod(secs, 3600)
minutes, secs = divmod(secs, 60)
- return "{:2.0f}:{:02.0f}:{:06.3f}".format(hours, minutes, secs)
+ return f"{hours:2.0f}:{minutes:02.0f}:{secs:06.3f}"
def select_directory(collect_files=True):
@@ -585,7 +584,7 @@ def start_training(
cfg.TRAIN_CACHE_MODE = cache_mode
cfg.TRAIN_CACHE_FILE = os.path.join(cache_file, cache_file_name) if cache_mode == "save" else cache_file
cfg.TFLITE_THREADS = 1
- cfg.CPU_THREADS = max(1, multiprocessing.cpu_count() - 1) # let's use everything we have (well, almost)
+ cfg.CPU_THREADS = max(1, multiprocessing.cpu_count() - 1) # let's use everything we have (well, almost)
cfg.AUTOTUNE = autotune
cfg.AUTOTUNE_TRIALS = autotune_trials
@@ -593,7 +592,9 @@ def start_training(
def dataLoadProgression(num_files, num_total_files, label):
if progress is not None:
- progress((num_files, num_total_files), total=num_total_files, unit="files", desc=f"Loading data for '{label}'")
+ progress(
+ (num_files, num_total_files), total=num_total_files, unit="files", desc=f"Loading data for '{label}'"
+ )
def epochProgression(epoch, logs=None):
if progress is not None:
@@ -606,7 +607,9 @@ def trialProgression(trial):
if progress is not None:
progress((trial, autotune_trials), total=autotune_trials, unit="trials", desc=f"Autotune in progress")
- history = trainModel(on_epoch_end=epochProgression, on_trial_result=trialProgression, on_data_load_end=dataLoadProgression)
+ history = trainModel(
+ on_epoch_end=epochProgression, on_trial_result=trialProgression, on_data_load_end=dataLoadProgression
+ )
if len(history.epoch) < epochs:
gr.Info("Stopped early - validation metric not improving.")
@@ -845,37 +848,36 @@ def on_custom_classifier_selection_click():
if __name__ == "__main__":
- freeze_support()
+ multiprocessing.freeze_support()
def build_header():
# Custom HTML header with gr.Markdown
+ # There has to be another way, but this works for now; paths are weird in gradio
with gr.Row():
gr.Markdown(
- """
+ f"""
-
+
BirdNET Analyzer
- """.format(
- utils.img2base64("gui/img/birdnet_logo.png") # There has to be another way, but this works for now; paths are weird in gradio
- )
+ """
)
def build_footer():
with gr.Row():
gr.Markdown(
- """
+ f"""
-
GUI version: {}
Model version: {}
+
+
GUI version:
{cfg.GUI_VERSION}+
+
Model version: {cfg.MODEL_VERSION}
+
K. Lisa Yang Center for Conservation Bioacoustics
Chemnitz University of Technology
- """.format(
- cfg.GUI_VERSION, cfg.MODEL_VERSION
- )
- )
-
+ """
+ )
def build_single_analysis_tab():
with gr.Tab("Single file"):
@@ -1007,16 +1009,22 @@ def on_output_type_change(value, check):
return gr.Checkbox(visible=value == "Raven selection table"), gr.Textbox(visible=check)
output_type_radio.change(
- on_output_type_change, inputs=[output_type_radio, combine_tables_checkbox], outputs=[combine_tables_checkbox, output_filename], show_progress=False
+ on_output_type_change,
+ inputs=[output_type_radio, combine_tables_checkbox],
+ outputs=[combine_tables_checkbox, output_filename],
+ show_progress=False,
)
def on_combine_tables_change(value):
return gr.Textbox(visible=value)
combine_tables_checkbox.change(
- on_combine_tables_change, inputs=combine_tables_checkbox, outputs=output_filename, show_progress=False
+ on_combine_tables_change,
+ inputs=combine_tables_checkbox,
+ outputs=output_filename,
+ show_progress=False,
)
-
+
with gr.Row():
batch_size_number = gr.Number(
precision=1, label="Batch size", value=1, info="Number of samples to process at the same time."
@@ -1155,7 +1163,7 @@ def on_autotune_change(value):
autotune_cb.change(
on_autotune_change, inputs=autotune_cb, outputs=[custom_params, autotune_params], show_progress=False
- )
+ )
with gr.Row():
@@ -1404,7 +1412,8 @@ def select_directory_and_update_tb():
)
with gr.Blocks(
- css=r".d-block .wrap {display: block !important;} .mh-200 {max-height: 300px; overflow-y: auto !important;} footer {display: none !important;} #single_file_audio, #single_file_audio * {max-height: 81.6px; min-height: 0;}",
+ css="gui/gui.css",
+ js="gui/gui.js",
theme=gr.themes.Default(),
analytics_enabled=False,
) as demo:
diff --git a/gui/gui.css b/gui/gui.css
new file mode 100644
index 00000000..75551657
--- /dev/null
+++ b/gui/gui.css
@@ -0,0 +1,25 @@
+.d-block .wrap {
+ display: block !important;
+}
+
+.mh-200 {
+ max-height: 300px;
+ overflow-y: auto !important;
+}
+
+footer {
+ display: none !important;
+}
+
+#single_file_audio,
+#single_file_audio * {
+ max-height: 81.6px;
+ min-height: 0;
+}
+
+#update-notification {
+ background: green;
+ border-radius: 50%;
+ width: 1rem;
+ height: 1rem;
+}
\ No newline at end of file
diff --git a/gui/gui.js b/gui/gui.js
new file mode 100644
index 00000000..e952d831
--- /dev/null
+++ b/gui/gui.js
@@ -0,0 +1,38 @@
+function checkForNewerVersion() {
+ function sendGetRequest(url) {
+ return new Promise((resolve, reject) => {
+ const xhr = new XMLHttpRequest();
+ xhr.open("GET", url);
+ xhr.onload = () => {
+ if (xhr.status === 200) {
+ resolve(xhr.responseText);
+ } else {
+ reject(new Error(`Request failed with status ${xhr.status}`));
+ }
+ };
+ xhr.onerror = () => {
+ reject(new Error("Request failed"));
+ };
+ xhr.send();
+ });
+ }
+
+ const apiUrl = "https://api.github.com/repos/kahst/BirdNET-Analyzer/releases/latest";
+
+ sendGetRequest(apiUrl)
+ .then(response => {
+ const current_version = "v" + document.getElementById("current-version").textContent;
+ const response_object = JSON.parse(response);
+ const latest_version = response_object.tag_name;
+
+ if (current_version !== latest_version) {
+ const updateNotification = document.getElementById("update-available");
+
+ updateNotification.style.display = "block";
+ updateNotification.getElementsByTagName("a")[0].href = response_object.html_url;
+ }
+ })
+ .catch(error => {
+ console.error(error);
+ });
+}
\ No newline at end of file