Skip to content

fix(extractors): detect livestreams from the thumbnail time-status overlay - #5844

Open
mdbraber wants to merge 1 commit into
iv-org:masterfrom
mdbraber:fix/livestream-thumbnail-overlay
Open

fix(extractors): detect livestreams from the thumbnail time-status overlay#5844
mdbraber wants to merge 1 commit into
iv-org:masterfrom
mdbraber:fix/livestream-thumbnail-overlay

Conversation

@mdbraber

@mdbraber mdbraber commented Jul 30, 2026

Copy link
Copy Markdown

VideoRendererParser decides liveNow solely from a "LIVE" entry in videoRenderer.badges. YouTube no longer always sends one: on the trending feed badges is absent entirely, and the only live marker is the thumbnail's time-status overlay, which carries {"style": "LIVE", "text": "LIVE"} where a duration would otherwise be.

The result is that the list and detail endpoints contradict each other for the same video id.

This is especially visible because trending is the livestreams feed now — fetch_trending browses the livestreams channel because YouTube removed the aggregated trending page (#5397) — so in practice nearly every trending item is affected. It also affects Invidious' own web UI, which renders the same liveNow.

Reproduction

Against an instance at 9d1291a0, region NL:

$ curl -s '<instance>/api/v1/trending?region=NL' | jq -r '.[] | "\(.videoId) liveNow=\(.liveNow)"'
BRha0tJtI8s liveNow=false
qEcVsczCkBw liveNow=false
...

$ curl -s '<instance>/api/v1/videos/BRha0tJtI8s' | jq .liveNow
true

14 of 15 trending items disagreed with their own detail endpoint. After the patch, 0 of 15.

The raw InnerTube payload for those items shows why — no badges key at all:

{
  "videoId": "BRha0tJtI8s",
  "badges": null,
  "lengthText": null,
  "thumbnailOverlays": [
    { "thumbnailOverlayTimeStatusRenderer": { "style": "LIVE", "text": { "simpleText": "LIVE" } } }
  ],
  "viewCountText": { "simpleText": "6,091 watching" }
}

Notes for reviewers

  • Existing badges parsing is untouched; the overlay is read as an additional source, so liveNow still comes from a "LIVE" badge wherever YouTube sends one.
  • length_seconds deliberately needs no equivalent change: the overlay text is "LIVE", which decode_length_seconds already reduces to 0 — correct for a stream with no fixed duration.
  • No false positives found. A channel's videos tab (60 items), search (20, of which 2 genuinely live) and popular (40) were all unchanged by the patch, cross-checked per item against /api/v1/videos.
  • crystal tool format --check clean.
  • Overlaps with feat(api): expose isShort on list items #5846 (feat(api): expose isShort on list items). Both insert
    a thumbnailOverlays read immediately after the badges loop in
    VideoRendererParser. Each branch applies cleanly to master on its own, but
    whichever merges second will need a one-hunk rebase — happy to do that whenever
    you like. They are independent changes, so they are offered separately rather
    than bundled.

AI disclosure (per AI_POLICY.md)

This patch was written with AI assistance.

  • Exact model: Claude Opus 5 — model ID claude-opus-5[1m]
  • Tool used to interact with it: Claude Code, Anthropic's agentic CLI

How it was checked, stated precisely so you can judge it: the JSON path was not guessed — the raw InnerTube browse payload was fetched and inspected to find where the live marker had moved. The patch was then built, deployed to a live instance, and the before/after numbers above were measured on live data, including the false-positive check across three other endpoints. It is running in production on my instance.

I am the submitter and, per the policy, solely responsible for this change. Happy to run any further test you would like before merge.

…erlay

`VideoRendererParser` decides `liveNow` solely from a "LIVE" entry in
`videoRenderer.badges`. YouTube no longer always sends one: on the trending feed
`badges` is absent entirely, and the only live marker is the thumbnail's
time-status overlay, which carries `{"style": "LIVE", "text": "LIVE"}` where a
duration would otherwise be.

The result is that the list and detail endpoints contradict each other for the
same video id: `/api/v1/trending` reports `liveNow: false` while
`/api/v1/videos/<id>` reports `liveNow: true`. This is especially visible because
trending *is* the livestreams feed now (`fetch_trending` browses the livestreams
channel since YouTube removed the aggregated trending page, iv-org#5397), so nearly
every trending item is affected. Measured 14 of 15 before, 0 of 15 after.

Read the overlay as a second source for the LiveNow badge, keeping the existing
`badges` parsing unchanged. `length_seconds` deliberately needs no equivalent
change: the overlay text is "LIVE", which `decode_length_seconds` already reduces
to 0, correct for a stream with no fixed duration.

AI disclosure, per AI_POLICY.md: this patch was written with AI assistance.
Exact model: Claude Opus 5, model ID `claude-opus-5[1m]`. Tool used to interact
with it: Claude Code, Anthropic's agentic CLI. The change was verified against a
live patched instance (see the PR for the measurements) and is running in
production on the submitter's instance. Submitted by and the responsibility of
@mdbraber.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the InnerTube VideoRendererParser so liveNow can be detected from the thumbnail time-status overlay when YouTube omits the "LIVE" metadata badge (notably on the trending/livestreams feed), preventing list and detail endpoints from disagreeing for the same video.

Changes:

  • Detect LiveNow from thumbnailOverlays[].thumbnailOverlayTimeStatusRenderer.style == "LIVE" as an additional signal alongside existing badge parsing.
  • Add explanatory in-code context for why the overlay check is necessary.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +160 to +163
# On the trending feed — which is the livestreams feed since YouTube
# removed the aggregated trending page — `badges` is absent altogether and
# the only marker is the thumbnail's time-status overlay, which reads
# `{"style": "LIVE", "text": "LIVE"}` in place of a duration.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants