When YouTube rotates its player JS, the existing configs no longer match the new player hash. Adding a new entry toDocumentation 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.
player_configs.json and pushing to the repository’s master branch fixes all deployed apps running Zemer Cipher within minutes — no APK release needed. Deployed apps fetch configs from the raw GitHub URL at runtime (6-hour TTL) and force-refresh the moment they encounter an unknown player hash.
When to Add a Config
The trigger for adding a new config entry is playback failure following a YouTube player rotation. Symptoms include:- HTTP 403 responses on stream URLs from
WEB_REMIXorWEBclients - Audio or video quality degradation (streams cut off early or serve only partial content)
- Timber log output:
No hardcoded config for hash: <hash>— this is the most reliable indicator that a new player has been deployed and is not yet in the config table
/s/player/{hash}/player_ias.vflset/).
Config Entry Format
Each entry inplayer_configs.json maps an 8-hex player hash to its deobfuscation parameters:
| Field | Description | Validation |
|---|---|---|
| hash (key) | 8-hex player hash from the player JS URL | Must be unique across all primary keys |
| sig | Signature call expression. INPUT is substituted with the obfuscated signature at runtime. | Must match ^[A-Za-z0-9$_]{1,8}\(\d+,\d+,INPUT\)$ |
| nClass | URL class name for the n-transform IIFE built from a local template | Must match ^[A-Za-z0-9$_]{1,8}$ |
| sts | signatureTimestamp integer sent in InnerTube /player requests | Must be a positive integer |
| aliases | Optional array of 8-hex MD5-of-first-10000-bytes fallback hashes for the same player | Each alias must be globally unique |
Validating a New Entry
Before adding an entry, validate it against a live stream to confirm the CDN accepts the deciphered URL. From thezemer-app repository:
(constantArg1, constantArg2) pairs can produce output that looks like a valid URL, but only the correct pair produces a URL the CDN accepts. The script prints a paste-ready JSON entry on success.
Adding an Entry
Identify the new player hash
Find the hash in Timber logs (
No hardcoded config for hash: <hash>) or extract it from the YouTube iframe_api endpoint at https://www.youtube.com/iframe_api, which contains a path of the form /s/player/{hash}/player_ias.vflset/.Run the validation script
Run
node tests/validate-player-config.mjs <hash> in the zemer-app repository. This deciphers a live stream and confirms CDN acceptance with an HTTP 206 response. Copy the printed JSON entry.Add the entry to player_configs.json
Open
library/src/main/assets/player_configs.json and add the new entry inside the "players" object. Keep the file alphabetically or chronologically ordered by hash for readability.Run unit tests
Run
./gradlew :library:testDebugUnitTest to catch duplicate primary hashes, duplicate aliases, or alias-equals-primary-hash collisions before pushing. A file rejected by the parser leaves all deployed apps on their previous config.Push to master
Push to the
master branch. Deployed apps performing a failure-triggered refresh resolve the unknown hash immediately; apps performing the routine startup refresh pick up the new entry within 6 hours.Collision Rules
PlayerConfigParser is the validation boundary for all config data. It enforces strict uniqueness rules:
- A duplicate primary hash (two entries with the same key) causes the entire file to be rejected.
- A duplicate alias (two entries sharing an alias value) causes the entire file to be rejected.
- An alias that equals another entry’s primary hash causes the entire file to be rejected.
master. Fix collisions before pushing.
Schema Version
TheschemaVersion field is currently 1. Older apps that receive a file with a higher schemaVersion automatically reject it and continue operating from their last-good config table — they do not break.
Only bump schemaVersion for breaking shape changes (for example, renaming a required field or changing the type of an existing field). Adding a new optional field within the existing shape does not require a version bump.