ESPN+ with Custom Channels via ADBTuner

Sorry for that guys. Do you think it would be better if I hot patched mine to return the old expected Call that he was using or better to patch his?

ADBTuner removes the "dynamic_url_json_key" parameter from the URL and passes the rest through.

So if the URL is configured in ADBTuner as:
http://x.x.x.x:8094/whatson/2?deeplink=1&dynamic_url_json_key=deeplink_url

ESPN4CC4C will see:
http://x.x.x.x:8094/whatson/2?deeplink=1

Last I checked, ESPN4CC4C requires that deeplink=1 parameter. Otherwise the return value for deeplink_url is None.

@bnhf @turtletank ... again, my fault guys... here's updated API documentation. if it's easier to change something to what your programs expect, let me know. sorry!!

@KineticMan The above structure looks to be correct, so the deeplink is being retrieved and passed -- but virtual tuning still isn't working:

2025-11-13 13:33:12.374 - stream - [Tune (VwPDNE7gvktNoYSNfFNmme)] Resolving dynamic URL (http://host.docker.internal:8094/whatson/1?deeplink=1) for channel.
2025-11-13 13:33:12.387 - stream - [Tune (VwPDNE7gvktNoYSNfFNmme)] Retrieved dynamic URL data: {'ok': True, 'lane': 1, 'event_uid': '6bc21b96-328e-4a44-b9b1-cb4038851d1b:6a1f4a7a74f32a51738e7a492923755e', 'at': '2025-11-13T13:33:12+00:00', 'deeplink_url': 'sportscenter://x-callback-url/showWatchStream?playID=6bc21b96-328e-4a44-b9b1-cb4038851d1b'}
2025-11-13 13:33:12.387 - stream - [Tune (VwPDNE7gvktNoYSNfFNmme)] Using sportscenter://x-callback-url/showWatchStream?playID=6bc21b96-328e-4a44-b9b1-cb4038851d1b to load channel.
2025-11-13 13:33:12.387 - lib.adb - [Tune (VwPDNE7gvktNoYSNfFNmme)] ADB: firestick-rack4 - input keyevent KEYCODE_MEDIA_STOP
2025-11-13 13:33:12.825 - stream - [Tune VwPDNE7gvktNoYSNfFNmme] Redirecting to stream after 1.0 seconds (fixed delay of 1 seconds exceeded). Tuning is still in progress.
2025-11-13 13:33:12.825 - uvicorn.access - 100.98.232.107:0 - "GET /stream/183?web-preview HTTP/1.1" 307
2025/11/13 13:33:12 [PROXY] 100.98.232.107 -> GET "/proxy/2?requestKey=VwPDNE7gvktNoYSNfFNmme" -> "http://encoder_48007/12.ts"
2025-11-13 13:33:13.147 - lib.adb - [Tune (VwPDNE7gvktNoYSNfFNmme)] ADB: firestick-rack4 - am start -n com.espn.gtv/com.espn.startup.presentation.StartupActivity -d sportscenter://x-callback-url/showWatchStream?playID=6bc21b96-328e-4a44-b9b1-cb4038851d1b

I'm not sure where I dropped the deeplink=1 out, but I can fix that easily enough. I'm wondering though if these deeplinks work on non-ESPN+ events on Android? The IDs seem to work fine in cc4c, but that same ID used in the Android deeplink structure are failing for me.

The first ESPN+ event of the day is on soon, so I'll be able to confirm then...

@TJN @Jean0987654321 A little mixup on the API format, but I believe we have that straightened out now. You'll need to edit, or delete and re-import, the ADBTuner Virtual Channels we're calling lanes. Deleting and re-importing is probably the easiest overall. See Post #1 for the structure in the JSON channels.

I'll ultimately be automating adding and deleting certain groups of channels into ADBTuner (now that there's an API), but it's not done yet. This will be available via OliveTin's Project One-Click.

1 Like

@KineticMan

Here's what I'm seeing atm in terms of which events are working, and which aren't:

ACCN=no
ACCNX=no
ESPN=yes
ESPN Deportes=yes
ESPN2=no
ESPNU=yes
ESPNews=no
SEC=no

Are you seeing anything similar, in terms of non-ESPN+ deeplinks?

Ok, that worked. Thanks guys

1 Like

@Jean0987654321 What are you seeing as far as whether deeplinks are working or not for content from the channels I listed in the linked post? I believe ESPN+ events are working fine, but I'm not sure about others:

I have a few ideas.. Currently, the deeplink URL only pulls out the playID (which seems to work fine but ESPN+ events). There is an additional field called airingID. I didn't apply that field to the URL as it didn't seem to matter in my testing, but luckily it's scraped/store in the DB. I can't test until tonight, but here's what I'm going to try.

# Format 1: Current (playID only)
adb shell am start -a android.intent.action.VIEW -d "sportscenter://x-callback-url/showWatchStream?playID=6f5aa775-701e-4579-8fe5-04321943b249"

# Format 2: playID + networkId
adb shell am start -a android.intent.action.VIEW -d "sportscenter://x-callback-url/showWatchStream?playID=6f5aa775-701e-4579-8fe5-04321943b249&networkId=espn1"

# Format 3: airingId only
adb shell am start -a android.intent.action.VIEW -d "sportscenter://x-callback-url/showWatchStream?airingId=118138907"

# Format 4: airingId + networkId
adb shell am start -a android.intent.action.VIEW -d "sportscenter://x-callback-url/showWatchStream?airingId=118138907&networkId=espn1"

# Format 5: playID + airingId
adb shell am start -a android.intent.action.VIEW -d "sportscenter://x-callback-url/showWatchStream?playID=6f5aa775-701e-4579-8fe5-04321943b249&airingId=118138907"

# Format 6: playID + airingId + networkId
adb shell am start -a android.intent.action.VIEW -d "sportscenter://x-callback-url/showWatchStream?playID=6f5aa775-701e-4579-8fe5-04321943b249&airingId=118138907&networkId=espn1"

# Format 7: Linear channel (airingId + networkId)
adb shell am start -a android.intent.action.VIEW -d "sportscenter://x-callback-url/showLinearChannel?airingId=118138907&networkId=espn1"

# Format 8: showVideo endpoint (playID)
adb shell am start -a android.intent.action.VIEW -d "sportscenter://x-callback-url/showVideo?playID=6f5aa775-701e-4579-8fe5-04321943b249"

# Format 9: showVideo + airingId
adb shell am start -a android.intent.action.VIEW -d "sportscenter://x-callback-url/showVideo?playID=6f5aa775-701e-4579-8fe5-04321943b249&airingId=118138907"
2 Likes

I only checked the ESPN+ (or ESPN Select) stuff. Let me check on the other stuff...

ESPN - Good
ESPN2 - No Good
ESPNews - No Good
ESPNDeportes - No Good
ESPNU - No Good
@ESPN - Good
SEC - No Good
ACCN - No Good
ACCNX - No Good
SEC+ - Don't know yet

1 Like

If you can get linear channels to work with this, then it may be worth getting ESPN Unlimited (and I would save a bunch of money really :D).

there does seem to be a flag in the scrape called authTypes: ['MVPD'] which is MVPD = Multichannel Video Programming Distributor (aka cable/satellite provider)

are you all able to actually watch an event directly on the app that deeplinks doesn't work with?

Yes -- with an ESPN Unlimited subscription in my case.

1 Like

can you guys tell me exactly what's not working? i just grabbed a ESPN2 event live right now (Purdue vs Alabama). FWIW - I'm testing with ESPN+ synced with Spectrum acct

