ADBTuner: A "channel tuning" application for networked Google TV / Android TV devices

I would like to test tuning with numbers on a Android sat tuner. Is it possible to have a config.json that always keeps the last channel playing so tuning with numbers always work or would i need to have one config with deeplink start or some ""input keyevent KEYCODE_DPAD_CENTER"," tuning on the first channel?
Could you give me a example config for a simple tune to "ch 1" with number input so i can start testing ? Thanks!

Disney pulling TVE for ESPN was what killed the remaining value of my U-verse TV subscription. Dropped that, picked up ESPN Unlimited for 1/4 the price, set up ADBTuner for the ESPN linear channels, and I'm back where I was. I hope Disney was making more from U-verse than they are from me directly (but I doubt it).

Months ago, I played with doing dynamic events (which would be great for things like WCBB and WCS) using FDL, but the inability for Team Passes to pick them up soured me on the whole idea. I may pick the idea up again, but all the extra channels and stuff don't thrill me; I'm thinking I might need to use FDL with my own middleware to recognize my teams using the Apple team ID and generate events in a M3U for them and somehow record all the events that show up there.

I never considered that for FDL. I like that idea... The apple data is pretty stable - I could probably wire something up in there similiar to how I have leagues/sports already.

1 Like

That would rock.

I actually spent a few minutes earlier in the year sussing out the Apple Team IDs with an eye towards providing a mapping to the Gracenote ones. I was able to ID a large number, but dropped the project when we realized that CDVR wasn't going to support importing the Gracenote Team IDs, but that doesn't mean FDL couldn't filter by Apple Team ID, and I could just record everything from its M3U... I think?

Having all the ESPN lanes annoyed me, I think being able to filter by a team ID and limiting the lanes would be really sweet.

Tuning with numbers Is possible.
But ah4c is really a tool for that. It's already proven with the Osprey scripts; if you take a look, then you can probably adapt it to whatever sat tuner you're using.
ADBTuner is really meant for deep linking. You'll just have to be comfortable with Bash. And I would just take a look at the scripts for the Osprey boxes and modify them slightly to work with what you're trying to do.

Honestly, an AI chatbot can probably figure it out in a few minutes.

Thanks! But just wanted to see if adbtuner can tune faster then my current ah4c setup.

1 Like

With deep linking, probably. Otherwise, I don't believe so, no. I think it'll be pretty much identical. The process is pretty much the same on device.

Yeah , my tuning time is about 4-5 seconds in adbtuner so not to bad, but a dream would make it down to 2 seconds :slight_smile:

1 Like

LOL, I can't even get my HDHRs to do that so thats not gonna happen. I think 5 seconds is pretty good IMO

ADBTuner 20260723-1 no longer records anything from YouTubeTV for me from ONN devices. It seemed to be working fine in July before a long vacation. When I use preview on any channel, I eventually get a black screen showing Please sign in How do I sign in? The remotes for both ONN devices seem to work, but no action is obvious. I've probably forgotten something I did before.

I see these in the log:
2026-09-03 17:09:57.838 - tuning - [Tune BYTuNXhxremmURno6tYsYp] Text match result: Text to match: ["who's watching", 'choose an account', 'edit profiles']. Matches found: None.
2026-09-03 17:09:57.838 - lib.adb - [Tune BYTuNXhxremmURno6tYsYp] On screen text: 'please sign in'

That is less about ADBTuner and more about your devices. For whatever reason, you got signed out of YouTubeTV on your devices. Maybe an app update, or maybe just Google deciding to invalidate a bunch of sessions and force people to log back in. (I suspect they do this from time to time to try and reduce the number of people using shared devices, logins from family members, etc.) This happed on 2 of my 4 devices recently, only Lord Google knows why.

You'll need to either:

  1. Use the device's remote to login on each device (scan the bardcode with your phone, etc etc)
  2. Use one of the ways which allow you to screen share the devices from your PC and login that way

Thanks for that response which was enough to jog my memory. I now remember how I open a video session from the IP address of the EndPoint.so I can use the remote to sign in to YouTube. I got both signed in and working.

