Compare commits

..

No commits in common. "main" and "0.1.6" have entirely different histories.

12 changed files with 34 additions and 135 deletions

View file

@ -13,9 +13,9 @@ jobs:
name: Flake Checker
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: DeterminateSystems/nix-installer-action@v21
- uses: DeterminateSystems/nix-installer-action@v19
- uses: DeterminateSystems/magic-nix-cache-action@v13
- uses: DeterminateSystems/flake-checker-action@v12

View file

@ -10,11 +10,11 @@ jobs:
name: Flake Lock Updater
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: DeterminateSystems/nix-installer-action@v21
- uses: DeterminateSystems/nix-installer-action@v19
- uses: DeterminateSystems/magic-nix-cache-action@v13
- uses: DeterminateSystems/update-flake-lock@v28
- uses: DeterminateSystems/update-flake-lock@v27
with:
pr-title: "chore: update flake.lock"

View file

@ -15,7 +15,7 @@ jobs:
name: Validate pull request title
runs-on: ubuntu-22.04
steps:
- uses: amannn/action-semantic-pull-request@v6
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:

View file

@ -10,7 +10,7 @@ jobs:
name: Shellcheck
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
with:

View file

@ -16,7 +16,7 @@ jobs:
name: "Check versions ⚖️"
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: "Compare App and Git versions 🟰"
@ -37,7 +37,7 @@ jobs:
name: "Build Release 👨‍🔧"
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- name: "Build .deb 🍥"
env:
DEBFULLNAME: "Martin Wimpress"
@ -69,7 +69,7 @@ jobs:
id-token: "write"
contents: "read"
steps:
- uses: "actions/checkout@v6"
- uses: "actions/checkout@v5"
with:
ref: "${{ (inputs.tag != null) && format('refs/tags/{0}', inputs.tag) || '' }}"
- uses: "DeterminateSystems/nix-installer-action@main"
@ -86,7 +86,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: "Checkout 🥡"
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Container Buildx

View file

@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
@ -26,7 +26,7 @@ jobs:
load: true
- name: Scan image
uses: anchore/scan-action@v7
uses: anchore/scan-action@v6
with:
image: "localbuild/testimage:latest"
output-format: table

View file

@ -33,7 +33,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: "Checkout 🥡"
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: "Build & Test .deb 🍥"
env:
DEBFULLNAME: "Martin Wimpress"
@ -55,9 +55,9 @@ jobs:
contents: "read"
steps:
- name: "Checkout 🥡"
uses: "actions/checkout@v6"
uses: "actions/checkout@v5"
- name: "Install Nix ❄️"
uses: "DeterminateSystems/nix-installer-action@v21"
uses: "DeterminateSystems/nix-installer-action@v19"
- name: "Enable Magic Nix Cache 🪄"
uses: "DeterminateSystems/magic-nix-cache-action@v13"
- name: "Build & Test .nix ❄️"
@ -69,7 +69,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: "Checkout 🥡"
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Container Buildx
@ -103,7 +103,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout 🥡
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Build snap 🐊
uses: snapcore/action-build@v1
id: snapcraft
@ -117,7 +117,7 @@ jobs:
snap: ${{ steps.snapcraft.outputs.snap }}
isClassic: false
- name: Upload artifacts ⤴️
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v4
with:
name: stream-sprout-snap
path: ${{ steps.snapcraft.outputs.snap}}

105
AGENTS.md
View file