API request -  http://192.168.86.72:8094/whatson/40?deeplink=1)
Response->
Response - {"ok":true,"lane":40,"event_uid":"56bc5a31-df66-45ae-a5a8-3d738c046325:9708855a2fc52fb09cbc1654c59a0960","at":"2025-11-14T01:49:23+00:00","deeplink_url":"sportscenter://x-callback-url/showWatchStream?playID=56bc5a31-df66-45ae-a5a8-3d738c046325"
Test:
adb shell am start -a android.intent.action.VIEW -d "sportscenter://x-callback-url/showWatchStream?playID=56bc5a31-df66-45ae-a5a8-3d738c046325"

Fired right up....

edit- tried ESPN2, ACCN... not sure if you have a bug on your end, but the exact ADB commands i'm sending work everytime. please double check exactly the command you are sending-- is the syntax the same that i'm testing with?

2025-11-13 13:33:13.147 - lib.adb - [Tune (VwPDNE7gvktNoYSNfFNmme)] ADB: firestick-rack4 - am start -n com.espn.gtv/com.espn.startup.presentation.StartupActivity -d sportscenter://x-callback-url/showWatchStream?playID=6bc21b96-328e-4a44-b9b1-cb4038851d1b

im not familiar with the exact syntax here, but its different than how i'm testing myself. can you just humor me and try it like the tests?

remove -n ... and add -a android.intent.action.VIEW

like my tests:

adb shell am start -a android.intent.action.VIEW -d "sportscenter://x-callback-url/showWatchStream?playID=897a418c-6a68-47e3-9446-fa84c9ee8de3"

@turtletank

I'm not seeing any difference in behavior between these approaches to launching:

am start -a android.intent.action.VIEW -d "sportscenter://x-callback-url/showWatchStream?playID=56bc5a31-df66-45ae-a5a8-3d738c046325"
am start -n com.espn.gtv/com.espn.startup.presentation.StartupActivity -d "sportscenter://x-callback-url/showWatchStream?playID=56bc5a31-df66-45ae-a5a8-3d738c046325"
am start -d "sportscenter://x-callback-url/showWatchStream?playID=56bc5a31-df66-45ae-a5a8-3d738c046325"
am start -W -a android.intent.action.VIEW -d "sportscenter://x-callback-url/showWatchStream?playID=56bc5a31-df66-45ae-a5a8-3d738c046325" com.espn.gtv

They're all somewhat different in how they function, but with com.espn.gtv the resulting behavior is identical.

My current theory is that this has something to do with Re-Air events, but I won't be able to test that for some hours yet -- when the first live events start popping up.

@KineticMan

The above appears to be confirmed in the 5:30AM hour, as the events in red are labeled as Live in the app, and they are the only ones of the seven active that will actually tune. In the other cases, the events are labeled Re-Air, and will not tune using a deeplink.

nice detective work-- shot u a direct msg.

New test endpoint: ESPN Android deeplink helper

I’ve added a small experimental API to the ESPN4CC4C resolver to help test Android deeplinks for ESPN.

Two new endpoints:

  • GET /api/test/{lane}
  • GET /api/test/{lane}?format=txt

These are read-only and do not affect /epg.xml or /playlist.m3u. They just look at the current event for a lane and generate a few sportscenter:// deeplink variants.

Example (replace HOST with your box):

curl -s "http://HOST:8094/api/test/1" | jq

You’ll see something like:

{
  "ok": true,
  "lane": 1,
  "ids": {
    "play_id": "…",
    "airing_id": "…",
    "network_id": "acc"
  },
  "deeplinks": {
    "1": "sportscenter://x-callback-url/showWatchStream?playID=…",
    "2": "sportscenter://x-callback-url/showWatchStream?playID=…&networkId=acc",
    "3": "sportscenter://x-callback-url/showWatchStream?playID=…&airingId=…",
    "4": "sportscenter://x-callback-url/showWatchStream?playID=…&airingId=…&networkId=acc"
  }
}

For quick copy/paste, use the text view:

curl -s "http://HOST:8094/api/test/1?format=txt"

That prints:

1: sportscenter://x-callback-url/…
2: sportscenter://x-callback-url/…
3: …
4: …

What to test

On an Android device with ADB:

  1. Pick a lane that has a real event (1–5 are usually good).

  2. Grab one of the URLs from /api/test/{lane}?format=txt.

  3. Run:

    adb shell am start -a android.intent.action.VIEW -d "<one of the URLs>"
    

What I’d like feedback on:

  • Lane number and variant number (1, 2, 3, or 4).
  • Whether the event was live or a replay.
  • What happened:
    • Opened ESPN and started playing the correct event
    • Opened ESPN but did not start playback
    • Failed or showed an error

This should help narrow down which deeplink format is most reliable for live vs replay events on Android. Sitting in car for a long car ride this weekend so no access to Firestick to test if anyone has time this weekend, please share results!

Just spun-up ESPN4CC. Tuning is working great and the quality of the stream is fantastic, but having a couple issues that I haven't been able to resolve on my own.

  1. Filters don't seem to be working. e.g. I have the
enabled_networks = ESPN+
exclude_networks =

require_espn_plus = true

In my filters.ini file, but still get events for things ACCN, ESPN, ESPN2, etc. Do I need to exclude all others? I ran the rebuild command and it appeared to run successfully, but the details of the schedule update only showed in the terminal window. Don't know if that is expected or not.

  1. Also, I am getting a "cron: can't lock /var/run/crond.pid, otherpid may be 21: Resource temporarily unavailable" as the first line in the log file. Thought it might be a permissions issue and tried everything I could think of including running as root and still get the error. It may not have been long enough for the cron

Thank you so much for this, love having ESPN+ back.