Move over to LinkPi thread.

I pushed a new build (20260906-4) to both stable and development this morning.

Compared to the previous development build it's just a few small bug fixes and some polish.

Compared to the last stable build it includes the following changes:

  • Updated admin user interface to make it easier to manage a large amount of tuners and/or channels
  • Added the ability to backup and restore an entire ADBTuner instance
  • Added additional capabilities to Tuning Configurations (in app docs: /ui/configurations/documentation)
  • Added (beta) feature that uses the Android remote interface to automatically re-enable ADB on Android 14 devices that disable ADB on each reboot (ex. CCWGTV). Devices that don't do this are still preferred, but this provides a reasonable path to continue using them.
  • Bug fixes and optimizations

Please let me know if you run into any issues. Thanks!

6 Likes

I’ve been using ADBTuner for quite some time with a single encoder.
As OTT channels evaporate from YouTubeTV, I’m adding a couple more encoders, and rolling every YouTube TV channel into ADBTuner. (Whether they’re available OTT or not)

Question: What are you guys doing for your channel numbering? So far I’ve been trying to follow the channel numbering that comes in from the guide data, but that lead to a channel number that doesn’t really make much sense.

  • Do you guys group all of the news channels together?
  • Do you guys group all of the sports channels together?
  • Do you guys group all of the entertainment channels together?
  • Do you alphabetize?

Just trying to think of some ways to have this make a bit more sense.

A new development build (20260907-9) was released this morning. It adds an activity feed to provide more insight into how well the application is working and to assist with troubleshooting.

Tuning logs are kept for 30 days, but are limited to the last 1,000 for those that may watch a ton of TV. Low resolution screenshots are included, but are automatically disabled if less than 500MB of storage is available in the docker volume.

2 Likes

I pushed a new development version (20260909-2) this morning. It adds a new feature I'm calling Passthrough Channels that will allow other projects to use ADBTuner's tuning resources.

It loads a remote playlist (m3u) and populates virtual ADBTuner channels with the channel fields provided by the remote playlist. These fields can then be used with custom tuning configurations.

This is new and probably needs some more work. If anyone has time to help test that would be appreciated. It works well with FastChannels "bridge" channels.

I wrote up some notes on that here:


4 Likes

I just happened to check my ADBTuner page today and while the tuners are actually working and I'm seeing channels playing through Channels DVR like normal, my latest version of ADBTuner is showing "Failed to Load Tuner Data" and to "Check the container logs for details". I tried restarting the container (I have it installed through portainer on an Ubuntu Server 26.04.1) and I even tried an entire reinstall of the container and same result. I'm attaching a picture of what I'm seeing.


And here is what my logs show:

return cls.__pydantic_validator__.validate_python(

           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^


        obj,


        ^^^^

    ...<5 lines>...

        by_name=by_name,

        ^^^^^^^^^^^^^^^^

    )

    ^

pydantic_core._pydantic_core.ValidationError: 1 validation error for TunerSchema

keep_alive

  Input should be a valid boolean [type=bool_type, input_value=None, input_type=NoneType]

    For further information visit https://errors.pydantic.dev/2.13/v/bool_type

2026-09-08 15:17:09.614 - uvicorn.access - 192.168.1.6:0 - "GET /api/v1/tuners/installed-packages HTTP/1.1" 200

2026-09-08 15:18:17.982 - stream - [Tune n2EKUqA5JN3QwumeeiqyFX] 192.168.1.36:5592 | channels-dvr/2026.09.05.0220 Go-http-client/1.1 (windows-x86_64)

2026-09-08 15:18:17.985 - stream - [Tune (n2EKUqA5JN3QwumeeiqyFX)] No active tuners found for NBC Sports Philadelphia. Reserving a new tuner.

2026-09-08 15:18:17.986 - tuner_management - [Tune n2EKUqA5JN3QwumeeiqyFX] Attempting to reserve tuner for: ['com.google.android.youtube.tvunplugged'].

2026-09-08 15:18:17.991 - tuner_management - [Tune n2EKUqA5JN3QwumeeiqyFX] Available Tuners:

2026-09-08 15:18:17.991 - tuner_management - [Tune n2EKUqA5JN3QwumeeiqyFX] Name: Google TV Priority: 1

2026-09-08 15:18:17.991 - tuner_management - [Tune n2EKUqA5JN3QwumeeiqyFX] Name: Google TV 2 Priority: 2

2026-09-08 15:18:17.991 - tuner_management - [Tune n2EKUqA5JN3QwumeeiqyFX] Using first available tuner (by priority).

2026-09-08 15:18:17.991 - tuner_management - [Tune n2EKUqA5JN3QwumeeiqyFX] Reserved Tuner: Name: Google TV Priority: 1

2026-09-08 15:18:17.991 - stream - [Tune (n2EKUqA5JN3QwumeeiqyFX)] Tuning to NBC Sports Philadelphia on Google TV

2026-09-08 15:18:17.994 - stream - [Tune (n2EKUqA5JN3QwumeeiqyFX)] Using channel configuration: Deep Links - Compatibility Mode (ADBTuner) (bb353259-17e0-4b38-a328-8629fb1ec2ca)

2026/09/08 15:18:18 [PROXY] Registered pending stream for key n2EKUqA5JN3QwumeeiqyFX

2026-09-08 15:18:18.009 - stream - [Tune n2EKUqA5JN3QwumeeiqyFX] MPEG null packets mode: Redirecting immediately to /proxy/1?requestKey=n2EKUqA5JN3QwumeeiqyFX

2026-09-08 15:18:18.009 - uvicorn.access - 192.168.1.6:0 - "GET /stream/51 HTTP/1.1" 307

2026/09/08 15:18:18 [PROXY] 192.168.1.6 -> GET "/proxy/1?requestKey=n2EKUqA5JN3QwumeeiqyFX" -> "http://192.168.1.26:8090/stream0"

2026/09/08 15:18:18 [PROXY] Starting null packet stream for key n2EKUqA5JN3QwumeeiqyFX

2026-09-08 15:18:18.181 - lib.adb - [Tune n2EKUqA5JN3QwumeeiqyFX] ADB: 192.168.1.2 - pidof com.google.android.youtube.tvunplugged

2026-09-08 15:18:18.279 - tuning - [Tune (n2EKUqA5JN3QwumeeiqyFX)] Using https://tv.youtube.com/watch/89dEK_TTJTM to load channel.

2026-09-08 15:18:18.279 - tuning - [Tune (n2EKUqA5JN3QwumeeiqyFX)] Executing pre-tune commands: [{'type': 'ADB', 'command': 'input keyevent KEYCODE_MEDIA_STOP'}, {'type': 'ADB', 'command': "am force-stop 'com.google.android.youtube.tvunplugged'"}, {'type': 'ADB', 'command': 'sleep 2'}]

2026-09-08 15:18:18.279 - lib.adb - [Tune n2EKUqA5JN3QwumeeiqyFX] ADB: 192.168.1.2 - input keyevent KEYCODE_MEDIA_STOP

2026-09-08 15:18:18.399 - lib.adb - [Tune n2EKUqA5JN3QwumeeiqyFX] ADB: 192.168.1.2 - am force-stop 'com.google.android.youtube.tvunplugged'

2026-09-08 15:18:18.518 - lib.adb - [Tune n2EKUqA5JN3QwumeeiqyFX] ADB: 192.168.1.2 - sleep 2

2026-09-08 15:18:18.518 - lib.adb - [Tune n2EKUqA5JN3QwumeeiqyFX] ADB: Sleeping for 2.0 seconds.

2026-09-08 15:18:20.519 - tuning - [Tune (n2EKUqA5JN3QwumeeiqyFX)] Executing tune commands: [{'type': 'ADB', 'command': "am start -W -a android.intent.action.VIEW -d 'https://tv.youtube.com/watch/89dEK_TTJTM' 'com.google.android.youtube.tvunplugged'"}]

