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

Hello All.

I can't believe it's been three years already.

When I started this project, I had originally intended for it to be an open source, collaborative situation.
Unfortunately, some people were surprisingly rude and it immediately took all the fun out of it. And if it's not fun, there is no reason to do it at all.

I thought there was some value to the project, and I was using it myself, so I continued to hack away at it, share knowledge, and help others wherever I could. I probably have thousands of hours into this so far and I have no intention of abandoning the project. However, at this point, I don't have any immediate plans to change the license. It's just not something I have the time/energy to deal with right now. If I do intend to walk away that would change.

Honestly, I would like to keep the ADBTuner core focused on its original purpose which is loading deep linked content in apps that properly support doing as such. AH4C and android-hdmi-for-channels were always better platforms where getting every possible app to work is the goal. Great work has been done by the developers of those tools.

This being said, I would absolutely love to add support for loading configurations from third-party git repositories and/or even loading third-party Python code to assist in tuning actions. If there is interest in this, please let me know and I will get that done. @babsonnexus is this what you are looking to do? I'd be glad to help with whatever I can.

4 Likes

Some people can be dicks yeah but this tool has been pretty useful for me and plenty of others. I'd be happy to chip in a bit and help in some way as I said above. I hope you reconsider but I understand if you want to keep the license the way it is

I have a quick question. Is there a video encoder that works with HDR since the uray video encoder doesn't do hdr? I have looked everywhere but I can't find a solution. Would a elgato capture card with obs work somehow (I have a good hdmi splitter)?

This is mainly for the atsc 3.0 boxes which forces hdr on nbc 5 and fox 32.

Not at the moment, but I will set something up as I do enjoy a cup of coffee. Thanks.

3 Likes

The development build (20260417-2) was updated with initial support for importing third-party configuration libraries. A bit of a prototype for now, but something to play with, maybe it helps.

I need to expose this to the API, add optional automatic updates, and deal with some edge cases. I should have time for that tomorrow.

An example repository layout is available here:
https://github.com/adbtuner/configuration-repository-example

If there is anything anyone is doing in configurations that seems tedious let me know. I can add better support for repeating commands, custom variables, etc.

2 Likes

Yeah...you may need to use ah4c for those boxes instead. Theres very little HDMI encoders out there that does HDR.

Ah4c CAN do tone-mapping and you can set it up to do that

I love ADBTuner and it's been working solidly for years.

This week I'm trying to get FruitDeepLinks going. I used OliveTin to install FDL and created custom channels for Prime, Paramount, Peacock, and ESPN.

So far I've had little luck getting events to open via ADBTuner and am thrashing...no idea what's going on and beginning to think I have multiple problems. The one app that will open events is Paramount but it gets stuck on the "who's watching?" screen. I've played around a bit with the custom configs but haven't made any progress. And now (to me) the strange part:

All of my testing has been opening programs from the web guide which for paramount lands on the who's watching page:

However I just tried opening the exact same program from the iOS client and it doesn't work at all: just gives a 404 error:

I checked the ADBTuner logs and for the iOS open there are no entries.

When opening via the guide I see the attempt to tune with some errors including a KeyError: 'check_for_and_clear_whos_watching_prompts' (the config is the One Click version for Paramount).

Do these differences in event openings point to a bigger underlying issue?

I include the log snippet below.

