MeteorLight/README.md

238 lines
6.5 KiB
Markdown

# MeteorLight
Radio streamer ([kawa](https://github.com/Luminarys/kawa) drop-in compatible).
# Improvements / differences from Kawa
* Does not use libav ([see supported formats/codecs on Kirika](https://git.gammaspectra.live/S.O.N.G/Kirika#codecs-supported))
* Supports HTTP clients that have more than 16 HTTP request headers or longer than 64 bytes per header.
* Does not restart stream per-track, instead being a continuous stream without parameter changes.
* Normalized channels / sample rates for all mounts.
* Implements ICY metadata (artist, title, url).
* Uses sample/timed packet buffers, instead of kawa byte buffers, which caused wild differences between endpoints. Mounts usually align within 0.2s of each other, depending on client.
* Use `queue.buffer_size` to specify number of seconds to buffer (by default 0, automatic per client).
* Implements `queue.nr` and `/random` (to be deprecated/changed).
* Supports max queue length via `queue.length` config.
* Supports extra encoder bitrate control settings (CBR, VBR, auto, etc.)
* Can set custom sample rate / channel count / bitdepth / compression level per stream mount.
* Can read and apply ReplayGain tags, or normalize audio loudness.
* Can have audio sources over HTTP(s) URLs on `path` property, and supports seeking.
* [Precise metadata and timing information packet stream](PACKET_STREAM.md), trigger via `x-audio-packet-stream: 1` HTTP header.
* Workaround to allow FLAC streaming under Safari.
* API additions to allow working with direct queue items or listeners.
## Dependencies
### Go >= 1.19
### [Kirika](https://git.gammaspectra.live/S.O.N.G/Kirika) dependencies
Kirika is a collection of audio utilities for decoding/encoding files and streams.
Check its native dependencies that must be installed before usage.
## Usage
Start by copying [example_config.toml](example_config.toml) to the location of your choice and reading through it. Of importance are `queue.fallback`, and `queue.random_song_api`.
MeteorLight will search for `config.toml` in its working directory. Alternatively you can pass `-config "/example/path/config.toml"` to specify a different location.
Batteries are not included - MeteorLight needs to be paired with your own software to find songs to stream.
You will have to provide an external API that MeteorLight can query for songs to play and notify as new songs being played.
Before continuing, you will need to install the dependencies listed above.
### From Git repository
```shell
$ git clone https://git.gammaspectra.live/S.O.N.G/MeteorLight.git && cd MeteorLight
# create/edit config.toml
$ go run .
```
### From Go run
```shell
$ go run git.gammaspectra.live/S.O.N.G/MeteorLight@<commit_hash>
```
### From Docker/Podman
Image is using `golang:1.19-alpine`, dependencies are built from scratch. See [Dockerfile](Dockerfile).
```shell
$ docker build -t meteorlight .
$ docker run --rm -it -v "$(pwd)/config.toml:/config.toml:ro" -v "$(pwd)/fallback.flac:/fallback.flac:ro" -p 8001:8001 -p 127.0.0.1:4040:4040 meteorlight
```
## API
See [kawa API](https://github.com/Luminarys/kawa#api) for a general overview. Additional endpoints or changed ones are listed below.
Track blobs returned have a `queue_id` parameter, regardless of source.
Queue entry metadata (`title`, `artist`, `album`, and ReplayGain tags `track_peak`, `track_gain`, `album_peak`, `album_gain`) will be used if provided, then fallback to on-file tags where possible. These will be made available over `/np` and similar endpoints, and be exposed via ICY metadata and timing/metadata stream.
### `NEW` DELETE /queue/<queue_id>
Unqueues the track with `queue_id` specified as a parameter.
#### Response
```json
{
"success": true,
"reason": null
}
```
### `CHANGED` POST /queue/head
Same as kawa's, but `queue_id` is added to response directly.
#### Response
```json
{
"success": true,
"reason": null,
"queue_id": 3
}
```
### `CHANGED` POST /queue/tail
Same as kawa's, but `queue_id` is added to response directly.
#### Response
```json
{
"success": true,
"reason": null,
"queue_id": 5
}
```
### `NEW` DELETE /listeners/<listener_id>
Drops the listener connection with `listener_id` specified as a parameter.
#### Response
```json
{
"success": true,
"reason": null
}
```
### `CHANGED` GET /listeners
Same as kawa's, but `identifier` and `start` is added to each listener entry.
The listener `identifier` is generated based on user connection address, port, user-agent and mount.
The `start` field denotes the unix time this listener connected.
Additionally, a `x-listener-identifier` header is exposed to mount response.
#### Response
```json
[
{
"identifier": "641df131cb52f8f6381d9946cccb822e",
"mount": "stream.flac",
"path": "/stream.flac",
"start": 1661283903,
"headers": [
{
"name": "User-Agent",
"value": "libmpv"
},
{
"name": "Accept",
"value": "*/*"
},
{
"name": "Range",
"value": "bytes=0-"
},
{
"name": "Connection",
"value": "close"
},
{
"name": "Icy-Metadata",
"value": "1"
}
]
}
]
```
## Mount API
### `NEW` GET /mounts
A simple listing of the working mounts + settings are made available.
#### Response
```json
[
{
"mount": "/stream128.mp3",
"mime": "audio/mpeg;codecs=mp3",
"sampleRate": 44100,
"channels": 2,
"listeners": 0,
"options": {
"bitrate": 128
}
},
{
"mount": "/stream192.mp3",
"mime": "audio/mpeg;codecs=mp3",
"sampleRate": 44100,
"channels": 2,
"listeners": 0,
"options": {
"bitrate": 192
}
},
{
"mount": "/stream128.aac",
"mime": "audio/aac",
"sampleRate": 44100,
"channels": 2,
"listeners": 0,
"options": {
"bitrate": 128
}
},
{
"mount": "/stream128.opus",
"mime": "audio/ogg;codecs=opus",
"sampleRate": 48000,
"channels": 2,
"listeners": 0,
"options": {
"bitrate": 128
}
},
{
"mount": "/stream192.opus",
"mime": "audio/ogg;codecs=opus",
"sampleRate": 48000,
"channels": 2,
"listeners": 0,
"options": {
"bitrate": 192
}
},
{
"mount": "/stream256.opus",
"mime": "audio/ogg;codecs=opus",
"sampleRate": 48000,
"channels": 2,
"listeners": 0,
"options": {
"bitrate": 256
}
},
{
"mount": "/stream.flac",
"mime": "audio/flac",
"sampleRate": 44100,
"channels": 2,
"listeners": 0,
"options": {
"bitdepth": 16,
"block_size": 0,
"compression_level": 5
}
}
]
```