├── .gitattributes ├── scripts ├── chromecast │ ├── kodi_faves │ │ ├── config-local.sh │ │ ├── bmitune.sh │ │ ├── prebmitune.sh │ │ └── stopbmitune.sh │ ├── pbs │ │ ├── bmitune.sh │ │ ├── prebmitune.sh │ │ ├── stopbmitune.sh │ │ └── README.txt │ └── npo │ │ ├── stopbmitune.sh │ │ └── prebmitune.sh ├── allente │ └── livetv │ │ ├── prebmitune.sh │ │ ├── stopbmitune.sh │ │ └── bmitune.sh ├── zinwell │ └── livetv │ │ ├── stopbmitune.sh │ │ ├── prebmitune.sh │ │ └── bmitune.sh ├── mecool │ └── youtubetv │ │ ├── stopbmitune.sh │ │ ├── prebmitune.sh │ │ └── bmitune.sh ├── onn │ └── youtubetv │ │ ├── stopbmitune.sh │ │ ├── prebmitune.sh │ │ └── bmitune.sh ├── linux │ └── kodi │ │ ├── bmitune.sh │ │ ├── prebmitune.sh │ │ ├── stopbmitune.sh │ │ └── README.txt ├── osprey │ ├── dtvosprey │ │ ├── stopbmitune.sh │ │ ├── bmitune.sh │ │ └── prebmitune.sh │ └── dtvospreydeeplinks │ │ ├── stopbmitune.sh │ │ ├── prebmitune.sh │ │ └── bmitune.sh ├── shieldtv │ └── youtubetv │ │ ├── bmitune.sh │ │ ├── stopbmitune.sh │ │ ├── prebmitune.sh │ │ └── yttv_contentid.txt └── firetv │ ├── channels │ ├── stopbmitune.sh │ ├── bmitune.sh │ └── prebmitune.sh │ ├── directv │ ├── stopbmitune.sh │ ├── prebmitune.sh │ └── bmitune.sh │ ├── dtvstream │ ├── stopbmitune.sh │ ├── prebmitune.sh │ └── bmitune.sh │ ├── livetv │ ├── stopbmitune.sh │ ├── prebmitune.sh │ └── createm3u.sh │ ├── espn │ ├── prebmitune.sh │ ├── stopbmitune.sh │ └── bmitune.sh │ ├── sling │ ├── stopbmitune.sh │ ├── prebmitune.sh │ └── bmitune.sh │ ├── dtvstreamdeeplinks │ ├── stopbmitune.sh │ └── prebmitune.sh │ ├── fubo │ ├── stopbmitune.sh │ ├── prebmitune.sh │ └── bmitune.sh │ ├── hulu │ ├── isconnected.sh │ ├── reboot.sh │ ├── stopbmitune.sh │ ├── prebmitune.sh │ ├── keep_alive.sh │ └── bmitune.sh │ ├── dtvdeeplinks │ ├── stopbmitune.sh │ └── prebmitune.sh │ └── xfinity │ ├── prebmitune.sh │ └── stopbmitune.sh ├── static ├── favicon.ico ├── logos │ ├── exit.png │ ├── reboot.png │ └── androidhdmi-for-channels.jpeg ├── status_ss.png ├── LICENSE └── status_logs.html ├── m3u ├── allente.m3u ├── pbs-seatac.m3u ├── kodifaves-pbs-seatac.m3u ├── pbs-worcester.m3u ├── zinwell.m3u ├── edc.m3u ├── npo.m3u ├── silicondust.m3u ├── foo-fighters.m3u ├── nbc.m3u ├── channels.m3u ├── youtubetv.m3u ├── fubo.m3u ├── coachella.m3u ├── directv.m3u ├── livetv.m3u ├── sling.m3u ├── dtvstream.m3u └── hulu.m3u ├── html ├── edit.html ├── stream.html ├── m3us.html ├── routes.html ├── status_and_logs.html ├── index.html ├── logs.html ├── config.html └── editm3u.html ├── adbpackages.sh ├── env.sample.network ├── env.sample.serial ├── ah4c.env ├── env.sample.decklink ├── env.sample.hauppauge ├── LICENSE ├── hulu_contentid.txt ├── Dockerfile-pyatv ├── env.sample.magewell ├── start.sh ├── go.mod ├── getting_started.txt ├── yttv_contentid.txt ├── Dockerfile ├── ah4c.yaml ├── docker-start-pyatv.sh └── docker-start.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.asset binary 3 | *.png binary 4 | -------------------------------------------------------------------------------- /scripts/chromecast/kodi_faves/config-local.sh: -------------------------------------------------------------------------------- 1 | CONFIG_KODI_JSONRPC_PASSWORD="kodi" 2 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sullrich/ah4c/HEAD/static/favicon.ico -------------------------------------------------------------------------------- /static/logos/exit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sullrich/ah4c/HEAD/static/logos/exit.png -------------------------------------------------------------------------------- /static/status_ss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sullrich/ah4c/HEAD/static/status_ss.png -------------------------------------------------------------------------------- /scripts/chromecast/pbs/bmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . `dirname $0`/common.sh 3 | bmitune "$@" 4 | -------------------------------------------------------------------------------- /static/logos/reboot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sullrich/ah4c/HEAD/static/logos/reboot.png -------------------------------------------------------------------------------- /scripts/chromecast/pbs/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . `dirname $0`/common.sh 3 | prebmitune "$@" 4 | -------------------------------------------------------------------------------- /scripts/chromecast/pbs/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . `dirname $0`/common.sh 3 | stopbmitune "$@" 4 | -------------------------------------------------------------------------------- /scripts/allente/livetv/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #prebmitune.sh for allente/livetv 3 | #2024.03.22 4 | echo "Script not required" -------------------------------------------------------------------------------- /static/logos/androidhdmi-for-channels.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sullrich/ah4c/HEAD/static/logos/androidhdmi-for-channels.jpeg -------------------------------------------------------------------------------- /scripts/allente/livetv/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #stopbmitune.sh for allente/livetv 3 | #2024.03.22 4 | echo "Script not required" 5 | -------------------------------------------------------------------------------- /scripts/zinwell/livetv/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # stopbmitune.sh for zinwell/livetv 3 | # 2025.01.26 4 | 5 | echo "Script not required" -------------------------------------------------------------------------------- /scripts/zinwell/livetv/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # prebmitune.sh for zinwell/livetv 3 | # 2025.01.26 4 | 5 | echo "Script not required" 6 | -------------------------------------------------------------------------------- /scripts/mecool/youtubetv/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #stopbmitune.sh for android/yttv 3 | IPADD="$1" 4 | 5 | adb -s $IPADD shell input keyevent 86 -------------------------------------------------------------------------------- /scripts/onn/youtubetv/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | STOP="am force-stop com.google.android.youtube.tvunplugged; sleep 2" 3 | 4 | #Stop Video 5 | adb -s $1 shell $STOP 6 | adb -s $1 shell input keyevent KEYCODE_SLEEP 7 | -------------------------------------------------------------------------------- /m3u/allente.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | 3 | #EXTINF:-1 channel-id="1" tvc-guide-stationid="",SVT1 4 | http://{{ .IPADDRESS }}/play/tuner/1 5 | 6 | #EXTINF:-1 channel-id="10" tvc-guide-stationid="",Tv10 7 | http://{{ .IPADDRESS }}/play/tuner/10 8 | -------------------------------------------------------------------------------- /scripts/mecool/youtubetv/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #prebmitune.sh for android/yttv 3 | STREAMERIP="$1" 4 | ADB_CMD="adb -s $1 shell" 5 | WAKE="input keyevent KEYCODE_WAKEUP" 6 | HOME="input keyevent KEYCODE_HOME" 7 | adb connect $STREAMERIP 8 | $ADB_CMD $HOME -------------------------------------------------------------------------------- /scripts/linux/kodi/bmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | FLAVOR=linux 3 | STUB=$(dirname $(dirname $(dirname $(realpath $0)))) 4 | if [[ -z "${STUB}" || "${STUB}" = "/" ]]; then exit 12; fi 5 | COMMON_DIR=${STUB}/chromecast/kodi_faves 6 | source ${COMMON_DIR}/common.sh 7 | DO_THIS=$(basename ${0%.*}) 8 | ${DO_THIS} "$@" 1>&2 9 | -------------------------------------------------------------------------------- /scripts/linux/kodi/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | FLAVOR=linux 3 | STUB=$(dirname $(dirname $(dirname $(realpath $0)))) 4 | if [[ -z "${STUB}" || "${STUB}" = "/" ]]; then exit 12; fi 5 | COMMON_DIR=${STUB}/chromecast/kodi_faves 6 | source ${COMMON_DIR}/common.sh 7 | DO_THIS=$(basename ${0%.*}) 8 | ${DO_THIS} "$@" 1>&2 9 | -------------------------------------------------------------------------------- /scripts/linux/kodi/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | FLAVOR=linux 3 | STUB=$(dirname $(dirname $(dirname $(realpath $0)))) 4 | if [[ -z "${STUB}" || "${STUB}" = "/" ]]; then exit 12; fi 5 | COMMON_DIR=${STUB}/chromecast/kodi_faves 6 | source ${COMMON_DIR}/common.sh 7 | DO_THIS=$(basename ${0%.*}) 8 | ${DO_THIS} "$@" 1>&2 9 | -------------------------------------------------------------------------------- /scripts/chromecast/kodi_faves/bmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | FLAVOR=android 3 | STUB=$(dirname $(dirname $(dirname $(realpath $0)))) 4 | if [[ -z "${STUB}" || "${STUB}" = "/" ]]; then exit 12; fi 5 | COMMON_DIR=${STUB}/chromecast/kodi_faves 6 | source ${COMMON_DIR}/common.sh 7 | DO_THIS=$(basename ${0%.*}) 8 | ${DO_THIS} "$@" 1>&2 9 | -------------------------------------------------------------------------------- /scripts/chromecast/kodi_faves/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | FLAVOR=android 3 | STUB=$(dirname $(dirname $(dirname $(realpath $0)))) 4 | if [[ -z "${STUB}" || "${STUB}" = "/" ]]; then exit 12; fi 5 | COMMON_DIR=${STUB}/chromecast/kodi_faves 6 | source ${COMMON_DIR}/common.sh 7 | DO_THIS=$(basename ${0%.*}) 8 | ${DO_THIS} "$@" 1>&2 9 | -------------------------------------------------------------------------------- /scripts/chromecast/kodi_faves/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | FLAVOR=android 3 | STUB=$(dirname $(dirname $(dirname $(realpath $0)))) 4 | if [[ -z "${STUB}" || "${STUB}" = "/" ]]; then exit 12; fi 5 | COMMON_DIR=${STUB}/chromecast/kodi_faves 6 | source ${COMMON_DIR}/common.sh 7 | DO_THIS=$(basename ${0%.*}) 8 | ${DO_THIS} "$@" 1>&2 9 | -------------------------------------------------------------------------------- /m3u/pbs-seatac.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | 3 | #EXTINF:-1 channel-id="KCTS" channel-number="9" tvc-guide-stationid="19631",KCTS 4 | http://{{ .IPADDRESS }}/play/tuner/KCTS-Cascade-PBS_98011_1 5 | 6 | #EXTINF:-1 channel-id="KBTC" channel-number="28" tvc-guide-stationid="34635",KBTC 7 | http://{{ .IPADDRESS }}/play/tuner/KBTC-Public-Television_98011_2 8 | -------------------------------------------------------------------------------- /m3u/kodifaves-pbs-seatac.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | 3 | #EXTINF:-1 channel-id="KCTS" channel-number="9" tvc-guide-stationid="19631",KCTS 4 | http://{{ .IPADDRESS }}/play/tuner/KCTS_favourites_Cascade%20PBS 5 | 6 | #EXTINF:-1 channel-id="KBTC" channel-number="28" tvc-guide-stationid="34635",KBTC 7 | http://{{ .IPADDRESS }}/play/tuner/KBTC_favorites_KBTC%20Public%20Television 8 | -------------------------------------------------------------------------------- /scripts/mecool/youtubetv/bmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #bmitune.sh for android/yttv 3 | ADB_CMD="adb -s $2 shell" 4 | CHANNEL=\""$1\"" 5 | APP_LAUNCH="com.google.android.youtube.tvunplugged" 6 | APP_NAME="com.google.android.apps.youtube.tvunplugged.activity.MainActivity" 7 | 8 | #Send the command 9 | $ADB_CMD am start -a android.intent.action.VIEW -d https://tv.youtube.com/watch/$CHANNEL -n $APP_LAUNCH/$APP_NAME -------------------------------------------------------------------------------- /html/edit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Text Editor 5 | 11 | 12 | 13 |
14 |
15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /adbpackages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #set -x 4 | 5 | devices=$(adb devices | grep -w "device" | awk '{print $1}') 6 | 7 | if [ -z "$devices" ]; then 8 | echo "No devices connected." 9 | exit 0 10 | fi 11 | 12 | for device in $devices; do 13 | echo "Device: $device" 14 | echo "Third-party packages installed:" 15 | 16 | adb -s "$device" shell pm list packages -3 | sed 's/package://g' 17 | 18 | echo -e "\n------------------------------\n" 19 | done 20 | -------------------------------------------------------------------------------- /m3u/pbs-worcester.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | 3 | #EXTINF:-1 channel-id="WGBH" channel-number="2" tvc-guide-stationid="28055",WGBH 4 | http://{{ .IPADDRESS }}/play/tuner/WGBH-GBH_01601_1 5 | 6 | #EXTINF:-1 channel-id="WSBE" channel-number="36" tvc-guide-stationid="34635",WSBE 7 | http://{{ .IPADDRESS }}/play/tuner/WSBE-Rhode-Island-PBS_01601_2 8 | 9 | #EXTINF:-1 channel-id="WFCR" channel-number="57" tvc-guide-stationid="",WFCR 10 | http://{{ .IPADDRESS }}/play/tuner/WFCR-New-England-Public-Media_01601_3 11 | -------------------------------------------------------------------------------- /scripts/onn/youtubetv/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #CONNECT="connect 192.168.1.171" 3 | WAKE="input keyevent KEYCODE_WAKEUP" 4 | HOME="input keyevent KEYCODE_HOME" 5 | 6 | adb connect $1 7 | adb connect $1 8 | adb connect $1 9 | adb -s $1 shell $WAKE 10 | adb -s $1 shell $WAKE 11 | #adb -s $1 shell $WAKE 12 | adb -s $1 shell $HOME; sleep 2 13 | #adb -s $1 shell am start com.google.android.youtube.tvunplugged; sleep 2 14 | #adb -s $1 shell am force-stop com.google.android.youtube.tvunplugged 15 | -------------------------------------------------------------------------------- /m3u/zinwell.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | 3 | #EXTINF:-1 channel-id="102.1" channel-number="" tvc-guide-stationid="" tvg-group="" tvg-logo="",CBS 2 NEXTGENTV 4 | http://{{ .IPADDRESS }}/play/tuner/02-1 5 | 6 | #EXTINF:-1 channel-id="104.1" channel-number="" tvc-guide-stationid="" tvg-group="" tvg-logo="",NBC 4 NEXTGENTV 7 | http://{{ .IPADDRESS }}/play/tuner/04-1 8 | 9 | #EXTINF:-1 channel-id="147.1" channel-number="" tvc-guide-stationid="" tvg-group="" tvg-logo="",WNJU 47 NEXTGENTV 10 | http://{{ .IPADDRESS }}/play/tuner/47-1 11 | -------------------------------------------------------------------------------- /scripts/osprey/dtvosprey/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #stopbmitune.sh for osprey/dtvosprey 3 | # 2025.09.10 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | streamerIP="$1" 9 | streamerNoPort="${streamerIP%%:*}" 10 | adbTarget="adb -s $streamerIP" 11 | 12 | #Device sleep 13 | adbSleep() { 14 | sleep="input keyevent KEYCODE_SLEEP" 15 | 16 | $adbTarget shell $sleep 17 | echo "Sleep initiated for $streamerIP" 18 | date +%s > $streamerNoPort/stream_stopped 19 | echo "$streamerNoPort/stream_stopped written with epoch stop time" 20 | } 21 | 22 | main() { 23 | adbSleep 24 | } 25 | 26 | main 27 | -------------------------------------------------------------------------------- /scripts/osprey/dtvospreydeeplinks/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # stopbmitune.sh for osprey/dtvospreydeeplinks 3 | # 2025.09.26 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | streamerIP="$1" 9 | streamerNoPort="${streamerIP%%:*}" 10 | adbTarget="adb -s $streamerIP" 11 | 12 | #Device sleep 13 | adbSleep() { 14 | sleep="input keyevent KEYCODE_SLEEP" 15 | $adbTarget shell $sleep 16 | echo "Sleep initiated for $streamerIP" 17 | date +%s > $streamerNoPort/stream_stopped 18 | echo "$streamerNoPort/stream_stopped written with epoch stop time" 19 | } 20 | 21 | main() { 22 | adbSleep 23 | } 24 | 25 | main 26 | -------------------------------------------------------------------------------- /m3u/edc.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | 3 | #EXTINF:-1 channel-id="EDC-kineticFIELD" tvc-guide-stationid="5.1" tvg-group="EDC-kineticFIELD" tvc-guide-placeholders="1800" tvg-logo="https://www.billboard.com/wp-content/uploads/2022/05/kineticFIELD-cr-Jose-Murga-for-Insomniac-Events-2022-billboard-1548.jpg",EDC kineticFIELD 4 | http://{{ .IPADDRESS }}:7654/play/tuner/youtube__IuCF8mr9mYo 5 | 6 | #EXTINF:-1 channel-id="EDC-bassPOD" tvc-guide-stationid="5.2" tvg-group="EDC-bassPOD" tvc-guide-placeholders="1800" tvg-logo="https://gistspecialties.com/wp-content/uploads/2015/08/edc-final-1.jpg",EDC bassPOD 7 | http://{{ .IPADDRESS }}:7654/play/tuner/youtube__N8aRtjPIoj4 8 | -------------------------------------------------------------------------------- /m3u/npo.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | #EXTINF:-1 channel-id="NPO1" tvc-guide-stationid="61014",NPO1 3 | http://{{ .IPADDRESS }}/play/tuner/NPO1 4 | #EXTINF:-1 channel-id="NPO2" tvc-guide-stationid="63528",NPO2 5 | http://{{ .IPADDRESS }}/play/tuner/NPO2 6 | #EXTINF:-1 channel-id="NPO3" tvc-guide-stationid="63529",NPO3 7 | http://{{ .IPADDRESS }}/play/tuner/NPO3 8 | #EXTINF:-1 channel-id="NPO1ex" tvc-guide-stationid="115467",NPO1 Extra 9 | http://{{ .IPADDRESS }}/play/tuner/NPO1ex 10 | #EXTINF:-1 channel-id="NPO2ex" tvc-guide-stationid="115564",NPO2 Extra 11 | http://{{ .IPADDRESS }}/play/tuner/NPO2ex 12 | #EXTINF:-1 channel-id="NPO3ex" tvc-guide-stationid="115469",NPO3 Extra 13 | http://{{ .IPADDRESS }}/play/tuner/NPO3ex 14 | -------------------------------------------------------------------------------- /scripts/allente/livetv/bmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #bmitune.sh for allente/livetv 3 | #2024.03.22 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | #Global 9 | channelID="$1" 10 | streamerIP="$2" 11 | adbTarget="adb -s $streamerIP" 12 | 13 | #Trap end of script run 14 | finish() { 15 | echo "bmitune.sh is exiting for $streamerIP with exit code $?" 16 | } 17 | 18 | trap finish EXIT 19 | 20 | #Tuning is based on channel number values from allente.m3u 21 | tuneChannel() { 22 | for (( digit=0; digit<${#channelID}; digit++ )); do 23 | keypress=${channelID:$digit:1} 24 | $adbTarget shell input keyevent KEYCODE_$keypress 25 | done 26 | } 27 | 28 | main() { 29 | tuneChannel 30 | } 31 | 32 | main 33 | -------------------------------------------------------------------------------- /scripts/zinwell/livetv/bmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # bmitune.sh for zinwell/livetv 3 | # 2025.09.23 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | #Global 9 | channelID="$1" 10 | streamerIP="$2" 11 | adbTarget="adb -s $streamerIP" 12 | 13 | #Trap end of script run 14 | finish() { 15 | echo "bmitune.sh is exiting for $streamerIP with exit code $?" 16 | } 17 | 18 | trap finish EXIT 19 | 20 | #Tuning is based on channel number values from zinwell.m3u 21 | tuneChannel() { 22 | for (( i=0; i<${#channelID}; i++ )); do 23 | keypress="${channelID:$i:1}" 24 | $adbTarget shell input keyevent KEYCODE_$keypress 25 | sleep 2 26 | done 27 | $adbTarget shell input keyevent KEYCODE_DPAD_CENTER 28 | } 29 | 30 | main() { 31 | tuneChannel 32 | } 33 | 34 | main 35 | -------------------------------------------------------------------------------- /env.sample.network: -------------------------------------------------------------------------------- 1 | CHANNELSIP="10.0.250.69" 2 | IPADDRESS="10.0.250.69" 3 | 4 | STREAMER_APP="scripts/firetv/hulu" 5 | 6 | NUMBER_TUNERS="2" 7 | 8 | ENCODER1_URL="http://10.0.250.110/ts/1_0" 9 | TUNER1_IP="10.0.250.154" 10 | CMD1="" 11 | CMD1_DEVICE="" 12 | 13 | ENCODER2_URL="http://10.0.250.145/ts/1_0" 14 | TUNER2_IP="10.0.250.158" 15 | CMD2="" 16 | CMD2_DEVICE="" 17 | 18 | ENCODER3_URL="" 19 | TUNER3_IP="" 20 | CMD3="" 21 | CMD3_DEVICE="" 22 | 23 | ENCODER4_URL="" 24 | TUNER4_IP="" 25 | CMD4="" 26 | CMD4_DEVICE="" 27 | 28 | ENCODER5_URL="" 29 | TUNER5_IP="" 30 | CMD5="" 31 | CMD5_DEVICE="" 32 | 33 | ALERT_SMTP_SERVER="smtp.gmail.com:587" 34 | ALERT_AUTH_SERVER="smtp.gmail.com" 35 | ALERT_EMAIL_FROM="" 36 | ALERT_EMAIL_PASS="" 37 | ALERT_EMAIL_TO="" 38 | 39 | ALERT_WEBHOOK_URL="" 40 | 41 | ALLOW_DEBUG_VIDEO_PREVIEW="FALSE" 42 | -------------------------------------------------------------------------------- /env.sample.serial: -------------------------------------------------------------------------------- 1 | CHANNELSIP="10.0.250.69" 2 | IPADDRESS="10.0.250.69" 3 | 4 | STREAMER_APP="scripts/firetv/hulu" 5 | 6 | NUMBER_TUNERS="2" 7 | 8 | ENCODER1_URL="http://10.0.250.110/ts/1_0" 9 | TUNER1_IP="G0723H0825150213" 10 | CMD1="" 11 | CMD1_DEVICE="" 12 | 13 | ENCODER2_URL="http://10.0.250.145/ts/1_0" 14 | TUNER2_IP="1G0723H08247205XX" 15 | CMD2="" 16 | CMD2_DEVICE="" 17 | 18 | ENCODER3_URL="" 19 | TUNER3_IP="" 20 | CMD3="" 21 | CMD3_DEVICE="" 22 | 23 | ENCODER4_URL="" 24 | TUNER4_IP="" 25 | CMD4="" 26 | CMD4_DEVICE="" 27 | 28 | ENCODER5_URL="" 29 | TUNER5_IP="" 30 | CMD5="" 31 | CMD5_DEVICE="" 32 | 33 | ALERT_SMTP_SERVER="smtp.gmail.com:587" 34 | ALERT_AUTH_SERVER="smtp.gmail.com" 35 | ALERT_EMAIL_FROM="" 36 | ALERT_EMAIL_PASS="" 37 | ALERT_EMAIL_TO="" 38 | 39 | ALERT_WEBHOOK_URL="" 40 | 41 | ALLOW_DEBUG_VIDEO_PREVIEW="FALSE" 42 | -------------------------------------------------------------------------------- /m3u/silicondust.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | 3 | #EXTINF:-1 channel-id="nbc" tvc-guide-stationid="",NBC 4 | http://{{ .IPADDRESS }}:7654/play/tuner/silicondust__3.1! # ! scrolls up - bug in GUI. 5 | 6 | #EXTINF:-1 channel-id="abc" tvc-guide-stationid="",ABC 7 | http://{{ .IPADDRESS }}:7654/play/tuner/silicondust__11.1 8 | 9 | #EXTINF:-1 channel-id="ket" tvc-guide-stationid="",KET 10 | http://{{ .IPADDRESS }}:7654/play/tuner/silicondust__15.1 11 | 12 | #EXTINF:-1 channel-id="cbs" tvc-guide-stationid="",CBS 13 | http://{{ .IPADDRESS }}:7654/play/tuner/silicondust__32.1 14 | 15 | #EXTINF:-1 channel-id="fox" tvc-guide-stationid="",Fox 16 | http://{{ .IPADDRESS }}:7654/play/tuner/silicondust__41.1 17 | 18 | #EXTINF:-1 channel-id="cw" tvc-guide-stationid="",CW 19 | http://{{ .IPADDRESS }}:7654/play/tuner/silicondust__58.1 20 | 21 | #EXTINF:-1 channel-id="ket2" tvc-guide-stationid="",KET2 22 | http://{{ .IPADDRESS }}:7654/play/tuner/silicondust__68.1 23 | -------------------------------------------------------------------------------- /scripts/shieldtv/youtubetv/bmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "$1" > /tmp/temp.txt 4 | echo "$2" >> /tmp/temp.txt 5 | echo "$3" >> /tmp/temp.txt 6 | 7 | STATION="$1" 8 | TUNERIP="$2" 9 | CONTENT_FILE="contentid.txt" 10 | CONTENT_ID="" 11 | URL="" 12 | PROVIDER="" 13 | STATUS="notplaying" 14 | ADBSTATUS="" 15 | PID="" 16 | RESULT="" 17 | EXE="" 18 | ISPKG="" 19 | WHICH_PROVIDER="" 20 | 21 | 22 | content_file="yttv_contentid.txt" 23 | content_id="" 24 | 25 | # Read content_id from file 26 | if [ -f "$content_file" ]; then 27 | content_id=$(grep -w "^$1" "$content_file" | cut -d " " -f3) 28 | fi 29 | 30 | # Check if content_id is empty 31 | if [ -z "$content_id" ]; then 32 | echo "Invalid option or content_id not found in $content_file" 33 | exit 1 34 | fi 35 | 36 | adb -s $TUNERIP shell "am start -a android.intent.action.VIEW -d https://tv.youtube.com/watch/"$content_id"" 37 | 38 | adb -s $TUNERIP shell "input keyevent 66" 39 | -------------------------------------------------------------------------------- /ah4c.env: -------------------------------------------------------------------------------- 1 | TAG=latest 2 | DOMAIN=localdomain tailxxxxx.ts.net 3 | ADBS_PORT=5037 4 | HOST_PORT=7654 5 | SCRC_PORT=7655 6 | IPADDRESS=htpc6:7654 7 | NUMBER_TUNERS=5 8 | TUNER1_IP=firestick-rack1:5555 9 | ENCODER1_URL=http://encoder_48007/0.ts 10 | TUNER2_IP=firestick-rack2:5555 11 | ENCODER2_URL=http://encoder_48007/4.ts 12 | TUNER3_IP=firestick-rack3:5555 13 | ENCODER3_URL=http://encoder_48007/8.ts 14 | TUNER4_IP=firestick-rack4:5555 15 | ENCODER4_URL=http://encoder_48007/12.ts 16 | TUNER5_IP=firestick-travel2:5555 17 | ENCODER5_URL=http://encoder_23393/0.ts 18 | STREAMER_APP=scripts/firetv/dtvdeeplinks 19 | CHANNELSIP=media-server6 20 | ALERT_SMTP_SERVER=smtp.gmail.com:587 21 | ALERT_AUTH_SERVER=smtp.gmail.com 22 | ALERT_EMAIL_FROM=xxxxxxxxxx@gmail.com 23 | ALERT_EMAIL_PASS=xxxxxxxxxxxxxxxx 24 | ALERT_EMAIL_TO=xxxxxxxxxx@gmail.com 25 | UPDATE_SCRIPTS=true 26 | UPDATE_M3US=true 27 | TZ=US/Mountain 28 | SPEED_MODE=false 29 | KEEP_WATCHING=4h 30 | HOST_DIR=/data -------------------------------------------------------------------------------- /env.sample.decklink: -------------------------------------------------------------------------------- 1 | CHANNELSIP="10.0.250.69" 2 | IPADDRESS="10.0.250.69" 3 | 4 | STREAMER_APP="scripts/firetv/hulu" 5 | 6 | NUMBER_TUNERS="1" 7 | 8 | ENCODER1_URL="" 9 | TUNER1_IP="10.0.250.153" 10 | CMD1_DEVICE="Intensity Extreme" 11 | CMD1="ffmpeg -hide_banner -loglevel error -format_code hp50 -f decklink -i 'Intensity Extreme' -vf 'yadif=1,hqdn3d=4:3:6,scale=1920:1080' -c:a aac -g 90 -b:a 1.5M -b:v 50M -f mpegts -maxrate 50M -bufsize 200M -" 12 | 13 | ENCODER2_URL="" 14 | TUNER2_IP="" 15 | CMD2="" 16 | CMD2_DEVICE="" 17 | 18 | ENCODER3_URL="" 19 | TUNER3_IP="" 20 | CMD3="" 21 | CMD3_DEVICE="" 22 | 23 | ENCODER4_URL="" 24 | TUNER4_IP="" 25 | CMD4="" 26 | CMD4_DEVICE="" 27 | 28 | ENCODER5_URL="" 29 | TUNER5_IP="" 30 | CMD5="" 31 | CMD5_DEVICE="" 32 | 33 | ALERT_SMTP_SERVER="" 34 | ALERT_AUTH_SERVER="" 35 | ALERT_EMAIL_FROM="" 36 | ALERT_EMAIL_PASS="" 37 | ALERT_EMAIL_TO="" 38 | 39 | ALERT_WEBHOOK_URL="" 40 | 41 | ALLOW_DEBUG_VIDEO_PREVIEW="FALSE" 42 | -------------------------------------------------------------------------------- /scripts/firetv/channels/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # stopbmitune.sh for firetv/channels 3 | # 2025.05.03 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | streamerIP="$1" 9 | streamerNoPort="${streamerIP%%:*}" 10 | adbTarget="adb -s $streamerIP" 11 | packageName=com.getchannels.dvr.app 12 | [[ $SPEED_MODE == "" ]] && speedMode="true" || speedMode="$SPEED_MODE" 13 | 14 | #Stop stream 15 | adbStop() { 16 | [[ $speedMode == "true" ]] \ 17 | && stop="input keyevent KEYCODE_BACK" \ 18 | || stop="am force-stop $packageName" 19 | 20 | $adbTarget shell $stop; sleep 2 21 | echo "Streaming stopped for $streamerIP" 22 | } 23 | 24 | #Device sleep 25 | adbSleep() { 26 | sleep="input keyevent KEYCODE_SLEEP" 27 | 28 | $adbTarget shell $sleep 29 | echo "Sleep initiated for $streamerIP" 30 | date +%s > $streamerNoPort/stream_stopped 31 | echo "$streamerNoPort/stream_stopped written with epoch stop time" 32 | } 33 | 34 | main() { 35 | adbStop 36 | adbSleep 37 | } 38 | 39 | main 40 | -------------------------------------------------------------------------------- /env.sample.hauppauge: -------------------------------------------------------------------------------- 1 | CHANNELSIP="10.0.250.69" 2 | IPADDRESS="10.0.250.159" 3 | 4 | STREAMER_APP="scripts/firetv/hulu" 5 | 6 | NUMBER_TUNERS="1" 7 | 8 | ENCODER1_URL="" 9 | TUNER1_IP="10.0.250.153:5555" 10 | CMD1="/opt/Hauppauge/bin/hauppauge2 --serial E585-00-00D8BC0D -i 3 -a 3 -d 2 -S 3 -R 29 -C 0 -B 5 -A 0 -p 4 -r 14800000 -X 2 -F 0" 11 | CMD1_DEVICE="" 12 | 13 | ENCODER2_URL="" 14 | TUNER2_IP="10.0.250.136:5555" 15 | CMD2="/opt/Hauppauge/bin/hauppauge2 --serial E585-00-00D8CB5F -i 3 -a 3 -d 2 -S 3 -R 29 -C 0 -B 5 -A 0 -p 4 -r 14800000 -X 2 -F 0" 16 | CMD2_DEVICE="" 17 | 18 | ENCODER3_URL="" 19 | TUNER3_IP="" 20 | CMD3="" 21 | CMD3_DEVICE="" 22 | 23 | ENCODER4_URL="" 24 | TUNER4_IP="" 25 | CMD4="" 26 | CMD4_DEVICE="" 27 | 28 | ENCODER5_URL="" 29 | TUNER5_IP="" 30 | CMD5="" 31 | CMD5_DEVICE="" 32 | 33 | ALERT_SMTP_SERVER="" 34 | ALERT_AUTH_SERVER="" 35 | ALERT_EMAIL_FROM="" 36 | ALERT_EMAIL_PASS="" 37 | ALERT_EMAIL_TO="" 38 | 39 | ALERT_WEBHOOK_URL="" 40 | 41 | ALLOW_DEBUG_VIDEO_PREVIEW="FALSE" 42 | -------------------------------------------------------------------------------- /scripts/linux/kodi/README.txt: -------------------------------------------------------------------------------- 1 | A tuner for kodi favourites list 2 | 3 | This set of scripts shares commonality with the scripts under 4 | scripts/chromecast/kodi_faves/. See the README.txt under there for 5 | important details, configuration options, etc. 6 | 7 | This should work on any sort of Linux box that can run kodi. I tested 8 | it with a few distributions before I settled on one called 9 | plain old raspbian. I tried it on a few differnt Raspberry Pi boards that I had 10 | on hand. 11 | 12 | On a couple of RPi 1 boards, the lag and stutter was pretty bad. On an 13 | RPi 2 board with 1 GB of RAM, playing was mostly smooth but with 14 | occasional lag and stutter. On an RPi 3B+ with 1 GB of RAM, playback 15 | was completely smooth for 1080p/60fps. An 8gb microSD card is way more 16 | than enough to hold the image and kodi addons. Of course, you have to 17 | decide if running from a microSD card is too risky. If you run from a 18 | USB thumb drive, navigation may be a bit sluggish but playback should 19 | be unaffected. 20 | -------------------------------------------------------------------------------- /scripts/firetv/directv/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #stopbmitune.sh for firetv/directv 3 | 4 | #Debug on if uncommented 5 | set -x 6 | 7 | streamerIP="$1" 8 | streamerNoPort="${streamerIP%%:*}" 9 | adbTarget="adb -s $streamerIP" 10 | 11 | #Check if bmitune.sh is done running 12 | bmituneDone() { 13 | bmitunePID=$(<"$streamerNoPort/bmitune_pid") 14 | 15 | while ps -p $bmitunePID > /dev/null; do 16 | echo "Waiting for bmitune.sh to complete..." 17 | sleep 2 18 | done 19 | } 20 | 21 | #Stop stream 22 | adbStop() { 23 | stop="input keyevent KEYCODE_HOME" 24 | 25 | $adbTarget shell $stop; sleep 2 26 | echo "Streaming stopped for $streamerIP" 27 | } 28 | 29 | #Device sleep 30 | adbSleep() { 31 | sleep="input keyevent KEYCODE_SLEEP" 32 | 33 | $adbTarget shell $sleep 34 | echo "Sleep initiated for $streamerIP" 35 | date +%s > $streamerNoPort/stream_stopped 36 | echo "$streamerNoPort/stream_stopped written with epoch stop time" 37 | } 38 | 39 | main() { 40 | bmituneDone 41 | adbStop 42 | adbSleep 43 | } 44 | 45 | main 46 | -------------------------------------------------------------------------------- /scripts/firetv/dtvstream/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #stopbmitune.sh for firetv/directv 3 | 4 | #Debug on if uncommented 5 | set -x 6 | 7 | streamerIP="$1" 8 | streamerNoPort="${streamerIP%%:*}" 9 | adbTarget="adb -s $streamerIP" 10 | 11 | #Check if bmitune.sh is done running 12 | bmituneDone() { 13 | bmitunePID=$(<"$streamerNoPort/bmitune_pid") 14 | 15 | while ps -p $bmitunePID > /dev/null; do 16 | echo "Waiting for bmitune.sh to complete..." 17 | sleep 2 18 | done 19 | } 20 | 21 | #Stop stream 22 | adbStop() { 23 | stop="input keyevent KEYCODE_HOME" 24 | 25 | $adbTarget shell $stop; sleep 2 26 | echo "Streaming stopped for $streamerIP" 27 | } 28 | 29 | #Device sleep 30 | adbSleep() { 31 | sleep="input keyevent KEYCODE_SLEEP" 32 | 33 | $adbTarget shell $sleep 34 | echo "Sleep initiated for $streamerIP" 35 | date +%s > $streamerNoPort/stream_stopped 36 | echo "$streamerNoPort/stream_stopped written with epoch stop time" 37 | } 38 | 39 | main() { 40 | bmituneDone 41 | adbStop 42 | adbSleep 43 | } 44 | 45 | main 46 | -------------------------------------------------------------------------------- /scripts/firetv/livetv/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #stopbmitune.sh for firetv/livetv 3 | 4 | #Debug on if uncommented 5 | #set -x 6 | 7 | streamerIP="$1" 8 | streamerNoPort="${streamerIP%%:*}" 9 | adbTarget="adb -s $streamerIP" 10 | 11 | #Check if bmitune.sh is done running 12 | bmituneDone() { 13 | bmitunePID=$(<"$streamerNoPort/bmitune_pid") 14 | 15 | while ps -p $bmitunePID > /dev/null; do 16 | echo "Waiting for bmitune.sh to complete..." 17 | sleep 2 18 | done 19 | } 20 | 21 | #Stop stream 22 | adbStop() { 23 | stop="input keyevent KEYCODE_HOME" 24 | 25 | $adbTarget shell $stop; sleep 2 26 | echo "Streaming stopped for $streamerIP" 27 | } 28 | 29 | #Device sleep 30 | adbSleep() { 31 | sleep="input keyevent KEYCODE_SLEEP" 32 | 33 | $adbTarget shell $sleep 34 | echo "Sleep initiated for $streamerIP" 35 | date +%s > $streamerNoPort/stream_stopped 36 | echo "$streamerNoPort/stream_stopped written with epoch stop time" 37 | } 38 | 39 | main() { 40 | bmituneDone 41 | adbStop 42 | adbSleep 43 | } 44 | 45 | main 46 | -------------------------------------------------------------------------------- /scripts/onn/youtubetv/bmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | HOME="input keyevent KEYCODE_HOME; sleep 1" 3 | PRIME1="input keyevent 19 19 19 19; sleep 1; input keyevent 21 21 21; sleep 1; input keyevent 22; sleep 1; input keyevent 23; sleep 1" 4 | PRIME2="input keyevent 19; sleep 1" 5 | PRIME3="input keyevent --longpress 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67" 6 | SEARCH1="input keyevent 66; sleep 2" 7 | SEARCH2="input keyevent 66" 8 | 9 | #USA 10 | if [ $1 = "111" ];then 11 | adb -s $2 shell $HOME 12 | adb -s $2 shell $PRIME1 13 | adb -s $2 shell $PRIME2 14 | #adb -s $2 shell $PRIME3 15 | adb -s $2 shell input text "stream\ usa\ channel\ on\ YouTube\ TV" 16 | adb -s $2 shell $SEARCH1 17 | adb -s $2 shell $SEARCH2 18 | fi 19 | 20 | #SYFY 21 | if [ $1 = "135" ];then 22 | adb -s $2 shell $HOME 23 | adb -s $2 shell $PRIME1 24 | adb -s $2 shell $PRIME2 25 | #adb -s $2 shell $PRIME3 26 | adb -s $2 shell input text "stream\ syfy\ on\ YouTube\ TV" 27 | adb -s $2 shell $SEARCH1 28 | adb -s $2 shell $SEARCH2 29 | fi 30 | -------------------------------------------------------------------------------- /static/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2023 Fancy Bits, LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | -------------------------------------------------------------------------------- /scripts/firetv/espn/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #prebmitune.sh for firetv/espn 3 | #2025.11.03 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | streamerIP="$1" 9 | streamerNoPort="${streamerIP%%:*}" 10 | adbTarget="adb -s $streamerIP" 11 | 12 | mkdir -p $streamerNoPort 13 | 14 | #Trap end of script run 15 | finish() { 16 | echo "prebmitune.sh is exiting for $streamerIP with exit code $?" 17 | } 18 | 19 | trap finish EXIT 20 | 21 | adbConnect() { 22 | adb connect $streamerIP 23 | 24 | local -i adbMaxRetries=2 25 | local -i adbCounter=0 26 | 27 | while true; do 28 | $adbTarget shell input keyevent KEYCODE_WAKEUP 29 | local adbEventSuccess=$? 30 | 31 | if [[ $adbEventSuccess -eq 0 ]]; then 32 | break 33 | fi 34 | 35 | if (($adbCounter > $adbMaxRetries)); then 36 | touch $streamerNoPort/adbCommunicationFail 37 | echo "Communication with $streamerIP failed after $adbMaxRetries retries" 38 | exit 1 39 | fi 40 | 41 | sleep 1 42 | ((adbCounter++)) 43 | done 44 | } 45 | 46 | main() { 47 | adbConnect 48 | } 49 | 50 | main -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Unless noted at the top of each file, Copyright 2023 Fancy Bits, LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | -------------------------------------------------------------------------------- /scripts/chromecast/npo/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #stopbmitune.sh for chromecast/npo 3 | 4 | #Debug on if uncommented 5 | set -x 6 | 7 | streamerIP="$1" 8 | streamerNoPort="${streamerIP%%:*}" 9 | adbTarget="adb -s $streamerIP" 10 | 11 | #Check if bmitune.sh is done running 12 | bmituneDone() { 13 | bmitunePID=$(<"$streamerNoPort/bmitune_pid") 14 | 15 | while ps -p $bmitunePID > /dev/null; do 16 | echo "Waiting for bmitune.sh to complete..." 17 | sleep 2 18 | done 19 | } 20 | 21 | #Stop stream 22 | adbStop() { 23 | stop="input keyevent KEYCODE_HOME" 24 | 25 | $adbTarget shell $stop; sleep 2 26 | $adbTarget shell am force-stop nl.uitzendinggemist 27 | echo "Streaming stopped for $streamerIP" 28 | } 29 | 30 | #Device sleep 31 | adbSleep() { 32 | sleep="input keyevent KEYCODE_SLEEP" 33 | 34 | $adbTarget shell $sleep 35 | echo "Sleep initiated for $streamerIP" 36 | date +%s > $streamerNoPort/stream_stopped 37 | echo "$streamerNoPort/stream_stopped written with epoch stop time" 38 | } 39 | 40 | main() { 41 | bmituneDone 42 | adbStop 43 | adbSleep 44 | } 45 | 46 | main 47 | -------------------------------------------------------------------------------- /scripts/firetv/sling/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #stopbmitune.sh for firetv/sling 3 | 4 | #Debug on if uncommented 5 | set -x 6 | 7 | streamerIP="$1" 8 | streamerNoPort="${streamerIP%%:*}" 9 | adbTarget="adb -s $streamerIP" 10 | packageName="com.sling" 11 | 12 | #Check if bmitune.sh is done running 13 | bmituneDone() { 14 | bmitunePID=$(<"$streamerNoPort/bmitune_pid") 15 | 16 | while ps -p $bmitunePID > /dev/null; do 17 | echo "Waiting for bmitune.sh to complete..." 18 | sleep 2 19 | done 20 | } 21 | 22 | #Stop stream 23 | adbStop() { 24 | stop="input keyevent KEYCODE_BACK; \ 25 | input keyevent KEYCODE_BACK; \ 26 | input keyevent KEYCODE_HOME" 27 | #stop="am force-stop $packageName" 28 | $adbTarget shell $stop; sleep 2 29 | echo "Streaming stopped for $streamerIP" 30 | } 31 | 32 | #Device sleep 33 | adbSleep() { 34 | sleep="input keyevent KEYCODE_SLEEP" 35 | 36 | $adbTarget shell $sleep 37 | echo "Sleep initiated for $streamerIP" 38 | date +%s > $streamerNoPort/stream_stopped 39 | echo "$streamerNoPort/stream_stopped written with epoch stop time" 40 | } 41 | 42 | main() { 43 | bmituneDone 44 | adbStop 45 | adbSleep 46 | } 47 | 48 | main 49 | -------------------------------------------------------------------------------- /scripts/osprey/dtvosprey/bmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #bmitune.sh for osprey/dtvosprey 3 | # 2025.09.10 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | #Global 9 | channelID=\""$1\"" 10 | specialID="$1" 11 | streamerIP="$2" 12 | streamerNoPort="${streamerIP%%:*}" 13 | adbTarget="adb -s $streamerIP" 14 | m3uName="${STREAMER_APP#*/*/}.m3u" 15 | 16 | 17 | #Trap end of script run 18 | finish() { 19 | echo "bmitune.sh is exiting for $streamerIP with exit code $?" 20 | } 21 | 22 | trap finish EXIT 23 | 24 | #Set encoderURL based on the value of streamerIP 25 | matchEncoderURL() { 26 | 27 | case "$streamerIP" in 28 | "$TUNER1_IP") 29 | encoderURL=$ENCODER1_URL 30 | ;; 31 | "$TUNER2_IP") 32 | encoderURL=$ENCODER2_URL 33 | ;; 34 | "$TUNER3_IP") 35 | encoderURL=$ENCODER3_URL 36 | ;; 37 | "$TUNER4_IP") 38 | encoderURL=$ENCODER4_URL 39 | ;; 40 | *) 41 | exit 1 42 | ;; 43 | esac 44 | } 45 | 46 | #Tuning is based on channel name values from $m3uName. 47 | tuneChannel() { 48 | 49 | $adbTarget shell input text $channelID; 50 | } 51 | 52 | 53 | main() { 54 | matchEncoderURL 55 | tuneChannel 56 | } 57 | 58 | main 59 | -------------------------------------------------------------------------------- /scripts/firetv/dtvstreamdeeplinks/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # stopbmitune.sh for firetv/dtvstreamdeeplinks 3 | # 2024.09.18 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | streamerIP="$1" 9 | streamerNoPort="${streamerIP%%:*}" 10 | adbTarget="adb -s $streamerIP" 11 | packageName=com.att.tv 12 | 13 | #Check if bmitune.sh is done running 14 | bmituneDone() { 15 | bmitunePID=$(<"$streamerNoPort/bmitune_pid") 16 | 17 | while ps -p $bmitunePID > /dev/null; do 18 | echo "Waiting for bmitune.sh to complete..." 19 | sleep 2 20 | done 21 | } 22 | 23 | #Stop stream 24 | adbStop() { 25 | stop="input keyevent KEYCODE_BACK; \ 26 | input keyevent KEYCODE_HOME" 27 | #stop="am force-stop $packageName" 28 | 29 | $adbTarget shell $stop; sleep 2 30 | echo "Streaming stopped for $streamerIP" 31 | } 32 | 33 | #Device sleep 34 | adbSleep() { 35 | sleep="input keyevent KEYCODE_SLEEP" 36 | 37 | $adbTarget shell $sleep 38 | echo "Sleep initiated for $streamerIP" 39 | date +%s > $streamerNoPort/stream_stopped 40 | echo "$streamerNoPort/stream_stopped written with epoch stop time" 41 | } 42 | 43 | main() { 44 | bmituneDone 45 | adbStop 46 | adbSleep 47 | } 48 | 49 | main 50 | -------------------------------------------------------------------------------- /scripts/firetv/fubo/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #stopbmitune.sh for firetv/fubo 3 | 4 | #Debug on if uncommented 5 | set -x 6 | 7 | streamerIP="$1" 8 | streamerNoPort="${streamerIP%%:*}" 9 | adbTarget="adb -s $streamerIP" 10 | packageName="com.fubo.firetv.screen" 11 | 12 | #Check if bmitune.sh is done running 13 | bmituneDone() { 14 | bmitunePID=$(<"$streamerNoPort/bmitune_pid") 15 | 16 | while ps -p $bmitunePID > /dev/null; do 17 | echo "Waiting for bmitune.sh to complete..." 18 | sleep 2 19 | done 20 | } 21 | 22 | #Stop stream 23 | adbStop() { 24 | stop="input keyevent KEYCODE_BACK; \ 25 | input keyevent KEYCODE_BACK; \ 26 | input keyevent KEYCODE_HOME" 27 | #stop="am force-stop $packageName" 28 | $adbTarget shell $stop; sleep 2 29 | echo "Streaming stopped for $streamerIP" 30 | } 31 | 32 | #Device sleep 33 | adbSleep() { 34 | sleep="input keyevent KEYCODE_SLEEP" 35 | 36 | $adbTarget shell $sleep 37 | echo "Sleep initiated for $streamerIP" 38 | date +%s > $streamerNoPort/stream_stopped 39 | echo "$streamerNoPort/stream_stopped written with epoch stop time" 40 | } 41 | 42 | main() { 43 | bmituneDone 44 | adbStop 45 | adbSleep 46 | } 47 | 48 | main 49 | -------------------------------------------------------------------------------- /html/stream.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Stream Page 6 | 7 | 8 | 9 |

