pumaguard 20.post232__py3-none-any.whl → 20.post240__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 (27) hide show
  1. pumaguard/presets.py +25 -0
  2. pumaguard/pumaguard-ui/.last_build_id +1 -1
  3. pumaguard/pumaguard-ui/assets/AssetManifest.bin +1 -1
  4. pumaguard/pumaguard-ui/assets/AssetManifest.bin.json +1 -1
  5. pumaguard/pumaguard-ui/assets/FontManifest.json +1 -1
  6. pumaguard/pumaguard-ui/assets/NOTICES +213 -0
  7. pumaguard/pumaguard-ui/assets/fonts/MaterialIcons-Regular.otf +0 -0
  8. pumaguard/pumaguard-ui/assets/fonts/Roboto-Bold.ttf +0 -0
  9. pumaguard/pumaguard-ui/assets/fonts/Roboto-Light.ttf +0 -0
  10. pumaguard/pumaguard-ui/assets/fonts/Roboto-Medium.ttf +0 -0
  11. pumaguard/pumaguard-ui/assets/fonts/Roboto-Regular.ttf +0 -0
  12. pumaguard/pumaguard-ui/assets/fonts/RobotoMono-Bold.ttf +0 -0
  13. pumaguard/pumaguard-ui/assets/fonts/RobotoMono-Medium.ttf +0 -0
  14. pumaguard/pumaguard-ui/assets/fonts/RobotoMono-Regular.ttf +0 -0
  15. pumaguard/pumaguard-ui/flutter_bootstrap.js +1 -5
  16. pumaguard/pumaguard-ui/flutter_service_worker.js +0 -207
  17. pumaguard/pumaguard-ui/index.html +0 -22
  18. pumaguard/pumaguard-ui/main.dart.js +55083 -52886
  19. pumaguard/server.py +1 -1
  20. pumaguard/sound.py +138 -5
  21. pumaguard/web_routes/settings.py +80 -6
  22. {pumaguard-20.post232.dist-info → pumaguard-20.post240.dist-info}/METADATA +1 -1
  23. {pumaguard-20.post232.dist-info → pumaguard-20.post240.dist-info}/RECORD +27 -20
  24. {pumaguard-20.post232.dist-info → pumaguard-20.post240.dist-info}/WHEEL +0 -0
  25. {pumaguard-20.post232.dist-info → pumaguard-20.post240.dist-info}/entry_points.txt +0 -0
  26. {pumaguard-20.post232.dist-info → pumaguard-20.post240.dist-info}/licenses/LICENSE +0 -0
  27. {pumaguard-20.post232.dist-info → pumaguard-20.post240.dist-info}/top_level.txt +0 -0
pumaguard/server.py CHANGED
@@ -272,7 +272,7 @@ class FolderObserver:
272
272
  sound_file_path = os.path.join(
273
273
  self.presets.sound_path, self.presets.deterrent_sound_file
274
274
  )
275
- playsound(sound_file_path)
275
+ playsound(sound_file_path, self.presets.volume)
276
276
  # Move original file into classification folder
277
277
  try:
278
278
  dest_root = (
pumaguard/sound.py CHANGED
@@ -2,22 +2,155 @@
2
2
  Sounds
3
3
  """
4
4
 
5
+ import logging
5
6
  import subprocess
6
7
  import sys
8
+ import threading
9
+ from typing import Optional
7
10
 
11
+ logger = logging.getLogger(__name__)
8
12
 
9
- def playsound(soundfile: str):
13
+ # Global variable to track the current playing process
14
+ _current_process: Optional[subprocess.Popen] = None
15
+ _process_lock = threading.Lock()
16
+
17
+
18
+ def playsound(soundfile: str, volume: int = 80, blocking: bool = True):
10
19
  """
11
- Play a sound file.
20
+ Play a sound file with specified volume.
21
+
22
+ Args:
23
+ soundfile: Path to the sound file to play
24
+ volume: Volume level from 0-100 (default: 80)
25
+ blocking: If True, wait for sound to finish. If False, return
26
+ immediately (default: True)
12
27
  """
28
+ global _current_process # pylint: disable=global-statement
29
+
30
+ logger.info(
31
+ "playsound called: file=%s, volume=%d, blocking=%s",
32
+ soundfile,
33
+ volume,
34
+ blocking,
35
+ )
36
+
37
+ # mpg123 -f flag scales output samples (soft gain)
38
+ # Default/normal is 32768 (100%)
39
+ # Valid range: 0 to much higher than 32768
40
+ # Convert 0-100 percentage to mpg123 scale:
41
+ # 0% = 0 (muted), 100% = 32768 (normal), 200% = 65536 (double)
42
+ # Linear scaling: mpg123_volume = (volume / 100) * 32768
43
+ mpg123_volume = int((volume / 100.0) * 32768)
44
+
45
+ logger.debug(
46
+ "Volume conversion: %d%% -> mpg123 scale %d", volume, mpg123_volume
47
+ )
48
+
13
49
  try:
14
- subprocess.run(["mpg123", soundfile], check=True)
15
- except subprocess.CalledProcessError as e:
50
+ with _process_lock:
51
+ # Stop any currently playing sound
52
+ if _current_process is not None:
53
+ try:
54
+ _current_process.terminate()
55
+ _current_process.wait(timeout=1)
56
+ except (subprocess.TimeoutExpired, ProcessLookupError):
57
+ pass
58
+ _current_process = None
59
+
60
+ # Start new process
61
+ # pylint: disable=consider-using-with
62
+ cmd = ["mpg123", "-o", "alsa", "-f", str(mpg123_volume), soundfile]
63
+ logger.info("Executing command: %s", " ".join(cmd))
64
+
65
+ _current_process = subprocess.Popen(
66
+ cmd,
67
+ stdout=subprocess.DEVNULL,
68
+ stderr=subprocess.DEVNULL,
69
+ )
70
+
71
+ logger.info(
72
+ "Sound playback started, PID: %d", _current_process.pid
73
+ )
74
+
75
+ if blocking:
76
+ # Wait for completion
77
+ _current_process.wait()
78
+ _current_process = None
79
+
80
+ except subprocess.SubprocessError as e:
81
+ logger.error("Error playing soundfile %s: %s", soundfile, e)
16
82
  print(f"Error playing soundfile {soundfile}: {e}")
83
+ with _process_lock:
84
+ _current_process = None
85
+
86
+
87
+ def stop_sound():
88
+ """
89
+ Stop any currently playing sound.
90
+
91
+ Returns:
92
+ bool: True if a sound was stopped, False if nothing was playing
93
+ """
94
+ global _current_process # pylint: disable=global-statement
95
+
96
+ with _process_lock:
97
+ if _current_process is not None:
98
+ try:
99
+ logger.info(
100
+ "Stopping sound playback, PID: %d", _current_process.pid
101
+ )
102
+ _current_process.terminate()
103
+ _current_process.wait(timeout=1)
104
+ logger.info("Sound playback stopped successfully")
105
+ return True
106
+ except (subprocess.TimeoutExpired, ProcessLookupError):
107
+ try:
108
+ _current_process.kill()
109
+ _current_process.wait(timeout=1)
110
+ except (subprocess.TimeoutExpired, ProcessLookupError):
111
+ pass
112
+ return True
113
+ finally:
114
+ _current_process = None
115
+ return False
116
+
117
+
118
+ def is_playing():
119
+ """
120
+ Check if a sound is currently playing.
121
+
122
+ Returns:
123
+ bool: True if a sound is currently playing, False otherwise
124
+ """
125
+ global _current_process # pylint: disable=global-statement
126
+
127
+ with _process_lock:
128
+ if _current_process is not None:
129
+ # Check if process is still running
130
+ if _current_process.poll() is None:
131
+ return True
132
+ # Process finished, clean up
133
+ _current_process = None
134
+ return False
17
135
 
18
136
 
19
137
  def main():
20
138
  """
21
139
  Main entry point.
22
140
  """
23
- playsound(sys.argv[1])
141
+ if len(sys.argv) < 2:
142
+ print("Usage: pumaguard-sound <soundfile> [volume]")
143
+ sys.exit(1)
144
+
145
+ volume = 80
146
+ if len(sys.argv) >= 3:
147
+ try:
148
+ volume = int(sys.argv[2])
149
+ if volume < 0 or volume > 100:
150
+ print("Volume must be between 0 and 100")
151
+ sys.exit(1)
152
+ except ValueError:
153
+ print("Volume must be an integer")
154
+ sys.exit(1)
155
+
156
+ playsound(sys.argv[1], volume)
@@ -26,7 +26,9 @@ from pumaguard.model_downloader import (
26
26
  verify_file_checksum,
27
27
  )
28
28
  from pumaguard.sound import (
29
+ is_playing,
29
30
  playsound,
31
+ stop_sound,
30
32
  )
31
33
 
32
34
  if TYPE_CHECKING:
@@ -64,6 +66,8 @@ def register_settings_routes(app: "Flask", webui: "WebUI") -> None:
64
66
  "deterrent-sound-file",
65
67
  "file-stabilization-extra-wait",
66
68
  "play-sound",
69
+ "volume",
70
+ "camera-url",
67
71
  ]
68
72
 
69
73
  if len(data) == 0:
@@ -71,9 +75,18 @@ def register_settings_routes(app: "Flask", webui: "WebUI") -> None:
71
75
 
72
76
  for key, value in data.items():
73
77
  if key in allowed_settings:
74
- logger.debug("Updating %s with %s", key, value)
78
+ logger.info(
79
+ "Updating setting %s with value %s", key, value
80
+ )
75
81
  attr_name = key.replace("-", "_").replace("YOLO_", "yolo_")
76
82
  setattr(webui.presets, attr_name, value)
83
+ # Log verification of volume setting
84
+ if key == "volume":
85
+ logger.info(
86
+ "Volume setting updated to %d, verified: %d",
87
+ value,
88
+ webui.presets.volume,
89
+ )
77
90
  else:
78
91
  logger.debug("Skipping unknown/read-only setting: %s", key)
79
92
 
@@ -82,7 +95,11 @@ def register_settings_routes(app: "Flask", webui: "WebUI") -> None:
82
95
  settings_dict = dict(webui.presets)
83
96
  with open(filepath, "w", encoding="utf-8") as f:
84
97
  yaml.dump(settings_dict, f, default_flow_style=False)
85
- logger.info("Settings updated and saved to %s", filepath)
98
+ logger.info(
99
+ "Settings updated and saved to %s (volume: %d)",
100
+ filepath,
101
+ webui.presets.volume,
102
+ )
86
103
  except YAMLError:
87
104
  logger.exception("Error saving settings")
88
105
  return (
@@ -151,19 +168,65 @@ def register_settings_routes(app: "Flask", webui: "WebUI") -> None:
151
168
  404,
152
169
  )
153
170
 
154
- # Play the sound
155
- logger.info("Testing sound playback: %s", sound_file_path)
156
- playsound(sound_file_path)
171
+ # Play the sound with configured volume (non-blocking)
172
+ volume = webui.presets.volume
173
+ logger.info(
174
+ "Testing sound playback: file=%s, volume=%d",
175
+ sound_file_path,
176
+ volume,
177
+ )
178
+ logger.debug(
179
+ "Current presets.volume value before playsound: %d",
180
+ webui.presets.volume,
181
+ )
182
+ playsound(sound_file_path, volume, blocking=False)
157
183
  return jsonify(
158
184
  {
159
185
  "success": True,
160
- "message": f"Sound played: {sound_file}",
186
+ "message": f"Sound started: {sound_file}",
161
187
  }
162
188
  )
163
189
  except Exception as e: # pylint: disable=broad-except
164
190
  logger.exception("Error testing sound")
165
191
  return jsonify({"error": str(e)}), 500
166
192
 
193
+ @app.route("/api/settings/stop-sound", methods=["POST"])
194
+ def stop_test_sound():
195
+ """Stop the currently playing test sound."""
196
+ try:
197
+ stopped = stop_sound()
198
+ if stopped:
199
+ logger.info("Sound playback stopped")
200
+ return jsonify(
201
+ {
202
+ "success": True,
203
+ "message": "Sound stopped",
204
+ }
205
+ )
206
+ return jsonify(
207
+ {
208
+ "success": True,
209
+ "message": "No sound was playing",
210
+ }
211
+ )
212
+ except Exception as e: # pylint: disable=broad-except
213
+ logger.exception("Error stopping sound")
214
+ return jsonify({"error": str(e)}), 500
215
+
216
+ @app.route("/api/settings/sound-status", methods=["GET"])
217
+ def get_sound_status():
218
+ """Check if a sound is currently playing."""
219
+ try:
220
+ playing = is_playing()
221
+ return jsonify(
222
+ {
223
+ "playing": playing,
224
+ }
225
+ )
226
+ except Exception as e: # pylint: disable=broad-except
227
+ logger.exception("Error checking sound status")
228
+ return jsonify({"error": str(e)}), 500
229
+
167
230
  @app.route("/api/models/available", methods=["GET"])
168
231
  def get_available_models():
169
232
  """Get list of available models with cache status.
@@ -261,3 +324,14 @@ def register_settings_routes(app: "Flask", webui: "WebUI") -> None:
261
324
  except Exception as e: # pylint: disable=broad-except
262
325
  logger.exception("Error getting available sounds")
263
326
  return jsonify({"error": str(e)}), 500
327
+
328
+ @app.route("/api/camera/url", methods=["GET"])
329
+ def get_camera_url():
330
+ """Get the configured camera URL."""
331
+ try:
332
+ camera_url = webui.presets.camera_url
333
+ logger.info("Camera URL requested: '%s'", camera_url)
334
+ return jsonify({"camera_url": camera_url})
335
+ except Exception as e: # pylint: disable=broad-except
336
+ logger.exception("Error getting camera URL")
337
+ return jsonify({"error": str(e)}), 500
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pumaguard
3
- Version: 20.post232
3
+ Version: 20.post240
4
4
  Author-email: Nicolas Bock <nicolasbock@gmail.com>
5
5
  Project-URL: Homepage, http://pumaguard.rtfd.io/
6
6
  Project-URL: Repository, https://github.com/PEEC-Nature-Youth-Group/pumaguard
@@ -5,9 +5,9 @@ pumaguard/main.py,sha256=1Wazv1wjwb46yNlqgWt88HQwKSxGmY24X5OsUv8gYyE,7029
5
5
  pumaguard/model-registry.yaml,sha256=V-pTaqJrk_jJo568okBDaVhs51qTHttSqKe6PqdX6Bc,10318
6
6
  pumaguard/model_cli.py,sha256=nzDv0lXSvRKpLxs579tiHInJPPV-AFO4jzeLk5t2GaA,1394
7
7
  pumaguard/model_downloader.py,sha256=zJQgCMOF2AfhB30VsfOMYtgRxcxVxkZBAdtG8KznPyY,12895
8
- pumaguard/presets.py,sha256=fr3zK_c--x10rMJs8IyAxsp2dx3Qqm5tLMifjWJCGas,26915
9
- pumaguard/server.py,sha256=gpn1Lco61OO0IPYzG5eWQ4BaxfIpMFN-AwpBip5csSQ,14684
10
- pumaguard/sound.py,sha256=dBylHyuS8ro9tFJH5y3s6Bn2kSGnfrOyGeGYZlgYTsU,369
8
+ pumaguard/presets.py,sha256=2owFBEqpeAjrFLSYtF9o3wQCc8aDjpwnU5j_YXSyIZ0,27757
9
+ pumaguard/server.py,sha256=zmzSXabt6K26u8kwBPdm1gI6aMAwJo3gCaSaX5Sh0vk,14705
10
+ pumaguard/sound.py,sha256=1qIV4NFOtyRfiIpFTnjbm_hYdNw54bGjMnopeHj_bhM,4633
11
11
  pumaguard/stats.py,sha256=ZwocfnFCQ-ky7me-YTTrEoJqsIHOWAgSzeoJHItsIU4,927
12
12
  pumaguard/utils.py,sha256=w1EgOLSZGyjq_b49hvVZhBESy-lVP0yRtNHe-sXBoIU,19735
13
13
  pumaguard/verify.py,sha256=vfw3PRzDt1uuH5FKV9F5vb1PH7KQ6AEgVNhJ6jck_hQ,5513
@@ -16,20 +16,27 @@ pumaguard/completions/pumaguard-classify-completions.sh,sha256=5QySg-2Jdinj15qpU
16
16
  pumaguard/completions/pumaguard-completions.sh,sha256=bRx3Q3_gM__3w0PyfQSCVdxylhhr3QlzaLCav24dfNc,1196
17
17
  pumaguard/completions/pumaguard-server-completions.sh,sha256=33c6GjbTImBOHn0SSNUOJoxqJ2mMHuDv3P3GQJGGHhA,1161
18
18
  pumaguard/completions/pumaguard-train-completions.sh,sha256=lI8LG-QrncvhUqCeKtfrSU1MSRBn52KnDsiJJQm37W4,1184
19
- pumaguard/pumaguard-ui/.last_build_id,sha256=KOyBJ_AUCj-K8m6Ps6UbkVT7gtCLkXyzJIBgG2_w-w8,32
19
+ pumaguard/pumaguard-ui/.last_build_id,sha256=44nL03JzUFDZwdvMJrofy7jk3ZWVr-aW8se0TOV0weg,32
20
20
  pumaguard/pumaguard-ui/favicon.png,sha256=erJSX0uGtl0-THA1ihfloar29Df5nLzARtrXPVm7kBU,917
21
21
  pumaguard/pumaguard-ui/flutter.js,sha256=7V1ZIKmGiouT15CpquQWWmKWJyjUq77FoU9gDXPFO9M,9412
22
- pumaguard/pumaguard-ui/flutter_bootstrap.js,sha256=AiTzI4PQXILiNyclywmlQEzvVZeC0ok332eZmsju86A,9765
23
- pumaguard/pumaguard-ui/flutter_service_worker.js,sha256=BtDeiZ61AGXiiT9kMJZCD5Bcr9WRVp-bdmA9ATI1sHk,8293
24
- pumaguard/pumaguard-ui/index.html,sha256=CVJ095EIGJkI-4Ts7UTHhb_luPF7z-UKcuZm47mml3Y,1865
25
- pumaguard/pumaguard-ui/main.dart.js,sha256=RUVQxWmOMGVQSBjdSUiYJGZ639iWpOhb7DNAl1RxyQM,2673541
22
+ pumaguard/pumaguard-ui/flutter_bootstrap.js,sha256=RYi4PhelQMs063sa3jCr0BLWDMAzY7lKs-ksCptkaQM,9692
23
+ pumaguard/pumaguard-ui/flutter_service_worker.js,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ pumaguard/pumaguard-ui/index.html,sha256=901-ZY0WysVAZWPwj2xGatoezwm9TX9IV_jpMrlsaXg,1205
25
+ pumaguard/pumaguard-ui/main.dart.js,sha256=_GkHZTEXl7rrDH_m76wch3azHo10GdLVSDxnJCJGYGM,2725345
26
26
  pumaguard/pumaguard-ui/manifest.json,sha256=Hhnw_eLUivdrOlL7O9KGBsGXCKKt3lix17Fh3GB0g-s,920
27
27
  pumaguard/pumaguard-ui/version.json,sha256=uXZ6musTJUZaO0N2bEbr3cy9rpx2aesAS2YFMcu2WF8,94
28
- pumaguard/pumaguard-ui/assets/AssetManifest.bin,sha256=AK9VrT1vIYmP534P8JLRoc2lLJQbaGDpko1FyK-MCV0,117
29
- pumaguard/pumaguard-ui/assets/AssetManifest.bin.json,sha256=hGC19Cmcoe1nfFpUyAOK6c8fJvbpGWaxt2fKbLbS5dI,158
30
- pumaguard/pumaguard-ui/assets/FontManifest.json,sha256=zX4DZFvESy3Ue3y2JvUcTsv1Whl6t3JBYotHrBZfviE,208
31
- pumaguard/pumaguard-ui/assets/NOTICES,sha256=36KDhv5JjUTHLHSv3M-zMkJyjezJjCQB-20DGbpx7JM,1372380
32
- pumaguard/pumaguard-ui/assets/fonts/MaterialIcons-Regular.otf,sha256=R8-aO9K-VgimZR7FNPwS9yffsSwG_MlvUwqMEcH0Syg,10768
28
+ pumaguard/pumaguard-ui/assets/AssetManifest.bin,sha256=Qzp1G9iPlHSW-PnHyszTxZO31_NjmTlvSBWY_REPH_8,562
29
+ pumaguard/pumaguard-ui/assets/AssetManifest.bin.json,sha256=_6pfLT_4Bcd6SkcHE6GNc8Uoh6UyL4dxCaK7bypu5lc,754
30
+ pumaguard/pumaguard-ui/assets/FontManifest.json,sha256=TbzXC0njmfIhJ8D_sqF4P6NGmuI9BBSOl8AzaY-zNMo,598
31
+ pumaguard/pumaguard-ui/assets/NOTICES,sha256=pXK1o4s9viUW6snqVEqfZsFJa-43d8tCPzyBAanqn0I,1383980
32
+ pumaguard/pumaguard-ui/assets/fonts/MaterialIcons-Regular.otf,sha256=_jHTXqz97mq6qdprZetBY0OYMj2DjhQS8g7hSNZS9X4,11092
33
+ pumaguard/pumaguard-ui/assets/fonts/Roboto-Bold.ttf,sha256=YfifjbSSYcL2EG6NzMNd97L37ZCQINtAo_yQXpX5kzQ,514260
34
+ pumaguard/pumaguard-ui/assets/fonts/Roboto-Light.ttf,sha256=Ao-EOxmQukbiocTvG4JynE2pqUaw2djb9Z5iPRCV5FQ,518580
35
+ pumaguard/pumaguard-ui/assets/fonts/Roboto-Medium.ttf,sha256=KHml7Lf7-hOn_D4s3X_sv3OqRekbVB39-ixELu0KrCE,511592
36
+ pumaguard/pumaguard-ui/assets/fonts/Roboto-Regular.ttf,sha256=VqRSM9KfEbTfuG0kjpIZOdEVd4-HMl566MwQg4PWZk0,515100
37
+ pumaguard/pumaguard-ui/assets/fonts/RobotoMono-Bold.ttf,sha256=Ps815eh6zMdXi2BdH18Lww2IsZXWgHvsigxX9qqVxNs,126680
38
+ pumaguard/pumaguard-ui/assets/fonts/RobotoMono-Medium.ttf,sha256=SsuyclomDq__G8qt41ASBMp3TlY0biWe4bYdVpltJhE,127268
39
+ pumaguard/pumaguard-ui/assets/fonts/RobotoMono-Regular.ttf,sha256=rwv_dZnD3zgxdVwW45s8SW33S4yNihFhsU3IRhvhfLQ,125748
33
40
  pumaguard/pumaguard-ui/assets/packages/cupertino_icons/assets/CupertinoIcons.ttf,sha256=PZDDcKpM8A3FfuK5AvZlIUfA-B4D5IPfWEqcCLFofJ0,1472
34
41
  pumaguard/pumaguard-ui/assets/shaders/ink_sparkle.frag,sha256=4Bths5TPZqZBvqh-O0_jeH03L2Nsi8iipnQ4zcKdpvY,8867
35
42
  pumaguard/pumaguard-ui/assets/shaders/stretch_effect.frag,sha256=-pzPjkKWMaRhx6fGRNq6gRpTRj94qirQI-DpyBuBW0o,6714
@@ -55,9 +62,9 @@ pumaguard/web_routes/diagnostics.py,sha256=EIIbjuixJyGXdnVQf8RQ6xQxJar0UHZO8dF-9
55
62
  pumaguard/web_routes/directories.py,sha256=yy5TghCEyB4reRGAcVHIEfr2vlHnuiDChIXl9ZFquRM,2410
56
63
  pumaguard/web_routes/folders.py,sha256=Z63ap6dRi6NWye70HYurpCnsSXmFgzTbTsFKYdZ1Bjk,6305
57
64
  pumaguard/web_routes/photos.py,sha256=Tac_CbaZSeZzOfaJ73vlp3iyZbvfD7ei1YM3tsb0nTY,5106
58
- pumaguard/web_routes/settings.py,sha256=kVkKdaBLitKCGJiePlv34H_-E0yu_HUC3Wnxxynw5QU,8982
65
+ pumaguard/web_routes/settings.py,sha256=kjSqoX6E38UJ_J_YZRQr7QmACR7bDwFcUarqnowjHP4,11655
59
66
  pumaguard/web_routes/sync.py,sha256=Zvv6VARGE5xP29C5gWH3ul81PISRxoF8n472DITItE0,6378
60
- pumaguard-20.post232.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
67
+ pumaguard-20.post240.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
61
68
  pumaguard-sounds/cougar_call.mp3,sha256=jdPzi7Qneect3ez2G6XAeHWtetU5vSOSB6pceuB26Wc,129048
62
69
  pumaguard-sounds/cougarsounds.wav,sha256=hwVmmQ75dkOP3qd07YAvVOSm1neYtxLSzxw3Ulvs2cM,96346
63
70
  pumaguard-sounds/dark-engine-logo-141942.mp3,sha256=Vw-qyLTMPJZvsgQcZtH0DpGcP1dd7nJq-9BnHuNPGug,372819
@@ -75,8 +82,8 @@ pumaguard-sounds/mixkit-vintage-telephone-ringtone-1356.wav,sha256=zWWY2uFF0-l7P
75
82
  pumaguard-sounds/pumaguard-warning.mp3,sha256=wcCfHsulPo5P5s8MjpQAG2NYHQDsRpjqoMig1-o_MDI,232249
76
83
  pumaguard-sounds/short-round-110940.mp3,sha256=vdskGD94SeH1UJyJyR0Ek_7xGXPIZfnPdoBvxGnUt98,450816
77
84
  pumaguard-ui/ios/Flutter/ephemeral/flutter_lldb_helper.py,sha256=Bc_jl3_e5ZPvrSBJpPYtN05VxpztyKq-7lVms3rLg4Q,1276
78
- pumaguard-20.post232.dist-info/METADATA,sha256=Sn-L2GcZm3VGKzc6W_eEON-LzwsT95mAo7FpDIR8Ggo,8618
79
- pumaguard-20.post232.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
80
- pumaguard-20.post232.dist-info/entry_points.txt,sha256=rmCdBTPWrbJQvPPwABSVobXE9D7hrKsITGZ6nvCrko8,127
81
- pumaguard-20.post232.dist-info/top_level.txt,sha256=B-PzS4agkQNhOYbLLIrMVOyMD_pl5F-yujPBm5zYYjY,40
82
- pumaguard-20.post232.dist-info/RECORD,,
85
+ pumaguard-20.post240.dist-info/METADATA,sha256=uE_jBxjvZQ7SMR0DF4LEAElU9NdCUhH9QdElUYqWGvk,8618
86
+ pumaguard-20.post240.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
87
+ pumaguard-20.post240.dist-info/entry_points.txt,sha256=rmCdBTPWrbJQvPPwABSVobXE9D7hrKsITGZ6nvCrko8,127
88
+ pumaguard-20.post240.dist-info/top_level.txt,sha256=B-PzS4agkQNhOYbLLIrMVOyMD_pl5F-yujPBm5zYYjY,40
89
+ pumaguard-20.post240.dist-info/RECORD,,