Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added version check in GUI #268

Merged
merged 2 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 36 additions & 27 deletions gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -20,7 +19,6 @@
import species
import utils
from train import trainModel
import webbrowser

_WINDOW: webview.Window
OUTPUT_TYPE_MAP = {
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand All @@ -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


Expand All @@ -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):
Expand Down Expand Up @@ -585,15 +584,17 @@ 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
cfg.AUTOTUNE_EXECUTIONS_PER_TRIAL = int(autotune_executions_per_trials)

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:
Expand All @@ -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.")
Expand Down Expand Up @@ -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"""
<div style='display: flex; align-items: center;'>
<img src='data:image/png;base64,{}' style='width: 50px; height: 50px; margin-right: 10px;'>
<img src='data:image/png;base64,{utils.img2base64("gui/img/birdnet_logo.png")}' style='width: 50px; height: 50px; margin-right: 10px;'>
<h2>BirdNET Analyzer</h2>
</div>
""".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"""
<div style='display: flex; justify-content: space-around; align-items: center; padding: 10px; text-align: center'>
<div>GUI version: {}<br>Model version: {}</div>
<div>
<div style="display: flex;flex-direction: row;">GUI version: <span id="current-version">{cfg.GUI_VERSION}</span><span style="display: none" id="update-available"><a>+</a></span></div>
<div>Model version: {cfg.MODEL_VERSION}</div>
</div>
<div>K. Lisa Yang Center for Conservation Bioacoustics<br>Chemnitz University of Technology</div>
<div>For docs and support visit:<br><a href='https://birdnet.cornell.edu/analyzer' target='_blank'>birdnet.cornell.edu/analyzer</a></div>
</div>
""".format(
cfg.GUI_VERSION, cfg.MODEL_VERSION
)
)

"""
)

def build_single_analysis_tab():
with gr.Tab("Single file"):
Expand Down Expand Up @@ -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."
Expand Down Expand Up @@ -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():

Expand Down Expand Up @@ -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:
Expand Down
25 changes: 25 additions & 0 deletions gui/gui.css
Original file line number Diff line number Diff line change
@@ -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;
}
38 changes: 38 additions & 0 deletions gui/gui.js
Original file line number Diff line number Diff line change
@@ -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);
});
}