restrucuture_for_cloud (#4)
- update multicast_server - modify config models - add dockerfile and docker compose Reviewed-on: https://gitea.pstruebi.xyz/auracaster/bumble-auracast/pulls/4
This commit was merged in pull request #4.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@ coverage/ # Coverage results after running tests with coverage tools
|
||||
.dist-info/ # Wheel metadata (use poetry build to handle this)
|
||||
*.egg-info/ # Egg info directory (automatically created by pip)
|
||||
auracast.egg-info/
|
||||
/build
|
||||
|
||||
# Ignore these file types and extensions
|
||||
*.pyc # Compiled Python files (.pyc, .pyo are automatically ignored by git)
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
services:
|
||||
app:
|
||||
multicaster:
|
||||
privileged: true # Grants full access to all devices (needed for serial access)
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5000:5000"
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ssh:
|
||||
- default=~/.ssh/id_ed25519 #lappi
|
||||
#- default=~/.ssh/id_rsa #raspi
|
||||
devices:
|
||||
- /dev/serial/by-id/usb-ZEPHYR_Zephyr_HCI_UART_sample_81BD14B8D71B5662-if00
|
||||
volumes:
|
||||
- "/dev/serial:/dev/serial"
|
||||
#devices:
|
||||
# - /dev/serial/by-id/usb-ZEPHYR_Zephyr_HCI_UART_sample_81BD14B8D71B5662-if00
|
||||
environment:
|
||||
LOG_LEVEL: INFO
|
||||
|
||||
# - DOCKER_BUILDKIT=1 # Enable BuildKit (can also be passed during build)
|
||||
#command: python ./auracast/multicast_server.py #devserver
|
||||
command: python ./auracast/multicast.py # continously streaming test app
|
||||
command: python ./auracast/multicast_server.py
|
||||
#command: python ./auracast/multicast.py # continously streaming test app
|
||||
|
||||
|
||||
# use docker compose up --build
|
||||
@@ -8,8 +8,10 @@ dependencies = [
|
||||
"lc3 @ git+ssh://git@ssh.pstruebi.xyz:222/auracaster/liblc3.git@7558637303106c7ea971e7bb8cedf379d3e08bcc",
|
||||
"sounddevice",
|
||||
"aioconsole",
|
||||
"quart == 0.20.0",
|
||||
"pydantic"
|
||||
"fastapi==0.115.11",
|
||||
"uvicorn==0.34.0",
|
||||
"pydantic",
|
||||
"aiohttp==3.9.3"
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from typing import List
|
||||
from pydantic import BaseModel
|
||||
|
||||
# Define some base to hold the relevant parameters
|
||||
@@ -32,8 +33,8 @@ class AuracastGlobalConfig(BaseModel):
|
||||
octets_per_frame: int = 40 #48kbps@24kHz # bitrate = octets_per_frame * 8 / frame len
|
||||
frame_duration_us: int = 10000
|
||||
presentation_delay_us: int = 40000
|
||||
manufacturer_data: tuple[int, bytes] |None = None # TODO:pydantic does not support bytes serialization
|
||||
|
||||
# TODO:pydantic does not support bytes serialization - use .hex and np.fromhex()
|
||||
manufacturer_data: tuple[int, bytes] | tuple[None, None] = (None, None)
|
||||
|
||||
# "Audio input. "
|
||||
# "'device' -> use the host's default sound input device, "
|
||||
@@ -54,7 +55,7 @@ class AuracastBigConfig(BaseModel):
|
||||
precode_wav: bool = False
|
||||
iso_que_len: int = 64
|
||||
|
||||
class AuracastBigConfigDe(AuracastBigConfig):
|
||||
class AuracastBigConfigDeu(AuracastBigConfig):
|
||||
id: int = 12
|
||||
random_address: str = 'F1:F1:F2:F3:F4:F5'
|
||||
name: str = 'Broadcast0'
|
||||
@@ -62,7 +63,7 @@ class AuracastBigConfigDe(AuracastBigConfig):
|
||||
program_info: str = 'Announcements German'
|
||||
audio_source: str = 'file:./testdata/announcement_de.wav'
|
||||
|
||||
class AuracastBigConfigEn(AuracastBigConfig):
|
||||
class AuracastBigConfigEng(AuracastBigConfig):
|
||||
id: int = 123
|
||||
random_address: str = 'F2:F1:F2:F3:F4:F5'
|
||||
name: str = 'Broadcast1'
|
||||
@@ -70,7 +71,7 @@ class AuracastBigConfigEn(AuracastBigConfig):
|
||||
program_info: str = 'Announcements English'
|
||||
audio_source: str = 'file:./testdata/announcement_en.wav'
|
||||
|
||||
class AuracastBigConfigFr(AuracastBigConfig):
|
||||
class AuracastBigConfigFra(AuracastBigConfig):
|
||||
id: int = 1234
|
||||
random_address: str = 'F3:F1:F2:F3:F4:F5'
|
||||
name: str = 'Broadcast2'
|
||||
@@ -78,7 +79,7 @@ class AuracastBigConfigFr(AuracastBigConfig):
|
||||
program_info: str = 'Announcements French'
|
||||
audio_source: str = 'file:./testdata/announcement_fr.wav'
|
||||
|
||||
class AuracastBigConfigEs(AuracastBigConfig):
|
||||
class AuracastBigConfigSpa(AuracastBigConfig):
|
||||
id: int =12345
|
||||
random_address: str = 'F4:F1:F2:F3:F4:F5'
|
||||
name: str = 'Broadcast3'
|
||||
@@ -86,7 +87,7 @@ class AuracastBigConfigEs(AuracastBigConfig):
|
||||
program_info: str = 'Announcements Spanish'
|
||||
audio_source: str = 'file:./testdata/announcement_es.wav'
|
||||
|
||||
class AuracastBigConfigIt(AuracastBigConfig):
|
||||
class AuracastBigConfigIta(AuracastBigConfig):
|
||||
id: int =1234567
|
||||
random_address: str = 'F5:F1:F2:F3:F4:F5'
|
||||
name: str = 'Broadcast4'
|
||||
@@ -95,4 +96,7 @@ class AuracastBigConfigIt(AuracastBigConfig):
|
||||
audio_source: str = 'file:./testdata/announcement_it.wav'
|
||||
|
||||
|
||||
# TODO: could be best to merge all in just one CONFIG class and give every language an enable parameter
|
||||
class AuracastConfigGroup(AuracastGlobalConfig):
|
||||
bigs: List[AuracastBigConfig] = [
|
||||
AuracastBigConfigDeu(),
|
||||
]
|
||||
|
||||
@@ -23,7 +23,7 @@ import logging
|
||||
import wave
|
||||
import itertools
|
||||
import struct
|
||||
from typing import cast, Any, AsyncGenerator, Coroutine, Dict, Optional, Tuple, List
|
||||
from typing import cast, Any, AsyncGenerator, Coroutine, List
|
||||
import itertools
|
||||
|
||||
try:
|
||||
@@ -186,7 +186,7 @@ async def init_broadcast(
|
||||
logger.info('Setup Advertising')
|
||||
advertising_manufacturer_data = (
|
||||
b''
|
||||
if global_config.manufacturer_data is None
|
||||
if global_config.manufacturer_data == (None, None)
|
||||
else bytes(
|
||||
core.AdvertisingData(
|
||||
[
|
||||
@@ -502,31 +502,27 @@ if __name__ == "__main__":
|
||||
)
|
||||
os.chdir(os.path.dirname(__file__))
|
||||
|
||||
global_conf = auracast_config.AuracastGlobalConfig(
|
||||
qos_config=auracast_config.AuracastQosHigh()
|
||||
|
||||
config = auracast_config.AuracastConfigGroup(
|
||||
bigs = [
|
||||
auracast_config.AuracastBigConfigDeu(),
|
||||
#auracast_config.AuracastBigConfigEng(),
|
||||
#auracast_config.AuracastBigConfigFra(),
|
||||
#auracast_config.AuracastBigConfigEs(),
|
||||
#auracast_config.AuracastBigConfigIt(),
|
||||
]
|
||||
)
|
||||
|
||||
# TODO: How can we use other iso interval than 10ms ?(medium or low rel) ? - nrf53audio receiver repports I2S tx underrun
|
||||
config.qos_config=auracast_config.AuracastQosHigh()
|
||||
|
||||
#global_conf.transport='serial:/dev/serial/by-id/usb-ZEPHYR_Zephyr_HCI_UART_sample_81BD14B8D71B5662-if00,1000000,rtscts' # transport for nrf52 dongle
|
||||
|
||||
#global_conf.transport='serial:/dev/serial/by-id/usb-SEGGER_J-Link_001050076061-if02,1000000,rtscts' # transport for nrf53dk
|
||||
|
||||
#global_conf.transport='serial:/dev/serial/by-id/usb-SEGGER_J-Link_001057705357-if02,1000000,rtscts' # transport for nrf54l15dk
|
||||
|
||||
global_conf.transport='serial:/dev/serial/by-id/usb-ZEPHYR_Zephyr_HCI_UART_sample_81BD14B8D71B5662-if00,115200,rtscts' #nrf52dongle hci_uart usb cdc
|
||||
|
||||
config.transport='serial:/dev/serial/by-id/usb-ZEPHYR_Zephyr_HCI_UART_sample_95A087EADB030B24-if00,115200,rtscts' #nrf52dongle hci_uart usb cdc
|
||||
# global_conf.transport='usb:2fe3:000b' #nrf52dongle hci_usb # TODO: iso packet over usb not supported
|
||||
|
||||
|
||||
# TODO: How can we use other iso interval than 10ms ?(medium or low rel) ? - nrf53audio receiver repports I2S tx underrun
|
||||
|
||||
bigs = [
|
||||
auracast_config.AuracastBigConfigDe(),
|
||||
auracast_config.AuracastBigConfigEn(),
|
||||
auracast_config.AuracastBigConfigFr(),
|
||||
#auracast_config.AuracastBigConfigEs(),
|
||||
#auracast_config.AuracastBigConfigIt(),
|
||||
]
|
||||
for big in bigs: # TODO: encrypted streams are not working
|
||||
for big in config.bigs: # TODO: encrypted streams are not working
|
||||
#big.code = 'ff'*16 # returns hci/HCI_ENCRYPTION_MODE_NOT_ACCEPTABLE_ERROR
|
||||
#big.code = '78 e5 dc f1 34 ab 42 bf c1 92 ef dd 3a fd 67 ae'
|
||||
big.precode_wav = True
|
||||
@@ -539,14 +535,15 @@ if __name__ == "__main__":
|
||||
# TODO: find the bottleneck - probably airtime
|
||||
# TODO: test encrypted streams
|
||||
|
||||
global_conf.auracast_sampling_rate_hz = 16000
|
||||
global_conf.octets_per_frame = 40 # 32kbps@16kHz
|
||||
#global_conf.debug = True
|
||||
config.auracast_sampling_rate_hz = 16000
|
||||
config.octets_per_frame = 40 # 32kbps@16kHz
|
||||
#config.debug = True
|
||||
|
||||
|
||||
run_async(
|
||||
broadcast(
|
||||
global_conf,
|
||||
bigs
|
||||
config,
|
||||
config.bigs
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -1,36 +1,87 @@
|
||||
import requests
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import base64
|
||||
|
||||
from typing import Optional, Dict
|
||||
from auracast import auracast_config
|
||||
from auracast.utils.read_lc3_file import read_lc3_file
|
||||
from auracast.auracast_config import AuracastConfigGroup
|
||||
|
||||
BASE_URL = "http://127.0.0.1:5000" # Adjust based on your actual API URL
|
||||
BASE_URL = "http://127.0.0.1:5000" # Default base URL
|
||||
|
||||
def initialize():
|
||||
response = requests.post(f"{BASE_URL}/init")
|
||||
return response.json()
|
||||
|
||||
def shutdown():
|
||||
response = requests.post(f"{BASE_URL}/shutdown")
|
||||
return response.json()
|
||||
async def init(request_data : AuracastConfigGroup, base_url: Optional[str] = None):
|
||||
url = base_url if base_url is not None else BASE_URL
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(f"{url}/init", json=request_data.model_dump()) as response:
|
||||
if response.status != 200:
|
||||
raise Exception(f"Error: {response.status}, {await response.text()}")
|
||||
return await response.json()
|
||||
|
||||
def stop_audio():
|
||||
response = requests.post(f"{BASE_URL}/stop_audio")
|
||||
return response.json()
|
||||
|
||||
def send_audio(data_dict):
|
||||
response = requests.post(f"{BASE_URL}/stream_lc3", json=data_dict)
|
||||
return response.json()
|
||||
async def send_audio(data_dict: Dict[str, str], base_url: Optional[str] = None):
|
||||
# TODO: use base64 encoding
|
||||
for language, lc3_data in data_dict.items():
|
||||
data_dict[language] =lc3_data.decode('latin-1')
|
||||
|
||||
url = base_url if base_url is not None else BASE_URL
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(f"{url}/stream_lc3", json=data_dict) as response:
|
||||
if response.status != 200:
|
||||
raise Exception(f"Error: {response.status}, {await response.text()}")
|
||||
return await response.json()
|
||||
|
||||
|
||||
async def shutdown(base_url: Optional[str] = None):
|
||||
url = base_url if base_url is not None else BASE_URL
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(f"{url}/shutdown") as response:
|
||||
if response.status != 200:
|
||||
raise Exception(f"Error: {response.status}, {await response.text()}")
|
||||
return await response.json()
|
||||
|
||||
|
||||
async def stop_audio(base_url: Optional[str] = None):
|
||||
url = base_url if base_url is not None else BASE_URL
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(f"{url}/stop_audio") as response:
|
||||
if response.status != 200:
|
||||
raise Exception(f"Error: {response.status}, {await response.text()}")
|
||||
return await response.json()
|
||||
|
||||
|
||||
async def get_status(base_url: Optional[str] = None):
|
||||
url = base_url if base_url is not None else BASE_URL
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(f"{url}/status") as response:
|
||||
if response.status != 200:
|
||||
raise Exception(f"Error: {response.status}, {await response.text()}")
|
||||
return await response.json()
|
||||
|
||||
def get_status():
|
||||
response = requests.get(f"{BASE_URL}/status")
|
||||
return response.json()
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_audio_data = { # TODO: investigate further whats the best way to actually transfer the data
|
||||
"broadcast_deu": read_lc3_file('src/auracast/testdata/announcement_de_10_16_32.lc3').decode('latin-1'),
|
||||
"broadcast_eng": read_lc3_file('src/auracast/testdata/announcement_en_10_16_32.lc3').decode('latin-1')
|
||||
}
|
||||
import asyncio
|
||||
|
||||
print("Getting status:", get_status())
|
||||
print("Initializing server:", initialize())
|
||||
print("Getting status:", get_status())
|
||||
print("Sending audio:", send_audio())
|
||||
print("Getting status:", get_status())
|
||||
async def main():
|
||||
config = AuracastConfigGroup(
|
||||
auracast_config.AuracastBigConfigDeu()
|
||||
)
|
||||
config.transport = 'serial:/dev/serial/by-id/usb-ZEPHYR_Zephyr_HCI_UART_sample_81BD14B8D71B5662-if00,115200,rtscts'
|
||||
|
||||
# Initialize language-based configurations
|
||||
for conf in config.bigs:
|
||||
conf.loop = False
|
||||
|
||||
audio_data = { # TODO: investigate further whats the best way to actually transfer the data
|
||||
"deu": read_lc3_file('src/auracast/testdata/announcement_de_10_16_32.lc3').decode('latin-1'),
|
||||
#"eng": read_lc3_file('src/auracast/testdata/announcement_en_10_16_32.lc3').decode('latin-1'),
|
||||
#"fra": read_lc3_file('src/auracast/testdata/announcement_fr_10_16_32.lc3').decode('latin-1'),
|
||||
}
|
||||
|
||||
print("Getting status:", await get_status())
|
||||
print("Initializing server:", await init(config))
|
||||
print("Getting status:", await get_status())
|
||||
print("Sending audio:", await send_audio(audio_data))
|
||||
print("Getting status:", await get_status())
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -128,9 +128,9 @@ async def main():
|
||||
global_conf.transport='serial:/dev/serial/by-id/usb-ZEPHYR_Zephyr_HCI_UART_sample_81BD14B8D71B5662-if00,115200,rtscts' #nrf52dongle hci_uart usb cdc
|
||||
|
||||
big_conf = [
|
||||
auracast_config.AuracastBigConfigDe(),
|
||||
auracast_config.AuracastBigConfigEn(),
|
||||
auracast_config.AuracastBigConfigFr(),
|
||||
auracast_config.AuracastBigConfigDeu(),
|
||||
auracast_config.AuracastBigConfigEng(),
|
||||
auracast_config.AuracastBigConfigFra(),
|
||||
#auracast_config.broadcast_es,
|
||||
#auracast_config.broadcast_it,
|
||||
]
|
||||
|
||||
@@ -1,103 +1,104 @@
|
||||
import glob
|
||||
import logging as log
|
||||
from dataclasses import asdict
|
||||
from quart import Quart, request, jsonify
|
||||
from auracast import multicast_control
|
||||
from auracast import auracast_config
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from auracast import multicast_control, auracast_config
|
||||
|
||||
app = Quart(__name__)
|
||||
app = FastAPI()
|
||||
|
||||
# Initialize global configuration
|
||||
global_config_group = auracast_config.AuracastConfigGroup()
|
||||
|
||||
# Create multicast controller
|
||||
multicaster: multicast_control.Multicaster | None = None
|
||||
|
||||
|
||||
# TODO: redo this with fastapi, transfer whole radio config on init
|
||||
|
||||
# Initialize the multicaster instance globally
|
||||
global_conf = auracast_config.AuracastGlobalConfig()
|
||||
|
||||
#global_conf.transport='serial:/dev/serial/by-id/usb-SEGGER_J-Link_001057705357-if02,1000000,rtscts' # transport for nrf54l15dk
|
||||
global_conf.transport='serial:/dev/serial/by-id/usb-ZEPHYR_Zephyr_HCI_UART_sample_81BD14B8D71B5662-if00,115200,rtscts' #nrf52dongle hci_uart usb cdc
|
||||
|
||||
big_conf = { # TODO: use another dataclass for this to be able to iterate over the names
|
||||
'deu': auracast_config.AuracastBigConfigDe(),
|
||||
'eng': auracast_config.AuracastBigConfigEn(),
|
||||
'fra': auracast_config.AuracastBigConfigFr(),
|
||||
#auracast_config.broadcast_es,
|
||||
#auracast_config.broadcast_it,
|
||||
}
|
||||
for conf in big_conf.values():
|
||||
conf.loop = False
|
||||
|
||||
multicaster = multicast_control.Multicaster(
|
||||
global_conf,
|
||||
list(big_conf.values()),
|
||||
)
|
||||
|
||||
@app.route('/init', methods=['POST'])
|
||||
async def initialize():
|
||||
@app.post("/init")
|
||||
async def initialize(conf: auracast_config.AuracastConfigGroup):
|
||||
"""Initializes the broadcasters."""
|
||||
#data = await request.json
|
||||
#global_conf = auracast_config.AuracastGlobalConfig.from_dict(data['global_config'])
|
||||
#stream_configs = [auracast_config.AuracastBigConfig.from_dict(big) for big in data['big_configs']]
|
||||
global global_config_group
|
||||
global multicaster
|
||||
try:
|
||||
await multicaster.init_broadcast()
|
||||
return jsonify({"status": "initialized"}), 200
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
if conf.transport == 'auto':
|
||||
serial_devices = glob.glob('/dev/serial/by-id/*')
|
||||
log.info('Found serial devices: %s', serial_devices)
|
||||
for device in serial_devices:
|
||||
if 'usb-ZEPHYR_Zephyr_HCI_UART_sample' in device:
|
||||
log.info('Using: %s', device)
|
||||
conf.transport = f'serial:{device},115200,rtscts'
|
||||
break
|
||||
|
||||
@app.route('/shutdown', methods=['POST'])
|
||||
async def stop():
|
||||
# check again if transport is still auto
|
||||
if conf.transport == 'auto':
|
||||
HTTPException(status_code=500, detail='No suitable transport found.')
|
||||
|
||||
# initialize the streams dict
|
||||
global_config_group = conf
|
||||
log.info(
|
||||
'Initializing multicaster with config:\n %s', conf.model_dump_json(indent=2)
|
||||
)
|
||||
multicaster = multicast_control.Multicaster(
|
||||
conf,
|
||||
conf.bigs,
|
||||
)
|
||||
await multicaster.init_broadcast()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/stream_lc3")
|
||||
async def send_audio(audio_data: dict[str, str]):
|
||||
"""Streams pre-coded LC3 audio."""
|
||||
if multicaster is None:
|
||||
raise HTTPException(status_code=500, detail='Auracast endpoint was never intialized')
|
||||
try:
|
||||
for big in global_config_group.bigs:
|
||||
assert big.language in audio_data, HTTPException(status_code=500, detail='language len missmatch')
|
||||
log.info('Received a send audio request for %s', big.language)
|
||||
big.audio_source = audio_data[big.language].encode('latin-1') # TODO: use base64 encoding
|
||||
|
||||
multicaster.big_conf = global_config_group.bigs
|
||||
multicaster.start_streaming()
|
||||
return {"status": "audio_sent"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/shutdown")
|
||||
async def shutdown():
|
||||
"""Stops broadcasting."""
|
||||
try:
|
||||
await multicaster.reset()
|
||||
return jsonify({"status": "stopped"}), 200
|
||||
return {"status": "stopped"}
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route('/stop_audio', methods=['POST'])
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/stop_audio")
|
||||
async def stop_audio():
|
||||
"""Stops streaming."""
|
||||
try:
|
||||
multicaster.stop_streaming()
|
||||
return jsonify({"status": "stopped"}), 200
|
||||
return {"status": "stopped"}
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route('/stream_lc3', methods=['POST'])
|
||||
async def send_audio():
|
||||
"""Streams pre-coded LC3 audio.
|
||||
# post data in the format
|
||||
{
|
||||
broadcast_deu: b''
|
||||
broadcast_fra: b''
|
||||
|
||||
}
|
||||
"""
|
||||
post_data = await request.json
|
||||
try:
|
||||
for key, val in big_conf.items(): #TODO: loop over caster.big_conf directly
|
||||
if key in post_data:
|
||||
log.info('Received a send audio request for %s', key)
|
||||
val.audio_source = post_data[key].encode('latin-1')
|
||||
else:
|
||||
val.audio_source = b''
|
||||
|
||||
multicaster.big_conf = list(big_conf.values())
|
||||
multicaster.start_streaming()
|
||||
return jsonify({"status": "audio_sent"}), 200
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
# TODO: Also a queue should be implemented - probably as its own endpoint,
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.route('/status', methods=['GET'])
|
||||
@app.get("/status")
|
||||
async def get_status():
|
||||
"""Gets the current status of the multicaster."""
|
||||
status = multicaster.get_status()
|
||||
# TODO: also get queue status, announcements, samples etc.
|
||||
return jsonify({"status": status}), 200
|
||||
if multicaster:
|
||||
return multicaster.get_status()
|
||||
else:
|
||||
return {
|
||||
'is_initialized': False,
|
||||
'is_streaming': False,
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import uvicorn
|
||||
log.basicConfig(
|
||||
level=log.INFO,
|
||||
format='%(module)s.py:%(lineno)d %(levelname)s: %(message)s'
|
||||
)
|
||||
app.run(host='0.0.0.0', port=5000, debug=True)
|
||||
uvicorn.run(app, host="0.0.0.0", port=5000)
|
||||
Reference in New Issue
Block a user