Select Tuner:

10 | 15 |

16 | 17 | 37 | 38 | -------------------------------------------------------------------------------- /scripts/firetv/espn/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #stopbmitune.sh for firetv/espn 3 | #2025.11.03 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | streamerIP="$1" 9 | streamerNoPort="${streamerIP%%:*}" 10 | adbTarget="adb -s $streamerIP" 11 | packageName="com.espn.gtv" 12 | 13 | #Check if bmitune.sh is done running 14 | bmituneDone() { 15 | bmitunePID=$(<"$streamerNoPort/bmitune_pid") 16 | 17 | while ps -p $bmitunePID > /dev/null; do 18 | echo "Waiting for bmitune.sh to complete..." 19 | sleep 2 20 | done 21 | } 22 | 23 | #Stop stream 24 | adbStop() { 25 | #stop="input keyevent KEYCODE_BACK" 26 | #input keyevent KEYCODE_BACK; \ 27 | #input keyevent KEYCODE_HOME" 28 | stop="am force-stop $packageName" 29 | $adbTarget shell $stop; sleep 4 30 | echo "Streaming stopped for $streamerIP" 31 | } 32 | 33 | #Device sleep 34 | adbSleep() { 35 | sleep="input keyevent KEYCODE_SLEEP" 36 | 37 | $adbTarget shell $sleep 38 | echo "Sleep initiated for $streamerIP" 39 | date +%s > $streamerNoPort/stream_stopped 40 | echo "$streamerNoPort/stream_stopped written with epoch stop time" 41 | } 42 | 43 | main() { 44 | bmituneDone 45 | adbStop 46 | adbSleep 47 | } 48 | 49 | main 50 | -------------------------------------------------------------------------------- /hulu_contentid.txt: -------------------------------------------------------------------------------- 1 | bravo 414af5d5-8037-48f2-9aec-7fcbb012859b 2 | discovery 15c4547b-7457-44d2-8cf3-e6aefa2ac09d 3 | foodnetwork 5a757cdb-7d34-45f4-99b8-338d0461e51f 4 | mtv aa3d9731-5bfa-4de4-9128-b5adbe84f4d2 5 | paramount 23586acc-6f36-4b00-9ecf-9c7f5973bd68 6 | syfy 07fb8004-d9af-43fe-8f7c-811cf3b7e0b0 7 | history bf84c00e-26c4-4c19-b4f2-e113fd4ebbc8 8 | cnbc abf13f07-a494-4c3e-b56f-866145f64916 9 | usa 02924d6e-d1bb-4286-a62b-0adfd1ecec7b 10 | vice bde4af8e-b348-448f-a580-a7ea6e5d4dc4 11 | tbs 94d96b72-90dd-4299-93ab-6963d717635d 12 | abc 80dd3a6b-15df-41ac-acec-d822fb826332 13 | cbs 5a19f5b5-75ea-4024-807e-bbc54467a55b 14 | fox 8f7c4d8f-87fb-42c6-98af-34ff4362cfd5 15 | fyi 172b34f5-7390-4dad-af63-c82f5fb4d10f 16 | hgtv af9c0379-06e2-44f4-ac5d-bda681c704f2 17 | motortrend b41b0d7e-58e2-4a5e-bf1b-1f4d19eab44c 18 | msnbc 529a7abd-7113-4b74-bed0-3f4f0129d77c 19 | nbc 0dd6cd60-e42d-458f-b7b5-5c749fd4a2af 20 | cw 2f06b839-579b-4877-800d-f40cf5335a89 21 | travel f8e60a0b-d5ed-4c8d-8e01-e2e7a7c77f26 22 | vh1 889fc1eb-79f2-4db8-9614-bd249af16f03 23 | tnteast dd326f4a-244a-464e-8b87-4d321c2937a3 24 | tntwest d831e256-4c6b-4aba-8776-f76aff409b2e 25 | a&e 67d198b0-9d65-485e-a381-7a24848cfe38 26 | fox 8f7c4d8f-87fb-42c6-98af-34ff4362cfd5 27 | e! b6f5e4b5-3e92-45ce-b6c8-179a2665c245 -------------------------------------------------------------------------------- /html/m3us.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | M3U Files 5 | 6 | 7 | 8 | 9 | 10 |

