I don't normally use this, but I just tested it a couple of times -- and it's working as intended for me.
For a definitive test, I set the -ss value to 20, which is also the number of seconds I wait before jumping to "live" in the current testing I'm doing with the ESPN app. Sure enough, I saw nothing of the tuning process, but when the video started it jumped to live immediately.
Your Docker Compose should look something like this:
services:
ah4c: # This docker-compose typically requires no editing. Use the Environment variables section of Portainer to set your values.
# 2025.09.13
# GitHub home for this project: https://github.com/bnhf/ah4c.
# Docker container home for this project with setup instructions: https://hub.docker.com/r/bnhf/ah4c.
image: bnhf/ah4c:${TAG:-latest}
container_name: ${CONTAINER_NAME:-ah4c}
hostname: ${HOSTNAME:-ah4c}
dns_search: ${DOMAIN:-localdomain} # Specify the name of your LAN's domain, usually local or localdomain
#devices:
#- /dev/dri:/dev/dri # Uncomment for Intel Quick Sync (GPU) access
ports:
- ${ADBS_PORT:-5037}:5037 # Port used by adb-server
- ${HOST_PORT:-7654}:7654 # Port used by this ah4c proxy
- ${SCRC_PORT:-7655}:8000 # Port used by ws-scrcpy
environment:
- IPADDRESS=${IPADDRESS} # Hostname or IP address of this ah4c extension to be used in M3U file (also add port number if not in M3U)
- NUMBER_TUNERS=${NUMBER_TUNERS} # Number of tuners you'd like defined 1, 2, 3 or 4 supported
- TUNER1_IP=${TUNER1_IP} # Streaming device #1 with adb port in the form hostname:port or ip:port
- TUNER2_IP=${TUNER2_IP} # Streaming device #2 with adb port in the form hostname:port or ip:port
- TUNER3_IP=${TUNER3_IP} # Streaming device #3 with adb port in the form hostname:port or ip:port
- TUNER4_IP=${TUNER4_IP} # Streaming device #4 with adb port in the form hostname:port or ip:port
- TUNER5_IP=${TUNER5_IP} # Streaming device #5 with adb port in the form hostname:port or ip:port
- TUNER6_IP=${TUNER6_IP} # Streaming device #6 with adb port in the form hostname:port or ip:port
- TUNER7_IP=${TUNER7_IP} # Streaming device #7 with adb port in the form hostname:port or ip:port
- TUNER8_IP=${TUNER8_IP} # Streaming device #8 with adb port in the form hostname:port or ip:port
- TUNER9_IP=${TUNER9_IP} # Streaming device #9 with adb port in the form hostname:port or ip:port
- ENCODER1_URL=${ENCODER1_URL} # Full URL for tuner #1 in the form http://hostname/stream or http://ip/stream
- ENCODER2_URL=${ENCODER2_URL} # Full URL for tuner #2 in the form http://hostname/stream or http://ip/stream
- ENCODER3_URL=${ENCODER3_URL} # Full URL for tuner #3 in the form http://hostname/stream or http://ip/stream
- ENCODER4_URL=${ENCODER4_URL} # Full URL for tuner #4 in the form http://hostname/stream or http://ip/stream
- ENCODER5_URL=${ENCODER5_URL} # Full URL for tuner #5 in the form http://hostname/stream or http://ip/stream
- ENCODER6_URL=${ENCODER6_URL} # Full URL for tuner #6 in the form http://hostname/stream or http://ip/stream
- ENCODER7_URL=${ENCODER7_URL} # Full URL for tuner #7 in the form http://hostname/stream or http://ip/stream
- ENCODER8_URL=${ENCODER8_URL} # Full URL for tuner #8 in the form http://hostname/stream or http://ip/stream
- ENCODER9_URL=${ENCODER9_URL} # Full URL for tuner #9 in the form http://hostname/stream or http://ip/stream
- CMD1=${CMD1} # Typically used for ffmpeg processing of a device's stream. ffmpeg -i ${ENCODER1_URL} -ss 8 -c:v copy -c:a copy -f mpegts -
- CMD2=${CMD2} # Typically used for ffmpeg processing of a device's stream. ffmpeg -i ${ENCODER2_URL} -ss 8 -c:v copy -c:a copy -f mpegts -
- STREAMER_APP=${STREAMER_APP} # Streaming device name and streaming app you're using in the form scripts/streamer/app (use lowercase with slashes between as shown)
- CHANNELSIP=${CHANNELSIP} # Hostname or IP address of the Channels DVR server itself
- ALERT_SMTP_SERVER=${ALERT_SMTP_SERVER} # The domainname:port of the SMTP server you'll be using like smtp.gmail.com:587. This is for sending ah4c alerts if tuning fails.
- ALERT_AUTH_SERVER=${ALERT_AUTH_SERVER} # The auth server for the e-mail you'll be using like smtp.gmail.com
- ALERT_EMAIL_FROM=${ALERT_EMAIL_FROM} # The e-mail address you'd like your ah4c failure alert e-mails to show as being from.
- ALERT_EMAIL_PASS=${ALERT_EMAIL_PASS} # Gmail and Yahoo both support the creation of app-specific e-mail passwords, and this is the way to go! It's NOT recommended to use your everyday e-mail password.
- ALERT_EMAIL_TO=${ALERT_EMAIL_TO} # The e-mail address you'd like your alert e-mails sent to.
#- ALERT_WEBHOOK_URL=""
- LIVETV_ATTEMPTS=${LIVETV_ATTEMPTS} # For FireTV Live Guide tuning only, set maximum number of attempts at finding the desired channel
- CREATE_M3US=${CREATE_M3US:-false} # Set to true to create device-specific M3Us for use with Amazon Prime Premium channels -- requires a FireTV device
- UPDATE_SCRIPTS=${UPDATE_SCRIPTS:-true} # Set to true if you'd like the sample scripts and STREAMER_APP scripts updated whether they exist of not
- UPDATE_M3US=${UPDATE_M3US:-true} # Set to true if you'd like the sample m3us updated whether they exist of not
- TZ=${TZ} # Your local timezone in Linux "tz" format
- SPEED_MODE=${SPEED_MODE:-false} # Set to false if you'd like the target streaming app to be closed after each tuning cycle (limited script support).
- KEEP_WATCHING=${KEEP_WATCHING:-235m} # In supported scripts, set the delay before resending a tuning deeplink to prevent "Are you still watching?" type messages. Examples: Use 4h for 4 hours or 240m for 240 minutes.
- AUTOCROP_CHANNELS=${AUTOCROP_CHANNELS} # Space separated list of channels (by number) with black borders on 4 sides to autocrop while maintaining aspect ratio. Requires LinkPi Encoder!
- LINKPI_HOSTNAME=${LINKPI_HOSTNAME} # Hostname or IP address of your LinkPi encoder. For use with AUTOCROP_CHANNELS.
- LINKPI_USERNAME=${LINKPI_USERNAME} # LinkPi username. For use with AUTOCROP_CHANNELS.
- LINKPI_PASSWORD=${LINKPI_PASSWORD} # LinkPi password. For use with AUTOCROP_CHANNELS.
- USER_SCRIPT=${USER_SCRIPT} # A custom user script to be run at container start. If placed in /HOST_DIR/olivetin, only the script name is required, like ./userscript.sh
volumes:
- ${HOST_DIR:-/data}/ah4c/scripts:/opt/scripts # pre/stop/bmitune.sh scripts will be stored in this bound host directory under streamer/app
- ${HOST_DIR:-/data}/ah4c/m3u:/opt/m3u # m3u files will be stored here and hosted at http://<hostname or ip>:7654/m3u for use in Channels DVR - Custom Channels settings
- ${HOST_DIR:-/data}/ah4c/adb:/root/.android # Persistent data directory for adb keys
restart: unless-stopped
And in the Environment variables section of the Portainer-Stacks Editor:
TAG=latest
CONTAINER_NAME=ah4c
HOSTNAME=ah4c
DOMAIN=localdomain tailxxxxx.ts.net
ADBS_PORT=5037
HOST_PORT=7654
SCRC_PORT=7655
IPADDRESS=htpc6:7654
NUMBER_TUNERS=2
TUNER1_IP=firestick-rack1:5555
ENCODER1_URL=http://encoder_48007/0.ts
CMD1=ffmpeg -i ${ENCODER1_URL} -ss 20 -c:v copy -c:a copy -f mpegts -
TUNER2_IP=firestick-rack2:5555
ENCODER2_URL=http://encoder_48007/4.ts
CMD2=ffmpeg -i ${ENCODER2_URL} -ss 20 -c:v copy -c:a copy -f mpegts -
STREAMER_APP=scripts/firetv/espn
CHANNELSIP=media-server8
ALERT_SMTP_SERVER=smtp.gmail.com:587
ALERT_AUTH_SERVER=smtp.gmail.com
ALERT_EMAIL_FROM=[Redacted]
ALERT_EMAIL_PASS=[Redacted]
ALERT_EMAIL_TO=[Redacted]
LIVETV_ATTEMPTS=
CREATE_M3US=false
UPDATE_SCRIPTS=true
UPDATE_M3US=true
TZ=US/Mountain
SPEED_MODE=false
KEEP_WATCHING=235m
AUTOCROP_CHANNELS=
LINKPI_HOSTNAME=
LINKPI_USERNAME=
LINKPI_PASSWORD=
USER_SCRIPT=
HOST_DIR=/data
CDVR_M3U_NAME=espn_plus.m3u
Note that the env vars used in CMD1 and CMD2 are not placeholders -- those variable names should be used as shown. Adjust the number of seconds after -ss to suit.