feat(playout): load config using shared helpers
- backport >=py38 Literal type - update configuration sample - add missing config filepath to systemd service - make config sections optional BREAKING CHANGE: the playout config schema has been changed like the following: - the top section 'pypo' was renamed to 'playout' - the 'playout.ls_*' entries were renamed to 'playout.liquidsoap_*' - the 'playout.*_dir' entries were removed - the 'playout.api_client' entry was removed - the 'playout.record_file_type' entry was renamed to 'playout.record_file_format' - the 'playout.base_recorded_files' entry was removed - the 'playout.poll_interval' entry was removed - the 'playout.push_interval' entry was removed - the 'playout.cue_style' entry was removed
This commit is contained in:
parent
12f96f5043
commit
fa7692071c
|
@ -9,7 +9,6 @@
|
||||||
#
|
#
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
# G E N E R A L S E T T I N G S
|
# G E N E R A L S E T T I N G S
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
@ -73,7 +72,6 @@ auth = local
|
||||||
#
|
#
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
# D A T A B A S E
|
# D A T A B A S E
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
@ -100,38 +98,21 @@ dbpass = airtime
|
||||||
#
|
#
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# RABBITMQ
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
# R A B B I T M Q
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# These settings are used to configure the RabbitMQ messaging
|
|
||||||
# configuration for your Airtime installation.
|
|
||||||
#
|
|
||||||
# host: The IP address for the RabbitMQ service.
|
|
||||||
# The default is 127.0.0.1.
|
|
||||||
#
|
|
||||||
# port: The port for the RabbitMQ service.
|
|
||||||
# The default is 5672.
|
|
||||||
#
|
|
||||||
# user: The username for the RabbitMQ user.
|
|
||||||
# The default is airtime.
|
|
||||||
#
|
|
||||||
# password: The password for the RabbitMQ user.
|
|
||||||
# The default is airtime.
|
|
||||||
#
|
|
||||||
# vhost: The virtual host for the RabbitMQ service database.
|
|
||||||
# The default is /airtime.
|
|
||||||
#
|
|
||||||
[rabbitmq]
|
[rabbitmq]
|
||||||
host = 127.0.0.1
|
# The host for the RabbitMQ service, default is localhost
|
||||||
|
host = localhost
|
||||||
|
# The port for the RabbitMQ service, default is 5672
|
||||||
port = 5672
|
port = 5672
|
||||||
user = airtime
|
# The virtual host for the RabbitMQ service, default is /libretime
|
||||||
password = airtime
|
|
||||||
vhost = /airtime
|
vhost = /airtime
|
||||||
#
|
# The user for the RabbitMQ user, default is libretime
|
||||||
# ----------------------------------------------------------------------
|
user = airtime
|
||||||
|
# The password for the RabbitMQ user, default is libretime
|
||||||
|
password = airtime
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
# S T O R A G E
|
# S T O R A G E
|
||||||
|
@ -140,7 +121,6 @@ vhost = /airtime
|
||||||
[current_backend]
|
[current_backend]
|
||||||
storage_backend=file
|
storage_backend=file
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
# M O N I T
|
# M O N I T
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
@ -157,115 +137,26 @@ password =
|
||||||
#
|
#
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
# P Y P O
|
# PLAYOUT
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
#
|
[playout]
|
||||||
# api_client: Set the type of client you are using.
|
# Liquidsoap connection host, default is localhost
|
||||||
# Currently supported types:
|
liquidsoap_host = localhost
|
||||||
# 1) 'obp' = Open Broadcast Platform
|
# Liquidsoap connection port, default is 1234
|
||||||
# 2) 'airtime'
|
liquidsoap_port = 1234
|
||||||
# The default is 'airtime'
|
|
||||||
#
|
# The format for recordings, allowed values ogg|mp3, default is ogg
|
||||||
# cache_dir: The directory for pypo cache files
|
record_file_format = ogg
|
||||||
# The default is '/var/tmp/airtime/pypo/cache/'
|
# The bitrate for recordings, default is 256
|
||||||
#
|
|
||||||
# file_dir: The directory for pypo media files
|
|
||||||
# The default is '/var/tmp/airtime/pypo/files/'
|
|
||||||
#
|
|
||||||
# tmp_dir: The directory for pypo temp files
|
|
||||||
# The default is '/var/tmp/airtime/pypo/tmp/'
|
|
||||||
#
|
|
||||||
# cache_base_dir: The pypo base cache directory
|
|
||||||
# The default is '/var/tmp/airtime/pypo/'
|
|
||||||
#
|
|
||||||
# log_base_dir: The base directory for Airtime log files
|
|
||||||
# The default is '/var/log/airtime'
|
|
||||||
#
|
|
||||||
# pypo_log_dir: The directory for pypo log files
|
|
||||||
# The default is '/var/log/airtime/pypo'
|
|
||||||
#
|
|
||||||
# liquidsoap_log_dir: The directory for liquidsoap log files
|
|
||||||
# The default is '/var/log/airtime/pypo-liquidsoap'
|
|
||||||
#
|
|
||||||
# ls_host: Liquidsoap connection host
|
|
||||||
# The default is '127.0.0.1'
|
|
||||||
#
|
|
||||||
# ls_port: Liquidsoap connection port
|
|
||||||
# The default is '1234'
|
|
||||||
#
|
|
||||||
# poll_interval: Poll interval in seconds
|
|
||||||
#
|
|
||||||
# This will rarely need to be changed because any schedule
|
|
||||||
# changes are automatically sent to pypo immediately
|
|
||||||
# This is how often the poll script downloads new schedules
|
|
||||||
# and files from the server in the event that no changes
|
|
||||||
# are made to the schedule
|
|
||||||
# The default is 3600
|
|
||||||
#
|
|
||||||
# push_interval: Push interval in seconds
|
|
||||||
#
|
|
||||||
# This is how often the push script checks whether it has
|
|
||||||
# something new to push to liquidsoap
|
|
||||||
# The default is 1
|
|
||||||
#
|
|
||||||
# cue_style: Can be set to 'pre' or 'otf'
|
|
||||||
# 'pre' cues while playlist preparation
|
|
||||||
# 'otf' (on the fly) cues while loading into ls
|
|
||||||
# (needs the post_processor patch)
|
|
||||||
# The default is 'pre'
|
|
||||||
#
|
|
||||||
# record_bitrate: The bitrate for recordings
|
|
||||||
# The default is 256
|
|
||||||
#
|
|
||||||
# record_samplerate: The samplerate for recordings
|
|
||||||
# The default is 44100
|
|
||||||
#
|
|
||||||
# record_channels: The number of channels for recordings
|
|
||||||
# The default is 2
|
|
||||||
#
|
|
||||||
# record_sample_size: The sample size for recordings
|
|
||||||
# The default is 16
|
|
||||||
#
|
|
||||||
# record_file_type: Can be either ogg|mp3, mp3 recording requires
|
|
||||||
# installation of the package "lame"
|
|
||||||
# The default is ogg
|
|
||||||
#
|
|
||||||
# base_recorded_files: Base path to store recordered shows at
|
|
||||||
# The default is '/var/tmp/airtime/show-recorder/'
|
|
||||||
#
|
|
||||||
[pypo]
|
|
||||||
api_client = 'airtime'
|
|
||||||
# ---------- Cache directories - !! Include trailing slash !! ----------
|
|
||||||
cache_dir = '/var/tmp/airtime/pypo/cache/'
|
|
||||||
file_dir = '/var/tmp/airtime/pypo/files/'
|
|
||||||
tmp_dir = '/var/tmp/airtime/pypo/tmp/'
|
|
||||||
# ------- Setup directories - !! Don't include trailing slash !! -------
|
|
||||||
cache_base_dir = '/var/tmp/airtime/pypo'
|
|
||||||
log_base_dir = '/var/log/airtime'
|
|
||||||
pypo_log_dir = '/var/log/airtime/pypo'
|
|
||||||
liquidsoap_log_dir = '/var/log/airtime/pypo-liquidsoap'
|
|
||||||
# ------------------------ Liquidsoap Settings -------------------------
|
|
||||||
ls_host = '127.0.0.1'
|
|
||||||
ls_port = '1234'
|
|
||||||
# -------------------------- Pypo Preferences --------------------------
|
|
||||||
poll_interval = 3600
|
|
||||||
push_interval = 1
|
|
||||||
cue_style = 'pre'
|
|
||||||
# ---------------------- Recorded Audio Settings -----------------------
|
|
||||||
record_bitrate = 256
|
record_bitrate = 256
|
||||||
|
# The samplerate for recordings, default is 256
|
||||||
record_samplerate = 44100
|
record_samplerate = 44100
|
||||||
|
# The number of channels for recordings, default is 2
|
||||||
record_channels = 2
|
record_channels = 2
|
||||||
|
# The sample size for recordings, default is 16
|
||||||
record_sample_size = 16
|
record_sample_size = 16
|
||||||
record_file_type = 'ogg'
|
|
||||||
base_recorded_files = '/var/tmp/airtime/show-recorder/'
|
|
||||||
#
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
# F A C E B O O K
|
# F A C E B O O K
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
|
@ -3,6 +3,7 @@ Description=Libretime Liquidsoap Service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Environment=LIBRETIME_LOG_FILEPATH=/var/log/libretime/liquidsoap.log
|
Environment=LIBRETIME_LOG_FILEPATH=/var/log/libretime/liquidsoap.log
|
||||||
|
Environment=LIBRETIME_CONFIG_FILEPATH=/etc/airtime/airtime.conf
|
||||||
|
|
||||||
ExecStart=/usr/local/bin/libretime-liquidsoap
|
ExecStart=/usr/local/bin/libretime-liquidsoap
|
||||||
User=libretime-playout
|
User=libretime-playout
|
||||||
|
|
|
@ -4,6 +4,7 @@ After=network-online.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Environment=LIBRETIME_LOG_FILEPATH=/var/log/libretime/playout.log
|
Environment=LIBRETIME_LOG_FILEPATH=/var/log/libretime/playout.log
|
||||||
|
Environment=LIBRETIME_CONFIG_FILEPATH=/etc/airtime/airtime.conf
|
||||||
WorkingDirectory=/var/lib/libretime/playout
|
WorkingDirectory=/var/lib/libretime/playout
|
||||||
|
|
||||||
ExecStart=/usr/local/bin/libretime-playout
|
ExecStart=/usr/local/bin/libretime-playout
|
||||||
|
|
|
@ -1,4 +1,24 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from libretime_shared.config import BaseConfig, RabbitMQConfig
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from typing_extensions import Literal
|
||||||
|
|
||||||
CACHE_DIR = Path.cwd() / "scheduler"
|
CACHE_DIR = Path.cwd() / "scheduler"
|
||||||
RECORD_DIR = Path.cwd() / "recorder"
|
RECORD_DIR = Path.cwd() / "recorder"
|
||||||
|
|
||||||
|
|
||||||
|
class PlayoutConfig(BaseModel):
|
||||||
|
liquidsoap_host: str = "localhost"
|
||||||
|
liquidsoap_port: int = 1234
|
||||||
|
|
||||||
|
record_file_format: Literal["mp3", "ogg"] = "ogg" # record_file_type
|
||||||
|
record_bitrate: int = 256
|
||||||
|
record_samplerate: int = 44100
|
||||||
|
record_channels: int = 2
|
||||||
|
record_sample_size: int = 16
|
||||||
|
|
||||||
|
|
||||||
|
class Config(BaseConfig):
|
||||||
|
rabbitmq = RabbitMQConfig()
|
||||||
|
playout = PlayoutConfig()
|
||||||
|
|
|
@ -11,12 +11,14 @@ import defusedxml.minidom
|
||||||
from libretime_api_client import version1 as api_client
|
from libretime_api_client import version1 as api_client
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
from .config import Config
|
||||||
|
|
||||||
|
|
||||||
class ListenerStat(Thread):
|
class ListenerStat(Thread):
|
||||||
|
|
||||||
HTTP_REQUEST_TIMEOUT = 30 # 30 second HTTP request timeout
|
HTTP_REQUEST_TIMEOUT = 30 # 30 second HTTP request timeout
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config: Config):
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
self.config = config
|
self.config = config
|
||||||
self.api_client = api_client.AirtimeApiClient()
|
self.api_client = api_client.AirtimeApiClient()
|
||||||
|
|
|
@ -16,15 +16,14 @@ from threading import Lock
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import click
|
import click
|
||||||
from configobj import ConfigObj
|
|
||||||
from libretime_api_client.version1 import AirtimeApiClient as ApiClient
|
from libretime_api_client.version1 import AirtimeApiClient as ApiClient
|
||||||
from libretime_shared.cli import cli_logging_options
|
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 . import pure
|
from . import pure
|
||||||
from .config import CACHE_DIR, RECORD_DIR
|
from .config import CACHE_DIR, RECORD_DIR, Config
|
||||||
from .listenerstat import ListenerStat
|
from .listenerstat import ListenerStat
|
||||||
from .pypofetch import PypoFetch
|
from .pypofetch import PypoFetch
|
||||||
from .pypofile import PypoFile
|
from .pypofile import PypoFile
|
||||||
|
@ -81,15 +80,23 @@ def get_liquidsoap_version(version_string):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def liquidsoap_startup_test(telnet_lock, ls_host, ls_port):
|
def liquidsoap_startup_test(telnet_lock, liquidsoap_host, liquidsoap_port):
|
||||||
|
|
||||||
liquidsoap_version_string = liquidsoap_get_info(telnet_lock, ls_host, ls_port)
|
liquidsoap_version_string = liquidsoap_get_info(
|
||||||
|
telnet_lock,
|
||||||
|
liquidsoap_host,
|
||||||
|
liquidsoap_port,
|
||||||
|
)
|
||||||
while not liquidsoap_version_string:
|
while not liquidsoap_version_string:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Liquidsoap doesn't appear to be running!, " + "Sleeping and trying again"
|
"Liquidsoap doesn't appear to be running!, " + "Sleeping and trying again"
|
||||||
)
|
)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
liquidsoap_version_string = liquidsoap_get_info(telnet_lock, ls_host, ls_port)
|
liquidsoap_version_string = liquidsoap_get_info(
|
||||||
|
telnet_lock,
|
||||||
|
liquidsoap_host,
|
||||||
|
liquidsoap_port,
|
||||||
|
)
|
||||||
|
|
||||||
while pure.version_cmp(liquidsoap_version_string, LIQUIDSOAP_MIN_VERSION) < 0:
|
while pure.version_cmp(liquidsoap_version_string, LIQUIDSOAP_MIN_VERSION) < 0:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
|
@ -98,24 +105,24 @@ def liquidsoap_startup_test(telnet_lock, ls_host, ls_port):
|
||||||
% LIQUIDSOAP_MIN_VERSION
|
% LIQUIDSOAP_MIN_VERSION
|
||||||
)
|
)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
liquidsoap_version_string = liquidsoap_get_info(telnet_lock, ls_host, ls_port)
|
liquidsoap_version_string = liquidsoap_get_info(
|
||||||
|
telnet_lock,
|
||||||
|
liquidsoap_host,
|
||||||
|
liquidsoap_port,
|
||||||
|
)
|
||||||
|
|
||||||
logger.info("Liquidsoap version string found %s" % liquidsoap_version_string)
|
logger.info("Liquidsoap version string found %s" % liquidsoap_version_string)
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@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 playout.
|
Run playout.
|
||||||
"""
|
"""
|
||||||
setup_logger(level_from_name(log_level), log_filepath)
|
setup_logger(level_from_name(log_level), log_filepath)
|
||||||
|
config = Config(filepath=config_filepath)
|
||||||
# loading config file
|
|
||||||
try:
|
|
||||||
config = ConfigObj("/etc/airtime/airtime.conf")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error("Error loading config file: %s", e)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for dir_path in [CACHE_DIR, RECORD_DIR]:
|
for dir_path in [CACHE_DIR, RECORD_DIR]:
|
||||||
|
@ -154,16 +161,16 @@ def cli(log_level: str, log_filepath: Optional[Path]):
|
||||||
|
|
||||||
telnet_lock = Lock()
|
telnet_lock = Lock()
|
||||||
|
|
||||||
ls_host = config["pypo"]["ls_host"]
|
liquidsoap_host = config.playout.liquidsoap_host
|
||||||
ls_port = config["pypo"]["ls_port"]
|
liquidsoap_port = config.playout.liquidsoap_port
|
||||||
|
|
||||||
liquidsoap_startup_test(telnet_lock, ls_host, ls_port)
|
liquidsoap_startup_test(telnet_lock, liquidsoap_host, liquidsoap_port)
|
||||||
|
|
||||||
pypoFetch_q = Queue()
|
pypoFetch_q = Queue()
|
||||||
recorder_q = Queue()
|
recorder_q = Queue()
|
||||||
pypoPush_q = Queue()
|
pypoPush_q = Queue()
|
||||||
|
|
||||||
pypo_liquidsoap = PypoLiquidsoap(telnet_lock, ls_host, ls_port)
|
pypo_liquidsoap = PypoLiquidsoap(telnet_lock, liquidsoap_host, liquidsoap_port)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This queue is shared between pypo-fetch and pypo-file, where pypo-file
|
This queue is shared between pypo-fetch and pypo-file, where pypo-file
|
||||||
|
@ -174,25 +181,30 @@ def cli(log_level: str, log_filepath: Optional[Path]):
|
||||||
media_q = Queue()
|
media_q = Queue()
|
||||||
|
|
||||||
# Pass only the configuration sections needed; PypoMessageHandler only needs rabbitmq settings
|
# Pass only the configuration sections needed; PypoMessageHandler only needs rabbitmq settings
|
||||||
pmh = PypoMessageHandler(pypoFetch_q, recorder_q, config["rabbitmq"])
|
pmh = PypoMessageHandler(pypoFetch_q, recorder_q, config.rabbitmq)
|
||||||
pmh.daemon = True
|
pmh.daemon = True
|
||||||
pmh.start()
|
pmh.start()
|
||||||
|
|
||||||
pfile = PypoFile(media_q, config["pypo"])
|
pfile = PypoFile(media_q)
|
||||||
pfile.daemon = True
|
pfile.daemon = True
|
||||||
pfile.start()
|
pfile.start()
|
||||||
|
|
||||||
pf = PypoFetch(
|
pf = PypoFetch(
|
||||||
pypoFetch_q, pypoPush_q, media_q, telnet_lock, pypo_liquidsoap, config["pypo"]
|
pypoFetch_q,
|
||||||
|
pypoPush_q,
|
||||||
|
media_q,
|
||||||
|
telnet_lock,
|
||||||
|
pypo_liquidsoap,
|
||||||
|
config,
|
||||||
)
|
)
|
||||||
pf.daemon = True
|
pf.daemon = True
|
||||||
pf.start()
|
pf.start()
|
||||||
|
|
||||||
pp = PypoPush(pypoPush_q, telnet_lock, pypo_liquidsoap, config["pypo"])
|
pp = PypoPush(pypoPush_q, telnet_lock, pypo_liquidsoap, config)
|
||||||
pp.daemon = True
|
pp.daemon = True
|
||||||
pp.start()
|
pp.start()
|
||||||
|
|
||||||
recorder = Recorder(recorder_q)
|
recorder = Recorder(recorder_q, config)
|
||||||
recorder.daemon = True
|
recorder.daemon = True
|
||||||
recorder.start()
|
recorder.start()
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,13 @@ import copy
|
||||||
import json
|
import json
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
|
||||||
import signal
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import telnetlib
|
import telnetlib
|
||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
from queue import Empty
|
from queue import Empty
|
||||||
from subprocess import PIPE, Popen
|
from subprocess import PIPE, Popen
|
||||||
from threading import Thread, Timer
|
from threading import Thread, Timer
|
||||||
|
@ -18,7 +18,7 @@ from libretime_api_client import version2 as api_client
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from . import pure
|
from . import pure
|
||||||
from .config import CACHE_DIR
|
from .config import CACHE_DIR, Config
|
||||||
from .timeout import ls_timeout
|
from .timeout import ls_timeout
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,7 +34,13 @@ POLL_INTERVAL = 400
|
||||||
|
|
||||||
class PypoFetch(Thread):
|
class PypoFetch(Thread):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, pypoFetch_q, pypoPush_q, media_q, telnet_lock, pypo_liquidsoap, config
|
self,
|
||||||
|
pypoFetch_q,
|
||||||
|
pypoPush_q,
|
||||||
|
media_q,
|
||||||
|
telnet_lock,
|
||||||
|
pypo_liquidsoap,
|
||||||
|
config: Config,
|
||||||
):
|
):
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
|
|
||||||
|
@ -186,7 +192,8 @@ class PypoFetch(Thread):
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
tn = telnetlib.Telnet(
|
tn = telnetlib.Telnet(
|
||||||
self.config["ls_host"], self.config["ls_port"]
|
self.config.playout.liquidsoap_host,
|
||||||
|
self.config.playout.liquidsoap_port,
|
||||||
)
|
)
|
||||||
tn.write("exit\n".encode("utf-8"))
|
tn.write("exit\n".encode("utf-8"))
|
||||||
tn.read_all()
|
tn.read_all()
|
||||||
|
@ -219,7 +226,10 @@ class PypoFetch(Thread):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.telnet_lock.acquire()
|
self.telnet_lock.acquire()
|
||||||
tn = telnetlib.Telnet(self.config["ls_host"], self.config["ls_port"])
|
tn = telnetlib.Telnet(
|
||||||
|
self.config.playout.liquidsoap_host,
|
||||||
|
self.config.playout.liquidsoap_port,
|
||||||
|
)
|
||||||
# update the boot up time of Liquidsoap. Since Liquidsoap is not restarting,
|
# update the boot up time of Liquidsoap. Since Liquidsoap is not restarting,
|
||||||
# we are manually adjusting the bootup time variable so the status msg will get
|
# we are manually adjusting the bootup time variable so the status msg will get
|
||||||
# updated.
|
# updated.
|
||||||
|
@ -266,7 +276,10 @@ class PypoFetch(Thread):
|
||||||
# TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!!
|
# TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!!
|
||||||
try:
|
try:
|
||||||
self.telnet_lock.acquire()
|
self.telnet_lock.acquire()
|
||||||
tn = telnetlib.Telnet(self.config["ls_host"], self.config["ls_port"])
|
tn = telnetlib.Telnet(
|
||||||
|
self.config.playout.liquidsoap_host,
|
||||||
|
self.config.playout.liquidsoap_port,
|
||||||
|
)
|
||||||
command = ("vars.stream_metadata_type %s\n" % stream_format).encode("utf-8")
|
command = ("vars.stream_metadata_type %s\n" % stream_format).encode("utf-8")
|
||||||
logger.info(command)
|
logger.info(command)
|
||||||
tn.write(command)
|
tn.write(command)
|
||||||
|
@ -283,7 +296,10 @@ class PypoFetch(Thread):
|
||||||
# TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!!
|
# TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!!
|
||||||
try:
|
try:
|
||||||
self.telnet_lock.acquire()
|
self.telnet_lock.acquire()
|
||||||
tn = telnetlib.Telnet(self.config["ls_host"], self.config["ls_port"])
|
tn = telnetlib.Telnet(
|
||||||
|
self.config.playout.liquidsoap_host,
|
||||||
|
self.config.playout.liquidsoap_port,
|
||||||
|
)
|
||||||
command = ("vars.default_dj_fade %s\n" % fade).encode("utf-8")
|
command = ("vars.default_dj_fade %s\n" % fade).encode("utf-8")
|
||||||
logger.info(command)
|
logger.info(command)
|
||||||
tn.write(command)
|
tn.write(command)
|
||||||
|
@ -301,7 +317,10 @@ class PypoFetch(Thread):
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
self.telnet_lock.acquire()
|
self.telnet_lock.acquire()
|
||||||
tn = telnetlib.Telnet(self.config["ls_host"], self.config["ls_port"])
|
tn = telnetlib.Telnet(
|
||||||
|
self.config.playout.liquidsoap_host,
|
||||||
|
self.config.playout.liquidsoap_port,
|
||||||
|
)
|
||||||
command = ("vars.station_name %s\n" % station_name).encode("utf-8")
|
command = ("vars.station_name %s\n" % station_name).encode("utf-8")
|
||||||
logger.info(command)
|
logger.info(command)
|
||||||
tn.write(command)
|
tn.write(command)
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
import configparser
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import stat
|
import stat
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from configparser import NoOptionError
|
|
||||||
from queue import Empty
|
from queue import Empty
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
|
@ -16,15 +13,12 @@ from libretime_api_client import version2 as api_client
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from requests.exceptions import ConnectionError, HTTPError, Timeout
|
from requests.exceptions import ConnectionError, HTTPError, Timeout
|
||||||
|
|
||||||
CONFIG_PATH = "/etc/airtime/airtime.conf"
|
|
||||||
|
|
||||||
|
|
||||||
class PypoFile(Thread):
|
class PypoFile(Thread):
|
||||||
def __init__(self, schedule_queue, config):
|
def __init__(self, schedule_queue):
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
self.media_queue = schedule_queue
|
self.media_queue = schedule_queue
|
||||||
self.media = None
|
self.media = None
|
||||||
self._config = self.read_config_file(CONFIG_PATH)
|
|
||||||
self.api_client = api_client.AirtimeApiClient()
|
self.api_client = api_client.AirtimeApiClient()
|
||||||
|
|
||||||
def copy_file(self, media_item):
|
def copy_file(self, media_item):
|
||||||
|
@ -158,19 +152,6 @@ class PypoFile(Thread):
|
||||||
|
|
||||||
return media_item
|
return media_item
|
||||||
|
|
||||||
def read_config_file(self, config_path):
|
|
||||||
"""Parse the application's config file located at config_path."""
|
|
||||||
config = configparser.SafeConfigParser(allow_no_value=True)
|
|
||||||
try:
|
|
||||||
config.readfp(open(config_path))
|
|
||||||
except IOError as e:
|
|
||||||
logger.debug(
|
|
||||||
"Failed to open config file at %s: %s" % (config_path, e.strerror)
|
|
||||||
)
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
return config
|
|
||||||
|
|
||||||
def main(self):
|
def main(self):
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -12,6 +12,7 @@ from kombu.connection import Connection
|
||||||
from kombu.messaging import Exchange, Queue
|
from kombu.messaging import Exchange, Queue
|
||||||
from kombu.mixins import ConsumerMixin
|
from kombu.mixins import ConsumerMixin
|
||||||
from kombu.simple import SimpleQueue
|
from kombu.simple import SimpleQueue
|
||||||
|
from libretime_shared.config import RabbitMQConfig
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ class RabbitConsumer(ConsumerMixin):
|
||||||
|
|
||||||
|
|
||||||
class PypoMessageHandler(Thread):
|
class PypoMessageHandler(Thread):
|
||||||
def __init__(self, pq, rq, config):
|
def __init__(self, pq, rq, config: RabbitMQConfig):
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
self.pypo_queue = pq
|
self.pypo_queue = pq
|
||||||
self.recorder_queue = rq
|
self.recorder_queue = rq
|
||||||
|
@ -46,10 +47,9 @@ class PypoMessageHandler(Thread):
|
||||||
)
|
)
|
||||||
schedule_queue = Queue("pypo-fetch", exchange=schedule_exchange, key="foo")
|
schedule_queue = Queue("pypo-fetch", exchange=schedule_exchange, key="foo")
|
||||||
with Connection(
|
with Connection(
|
||||||
self.config["host"],
|
f"amqp://{self.config.user}:{self.config.password}"
|
||||||
self.config["user"],
|
f"@{self.config.host}:{self.config.port}"
|
||||||
self.config["password"],
|
f"/{self.config.vhost}",
|
||||||
self.config["vhost"],
|
|
||||||
heartbeat=5,
|
heartbeat=5,
|
||||||
) as connection:
|
) as connection:
|
||||||
rabbit = RabbitConsumer(connection, [schedule_queue], self)
|
rabbit = RabbitConsumer(connection, [schedule_queue], self)
|
||||||
|
|
|
@ -9,10 +9,10 @@ from datetime import datetime, timedelta
|
||||||
from queue import Empty, Queue
|
from queue import Empty, Queue
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
from configobj import ConfigObj
|
|
||||||
from libretime_api_client import version1 as api_client
|
from libretime_api_client import version1 as api_client
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
from .config import Config
|
||||||
from .pypofetch import PypoFetch
|
from .pypofetch import PypoFetch
|
||||||
from .pypoliqqueue import PypoLiqQueue
|
from .pypoliqqueue import PypoLiqQueue
|
||||||
from .timeout import ls_timeout
|
from .timeout import ls_timeout
|
||||||
|
@ -29,7 +29,7 @@ def is_file(media_item):
|
||||||
|
|
||||||
|
|
||||||
class PypoPush(Thread):
|
class PypoPush(Thread):
|
||||||
def __init__(self, q, telnet_lock, pypo_liquidsoap, config):
|
def __init__(self, q, telnet_lock, pypo_liquidsoap, config: Config):
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
self.api_client = api_client.AirtimeApiClient()
|
self.api_client = api_client.AirtimeApiClient()
|
||||||
self.queue = q
|
self.queue = q
|
||||||
|
@ -119,7 +119,10 @@ class PypoPush(Thread):
|
||||||
def stop_web_stream_all(self):
|
def stop_web_stream_all(self):
|
||||||
try:
|
try:
|
||||||
self.telnet_lock.acquire()
|
self.telnet_lock.acquire()
|
||||||
tn = telnetlib.Telnet(self.config["LS_HOST"], self.config["LS_PORT"])
|
tn = telnetlib.Telnet(
|
||||||
|
self.config.playout.liquidsoap_host,
|
||||||
|
self.config.playout.liquidsoap_port,
|
||||||
|
)
|
||||||
|
|
||||||
# msg = 'dynamic_source.read_stop_all xxx\n'
|
# msg = 'dynamic_source.read_stop_all xxx\n'
|
||||||
msg = "http.stop\n"
|
msg = "http.stop\n"
|
||||||
|
|
|
@ -12,11 +12,10 @@ from threading import Thread
|
||||||
|
|
||||||
import mutagen
|
import mutagen
|
||||||
import pytz
|
import pytz
|
||||||
from configobj import ConfigObj
|
|
||||||
from libretime_api_client.version1 import AirtimeApiClient as AirtimeApiClientV1
|
from libretime_api_client.version1 import AirtimeApiClient as AirtimeApiClientV1
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from libretime_playout.config import RECORD_DIR
|
from libretime_playout.config import RECORD_DIR, Config
|
||||||
|
|
||||||
|
|
||||||
def api_client():
|
def api_client():
|
||||||
|
@ -27,13 +26,6 @@ def api_client():
|
||||||
return AirtimeApiClientV1()
|
return AirtimeApiClientV1()
|
||||||
|
|
||||||
|
|
||||||
# loading config file
|
|
||||||
try:
|
|
||||||
config = ConfigObj("/etc/airtime/airtime.conf")
|
|
||||||
except Exception as e:
|
|
||||||
print("Error loading config file: {}".format(e))
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
# TODO : add docstrings everywhere in this module
|
# TODO : add docstrings everywhere in this module
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,9 +49,17 @@ PUSH_INTERVAL = 2
|
||||||
|
|
||||||
|
|
||||||
class ShowRecorder(Thread):
|
class ShowRecorder(Thread):
|
||||||
def __init__(self, show_instance, show_name, filelength, start_time):
|
def __init__(
|
||||||
|
self,
|
||||||
|
show_instance,
|
||||||
|
show_name,
|
||||||
|
filelength,
|
||||||
|
start_time,
|
||||||
|
config: Config,
|
||||||
|
):
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
self.api_client = api_client()
|
self.api_client = api_client()
|
||||||
|
self.config = config
|
||||||
self.filelength = filelength
|
self.filelength = filelength
|
||||||
self.start_time = start_time
|
self.start_time = start_time
|
||||||
self.show_instance = show_instance
|
self.show_instance = show_instance
|
||||||
|
@ -71,18 +71,13 @@ class ShowRecorder(Thread):
|
||||||
filename = self.start_time
|
filename = self.start_time
|
||||||
filename = filename.replace(" ", "-")
|
filename = filename.replace(" ", "-")
|
||||||
|
|
||||||
if config["pypo"]["record_file_type"] in ["mp3", "ogg"]:
|
|
||||||
filetype = config["pypo"]["record_file_type"]
|
|
||||||
else:
|
|
||||||
filetype = "ogg"
|
|
||||||
|
|
||||||
joined_path = os.path.join(RECORD_DIR, filename)
|
joined_path = os.path.join(RECORD_DIR, filename)
|
||||||
filepath = "%s.%s" % (joined_path, filetype)
|
filepath = "%s.%s" % (joined_path, self.config.playout.record_file_format)
|
||||||
|
|
||||||
br = config["pypo"]["record_bitrate"]
|
br = self.config.playout.record_bitrate
|
||||||
sr = config["pypo"]["record_samplerate"]
|
sr = self.config.playout.record_samplerate
|
||||||
c = config["pypo"]["record_channels"]
|
c = self.config.playout.record_channels
|
||||||
ss = config["pypo"]["record_sample_size"]
|
ss = self.config.playout.record_sample_size
|
||||||
|
|
||||||
# -f:16,2,44100
|
# -f:16,2,44100
|
||||||
# -b:256
|
# -b:256
|
||||||
|
@ -183,9 +178,10 @@ class ShowRecorder(Thread):
|
||||||
|
|
||||||
|
|
||||||
class Recorder(Thread):
|
class Recorder(Thread):
|
||||||
def __init__(self, q):
|
def __init__(self, q, config: Config):
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
self.api_client = api_client()
|
self.api_client = api_client()
|
||||||
|
self.config = config
|
||||||
self.sr = None
|
self.sr = None
|
||||||
self.shows_to_record = {}
|
self.shows_to_record = {}
|
||||||
self.server_timezone = ""
|
self.server_timezone = ""
|
||||||
|
@ -316,6 +312,7 @@ class Recorder(Thread):
|
||||||
show_name,
|
show_name,
|
||||||
show_length_seconds,
|
show_length_seconds,
|
||||||
start_time_formatted,
|
start_time_formatted,
|
||||||
|
self.config,
|
||||||
)
|
)
|
||||||
self.sr.start()
|
self.sr.start()
|
||||||
break
|
break
|
||||||
|
|
|
@ -42,6 +42,7 @@ setup(
|
||||||
"packaging",
|
"packaging",
|
||||||
"pytz",
|
"pytz",
|
||||||
"requests",
|
"requests",
|
||||||
|
"typing-extensions",
|
||||||
],
|
],
|
||||||
extras_require={
|
extras_require={
|
||||||
"dev": [
|
"dev": [
|
||||||
|
|
Loading…
Reference in New Issue