pumaguard 21.post19__py3-none-any.whl → 21.post23__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.
- pumaguard/pumaguard-ui/.last_build_id +1 -1
- pumaguard/pumaguard-ui/assets/fonts/MaterialIcons-Regular.otf +0 -0
- pumaguard/pumaguard-ui/flutter_bootstrap.js +1 -1
- pumaguard/pumaguard-ui/main.dart.js +48944 -48185
- pumaguard/web_routes/settings.py +135 -2
- {pumaguard-21.post19.dist-info → pumaguard-21.post23.dist-info}/METADATA +1 -1
- {pumaguard-21.post19.dist-info → pumaguard-21.post23.dist-info}/RECORD +11 -19
- {pumaguard-21.post19.dist-info → pumaguard-21.post23.dist-info}/WHEEL +1 -1
- pumaguard-sounds/dark-engine-logo-141942.mp3 +0 -0
- pumaguard-sounds/forest-ambience-296528.mp3 +0 -0
- pumaguard-sounds/mixkit-cinematic-laser-gun-thunder-1287.wav +0 -0
- pumaguard-sounds/mixkit-classic-alarm-995.wav +0 -0
- pumaguard-sounds/mixkit-dog-barking-twice-1.wav +0 -0
- pumaguard-sounds/mixkit-sad-game-over-trombone-471.wav +0 -0
- pumaguard-sounds/mixkit-small-group-cheer-and-applause-518.wav +0 -0
- pumaguard-sounds/mixkit-vintage-telephone-ringtone-1356.wav +0 -0
- {pumaguard-21.post19.dist-info → pumaguard-21.post23.dist-info}/entry_points.txt +0 -0
- {pumaguard-21.post19.dist-info → pumaguard-21.post23.dist-info}/licenses/LICENSE +0 -0
- {pumaguard-21.post19.dist-info → pumaguard-21.post23.dist-info}/top_level.txt +0 -0
pumaguard/web_routes/settings.py
CHANGED
|
@@ -6,6 +6,7 @@ from __future__ import (
|
|
|
6
6
|
|
|
7
7
|
import logging
|
|
8
8
|
import os
|
|
9
|
+
import subprocess
|
|
9
10
|
from typing import (
|
|
10
11
|
TYPE_CHECKING,
|
|
11
12
|
)
|
|
@@ -15,6 +16,9 @@ from flask import (
|
|
|
15
16
|
jsonify,
|
|
16
17
|
request,
|
|
17
18
|
)
|
|
19
|
+
from werkzeug.utils import (
|
|
20
|
+
secure_filename,
|
|
21
|
+
)
|
|
18
22
|
from yaml.representer import (
|
|
19
23
|
YAMLError,
|
|
20
24
|
)
|
|
@@ -313,8 +317,8 @@ def register_settings_routes(app: "Flask", webui: "WebUI") -> None:
|
|
|
313
317
|
404,
|
|
314
318
|
)
|
|
315
319
|
|
|
316
|
-
#
|
|
317
|
-
audio_extensions = {".mp3"
|
|
320
|
+
# Only MP3 files are supported (mpg123 player)
|
|
321
|
+
audio_extensions = {".mp3"}
|
|
318
322
|
|
|
319
323
|
sound_files = []
|
|
320
324
|
for filename in os.listdir(sound_path):
|
|
@@ -338,3 +342,132 @@ def register_settings_routes(app: "Flask", webui: "WebUI") -> None:
|
|
|
338
342
|
except Exception as e: # pylint: disable=broad-except
|
|
339
343
|
logger.exception("Error getting available sounds")
|
|
340
344
|
return jsonify({"error": str(e)}), 500
|
|
345
|
+
|
|
346
|
+
def _validate_uploaded_file():
|
|
347
|
+
"""Validate uploaded file exists and has proper format."""
|
|
348
|
+
if "file" not in request.files:
|
|
349
|
+
return {"error": "No file provided"}, 400
|
|
350
|
+
|
|
351
|
+
logger.debug("Verifying uploaded sound file")
|
|
352
|
+
uploaded_file = request.files["file"]
|
|
353
|
+
|
|
354
|
+
if uploaded_file.filename == "" or uploaded_file.filename is None:
|
|
355
|
+
return {"error": "No file selected"}, 400
|
|
356
|
+
|
|
357
|
+
filename = os.path.basename(uploaded_file.filename)
|
|
358
|
+
ext = os.path.splitext(filename)[1].lower()
|
|
359
|
+
|
|
360
|
+
if ext != ".mp3":
|
|
361
|
+
return {
|
|
362
|
+
"error": (
|
|
363
|
+
"Unsupported file type. Only MP3 files are supported."
|
|
364
|
+
)
|
|
365
|
+
}, 400
|
|
366
|
+
|
|
367
|
+
return None
|
|
368
|
+
|
|
369
|
+
def _validate_mp3_file(filepath):
|
|
370
|
+
"""Validate MP3 file integrity and format."""
|
|
371
|
+
# Check file size first
|
|
372
|
+
file_size = os.path.getsize(filepath)
|
|
373
|
+
if file_size == 0:
|
|
374
|
+
os.remove(filepath)
|
|
375
|
+
return {"error": "Uploaded file is empty"}, 400
|
|
376
|
+
|
|
377
|
+
# Check MP3 file signature
|
|
378
|
+
with open(filepath, "rb") as f:
|
|
379
|
+
header = f.read(3)
|
|
380
|
+
is_id3 = header == b"ID3"
|
|
381
|
+
is_mpeg = (
|
|
382
|
+
len(header) >= 2
|
|
383
|
+
and header[0] == 0xFF
|
|
384
|
+
and (header[1] & 0xE0) == 0xE0
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
if not (is_id3 or is_mpeg):
|
|
388
|
+
os.remove(filepath)
|
|
389
|
+
return {
|
|
390
|
+
"error": ("File does not appear to be a valid MP3 file")
|
|
391
|
+
}, 400
|
|
392
|
+
|
|
393
|
+
# Test with mpg123 to ensure it can play
|
|
394
|
+
try:
|
|
395
|
+
result = subprocess.run(
|
|
396
|
+
["mpg123", "--test", filepath],
|
|
397
|
+
capture_output=True,
|
|
398
|
+
timeout=5,
|
|
399
|
+
check=False,
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
if result.returncode != 0:
|
|
403
|
+
os.remove(filepath)
|
|
404
|
+
return {
|
|
405
|
+
"error": (
|
|
406
|
+
"MP3 file validation failed. File may be corrupted."
|
|
407
|
+
)
|
|
408
|
+
}, 400
|
|
409
|
+
|
|
410
|
+
except subprocess.TimeoutExpired:
|
|
411
|
+
os.remove(filepath)
|
|
412
|
+
return {"error": "File validation timeout"}, 400
|
|
413
|
+
except FileNotFoundError:
|
|
414
|
+
logger.warning("mpg123 not found, skipping audio validation")
|
|
415
|
+
except Exception as e: # pylint: disable=broad-except
|
|
416
|
+
logger.warning("Audio validation failed: %s", e)
|
|
417
|
+
# Don't fail upload if validation fails, just log it
|
|
418
|
+
|
|
419
|
+
return None
|
|
420
|
+
|
|
421
|
+
@app.route("/api/sounds/upload", methods=["POST"])
|
|
422
|
+
def upload_sound():
|
|
423
|
+
"""Upload a new sound file."""
|
|
424
|
+
try:
|
|
425
|
+
# Validate uploaded file
|
|
426
|
+
error = _validate_uploaded_file()
|
|
427
|
+
if error:
|
|
428
|
+
return jsonify(error[0]), error[1]
|
|
429
|
+
|
|
430
|
+
file = request.files["file"]
|
|
431
|
+
# Type guard: _validate_uploaded_file ensures filename is not None
|
|
432
|
+
assert file.filename is not None
|
|
433
|
+
filename = secure_filename(file.filename)
|
|
434
|
+
|
|
435
|
+
# Ensure sound path exists
|
|
436
|
+
sound_path = webui.presets.sound_path
|
|
437
|
+
os.makedirs(sound_path, exist_ok=True)
|
|
438
|
+
|
|
439
|
+
filepath = os.path.join(sound_path, filename)
|
|
440
|
+
|
|
441
|
+
# Check if file already exists
|
|
442
|
+
if os.path.exists(filepath):
|
|
443
|
+
msg = (
|
|
444
|
+
f"File '{filename}' already exists. "
|
|
445
|
+
"Please rename your file or "
|
|
446
|
+
"delete the existing one."
|
|
447
|
+
)
|
|
448
|
+
return jsonify({"error": msg}), 409
|
|
449
|
+
|
|
450
|
+
# Save the file
|
|
451
|
+
file.save(filepath)
|
|
452
|
+
logger.info("Uploaded sound file: %s", filepath)
|
|
453
|
+
|
|
454
|
+
# Validate MP3 file
|
|
455
|
+
error = _validate_mp3_file(filepath)
|
|
456
|
+
if error:
|
|
457
|
+
return jsonify(error[0]), error[1]
|
|
458
|
+
|
|
459
|
+
# Get file size
|
|
460
|
+
size_mb = os.path.getsize(filepath) / (1024 * 1024)
|
|
461
|
+
|
|
462
|
+
return jsonify(
|
|
463
|
+
{
|
|
464
|
+
"success": True,
|
|
465
|
+
"message": f"Sound file uploaded: {filename}",
|
|
466
|
+
"filename": filename,
|
|
467
|
+
"size_mb": size_mb,
|
|
468
|
+
}
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
except Exception as e: # pylint: disable=broad-except
|
|
472
|
+
logger.exception("Error uploading sound file")
|
|
473
|
+
return jsonify({"error": str(e)}), 500
|
|
@@ -17,20 +17,20 @@ pumaguard/completions/pumaguard-classify-completions.sh,sha256=5QySg-2Jdinj15qpU
|
|
|
17
17
|
pumaguard/completions/pumaguard-completions.sh,sha256=bRx3Q3_gM__3w0PyfQSCVdxylhhr3QlzaLCav24dfNc,1196
|
|
18
18
|
pumaguard/completions/pumaguard-server-completions.sh,sha256=33c6GjbTImBOHn0SSNUOJoxqJ2mMHuDv3P3GQJGGHhA,1161
|
|
19
19
|
pumaguard/completions/pumaguard-train-completions.sh,sha256=lI8LG-QrncvhUqCeKtfrSU1MSRBn52KnDsiJJQm37W4,1184
|
|
20
|
-
pumaguard/pumaguard-ui/.last_build_id,sha256=
|
|
20
|
+
pumaguard/pumaguard-ui/.last_build_id,sha256=JEtHncb8ih-1SZHF-ZKJTdX5yFIJ3hBh80COExBai0E,32
|
|
21
21
|
pumaguard/pumaguard-ui/favicon.png,sha256=erJSX0uGtl0-THA1ihfloar29Df5nLzARtrXPVm7kBU,917
|
|
22
22
|
pumaguard/pumaguard-ui/flutter.js,sha256=7V1ZIKmGiouT15CpquQWWmKWJyjUq77FoU9gDXPFO9M,9412
|
|
23
|
-
pumaguard/pumaguard-ui/flutter_bootstrap.js,sha256=
|
|
23
|
+
pumaguard/pumaguard-ui/flutter_bootstrap.js,sha256=FjbTGwVfXBKfvE3LaFNYPDPJPb-q04spocpkWYP355w,9692
|
|
24
24
|
pumaguard/pumaguard-ui/flutter_service_worker.js,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
25
|
pumaguard/pumaguard-ui/index.html,sha256=901-ZY0WysVAZWPwj2xGatoezwm9TX9IV_jpMrlsaXg,1205
|
|
26
|
-
pumaguard/pumaguard-ui/main.dart.js,sha256=
|
|
26
|
+
pumaguard/pumaguard-ui/main.dart.js,sha256=iueYPu3l5iRPZn9XKmyqnauZxUMJCfQOVD-rSgiq0_0,2839622
|
|
27
27
|
pumaguard/pumaguard-ui/manifest.json,sha256=Hhnw_eLUivdrOlL7O9KGBsGXCKKt3lix17Fh3GB0g-s,920
|
|
28
28
|
pumaguard/pumaguard-ui/version.json,sha256=uXZ6musTJUZaO0N2bEbr3cy9rpx2aesAS2YFMcu2WF8,94
|
|
29
29
|
pumaguard/pumaguard-ui/assets/AssetManifest.bin,sha256=Qzp1G9iPlHSW-PnHyszTxZO31_NjmTlvSBWY_REPH_8,562
|
|
30
30
|
pumaguard/pumaguard-ui/assets/AssetManifest.bin.json,sha256=_6pfLT_4Bcd6SkcHE6GNc8Uoh6UyL4dxCaK7bypu5lc,754
|
|
31
31
|
pumaguard/pumaguard-ui/assets/FontManifest.json,sha256=TbzXC0njmfIhJ8D_sqF4P6NGmuI9BBSOl8AzaY-zNMo,598
|
|
32
32
|
pumaguard/pumaguard-ui/assets/NOTICES,sha256=pXK1o4s9viUW6snqVEqfZsFJa-43d8tCPzyBAanqn0I,1383980
|
|
33
|
-
pumaguard/pumaguard-ui/assets/fonts/MaterialIcons-Regular.otf,sha256=
|
|
33
|
+
pumaguard/pumaguard-ui/assets/fonts/MaterialIcons-Regular.otf,sha256=VEHZorBgkXvxE-zgZoxjQwMZ0eOJYlvkvXuOGTf0Pos,12592
|
|
34
34
|
pumaguard/pumaguard-ui/assets/fonts/Roboto-Bold.ttf,sha256=YfifjbSSYcL2EG6NzMNd97L37ZCQINtAo_yQXpX5kzQ,514260
|
|
35
35
|
pumaguard/pumaguard-ui/assets/fonts/Roboto-Light.ttf,sha256=Ao-EOxmQukbiocTvG4JynE2pqUaw2djb9Z5iPRCV5FQ,518580
|
|
36
36
|
pumaguard/pumaguard-ui/assets/fonts/Roboto-Medium.ttf,sha256=KHml7Lf7-hOn_D4s3X_sv3OqRekbVB39-ixELu0KrCE,511592
|
|
@@ -64,28 +64,20 @@ pumaguard/web_routes/diagnostics.py,sha256=EIIbjuixJyGXdnVQf8RQ6xQxJar0UHZO8dF-9
|
|
|
64
64
|
pumaguard/web_routes/directories.py,sha256=yy5TghCEyB4reRGAcVHIEfr2vlHnuiDChIXl9ZFquRM,2410
|
|
65
65
|
pumaguard/web_routes/folders.py,sha256=Z63ap6dRi6NWye70HYurpCnsSXmFgzTbTsFKYdZ1Bjk,6305
|
|
66
66
|
pumaguard/web_routes/photos.py,sha256=Tac_CbaZSeZzOfaJ73vlp3iyZbvfD7ei1YM3tsb0nTY,5106
|
|
67
|
-
pumaguard/web_routes/settings.py,sha256=
|
|
67
|
+
pumaguard/web_routes/settings.py,sha256=01nHNvQVCs_16_wi0_fuKMa7Muv_rjHCyXu_ARBsAtY,16168
|
|
68
68
|
pumaguard/web_routes/sync.py,sha256=Zvv6VARGE5xP29C5gWH3ul81PISRxoF8n472DITItE0,6378
|
|
69
|
-
pumaguard-21.
|
|
69
|
+
pumaguard-21.post23.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
70
70
|
pumaguard-sounds/cougar_call.mp3,sha256=jdPzi7Qneect3ez2G6XAeHWtetU5vSOSB6pceuB26Wc,129048
|
|
71
71
|
pumaguard-sounds/cougarsounds.wav,sha256=hwVmmQ75dkOP3qd07YAvVOSm1neYtxLSzxw3Ulvs2cM,96346
|
|
72
|
-
pumaguard-sounds/dark-engine-logo-141942.mp3,sha256=Vw-qyLTMPJZvsgQcZtH0DpGcP1dd7nJq-9BnHuNPGug,372819
|
|
73
72
|
pumaguard-sounds/deterrent_puma.mp3,sha256=Jp14Kq9y6NmMZsYJ2ScPWZawl1THk4LXeW63ty12imI,164783
|
|
74
73
|
pumaguard-sounds/detterent_ringtail.mp3,sha256=7UyOboW5mehjY2ZhefypLtxv95W5DHq6zVG8PQzkbvg,97283
|
|
75
|
-
pumaguard-sounds/forest-ambience-296528.mp3,sha256=8IDNPvfu61r5JBaXIVVNDb-wX69QGbqQsBn9xgZD1W8,6815242
|
|
76
74
|
pumaguard-sounds/lion-roar-6011.mp3,sha256=M9-kKJI0PpQDiZI7oDfXv9dHuVO8h5Hpp3b8Bi-LJqk,77280
|
|
77
75
|
pumaguard-sounds/lion_cry.wav,sha256=6DgEguM5UQQ84yksvwYH20tut8HE7mIxKk8AcSLSGeg,51676
|
|
78
|
-
pumaguard-sounds/mixkit-cinematic-laser-gun-thunder-1287.wav,sha256=tPVhcUw5H6uSv7egLuOklcBPwlbPJ6fKq891ifLNfGk,1272536
|
|
79
|
-
pumaguard-sounds/mixkit-classic-alarm-995.wav,sha256=3Y0nnKkOCGU1e3OJ-uOc18OC0S-nZ17SS4SIZWqPZJY,886612
|
|
80
|
-
pumaguard-sounds/mixkit-dog-barking-twice-1.wav,sha256=UlPmHfz9i6JdKu6hVnxlmAhUZX3PRRNDNzvZh3GXO78,286330
|
|
81
|
-
pumaguard-sounds/mixkit-sad-game-over-trombone-471.wav,sha256=MEZ26kzIoueqi8rFpw57Ej99BCSSCVkgofIez_XSIV8,990152
|
|
82
|
-
pumaguard-sounds/mixkit-small-group-cheer-and-applause-518.wav,sha256=GZv1Pvw2PsDEQHdzA9gKG5JxOiUa9P7in6M8R516dnM,1869442
|
|
83
|
-
pumaguard-sounds/mixkit-vintage-telephone-ringtone-1356.wav,sha256=zWWY2uFF0-l7PMN58M9UDDvgYSzlCD6D00P_F_uZQhA,1303078
|
|
84
76
|
pumaguard-sounds/pumaguard-warning.mp3,sha256=wcCfHsulPo5P5s8MjpQAG2NYHQDsRpjqoMig1-o_MDI,232249
|
|
85
77
|
pumaguard-sounds/short-round-110940.mp3,sha256=vdskGD94SeH1UJyJyR0Ek_7xGXPIZfnPdoBvxGnUt98,450816
|
|
86
78
|
pumaguard-ui/ios/Flutter/ephemeral/flutter_lldb_helper.py,sha256=Bc_jl3_e5ZPvrSBJpPYtN05VxpztyKq-7lVms3rLg4Q,1276
|
|
87
|
-
pumaguard-21.
|
|
88
|
-
pumaguard-21.
|
|
89
|
-
pumaguard-21.
|
|
90
|
-
pumaguard-21.
|
|
91
|
-
pumaguard-21.
|
|
79
|
+
pumaguard-21.post23.dist-info/METADATA,sha256=LXRFywh6abI_TRyiQr5v1-UZIrL0kd122m0OsAGXSYc,8617
|
|
80
|
+
pumaguard-21.post23.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
81
|
+
pumaguard-21.post23.dist-info/entry_points.txt,sha256=rmCdBTPWrbJQvPPwABSVobXE9D7hrKsITGZ6nvCrko8,127
|
|
82
|
+
pumaguard-21.post23.dist-info/top_level.txt,sha256=B-PzS4agkQNhOYbLLIrMVOyMD_pl5F-yujPBm5zYYjY,40
|
|
83
|
+
pumaguard-21.post23.dist-info/RECORD,,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|