2026-04-17 21:33:55.302 - stream - [Tune (GcXUh7UDvfjgxtWfcCT64X)] Using channel configuration: FruitDeepLinks - Parmount+ v4.0 (FireTV and AndroidTV) (62a01c95-94f8-41b7-b56b-4838859ab42d)
2026-04-17 21:33:55.524 - lib.adb - [Tune (GcXUh7UDvfjgxtWfcCT64X)] ADB: 192.168.30.23 - pidof com.cbs.ott
2026-04-17 21:33:55.566 - stream - [Tune (GcXUh7UDvfjgxtWfcCT64X)] Resolving dynamic URL (http://10.167.168.6:6655/api/adb/lanes/pplus/5/deeplink?format=json) for channel.
2026-04-17 21:33:55.606 - stream - [Tune (GcXUh7UDvfjgxtWfcCT64X)] Retrieved dynamic URL data: {'channel_id': 'pplus05', 'channel_name': 'Paramount+ Sports US EBS', 'deeplink': 'pplus://www.paramountplus.com/live-tv/stream/serie-a/811b80b0-6b79-42fb-bb82-5b3f6ec051e1?searchReferral=appleatv&source=spotlight', 'deeplink_format': 'scheme', 'event_end_utc': '2026-04-17T22:25:00+00:00', 'event_start_utc': '2026-04-17T18:45:00+00:00', 'lane_number': 5, 'provider_code': 'pplus', 'start_utc': '2026-04-17T18:45:00+00:00', 'status': 'success', 'stop_utc': '2026-04-17T22:25:00+00:00', 'title': 'Serie A: Internazionale Milano vs. Cagliari Calcio'}
2026-04-17 21:33:55.606 - stream - [Tune (GcXUh7UDvfjgxtWfcCT64X)] Using pplus://www.paramountplus.com/live-tv/stream/serie-a/811b80b0-6b79-42fb-bb82-5b3f6ec051e1?searchReferral=appleatv&source=spotlight to load channel.
2026-04-17 21:33:55.606 - lib.adb - [Tune (GcXUh7UDvfjgxtWfcCT64X)] ADB: 192.168.30.23 - input keyevent KEYCODE_MEDIA_STOP
2026-04-17 21:33:56.603 - lib.adb - [Tune (GcXUh7UDvfjgxtWfcCT64X)] ADB: 192.168.30.23 - am force-stop com.cbs.ott
2026-04-17 21:33:56.689 - lib.adb - [Tune (GcXUh7UDvfjgxtWfcCT64X)] ADB: 192.168.30.23 - am start -W -a android.intent.action.VIEW -d 'pplus://www.paramountplus.com/live-tv/stream/serie-a/811b80b0-6b79-42fb-bb82-5b3f6ec051e1?searchReferral=appleatv&source=spotlight' 'com.cbs.ott'
Exception in thread Thread-37 (background_tune):
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/threading.py", line 1075, in _bootstrap_inner
    self.run()
  File "/usr/local/lib/python3.12/threading.py", line 1012, in run
    self._target(*self._args, **self._kwargs)
  File "/app/app/routers/stream.py", line 193, in background_tune
    if channel_configuration["global_options"][
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
KeyError: 'check_for_and_clear_whos_watching_prompts'
2026-04-17 21:34:00.363 - stream - [Tune GcXUh7UDvfjgxtWfcCT64X] Redirecting to stream after 5.06 seconds (fixed delay of 5 seconds exceeded). Tuning is still in progress.
2026-04-17 21:34:00.363 - uvicorn.access - 138.68.32.225:0 - "GET /stream/210 HTTP/1.1" 307
2026/04/17 21:34:00 [PROXY] 138.68.32.225 -> GET "/proxy/1?requestKey=GcXUh7UDvfjgxtWfcCT64X" -> "http://192.168.30.20/8.ts"
2026-04-17 21:34:18.646 - server - 

--------------------------------------------------
Tuner "FireTV23-TV3" is currently in use and locked.
Tune ID: N/A
Channel: Paramount+ 5
Lock Obtained: 2026-04-17 21:33:55 (0.39 minutes ago)
Last Seen: N/A
Bytes Transferred: None
Remote User Agent:
N/A
--------------------------------------------------
2026-04-17 21:34:48.675 - server - 

--------------------------------------------------
Tuner "FireTV23-TV3" is currently in use and locked.
Tune ID: N/A
Channel: Paramount+ 5
Lock Obtained: 2026-04-17 21:33:55 (0.89 minutes ago)
Last Seen: N/A
Bytes Transferred: None
Remote User Agent:
N/A
--------------------------------------------------
2026-04-17 21:34:56.556 - tuner_management - Releasing tuner 1 as it is untracked and has been locked for over a minute.
2026-04-17 21:34:56.556 - tuner_management - Released tuner (1).

Can you try the default configuration for those Paramount channels? It looks like the configuration you are using might be out of date or corrupt as it's missing a field

I'm actually using the default Paramount config created by OliveTin. Have recreated (and chosen to remove old one) a few times. Here's the config as shown in ADB editor:

{
    "name": "FruitDeepLinks - Parmount+ v4.0 (FireTV and AndroidTV)",
    "author": "bnhf",
    "version": "4.0",
    "description": "Paramount+ for FruitDeepLinks. Compatible with FireTV and AndroidTV devices.",
    "uuid": "62a01c95-94f8-41b7-b56b-4838859ab42d",
    "global_options": {
        "wait_for_video_playback_detection": false,
        "use_fixed_delay": true,
        "fixed_delay_seconds": 5,
        "wait_after_post_playback_start_commands_seconds": 0
    },
    "pre_tune_commands": [
        "input keyevent KEYCODE_MEDIA_STOP",
        "am force-stop ||TARGET_PACKAGE_NAME||"
    ],
    "tune_commands": [
        "am start -W -a android.intent.action.VIEW -d '||TARGET_URL_OR_IDENTIFIER||' '||TARGET_PACKAGE_NAME||'"
    ],
    "tune_match_text_commands": [
        {
            "match_text": [
                "who's watching",
                "edit profiles"
            ],
            "commands": [
                "input keyevent KEYCODE_DPAD_CENTER"
            ],
            "check_after_seconds": 5
        }
    ],
    "post_playback_start_commands": [],
    "post_tune_commands": [
        "input keyevent KEYCODE_HOME",
        "am force-stop ||TARGET_PACKAGE_NAME||"
    ],
    "timed_keep_active_commands": []
}

In case it matters: I have two different ADBTuner instances running atm.

  • The original one which is on a Synology and everything still works fine.
  • The new one running in Docker/Portainer on the Mac with bare metal DVR

This doesn't seem like it should cause an issue but wanted to point it out just in case

1 Like

I'm not in a position to confirm this atm, but I believe you still need to use the version of ADBTuner with the development tag for the custom configs to work.

1 Like

Pulled the development version and now everything opens.

Yes: Development version is still needed!

Do you know what tag you were using prior to switching to development? The current stable version (20251228-2) (turtletank99/adbtuner:stable) should also work fine with that configuration.

Pretty sure i was using latest and that it was set by OliveTin (I was using the automation as much as possible and it was awesome!)

I understand and sympathize with what it can be like! ADBTuner is my preferred capture method, too, because I find it so intuitive and clean. I'm going to add you to a PM offline to discuss what we're up to, but to be clear, I'm perfectly happy working within whatever confines you or ADBTuner might have.

1 Like

The development build (20260418-5) was updated to add api endpoints for managing remote repositories, optional automatic repository updates, improved duplicate configuration UUID handling, and a new dialog for selecting configurations (the dropdown was getting cumbersome with a large list of configurations).

1 Like

With my shiny new ADBTuner setup fed by FDL I was able to setup a recording of the Warriors/Suns game last night. When I watched it this morning it was 3 hours of screen saver.

The tuner got me to the game but the custom config for Prime (FruitDeepLinks - Prime Video v3.0) didn't click the "Watch" button. I've pasted screenshot of recording and custom config below.

(Just realized I have a second issue which is the screenshot is for the prior game...the FDL start time was 30 minutes earlier than the actual game)

Anyway...I'm new to these configs and wondering how to tweak the custom config so it gets past the watch screen. In this case is it as simple as adding a new text snipped "watch" to the "match_text" blob?

{
    "name": "FruitDeepLinks - Prime Video v3.0 (FireTV and AndroidTV)",
    "author": "bnhf",
    "version": "3.0",
    "description": "Prime Video for FruitDeepLinks. Compatible with FireTV and AndroidTV devices.",
    "uuid": "a176643f-b3b6-47fb-8527-47908d089695",
    "global_options": {
        "wait_for_video_playback_detection": false,
        "use_fixed_delay": true,
        "fixed_delay_seconds": 5,
        "wait_after_post_playback_start_commands_seconds": 0
    },
    "pre_tune_commands": [
        "input keyevent KEYCODE_MEDIA_STOP",
        "input keyevent KEYCODE_HOME"
    ],
    "tune_commands": [
        "am start -n $(dumpsys package '||TARGET_PACKAGE_NAME||' 2>/dev/null | awk '/https:/{getline; print $2; exit}') -d '||TARGET_URL_OR_IDENTIFIER||'"
    ],
    "tune_match_text_commands": [
        {
            "match_text": [
                "who's watching",
                "watch live",
                "new",
                "guest"
            ],
            "commands": [
                "input keyevent KEYCODE_DPAD_CENTER"
            ],
            "check_after_seconds": 5
        }
    ],
    "post_playback_start_commands": [],
    "post_tune_commands": [
        "input keyevent KEYCODE_MEDIA_STOP",
        "input keyevent KEYCODE_HOME"
    ],
    "timed_keep_active_commands": []
}

Getting some data here, after a reboot, my LinkPi ENC5V2 starts streaming video at the ~20 second mark. However, that video drops out at ~40 seconds and doesn't return until ~80 seconds. So even if we do wait until the stream is available that just gets us ~20 seconds of video before it drops out and the tuning session is torn down. Does that match what you are seeing?

Edit: so you are suggesting serving null data during that downtime in hopes it comes back up and the stream can resume?

Ok. Thanks. Let me chew on this for a while and see what might work with ADBTuner.

1 Like

The docker logs should tell you what text was matched on that screen, but yes, it may be as easy as adding "watch" as a string there.