13 | 14 |
15 | {{range .m3us}} 16 | {{.}} 17 | {{end}} 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /scripts/osprey/dtvospreydeeplinks/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #prebmitune.sh for osprey/dtvospreydeeplinks 3 | # 2025.09.26 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | streamerIP="$1" 9 | streamerNoPort="${streamerIP%%:*}" 10 | adbTarget="adb -s $streamerIP" 11 | 12 | mkdir -p $streamerNoPort 13 | 14 | #Trap end of script run 15 | finish() { 16 | echo "prebmitune.sh is exiting for $streamerIP with exit code $?" 17 | } 18 | 19 | trap finish EXIT 20 | 21 | adbConnect() { 22 | adb connect $streamerIP 23 | 24 | local -i adbMaxRetries=3 25 | local -i adbCounter=0 26 | 27 | while true; do 28 | $adbTarget shell input keyevent KEYCODE_WAKEUP 29 | local adbEventSuccess=$? 30 | 31 | if [[ $adbEventSuccess -eq 0 ]]; then 32 | break 33 | fi 34 | 35 | if (($adbCounter > $adbMaxRetries)); then 36 | touch $streamerNoPort/adbCommunicationFail 37 | echo "Communication with $streamerIP failed after $adbMaxRetries retries" 38 | exit 2 39 | fi 40 | 41 | 42 | ((adbCounter++)) 43 | done 44 | } 45 | 46 | adbWake() { 47 | $adbTarget shell input keyevent KEYCODE_WAKEUP 48 | echo "Waking $streamerIP" 49 | touch $streamerNoPort/adbAppRunning 50 | } 51 | 52 | main() { 53 | adbConnect 54 | adbWake 55 | } 56 | 57 | main 58 | -------------------------------------------------------------------------------- /scripts/osprey/dtvosprey/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #prebmitune.sh for osprey/dtvosprey 3 | # 2025.09.10 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | streamerIP="$1" 9 | streamerNoPort="${streamerIP%%:*}" 10 | adbTarget="adb -s $streamerIP" 11 | 12 | mkdir -p $streamerNoPort 13 | 14 | #Trap end of script run 15 | finish() { 16 | echo "prebmitune.sh is exiting for $streamerIP with exit code $?" 17 | } 18 | 19 | trap finish EXIT 20 | 21 | adbConnect() { 22 | adb connect $streamerIP 23 | 24 | local -i adbMaxRetries=3 25 | local -i adbCounter=0 26 | 27 | while true; do 28 | $adbTarget shell input keyevent KEYCODE_WAKEUP 29 | local adbEventSuccess=$? 30 | 31 | if [[ $adbEventSuccess -eq 0 ]]; then 32 | break 33 | fi 34 | 35 | if (($adbCounter > $adbMaxRetries)); then 36 | touch $streamerNoPort/adbCommunicationFail 37 | echo "Communication with $streamerIP failed after $adbMaxRetries retries" 38 | exit 2 39 | fi 40 | 41 | 42 | ((adbCounter++)) 43 | done 44 | } 45 | 46 | adbWake() { 47 | 48 | $adbTarget shell input keyevent KEYCODE_WAKEUP; sleep 2; 49 | echo "Waking $streamerIP" 50 | touch $streamerNoPort/adbAppRunning 51 | 52 | } 53 | 54 | main() { 55 | adbConnect 56 | adbWake 57 | } 58 | 59 | main 60 | -------------------------------------------------------------------------------- /m3u/foo-fighters.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | 3 | #EXTINF:-1 channel-id="foo1" tvc-guide-stationid="" tvg-group="" tvg-logo="https://www.rollingstone.com/wp-content/uploads/2021/06/FF_SNIPE_JUNE_20TH.jpg",Foo Fighters, MSG, 2021 4 | http://{{ .IPADDRESS }}:7654/play/tuner/youtube__XHsLst7ypR0 5 | 6 | #EXTINF:-1 channel-id="foo2" tvc-guide-stationid="" tvg-group="" tvg-logo="https://k8g7u9q7.stackpathcdn.com/wp-content/uploads/2022/04/foofighters-22lollapalooza-chile1.jpg",Foo Fighters, Lollapalooza, Chile 2022 7 | http://{{ .IPADDRESS }}:7654/play/tuner/youtube__MhEW7Qb_DXc 8 | 9 | #EXTINF:-1 channel-id="foo5" tvc-guide-stationid="" tvg-group="" tvg-logo="https://www.billboard.com/wp-content/uploads/2022/08/Dave-Grohl-of-Foo-Fighters-2022-billboard-1548.jpg?w=1024",FooFighters, Gilford NH, 2023 10 | http://{{ .IPADDRESS }}:7654/play/tuner/youtube__TLr86SwK1ug 11 | 12 | #EXTINF:-1 channel-id="foo6" tvc-guide-stationid="" tvg-group="" tvg-logo="https://pbs.twimg.com/media/E9vBEHfVUAAwjd8.jpg:large",Foo Fighters, LA, 2021 13 | http://{{ .IPADDRESS }}:7654/play/tuner/youtube__KlZFS6HR6P8 14 | 15 | #EXTINF:-1 channel-id="foo7" tvc-guide-stationid="" tvg-group="" tvg-logo="https://jambands.com/wp-content/uploads/2023/06/unnamed-2023-06-19T110834.683-700x459.png",FooFighters Bonnaroo 2023 16 | http://{{ .IPADDRESS }}:7654/play/tuner/youtube__zK_QVjwQ6Nw 17 | 18 | -------------------------------------------------------------------------------- /scripts/firetv/espn/bmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #bmitune.sh for firetv/espn 3 | #2025.11.03 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | #Global 9 | channelID="$1" 10 | specialID="$1" 11 | streamerIP="$2" 12 | streamerNoPort="${streamerIP%%:*}" 13 | adbTarget="adb -s $streamerIP" 14 | packageName=com.espn.gtv 15 | packageLaunch=com.espn.startup.presentation.StartupActivity 16 | 17 | #Trap end of script run 18 | finish() { 19 | echo "bmitune.sh is exiting for $streamerIP with exit code $?" 20 | } 21 | 22 | trap finish EXIT 23 | 24 | updateReferenceFiles() { 25 | 26 | # Handle cases where stream_stopped or last_channel don't exist 27 | mkdir -p $streamerNoPort 28 | [[ -f "$streamerNoPort/stream_stopped" ]] || echo 0 > "$streamerNoPort/stream_stopped" 29 | [[ -f "$streamerNoPort/last_channel" ]] || echo 0 > "$streamerNoPort/last_channel" 30 | 31 | # Write PID for this script to bmitune_pid for use in stopbmitune.sh 32 | echo $$ > "$streamerNoPort/bmitune_pid" 33 | echo "Current PID for this script is $$" 34 | } 35 | 36 | #Tuning is based on channel ID values from espn_plus.m3u. 37 | tuneChannel() { 38 | $adbTarget shell am start -n $packageName/$packageLaunch sportscenter://x-callback-url/showWatchStream?playID=$channelID 39 | } 40 | 41 | main() { 42 | updateReferenceFiles 43 | tuneChannel 44 | } 45 | 46 | main 47 | -------------------------------------------------------------------------------- /scripts/shieldtv/youtubetv/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2023 Scott Ullrich 4 | # sullrich@gmail.com 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | # this software and associated documentation files (the “Software”), to deal in the 8 | # Software without restriction, including without limitation the rights to use, copy, 9 | # modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 10 | # and to permit persons to whom the Software is furnished to do so, subject to the 11 | # following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 17 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | # 23 | 24 | TUNERIP="$1" 25 | 26 | #stop_provider 27 | 28 | adb -s $TUNERIP shell input keyevent 86 29 | 30 | -------------------------------------------------------------------------------- /m3u/nbc.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | 3 | #EXTINF:-1 channel-id="usa_east" channel-number="" tvc-guide-stationid="58452" tvg-group="" tvg-logo="",USA East 4 | http://{{ .IPADDRESS }}/play/tuner/usa-usa_east 5 | 6 | #EXTINF:-1 channel-id="usa_west" channel-number="" tvc-guide-stationid="74030" tvg-group="" tvg-logo="",USA West 7 | http://{{ .IPADDRESS }}/play/tuner/usa-usa_west 8 | 9 | #EXTINF:-1 channel-id="oxygen_east" channel-number="" tvc-guide-stationid="21484" tvg-group="" tvg-logo="",Oxygen East 10 | http://{{ .IPADDRESS }}/play/tuner/oxygen-oxygen_east 11 | 12 | #EXTINF:-1 channel-id="oxygen_west" channel-number="" tvc-guide-stationid="21744" tvg-group="" tvg-logo="",Oxygen West 13 | http://{{ .IPADDRESS }}/play/tuner/oxygen-oxygen_east 14 | 15 | #EXTINF:-1 channel-id="e_east" channel-number="" tvc-guide-stationid="61812" tvg-group="" tvg-logo="",E! East 16 | http://{{ .IPADDRESS }}/play/tuner/e-e_east 17 | 18 | #EXTINF:-1 channel-id="e_west" channel-number="" tvc-guide-stationid="17561" tvg-group="" tvg-logo="",E! West 19 | http://{{ .IPADDRESS }}/play/tuner/e-e_west 20 | 21 | #EXTINF:-1 channel-id="cnbc_east" channel-number="" tvc-guide-stationid="58780" tvg-group="" tvg-logo="",CNBC 22 | http://{{ .IPADDRESS }}/play/tuner/cnbc-cnbc_east 23 | 24 | #EXTINF:-1 channel-id="golf_east" channel-number="" tvc-guide-stationid="61854" tvg-group="" tvg-logo="",Golf Channel 25 | http://{{ .IPADDRESS }}/play/tuner/golf-golf_east 26 | -------------------------------------------------------------------------------- /scripts/shieldtv/youtubetv/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2023 Scott Ullrich 4 | # sullrich@gmail.com 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | # this software and associated documentation files (the “Software”), to deal in the 8 | # Software without restriction, including without limitation the rights to use, copy, 9 | # modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 10 | # and to permit persons to whom the Software is furnished to do so, subject to the 11 | # following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 17 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | # 23 | 24 | TUNERIP="$1" 25 | 26 | # Wake up 27 | adb -s $TUNERIP shell input keyevent 224 28 | adb -s $TUNERIP shell input keyevent 224 29 | adb -s $TUNERIP shell input keyevent 224 30 | 31 | 32 | -------------------------------------------------------------------------------- /Dockerfile-pyatv: -------------------------------------------------------------------------------- 1 | #docker buildx build --platform linux/amd64,linux/arm64 -f Dockerfile-pyatv -t bnhf/ah4c:appletv . --push --no-cache 2 | 3 | # Build ah4c application in 2nd stage 4 | #FROM golang:alpine3.14 AS builder2 5 | FROM golang:alpine3.21 AS builder2 6 | 7 | # Set working directory 8 | RUN mkdir -p /go/src/github.com/sullrich 9 | WORKDIR /go/src/github.com/sullrich 10 | 11 | # Install dependencies and build 12 | RUN apk upgrade --no-cache && apk add --no-cache git \ 13 | && git clone https://github.com/sullrich/ah4c . \ 14 | && go build -o /opt/ah4c 15 | 16 | # Copy files from local workspace 17 | COPY docker-start-pyatv.sh /opt/ 18 | COPY scripts /tmp/scripts/ 19 | COPY m3u/* /tmp/m3u/ 20 | COPY html/* /opt/html/ 21 | COPY static /opt/static/ 22 | 23 | # Build combined ah4c with ws-scrcpy 24 | FROM ghcr.io/postlund/pyatv:0.14.5 AS runner 25 | LABEL maintainer="The Slayer " 26 | 27 | # Set working directory 28 | RUN mkdir -p /opt/scripts /tmp/scripts /tmp/m3u /opt/html /opt/static 29 | WORKDIR /opt 30 | 31 | # Install dependencies 32 | RUN apk upgrade --no-cache \ 33 | && apk add --no-cache android-tools curl npm bash bind-tools ffmpeg procps nano tzdata tesseract-ocr 34 | 35 | # Copy files from builders 36 | COPY --from=builder2 /opt /opt 37 | COPY --from=builder2 /tmp /tmp 38 | 39 | # Expose needed ports 40 | EXPOSE 7654 41 | EXPOSE 8000 42 | 43 | # Run start script 44 | CMD ["./docker-start-pyatv.sh"] 45 | -------------------------------------------------------------------------------- /m3u/channels.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | 3 | #EXTINF:-1 channel-id="10001" tvg-logo="/dvr/uploads/3/content",Police Dramas 4 | http://{{ .IPADDRESS }}/play/tuner/10001 5 | 6 | #EXTINF:-1 channel-id="10002" tvg-logo="/dvr/uploads/4/content",The Sixties 7 | http://{{ .IPADDRESS }}/play/tuner/10002 8 | 9 | #EXTINF:-1 channel-id="10003" tvg-logo="/dvr/uploads/5/content",Victorian Age 10 | http://{{ .IPADDRESS }}/play/tuner/10003 11 | 12 | #EXTINF:-1 channel-id="10004" tvg-logo="/dvr/uploads/6/content",Star Wars 13 | http://{{ .IPADDRESS }}/play/tuner/10004 14 | 15 | #EXTINF:-1 channel-id="10005" tvg-logo="/dvr/uploads/7/content",Political 16 | http://{{ .IPADDRESS }}/play/tuner/10005 17 | 18 | #EXTINF:-1 channel-id="10006" tvg-logo="/dvr/uploads/8/content",Medical 19 | http://{{ .IPADDRESS }}/play/tuner/10006 20 | 21 | #EXTINF:-1 channel-id="10007" tvg-logo="/dvr/uploads/9/content",Napoleonic Era 22 | http://{{ .IPADDRESS }}/play/tuner/10007 23 | 24 | #EXTINF:-1 channel-id="10008" tvg-logo="/dvr/uploads/10/content",Chicago Shows 25 | http://{{ .IPADDRESS }}/play/tuner/10008 26 | 27 | #EXTINF:-1 channel-id="10009" tvg-logo="/dvr/uploads/11/content",Police Consultants 28 | http://{{ .IPADDRESS }}/play/tuner/10009 29 | 30 | #EXTINF:-1 channel-id="10010" tvg-logo="/dvr/uploads/14/content",Global Catastrophe 31 | http://{{ .IPADDRESS }}/play/tuner/10009 32 | 33 | #EXTINF:-1 channel-id="10011" tvg-logo="/dvr/uploads/15/content",Criminals 34 | http://{{ .IPADDRESS }}/play/tuner/10011 35 | -------------------------------------------------------------------------------- /scripts/firetv/hulu/isconnected.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2023 Scott Ullrich 4 | # sullrich@gmail.com 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | # this software and associated documentation files (the “Software”), to deal in the 8 | # Software without restriction, including without limitation the rights to use, copy, 9 | # modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 10 | # and to permit persons to whom the Software is furnished to do so, subject to the 11 | # following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 17 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | # 23 | 24 | TUNERIP="$1" 25 | 26 | . ./scripts/firetv/hulu/common_functions.sh 27 | 28 | is_ip_address $TUNERIP && adb_connect 29 | 30 | ms=$(adb -s $TUNERIP shell dumpsys media_session | grep "state=PlaybackState {state=3" | wc -l) 31 | if ((ms > 0)); then 32 | echo true 33 | else 34 | echo false 35 | fi -------------------------------------------------------------------------------- /scripts/firetv/hulu/reboot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2023 Scott Ullrich 4 | # sullrich@gmail.com 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | # this software and associated documentation files (the “Software”), to deal in the 8 | # Software without restriction, including without limitation the rights to use, copy, 9 | # modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 10 | # and to permit persons to whom the Software is furnished to do so, subject to the 11 | # following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 17 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | # 23 | 24 | function finish { 25 | logger "reboot.sh is now exiting" 26 | } 27 | 28 | trap finish EXIT 29 | 30 | TUNERIP="$1" 31 | 32 | . ./scripts/firetv/hulu/common_functions.sh 33 | 34 | is_ip_address $TUNERIP && adb -s $TUNERIP disconnect 35 | is_ip_address $TUNERIP && adb connect $TUNERIP 36 | 37 | adb -s $TUNERIP shell reboot 38 | 39 | -------------------------------------------------------------------------------- /env.sample.magewell: -------------------------------------------------------------------------------- 1 | CHANNELSIP="10.0.250.69" 2 | IPADDRESS="10.0.250.142" 3 | 4 | STREAMER_APP="scripts/firetv/hulu" 5 | 6 | NUMBER_TUNERS="2" 7 | 8 | ENCODER1_URL="" 9 | TUNER1_IP="10.0.250.158:5555" 10 | CMD1_DEVICE="/dev/video0" 11 | CMD1="ffmpeg -thread_queue_size 1024 -y -hwaccel cuda -hwaccel_output_format cuda -f v4l2 -i $CMD1_DEVICE -thread_queue_size 1024 -f pulse -i alsa_input.pci-0000_08_00.0.stereo-fallback -c:v h264_nvenc -b:v 70M -minrate 50M -maxrate 95M -bufsize 150M -vf 'hqdn3d=1.5:1.5:6:6,scale=3840:2160:flags=lanczos,unsharp=5:5:1.5:5:5:0.0' -c:a eac3 -strict -2 -b:a 6144000 -ar 48000 -af 'volume=2.0' -vsync 1 -async 1 -f mpegts -" 12 | 13 | 14 | ENCODER2_URL="" 15 | TUNER2_IP="10.0.250.153:5555" 16 | CMD2_DEVICE="/dev/video1" 17 | CMD2="ffmpeg -thread_queue_size 1024 -y -hwaccel cuda -hwaccel_output_format cuda -f v4l2 -i $CMD2_DEVICE -thread_queue_size 1024 -f pulse -i alsa_input.pci-0000_0a_00.0.stereo-fallback -c:v h264_nvenc -b:v 70M -minrate 50M -maxrate 95M -bufsize 150M -vf 'hqdn3d=1.5:1.5:6:6,scale=3840:2160:flags=lanczos,unsharp=5:5:1.5:5:5:0.0' -c:a eac3 -strict -2 -b:a 6144000 -ar 48000 -af 'volume=2.0' -vsync 1 -async 1 -f mpegts -" 18 | 19 | ENCODER3_URL="" 20 | TUNER3_IP="" 21 | CMD3="" 22 | CMD3_DEVICE="" 23 | 24 | ENCODER4_URL="" 25 | TUNER4_IP="" 26 | CMD4="" 27 | CMD4_DEVICE="" 28 | 29 | ENCODER5_URL="" 30 | TUNER5_IP="" 31 | CMD5="" 32 | CMD5_DEVICE="" 33 | 34 | ALERT_SMTP_SERVER="" 35 | ALERT_AUTH_SERVER="" 36 | ALERT_EMAIL_FROM="" 37 | ALERT_EMAIL_PASS="" 38 | ALERT_EMAIL_TO="" 39 | 40 | ALERT_WEBHOOK_URL="" 41 | 42 | ALLOW_DEBUG_VIDEO_PREVIEW="FALSE" 43 | -------------------------------------------------------------------------------- /scripts/firetv/dtvdeeplinks/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # stopbmitune.sh for firetv/dtvdeeplinks 3 | # 2024.10.29 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | streamerIP="$1" 9 | streamerNoPort="${streamerIP%%:*}" 10 | adbTarget="adb -s $streamerIP" 11 | packageName=com.att.tv 12 | [[ $SPEED_MODE == "" ]] && speedMode="true" || speedMode="$SPEED_MODE" 13 | 14 | #Check if bmitune.sh is done running 15 | bmituneDone() { 16 | bmitunePID=$(<"$streamerNoPort/bmitune_pid") 17 | keepWatchingPID=$(pgrep -f "$streamerNoPort/keep_watching.sh") 18 | keepWatchingPPID=$(ps -o ppid= -p "$keepWatchingPID") 19 | keepWatchingCPID=$(pgrep -P $keepWatchingPID) 20 | 21 | while ps -p $bmitunePID > /dev/null; do 22 | echo "Waiting for bmitune.sh to complete..." 23 | sleep 2 24 | done 25 | 26 | [[ $KEEP_WATCHING ]] && pkill -P $keepWatchingPPID && kill $keepWatchingCPID 27 | rm ./$streamerNoPort/keep_watching.sh 28 | } 29 | 30 | #Stop stream 31 | adbStop() { 32 | [[ $speedMode == "true" ]] \ 33 | && stop="input keyevent KEYCODE_BACK; \ 34 | input keyevent KEYCODE_HOME" \ 35 | || stop="am force-stop $packageName" 36 | 37 | $adbTarget shell $stop; sleep 2 38 | echo "Streaming stopped for $streamerIP" 39 | } 40 | 41 | #Device sleep 42 | adbSleep() { 43 | sleep="input keyevent KEYCODE_SLEEP" 44 | 45 | $adbTarget shell $sleep 46 | echo "Sleep initiated for $streamerIP" 47 | date +%s > $streamerNoPort/stream_stopped 48 | echo "$streamerNoPort/stream_stopped written with epoch stop time" 49 | } 50 | 51 | main() { 52 | bmituneDone 53 | adbStop 54 | adbSleep 55 | } 56 | 57 | main 58 | -------------------------------------------------------------------------------- /html/routes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Available Routes 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | {{ range . }} 25 | 26 | 27 | 28 | 29 | 30 | {{ end }} 31 | 32 |
HTTP MethodPathHandler
{{ .Method }}{{ .Path }}{{ .Handler }}
33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /scripts/firetv/hulu/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2023 Scott Ullrich 4 | # sullrich@gmail.com 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | # this software and associated documentation files (the “Software”), to deal in the 8 | # Software without restriction, including without limitation the rights to use, copy, 9 | # modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 10 | # and to permit persons to whom the Software is furnished to do so, subject to the 11 | # following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 17 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | # 23 | 24 | TUNERIP="$1" 25 | PROVIDER=$(cat /tmp/$TUNERIP.provider) 26 | EXE="" 27 | 28 | logger "[STOPPING] stopbmitune.sh is starting for $TUNERIP" 29 | 30 | function finish { 31 | rm -f /tmp/$TUNERIP.* 32 | } 33 | 34 | trap finish EXIT 35 | 36 | . ./scripts/firetv/hulu/common_functions.sh 37 | 38 | is_ip_address $TUNERIP && adb connect $TUNERIP 39 | 40 | stop_provider 41 | 42 | adb -s $TUNERIP shell input keyevent KEYCODE_HOME 43 | 44 | -------------------------------------------------------------------------------- /scripts/firetv/directv/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #prebmitune.sh for firetv/directv 3 | 4 | #Debug on if uncommented 5 | set -x 6 | 7 | streamerIP="$1" 8 | streamerNoPort="${streamerIP%%:*}" 9 | adbTarget="adb -s $streamerIP" 10 | 11 | mkdir -p $streamerNoPort 12 | 13 | #Trap end of script run 14 | finish() { 15 | echo "prebmitune.sh is exiting for $streamerIP with exit code $?" 16 | } 17 | 18 | trap finish EXIT 19 | 20 | adbConnect() { 21 | adb connect $streamerIP 22 | 23 | local -i adbMaxRetries=25 24 | local -i adbCounter=0 25 | 26 | while true; do 27 | $adbTarget shell input keyevent KEYCODE_WAKEUP 28 | local adbEventSuccess=$? 29 | 30 | if [[ $adbEventSuccess -eq 0 ]]; then 31 | break 32 | fi 33 | 34 | if (($adbCounter > $adbMaxRetries)); then 35 | touch $streamerNoPort/adbCommunicationFail 36 | echo "Communication with $streamerIP failed after $adbMaxRetries retries" 37 | exit 1 38 | fi 39 | 40 | sleep 1 41 | ((adbCounter++)) 42 | done 43 | } 44 | 45 | adbWake() { 46 | packageLaunch="com.clientapp.MainActivity" 47 | packageName="com.att.tv" 48 | packagePID=$($adbTarget shell pidof $packageName) 49 | 50 | if [ ! -z $packagePID ]; then 51 | $adbTarget shell input keyevent KEYCODE_WAKEUP 52 | $adbTarget shell am start -n $packageName/$packageLaunch 53 | echo "Waking $streamerIP" 54 | touch $streamerNoPort/adbAppRunning 55 | else 56 | $adbTarget shell input keyevent KEYCODE_WAKEUP 57 | $adbTarget shell am start -n $packageName/$packageLaunch 58 | echo "Starting $packageName on $streamerIP" 59 | fi 60 | } 61 | 62 | main() { 63 | adbConnect 64 | adbWake 65 | } 66 | 67 | main 68 | -------------------------------------------------------------------------------- /scripts/firetv/dtvstream/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #prebmitune.sh for firetv/directv 3 | 4 | #Debug on if uncommented 5 | set -x 6 | 7 | streamerIP="$1" 8 | streamerNoPort="${streamerIP%%:*}" 9 | adbTarget="adb -s $streamerIP" 10 | 11 | mkdir -p $streamerNoPort 12 | 13 | #Trap end of script run 14 | finish() { 15 | echo "prebmitune.sh is exiting for $streamerIP with exit code $?" 16 | } 17 | 18 | trap finish EXIT 19 | 20 | adbConnect() { 21 | adb connect $streamerIP 22 | 23 | local -i adbMaxRetries=25 24 | local -i adbCounter=0 25 | 26 | while true; do 27 | $adbTarget shell input keyevent KEYCODE_WAKEUP 28 | local adbEventSuccess=$? 29 | 30 | if [[ $adbEventSuccess -eq 0 ]]; then 31 | break 32 | fi 33 | 34 | if (($adbCounter > $adbMaxRetries)); then 35 | touch $streamerNoPort/adbCommunicationFail 36 | echo "Communication with $streamerIP failed after $adbMaxRetries retries" 37 | exit 1 38 | fi 39 | 40 | sleep 1 41 | ((adbCounter++)) 42 | done 43 | } 44 | 45 | adbWake() { 46 | packageLaunch="com.clientapp.MainActivity" 47 | packageName="com.att.tv" 48 | packagePID=$($adbTarget shell pidof $packageName) 49 | 50 | if [ ! -z $packagePID ]; then 51 | $adbTarget shell input keyevent KEYCODE_WAKEUP 52 | $adbTarget shell am start -n $packageName/$packageLaunch 53 | echo "Waking $streamerIP" 54 | touch $streamerNoPort/adbAppRunning 55 | else 56 | $adbTarget shell input keyevent KEYCODE_WAKEUP 57 | $adbTarget shell am start -n $packageName/$packageLaunch 58 | echo "Starting $packageName on $streamerIP" 59 | fi 60 | } 61 | 62 | main() { 63 | adbConnect 64 | adbWake 65 | } 66 | 67 | main 68 | -------------------------------------------------------------------------------- /scripts/firetv/channels/bmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # bmitune.sh for firetv/channels 3 | # 2025.05.03 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | #Global 9 | channelID="$1" 10 | streamerIP="$2" 11 | streamerNoPort="${streamerIP%%:*}" 12 | adbTarget="adb -s $streamerIP" 13 | packageName=com.getchannels.dvr.app 14 | packageAction=com.getchannels.android.MainActivity 15 | [[ $SPEED_MODE == "" ]] && speedMode="true" || speedMode="$SPEED_MODE" 16 | 17 | #Trap end of script run 18 | finish() { 19 | echo "bmitune.sh is exiting for $streamerIP with exit code $?" 20 | } 21 | 22 | trap finish EXIT 23 | 24 | updateReferenceFiles() { 25 | 26 | # Handle cases where stream_stopped or last_channel don't exist 27 | mkdir -p $streamerNoPort 28 | [[ -f "$streamerNoPort/stream_stopped" ]] || echo 0 > "$streamerNoPort/stream_stopped" 29 | [[ -f "$streamerNoPort/last_channel" ]] || echo 0 > "$streamerNoPort/last_channel" 30 | 31 | # Write PID for this script to bmitune_pid for use in stopbmitune.sh 32 | echo $$ > "$streamerNoPort/bmitune_pid" 33 | echo "Current PID for this script is $$" 34 | } 35 | 36 | appFocusCheck() { 37 | appFocus=$($adbTarget shell dumpsys window windows | grep -E 'mCurrentFocus' | cut -d '/' -f1 | sed 's/.* //g') 38 | 39 | if [[ $appFocus == $packageName ]]; then 40 | return 0 41 | else 42 | return 1 43 | fi 44 | } 45 | 46 | #Tuning is based on channel name values from channels.m3u. 47 | tuneChannel() { 48 | ! appFocusCheck && $adbTarget shell am start -n $packageName/$packageAction && sleep 3 49 | curl -s -X POST http://$streamerNoPort:57000/api/play/channel/$channelID 50 | } 51 | 52 | main() { 53 | updateReferenceFiles 54 | tuneChannel 55 | } 56 | 57 | main 58 | -------------------------------------------------------------------------------- /scripts/firetv/fubo/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #prebmitune.sh for firetv/fubo 3 | 4 | #Debug on if uncommented 5 | set -x 6 | 7 | streamerIP="$1" 8 | streamerNoPort="${streamerIP%%:*}" 9 | adbTarget="adb -s $streamerIP" 10 | 11 | mkdir -p $streamerNoPort 12 | 13 | #Trap end of script run 14 | finish() { 15 | echo "prebmitune.sh is exiting for $streamerIP with exit code $?" 16 | } 17 | 18 | trap finish EXIT 19 | 20 | adbConnect() { 21 | adb connect $streamerIP 22 | 23 | local -i adbMaxRetries=2 24 | local -i adbCounter=0 25 | 26 | while true; do 27 | $adbTarget shell input keyevent KEYCODE_WAKEUP 28 | local adbEventSuccess=$? 29 | 30 | if [[ $adbEventSuccess -eq 0 ]]; then 31 | break 32 | fi 33 | 34 | if (($adbCounter > $adbMaxRetries)); then 35 | touch $streamerNoPort/adbCommunicationFail 36 | echo "Communication with $streamerIP failed after $adbMaxRetries retries" 37 | exit 1 38 | fi 39 | 40 | sleep 1 41 | ((adbCounter++)) 42 | done 43 | } 44 | 45 | # adbWake() { 46 | # #packageLaunch="tv.youi.clientapp.AppActivity" 47 | # packageName="com.sling" 48 | # packagePID=$($adbTarget shell pidof $packageName) 49 | 50 | # if [ ! -z $packagePID ]; then 51 | # $adbTarget shell input keyevent KEYCODE_WAKEUP 52 | # $adbTarget shell am start -n $packageName/$packageLaunch 53 | # echo "Waking $streamerIP" 54 | # touch $streamerNoPort/adbAppRunning 55 | # else 56 | # $adbTarget shell input keyevent KEYCODE_WAKEUP 57 | # $adbTarget shell am start -n $packageName/$packageLaunch 58 | # echo "Starting $packageName on $streamerIP" 59 | # fi 60 | # } 61 | 62 | main() { 63 | adbConnect 64 | #adbWake 65 | } 66 | 67 | main -------------------------------------------------------------------------------- /scripts/firetv/sling/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #prebmitune.sh for firetv/sling 3 | 4 | #Debug on if uncommented 5 | set -x 6 | 7 | streamerIP="$1" 8 | streamerNoPort="${streamerIP%%:*}" 9 | adbTarget="adb -s $streamerIP" 10 | 11 | mkdir -p $streamerNoPort 12 | 13 | #Trap end of script run 14 | finish() { 15 | echo "prebmitune.sh is exiting for $streamerIP with exit code $?" 16 | } 17 | 18 | trap finish EXIT 19 | 20 | adbConnect() { 21 | adb connect $streamerIP 22 | 23 | local -i adbMaxRetries=2 24 | local -i adbCounter=0 25 | 26 | while true; do 27 | $adbTarget shell input keyevent KEYCODE_WAKEUP 28 | local adbEventSuccess=$? 29 | 30 | if [[ $adbEventSuccess -eq 0 ]]; then 31 | break 32 | fi 33 | 34 | if (($adbCounter > $adbMaxRetries)); then 35 | touch $streamerNoPort/adbCommunicationFail 36 | echo "Communication with $streamerIP failed after $adbMaxRetries retries" 37 | exit 1 38 | fi 39 | 40 | sleep 1 41 | ((adbCounter++)) 42 | done 43 | } 44 | 45 | # adbWake() { 46 | # #packageLaunch="tv.youi.clientapp.AppActivity" 47 | # packageName="com.sling" 48 | # packagePID=$($adbTarget shell pidof $packageName) 49 | 50 | # if [ ! -z $packagePID ]; then 51 | # $adbTarget shell input keyevent KEYCODE_WAKEUP 52 | # $adbTarget shell am start -n $packageName/$packageLaunch 53 | # echo "Waking $streamerIP" 54 | # touch $streamerNoPort/adbAppRunning 55 | # else 56 | # $adbTarget shell input keyevent KEYCODE_WAKEUP 57 | # $adbTarget shell am start -n $packageName/$packageLaunch 58 | # echo "Starting $packageName on $streamerIP" 59 | # fi 60 | # } 61 | 62 | main() { 63 | adbConnect 64 | #adbWake 65 | } 66 | 67 | main -------------------------------------------------------------------------------- /scripts/firetv/dtvdeeplinks/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # prebmitune.sh for firetv/dtvdeeplinks 3 | # 2024.07.30 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | streamerIP="$1" 9 | streamerNoPort="${streamerIP%%:*}" 10 | adbTarget="adb -s $streamerIP" 11 | 12 | mkdir -p $streamerNoPort 13 | 14 | #Trap end of script run 15 | finish() { 16 | echo "prebmitune.sh is exiting for $streamerIP with exit code $?" 17 | } 18 | 19 | trap finish EXIT 20 | 21 | adbConnect() { 22 | adb connect $streamerIP 23 | 24 | local -i adbMaxRetries=2 25 | local -i adbCounter=0 26 | 27 | while true; do 28 | $adbTarget shell input keyevent KEYCODE_WAKEUP 29 | local adbEventSuccess=$? 30 | 31 | if [[ $adbEventSuccess -eq 0 ]]; then 32 | break 33 | fi 34 | 35 | if (($adbCounter > $adbMaxRetries)); then 36 | touch $streamerNoPort/adbCommunicationFail 37 | echo "Communication with $streamerIP failed after $adbMaxRetries retries" 38 | exit 1 39 | fi 40 | 41 | sleep 1 42 | ((adbCounter++)) 43 | done 44 | } 45 | 46 | adbWake() { 47 | packageLaunch="com.clientapp.MainActivity" 48 | packageName="com.att.tv" 49 | packagePID=$($adbTarget shell pidof $packageName) 50 | 51 | if [ ! -z $packagePID ]; then 52 | #$adbTarget shell input keyevent KEYCODE_WAKEUP 53 | $adbTarget shell am start -n $packageName/$packageLaunch 54 | echo "Confirming $packageName on $streamerIP" 55 | touch $streamerNoPort/adbAppRunning 56 | else 57 | #$adbTarget shell input keyevent KEYCODE_WAKEUP 58 | $adbTarget shell am start -n $packageName/$packageLaunch 59 | echo "Starting $packageName on $streamerIP" 60 | fi 61 | } 62 | 63 | main() { 64 | adbConnect 65 | adbWake 66 | } 67 | 68 | main 69 | -------------------------------------------------------------------------------- /scripts/firetv/xfinity/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # prebmitune.sh for firetv/xfinity 3 | # 2025.01.25 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | streamerIP="$1" 9 | streamerNoPort="${streamerIP%%:*}" 10 | adbTarget="adb -s $streamerIP" 11 | 12 | mkdir -p $streamerNoPort 13 | 14 | #Trap end of script run 15 | finish() { 16 | echo "prebmitune.sh is exiting for $streamerIP with exit code $?" 17 | } 18 | 19 | trap finish EXIT 20 | 21 | adbConnect() { 22 | adb connect $streamerIP 23 | 24 | local -i adbMaxRetries=2 25 | local -i adbCounter=0 26 | 27 | while true; do 28 | $adbTarget shell input keyevent KEYCODE_WAKEUP 29 | local adbEventSuccess=$? 30 | 31 | if [[ $adbEventSuccess -eq 0 ]]; then 32 | break 33 | fi 34 | 35 | if (($adbCounter > $adbMaxRetries)); then 36 | touch $streamerNoPort/adbCommunicationFail 37 | echo "Communication with $streamerIP failed after $adbMaxRetries retries" 38 | exit 1 39 | fi 40 | 41 | sleep 1 42 | ((adbCounter++)) 43 | done 44 | } 45 | 46 | # adbWake() { 47 | # #packageLaunch="tv.youi.clientapp.AppActivity" 48 | # packageName="com.sling" 49 | # packagePID=$($adbTarget shell pidof $packageName) 50 | 51 | # if [ ! -z $packagePID ]; then 52 | # $adbTarget shell input keyevent KEYCODE_WAKEUP 53 | # $adbTarget shell am start -n $packageName/$packageLaunch 54 | # echo "Waking $streamerIP" 55 | # touch $streamerNoPort/adbAppRunning 56 | # else 57 | # $adbTarget shell input keyevent KEYCODE_WAKEUP 58 | # $adbTarget shell am start -n $packageName/$packageLaunch 59 | # echo "Starting $packageName on $streamerIP" 60 | # fi 61 | # } 62 | 63 | main() { 64 | adbConnect 65 | #adbWake 66 | } 67 | 68 | main 69 | -------------------------------------------------------------------------------- /m3u/youtubetv.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | #EXTINF:-1 channel-id="aRGFZpHQkzY" tvc-guide-stationid="10518",NBC 3 | http://{{ .IPADDRESS }}/play/tuner/aRGFZpHQkzY 4 | #EXTINF:-1 channel-id="q6bWEVqhP8o" tvc-guide-stationid="16300",MSNBC 5 | http://{{ .IPADDRESS }}/play/tuner/q6bWEVqhP8o 6 | #EXTINF:-1 channel-id="ILTrUfLFrHI" tvc-guide-stationid="17561",E! 7 | http://{{ .IPADDRESS }}/play/tuner/ILTrUfLFrHI 8 | #EXTINF:-1 channel-id="xNk2Sv4t4Tc" tvc-guide-stationid="31555",Bravo 9 | http://{{ .IPADDRESS }}/play/tuner/xNk2Sv4t4Tc 10 | #EXTINF:-1 channel-id="v4upAc0UExk" tvc-guide-stationid="21744",Oxygen 11 | http://{{ .IPADDRESS }}/play/tuner/v4upAc0UExk 12 | #EXTINF:-1 channel-id="DJ7m0fdLKEY", tvc-guide-stationid="11208",USA 13 | http://{{ .IPADDRESS }}/play/tuner/DJ7m0fdLKEY 14 | #EXTINF:-1 channel-id="2_WogcoZ1cY" tvc-guide-stationid="24533",SyFy 15 | http://{{ .IPADDRESS }}/play/tuner/2_WogcoZ1cY 16 | #EXTINF:-1 channel-id="GIBoX-XF5i0" tvc-guide-stationid="10139",CNBC 17 | http://{{ .IPADDRESS }}/play/tuner/GIBoX-XF5i0 18 | #EXTINF:-1 channel-id="XK34g7QRvGk" tvc-guide-stationid="14899",Golf 19 | http://{{ .IPADDRESS }}/play/tuner/XK34g7QRvGk 20 | #EXTINF:-1 channel-id="_pYg9qMKKIA" tvc-guide-stationid="34710",NFL 21 | http://{{ .IPADDRESS }}/play/tuner/_pYg9qMKKIA 22 | #EXTINF:-1 channel-id="ZIzM3eNrylg" tvc-guide-stationid="91096",NewsNation 23 | http://{{ .IPADDRESS }}/play/tuner/ZIzM3eNrylg 24 | #EXTINF:-1 channel-id="isxl2N52bUQ" tvc-guide-stationid="59054",MTV Classic 25 | http://{{ .IPADDRESS }}/play/tuner/isxl2N52bUQ 26 | #EXTINF:-1 channel-id="g5BmB1qXulc" tvc-guide-stationid="89542",BBCNews 27 | http://{{ .IPADDRESS }}/play/tuner/g5BmB1qXulc 28 | #EXTINF:-1 channel-id="c05ACdzJxiE" tvc-guide-stationid="65799",Smithsonian 29 | http://{{ .IPADDRESS }}/play/tuner/c05ACdzJxiE -------------------------------------------------------------------------------- /scripts/firetv/channels/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # prebmitune.sh for firetv/channels 3 | # 2025.05.03 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | streamerIP="$1" 9 | streamerNoPort="${streamerIP%%:*}" 10 | adbTarget="adb -s $streamerIP" 11 | 12 | mkdir -p $streamerNoPort 13 | 14 | #Trap end of script run 15 | finish() { 16 | echo "prebmitune.sh is exiting for $streamerIP with exit code $?" 17 | } 18 | 19 | trap finish EXIT 20 | 21 | adbConnect() { 22 | adb connect $streamerIP 23 | 24 | local -i adbMaxRetries=2 25 | local -i adbCounter=0 26 | 27 | while true; do 28 | $adbTarget shell input keyevent KEYCODE_WAKEUP 29 | local adbEventSuccess=$? 30 | 31 | if [[ $adbEventSuccess -eq 0 ]]; then 32 | break 33 | fi 34 | 35 | if (($adbCounter > $adbMaxRetries)); then 36 | touch $streamerNoPort/adbCommunicationFail 37 | echo "Communication with $streamerIP failed after $adbMaxRetries retries" 38 | exit 1 39 | fi 40 | 41 | sleep 1 42 | ((adbCounter++)) 43 | done 44 | } 45 | 46 | adbWake() { 47 | packageLaunch="com.getchannels.android.MainActivity" 48 | packageName="com.getchannels.dvr.app" 49 | packagePID=$($adbTarget shell pidof $packageName) 50 | 51 | if [ ! -z $packagePID ]; then 52 | #$adbTarget shell input keyevent KEYCODE_WAKEUP 53 | $adbTarget shell am start -n $packageName/$packageLaunch 54 | echo "Confirming $packageName on $streamerIP" 55 | touch $streamerNoPort/adbAppRunning 56 | else 57 | #$adbTarget shell input keyevent KEYCODE_WAKEUP 58 | $adbTarget shell am start -n $packageName/$packageLaunch 59 | echo "Starting $packageName on $streamerIP" 60 | fi 61 | } 62 | 63 | main() { 64 | adbConnect 65 | adbWake 66 | } 67 | 68 | main 69 | -------------------------------------------------------------------------------- /scripts/firetv/dtvstreamdeeplinks/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # prebmitune.sh for firetv/dtvstreamdeeplinks 3 | # 2024.09.18 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | streamerIP="$1" 9 | streamerNoPort="${streamerIP%%:*}" 10 | adbTarget="adb -s $streamerIP" 11 | 12 | mkdir -p $streamerNoPort 13 | 14 | #Trap end of script run 15 | finish() { 16 | echo "prebmitune.sh is exiting for $streamerIP with exit code $?" 17 | } 18 | 19 | trap finish EXIT 20 | 21 | adbConnect() { 22 | adb connect $streamerIP 23 | 24 | local -i adbMaxRetries=2 25 | local -i adbCounter=0 26 | 27 | while true; do 28 | $adbTarget shell input keyevent KEYCODE_WAKEUP 29 | local adbEventSuccess=$? 30 | 31 | if [[ $adbEventSuccess -eq 0 ]]; then 32 | break 33 | fi 34 | 35 | if (($adbCounter > $adbMaxRetries)); then 36 | touch $streamerNoPort/adbCommunicationFail 37 | echo "Communication with $streamerIP failed after $adbMaxRetries retries" 38 | exit 1 39 | fi 40 | 41 | sleep 1 42 | ((adbCounter++)) 43 | done 44 | } 45 | 46 | adbWake() { 47 | packageLaunch="com.clientapp.MainActivity" 48 | packageName="com.att.tv" 49 | packagePID=$($adbTarget shell pidof $packageName) 50 | 51 | if [ ! -z $packagePID ]; then 52 | #$adbTarget shell input keyevent KEYCODE_WAKEUP 53 | $adbTarget shell am start -n $packageName/$packageLaunch 54 | echo "Confirming $packageName on $streamerIP" 55 | touch $streamerNoPort/adbAppRunning 56 | else 57 | #$adbTarget shell input keyevent KEYCODE_WAKEUP 58 | $adbTarget shell am start -n $packageName/$packageLaunch 59 | echo "Starting $packageName on $streamerIP" 60 | fi 61 | } 62 | 63 | main() { 64 | adbConnect 65 | adbWake 66 | } 67 | 68 | main 69 | -------------------------------------------------------------------------------- /scripts/firetv/livetv/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #prebmitune.sh for firetv/livetv 3 | 4 | #Debug on if uncommented 5 | #set -x 6 | 7 | specialID="$2" 8 | streamerIP="$1" 9 | streamerNoPort="${streamerIP%%:*}" 10 | adbTarget="adb -s $streamerIP" 11 | 12 | mkdir -p $streamerNoPort 13 | echo "Beginning tuning for $specialID" 14 | 15 | #Trap end of script run 16 | finish() { 17 | echo "prebmitune.sh is exiting for $streamerIP with exit code $?" 18 | } 19 | 20 | trap finish EXIT 21 | 22 | adbConnect() { 23 | adb connect $streamerIP 24 | 25 | local -i adbMaxRetries=25 26 | local -i adbCounter=0 27 | 28 | while true; do 29 | $adbTarget shell input keyevent KEYCODE_WAKEUP 30 | local adbEventSuccess=$? 31 | 32 | if [[ $adbEventSuccess -eq 0 ]]; then 33 | break 34 | fi 35 | 36 | if (($adbCounter > $adbMaxRetries)); then 37 | touch $streamerNoPort/adbCommunicationFail 38 | echo "Communication with $streamerIP failed after $adbMaxRetries retries" 39 | exit 1 40 | fi 41 | 42 | sleep 1 43 | ((adbCounter++)) 44 | done 45 | } 46 | 47 | adbWake() { 48 | packageLaunch="com.clientapp.MainActivity" 49 | packageName="com.att.tv" 50 | packagePID=$($adbTarget shell pidof $packageName) 51 | 52 | if [ ! -z $packagePID ]; then 53 | $adbTarget shell input keyevent KEYCODE_WAKEUP 54 | $adbTarget shell am start -n $packageName/$packageLaunch 55 | echo "Waking $streamerIP" 56 | touch $streamerNoPort/adbAppRunning 57 | else 58 | $adbTarget shell input keyevent KEYCODE_WAKEUP 59 | $adbTarget shell am start -n $packageName/$packageLaunch 60 | echo "Starting $packageName on $streamerIP" 61 | fi 62 | } 63 | 64 | main() { 65 | adbConnect 66 | #adbWake 67 | } 68 | 69 | main 70 | -------------------------------------------------------------------------------- /html/status_and_logs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 34 | 35 | 36 |
37 |

Last updated:

38 |
39 |
40 |
41 | 42 |
43 |
44 | 45 |
46 |
47 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /scripts/chromecast/npo/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #prebmitune.sh for chromecast/npo 3 | 4 | #Debug on if uncommented 5 | set -x 6 | 7 | streamerIP="$1" 8 | streamerNoPort="${streamerIP%%:*}" 9 | adbTarget="adb -s $streamerIP" 10 | 11 | mkdir -p $streamerNoPort 12 | 13 | #Trap end of script run 14 | finish() { 15 | echo "prebmitune.sh is exiting for $streamerIP with exit code $?" 16 | } 17 | 18 | trap finish EXIT 19 | 20 | adbConnect() { 21 | adb connect $streamerIP 22 | 23 | local -i adbMaxRetries=25 24 | local -i adbCounter=0 25 | 26 | while true; do 27 | $adbTarget shell input keyevent KEYCODE_WAKEUP 28 | local adbEventSuccess=$? 29 | 30 | if [[ $adbEventSuccess -eq 0 ]]; then 31 | break 32 | fi 33 | 34 | if (($adbCounter > $adbMaxRetries)); then 35 | touch $streamerNoPort/adbCommunicationFail 36 | echo "Communication with $streamerIP failed after $adbMaxRetries retries" 37 | exit 1 38 | fi 39 | 40 | sleep 1 41 | ((adbCounter++)) 42 | done 43 | } 44 | 45 | adbWake() { 46 | packageLaunch=".tv.presentation.splash.StartupActivity" 47 | packageName="nl.uitzendinggemist" 48 | packagePID=$($adbTarget shell pidof $packageName) 49 | 50 | if [ ! -z $packagePID ]; then 51 | $adbTarget shell input keyevent KEYCODE_WAKEUP 52 | $adbTarget shell am start -a android.intent.action.VIEW -n $packageName/$packageLaunch 53 | echo "Waking $streamerIP" 54 | touch $streamerNoPort/adbAppRunning 55 | else 56 | $adbTarget shell input keyevent KEYCODE_WAKEUP 57 | $adbTarget shell am start -a android.intent.action.VIEW -n $packageName/$packageLaunch 58 | echo "Starting $packageName on $streamerIP" 59 | fi 60 | } 61 | 62 | main() { 63 | adbConnect 64 | adbWake 65 | sleep 10 66 | } 67 | 68 | main 69 | -------------------------------------------------------------------------------- /scripts/firetv/hulu/prebmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2023 Scott Ullrich 4 | # sullrich@gmail.com 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | # this software and associated documentation files (the “Software”), to deal in the 8 | # Software without restriction, including without limitation the rights to use, copy, 9 | # modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 10 | # and to permit persons to whom the Software is furnished to do so, subject to the 11 | # following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 17 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | # 23 | 24 | TUNERIP="$1" 25 | 26 | logger ">>> prebmitune.sh is starting for $TUNERIP" 27 | 28 | . ./scripts/firetv/hulu/common_functions.sh 29 | 30 | function finish { 31 | date 32 | } 33 | 34 | trap finish EXIT 35 | 36 | touch /tmp/$TUNERIP.lock 37 | 38 | adb -s $TUNERIP shell pm trim-caches 9999999999 39 | 40 | is_ip_address $TUNERIP && adb_connect 41 | 42 | # Wake up 43 | adb -s $TUNERIP shell input keyevent 224 44 | 45 | stop_provider 46 | 47 | # Back 48 | adb -s $TUNERIP shell input keyevent 4 49 | 50 | # Home 51 | adb -s $TUNERIP shell input keyevent 3 52 | 53 | -------------------------------------------------------------------------------- /m3u/fubo.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | #EXTINF:-1 channel-id="STZHD" tvc-guide-stationid="34941",Starz HD 3 | http://{{ .IPADDRESS }}/play/tuner/12719 4 | #EXTINF:-1 channel-id="STZKHD" tvc-guide-stationid="57581",Starz Kids & Family HD 5 | http://{{ .IPADDRESS }}/play/tuner/67703 6 | #EXTINF:-1 channel-id="STZCHD" tvc-guide-stationid="57569",Starz Comedy HD 7 | http://{{ .IPADDRESS }}/play/tuner/67691 8 | #EXTINF:-1 channel-id="STZEHD" tvc-guide-stationid="57573",Starz Edge HD 9 | http://{{ .IPADDRESS }}/play/tuner/67695 10 | #EXTINF:-1 channel-id="STRZIBH" tvc-guide-stationid="67235",Starz in Black HD 11 | http://{{ .IPADDRESS }}/play/tuner/78393 12 | #EXTINF:-1 channel-id="STZENHD" tvc-guide-stationid="36225",Starz Encore HD 13 | http://{{ .IPADDRESS }}/play/tuner/36225 14 | #EXTINF:-1 channel-id="SHOWHD" tvc-guide-stationid="21868",Showtime HD 15 | http://{{ .IPADDRESS }}/play/tuner/21868 16 | #EXTINF:-1 channel-id="SHOWHDP" tvc-guide-stationid="22532",Showtime HD West 17 | http://{{ .IPADDRESS }}/play/tuner/22532 18 | #EXTINF:-1 channel-id="SHO2HD" tvc-guide-stationid="58533",Showtime 2 HD 19 | http://{{ .IPADDRESS }}/play/tuner/68704 20 | #EXTINF:-1 channel-id="SHOBETH" tvc-guide-stationid="68340",Showtime BET HD 21 | http://{{ .IPADDRESS }}/play/tuner/79670 22 | #EXTINF:-1 channel-id="SHOXHD" tvc-guide-stationid="60947",Showtime Extreme HD 23 | http://{{ .IPADDRESS }}/play/tuner/71379 24 | #EXTINF:-1 channel-id="SHOCSHD" tvc-guide-stationid="61001",Showtime Showcase HD 25 | http://{{ .IPADDRESS }}/play/tuner/71444 26 | #EXTINF:-1 channel-id="NEXTHD" tvc-guide-stationid="68342",Showtime NextHD 27 | http://{{ .IPADDRESS }}/play/tuner/79672 28 | #EXTINF:-1 channel-id="FAMZHD" tvc-guide-stationid="103892",Showtime Familyzone HD 29 | http://{{ .IPADDRESS }}/play/tuner/117799 30 | #EXTINF:-1 channel-id="WOMNHD" tvc-guide-stationid="68338",Showtime Women 31 | http://{{ .IPADDRESS }}/play/tuner/79668 32 | -------------------------------------------------------------------------------- /scripts/osprey/dtvospreydeeplinks/bmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # bmitune.sh for osprey/dtvospreydeeplinks 3 | # 2025.09.26 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | #Global 9 | channelID=$(echo $1 | awk -F~ '{print $2}') 10 | channelName=$(echo $1 | awk -F~ '{print $1}') 11 | specialID="$channelName" 12 | streamerIP="$2" 13 | streamerNoPort="${streamerIP%%:*}" 14 | adbTarget="adb -s $streamerIP" 15 | [[ $SPEED_MODE == "" ]] && speedMode="true" || speedMode="$SPEED_MODE" 16 | 17 | #Trap end of script run 18 | finish() { 19 | echo "bmitune.sh is exiting for $streamerIP with exit code $?" 20 | } 21 | 22 | trap finish EXIT 23 | 24 | #Set encoderURL based on the value of streamerIP 25 | matchEncoderURL() { 26 | 27 | case "$streamerIP" in 28 | "$TUNER1_IP") 29 | encoderURL=$ENCODER1_URL 30 | ;; 31 | "$TUNER2_IP") 32 | encoderURL=$ENCODER2_URL 33 | ;; 34 | "$TUNER3_IP") 35 | encoderURL=$ENCODER3_URL 36 | ;; 37 | "$TUNER4_IP") 38 | encoderURL=$ENCODER4_URL 39 | ;; 40 | "$TUNER5_IP") 41 | encoderURL=$ENCODER5_URL 42 | ;; 43 | "$TUNER6_IP") 44 | encoderURL=$ENCODER6_URL 45 | ;; 46 | "$TUNER7_IP") 47 | encoderURL=$ENCODER7_URL 48 | ;; 49 | "$TUNER8_IP") 50 | encoderURL=$ENCODER8_URL 51 | ;; 52 | "$TUNER9_IP") 53 | encoderURL=$ENCODER9_URL 54 | ;; 55 | *) 56 | exit 1 57 | ;; 58 | esac 59 | } 60 | 61 | #Tuning is based on channel name/ID values from dtvospreydeeplinks.m3u. 62 | tuneChannel() { 63 | #$adbTarget shell am start -a android.intent.action.VIEW -d https://deeplink.directvnow.com/tune/live/$channelName/$channelID 64 | $adbTarget shell am start -W -a android.intent.action.VIEW -d https://deeplink.directvnow.com/tune/live/channel/$channelName/$channelID com.att.tv.openvideo 65 | } 66 | 67 | main() { 68 | tuneChannel 69 | } 70 | 71 | main 72 | -------------------------------------------------------------------------------- /static/status_logs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 43 | 44 | 45 | 46 | 47 | 48 |
49 |

Last updated:

50 |
51 |
52 | 53 |
54 | 55 |
56 | 57 |
58 | 59 |
60 |
61 | 62 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2023 Scott Ullrich 4 | # sullrich@gmail.com 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | # this software and associated documentation files (the “Software”), to deal in the 8 | # Software without restriction, including without limitation the rights to use, copy, 9 | # modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 10 | # and to permit persons to whom the Software is furnished to do so, subject to the 11 | # following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 17 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | # 23 | 24 | IPADDRESS="10.0.250.69" 25 | STREAMER_APP="scripts/firetv/hulu" 26 | 27 | NUMBER_TUNERS="2" 28 | 29 | ENCODER1_URL="http://10.0.250.110/ts/1_0" 30 | TUNER1_IP="10.0.250.154" 31 | CMD1="" 32 | 33 | ENCODER2_URL="http://10.0.250.145/ts/1_0" 34 | TUNER2_IP="10.0.250.158" 35 | CMD2="" 36 | 37 | ENCODER3_URL="" 38 | TUNER3_IP="" 39 | CMD3="" 40 | 41 | ENCODER4_URL="" 42 | TUNER4_IP="" 43 | CMD4="" 44 | 45 | ENCODER5_URL="" 46 | TUNER5_IP="" 47 | CMD5="" 48 | 49 | export STREAMER_APP ENCODER1_URL TUNER1_IP ENCODER2_URL TUNER2_IP CMD1 CMD2 CMD3 CMD4 CMD5 50 | 51 | if [ -d "m3u/@eaDir" ]; then 52 | echo ">>> Removing m3u/@eaDir" 53 | rm -rf "m3u/@eaDir" 54 | fi 55 | 56 | if [ -d "html/@eaDir" ]; then 57 | echo ">>> Removing html/@eaDir" 58 | rm -rf "html/@eaDir" 59 | fi 60 | 61 | go build . && go run . 62 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module androidhdmi-for-channels 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.3 6 | 7 | require ( 8 | github.com/gin-gonic/gin v1.10.0 9 | github.com/joho/godotenv v1.5.1 10 | github.com/natefinch/lumberjack v2.0.0+incompatible 11 | github.com/shirou/gopsutil v3.21.11+incompatible 12 | ) 13 | 14 | require ( 15 | github.com/BurntSushi/toml v1.3.2 // indirect 16 | github.com/bytedance/sonic v1.13.2 // indirect 17 | github.com/bytedance/sonic/loader v0.2.4 // indirect 18 | github.com/cloudwego/base64x v0.1.5 // indirect 19 | github.com/cloudwego/iasm v0.2.0 // indirect 20 | github.com/gabriel-vasile/mimetype v1.4.8 // indirect 21 | github.com/gin-contrib/sse v1.0.0 // indirect 22 | github.com/go-ole/go-ole v1.3.0 // indirect 23 | github.com/go-playground/locales v0.14.1 // indirect 24 | github.com/go-playground/universal-translator v0.18.1 // indirect 25 | github.com/go-playground/validator/v10 v10.26.0 // indirect 26 | github.com/goccy/go-json v0.10.5 // indirect 27 | github.com/json-iterator/go v1.1.12 // indirect 28 | github.com/klauspost/cpuid/v2 v2.2.10 // indirect 29 | github.com/leodido/go-urn v1.4.0 // indirect 30 | github.com/mattn/go-isatty v0.0.20 // indirect 31 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 32 | github.com/modern-go/reflect2 v1.0.2 // indirect 33 | github.com/pelletier/go-toml/v2 v2.2.3 // indirect 34 | github.com/tklauser/go-sysconf v0.3.15 // indirect 35 | github.com/tklauser/numcpus v0.10.0 // indirect 36 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 37 | github.com/ugorji/go/codec v1.2.12 // indirect 38 | github.com/yusufpapurcu/wmi v1.2.4 // indirect 39 | golang.org/x/arch v0.15.0 // indirect 40 | golang.org/x/crypto v0.36.0 // indirect 41 | golang.org/x/net v0.38.0 // indirect 42 | golang.org/x/sys v0.31.0 // indirect 43 | golang.org/x/text v0.23.0 // indirect 44 | google.golang.org/protobuf v1.36.6 // indirect 45 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect 46 | gopkg.in/yaml.v2 v2.4.0 // indirect 47 | gopkg.in/yaml.v3 v3.0.1 // indirect 48 | ) 49 | -------------------------------------------------------------------------------- /m3u/coachella.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | 3 | #EXTINF:-1 channel-id="Coachella-Theatre" tvc-guide-stationid="4.1" tvg-group="" tvc-guide-placeholders="1800" tvg-logo="https://i.ytimg.com/vi/M7zW11KC1Nk/hqdefault_live.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBvryT7sXTYnNI5nHRwehOyoadtEQ",Coachella Theatre 4 | http://{{ .IPADDRESS }}:7654/play/tuner/youtube__M7zW11KC1Nk 5 | 6 | #EXTINF:-1 channel-id="Coachella-Main-Stage" tvc-guide-stationid="4.2" tvg-group="" tvc-guide-placeholders="1800" tvg-logo="https://i.ytimg.com/vi/iYPwEzMm8i4/hqdefault_live.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBcPo0QpEdc91EHyTUp2sXz6ktrlw",Coachella Main Stage 7 | http://{{ .IPADDRESS }}:7654/play/tuner/youtube__iYPwEzMm8i4 8 | 9 | #EXTINF:-1 channel-id="Coachella-Sahara" tvc-guide-stationid="4.3" tvg-group="" tvc-guide-placeholders="1800" tvg-logo="https://i.ytimg.com/vi/keHf1_peYws/hqdefault_live.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLB-nIU_UtZCh8uYUWNJWD0nBK-rpw",Coachella Sahara 10 | http://{{ .IPADDRESS }}:7654/play/tuner/youtube__keHf1_peYws 11 | 12 | #EXTINF:-1 channel-id="Coachella-Mohave" tvc-guide-stationid="4.4" tvg-group="" tvc-guide-placeholders="1800" tvg-logo="https://i.ytimg.com/vi/qwkyHiPoYY4/hqdefault_live.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCwj9XhmhFBrfAMASYG7OcynXXk0w",Coachella Mohave 13 | http://{{ .IPADDRESS }}:7654/play/tuner/youtube__qwkyHiPoYY4 14 | 15 | #EXTINF:-1 channel-id="Coachella-Gobi" tvc-guide-stationid="4.5" tvg-group="" tvc-guide-placeholders="1800" tvg-logo="https://i.ytimg.com/vi/GSRtPr96hss/hqdefault_live.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAIufahUWJuod-TH4Y16lGbNGQ19g",Coachella Gobi 16 | http://{{ .IPADDRESS }}:7654/play/tuner/youtube__GSRtPr96hss 17 | 18 | #EXTINF:-1 channel-id="Coachella-Yuma" tvc-guide-stationid="4.6" tvg-group="" tvc-guide-placeholders="1800" tvg-logo="https://i.ytimg.com/vi/CN_4koTned0/hqdefault_live.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAgznCBllCMtpz-2y2NbkGd3Xorqg",Coachella Yuma 19 | http://{{ .IPADDRESS }}:7654/play/tuner/youtube__CN_4koTned0 20 | -------------------------------------------------------------------------------- /getting_started.txt: -------------------------------------------------------------------------------- 1 | 2 | Getting started 3 | 4 | What you will need 5 | Working golang install 6 | HDMI Encoder device - I am using a pair of these: https://www.amazon.com/dp/B08FDZ2VNZ?psc=1&ref=ppx_yo2ov_dt_b_product_details 7 | FireStick 4K or Cube device (I am using the cube): https://www.amazon.com/gp/product/B09BZZ3MM7/ref=ppx_yo_dt_b_asin_title_o02_s00?ie=UTF8&psc=1 8 | ChannelsDVR setup and working: https://getchannels.com/ 9 | Hulu Live subscription 10 | 11 | * Install the Android Bridge https://developer.android.com/tools/releases/platform-tools 12 | * Setup the FireStick or cube for debugging https://www.xda-developers.com/how-to-access-developer-options-amazon-fire-tv/ 13 | * Setup the encoder (connect to its default IP address and then change the IP to a address on your local network) 14 | * Plug the encoder into Fire device. Navigate to the encoders webGUI and verify you see Fire device in preview. 15 | * Clone the androidhdmi-for-channels repo to the machine you wish to run it on 16 | * Modify the env file and set your ENCODERX_URL(s) and TUNERX_IP(s). Set the number of encoders you have. 17 | * Start the androidhdmi-for-channels proxy by running ./start.sh - I currently use screen to detach from the running process to keep in background. https://formulae.brew.sh/formula/screen 18 | * Setup the custom channel in channels using the custom_channel.txt. Be sure to modify the IP address in custom_channel.txt to the IP address of the running androidhdmi-for-channels proxy: 19 | ChannelsDVR > Add Source 20 | Nickname > ChannelsHDMI 21 | Stream Format > MPEG-TS 22 | Source > Text 23 | Paste in the custom_channel.txt into the textbox. 24 | Prefer channel-number from M3U 25 | Prefer channel logos from Guide Data 26 | Limit to X streams (the number of encoders you have) 27 | XMLTV Guide Data > Refresh daily 28 | * Navigate to ChannelsDVR > Live TV > Guide. Select the new custom channel source. CLick on a channel and Watch Live to verify it's working. 29 | * If recording networks that also exist in TVE you might want to set the channel ordering to list custom channel 1st if you wish to prefer it recording vs TVE. 30 | * Schedule some content to record and enjoy! 31 | -------------------------------------------------------------------------------- /m3u/directv.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | #EXTINF:-1 channel-id="8" tvc-guide-stationid="25102",KAET PBS 8 3 | http://{{ .IPADDRESS }}:7654/play/tuner/8 4 | #EXTINF:-1 channel-id="10" tvc-guide-stationid="21209",KSAZ FOX 10 5 | http://{{ .IPADDRESS }}:7654/play/tuner/10 6 | #EXTINF:-1 channel-id="12" tvc-guide-stationid="20507",KPNX NBC 12 7 | http://{{ .IPADDRESS }}:7654/play/tuner/12 8 | #EXTINF:-1 channel-id="212" tvc-guide-stationid="45399",NFL Network HD 9 | http://{{ .IPADDRESS }}:7654/play/tuner/212 10 | #EXTINF:-1 channel-id="216" tvc-guide-stationid="45526",NBA TV HD 11 | http://{{ .IPADDRESS }}:7654/play/tuner/216 12 | #EXTINF:-1 channel-id="217" tvc-guide-stationid="60316",Tennis Channel HD 13 | http://{{ .IPADDRESS }}:7654/play/tuner/217 14 | #EXTINF:-1 channel-id="219" tvc-guide-stationid="82547",FOX Sports 1 HD 15 | http://{{ .IPADDRESS }}:7654/play/tuner/219 16 | #EXTINF:-1 channel-id="241" tvc-guide-stationid="59186",Paramount Network HD 17 | http://{{ .IPADDRESS }}:7654/play/tuner/241 18 | #EXTINF:-1 channel-id="242" tvc-guide-stationid="58452",USA Network HD 19 | http://{{ .IPADDRESS }}:7654/play/tuner/242 20 | #EXTINF:-1 channel-id="501" tvc-guide-stationid="19548",HBO HD East 21 | http://{{ .IPADDRESS }}:7654/play/tuner/501 22 | #EXTINF:-1 channel-id="502" tvc-guide-stationid="59368",HBO2 HD East 23 | http://{{ .IPADDRESS }}:7654/play/tuner/502 24 | #EXTINF:-1 channel-id="503" tvc-guide-stationid="59363",HBO Signature HD East 25 | http://{{ .IPADDRESS }}:7654/play/tuner/503 26 | #EXTINF:-1 channel-id="504" tvc-guide-stationid="19566",HBO West HD 27 | http://{{ .IPADDRESS }}:7654/play/tuner/504 28 | #EXTINF:-1 channel-id="505" tvc-guide-stationid="59368",HBO2 West HD 29 | http://{{ .IPADDRESS }}:7654/play/tuner/505 30 | #EXTINF:-1 channel-id="506" tvc-guide-stationid="59839",HBO Comedy HD 31 | http://{{ .IPADDRESS }}:7654/play/tuner/506 32 | #EXTINF:-1 channel-id="507" tvc-guide-stationid="59357",HBO Family East HD 33 | http://{{ .IPADDRESS }}:7654/play/tuner/507 34 | #EXTINF:-1 channel-id="509" tvc-guide-stationid="59845",HBO Zone HD 35 | http://{{ .IPADDRESS }}:7654/play/tuner/509 36 | #EXTINF:-1 channel-id="618" tvc-guide-stationid="59305",FOX Sports 2 HD 37 | http://{{ .IPADDRESS }}:7654/play/tuner/618 -------------------------------------------------------------------------------- /html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AH4C 5 | 6 | 7 | 23 | 24 | 25 | 26 | 32 | 33 |
34 | Activity 35 | Activity & logs 36 | Logs 37 | Edit ENV configuration & tuners 38 | Edit channel M3Us 39 | Show API routes 40 | Show ENV variables and current loaded config 41 | Test alert WebHook 42 | Test alert E-Mail 43 |
44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /yttv_contentid.txt: -------------------------------------------------------------------------------- 1 | 155 smitsonian E7D0KRZIUso?onboard=1 2 | 206 starzencw hwnVcleybjg?onboard=1 3 | 265 fs2hd Cf2Bf4xPJlc?onboard=1 4 | 275 sechd 7WP3gsN-qmA?onboard=1 5 | 277 acchd YaqNgmedD7U?onboard=1 6 | 702 fxxhd RjLLYcT5lto?onboard=1 7 | 717 cbspn rdy7C3fJHGs?onboard=1 8 | 719 espnnews 8Cvt9lqM-38?onboard=1 9 | 720 espnhd xDLamOKnZDc?onboard=1 10 | 721 espn2hd bANYnloGyMw?onboard=1 11 | 723 nflhd _pYg9qMKKIA?onboard=1 12 | 724 golfhd XK34g7QRvGk?onboard=1 13 | 726 nbahd o24XePuCR2k?onboard=1 14 | 728 fs1hd XdHxqZrpD2U?onboard=1&vp=0gEEEgIwAQ%3D%3D 15 | 730 tnthd FVJ2BR5wLlo?onboard=1 16 | 731 tbshd cv3L5_6Xsu8?onboard=1 17 | 732 usahd 8xN3o2PSL5s?onboard=1 18 | 735 dischd U3EVaFmJTZw?onboard=1 19 | 737 hgtvhd OTlnIGqIqXU?onboard=1 20 | 739 aplhd QY2JAQHPM-o?onboard=1 21 | 740 mtthd BlyWlY5-VZE?onboard=1 22 | 741 natgeohd BhVSS8f3IlA?onboard=1 23 | 744 trvlhd EYGYiAeqTB8?onboard=1 24 | 752 syfyhd uSfozGAsGMk?onboard=1 25 | 753 fxhd 6jfXLbqPsiw?onboard=1 26 | 754 paramount aoykbklvI1I?onboard=1 27 | 755 comdedy VMJPZpmRVzs?onboard=1 28 | 761 idhd 9LCLkNBxa9E?onboard=1 29 | 762 bbcam qFL2BsrjfMY?onboard=1 30 | 767 e!hd RyrS0s1rL1s?onboard=1 31 | 769 hallmk utR-O-CS0RY?onboard=1 32 | 771 nick cN5V1eeO3E4?onboard=1 33 | 774 cartoonw OqVooAkQo6I?onboard=1 34 | 775 freeform IIi5K7rTg2k?onboard=1 35 | 776 natgeo DyGBEn7C-Ek?onboard=1 36 | 777 diyhd qP9jx07zHrM?onboard=1 37 | 782 tvland KW_CbfPeqn0?onboard=1 38 | 786 pop urHiO4b1k9w?onboard=1 39 | 788 msnbc q6bWEVqhP8o?onboard=1 40 | 795 amchd FTuQnhIQcLc?onboard=1 41 | 796 hallmkmm 06qhGwqCNTY?onboard=1 42 | 798 mgm h8IZTwUlQL8?onboard=1 43 | 800 stzenc Ocv1zVBv5qk?onboard=1 44 | 801 stzencclass dWygb7uEszs?onboard=1 45 | 802 stzencsus jQiJ4pVpj58?onboard=1 46 | 806 sundance 1P9xDqLSkLk?onboard=1 47 | 810 hbohd gXhB9lmTtZU?onboard=1 48 | 811 hbo2hd Wirvy0sSReI?onboard=1 49 | 812 hbosig 0d-qO3YeD8I?onboard=1 50 | 813 hbofam Y3RTK_FsAwU?onboard=1 51 | 815 hboze 1B7p4BiwzIs?onboard=1 52 | 818 hbocomedy Dm_Boi2AqHw?onboard=1 53 | 842 stzhd -Y5vv28gO_I?onboard=1 54 | 843 stzedge 0aFRvqZJMw8?onboard=1 55 | 844 stzhdw Dpm6GnzDtdI?onboard=1 56 | 845 stzblk XT6u3OnJWgs?onboard=1 57 | 846 stzkds&fam O7ROqxJ8g5I?onboard=1 58 | 847 stzcomdedy qsx6OIn4b8M?onboard=1 59 | 848 stzcinema Rjr3Yp9kSWc?onboard=1 60 | 852 univkids Atjp6gLhvmo?onboard=1 61 | 858 nicktoons updG5RVnwk8?onboard=1 62 | 863 fxmhd NyyOdWuSRbA?onboard=1 63 | 890 stzencfam sczwa66ohJk?onboard=1 64 | 891 stzencblk AbeKByFBMLg?onboard=1 65 | 892 stzencact fBCPqOhR8Bc?onboard=1 66 | 894 stzencwesterns 5YCbnUR862E?onboard=1 67 | 895 mgmhits rgYH5PxFG5E?onboard=1 68 | 896 mgmmarquee q9slnpBHDcc?onboard=1 69 | 897 mgmdriveIn fOLjvUIs_Bo?onboard=1 70 | -------------------------------------------------------------------------------- /scripts/shieldtv/youtubetv/yttv_contentid.txt: -------------------------------------------------------------------------------- 1 | 155 smitsonian E7D0KRZIUso?onboard=1 2 | 206 starzencw hwnVcleybjg?onboard=1 3 | 265 fs2hd Cf2Bf4xPJlc?onboard=1 4 | 275 sechd 7WP3gsN-qmA?onboard=1 5 | 277 acchd YaqNgmedD7U?onboard=1 6 | 702 fxxhd RjLLYcT5lto?onboard=1 7 | 717 cbspn rdy7C3fJHGs?onboard=1 8 | 719 espnnews 8Cvt9lqM-38?onboard=1 9 | 720 espnhd xDLamOKnZDc?onboard=1 10 | 721 espn2hd bANYnloGyMw?onboard=1 11 | 723 nflhd _pYg9qMKKIA?onboard=1 12 | 724 golfhd XK34g7QRvGk?onboard=1 13 | 726 nbahd o24XePuCR2k?onboard=1 14 | 728 fs1hd XdHxqZrpD2U?onboard=1&vp=0gEEEgIwAQ%3D%3D 15 | 730 tnthd FVJ2BR5wLlo?onboard=1 16 | 731 tbshd cv3L5_6Xsu8?onboard=1 17 | 732 usahd 8xN3o2PSL5s?onboard=1 18 | 735 dischd U3EVaFmJTZw?onboard=1 19 | 737 hgtvhd OTlnIGqIqXU?onboard=1 20 | 739 aplhd QY2JAQHPM-o?onboard=1 21 | 740 mtthd BlyWlY5-VZE?onboard=1 22 | 741 natgeohd BhVSS8f3IlA?onboard=1 23 | 744 trvlhd EYGYiAeqTB8?onboard=1 24 | 752 syfyhd uSfozGAsGMk?onboard=1 25 | 753 fxhd 6jfXLbqPsiw?onboard=1 26 | 754 paramount aoykbklvI1I?onboard=1 27 | 755 comdedy VMJPZpmRVzs?onboard=1 28 | 761 idhd 9LCLkNBxa9E?onboard=1 29 | 762 bbcam qFL2BsrjfMY?onboard=1 30 | 767 e!hd RyrS0s1rL1s?onboard=1 31 | 769 hallmk utR-O-CS0RY?onboard=1 32 | 771 nick cN5V1eeO3E4?onboard=1 33 | 774 cartoonw OqVooAkQo6I?onboard=1 34 | 775 freeform IIi5K7rTg2k?onboard=1 35 | 776 natgeo DyGBEn7C-Ek?onboard=1 36 | 777 diyhd qP9jx07zHrM?onboard=1 37 | 782 tvland KW_CbfPeqn0?onboard=1 38 | 786 pop urHiO4b1k9w?onboard=1 39 | 788 msnbc q6bWEVqhP8o?onboard=1 40 | 795 amchd FTuQnhIQcLc?onboard=1 41 | 796 hallmkmm 06qhGwqCNTY?onboard=1 42 | 798 mgm h8IZTwUlQL8?onboard=1 43 | 800 stzenc Ocv1zVBv5qk?onboard=1 44 | 801 stzencclass dWygb7uEszs?onboard=1 45 | 802 stzencsus jQiJ4pVpj58?onboard=1 46 | 806 sundance 1P9xDqLSkLk?onboard=1 47 | 810 hbohd gXhB9lmTtZU?onboard=1 48 | 811 hbo2hd Wirvy0sSReI?onboard=1 49 | 812 hbosig 0d-qO3YeD8I?onboard=1 50 | 813 hbofam Y3RTK_FsAwU?onboard=1 51 | 815 hboze 1B7p4BiwzIs?onboard=1 52 | 818 hbocomedy Dm_Boi2AqHw?onboard=1 53 | 842 stzhd -Y5vv28gO_I?onboard=1 54 | 843 stzedge 0aFRvqZJMw8?onboard=1 55 | 844 stzhdw Dpm6GnzDtdI?onboard=1 56 | 845 stzblk XT6u3OnJWgs?onboard=1 57 | 846 stzkds&fam O7ROqxJ8g5I?onboard=1 58 | 847 stzcomdedy qsx6OIn4b8M?onboard=1 59 | 848 stzcinema Rjr3Yp9kSWc?onboard=1 60 | 852 univkids Atjp6gLhvmo?onboard=1 61 | 858 nicktoons updG5RVnwk8?onboard=1 62 | 863 fxmhd NyyOdWuSRbA?onboard=1 63 | 890 stzencfam sczwa66ohJk?onboard=1 64 | 891 stzencblk AbeKByFBMLg?onboard=1 65 | 892 stzencact fBCPqOhR8Bc?onboard=1 66 | 894 stzencwesterns 5YCbnUR862E?onboard=1 67 | 895 mgmhits rgYH5PxFG5E?onboard=1 68 | 896 mgmmarquee q9slnpBHDcc?onboard=1 69 | 897 mgmdriveIn fOLjvUIs_Bo?onboard=1 70 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | #docker buildx build --platform linux/amd64,linux/arm64 -f Dockerfile -t bnhf/ah4c:latest -t bnhf/ah4c:2025.08.31 . --push --no-cache 2 | 3 | # First Stage: Build ws-scrcpy and ah4c 4 | FROM golang:bookworm AS builder 5 | 6 | ARG TARGETARCH 7 | ENV DEBIAN_FRONTEND=noninteractive 8 | 9 | # Install dependencies 10 | RUN apt-get update && apt-get install -y --no-install-recommends \ 11 | git nodejs npm python3 make g++ \ 12 | && rm -rf /var/lib/apt/lists/* 13 | 14 | # Build ws-scrcpy application 15 | WORKDIR /ws-scrcpy 16 | RUN git clone https://github.com/NetrisTV/ws-scrcpy.git . \ 17 | && npm install && npm run dist 18 | 19 | WORKDIR /ws-scrcpy/dist 20 | RUN npm install 21 | 22 | # Build ah4c application 23 | WORKDIR /go/src/github.com/sullrich 24 | RUN git clone https://github.com/sullrich/ah4c . \ 25 | && go build -o /opt/ah4c 26 | 27 | # Second Stage: Create the Runtime Environment 28 | FROM debian:bookworm-slim AS runner 29 | LABEL maintainer="The Slayer " 30 | 31 | ARG TARGETARCH 32 | ENV DEBIAN_FRONTEND=noninteractive 33 | 34 | # Add contrib/non-free/non-free-firmware components 35 | RUN sed -i 's/^Components: .*/Components: main contrib non-free non-free-firmware/' /etc/apt/sources.list.d/debian.sources 36 | 37 | # Install runtime dependencies 38 | RUN apt-get update && apt-get install -y --no-install-recommends \ 39 | ca-certificates curl bash dnsutils procps nano tzdata jq bc \ 40 | android-tools-adb tesseract-ocr \ 41 | nodejs npm \ 42 | ffmpeg libva2 libva-drm2 vainfo \ 43 | && rm -rf /var/lib/apt/lists/* 44 | 45 | # Add Intel VA driver & (optionally) QSV libs only on amd64 46 | RUN if [ "$TARGETARCH" = "amd64" ]; then \ 47 | apt-get update && apt-get install -y --no-install-recommends \ 48 | intel-media-va-driver-non-free libmfx1 && \ 49 | rm -rf /var/lib/apt/lists/* ; \ 50 | fi 51 | 52 | # (Optional) set for Intel VA driver name 53 | ENV LIBVA_DRIVER_NAME=iHD 54 | 55 | # Set up working directories 56 | RUN mkdir -p /opt/scripts /tmp/scripts /tmp/m3u /opt/html /opt/static 57 | 58 | WORKDIR /opt 59 | 60 | # Copy built files from builder 61 | COPY --from=builder /ws-scrcpy/dist /opt/ws-scrcpy 62 | COPY --from=builder /opt/ah4c /opt/ah4c 63 | 64 | # Copy necessary scripts and static files 65 | COPY docker-start.sh adbpackages.sh /opt/ 66 | COPY scripts /tmp/scripts/ 67 | COPY m3u/* /tmp/m3u/ 68 | COPY html/* /opt/html/ 69 | COPY static /opt/static/ 70 | 71 | # Ensure start script is executable 72 | RUN chmod +x /opt/docker-start.sh \ 73 | && groupadd render || true 74 | 75 | # Expose needed ports 76 | EXPOSE 7654 8000 77 | 78 | # Run start script 79 | CMD ["./docker-start.sh"] 80 | -------------------------------------------------------------------------------- /scripts/firetv/hulu/keep_alive.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2023 Scott Ullrich 4 | # sullrich@gmail.com 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | # this software and associated documentation files (the “Software”), to deal in the 8 | # Software without restriction, including without limitation the rights to use, copy, 9 | # modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 10 | # and to permit persons to whom the Software is furnished to do so, subject to the 11 | # following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 17 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | # 23 | 24 | STATION="" 25 | TUNERIP="" 26 | PROVIDER="" 27 | CONTENT_ID="" 28 | 29 | function finish { 30 | date 31 | echo "keep_alive.sh is exiting." 32 | } 33 | 34 | trap finish EXIT 35 | 36 | declare -i counter=0 37 | declare -i failcounter=0 38 | declare -i i=0 39 | declare -i numTuners=0 40 | 41 | # Array to hold the encoders 42 | declare -a tunerArray 43 | declare -a tunerDeviceArray 44 | declare -a tunerURLArray 45 | 46 | if [ -f './env' ]; then 47 | source ./env 48 | # Read each line in the file 49 | while IFS= read -r line; do 50 | # Check if the line contains a variable assignment 51 | if [[ $line == *=* ]]; then 52 | # Extract the variable name and value 53 | varName="${line%%=*}" 54 | varValue="${line#*=}" 55 | # Remove the quotes around the variable value if any 56 | varValue=$(echo $varValue | tr -d '"') 57 | # Export the variable 58 | export "$varName=$varValue" 59 | fi 60 | done < ./env 61 | else 62 | logger "[WARNING] Could not locate ./env. Docker users can ignore this warning." 63 | fi 64 | 65 | # Get the number of tuners 66 | numTuners=$NUMBER_TUNERS 67 | 68 | . ./scripts/firetv/hulu/common_functions.sh 69 | 70 | # Loop through the tuner environment variables 71 | for i in $(seq 1 $numTuners); do 72 | varName="TUNER${i}_IP" 73 | cmdvarName="CMD${i}_DEVICE" 74 | encoderVarName="ENCODER${i}_URL" 75 | TUNERIP=${!varName} 76 | CMDDEVICE=${!cmdvarName} 77 | ENCODERURL=${!encoderVarName} 78 | tuneripArray+=($TUNERIP) 79 | tunerDeviceArray+=($CMDDEVICE) 80 | tunerURLArray+=($ENCODERURL) 81 | done 82 | 83 | if [ "$i" -lt 1 ]; then 84 | echo "Could not find ENV variables describing tuners." 85 | exit 1 86 | fi 87 | 88 | while [ /bin/true ]; do 89 | echo "" 90 | date 91 | for index in ${!tuneripArray[@]}; do 92 | ip=${tuneripArray[$index]} 93 | device=${tunerDeviceArray[$index]} 94 | encoderurl=${tunerURLArray[$index]} 95 | if ((counter > 3600)); then 96 | keepalive $ip $device 97 | counter=0 98 | fi 99 | if [ "$encoderurl" != "" ]; then 100 | check $ip $encoderurl 101 | fi 102 | if [ "$device" != "" ]; then 103 | check $ip $device 104 | fi 105 | ((counter++)) 106 | TIMEA=$(/bin/date "+%H:%M") 107 | if [ "$TIMEA" = "05:00" ]; then 108 | rebootall 109 | fi 110 | done 111 | sleep 5 112 | done 113 | 114 | 115 | -------------------------------------------------------------------------------- /scripts/firetv/hulu/bmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2023 Scott Ullrich 4 | # sullrich@gmail.com 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | # this software and associated documentation files (the “Software”), to deal in the 8 | # Software without restriction, including without limitation the rights to use, copy, 9 | # modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 10 | # and to permit persons to whom the Software is furnished to do so, subject to the 11 | # following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 17 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | # 23 | 24 | echo "$1" > /tmp/temp.txt 25 | echo "$2" >> /tmp/temp.txt 26 | echo "$3" >> /tmp/temp.txt 27 | 28 | STATION="$1" 29 | TUNERIP="$2" 30 | CONTENT_FILE="hulu_contentid.txt" 31 | CONTENT_ID="" 32 | URL="" 33 | PROVIDER="" 34 | STATUS="notplaying" 35 | ADBSTATUS="" 36 | PID="" 37 | RESULT="" 38 | EXE="" 39 | ISPKG="" 40 | WHICH_PROVIDER="" 41 | 42 | declare -i COUNTER=0 43 | declare -i FAILSAFE=0 44 | declare -i GIVEUP=0 45 | declare -i REBOOTCOUNTER=0 46 | declare -i ADBCOUNTER=0 47 | declare -i MS=0 48 | 49 | logger "[STARTING] bmitune.sh $STATION $TUNERIP" 50 | 51 | function finish { 52 | rm -f /tmp/$TUNERIP.lock 53 | logger "[FINISH] bmitune.sh is ending for $TUNERIP" 54 | } 55 | 56 | trap finish EXIT 57 | 58 | . ./scripts/firetv/hulu/common_functions.sh 59 | 60 | is_ip_address $TUNERIP && adb_connect 61 | 62 | setup_provider 63 | 64 | # Check if CONTENT_ID is empty 65 | if [ -z "$CONTENT_ID" ]; then 66 | logger "[ERROR] Invalid option or CONTENT_ID not found in $CONTENT_FILE" 67 | rm -f /tmp/$TUNERIP.* 68 | exit 1 69 | fi 70 | 71 | # Start Youtube or Hulu 72 | start_provider 73 | 74 | # Send media intent URL 75 | tunein 76 | 77 | /bin/echo -n "[WAITING] for stream to start..." 78 | while [ "$STATUS" == "notplaying" ]; do 79 | sleep 1 80 | if is_media_playing; then 81 | STATUS="playing" 82 | /bin/echo "" 83 | logger "[SUCCESS] Stream $CONTENT_ID has started." 84 | else 85 | /bin/echo -n "." 86 | if ((COUNTER > 30)); then 87 | ((FAILSAFE++)) 88 | /bin/echo "" 89 | logger "[TIMEOUT] Stream timeout. Killing PID $PID & Sending intent again ($FAILSAFE) " 90 | stop_provider 91 | sleep 1 92 | start_provider 93 | sleep 7 94 | tunein 95 | COUNTER=0 96 | fi 97 | if ((FAILSAFE > 3)); then 98 | /bin/echo "" 99 | logger "[ERROR] Could not stream $CONTENT_ID on $TUNERIP." 100 | /bin/echo -n "[REBOOT] Issuing reboot for $TUNERIP." 101 | adb -s $TUNERIP shell reboot 102 | REBOOTCOUNTER=0 103 | while true; do 104 | if ((REBOOTCOUNTER > 35)); then 105 | /bin/echo "" 106 | break 107 | fi 108 | /bin/echo -n "." 109 | ((REBOOTCOUNTER++)) 110 | sleep 3 111 | done 112 | adb_connect 113 | COUNTER=0 114 | FAILSAFE=0 115 | ((GIVEUP++)) 116 | fi 117 | if ((GIVEUP > 2)); then 118 | logger "[FAIL] Cannot stream after rebooting $TUNERIP Android device. Giving up." 119 | rm /tmp/$TUNERIP.* 120 | exit 1 121 | fi 122 | ((COUNTER++)) 123 | fi 124 | done 125 | -------------------------------------------------------------------------------- /html/logs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Log Viewer 5 | 25 | 26 | 27 | 28 |

Last updated:

29 |
30 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /m3u/livetv.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | 3 | #EXTINF:-1 channel-id="PPLUSLAUGH" tvc-guide-stationid="121815",24/7 Laughs 4 | http://{{ .IPADDRESS }}/play/tuner/PPLUSLAUGH 5 | 6 | #EXTINF:-1 channel-id="5MAXHD" tvc-guide-stationid="59961",5StarMAX East 7 | http://{{ .IPADDRESS }}/play/tuner/5MAXHD 8 | 9 | #EXTINF:-1 channel-id="ACMAXHD" tvc-guide-stationid="59948",ActionMAX HD East 10 | http://{{ .IPADDRESS }}/play/tuner/ACMAXHD 11 | 12 | #EXTINF:-1 channel-id="MAXHD" tvc-guide-stationid="34933",Cinemax HD East 13 | http://{{ .IPADDRESS }}/play/tuner/MAXHD 14 | 15 | #EXTINF:-1 channel-id="MAXHDP" tvc-guide-stationid="35975",Cinemax HD West 16 | http://{{ .IPADDRESS }}/play/tuner/MAXHDP 17 | 18 | #EXTINF:-1 channel-id="SZEAHD" tvc-guide-stationid="72015",Encore Action HD East 19 | http://{{ .IPADDRESS }}/play/tuner/SZEAHD 20 | 21 | #EXTINF:-1 channel-id="STZENHD" tvc-guide-stationid="36225",Encore HD East 22 | http://{{ .IPADDRESS }}/play/tuner/STZENHD 23 | 24 | #EXTINF:-1 channel-id="HBOHD" tvc-guide-stationid="19548",HBO 25 | http://{{ .IPADDRESS }}/play/tuner/HBOHD 26 | 27 | #EXTINF:-1 channel-id="HBO2HD" tvc-guide-stationid="59368",HBO 2 28 | http://{{ .IPADDRESS }}/play/tuner/HBO2HD 29 | 30 | #EXTINF:-1 channel-id="HBOCHD" tvc-guide-stationid="59839",HBO Comedy 31 | http://{{ .IPADDRESS }}/play/tuner/HBOCHD 32 | 33 | #EXTINF:-1 channel-id="HBOFHD" tvc-guide-stationid="59357",HBO Family 34 | http://{{ .IPADDRESS }}/play/tuner/HBOFHD 35 | 36 | #EXTINF:-1 channel-id="HBOSGHD" tvc-guide-stationid="59363",HBO Signature 37 | http://{{ .IPADDRESS }}/play/tuner/HBOSGHD 38 | 39 | #EXTINF:-1 channel-id="HBOHDP" tvc-guide-stationid="19566",HBO West 40 | http://{{ .IPADDRESS }}/play/tuner/HBOHDP 41 | 42 | #EXTINF:-1 channel-id="HBOZHD" tvc-guide-stationid="59845",HBO Zone 43 | http://{{ .IPADDRESS }}/play/tuner/HBOZHD 44 | 45 | #EXTINF:-1 channel-id="MGMHD" tvc-guide-stationid="65669",MGM+ 46 | http://{{ .IPADDRESS }}/play/tuner/MGMHD 47 | 48 | #EXTINF:-1 channel-id="MGMDRHD" tvc-guide-stationid="103828",MGM+ DRIVE-IN 49 | http://{{ .IPADDRESS }}/play/tuner/MGMDRHD 50 | 51 | #EXTINF:-1 channel-id="MGMHTH" tvc-guide-stationid="67929",MGM+ HITS 52 | http://{{ .IPADDRESS }}/play/tuner/MGMHTH 53 | 54 | #EXTINF:-1 channel-id="MGMMRHD" tvc-guide-stationid="74073",MGM+ MARQUEE 55 | http://{{ .IPADDRESS }}/play/tuner/MGMMRHD 56 | 57 | #EXTINF:-1 channel-id="MOMAXHD" tvc-guide-stationid="59373",MoreMAX HD East 58 | http://{{ .IPADDRESS }}/play/tuner/MOMAXHD 59 | 60 | #EXTINF:-1 channel-id="MOVIEHD" tvc-guide-stationid="59963",MovieMAX HD East 61 | http://{{ .IPADDRESS }}/play/tuner/MOVIEHD 62 | 63 | #EXTINF:-1 channel-id="OUTMAXHD" tvc-guide-stationid="59952",OuterMAX HD East 64 | http://{{ .IPADDRESS }}/play/tuner/OUTMAXHD 65 | 66 | #EXTINF:-1 channel-id="SHOWHD" tvc-guide-stationid="21868",Showtime 67 | http://{{ .IPADDRESS }}/play/tuner/SHOWHD 68 | 69 | #EXTINF:-1 channel-id="SHOWHDP" tvc-guide-stationid="22532",Showtime West 70 | http://{{ .IPADDRESS }}/play/tuner/SHOWHDP 71 | 72 | #EXTINF:-1 channel-id="STRZCIH" tvc-guide-stationid="67236",Starz Cinema HD East 73 | http://{{ .IPADDRESS }}/play/tuner/STRZCIH 74 | 75 | #EXTINF:-1 channel-id="STZCHD" tvc-guide-stationid="57569",Starz Comedy HD East 76 | http://{{ .IPADDRESS }}/play/tuner/STZCHD 77 | 78 | #EXTINF:-1 channel-id="STZEHD" tvc-guide-stationid="57573",Starz Edge HD East 79 | http://{{ .IPADDRESS }}/play/tuner/STZEHD 80 | 81 | #EXTINF:-1 channel-id="STZHD" tvc-guide-stationid="34941",Starz HD East 82 | http://{{ .IPADDRESS }}/play/tuner/STZHD 83 | 84 | #EXTINF:-1 channel-id="STZHDP" tvc-guide-stationid="34949",Starz HD West 85 | http://{{ .IPADDRESS }}/play/tuner/STZHDP 86 | 87 | #EXTINF:-1 channel-id="STRZIBH" tvc-guide-stationid="67235",Starz inBlack HD East 88 | http://{{ .IPADDRESS }}/play/tuner/STRZIBH 89 | 90 | #EXTINF:-1 channel-id="STZKHD" tvc-guide-stationid="57581",Starz Kids & Family HD East 91 | http://{{ .IPADDRESS }}/play/tuner/STZKHD 92 | 93 | #EXTINF:-1 channel-id="THMAXHD" tvc-guide-stationid="59954",ThrillerMAX East 94 | http://{{ .IPADDRESS }}/play/tuner/THMAXHD 95 | -------------------------------------------------------------------------------- /m3u/sling.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | #EXTINF:-1 channel-id="STZHD" tvc-guide-stationid="34941",Starz HD 3 | http://{{ .IPADDRESS }}/play/tuner/1a3c345b84b149918e0bad8f797df70a 4 | #EXTINF:-1 channel-id="STZHDP" tvc-guide-stationid="34949",Starz HD West 5 | http://{{ .IPADDRESS }}/play/tuner/8a84458592844ff5849712ef7766fb4e 6 | #EXTINF:-1 channel-id="STZKHD" tvc-guide-stationid="57581",Starz Kids & Family HD 7 | http://{{ .IPADDRESS }}/play/tuner/148a1e391db94edf96bf25e25beb307d 8 | #EXTINF:-1 channel-id="STZCHD" tvc-guide-stationid="57569",Starz Comedy HD 9 | http://{{ .IPADDRESS }}/play/tuner/d96b102ac5c54967bf56b8b57e20c84e 10 | #EXTINF:-1 channel-id="STZEHD" tvc-guide-stationid="57573",Starz Edge HD 11 | http://{{ .IPADDRESS }}/play/tuner/1535e572f07348159d8924d58200690e 12 | #EXTINF:-1 channel-id="STRZIBH" tvc-guide-stationid="67235",Starz in Black HD 13 | http://{{ .IPADDRESS }}/play/tuner/23a6ea3c163a47f390f73900ccc76bc1 14 | #EXTINF:-1 channel-id="STRZCIH" tvc-guide-stationid="67236",Starz Cinema HD 15 | http://{{ .IPADDRESS }}/play/tuner/0eedb59656d243da914fef990a8db903 16 | #EXTINF:-1 channel-id="STZENHD" tvc-guide-stationid="36225",Starz Encore HD 17 | http://{{ .IPADDRESS }}/play/tuner/ed5d0f85928f45249f8a67c9746a80b7 18 | #EXTINF:-1 channel-id="STZENCP" tvc-guide-stationid="17125",Starz Encore West 19 | http://{{ .IPADDRESS }}/play/tuner/a0515fde0c074dc389981e8881874aae 20 | #EXTINF:-1 channel-id="STZENCL" tvc-guide-stationid="14764",Starz Encore Classic 21 | http://{{ .IPADDRESS }}/play/tuner/2230d31d16d541c08e654d4a11863d2c 22 | #EXTINF:-1 channel-id="STZEWSS" tvc-guide-stationid="102906",Starz Encore Westerns 23 | http://{{ .IPADDRESS }}/play/tuner/73ab11e5664241a9811e7e22602bfda9 24 | #EXTINF:-1 channel-id="STZENSU" tvc-guide-stationid="14766",Starz Encore Suspense 25 | http://{{ .IPADDRESS }}/play/tuner/04e4a4f1e0ec4dfba961414197e57d7f 26 | #EXTINF:-1 channel-id="STZENBK" tvc-guide-stationid="14870",Starz Encore Black 27 | http://{{ .IPADDRESS }}/play/tuner/1ec67d324b564a528f6901ed8e09334e 28 | #EXTINF:-1 channel-id="SZEAHD" tvc-guide-stationid="72015",Starz Encore Action HD 29 | http://{{ .IPADDRESS }}/play/tuner/8aed8223a6104b53b24f03555fc933a9 30 | #EXTINF:-1 channel-id="STZENFS" tvc-guide-stationid="102903",Starz Encore Family 31 | http://{{ .IPADDRESS }}/play/tuner/22076462a8124832967e30e0f9f92213 32 | #EXTINF:-1 channel-id="SHOWHD" tvc-guide-stationid="21868",Showtime HD 33 | http://{{ .IPADDRESS }}/play/tuner/7f5f5993457648d68db2fa5d5103cbcc 34 | #EXTINF:-1 channel-id="SHOWHDP" tvc-guide-stationid="22532",Showtime HD West 35 | http://{{ .IPADDRESS }}/play/tuner/f8ee74606d2a47ec8ca14842b2762b9b 36 | #EXTINF:-1 channel-id="SHO2HD" tvc-guide-stationid="58533",Showtime 2 HD 37 | http://{{ .IPADDRESS }}/play/tuner/30b674a6204f4d9b8ec053e0f6381a28 38 | #EXTINF:-1 channel-id="SHOBETH" tvc-guide-stationid="68340",Showtime BET HD 39 | http://{{ .IPADDRESS }}/play/tuner/df9f6f06051d4681938a627be72a4cf9 40 | #EXTINF:-1 channel-id="SHOXHD" tvc-guide-stationid="60947",Showtime Extreme HD 41 | http://{{ .IPADDRESS }}/play/tuner/ea47544f20394a4b83a2c0678c078d66 42 | #EXTINF:-1 channel-id="SHOCSHD" tvc-guide-stationid="61001",Showtime Showcase HD 43 | http://{{ .IPADDRESS }}/play/tuner/b24b20f009e94eb1ad2441a19f98b7ff 44 | #EXTINF:-1 channel-id="NEXTHD" tvc-guide-stationid="68342",Showtime NextHD 45 | http://{{ .IPADDRESS }}/play/tuner/b5540f5fb9b542aeb0f253d9854c174f 46 | #EXTINF:-1 channel-id="FAMZHD" tvc-guide-stationid="103892",Showtime Familyzone HD 47 | http://{{ .IPADDRESS }}/play/tuner/7b21fc1766304e268ef4757d56b3ac44 48 | #EXTINF:-1 channel-id="WOMNHD" tvc-guide-stationid="68338",Showtime Women 49 | http://{{ .IPADDRESS }}/play/tuner/1157b0988f29416485ce768e33f3c396 50 | #EXTINF:-1 channel-id="INDIEHD" tvc-guide-stationid="65795",IndiePlex HD 51 | http://{{ .IPADDRESS }}/play/tuner/7a17fd0c15754658885b368a0b273565 52 | #EXTINF:-1 channel-id="RETROHD" tvc-guide-stationid="65791",RetroPlex HD 53 | http://{{ .IPADDRESS }}/play/tuner/9ad3916eb22d4c169c7713afa5ee0f2b 54 | #EXTINF:-1 channel-id="MPLEXHD" tvc-guide-stationid="83075",MoviePlex HD 55 | http://{{ .IPADDRESS }}/play/tuner/deba2421b6ff44478320b2c60051f1d7 56 | -------------------------------------------------------------------------------- /m3u/dtvstream.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | #EXTINF:-1 channel-id="3" tvc-guide-stationid="19611",CBS-KYW 3 | http://{{ .IPADDRESS }}/play/tuner/3 4 | #EXTINF:-1 channel-id="6" tvc-guide-stationid="19612",ABC-WPVI 5 | http://{{ .IPADDRESS }}/play/tuner/6 6 | #EXTINF:-1 channel-id="10" tvc-guide-stationid="19613",NBC-WCAU 7 | http://{{ .IPADDRESS }}/play/tuner/10 8 | #EXTINF:-1 channel-id="12" tvc-guide-stationid="24114",PBS-WHYY 9 | http://{{ .IPADDRESS }}/play/tuner/12 10 | #EXTINF:-1 channel-id="17" tvc-guide-stationid="32619",CW-WPHL 11 | http://{{ .IPADDRESS }}/play/tuner/17 12 | #EXTINF:-1 channel-id="23" tvc-guide-stationid="43689",PBS-WNJS 13 | http://{{ .IPADDRESS }}/play/tuner/23 14 | #EXTINF:-1 channel-id="29" tvc-guide-stationid="19614",FOX-WTXF-TV 15 | http://{{ .IPADDRESS }}/play/tuner/29 16 | #EXTINF:-1 channel-id="39" tvc-guide-stationid="24783",PBS-WLVT 17 | http://{{ .IPADDRESS }}/play/tuner/39 18 | #EXTINF:-1 channel-id="57" tvc-guide-stationid="34628",WPSG 19 | http://{{ .IPADDRESS }}/play/tuner/57 20 | #EXTINF:-1 channel-id="206" tvc-guide-stationid="32645",ESPN 21 | http://{{ .IPADDRESS }}/play/tuner/206 22 | #EXTINF:-1 channel-id="209" tvc-guide-stationid="45507",ESPN2 23 | http://{{ .IPADDRESS }}/play/tuner/209 24 | #EXTINF:-1 channel-id="211" tvc-guide-stationid="65025",NFL Red Zone 25 | http://{{ .IPADDRESS }}/play/tuner/211 26 | #EXTINF:-1 channel-id="212" tvc-guide-stationid="45399",NFL Network 27 | http://{{ .IPADDRESS }}/play/tuner/212 28 | #EXTINF:-1 channel-id="213" tvc-guide-stationid="62081",MLB Network 29 | http://{{ .IPADDRESS }}/play/tuner/213 30 | #EXTINF:-1 channel-id="213A" tvc-guide-stationid="62085",MLB Network Alternate 31 | http://{{ .IPADDRESS }}/play/tuner/213A 32 | #EXTINF:-1 channel-id="215" tvc-guide-stationid="58690",NHL Network HD 33 | http://{{ .IPADDRESS }}/play/tuner/215 34 | #EXTINF:-1 channel-id="216" tvc-guide-stationid="45526",NBA TV 35 | http://{{ .IPADDRESS }}/play/tuner/216 36 | #EXTINF:-1 channel-id="217" tvc-guide-stationid="60316",Tennis Channel HD 37 | http://{{ .IPADDRESS }}/play/tuner/217 38 | #EXTINF:-1 channel-id="218" tvc-guide-stationid="61854",Golf Channel 39 | http://{{ .IPADDRESS }}/play/tuner/218 40 | #EXTINF:-1 channel-id="219" tvc-guide-stationid="82547",FOX Sports 1 41 | http://{{ .IPADDRESS }}/play/tuner/219 42 | #EXTINF:-1 channel-id="241" tvc-guide-stationid="59186",Paramount TV 43 | http://{{ .IPADDRESS }}/play/tuner/241 44 | #EXTINF:-1 channel-id="242" tvc-guide-stationid="58452",USA Network 45 | http://{{ .IPADDRESS }}/play/tuner/242 46 | #EXTINF:-1 channel-id="244" tvc-guide-stationid="11097",Syfy 47 | http://{{ .IPADDRESS }}/play/tuner/244 48 | #EXTINF:-1 channel-id="256" tvc-guide-stationid="12852",TCM 49 | http://{{ .IPADDRESS }}/play/tuner/256 50 | #EXTINF:-1 channel-id="264" tvc-guide-stationid="18332",BBC America 51 | http://{{ .IPADDRESS }}/play/tuner/264 52 | #EXTINF:-1 channel-id="269" tvc-guide-stationid="14771",HISTORY 53 | http://{{ .IPADDRESS }}/play/tuner/269 54 | #EXTINF:-1 channel-id="274" tvc-guide-stationid="15807",Ovation HD 55 | http://{{ .IPADDRESS }}/play/tuner/274 56 | #EXTINF:-1 channel-id="281" tvc-guide-stationid="31046",MotorTrend 57 | http://{{ .IPADDRESS }}/play/tuner/281 58 | #EXTINF:-1 channel-id="345" tvc-guide-stationid="63717",RFD-TV 59 | http://{{ .IPADDRESS }}/play/tuner/345 60 | #EXTINF:-1 channel-id="501" tvc-guide-stationid="19548",HBO HD East 61 | http://{{ .IPADDRESS }}/play/tuner/501 62 | #EXTINF:-1 channel-id="502" tvc-guide-stationid="59368",HBO2 HD East 63 | http://{{ .IPADDRESS }}/play/tuner/502 64 | #EXTINF:-1 channel-id="503" tvc-guide-stationid="59363",HBO Signature HD East 65 | http://{{ .IPADDRESS }}/play/tuner/503 66 | #EXTINF:-1 channel-id="504" tvc-guide-stationid="19566",HBO West HD 67 | http://{{ .IPADDRESS }}/play/tuner/504 68 | #EXTINF:-1 channel-id="505" tvc-guide-stationid="59368",HBO2 West HD 69 | http://{{ .IPADDRESS }}/play/tuner/505 70 | #EXTINF:-1 channel-id="506" tvc-guide-stationid="59839",HBO Comedy HD 71 | http://{{ .IPADDRESS }}/play/tuner/506 72 | #EXTINF:-1 channel-id="507" tvc-guide-stationid="59357",HBO Family East HD 73 | http://{{ .IPADDRESS }}/play/tuner/507 74 | #EXTINF:-1 channel-id="509" tvc-guide-stationid="59845",HBO Zone HD 75 | http://{{ .IPADDRESS }}/play/tuner/509 76 | #EXTINF:-1 channel-id="618" tvc-guide-stationid="59305",FOX Sports 2 77 | http://{{ .IPADDRESS }}/play/tuner/618 78 | #EXTINF:-1 channel-id="exit" tvc-guide-stationid="",Exit DirecTV 79 | http://{{ .IPADDRESS }}/play/tuner/exit 80 | #EXTINF:-1 channel-id="reboot" tvc-guide-stationid="",Reboot FireTV 81 | http://{{ .IPADDRESS }}/play/tuner/reboot 82 | -------------------------------------------------------------------------------- /scripts/firetv/xfinity/stopbmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # stopbmitune.sh for firetv/xfinity 3 | # 2025.03.14 4 | 5 | #Debug on if uncommented 6 | set -x 7 | 8 | dvr="$CHANNELSIP:8089" 9 | streamerIP="$1" 10 | streamerNoPort="${streamerIP%%:*}" 11 | channelNameID="$2" 12 | adbTarget="adb -s $streamerIP" 13 | packageName="com.xfinity.cloudtvr.tenfoot" 14 | m3uName="${STREAMER_APP#*/*/}.m3u" 15 | m3uChannelID=$(grep -B1 "/play/tuner/$channelNameID" "/opt/m3u/$m3uName" | awk -F 'channel-id="' 'NF>1 {split($2, a, "\""); print a[1]}') 16 | channelNumber=$(curl -s http://$dvr/api/v1/channels | jq -r '.[] | select(.id == "'$m3uChannelID'") | .number') 17 | [[ $SPEED_MODE == "" ]] && speedMode="false" || speedMode="$SPEED_MODE" 18 | read -a autoCropChannels <<< "$AUTOCROP_CHANNELS" 19 | printf "%s\n" "${autoCropChannels[@]}" | grep -qx "$channelNumber" && croppedChannel="true" 20 | 21 | #Set encoderURL based on the value of streamerIP 22 | matchEncoderURL() { 23 | 24 | case "$streamerIP" in 25 | "$TUNER1_IP") encoderURL=$ENCODER1_URL ;; 26 | "$TUNER2_IP") encoderURL=$ENCODER2_URL ;; 27 | "$TUNER3_IP") encoderURL=$ENCODER3_URL ;; 28 | "$TUNER4_IP") encoderURL=$ENCODER4_URL ;; 29 | "$TUNER5_IP") encoderURL=$ENCODER5_URL ;; 30 | "$TUNER6_IP") encoderURL=$ENCODER6_URL ;; 31 | "$TUNER7_IP") encoderURL=$ENCODER7_URL ;; 32 | "$TUNER8_IP") encoderURL=$ENCODER8_URL ;; 33 | "$TUNER9_IP") encoderURL=$ENCODER9_URL ;; 34 | *) exit 1 ;; 35 | esac 36 | } 37 | 38 | linkpiCrop() { 39 | linkpiStreamID=$(echo "$encoderURL" | awk -F'stream' '{print $2}') 40 | linkpiCropLeft="\"$1\"" 41 | linkpiCropRight="\"$2\"" 42 | linkpiCropTop="\"$3\"" 43 | linkpiCropBottom="\"$4\"" 44 | linkpiPasswordMD5=$(echo -n admin | md5sum | awk '{print $1}') 45 | 46 | linkpiJSON=$(curl -v -c linkpi_cookie --digest -u "$LINKPI_USERNAME:admin" "http://$LINKPI_HOSTNAME/link/user/lph_login?username=$LINKPI_USERNAME&passwd=$linkpiPasswordMD5") 47 | lHash=$(echo "$linkpiJSON" | jq -r '.data."L-HASH"') 48 | pHash=$(echo "$linkpiJSON" | jq -r '.data."P-HASH"') 49 | hHash=$(echo "$linkpiJSON" | jq -r '.data."H-HASH"') 50 | 51 | # echo "L-HASH: $lHash" 52 | # echo "P-HASH: $pHash" 53 | # echo "H-HASH: $hHash" 54 | 55 | curl -s --digest -u "$LINKPI_USERNAME:admin" -b linkpi_cookie -X POST -L http://$LINKPI_HOSTNAME/link/encoder/set_cap_chns \ 56 | -H "L-HASH: $lHash" \ 57 | -H "P-HASH: $pHash" \ 58 | -H "H-HASH: $hHash" \ 59 | -H "Content-Type: application/json" \ 60 | -d '[{"id":'$linkpiStreamID', "L":'$linkpiCropLeft', "R":'$linkpiCropRight', "T":'$linkpiCropTop', "B":'$linkpiCropBottom'}]' 61 | 62 | curl -s --digest -u "$LINKPI_USERNAME:admin" -b linkpi_cookie -L http://$LINKPI_HOSTNAME/link/user/lph_logout \ 63 | -H "L-HASH: $lHash" \ 64 | -H "P-HASH: $pHash" \ 65 | -H "H-HASH: $hHash" 66 | } 67 | 68 | #Check if bmitune.sh is done running 69 | bmituneDone() { 70 | bmitunePID=$(<"$streamerNoPort/bmitune_pid") 71 | keepWatchingPID=$(pgrep -f "$streamerNoPort/keep_watching.sh") 72 | #keepWatchingPPID=$(ps -o ppid= -p "$keepWatchingPID") 73 | keepWatchingCPID=$(pgrep -P $keepWatchingPID) 74 | 75 | if [[ $croppedChannel ]]; then 76 | currentAiringPID=$(pgrep -f "$encoderIP/$encoderStreamNumber.sh") 77 | #currentAiringPPID=$(ps -o ppid= -p "$currentAiringPID") 78 | currentAiringCPID=$(pgrep -P $currentAiringPID) 79 | fi 80 | 81 | while ps -p $bmitunePID > /dev/null; do 82 | echo "Waiting for bmitune.sh to complete..." 83 | sleep 2 84 | done 85 | 86 | #[[ $KEEP_WATCHING ]] && kill $keepWatchingPID && pkill -P $keepWatchingCPID 87 | [[ $KEEP_WATCHING ]] && kill $keepWatchingPID $keepWatchingCPID 88 | rm ./$streamerNoPort/keep_watching.sh 89 | #[[ $croppedChannel ]] && linkpiCrop 0 0 0 0 && kill $currentAiringPID && pkill -P $currentAiringCPID 90 | [[ $croppedChannel ]] && linkpiCrop 0 0 0 0 && kill $currentAiringPID $currentAiringCPID 91 | rm ./$encoderIP/$encoderStreamNumber.sh 92 | } 93 | 94 | #Stop stream 95 | adbStop() { 96 | [[ $speedMode == "true" ]] \ 97 | && stop="input keyevent KEYCODE_BACK; \ 98 | input keyevent KEYCODE_HOME" \ 99 | || stop="am force-stop $packageName" 100 | $adbTarget shell $stop; sleep 2 101 | echo "Streaming stopped for $streamerIP" 102 | } 103 | 104 | #Device sleep 105 | adbSleep() { 106 | sleep="input keyevent KEYCODE_SLEEP" 107 | 108 | $adbTarget shell $sleep 109 | echo "Sleep initiated for $streamerIP" 110 | date +%s > $streamerNoPort/stream_stopped 111 | echo "$streamerNoPort/stream_stopped written with epoch stop time" 112 | } 113 | 114 | main() { 115 | [[ croppedChannel ]] && matchEncoderURL && encoderStreamNumber="${encoderURL##*/}" \ 116 | && encoderIP="${encoderURL#*//}" && encoderIP="${encoderIP%%:*}" 117 | bmituneDone 118 | adbStop 119 | adbSleep 120 | } 121 | 122 | main 123 | -------------------------------------------------------------------------------- /html/config.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Config 5 | 6 | 7 | 30 | 62 | 63 | 64 |

Global Variables

65 |
66 |
67 | {{range .EnvVariables}} 68 |
69 | 70 |
71 | 72 |
73 |
74 | {{end}} 75 |
76 |
77 |

Tuners

