Releases: pepperonas/flipper-the-ripper
Release list
Flipper the Ripper v1.10.0
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 indocs/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
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 inHomeViewModel,
whichhiltViewModel()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
ShareLinkHandlerthat 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.
RUNNINGused 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 and0 %…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
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
What's new
Changed
- One APK per release — the 32-bit build is gone. Every release since 1.1.1 shipped two files,
arm64-v8aandarmeabi-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.mdand 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. ASHA256SUMS.txtis 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 missingACCESS_NETWORK_STATE; X and Dailymotion were missing from the platform lists
in bothSECURITY.mdandCONTRIBUTING.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
Full Changelog: v1.8.2...v1.8.3
Flipper the Ripper v1.8.2
Full Changelog: v1.8.1...v1.8.2
Flipper the Ripper v1.8.1
Full Changelog: v1.8.0...v1.8.1
Flipper the Ripper v1.8.0
Full Changelog: v1.7.0...v1.8.0
Flipper the Ripper v1.7.0
Full Changelog: v1.6.0...v1.7.0
Flipper the Ripper v1.6.0
Full Changelog: v1.5.0...v1.6.0