Skip to content

Releases: pepperonas/flipper-the-ripper

Flipper the Ripper v1.10.0

Choose a tag to compare

@github-actions github-actions released this 20 Sep 23:16

What's new

Downloads now run one at a time, in the order you see, and you can say what quality you want.
Both were measured on a device before and after; the numbers are below rather than adjectives.

Added

  • A queue that is real. Until now every download was its own background job and Android ran
    several at once, so "queued" was a label with nothing behind it — what started next was whatever
    the scheduler picked, and the order in the list was decoration. One runner now drains the queue in
    order. Measured: never more than 1 download active across 80 samples over 160 s, where two
    used to run in parallel.
  • Pause and resume. Pausing keeps what has already been fetched and resuming continues from that
    byte. Measured: paused at 52,345,281 bytes, resumed at 59,932,610 and carried on — not a restart.
    A paused download keeps its place in the queue.
  • Reorder by dragging. Waiting downloads carry a handle; the running one deliberately does not,
    because moving it would mean stopping it. The new order is what actually runs next — verified end
    to end on the device.
  • Swipe a finished download away to clear it from the list. Only finished ones: swiping away
    something still downloading would be an accident waiting to happen, and the card carries Cancel.
  • One notification for the batch — "Downloading… · 2 of 5" — instead of one per download.
  • Quality picker. The Download button gained a chevron that opens an options sheet:
    Best · 1080p · 720p · 480p · Audio only, with a line under the button saying which one will
    happen. After Load info, a tier the video does not reach is greyed out and the sheet names the
    actual maximum. Measured on one video: 480p → 38.8 MB, 720p → 161.2 MB.
  • A remembered default quality (Settings → Behaviour), used for shared links. The one-tap path
    stays one tap — the picker is an offer beside it, never a question in front of it.

Changed

  • The Video/Audio toggle is gone. "Audio only" is a quality tier in the same list, because that
    is the one decision being made. An existing Audio default is read forward, so nobody silently gets
    video back.
  • The History list has two sections — what is in the queue, and what is finished.
  • The card names the quality that was asked for, so several downloads of the same video are not a
    row of identical entries.

Fixed

  • ⚠️ The database would have deleted your download history. It was built with a destructive
    fallback, meaning the first schema change — this one — would have wiped every entry without a
    word. There is a real migration now, and a test that writes a genuine old database and checks the
    rows survive.
  • A pause could be erased a moment after you tapped it. Resolving a video's metadata takes
    3–11 s with the Pause button visible for all of it, and the phase written at the end of that
    overwrote the pause — the download carried on and, on the way out, deleted the partial file the
    pause existed to keep.
  • Resume started from zero. Every engine opens a run by clearing its working directory, which
    deleted exactly that partial file. Measured before the fix: paused at 62,436,533 bytes, resumed
    at 0.
  • A download that was only waiting looked like one that was running — spinner, moving progress
    bar and all. Defensible when a download started the instant you asked for it; untrue once there
    is a queue.
  • Every YouTube link showed the word "watch" on its card until the title resolved.
  • The server backend silently ignored the chosen quality and always returned best.

Verified

  • On the emulator with real downloads: queue order followed end to end including after a drag;
    pause/resume with the partial file measured at every step; the migration run against a database
    written by the previous version; the quality cap checked against yt-dlp's own format selection.
  • 386 unit tests, 20 instrumented, every new pin mutation-probed.

Not in this release

  • Subtitles and playlist downloads were designed for 1.10.0 and dropped before implementation;
    nothing was built for either. The design notes are kept in docs/specs.
  • Known limitation: if the ffmpeg merge fails, the existing self-heal retries with a single
    pre-muxed stream, and those top out well below 720p on YouTube. The download then succeeds at a
    lower resolution than the card says was requested. Observed once during verification.

Install

One file: flipper-the-ripper-v1.10.0.apk (64-bit ARM — every phone since about 2016).
Install it over the existing app; every release since 1.0.0 is signed with the same key.
Getting "App not installed"? See the README's
Troubleshooting table.

