====== TV-Headend ====== ====== Dokumentation ====== TvHeadend kann tvkarten verarbeiten und streamen. Es nutzt tunebare v4l devices. ===== Docker Run (einfach testen) ===== docker run -d \ --name tvheadend2 \ --restart unless-stopped \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/Berlin \ -e RUN_OPTS="" \ -p 9981:9981 \ -p 9982:9982 \ -v /yacht/AppData/tvheadend2/data:/config \ -v /yacht/AppData/tvheadend2/recordings:/recordings \ --device /dev/dri:/dev/dri \ --device /dev/dvb:/dev/dvb \ lscr.io/linuxserver/tvheadend:latest ===== Dauereinsatz (Grundlage) ===== #!/usr/bin/env bash # # tvheadend2-manage.sh # # Management-Script für den TVHeadend-Docker-Container "tvheadend2": # - install : Erstinstallation / Neuinstallation # - update : Image aktualisieren + Container neu erstellen # - clean : Container stoppen + löschen (Daten bleiben erhalten) # - remove : Alias für "clean" # - purge : Container + Image + (optional) Daten löschen # - logs : Logs anzeigen (follow) # - status : Status anzeigen # - shell : Shell im Container öffnen # # Unterstützt variable Basis-Verzeichnisse: # - /yacht/AppData/tvheadend2 # - /srv/docker/tvheadend2 # set -euo pipefail ######################################## # Konfiguration ######################################## # Name des Containers CONTAINER_NAME="tvheadend2" # Docker-Image IMAGE_NAME="lscr.io/linuxserver/tvheadend:latest" # Standard-Ports (Host:Container) HTTP_PORT="9981" HTSP_PORT="9982" # Mögliche Basisverzeichnisse (Host) BASE_DIR_CANDIDATES=( "/yacht/AppData/tvheadend2" "/srv/docker/tvheadend2" ) # Effektives Basisverzeichnis bestimmen BASE_DIR="" for c in "${BASE_DIR_CANDIDATES[@]}"; do if [ -d "$c" ]; then BASE_DIR="$c" break fi done # Falls noch keins existiert: erstes als Default nehmen und anlegen if [ -z "$BASE_DIR" ]; then BASE_DIR="${BASE_DIR_CANDIDATES[0]}" mkdir -p "$BASE_DIR" fi CONFIG_DIR="$BASE_DIR/data" RECORDINGS_DIR="$BASE_DIR/recordings" # Default-UID/GID (bei Bedarf anpassen) PUID="${PUID:-1000}" PGID="${PGID:-1000}" ######################################## # Hilfsfunktionen ######################################## log() { echo "[tvh2-manage] $*" } ensure_dirs() { log "Erzeuge Verzeichnisse (falls nicht vorhanden):" log " CONFIG_DIR = $CONFIG_DIR" log " RECORDINGS_DIR= $RECORDINGS_DIR" mkdir -p "$CONFIG_DIR" "$RECORDINGS_DIR" # Rechte setzen (damit der Container schreiben kann) log "Setze Eigentümer auf PUID=$PUID, PGID=$PGID" chown -R "$PUID:$PGID" "$BASE_DIR" } container_exists() { docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$" } container_running() { docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$" } ######################################## # Aktionen ######################################## do_install() { log "Starte INSTALL für $CONTAINER_NAME mit Image $IMAGE_NAME" ensure_dirs() if container_exists; then log "Container $CONTAINER_NAME existiert bereits – entferne alte Instanz..." docker stop "$CONTAINER_NAME" >/dev/null 2>&1 || true docker rm "$CONTAINER_NAME" >/dev/null 2>&1 || true fi log "Pull neues Image..." docker pull "$IMAGE_NAME" log "Starte neuen Container mit --restart unless-stopped" docker run -d \ --name "$CONTAINER_NAME" \ --restart unless-stopped \ -e PUID="$PUID" \ -e PGID="$PGID" \ -e TZ="Europe/Berlin" \ -e RUN_OPTS="" \ -p "${HTTP_PORT}:9981" \ -p "${HTSP_PORT}:9982" \ -v "${CONFIG_DIR}:/config" \ -v "${RECORDINGS_DIR}:/recordings" \ --device /dev/dri:/dev/dri \ --device /dev/dvb:/dev/dvb \ "$IMAGE_NAME" log "Install/Neu-Deploy abgeschlossen. Web-GUI sollte unter http://:${HTTP_PORT}/ erreichbar sein." } do_update() { log "Starte UPDATE für $CONTAINER_NAME" ensure_dirs if ! container_exists; then log "Container existiert nicht. Führe stattdessen INSTALL aus." do_install return fi log "Pull neues Image..." docker pull "$IMAGE_NAME" log "Stoppe alten Container..." docker stop "$CONTAINER_NAME" || true log "Entferne alten Container..." docker rm "$CONTAINER_NAME" || true log "Starte Container mit neuem Image..." docker run -d \ --name "$CONTAINER_NAME" \ --restart unless-stopped \ -e PUID="$PUID" \ -e PGID="$PGID" \ -e TZ="Europe/Berlin" \ -e RUN_OPTS="" \ -p "${HTTP_PORT}:9981" \ -p "${HTSP_PORT}:9982" \ -v "${CONFIG_DIR}:/config" \ -v "${RECORDINGS_DIR}:/recordings" \ --device /dev/dri:/dev/dri \ --device /dev/dvb:/dev/dvb \ "$IMAGE_NAME" log "Update abgeschlossen." } do_clean() { log "CLEAN: Container stoppen und löschen (Daten bleiben erhalten)." if container_running; then log "Stoppe laufenden Container..." docker stop "$CONTAINER_NAME" || true fi if container_exists; then log "Entferne Container..." docker rm "$CONTAINER_NAME" || true else log "Container existiert nicht – nichts zu tun." fi } do_purge() { log "PURGE: Container, Image und optional Daten löschen." # 1) Container stoppen/entfernen do_clean # 2) Image löschen if docker images --format '{{.Repository}}:{{.Tag}}' | grep -q "^${IMAGE_NAME}$"; then log "Entferne Image $IMAGE_NAME..." docker rmi "$IMAGE_NAME" || true else log "Image $IMAGE_NAME nicht gefunden – überspringe." fi # 3) Daten löschen (nur nach expliziter Bestätigung) echo echo "WARNUNG: Sollen auch die Datenverzeichnisse gelöscht werden?" echo " CONFIG_DIR = $CONFIG_DIR" echo " RECORDINGS_DIR= $RECORDINGS_DIR" read -r -p "WIRKLICH ALLES LÖSCHEN? (yes/NO): " ans if [ "$ans" = "yes" ]; then log "Lösche Datenverzeichnisse..." rm -rf "$CONFIG_DIR" "$RECORDINGS_DIR" log "Daten entfernt." else log "Daten bleiben erhalten." fi } do_logs() { log "Zeige Logs von $CONTAINER_NAME (Ctrl+C zum Beenden)..." docker logs -f "$CONTAINER_NAME" } do_status() { log "Status von $CONTAINER_NAME:" if container_running; then echo " - Container läuft." elif container_exists; then echo " - Container existiert, läuft aber nicht." echo " - Letzter Exit-Code siehe: docker inspect $CONTAINER_NAME" else echo " - Container existiert nicht." fi echo echo "Gemappte Verzeichnisse:" echo " BASE_DIR = $BASE_DIR" echo " CONFIG_DIR = $CONFIG_DIR (-> /config)" echo " RECORDINGS_DIR= $RECORDINGS_DIR (-> /recordings)" echo echo "Image:" docker images | grep -E 'REPOSITORY|tvheadend|linuxserver/tvheadend|lscr.io/linuxserver/tvheadend' || true echo echo "Container:" docker ps -a | grep -E 'CONTAINER ID|tvheadend2' || true } do_shell() { if ! container_running; then log "Container läuft nicht – starte ihn kurz an..." docker start "$CONTAINER_NAME" >/dev/null 2>&1 || true fi log "Öffne Shell im Container..." docker exec -it "$CONTAINER_NAME" bash 2>/dev/null || docker exec -it "$CONTAINER_NAME" sh } ######################################## # Main ######################################## ACTION="${1:-}" case "$ACTION" in install) do_install ;; update) do_update ;; clean|remove) do_clean ;; purge) do_purge ;; logs) do_logs ;; status) do_status ;; shell) do_shell ;; *) echo "Usage: $0 {install|update|clean|remove|purge|logs|status|shell}" echo echo " install : Container (neu) erstellen mit restart=unless-stopped" echo " update : Image pullen + Container neu erstellen" echo " clean : Container stoppen + löschen (Daten bleiben)" echo " remove : Alias für clean" echo " purge : Container+Image und optional Daten löschen" echo " logs : docker logs -f" echo " status : Übersicht über Container/Image/Verzeichnisse" echo " shell : Shell in laufendem Container öffnen" exit 1 ;; esac