While we wait for this to be an actual feature I created a script that goes through your mux URLs and adds variables to them. Specifically for piping to ffmpeg to stop those pesky stalls. cit: https://tvheadend.org/d/5282-iptv-buffering-stalls-general-drop-outs-settings-that-resolved-things-for-me/50

It has a little retry logic built in and gives you a status of each mux afterwards. Hope this helps someone 🙂


#!/bin/bash

# Tvheadend credentials and URL
TVH_USER="admin"
TVH_PASS="password"
TVH_URL="http://localhost:9981"

# Network Name to target
NETWORK_NAME="MYIPTVNETWORKNAME"  # Change this to match your target network

# Fetch all MUXes
MUXES=$(curl -s --digest -u "$TVH_USER:$TVH_PASS" "$TVH_URL/api/mpegts/mux/grid")

if [[ -z "$MUXES" ]]; then
    echo "Error: No MUXes found in Tvheadend."
    exit 1
fi

# Process each MUX in the targeted network
echo "$MUXES" | jq -c --arg net_name "$NETWORK_NAME" '.entries[] | select(.network == $net_name)' | while read -r mux; do
    # Extract MUX UUID, Name, and Current URL
    MUX_UUID=$(echo "$mux" | jq -r '.uuid')
    MUX_NAME=$(echo "$mux" | jq -r '.name')
    CURRENT_URL=$(echo "$mux" | jq -r '.iptv_url')

    # Skip if the URL is already using ffmpeg pipe
    if [[ "$CURRENT_URL" == pipe* ]]; then
        echo "Skipping MUX: $MUX_NAME - already using ffmpeg pipe"
        continue
    fi

    # Construct the new URL with ffmpeg
    NEW_URL="pipe:///usr/bin/ffmpeg -reconnect 1 -reconnect_at_eof 1 -reconnect_streamed 1 \
-reconnect_delay_max 2000 -probesize 1000k -analyzeduration 0 -fpsprobesize 0 -fflags -nobuffer \
-err_detect ignore_err -i $CURRENT_URL -codec copy -f mpegts -tune zerolatency pipe:1"

    # Form-encode the payload
    PAYLOAD="node={\"uuid\":\"$MUX_UUID\",\"iptv_url\":\"$NEW_URL\"}"

    # Send the update request
    RESPONSE=$(curl -s --digest -u "$TVH_USER:$TVH_PASS" -X POST "$TVH_URL/api/idnode/save" \
        -H "Content-Type: application/x-www-form-urlencoded" --data-urlencode "$PAYLOAD")

    # If the response is empty, warn instead of failing
    if [[ "$RESPONSE" == "{}" ]]; then
        echo "⚠️  Warning: No confirmation from API for MUX: $MUX_NAME, re-checking..."
        sleep 2  # Wait before re-checking

        # Fetch updated MUX list
        UPDATED_MUXES=$(curl -s --digest -u "$TVH_USER:$TVH_PASS" "$TVH_URL/api/mpegts/mux/grid")
        UPDATED_URL=$(echo "$UPDATED_MUXES" | jq -r --arg uuid "$MUX_UUID" '.entries[] | select(.uuid == $uuid) | .iptv_url')

        # Verify if the update was actually applied
        if [[ "$UPDATED_URL" == "$NEW_URL" ]]; then
            echo "✅ Update confirmed for MUX: $MUX_NAME."
        else
            echo "❌ Failed to confirm update for MUX: $MUX_NAME."
        fi
    elif echo "$RESPONSE" | grep -q '"success":true'; then
        echo "✅ Updated MUX: $MUX_NAME successfully."
    else
        echo "❌ Failed to update MUX: $MUX_NAME. Response: $RESPONSE"
    fi