Verify

Signing certificate SHA-256 (apksigner verify --print-certs):

1fc3904fd80eb8135b25ee15fe62ec3b74545eba360298c152f712646fd910cb

File checksum: SHA256SUMS.txt below.

Full Changelog: v1.9.2...v1.10.0

Flipper the Ripper v1.9.2

Choose a tag to compare

@github-actions github-actions released this 20 Sep 10:29

What's new

A shared link now always turns into a download you can see. Six defects behind that, all measured
on a device before anything was changed, all of them silent — nothing crashed and nothing logged.

Fixed

  • A shared link was silently dropped when the app had last been on History or Settings.
    Reproduced 5 times out of 5. The only consumer of the shared-link bus lived in HomeViewModel,
    which hiltViewModel() creates when the Home screen composes. After the system reclaimed the
    process, the NavHost restored whichever tab was last open — if that was not Home, the ViewModel
    was never built, nobody read the bus, and the link stayed in the channel: the app opened and
    nothing happened, with no error anywhere. That is why the behaviour looked random rather than
    broken; it depended on the tab you had used last. The consumer is now an application-scoped
    ShareLinkHandler that exists from process start, so no screen can be a precondition for a
    download.

    App parked on Process killed by the system Before Now
    Home yes download starts download starts
    History yes link lost download starts
    Settings yes link lost download starts
  • The status ran backwards mid-download, and the loading animation went with it. The worker read
    the record once, while it still said QUEUED, and later wrote that stale copy back twice with
    REPLACE — after metadata resolution and again at 100 % just before saving. Measured:
    RUNNING/99.4 %QUEUED/0 %. The spinner is drawn for RUNNING, so it blinked out at exactly
    those two moments, the second time for the whole duration of the save. Both writes are now
    field-scoped updates that cannot touch the phase or the progress.

  • The real progress never reached the screen. The database recorded it on every step
    (2 → 12 → 23 → … → 99) and the notification showed it as a determinate bar, but
    DownloadEntity.toDomain() dropped the column, so the History card could only ever draw an
    indeterminate wave. Card and notification now read the same field, and the card shows the
    percentage in its status line.

  • A finished download left no notification at all. The completion notification was posted under
    the same id as the ongoing foreground notification, and WorkManager cancels that id when the
    worker ends — measured as zero notifications at +0 s, +2 s and +5 s after completion. Terminal
    notifications now have an id of their own.

  • Sharing a link left the app on the start screen for seconds with no sign of anything happening.
    Measured 1.1 s warm and 9.3 s on a cold start between the share and the first visible evidence,
    because navigation waited for preferences, the database and WorkManager. The handler now runs from
    process start (no ViewModel to build first) and moves to History before enqueuing.

  • History answered a share with "No downloads yet." Its list started as an empty list, which the
    screen cannot tell apart from "not loaded", so the empty state was drawn for 260–320 ms right
    after a share. Not-yet-loaded is now its own state.

Added

  • Real phases: Preparing → Downloading → Finishing → Saved. RUNNING used to cover four
    different things, including a 3–11 s metadata resolve where nothing downloads and an ffmpeg merge
    after the bar already read 100 %. Post-processing is detected from the engine's own status line
    (DownloadPhases), so a merge no longer reads as a finished download that is stuck. The loading
    indicator is shown for every in-flight phase, not only one of them.
  • The notification says what is happening and how far along it is: phase as the title, video
    title as the text, the percentage beside them, and tapping it opens the app. Indeterminate while
    there is no measurable total, determinate as soon as there is — never an invented number.

Changed

  • The progress callback no longer blocks the engine's download thread on a database round-trip and a
    binder call (runBlocking, roughly a hundred times per file). It hands the value to a conflated
    channel; a slow write cannot slow the download, and the newest phase always wins.
  • Cancelling covers every in-flight phase, via the domain rule rather than a hand-written list.

