diff --git a/.github/workflows/flake-checker.yml b/.github/workflows/flake-checker.yml index 6f0b13f..ebb5660 100644 --- a/.github/workflows/flake-checker.yml +++ b/.github/workflows/flake-checker.yml @@ -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 diff --git a/.github/workflows/flake-updater.yml b/.github/workflows/flake-updater.yml index a0c019d..c214391 100644 --- a/.github/workflows/flake-updater.yml +++ b/.github/workflows/flake-updater.yml @@ -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" diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml index b6dbcb5..c70997b 100644 --- a/.github/workflows/lint-pr.yml +++ b/.github/workflows/lint-pr.yml @@ -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: diff --git a/.github/workflows/lint-shellcheck.yml b/.github/workflows/lint-shellcheck.yml index 81b8108..658e48b 100644 --- a/.github/workflows/lint-shellcheck.yml +++ b/.github/workflows/lint-shellcheck.yml @@ -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: diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 63153dd..6c1a719 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -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 diff --git a/.github/workflows/scan-container.yaml b/.github/workflows/scan-container.yaml index b60b1a0..652e160 100644 --- a/.github/workflows/scan-container.yaml +++ b/.github/workflows/scan-container.yaml @@ -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 diff --git a/.github/workflows/test-build-stream-sprout.yml b/.github/workflows/test-build-stream-sprout.yml index e9a43b0..e7eacc0 100644 --- a/.github/workflows/test-build-stream-sprout.yml +++ b/.github/workflows/test-build-stream-sprout.yml @@ -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}} diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 2a806d8..0000000 --- a/AGENTS.md +++ /dev/null @@ -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) diff --git a/Containerfile b/Containerfile index 5e0b5dc..a2c2a94 100644 --- a/Containerfile +++ b/Containerfile @@ -2,6 +2,7 @@ FROM alpine:latest RUN apk add --no-cache --update \ bash \ + coreutils \ jellyfin-ffmpeg \ gawk \ grep \ diff --git a/Dockerfile b/Dockerfile deleted file mode 120000 index 5240dc0..0000000 --- a/Dockerfile +++ /dev/null @@ -1 +0,0 @@ -Containerfile \ No newline at end of file diff --git a/README.md b/README.md index c2c81e7..8f8a19c 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/flake.lock b/flake.lock index 6186ae3..1aa9ec4 100644 --- a/flake.lock +++ b/flake.lock @@ -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",