Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ZemerTeam/zemer-cipher/llms.txt

Use this file to discover all available pages before exploring further.

Every YouTube streaming URL includes an n= query parameter. If a request reaches the CDN with an untransformed n value, the CDN recognizes that the player’s transform function was never run and intentionally caps delivery to roughly 50 KB/s — or returns a 403 entirely. Zemer Cipher prevents this by running the actual player JavaScript to transform the n value before the stream request is made.

The n-Parameter

The n value is a short, random-looking alphanumeric string embedded in the CDN URL. The player JavaScript contains an obfuscated single-argument function that accepts the raw n value and returns the accepted transformed value. The CDN validates the transformation server-side: if the value it receives does not match what the function would have produced, bandwidth is throttled regardless of connection speed. Without the transform, sustained playback at any quality above the lowest tier is effectively impossible.

How N-Transform Works

1. Identify the n-transform function FunctionNameExtractor.extractNFunctionInfo() resolves the function to use:
  • The player hash is looked up in PlayerConfigStore. If a config entry is found, the nClass field (e.g. Yx) is used to build the transform IIFE locally via PlayerConfigParser.buildNJsExpression():
(function(n){try{var u=new g.Yx('https://x.googlevideo.com/videoplayback?n='+n,true);var t=u.get('n');return(t&&t!==n)?t:n;}catch(e){return n;}})(INPUT)
  • If no config entry exists, legacy regex patterns scan the player JS to extract the function name and optional array index.
2. Inject and export as window._nTransformFunc The resolved expression is injected into the player JS as an export inside the IIFE closure, just before })(_yt_player);:
window._nTransformFunc = function(n) {
  try { return <expression with INPUT replaced by n>; } catch(e) { return n; }
};
The modified player JS is loaded into the same CipherWebView used for signature deobfuscation. Both transforms share the single WebView instance. 3. Transform the URL CipherDeobfuscator.transformNParamInUrl(url) extracts the n= value from the URL, calls webView.transformN(nValue) which dispatches a transformN(...) call via WebView.evaluateJavascript, waits for the CipherBridge.onNResult callback, and replaces the original n= value in the URL with the transformed value:
val transformedUrl = url.replaceFirst(
    Regex("([?&])n=[^&]+"),
    "$1n=${Uri.encode(transformedN)}"
)

Brute-Force Fallback

When window._nTransformFunc is not exported — because no config entry matched and regex extraction also failed — the discoverAndInit() JavaScript that runs at WebView load time performs a brute-force scan:
  1. It iterates over all property names on window using Object.getOwnPropertyNames(window).
  2. For each property that is a function with exactly one argument, it calls the function with the test input "T2Xw3pWQ_Wk0xbOg".
  3. If the result is a string that differs from the input, has at least 5 characters, and matches /^[a-zA-Z0-9_-]+$/, it is treated as the n-transform function.
  4. The first passing candidate is assigned to window._nTransformFunc and its name is reported back via CipherBridge.onDiscoveryDone.
Built-in browser globals (webkit*, on* event handlers), the CipherBridge interface itself, and previously set export slots are skipped to avoid false positives.

N-Class Regex Validation

nClass values from remote player configs are validated in PlayerConfigParser against:
^[A-Za-z0-9$_]{1,8}$
This constrains nClass to a bare JavaScript identifier — no operators, no brackets, no dots. The n-transform IIFE template is defined entirely in PlayerConfigParser.buildNJsExpression() in the library source. The remote config supplies only the identifier; the actual executable expression is always constructed locally from that fixed template.
Always call transformNParamInUrl after deobfuscateStreamUrl. Both transforms are required for streams to play without throttling or 403 errors. The signature deobfuscation produces a URL that the CDN will accept at all; the n-transform ensures it is served at full speed.

Build docs developers (and LLMs) love