cd /news/developer-tools/how-i-spent-40-hours-building-a-serv… · home › topics › developer-tools › article
[ARTICLE · art-159942] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=↑ positive

How I Spent 40 Hours Building a Serverless 8K Video Downloader on Android by Embedding CPython and FFmpeg

A developer spent 40 hours building a fully on-device Android video downloader that embeds a CPython 3.11 runtime and an ARM64 FFmpeg binary inside a Kotlin and Jetpack Compose app, enabling 4K and 8K downloads with no servers. The approach bypasses YouTube's 360p ceiling by muxing separate DASH video and audio streams locally, and sidesteps datacenter IP blocking by running entirely on the phone's own network. The build also embeds the QuickJS engine to solve YouTube's n-sig JavaScript challenges and uses a foreground data-sync service with a partial wake lock to keep downloads alive when the screen is off.

by read4 min views4 publishedOct 28, 2026

Building a mobile YouTube down seems straightforward at first glance—until you actually try to build one that runs 100% on-device, downloads in 4K and 8K, requires zero servers, and costs ₹0.

Over the last 40 hours of continuous research, day-and-night testing, and hundreds of failed experiments, I went down the deepest rabbit hole of mobile systems engineering I’ve ever experienced.

Here is the story of how I bypassed the infamous "360p ceiling," why free cloud hosting failed, and how I ended up embedding a full CPython 3.11 runtime and an ARM64 FFmpeg binary directly inside a modern Kotlin + Jetpack Compose Android app.

My original goal was simple: write a lightweight client using cross-platform tooling (Flutter + Dart) to download videos directly to mobile storage.

I quickly hit three massive walls:

YouTube stopped serving combined video-and-audio files for high resolutions years ago. If you want 720p, 1080p, 1440p, 4K, or 8K, YouTube serves them via DASH (Dynamic Adaptive Streaming over HTTP)—meaning the video track and the audio track are two completely separate network streams.

To get a playable .mp4, you must have a multiplexer ( muxer) like FFmpeg running locally to combine them. Without an on-device muxer, pure mobile scrapers are permanently locked to legacy 360p (itag 18).

When down direct HTTP stream chunks, downloads would start at full speed, hit around 5 MB, and suddenly choke to 0 KB/s before failing. YouTube's CDN actively rate-limits and drops TCP sockets if the client does not continuously solve and send valid player tokens.

Direct scraping triggered YouTube’s bot detection systems: HTTP 403 Forbidden and "Sign in to confirm you're not a bot".

Thinking the mobile device’s network or CPU was the issue, I moved the extraction engine to free cloud hosting platforms:

Every single cloud host failed. Why? Datacenter IP reputation.

YouTube maintains real-time blacklists of known cloud provider IP CIDR blocks (AWS, Google Cloud, Fastly, Cloudflare). The moment a request comes from a cloud datacenter IP, YouTube demands a CAPTCHA or blocks the connection outright.

A remote cloud server was not going to work. The only way to guarantee longevity was to make the app run entirely locally on the user's phone IP.

I stepped back and built a standalone Python desktop script using Tkinter, yt-dlp, and native ffmpeg.

On my PC, it worked flawlessly:

That led to the ultimate question:

"If a local CPython engine and FFmpeg can download 8K videos without any server, why can't we bundle that entire engine inside an Android APK?"

I started a clean native Android project built with Kotlin 2.0, Jetpack Compose, and Media3 ExoPlayer.

Here are the key technical challenges I had to overcome to embed CPython on Android:

Modern Android (API 29+) forbids executing binaries from external storage or normal cache partitions (noexec mount flag).

assets/ into the app's private internal storage (/data/user/0/<package>/files/runtime/).

  android.system.Os.chmod(binaryFile.absolutePath, 493) // 0755 rwxr-xr-x

PYTHONHOME, PYTHONPATH, and LD_LIBRARY_PATH) are mapped dynamically so Python can load shared cryptographic libraries (libcrypto.so, libssl.so). YouTube uses dynamic JavaScript challenges (n-sig transformations) to throttle download speeds. Rather than bundling a bulky Node.js binary, I embedded QuickJS (libqjs.so), a tiny, lightweight C-based JavaScript engine that allows yt-dlp to evaluate cipher challenges in milliseconds directly on mobile.

High-resolution 4K/8K multiplexing consumes CPU cycles. When the user turned off the screen, Android's battery manager would kill the child process mid-stream.

FOREGROUND_SERVICE_TYPE_DATA_SYNC and a PARTIAL_WAKE_LOCK. Real-time stdout from the child process is parsed into an ongoing notification progress bar with download speed (MB/s) and ETA. Under Android 11–15 Scoped Storage, child processes cannot write directly to /storage/emulated/0/Download/.

context.cacheDir/staging/). Once FFmpeg finishes merging, a native Kotlin worker commits the file directly into MediaStore.Video or MediaStore.Audio using Android's ContentResolver. The files immediately appear in the user's Gallery, VLC, and MX Player. Down high-quality media is only half the experience; playing it is the other half. I built a native player powered by Media3 ExoPlayer featuring:

FIT TO SCREEN, STRETCH, CROP, and 100% (unscaled 1:1 pixel rendering with borders all around) complete with centered bold HUD overlays.+Xs / -Xs (MM:SS) HUD.awaitPointerEvent()) only fire when the finger

  val holdJob = coroutineScope.launch {
      delay(1800L)
      if (!hasDragged && !isMultiTouch) {
          player?.setPlaybackSpeed(2.0f)
      }
  }

0.25x to 2.0x that persist across all video and audio tracks globally. After 40 hours of focused development:

The project is completely open source under the MIT License. You can check out the source code, architecture diagrams, and download the pre-compiled APK directly:

👉 GitHub Repository: https://github.com/Rajan7013/YtMax

📦 Direct v1.0.0 APK: Download from GitHub Releases

── more in #developer-tools 4 stories · sorted by recency
── more on @youtube 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/how-i-spent-40-hours…] indexed:0 read:4min 2026-10-28 · —