Skip to content

fix(youtube): refresh rejected SABR player contexts#88

Closed
Priveetee wants to merge 2 commits into
InfinityLoop1308:mainfrom
Priveetee:fix/youtube-player-admission
Closed

fix(youtube): refresh rejected SABR player contexts#88
Priveetee wants to merge 2 commits into
InfinityLoop1308:mainfrom
Priveetee:fix/youtube-player-admission

Conversation

@Priveetee

@Priveetee Priveetee commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

fix(youtube): refresh rejected SABR player contexts

Summary

While investigating current WEB and MWEB player responses that return LOGIN_REQUIRED before exposing any streamingData, I found that the SABR path was treating two different PO-token roles as if they were one piece of state.

The Innertube /player request needs a visitor/session-bound PO token sent through serviceIntegrityDimensions.poToken. That token has to stay attached to the visitorData from the same player context. The later SABR media requests use a different, video ID-bound token in StreamerContext field 2.

The extractor already accepted raw player token and visitor values for the initial probe, but it could not refresh them after YouTube rejected the player request. A later SABR session reload also called the anonymous probe overload and silently discarded the context that had admitted the original request.

This hotfix keeps both token roles separate, preserves the player context across SABR reloads and adds one bounded refresh when YouTube explicitly rejects player admission as bot verification.

Problem

The failure happens before the SABR protocol starts:

WEB/MWEB /player
playabilityStatus.status = LOGIN_REQUIRED
reason = Sign in to confirm you are not a bot
streamingData = absent
serverAbrStreamingUrl = absent

The SABR probe previously checked for streamingData first, so this became a generic protocol error. The caller could not distinguish an expired or rejected player context from a malformed SABR response.

There were also two recovery problems:

  • the raw visitorData and player PO token had no refresh contract;
  • YoutubeSabrSession reloaded metadata anonymously, even when the session had originally been opened with valid player admission material.

Refreshing only one value is not sufficient. The visitor data and player PO token are route-affine and must be replaced together. Reusing the video ID-bound SABR media token for /player is also invalid because it has a different binding and wire role.

Without an extractor-level distinction, downstream callers either lose the original session context or have to recover around a generic missing-streamingData error.

Changes

  • Move the existing YouTube playability mapping into a shared player-response validator so normal stream extraction and SABR use the same private, age-restricted, paid, members-only, scheduled and anti-bot classifications.
  • Recognize the bot-verification response from both its current English reason and the stable structured help endpoint, so localized WEB/MWEB responses remain classifiable.
  • Add YoutubePlayerAdmissionException with the rejected client profile, playability status and non-sensitive context-presence diagnostics.
  • Add YoutubeSabrPlayerContext to keep visitorData and its player PO token as one atomic value.
  • Add YoutubeSabrPlayerContextProvider so callers can supply the current context and explicitly refresh it when rejected.
  • Retry /player exactly once after an admission rejection, then preserve both failures if the refreshed request or provider still fails.
  • Retain the provider and raw player context in YoutubeSabrInfo for metadata reloads.
  • Keep provider and player-token references transient so they are not written when SABR info is serialized.
  • Preserve the original anonymous and raw-value probe overloads for existing callers.
  • Rename the internal SABR media token state to contentPoToken and document that it belongs only in StreamerContext.
  • Keep setPoToken and getPoToken as deprecated compatibility aliases.

Recovery behavior

The provider-backed flow is now bounded:

1. Request the current visitorData + player PO token pair.
2. Call WEB/MWEB /player with both values.
3. If player admission succeeds, continue with the normal SABR flow.
4. If YouTube returns the bot-verification admission error, request one fresh pair.
5. Retry /player once with the fresh pair.
6. Return the refreshed result or surface the final typed failure.

Compatibility

Existing callers can continue using the anonymous probe or the overload that accepts raw player values. Existing YoutubeSabrStreamState#setPoToken and getPoToken calls also continue to work.

Automatic admission refresh is available only through YoutubeSabrPlayerContextProvider, because the extractor cannot mint or renew caller-owned BotGuard material by itself.

The SABR request wire format is unchanged. StreamerContext field 2 still carries the video ID-bound media token; the change makes that role explicit and prevents it from being confused with the /player token.

Validation

Build:

  • ./gradlew clean build
  • git diff --check

Local admission harness using captured official WEB and MWEB player responses:

  • classified the captured LOGIN_REQUIRED responses before the generic missing-streamingData path;
  • classified the same response after replacing the English message with localized text;
  • sent the stale visitor/token pair together on the first request;
  • requested one forced refresh and sent the fresh pair together on the second request;
  • returned SABR metadata after the refreshed player response succeeded;
  • stopped after exactly two rejected player requests;
  • retained the original admission failure when the refreshed request failed;
  • retained the original admission failure when the provider itself failed during refresh;
  • preserved the provider in memory for SABR metadata reloads;
  • confirmed that the provider and raw player token are not serialized;
  • confirmed that the legacy raw-value overload retains its context for reloads.

Notes

This change does not make PipePipeExtractor responsible for BotGuard execution or token minting. It defines the missing player-admission boundary: callers provide one atomic context, and the extractor knows when and how often that context may be refreshed.

The media-token provider remains separate because a token bound to the video ID cannot replace the visitor/session-bound token required by /player.

@InfinityLoop1308

Copy link
Copy Markdown
Owner

I think there is a mismatch. The main issue here is PipePipe doesn't currently pass potoken to the player extraction. And even if we pass it,the potoken won't be cached (unlike sabr_po_token) so the retry mechanism doesn't really add value - the potoken should always be fresh :)

@Priveetee

Copy link
Copy Markdown
Contributor Author

I went back through PipePipe Client and I mixed up two different token flows here.

PipePipe fetches the player response without a PO token. The Local DOM provider only runs afterwards, using the visitor data from that response to mint the video-bound token used for SABR requests. So there is no cached player token to refresh in PipePipe, and the retry path I added only makes sense with how TypeType currently manages its player context.

My bad, I should have checked the Client flow before opening this PR. I’m closing it and keeping this recovery on the TypeType side.

@Priveetee Priveetee closed this Jul 19, 2026
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