on my boxes changing the channel brings up a splash screen for the new channel, and cuts out the audio. usually about 2 seconds. might be enough window to re-detect audio.
ie, wake, wait for audio, tune, wait 1 sec, wait for audio, send stream.
on my boxes changing the channel brings up a splash screen for the new channel, and cuts out the audio. usually about 2 seconds. might be enough window to re-detect audio.
ie, wake, wait for audio, tune, wait 1 sec, wait for audio, send stream.
My 2 cents:
When I tune to an Osprey channel using deeplinks, I will see the channel splash screen just prior to the channel going live. Prior to deeplinks, I would see the channel numbers. I really like that behavior because it gives me visual proof that everything is working as expected. I have had instances where the box has missed the adb authorization and it just goes back to last viewed channel. The adb authorization screen never shows on the tv, only on the link pi for 1-2 seconds. Retuning to another channel has always fixed it. When it occurs on 1, the others will follow suit so I know to fix them.
ah4c (appletv tag): tuner held permanently when encoder input goes silent โ io.Copy blocked in Read with no deadline
In a nutshell, I'd periodically find both of my ah4c tuners showing in use when they are not being used.
I've been working through this one with Claude for a little bit, and this is the summary of what it's helped me find:
On bnhf/ah4c:appletv using the Spectrum scripts, a tuner can be marked Active forever with no error logged. It is only recoverable by restarting the container.
The trigger is reproducible on demand: put the streaming Apple TV to sleep while a stream is in progress. The HDMI encoder stops emitting bytes but holds the TCP connection open. ah4c blocks in net.(*conn).Read with no deadline and never returns, so Close() and the stop script never run and the tuner is never released.
I've got a much longer doc outlining what was done and the logs captured showing everything, which I threw into a google doc to avoid a long post here.
Can you post the Spectrum scripts you're using please, along with the M3U file?
This is what I've got:
{
echo "### `prebmitune.sh`"
echo 'bash' cat scripts/atv/spectrum/prebmitune.sh echo ''
echo
echo "### `bmitune.sh`"
echo 'bash' cat scripts/atv/spectrum/bmitune.sh echo ''
echo
echo "### `stopbmitune.sh`"
echo 'bash' cat scripts/atv/spectrum/stopbmitune.sh echo ''
echo
echo "### `spectrum.m3u`"
echo '' cat m3u/spectrum.m3u echo ''
} > /home/peter/ah4c_watchdog/scripts-and-m3u.md
cat /home/peter/ah4c_watchdog/scripts-and-m3u.md
prebmitune.sh
#!/bin/bash
streamerIP="$1"
# 1. Wake the Apple TV
/usr/local/bin/atvremote --storage-filename /root/.android/.pyatv.conf -s $streamerIP home
sleep 2
# 2. Gracefully open the Spectrum app to its default startup screen
/usr/local/bin/atvremote --storage-filename /root/.android/.pyatv.conf -s $streamerIP launch_app=spectrumTV://
# 3. Give the app 7 seconds to load your profile and system preferences
sleep 7
bmitune.sh
#!/bin/bash
channelID="$1"
streamerIP="$2"
/usr/local/bin/atvremote --storage-filename /root/.android/.pyatv.conf -s $streamerIP launch_app=spectrumTV://watch.spectrum.net/livetv/$channelID
stopbmitune.sh
#!/bin/bash
streamerIP="$1"
# 1. Exit the stream and drop to the home screen
/usr/local/bin/atvremote --storage-filename /root/.android/.pyatv.conf -s $streamerIP home
sleep 2
# 2. Open the App Switcher
/usr/local/bin/atvremote --storage-filename /root/.android/.pyatv.conf -s $streamerIP home home
sleep 3
# 3. Navigate left to focus on the Spectrum app
/usr/local/bin/atvremote --storage-filename /root/.android/.pyatv.conf -s $streamerIP left
sleep 2
# 4. Perform the "Double-Click UP" force-quit maneuver
# We send two 'up' commands in immediate succession
/usr/local/bin/atvremote --storage-filename /root/.android/.pyatv.conf -s $streamerIP up up
sleep 2
# 5. Return to Home Screen
/usr/local/bin/atvremote --storage-filename /root/.android/.pyatv.conf -s $streamerIP home
spectrum.m3u
#EXTM3U
#EXTINF:-1 channel-id="31507" channel-number="" tvc-guide-stationid="31507" tvg-group="" tvg-logo="",WGRZ
http://{{ .IPADDRESS }}/play/tuner/31507
#EXTINF:-1 channel-id="24052" channel-number="" tvc-guide-stationid="24052" tvg-group="" tvg-logo="",WKBW
http://{{ .IPADDRESS }}/play/tuner/24052
#EXTINF:-1 channel-id="24051" channel-number="" tvc-guide-stationid="24051" tvg-group="" tvg-logo="",WIVB
http://{{ .IPADDRESS }}/play/tuner/24051
#EXTINF:-1 channel-id="10091" channel-number="" tvc-guide-stationid="10091" tvg-group="" tvg-logo="",CBLT
http://{{ .IPADDRESS }}/play/tuner/10091
#EXTINF:-1 channel-id="65437" channel-number="" tvc-guide-stationid="65437" tvg-group="" tvg-logo="",MSG Sports Buffalo
http://{{ .IPADDRESS }}/play/tuner/65437
#EXTINF:-1 channel-id="43432" channel-number="" tvc-guide-stationid="43432" tvg-group="" tvg-logo="",Rewind TV
http://{{ .IPADDRESS }}/play/tuner/43432
#EXTINF:-1 channel-id="123810" channel-number="" tvc-guide-stationid="123810" tvg-group="" tvg-logo="",Bay News 9 Tampa
http://{{ .IPADDRESS }}/play/tuner/123810
#EXTINF:-1 channel-id="68904" channel-number="" tvc-guide-stationid="68904" tvg-group="" tvg-logo="",Spectrum News 1 Buffalo
http://{{ .IPADDRESS }}/play/tuner/68904
#EXTINF:-1 channel-id="63992" channel-number="" tvc-guide-stationid="63992" tvg-group="" tvg-logo="",WBBZ MeTv Buffalo
http://{{ .IPADDRESS }}/play/tuner/63992
#EXTINF:-1 channel-id="123830" channel-number="" tvc-guide-stationid="123830" tvg-group="" tvg-logo="",Spectrum News 13 Central Florida
http://{{ .IPADDRESS }}/play/tuner/123830
#EXTINF:-1 channel-id="34915" channel-number="" tvc-guide-stationid="34915" tvg-group="" tvg-logo="",WNED
http://{{ .IPADDRESS }}/play/tuner/34915
#EXTINF:-1 channel-id="43430" channel-number="" tvc-guide-stationid="43430" tvg-group="" tvg-logo="",WNLO
http://{{ .IPADDRESS }}/play/tuner/43430
#EXTINF:-1 channel-id="44668" channel-number="" tvc-guide-stationid="44668" tvg-group="" tvg-logo="",WNYO
http://{{ .IPADDRESS }}/play/tuner/44668
#EXTINF:-1 channel-id="42945" channel-number="" tvc-guide-stationid="42945" tvg-group="" tvg-logo="",WUTV
http://{{ .IPADDRESS }}/play/tuner/42945
#EXTINF:-1 channel-id="47796" channel-number="" tvc-guide-stationid="47796" tvg-group="" tvg-logo="",Antenna TV
http://{{ .IPADDRESS }}/play/tuner/47796
These channels are coming in over a VPN so I adjusted the stop script to force quit the spectrum app to minimize vpn traffic when we aren't streaming these channels. I also added the bit where it opens the Spectrum app and waits a few seconds for it to load before tuning to a channel, without this the app sometimes would launch with subtitles turned on, even though all settings had them disabled.
I'm concerned the way you've implemented the force-quit may be part of the issue here, so I'm posting a new set of scripts I'd like you to try below.
On the subject of your AI-generated report, we may already have a fix in the code tied to some work done a couple of months ago by @anon38303228. The fix is gated behind an env var, so I'm also posting a new Docker Compose and sample set of env vars below.
prebmitune.sh:
#!/bin/bash
# prebmitune.sh for atv/spectrum
# 2026.08.05
streamerIP="$1"
atvTarget="timeout 15 /usr/local/bin/atvremote --storage-filename /root/.android/.pyatv.conf -s $streamerIP"
# Wake the Apple TV -- also wakes a device suspended by stopbmitune.sh's turn_off
$atvTarget home
sleep 2
# Gracefully open the Spectrum app to its default startup screen
$atvTarget launch_app=spectrumTV://
sleep 7
bmitune.sh:
#!/bin/bash
# bmitune.sh for atv/spectrum
# 2026.08.05
channelID="$1"
streamerIP="$2"
/usr/local/bin/atvremote --storage-filename /root/.android/.pyatv.conf -s $streamerIP launch_app=spectrumTV://watch.spectrum.net/livetv/$channelID
stopbmitune.sh:
#!/bin/bash
# stopbmitune.sh for atv/spectrum
# 2026.08.05
streamerIP="$1"
channelID="$2"
atvTarget="timeout 15 /usr/local/bin/atvremote --storage-filename /root/.android/.pyatv.conf -s $streamerIP"
# Best-effort: back out of the stream to the home screen
$atvTarget home
# Deterministic backstop: guarantees playback stops and the tuner is
# released even if the app didn't background cleanly above. This is the
# same role KEYCODE_SLEEP plays in the adb-based script sets.
$atvTarget turn_off
ah4c.yaml:
services:
# 2026.08.05
# 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 - 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)
- 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.
- NULL_FRAME_INSERTION=${NULL_FRAME_INSERTION} # 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. Must be the literal string TRUE; anything else (including true/1/yes) leaves the feature off.
- HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL} # 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.
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
ah4c.env:
TAG=appletv
DOMAIN=localdomain tailxxxxx.ts.net
ADBS_PORT=5037
HOST_PORT=7654
SCRC_PORT=7655
IPADDRESS=htpc6:7654
NUMBER_TUNERS=5
TUNER1_IP=firestick-rack1:5555
ENCODER1_URL=http://encoder_48007/0.ts
TUNER2_IP=firestick-rack2:5555
ENCODER2_URL=http://encoder_48007/4.ts
TUNER3_IP=firestick-rack3:5555
ENCODER3_URL=http://encoder_48007/8.ts
TUNER4_IP=firestick-rack4:5555
ENCODER4_URL=http://encoder_48007/12.ts
TUNER5_IP=firestick-travel2:5555
ENCODER5_URL=http://encoder_23393/0.ts
STREAMER_APP=scripts/firetv/dtvdeeplinks
CHANNELSIP=media-server6
ALERT_SMTP_SERVER=smtp.gmail.com:587
ALERT_AUTH_SERVER=smtp.gmail.com
[email protected]
ALERT_EMAIL_PASS=xxxxxxxxxxxxxxxx
[email protected]
UPDATE_SCRIPTS=true
UPDATE_M3US=true
TZ=America/Denver
SPEED_MODE=false
KEEP_WATCHING=4h
NULL_FRAME_INSERTION=TRUE
HEARTBEAT_INTERVAL=0
HOST_DIR=/data
It's the NULL_FRAME_INSERTION=TRUE (and corresponding line in the Compose) that we're implementing here -- if you just want to update your existing stack.
Let me know how this goes, as I'd like to update the repo accordingly, if everything looks good.
Updated everything and testing now. Thank you for the stopbmitune modification, that is much cleaner than what I worked out.
I'll report back once I know if this corrects it
100% - the ways I could reproduce the issue yesterday now work correctly and the tuner releases within a couple seconds! Thank you!
You hit the nail on the head. I utilize ah4c to run my Osprey's and ADBTuner w/Fruitlinks to pick up my ESPN+ stuff to watch & record.
I'll give it a try once I get home. Looks promising enough.
Please make sure env vars like $PLAYBACK_DETECTION are case insensitive (TRUE,True and true should all work). I fixed $NULL_FRAME_INSERTION yesterday to conform to that, though I haven't built a new container yet.
Haven't had any issue using your fork with YTTV. I had to modify the script since its long been updated (for those who need it)
bmitune.sh
#!/bin/bash
#bmitune.sh for android/yttv
ADB_CMD="adb -s $2 shell"
CHANNEL=\""$1\""
APP_LAUNCH="com.google.android.youtube.tvunplugged"
APP_NAME="com.google.android.apps.youtube.tvunplugged.activity.MainActivity"
#Send the command
if $ADB_CMD pm list packages | grep -q "^package:${APP_LAUNCH}$"; then
$ADB_CMD am start -a android.intent.action.VIEW \
-d "https://tv.youtube.com/watch/${CHANNEL}" \
-n "${APP_LAUNCH}/${APP_NAME}"
else
$ADB_CMD am start -a android.intent.action.VIEW \
-d "https://tv.youtube.com/watch/${CHANNEL}"
fi
stopbmitune.sh
#!/bin/bash
#stopbmitune.sh for android/yttv
IPADD="$1"
ADB_CMD="adb -s $1 shell"
HOME="input keyevent KEYCODE_HOME"
adb -s $IPADD shell input keyevent 86
$ADB_CMD $HOME
But yeah, I have success
No. If you don't kill the app, you rarely see it anyways. I used YTTV with apituner and adbtuner and you only see it once in a blue moon but channel tuning is relatively quick after the app launches for the first time (just don't kill, just send the device to the home screen)
33 posts were split to a new topic: Confused and Lost. Need help understanding how various community projects work, and are used with Channels DVR
That's certainly an interesting idea. ah4c was originally built with no UI, and what's there is something of an afterthought. The other thing to consider is that the UI is entirely optional at this point -- and personally, I wouldn't want that to change.
BTW, I recently published some scripts under scripts/all/all that are designed to be proxies for anyone that wants to use multiple providers. I haven't documented it yet, but basically the format in the M3U for the tuning URL would be:
http://{{ .IPADDRESS }}/play/tuner/<device>~<provider>~KTVK~f8803ee2-28ec-4b62-9c22-8243f7d06ef9
Where <device> and <provider> would be the exact names used for the script directories. And, as you can see, for something like callsign~UUID we already use for DTV deeplinks -- that will continue to work fine.
So in short when someone uses scripts/all/all, their M3U will need to include device and provider, and the proxy scripts will hand off tuning to the appropriate scripts for that combo of device and provider.
This is totally untested, but should work in theory, and I'm mentioning it because it would/could affect any UI work -- especially the M3U Editor.
Screenshots look very good! It'll likely be Saturday before I can take a closer look at this...
It would really cool to test some of these new features like the closed caption functionality with apple tv based tuners on apple silicon based machines.
@anon38303228 is your build , especially the closed caption functionality , capable of running on Apple Silicon based machines - in docker. The closed capture feature addition for encoder based streams is awesome.
@bnhf any chance of doing the merge you mentioned several weeks ago of the ah4c:apple TV branch into the main ah4c branch like you were thinking earlier?
Would this mean the underlying VM OS that @anon38303228 is using in his beta is the same as the one used for the ah4c apple tv folks? Or would the extra new env variable you mentioned preclude using these new beta features?
Happy to lend a hand testing.
I still want to do that. I'll see if I can build a combined image tomorrow under the beta tag.
I'm going to go ahead and build a first test image for the :appletv crowd to try. It doesn't sound like Cohere Transcribe is going to be a model they'd choose anyway -- at least out of the gate. What I'm working towards here, is a combined ah4c image that works for both adb and atv -- with the new interface, and hopefully at least the CPU-based closed caption models.
@mnwxman132 I've built a first crack at a combined image under a beta2 tag. Here's the Docker Compose, and sample env vars I'd like you to use:
services:
# 2026.08.16
# 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:
- ${ADBS_PORT:-5037}:5037 # Port used by adb-server
- ${HOST_PORT:-7654}:7654 # Port used by this ah4c proxy
- ${WSCR_PORT:-8000}: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 - 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=""
- 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.
- 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.
- 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
Sample env vars:
TAG=latest
DOMAIN=localdomain tailxxxxx.ts.net
DOCKER_RUNTIME=runc
GPU_DEVICE=/dev/null
ADBS_PORT=5037
HOST_PORT=7654
SCRC_PORT=7655
IPADDRESS=htpc6:7654
NUMBER_TUNERS=5
TUNER1_IP=firestick-rack1:5555
ENCODER1_URL=http://encoder_48007/0.ts
TUNER2_IP=firestick-rack2:5555
ENCODER2_URL=http://encoder_48007/4.ts
TUNER3_IP=firestick-rack3:5555
ENCODER3_URL=http://encoder_48007/8.ts
TUNER4_IP=firestick-rack4:5555
ENCODER4_URL=http://encoder_48007/12.ts
TUNER5_IP=firestick-travel2:5555
ENCODER5_URL=http://encoder_23393/0.ts
STREAMER_APP=scripts/firetv/dtvdeeplinks
PYATV=true
CHANNELSIP=media-server6
ALERT_SMTP_SERVER=smtp.gmail.com:587
ALERT_AUTH_SERVER=smtp.gmail.com
[email protected]
ALERT_EMAIL_PASS=xxxxxxxxxxxxxxxx
[email protected]
UPDATE_SCRIPTS=true
UPDATE_M3US=true
TZ=America/Denver
SPEED_MODE=false
KEEP_WATCHING=4h
NULL_FRAME_INSERTION=false
PLAYBACK_DETECTION=false
PLAYBACK_DELAY=0
HEARTBEAT_INTERVAL=0
HOST_DIR=/data
NVIDIA_VISIBLE_DEVICES=
NVIDIA_DRIVER_CAPABILITIES=