Verified

  • On the emulator, with real downloads: share from a cold start (8 of 8, first database row after
    1.2–2.4 s), from a running app, and after the system killed the process while parked on Home,
    History and Settings; the in-app Download button; an Instagram share (platform detected, typed
    LoginRequired — the media itself needs a logged-in session this emulator does not have).
  • The full phase sequence on one file: QUEUED → PREPARING → RUNNING with rising measured percentages
    → PROCESSING → COMPLETED, with the notification following it (Preparing… indeterminate,
    Downloading… determinate with the video title and 0 %100 %, Finishing…, then
    Download complete, which now survives the worker ending).
  • Leaving the app and killing its process mid-download: progress kept rising (4 % → 46 %) with the
    notification live throughout, and the card showed the same percentage on return.
  • A failing link: QUEUED → PREPARING → FAILED with the engine's own message in the notification.
  • Two downloads at once: independent progress, one notification each.
  • Rotation, repeatedly and with a share intent still in the activity: no duplicate download.

Install

One file: flipper-the-ripper-v1.9.2.apk (64-bit ARM — every phone since about 2016).
Install it over the existing app; every release since 1.0.0 is signed with the same key.
Getting "App not installed"? See the README's
Troubleshooting table.

Verify

Signing certificate SHA-256 (apksigner verify --print-certs):

1fc3904fd80eb8135b25ee15fe62ec3b74545eba360298c152f712646fd910cb

File checksum: SHA256SUMS.txt below.

Full Changelog: v1.9.1...v1.9.2

Flipper the Ripper v1.9.1

Choose a tag to compare

@github-actions github-actions released this 15 Sep 16:16

What's new

Fixed

  • A shared link always shows its download. Reported as "when I hand a download to the app via
    share, it must always be visible that the file is now being downloaded — show the History list and
    the animation at the top; at the moment I just see the start screen". That start screen was
    1.8.3's doing: a share stayed on Home with a snackbar, and what the user actually saw was an empty
    form (the state resets once the download is enqueued) and a message gone in four seconds. A
    download you cannot see is a download that may or may not be happening.
    A share with auto-download on now lands on History, where the new download is the top card,
    Queued or Downloading… with its progress wave — the row is written before the worker starts,
    the list is newest-first and scrolls to the top on a new entry, so the card is there the moment
    the screen appears. With auto-download off the share still opens Home with the link filled in;
    nothing is downloading yet, so the form is the right screen.
    The 1.8.3 snackbar is gone with it: buffered while History shows, it would have surfaced minutes
    later on Home about a download long finished.

Verified

  • On the emulator, from Home: share a link → History, new card on top, Downloading… with the
    wave; the same from Settings. Tapping Download by hand is unchanged.

Install

One file: flipper-the-ripper-v1.9.1.apk (64-bit ARM — every phone since about 2016).
Install it over the existing app; every release since 1.0.0 is signed with the same key.
Getting "App not installed"? See the README's
Troubleshooting table.

Verify

Signing certificate SHA-256 (apksigner verify --print-certs):

1fc3904fd80eb8135b25ee15fe62ec3b74545eba360298c152f712646fd910cb

File checksum: SHA256SUMS.txt below.

Full Changelog: v1.9.0...v1.9.1

Flipper the Ripper v1.9.0

Choose a tag to compare

@github-actions github-actions released this 15 Sep 14:56

What's new

