feat(playout): build liquidsoap entrypoint with stream config
This commit is contained in:
parent
d9920a1196
commit
b9368d1b7b
|
@ -1,6 +1,11 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from libretime_shared.config import BaseConfig, GeneralConfig, RabbitMQConfig
|
from libretime_shared.config import (
|
||||||
|
BaseConfig,
|
||||||
|
GeneralConfig,
|
||||||
|
RabbitMQConfig,
|
||||||
|
StreamConfig,
|
||||||
|
)
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from typing_extensions import Literal
|
from typing_extensions import Literal
|
||||||
|
|
||||||
|
@ -26,3 +31,4 @@ class Config(BaseConfig):
|
||||||
general: GeneralConfig
|
general: GeneralConfig
|
||||||
rabbitmq: RabbitMQConfig = RabbitMQConfig()
|
rabbitmq: RabbitMQConfig = RabbitMQConfig()
|
||||||
playout: PlayoutConfig = PlayoutConfig()
|
playout: PlayoutConfig = PlayoutConfig()
|
||||||
|
stream: StreamConfig = StreamConfig()
|
||||||
|
|
|
@ -1,28 +1,11 @@
|
||||||
%include "/etc/libretime/liquidsoap.cfg"
|
time = ref string_of(gettimeofday())
|
||||||
|
|
||||||
if (log_file != "") then
|
|
||||||
set("log.file.path", log_file)
|
|
||||||
else
|
|
||||||
set("log.file", false)
|
|
||||||
end
|
|
||||||
|
|
||||||
set("server.telnet", true)
|
|
||||||
set("server.telnet.port", 1234)
|
|
||||||
|
|
||||||
#Dynamic source list
|
#Dynamic source list
|
||||||
#dyn_sources = ref []
|
#dyn_sources = ref []
|
||||||
webstream_enabled = ref false
|
webstream_enabled = ref false
|
||||||
|
|
||||||
time = ref string_of(gettimeofday())
|
|
||||||
|
|
||||||
#live stream setup
|
|
||||||
set("harbor.bind_addr", "0.0.0.0")
|
|
||||||
|
|
||||||
current_dyn_id = ref '-1'
|
current_dyn_id = ref '-1'
|
||||||
|
|
||||||
stream_metadata_type = ref 0
|
|
||||||
default_dj_fade = ref 0.
|
|
||||||
station_name = ref ''
|
|
||||||
show_name = ref ''
|
show_name = ref ''
|
||||||
|
|
||||||
dynamic_metadata_callback = ref fun (s) -> begin () end
|
dynamic_metadata_callback = ref fun (s) -> begin () end
|
||||||
|
|
|
@ -1,28 +1,11 @@
|
||||||
%include "/etc/libretime/liquidsoap.cfg"
|
time = ref string_of(gettimeofday())
|
||||||
|
|
||||||
if (log_file != "") then
|
|
||||||
set("log.file.path", log_file)
|
|
||||||
else
|
|
||||||
set("log.file", false)
|
|
||||||
end
|
|
||||||
|
|
||||||
set("server.telnet", true)
|
|
||||||
set("server.telnet.port", 1234)
|
|
||||||
|
|
||||||
#Dynamic source list
|
#Dynamic source list
|
||||||
#dyn_sources = ref []
|
#dyn_sources = ref []
|
||||||
webstream_enabled = ref false
|
webstream_enabled = ref false
|
||||||
|
|
||||||
time = ref string_of(gettimeofday())
|
|
||||||
|
|
||||||
#live stream setup
|
|
||||||
set("harbor.bind_addrs", ["0.0.0.0"])
|
|
||||||
|
|
||||||
current_dyn_id = ref '-1'
|
current_dyn_id = ref '-1'
|
||||||
|
|
||||||
stream_metadata_type = ref 0
|
|
||||||
default_dj_fade = ref 0.
|
|
||||||
station_name = ref ''
|
|
||||||
show_name = ref ''
|
show_name = ref ''
|
||||||
|
|
||||||
dynamic_metadata_callback = ref fun (s) -> begin () end
|
dynamic_metadata_callback = ref fun (s) -> begin () end
|
||||||
|
|
|
@ -1,28 +1,11 @@
|
||||||
%include "/etc/libretime/liquidsoap.cfg"
|
time = ref string_of(gettimeofday())
|
||||||
|
|
||||||
if (log_file != "") then
|
|
||||||
set("log.file.path", log_file)
|
|
||||||
else
|
|
||||||
set("log.file", false)
|
|
||||||
end
|
|
||||||
|
|
||||||
set("server.telnet", true)
|
|
||||||
set("server.telnet.port", 1234)
|
|
||||||
|
|
||||||
#Dynamic source list
|
#Dynamic source list
|
||||||
#dyn_sources = ref []
|
#dyn_sources = ref []
|
||||||
webstream_enabled = ref false
|
webstream_enabled = ref false
|
||||||
|
|
||||||
time = ref string_of(gettimeofday())
|
|
||||||
|
|
||||||
#live stream setup
|
|
||||||
set("harbor.bind_addrs", ["0.0.0.0"])
|
|
||||||
|
|
||||||
current_dyn_id = ref '-1'
|
current_dyn_id = ref '-1'
|
||||||
|
|
||||||
stream_metadata_type = ref 0
|
|
||||||
default_dj_fade = ref 0.
|
|
||||||
station_name = ref ''
|
|
||||||
show_name = ref ''
|
show_name = ref ''
|
||||||
|
|
||||||
dynamic_metadata_callback = ref fun (~new_track=false, s) -> begin () end
|
dynamic_metadata_callback = ref fun (~new_track=false, s) -> begin () end
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
# THIS FILE IS AUTO GENERATED. PLEASE DO NOT EDIT!
|
||||||
|
###########################################################
|
||||||
|
# The ignore() lines are to squash unused variable warnings
|
||||||
|
|
||||||
|
# Inputs
|
||||||
|
master_live_stream_mp = "{{ config.stream.inputs.main.mount }}"
|
||||||
|
master_live_stream_port = {{ config.stream.inputs.main.port }}
|
||||||
|
dj_live_stream_mp = "{{ config.stream.inputs.show.mount }}"
|
||||||
|
dj_live_stream_port = {{ config.stream.inputs.show.port }}
|
||||||
|
|
||||||
|
{% for output in config.stream.outputs.merged -%}
|
||||||
|
# Output s{{ loop.index }}
|
||||||
|
s{{ loop.index }}_enable = {{ output.enabled | lower }}
|
||||||
|
s{{ loop.index }}_output = "{{ output.kind }}"
|
||||||
|
s{{ loop.index }}_host = "{{ output.host }}"
|
||||||
|
s{{ loop.index }}_port = {{ output.port }}
|
||||||
|
s{{ loop.index }}_mount = "{{ output.mount }}"
|
||||||
|
s{{ loop.index }}_user = "{{ output.source_user }}"
|
||||||
|
s{{ loop.index }}_pass = "{{ output.source_password }}"
|
||||||
|
|
||||||
|
s{{ loop.index }}_channels = "{{ output.audio.channels.value }}"
|
||||||
|
s{{ loop.index }}_type = "{{ output.audio.format.value }}"
|
||||||
|
s{{ loop.index }}_bitrate = {{ output.audio.bitrate }}
|
||||||
|
|
||||||
|
s{{ loop.index }}_name = "{{ output.name or '' }}"
|
||||||
|
s{{ loop.index }}_description = "{{ output.description or '' }}"
|
||||||
|
s{{ loop.index }}_genre = "{{ output.genre or '' }}"
|
||||||
|
s{{ loop.index }}_url = "{{ output.website or '' }}"
|
||||||
|
|
||||||
|
{% endfor -%}
|
||||||
|
icecast_vorbis_metadata = {{ icecast_vorbis_metadata | lower }}
|
||||||
|
|
||||||
|
# System output
|
||||||
|
output_sound_device = {{ config.stream.outputs.system[0].enabled | lower }}
|
||||||
|
output_sound_device_type = "{{ config.stream.outputs.system[0].kind.value }}"
|
||||||
|
|
||||||
|
# Settings
|
||||||
|
auth_path = "{{ paths.auth_filepath }}"
|
||||||
|
|
||||||
|
{% if paths.log_filepath is defined -%}
|
||||||
|
set("log.file.path", "{{ paths.log_filepath }}")
|
||||||
|
{%- else -%}
|
||||||
|
set("log.file", false)
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
set("server.telnet", true)
|
||||||
|
set("server.telnet.port", 1234)
|
||||||
|
|
||||||
|
{% if version >= (1, 3, 3) -%}
|
||||||
|
set("harbor.bind_addrs", ["0.0.0.0"])
|
||||||
|
{%- else -%}
|
||||||
|
set("harbor.bind_addr", "0.0.0.0")
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
station_name = ref "{{ info.station_name }}"
|
||||||
|
|
||||||
|
off_air_meta = "{{ preferences.message_offline }}"
|
||||||
|
stream_metadata_type = ref {{ preferences.message_format.value }}
|
||||||
|
default_dj_fade = ref {{ preferences.input_fade_transition }}
|
||||||
|
|
||||||
|
%include "{{ paths.lib_filepath }}"
|
|
@ -1,64 +1,70 @@
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
from libretime_api_client.v1 import ApiClient as LegacyClient
|
from jinja2 import Template
|
||||||
from loguru import logger
|
from libretime_shared.config import AudioFormat, IcecastOutput, SystemOutput
|
||||||
|
|
||||||
|
from ..config import Config
|
||||||
|
from .models import Info, StreamPreferences
|
||||||
|
|
||||||
|
here = Path(__file__).parent
|
||||||
|
|
||||||
|
entrypoint_template_path = here / "entrypoint.liq.j2"
|
||||||
|
entrypoint_template = Template(
|
||||||
|
entrypoint_template_path.read_text(encoding="utf-8"),
|
||||||
|
keep_trailing_newline=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Liquidsoap has 4 hardcoded output stream set of variables, so we need to
|
||||||
|
# fill the missing stream outputs with placeholders so Liquidsoap does
|
||||||
|
# not fail with missing variables in the entrypoint.
|
||||||
|
_icecast_placeholder = IcecastOutput(
|
||||||
|
enabled=False,
|
||||||
|
mount="",
|
||||||
|
source_password="",
|
||||||
|
audio=dict(format="ogg", bitrate=256),
|
||||||
|
)
|
||||||
|
|
||||||
|
_system_placeholder = SystemOutput()
|
||||||
|
|
||||||
|
|
||||||
def generate_liquidsoap_config(ss, log_filepath: Optional[Path]):
|
def generate_entrypoint(
|
||||||
data = ss["msg"]
|
entrypoint_filepath: Path,
|
||||||
fh = open("/etc/libretime/liquidsoap.cfg", "w")
|
log_filepath: Optional[Path],
|
||||||
fh.write("################################################\n")
|
config: Config,
|
||||||
fh.write("# THIS FILE IS AUTO GENERATED. DO NOT CHANGE!! #\n")
|
preferences: StreamPreferences,
|
||||||
fh.write("################################################\n")
|
info: Info,
|
||||||
fh.write("# The ignore() lines are to squash unused variable warnings\n")
|
version: Tuple[int, int, int],
|
||||||
|
):
|
||||||
|
paths = {}
|
||||||
|
paths["auth_filepath"] = here / "liquidsoap_auth.py"
|
||||||
|
paths["lib_filepath"] = here / f"{version[0]}.{version[1]}/ls_script.liq"
|
||||||
|
|
||||||
for key, value in data.items():
|
if log_filepath is not None:
|
||||||
try:
|
paths["log_filepath"] = log_filepath.resolve()
|
||||||
if not "port" in key and not "bitrate" in key: # Stupid hack
|
|
||||||
raise ValueError()
|
|
||||||
str_buffer = f"{key} = {int(value)}\n"
|
|
||||||
except ValueError:
|
|
||||||
try: # Is it a boolean?
|
|
||||||
if value == "true" or value == "false":
|
|
||||||
str_buffer = f"{key} = {value.lower()}\n"
|
|
||||||
else:
|
|
||||||
raise ValueError() # Just drop into the except below
|
|
||||||
except: # Everything else is a string
|
|
||||||
str_buffer = f'{key} = "{value}"\n'
|
|
||||||
|
|
||||||
fh.write(str_buffer)
|
config = config.copy()
|
||||||
# ignore squashes unused variable errors from Liquidsoap
|
missing_outputs = [_icecast_placeholder] * (4 - len(config.stream.outputs.merged))
|
||||||
fh.write("ignore(%s)\n" % key)
|
config.stream.outputs.icecast.extend(missing_outputs)
|
||||||
|
|
||||||
auth_path = os.path.dirname(os.path.realpath(__file__))
|
if not config.stream.outputs.system:
|
||||||
log_file = log_filepath.resolve() if log_filepath is not None else ""
|
config.stream.outputs.system.append(_system_placeholder)
|
||||||
|
|
||||||
fh.write(f'log_file = "{log_file}"\n')
|
# Global icecast_vorbis_metadata until it is
|
||||||
fh.write('auth_path = "%s/liquidsoap_auth.py"\n' % auth_path)
|
# handled per output
|
||||||
fh.close()
|
icecast_vorbis_metadata = any(
|
||||||
|
o.enabled and o.audio.format == AudioFormat.OGG and o.audio.enable_metadata # type: ignore
|
||||||
|
for o in config.stream.outputs.icecast
|
||||||
|
)
|
||||||
|
|
||||||
|
entrypoint_filepath.write_text(
|
||||||
def generate_entrypoint(log_filepath: Optional[Path]):
|
entrypoint_template.render(
|
||||||
attempts = 0
|
config=config,
|
||||||
max_attempts = 10
|
preferences=preferences,
|
||||||
successful = False
|
info=info,
|
||||||
|
paths=paths,
|
||||||
while not successful:
|
version=version,
|
||||||
try:
|
icecast_vorbis_metadata=icecast_vorbis_metadata,
|
||||||
legacy_client = LegacyClient(logger)
|
),
|
||||||
ss = legacy_client.get_stream_setting()
|
encoding="utf-8",
|
||||||
generate_liquidsoap_config(ss, log_filepath)
|
)
|
||||||
successful = True
|
|
||||||
except Exception:
|
|
||||||
logger.exception("Unable to connect to the Airtime server")
|
|
||||||
if attempts == max_attempts:
|
|
||||||
logger.error("giving up and exiting...")
|
|
||||||
sys.exit(1)
|
|
||||||
else:
|
|
||||||
logger.info("Retrying in 3 seconds...")
|
|
||||||
time.sleep(3)
|
|
||||||
attempts += 1
|
|
||||||
|
|
|
@ -3,12 +3,15 @@ from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import click
|
import click
|
||||||
from libretime_shared.cli import cli_logging_options
|
from libretime_api_client.v2 import ApiClient
|
||||||
|
from libretime_shared.cli import cli_config_options, cli_logging_options
|
||||||
from libretime_shared.config import DEFAULT_ENV_PREFIX
|
from libretime_shared.config import DEFAULT_ENV_PREFIX
|
||||||
from libretime_shared.logging import level_from_name, setup_logger
|
from libretime_shared.logging import level_from_name, setup_logger
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
from ..config import Config
|
||||||
from .entrypoint import generate_entrypoint
|
from .entrypoint import generate_entrypoint
|
||||||
|
from .models import Info, StreamPreferences
|
||||||
from .version import get_liquidsoap_version
|
from .version import get_liquidsoap_version
|
||||||
|
|
||||||
here = Path(__file__).parent
|
here = Path(__file__).parent
|
||||||
|
@ -16,25 +19,42 @@ here = Path(__file__).parent
|
||||||
|
|
||||||
@click.command(context_settings={"auto_envvar_prefix": DEFAULT_ENV_PREFIX})
|
@click.command(context_settings={"auto_envvar_prefix": DEFAULT_ENV_PREFIX})
|
||||||
@cli_logging_options()
|
@cli_logging_options()
|
||||||
def cli(log_level: str, log_filepath: Optional[Path]):
|
@cli_config_options()
|
||||||
|
def cli(log_level: str, log_filepath: Optional[Path], config_filepath: Optional[Path]):
|
||||||
"""
|
"""
|
||||||
Run liquidsoap.
|
Run liquidsoap.
|
||||||
"""
|
"""
|
||||||
logger_level, _ = setup_logger(level_from_name(log_level), log_filepath)
|
logger_level, _ = setup_logger(level_from_name(log_level), log_filepath)
|
||||||
|
config = Config(config_filepath)
|
||||||
|
|
||||||
generate_entrypoint(log_filepath)
|
api_client = ApiClient(
|
||||||
|
base_url=config.general.public_url,
|
||||||
|
api_key=config.general.api_key,
|
||||||
|
)
|
||||||
|
|
||||||
version = get_liquidsoap_version()
|
version = get_liquidsoap_version()
|
||||||
|
|
||||||
script_path = here / f"{version[0]}.{version[1]}/ls_script.liq"
|
info = Info(**api_client.get_info().json())
|
||||||
|
preferences = StreamPreferences(**api_client.get_stream_preferences().json())
|
||||||
|
|
||||||
|
entrypoint_filepath = Path.cwd() / "radio.liq"
|
||||||
|
generate_entrypoint(
|
||||||
|
entrypoint_filepath,
|
||||||
|
log_filepath,
|
||||||
|
config,
|
||||||
|
preferences,
|
||||||
|
info,
|
||||||
|
version,
|
||||||
|
)
|
||||||
|
|
||||||
exec_args = [
|
exec_args = [
|
||||||
"/usr/bin/liquidsoap",
|
"/usr/bin/liquidsoap",
|
||||||
"libretime-liquidsoap",
|
"libretime-liquidsoap",
|
||||||
"--verbose",
|
"--verbose",
|
||||||
str(script_path),
|
str(entrypoint_filepath),
|
||||||
]
|
]
|
||||||
if logger_level.is_debug():
|
if logger_level.is_debug():
|
||||||
exec_args.append("--debug")
|
exec_args.append("--debug")
|
||||||
|
|
||||||
logger.debug(f"Liquidsoap {version} using script: {script_path}")
|
logger.debug(f"liquidsoap {version} using script: {entrypoint_filepath}")
|
||||||
os.execl(*exec_args)
|
os.execl(*exec_args)
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class Info(BaseModel):
|
||||||
|
station_name: str
|
||||||
|
|
||||||
|
|
||||||
|
class MessageFormatKind(int, Enum):
|
||||||
|
ARTIST_TITLE = 0
|
||||||
|
SHOW_ARTIST_TITLE = 1
|
||||||
|
RADIO_SHOW = 2
|
||||||
|
|
||||||
|
|
||||||
|
class StreamPreferences(BaseModel):
|
||||||
|
input_fade_transition: float
|
||||||
|
message_format: MessageFormatKind
|
||||||
|
message_offline: str
|
||||||
|
|
||||||
|
|
||||||
|
class StreamState(BaseModel):
|
||||||
|
input_main_connected: bool
|
||||||
|
input_main_streaming: bool
|
||||||
|
input_show_connected: bool
|
||||||
|
input_show_streaming: bool
|
||||||
|
schedule_streaming: bool
|
|
@ -2,6 +2,7 @@
|
||||||
# This file is auto-generated by tools/extract_requirements.py.
|
# This file is auto-generated by tools/extract_requirements.py.
|
||||||
backports.zoneinfo>=0.2.1,<0.3;python_version<'3.9'
|
backports.zoneinfo>=0.2.1,<0.3;python_version<'3.9'
|
||||||
dataclasses>=0.8,<0.9;python_version<'3.7'
|
dataclasses>=0.8,<0.9;python_version<'3.7'
|
||||||
|
jinja2>=3.0.3,<3.2
|
||||||
kombu==4.6.11
|
kombu==4.6.11
|
||||||
lxml>=4.5.0,<4.10.0
|
lxml>=4.5.0,<4.10.0
|
||||||
mutagen>=1.45.1,<1.46
|
mutagen>=1.45.1,<1.46
|
||||||
|
|
|
@ -13,7 +13,7 @@ setup(
|
||||||
},
|
},
|
||||||
license="AGPLv3",
|
license="AGPLv3",
|
||||||
packages=find_packages(exclude=["*tests*", "*fixtures*"]),
|
packages=find_packages(exclude=["*tests*", "*fixtures*"]),
|
||||||
package_data={"": ["**/*.liq", "*.types"]},
|
package_data={"": ["**/*.liq", "**/*.liq.j2", "*.types"]},
|
||||||
entry_points={
|
entry_points={
|
||||||
"console_scripts": [
|
"console_scripts": [
|
||||||
"libretime-playout=libretime_playout.main:cli",
|
"libretime-playout=libretime_playout.main:cli",
|
||||||
|
@ -25,6 +25,7 @@ setup(
|
||||||
install_requires=[
|
install_requires=[
|
||||||
"backports.zoneinfo>=0.2.1,<0.3;python_version<'3.9'",
|
"backports.zoneinfo>=0.2.1,<0.3;python_version<'3.9'",
|
||||||
"dataclasses>=0.8,<0.9;python_version<'3.7'",
|
"dataclasses>=0.8,<0.9;python_version<'3.7'",
|
||||||
|
"jinja2>=3.0.3,<3.2",
|
||||||
"kombu==4.6.11",
|
"kombu==4.6.11",
|
||||||
"lxml>=4.5.0,<4.10.0",
|
"lxml>=4.5.0,<4.10.0",
|
||||||
"mutagen>=1.45.1,<1.46",
|
"mutagen>=1.45.1,<1.46",
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from libretime_playout.config import Config
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def config():
|
||||||
|
return Config(
|
||||||
|
**{
|
||||||
|
"general": {
|
||||||
|
"public_url": "http://localhost:8080",
|
||||||
|
"api_key": "some_api_key",
|
||||||
|
},
|
||||||
|
"stream": {
|
||||||
|
"outputs": {
|
||||||
|
"icecast": [
|
||||||
|
{
|
||||||
|
"enabled": True,
|
||||||
|
"mount": "main",
|
||||||
|
"source_password": "hackme",
|
||||||
|
"audio": {"format": "ogg", "bitrate": 256},
|
||||||
|
"name": "LibreTime!",
|
||||||
|
"description": "LibreTime Radio! Stream #1",
|
||||||
|
"website": "https://libretime.org",
|
||||||
|
"genre": "various",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enabled": True,
|
||||||
|
"mount": "second",
|
||||||
|
"source_password": "hackme",
|
||||||
|
"audio": {"format": "mp3", "bitrate": 256},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
|
@ -1,6 +1,11 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
fixture_path = Path(__file__).parent
|
fixtures_path = Path(__file__).parent
|
||||||
|
|
||||||
icecast_stats = fixture_path / "icecast_stats.xml"
|
icecast_stats = fixtures_path / "icecast_stats.xml"
|
||||||
shoutcast_admin = fixture_path / "shoutcast_admin.xml"
|
shoutcast_admin = fixtures_path / "shoutcast_admin.xml"
|
||||||
|
|
||||||
|
entrypoint_1_1 = fixtures_path / "entrypoint-1.1.liq"
|
||||||
|
entrypoint_1_1_snapshot = entrypoint_1_1.read_text(encoding="utf-8")
|
||||||
|
entrypoint_1_4 = fixtures_path / "entrypoint-1.4.liq"
|
||||||
|
entrypoint_1_4_snapshot = entrypoint_1_4.read_text(encoding="utf-8")
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
# THIS FILE IS AUTO GENERATED. PLEASE DO NOT EDIT!
|
||||||
|
###########################################################
|
||||||
|
# The ignore() lines are to squash unused variable warnings
|
||||||
|
|
||||||
|
# Inputs
|
||||||
|
master_live_stream_mp = "main"
|
||||||
|
master_live_stream_port = 8001
|
||||||
|
dj_live_stream_mp = "show"
|
||||||
|
dj_live_stream_port = 8002
|
||||||
|
|
||||||
|
# Output s1
|
||||||
|
s1_enable = true
|
||||||
|
s1_output = "icecast"
|
||||||
|
s1_host = "localhost"
|
||||||
|
s1_port = 8000
|
||||||
|
s1_mount = "main"
|
||||||
|
s1_user = "source"
|
||||||
|
s1_pass = "hackme"
|
||||||
|
|
||||||
|
s1_channels = "stereo"
|
||||||
|
s1_type = "ogg"
|
||||||
|
s1_bitrate = 256
|
||||||
|
|
||||||
|
s1_name = "LibreTime!"
|
||||||
|
s1_description = "LibreTime Radio! Stream #1"
|
||||||
|
s1_genre = "various"
|
||||||
|
s1_url = "https://libretime.org"
|
||||||
|
|
||||||
|
# Output s2
|
||||||
|
s2_enable = true
|
||||||
|
s2_output = "icecast"
|
||||||
|
s2_host = "localhost"
|
||||||
|
s2_port = 8000
|
||||||
|
s2_mount = "second"
|
||||||
|
s2_user = "source"
|
||||||
|
s2_pass = "hackme"
|
||||||
|
|
||||||
|
s2_channels = "stereo"
|
||||||
|
s2_type = "mp3"
|
||||||
|
s2_bitrate = 256
|
||||||
|
|
||||||
|
s2_name = ""
|
||||||
|
s2_description = ""
|
||||||
|
s2_genre = ""
|
||||||
|
s2_url = ""
|
||||||
|
|
||||||
|
# Output s3
|
||||||
|
s3_enable = false
|
||||||
|
s3_output = "icecast"
|
||||||
|
s3_host = "localhost"
|
||||||
|
s3_port = 8000
|
||||||
|
s3_mount = ""
|
||||||
|
s3_user = "source"
|
||||||
|
s3_pass = ""
|
||||||
|
|
||||||
|
s3_channels = "stereo"
|
||||||
|
s3_type = "ogg"
|
||||||
|
s3_bitrate = 256
|
||||||
|
|
||||||
|
s3_name = ""
|
||||||
|
s3_description = ""
|
||||||
|
s3_genre = ""
|
||||||
|
s3_url = ""
|
||||||
|
|
||||||
|
# Output s4
|
||||||
|
s4_enable = false
|
||||||
|
s4_output = "icecast"
|
||||||
|
s4_host = "localhost"
|
||||||
|
s4_port = 8000
|
||||||
|
s4_mount = ""
|
||||||
|
s4_user = "source"
|
||||||
|
s4_pass = ""
|
||||||
|
|
||||||
|
s4_channels = "stereo"
|
||||||
|
s4_type = "ogg"
|
||||||
|
s4_bitrate = 256
|
||||||
|
|
||||||
|
s4_name = ""
|
||||||
|
s4_description = ""
|
||||||
|
s4_genre = ""
|
||||||
|
s4_url = ""
|
||||||
|
|
||||||
|
icecast_vorbis_metadata = false
|
||||||
|
|
||||||
|
# System output
|
||||||
|
output_sound_device = false
|
||||||
|
output_sound_device_type = "alsa"
|
||||||
|
|
||||||
|
# Settings
|
||||||
|
auth_path = "/fake/liquidsoap_auth.py"
|
||||||
|
|
||||||
|
set("log.file.path", "/var/log/radio.log")
|
||||||
|
|
||||||
|
set("server.telnet", true)
|
||||||
|
set("server.telnet.port", 1234)
|
||||||
|
|
||||||
|
set("harbor.bind_addr", "0.0.0.0")
|
||||||
|
|
||||||
|
station_name = ref "LibreTime"
|
||||||
|
|
||||||
|
off_air_meta = "LibreTime - offline"
|
||||||
|
stream_metadata_type = ref 0
|
||||||
|
default_dj_fade = ref 0.0
|
||||||
|
|
||||||
|
%include "/fake/1.1/ls_script.liq"
|
|
@ -0,0 +1,105 @@
|
||||||
|
# THIS FILE IS AUTO GENERATED. PLEASE DO NOT EDIT!
|
||||||
|
###########################################################
|
||||||
|
# The ignore() lines are to squash unused variable warnings
|
||||||
|
|
||||||
|
# Inputs
|
||||||
|
master_live_stream_mp = "main"
|
||||||
|
master_live_stream_port = 8001
|
||||||
|
dj_live_stream_mp = "show"
|
||||||
|
dj_live_stream_port = 8002
|
||||||
|
|
||||||
|
# Output s1
|
||||||
|
s1_enable = true
|
||||||
|
s1_output = "icecast"
|
||||||
|
s1_host = "localhost"
|
||||||
|
s1_port = 8000
|
||||||
|
s1_mount = "main"
|
||||||
|
s1_user = "source"
|
||||||
|
s1_pass = "hackme"
|
||||||
|
|
||||||
|
s1_channels = "stereo"
|
||||||
|
s1_type = "ogg"
|
||||||
|
s1_bitrate = 256
|
||||||
|
|
||||||
|
s1_name = "LibreTime!"
|
||||||
|
s1_description = "LibreTime Radio! Stream #1"
|
||||||
|
s1_genre = "various"
|
||||||
|
s1_url = "https://libretime.org"
|
||||||
|
|
||||||
|
# Output s2
|
||||||
|
s2_enable = true
|
||||||
|
s2_output = "icecast"
|
||||||
|
s2_host = "localhost"
|
||||||
|
s2_port = 8000
|
||||||
|
s2_mount = "second"
|
||||||
|
s2_user = "source"
|
||||||
|
s2_pass = "hackme"
|
||||||
|
|
||||||
|
s2_channels = "stereo"
|
||||||
|
s2_type = "mp3"
|
||||||
|
s2_bitrate = 256
|
||||||
|
|
||||||
|
s2_name = ""
|
||||||
|
s2_description = ""
|
||||||
|
s2_genre = ""
|
||||||
|
s2_url = ""
|
||||||
|
|
||||||
|
# Output s3
|
||||||
|
s3_enable = false
|
||||||
|
s3_output = "icecast"
|
||||||
|
s3_host = "localhost"
|
||||||
|
s3_port = 8000
|
||||||
|
s3_mount = ""
|
||||||
|
s3_user = "source"
|
||||||
|
s3_pass = ""
|
||||||
|
|
||||||
|
s3_channels = "stereo"
|
||||||
|
s3_type = "ogg"
|
||||||
|
s3_bitrate = 256
|
||||||
|
|
||||||
|
s3_name = ""
|
||||||
|
s3_description = ""
|
||||||
|
s3_genre = ""
|
||||||
|
s3_url = ""
|
||||||
|
|
||||||
|
# Output s4
|
||||||
|
s4_enable = false
|
||||||
|
s4_output = "icecast"
|
||||||
|
s4_host = "localhost"
|
||||||
|
s4_port = 8000
|
||||||
|
s4_mount = ""
|
||||||
|
s4_user = "source"
|
||||||
|
s4_pass = ""
|
||||||
|
|
||||||
|
s4_channels = "stereo"
|
||||||
|
s4_type = "ogg"
|
||||||
|
s4_bitrate = 256
|
||||||
|
|
||||||
|
s4_name = ""
|
||||||
|
s4_description = ""
|
||||||
|
s4_genre = ""
|
||||||
|
s4_url = ""
|
||||||
|
|
||||||
|
icecast_vorbis_metadata = false
|
||||||
|
|
||||||
|
# System output
|
||||||
|
output_sound_device = false
|
||||||
|
output_sound_device_type = "alsa"
|
||||||
|
|
||||||
|
# Settings
|
||||||
|
auth_path = "/fake/liquidsoap_auth.py"
|
||||||
|
|
||||||
|
set("log.file.path", "/var/log/radio.log")
|
||||||
|
|
||||||
|
set("server.telnet", true)
|
||||||
|
set("server.telnet.port", 1234)
|
||||||
|
|
||||||
|
set("harbor.bind_addrs", ["0.0.0.0"])
|
||||||
|
|
||||||
|
station_name = ref "LibreTime"
|
||||||
|
|
||||||
|
off_air_meta = "LibreTime - offline"
|
||||||
|
stream_metadata_type = ref 0
|
||||||
|
default_dj_fade = ref 0.0
|
||||||
|
|
||||||
|
%include "/fake/1.4/ls_script.liq"
|
|
@ -0,0 +1,43 @@
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from libretime_playout.config import Config
|
||||||
|
from libretime_playout.liquidsoap.entrypoint import generate_entrypoint
|
||||||
|
from libretime_playout.liquidsoap.models import Info, StreamPreferences
|
||||||
|
|
||||||
|
from ..fixtures import entrypoint_1_1_snapshot, entrypoint_1_4_snapshot
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"version, expected",
|
||||||
|
[
|
||||||
|
pytest.param((1, 1, 1), entrypoint_1_1_snapshot, id="snapshot_1.1"),
|
||||||
|
pytest.param((1, 4, 4), entrypoint_1_4_snapshot, id="snapshot_1.4"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_generate_entrypoint(tmp_path: Path, config: Config, version, expected):
|
||||||
|
entrypoint_filepath = tmp_path / "radio.liq"
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"libretime_playout.liquidsoap.entrypoint.here",
|
||||||
|
Path("/fake"),
|
||||||
|
):
|
||||||
|
generate_entrypoint(
|
||||||
|
entrypoint_filepath,
|
||||||
|
log_filepath=Path("/var/log/radio.log"),
|
||||||
|
config=config,
|
||||||
|
preferences=StreamPreferences(
|
||||||
|
input_fade_transition=0.0,
|
||||||
|
message_format=0,
|
||||||
|
message_offline="LibreTime - offline",
|
||||||
|
),
|
||||||
|
info=Info(
|
||||||
|
station_name="LibreTime",
|
||||||
|
),
|
||||||
|
version=version,
|
||||||
|
)
|
||||||
|
|
||||||
|
found = entrypoint_filepath.read_text(encoding="utf-8")
|
||||||
|
assert found == expected
|
Loading…
Reference in New Issue