2026-09-08 15:18:20.519 - lib.adb - [Tune n2EKUqA5JN3QwumeeiqyFX] ADB: 192.168.1.2 - am start -W -a android.intent.action.VIEW -d 'https://tv.youtube.com/watch/89dEK_TTJTM' 'com.google.android.youtube.tvunplugged'

2026-09-08 15:18:21.161 - tuning - [Tune n2EKUqA5JN3QwumeeiqyFX] Starting tune match text monitoring. Match commands: [{'match_text': ["who's watching", 'choose an account', 'edit profiles'], 'commands': [{'type': 'ADB', 'command': 'input keyevent KEYCODE_DPAD_CENTER'}], 'start_checking_after_seconds': 5}]

2026-09-08 15:18:21.600 - tuning - [Tune n2EKUqA5JN3QwumeeiqyFX] Waiting for playback start. Status: unknown

2026/09/08 15:18:23 [PROXY] Sending null packets for key n2EKUqA5JN3QwumeeiqyFX (5.001s elapsed, 65800 bytes sent)

2026-09-08 15:18:23.243 - server - 

--------------------------------------------------

Tuner "Google TV" is currently in use and locked.

Tune ID: n2EKUqA5JN3QwumeeiqyFX

Channel: NBC Sports Philadelphia

Lock Obtained: 2026-09-08 15:18:17 (0.1 minutes ago)

Last Seen: 2026-09-08 15:18:23 (0.24 seconds ago)

Bytes Transferred: 68432 (0.07MB)

Remote User Agent:

channels-dvr/2026.09.05.0220 Go-http-client/1.1 

(windows-x86_64)

--------------------------------------------------

2026-09-08 15:18:25.379 - lib.adb - [Tune n2EKUqA5JN3QwumeeiqyFX] Detected 100.0% black pixels. Assuming HDCP is active and video is playing.

2026/09/08 15:18:25 [PROXY] Signaled stream start for key n2EKUqA5JN3QwumeeiqyFX

2026/09/08 15:18:25 [PROXY] Switching to real stream for key n2EKUqA5JN3QwumeeiqyFX after 7.381s (73 batches sent)

2026/09/08 15:18:25 [PROXY] Beginning reverse proxy for key n2EKUqA5JN3QwumeeiqyFX

2026-09-08 15:18:25.392 - tuning - [Tune n2EKUqA5JN3QwumeeiqyFX] Signaled proxy to start real stream.

2026-09-08 15:18:25.393 - tuning - [Tune n2EKUqA5JN3QwumeeiqyFX] Tuning completed after 7.41 seconds. [Tune n2EKUqA5JN3QwumeeiqyFX] Display was on, application (com.google.android.youtube.tvunplugged) was closed.

2026-09-08 15:18:25.393 - tuning - Initializing keep active command schedule for tuner Google TV (1).

2026/09/08 15:18:25 [PROXY] No keyframe found within 500ms for key n2EKUqA5JN3QwumeeiqyFX (16732 bytes buffered), flushing buffer

2026-09-08 15:18:28.750 - tuning - [Tune n2EKUqA5JN3QwumeeiqyFX] Text match result: Text to match: ["who's watching", 'choose an account', 'edit profiles']. Matches found: None.

2026-09-08 15:18:34.478 - lib.adb - [Tune n2EKUqA5JN3QwumeeiqyFX] On screen text: '7 =o  ore jj  crp  cuetamer recnancihility praaram  pgworks.com'

2026-09-08 15:18:34.478 - tuning - [Tune n2EKUqA5JN3QwumeeiqyFX] Text match result: Text to match: ["who's watching", 'choose an account', 'edit profiles']. Matches found: None.

2026-09-08 15:18:37.068 - lib.adb - [Tune n2EKUqA5JN3QwumeeiqyFX] Detected 100.0% black pixels. Assuming HDCP is active and video is playing.

2026-09-08 15:18:37.070 - tuning - [Tune n2EKUqA5JN3QwumeeiqyFX] Media playback detected. Stopping tuning text match process.

2026-09-08 15:18:53.258 - server - 

--------------------------------------------------

Tuner "Google TV" is currently in use and locked.

