Generate new op25.liq each time we change the config + mini fixes
This commit is contained in:
34
app/internal/liquidsoap_config_utils.py
Normal file
34
app/internal/liquidsoap_config_utils.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import os
|
||||||
|
from internal.op25.liq_template import liquidsoap_config_template
|
||||||
|
from models import IcecastConfig
|
||||||
|
|
||||||
|
def generate_liquid_script(config: IcecastConfig):
|
||||||
|
"""
|
||||||
|
Generates the "*.liq" file that's run by OP25 on startup.
|
||||||
|
|
||||||
|
Placeholders in the template must be formatted as ${VARIABLE_NAME}.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config (dict): A dictionary of key-value pairs for substitution.
|
||||||
|
Keys should match the variable names in the template (e.g., 'icecast_host').
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
content = liquidsoap_config_template
|
||||||
|
# Replace variables
|
||||||
|
for key, value in config.items():
|
||||||
|
placeholder = f"${{{key}}}"
|
||||||
|
# Ensure the value is converted to string for replacement
|
||||||
|
content = content.replace(placeholder, str(value))
|
||||||
|
print(f" - Replaced placeholder {placeholder}")
|
||||||
|
|
||||||
|
# Write the processed content to the output path
|
||||||
|
output_path = "/op25/op25/gr-op25_repeater/apps/op25.liq"
|
||||||
|
with open(output_path, 'w') as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
print(f"\nSuccessfully wrote processed configuration to: {output_path}")
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"Error: Template file not found at {template_path}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"An unexpected error occurred: {e}")
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/liquidsoap
|
liquidsoap_config_template = """#!/usr/bin/liquidsoap
|
||||||
|
|
||||||
# Example liquidsoap streaming from op25 to icecast
|
# Example liquidsoap streaming from op25 to icecast
|
||||||
# (c) 2019-2021 gnorbury@bondcar.com, wllmbecks@gmail.com
|
# (c) 2019-2021 gnorbury@bondcar.com, wllmbecks@gmail.com
|
||||||
@@ -10,17 +10,15 @@ set("log.level", 1)
|
|||||||
|
|
||||||
# Make the native sample rate compatible with op25
|
# Make the native sample rate compatible with op25
|
||||||
set("frame.audio.samplerate", 8000)
|
set("frame.audio.samplerate", 8000)
|
||||||
|
set("init.allow_root", true)
|
||||||
|
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
# 📢 ICECAST CONFIGURATION USING ENVIRONMENT VARIABLES
|
ICE_HOST = "${icecast_host}"
|
||||||
# Use getenv("VAR_NAME", "default_value")
|
ICE_PORT = ${icecast_port}
|
||||||
# ==========================================================
|
ICE_MOUNT = "${icecast_mountpoint}"
|
||||||
ICE_HOST = getenv("ICE_HOST", "ic2.vpn.cusano.net")
|
ICE_PASSWORD = "${icecast_password}"
|
||||||
ICE_PORT = int_of_string(getenv("ICE_PORT", "8000"))
|
ICE_DESCRIPTION = "${icecast_description}"
|
||||||
ICE_MOUNT = getenv("ICE_MOUNT", "mountpoint")
|
ICE_GENRE = "${icecast_genre}"
|
||||||
ICE_PASSWORD = getenv("ICE_PASSWORD", "hackme")
|
|
||||||
ICE_DESCRIPTION = getenv("ICE_DESCRIPTION", "op25")
|
|
||||||
ICE_GENRE = getenv("ICE_GENRE", "Public Safety")
|
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
|
|
||||||
input = mksafe(input.external(buffer=0.25, channels=2, samplerate=8000, restart_on_error=false, "./audio.py -x 2.5 -s"))
|
input = mksafe(input.external(buffer=0.25, channels=2, samplerate=8000, restart_on_error=false, "./audio.py -x 2.5 -s"))
|
||||||
@@ -46,4 +44,5 @@ output.icecast(
|
|||||||
mount=ICE_MOUNT,
|
mount=ICE_MOUNT,
|
||||||
password=ICE_PASSWORD,
|
password=ICE_PASSWORD,
|
||||||
mean(input)
|
mean(input)
|
||||||
)
|
)
|
||||||
|
"""
|
||||||
@@ -17,7 +17,7 @@ def save_talkgroup_tags(talkgroup_tags: List[TalkgroupTag]) -> None:
|
|||||||
writer = csv.writer(file, delimiter='\t', lineterminator='\n')
|
writer = csv.writer(file, delimiter='\t', lineterminator='\n')
|
||||||
# Write rows
|
# Write rows
|
||||||
for tag in talkgroup_tags:
|
for tag in talkgroup_tags:
|
||||||
writer.writerow([tag.talkgroup, tag.tagDec])
|
writer.writerow([tag.tagDec, tag.talkgroup])
|
||||||
|
|
||||||
def save_whitelist(talkgroup_tags: List[int]) -> None:
|
def save_whitelist(talkgroup_tags: List[int]) -> None:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class ConfigGenerator(BaseModel):
|
|||||||
channels: List[Union[int, str]]
|
channels: List[Union[int, str]]
|
||||||
tags: Optional[List[TalkgroupTag]]
|
tags: Optional[List[TalkgroupTag]]
|
||||||
whitelist: Optional[List[int]]
|
whitelist: Optional[List[int]]
|
||||||
icecastConfig: Optional[MetadataStreamConfig]
|
icecastConfig: Optional[IcecastConfig]
|
||||||
|
|
||||||
class DemodType(str, Enum):
|
class DemodType(str, Enum):
|
||||||
CQPSK = "cqpsk"
|
CQPSK = "cqpsk"
|
||||||
@@ -97,3 +97,15 @@ class TerminalConfig(BaseModel):
|
|||||||
http_plot_directory: Optional[str] = "../www/images"
|
http_plot_directory: Optional[str] = "../www/images"
|
||||||
tuning_step_large: Optional[int] = 1200
|
tuning_step_large: Optional[int] = 1200
|
||||||
tuning_step_small: Optional[int] = 100
|
tuning_step_small: Optional[int] = 100
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### ======================================================
|
||||||
|
# Icecast models
|
||||||
|
class IcecastConfig:
|
||||||
|
icecast_host: str
|
||||||
|
icecast_port: int
|
||||||
|
icecast_mountpoint: str
|
||||||
|
icecast_password: str
|
||||||
|
icecast_description: Optional[str] = "OP25"
|
||||||
|
icecast_genre: Optional[str] = "Public Safety"
|
||||||
@@ -6,6 +6,7 @@ import json
|
|||||||
from models import ConfigGenerator, DecodeMode, ChannelConfig, DeviceConfig, TrunkingConfig, TrunkingChannelConfig, TerminalConfig, MetadataConfig, MetadataStreamConfig
|
from models import ConfigGenerator, DecodeMode, ChannelConfig, DeviceConfig, TrunkingConfig, TrunkingChannelConfig, TerminalConfig, MetadataConfig, MetadataStreamConfig
|
||||||
from internal.logger import create_logger
|
from internal.logger import create_logger
|
||||||
from internal.op25_config_utls import save_talkgroup_tags, save_whitelist, del_none_in_dict, get_current_system_from_config
|
from internal.op25_config_utls import save_talkgroup_tags, save_whitelist, del_none_in_dict, get_current_system_from_config
|
||||||
|
from internal.liquidsoap_config_utils import generate_liquid_script
|
||||||
|
|
||||||
LOGGER = create_logger(__name__)
|
LOGGER = create_logger(__name__)
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ def create_op25_router():
|
|||||||
demod_type="cqpsk",
|
demod_type="cqpsk",
|
||||||
cqpsk_tracking=True,
|
cqpsk_tracking=True,
|
||||||
filter_type="rc",
|
filter_type="rc",
|
||||||
meta_stream_name=generator.icecastConfig.stream_name
|
meta_stream_name="stream_0"
|
||||||
)]
|
)]
|
||||||
devices = [DeviceConfig()]
|
devices = [DeviceConfig()]
|
||||||
save_talkgroup_tags(generator.tags)
|
save_talkgroup_tags(generator.tags)
|
||||||
@@ -73,9 +74,20 @@ def create_op25_router():
|
|||||||
)
|
)
|
||||||
|
|
||||||
metadata = MetadataConfig(
|
metadata = MetadataConfig(
|
||||||
streams=[generator.icecastConfig]
|
streams=[
|
||||||
|
MetadataStreamConfig(
|
||||||
|
stream_name="stream_0",
|
||||||
|
icecastServerAddress = f"{generator.icecastConfig.icecast_host}:{generator.icecastConfig.icecast_port}",
|
||||||
|
icecastMountpoint = generator.icecastConfig.icecast_mountpoint,
|
||||||
|
icecastPass = generator.icecastConfig.icecast_password
|
||||||
|
)
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Generate the op25.liq file
|
||||||
|
generate_liquid_script(generator.icecastConfig)
|
||||||
|
|
||||||
|
|
||||||
terminal = TerminalConfig()
|
terminal = TerminalConfig()
|
||||||
|
|
||||||
config_dict = {
|
config_dict = {
|
||||||
|
|||||||
Reference in New Issue
Block a user