Linting + touches
This commit is contained in:
@@ -10,10 +10,10 @@ from app.config import settings
|
|||||||
from app.internal import credentials
|
from app.internal import credentials
|
||||||
from app.internal.logger import logger
|
from app.internal.logger import logger
|
||||||
|
|
||||||
MAX_RECORDING_SECONDS = 600 # safety cap; drop call if it runs this long
|
MAX_RECORDING_SECONDS = 600 # safety cap; drop call if it runs this long
|
||||||
PRE_BUFFER_SECONDS = 1.0 # seconds of audio to include before call_start
|
PRE_BUFFER_SECONDS = 1.0 # seconds of audio to include before call_start
|
||||||
RING_BUFFER_SECONDS = 60 # how much history to keep when no call is active
|
RING_BUFFER_SECONDS = 60 # how much history to keep when no call is active
|
||||||
READ_CHUNK_BYTES = 4096 # bytes per httpx read
|
READ_CHUNK_BYTES = 4096 # bytes per httpx read
|
||||||
|
|
||||||
|
|
||||||
class CallRecorder:
|
class CallRecorder:
|
||||||
@@ -119,9 +119,9 @@ class CallRecorder:
|
|||||||
if not self._call_id:
|
if not self._call_id:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
call_id = self._call_id
|
call_id = self._call_id
|
||||||
call_start = self._call_start_mono
|
call_start = self._call_start_mono
|
||||||
self._call_id = None
|
self._call_id = None
|
||||||
self._call_start_mono = None
|
self._call_start_mono = None
|
||||||
|
|
||||||
# Slice: everything from (call_start - pre_buffer) to now
|
# Slice: everything from (call_start - pre_buffer) to now
|
||||||
@@ -180,8 +180,8 @@ class CallRecorder:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
upload_url = f"{settings.c2_url}/upload"
|
upload_url = f"{settings.c2_url}/upload"
|
||||||
api_key = credentials.get_api_key()
|
api_key = credentials.get_api_key()
|
||||||
headers = {"Authorization": f"Bearer {api_key}"} if api_key else {}
|
headers = {"Authorization": f"Bearer {api_key}"} if api_key else {}
|
||||||
|
|
||||||
form: dict = {"call_id": call_id, "node_id": settings.node_id}
|
form: dict = {"call_id": call_id, "node_id": settings.node_id}
|
||||||
if talkgroup_id is not None:
|
if talkgroup_id is not None:
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
from app.models import NodeConfig, SystemConfig
|
from app.models import NodeConfig, SystemConfig
|
||||||
from app.internal.logger import logger
|
from app.internal.logger import logger
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from app.internal.logger import logger
|
|||||||
|
|
||||||
BOT_READY_TIMEOUT = 15 # seconds to wait for Discord bot to become ready
|
BOT_READY_TIMEOUT = 15 # seconds to wait for Discord bot to become ready
|
||||||
WATCHDOG_INTERVAL = 30 # seconds between voice-connection health checks
|
WATCHDOG_INTERVAL = 30 # seconds between voice-connection health checks
|
||||||
REJOIN_DELAY = 5 # seconds to wait before attempting a rejoin
|
REJOIN_DELAY = 5 # seconds to wait before attempting a rejoin
|
||||||
|
|
||||||
|
|
||||||
class RadioBot:
|
class RadioBot:
|
||||||
@@ -116,7 +116,8 @@ class RadioBot:
|
|||||||
def _on_stream_end(self, error):
|
def _on_stream_end(self, error):
|
||||||
if error:
|
if error:
|
||||||
logger.error(f"Stream ended with error: {error}")
|
logger.error(f"Stream ended with error: {error}")
|
||||||
if not (self._loop and self._voice_client and self._voice_client.is_connected() and not self._voice_client.is_playing()):
|
vc = self._voice_client
|
||||||
|
if not (self._loop and vc and vc.is_connected() and not vc.is_playing()):
|
||||||
return
|
return
|
||||||
if error:
|
if error:
|
||||||
# Back off before retrying — prevents tight loop when PulseAudio is unavailable
|
# Back off before retrying — prevents tight loop when PulseAudio is unavailable
|
||||||
|
|||||||
@@ -24,14 +24,14 @@ class MQTTManager:
|
|||||||
self.on_api_key: Optional[ApiKeyCallback] = None
|
self.on_api_key: Optional[ApiKeyCallback] = None
|
||||||
|
|
||||||
nid = settings.node_id
|
nid = settings.node_id
|
||||||
self._t_checkin = f"nodes/{nid}/checkin"
|
self._t_checkin = f"nodes/{nid}/checkin"
|
||||||
self._t_status = f"nodes/{nid}/status"
|
self._t_status = f"nodes/{nid}/status"
|
||||||
self._t_metadata = f"nodes/{nid}/metadata"
|
self._t_metadata = f"nodes/{nid}/metadata"
|
||||||
self._t_commands = f"nodes/{nid}/commands"
|
self._t_commands = f"nodes/{nid}/commands"
|
||||||
self._t_config = f"nodes/{nid}/config"
|
self._t_config = f"nodes/{nid}/config"
|
||||||
self._t_api_key = f"nodes/{nid}/api_key"
|
self._t_api_key = f"nodes/{nid}/api_key"
|
||||||
self._t_key_request = f"nodes/{nid}/key_request"
|
self._t_key_request = f"nodes/{nid}/key_request"
|
||||||
self._t_discovery = "nodes/discovery/request"
|
self._t_discovery = "nodes/discovery/request"
|
||||||
|
|
||||||
def _build_client(self) -> mqtt.Client:
|
def _build_client(self) -> mqtt.Client:
|
||||||
client = mqtt.Client(
|
client = mqtt.Client(
|
||||||
@@ -49,9 +49,9 @@ class MQTTManager:
|
|||||||
client.will_set(self._t_status, lwt, qos=1, retain=True)
|
client.will_set(self._t_status, lwt, qos=1, retain=True)
|
||||||
|
|
||||||
client.reconnect_delay_set(min_delay=2, max_delay=60)
|
client.reconnect_delay_set(min_delay=2, max_delay=60)
|
||||||
client.on_connect = self._on_connect
|
client.on_connect = self._on_connect
|
||||||
client.on_disconnect = self._on_disconnect
|
client.on_disconnect = self._on_disconnect
|
||||||
client.on_message = self._on_message
|
client.on_message = self._on_message
|
||||||
return client
|
return client
|
||||||
|
|
||||||
def _on_connect(self, client, userdata, flags, reason_code, properties):
|
def _on_connect(self, client, userdata, flags, reason_code, properties):
|
||||||
|
|||||||
@@ -44,7 +44,10 @@ async def on_call_end(data: dict):
|
|||||||
else:
|
else:
|
||||||
logger.error(f"Audio upload failed for call {data['call_id']}. Verify C2_URL and Node API Key.")
|
logger.error(f"Audio upload failed for call {data['call_id']}. Verify C2_URL and Node API Key.")
|
||||||
else:
|
else:
|
||||||
logger.warning(f"No recording file generated for call {data['call_id']} — call may have been too short or Icecast unreachable.")
|
logger.warning(
|
||||||
|
f"No recording file generated for call {data['call_id']} "
|
||||||
|
"— call may have been too short or Icecast unreachable."
|
||||||
|
)
|
||||||
await mqtt_manager.publish_metadata("call_end", data)
|
await mqtt_manager.publish_metadata("call_end", data)
|
||||||
await mqtt_manager.publish_status("online")
|
await mqtt_manager.publish_status("online")
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# Icecast streaming server
|
||||||
FROM debian:bookworm-slim
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
## OP25 Core Container
|
# OP25 Core Container
|
||||||
FROM python:slim-trixie
|
FROM python:slim-trixie
|
||||||
|
|
||||||
# Set environment variables
|
# Set environment variables
|
||||||
|
|||||||
Reference in New Issue
Block a user