updating readme
This commit is contained in:
parent
5f4418bbf6
commit
affcf2d7dc
36
README.md
36
README.md
|
@ -1,13 +1,13 @@
|
||||||
# Croaker
|
# Croaker
|
||||||
A command-and-control web application for icecast / liquidaudio, with helpers for D&D session music.
|
|
||||||
|
A shoutcast audio playlist designed for serving D&D session music.
|
||||||
|
|
||||||
## What? Why?
|
## What? Why?
|
||||||
|
|
||||||
Because I run an online D&D game, which includes a background music stream for my players. The stream is controlled by a bunch of bash scripts I cobbled together which are functional but brittle. Also, this currently requires me to have a terminal window open to my media server to control liquidsoap directly, and I'd rather integrate the music controls directly with the rest of my DM tools, all of which run on my laptop. A web-based commmand-and-control app lets me use vanilla HTTP requests to control liquidsoap.
|
Because I run an online D&D game, which includes a background music stream for my players. The stream used to be served by liquidsoap and controlled by a bunch of bash scripts I cobbled together which are functional but brittle, and liquidsoap is a nightmare for the small use case. Also, this currently requires me to have a terminal window open to my media server to control liquidsoap directly, and I'd rather integrate the music controls directly with the rest of my DM tools, all of which run on my laptop.
|
||||||
|
|
||||||
*Now that is a powerful yak! -- Aesop Rock (misquoted)*
|
*Now that is a powerful yak! -- Aesop Rock (misquoted)*
|
||||||
|
|
||||||
|
|
||||||
## Quick Start (Server)
|
## Quick Start (Server)
|
||||||
|
|
||||||
This assumes you have a functioning icecast2 installation already.
|
This assumes you have a functioning icecast2 installation already.
|
||||||
|
@ -24,34 +24,44 @@ This assumes you have a functioning icecast2 installation already.
|
||||||
Now start the server, which will begin streaming the `session_start` playlist:
|
Now start the server, which will begin streaming the `session_start` playlist:
|
||||||
|
|
||||||
```
|
```
|
||||||
% croaker server start
|
% croaker start
|
||||||
Daemonizing webserver on http://0.0.0.0:8003, pidfile and output in ~/.dnd/croaker
|
INFO Daemonizing controller on (localhost, 8003); pidfile and logs in ~/.dnd/croaker
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quick Start (Client)
|
Connnect to the command & control server:
|
||||||
|
|
||||||
```
|
```
|
||||||
% mkdir -p ~/.dnd/croaker
|
% telnet localhost 8003
|
||||||
% croaker setup > ~/.dnd/croaker/defaults # only the client config is required
|
Trying 127.0.0.1...
|
||||||
% vi ~/.dnd/croaker/defaults # adjust to taste
|
Connected to croaker.local.
|
||||||
|
Escape character is '^]'.
|
||||||
|
HELP
|
||||||
|
PLAY $PLAYLIST_NAME - Switch to the specified playlist.
|
||||||
|
FFWD - Skip to the next track in the playlist.
|
||||||
|
HELP - Display command help.
|
||||||
|
KTHX - Close the current connection.
|
||||||
|
STOP - Stop Croaker.
|
||||||
|
OK
|
||||||
```
|
```
|
||||||
|
|
||||||
Switch to battle music -- roll initiative!
|
Switch to battle music -- roll initiative!
|
||||||
|
|
||||||
```
|
```
|
||||||
% croaker play battle
|
PLAY battle
|
||||||
OK
|
OK
|
||||||
```
|
```
|
||||||
|
|
||||||
Skip this track and move on to the next:
|
Skip this track and move on to the next:
|
||||||
|
|
||||||
```
|
```
|
||||||
% croaker skip
|
FFWD
|
||||||
OK
|
OK
|
||||||
```
|
```
|
||||||
|
|
||||||
Stop the server:
|
Stop the server:
|
||||||
|
|
||||||
```
|
```
|
||||||
% croaker stop
|
STOP
|
||||||
OK
|
Shutting down.
|
||||||
|
Connection closed by foreign host.
|
||||||
```
|
```
|
||||||
|
|
|
@ -19,25 +19,18 @@ SETUP_HELP = """
|
||||||
# Root directory for croaker configuration and logs. See also croaker --root.
|
# Root directory for croaker configuration and logs. See also croaker --root.
|
||||||
CROAKER_ROOT=~/.dnd/croaker
|
CROAKER_ROOT=~/.dnd/croaker
|
||||||
|
|
||||||
## COMMAND AND CONTROL WEBSERVER
|
|
||||||
|
|
||||||
# Please make sure you set SECRET_KEY in your environment if you are running
|
|
||||||
# the command and control webserver. Clients do not need this.
|
|
||||||
SECRET_KEY=
|
|
||||||
|
|
||||||
# Where the record the webserver daemon's PID
|
|
||||||
PIDFILE=~/.dnd/croaker/croaker.pid
|
|
||||||
|
|
||||||
HOST=0.0.0.0
|
|
||||||
PORT=8003
|
|
||||||
|
|
||||||
## MEDIA
|
|
||||||
|
|
||||||
# where to store playlist sources
|
# where to store playlist sources
|
||||||
PLAYLIST_ROOT=~/.dnd/croaker/playlists
|
#PLAYLIST_ROOT=$CROAKER_ROOT/playlists
|
||||||
|
|
||||||
# where to cache transcoded media files
|
# where to cache transcoded media files
|
||||||
CACHE_ROOT=~/.dnd/croaker/cache
|
#CACHE_ROOT=$CROAKER_ROOT/cache
|
||||||
|
|
||||||
|
# Where the record the daemon's PID
|
||||||
|
#PIDFILE=$CROAKER_ROOT/croaker.pid
|
||||||
|
|
||||||
|
# Command and Control TCP Server bind address
|
||||||
|
HOST=0.0.0.0
|
||||||
|
PORT=8003
|
||||||
|
|
||||||
# the kinds of files to add to playlists
|
# the kinds of files to add to playlists
|
||||||
MEDIA_GLOB=*.mp3,*.flac,*.m4a
|
MEDIA_GLOB=*.mp3,*.flac,*.m4a
|
||||||
|
@ -47,11 +40,6 @@ MEDIA_GLOB=*.mp3,*.flac,*.m4a
|
||||||
# the cached output location, respectively.
|
# the cached output location, respectively.
|
||||||
TRANSCODER=/usr/bin/ffmpeg -i INFILE '-hide_banner -loglevel error -codec:v copy -codec:a libmp3lame -q:a 2' OUTFILE
|
TRANSCODER=/usr/bin/ffmpeg -i INFILE '-hide_banner -loglevel error -codec:v copy -codec:a libmp3lame -q:a 2' OUTFILE
|
||||||
|
|
||||||
## LIQUIDSOAP AND ICECAST
|
|
||||||
|
|
||||||
# The liquidsoap executable
|
|
||||||
LIQUIDSOAP=/usr/bin/liquidsoap
|
|
||||||
|
|
||||||
# Icecast2 configuration for Liquidsoap
|
# Icecast2 configuration for Liquidsoap
|
||||||
ICECAST_PASSWORD=
|
ICECAST_PASSWORD=
|
||||||
ICECAST_MOUNT=
|
ICECAST_MOUNT=
|
||||||
|
@ -108,13 +96,11 @@ def setup(context: typer.Context):
|
||||||
@app.command()
|
@app.command()
|
||||||
def start(
|
def start(
|
||||||
context: typer.Context,
|
context: typer.Context,
|
||||||
daemonize: bool = typer.Option(True, help="Daemonize the webserver."),
|
daemonize: bool = typer.Option(True, help="Daemonize the server."),
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Start the Croaker command and control webserver.
|
Start the Croaker command and control server.
|
||||||
"""
|
"""
|
||||||
logger.debug("Switching to session_start playlist...")
|
|
||||||
logger.debug("Starting server...")
|
|
||||||
if daemonize:
|
if daemonize:
|
||||||
server.daemonize()
|
server.daemonize()
|
||||||
else:
|
else:
|
||||||
|
@ -124,7 +110,7 @@ def start(
|
||||||
@app.command()
|
@app.command()
|
||||||
def stop():
|
def stop():
|
||||||
"""
|
"""
|
||||||
Terminate the webserver process and liquidsoap.
|
Terminate the server.
|
||||||
"""
|
"""
|
||||||
server.stop()
|
server.stop()
|
||||||
|
|
||||||
|
|
|
@ -10,15 +10,10 @@ def root():
|
||||||
|
|
||||||
|
|
||||||
def cache_root():
|
def cache_root():
|
||||||
path = Path(os.environ.get("CACHE_ROOT", root() / Path("cache"))).expanduser()
|
path = Path(os.environ.get("CACHE_ROOT", root() / "cache")).expanduser()
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
def playlist_root():
|
def playlist_root():
|
||||||
path = Path(os.environ.get("PLAYLIST_ROOT", root() / Path("playlsits"))).expanduser()
|
path = Path(os.environ.get("PLAYLIST_ROOT", root() / "playlists")).expanduser()
|
||||||
return path
|
|
||||||
|
|
||||||
|
|
||||||
def transcoded_media(relpath):
|
|
||||||
path = cache_root() / Path(relpath + ".webm")
|
|
||||||
return path
|
return path
|
||||||
|
|
|
@ -73,9 +73,12 @@ class CroakerServer(socketserver.TCPServer):
|
||||||
def tell_controller(self, msg):
|
def tell_controller(self, msg):
|
||||||
self._queue.put(msg)
|
self._queue.put(msg)
|
||||||
|
|
||||||
|
def bind_address(self):
|
||||||
|
return (os.environ["HOST"], int(os.environ["PORT"]))
|
||||||
|
|
||||||
def daemonize(self) -> None:
|
def daemonize(self) -> None:
|
||||||
logger.info(f"Daemonizing controller; pidfile and output in {path.root()}")
|
logger.info(f"Daemonizing controller on {self.bind_address()}; pidfile and output in {path.root()}")
|
||||||
super().__init__((os.environ["HOST"], int(os.environ["PORT"])), RequestHandler)
|
super().__init__(self.bind_address(), RequestHandler)
|
||||||
|
|
||||||
self._context.pidfile = self._pidfile()
|
self._context.pidfile = self._pidfile()
|
||||||
self._context.stdout = open(path.root() / Path("croaker.out"), "wb")
|
self._context.stdout = open(path.root() / Path("croaker.out"), "wb")
|
||||||
|
|
Loading…
Reference in New Issue
Block a user