Skip to content

feat: Flutter shared-preferences recovery route (v0.2) - #2

Merged
burgesQ merged 8 commits into
mainfrom
feat/flutter-prefs-route
Aug 2, 2026
Merged

burgesQ merged 8 commits into
mainfrom
feat/flutter-prefs-route

Conversation

@burgesQ

@burgesQ burgesQ commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Why

DioCache.db is only TV Time's HTTP cache and is frequently empty (the maintainer's real dump had cache_dio = 0 rows). But durable data survives elsewhere in the app's private storage, so v0.1's DioCache-only recovery returned nothing for a real user. This adds a second recovery source.

Design spec: docs/superpowers/specs/2026-08-02-flutter-prefs-route-design.md.

What

  • tvtime_core.prefs — decodes the base64 / Java-serialized ArrayList<String> braze_* values in FlutterSharedPreferences.xml (name-level library by category) and the showsWatchList in HomeWidgetPreferences.xml (structured "up next"). The flutter.user profile is intentionally not parsed.
  • tvtime_core.sources.recover_files — unified entry point: accepts a DioCache.db, the whole tvtime-private tar/zip, or loose .xml files (any mix), classifies each by content, expands archives, skips non-DioCache SQLite DBs (analytics/image caches), and merges everything into one RecoveryResult. Library rows gain a source column; prefs shows/movies fold into the library (deduped, categories unioned); home-widget up-next folds into episodes.
  • CLI is now variadic: tvtime-recover <inputs...> --out DIR | --zip FILE.
  • Web app takes multiple files / an archive and runs recover_files fully client-side; preview shows the source column; an empty HTTP cache is no longer treated as failure.
  • Version bumped to 0.2.0; README + extraction guide updated; badges added.

Testing (evidence)

  • ruff, ruff format --check, mypy --strict, pytest — 29 passing (new test_prefs.py, test_sources.py; Java list codec is round-tripped through an encoder helper, no opaque blobs).
  • Real data: CLI over the maintainer's real tvtime-private.tar → 27 shows + 307 movies + 3 up-next merged into the exports.
  • Headless Chrome against the updated web app with the real shared_prefs XMLs and the full 23 MB tar → "Recovered 334 library entries", source column populated, zero console errors.

Follow-up

🤖 Generated with Claude Code

burgesQ and others added 6 commits August 2, 2026 18:39
Add tvtime_core.prefs: decode_java_string_list decodes the base64 /
Java-serialized ArrayList<String> braze_* values, parse_flutter_prefs
categorizes the recovered show and movie names, and parse_home_widget
extracts the structured "up next" episodes from showsWatchList. This is
the second recovery source for when DioCache.db is empty. The
flutter.user profile is intentionally not parsed. TDD with a
Java-serialization encoder helper.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add tvtime_core.sources.recover_files: the unified entry point that
accepts a DioCache.db, a tvtime-private tar/zip, or loose shared_prefs
XML files (in any mix), classifies each by content, expands archives,
and merges everything into one RecoveryResult. Library rows gain a
"source" column; prefs shows/movies fold into the library (deduped
against DioCache, categories unioned) and home-widget up-next folds
into episodes. CSV rendering now unions heterogeneous row keys, and the
summary reports library_by_source.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Switch tvtime-recover to accept one or more inputs (DioCache.db, a
tvtime-private tar/zip, or shared_prefs .xml files) with output via
--out DIR or --zip FILE, routing through recover_files. Also make
recover_files skip SQLite files that have no cache_dio table, so the
app's analytics and image-cache databases inside the tar no longer
crash the run. Verified end to end against a real tvtime-private.tar.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The page now takes multiple files (or the whole tvtime-private tar/zip)
and runs recover_files over them in the browser, so recovery works when
DioCache.db is empty. Each upload is written to a fresh Pyodide FS
directory; the preview gains a source column and no longer treats an
empty HTTP cache as failure. Bump tvtime-core and tvtime-cli to 0.2.0.

Verified with headless Chrome against the real shared_prefs XMLs and the
full 23 MB tvtime-private.tar: 334 library entries recovered, no errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Explain that when DioCache.db is empty the tool recovers a name-level
library and up-next list from shared_prefs, that you can hand it the
whole tvtime-private tar, and update the CLI usage to the new
variadic --out/--zip form. Add CI/Pages/web/license badges.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move GitHub Pages to the gh-pages branch so PRs get browsable previews:
production (push to main) deploys web/ to the branch root and preserves
pr-preview/, while pull requests deploy to pr-preview/pr-N/ via
rossjrw/pr-preview-action (and are removed on close). Also strip the
uv-generated .gitignore from web/dist so git-based deploys still publish
the wheel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 2, 2026 •

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-08-02 17:39 UTC

burgesQ and others added 2 commits August 2, 2026 19:29
The gh-pages branch holds only the built site (no pyproject/tests), so
skip CI there to avoid spurious failing runs if it is ever pushed with a
non-Actions token.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The preview no longer caps at 200 rows (which hid every series behind
the 307 movies) — it renders the whole recovered library. Add an
All / Movies / Series filter with live counts and a leading checkbox
column on each row (visual only for now). Downloads are unaffected and
still contain the full data.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@burgesQ
burgesQ merged commit 4315197 into main Aug 2, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant