Skip to content

Releases: kiinshuk/pineapple

Pineapple v1.5

Choose a tag to compare

@kiinshuk kiinshuk released this 25 Oct 19:56

Blue Pineapple theme, audio enhancement engine (EQ/presets/profiles/bass/loudness/spatial where supported), F-Droid metadata prep. Build tested via gradle assembleDebug/testDebugUnitTest.

Pineapple 1.4

Choose a tag to compare

@kiinshuk kiinshuk released this 24 Oct 16:09

Pineapple 1.4

Comes with the full navigation catalogue (artists & albums), an image-pipeline + UI polish wave, offline/reconnect, the media-notification barrier fix, and discovery carousels.

Install

Download the asset below and sideload (Android 8.0+).

size   2.20 MB (2,201,350 bytes)
sha256 6a953b53c8b31de09e846434a4e6612f791872649cb88acc4f420e954f4b4143

What's new since 1.3

Catalogue Phase 1-2.5 (artist + album pages)

  • Artist rows in search (Phase 1): Artist · audience doors; tapping opens a dedicated artist page with
    banner art, audience line, top songs, and now Albums, Videos, Fans might also like, About carousels.
  • Album pages (Phase 2): cover art, artist · year, kind · N songs · M minutes facts, ordered track list with tap-to-play.
  • Top-song / track playback uses the same TrackRow everywhere (search, artist, album, recents),
    so a track looks identical whether you found it in search or an album.

UI/UX wave

  • Coil image pipeline replaces the hand-rolled downloadBitmap pump: memory+disk cache,
    crossfade from the synthesised gradient tile, no blank flashes on scroll.
  • Now Playing — poster-bleed background replaced with a clean PinePlayerTop/Bttom two-stop
    wash (classic Apple/Spotify cleanliness), 7 dp seek bar with scaling thumb, scale-in artwork entrance.
  • Motion system PineMotion shared: easing curves, durations, springs (no per-call ad-hoc magic).
  • Notification tray player fully functional - Android 12 (API 31) was broken because
    MediaSessionService only posts when a provider is attached:
    • Added runtime POST_NOTIFICATIONS prompt on Android 13+.
    • Hard-wired DefaultMediaNotificationProvider in PlaybackService.onCreate.
    • added the media_notification_channel_name string and R import.
      Notification now shows cover, title, artist and standard play/pause/next/previous controls.
  • Discovery screen now surfaces New releases and New music videos from payloads we
    already fetched (carousels scroll horizontally), matching the visual rhythm of the rest of the
    app. Always-removed-empty-sections (no placeholders).

Network handling wave (PR #114)

  • Distinct offline / retrying / stale / failed states at the request boundary.
  • Connectivity observer registered in the MainViewModel, released on clear; reconnect banner without
    silent refetch.

Visual wave (PR #112)

  • Worked letterbox: artwork fills its square frame in all three states (empty / loading / loaded).

Catalogue integrity

  • OFFICIAL_MUSIC_TYPES, PLAYER_CLIENTS and EXPLORE_BROWSE_ID (inner constants) were never touched.
  • Search still only returns official music + the artist/album doors — user-uploads never surface.

Verification

  • InnerTubeParsingTest: 30 tests, all green (extract id/title/counts, OFFICIAL_MUSIC whitelist,
    artist detail (5 tracks, 14 albums, 17 videos, 10 related, bio), album detail (header + 11 tracks),
    mixed order assertion).
  • NetworkAccessTest: 10 tests, all green.
  • assembleDebug green + APK installed on the M21; primary actions verified.
  • assembleRelease with R8 + resource shrinking green.

Note

This app uses YouTube's private InnerTube API without an API key, the same technique NewPipe uses. It is sideloaded and cannot be published to Google Play. See Legal.

Pineapple 1.3

Choose a tag to compare

@kiinshuk kiinshuk released this 24 Oct 06:46

Pineapple 1.3

Streams YouTube music with as little ceremony as possible — now with a
real artist/album catalogue on top of search and a much tougher
network story.

Install

Download the asset below and sideload it. Android only, native Kotlin.
Android 8.0+ (minSdk 26).

size   2.19 MB (2,201,350 bytes / 2,201,350)
sha256 c2e812e776083fcbcee7058716adc20f645c47070e44253bf2a05f30ba62ec18

What's new

Catalogue (Phase 1 + Phase 2 + 2.5) — surfaced entirely from the InnerTube
payloads Pineapple already fetched.

  • Artist pages: banner header, name, audience line, Top songs, and now
    releases (Albums + Singles & EPs), Videos, Live performances, "Fans might
    also like" (related artists) and a real About/bio block (from the
    description shelf). Tap an album goes to its page, tap a related artist to
    theirs.
  • Album pages: square cover art, artist · year line, kind · N songs · M minutes fact line, ordered track list with play, library participation.
  • Search keeps artist and album doors instead of dropping them at the old
    videoId.isEmpty() guard. An artist or album row now opens a real page
    instead of being a dead entry.

Offline & slow networks (from PR #114):

  • Explicit connectivity checks at the request boundary: distinct
    "You're offline" state with the existing retry, while HTTP / rate / API
    errors keep their own message.
  • Network observer registered in the MainViewModel and released on clear;
    reconnect notices shown alongside cached results without silently
    re-requesting.
  • Cancellation + race-free bootstrap (callback installed before the initial
    snapshot), trySend under lock.

Visual polish (from PR #112):

  • Artwork letterboxing fixed: the player hero now fills its square frame in
    all three states (empty / loading / loaded).

Detail fixes:

  • Like button now fills while playback is paused.
  • Artist avatar now uses the same thumbnail path as tracks.

Verification

  • 40/40 unit tests pass (parsing: 30; network: 10)
  • assembleRelease with R8 + resource shrinking green
  • Debug + release launch clean on a Galaxy M21
  • Protected InnerTube constants untouched
    (PLAYER_CLIENTS, OFFICIAL_MUSIC_TYPES, EXPLORE_BROWSE_ID)

Note

This app uses YouTube's private InnerTube API without an API key, the same
technique NewPipe uses. It is sideloaded and cannot be published to Google
Play
. See Legal.

Pineapple 1.2

Choose a tag to compare

@kiinshuk kiinshuk released this 21 Oct 12:43

Pineapple 1.2

Streams YouTube music with as little ceremony as possible.

Install

Download Pineapple-1.2.apk and sideload it. Android only, native Kotlin.

sha256 6995e2f0b252b46bc83966141de0eaedb5dd9c22d6bb8c62b9b64e7dd0dc4e88
size   2,184,966 bytes (2.08 MB)

What's fixed since 1.0

Search in the bottom nav bar was dead. Tapping it from Discovery or
Library did nothing. The tap was being delivered the whole time — the real
cause was a stale closure. goTab compared against a composition-time tab
that the nav bar kept captured from the first frame and never advanced past
SEARCH, so Search was the one tab where next == tab matched and returned
early. Discovery and Library only worked because the stale value happened to
differ from them. Verified over eight consecutive taps after a cold start.

A cancellation was showing up as an error. Every catch in
MainViewModel caught Throwable, which includes CancellationException.
When a newer query superseded an in-flight one, the ordinary cancel was
reported to you as a red banner reading "StandaloneCoroutine was cancelled"
— sitting directly above the results that had loaded successfully. All five
handlers now rethrow it. The play path and the player polling loop had the
same latent flaw.

Icons were invisible. The bundled vector drawables left android:fillColor
off every path. A VectorDrawable path with no fill colour is transparent
black
, and Compose's Icon tints with SrcIn, which keeps the source's
alpha — so every icon was rendering perfectly at zero opacity. The build was
green throughout; it took looking at the app to catch.

Changed

  • Three-dot menu on every search result, opening the same actions sheet
    the Library uses: add to liked, play next, add to queue, add to playlist.
    Long-press does the same.
  • Removed the BEST badge from the top search result.
  • Removed the STREAMS INSTANTLY badge from the top right.
  • Icons replaced with Google's Material Symbols Rounded (Apache-2.0),
    bundled as vector drawables rather than pulled in as a dependency. The
    hand-drawn compass, library and heart approximations are gone.
  • Full-screen player gained real shuffle, repeat and an Up Next queue.

Verification

  • 23/23 unit tests pass
  • Release build with R8 and resource shrinking intact
  • Playback confirmed on device: state=3 with the queue auto-advancing
  • All nav tabs confirmed reachable on device

Note

This app uses YouTube's private InnerTube API without an API key, the same
technique NewPipe uses. It is sideloaded and cannot be published to Google
Play
. See Legal.

Pineapple 1.0

Choose a tag to compare

@kiinshuk kiinshuk released this 21 Oct 05:00

First public release of Pineapple — a 1.9 MB Android music player that plays YouTube music with no JavaScript engine, no WebView, and no proof-of-origin token.

Search, tap, sound. The top five stream URLs are resolved while the results are still on screen, so there is no spinner between the tap and the audio.

Install

  1. Download Pineapple-1.0.apk below.
  2. Open it. Android will ask you to allow installation from the app you downloaded with.
  3. Requires Android 8.0 (API 26) or later.
sha256  9d3d587d830318c7fab58bb13b9a54c8a1d0bac4c8c8669beda33ac930c21ebc
size    1.92 MB

What's in it

Playback — starts immediately, keeps going in the background with notification and lock-screen controls, and builds a mix of related tracks behind whatever you chose.

Search — queries YouTube Music rather than YouTube, so results are releases instead of everything the site has ever indexed. Filtering is a whitelist, not a blacklist: a result counts only when YouTube typed it as the track's own audio or its official video. Relevance order is preserved and the first hit is marked BEST — never re-sorted by view count, which would only promote the loudest cover.

Discovery — opens on the current trending chart plus what you played recently.

Library — likes, pinned releases, listening history and your own playlists, persisted across restarts. Long-press any track for its actions sheet.

Interface — dark throughout (#121212 ground, #FF5500 accent), a full-screen player whose background is derived from the artwork, and bottom tabs with a real back-stack: the system back button returns to the page you were on instead of closing the app.

How it plays at all

Audio was blocked for weeks by a bare Response code: 403. Three explanations were plausible and each reproduced something real — IP binding, regional gating, and an apparent 1 MiB cap. All three were wrong.

The gate is the player client that issued the URL. Measured from one IP, same minute, same video, fresh URL per request:

range ANDROID 20.10.38 VISIONOS 1.02
no Range (whole file) 403 200 — read all 4738365 bytes
bytes=0-65535 206 206
bytes=1048576-2097151 403 206
bytes=3000000-4194303 403 206

InnerTube now leads with VISIONOS 1.02 (client id 101) and sends X-Goog-Visitor-Id. Nothing else changed. Proof-of-origin tokens turned out to be required by none of it — the working stream URL contains no pot field at all.

Full story in the README.

Verified

  • 23/23 unit tests passing, pinning YouTube's exact response schemas
  • Playback confirmed on device: state=3 with the position advancing across repeated readings
  • Stream-URL prefetch 5/5
  • Built with R8 + resource shrinking (12.5 MB debug → 1.9 MB release)

Known limitations

  • The queue is not music-only yet — related tracks come from the plain web /next, so a continuous mix can occasionally hand you a vlog between songs.
  • Discovery's chart is a single /browse call; if YouTube renames the explore shelves it falls back to the first carousel rather than erroring.
  • It will break when YouTube changes. See the legal notice below.

Building it yourself

./gradlew clean assembleRelease --console=plain

JDK 17 and ANDROID_HOME required. See CONTRIBUTING.md for setup, the two rules that shape the codebase, and how to diagnose a broken build with the probes in tools/.

Legal

This uses YouTube's private InnerTube API without an API key, which falls outside Google's Terms of Service. It is an independent, sideloaded project and cannot be published to Google Play. It is not affiliated with, endorsed by, or connected to YouTube, Google, or their affiliates. The same technique is used by NewPipe.

Released under the MIT License.