done

    Tachioma

    I forgot to point out, It has 4 variables in the script that you will need to alter :

    TVH_USER
    TVH_PASS
    TVH_URL
    NETWORK_NAME

    Tags for search: IPTV ffmpeg pipe

    14 days later

    Thank you for (REALLY) useful script. I am real newbie in the pipe topic but i just found out it handles the streams better as there are some parameters that help to keep the stream. I had really big issues with keeping the stream, well, streaming. But i found small issue with your script - it picks up just first 50 entries. I have more than 400 channels in my network so i decided to change it a bit (i just changed the API call to specify the limit to very high number). It now picks up 9999 channels (i think very few people has more than that lol). Here it is:

    #!/bin/bash
    
    # Tvheadend credentials and URL
    TVH_USER="admin"
    TVH_PASS="password"
    TVH_URL="http://localhost:9981"
    
    # Network Name to target
    NETWORK_NAME="MYIPTVNETWORKNAME"  # Change this to match your target network
    
    # Fetch all MUXes by requesting a large limit value
    MUXES=$(curl -s --digest -u "$TVH_USER:$TVH_PASS" "$TVH_URL/api/mpegts/mux/grid?start=0&limit=9999")
    
    if [[ -z "$MUXES" ]]; then
        echo "Error: No MUXes found in Tvheadend."
        exit 1
    fi
    
    # Process each MUX in the targeted network
    echo "$MUXES" | jq -c --arg net_name "$NETWORK_NAME" '.entries[] | select(.network == $net_name)' | while read -r mux; do
        # Extract MUX UUID, Name, and Current URL
        MUX_UUID=$(echo "$mux" | jq -r '.uuid')
        MUX_NAME=$(echo "$mux" | jq -r '.name')
        CURRENT_URL=$(echo "$mux" | jq -r '.iptv_url')
    
        # Skip if the URL is already using ffmpeg pipe
        if [[ "$CURRENT_URL" == pipe* ]]; then
            echo "Skipping MUX: $MUX_NAME - already using ffmpeg pipe"
            continue
        fi
    
        # Construct the new URL with ffmpeg
        NEW_URL="pipe:///usr/bin/ffmpeg -reconnect 1 -reconnect_at_eof 1 -reconnect_streamed 1 \
    -reconnect_delay_max 2000 -probesize 1000k -analyzeduration 0 -fpsprobesize 0 -fflags -nobuffer \
    -err_detect ignore_err -i $CURRENT_URL -codec copy -f mpegts -tune zerolatency pipe:1"
    
        # Form-encode the payload
        PAYLOAD="node={\"uuid\":\"$MUX_UUID\",\"iptv_url\":\"$NEW_URL\"}"
    
        # Send the update request
        RESPONSE=$(curl -s --digest -u "$TVH_USER:$TVH_PASS" -X POST "$TVH_URL/api/idnode/save" \
            -H "Content-Type: application/x-www-form-urlencoded" --data-urlencode "$PAYLOAD")
    
        # If the response is empty, warn instead of failing
        if [[ "$RESPONSE" == "{}" ]]; then
            echo "  Warning: No confirmation from API for MUX: $MUX_NAME, re-checking..."
            sleep 2  # Wait before re-checking
    
            # Fetch updated MUX list with the same pagination parameters
            UPDATED_MUXES=$(curl -s --digest -u "$TVH_USER:$TVH_PASS" "$TVH_URL/api/mpegts/mux/grid?start=0&limit=9999")
            UPDATED_URL=$(echo "$UPDATED_MUXES" | jq -r --arg uuid "$MUX_UUID" '.entries[] | select(.uuid == $uuid) | .iptv_url')
    
            # Verify if the update was actually applied
            if [[ "$UPDATED_URL" == "$NEW_URL" ]]; then
                echo " Update confirmed for MUX: $MUX_NAME."
            else
                echo " Failed to confirm update for MUX: $MUX_NAME."
            fi
        elif echo "$RESPONSE" | grep -q '"success":true'; then
            echo " Updated MUX: $MUX_NAME successfully."
        else
            echo " Failed to update MUX: $MUX_NAME. Response: $RESPONSE"
        fi
    done