Changed

  • One APK per release — the 32-bit build is gone. Every release since 1.1.1 shipped two files,
    arm64-v8a and armeabi-v7a, and asked people to pick. Measured over all releases the 32-bit file
    drew 30 of 177 downloads, and half of those are a crawler pattern (exactly one per file across
    thirteen consecutive 1.2.x releases). A genuinely 32-bit phone on Android 7+ is a budget device
    from 2014–2016. Meanwhile picking the wrong file failed in three directions, all ending in
    "App not installed": the 32-bit APK on a 64-bit phone with the app installed is a downgrade
    (it carries the lower version code); the 32-bit APK on a 64-bit-only device (Pixel 7+, Android 15)
    has no matching ABI; the 64-bit APK on a 32-bit phone has none either. One file removes the
    choice. 1.8.3 remains the last release for 32-bit devices, and the app no longer shows an
    update notice on a device that cannot install a release (UpdatePolicy) — the alternative was a
    permanent nag nobody could act on.
  • Version codes keep the ×10 scheme although only one ABI is built: every installed copy carries
    a code from it (1.8.3 is 282), and a plain 29 for 1.9.0 would be refused as a downgrade.
  • The GitHub release page shows the release notes. Until now it showed a single "Full Changelog"
    link while the notes sat unread in this file. The workflow now cuts the tag's section out of
    CHANGELOG.md and publishes it, followed by an install block (one file, same key since 1.0.0,
    where to look if it will not install) and a verify block.
  • The release workflow verifies the signing certificate of the built APK against the published
    digest and refuses to publish on a mismatch. A SHA256SUMS.txt is published alongside the APK.

Fixed

  • SECURITY.md said no authentication tokens are stored. That has been false since 1.2.11, when
    the optional Instagram sign-in began keeping the session cookie in the WebView cookie store — the
    README described it correctly, the security policy contradicted it. The policy now says what is
    kept, where, that it never leaves the device, and that Sign out expires it. The permission list
    gained the missing ACCESS_NETWORK_STATE; X and Dailymotion were missing from the platform lists
    in both SECURITY.md and CONTRIBUTING.md.
  • README: the architecture diagram had none of the parts added since it was drawn (motion,
    share target, navigator, link bus, update coordinator; yt-dlp also serves X and Dailymotion).
    Troubleshooting gained the "App not installed" row with its three causes. The roadmap now
    shows what shipped. The signing certificate's SHA-256 is published — for a sideloaded app it is
    the only thing that ties a file to the project, and it was written nowhere.

Added

  • README sections Testing (what the suite is, the drift guards, the mutation rule) and
    Changelog; CONTRIBUTING describes the three test conventions the repo actually follows.
  • Drift guards for the release path and the documentation: ReleaseArtifactsTest (ABI split ↔
    workflow ↔ README), ChangelogTest (a section for the version being built), ReleaseNotesScriptTest
    (the script cuts the right section and fails on an unknown tag), SecurityPolicyTest (permissions
    and platforms), SigningDocsTest (the published digest, checked against the keystore where it is
    present), UpdatePolicyTest. All mutation-probed.

Install

One file: flipper-the-ripper-v1.9.0.apk (64-bit ARM — every phone since about 2016).
Install it over the existing app; every release since 1.0.0 is signed with the same key.
Getting "App not installed"? See the README's
Troubleshooting table.

Verify

Signing certificate SHA-256 (apksigner verify --print-certs):

1fc3904fd80eb8135b25ee15fe62ec3b74545eba360298c152f712646fd910cb

File checksum: SHA256SUMS.txt below.

Full Changelog: v1.8.3...v1.9.0

Flipper the Ripper v1.8.3

Choose a tag to compare

@github-actions github-actions released this 15 Sep 10:22

Full Changelog: v1.8.2...v1.8.3

Flipper the Ripper v1.8.2

Choose a tag to compare

@github-actions github-actions released this 08 Sep 09:04

Full Changelog: v1.8.1...v1.8.2

Flipper the Ripper v1.8.1

Choose a tag to compare

@github-actions github-actions released this 08 Sep 06:51

Full Changelog: v1.8.0...v1.8.1

Flipper the Ripper v1.8.0

Choose a tag to compare

@github-actions github-actions released this 08 Sep 00:47

Full Changelog: v1.7.0...v1.8.0

Flipper the Ripper v1.7.0

Choose a tag to compare

@github-actions github-actions released this 08 Sep 00:08

Full Changelog: v1.6.0...v1.7.0

Flipper the Ripper v1.6.0

Choose a tag to compare

@github-actions github-actions released this 07 Sep 18:39

Full Changelog: v1.5.0...v1.6.0