Release: v2.7.0 + Dokumentation ausgelagert
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
# Benutzerspezifische Konfiguration (aus .dist erstellt)
|
||||||
|
config/stream-recorder.conf
|
||||||
|
|
||||||
|
# Job-Dateien (nur Vorlagen werden versioniert)
|
||||||
|
jobs/*.job
|
||||||
|
!jobs/templates/
|
||||||
@@ -19,334 +19,71 @@
|
|||||||
·
|
·
|
||||||
<a href="https://matrix.to/#/#support:techniverse.net">💬 Support</a>
|
<a href="https://matrix.to/#/#support:techniverse.net">💬 Support</a>
|
||||||
</h6>
|
</h6>
|
||||||
<br><br>
|
<br>
|
||||||
|
|
||||||
## Beschreibung
|
|
||||||
|
|
||||||
**stream-recorder** ist ein Bash-basiertes Tool zur automatisierten Aufzeichnung von Streams. Es unterstützt verschiedene Stream-Typen und kann sowohl interaktiv als auch vollautomatisch genutzt werden.
|
|
||||||
|
|
||||||
Jeder Stream wird als eigene Job-Datei definiert. Aufnahmezeiten werden direkt in der Job-Datei festgelegt - kein Cron-Wissen nötig. Ein systemd-Timer übernimmt die Zeitsteuerung automatisch.
|
|
||||||
|
|
||||||
## Funktionen
|
## Funktionen
|
||||||
|
|
||||||
- **Mehrere Stream-Typen:** RTMP, RTMPS, HLS (m3u8), MP3, AAC, OGG, FLAC und generische HTTP-Streams
|
- **Mehrere Stream-Typen** — RTMP, RTMPS, HLS (m3u8), MP3, AAC, OGG, FLAC und HTTP-Streams
|
||||||
- **Job-basiert:** Ein Stream = eine Job-Datei, inklusive Zeitplanung
|
- **Job-basiert** — Ein Stream = eine Job-Datei, inklusive Zeitplanung
|
||||||
- **Automatische Zeitplanung:** Aufnahmezeiten direkt in der Job-Datei konfigurieren
|
- **Segment-Splitting** — Lange Aufnahmen automatisch in Teile zerlegen (z.B. stündlich)
|
||||||
- **Schnellaufnahme:** Interaktiver Modus für spontane Aufnahmen (`quick`)
|
- **Plik-Upload** — Aufnahmen und Segmente automatisch auf Plik hochladen
|
||||||
- **Automatische Erkennung:** Stream-Typ und Dateiformat werden anhand der URL erkannt
|
- **Zeitplanung** — Aufnahmezeiten direkt in der Job-Datei konfigurieren (kein Cron nötig)
|
||||||
- **Auto-Reconnect:** Automatische Wiederverbindung bei Verbindungsabbrüchen
|
- **Auto-Reconnect** — Automatische Wiederverbindung bei Verbindungsabbrüchen
|
||||||
- **NTFY-Benachrichtigungen:** Optionale Push-Nachrichten bei Start, Stop und Fehlern
|
- **Live-Status** — Laufende Aufnahmen mit Laufzeit, Dateigröße und Segment-Anzahl anzeigen
|
||||||
- **Logging:** Strukturiertes Logging mit konfigurierbarem Log-Level und Live-Ansicht
|
- **NTFY-Benachrichtigungen** — Push-Nachrichten bei Start, Stop, Fehler und Upload
|
||||||
- **Prozessverwaltung:** Start, Stop und Status einzelner oder aller Aufnahmen
|
- **Schnellaufnahme** — Interaktiver Modus für spontane Aufnahmen
|
||||||
- **Einfache Installation:** Install/Uninstall-Scripts für den systemd-Service
|
|
||||||
|
|
||||||
## Voraussetzungen
|
## Voraussetzungen
|
||||||
|
|
||||||
- **Betriebssystem:** Linux (getestet unter Ubuntu/Debian)
|
- **Linux** (getestet unter Ubuntu/Debian)
|
||||||
- **Bash:** Version 4.0 oder neuer
|
- **Bash** 4.0+
|
||||||
- **ffmpeg:** Muss installiert sein (`sudo apt install ffmpeg`)
|
- **ffmpeg** (`sudo apt install ffmpeg`)
|
||||||
- **curl:** Optional, nur für NTFY-Benachrichtigungen (`sudo apt install curl`)
|
- **curl** (optional, für NTFY und Plik)
|
||||||
|
|
||||||
## Installation
|
## Schnellstart
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Repository klonen
|
# Klonen und einrichten
|
||||||
git clone https://git.techniverse.net/scriptos/stream-recorder.git /opt/stream-recorder
|
git clone https://git.techniverse.net/scriptos/stream-recorder.git /opt/stream-recorder
|
||||||
cd /opt/stream-recorder
|
cd /opt/stream-recorder
|
||||||
|
cp config/stream-recorder.conf.dist config/stream-recorder.conf
|
||||||
# Konfiguration anpassen
|
|
||||||
nano config/stream-recorder.conf
|
nano config/stream-recorder.conf
|
||||||
|
|
||||||
# Scheduler installieren (systemd-Timer)
|
# Scheduler installieren (optional)
|
||||||
sudo install/install.sh
|
sudo install/install.sh
|
||||||
```
|
|
||||||
|
|
||||||
Das Install-Script macht das Hauptscript ausführbar, installiert fehlende Abhängigkeiten (ffmpeg) und richtet den systemd-Timer ein, der jede Minute die Zeitpläne prüft.
|
# Ersten Job erstellen
|
||||||
|
|
||||||
### Deinstallation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo install/uninstall.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
Entfernt den systemd-Timer. Job-Dateien, Konfiguration und Aufnahmen bleiben erhalten.
|
|
||||||
|
|
||||||
## Konfiguration
|
|
||||||
|
|
||||||
Die Konfigurationsdatei liegt unter `config/stream-recorder.conf`:
|
|
||||||
|
|
||||||
| Parameter | Standard | Beschreibung |
|
|
||||||
|-----------------|------------------------------|---------------------------------------|
|
|
||||||
| `DOWNLOAD_PATH` | `/home/downloads/recordings` | Basis-Verzeichnis für Aufnahmen |
|
|
||||||
| `LOG_DIR` | `/var/log/stream-recorder` | Log-Verzeichnis |
|
|
||||||
| `LOG_LEVEL` | `INFO` | Log-Level: DEBUG, INFO, WARN, ERROR |
|
|
||||||
| `PID_DIR` | `/tmp/stream-recorder` | PID-Dateien für laufende Aufnahmen |
|
|
||||||
| `STATE_DIR` | `/var/lib/stream-recorder` | Status für bereits gestartete Zeitpläne |
|
|
||||||
| `MAX_RETRIES` | `0` | Wiederholungsversuche (0 = unbegrenzt)|
|
|
||||||
| `RETRY_DELAY` | `5` | Wartezeit zwischen Versuchen (Sek.) |
|
|
||||||
| `NTFY_URL` | (leer) | NTFY Server-URL inkl. Topic |
|
|
||||||
| `NTFY_TOKEN` | (leer) | NTFY Access-Token (optional) |
|
|
||||||
| `NTFY_EVENTS` | `error` | Benachrichtigungs-Events (s. unten) |
|
|
||||||
| `PLIK_URL` | (leer) | Plik Server-URL |
|
|
||||||
| `PLIK_API_KEY` | (leer) | Plik API-Key (optional) |
|
|
||||||
| `PLIK_TTL` | `30d` | Gültigkeitsdauer der Uploads |
|
|
||||||
|
|
||||||
## Job-Dateien
|
|
||||||
|
|
||||||
Jeder Stream wird durch eine `.job`-Datei im `jobs/`-Verzeichnis definiert. Vorlagen befinden sich unter `jobs/templates/`.
|
|
||||||
|
|
||||||
### Job erstellen
|
|
||||||
|
|
||||||
**Interaktiv (empfohlen):**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./stream-recorder.sh create
|
|
||||||
```
|
|
||||||
|
|
||||||
Das Script fragt Schritt für Schritt nach URL, Name, Typ, Zeitplan und Plik-Upload und erstellt die Job-Datei automatisch. Bei aktiviertem Zeitplan wird die Aufnahmedauer wahlweise per Endzeit oder Laufzeit begrenzt — das jeweils andere wird automatisch berechnet.
|
|
||||||
|
|
||||||
**Manuell (aus Vorlage):**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Vorlage kopieren
|
|
||||||
cp jobs/templates/example-rtmp.job.dist jobs/mein-stream.job
|
|
||||||
|
|
||||||
# Anpassen
|
|
||||||
nano jobs/mein-stream.job
|
|
||||||
```
|
|
||||||
|
|
||||||
### Stream-Parameter
|
|
||||||
|
|
||||||
| Parameter | Pflicht | Beschreibung |
|
|
||||||
|--------------------|---------|-----------------------------------------------------|
|
|
||||||
| `STREAM_NAME` | Ja | Name des Streams (wird für Dateinamen verwendet) |
|
|
||||||
| `STREAM_URL` | Ja | URL des Streams |
|
|
||||||
| `STREAM_TYPE` | Nein | `auto`, `rtmp`, `hls`, `mp3`, `aac`, `ogg`, `flac` |
|
|
||||||
| `OUTPUT_FORMAT` | Nein | Dateiendung (z.B. `mp4`, `mp3`) - sonst automatisch |
|
|
||||||
| `MAX_DURATION` | Nein | Aufnahmedauer in Sekunden (wird bei Zeitplanung automatisch aus Endzeit oder Laufzeit berechnet) |
|
|
||||||
| `EXTRA_FFMPEG_ARGS`| Nein | Zusätzliche ffmpeg-Parameter |
|
|
||||||
| `PLIK_ENABLED` | Nein | Plik-Upload aktivieren (`true`/`false`) |
|
|
||||||
| `DELETE_AFTER_UPLOAD` | Nein | Lokale Datei nach erfolgreichem Upload löschen (`true`/`false`) |
|
|
||||||
|
|
||||||
### Zeitplan-Parameter
|
|
||||||
|
|
||||||
| Parameter | Standard | Beschreibung |
|
|
||||||
|--------------------|----------|-------------------------------------------------|
|
|
||||||
| `SCHEDULE_ENABLED` | `false` | Zeitplan aktivieren (`true`/`false`) |
|
|
||||||
| `SCHEDULE_ONCE` | `false` | Einmaliger Termin statt wöchentlichem Zeitplan |
|
|
||||||
| `SCHEDULE_DATE` | (leer) | Datum für einmalige Termine (`YYYY-MM-DD`) |
|
|
||||||
| `SCHEDULE_DAYS` | `*` | Aufnahmetage für wöchentliche Zeitpläne |
|
|
||||||
| `SCHEDULE_START` | (leer) | Startzeit im Format `HH:MM` |
|
|
||||||
| `SCHEDULE_STOP` | (leer) | Endzeit im Format `HH:MM` (leer = kein Auto-Stop)|
|
|
||||||
|
|
||||||
**Tage-Formate:**
|
|
||||||
|
|
||||||
| Format | Bedeutung |
|
|
||||||
|--------------|----------------------------|
|
|
||||||
| `*` | Täglich |
|
|
||||||
| `Mo-Fr` | Montag bis Freitag |
|
|
||||||
| `Sa,So` | Samstag und Sonntag |
|
|
||||||
| `Mo,Mi,Fr` | Einzelne Tage |
|
|
||||||
| `Mo-Fr,So` | Kombiniert |
|
|
||||||
|
|
||||||
Die Tagesangaben funktionieren auf Deutsch (Mo, Di, Mi, Do, Fr, Sa, So) und Englisch (Mon, Tue, Wed, Thu, Fri, Sat, Sun).
|
|
||||||
|
|
||||||
Ohne `SCHEDULE_ONCE=true` sind Tagesangaben wöchentlich/wiederkehrend. Ein Job mit `SCHEDULE_DAYS="Mi"` und `SCHEDULE_START="21:00"` läuft also jeden Mittwoch.
|
|
||||||
|
|
||||||
### Beispiel: Tägliche RTMP-Aufnahme von 20:00 bis 22:00
|
|
||||||
|
|
||||||
```bash
|
|
||||||
STREAM_NAME="Abend-Livestream"
|
|
||||||
STREAM_URL="rtmp://example.com/live/stream-key"
|
|
||||||
SCHEDULE_ENABLED=true
|
|
||||||
SCHEDULE_DAYS="*"
|
|
||||||
SCHEDULE_START="20:00"
|
|
||||||
SCHEDULE_STOP="22:00"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Beispiel: Radio-Mitschnitt werktags um 18:00, 1 Stunde
|
|
||||||
|
|
||||||
```bash
|
|
||||||
STREAM_NAME="Mein-Radiosender"
|
|
||||||
STREAM_URL="https://example.com/stream.mp3"
|
|
||||||
MAX_DURATION="1h"
|
|
||||||
SCHEDULE_ENABLED=true
|
|
||||||
SCHEDULE_ONCE=false
|
|
||||||
SCHEDULE_DAYS="Mo-Fr"
|
|
||||||
SCHEDULE_START="18:00"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Beispiel: Einmalige Aufnahme am 09.09.2026 um 21:00, 30 Minuten
|
|
||||||
|
|
||||||
```bash
|
|
||||||
STREAM_NAME="Einmaliger-Mitschnitt"
|
|
||||||
STREAM_URL="https://example.com/stream.mp3"
|
|
||||||
MAX_DURATION="30m"
|
|
||||||
SCHEDULE_ENABLED=true
|
|
||||||
SCHEDULE_ONCE=true
|
|
||||||
SCHEDULE_DATE="2026-09-09"
|
|
||||||
SCHEDULE_START="21:00"
|
|
||||||
SCHEDULE_STOP=""
|
|
||||||
```
|
|
||||||
|
|
||||||
## Verwendung
|
|
||||||
|
|
||||||
### Befehle
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Einzelnen Stream aufzeichnen
|
|
||||||
./stream-recorder.sh record mein-stream
|
|
||||||
|
|
||||||
# Im Vordergrund aufzeichnen, z.B. für Tests
|
|
||||||
./stream-recorder.sh --foreground record mein-stream
|
|
||||||
|
|
||||||
# Alle Jobs gleichzeitig aufzeichnen
|
|
||||||
./stream-recorder.sh record-all
|
|
||||||
|
|
||||||
# Neuen Job interaktiv erstellen
|
|
||||||
./stream-recorder.sh create
|
./stream-recorder.sh create
|
||||||
|
|
||||||
# Interaktive Schnellaufnahme (URL direkt eingeben)
|
# Oder direkt loslegen
|
||||||
./stream-recorder.sh quick
|
|
||||||
|
|
||||||
# Vorhandene Aufnahme erneut nach Plik hochladen
|
|
||||||
./stream-recorder.sh upload /pfad/zur/aufnahme.mp4 "Stream-Name"
|
|
||||||
|
|
||||||
# Aufnahme mit ausführlicher Ausgabe
|
|
||||||
./stream-recorder.sh -V record mein-stream
|
|
||||||
|
|
||||||
# Verfügbare Jobs und Zeitpläne anzeigen
|
|
||||||
./stream-recorder.sh list
|
|
||||||
|
|
||||||
# Laufende Aufnahmen und Scheduler-Status anzeigen
|
|
||||||
./stream-recorder.sh status
|
|
||||||
|
|
||||||
# Log-Ausgabe live verfolgen
|
|
||||||
./stream-recorder.sh logs
|
|
||||||
|
|
||||||
# Einzelne Aufnahme stoppen
|
|
||||||
./stream-recorder.sh stop mein-stream
|
|
||||||
|
|
||||||
# Alle Aufnahmen stoppen
|
|
||||||
./stream-recorder.sh stop-all
|
|
||||||
```
|
|
||||||
|
|
||||||
### Schnellaufnahme
|
|
||||||
|
|
||||||
Mit dem `quick`-Befehl kann ein beliebiger Stream ohne Job-Datei aufgezeichnet werden:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./stream-recorder.sh quick
|
./stream-recorder.sh quick
|
||||||
```
|
```
|
||||||
|
|
||||||
Das Script fragt interaktiv nach URL, Name, Typ und Dauer. Die Dauer kann in verschiedenen Formaten angegeben werden: `3600` (Sekunden), `1h`, `30m`, `1h30m`.
|
## Befehle
|
||||||
|
|
||||||
### Job-Angabe
|
|
||||||
|
|
||||||
Der `record`-Befehl akzeptiert verschiedene Formate:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./stream-recorder.sh record jobs/mein-stream.job # Vollständiger Pfad
|
./stream-recorder.sh record <job> # Aufnahme starten
|
||||||
./stream-recorder.sh record mein-stream.job # Nur Dateiname
|
./stream-recorder.sh stop <job> # Aufnahme stoppen
|
||||||
./stream-recorder.sh record mein-stream # Nur Job-Name
|
./stream-recorder.sh status # Laufende Aufnahmen anzeigen
|
||||||
|
./stream-recorder.sh list # Alle Jobs anzeigen
|
||||||
|
./stream-recorder.sh create # Neuen Job erstellen
|
||||||
|
./stream-recorder.sh quick # Schnellaufnahme
|
||||||
|
./stream-recorder.sh logs # Logs live verfolgen
|
||||||
```
|
```
|
||||||
|
|
||||||
## Zeitplanung
|
## Dokumentation
|
||||||
|
|
||||||
Die Zeitplanung wird direkt in der Job-Datei konfiguriert - kein Cron nötig.
|
| Thema | Beschreibung |
|
||||||
|
|-------|-------------|
|
||||||
### So funktioniert's
|
| [Installation](docs/installation.md) | Installation, Updates, Deinstallation |
|
||||||
|
| [Konfiguration](docs/konfiguration.md) | Alle Konfigurationsparameter |
|
||||||
1. **Job-Datei erstellen** und `SCHEDULE_*`-Parameter setzen
|
| [Job-Dateien](docs/jobs.md) | Jobs erstellen und verwalten |
|
||||||
2. **Scheduler installieren** mit `sudo install/install.sh` (einmalig)
|
| [Zeitplanung](docs/zeitplanung.md) | Automatische Aufnahmezeiten |
|
||||||
3. Der systemd-Timer prüft **jede Minute** alle Jobs und startet/stoppt Aufnahmen automatisch
|
| [Segment-Splitting](docs/segment-splitting.md) | Aufnahmen in Teile zerlegen |
|
||||||
|
| [Plik-Upload](docs/plik-upload.md) | Automatischer Upload auf Plik |
|
||||||
### Verhalten
|
| [Benachrichtigungen](docs/benachrichtigungen.md) | NTFY Push-Nachrichten |
|
||||||
|
| [Befehle](docs/befehle.md) | Alle Befehle im Detail |
|
||||||
- **Start + Stop gesetzt:** Aufnahme läuft im angegebenen Zeitfenster
|
| [Beispiele](docs/beispiele.md) | Praxisbeispiele und Workflows |
|
||||||
- **Nur Start gesetzt:** Aufnahme startet zur angegebenen Zeit, läuft bis `MAX_DURATION` oder manueller Stop
|
|
||||||
- **Einmalig:** Mit `SCHEDULE_ONCE=true` und `SCHEDULE_DATE="YYYY-MM-DD"` startet der Job nur an diesem Datum
|
|
||||||
- **Manuell gestartete Aufnahmen** werden vom Scheduler nicht beeinflusst
|
|
||||||
- **Nach Neustart** des Servers erkennt der Scheduler automatisch, welche Aufnahmen laufen sollten
|
|
||||||
|
|
||||||
### NTFY-Benachrichtigungen
|
|
||||||
|
|
||||||
Push-Nachrichten via [ntfy](https://ntfy.sh) (selbst gehostet oder öffentlich). In der Konfigurationsdatei:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
NTFY_URL="https://ntfy.sh/mein-stream-topic"
|
|
||||||
NTFY_TOKEN="" # Optional, nur bei geschützten Topics
|
|
||||||
NTFY_EVENTS="start,stop,error" # Kommasepariert: start, stop, error
|
|
||||||
```
|
|
||||||
|
|
||||||
| Event | Auslöser | Priorität |
|
|
||||||
|---------|-----------------------------------------------------|-----------|
|
|
||||||
| `start` | Aufnahme wurde gestartet | Normal |
|
|
||||||
| `stop` | Aufnahme wurde beendet (regulär oder manuell) | Normal |
|
|
||||||
| `error` | Verbindung verloren oder max. Versuche erreicht | Hoch |
|
|
||||||
| `upload` | Datei wurde auf Plik hochgeladen (mit Download-Link)| Normal |
|
|
||||||
|
|
||||||
## Auto-Reconnect
|
|
||||||
|
|
||||||
Bei Verbindungsabbrüchen versucht das Script automatisch, die Verbindung wiederherzustellen:
|
|
||||||
|
|
||||||
- `MAX_RETRIES=0` - Unbegrenzte Wiederverbindungsversuche (Standard)
|
|
||||||
- `MAX_RETRIES=5` - Maximal 5 Versuche, danach Abbruch mit NTFY-Benachrichtigung
|
|
||||||
- `RETRY_DELAY=5` - 5 Sekunden Wartezeit zwischen den Versuchen
|
|
||||||
|
|
||||||
Bei jedem Reconnect wird eine neue Datei mit aktuellem Zeitstempel erzeugt.
|
|
||||||
|
|
||||||
## Plik-Upload
|
|
||||||
|
|
||||||
Fertige Aufnahmen können automatisch auf einen [Plik](https://github.com/root-gg/plik)-Server hochgeladen werden.
|
|
||||||
|
|
||||||
### Einrichtung
|
|
||||||
|
|
||||||
1. **Plik-Server konfigurieren** in `config/stream-recorder.conf`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
PLIK_URL="https://plik.example.com"
|
|
||||||
PLIK_API_KEY="" # Optional, nur bei geschützten Servern
|
|
||||||
PLIK_TTL="30d" # Gültigkeitsdauer: 30d, 24h, oder Sekunden
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Pro Job aktivieren** in der Job-Datei:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
PLIK_ENABLED=true
|
|
||||||
```
|
|
||||||
|
|
||||||
### Verhalten
|
|
||||||
|
|
||||||
- Der Upload erfolgt automatisch nach jeder regulär abgeschlossenen Aufnahme
|
|
||||||
- Bei manuellem Stop oder Verbindungsabbruch wird kein Upload ausgeführt
|
|
||||||
- Der Browser-Link zur Plik-Upload-Seite wird im Log und per NTFY ausgegeben
|
|
||||||
- Die direkte Datei-URL wird zusätzlich ins Log geschrieben
|
|
||||||
- Optional wird der Link per NTFY-Benachrichtigung gesendet (Event: `upload`)
|
|
||||||
- Plik-Uploads verwenden den direkten Multipart-Upload-Endpunkt des Servers.
|
|
||||||
|
|
||||||
### NTFY + Plik
|
|
||||||
|
|
||||||
Um den Download-Link per Push-Nachricht zu erhalten:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
NTFY_EVENTS="start,stop,error,upload"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Logging
|
|
||||||
|
|
||||||
Logs werden nach `LOG_DIR` geschrieben (Standard: `/var/log/stream-recorder/`):
|
|
||||||
|
|
||||||
- `stream-recorder.log` - Alle Operationen des Scripts
|
|
||||||
- `ffmpeg_<job-id>.log` - ffmpeg-Ausgabe pro Job
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Logs in Echtzeit verfolgen
|
|
||||||
./stream-recorder.sh logs
|
|
||||||
|
|
||||||
# Aufnahme mit ausführlicher Ausgabe starten
|
|
||||||
./stream-recorder.sh -V record mein-stream
|
|
||||||
```
|
|
||||||
|
|
||||||
## Verzeichnisstruktur
|
## Verzeichnisstruktur
|
||||||
|
|
||||||
@@ -354,12 +91,13 @@ Logs werden nach `LOG_DIR` geschrieben (Standard: `/var/log/stream-recorder/`):
|
|||||||
stream-recorder/
|
stream-recorder/
|
||||||
├── stream-recorder.sh # Hauptscript
|
├── stream-recorder.sh # Hauptscript
|
||||||
├── config/
|
├── config/
|
||||||
│ └── stream-recorder.conf # Konfiguration
|
│ └── stream-recorder.conf.dist # Konfigurations-Vorlage
|
||||||
├── jobs/ # Job-Definitionen
|
├── jobs/ # Job-Definitionen
|
||||||
│ └── templates/ # Vorlagen
|
│ └── templates/ # Vorlagen
|
||||||
│ ├── example-rtmp.job.dist
|
│ ├── example-rtmp.job.dist
|
||||||
│ ├── example-mp3.job.dist
|
│ ├── example-hls.job.dist
|
||||||
│ └── example-hls.job.dist
|
│ └── example-mp3.job.dist
|
||||||
|
├── docs/ # Dokumentation
|
||||||
├── install/
|
├── install/
|
||||||
│ ├── install.sh # Scheduler installieren
|
│ ├── install.sh # Scheduler installieren
|
||||||
│ └── uninstall.sh # Scheduler deinstallieren
|
│ └── uninstall.sh # Scheduler deinstallieren
|
||||||
|
|||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
# Befehle
|
||||||
|
|
||||||
|
## Übersicht
|
||||||
|
|
||||||
|
| Befehl | Beschreibung |
|
||||||
|
|----------------------|-------------------------------------------------|
|
||||||
|
| `record <job>` | Einzelnen Stream aufzeichnen |
|
||||||
|
| `record-all` | Alle Jobs gleichzeitig aufzeichnen |
|
||||||
|
| `create` | Neuen Job interaktiv erstellen |
|
||||||
|
| `quick` | Schnellaufnahme (URL direkt eingeben) |
|
||||||
|
| `upload <datei>` | Vorhandene Datei nach Plik hochladen |
|
||||||
|
| `stop <name>` | Aufnahme stoppen (Teilaufnahme wird hochgeladen)|
|
||||||
|
| `stop-all` | Alle Aufnahmen stoppen |
|
||||||
|
| `list` | Jobs und deren Zeitpläne anzeigen |
|
||||||
|
| `status` | Laufende Aufnahmen mit Details anzeigen |
|
||||||
|
| `scheduler` | Zeitplanung prüfen und Jobs starten/stoppen |
|
||||||
|
| `logs` | Log-Ausgabe live verfolgen |
|
||||||
|
|
||||||
|
## Optionen
|
||||||
|
|
||||||
|
| Option | Beschreibung |
|
||||||
|
|--------------------------|---------------------------------------|
|
||||||
|
| `-c, --config <datei>` | Alternative Konfigurationsdatei |
|
||||||
|
| `-V, --verbose` | Ausführliche Ausgabe |
|
||||||
|
| `--foreground` | Aufnahme im Vordergrund ausführen |
|
||||||
|
| `-h, --help` | Hilfe anzeigen |
|
||||||
|
| `-v, --version` | Version anzeigen |
|
||||||
|
|
||||||
|
## Details
|
||||||
|
|
||||||
|
### record
|
||||||
|
|
||||||
|
Startet eine Aufnahme im Hintergrund (Standard) oder Vordergrund (`--foreground`).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stream-recorder.sh record mein-stream
|
||||||
|
./stream-recorder.sh --foreground record mein-stream
|
||||||
|
./stream-recorder.sh -V record mein-stream
|
||||||
|
```
|
||||||
|
|
||||||
|
### create
|
||||||
|
|
||||||
|
Erstellt interaktiv eine neue Job-Datei. Fragt nach URL, Name, Typ, Segment-Splitting, Zeitplan und Plik-Upload.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stream-recorder.sh create
|
||||||
|
```
|
||||||
|
|
||||||
|
### quick
|
||||||
|
|
||||||
|
Schnellaufnahme ohne Job-Datei. Fragt nach URL, Name, Typ und Dauer.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stream-recorder.sh quick
|
||||||
|
```
|
||||||
|
|
||||||
|
### stop / stop-all
|
||||||
|
|
||||||
|
Stoppt eine laufende Aufnahme. Wenn Plik aktiviert ist, wird die Teilaufnahme hochgeladen. Bei Segment-Splitting werden alle verbleibenden Segmente hochgeladen.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stream-recorder.sh stop mein-stream
|
||||||
|
./stream-recorder.sh stop-all
|
||||||
|
```
|
||||||
|
|
||||||
|
### status
|
||||||
|
|
||||||
|
Zeigt detaillierte Informationen zu laufenden Aufnahmen:
|
||||||
|
- Stream-Name und Typ
|
||||||
|
- PID und Startmodus (manuell/geplant)
|
||||||
|
- Startzeit und bisherige Laufzeit
|
||||||
|
- Dateigröße bzw. Anzahl der Segmente und Gesamtgröße
|
||||||
|
- Scheduler-Status (systemd-Timer)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stream-recorder.sh status
|
||||||
|
```
|
||||||
|
|
||||||
|
### list
|
||||||
|
|
||||||
|
Zeigt alle konfigurierten Jobs mit Status, Zeitplan, Segment-Einstellungen und Plik-Konfiguration.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stream-recorder.sh list
|
||||||
|
```
|
||||||
|
|
||||||
|
### logs
|
||||||
|
|
||||||
|
Verfolgt die Log-Ausgabe in Echtzeit (`tail -f`).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stream-recorder.sh logs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
Logs werden nach `LOG_DIR` geschrieben (Standard: `/var/log/stream-recorder/`):
|
||||||
|
|
||||||
|
| Datei | Inhalt |
|
||||||
|
|----------------------------|---------------------------------|
|
||||||
|
| `stream-recorder.log` | Alle Operationen des Scripts |
|
||||||
|
| `ffmpeg_<job-id>.log` | ffmpeg-Ausgabe pro Job |
|
||||||
|
| `nohup_<job-id>.log` | Ausgabe im Hintergrund-Modus |
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
# Praxisbeispiele
|
||||||
|
|
||||||
|
## RTMP-Livestream täglich aufzeichnen
|
||||||
|
|
||||||
|
Tägliche Aufnahme eines Twitch-/YouTube-/RTMP-Streams von 20:00 bis 22:00 Uhr mit Plik-Upload:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# jobs/abend-livestream.job
|
||||||
|
STREAM_NAME="Abend-Livestream"
|
||||||
|
STREAM_URL="rtmp://example.com/live/stream-key"
|
||||||
|
STREAM_TYPE="auto"
|
||||||
|
OUTPUT_FORMAT=""
|
||||||
|
MAX_DURATION=""
|
||||||
|
EXTRA_FFMPEG_ARGS=""
|
||||||
|
|
||||||
|
SEGMENT_DURATION=""
|
||||||
|
|
||||||
|
SCHEDULE_ENABLED=true
|
||||||
|
SCHEDULE_DAYS="*"
|
||||||
|
SCHEDULE_START="20:00"
|
||||||
|
SCHEDULE_STOP="22:00"
|
||||||
|
|
||||||
|
PLIK_ENABLED=true
|
||||||
|
DELETE_AFTER_UPLOAD=false
|
||||||
|
```
|
||||||
|
|
||||||
|
## Radio-Mitschnitt werktags
|
||||||
|
|
||||||
|
Werktags um 18:00 Uhr eine Stunde Radio mitschneiden:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# jobs/radio-abends.job
|
||||||
|
STREAM_NAME="Radio-Abendshow"
|
||||||
|
STREAM_URL="https://example.com/stream.mp3"
|
||||||
|
STREAM_TYPE="auto"
|
||||||
|
OUTPUT_FORMAT=""
|
||||||
|
MAX_DURATION="1h"
|
||||||
|
EXTRA_FFMPEG_ARGS=""
|
||||||
|
|
||||||
|
SEGMENT_DURATION=""
|
||||||
|
|
||||||
|
SCHEDULE_ENABLED=true
|
||||||
|
SCHEDULE_DAYS="Mo-Fr"
|
||||||
|
SCHEDULE_START="18:00"
|
||||||
|
SCHEDULE_STOP=""
|
||||||
|
|
||||||
|
PLIK_ENABLED=false
|
||||||
|
```
|
||||||
|
|
||||||
|
## 24/7-Stream mit stündlichem Upload
|
||||||
|
|
||||||
|
Ein Überwachungsstream, der dauerhaft läuft und stündlich Segmente zu Plik hochlädt. Lokale Dateien werden nach dem Upload gelöscht:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# jobs/ueberwachung.job
|
||||||
|
STREAM_NAME="Kamera-Front"
|
||||||
|
STREAM_URL="rtmp://192.168.1.100/live/cam1"
|
||||||
|
STREAM_TYPE="auto"
|
||||||
|
OUTPUT_FORMAT="mp4"
|
||||||
|
MAX_DURATION=""
|
||||||
|
EXTRA_FFMPEG_ARGS=""
|
||||||
|
|
||||||
|
SEGMENT_DURATION="1h"
|
||||||
|
|
||||||
|
SCHEDULE_ENABLED=false
|
||||||
|
|
||||||
|
PLIK_ENABLED=true
|
||||||
|
DELETE_AFTER_UPLOAD=true
|
||||||
|
```
|
||||||
|
|
||||||
|
Starten mit:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stream-recorder.sh record ueberwachung
|
||||||
|
```
|
||||||
|
|
||||||
|
## Podcast-/HLS-Stream in 2-Stunden-Blöcken
|
||||||
|
|
||||||
|
Ein langer HLS-Livestream (z.B. Konferenz) in 2-Stunden-Segmenten aufzeichnen, jedes Segment wird sofort hochgeladen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# jobs/konferenz.job
|
||||||
|
STREAM_NAME="Dev-Konferenz-2026"
|
||||||
|
STREAM_URL="https://example.com/live/playlist.m3u8"
|
||||||
|
STREAM_TYPE="hls"
|
||||||
|
OUTPUT_FORMAT="mp4"
|
||||||
|
MAX_DURATION="10h"
|
||||||
|
EXTRA_FFMPEG_ARGS=""
|
||||||
|
|
||||||
|
SEGMENT_DURATION="2h"
|
||||||
|
|
||||||
|
SCHEDULE_ENABLED=false
|
||||||
|
|
||||||
|
PLIK_ENABLED=true
|
||||||
|
DELETE_AFTER_UPLOAD=false
|
||||||
|
```
|
||||||
|
|
||||||
|
## Einmalige Aufnahme zu einem bestimmten Termin
|
||||||
|
|
||||||
|
Ein Event am 15.09.2026 um 21:00 Uhr aufnehmen, 2 Stunden:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# jobs/einmaliges-event.job
|
||||||
|
STREAM_NAME="Einmaliges-Event"
|
||||||
|
STREAM_URL="https://example.com/live/playlist.m3u8"
|
||||||
|
STREAM_TYPE="hls"
|
||||||
|
OUTPUT_FORMAT="mp4"
|
||||||
|
MAX_DURATION="2h"
|
||||||
|
EXTRA_FFMPEG_ARGS=""
|
||||||
|
|
||||||
|
SEGMENT_DURATION=""
|
||||||
|
|
||||||
|
SCHEDULE_ENABLED=true
|
||||||
|
SCHEDULE_ONCE=true
|
||||||
|
SCHEDULE_DATE="2026-09-15"
|
||||||
|
SCHEDULE_START="21:00"
|
||||||
|
SCHEDULE_STOP=""
|
||||||
|
|
||||||
|
PLIK_ENABLED=true
|
||||||
|
DELETE_AFTER_UPLOAD=false
|
||||||
|
```
|
||||||
|
|
||||||
|
## Schnellaufnahme ohne Job-Datei
|
||||||
|
|
||||||
|
Für spontane Aufnahmen ohne Konfiguration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stream-recorder.sh quick
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Script fragt interaktiv nach URL, Name, Typ und Dauer.
|
||||||
|
|
||||||
|
## Typischer Workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Konfiguration erstellen (einmalig)
|
||||||
|
cp config/stream-recorder.conf.dist config/stream-recorder.conf
|
||||||
|
nano config/stream-recorder.conf
|
||||||
|
|
||||||
|
# 2. Scheduler installieren (einmalig)
|
||||||
|
sudo install/install.sh
|
||||||
|
|
||||||
|
# 3. Job erstellen
|
||||||
|
./stream-recorder.sh create
|
||||||
|
|
||||||
|
# 4. Jobs anzeigen
|
||||||
|
./stream-recorder.sh list
|
||||||
|
|
||||||
|
# 5. Status prüfen
|
||||||
|
./stream-recorder.sh status
|
||||||
|
|
||||||
|
# 6. Logs verfolgen
|
||||||
|
./stream-recorder.sh logs
|
||||||
|
|
||||||
|
# 7. Aufnahme stoppen (falls nötig)
|
||||||
|
./stream-recorder.sh stop mein-stream
|
||||||
|
```
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# NTFY-Benachrichtigungen
|
||||||
|
|
||||||
|
Push-Nachrichten via [ntfy](https://ntfy.sh) (selbst gehostet oder öffentlich) bei wichtigen Ereignissen.
|
||||||
|
|
||||||
|
## Einrichtung
|
||||||
|
|
||||||
|
In `config/stream-recorder.conf`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
NTFY_URL="https://ntfy.sh/mein-stream-topic"
|
||||||
|
NTFY_TOKEN="" # Optional, nur bei geschützten Topics
|
||||||
|
NTFY_EVENTS="start,stop,error" # Kommasepariert
|
||||||
|
```
|
||||||
|
|
||||||
|
## Events
|
||||||
|
|
||||||
|
| Event | Auslöser | Priorität |
|
||||||
|
|----------|-----------------------------------------------------|-----------|
|
||||||
|
| `start` | Aufnahme wurde gestartet | Normal |
|
||||||
|
| `stop` | Aufnahme beendet (regulär oder manuell) | Normal |
|
||||||
|
| `error` | Verbindung verloren oder max. Versuche erreicht | Hoch |
|
||||||
|
| `upload` | Datei auf Plik hochgeladen (mit Download-Link) | Normal |
|
||||||
|
|
||||||
|
## Alle Events aktivieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
NTFY_EVENTS="start,stop,error,upload"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Nur Fehler (Standard)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
NTFY_EVENTS="error"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hinweise
|
||||||
|
|
||||||
|
- Benachrichtigungen werden asynchron im Hintergrund gesendet
|
||||||
|
- Ein fehlgeschlagener Versand blockiert die Aufnahme nicht
|
||||||
|
- curl muss installiert sein (`sudo apt install curl`)
|
||||||
|
- Ohne gesetzte `NTFY_URL` sind Benachrichtigungen komplett deaktiviert
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# Installation
|
||||||
|
|
||||||
|
## Voraussetzungen
|
||||||
|
|
||||||
|
| Komponente | Anforderung |
|
||||||
|
|------------------|------------------------------------------|
|
||||||
|
| Betriebssystem | Linux (getestet unter Ubuntu/Debian) |
|
||||||
|
| Bash | Version 4.0 oder neuer |
|
||||||
|
| ffmpeg | Pflicht (`sudo apt install ffmpeg`) |
|
||||||
|
| curl | Optional, für NTFY und Plik-Upload |
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Repository klonen
|
||||||
|
git clone https://git.techniverse.net/scriptos/stream-recorder.git /opt/stream-recorder
|
||||||
|
cd /opt/stream-recorder
|
||||||
|
|
||||||
|
# Konfiguration aus Vorlage erstellen und anpassen
|
||||||
|
cp config/stream-recorder.conf.dist config/stream-recorder.conf
|
||||||
|
nano config/stream-recorder.conf
|
||||||
|
|
||||||
|
# Scheduler installieren (systemd-Timer)
|
||||||
|
sudo install/install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Install-Script:
|
||||||
|
- Macht das Hauptscript ausführbar
|
||||||
|
- Installiert fehlende Abhängigkeiten (ffmpeg)
|
||||||
|
- Erstellt die Konfigurationsdatei aus der Vorlage (falls nicht vorhanden)
|
||||||
|
- Richtet einen systemd-Timer ein, der jede Minute die Zeitpläne prüft
|
||||||
|
|
||||||
|
## Updates
|
||||||
|
|
||||||
|
Da die Konfiguration als Kopie (`stream-recorder.conf`) vorliegt und die Vorlage (`stream-recorder.conf.dist`) getrennt gepflegt wird, gehen eigene Einstellungen bei einem `git pull` nicht verloren.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/stream-recorder
|
||||||
|
git pull
|
||||||
|
```
|
||||||
|
|
||||||
|
Nach einem Update sollte man prüfen, ob in `stream-recorder.conf.dist` neue Parameter hinzugekommen sind, und diese ggf. in die eigene `stream-recorder.conf` übernehmen.
|
||||||
|
|
||||||
|
## Deinstallation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo install/uninstall.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Entfernt den systemd-Timer und stoppt optional alle laufenden Aufnahmen. Job-Dateien, Konfiguration und Aufnahmen bleiben erhalten.
|
||||||
|
|
||||||
|
## Ohne systemd
|
||||||
|
|
||||||
|
Das Script funktioniert auch ohne Scheduler. In dem Fall muss man Aufnahmen manuell starten:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stream-recorder.sh record mein-stream
|
||||||
|
```
|
||||||
|
|
||||||
|
Oder den Scheduler manuell per Cron aufrufen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# crontab -e
|
||||||
|
* * * * * /opt/stream-recorder/stream-recorder.sh scheduler
|
||||||
|
```
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# Job-Dateien
|
||||||
|
|
||||||
|
Jeder Stream wird durch eine `.job`-Datei im `jobs/`-Verzeichnis definiert. Vorlagen befinden sich unter `jobs/templates/`.
|
||||||
|
|
||||||
|
## Job erstellen
|
||||||
|
|
||||||
|
### Interaktiv (empfohlen)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stream-recorder.sh create
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Script fragt Schritt für Schritt nach URL, Name, Typ, Segment-Splitting, Zeitplan und Plik-Upload und erstellt die Job-Datei automatisch.
|
||||||
|
|
||||||
|
### Aus Vorlage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Vorlage kopieren
|
||||||
|
cp jobs/templates/example-rtmp.job.dist jobs/mein-stream.job
|
||||||
|
|
||||||
|
# Anpassen
|
||||||
|
nano jobs/mein-stream.job
|
||||||
|
```
|
||||||
|
|
||||||
|
Verfügbare Vorlagen:
|
||||||
|
|
||||||
|
| Vorlage | Beschreibung |
|
||||||
|
|----------------------------|-------------------------|
|
||||||
|
| `example-rtmp.job.dist` | RTMP/RTMPS-Stream |
|
||||||
|
| `example-hls.job.dist` | HLS/m3u8-Stream |
|
||||||
|
| `example-mp3.job.dist` | HTTP-Audio (MP3/Radio) |
|
||||||
|
|
||||||
|
## Stream-Parameter
|
||||||
|
|
||||||
|
| Parameter | Pflicht | Beschreibung |
|
||||||
|
|----------------------|---------|-----------------------------------------------------|
|
||||||
|
| `STREAM_NAME` | Ja | Name des Streams (wird für Dateinamen verwendet) |
|
||||||
|
| `STREAM_URL` | Ja | URL des Streams |
|
||||||
|
| `STREAM_TYPE` | Nein | `auto`, `rtmp`, `hls`, `mp3`, `aac`, `ogg`, `flac` |
|
||||||
|
| `OUTPUT_FORMAT` | Nein | Dateiendung (z.B. `mp4`, `mp3`) — sonst automatisch |
|
||||||
|
| `MAX_DURATION` | Nein | Aufnahmedauer (z.B. `1h`, `30m`, `1d12h`, `3600`) |
|
||||||
|
| `EXTRA_FFMPEG_ARGS` | Nein | Zusätzliche ffmpeg-Parameter |
|
||||||
|
| `SEGMENT_DURATION` | Nein | Segment-Splitting (z.B. `1h`, `2h`) — siehe [Segment-Splitting](segment-splitting.md) |
|
||||||
|
| `PLIK_ENABLED` | Nein | Plik-Upload aktivieren (`true`/`false`) |
|
||||||
|
| `DELETE_AFTER_UPLOAD` | Nein | Lokale Datei nach Upload löschen (`true`/`false`) |
|
||||||
|
|
||||||
|
### Dauerformate
|
||||||
|
|
||||||
|
Die Dauer kann in verschiedenen Formaten angegeben werden:
|
||||||
|
|
||||||
|
| Format | Bedeutung |
|
||||||
|
|----------|------------------|
|
||||||
|
| `3600` | 3600 Sekunden |
|
||||||
|
| `1h` | 1 Stunde |
|
||||||
|
| `30m` | 30 Minuten |
|
||||||
|
| `1h30m` | 90 Minuten |
|
||||||
|
| `2d` | 2 Tage |
|
||||||
|
| `1d12h` | 36 Stunden |
|
||||||
|
|
||||||
|
## Job-Angabe beim Starten
|
||||||
|
|
||||||
|
Der `record`-Befehl akzeptiert verschiedene Formate:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stream-recorder.sh record jobs/mein-stream.job # Vollständiger Pfad
|
||||||
|
./stream-recorder.sh record mein-stream.job # Nur Dateiname
|
||||||
|
./stream-recorder.sh record mein-stream # Nur Job-Name (ohne .job)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Jobs anzeigen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stream-recorder.sh list
|
||||||
|
```
|
||||||
|
|
||||||
|
Zeigt alle Jobs mit Stream-Typ, URL, Status, Zeitplan, Segment-Einstellungen und Plik-Konfiguration.
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# Konfiguration
|
||||||
|
|
||||||
|
Die Konfigurationsdatei liegt unter `config/stream-recorder.conf`. Sie wird aus der Vorlage `config/stream-recorder.conf.dist` erstellt:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp config/stream-recorder.conf.dist config/stream-recorder.conf
|
||||||
|
nano config/stream-recorder.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Die `.conf`-Datei wird von Git ignoriert und bleibt bei Updates erhalten.
|
||||||
|
|
||||||
|
## Parameter
|
||||||
|
|
||||||
|
### Basis-Einstellungen
|
||||||
|
|
||||||
|
| Parameter | Standard | Beschreibung |
|
||||||
|
|-----------------|------------------------------|-------------------------------------------|
|
||||||
|
| `DOWNLOAD_PATH` | `/home/downloads/recordings` | Basis-Verzeichnis für Aufnahmen |
|
||||||
|
| `LOG_DIR` | `/var/log/stream-recorder` | Log-Verzeichnis |
|
||||||
|
| `LOG_LEVEL` | `INFO` | Log-Level: `DEBUG`, `INFO`, `WARN`, `ERROR` |
|
||||||
|
| `PID_DIR` | `/tmp/stream-recorder` | PID-Dateien für laufende Aufnahmen |
|
||||||
|
| `STATE_DIR` | `/var/lib/stream-recorder` | Status für bereits gestartete Zeitpläne |
|
||||||
|
|
||||||
|
### Reconnect
|
||||||
|
|
||||||
|
| Parameter | Standard | Beschreibung |
|
||||||
|
|---------------|----------|-------------------------------------------|
|
||||||
|
| `MAX_RETRIES` | `0` | Wiederholungsversuche (0 = unbegrenzt) |
|
||||||
|
| `RETRY_DELAY` | `5` | Wartezeit zwischen Versuchen (Sekunden) |
|
||||||
|
|
||||||
|
Bei Verbindungsabbrüchen versucht das Script automatisch, die Verbindung wiederherzustellen. Bei jedem Reconnect wird eine neue Datei mit aktuellem Zeitstempel erzeugt.
|
||||||
|
|
||||||
|
- `MAX_RETRIES=0` — Unbegrenzte Versuche (empfohlen für Langzeitaufnahmen)
|
||||||
|
- `MAX_RETRIES=5` — Maximal 5 Versuche, danach Abbruch mit Benachrichtigung
|
||||||
|
|
||||||
|
### NTFY-Benachrichtigungen
|
||||||
|
|
||||||
|
Push-Nachrichten via [ntfy](https://ntfy.sh) (selbst gehostet oder öffentlich).
|
||||||
|
|
||||||
|
| Parameter | Standard | Beschreibung |
|
||||||
|
|---------------|----------|-------------------------------------------|
|
||||||
|
| `NTFY_URL` | (leer) | NTFY Server-URL inkl. Topic |
|
||||||
|
| `NTFY_TOKEN` | (leer) | NTFY Access-Token (optional) |
|
||||||
|
| `NTFY_EVENTS` | `error` | Benachrichtigungs-Events (kommasepariert) |
|
||||||
|
|
||||||
|
Siehe [Benachrichtigungen](benachrichtigungen.md) für Details zu den Events.
|
||||||
|
|
||||||
|
### Plik-Upload
|
||||||
|
|
||||||
|
Fertige Aufnahmen auf einen [Plik](https://github.com/root-gg/plik)-Server hochladen.
|
||||||
|
|
||||||
|
| Parameter | Standard | Beschreibung |
|
||||||
|
|----------------|----------|-------------------------------------------|
|
||||||
|
| `PLIK_URL` | (leer) | Plik Server-URL |
|
||||||
|
| `PLIK_API_KEY` | (leer) | Plik API-Key (optional) |
|
||||||
|
| `PLIK_TTL` | `30d` | Gültigkeitsdauer: `30d`, `24h`, Sekunden |
|
||||||
|
|
||||||
|
Siehe [Plik-Upload](plik-upload.md) für die Einrichtung pro Job.
|
||||||
|
|
||||||
|
## Alternative Konfigurationsdatei
|
||||||
|
|
||||||
|
Eine andere Konfigurationsdatei kann per `-c` übergeben werden:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stream-recorder.sh -c /pfad/zur/config.conf record mein-stream
|
||||||
|
```
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# Plik-Upload
|
||||||
|
|
||||||
|
Fertige Aufnahmen können automatisch auf einen [Plik](https://github.com/root-gg/plik)-Server hochgeladen werden. Die Browser-URL zum Download wird im Log und optional per NTFY-Benachrichtigung ausgegeben.
|
||||||
|
|
||||||
|
## Einrichtung
|
||||||
|
|
||||||
|
### 1. Plik-Server konfigurieren
|
||||||
|
|
||||||
|
In `config/stream-recorder.conf`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PLIK_URL="https://plik.example.com"
|
||||||
|
PLIK_API_KEY="" # Optional, nur bei geschützten Servern
|
||||||
|
PLIK_TTL="30d" # Gültigkeitsdauer: 30d, 24h, oder Sekunden
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Pro Job aktivieren
|
||||||
|
|
||||||
|
In der Job-Datei:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PLIK_ENABLED=true
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Optional: Datei nach Upload löschen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
DELETE_AFTER_UPLOAD=true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verhalten
|
||||||
|
|
||||||
|
- Upload nach jeder regulär abgeschlossenen Aufnahme
|
||||||
|
- Upload nach manuellem Stop (`stop`-Befehl) — die Teilaufnahme wird hochgeladen
|
||||||
|
- Upload nach Verbindungsabbruch, wenn eine Teildatei vorhanden ist
|
||||||
|
- Browser-Link und Datei-URL werden ins Log geschrieben
|
||||||
|
- Optional wird der Link per NTFY gesendet (Event: `upload`)
|
||||||
|
|
||||||
|
## Mit Segment-Splitting
|
||||||
|
|
||||||
|
Bei aktiviertem [Segment-Splitting](segment-splitting.md) wird jedes Segment einzeln hochgeladen:
|
||||||
|
|
||||||
|
- Fertige Segmente werden während der Aufnahme hochgeladen (alle 60s geprüft)
|
||||||
|
- Das letzte Segment wird nach Ende der Aufnahme hochgeladen
|
||||||
|
- `DELETE_AFTER_UPLOAD=true` löscht jedes Segment nach dem Upload
|
||||||
|
|
||||||
|
## Manueller Upload
|
||||||
|
|
||||||
|
Eine vorhandene Datei kann auch manuell hochgeladen werden:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stream-recorder.sh upload /pfad/zur/aufnahme.mp4 "Stream-Name"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Download-Link per Push
|
||||||
|
|
||||||
|
Um den Plik-Link automatisch als Push-Nachricht zu erhalten:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# In config/stream-recorder.conf
|
||||||
|
NTFY_EVENTS="start,stop,error,upload"
|
||||||
|
```
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# Segment-Splitting
|
||||||
|
|
||||||
|
Lange Aufnahmen (z.B. 8-Stunden-Streams) können automatisch in kleinere Dateien geteilt werden. Jedes Segment wird einzeln nach Plik hochgeladen, sobald es fertig ist.
|
||||||
|
|
||||||
|
## Einrichtung
|
||||||
|
|
||||||
|
In der Job-Datei `SEGMENT_DURATION` setzen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Aufnahme alle 2 Stunden teilen
|
||||||
|
SEGMENT_DURATION="2h"
|
||||||
|
|
||||||
|
# Plik-Upload aktivieren (empfohlen)
|
||||||
|
PLIK_ENABLED=true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dauerformate
|
||||||
|
|
||||||
|
| Format | Bedeutung |
|
||||||
|
|----------|------------------|
|
||||||
|
| `30m` | Alle 30 Minuten |
|
||||||
|
| `1h` | Stündlich |
|
||||||
|
| `2h` | Alle 2 Stunden |
|
||||||
|
| `3600` | 3600 Sekunden |
|
||||||
|
|
||||||
|
## So funktioniert's
|
||||||
|
|
||||||
|
1. ffmpeg teilt die Aufnahme automatisch in Segmente der angegebenen Länge
|
||||||
|
2. Ein Hintergrund-Prozess prüft alle 60 Sekunden auf fertige Segmente
|
||||||
|
3. Fertige Segmente werden sofort nach Plik hochgeladen (falls aktiviert)
|
||||||
|
4. Beim Beenden (regulär oder per `stop`) wird das letzte Segment noch hochgeladen
|
||||||
|
|
||||||
|
### Dateinamen
|
||||||
|
|
||||||
|
Segmente erhalten fortlaufende Nummern:
|
||||||
|
|
||||||
|
```
|
||||||
|
Mein-Stream_20260907_200000_seg000.mp4
|
||||||
|
Mein-Stream_20260907_200000_seg001.mp4
|
||||||
|
Mein-Stream_20260907_200000_seg002.mp4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Kombination mit Plik
|
||||||
|
|
||||||
|
Segment-Splitting ist besonders nützlich in Kombination mit Plik-Upload:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SEGMENT_DURATION="1h"
|
||||||
|
PLIK_ENABLED=true
|
||||||
|
DELETE_AFTER_UPLOAD=true # Lokale Segmente nach Upload löschen
|
||||||
|
```
|
||||||
|
|
||||||
|
So wird jede Stunde ein Segment hochgeladen und lokal gelöscht. Das spart Speicherplatz bei langen Aufnahmen.
|
||||||
|
|
||||||
|
## Kombination mit Zeitplanung
|
||||||
|
|
||||||
|
Segment-Splitting und Zeitplanung lassen sich kombinieren:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SCHEDULE_ENABLED=true
|
||||||
|
SCHEDULE_DAYS="*"
|
||||||
|
SCHEDULE_START="08:00"
|
||||||
|
SCHEDULE_STOP="20:00"
|
||||||
|
SEGMENT_DURATION="2h"
|
||||||
|
PLIK_ENABLED=true
|
||||||
|
```
|
||||||
|
|
||||||
|
Die 12-Stunden-Aufnahme wird in 6 Segmente zu je 2 Stunden geteilt, jedes wird nach Fertigstellung hochgeladen.
|
||||||
|
|
||||||
|
## Verhalten bei Reconnect
|
||||||
|
|
||||||
|
Wenn die Verbindung abbricht und wiederhergestellt wird, werden die bisherigen Segmente hochgeladen und eine neue Segment-Serie mit neuem Zeitstempel begonnen.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Mit `./stream-recorder.sh status` werden bei segmentierten Aufnahmen die Anzahl der Segmente und die Gesamtgröße angezeigt.
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# Zeitplanung
|
||||||
|
|
||||||
|
Die Zeitplanung wird direkt in der Job-Datei konfiguriert — kein Cron-Wissen nötig. Ein systemd-Timer prüft jede Minute alle Jobs und startet/stoppt Aufnahmen automatisch.
|
||||||
|
|
||||||
|
## Einrichtung
|
||||||
|
|
||||||
|
1. **Job-Datei erstellen** und `SCHEDULE_*`-Parameter setzen
|
||||||
|
2. **Scheduler installieren** mit `sudo install/install.sh` (einmalig)
|
||||||
|
3. Fertig — der Timer prüft jede Minute alle Jobs
|
||||||
|
|
||||||
|
## Zeitplan-Parameter
|
||||||
|
|
||||||
|
| Parameter | Standard | Beschreibung |
|
||||||
|
|--------------------|----------|-------------------------------------------------|
|
||||||
|
| `SCHEDULE_ENABLED` | `false` | Zeitplan aktivieren (`true`/`false`) |
|
||||||
|
| `SCHEDULE_ONCE` | `false` | Einmaliger Termin statt wöchentlichem Zeitplan |
|
||||||
|
| `SCHEDULE_DATE` | (leer) | Datum für einmalige Termine (`YYYY-MM-DD`) |
|
||||||
|
| `SCHEDULE_DAYS` | `*` | Aufnahmetage für wöchentliche Zeitpläne |
|
||||||
|
| `SCHEDULE_START` | (leer) | Startzeit im Format `HH:MM` |
|
||||||
|
| `SCHEDULE_STOP` | (leer) | Endzeit im Format `HH:MM` (leer = kein Auto-Stop)|
|
||||||
|
|
||||||
|
## Tagesformate
|
||||||
|
|
||||||
|
| Format | Bedeutung |
|
||||||
|
|--------------|----------------------------|
|
||||||
|
| `*` | Täglich |
|
||||||
|
| `Mo-Fr` | Montag bis Freitag |
|
||||||
|
| `Sa,So` | Samstag und Sonntag |
|
||||||
|
| `Mo,Mi,Fr` | Einzelne Tage |
|
||||||
|
| `Mo-Fr,So` | Kombiniert |
|
||||||
|
|
||||||
|
Die Tagesangaben funktionieren auf Deutsch (Mo, Di, Mi, Do, Fr, Sa, So) und Englisch (Mon, Tue, Wed, Thu, Fri, Sat, Sun).
|
||||||
|
|
||||||
|
## Verhalten
|
||||||
|
|
||||||
|
- **Start + Stop gesetzt:** Aufnahme läuft im angegebenen Zeitfenster
|
||||||
|
- **Nur Start gesetzt:** Aufnahme startet zur angegebenen Zeit, läuft bis `MAX_DURATION` oder manueller Stop
|
||||||
|
- **Einmalig:** Mit `SCHEDULE_ONCE=true` und `SCHEDULE_DATE="YYYY-MM-DD"` startet der Job nur an diesem Datum
|
||||||
|
- **Manuell gestartete Aufnahmen** werden vom Scheduler nicht beeinflusst
|
||||||
|
- **Nach Neustart** des Servers erkennt der Scheduler automatisch, welche Aufnahmen laufen sollten
|
||||||
|
- **Nachtübergreifend:** Start- und Stoppzeiten können über Mitternacht gehen (z.B. `23:00` bis `02:00`)
|
||||||
|
|
||||||
|
## Beispiele
|
||||||
|
|
||||||
|
### Tägliche Aufnahme von 20:00 bis 22:00
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SCHEDULE_ENABLED=true
|
||||||
|
SCHEDULE_DAYS="*"
|
||||||
|
SCHEDULE_START="20:00"
|
||||||
|
SCHEDULE_STOP="22:00"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Werktags um 18:00, 1 Stunde
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SCHEDULE_ENABLED=true
|
||||||
|
SCHEDULE_DAYS="Mo-Fr"
|
||||||
|
SCHEDULE_START="18:00"
|
||||||
|
MAX_DURATION="1h"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nur am Wochenende
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SCHEDULE_ENABLED=true
|
||||||
|
SCHEDULE_DAYS="Sa,So"
|
||||||
|
SCHEDULE_START="14:00"
|
||||||
|
SCHEDULE_STOP="18:00"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Einmalige Aufnahme am 15.09.2026
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SCHEDULE_ENABLED=true
|
||||||
|
SCHEDULE_ONCE=true
|
||||||
|
SCHEDULE_DATE="2026-09-15"
|
||||||
|
SCHEDULE_START="21:00"
|
||||||
|
MAX_DURATION="30m"
|
||||||
|
```
|
||||||
+15
-4
@@ -39,6 +39,16 @@ fi
|
|||||||
# Hauptscript ausführbar machen
|
# Hauptscript ausführbar machen
|
||||||
chmod +x "${SCRIPT_DIR}/stream-recorder.sh"
|
chmod +x "${SCRIPT_DIR}/stream-recorder.sh"
|
||||||
|
|
||||||
|
# Konfiguration aus Vorlage erstellen, falls nicht vorhanden
|
||||||
|
if [[ ! -f "${SCRIPT_DIR}/config/stream-recorder.conf" ]]; then
|
||||||
|
if [[ -f "${SCRIPT_DIR}/config/stream-recorder.conf.dist" ]]; then
|
||||||
|
cp "${SCRIPT_DIR}/config/stream-recorder.conf.dist" "${SCRIPT_DIR}/config/stream-recorder.conf"
|
||||||
|
echo "Konfiguration erstellt: config/stream-recorder.conf"
|
||||||
|
echo " Bitte anpassen: nano ${SCRIPT_DIR}/config/stream-recorder.conf"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Installationspfad: ${SCRIPT_DIR}"
|
echo "Installationspfad: ${SCRIPT_DIR}"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
@@ -85,9 +95,10 @@ echo "Der Scheduler prüft jetzt jede Minute die Job-Dateien"
|
|||||||
echo "und startet/stoppt Aufnahmen automatisch."
|
echo "und startet/stoppt Aufnahmen automatisch."
|
||||||
echo ""
|
echo ""
|
||||||
echo "Nächste Schritte:"
|
echo "Nächste Schritte:"
|
||||||
echo " 1. Job-Datei erstellen: cp jobs/templates/example-rtmp.job.dist jobs/mein-stream.job"
|
echo " 1. Konfiguration anpassen: nano ${SCRIPT_DIR}/config/stream-recorder.conf"
|
||||||
echo " 2. Job-Datei bearbeiten: nano jobs/mein-stream.job"
|
echo " 2. Job-Datei erstellen: cp jobs/templates/example-rtmp.job.dist jobs/mein-stream.job"
|
||||||
echo " 3. SCHEDULE_ENABLED=true setzen und Zeiten konfigurieren"
|
echo " 3. Job-Datei bearbeiten: nano jobs/mein-stream.job"
|
||||||
echo " 4. Jobs prüfen: ./stream-recorder.sh list"
|
echo " 4. SCHEDULE_ENABLED=true setzen und Zeiten konfigurieren"
|
||||||
|
echo " 5. Jobs prüfen: ./stream-recorder.sh list"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Deinstallation: sudo install/uninstall.sh"
|
echo "Deinstallation: sudo install/uninstall.sh"
|
||||||
|
|||||||
@@ -22,6 +22,13 @@ MAX_DURATION=""
|
|||||||
# Zusätzliche ffmpeg-Argumente (optional)
|
# Zusätzliche ffmpeg-Argumente (optional)
|
||||||
EXTRA_FFMPEG_ARGS=""
|
EXTRA_FFMPEG_ARGS=""
|
||||||
|
|
||||||
|
###############################################
|
||||||
|
# Segment-Splitting (optional)
|
||||||
|
# z.B. 1h = stündlich, 2h = alle 2 Stunden
|
||||||
|
###############################################
|
||||||
|
|
||||||
|
SEGMENT_DURATION=""
|
||||||
|
|
||||||
###############################################
|
###############################################
|
||||||
# Zeitplanung (optional)
|
# Zeitplanung (optional)
|
||||||
###############################################
|
###############################################
|
||||||
|
|||||||
@@ -23,6 +23,13 @@ MAX_DURATION="1h"
|
|||||||
# Zusätzliche ffmpeg-Argumente (optional)
|
# Zusätzliche ffmpeg-Argumente (optional)
|
||||||
EXTRA_FFMPEG_ARGS=""
|
EXTRA_FFMPEG_ARGS=""
|
||||||
|
|
||||||
|
###############################################
|
||||||
|
# Segment-Splitting (optional)
|
||||||
|
# z.B. 1h = stündlich, 2h = alle 2 Stunden
|
||||||
|
###############################################
|
||||||
|
|
||||||
|
SEGMENT_DURATION=""
|
||||||
|
|
||||||
###############################################
|
###############################################
|
||||||
# Zeitplanung (optional)
|
# Zeitplanung (optional)
|
||||||
###############################################
|
###############################################
|
||||||
|
|||||||
@@ -25,6 +25,16 @@ MAX_DURATION=""
|
|||||||
# Zusätzliche ffmpeg-Argumente (optional)
|
# Zusätzliche ffmpeg-Argumente (optional)
|
||||||
EXTRA_FFMPEG_ARGS=""
|
EXTRA_FFMPEG_ARGS=""
|
||||||
|
|
||||||
|
###############################################
|
||||||
|
# Segment-Splitting (optional)
|
||||||
|
# Aufnahme in Teile zerlegen
|
||||||
|
# Jedes Segment wird einzeln nach Plik hochgeladen
|
||||||
|
# Beispiele: 1h = stündlich, 2h = alle 2 Stunden, 30m = alle 30 Minuten
|
||||||
|
# Leer = deaktiviert (eine durchgehende Datei)
|
||||||
|
###############################################
|
||||||
|
|
||||||
|
SEGMENT_DURATION=""
|
||||||
|
|
||||||
###############################################
|
###############################################
|
||||||
# Zeitplanung (optional)
|
# Zeitplanung (optional)
|
||||||
# Damit der Scheduler funktioniert, muss er
|
# Damit der Scheduler funktioniert, muss er
|
||||||
|
|||||||
+279
-11
@@ -8,15 +8,15 @@
|
|||||||
# Web: https://www.cleveradmin.de
|
# Web: https://www.cleveradmin.de
|
||||||
# Repository: https://git.techniverse.net/scriptos/stream-recorder.git
|
# Repository: https://git.techniverse.net/scriptos/stream-recorder.git
|
||||||
# License: MIT
|
# License: MIT
|
||||||
# Version: v2.6.2
|
# Version: v2.7.0
|
||||||
# Date: 06.09.2026
|
# Date: 07.09.2026
|
||||||
# Modifications: Mehrtägige Aufnahmen nun möglich
|
# Modifications: Segment-Splitting, erweiterter Status, verbessertes Stoppen
|
||||||
###############################################
|
###############################################
|
||||||
|
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
|
|
||||||
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
readonly VERSION="2.6.0"
|
readonly VERSION="2.7.0"
|
||||||
|
|
||||||
# --- Standardwerte (überschreibbar via Konfiguration) ---
|
# --- Standardwerte (überschreibbar via Konfiguration) ---
|
||||||
CONFIG_FILE="${SCRIPT_DIR}/config/stream-recorder.conf"
|
CONFIG_FILE="${SCRIPT_DIR}/config/stream-recorder.conf"
|
||||||
@@ -224,6 +224,57 @@ parse_duration() {
|
|||||||
echo "$total"
|
echo "$total"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
format_elapsed() {
|
||||||
|
local seconds=$1
|
||||||
|
local d=$((seconds / 86400))
|
||||||
|
local h=$(( (seconds % 86400) / 3600 ))
|
||||||
|
local m=$(( (seconds % 3600) / 60 ))
|
||||||
|
local s=$((seconds % 60))
|
||||||
|
|
||||||
|
if [[ $d -gt 0 ]]; then
|
||||||
|
printf "%dd %dh %dm" "$d" "$h" "$m"
|
||||||
|
elif [[ $h -gt 0 ]]; then
|
||||||
|
printf "%dh %dm %ds" "$h" "$m" "$s"
|
||||||
|
else
|
||||||
|
printf "%dm %ds" "$m" "$s"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
format_size() {
|
||||||
|
local bytes=$1
|
||||||
|
if [[ $bytes -ge 1073741824 ]]; then
|
||||||
|
awk "BEGIN {printf \"%.1f GB\", $bytes/1073741824}"
|
||||||
|
elif [[ $bytes -ge 1048576 ]]; then
|
||||||
|
awk "BEGIN {printf \"%.1f MB\", $bytes/1048576}"
|
||||||
|
elif [[ $bytes -ge 1024 ]]; then
|
||||||
|
awk "BEGIN {printf \"%.1f KB\", $bytes/1024}"
|
||||||
|
else
|
||||||
|
printf "%d B" "$bytes"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
write_job_info() {
|
||||||
|
local job_id="$1" stream_name="$2" output_dir="$3" output_file="$4"
|
||||||
|
local stream_type="$5" segment_mode="$6" job_file="$7"
|
||||||
|
local info_file="${PID_DIR}/${job_id}.info"
|
||||||
|
|
||||||
|
cat > "$info_file" <<INFOEOF
|
||||||
|
STREAM_NAME="${stream_name}"
|
||||||
|
STREAM_TYPE="${stream_type}"
|
||||||
|
OUTPUT_DIR="${output_dir}"
|
||||||
|
OUTPUT_FILE="${output_file}"
|
||||||
|
START_TIME="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
|
START_EPOCH=$(date +%s)
|
||||||
|
SEGMENT_MODE="${segment_mode}"
|
||||||
|
JOB_FILE="${job_file}"
|
||||||
|
INFOEOF
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_job_info() {
|
||||||
|
local job_id="$1"
|
||||||
|
rm -f "${PID_DIR}/${job_id}.info" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
# --- Tages-/Zeitplanung ---
|
# --- Tages-/Zeitplanung ---
|
||||||
|
|
||||||
_day_name_to_num() {
|
_day_name_to_num() {
|
||||||
@@ -297,9 +348,15 @@ load_config() {
|
|||||||
if [[ -f "$CONFIG_FILE" ]]; then
|
if [[ -f "$CONFIG_FILE" ]]; then
|
||||||
# shellcheck source=/dev/null
|
# shellcheck source=/dev/null
|
||||||
source "$CONFIG_FILE"
|
source "$CONFIG_FILE"
|
||||||
|
else
|
||||||
|
local dist_file="${CONFIG_FILE}.dist"
|
||||||
|
if [[ -f "$dist_file" ]]; then
|
||||||
|
log "WARN" "Konfigurationsdatei nicht gefunden: ${CONFIG_FILE}"
|
||||||
|
log "INFO" "Vorlage kopieren: cp ${dist_file} ${CONFIG_FILE}"
|
||||||
else
|
else
|
||||||
log "WARN" "Konfigurationsdatei nicht gefunden: ${CONFIG_FILE} - verwende Standardwerte"
|
log "WARN" "Konfigurationsdatei nicht gefunden: ${CONFIG_FILE} - verwende Standardwerte"
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
mkdir -p "$DOWNLOAD_PATH" "$LOG_DIR" "$PID_DIR" "$STATE_DIR" 2>/dev/null || true
|
mkdir -p "$DOWNLOAD_PATH" "$LOG_DIR" "$PID_DIR" "$STATE_DIR" 2>/dev/null || true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,6 +417,7 @@ load_job() {
|
|||||||
SCHEDULE_STOP=""
|
SCHEDULE_STOP=""
|
||||||
PLIK_ENABLED="false"
|
PLIK_ENABLED="false"
|
||||||
DELETE_AFTER_UPLOAD="false"
|
DELETE_AFTER_UPLOAD="false"
|
||||||
|
SEGMENT_DURATION=""
|
||||||
|
|
||||||
if [[ ! -f "$job_file" ]]; then
|
if [[ ! -f "$job_file" ]]; then
|
||||||
log "ERROR" "Job-Datei nicht gefunden: ${job_file}"
|
log "ERROR" "Job-Datei nicht gefunden: ${job_file}"
|
||||||
@@ -462,6 +520,58 @@ mark_schedule_started() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# =============================================
|
||||||
|
# Segment-Upload
|
||||||
|
# =============================================
|
||||||
|
|
||||||
|
_segment_upload_loop() {
|
||||||
|
local output_dir="$1" segment_glob="$2" stream_name="$3"
|
||||||
|
local uploaded_list="$4" stop_flag="$5"
|
||||||
|
|
||||||
|
while [[ ! -f "$stop_flag" ]]; do
|
||||||
|
sleep 60
|
||||||
|
[[ -f "$stop_flag" ]] && break
|
||||||
|
|
||||||
|
local -a all_segments=()
|
||||||
|
for f in "$output_dir"/$segment_glob; do
|
||||||
|
[[ -f "$f" ]] && all_segments+=("$f")
|
||||||
|
done
|
||||||
|
|
||||||
|
IFS=$'\n' all_segments=($(sort <<<"${all_segments[*]}")); unset IFS
|
||||||
|
|
||||||
|
[[ ${#all_segments[@]} -lt 2 ]] && continue
|
||||||
|
|
||||||
|
local i
|
||||||
|
for (( i=0; i < ${#all_segments[@]} - 1; i++ )); do
|
||||||
|
local seg="${all_segments[$i]}"
|
||||||
|
if ! grep -qxF "$seg" "$uploaded_list" 2>/dev/null; then
|
||||||
|
if [[ -s "$seg" ]]; then
|
||||||
|
log "INFO" "Segment-Upload: $(basename "$seg")"
|
||||||
|
upload_to_plik "$seg" "$stream_name" && echo "$seg" >> "$uploaded_list"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_upload_remaining_segments() {
|
||||||
|
local output_dir="$1" segment_glob="$2" stream_name="$3" uploaded_list="$4"
|
||||||
|
|
||||||
|
local count=0
|
||||||
|
for seg in "$output_dir"/$segment_glob; do
|
||||||
|
[[ -f "$seg" ]] || continue
|
||||||
|
[[ -s "$seg" ]] || continue
|
||||||
|
if ! grep -qxF "$seg" "$uploaded_list" 2>/dev/null; then
|
||||||
|
log "INFO" "Segment-Upload (verbleibend): $(basename "$seg")"
|
||||||
|
upload_to_plik "$seg" "$stream_name" && echo "$seg" >> "$uploaded_list"
|
||||||
|
count=$((count + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ $count -gt 0 ]] && log "INFO" "${count} verbleibende(s) Segment(e) hochgeladen"
|
||||||
|
rm -f "$uploaded_list" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
# =============================================
|
# =============================================
|
||||||
# Aufnahme
|
# Aufnahme
|
||||||
# =============================================
|
# =============================================
|
||||||
@@ -480,12 +590,26 @@ record_stream() {
|
|||||||
local ext="${OUTPUT_FORMAT}"
|
local ext="${OUTPUT_FORMAT}"
|
||||||
[[ -z "$ext" ]] && ext=$(default_extension "$stream_type")
|
[[ -z "$ext" ]] && ext=$(default_extension "$stream_type")
|
||||||
|
|
||||||
|
local segment_seconds=""
|
||||||
|
local segment_mode=false
|
||||||
|
if [[ -n "${SEGMENT_DURATION:-}" ]]; then
|
||||||
|
segment_seconds=$(parse_duration "$SEGMENT_DURATION")
|
||||||
|
if [[ -n "$segment_seconds" && "$segment_seconds" -gt 0 ]]; then
|
||||||
|
segment_mode=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
local safe_name
|
local safe_name
|
||||||
safe_name=$(echo "$STREAM_NAME" | tr -cs 'A-Za-z0-9_-' '_' | sed 's/_$//')
|
safe_name=$(echo "$STREAM_NAME" | tr -cs 'A-Za-z0-9_-' '_' | sed 's/_$//')
|
||||||
local timestamp
|
local timestamp
|
||||||
timestamp=$(date +'%Y%m%d_%H%M%S')
|
timestamp=$(date +'%Y%m%d_%H%M%S')
|
||||||
local output_dir="${DOWNLOAD_PATH}/${safe_name}"
|
local output_dir="${DOWNLOAD_PATH}/${safe_name}"
|
||||||
local output_file="${output_dir}/${safe_name}_${timestamp}.${ext}"
|
local output_file="${output_dir}/${safe_name}_${timestamp}.${ext}"
|
||||||
|
local output_pattern=""
|
||||||
|
if $segment_mode; then
|
||||||
|
output_file="${output_dir}/${safe_name}_${timestamp}_seg%03d.${ext}"
|
||||||
|
output_pattern="${safe_name}_${timestamp}_seg*.${ext}"
|
||||||
|
fi
|
||||||
local pid_file="${PID_DIR}/${job_id}.pid"
|
local pid_file="${PID_DIR}/${job_id}.pid"
|
||||||
local ffmpeg_log="${LOG_DIR}/ffmpeg_${job_id}.log"
|
local ffmpeg_log="${LOG_DIR}/ffmpeg_${job_id}.log"
|
||||||
|
|
||||||
@@ -496,8 +620,13 @@ record_stream() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if $segment_mode; then
|
||||||
|
log "INFO" "Starte Aufnahme: ${STREAM_NAME} [${stream_type}] -> ${output_dir} (Segment: alle $(format_elapsed "$segment_seconds"))"
|
||||||
|
else
|
||||||
log "INFO" "Starte Aufnahme: ${STREAM_NAME} [${stream_type}] -> ${output_file}"
|
log "INFO" "Starte Aufnahme: ${STREAM_NAME} [${stream_type}] -> ${output_file}"
|
||||||
|
fi
|
||||||
notify "start" "Aufnahme gestartet" "${STREAM_NAME} [${stream_type}]"
|
notify "start" "Aufnahme gestartet" "${STREAM_NAME} [${stream_type}]"
|
||||||
|
write_job_info "$job_id" "$STREAM_NAME" "$output_dir" "$output_file" "$stream_type" "$segment_mode" "$job_file"
|
||||||
|
|
||||||
local ffmpeg_loglevel="warning"
|
local ffmpeg_loglevel="warning"
|
||||||
[[ "$VERBOSE" == "true" ]] && ffmpeg_loglevel="info"
|
[[ "$VERBOSE" == "true" ]] && ffmpeg_loglevel="info"
|
||||||
@@ -509,6 +638,10 @@ record_stream() {
|
|||||||
*) ffmpeg_args+=(-i "$STREAM_URL" -c copy) ;;
|
*) ffmpeg_args+=(-i "$STREAM_URL" -c copy) ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
if $segment_mode; then
|
||||||
|
ffmpeg_args+=(-f segment -segment_time "$segment_seconds" -reset_timestamps 1)
|
||||||
|
fi
|
||||||
|
|
||||||
[[ -n "$MAX_DURATION" ]] && ffmpeg_args+=(-t "$MAX_DURATION")
|
[[ -n "$MAX_DURATION" ]] && ffmpeg_args+=(-t "$MAX_DURATION")
|
||||||
|
|
||||||
if [[ -n "$EXTRA_FFMPEG_ARGS" ]]; then
|
if [[ -n "$EXTRA_FFMPEG_ARGS" ]]; then
|
||||||
@@ -521,16 +654,33 @@ record_stream() {
|
|||||||
|
|
||||||
_SR_FFMPEG_PID=""
|
_SR_FFMPEG_PID=""
|
||||||
_SR_PID_FILE="$pid_file"
|
_SR_PID_FILE="$pid_file"
|
||||||
|
_SR_JOB_ID="$job_id"
|
||||||
|
_SR_SEGMENT_STOP_FLAG=""
|
||||||
|
_SR_SEGMENT_UPLOADER_PID=""
|
||||||
_cleanup() {
|
_cleanup() {
|
||||||
|
if [[ -n "${_SR_SEGMENT_STOP_FLAG:-}" ]]; then
|
||||||
|
touch "$_SR_SEGMENT_STOP_FLAG" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if [[ -n "${_SR_SEGMENT_UPLOADER_PID:-}" ]] && kill -0 "$_SR_SEGMENT_UPLOADER_PID" 2>/dev/null; then
|
||||||
|
wait "$_SR_SEGMENT_UPLOADER_PID" 2>/dev/null || true
|
||||||
|
fi
|
||||||
if [[ -n "${_SR_FFMPEG_PID:-}" ]] && kill -0 "$_SR_FFMPEG_PID" 2>/dev/null; then
|
if [[ -n "${_SR_FFMPEG_PID:-}" ]] && kill -0 "$_SR_FFMPEG_PID" 2>/dev/null; then
|
||||||
kill -INT "$_SR_FFMPEG_PID" 2>/dev/null || true
|
kill -INT "$_SR_FFMPEG_PID" 2>/dev/null || true
|
||||||
wait "$_SR_FFMPEG_PID" 2>/dev/null || true
|
wait "$_SR_FFMPEG_PID" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
rm -f "${_SR_PID_FILE:-}"
|
rm -f "${_SR_PID_FILE:-}"
|
||||||
rm -f "${_SR_PID_FILE%.pid}.scheduled" 2>/dev/null || true
|
rm -f "${_SR_PID_FILE%.pid}.scheduled" 2>/dev/null || true
|
||||||
|
remove_job_info "${_SR_JOB_ID:-}"
|
||||||
|
rm -f "${_SR_SEGMENT_STOP_FLAG:-}" 2>/dev/null || true
|
||||||
}
|
}
|
||||||
trap _cleanup EXIT TERM INT
|
trap _cleanup EXIT TERM INT
|
||||||
|
|
||||||
|
local _sr_uploaded_list=""
|
||||||
|
if $segment_mode; then
|
||||||
|
_sr_uploaded_list=$(mktemp)
|
||||||
|
_SR_SEGMENT_STOP_FLAG=$(mktemp -u)
|
||||||
|
fi
|
||||||
|
|
||||||
local retry=0
|
local retry=0
|
||||||
while true; do
|
while true; do
|
||||||
if [[ "$VERBOSE" == "true" ]]; then
|
if [[ "$VERBOSE" == "true" ]]; then
|
||||||
@@ -541,12 +691,28 @@ record_stream() {
|
|||||||
_SR_FFMPEG_PID=$!
|
_SR_FFMPEG_PID=$!
|
||||||
echo "$_SR_FFMPEG_PID" > "$pid_file"
|
echo "$_SR_FFMPEG_PID" > "$pid_file"
|
||||||
|
|
||||||
|
if $segment_mode; then
|
||||||
|
rm -f "$_SR_SEGMENT_STOP_FLAG" 2>/dev/null || true
|
||||||
|
_segment_upload_loop "$output_dir" "$output_pattern" "$STREAM_NAME" "$_sr_uploaded_list" "$_SR_SEGMENT_STOP_FLAG" &
|
||||||
|
_SR_SEGMENT_UPLOADER_PID=$!
|
||||||
|
fi
|
||||||
|
|
||||||
local ret=0
|
local ret=0
|
||||||
wait "$_SR_FFMPEG_PID" || ret=$?
|
wait "$_SR_FFMPEG_PID" || ret=$?
|
||||||
_SR_FFMPEG_PID=""
|
_SR_FFMPEG_PID=""
|
||||||
|
|
||||||
|
if [[ -n "${_SR_SEGMENT_UPLOADER_PID:-}" ]] && kill -0 "$_SR_SEGMENT_UPLOADER_PID" 2>/dev/null; then
|
||||||
|
touch "$_SR_SEGMENT_STOP_FLAG" 2>/dev/null || true
|
||||||
|
wait "$_SR_SEGMENT_UPLOADER_PID" 2>/dev/null || true
|
||||||
|
_SR_SEGMENT_UPLOADER_PID=""
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ $ret -eq 0 ]]; then
|
if [[ $ret -eq 0 ]]; then
|
||||||
if [[ -s "$output_file" ]]; then
|
if $segment_mode; then
|
||||||
|
_upload_remaining_segments "$output_dir" "$output_pattern" "$STREAM_NAME" "$_sr_uploaded_list"
|
||||||
|
log "INFO" "Aufnahme abgeschlossen: ${STREAM_NAME} (segmentiert)"
|
||||||
|
notify "stop" "Aufnahme abgeschlossen" "${STREAM_NAME} (segmentiert)"
|
||||||
|
elif [[ -s "$output_file" ]]; then
|
||||||
log "INFO" "Aufnahme abgeschlossen: ${output_file}"
|
log "INFO" "Aufnahme abgeschlossen: ${output_file}"
|
||||||
notify "stop" "Aufnahme abgeschlossen" "${STREAM_NAME}: ${output_file}"
|
notify "stop" "Aufnahme abgeschlossen" "${STREAM_NAME}: ${output_file}"
|
||||||
upload_to_plik "$output_file" "$STREAM_NAME"
|
upload_to_plik "$output_file" "$STREAM_NAME"
|
||||||
@@ -561,7 +727,9 @@ record_stream() {
|
|||||||
if [[ $ret -eq 143 || $ret -eq 130 ]]; then
|
if [[ $ret -eq 143 || $ret -eq 130 ]]; then
|
||||||
log "INFO" "Aufnahme gestoppt: ${STREAM_NAME}"
|
log "INFO" "Aufnahme gestoppt: ${STREAM_NAME}"
|
||||||
notify "stop" "Aufnahme gestoppt" "${STREAM_NAME} wurde gestoppt"
|
notify "stop" "Aufnahme gestoppt" "${STREAM_NAME} wurde gestoppt"
|
||||||
if [[ -s "$output_file" ]]; then
|
if $segment_mode; then
|
||||||
|
_upload_remaining_segments "$output_dir" "$output_pattern" "$STREAM_NAME" "$_sr_uploaded_list"
|
||||||
|
elif [[ -s "$output_file" ]]; then
|
||||||
upload_to_plik "$output_file" "$STREAM_NAME"
|
upload_to_plik "$output_file" "$STREAM_NAME"
|
||||||
fi
|
fi
|
||||||
break
|
break
|
||||||
@@ -570,7 +738,9 @@ record_stream() {
|
|||||||
if [[ ! -f "$pid_file" ]]; then
|
if [[ ! -f "$pid_file" ]]; then
|
||||||
log "INFO" "Aufnahme gestoppt: ${STREAM_NAME}"
|
log "INFO" "Aufnahme gestoppt: ${STREAM_NAME}"
|
||||||
notify "stop" "Aufnahme gestoppt" "${STREAM_NAME} wurde gestoppt"
|
notify "stop" "Aufnahme gestoppt" "${STREAM_NAME} wurde gestoppt"
|
||||||
if [[ -s "$output_file" ]]; then
|
if $segment_mode; then
|
||||||
|
_upload_remaining_segments "$output_dir" "$output_pattern" "$STREAM_NAME" "$_sr_uploaded_list"
|
||||||
|
elif [[ -s "$output_file" ]]; then
|
||||||
upload_to_plik "$output_file" "$STREAM_NAME"
|
upload_to_plik "$output_file" "$STREAM_NAME"
|
||||||
fi
|
fi
|
||||||
break
|
break
|
||||||
@@ -592,18 +762,38 @@ record_stream() {
|
|||||||
|
|
||||||
log "WARN" "Verbindung zu '${STREAM_NAME}' verloren (Code: ${ret})${retry_info}. Reconnect in ${RETRY_DELAY}s..."
|
log "WARN" "Verbindung zu '${STREAM_NAME}' verloren (Code: ${ret})${retry_info}. Reconnect in ${RETRY_DELAY}s..."
|
||||||
notify "error" "Stream abgerissen" "${STREAM_NAME}: Verbindung verloren (Code: ${ret}). Reconnect in ${RETRY_DELAY}s..." "default"
|
notify "error" "Stream abgerissen" "${STREAM_NAME}: Verbindung verloren (Code: ${ret}). Reconnect in ${RETRY_DELAY}s..." "default"
|
||||||
|
|
||||||
|
if $segment_mode; then
|
||||||
|
_upload_remaining_segments "$output_dir" "$output_pattern" "$STREAM_NAME" "$_sr_uploaded_list"
|
||||||
|
fi
|
||||||
|
|
||||||
sleep "$RETRY_DELAY"
|
sleep "$RETRY_DELAY"
|
||||||
|
|
||||||
timestamp=$(date +'%Y%m%d_%H%M%S')
|
timestamp=$(date +'%Y%m%d_%H%M%S')
|
||||||
|
if $segment_mode; then
|
||||||
|
output_file="${output_dir}/${safe_name}_${timestamp}_seg%03d.${ext}"
|
||||||
|
output_pattern="${safe_name}_${timestamp}_seg*.${ext}"
|
||||||
|
else
|
||||||
output_file="${output_dir}/${safe_name}_${timestamp}.${ext}"
|
output_file="${output_dir}/${safe_name}_${timestamp}.${ext}"
|
||||||
|
fi
|
||||||
ffmpeg_args[-1]="$output_file"
|
ffmpeg_args[-1]="$output_file"
|
||||||
done
|
done
|
||||||
|
|
||||||
trap - EXIT TERM INT
|
trap - EXIT TERM INT
|
||||||
rm -f "$pid_file"
|
rm -f "$pid_file"
|
||||||
rm -f "${pid_file%.pid}.scheduled" 2>/dev/null || true
|
rm -f "${pid_file%.pid}.scheduled" 2>/dev/null || true
|
||||||
|
remove_job_info "$job_id"
|
||||||
|
rm -f "${_SR_SEGMENT_STOP_FLAG:-}" 2>/dev/null || true
|
||||||
|
|
||||||
if [[ -f "$output_file" && ! -s "$output_file" ]]; then
|
if $segment_mode; then
|
||||||
|
for seg in "$output_dir"/$output_pattern; do
|
||||||
|
[[ -f "$seg" ]] || continue
|
||||||
|
if [[ ! -s "$seg" ]]; then
|
||||||
|
rm -f "$seg"
|
||||||
|
log "WARN" "Leeres Segment entfernt: ${seg}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
elif [[ -f "$output_file" && ! -s "$output_file" ]]; then
|
||||||
rm -f "$output_file"
|
rm -f "$output_file"
|
||||||
log "WARN" "Leere Ausgabedatei entfernt: ${output_file}"
|
log "WARN" "Leere Ausgabedatei entfernt: ${output_file}"
|
||||||
fi
|
fi
|
||||||
@@ -887,6 +1077,19 @@ create_job() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "--- Segment-Splitting (optional) ---"
|
||||||
|
local segment_duration=""
|
||||||
|
read -rp "Aufnahme in Segmente teilen? (z.B. 1h, 2h oder leer=nein): " segment_input
|
||||||
|
if [[ -n "$segment_input" ]]; then
|
||||||
|
segment_duration=$(parse_duration "$segment_input")
|
||||||
|
if [[ -n "$segment_duration" && "$segment_duration" -gt 0 ]]; then
|
||||||
|
echo " Segmente alle $(format_elapsed "$segment_duration")"
|
||||||
|
else
|
||||||
|
segment_duration=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "--- Plik-Upload (optional) ---"
|
echo "--- Plik-Upload (optional) ---"
|
||||||
local plik_enabled="false"
|
local plik_enabled="false"
|
||||||
@@ -930,6 +1133,11 @@ create_job() {
|
|||||||
duration_display+="${d_h}h ${d_m}m"
|
duration_display+="${d_h}h ${d_m}m"
|
||||||
fi
|
fi
|
||||||
echo " Dauer: ${duration_display}"
|
echo " Dauer: ${duration_display}"
|
||||||
|
if [[ -n "$segment_duration" ]]; then
|
||||||
|
echo " Segmente: alle $(format_elapsed "$segment_duration")"
|
||||||
|
else
|
||||||
|
echo " Segmente: deaktiviert"
|
||||||
|
fi
|
||||||
if [[ "$sched_enabled" == "true" ]]; then
|
if [[ "$sched_enabled" == "true" ]]; then
|
||||||
if [[ "$sched_once" == "true" ]]; then
|
if [[ "$sched_once" == "true" ]]; then
|
||||||
echo " Zeitplan: einmalig ${sched_date} ${sched_start}${sched_stop:+ - ${sched_stop}}"
|
echo " Zeitplan: einmalig ${sched_date} ${sched_start}${sched_stop:+ - ${sched_stop}}"
|
||||||
@@ -963,6 +1171,13 @@ OUTPUT_FORMAT="${ext}"
|
|||||||
MAX_DURATION="${duration}"
|
MAX_DURATION="${duration}"
|
||||||
EXTRA_FFMPEG_ARGS=""
|
EXTRA_FFMPEG_ARGS=""
|
||||||
|
|
||||||
|
###############################################
|
||||||
|
# Segment-Splitting (optional)
|
||||||
|
# Aufnahme in Teile zerlegen, z.B. 1h, 2h, 30m
|
||||||
|
###############################################
|
||||||
|
|
||||||
|
SEGMENT_DURATION="${segment_duration}"
|
||||||
|
|
||||||
###############################################
|
###############################################
|
||||||
# Zeitplanung (optional)
|
# Zeitplanung (optional)
|
||||||
###############################################
|
###############################################
|
||||||
@@ -1032,6 +1247,13 @@ list_jobs() {
|
|||||||
extra_info+=" (Dauer: ${MAX_DURATION}s)"
|
extra_info+=" (Dauer: ${MAX_DURATION}s)"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
if [[ -n "${SEGMENT_DURATION:-}" ]]; then
|
||||||
|
local seg_secs
|
||||||
|
seg_secs=$(parse_duration "$SEGMENT_DURATION")
|
||||||
|
if [[ -n "$seg_secs" && "$seg_secs" -gt 0 ]]; then
|
||||||
|
extra_info+="${extra_info:+ | }Segmente: alle $(format_elapsed "$seg_secs")"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
if [[ "${PLIK_ENABLED:-false}" == "true" ]]; then
|
if [[ "${PLIK_ENABLED:-false}" == "true" ]]; then
|
||||||
extra_info+="${extra_info:+ | }Plik-Upload: an"
|
extra_info+="${extra_info:+ | }Plik-Upload: an"
|
||||||
fi
|
fi
|
||||||
@@ -1062,7 +1284,7 @@ list_jobs() {
|
|||||||
|
|
||||||
show_status() {
|
show_status() {
|
||||||
echo "Laufende Aufnahmen:"
|
echo "Laufende Aufnahmen:"
|
||||||
echo "$(printf '=%.0s' {1..50})"
|
echo "$(printf '=%.0s' {1..60})"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
local found=false
|
local found=false
|
||||||
@@ -1078,9 +1300,48 @@ show_status() {
|
|||||||
found=true
|
found=true
|
||||||
local mode="manuell"
|
local mode="manuell"
|
||||||
[[ -f "${PID_DIR}/${job_id}.scheduled" ]] && mode="geplant"
|
[[ -f "${PID_DIR}/${job_id}.scheduled" ]] && mode="geplant"
|
||||||
|
|
||||||
|
local info_file="${PID_DIR}/${job_id}.info"
|
||||||
|
if [[ -f "$info_file" ]]; then
|
||||||
|
local STREAM_NAME="" STREAM_TYPE="" OUTPUT_DIR="" OUTPUT_FILE=""
|
||||||
|
local START_TIME="" START_EPOCH="" SEGMENT_MODE=""
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$info_file"
|
||||||
|
|
||||||
|
printf " %-20s %s\n" "$job_id" "$STREAM_NAME"
|
||||||
|
printf " %-20s PID: %s | Modus: %s | Typ: %s\n" "" "$pid" "$mode" "$STREAM_TYPE"
|
||||||
|
printf " %-20s Gestartet: %s" "" "$START_TIME"
|
||||||
|
|
||||||
|
if [[ -n "$START_EPOCH" ]]; then
|
||||||
|
local now_epoch elapsed
|
||||||
|
now_epoch=$(date +%s)
|
||||||
|
elapsed=$((now_epoch - START_EPOCH))
|
||||||
|
printf " | Laufzeit: %s" "$(format_elapsed "$elapsed")"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
local total_size=0
|
||||||
|
if [[ "$SEGMENT_MODE" == "true" && -n "$OUTPUT_DIR" ]]; then
|
||||||
|
local seg_count=0
|
||||||
|
for seg in "$OUTPUT_DIR"/*_seg*; do
|
||||||
|
[[ -f "$seg" ]] || continue
|
||||||
|
local s
|
||||||
|
s=$(stat -c%s "$seg" 2>/dev/null || echo 0)
|
||||||
|
total_size=$((total_size + s))
|
||||||
|
seg_count=$((seg_count + 1))
|
||||||
|
done
|
||||||
|
printf " %-20s Verzeichnis: %s | Segmente: %d | Größe: %s\n" "" "$OUTPUT_DIR" "$seg_count" "$(format_size "$total_size")"
|
||||||
|
elif [[ -n "$OUTPUT_FILE" && -f "$OUTPUT_FILE" ]]; then
|
||||||
|
total_size=$(stat -c%s "$OUTPUT_FILE" 2>/dev/null || echo 0)
|
||||||
|
printf " %-20s Datei: %s | Größe: %s\n" "" "$OUTPUT_FILE" "$(format_size "$total_size")"
|
||||||
|
fi
|
||||||
|
else
|
||||||
printf " %-20s PID: %-10s [%s]\n" "$job_id" "$pid" "$mode"
|
printf " %-20s PID: %-10s [%s]\n" "$job_id" "$pid" "$mode"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
else
|
else
|
||||||
rm -f "$pid_file"
|
rm -f "$pid_file"
|
||||||
|
rm -f "${PID_DIR}/${job_id}.info" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -1181,10 +1442,10 @@ Befehle:
|
|||||||
create Neuen Job interaktiv erstellen
|
create Neuen Job interaktiv erstellen
|
||||||
quick Interaktive Schnellaufnahme (URL eingeben)
|
quick Interaktive Schnellaufnahme (URL eingeben)
|
||||||
upload <datei> Vorhandene Datei nach Plik hochladen
|
upload <datei> Vorhandene Datei nach Plik hochladen
|
||||||
stop <name> Aufnahme stoppen
|
stop <name> Aufnahme stoppen (Teilaufnahme wird hochgeladen)
|
||||||
stop-all Alle Aufnahmen stoppen
|
stop-all Alle Aufnahmen stoppen
|
||||||
list Jobs und deren Zeitpläne anzeigen
|
list Jobs und deren Zeitpläne anzeigen
|
||||||
status Laufende Aufnahmen und Scheduler-Status
|
status Laufende Aufnahmen mit Details anzeigen
|
||||||
scheduler Zeitplanung prüfen und Jobs starten/stoppen
|
scheduler Zeitplanung prüfen und Jobs starten/stoppen
|
||||||
logs Log-Ausgabe live verfolgen
|
logs Log-Ausgabe live verfolgen
|
||||||
|
|
||||||
@@ -1195,6 +1456,11 @@ Optionen:
|
|||||||
-h, --help Diese Hilfe
|
-h, --help Diese Hilfe
|
||||||
-v, --version Version
|
-v, --version Version
|
||||||
|
|
||||||
|
Segment-Splitting:
|
||||||
|
Aufnahmen können in Segmente (z.B. 1h, 2h) geteilt werden.
|
||||||
|
Jedes Segment wird einzeln nach Plik hochgeladen.
|
||||||
|
Konfiguration: SEGMENT_DURATION in der Job-Datei.
|
||||||
|
|
||||||
Zeitplanung:
|
Zeitplanung:
|
||||||
Aufnahmen werden über die Job-Dateien geplant (SCHEDULE_*).
|
Aufnahmen werden über die Job-Dateien geplant (SCHEDULE_*).
|
||||||
Der Scheduler wird automatisch per systemd-Timer ausgeführt.
|
Der Scheduler wird automatisch per systemd-Timer ausgeführt.
|
||||||
@@ -1204,6 +1470,8 @@ Beispiele:
|
|||||||
$(basename "$0") record mein-stream
|
$(basename "$0") record mein-stream
|
||||||
$(basename "$0") quick
|
$(basename "$0") quick
|
||||||
$(basename "$0") -V record mein-stream
|
$(basename "$0") -V record mein-stream
|
||||||
|
$(basename "$0") status
|
||||||
|
$(basename "$0") stop mein-stream
|
||||||
$(basename "$0") list
|
$(basename "$0") list
|
||||||
$(basename "$0") logs
|
$(basename "$0") logs
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user