78 |
79 | {{range .Tuners}} 80 |
81 |
Tuner {{.Number}}
82 |
83 | 84 | 85 |
86 |
87 | 88 | 89 |
90 |
91 | 92 | 93 |
94 |
95 | 96 |
97 |
98 | {{end}} 99 |
100 |
101 | 102 | 103 | 104 |
105 |
106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /ah4c.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | # 2024.10.30 3 | # GitHub home for this project with setup instructions: https://github.com/sullrich/ah4c 4 | # Docker Hub home for this project: https://hub.docker.com/repository/docker/bnhf/ah4c 5 | ah4c: 6 | image: bnhf/ah4c:${TAG} 7 | container_name: ah4c 8 | hostname: ah4c 9 | dns_search: ${DOMAIN} # Specify the name of your LAN's domain, usually local or localdomain 10 | ports: 11 | - ${ADBS_PORT}:5037 # Port used by adb-server 12 | - ${HOST_PORT}:7654 # Port used by this ah4c proxy 13 | - ${WSCR_PORT}:8000 # Port used by ws-scrcpy 14 | environment: 15 | - IPADDRESS=${IPADDRESS} # Hostname or IP address of this ah4c extension to be used in M3U file (also add port number if not in M3U) 16 | - NUMBER_TUNERS=${NUMBER_TUNERS} # Number of tuners you'd like defined 1, 2, 3, 4, 5, 6, 7, 8 or 9 supported 17 | - TUNER1_IP=${TUNER1_IP} # Streaming device #1 with adb port in the form hostname:port or ip:port 18 | - TUNER2_IP=${TUNER2_IP} # Streaming device #2 with adb port in the form hostname:port or ip:port 19 | - TUNER3_IP=${TUNER3_IP} # Streaming device #3 with adb port in the form hostname:port or ip:port 20 | - TUNER4_IP=${TUNER4_IP} # Streaming device #4 with adb port in the form hostname:port or ip:port 21 | - TUNER5_IP=${TUNER5_IP} # Streaming device #5 with adb port in the form hostname:port or ip:port 22 | - TUNER6_IP=${TUNER6_IP} # Streaming device #6 with adb port in the form hostname:port or ip:port 23 | - TUNER7_IP=${TUNER7_IP} # Streaming device #7 with adb port in the form hostname:port or ip:port 24 | - TUNER8_IP=${TUNER8_IP} # Streaming device #8 with adb port in the form hostname:port or ip:port 25 | - TUNER9_IP=${TUNER9_IP} # Streaming device #9 with adb port in the form hostname:port or ip:port 26 | - ENCODER1_URL=${ENCODER1_URL} # Full URL for tuner #1 in the form http://hostname/stream or http://ip/stream 27 | - ENCODER2_URL=${ENCODER2_URL} # Full URL for tuner #2 in the form http://hostname/stream or http://ip/stream 28 | - ENCODER3_URL=${ENCODER3_URL} # Full URL for tuner #3 in the form http://hostname/stream or http://ip/stream 29 | - ENCODER4_URL=${ENCODER4_URL} # Full URL for tuner #4 in the form http://hostname/stream or http://ip/stream 30 | - ENCODER5_URL=${ENCODER5_URL} # Full URL for tuner #5 in the form http://hostname/stream or http://ip/stream 31 | - ENCODER6_URL=${ENCODER6_URL} # Full URL for tuner #6 in the form http://hostname/stream or http://ip/stream 32 | - ENCODER7_URL=${ENCODER7_URL} # Full URL for tuner #7 in the form http://hostname/stream or http://ip/stream 33 | - ENCODER8_URL=${ENCODER8_URL} # Full URL for tuner #8 in the form http://hostname/stream or http://ip/stream 34 | - ENCODER9_URL=${ENCODER9_URL} # Full URL for tuner #9 in the form http://hostname/stream or http://ip/stream 35 | - 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) 36 | - CHANNELSIP=${CHANNELSIP} # Hostname or IP address of the Channels DVR server itself 37 | - 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. 38 | - ALERT_AUTH_SERVER=${ALERT_AUTH_SERVER} # The auth server for the e-mail you'll be using like smtp.gmail.com 39 | - ALERT_EMAIL_FROM=${ALERT_EMAIL_FROM} # The e-mail address you'd like your ah4c failure alert e-mails to show as being from. 40 | - 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. 41 | - ALERT_EMAIL_TO=${ALERT_EMAIL_TO} # The e-mail address you'd like your alert e-mails sent to. 42 | #- ALERT_WEBHOOK_URL="" 43 | - LIVETV_ATTEMPTS=${LIVETV_ATTEMPTS} # For FireTV Live Guide tuning only, set maximum number of attempts at finding the desired channel 44 | - CREATE_M3US=${CREATE_M3US} # Set to true to create device-specific M3Us for use with Amazon Prime Premium channels -- requires a FireTV device 45 | - UPDATE_SCRIPTS=${UPDATE_SCRIPTS} # Set to true if you'd like the sample scripts and STREAMER_APP scripts updated whether they exist or not 46 | - UPDATE_M3US=${UPDATE_M3US} # Set to true if you'd like the sample m3us updated whether they exist or not 47 | - TZ=${TZ} # Your local timezone in Linux "tz" format 48 | - 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). 49 | - 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. 50 | volumes: 51 | - ${HOST_DIR}/ah4c/scripts:/opt/scripts # pre/stop/bmitune.sh scripts will be stored in this bound host directory under streamer/app 52 | - ${HOST_DIR}/ah4c/m3u:/opt/m3u # m3u files will be stored here and hosted at http://:7654/m3u for use in Channels DVR - Custom Channels settings 53 | - ${HOST_DIR}/ah4c/adb:/root/.android # Persistent data directory for adb keys 54 | restart: unless-stopped -------------------------------------------------------------------------------- /scripts/firetv/livetv/createm3u.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | #createm3u.sh for firetv/livetv 3 | 4 | #Debug on if uncommented 5 | #set -x 6 | 7 | #Global 8 | streamerIP="$1" 9 | streamerNoPort="${streamerIP%%:*}" 10 | adbTarget="adb -s $streamerIP" 11 | m3uName="$streamerNoPort.m3u" 12 | 13 | initializeDevice() { 14 | redEcho "Waking $streamerNoPort..." 15 | 16 | $adbTarget shell input keyevent KEYCODE_WAKEUP; sleep 2 17 | $adbTarget shell input keyevent KEYCODE_HOME; sleep 2 18 | $adbTarget logcat -c; sleep 2 19 | $adbTarget shell input keyevent KEYCODE_LIVE_TV; sleep 2 20 | channelID=$($adbTarget shell "input keyevent KEYCODE_LIVE_TV && logcat -d" | grep GuideManager | tail -n 1 | awk -F? '{print$3}') 21 | 22 | while [ -z $channelID ]; do 23 | channelID=$($adbTarget shell "input keyevent KEYCODE_DPAD_UP; input keyevent KEYCODE_DPAD_DOWN; logcat -d" | grep GuideManager | tail -n 1 | awk -F? '{print$3}') 24 | done 25 | 26 | startingChannel=$channelID 27 | } 28 | 29 | sortM3U() { 30 | redEcho "Sorting livetv.m3u alphabetically to match FireTV LiveTV Guide..." 31 | 32 | cp m3u/livetv.m3u m3u/$m3uName 33 | sed -i ':a;N;$!ba;s/\nhttp/ ,http/g' m3u/$m3uName 34 | [ -z "$SORT_M3US" ] || [ "$SORT_M3US" == "true" ] \ 35 | && sort -t',' -k2 -f -o m3u/$m3uName m3u/$m3uName 36 | sed -i '/^$/d' m3u/$m3uName 37 | sed -i 's/ \& / AND /g' m3u/$m3uName 38 | } 39 | 40 | updateM3U() { 41 | redEcho "Reading $m3uName and updating it with device specific channelID..." 42 | echo "Starting channelID is $channelID" 43 | 44 | while IFS= read -r currentLineM3U; do 45 | if [ "$currentLineM3U" != "#EXTM3U" ]; then 46 | echo "$currentLineM3U" | awk -F, '{print "assigned to M3U channel name: " $2 "\n"}' 47 | newLineM3U=$(echo "$currentLineM3U" | sed 's|tuner/.*|tuner/'"$channelID"'|') 48 | sed -i 's|'"$currentLineM3U"'|'"$newLineM3U"'|' m3u/$m3uName 49 | channelID=$($adbTarget "$streamerNoPort/stream_stopped" 27 | [[ -f "$streamerNoPort/last_channel" ]] || echo 0 > "$streamerNoPort/last_channel" 28 | 29 | # Write PID for this script to bmitune_pid for use in stopbmitune.sh 30 | echo $$ > "$streamerNoPort/bmitune_pid" 31 | echo "Current PID for this script is $$" 32 | } 33 | 34 | #Set encoderURL based on the value of streamerIP 35 | matchEncoderURL() { 36 | 37 | case "$streamerIP" in 38 | "$TUNER1_IP") 39 | encoderURL=$ENCODER1_URL 40 | ;; 41 | "$TUNER2_IP") 42 | encoderURL=$ENCODER2_URL 43 | ;; 44 | "$TUNER3_IP") 45 | encoderURL=$ENCODER3_URL 46 | ;; 47 | "$TUNER4_IP") 48 | encoderURL=$ENCODER4_URL 49 | ;; 50 | *) 51 | exit 1 52 | ;; 53 | esac 54 | } 55 | 56 | #Check for active audio stream 57 | activeAudioCheck() { 58 | local startTime=$(date +%s) 59 | local maxDuration=60 60 | local minimumLoudness=-50 61 | local sleepDuration=0.5 62 | 63 | while true; do 64 | checkLoudness=$(ffmpeg -t 1 -i $encoderURL -filter:a ebur128 -map 0:a -f null -hide_banner - 2>&1 | awk '/I: /{print $2}') 65 | 66 | if (( $(date +%s) - $startTime > $maxDuration )); then 67 | echo "Active audio stream not detected in $maxDuration seconds." 68 | exit 1 69 | fi 70 | 71 | if (( $(echo "$checkLoudness > $minimumLoudness" | bc -l) )); then 72 | echo "Active audio stream detected with $checkLoudness LUF." 73 | break 74 | fi 75 | 76 | if appFocusCheck 0; then 77 | echo "Active audio stream not yet detected -- loudness is $checkLoudness LUF. Continuing..." 78 | sleep $sleepDuration 79 | else 80 | echo "No active audio stream detected and app is not in focus after $(($(date +%s) - $startTime)) seconds -- attempting to tune again..." 81 | tuneChannel 82 | fi 83 | 84 | done 85 | } 86 | 87 | appFocusCheck() { 88 | appFocus=$($adbTarget shell dumpsys window windows | grep -E 'mCurrentFocus' | cut -d '/' -f1 | sed 's/.* //g') 89 | 90 | if [[ $appFocus == $packageName ]]; then 91 | return 0 92 | else 93 | return 1 94 | fi 95 | } 96 | 97 | #Special channels to kill DirecTV app or reboot FireStick 98 | specialChannels() { 99 | 100 | if [ $specialID = "exit" ]; then 101 | echo "Exit $packageName requested on $streamerIP" 102 | rm $streamerNoPort/last_channel $streamerNoPort/adbAppRunning 103 | $adbTarget shell am force-stop $packageName 104 | exit 0 105 | elif [ $specialID = "reboot" ]; then 106 | echo "Reboot $streamerIP requested" 107 | rm $streamerNoPort/last_channel $streamerNoPort/adbAppRunning 108 | $adbTarget reboot 109 | exit 0 110 | elif [[ -f $streamerNoPort/adbCommunicationFail ]]; then 111 | rm $streamerNoPort/adbCommunicationFail 112 | exit 1 113 | else 114 | echo "Not a special channel (exit nor reboot)" 115 | #if appFocusCheck; then 116 | #echo "$packageName is the app in focus, OK to tune" 117 | #fi 118 | fi 119 | } 120 | 121 | #Variable delay based on whether app was running or needed to be launched 122 | #and whether less than maxTime seconds (maxTime/3600 for hours) has passed while sleeping 123 | launchDelay() { 124 | local lastChannel 125 | local lastAwake 126 | local timeNow 127 | local timeElapsed 128 | local maxTime=14400 129 | 130 | lastChannel=$(<"$streamerNoPort/last_channel") 131 | lastAwake=$(<"$streamerNoPort/stream_stopped") 132 | timeNow=$(date +%s) 133 | timeElapsed=$(($timeNow - $lastAwake)) 134 | 135 | if (( $lastChannel == $specialID )) && (( $timeElapsed < $maxTime )); then 136 | echo "Last channel selected on this tuner, no channel change required" 137 | exit 0 138 | elif [ -f $streamerNoPort/adbAppRunning ] && (( $timeElapsed < $maxTime )); then 139 | activeAudioCheck 140 | #sleep 14 141 | rm $streamerNoPort/adbAppRunning 142 | echo $specialID > "$streamerNoPort/last_channel" 143 | else 144 | activeAudioCheck 145 | #sleep 32 146 | echo $specialID > "$streamerNoPort/last_channel" 147 | fi 148 | } 149 | 150 | #Tuning is based on channel name values from sling.m3u. 151 | tuneChannel() { 152 | #channelName=$(awk '/channel-id='"$channelID"'/ {getline; print}' m3u/sling.m3u | cut -d'/' -f6) 153 | #channelName=$(echo $channelName | sed 's/^/"/;s/$/"/') 154 | 155 | #livetvMenu="input keyevent KEYCODE_HOME" 156 | 157 | #livetvGuide="input keyevent KEYCODE_LIVE_TV" 158 | 159 | #livetvTune="input keyevent KEYCODE_DPAD_DOWN" 160 | 161 | #$adbTarget shell $livetvMenu 162 | #$adbTarget shell $livetvGuide 163 | #$adbTarget shell $livetvGuide 164 | #$adbTarget shell input text $channelName 165 | #$adbTarget shell $livetvTune 166 | #livetvTune 167 | $adbTarget shell am start -a android.intent.action.VIEW -d https://watch.sling.com/1/channel/$channelID/watch 168 | } 169 | 170 | main() { 171 | updateReferenceFiles 172 | matchEncoderURL 173 | specialChannels 174 | #launchDelay 175 | tuneChannel 176 | activeAudioCheck 177 | } 178 | 179 | main 180 | -------------------------------------------------------------------------------- /scripts/firetv/fubo/bmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #bmitune.sh for firetv/fubo 3 | 4 | #Debug on if uncommented 5 | set -x 6 | 7 | #Global 8 | channelID="$1" 9 | specialID="$1" 10 | streamerIP="$2" 11 | streamerNoPort="${streamerIP%%:*}" 12 | adbTarget="adb -s $streamerIP" 13 | packageName=com.fubo.firetv.screen 14 | 15 | #Trap end of script run 16 | finish() { 17 | echo "bmitune.sh is exiting for $streamerIP with exit code $?" 18 | } 19 | 20 | trap finish EXIT 21 | 22 | updateReferenceFiles() { 23 | 24 | # Handle cases where stream_stopped or last_channel don't exist 25 | mkdir -p $streamerNoPort 26 | [[ -f "$streamerNoPort/stream_stopped" ]] || echo 0 > "$streamerNoPort/stream_stopped" 27 | [[ -f "$streamerNoPort/last_channel" ]] || echo 0 > "$streamerNoPort/last_channel" 28 | 29 | # Write PID for this script to bmitune_pid for use in stopbmitune.sh 30 | echo $$ > "$streamerNoPort/bmitune_pid" 31 | echo "Current PID for this script is $$" 32 | } 33 | 34 | #Set encoderURL based on the value of streamerIP 35 | matchEncoderURL() { 36 | 37 | case "$streamerIP" in 38 | "$TUNER1_IP") 39 | encoderURL=$ENCODER1_URL 40 | ;; 41 | "$TUNER2_IP") 42 | encoderURL=$ENCODER2_URL 43 | ;; 44 | "$TUNER3_IP") 45 | encoderURL=$ENCODER3_URL 46 | ;; 47 | "$TUNER4_IP") 48 | encoderURL=$ENCODER4_URL 49 | ;; 50 | *) 51 | exit 1 52 | ;; 53 | esac 54 | } 55 | 56 | #Check for active audio stream 57 | activeAudioCheck() { 58 | local startTime=$(date +%s) 59 | local maxDuration=60 60 | local minimumLoudness=-50 61 | local sleepDuration=0.5 62 | 63 | while true; do 64 | checkLoudness=$(ffmpeg -t 1 -i $encoderURL -filter:a ebur128 -map 0:a -f null -hide_banner - 2>&1 | awk '/I: /{print $2}') 65 | 66 | if (( $(date +%s) - $startTime > $maxDuration )); then 67 | echo "Active audio stream not detected in $maxDuration seconds." 68 | exit 1 69 | fi 70 | 71 | if (( $(echo "$checkLoudness > $minimumLoudness" | bc -l) )); then 72 | echo "Active audio stream detected with $checkLoudness LUF." 73 | break 74 | fi 75 | 76 | if appFocusCheck 0; then 77 | echo "Active audio stream not yet detected -- loudness is $checkLoudness LUF. Continuing..." 78 | sleep $sleepDuration 79 | else 80 | echo "No active audio stream detected and app is not in focus after $(($(date +%s) - $startTime)) seconds -- attempting to tune again..." 81 | #tuneChannel 82 | $adbTarget shell input keyevent KEYCODE_CENTER 83 | fi 84 | 85 | done 86 | } 87 | 88 | appFocusCheck() { 89 | appFocus=$($adbTarget shell dumpsys window windows | grep -E 'mCurrentFocus' | cut -d '/' -f1 | sed 's/.* //g') 90 | 91 | if [[ $appFocus == $packageName ]]; then 92 | return 0 93 | else 94 | return 1 95 | fi 96 | } 97 | 98 | #Special channels to kill DirecTV app or reboot FireStick 99 | specialChannels() { 100 | 101 | if [ $specialID = "exit" ]; then 102 | echo "Exit $packageName requested on $streamerIP" 103 | rm $streamerNoPort/last_channel $streamerNoPort/adbAppRunning 104 | $adbTarget shell am force-stop $packageName 105 | exit 0 106 | elif [ $specialID = "reboot" ]; then 107 | echo "Reboot $streamerIP requested" 108 | rm $streamerNoPort/last_channel $streamerNoPort/adbAppRunning 109 | $adbTarget reboot 110 | exit 0 111 | elif [[ -f $streamerNoPort/adbCommunicationFail ]]; then 112 | rm $streamerNoPort/adbCommunicationFail 113 | exit 1 114 | else 115 | echo "Not a special channel (exit nor reboot)" 116 | #if appFocusCheck; then 117 | #echo "$packageName is the app in focus, OK to tune" 118 | #fi 119 | fi 120 | } 121 | 122 | #Variable delay based on whether app was running or needed to be launched 123 | #and whether less than maxTime seconds (maxTime/3600 for hours) has passed while sleeping 124 | launchDelay() { 125 | local lastChannel 126 | local lastAwake 127 | local timeNow 128 | local timeElapsed 129 | local maxTime=14400 130 | 131 | lastChannel=$(<"$streamerNoPort/last_channel") 132 | lastAwake=$(<"$streamerNoPort/stream_stopped") 133 | timeNow=$(date +%s) 134 | timeElapsed=$(($timeNow - $lastAwake)) 135 | 136 | if (( $lastChannel == $specialID )) && (( $timeElapsed < $maxTime )); then 137 | echo "Last channel selected on this tuner, no channel change required" 138 | exit 0 139 | elif [ -f $streamerNoPort/adbAppRunning ] && (( $timeElapsed < $maxTime )); then 140 | activeAudioCheck 141 | #sleep 14 142 | rm $streamerNoPort/adbAppRunning 143 | echo $specialID > "$streamerNoPort/last_channel" 144 | else 145 | activeAudioCheck 146 | #sleep 32 147 | echo $specialID > "$streamerNoPort/last_channel" 148 | fi 149 | } 150 | 151 | #Tuning is based on channel name values from fubo.m3u. 152 | tuneChannel() { 153 | #channelName=$(awk '/channel-id='"$channelID"'/ {getline; print}' m3u/fubo.m3u | cut -d'/' -f6) 154 | #channelName=$(echo $channelName | sed 's/^/"/;s/$/"/') 155 | 156 | #livetvMenu="input keyevent KEYCODE_HOME" 157 | 158 | #livetvGuide="input keyevent KEYCODE_LIVE_TV" 159 | 160 | #livetvTune="input keyevent KEYCODE_DPAD_DOWN" 161 | 162 | #$adbTarget shell $livetvMenu 163 | #$adbTarget shell $livetvGuide 164 | #$adbTarget shell $livetvGuide 165 | #$adbTarget shell input text $channelName 166 | #$adbTarget shell $livetvTune 167 | #livetvTune 168 | $adbTarget shell am start -a android.intent.action.VIEW -d https://link.fubo.tv/al1%3Fv%3D1%26a%3Dplay%26t%3Dchannel%26channel_id%3D$channelID 169 | } 170 | 171 | main() { 172 | updateReferenceFiles 173 | matchEncoderURL 174 | specialChannels 175 | #launchDelay 176 | tuneChannel 177 | activeAudioCheck 178 | } 179 | 180 | main 181 | -------------------------------------------------------------------------------- /docker-start-pyatv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # docker-start-pyatv.sh 3 | # 2025.01.22 4 | 5 | #androids=( $TUNER1_IP $TUNER2_IP $TUNER3_IP $TUNER4_IP ) 6 | 7 | # Make tuner hostnames without local domain name resolvable in Alpine containers by adding each to /etc/hosts 8 | fixTunerDNS() { 9 | 10 | local androids=($@) 11 | local resolvFile=/etc/resolv.conf 12 | local hostsFile=/etc/hosts 13 | local localDomain=$(awk '/search/ {print $2}' $resolvFile) 14 | local ipv4Pattern='^([0-9]{1,3}\.){3}[0-9]{1,3}$' 15 | local hostnamePattern='^[a-zA-Z0-9_-]+$' 16 | 17 | for android in "${androids[@]}" 18 | do 19 | local tunerNoPort="${android%%:*}" 20 | 21 | if [[ -n $$android ]]; then 22 | if [[ $tunerNoPort =~ $ipv4Pattern ]]; then 23 | break 24 | elif [[ $tunerNoPort =~ $hostnamePattern ]]; then 25 | tunerIP=$(dig +short $tunerNoPort.$localDomain) 26 | echo "$tunerIP $tunerNoPort" >> $hostsFile 27 | fi 28 | fi 29 | done 30 | } 31 | 32 | # Make encoder hostnames without local domain name resolvable in Alpine containers by adding each to /etc/hosts 33 | fixEncoderDNS() { 34 | 35 | local encoders=($@) 36 | local resolvFile=/etc/resolv.conf 37 | local hostsFile=/etc/hosts 38 | local localDomain=$(awk '/search/ {print $2}' $resolvFile) 39 | local ipv4Pattern='^([0-9]{1,3}\.){3}[0-9]{1,3}$' 40 | local hostnamePattern='^[a-zA-Z0-9_-]+$' 41 | 42 | for encoder in "${encoders[@]}" 43 | do 44 | local encoderNoURL=$(echo "$encoder" | sed -n 's|^.*://\([^/]*\)/.*|\1|p') 45 | 46 | if [[ -n $encoder ]]; then 47 | if [[ $encoderNoURL =~ $ipv4Pattern ]]; then 48 | break 49 | elif [[ $encoderNoURL =~ $hostnamePattern ]]; then 50 | encoderIP=$(dig +short $encoderNoURL.$localDomain) 51 | echo "$encoderIP $encoderNoURL" >> $hostsFile 52 | fi 53 | fi 54 | done 55 | 56 | awk '!a[$0]++' $hostsFile 57 | } 58 | 59 | # List currently connected adb devices and then connect to each indivdually 60 | adbConnections() { 61 | 62 | local androids=($@) 63 | adb devices 64 | 65 | for android in "${androids[@]}" 66 | do 67 | if [[ -n $android ]]; then 68 | adb connect $android 69 | fi 70 | done 71 | } 72 | 73 | # List currently connected atv devices and then connect to each indivdually 74 | atvConnections() { 75 | 76 | local atvs=($@) 77 | 78 | for atv in "${atvs[@]}" 79 | do 80 | if [[ -n $atv ]]; then 81 | atvremote --scan-hosts $atv scan 82 | #atvremote -s $atv --protocol airplay pair 83 | #atvremote -s $atv --protocol companion pair 84 | #atvremote -s $atv --protocol raop pair 85 | fi 86 | done 87 | } 88 | 89 | # Check if a given script is already present in the appropriate scripts directory, and if not, copy it 90 | checkScripts() { 91 | 92 | local scripts=($@) 93 | mkdir -p ./scripts/firetv/directv ./$STREAMER_APP 94 | #scripts=( prebmitune.sh bmitune.sh stopbmitune.sh isconnected.sh keep_alive.sh reboot.sh ) 95 | 96 | for script in "${scripts[@]}" 97 | do 98 | if [ ! -f /opt/scripts/firetv/directv/$script ] && [ -f /tmp/scripts/firetv/directv/$script ] || [[ $UPDATE_SCRIPTS == "true" ]]; then 99 | cp /tmp/scripts/firetv/directv/$script ./scripts/firetv/directv 2>/dev/null \ 100 | && chmod +x ./scripts/firetv/directv/$script \ 101 | && echo "No existing ./scripts/firetv/directv/$script found or UPDATE_SCRIPTS set to true" 102 | else 103 | if [ -f /tmp/scripts/firetv/directv/$script ]; then 104 | echo "Existing ./scripts/firetv/directv/$script found, and will be preserved" 105 | fi 106 | fi 107 | 108 | if [ ! -f /opt/$STREAMER_APP/$script ] && [ -f /tmp/$STREAMER_APP/$script ] || [[ $UPDATE_SCRIPTS == "true" ]]; then 109 | cp /tmp/$STREAMER_APP/$script ./$STREAMER_APP 2>/dev/null \ 110 | && chmod +x ./$STREAMER_APP/$script \ 111 | && echo "No existing ./$STREAMER_APP/$script found or UPDATE_SCRIPTS set to true" 112 | else 113 | if [ -f /tmp/$STREAMER_APP/$script ]; then 114 | echo "Existing ./$STREAMER_APP/$script found, and will be preserved" 115 | fi 116 | fi 117 | done 118 | } 119 | 120 | # Check if a given M3U file is already present in the M3U directory, and if not, copy it 121 | checkM3Us() { 122 | 123 | local m3us=($@) 124 | mkdir -p ./m3u 125 | #m3us=( directv.m3u foo-fighters.m3u hulu.m3u youtubetv.m3u ) 126 | 127 | for m3u in "${m3us[@]}" 128 | do 129 | if [ ! -f /opt/m3u/$m3u ] || [[ $UPDATE_M3US == "true" ]]; then 130 | cp /tmp/m3u/$m3u ./m3u \ 131 | && echo "No existing $m3u found or UPDATE_M3US set to true" 132 | else 133 | echo "Existing $m3u found, and will be preserved" 134 | fi 135 | done 136 | } 137 | 138 | # Create device specific M3Us for use with firetv/livetv channels 139 | createM3Us() { 140 | local androids=($@) 141 | 142 | for android in "${androids[@]}" 143 | do 144 | if [[ -n $android ]] && [[ $CREATE_M3US == "true" ]]; then 145 | adb -s $android shell input keyevent KEYCODE_WAKEUP; sleep 5 146 | adb -s $android shell reboot; sleep 45 147 | $STREAMER_APP/createm3u.sh $android 148 | fi 149 | done 150 | } 151 | 152 | # Fix hostanme resolution, connect adb devices, copy scripts and M3U files as needed, start ws-scrcpy and ah4c 153 | main() { 154 | 155 | fixTunerDNS $TUNER1_IP $TUNER2_IP $TUNER3_IP $TUNER4_IP 156 | fixEncoderDNS $ENCODER1_URL $ENCODER2_URL $ENCODER3_URL $ENCODER4_URL 157 | atvConnections $TUNER1_IP $TUNER2_IP $TUNER3_IP $TUNER4_IP 158 | checkScripts prebmitune.sh bmitune.sh stopbmitune.sh isconnected.sh keep_alive.sh reboot.sh createm3u.sh atvpair.sh 159 | checkM3Us directv.m3u dtvosprey.m3u dtvstream.m3u foo-fighters.m3u fubo.m3u hulu.m3u livetv.m3u npo.m3u silicondust.m3u sling.m3u spectrum.m3u youtubetv_shield.m3u youtubetv.m3u 160 | #createM3Us $TUNER1_IP $TUNER2_IP $TUNER3_IP $TUNER4_IP 161 | [[ -n $USER_SCRIPT ]] && { ./"$USER_SCRIPT" & } || echo "No user-defined custom script to run" 162 | ./ah4c 163 | } 164 | 165 | main 166 | -------------------------------------------------------------------------------- /docker-start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # docker-start.sh 3 | # 2025.09.23 4 | 5 | # Ensure render group can access GPU device 6 | [[ -c /dev/dri/renderD128 ]] && chgrp render /dev/dri/renderD128 7 | 8 | #androids=( $TUNER1_IP $TUNER2_IP $TUNER3_IP $TUNER4_IP ) 9 | #[[ "$STREAMER_APP" == *"/atv/"* ]] && appleTV=true 10 | 11 | # Make tuner hostnames without local domain name resolvable in Alpine containers by adding each to /etc/hosts 12 | fixTunerDNS() { 13 | 14 | local androids=($@) 15 | local resolvFile=/etc/resolv.conf 16 | local hostsFile=/etc/hosts 17 | local localDomain=$(awk '/search/ {print $2}' $resolvFile) 18 | local ipv4Pattern='^([0-9]{1,3}\.){3}[0-9]{1,3}$' 19 | local hostnamePattern='^[a-zA-Z0-9_-]+$' 20 | 21 | for android in "${androids[@]}" 22 | do 23 | local tunerNoPort="${android%%:*}" 24 | 25 | if [[ -n $$android ]]; then 26 | if [[ $tunerNoPort =~ $ipv4Pattern ]]; then 27 | break 28 | elif [[ $tunerNoPort =~ $hostnamePattern ]]; then 29 | tunerIP=$(dig +short $tunerNoPort.$localDomain) 30 | echo "$tunerIP $tunerNoPort" >> $hostsFile 31 | fi 32 | fi 33 | done 34 | } 35 | 36 | # Make encoder hostnames without local domain name resolvable in Alpine containers by adding each to /etc/hosts 37 | fixEncoderDNS() { 38 | 39 | local encoders=($@) 40 | local resolvFile=/etc/resolv.conf 41 | local hostsFile=/etc/hosts 42 | local localDomain=$(awk '/search/ {print $2}' $resolvFile) 43 | local ipv4Pattern='^([0-9]{1,3}\.){3}[0-9]{1,3}$' 44 | local hostnamePattern='^[a-zA-Z0-9_-]+$' 45 | 46 | for encoder in "${encoders[@]}" 47 | do 48 | local encoderNoURL=$(echo "$encoder" | sed -n 's|^.*://\([^/]*\)/.*|\1|p') 49 | 50 | if [[ -n $encoder ]]; then 51 | if [[ $encoderNoURL =~ $ipv4Pattern ]]; then 52 | break 53 | elif [[ $encoderNoURL =~ $hostnamePattern ]]; then 54 | encoderIP=$(dig +short $encoderNoURL.$localDomain) 55 | echo "$encoderIP $encoderNoURL" >> $hostsFile 56 | fi 57 | fi 58 | done 59 | 60 | awk '!a[$0]++' $hostsFile 61 | } 62 | 63 | # List currently connected adb devices and then connect to each indivdually 64 | adbConnections() { 65 | 66 | local androids=($@) 67 | adb devices 68 | 69 | for android in "${androids[@]}" 70 | do 71 | if [[ -n $android ]]; then 72 | adb connect $android 73 | fi 74 | done 75 | } 76 | 77 | # List currently connected atv devices and then connect to each indivdually 78 | atvConnections() { 79 | 80 | local atvs=($@) 81 | 82 | for atv in "${atvs[@]}" 83 | do 84 | if [[ -n $atv ]]; then 85 | atvremote --scan-hosts $atv scan 86 | #atvremote -s $atv --protocol airplay pair 87 | #atvremote -s $atv --protocol companion pair 88 | #atvremote -s $atv --protocol raop pair 89 | fi 90 | done 91 | } 92 | 93 | # Check if a given script is already present in the appropriate scripts directory, and if not, copy it 94 | checkScripts() { 95 | 96 | local scripts=($@) 97 | mkdir -p ./scripts/firetv/directv ./$STREAMER_APP 98 | #scripts=( prebmitune.sh bmitune.sh stopbmitune.sh isconnected.sh keep_alive.sh reboot.sh ) 99 | 100 | for script in "${scripts[@]}" 101 | do 102 | if [ ! -f /opt/scripts/firetv/directv/$script ] && [ -f /tmp/scripts/firetv/directv/$script ] || [[ $UPDATE_SCRIPTS == "true" ]]; then 103 | cp /tmp/scripts/firetv/directv/$script ./scripts/firetv/directv 2>/dev/null \ 104 | && chmod +x ./scripts/firetv/directv/$script \ 105 | && echo "No existing ./scripts/firetv/directv/$script found or UPDATE_SCRIPTS set to true" 106 | else 107 | if [ -f /tmp/scripts/firetv/directv/$script ]; then 108 | echo "Existing ./scripts/firetv/directv/$script found, and will be preserved" 109 | fi 110 | fi 111 | 112 | if [ ! -f /opt/$STREAMER_APP/$script ] && [ -f /tmp/$STREAMER_APP/$script ] || [[ $UPDATE_SCRIPTS == "true" ]]; then 113 | cp /tmp/$STREAMER_APP/$script ./$STREAMER_APP 2>/dev/null \ 114 | && chmod +x ./$STREAMER_APP/$script \ 115 | && echo "No existing ./$STREAMER_APP/$script found or UPDATE_SCRIPTS set to true" 116 | else 117 | if [ -f /tmp/$STREAMER_APP/$script ]; then 118 | echo "Existing ./$STREAMER_APP/$script found, and will be preserved" 119 | fi 120 | fi 121 | done 122 | } 123 | 124 | # Check if a given M3U file is already present in the M3U directory, and if not, copy it 125 | checkM3Us() { 126 | 127 | local m3us=($@) 128 | mkdir -p ./m3u 129 | #m3us=( directv.m3u foo-fighters.m3u hulu.m3u youtubetv.m3u ) 130 | 131 | for m3u in "${m3us[@]}" 132 | do 133 | if [ ! -f /opt/m3u/$m3u ] || [[ $UPDATE_M3US == "true" ]]; then 134 | cp /tmp/m3u/$m3u ./m3u \ 135 | && echo "No existing $m3u found or UPDATE_M3US set to true" 136 | else 137 | echo "Existing $m3u found, and will be preserved" 138 | fi 139 | done 140 | } 141 | 142 | # Create device specific M3Us for use with firetv/livetv channels 143 | createM3Us() { 144 | local androids=($@) 145 | 146 | for android in "${androids[@]}" 147 | do 148 | if [[ -n $android ]] && [[ $CREATE_M3US == "true" ]]; then 149 | adb -s $android shell input keyevent KEYCODE_WAKEUP; sleep 5 150 | adb -s $android shell reboot; sleep 45 151 | $STREAMER_APP/createm3u.sh $android 152 | fi 153 | done 154 | } 155 | 156 | # Fix hostanme resolution, connect adb devices, copy scripts and M3U files as needed, start ws-scrcpy and ah4c 157 | main() { 158 | 159 | fixTunerDNS $TUNER1_IP $TUNER2_IP $TUNER3_IP $TUNER4_IP $TUNER5_IP $TUNER6_IP $TUNER7_IP $TUNER8_IP $TUNER9_IP 160 | fixEncoderDNS $ENCODER1_URL $ENCODER2_URL $ENCODER3_URL $ENCODER4_URL $ENCODER5_URL $ENCODER6_URL $ENCODER7_URL $ENCODER8_URL $ENCODER9_URL 161 | adbConnections $TUNER1_IP $TUNER2_IP $TUNER3_IP $TUNER4_IP $TUNER5_IP $TUNER6_IP $TUNER7_IP $TUNER8_IP $TUNER9_IP 162 | checkScripts prebmitune.sh bmitune.sh stopbmitune.sh isconnected.sh keep_alive.sh reboot.sh createm3u.sh common.sh 163 | checkM3Us allente.m3u channels.m3u coachella.m3u directv.m3u dtvdeeplinks.m3u dtvosprey.m3u dtvstream.m3u dtvstreamdeeplinks.m3u edc.m3u foo-fighters.m3u fubo.m3u hulu.m3u kodifaves-pbs-seatac.m3u livetv.m3u nbc.m3u npo.m3u pbs-seatac.m3u pbs-worcester.m3u silicondust.m3u sling.m3u spectrum.m3u xfinity.m3u youtubetv_shield.m3u youtubetv.m3u zinwell.m3u 164 | createM3Us $TUNER1_IP $TUNER2_IP $TUNER3_IP $TUNER4_IP $TUNER5_IP $TUNER6_IP $TUNER7_IP $TUNER8_IP $TUNER9_IP 165 | [[ -n $USER_SCRIPT ]] && { ./"$USER_SCRIPT" & } || echo "No user-defined custom script to run" 166 | npm start --prefix ws-scrcpy & 167 | ./ah4c 168 | } 169 | 170 | main 171 | -------------------------------------------------------------------------------- /scripts/firetv/directv/bmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #bmitune.sh for firetv/directv 3 | 4 | #Debug on if uncommented 5 | set -x 6 | 7 | #Global 8 | channelID=\""$1\"" 9 | specialID="$1" 10 | streamerIP="$2" 11 | streamerNoPort="${streamerIP%%:*}" 12 | adbTarget="adb -s $streamerIP" 13 | packageName=com.att.tv 14 | m3uName="${STREAMER_APP#*/*/}.m3u" 15 | 16 | #Trap end of script run 17 | finish() { 18 | echo "bmitune.sh is exiting for $streamerIP with exit code $?" 19 | } 20 | 21 | trap finish EXIT 22 | 23 | updateReferenceFiles() { 24 | 25 | # Handle cases where stream_stopped or last_channel don't exist 26 | mkdir -p $streamerNoPort 27 | [[ -f "$streamerNoPort/stream_stopped" ]] || echo 0 > "$streamerNoPort/stream_stopped" 28 | [[ -f "$streamerNoPort/last_channel" ]] || echo 0 > "$streamerNoPort/last_channel" 29 | 30 | # Write PID for this script to bmitune_pid for use in stopbmitune.sh 31 | echo $$ > "$streamerNoPort/bmitune_pid" 32 | echo "Current PID for this script is $$" 33 | } 34 | 35 | #Set encoderURL based on the value of streamerIP 36 | matchEncoderURL() { 37 | 38 | case "$streamerIP" in 39 | "$TUNER1_IP") 40 | encoderURL=$ENCODER1_URL 41 | ;; 42 | "$TUNER2_IP") 43 | encoderURL=$ENCODER2_URL 44 | ;; 45 | "$TUNER3_IP") 46 | encoderURL=$ENCODER3_URL 47 | ;; 48 | "$TUNER4_IP") 49 | encoderURL=$ENCODER4_URL 50 | ;; 51 | *) 52 | exit 1 53 | ;; 54 | esac 55 | } 56 | 57 | #Check for active audio stream with maxDuration, preTuneAudioCheck, sleepBeforeAudioCheck and sleepAfterAudioCheck as arguments 58 | activeAudioCheck() { 59 | local startTime=$(date +%s) 60 | local maxDuration=$1 61 | local minimumLoudness=-50 62 | local sleepBeforeAudioCheck=$3 63 | local sleepAfterAudioCheck=$4 64 | local preTuneAudioCheck=$2 65 | 66 | while true; do 67 | sleep $sleepBeforeAudioCheck 68 | checkLoudness=$(ffmpeg -t 1 -i $encoderURL -filter:a ebur128 -map 0:a -f null -hide_banner - 2>&1 | awk '/I: /{print $2}') 69 | 70 | if (( $(date +%s) - $startTime > $maxDuration )); then 71 | echo "Active audio stream not detected in $maxDuration seconds." 72 | if [ $preTuneAudioCheck = "false" ]; then 73 | echo "Active audio stream not detected after tuning completed" 74 | case "$specialID" in 75 | "212") 76 | echo "Possible sports event blackout on NFL Network, so bumping channel up" 77 | $adbTarget shell input keyevent KEYCODE_DPAD_LEFT 78 | echo 0 > "$streamerNoPort/last_channel" 79 | exit 1 80 | ;; 81 | "213") 82 | echo "Possible sports event blackout on MLB Network, so bumping channel down" 83 | $adbTarget shell input keyevent KEYCODE_DPAD_RIGHT 84 | echo 0 > "$streamerNoPort/last_channel" 85 | exit 1 86 | ;; 87 | *) 88 | echo "Possible sports event blackout, so bumping channel down" 89 | $adbTarget shell input keyevent KEYCODE_DPAD_RIGHT 90 | echo 0 > "$streamerNoPort/last_channel" 91 | exit 1 92 | ;; 93 | esac 94 | else 95 | exit 1 96 | fi 97 | fi 98 | 99 | if (( $(echo "$checkLoudness > $minimumLoudness" | bc -l) )); then 100 | echo "Active audio stream detected with $checkLoudness LUF." 101 | break 102 | fi 103 | 104 | echo "Active audio stream not yet detected -- loudness is $checkLoudness LUF. Continuing..." 105 | sleep $sleepAfterAudioCheck 106 | done 107 | } 108 | 109 | #Special channels to kill DirecTV app or reboot FireStick 110 | specialChannels() { 111 | 112 | if [ $specialID = "exit" ]; then 113 | echo "Exit $packageName requested on $streamerIP" 114 | rm $streamerNoPort/last_channel $streamerNoPort/adbAppRunning 115 | $adbTarget shell am force-stop $packageName 116 | exit 0 117 | elif [ $specialID = "reboot" ]; then 118 | echo "Reboot $streamerIP requested" 119 | rm $streamerNoPort/last_channel $streamerNoPort/adbAppRunning 120 | $adbTarget reboot 121 | exit 0 122 | elif [[ -f $streamerNoPort/adbCommunicationFail ]]; then 123 | rm $streamerNoPort/adbCommunicationFail 124 | exit 1 125 | else 126 | echo "Not a special channel (exit nor reboot)" 127 | appFocus=$($adbTarget shell dumpsys window windows | grep -E 'mCurrentFocus' | cut -d '/' -f1 | sed 's/.* //g') 128 | echo "Current app in focus is $appFocus" 129 | fi 130 | } 131 | 132 | #Variable delay based on whether app was running or needed to be launched 133 | #and whether less than maxTime seconds (maxTime/3600 for hours) has passed while sleeping 134 | launchDelay() { 135 | local lastChannel 136 | local lastAwake 137 | local timeNow 138 | local timeElapsed 139 | local maxTime=14400 140 | 141 | lastChannel=$(<"$streamerNoPort/last_channel") 142 | lastAwake=$(<"$streamerNoPort/stream_stopped") 143 | timeNow=$(date +%s) 144 | timeElapsed=$(($timeNow - $lastAwake)) 145 | 146 | if (( $lastChannel == $specialID )) && (( $timeElapsed < $maxTime )); then 147 | echo "Last channel selected on this tuner, no channel change required" 148 | exit 0 149 | elif [ -f $streamerNoPort/adbAppRunning ] && (( $timeElapsed < $maxTime )); then 150 | activeAudioCheck 42 true 0 1 # (maxDuration, preTuneAudioCheck, sleepBeforeAudioCheck, sleepAfterAudioCheck) 151 | #sleep 14 152 | rm $streamerNoPort/adbAppRunning 153 | echo $specialID > "$streamerNoPort/last_channel" 154 | else 155 | activeAudioCheck 42 true 0 1 # (maxDuration, preTuneAudioCheck, sleepBeforeAudioCheck, sleepAfterAudioCheck) 156 | #sleep 32 157 | echo $specialID > "$streamerNoPort/last_channel" 158 | fi 159 | } 160 | 161 | #Tuning is based on channel name values from $m3uName. 162 | tuneChannel() { 163 | channelName=$(awk -F, '/channel-id='"$channelID"'/ {print $2}' m3u/$m3uName) 164 | channelName=$(echo $channelName | sed 's/^/"/;s/$/"/') 165 | 166 | directvMenu="input keyevent KEYCODE_MENU; \ 167 | input keyevent KEYCODE_MENU; \ 168 | input keyevent KEYCODE_MENU; \ 169 | input keyevent KEYCODE_MENU" 170 | 171 | directvSearch="input keyevent KEYCODE_DPAD_RIGHT; \ 172 | input keyevent KEYCODE_DPAD_RIGHT; \ 173 | input keyevent KEYCODE_DPAD_RIGHT; \ 174 | input keyevent KEYCODE_DPAD_RIGHT; \ 175 | input keyevent KEYCODE_DPAD_DOWN; sleep 3; \ 176 | input keyevent KEYCODE_DPAD_CENTER; sleep 3" 177 | 178 | directvTune="input keyevent KEYCODE_MEDIA_PLAY_PAUSE; sleep 3; \ 179 | input keyevent KEYCODE_DPAD_DOWN; \ 180 | input keyevent KEYCODE_DPAD_DOWN; \ 181 | input keyevent KEYCODE_DPAD_DOWN; \ 182 | input keyevent KEYCODE_DPAD_LEFT; \ 183 | input keyevent KEYCODE_DPAD_CENTER" 184 | 185 | $adbTarget shell $directvMenu 186 | $adbTarget shell $directvSearch 187 | $adbTarget shell input text $channelName 188 | $adbTarget shell $directvTune 189 | } 190 | 191 | main() { 192 | updateReferenceFiles 193 | matchEncoderURL 194 | specialChannels 195 | launchDelay 196 | tuneChannel 197 | activeAudioCheck 24 false 5 1 # (maxDuration, preTuneAudioCheck, sleepBeforeAudioCheck, sleepAfterAudioCheck) 198 | } 199 | 200 | main 201 | -------------------------------------------------------------------------------- /scripts/firetv/dtvstream/bmitune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #bmitune.sh for firetv/directv 3 | 4 | #Debug on if uncommented 5 | set -x 6 | 7 | #Global 8 | channelID=\""$1\"" 9 | specialID="$1" 10 | streamerIP="$2" 11 | streamerNoPort="${streamerIP%%:*}" 12 | adbTarget="adb -s $streamerIP" 13 | packageName=com.att.tv 14 | m3uName="${STREAMER_APP#*/*/}.m3u" 15 | 16 | #Trap end of script run 17 | finish() { 18 | echo "bmitune.sh is exiting for $streamerIP with exit code $?" 19 | } 20 | 21 | trap finish EXIT 22 | 23 | updateReferenceFiles() { 24 | 25 | # Handle cases where stream_stopped or last_channel don't exist 26 | mkdir -p $streamerNoPort 27 | [[ -f "$streamerNoPort/stream_stopped" ]] || echo 0 > "$streamerNoPort/stream_stopped" 28 | [[ -f "$streamerNoPort/last_channel" ]] || echo 0 > "$streamerNoPort/last_channel" 29 | 30 | # Write PID for this script to bmitune_pid for use in stopbmitune.sh 31 | echo $$ > "$streamerNoPort/bmitune_pid" 32 | echo "Current PID for this script is $$" 33 | } 34 | 35 | #Set encoderURL based on the value of streamerIP 36 | matchEncoderURL() { 37 | 38 | case "$streamerIP" in 39 | "$TUNER1_IP") 40 | encoderURL=$ENCODER1_URL 41 | ;; 42 | "$TUNER2_IP") 43 | encoderURL=$ENCODER2_URL 44 | ;; 45 | "$TUNER3_IP") 46 | encoderURL=$ENCODER3_URL 47 | ;; 48 | "$TUNER4_IP") 49 | encoderURL=$ENCODER4_URL 50 | ;; 51 | *) 52 | exit 1 53 | ;; 54 | esac 55 | } 56 | 57 | #Check for active audio stream with maxDuration, preTuneAudioCheck, sleepBeforeAudioCheck and sleepAfterAudioCheck as arguments 58 | activeAudioCheck() { 59 | local startTime=$(date +%s) 60 | local maxDuration=$1 61 | local minimumLoudness=-50 62 | local sleepBeforeAudioCheck=$3 63 | local sleepAfterAudioCheck=$4 64 | local preTuneAudioCheck=$2 65 | 66 | while true; do 67 | sleep $sleepBeforeAudioCheck 68 | checkLoudness=$(ffmpeg -t 1 -i $encoderURL -filter:a ebur128 -map 0:a -f null -hide_banner - 2>&1 | awk '/I: /{print $2}') 69 | 70 | if (( $(date +%s) - $startTime > $maxDuration )); then 71 | echo "Active audio stream not detected in $maxDuration seconds." 72 | if [ $preTuneAudioCheck = "false" ]; then 73 | echo "Active audio stream not detected after tuning completed" 74 | case "$specialID" in 75 | "212") 76 | echo "Possible sports event blackout on NFL Network, so bumping channel up" 77 | $adbTarget shell input keyevent KEYCODE_DPAD_LEFT 78 | echo 0 > "$streamerNoPort/last_channel" 79 | exit 1 80 | ;; 81 | "213") 82 | echo "Possible sports event blackout on MLB Network, so bumping channel down" 83 | $adbTarget shell input keyevent KEYCODE_DPAD_RIGHT 84 | echo 0 > "$streamerNoPort/last_channel" 85 | exit 1 86 | ;; 87 | *) 88 | echo "Possible sports event blackout, so bumping channel down" 89 | $adbTarget shell input keyevent KEYCODE_DPAD_RIGHT 90 | echo 0 > "$streamerNoPort/last_channel" 91 | exit 1 92 | ;; 93 | esac 94 | else 95 | exit 1 96 | fi 97 | fi 98 | 99 | if (( $(echo "$checkLoudness > $minimumLoudness" | bc -l) )); then 100 | echo "Active audio stream detected with $checkLoudness LUF." 101 | break 102 | fi 103 | 104 | echo "Active audio stream not yet detected -- loudness is $checkLoudness LUF. Continuing..." 105 | sleep $sleepAfterAudioCheck 106 | done 107 | } 108 | 109 | #Special channels to kill DirecTV app or reboot FireStick 110 | specialChannels() { 111 | 112 | if [ $specialID = "exit" ]; then 113 | echo "Exit $packageName requested on $streamerIP" 114 | rm $streamerNoPort/last_channel $streamerNoPort/adbAppRunning 115 | $adbTarget shell am force-stop $packageName 116 | exit 0 117 | elif [ $specialID = "reboot" ]; then 118 | echo "Reboot $streamerIP requested" 119 | rm $streamerNoPort/last_channel $streamerNoPort/adbAppRunning 120 | $adbTarget reboot 121 | exit 0 122 | elif [[ -f $streamerNoPort/adbCommunicationFail ]]; then 123 | rm $streamerNoPort/adbCommunicationFail 124 | exit 1 125 | else 126 | echo "Not a special channel (exit nor reboot)" 127 | appFocus=$($adbTarget shell dumpsys window windows | grep -E 'mCurrentFocus' | cut -d '/' -f1 | sed 's/.* //g') 128 | echo "Current app in focus is $appFocus" 129 | fi 130 | } 131 | 132 | #Variable delay based on whether app was running or needed to be launched 133 | #and whether less than maxTime seconds (maxTime/3600 for hours) has passed while sleeping 134 | launchDelay() { 135 | local lastChannel 136 | local lastAwake 137 | local timeNow 138 | local timeElapsed 139 | local maxTime=14400 140 | 141 | lastChannel=$(<"$streamerNoPort/last_channel") 142 | lastAwake=$(<"$streamerNoPort/stream_stopped") 143 | timeNow=$(date +%s) 144 | timeElapsed=$(($timeNow - $lastAwake)) 145 | 146 | if (( $lastChannel == $specialID )) && (( $timeElapsed < $maxTime )); then 147 | echo "Last channel selected on this tuner, no channel change required" 148 | exit 0 149 | elif [ -f $streamerNoPort/adbAppRunning ] && (( $timeElapsed < $maxTime )); then 150 | activeAudioCheck 42 true 0 1 # (maxDuration, preTuneAudioCheck, sleepBeforeAudioCheck, sleepAfterAudioCheck) 151 | #sleep 14 152 | rm $streamerNoPort/adbAppRunning 153 | echo $specialID > "$streamerNoPort/last_channel" 154 | else 155 | activeAudioCheck 42 true 0 1 # (maxDuration, preTuneAudioCheck, sleepBeforeAudioCheck, sleepAfterAudioCheck) 156 | #sleep 32 157 | echo $specialID > "$streamerNoPort/last_channel" 158 | fi 159 | } 160 | 161 | #Tuning is based on channel name values from $m3uName. 162 | tuneChannel() { 163 | channelName=$(awk -F, '/channel-id='"$channelID"'/ {print $2}' m3u/$m3uName) 164 | channelName=$(echo $channelName | sed 's/^/"/;s/$/"/') 165 | numberOfBackspaces=25 166 | clearSearchBackspaces=$(for ((i=0; i<$numberOfBackspaces; i++)); do echo -n " KEYCODE_MEDIA_REWIND"; done) 167 | 168 | directvMenu="input keyevent KEYCODE_MENU; sleep 6" 169 | 170 | directvSearch="input keyevent KEYCODE_DPAD_LEFT; \ 171 | input keyevent KEYCODE_DPAD_UP; \ 172 | input keyevent KEYCODE_DPAD_CENTER; sleep 1; \ 173 | input keyevent KEYCODE_DPAD_CENTER; sleep 1" 174 | 175 | directvClearSearch="input keyevent$clearSearchBackspaces" 176 | 177 | directvTune="input keyevent KEYCODE_MEDIA_PLAY_PAUSE; sleep 1; \ 178 | input keyevent KEYCODE_DPAD_DOWN; \ 179 | input keyevent KEYCODE_DPAD_DOWN; \ 180 | input keyevent KEYCODE_DPAD_DOWN; \ 181 | input keyevent KEYCODE_DPAD_CENTER" 182 | 183 | $adbTarget shell $directvMenu 184 | $adbTarget shell $directvSearch 185 | $adbTarget shell $directvClearSearch 186 | $adbTarget shell input text "$channelName" 187 | $adbTarget shell $directvTune 188 | } 189 | 190 | main() { 191 | updateReferenceFiles 192 | matchEncoderURL 193 | specialChannels 194 | launchDelay 195 | tuneChannel 196 | activeAudioCheck 24 false 5 1 # (maxDuration, preTuneAudioCheck, sleepBeforeAudioCheck, sleepAfterAudioCheck) 197 | } 198 | 199 | main 200 | -------------------------------------------------------------------------------- /html/editm3u.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Edit M3U File 5 | 6 | 7 | 57 | 58 | 59 |

