Pineapple
A native Android music player built for YouTube Music — light, fast, no account, no ads, no premium. Streams YouTube's own audio via the InnerTube API and keeps your catalogue and playback entirely on-device.
Package
com.torrenttunes.appLicenseMIT
Android8.0+ · API 26 → current
LanguageKotlin + Jetpack Compose
DependenciesZero new ones — no icon libraries, no heavy framework weight.
Sourcegithub.com/kiinshuk/pineapple
1. How it's built
Pineapple sits on a small, deliberately conservative stack so it runs well on the cheapest hardware:
- Compose + Material 3 dark-first theme with
PineBackground #0B0B0D/PineAccent #FF8A24. - Media3 / ExoPlayer for streaming audio, gapless transitions, audio focus and "audio becoming noisy" awareness.
- Hilt for the small amount of dependency injection we actually need.
- InnerTube client (
InnerTube.kt) — hand-rolled, speaks the private YouTube Music endpoints with the same client (WEB_REMIX 67, VISIONOS for playback) the official apps use.
2. What it can retrieve from the InnerTube client
- Search: songs (official music only —
OFFICIAL_MUSIC_TYPESwhitelist), artists, albums, related artists. Debounced, cancel-safe — no request per keystroke. - Discovery: trending rail, new releases, new music videos (sourced from
FEmusic_explore). - Artist pages: immersive header (name, audience count, banner), Top songs, release carousels (Albums, Singles & EPs, Videos, Live performances), related artists, and a biography.
- Album pages: square cover art, artist + year, kind / song count / duration, ordered track list.
- Playback: stream resolution, related-track continuation for an auto-building mix, artwork URL.
- Related tracks continuation to traverse search results into a listening session.
3. Playback & slow networks
- Streaming handled by ExoPlayer directly from the HTTPS stream URL — no memory download whole track.
- Connection & read timeouts, bounded retry with exponential backoff, cancellation of stale requests.
- Request dedup; limited concurrency; cached catalogue metadata (Room-independent; local storage via the existing library layer).
- Crashes and permanent transport failures are surfaced clearly as "Could not play this track" plus recoverable retry.
- Cancellation-safety / rethrowing
CancellationExceptionin every MainViewModel catch block (no false "StandaloneCoroutine was cancelled" banners). - Distinct offline and reconnect states on Search/Discovery (a reconnect shows cached results without silent refetch).
- Android media sessions for lock-screen and Bluetooth + notification (Android 12/13+; permission prompt as required).
4. Visual & interaction design rules
- Dark-first, warm accent — big, expressive album artwork.
- Custom seek bar (7 dp, rounded caps, scaled thumb), poster-derived/brand-aligned palette, no poster-bleed background.
- Coil image pipeline with memory+disk cache and crossfade placeholders.
- Stable list keys and efficient lazy rendering; no main thread work for network/DB/image.
- Empty / loading / error states never mass-replace; skeletons and inline banners.
- Touch targets 48 dp (a11y); semantic labels on controls.
- Offline behaviour gracefully hides unavailable sections (never fake entries).
5. What it isn't
- Not on Google Play. Sideload-only. Uses InnerTube like NewPipe — can't be published under Google's Terms. See README legal.
- Not a Spotify clone — visual identity is original (pineapple-orange record-store aesthetic).
- No account, no sign-in required. Favourites and recents are local; no cloud sync.
- No ads and no telemetry. No third-party audience networks; crash logging is local only.
6. Contributing & ecosystem
- CONTRIBUTING.md rules: no new dependencies; icons via bundled vector drawables only; tests on a real device via
dev.ps1. - Verified-claims discipline: bugs/UX decisions are filed against device evidence; four reported issues closed with proof.
- Good-issue backlog: numbers 12–111 (99 of them), matching the bug/UX backlog.
- Issues are the welcome queue.