Skip to content

Anon implementation - #218

Open
Dum4G wants to merge 3 commits into
ryanbr:masterfrom
Dum4G:master
Open

Anon implementation#218
Dum4G wants to merge 3 commits into
ryanbr:masterfrom
Dum4G:master

Conversation

@Dum4G

@Dum4G Dum4G commented May 10, 2026

Copy link
Copy Markdown

This is my POC attempt to mimic Xtra client behavior using VAFT as a base script. Tested for a few days and it's mostly reliable for me. Chrome 147, latest Tampermonkey, no uBo userscripts applied

Core insight from Xtra (Android client): injecting a fresh random X-Device-Id (32 hex chars, UUID format without dashes) into the PlaybackAccessToken GQL request prevents Twitch from assigning a preroll to the session. The Authorization header stays intact — only the device ID changes.

What was added:
1. Main stream token interception (hookFetch)
Every outgoing PlaybackAccessToken GQL request (excluding picture-by-picture) gets its X-Device-Id / Device-ID header replaced with a freshly generated random value via crypto.getRandomValues. The key name is matched case-insensitively to avoid duplicate-header 400s. Client-Integrity is removed alongside Authorization only on the first anonymous load (see below).
2. Anonymous initial load
On the first PlaybackAccessToken request per channel, the request is anonymised: Client-Id is replaced with the Xtra mobile client ID (kd1unb4b3q4t58fwlpcbzcbnm76a8fp), and Authorization / Client-Integrity headers are stripped. This mirrors Xtra's fresh-install behaviour and skips the preroll entirely.
3. Backup token uses the same mobile client ID + random device ID
When a midroll is detected and the backup GQL request is made in the worker, it also uses kd1unb4b3q4t58fwlpcbzcbnm76a8fp + a new random X-Device-Id per attempt. Authorization is included if captured from the main session.
4. Backup contamination handling
If the backup media playlist contains ad tags, the encodings cache is invalidated so the next poll requests a fresh Usher URL (different CDN assignment). After a configurable number of consecutive contaminated polls, backup attempts stop and the break falls back to segment stripping only. Counters reset at the end of each ad break.

Initial plan was to use 1080p anonymous stream as a fallback and then switch back to authorized stream, consider multiple reattempts if anonymous stream also poisoned with ads. In fact I got exact Xtra behavior where only 1 attempt makes sense and a switch never happens, so likely scope.ReloadPlayerAfterAd = true may also be set to false

Drawbacks and advantages:
1440p playback will no longer be possible
In current form you're likely to wait for 20 seconds before the stream starts because preroll ad isn't being skipped. Xtra doesn't seem to have this problem as apparent, so there must be room for improvement
I believe there's a lesser chance to catch a preroll while being unauthorized anyway
Much lesser chance of seing midrolls
Player will show a message that you're not logged in
You will see a message that an ad is being stripped even when the content is already played
FFZ hooks may break on ReloadPlayerAfterAd action

However, this script doesn't affect your points and streaks somehow

You're free to make other variants and keep gamble, I'm tired of free spinning each 5h

@ryanbr

ryanbr commented May 13, 2026

Copy link
Copy Markdown
Owner

Probably too much breakages to consider

@Dum4G

Dum4G commented May 13, 2026

Copy link
Copy Markdown
Author

Treat it as a draft that can be polished. The concept itself has proven to make sense. This PR is free for edits and the reffered code is open source.

@troysjanda

Copy link
Copy Markdown

Thoughts, (used Claude to find possible bugs)

vaftnew_revised.js <--- Revised and corrected File

'use strict' was dead — it appeared on line 24, after several executable statements (if checks, let _isNested, etc.). Strict mode directives must be the very first statement of their containing function to take effect. Moved it to the top of the IIFE.

No-op XHR hook removed — XMLHttpRequest.prototype.open was overridden with a function that did nothing but call through. This wasted a hook, could interfere with other extensions or CSP reporting, and added confusing noise. Removed entirely.

Redundant if/else in EarlyReloadAwaitingResult block — both branches of the conditional set streamInfo.EarlyReloadTriggered = false. The if (!streamInfo.IsStrippingAdSegments) … else … structure was completely pointless. Collapsed to a single unconditional assignment.

BackupContaminationCount threshold mismatched its comment — the comment above said "After 5 consecutive contaminated polls stop trying" but the code used >= 1, giving up on the very first contaminated poll. Changed threshold to 5 and updated the log message ratio accordingly.

adBlockDiv.P non-standard DOM property — storing a

reference as .P directly on a DOM element is unconventional and can clash with future DOM APIs or strict-mode property checks. Replaced with a WeakMap<Element, HTMLParagraphElement> (_adBlockParagraphMap).

Dangling initialLoadState.reloadTimer on page unload — if the user closes the tab or navigates away within the 15-second anonymous-to-authorized reload window, the setTimeout callback would fire against a dead context (or keep the page alive in bfcache). Added a pagehide listener to clear it.

@Dum4G

Dum4G commented May 15, 2026

Copy link
Copy Markdown
Author

BackupContaminationCount threshold mismatched its comment — the comment above said "After 5 consecutive contaminated polls stop trying" but the code used >= 1, giving up on the very first contaminated poll. Changed threshold to 5 and updated the log message ratio accordingly.

🤦‍♂️ that was intentionally done by me to check if the retry mechanics is even worth it, turned out it's not needed at all.

My only issue with current implementation is that preroll ad steals 25 secs of your first segments while Xtra player manages to get the stream playing in 5 secs

@Dum4G

Dum4G commented May 25, 2026

Copy link
Copy Markdown
Author

After many attempts and various ideas this is the artifact that works best for me. Also one of the earliest. mobile_web works best with it and it is no longer tricking the player into thinking that anonymous stream is playing. Time of loading has improved on it's own, I did nothing for it. I guess you should check on it's own as preroll alghoritms may be different per streams, per country and time of watching

Known issues: midrolls do not load in rare occasions/do not swap immersively
Some rare streams do not start at first attempt
No ad stripping will happen if you open a stream after resuming PC from sleep
PiP player may not switch to 360p and will sometimes stop until you revisit the tab

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.

3 participants