### Wrapper Chroot and Namespace Setup Source: https://context7.com/worldobservationlog/wrapper/llms.txt Illustrates the execution chain of the wrapper binary, including setting up the chroot environment, bind-mounting, and optional PID namespace isolation. ```sh # wrapper sets up the chroot and forks; main runs inside. # Execution chain: # ./wrapper [args] # → bind-mount /dev/urandom into ./rootfs/dev/urandom # → chdir + chroot into ./rootfs # → (optional) unshare(CLONE_NEWPID) # → fork() # → child: mount proc, mkdir base-dir, execve /system/bin/main [args] # → parent: wait() and exit ``` -------------------------------- ### Install LLVM Source: https://github.com/worldobservationlog/wrapper/blob/main/README.md Installs LLVM by executing a script. This is a dependency for building the wrapper from source. ```bash sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ``` -------------------------------- ### Docker: First-time login Source: https://context7.com/worldobservationlog/wrapper/llms.txt Example of running the wrapper in a Docker container for the first-time login. It mounts the host's rootfs data directory and uses the wrapper binary as the entrypoint. ```sh # 2. First-time login — runs wrapper in login mode, then exits. # Credentials are persisted in ./rootfs/data on the host. docker run --privileged --rm -it \ -v ./rootfs/data:/app/rootfs/data \ --entrypoint ./wrapper \ ghcr.io/worldobservationlog/wrapper:local \ -L "user@example.com:MyP@ssword" -H 0.0.0.0 # Press Ctrl-C after login completes. ``` -------------------------------- ### Wrapper CLI: Subsequent runs Source: https://context7.com/worldobservationlog/wrapper/llms.txt Example for running the wrapper after a successful first-time login. It uses the existing session data stored in the rootfs. ```sh # Subsequent runs (session already stored in rootfs/data) ./wrapper -H 0.0.0.0 ``` -------------------------------- ### Wrapper CLI: With proxy Source: https://context7.com/worldobservationlog/wrapper/llms.txt Example demonstrating how to configure the wrapper to use an HTTP/HTTPS proxy for its network requests. ```sh # With proxy ./wrapper -H 0.0.0.0 -P http://127.0.0.1:8080 ``` -------------------------------- ### Wrapper CLI: First-time login (interactive 2FA) Source: https://context7.com/worldobservationlog/wrapper/llms.txt Example of initiating a first-time login with Apple ID credentials. This method will prompt for a 2FA code interactively on stdin if required. ```sh # First-time login (interactive 2FA prompt on stdin) ./wrapper -L "user@example.com:MyP@ssword" -H 0.0.0.0 ``` -------------------------------- ### Install Build Dependencies (Debian/Ubuntu) Source: https://github.com/worldobservationlog/wrapper/blob/main/README.md Installs essential build tools required for compiling the wrapper from source on Debian-based systems. ```bash sudo apt install build-essential cmake curl unzip git ``` -------------------------------- ### Wrapper CLI: First-time login (2FA from file) Source: https://context7.com/worldobservationlog/wrapper/llms.txt Example for first-time login where the 2FA code is read from a file named '2fa.txt' within the specified base directory. This is useful for automated environments like Docker. ```sh # First-time login with 2FA code written to a file (useful inside Docker) ./wrapper -L "user@example.com:MyP@ssword" -F -H 0.0.0.0 # Then in another terminal: echo -n 123456 > rootfs/data/data/com.apple.android.music/files/2fa.txt ``` -------------------------------- ### Run Docker Container (Interactive) Source: https://github.com/worldobservationlog/wrapper/blob/main/README.md Runs a Docker container interactively for initial setup or testing. Mounts a local directory for data persistence and sets the entrypoint to the wrapper executable. ```bash docker run --privileged --rm -it -v ./rootfs/data:/app/rootfs/data --entrypoint ./wrapper ghcr.io/worldobservationlog/wrapper:local -L "username:password" -H 0.0.0.0 ``` -------------------------------- ### Wrapper CLI: Custom ports Source: https://context7.com/worldobservationlog/wrapper/llms.txt Example showing how to specify custom port numbers for the decrypt, M3U8, and account sockets. ```sh # Custom ports ./wrapper -H 0.0.0.0 -D 11020 -M 21020 -A 31020 ``` -------------------------------- ### Docker Compose: Auto-login Source: https://context7.com/worldobservationlog/wrapper/llms.txt Example of using Docker Compose to run the wrapper, with environment variables for username and password enabling automatic first-time login via the entrypoint script. ```sh # Docker Compose (sets USERNAME/PASSWORD from environment for auto-login) USERNAME="user@example.com" PASSWORD="MyP@ssword" docker compose up ``` -------------------------------- ### Docker: Normal run Source: https://context7.com/worldobservationlog/wrapper/llms.txt Example of running the wrapper in a Docker container for normal operation after a successful login. It maps the service ports and passes arguments via an environment variable. ```sh # 3. Normal run — session already stored, starts all three service sockets. docker run --privileged \ -v ./rootfs/data:/app/rootfs/data \ -p 10020:10020 \ -p 20020:20020 \ -p 30020:30020 \ -e args="-H 0.0.0.0" \ ghcr.io/worldobservationlog/wrapper:local ``` -------------------------------- ### Account Info HTTP Server (Port 30020) Source: https://context7.com/worldobservationlog/wrapper/llms.txt An HTTP/1.1 server that provides access to cached Apple Music account tokens. It supports GET and POST requests and returns the tokens as a JSON object. ```APIDOC ## Account Info HTTP Server (Port 30020) — `handle_account` An HTTP/1.1 server (GET or POST) that returns the cached Apple Music account tokens as JSON. This endpoint is populated once at startup after authentication and stays constant for the lifetime of the process. ### Endpoint GET / POST / ### Response #### Success Response (200) Returns a JSON object containing account tokens. - **storefront_id** (string) - The storefront ID. - **developer_token** (string) - The developer token. - **music_user_token** (string) - The music user token. ### Response Example ```json { "storefront_id": "143441", "developer_token": "...", "music_user_token": "..." } ``` ### Request Example (Shell) ```sh # Retrieve account tokens (storefront ID, developer token, music user token) curl -s http://127.0.0.1:30020/ ``` ``` -------------------------------- ### Retrieve Account Tokens via HTTP Source: https://context7.com/worldobservationlog/wrapper/llms.txt Fetches cached Apple Music account tokens (storefront ID, developer token, music user token) from the account info HTTP server running on port 30020. Supports GET or POST requests. ```sh # Retrieve account tokens (storefront ID, developer token, music user token) curl -s http://127.0.0.1:30020/ ``` -------------------------------- ### Build Wrapper from Source Source: https://github.com/worldobservationlog/wrapper/blob/main/README.md Clones the wrapper repository, navigates into the directory, creates a build folder, configures the build with CMake, and compiles the project using make with parallel jobs. ```bash git clone https://github.com/WorldObservationLog/wrapper cd wrapper mkdir build cd build cmake .. make -j$(nproc) ``` -------------------------------- ### Wrapper Command-Line Options Source: https://github.com/worldobservationlog/wrapper/blob/main/README.md Displays the available command-line options for the wrapper executable, including help, version, host, ports, proxy, and login parameters. ```text Usage: wrapper [OPTION]... -h, --help Print help and exit -V, --version Print version and exit -H, --host=STRING (default=`127.0.0.1') -D, --decrypt-port=INT (default=`10020') -M, --m3u8-port=INT (default=`20020') -A, --account-port=INT (default=`30020') -P, --proxy=STRING (default=`') -L, --login=STRING (username:password) -F, --code-from-file (default=off) ``` -------------------------------- ### Wrapper CLI Usage Reference Source: https://context7.com/worldobservationlog/wrapper/llms.txt This is the full command-line usage reference for the wrapper binary, detailing all available options and their default values. Flags are passed through to the main payload. ```sh # Full usage reference (version 1.2.0) wrapper [OPTION]... -h, --help Print help and exit -V, --version Print version and exit -H, --host=STRING Bind host for all three sockets (default: 127.0.0.1) -D, --decrypt-port=INT Port for the raw decrypt socket (default: 10020) -M, --m3u8-port=INT Port for the M3U8 URL socket (default: 20020) -A, --account-port=INT Port for the account HTTP server (default: 30020) -P, --proxy=STRING HTTP/HTTPS proxy (e.g. http://127.0.0.1:8080) -L, --login=STRING Apple ID credentials: username:password -F, --code-from-file Read 2FA code from rootfs//2fa.txt --base-dir=STRING FairPlay data directory inside rootfs (default: /data/data/com.apple.android.music/files) --device-info=STRING Slash-separated device fingerprint: ClientId/VersionId/Platform/ProductVer/Model/ Build/Locale/Language/AndroidID (default: Music/4.9/Android/10/Samsung S9/ 7663313/en-US/en-US/dc28071e981c439e) ``` -------------------------------- ### Build Docker Image Source: https://github.com/worldobservationlog/wrapper/blob/main/README.md Builds the Docker image for the wrapper tool. Use this command to create a local image. ```bash docker build --tag ghcr.io/worldobservationlog/wrapper:local . ``` -------------------------------- ### Run Wrapper Without Docker Source: https://context7.com/worldobservationlog/wrapper/llms.txt Execute the wrapper binary directly. This requires root privileges or CAP_SYS_ADMIN and CAP_SYS_CHROOT capabilities. Specify the host and user credentials for authentication. ```bash sudo ./wrapper -H 127.0.0.1 -L "user@example.com:password" ``` -------------------------------- ### Build Wrapper for Android ARM64 using Docker Source: https://context7.com/worldobservationlog/wrapper/llms.txt Builds the wrapper project for the ARM64 architecture using Docker, specifying host and runtime platforms. ```bash docker build \ --build-arg BUILD_PLATFORM=linux/amd64 \ --build-arg RUNTIME_PLATFORM=linux/arm64 \ --build-arg TARGET_ARCH=arm64 \ --tag wrapper:arm64 . ``` -------------------------------- ### Run Docker Container (Background) Source: https://github.com/worldobservationlog/wrapper/blob/main/README.md Runs the wrapper tool as a Docker container in the background, exposing necessary ports and setting environment variables for arguments. ```bash docker run --privileged -v ./rootfs/data:/app/rootfs/data -p 10020:10020 -p 20020:20020 -p 30020:30020 -e args="-H 0.0.0.0" ghcr.io/worldobservationlog/wrapper:local ``` -------------------------------- ### Docker: Build Image Source: https://context7.com/worldobservationlog/wrapper/llms.txt Command to build the Docker image for the wrapper. This step requires the Dockerfile and potentially pre-built binaries or the Android NDK. ```sh # 1. Build the Docker image (requires prebuilt wrapper binary in the build context, # OR the full NDK build path — NDK download is ~1 GB) docker build --tag ghcr.io/worldobservationlog/wrapper:local . ``` -------------------------------- ### Fetch Storefront and Auth Tokens Source: https://context7.com/worldobservationlog/wrapper/llms.txt Fetches storefront ID, development token, and music token from a local server. The music token is then used with the Apple Music API. ```sh # Parse with jq STOREFRONT=$(curl -s http://127.0.0.1:30020/ | jq -r .storefront_id) DEV_TOKEN=$(curl -s http://127.0.0.1:30020/ | jq -r .dev_token) MUSIC_TOKEN=$(curl -s http://127.0.0.1:30020/ | jq -r .music_token) # Use music_token with the Apple Music API curl -s "https://api.music.apple.com/v1/catalog/${STOREFRONT}/songs/1234567890" \ -H "Authorization: Bearer ${DEV_TOKEN}" \ -H "Music-User-Token: ${MUSIC_TOKEN}" ``` -------------------------------- ### Download and Unzip Android NDK Source: https://github.com/worldobservationlog/wrapper/blob/main/README.md Downloads and extracts the Android NDK r23b, a required dependency for building the wrapper from source. ```bash curl -fLO https://dl.google.com/android/repository/android-ndk-r23b-linux.zip unzip -d . android-ndk-r23b-linux.zip ``` -------------------------------- ### Preshare FairPlay Context Initialization Source: https://context7.com/worldobservationlog/wrapper/llms.txt Initializes a FairPlay decryption context for offline accounts using a special Adam ID '0'. This context is pre-warmed at startup. ```c // Preshare context is pre-warmed at startup for offline accounts: preshareCtx = getKdContext("0", "skd://itunes.apple.com/P000000000/s1/e1"); ``` -------------------------------- ### Interactive Login with 2FA Source: https://context7.com/worldobservationlog/wrapper/llms.txt Initiates an interactive login process, prompting for 2FA codes via stdin. Displays connection information upon successful authentication. ```sh # Interactive login (2FA entered on stdin) ./wrapper -L "user@example.com:P@ssword!" -H 0.0.0.0 # [+] starting... # [+] initializing ctx... # [+] logging in... # [.] credentialHandler: {title: Sign In, message: ..., 2FA: false} # [+] StoreFront ID: 143441-2,32 # [+] Music-Token: Au1eHaKo...... # [!] listening 0.0.0.0:10020 # [!] listening m3u8 request on 0.0.0.0:20020 # [!] listening account info request on 0.0.0.0:30020 ``` -------------------------------- ### File-Based 2FA for Non-Interactive Environments Source: https://context7.com/worldobservationlog/wrapper/llms.txt Sets up a file-based 2FA mechanism for non-interactive environments like Docker. The 2FA code is read from a specified file. ```sh # File-based 2FA (non-interactive / Docker) ./wrapper -L "user@example.com:P@ssword!" -F -H 0.0.0.0 & # In another shell, once the [!] Waiting for input... prompt appears: echo -n 123456 > rootfs/data/data/com.apple.android.music/files/2fa.txt # The process detects the file, reads the code, removes the file, and continues. ``` -------------------------------- ### Python Client for Decrypting Audio Samples Source: https://context7.com/worldobservationlog/wrapper/llms.txt Connects to the decrypt socket (port 10020) to send an Adam ID and key URI, then streams encrypted audio samples for decryption. Requires the server to be running and accessible. ```python import socket, struct def decrypt_samples(adam_id: str, key_uri: str, samples: list[bytes]) -> list[bytes]: with socket.create_connection(("127.0.0.1", 10020)) as s: # Send Adam ID adam = adam_id.encode() s.sendall(bytes([len(adam)]) + adam) # Send key URI uri = key_uri.encode() s.sendall(bytes([len(uri)]) + uri) # Send each sample and collect decrypted output results = [] for sample in samples: s.sendall(struct.pack(" str | None: """ Send an Adam ID string to the M3U8 socket. Returns the HLS playlist URL or None on failure. """ with socket.create_connection((host, port)) as s: payload = adam_id.encode() s.sendall(bytes([len(payload)]) + payload) # Response is the URL followed by '\n'; a bare '\n' means failure response = b"" while not response.endswith(b"\n"): chunk = s.recv(4096) if not chunk: break response += chunk url = response.decode().strip() return url if url else None # Usage m3u8 = get_m3u8_url("1234567890") if m3u8: print(f"M3U8 URL: {m3u8}") # e.g. https://aod.itunes.apple.com/itunes-assets/...playlist.m3u8?... else: print("Failed to get M3U8 (check Adam ID or subscription status)") ``` -------------------------------- ### Per-Song FairPlay Context Retrieval and Refresh Source: https://context7.com/worldobservationlog/wrapper/llms.txt Retrieves a FairPlay decryption context for a specific song (Adam ID). If retrieval fails, it forces a refresh of the decryption context and lease. ```c // Per-song context (resolved on first use per Adam ID): void *ctx = getKdContext("1234567890", "skd://itunes.apple.com/P000000000/s1/e1"); if (ctx == NULL) { fprintf(stderr, "Failed to obtain KD context — lease may have expired\n"); refresh_decrypt_ctx(); // reset lease + all contexts, then retry } ``` -------------------------------- ### Decrypt Socket (Port 10020) Source: https://context7.com/worldobservationlog/wrapper/llms.txt The decrypt socket handles persistent binary protocol connections for decrypting audio samples. Clients send an Adam ID and key URI, followed by encrypted audio samples. The server decrypts each sample in-place and returns the plaintext. ```APIDOC ## Decrypt Socket (Port 10020) — `handle` / `handle_cpp` The decrypt socket accepts a persistent binary protocol connection. For each song, the client sends the Adam ID and key URI, then streams encrypted audio samples; the server decrypts each sample in-place using the FairPlay KD context and writes the plaintext back. The C++ shim `handle_cpp` wraps `handle` to catch C++ exceptions thrown by the Apple libraries. ### Protocol Description Client → Server per song: [1 byte] adamSize — length of Adam ID string [N bytes] adam — Adam ID (e.g. "1234567890") [1 byte] uri_size — length of key URI string [M bytes] uri — key URI (e.g. "skd://itunes.apple.com/P000000000/s1/e1") Then, per encrypted sample: [4 bytes] size (uint32_t) — sample length; send 0 to signal end-of-song [N bytes] encrypted_sample — raw encrypted audio bytes Server → Client per sample: [N bytes] decrypted_sample — plaintext audio bytes (same length) ### Request Example (Python) ```python import socket, struct def decrypt_samples(adam_id: str, key_uri: str, samples: list[bytes]) -> list[bytes]: with socket.create_connection(("127.0.0.1", 10020)) as s: # Send Adam ID adam = adam_id.encode() s.sendall(bytes([len(adam)]) + adam) # Send key URI uri = key_uri.encode() s.sendall(bytes([len(uri)]) + uri) # Send each sample and collect decrypted output results = [] for sample in samples: s.sendall(struct.pack(" str | None: """ Send an Adam ID string to the M3U8 socket. Returns the HLS playlist URL or None on failure. """ with socket.create_connection((host, port)) as s: payload = adam_id.encode() s.sendall(bytes([len(payload)]) + payload) # Response is the URL followed by '\n'; a bare '\n' means failure response = b"" while not response.endswith(b"\n"): chunk = s.recv(4096) if not chunk: break response += chunk url = response.decode().strip() return url if url else None # Usage m3u8 = get_m3u8_url("1234567890") if m3u8: print(f"M3U8 URL: {m3u8}") # e.g. https://aod.itunes.apple.com/itunes-assets/...playlist.m3u8?... else: print("Failed to get M3U8 (check Adam ID or subscription status)") ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.