Tune ID: n2EKUqA5JN3QwumeeiqyFX

Channel: NBC Sports Philadelphia

Lock Obtained: 2026-09-08 15:18:17 (0.6 minutes ago)

Last Seen: 2026-09-08 15:18:53 (0.26 seconds ago)

Bytes Transferred: 19687924 (18.78MB)

Remote User Agent:

channels-dvr/2026.09.05.0220 Go-http-client/1.1 

(windows-x86_64)

--------------------------------------------------

2026-09-08 15:19:02.056 - tuner_management - Releasing tuner 1 as it has been inactive for 8 seconds.

2026-09-08 15:19:02.056 - tuner_management - Releasing tuner: {'lock_obtained': 1788880697, 'tuner_name': 'Google TV', 'name': 'NBC Sports Philadelphia', 'number': 48, 'provider_name': 'YouTube TV', 'tuner_id': 1, 'channel_id': 51, 'tuning_status': 'tuned', 'request_key': 'n2EKUqA5JN3QwumeeiqyFX', 'target_package': 'com.google.android.youtube.tvunplugged'}

2026-09-08 15:19:02.209 - tuner_management - [Tune n2EKUqA5JN3QwumeeiqyFX] Using configuration: Deep Links - Compatibility Mode (ADBTuner) (bb353259-17e0-4b38-a328-8629fb1ec2ca) for channel cleanup.

2026-09-08 15:19:02.209 - tuner_management - Post tune commands: [{'type': 'ADB', 'command': "am force-stop 'com.google.android.youtube.tvunplugged'"}, {'type': 'ADB', 'command': 'input keyevent KEYCODE_HOME'}]

2026-09-08 15:19:02.209 - lib.adb - [Pool 1] ADB: 192.168.1.2 - am force-stop 'com.google.android.youtube.tvunplugged'

2026-09-08 15:19:02.370 - lib.adb - [Pool 1] ADB: 192.168.1.2 - input keyevent KEYCODE_HOME

2026-09-08 15:19:02.776 - tuner_management - [Tune n2EKUqA5JN3QwumeeiqyFX] Released tuner (1).

2026-09-08 15:19:02.776 - tuner_management - [Tune n2EKUqA5JN3QwumeeiqyFX] Cleanup complete. Google TV (NBC Sports Philadelphia)

2026-09-08 15:19:09.108 - uvicorn.access - 192.168.1.6:0 - "GET /api/v1/tuners/installed-packages HTTP/1.1" 200

2026-09-08 15:19:17.276 - uvicorn.access - 192.168.1.6:0 - "GET /api/v1/repositories HTTP/1.1" 200

2026-09-08 15:19:17.284 - uvicorn.access - 192.168.1.6:0 - "GET /api/v1/repositories/settings HTTP/1.1" 200

2026-09-08 15:19:33.040 - tuner_operations - Starting health check for 2 tuner(s).

2026-09-08 15:19:33.040 - tuner_operations - Google TV: Starting health check.

2026-09-08 15:19:33.123 - tuner_operations - Google TV: Health check complete.

2026-09-08 15:19:33.123 - tuner_operations - Google TV 2: Starting health check.

2026-09-08 15:19:33.227 - tuner_operations - Google TV 2: Health check complete.

2026-09-08 15:19:33.243 - uvicorn.access - 192.168.1.6:0 - "GET /up HTTP/1.1" 200

2026-09-08 15:19:41.400 - uvicorn.access - 192.168.1.6:0 - "GET /api/v1/tuners/installed-packages HTTP/1.1" 200

2026-09-08 15:19:43.242 - uvicorn.access - 192.168.1.6:0 - "GET /api/v1/tuners/installed-packages HTTP/1.1" 200

2026-09-08 15:19:44.629 - uvicorn.access - 192.168.1.6:0 - "GET /api/v1/tuners/installed-packages HTTP/1.1" 200`Preformatted text`

Not quite sure what is going on here. Hoping someone can help me out. Thanks!
1 Like

Can you pull the updated version (20260909-9) and let me know if this is fixed?

1 Like

Working good now with version 20260909-10 Thanks!