birdnet-analyzer 2.0.0__py3-none-any.whl → 2.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. birdnet_analyzer/__init__.py +9 -8
  2. birdnet_analyzer/analyze/__init__.py +5 -5
  3. birdnet_analyzer/analyze/__main__.py +3 -4
  4. birdnet_analyzer/analyze/cli.py +25 -25
  5. birdnet_analyzer/analyze/core.py +241 -245
  6. birdnet_analyzer/analyze/utils.py +692 -701
  7. birdnet_analyzer/audio.py +368 -372
  8. birdnet_analyzer/cli.py +709 -707
  9. birdnet_analyzer/config.py +242 -242
  10. birdnet_analyzer/eBird_taxonomy_codes_2021E.json +25279 -25279
  11. birdnet_analyzer/embeddings/__init__.py +3 -4
  12. birdnet_analyzer/embeddings/__main__.py +3 -3
  13. birdnet_analyzer/embeddings/cli.py +12 -13
  14. birdnet_analyzer/embeddings/core.py +69 -70
  15. birdnet_analyzer/embeddings/utils.py +179 -193
  16. birdnet_analyzer/evaluation/__init__.py +196 -195
  17. birdnet_analyzer/evaluation/__main__.py +3 -3
  18. birdnet_analyzer/evaluation/assessment/__init__.py +0 -0
  19. birdnet_analyzer/evaluation/assessment/metrics.py +388 -0
  20. birdnet_analyzer/evaluation/assessment/performance_assessor.py +409 -0
  21. birdnet_analyzer/evaluation/assessment/plotting.py +379 -0
  22. birdnet_analyzer/evaluation/preprocessing/__init__.py +0 -0
  23. birdnet_analyzer/evaluation/preprocessing/data_processor.py +631 -0
  24. birdnet_analyzer/evaluation/preprocessing/utils.py +98 -0
  25. birdnet_analyzer/gui/__init__.py +19 -23
  26. birdnet_analyzer/gui/__main__.py +3 -3
  27. birdnet_analyzer/gui/analysis.py +175 -174
  28. birdnet_analyzer/gui/assets/arrow_down.svg +4 -4
  29. birdnet_analyzer/gui/assets/arrow_left.svg +4 -4
  30. birdnet_analyzer/gui/assets/arrow_right.svg +4 -4
  31. birdnet_analyzer/gui/assets/arrow_up.svg +4 -4
  32. birdnet_analyzer/gui/assets/gui.css +28 -28
  33. birdnet_analyzer/gui/assets/gui.js +93 -93
  34. birdnet_analyzer/gui/embeddings.py +619 -620
  35. birdnet_analyzer/gui/evaluation.py +795 -813
  36. birdnet_analyzer/gui/localization.py +75 -68
  37. birdnet_analyzer/gui/multi_file.py +245 -246
  38. birdnet_analyzer/gui/review.py +519 -527
  39. birdnet_analyzer/gui/segments.py +191 -191
  40. birdnet_analyzer/gui/settings.py +128 -129
  41. birdnet_analyzer/gui/single_file.py +267 -269
  42. birdnet_analyzer/gui/species.py +95 -95
  43. birdnet_analyzer/gui/train.py +696 -698
  44. birdnet_analyzer/gui/utils.py +810 -808
  45. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_af.txt +6522 -6522
  46. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ar.txt +6522 -6522
  47. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_bg.txt +6522 -6522
  48. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ca.txt +6522 -6522
  49. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_cs.txt +6522 -6522
  50. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_da.txt +6522 -6522
  51. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_de.txt +6522 -6522
  52. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_el.txt +6522 -6522
  53. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_en_uk.txt +6522 -6522
  54. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_es.txt +6522 -6522
  55. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_fi.txt +6522 -6522
  56. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_fr.txt +6522 -6522
  57. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_he.txt +6522 -6522
  58. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_hr.txt +6522 -6522
  59. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_hu.txt +6522 -6522
  60. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_in.txt +6522 -6522
  61. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_is.txt +6522 -6522
  62. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_it.txt +6522 -6522
  63. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ja.txt +6522 -6522
  64. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ko.txt +6522 -6522
  65. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_lt.txt +6522 -6522
  66. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ml.txt +6522 -6522
  67. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_nl.txt +6522 -6522
  68. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_no.txt +6522 -6522
  69. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_pl.txt +6522 -6522
  70. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_pt_BR.txt +6522 -6522
  71. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_pt_PT.txt +6522 -6522
  72. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ro.txt +6522 -6522
  73. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ru.txt +6522 -6522
  74. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_sk.txt +6522 -6522
  75. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_sl.txt +6522 -6522
  76. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_sr.txt +6522 -6522
  77. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_sv.txt +6522 -6522
  78. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_th.txt +6522 -6522
  79. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_tr.txt +6522 -6522
  80. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_uk.txt +6522 -6522
  81. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_zh.txt +6522 -6522
  82. birdnet_analyzer/lang/de.json +334 -334
  83. birdnet_analyzer/lang/en.json +334 -334
  84. birdnet_analyzer/lang/fi.json +334 -334
  85. birdnet_analyzer/lang/fr.json +334 -334
  86. birdnet_analyzer/lang/id.json +334 -334
  87. birdnet_analyzer/lang/pt-br.json +334 -334
  88. birdnet_analyzer/lang/ru.json +334 -334
  89. birdnet_analyzer/lang/se.json +334 -334
  90. birdnet_analyzer/lang/tlh.json +334 -334
  91. birdnet_analyzer/lang/zh_TW.json +334 -334
  92. birdnet_analyzer/model.py +1212 -1243
  93. birdnet_analyzer/playground.py +5 -0
  94. birdnet_analyzer/search/__init__.py +3 -3
  95. birdnet_analyzer/search/__main__.py +3 -3
  96. birdnet_analyzer/search/cli.py +11 -12
  97. birdnet_analyzer/search/core.py +78 -78
  98. birdnet_analyzer/search/utils.py +107 -111
  99. birdnet_analyzer/segments/__init__.py +3 -3
  100. birdnet_analyzer/segments/__main__.py +3 -3
  101. birdnet_analyzer/segments/cli.py +13 -14
  102. birdnet_analyzer/segments/core.py +81 -78
  103. birdnet_analyzer/segments/utils.py +383 -394
  104. birdnet_analyzer/species/__init__.py +3 -3
  105. birdnet_analyzer/species/__main__.py +3 -3
  106. birdnet_analyzer/species/cli.py +13 -14
  107. birdnet_analyzer/species/core.py +35 -35
  108. birdnet_analyzer/species/utils.py +74 -75
  109. birdnet_analyzer/train/__init__.py +3 -3
  110. birdnet_analyzer/train/__main__.py +3 -3
  111. birdnet_analyzer/train/cli.py +13 -14
  112. birdnet_analyzer/train/core.py +113 -113
  113. birdnet_analyzer/train/utils.py +877 -847
  114. birdnet_analyzer/translate.py +133 -104
  115. birdnet_analyzer/utils.py +426 -419
  116. {birdnet_analyzer-2.0.0.dist-info → birdnet_analyzer-2.0.1.dist-info}/METADATA +137 -129
  117. birdnet_analyzer-2.0.1.dist-info/RECORD +125 -0
  118. {birdnet_analyzer-2.0.0.dist-info → birdnet_analyzer-2.0.1.dist-info}/WHEEL +1 -1
  119. {birdnet_analyzer-2.0.0.dist-info → birdnet_analyzer-2.0.1.dist-info}/licenses/LICENSE +18 -18
  120. birdnet_analyzer-2.0.0.dist-info/RECORD +0 -117
  121. {birdnet_analyzer-2.0.0.dist-info → birdnet_analyzer-2.0.1.dist-info}/entry_points.txt +0 -0
  122. {birdnet_analyzer-2.0.0.dist-info → birdnet_analyzer-2.0.1.dist-info}/top_level.txt +0 -0
