142 lines
3.7 KiB
Python
142 lines
3.7 KiB
Python
|
import logging
|
||
|
import os
|
||
|
from pathlib import Path
|
||
|
from subprocess import Popen
|
||
|
from time import sleep
|
||
|
|
||
|
from Exscript.protocols import Telnet
|
||
|
|
||
|
from croaker import path
|
||
|
from croaker.pidfile import pidfile
|
||
|
from croaker.playlist import Playlist, load_playlist
|
||
|
|
||
|
NOW_PLAYING = None
|
||
|
|
||
|
LIQUIDSOAP_CONFIG = """
|
||
|
set("server.telnet",true)
|
||
|
set("request.grace_time", 1.0)
|
||
|
set("init.daemon.pidfile.path", "{pidfile.path}")
|
||
|
set("decoder.ffmpeg.codecs.alac", ["alac"])
|
||
|
|
||
|
|
||
|
# deeebuggin
|
||
|
set("log.file.path","{debug_log}")
|
||
|
|
||
|
# set up the stream
|
||
|
stream = crossfade(normalize(playlist.safe(
|
||
|
id='stream',
|
||
|
reload_mode='watch',
|
||
|
mode='normal',
|
||
|
'{playlist_root}/now_playing',
|
||
|
)))
|
||
|
|
||
|
# if source files don't contain metadata tags, use the filename
|
||
|
def apply_metadata(m) =
|
||
|
title = m["title"]
|
||
|
print("Now Playing: #{{m['filename']}}")
|
||
|
if (title == "") then
|
||
|
[("title", "#{{path.remove_extension(path.basename(m['filename']))}}")]
|
||
|
else
|
||
|
[("title", "#{{title}}")]
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# apply the metadata parser
|
||
|
stream = map_metadata(apply_metadata, stream)
|
||
|
|
||
|
# define the source. ignore errors and provide no infallibale fallback. yolo.
|
||
|
radio = fallback(track_sensitive=false, [stream])
|
||
|
|
||
|
# transcode to icecast
|
||
|
output.icecast(
|
||
|
%mp3.vbr(quality=3),
|
||
|
name='Croaker Radio',
|
||
|
description='Background music for The Frog Hat Club',
|
||
|
host="{icecast_host}",
|
||
|
port={icecast_port},
|
||
|
password="{icecast_password}",
|
||
|
mount="{icecast_mount}",
|
||
|
icy_metadata="true",
|
||
|
url="{icecast_url}",
|
||
|
fallible=true,
|
||
|
radio
|
||
|
)
|
||
|
"""
|
||
|
|
||
|
|
||
|
def generate_liquidsoap_config():
|
||
|
log = path.root() / "liquidsoap.log"
|
||
|
if log.exists():
|
||
|
log.unlink()
|
||
|
log.touch()
|
||
|
ls_config = path.root() / "croaker.liq"
|
||
|
with ls_config.open("wt") as fh:
|
||
|
fh.write(
|
||
|
LIQUIDSOAP_CONFIG.format(
|
||
|
pidfile=_pidfile(terminate_if_running=False),
|
||
|
debug_log=log,
|
||
|
playlist_root=path.playlist_root(),
|
||
|
icecast_host=os.environ.get("ICECAST_HOST"),
|
||
|
icecast_port=os.environ.get("ICECAST_PORT"),
|
||
|
icecast_mount=os.environ.get("ICECAST_MOUNT"),
|
||
|
icecast_password=os.environ.get("ICECAST_PASSWORD"),
|
||
|
icecast_url=os.environ.get("ICECAST_URL"),
|
||
|
)
|
||
|
)
|
||
|
path.playlist_root().mkdir(exist_ok=True)
|
||
|
|
||
|
|
||
|
def start_liquidsoap():
|
||
|
logging.debug("Staring liquidsoap...")
|
||
|
pf = _pidfile(terminate_if_running=False)
|
||
|
pid = pf.read_pid()
|
||
|
if not pid:
|
||
|
logging.info("Liquidsoap does not appear to be running. Starting it...")
|
||
|
generate_liquidsoap_config()
|
||
|
Popen([os.environ["LIQUIDSOAP"], "--daemon", path.root() / "croaker.liq"])
|
||
|
sleep(1)
|
||
|
|
||
|
|
||
|
def start():
|
||
|
play_next("session_start")
|
||
|
|
||
|
|
||
|
def stop():
|
||
|
_pidfile(terminate_if_running=True)
|
||
|
|
||
|
|
||
|
def play_next(playlist_name: str = None):
|
||
|
start_liquidsoap()
|
||
|
if playlist_name:
|
||
|
pl = load_playlist(playlist_name)
|
||
|
logging.debug(f"Loaded playlist {pl = }")
|
||
|
if NOW_PLAYING != pl.name:
|
||
|
_switch_to(pl)
|
||
|
_send_liquidsoap_command("skip")
|
||
|
|
||
|
|
||
|
def _pidfile(terminate_if_running: bool = True):
|
||
|
pf = os.environ.get("LIQUIDSOAP_PIDFILE", None)
|
||
|
if pf:
|
||
|
pf = Path(pf)
|
||
|
else:
|
||
|
pf = path.root() / "liquidsoap.pid"
|
||
|
return pidfile(pf, terminate_if_running=terminate_if_running)
|
||
|
|
||
|
|
||
|
def _switch_to(playlist: Playlist):
|
||
|
logging.debug(f"Switching to {playlist = }")
|
||
|
np = path.playlist_root() / Path("now_playing")
|
||
|
with np.open("wt") as fh:
|
||
|
for track in playlist.tracks:
|
||
|
fh.write(f"{track}\n")
|
||
|
playlist.name
|
||
|
|
||
|
|
||
|
def _send_liquidsoap_command(command: str):
|
||
|
conn = Telnet()
|
||
|
conn.connect("localhost", port=1234)
|
||
|
conn.send(f"Croaker_Radio.{command}\r")
|
||
|
conn.send("quit\r")
|
||
|
conn.close()
|