I tried it and saw the same result as reported above.
So finally had time to update today. Didn’t turn on the playback detection or heartbeat- may do that later but it's not high on priority. Mainly looking for the CC options.
Like the new web interface @mackid1993
Had to wipe the adb authorizations on all (3) Osprey's and reauthorize. So all (3) tuners are working correctly now.
On the CC page, I selected "Choose automatically", added CC to every stream, Parakeet Unifed is highlighted and selected. On the client, CC shows as Track 1 and selected but no CC are coming thru. Not sure how to fix "the shared tuner does not answer”
Synology DSM 7
ah4c log:
2026/08/27 18:04:15 [CC] tuner0 picture rate is 30.00 fps, cc_count 20, line 21 at 29.97 pairs a second
2026/08/27 18:04:19 [CC] Loading a copy of parakeet-unified-en-0.6b-Q4_K_M.gguf for another stream (2 already in memory)
2026/08/27 18:04:22 [CC] Warmed parakeet-unified-en-0.6b-Q4_K_M.gguf into memory in 3.385s
2026/08/27 18:04:23 [CC] engine: parakeet: using cpu backend (strict)
2026/08/27 18:04:28 [CC] Loaded parakeet-unified-en-0.6b-Q4_K_M.gguf (3 in memory)
2026/08/27 18:04:28 [CC] recognizer worker up: parakeet-unified-en-0.6b-Q4_K_M.gguf, the processor backend, 2 threads, decoder window at the model's own default
2026/08/27 18:04:28 [CC] tuner0 captions on: parakeet-unified, en, phrase at a time, on the processor, ready in 12.493s
2026/08/27 18:04:52 [CC] tuner0 the shared recognizer did not answer
2026/08/27 18:04:52 [CC] tuner0 skipped a phrase 9s old to stay current (1 so far)
2026/08/27 18:04:55 [CC] tuner0 the shared recognizer did not answer
2026/08/27 18:04:58 [CC] tuner0 the shared recognizer did not answer
2026/08/27 18:04:58 [CC] tuner0 the shared recognizer did not answer
Environmental Var:
TAG=latest
DOMAIN=local
DOCKER_RUNTIME=runc
GPU_DEVICE=/dev/dri
ADBS_PORT=5037
HOST_PORT=7654
WSCR_PORT=7655
IPADDRESS=10.0.1.65:7654
NUMBER_TUNERS=3
TUNER1_IP=10.0.1.76:5555
ENCODER1_URL=http://10.0.1.34:8090/stream0
TUNER2_IP=10.0.1.29:5555
ENCODER2_URL=http://10.0.1.34:8090/stream1
TUNER3_IP=10.0.1.77:5555
ENCODER3_URL=http://10.0.1.34:8090/stream2
TUNER4_IP=
ENCODER4_URL=
TUNER5_IP=
ENCODER5_URL=
STREAMER_APP=scripts/osprey/dtvospreydeeplinks
PYATV=false
CHANNELSIP=10.0.1.65
ALERT_SMTP_SERVER=
ALERT_AUTH_SERVER=
ALERT_EMAIL_FROM=
ALERT_EMAIL_PASS=
ALERT_EMAIL_TO=
LIVETV_ATTEMPTS=
CREATE_M3US=false
UPDATE_SCRIPTS=true
UPDATE_M3US=false
TZ=US/CENTRAL
SPEED_MODE=false
KEEP_WATCHING=4h
LINKPI_HOSTNAME=http://10.0.1.34
LINKPI_USERNAME:admin=
LINKPI_PASSWORD=admin
USER_SCRIPT=
NULL_FRAME_INSERTION=false
PLAYBACK_DETECTION=false
PLAYBACK_STATIC_TIMEOUT=10
PLAYBACK_DELAY=0
HEARTBEAT_INTERVAL=0
NVIDIA_VISIBLE_DEVICES=
NVIDIA_DRIVER_CAPABILITIES-=
HOST_DIR=/volume1/data
version: '3.9'
services:
# 2024.10.30
# GitHub home for this project with setup instructions: https://github.com/sullrich/ah4c
# Docker Hub home for this project: https://hub.docker.com/repository/docker/bnhf/ah4c
ah4c:
image: bnhf/ah4c:${TAG}
container_name: ah4c
hostname: ah4c
dns_search: ${DOMAIN} # Specify the name of your LAN's domain, usually local or localdomain
ports:
- ${ADBS_PORT}:5037 # Port used by adb-server
- ${HOST_PORT}:7654 # Port used by this ah4c proxy
- ${WSCR_PORT}: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, 4, 5, 6, 7, 8 or 9 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
- 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} # Set to true to create device-specific M3Us for use with Amazon Prime Premium channels -- requires a FireTV device
- UPDATE_SCRIPTS=${UPDATE_SCRIPTS} # Set to true if you'd like the sample scripts and STREAMER_APP scripts updated whether they exist or not
- UPDATE_M3US=${UPDATE_M3US} # Set to true if you'd like the sample m3us updated whether they exist or not
- TZ=${TZ} # Your local timezone in Linux "tz" format
- SPEED_MODE=${SPEED_MODE} # 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} # 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.
volumes:
- ${HOST_DIR}/ah4c/scripts:/opt/scripts # pre/stop/bmitune.sh scripts will be stored in this bound host directory under streamer/app
- ${HOST_DIR}/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}/ah4c/adb:/root/.android # Persistent data directory for adb keys
restart: unless-stopped
You're not using the latest Docker Compose -- this should have a very recent date.
Updating thru Portainer pulling the repull image toggle
Stop the ah4c stack. Go into the Portainer-Stacks Editor. Delete the current Docker Compose, and paste in the latest one as posted here:
You updated your env var overrides (though there are several mistakes), but not the Docker Compose. Check the date of your compose versus the one I put in the linked post.
My bad - I thought repulsing the image would do everything. Corrected.
Revised Compose
services:
# 2026.08.20
# GitHub home for this project with setup instructions: https://github.com/sullrich/ah4c
# Docker Hub home for this project: https://hub.docker.com/repository/docker/bnhf/ah4c
ah4c:
image: bnhf/ah4c:${TAG:-latest}
container_name: ah4c
hostname: ah4c
dns_search: ${DOMAIN:-localdomain} # Specify the name of your LAN's domain, usually local or localdomain
runtime: ${DOCKER_RUNTIME:-runc} # Closed captions only. Set DOCKER_RUNTIME=nvidia for an NVIDIA GPU with the CUDA engine build. Requires the NVIDIA container toolkit
devices:
- ${GPU_DEVICE:-/dev/null} # Closed captions only. Set GPU_DEVICE=/dev/dri to let the Vulkan engine build use an Intel or AMD GPU. Left at the default it passes /dev/null, which always exists and does nothing
ports:
- ${HOST_PORT:-7654}:7654 # Port used by this ah4c proxy
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 - add a matching TUNERn_IP and ENCODERn_URL line below for each beyond 9
- 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
- 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)
- PYATV=${PYATV:-false} # Set to TRUE to run docker-start-pyatv.sh at container start for Apple TV tuners via pyatv, instead of the default docker-start.sh used for adb-based tuners. Case-insensitive; anything else runs the default.
- 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=${ALERT_WEBHOOK_URL} # URL to GET when an alert fires (same failures that trigger the e-mail); put $reason in the URL and it's replaced with the URL-encoded message. Blank disables it.
- 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 or not
- UPDATE_M3US=${UPDATE_M3US:-true} # Set to true if you'd like the sample m3us updated whether they exist or 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} # 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 of the LinkPi Encoder's web API. Required for AUTOCROP_CHANNELS.
- LINKPI_USERNAME=${LINKPI_USERNAME} # Username for the LinkPi Encoder's web API. Required for AUTOCROP_CHANNELS.
- LINKPI_PASSWORD=${LINKPI_PASSWORD} # Password for the LinkPi Encoder's web API. Required for AUTOCROP_CHANNELS; not currently read by the bundled scripts, which log in with the LinkPi default password instead.
- USER_SCRIPT=${USER_SCRIPT} # Path to a custom script to run alongside ah4c at container startup. Blank runs nothing extra.
- NULL_FRAME_INSERTION=${NULL_FRAME_INSERTION:-false} # Set to TRUE to fill encoder stalls with MPEG-TS NULL packets (PID 0x1FFF) so the DVR never sees a zero-byte gap mid-recording. Case-insensitive (true/True/TRUE all work); anything else, including 1/yes, leaves the feature off.
- PLAYBACK_DETECTION=${PLAYBACK_DETECTION:-false} # Set to TRUE to hold the stream until the device reports media audio playing and the picture is actually moving, then start on a keyframe, so a recording begins on the program rather than on the app's loading screen. Requires adb access to the tuner; network tuners only. Case-insensitive (true/True/TRUE all work); anything else, including 1/yes, leaves the feature off.
- PLAYBACK_DELAY=${PLAYBACK_DELAY} # Set to a whole number of seconds to skip the start of each tune, so a recording begins on the program rather than on the app's loading screen. Piped through the bundled ffmpeg with -ss and stream copy; no re-encoding, and the skip starts on the next keyframe so it can run slightly past the configured value. The value is the total tune time, scripts included. Supported range is 2 to 30, since the DVR allows a tune about 30 seconds; values outside the range are clamped and logged. Ignored when PLAYBACK_DETECTION is TRUE; network tuners only. 0 or unset leaves the feature off.
- PLAYBACK_STATIC_TIMEOUT=${PLAYBACK_STATIC_TIMEOUT} # Only used with PLAYBACK_DETECTION=TRUE. How many seconds the box may keep the exact player and media session it already had before the check stops watching adb and gates on motion alone. The default is 2, which suits Ospreys; apps that hold one player across channel changes, like the DirecTV app, need more. 0 or unset uses the default.
- HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-0} # In supported scripts (currently osprey), seconds between keepalive keyevents sent during playback to stop the app's UI inactivity timer from resetting the stream. Set to 0 to disable.
- NVIDIA_VISIBLE_DEVICES=${NVIDIA_VISIBLE_DEVICES} # Closed captions only. Set to all alongside DOCKER_RUNTIME=nvidia to expose an NVIDIA GPU. Empty means no GPU and is the default
- NVIDIA_DRIVER_CAPABILITIES=${NVIDIA_DRIVER_CAPABILITIES} # Closed captions only. Set to compute,utility when using an NVIDIA GPU, so the driver the CUDA engine build needs is passed in
volumes:
- ${HOST_DIR}/ah4c/scripts:/opt/scripts # pre/stop/bmitune.sh scripts will be stored in this bound host directory under streamer/app
- ${HOST_DIR}/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}/ah4c/adb:/root/.android # Persistent data directory for adb keys
- ${HOST_DIR}/ah4c/captions:/opt/captions # Closed caption settings, and the speech model, engine and any GPU driver downloaded from the Closed Captions page. Stays empty unless you turn captions on
restart: unless-stopped
Env Variables
TAG=latest
DOMAIN=local
DOCKER_RUNTIME=runc
GPU_DEVICE=/dev/dri
ADBS_PORT=5037
HOST_PORT=7654
WSCR_PORT=7655
IPADDRESS=10.0.1.65:7654
NUMBER_TUNERS=3
TUNER1_IP=10.0.1.76:5555
ENCODER1_URL=http://10.0.1.34:8090/stream0
TUNER2_IP=10.0.1.29:5555
ENCODER2_URL=http://10.0.1.34:8090/stream1
TUNER3_IP=10.0.1.77:5555
ENCODER3_URL=http://10.0.1.34:8090/stream2
TUNER4_IP=
ENCODER4_URL=
TUNER5_IP=
ENCODER5_URL=
STREAMER_APP=scripts/osprey/dtvospreydeeplinks
PYATV=false
CHANNELSIP=10.0.1.65
ALERT_SMTP_SERVER=
ALERT_AUTH_SERVER=
ALERT_EMAIL_FROM=
ALERT_EMAIL_PASS=
ALERT_EMAIL_TO=
LIVETV_ATTEMPTS=
CREATE_M3US=false
UPDATE_SCRIPTS=true
UPDATE_M3US=false
TZ=US/CENTRAL
SPEED_MODE=false
KEEP_WATCHING=4h
LINKPI_HOSTNAME=http://10.0.1.34
LINKPI_USERNAME:admin=
LINKPI_PASSWORD=admin
USER_SCRIPT=
NULL_FRAME_INSERTION=false
PLAYBACK_DETECTION=false
PLAYBACK_STATIC_TIMEOUT=10
PLAYBACK_DELAY=0
HEARTBEAT_INTERVAL=0
NVIDIA_VISIBLE_DEVICES=
NVIDIA_DRIVER_CAPABILITIES-=
HOST_DIR=/volume1/data
Ah4c log showing tune from start to end. Had a few captions but were way behind.
2026/08/27 20:27:55 Attempting network tune for device http://10.0.1.34:8090/stream0 10.0.1.76:5555 POPHD~d7f25adf-9813-5570-bdaa-83d1aef41777
2026/08/27 20:27:55 [EXECUTE] Running [./scripts/osprey/dtvospreydeeplinks/prebmitune.sh 10.0.1.76:5555 POPHD~d7f25adf-9813-5570-bdaa-83d1aef41777]
2026/08/27 20:28:00 [EXECUTE] Stdout: 'already connected to 10.0.1.76:5555
Waking 10.0.1.76:5555
Readiness gate: 10.0.1.76:5555 is live and hot
prebmitune.sh is exiting for 10.0.1.76:5555 with exit code 0
'
2026/08/27 20:28:00 [EXECUTE] Stderr: '+ streamerIP=10.0.1.76:5555
+ streamerNoPort=10.0.1.76
+ adbTarget='timeout 15 adb -s 10.0.1.76:5555'
+ dtvPackage=com.att.tv.openvideo
+ mkdir -p 10.0.1.76
+ trap finish EXIT
+ main
+ adbConnect
+ adb connect 10.0.1.76:5555
+ local -i adbMaxRetries=3
+ local -i adbCounter=0
+ true
+ timeout 15 adb -s 10.0.1.76:5555 shell input keyevent KEYCODE_WAKEUP
+ local adbEventSuccess=0
+ [[ 0 -eq 0 ]]
+ break
+ adbWake
+ timeout 15 adb -s 10.0.1.76:5555 shell input keyevent KEYCODE_WAKEUP
+ echo 'Waking 10.0.1.76:5555'
+ touch 10.0.1.76/adbAppRunning
+ readinessGate
+ timeout 15 adb -s 10.0.1.76:5555 shell '
end=$((SECONDS+10))
while [ $SECONDS -lt $end ]; do
dumpsys audio 2>/dev/null | grep -qE '\''pack: com.att.tv.openvideo.*gain: GAIN '\'' && exit 0
dumpsys media_session 2>/dev/null | grep -qE '\''PlaybackState \{state=3'\'' && exit 0
done
exit 1'
+ [[ 0 -ne 0 ]]
+ echo 'Readiness gate: 10.0.1.76:5555 is live and hot'
+ finish
+ echo 'prebmitune.sh is exiting for 10.0.1.76:5555 with exit code 0'
'
2026/08/27 20:28:00 [EXECUTE] Finished running ./scripts/osprey/dtvospreydeeplinks/prebmitune.sh in 5.090301067s
2026/08/27 20:28:00 [EXECUTE] Running [./scripts/osprey/dtvospreydeeplinks/bmitune.sh POPHD~d7f25adf-9813-5570-bdaa-83d1aef41777 10.0.1.76:5555]
2026/08/27 20:28:00 [CC] tuner0 video is H.264 on PID 100
2026/08/27 20:28:00 [CC] tuner0 announced the caption service in the program table
2026/08/27 20:28:00 [EXECUTE] Stdout: 'Starting: Intent { act=android.intent.action.VIEW dat=https://deeplink.directvnow.com/... pkg=com.att.tv.openvideo }
bmitune.sh is exiting for 10.0.1.76:5555 with exit code 1
'
2026/08/27 20:28:00 [EXECUTE] Stderr: '++ echo POPHD~d7f25adf-9813-5570-bdaa-83d1aef41777
++ awk -F~ '{print $2}'
+ channelID=d7f25adf-9813-5570-bdaa-83d1aef41777
++ echo POPHD~d7f25adf-9813-5570-bdaa-83d1aef41777
++ awk -F~ '{print $1}'
+ channelName=POPHD
+ streamerIP=10.0.1.76:5555
+ streamerNoPort=10.0.1.76
+ adbTarget='adb -s 10.0.1.76:5555'
+ heartbeatInterval=0
+ heartbeatKeycode=KEYCODE_ZENKAKU_HANKAKU
+ mkdir -p 10.0.1.76
+ echo 1258
+ trap finish EXIT
+ main
+ tuneChannel
+ adb -s 10.0.1.76:5555 shell 'am start -a android.intent.action.VIEW -d '\''https://deeplink.directvnow.com/tune/live/channel/POPHD/d7f25adf-9813-5570-bdaa-83d1aef41777'\'' com.att.tv.openvideo'
Warning: Activity not started, intent has been delivered to currently running top-most instance.
+ [[ 0 != 0 ]]
+ finish
+ echo 'bmitune.sh is exiting for 10.0.1.76:5555 with exit code 1'
'
2026/08/27 20:28:00 [EXECUTE] Finished running ./scripts/osprey/dtvospreydeeplinks/bmitune.sh in 112.373417ms
2026/08/27 20:28:00 [ERR] Failed to run start script: exit status 1
2026/08/27 20:28:01 [CC] tuner0 picture rate is 30.00 fps, cc_count 20, line 21 at 29.97 pairs a second
2026/08/27 20:28:05 [CC] Warmed parakeet-unified-en-0.6b-Q4_K_M.gguf into memory in 193ms
2026/08/27 20:28:05 [CC] engine: parakeet: using vulkan backend: Vulkan0
2026/08/27 20:28:12 [CC] Loaded parakeet-unified-en-0.6b-Q4_K_M.gguf (1 in memory)
2026/08/27 20:28:12 [CC] recognizer worker up: parakeet-unified-en-0.6b-Q4_K_M.gguf, Vulkan backend, 1 threads, decoder window at the model's own default
2026/08/27 20:28:12 [CC] tuner0 captions on: parakeet-unified, en, phrase at a time, on Vulkan, ready in 10.848s
2026/08/27 20:28:15 [CC] tuner0 passed over 1 stretch too short to be worth transcribing (1 so far)
2026/08/27 20:28:31 [CC] tuner0 captions are running 11s behind (nothing dropped)
2026/08/27 20:28:44 [CC] engine: decoder (rnnt): 71 iters, 28 tokens, T_enc=43 enc_proj=9.7 ms pred=141.6 ms joint=49.1 ms conf=1.0 ms total=201.3 ms
2026/08/27 20:28:44 [CC] engine: decoder (rnnt): 67 iters, 23 tokens, T_enc=44 enc_proj=9.9 ms pred=115.8 ms joint=45.3 ms conf=0.8 ms total=171.8 ms
2026/08/27 20:28:44 [CC] tuner0 skipped a phrase 6s old to stay current (1 so far)
2026/08/27 20:28:51 [CC] tuner0 the shared recognizer did not answer
2026/08/27 20:28:55 [CC] tuner0 the shared recognizer did not answer
2026/08/27 20:29:04 [CC] tuner0 the shared recognizer did not answer
2026/08/27 20:29:05 [CC] tuner0 the shared recognizer did not answer
2026/08/27 20:29:11 [CC] tuner0 the shared recognizer did not answer
2026/08/27 20:29:15 [CC] tuner0 the shared recognizer did not answer
2026/08/27 20:29:23 [CC] engine: decoder (rnnt): 80 iters, 37 tokens, T_enc=43 enc_proj=10.1 ms pred=186.8 ms joint=55.5 ms conf=1.2 ms total=253.7 ms
2026/08/27 20:29:23 [CC] engine: decoder (rnnt): 63 iters, 22 tokens, T_enc=41 enc_proj=9.2 ms pred=111.2 ms joint=44.1 ms conf=0.8 ms total=165.3 ms
2026/08/27 20:29:23 [CC] engine: decoder (rnnt): 66 iters, 19 tokens, T_enc=47 enc_proj=10.5 ms pred=96.8 ms joint=44.8 ms conf=0.7 ms total=152.7 ms
2026/08/27 20:29:23 [CC] engine: decoder (rnnt): 59 iters, 12 tokens, T_enc=47 enc_proj=10.9 ms pred=63.2 ms joint=39.0 ms conf=0.4 ms total=113.6 ms
2026/08/27 20:29:24 [CC] tuner0 the shared recognizer did not answer
2026/08/27 20:29:25 [CC] tuner0 the shared recognizer did not answer
2026/08/27 20:29:31 [CC] tuner0 the shared recognizer did not answer
2026/08/27 20:29:35 [CC] tuner0 the shared recognizer did not answer
2026/08/27 20:29:41 [CC] tuner0 behind: 1 phrase dropped
2026/08/27 20:29:44 [CC] tuner0 the shared recognizer did not answer
2026/08/27 20:29:45 [CC] tuner0 the shared recognizer did not answer
2026/08/27 20:29:51 [CC] tuner0 the shared recognizer did not answer
2026/08/27 20:29:55 [CC] tuner0 the shared recognizer did not answer
2026/08/27 20:30:02 [IO] io.Copy: write tcp 172.21.0.2:7654->172.21.0.1:56902: write: broken pipe
2026/08/27 20:30:02 [IOINFO] Successfully copied 51316668 bytes
2026/08/27 20:30:02 [IOINFO] Transfer speed: 3.375202715019211 Mbits/second
2026/08/27 20:30:02 Performing Close() for 10.0.1.76:5555
2026/08/27 20:30:02 [EXECUTE] Running [./scripts/osprey/dtvospreydeeplinks/stopbmitune.sh 10.0.1.76:5555 POPHD~d7f25adf-9813-5570-bdaa-83d1aef41777]
2026/08/27 20:30:03 [EXECUTE] Stdout: 'Sleep initiated for 10.0.1.76:5555
10.0.1.76/stream_stopped written with epoch stop time
'
2026/08/27 20:30:03 [EXECUTE] Stderr: '+ streamerIP=10.0.1.76:5555
+ streamerNoPort=10.0.1.76
+ adbTarget='adb -s 10.0.1.76:5555'
+ main
+ bmituneDone
+ bmitunePID=1258
+ ps -p 1258
+ [[ -f 10.0.1.76/heartbeat_pid ]]
+ rm -f ./10.0.1.76/heartbeat.sh
+ adbSleep
+ sleep='input keyevent KEYCODE_SLEEP'
+ adb -s 10.0.1.76:5555 shell input keyevent KEYCODE_SLEEP
+ echo 'Sleep initiated for 10.0.1.76:5555'
+ date +%s
+ echo '10.0.1.76/stream_stopped written with epoch stop time'
'
2026/08/27 20:30:03 [EXECUTE] Finished running ./scripts/osprey/dtvospreydeeplinks/stopbmitune.sh in 1.050487504s
2026/08/27 20:30:03 [GIN-debug] Request: 172.21.0.1 GET /play/tuner/POPHD~d7f25adf-9813-5570-bdaa-83d1aef41777, latency: 2m7.952972757s, status: 200
2026/08/27 20:30:03 [CC] tuner0 captions stopped
2026/08/27 20:30:25 [CC] engine: decoder (rnnt): 80 iters, 33 tokens, T_enc=47 enc_proj=10.4 ms pred=164.4 ms joint=55.0 ms conf=1.2 ms total=231.1 ms
2026/08/27 20:30:26 [CC] engine: decoder (rnnt): 76 iters, 27 tokens, T_enc=49 enc_proj=10.9 ms pred=135.4 ms joint=51.5 ms conf=1.0 ms total=198.8 ms
2026/08/27 20:30:26 [CC] engine: decoder (rnnt): 69 iters, 23 tokens, T_enc=46 enc_proj=10.3 ms pred=118.3 ms joint=48.7 ms conf=0.8 ms total=178.2 ms
2026/08/27 20:30:26 [CC] engine: decoder (rnnt): 68 iters, 24 tokens, T_enc=44 enc_proj=10.8 ms pred=121.3 ms joint=46.0 ms conf=0.8 ms total=178.9 ms
2026/08/27 20:30:26 [CC] engine: decoder (rnnt): 45 iters, 16 tokens, T_enc=29 enc_proj=6.5 ms pred=82.6 ms joint=30.7 ms conf=0.6 ms total=120.4 ms
2026/08/27 20:30:26 [CC] engine: decoder (rnnt): 79 iters, 34 tokens, T_enc=45 enc_proj=10.1 ms pred=168.1 ms joint=54.2 ms conf=1.2 ms total=233.5 ms
2026/08/27 20:30:39 [CC] The recognizer released its copy of the weights
Delete this.
Delete this.
Fix this. Country/Timezone has been deprecated. Use Continent/City now.
Fix this. It should be LINKPI_USERNAME=admin
EDIT:
Fix this. I should be NVIDIA_DRIVER_CAPABILITIES=
Is the iGPU working? Even if it is, you may need to be on the least resource intensive model. Your processor is pretty low-end.
From the CC page:
Hardware acceleration is active.
Choose automatically is selected & downloaded
Vulcan driver is loaded
Parakeet Unified EN 0.6B is selected
Defaults for caption settings
Recs for changing
For the most part, it's better. Captions are about 4-5 secs behind. I took it off Automatic and selected the GPU thru Vulcan. Accuracy is acceptable.
I did run it thru the CPU and it takes 35-40%. Running thru the GPU is 3-6%.
I may move this over to my Mac Mini M4 Pro which is only running PrismCast at this time. I'm pretty sure it would handle it better.
The M4 Mac will run it a lot better, but only on CPU, but it's not going to be a problem on the CPU on that machine.
Just as an aside, I want to tease. I've been working on support for longer delays, so you can essentially delay as long as you possibly could want to cover up the tuning process. So I've been able to get it to delay up to 5 minutes, and I haven't tried any longer because I don't have the patience. The environment variable doesn't change at all. It's just typing in 300 instead of 30 if you needed to delay 5 minutes for some reason. I don't know why anyone would need to do that, but it's good if you have a very long tuning process, such as 30 seconds or something, 35 seconds.
Most importantly, I have built pre-roll support. So while you're tuning, it will play a pre-roll either video or still image of whatever you choose. Now, the caveat with pre-roll support if your encoder is spitting out H.264, it works just fine, and ah4c will handle the conversion process of whatever your pre-roll is to H.264 to stitch it all together with the actual video that's going to be played. Now, if your encoder is spinning on H.265, you do have to convert it and supply H.265 yourself, whether it be a still image or a video that loops. And everything will loop automatically, by the way. So, if your timeout or tuning process is longer than the video that is being supplied, it will just loop until it completes.
If anyone's interested in demoing this, feel free to pull mackid1993/ah4c:latest from Docker Hub.
- ENCODER_CODEC=${ENCODER_CODEC:-h264} You need this environment variable if you want to use H.265 in your env. Just set it to h265.
To actually use pre-roll, you must set a new bind mount. This is where the actual video or image is stored.
- ${PREROLL_FILE:-${HOST_DIR}/ah4c/preroll}:/opt/preroll # A video or still image to show the DVR wherever it would otherwise be sent NULL packets: a tune held by PLAYBACK_DELAY or PLAYBACK_DETECTION, and an encoder stall covered by NULL_FRAME_INSERTION. Set PREROLL_FILE to the file's path on the host, or leave it blank and drop the file into this bound host directory, which stays empty unless you use one. Anything ffmpeg reads. It is prepared once at container start (remuxed when its codecs already suit a transport stream, encoded to H.264/AAC otherwise; a still becomes a 10 second clip with silent audio), starts the instant the DVR asks, before the box is even woken, loops for as long as the tune takes, and stops the moment the real stream is ready
I'm linking to my updated README with my changes, so it's a little bit more clear. There are also instructions to use FFmpeg to create your own pre-rolls that work with H.265. I recommend just sticking to H.264 because it's a bit more compatible.
New bnhf/olivetin:latest (aka bnhf/olivetin:2026.08.28) pushed with the Project One-Click for ah4c updated to include support for all of the latest changes. Up to, and including, the enhanced PLAYBACK_DELAY and pre-roll support described just above.
Many thanks to @mackid1993 for another amazing contribution, as pre-roll support now allows you to fill the slot between when you initiate a tune for a channel, and when the channel starts playing.
These pre-rolls can be videos or static images. I'm currently using this one:
Though I like this one too:
Use your imagination, and be sure to post what you're using here so others can use it too. @mackid1993 was testing with a video from the 50s that was an animated encouragement to go get some snacks at the movie theater. There are all kinds of classics out there to choose from! Static images are probably best for those with very quick tune times.
Here's the latest Docker Compose, to facilitate your upgrades to the current images:
services:
# 2026.08.28
# GitHub home for this project with setup instructions: https://github.com/sullrich/ah4c
# Docker Hub home for this project: https://hub.docker.com/repository/docker/bnhf/ah4c
ah4c: # This docker-compose typically requires no editing. Use the Environment variables section of Portainer to set your values.
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
runtime: ${DOCKER_RUNTIME:-runc} # Closed captions only. Set DOCKER_RUNTIME=nvidia for an NVIDIA GPU with the CUDA engine build. Requires the NVIDIA container toolkit.
devices:
- ${GPU_DEVICE:-/dev/null} # Closed captions only. Set GPU_DEVICE=/dev/dri to let the Vulkan engine build use an Intel or AMD GPU. Left at the default it passes /dev/null, which always exists and does nothing.
ports:
- ${HOST_PORT:-7654}:7654 # Port used by this ah4c proxy
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 - add a matching TUNERn_IP and ENCODERn_URL line below for each beyond 9
- 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
- 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)
- PYATV=${PYATV:-false} # Set to TRUE to run docker-start-pyatv.sh at container start for Apple TV tuners via pyatv, instead of the default docker-start.sh used for adb-based tuners. Case-insensitive; anything else runs the default.
- 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=${ALERT_WEBHOOK_URL} # URL to GET when an alert fires (same failures that trigger the e-mail); put $reason in the URL and it's replaced with the URL-encoded message. Blank disables it.
- 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 or not
- UPDATE_M3US=${UPDATE_M3US:-true} # Set to true if you'd like the sample m3us updated whether they exist or 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} # 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 of the LinkPi Encoder's web API. Required for AUTOCROP_CHANNELS.
- LINKPI_USERNAME=${LINKPI_USERNAME} # Username for the LinkPi Encoder's web API. Required for AUTOCROP_CHANNELS.
- LINKPI_PASSWORD=${LINKPI_PASSWORD} # Password for the LinkPi Encoder's web API. Required for AUTOCROP_CHANNELS; not currently read by the bundled scripts, which log in with the LinkPi default password instead.
- USER_SCRIPT=${USER_SCRIPT} # Path to a custom script to run alongside ah4c at container startup. Blank runs nothing extra.
- NULL_FRAME_INSERTION=${NULL_FRAME_INSERTION:-false} # Set to TRUE to fill encoder stalls with MPEG-TS NULL packets (PID 0x1FFF) so the DVR never sees a zero-byte gap mid-recording. Case-insensitive (true/True/TRUE all work); anything else, including 1/yes, leaves the feature off.
- PLAYBACK_DETECTION=${PLAYBACK_DETECTION:-false} # Set to TRUE to hold the stream until the device reports audio playing and the picture moving, then start on a keyframe, so recording begins on the program, not the loading screen. Requires adb; network tuners only. Case-insensitive; anything but true leaves it off.
- PLAYBACK_STATIC_TIMEOUT=${PLAYBACK_STATIC_TIMEOUT} # Only used with PLAYBACK_DETECTION=TRUE. Seconds the box may keep its prior player/session before the check falls back to gating on motion alone. Default 2 suits Ospreys; apps like DirecTV that hold one player across channel changes need more. 0 or unset uses the default.
- PLAYBACK_DELAY=${PLAYBACK_DELAY} # Hold every tune this long before handing the DVR the program, so a slow-starting app still records within the DVR's 30s window. Black or a mounted pre-roll fills the wait; the box's own video is never passed through. Accepts a bare number (seconds) or a duration like 30s/1m, capped at 10m. Network tuners only. Empty or 0 disables it.
- ENCODER_CODEC=${ENCODER_CODEC:-h264} # The video codec your encoder outputs: h264 (default) or h265. Filler black/pre-roll must match it, or playback won't cross to the program at hand-off. An H.264 pre-roll is refused on an H.265 encoder (falls back to black). Leave at h264 unless your encoder is H.265. Case-insensitive; h265/hevc both work.
- HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-0} # In supported scripts (currently osprey), seconds between keepalive keyevents sent during playback to stop the app's UI inactivity timer from resetting the stream. Set to 0 to disable.
- NVIDIA_VISIBLE_DEVICES=${NVIDIA_VISIBLE_DEVICES} # Closed captions only. Set to all alongside DOCKER_RUNTIME=nvidia to expose an NVIDIA GPU. Empty means no GPU and is the default.
- NVIDIA_DRIVER_CAPABILITIES=${NVIDIA_DRIVER_CAPABILITIES} # Closed captions only. Set to compute,utility when using an NVIDIA GPU, so the driver the CUDA engine build needs is passed in.
volumes:
- ${HOST_DIR}/ah4c/scripts:/opt/scripts # pre/stop/bmitune.sh scripts will be stored in this bound host directory under streamer/app
- ${HOST_DIR}/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}/ah4c/adb:/root/.android # Persistent data directory for adb keys
- ${HOST_DIR}/ah4c/captions:/opt/captions # Closed caption settings, and the speech model, engine and any GPU driver downloaded from the Closed Captions page. Stays empty unless you turn captions on
- ${PREROLL_FILE:-${HOST_DIR}/ah4c/preroll}:/opt/preroll # A video or still image shown to the DVR instead of NULL packets, during a PLAYBACK_DELAY/PLAYBACK_DETECTION hold or a NULL_FRAME_INSERTION stall. Set PREROLL_FILE to a host path, or drop the file into this directory. Anything ffmpeg reads; prepared once at startup, loops until the real stream is ready
restart: unless-stopped
And a sample set of env vars (with Portainer-Stacks in mind):
TAG=latest
CONTAINER_NAME=ah4c
HOSTNAME=ah4c
DOMAIN=localdomain tailxxxxx.ts.net
DOCKER_RUNTIME=runc
GPU_DEVICE=/dev/dri
HOST_PORT=7654
IPADDRESS=docker6:7654
NUMBER_TUNERS=5
TUNER1_IP=firestick-desk1:5555
ENCODER1_URL=http://linkpi-encoder2:8090/stream0
TUNER2_IP=firestick-desk2:5555
ENCODER2_URL=http://linkpi-encoder2:8090/stream1
TUNER3_IP=firestick-desk3:5555
ENCODER3_URL=http://linkpi-encoder2:8090/stream2
TUNER4_IP=firestick-desk4:5555
ENCODER4_URL=http://linkpi-encoder2:8090/stream3
TUNER5_IP=firestick-desk5:5555
ENCODER5_URL=http://linkpi-encoder2:8090/stream4
TUNER6_IP=
ENCODER6_URL=
TUNER7_IP=
ENCODER7_URL=
TUNER8_IP=
ENCODER8_URL=
TUNER9_IP=
ENCODER9_URL=
STREAMER_APP=scripts/firetv/dtvstreamdeeplinks
PYATV=false
CHANNELSIP=media-server10
ALERT_SMTP_SERVER=smtp.gmail.com:587
ALERT_AUTH_SERVER=smtp.gmail.com
[email protected]
ALERT_EMAIL_PASS=xxxxxxxxxxxxxxxx
[email protected]
ALERT_WEBHOOK_URL=
LIVETV_ATTEMPTS=
CREATE_M3US=false
UPDATE_SCRIPTS=true
UPDATE_M3US=true
TZ=America/Denver
SPEED_MODE=false
KEEP_WATCHING=235m
AUTOCROP_CHANNELS=
LINKPI_HOSTNAME=
LINKPI_USERNAME=
LINKPI_PASSWORD=
USER_SCRIPT=
NULL_FRAME_INSERTION=false
PLAYBACK_DETECTION=true
PLAYBACK_STATIC_TIMEOUT=12
PLAYBACK_DELAY=
PREROLL_FILE=
ENCODER_CODEC=h264
HEARTBEAT_INTERVAL=
NVIDIA_VISIBLE_DEVICES=
NVIDIA_DRIVER_CAPABILITIES=
HOST_DIR=/data
my internet must have had a hiccup this morning, and all my recordings have this screen

log was packed full of
2026/08/28 11:34:27 [CC] tuner0 a minute of audio with no speech found in it; resetting the level detector
can we get ah4c to try to re-tune when the detector is triggered for too long?
Interesting idea!
I saw your other post, but since it records all day it never tried to re-tune. I went ahead and set my padding to end 10 sec early so ah4c tunes for every recording.
another thought I had would be a round-robin setting so even if a tuner has the channel tuned, ah4c grabs another one (but extra tuners/encoders would be needed.)
edit, this is a similar experience when the internet goes out for more than a few minutes
There was nothing to retune, though. What the boxers were putting out was garbage. Like every Osprey was putting out garbage video. So there was nothing to attempt to retune, because AH4C had no understanding of what was being played. It assumed that the regular program was being aired. DirecTV was just playing the same loop of content over and over again, and/or the same commercial over and over again, and/or just buffering and crapping out. There's really no good way for us to detect that. If it happens to be that the service provider has an issue, I don't think we can really fix that.