Edit M3U File: {{.filename}}

60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | {{range .entries}} 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 87 | 90 | 91 | {{end}} 92 | 93 |
IdStation IdChannel NumberChannel NameStream LocationGroupLogoActionToggle
{{ .Id }}{{ .StationId }}{{ .ChannelNumber }}{{ .ChannelName }}{{ .StreamURL }}{{ .Group }}{{ .Logo }} 85 | 86 | 88 | 89 |
94 |
95 |
96 | 97 | 98 |
99 |
100 | 101 |
102 |
103 |

Total Channels: {{ len .entries }}

104 | 105 | 106 | 107 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /scripts/chromecast/pbs/README.txt: -------------------------------------------------------------------------------- 1 | Tunes live TV in the PBS app. 2 | 3 | * PBS STREAMING INFRASTRUCTURE 4 | 5 | Most PBS affiliates use the streaming platform operated by the 6 | national PBS umbrella organization, and that's what the PBS app 7 | is for. I think there are a few PBS affiliates that use some 8 | different infrastructure for streaming, and you won't be able 9 | to use the PBS app and these scripts for those affiliates. 10 | 11 | The scripts here are aimed at navigating to a PBS affiliate's 12 | live stream. They don't know anything about on-demand programming, 13 | even though it is available in the same PBS app. 14 | 15 | * SCRIPTS STRUCTURE AND CONFIGURATION 16 | 17 | Our caller, ah4c, expects there to exist 3 scripts: "prebmitune.sh", 18 | "bmitune.sh", and "stopbmitune.sh". For this set of scripts, there are 19 | several common definitions, functions, etc. Each of the expected 20 | scripts is a trivial 3-liner that sources "common.sh" and then calls 21 | the applicable function defined within "common.sh". The intent of that 22 | arrangement is to achive more consistent naming, simpler editing, and 23 | so on. 24 | 25 | At the top of "common.sh" is a collection of variables whose names 26 | start with "CONFIG_". As you might guess, those are things that 27 | conditionally control aspects of the scripts behaviors. If you are 28 | happy with the default values defined in "common.sh", then that's all 29 | you need to know. If you want to change any of them, you can, of 30 | course, just modify "common.sh". A better way is to create a file in 31 | this same directory called "config-local.sh" and provide modified 32 | values for just the items of interest. Copy/paste/modify is the most 33 | reliable way of doing that. The advantage of using "config-local.sh" 34 | is that your changes would not be overwritten by any updates to this 35 | set of scripts. If you don't want to modify any config values, it is 36 | not necessary to create "config-local.sh" at all. 37 | 38 | * TUNING AND STATION SELECTION 39 | 40 | The channel tuning info (the final part of the URLs in the m3u) can be 41 | one of two forms: 42 | 43 | "Waaa" 44 | or 45 | "Waaa_12345_2". 46 | 47 | In both cases, the tag "Waaa" (or whatever) is ignored. It's just 48 | documentation for you, our dear user. Station call sign or marketing 49 | names are good choices. For the second form, the separator character 50 | is underscore, so don't include any underscores in the tag. For URL 51 | reasons, don't include any slashes or spaces or other URL unsafe 52 | characters in any part of the channel tuning info. 53 | 54 | For the first form, live TV is selected with whatever local PBS 55 | channel you have last configured in the app. This is most useful for 56 | the majority of places which are only served by a single PBS affiliate 57 | (or if you only ever watch a single affiliate). 58 | 59 | The second form has a ZIP code used to populate the app's search box 60 | for stations. The last number is a one-based position in the results 61 | list. I'm hoping the results always come back in the same order, but I 62 | have no way to verify that. This is most useful for places served by 2 63 | or more PBS affiliates. There are many places with 2 and a few places 64 | with 3. I don't know if there are any places with more than 3. 65 | 66 | For example, in the Seattle and Tacoma, Washington, area, there are 2 67 | PBS affiliates. If you search for a Seattle area ZIP code, you get 2 68 | results back. So far, I've always seen them come back as KCTS (the 69 | Seattle affiliate) first and KBTC (the Tacoma affiliate) second. You 70 | select which one you want with either "_1" or "_2". You can see this 71 | in the sample M3U pbs-seatac.m3u. There's another example M3U, 72 | pbs-worcester.m3u, for a ZIP code in Worcester, Massachusetts. That 73 | ZIP code search offers a choice of 3 PBS affiliates. 74 | 75 | The tuning script remembers the last station it tuned, so it only goes 76 | through the station search dialog if it needs to change to a different 77 | PBS affiliate (or if a lot of time has passed). 78 | 79 | NOTE: Although the search for affiliates is based on the ZIP code 80 | entered in the search box, the PBS streaming platform does geographic 81 | restrictions based on the source IP address it sees. That's why you 82 | can't watch out-of-area PBS programming. The PBS app will let you 83 | search for any ZIP code and will display the results. Selecting an 84 | out-of-area PBS affiliate in the search results will give an error 85 | message. The code in these scripts doesn't have a way of knowing that 86 | and assumes the station selection went correctly. There's also a small 87 | chance that your ISP's location will be different from your location 88 | for the purposes of PBS restrctions. If you can select it manually in 89 | the PBS app, the scripts can select it. 90 | 91 | * DELAYS 92 | 93 | The scripts work mostly by doing what's called "remote emulation". 94 | That is, they use adb commands to simulate button presses on a remote 95 | control. When a human operates the remote, it's obvious when they can 96 | press more buttons. The scripts mostly don't know when a reaction has 97 | happened on the screen, so they resort to a bunch of fixed delays to 98 | wait until the right time for the next thing. Those delays are a bit 99 | fiddly and depend on your specific equipment, what your equipment is 100 | doing at the moment, and, in some cases, the response time to network 101 | requests over the internet. Phase of the moon might be in there 102 | somewhere. 103 | 104 | Delays are expressed in seconds and need not be limited to whole 105 | numbers. Decimal fraction amounts are acceptable, e.g., "1.345". 106 | 107 | Instead of calling sleep directly, the scripts use the "settle" 108 | function (defined in "common.sh") which scales how much they 109 | sleep. When called, that function alters the requested sleep duration 110 | according to CONFIG_DELAY_SCALING and CONFIG_DELAY_OFFSET. If the 111 | scaling is 1 and the offset is 0 (the defaults), you get exactly the 112 | sleep durations coded into the scripts. If you need proportionally 113 | longer delays, increase the scaling. If you want proportionally 114 | shorter delays, decrease the scaling. If you want to add (or 115 | subtract) a fixed amount to each delay, set the offset to that amount. 116 | If you configure the scaling and offset values to 0, sleep becomes a 117 | no-op (which probably will not be a good choice). 118 | 119 | There are CONFIG_ items for each individual delay. Instead of scaling 120 | or offsetting them across the board, you can adjust them individually. 121 | 122 | The advantage of long delays is that you can be sure your device has 123 | finished reacting to inputs before the script moves on to the next 124 | thing. 125 | 126 | The disadvantage of having overly long delays is that you risk losing 127 | the beginning of real programming. For example, if you are recording a 128 | 30 minute program and you have 1 minute worth of delays, then the 129 | first minute of the recording will be showing this tuning noise 130 | instead of the first minute of the real program. For PBS shows, that 131 | usually doesn't matter since they tend to thank sponsors at the 132 | beginning and sometimes show a preview of the show. However, it's best 133 | to not rely on that and have the delays as short as you can and still 134 | have them tune reliably. Timing is trickiest if you have multiple PBS 135 | affiliates and need to change your local station before recording. 136 | 137 | In my environment, with the default delays, it takes about 30 seconds 138 | to tune when a station change is required. It takes about 12 seconds 139 | when a channel change is not required. Tested on Google Chromecast HD, 140 | Tivo Stream 4k, and Onn 2k Stick. I also tested against a fairly old 141 | Amazon Fire TV stick, though I had to scale the delays a bit for that 142 | to work. 143 | --------------------------------------------------------------------------------