@ -1,105 +0,0 @@
# AGENTS.md
## Overview
Stream Sprout is a bash-based RTMP restreaming tool that forwards a single video source (from OBS Studio or similar) to multiple destinations like Twitch, YouTube, Owncast, and Peertube simultaneously. It uses FFmpeg's tee muxer to copy streams without transcoding.
## Tech Stack
- **Language:** Bash 5.0+ (single script: `stream-sprout`)
- **Runtime dependency:** FFmpeg (RTMP server and restreaming)
- **Configuration:** YAML parsed via awk/sed
- **Packaging:** Nix flake, Debian .deb, Snap, Docker/Podman
## Build and Run Commands
```bash
# Run directly (requires ffmpeg, bash 5.0+, awk, grep, sed)
./stream-sprout --config stream-sprout.yaml
# Show version and FFmpeg info
./stream-sprout --version
# Show system info (useful for bug reports)
./stream-sprout --info
# Nix build
nix build
# Enter development shell with all dependencies
nix develop
# Docker build and run
docker build -t stream-sprout .
docker run -p 1935:1935 -it -v $PWD:/data stream-sprout --config /data/stream-sprout.yaml
```
## Linting
ShellCheck is enforced via CI on all pull requests.
```bash
# Run locally before committing
shellcheck stream-sprout
```
The script includes `# shellcheck disable=SC2154` for variables set dynamically via `eval` from YAML parsing.
## Code Style
- Bash scripts use `#!/usr/bin/env bash`
- Functions use `function name() {}` syntax
- Use `local` for function-scoped variables
- Use `readonly` for constants
- Validation with informative error messages using Unicode icons and ANSI colours
- Version is tracked in the script: `readonly VERSION="x.y.z"`
## Project Structure
```
stream-sprout # Main bash script (single file)
stream-sprout.yaml # Local config (gitignored)
stream-sprout.yaml.example # Example configuration
package.nix # Nix package definition
devshell.nix # Nix development shell
flake.nix # Nix flake
Dockerfile # Alpine-based container
```
## Configuration
YAML config with two main sections:
- `server:` - RTMP server settings (ip, port, app, key, archive options)
- `services:` - Destination services (each with enabled, rtmp_server, key)
Config search order: `./stream-sprout.yaml`, `$XDG_CONFIG_HOME/stream-sprout.yaml`, `/etc/stream-sprout.yaml`
## PR and Commit Guidelines
- **Commit messages must follow [Conventional Commits](https://www.conventionalcommits.org/)**
- PR titles are validated against Conventional Commits format
- Single-commit PRs must have matching PR title and commit message
- ShellCheck must pass with no warnings
Common prefixes: `feat:`, `fix:`, `chore:`, `refactor:`, `docs:`
## Version Updates
When changing version:
1. Update `VERSION` in `stream-sprout` script
2. The Nix package extracts version automatically from the script
## Constraints
- Requires bash 5.0 or newer
- FFmpeg must be available on PATH
- RTMP only (no RTMPS support currently)
- FFmpeg does not enforce stream keys (documented security limitation)
## Security Considerations
- Stream keys are stored in plain text in YAML config
- FFmpeg accepts any RTMP stream on the configured port regardless of app/key path
- Do not expose the RTMP port to untrusted networks without additional protection (VPN, firewall, SSH tunnel)

View file

@ -2,6 +2,7 @@ FROM alpine:latest
RUN apk add --no-cache --update \
bash \
coreutils \
jellyfin-ffmpeg \
gawk \
grep \

View file

@ -1 +0,0 @@
Containerfile

View file

@ -271,6 +271,10 @@ services:
[rtmp @ 0x2ca9be80] Unexpected stream STREAMBOMB, expecting c5b559b2-589d-4925-a28e-20d1954fd6c5
Last message repeated 1 times
```
- Stream Sprout does not support restreaming using secure RTMP (RTMPS).
- *At least I don't think it does, but I haven't fully tested it.*
- Kick only appears to support rtmps:// URLs and Stream Sprout restreams do not appear on Kick.
- https://superuser.com/questions/1438939/live-streaming-over-rtmps-using-ffmpeg
- Each destination you add will increase your bandwidth requirements.
## References

20
flake.lock generated
View file

@ -2,12 +2,12 @@
"nodes": {
"flake-schemas": {
"locked": {
"lastModified": 1761577921,
"narHash": "sha256-eK3/xbUOrxp9fFlei09XNjqcdiHXxndzrTXp7jFpOk8=",
"rev": "47849c7625e223d36766968cc6dc23ba0e135922",
"revCount": 107,
"lastModified": 1721999734,
"narHash": "sha256-G5CxYeJVm4lcEtaO87LKzOsVnWeTcHGKbKxNamNWgOw=",
"rev": "0a5c42297d870156d9c57d8f99e476b738dcd982",
"revCount": 75,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/DeterminateSystems/flake-schemas/0.2.0/019a4a84-544d-7c59-b26d-e334e320c932/source.tar.gz"
"url": "https://api.flakehub.com/f/pinned/DeterminateSystems/flake-schemas/0.1.5/0190ef2f-61e0-794b-ba14-e82f225e55e6/source.tar.gz"
},
"original": {
"type": "tarball",
@ -16,12 +16,12 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1769318308,
"narHash": "sha256-Mjx6p96Pkefks3+aA+72lu1xVehb6mv2yTUUqmSet6Q=",
"rev": "1cd347bf3355fce6c64ab37d3967b4a2cb4b878c",
"revCount": 906484,
"lastModified": 1754937576,
"narHash": "sha256-3sWA5WJybUE16kIMZ3+uxcxKZY/JRR4DFBqLdSLBo7w=",
"rev": "ddae11e58c0c345bf66efbddbf2192ed0e58f896",
"revCount": 808080,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.2511.906484%2Brev-1cd347bf3355fce6c64ab37d3967b4a2cb4b878c/019bfb68-fb8e-7f55-bb2a-5bee98516c95/source.tar.gz"
"url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.2505.808080%2Brev-ddae11e58c0c345bf66efbddbf2192ed0e58f896/01989f5e-b09d-7b09-9699-5d522e6f12ce/source.tar.gz"
},
"original": {
"type": "tarball",