@@ -1,269 +1,267 @@
1
- import os
2
-
3
- import gradio as gr
4
-
5
- import birdnet_analyzer.audio as audio
6
- import birdnet_analyzer.config as cfg
7
- import birdnet_analyzer.gui.localization as loc
8
- import birdnet_analyzer.gui.utils as gu
9
- import birdnet_analyzer.utils as utils
10
-
11
-
12
- @gu.gui_runtime_error_handler
13
- def run_single_file_analysis(
14
- input_path,
15
- use_top_n,
16
- top_n,
17
- confidence,
18
- sensitivity,
19
- overlap,
20
- merge_consecutive,
21
- audio_speed,
22
- fmin,
23
- fmax,
24
- species_list_choice,
25
- species_list_file,
26
- lat,
27
- lon,
28
- week,
29
- use_yearlong,
30
- sf_thresh,
31
- custom_classifier_file,
32
- locale,
33
- ):
34
- import csv
35
- from datetime import timedelta
36
-
37
- from birdnet_analyzer.gui.analysis import run_analysis
38
-
39
- if species_list_choice == gu._CUSTOM_SPECIES:
40
- gu.validate(species_list_file, loc.localize("validation-no-species-list-selected"))
41
-
42
- gu.validate(input_path, loc.localize("validation-no-file-selected"))
43
-
44
- if fmin is None or fmax is None or fmin < cfg.SIG_FMIN or fmax > cfg.SIG_FMAX or fmin > fmax:
45
- raise gr.Error(f"{loc.localize('validation-no-valid-frequency')} [{cfg.SIG_FMIN}, {cfg.SIG_FMAX}]")
46
-
47
- result_filepath = run_analysis(
48
- input_path,
49
- None,
50
- use_top_n,
51
- top_n,
52
- confidence,
53
- sensitivity,
54
- overlap,
55
- merge_consecutive,
56
- audio_speed,
57
- fmin,
58
- fmax,
59
- species_list_choice,
60
- species_list_file,
61
- lat,
62
- lon,
63
- week,
64
- use_yearlong,
65
- sf_thresh,
66
- custom_classifier_file,
67
- "csv",
68
- None,
69
- "en" if not locale else locale,
70
- 1,
71
- 4,
72
- None,
73
- skip_existing=False,
74
- save_params=False,
75
- progress=None,
76
- )
77
-
78
- if not result_filepath:
79
- raise gr.Error(loc.localize("single-tab-analyze-file-error"))
80
-
81
- # read the result file to return the data to be displayed.
82
- with open(result_filepath, "r", encoding="utf-8") as f:
83
- reader = csv.reader(f)
84
- data = list(reader)
85
- data = [lc[0:-1] for lc in data[1:]] # remove last column (file path) and first row (header)
86
-
87
- for row in data:
88
- for col_idx in range(2):
89
- seconds = float(row[col_idx])
90
- time_str = str(timedelta(seconds=seconds))
91
-
92
- if "." in time_str:
93
- time_str = time_str[: time_str.index(".") + 2]
94
-
95
- row[col_idx] = time_str
96
- row.insert(0, "▶")
97
-
98
- return data, gr.update(visible=True), result_filepath
99
-
100
-
101
- def build_single_analysis_tab():
102
- with gr.Tab(loc.localize("single-tab-title")):
103
- audio_input = gr.Audio(type="filepath", label=loc.localize("single-audio-label"), sources=["upload"])
104
-
105
- with gr.Group():
106
- spectogram_output = gr.Plot(
107
- label=loc.localize("review-tab-spectrogram-plot-label"), visible=False, show_label=False
108
- )
109
- generate_spectrogram_cb = gr.Checkbox(
110
- value=True,
111
- label=loc.localize("single-tab-spectrogram-checkbox-label"),
112
- info=loc.localize("single-tab-spectrogram-checkbox-info"),
113
- )
114
- audio_path_state = gr.State()
115
- table_path_state = gr.State()
116
-
117
- (
118
- use_top_n,
119
- top_n_input,
120
- confidence_slider,
121
- sensitivity_slider,
122
- overlap_slider,
123
- merge_consecutive_slider,
124
- audio_speed_slider,
125
- fmin_number,
126
- fmax_number,
127
- ) = gu.sample_sliders(False)
128
-
129
- (
130
- species_list_radio,
131
- species_file_input,
132
- lat_number,
133
- lon_number,
134
- week_number,
135
- sf_thresh_number,
136
- yearlong_checkbox,
137
- selected_classifier_state,
138
- map_plot,
139
- ) = gu.species_lists(False)
140
- locale_radio = gu.locale()
141
-
142
- single_file_analyze = gr.Button(
143
- loc.localize("analyze-start-button-label"), variant="huggingface", interactive=False
144
- )
145
-
146
- with gr.Row(visible=False) as action_row:
147
- table_download_button = gr.Button(
148
- loc.localize("single-tab-download-button-label"),
149
- )
150
- segment_audio = gr.Audio(
151
- autoplay=True, type="numpy", show_download_button=True, show_label=False, editable=False, visible=False
152
- )
153
-
154
- output_dataframe = gr.Dataframe(
155
- type="pandas",
156
- headers=[
157
- "",
158
- loc.localize("single-tab-output-header-start"),
159
- loc.localize("single-tab-output-header-end"),
160
- loc.localize("single-tab-output-header-sci-name"),
161
- loc.localize("single-tab-output-header-common-name"),
162
- loc.localize("single-tab-output-header-confidence"),
163
- ],
164
- elem_id="single-file-output",
165
- interactive=False,
166
- )
167
-
168
- def get_audio_path(i, generate_spectrogram):
169
- if i:
170
- try:
171
- return (
172
- i["path"],
173
- gr.Audio(label=os.path.basename(i["path"])),
174
- gr.Plot(visible=True, value=utils.spectrogram_from_file(i["path"], fig_size=(20, 4)))
175
- if generate_spectrogram
176
- else gr.Plot(visible=False),
177
- gr.Button(interactive=True),
178
- )
179
- except:
180
- raise gr.Error(loc.localize("single-tab-generate-spectrogram-error"))
181
- else:
182
- return None, None, gr.Plot(visible=False), gr.update(interactive=False)
183
-
184
- def try_generate_spectrogram(audio_path, generate_spectrogram):
185
- if audio_path and generate_spectrogram:
186
- try:
187
- return gr.Plot(
188
- visible=True, value=utils.spectrogram_from_file(audio_path["path"], fig_size=(20, 4))
189
- )
190
- except:
191
- raise gr.Error(loc.localize("single-tab-generate-spectrogram-error"))
192
- else:
193
- return gr.Plot()
194
-
195
- generate_spectrogram_cb.change(
196
- try_generate_spectrogram,
197
- inputs=[audio_input, generate_spectrogram_cb],
198
- outputs=spectogram_output,
199
- preprocess=False,
200
- )
201
-
202
- audio_input.change(
203
- get_audio_path,
204
- inputs=[audio_input, generate_spectrogram_cb],
205
- outputs=[audio_path_state, audio_input, spectogram_output, single_file_analyze],
206
- preprocess=False,
207
- )
208
-
209
- inputs = [
210
- audio_path_state,
211
- use_top_n,
212
- top_n_input,
213
- confidence_slider,
214
- sensitivity_slider,
215
- overlap_slider,
216
- merge_consecutive_slider,
217
- audio_speed_slider,
218
- fmin_number,
219
- fmax_number,
220
- species_list_radio,
221
- species_file_input,
222
- lat_number,
223
- lon_number,
224
- week_number,
225
- yearlong_checkbox,
226
- sf_thresh_number,
227
- selected_classifier_state,
228
- locale_radio,
229
- ]
230
-
231
- def time_to_seconds(time_str):
232
- try:
233
- hours, minutes, seconds = time_str.split(":")
234
- total_seconds = int(hours) * 3600 + int(minutes) * 60 + float(seconds)
235
- return total_seconds
236
-
237
- except ValueError as e:
238
- raise ValueError("Input must be in the format hh:mm:ss or hh:mm:ss.ssssss with numeric values.") from e
239
-
240
- def get_selected_audio(evt: gr.SelectData, audio_path):
241
- if evt.index[1] == 0 and evt.row_value[1] and evt.row_value[2]:
242
- start = time_to_seconds(evt.row_value[1])
243
- end = time_to_seconds(evt.row_value[2])
244
-
245
- data, sr = audio.open_audio_file(audio_path, offset=start, duration=end - start)
246
- return gr.update(visible=True, value=(sr, data))
247
-
248
- return gr.update()
249
-
250
- def download_table(filepath):
251
- if filepath:
252
- ext = os.path.splitext(filepath)[1]
253
- gu.save_file_dialog(
254
- state_key="single-file-table",
255
- default_filename=os.path.basename(filepath),
256
- filetypes=(f"{ext[1:]} (*{ext})",),
257
- )
258
-
259
- output_dataframe.select(get_selected_audio, inputs=audio_path_state, outputs=segment_audio)
260
- single_file_analyze.click(
261
- run_single_file_analysis, inputs=inputs, outputs=[output_dataframe, action_row, table_path_state]
262
- )
263
- table_download_button.click(download_table, inputs=table_path_state)
264
-
265
- return lat_number, lon_number, map_plot
266
-
267
-
268
- if __name__ == "__main__":
269
- gu.open_window(build_single_analysis_tab)
1
+ import os
2
+
3
+ import gradio as gr
4
+
5
+ import birdnet_analyzer.config as cfg
6
+ import birdnet_analyzer.gui.localization as loc
7
+ import birdnet_analyzer.gui.utils as gu
8
+ from birdnet_analyzer import audio, utils
9
+
10
+
11
+ @gu.gui_runtime_error_handler
12
+ def run_single_file_analysis(
13
+ input_path,
14
+ use_top_n,
15
+ top_n,
16
+ confidence,
17
+ sensitivity,
18
+ overlap,
19
+ merge_consecutive,
20
+ audio_speed,
21
+ fmin,
22
+ fmax,
23
+ species_list_choice,
24
+ species_list_file,
25
+ lat,
26
+ lon,
27
+ week,
28
+ use_yearlong,
29
+ sf_thresh,
30
+ custom_classifier_file,
31
+ locale,
32
+ ):
33
+ import csv
34
+ from datetime import timedelta
35
+
36
+ from birdnet_analyzer.gui.analysis import run_analysis
37
+
38
+ if species_list_choice == gu._CUSTOM_SPECIES:
39
+ gu.validate(species_list_file, loc.localize("validation-no-species-list-selected"))
40
+
41
+ gu.validate(input_path, loc.localize("validation-no-file-selected"))
42
+
43
+ if fmin is None or fmax is None or fmin < cfg.SIG_FMIN or fmax > cfg.SIG_FMAX or fmin > fmax:
44
+ raise gr.Error(f"{loc.localize('validation-no-valid-frequency')} [{cfg.SIG_FMIN}, {cfg.SIG_FMAX}]")
45
+
46
+ result_filepath = run_analysis(
47
+ input_path,
48
+ None,
49
+ use_top_n,
50
+ top_n,
51
+ confidence,
52
+ sensitivity,
53
+ overlap,
54
+ merge_consecutive,
55
+ audio_speed,
56
+ fmin,
57
+ fmax,
58
+ species_list_choice,
59
+ species_list_file,
60
+ lat,
61
+ lon,
62
+ week,
63
+ use_yearlong,
64
+ sf_thresh,
65
+ custom_classifier_file,
66
+ "csv",
67
+ None,
68
+ locale if locale else "en",
69
+ 1,
70
+ 4,
71
+ None,
72
+ skip_existing=False,
73
+ save_params=False,
74
+ progress=None,
75
+ )
76
+
77
+ if not result_filepath:
78
+ raise gr.Error(loc.localize("single-tab-analyze-file-error"))
79
+
80
+ # read the result file to return the data to be displayed.
81
+ with open(result_filepath, encoding="utf-8") as f:
82
+ reader = csv.reader(f)
83
+ data = list(reader)
84
+ data = [lc[0:-1] for lc in data[1:]] # remove last column (file path) and first row (header)
85
+
86
+ for row in data:
87
+ for col_idx in range(2):
88
+ seconds = float(row[col_idx])
89
+ time_str = str(timedelta(seconds=seconds))
90
+
91
+ if "." in time_str:
92
+ time_str = time_str[: time_str.index(".") + 2]
93
+
94
+ row[col_idx] = time_str
95
+ row.insert(0, "▶")
96
+
97
+ return data, gr.update(visible=True), result_filepath
98
+
99
+
100
+ def build_single_analysis_tab():
101
+ with gr.Tab(loc.localize("single-tab-title")):
102
+ audio_input = gr.Audio(type="filepath", label=loc.localize("single-audio-label"), sources=["upload"])
103
+
104
+ with gr.Group():
105
+ spectogram_output = gr.Plot(
106
+ label=loc.localize("review-tab-spectrogram-plot-label"), visible=False, show_label=False
107
+ )
108
+ generate_spectrogram_cb = gr.Checkbox(
109
+ value=True,
110
+ label=loc.localize("single-tab-spectrogram-checkbox-label"),
111
+ info=loc.localize("single-tab-spectrogram-checkbox-info"),
112
+ )
113
+ audio_path_state = gr.State()
114
+ table_path_state = gr.State()
115
+
116
+ (
117
+ use_top_n,
118
+ top_n_input,
119
+ confidence_slider,
120
+ sensitivity_slider,
121
+ overlap_slider,
122
+ merge_consecutive_slider,
123
+ audio_speed_slider,
124
+ fmin_number,
125
+ fmax_number,
126
+ ) = gu.sample_sliders(False)
127
+
128
+ (
129
+ species_list_radio,
130
+ species_file_input,
131
+ lat_number,
132
+ lon_number,
133
+ week_number,
134
+ sf_thresh_number,
135
+ yearlong_checkbox,
136
+ selected_classifier_state,
137
+ map_plot,
138
+ ) = gu.species_lists(False)
139
+ locale_radio = gu.locale()
140
+
141
+ single_file_analyze = gr.Button(
142
+ loc.localize("analyze-start-button-label"), variant="huggingface", interactive=False
143
+ )
144
+
145
+ with gr.Row(visible=False) as action_row:
146
+ table_download_button = gr.Button(
147
+ loc.localize("single-tab-download-button-label"),
148
+ )
149
+ segment_audio = gr.Audio(
150
+ autoplay=True, type="numpy", show_download_button=True, show_label=False, editable=False, visible=False
151
+ )
152
+
153
+ output_dataframe = gr.Dataframe(
154
+ type="pandas",
155
+ headers=[
156
+ "",
157
+ loc.localize("single-tab-output-header-start"),
158
+ loc.localize("single-tab-output-header-end"),
159
+ loc.localize("single-tab-output-header-sci-name"),
160
+ loc.localize("single-tab-output-header-common-name"),
161
+ loc.localize("single-tab-output-header-confidence"),
162
+ ],
163
+ elem_id="single-file-output",
164
+ interactive=False,
165
+ )
166
+
167
+ def get_audio_path(i, generate_spectrogram):
168
+ if i:
169
+ try:
170
+ return (
171
+ i["path"],
172
+ gr.Audio(label=os.path.basename(i["path"])),
173
+ gr.Plot(visible=True, value=utils.spectrogram_from_file(i["path"], fig_size=(20, 4)))
174
+ if generate_spectrogram
175
+ else gr.Plot(visible=False),
176
+ gr.Button(interactive=True),
177
+ )
178
+ except Exception as e:
179
+ raise gr.Error(loc.localize("single-tab-generate-spectrogram-error")) from e
180
+ else:
181
+ return None, None, gr.Plot(visible=False), gr.update(interactive=False)
182
+
183
+ def try_generate_spectrogram(audio_path, generate_spectrogram):
184
+ if audio_path and generate_spectrogram:
185
+ try:
186
+ return gr.Plot(
187
+ visible=True, value=utils.spectrogram_from_file(audio_path["path"], fig_size=(20, 4))
188
+ )
189
+ except Exception as e:
190
+ raise gr.Error(loc.localize("single-tab-generate-spectrogram-error")) from e
191
+ else:
192
+ return gr.Plot()
193
+
194
+ generate_spectrogram_cb.change(
195
+ try_generate_spectrogram,
196
+ inputs=[audio_input, generate_spectrogram_cb],
197
+ outputs=spectogram_output,
198
+ preprocess=False,
199
+ )
200
+
201
+ audio_input.change(
202
+ get_audio_path,
203
+ inputs=[audio_input, generate_spectrogram_cb],
204
+ outputs=[audio_path_state, audio_input, spectogram_output, single_file_analyze],
205
+ preprocess=False,
206
+ )
207
+
208
+ inputs = [
209
+ audio_path_state,
210
+ use_top_n,
211
+ top_n_input,
212
+ confidence_slider,
213
+ sensitivity_slider,
214
+ overlap_slider,
215
+ merge_consecutive_slider,
216
+ audio_speed_slider,
217
+ fmin_number,
218
+ fmax_number,
219
+ species_list_radio,
220
+ species_file_input,
221
+ lat_number,
222
+ lon_number,
223
+ week_number,
224
+ yearlong_checkbox,
225
+ sf_thresh_number,
226
+ selected_classifier_state,
227
+ locale_radio,
228
+ ]
229
+
230
+ def time_to_seconds(time_str):
231
+ try:
232
+ hours, minutes, seconds = time_str.split(":")
233
+ return int(hours) * 3600 + int(minutes) * 60 + float(seconds)
234
+
235
+ except ValueError as e:
236
+ raise ValueError("Input must be in the format hh:mm:ss or hh:mm:ss.ssssss with numeric values.") from e
237
+
238
+ def get_selected_audio(evt: gr.SelectData, audio_path):
239
+ if evt.index[1] == 0 and evt.row_value[1] and evt.row_value[2]:
240
+ start = time_to_seconds(evt.row_value[1])
241
+ end = time_to_seconds(evt.row_value[2])
242
+
243
+ data, sr = audio.open_audio_file(audio_path, offset=start, duration=end - start)
244
+ return gr.update(visible=True, value=(sr, data))
245
+
246
+ return gr.update()
247
+
248
+ def download_table(filepath):
249
+ if filepath:
250
+ ext = os.path.splitext(filepath)[1]
251
+ gu.save_file_dialog(
252
+ state_key="single-file-table",
253
+ default_filename=os.path.basename(filepath),
254
+ filetypes=(f"{ext[1:]} (*{ext})",),
255
+ )
256
+
257
+ output_dataframe.select(get_selected_audio, inputs=audio_path_state, outputs=segment_audio)
258
+ single_file_analyze.click(
259
+ run_single_file_analysis, inputs=inputs, outputs=[output_dataframe, action_row, table_path_state]
260
+ )
261
+ table_download_button.click(download_table, inputs=table_path_state)
262
+
263
+ return lat_number, lon_number, map_plot
264
+
265
+
266
+ if __name__ == "__main__":
267
+ gu.open_window(build_single_analysis_tab)