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

Yes, I specified that in the last sentence. This is when you leave a channel for a few minutes, then go back to that channel. It's like you paused it, left for a while, then came back and pressed play. I don't know if that's because I don't have compatibility enabled, but it stops working if I enable compatibility mode.

Anyone else have Philo with compatibility mode off?

1 Like

I just took a look at this and I'm actually seeing this behavior with compatibility mode on as well. It seems if you load a show you were previously watching it will default to resume regardless of if you have closed the app or not. I need to test this further to confirm, but I think it goes to live if the selected channel has begun showing a different program.

At the moment I don't have any recommendations on how to work around this. There is no setting that can be changed to alter this behavior and I don't see an effective way to trigger the "jump to live" action.

Thank you for checking into it. I was starting to think I was losing my mind. Seems to happen even if it doesn't pick the same Fire Stick when going back to the channel. Hoping there will be an eventual fix.

@turtletank ADBTuner has been working great. I typically don't watch live TV but with the Olympics on I find myself watching it more. The issue that I am having is with buffering. I watch a live channel and usually I get 2-3 pauses randomly. If I pause the stream for a couple of seconds then it works perfect. When I look at the stats I see that there are buffer pauses recorded. I also see that the buffer is under 1MB and when I pause for a few seconds this increases and then the problem is gone. As a test I opened up a stream from my mlb docker and it's buffer is ~10MB right at the start. I also see in the logs that Channels is showing that it has a ~5sec live delay which probably accounts for the larger buffer.

Is there any way that ADBTuner could do the same thing? Or maybe have a parameter that we can change to increase this?

Thanks for all your hard work :slight_smile:

2024/08/10 18:30:27.971760 [TNR] Opened connection to M3U-LinkPi for ch5810 USA
2024/08/10 18:31:07.366286 [SNR] Buffer statistics for 10.100.12.78 (Seth's Office ATV) for ch5810 USA: buf=0% drop=0%
2024/08/10 18:31:07.366479 [TNR] Closed connection to M3U-LinkPi for ch5810 USA
2024/08/10 18:31:23.597253 [M3U] stream timestamps: 2_MLBTV.NYY: start_at=2024-08-10T16:28:42-04:00 end_at=2024-08-10T18:31:13-04:00 live_delay=5.799252147s
2024/08/10 18:31:23.597389 [TNR] Opened connection to M3U-mlbtvservernew for ch1016 Yankees
2024/08/10 18:32:00.028126 [SNR] Rewriter statistics for 10.100.12.78 (Seth's Office ATV) for ch1016 Yankees: discontinuity_detected=0 transport_errors=0 saw_pcr=true saw_pmt=true highest_pts=44.394356
2024/08/10 18:32:00.028514 [SNR] Buffer statistics for 10.100.12.78 (Seth's Office ATV) for ch1016 Yankees: buf=0% drop=0%
2024/08/10 18:32:00.028821 [SNR] Streaming statistics for 10.100.12.78 (Seth's Office ATV) for ch1016 Yankees: timeouts=0 segment_timeouts=0 playlist_timeouts=0
2024/08/10 18:32:00.065847 [TNR] Closed connection to M3U-mlbtvservernew for ch1016 Yankees

I seem to be having a Problem I am getting this error ...

2024/08/24 10:20:14.588915 [ERR] Failed to start stream for ch851: M3U: GET: http://192.168.50.162:5592/stream/1: 500 Internal Server Error
2024/08/24 10:20:14.588915 [HLS] Couldn't generate stream playlist for ch851-dANY-ip192.168.50.186: M3U: GET: http://192.168.50.162:5592/stream/1: 500 Internal Server Error
2024/08/24 10:20:14.589432 [HLS] Stopping transcoder session ch851-dANY-ip192.168.50.186 (out=0s finished=false first_seq=0 last_seq=-1)

Can you share the docker logs from ADBTuner?

I can look into this. In the meantime, have you tried lowering the bitrate in your encoder?

    raise exc
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "/usr/local/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 20, in __call__
    raise e
  File "/usr/local/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 17, in __call__
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 718, in __call__
    await route.handle(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 66, in app
    response = await func(request)
               ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/fastapi/routing.py", line 241, in app
    raw_response = await run_endpoint_function(
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/fastapi/routing.py", line 169, in run_endpoint_function
    return await run_in_threadpool(dependant.call, **values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/starlette/concurrency.py", line 41, in run_in_threadpool
    return await anyio.to_thread.run_sync(func, *args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/anyio/to_thread.py", line 56, in run_sync
    return await get_async_backend().run_sync_in_worker_thread(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2177, in run_sync_in_worker_thread
    return await future
           ^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 859, in run
    result = context.run(func, *args)
             ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/server.py", line 586, in stream_redirect
    available_tuner = tuner_manager.reserve_tuner()
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/server.py", line 99, in reserve_tuner
    selected_tuner = available_tuners[0]
                     ~~~~~~~~~~~~~~~~^^^
IndexError: list index out of range
2024-08-24 17:24:53.154 - server - Available Tuners:
2024-08-24 17:24:53.154 - server - Using first available tuner (by priority).
2024-08-24 17:24:53.155 - uvicorn.access - 172.23.0.1:40064 - "GET /stream/1 HTTP/1.1" 500
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/uvicorn/protocols/http/httptools_impl.py", line 435, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
    return await self.app(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/fastapi/applications.py", line 284, in __call__
    await super().__call__(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/applications.py", line 122, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/sessions.py", line 86, in __call__
    await self.app(scope, receive, send_wrapper)
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 79, in __call__
    raise exc
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "/usr/local/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 20, in __call__
    raise e
  File "/usr/local/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 17, in __call__
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 718, in __call__
    await route.handle(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 66, in app
    response = await func(request)
               ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/fastapi/routing.py", line 241, in app
    raw_response = await run_endpoint_function(
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/fastapi/routing.py", line 169, in run_endpoint_function
    return await run_in_threadpool(dependant.call, **values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/starlette/concurrency.py", line 41, in run_in_threadpool
    return await anyio.to_thread.run_sync(func, *args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/anyio/to_thread.py", line 56, in run_sync
    return await get_async_backend().run_sync_in_worker_thread(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2177, in run_sync_in_worker_thread
    return await future
           ^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 859, in run
    result = context.run(func, *args)
             ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/server.py", line 586, in stream_redirect
    available_tuner = tuner_manager.reserve_tuner()
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/server.py", line 99, in reserve_tuner
    selected_tuner = available_tuners[0]
                     ~~~~~~~~~~~~~~~~^^^
IndexError: list index out of range

Thanks. A new build has been pushed with a fix for this. I failed to check if any tuners were actually available before assigning one. Oops.

No tuners available ..... I had 4 available. They disappeared ... re-adding them

2024-08-24 21:36:07.290 - server - No tuners available.
2024-08-24 21:36:07.293 - uvicorn.access - 172.23.0.1:36382 - "GET /stream/1 HTTP/1.1" 404
2024-08-24 21:36:18.444 - server - No tuners available.
2024-08-24 21:36:18.448 - uvicorn.access - 172.23.0.1:38164 - "GET /stream/1 HTTP/1.1" 404
2024-08-24 21:36:29.324 - server - No tuners available.
2024-08-24 21:36:29.326 - uvicorn.access - 172.23.0.1:56262 - "GET /stream/1 HTTP/1.1" 404
2024-08-24 21:37:06.068 - server - No tuners available.
2024-08-24 21:37:06.071 - uvicorn.access - 172.23.0.1:58410 - "GET /stream/4 HTTP/1.1" 404
2024-08-24 21:37:17.187 - server - No tuners available.
2024-08-24 21:37:17.190 - uvicorn.access - 172.23.0.1:35900 - "GET /stream/4 HTTP/1.1" 404
2024-08-24 21:37:28.098 - server - No tuners available.
2024-08-24 21:37:28.100 - uvicorn.access - 172.23.0.1:59744 - "GET /stream/4 HTTP/1.1" 404
2024-08-24 21:38:35.466 - server - No tuners available.
2024-08-24 21:38:35.467 - uvicorn.access - 172.23.0.1:35264 - "GET /stream/5 HTTP/1.1" 404
2024-08-24 21:38:46.613 - server - No tuners available.
2024-08-24 21:38:46.614 - uvicorn.access - 172.23.0.1:56904 - "GET /stream/5 HTTP/1.1" 404
2024-08-24 21:38:58.116 - server - No tuners available.
2024-08-24 21:38:58.117 - uvicorn.access - 172.23.0.1:48170 - "GET /stream/5 HTTP/1.1" 404

@turtletank ... Appears to be working after I re-added my tuners have no clue why they dissappeared.

Hmm.. Yeah the root cause for both of these errors was that there were no available tuners. It's really odd that they all disappeared like that. Please let me know if it happens again and maybe we can get some logs that might indicate how they got deleted. Thanks!

I'm considering picking up an HDMI encoder to pull in channels via YouTube TV but also the NESN 360 app. It's obviously a big investment to purchase the encoder and the Android TV devices all for a few channels.

I guess my question is, will this solution ALWAYS work or could Google change things on their end that would prevent this from not working in the future? Nearly $500 wanted if Google makes a switch.

Also, what's the most economical encoder devices for 2-3-4 inputs? I'm thinking I really just need a stream for NESN, NBC Sports, but don't want to purchase an encoder and then realize I need more later on.

Another question, are devices shareable? Meaning NESN could be watched on two devices at once. Can that stream from a single device be shared?

Your going to find it hard to beat the price per tuner on LinkPi 5 port tuner...You can find lots of discussion on here about them. Lots of people including me really like them but others have had some issues with black levels and such if I remember right.
Google CAN ALWAYS change things up and complicate our lives. I believe ADBTuner specifically relies exclusively on DeepLInks and ADB for controlling android boxes to "tune" channels. There are other options like AH4C that can use deeplinks,scripts or even pyremote to control the "tuners". ADBTuner is a good starting point and will handle 90% of use cases. I have not used the NESN app or NBC Sports so I don't know if they have deeplinks or not off hand.
The "tuners" are fed to CDVR and it can send it back out over the network to all your client at the same time if you wish with that single "tuner"

1 Like

How many tuners you want? I usually want 2 for stuff like this as I'm not gonna record often and only have 2 TVs in the house. A single tuner LinkPi and a cheap HDMI usb encoder could do

What is your current or proposed setup? Cable? Satellite? Youtube TV? Fubo? Hulu Live?
What Services are you using or planning on using for watching tv?

Honestly it's likely just NESN and Boston Sports Net which I think I have issues with. Four would be the absolute max because typically I would pull up the four screens in a 2x2 grid.

What do you mean by a cheap HDMI USB encoder? Why couldn't you use many of these cheaper devices instead of a Linkpi?

Right now I use Channels DVR primarily to output a m3u playlist and epg that I feed into Tivimate. It works well to pull in my TVE channels from my Youtube TV subscription, (2) HD Homeruns (one towards Boston, one towards Portland, ME), Pluto and the MLB package.

Tivimate lets me display many screens on my 120" projector screen which is great for sports. The issue I have no is the Celtics (NBC Sports Boston) and Bruins/Red Sox (NESN) can no longer be pulled into Channels.

I haven't looked at Tivimate in a long time. Is it able to pull the Youtube TV channels direcetly in?

I never used the usb encoders with channels. But if you have the computer already running it would be a low cost way to try. Personally I prefer the stand alone linkpi or other stand alone encoder. Many are designed to run 24/7 including the LinkPi and at least for me it has been rock solid. More and more tv channels are doing away with their TVE intergration. Youtube TV through ADBTuner or AH4C is pretty solid so that would help future proof the rest of the youtube tv channels even when they drop TVE support.
If you are receiving the NBC Sports and NESN channels through Youtube TV then they would be able to be tuned like any other youtube tv channel and would not be any issues.

If you what to experiment on the cheap then a <$20 usb encoder and <$30 android tv dongle though I prefer the more expensive ones with ethernet included. Plus a computer to run ADBTuner on possibly the same one currently running channels.
Also it might be important to you to know that basically none of the encoder options are going to encode closed captions so you can turn them on and off on the fly or encode audio in anything more than stereo.

Just wanted to give an update after the nearly 2 months since we were all discussing this issue.

As I mentioned previously, I manually set the variable value via local use of adbtools. Since then, I have replaced the "other" 2 chromecasts with new ones (upgraded to 4k versions) and I purposely left them default w.r.t. the adb_allowed_connection_time thing in order to allow the upgraded app send the command. Both sets have not had a re-auth popup in all of this time. So...at least for my particular experience...I'm calling this "mission accomplished". Thanks for all the time you continue to put into this project..it has become my primary source to feed Channels for non-OTA programming and this quality-of-life improvement has made a measureable impact on both my enjoyment of the product as well as my sanity now that I don't have to babysit the chromecasts on seemingly a nearly hourly basis to ensure that they are not hung on a re-auth prompt.

1 Like