mirror of
https://github.com/narrrl/proton-stream.git
synced 2026-11-03 16:41:26 +01:00
feat(core,app,android,cli,ios): sync the library and settings, verify a share before adding it
Sync carried only watch positions, so a second device started empty. The per-installation history file now also carries per-key registers (pstr-core::profile): every share with its link and custom password, the playback, appearance and metadata settings, the TMDB key, hand-picked matches and per-title track choices. Newer updated_at wins per key, a removal travels as an empty register, and a first sync stamps at 0 so a fresh install takes the library the other devices have instead of overwriting it with defaults. Volume, mute and the device-only settings (cache budget, Wi-Fi only, background audio, hardware decoding) stay out. The secrets live only in the end-to-end encrypted Drive file, never in shares.json. FORMAT_VERSION stays 1; an older build ignores the registers. Adding a share stored it before opening the link, so a first failure left a broken entry and every retry said "already configured". add_verified opens the link first and stores nothing on failure, and only the new share is crawled afterwards, so another broken share cannot fail the add. Android drops picture-in-picture: the window sized the video wrongly and background audio already covers leaving the app. iOS sizes the Metal drawable on every layout, so the picture no longer stretches after a rotation or a Split View resize. Settings show a save at once rather than after the match run, and a failed save reverts and says why. Both phone clients gain an activity banner naming what is running, and the add-share, metadata and change-match forms show progress and errors in place.
This commit is contained in:
1 parent
dc70cafbb7
commit
8e42906a7f
37 files changed
+2018
-594
No files matched your search
+5
-5
@@ -55,15 +55,15 @@ installs the signed minified APK and runs instrumentation against it, which is
|
||||
the only path that exercises R8 — the step that has broken this app five distinct
|
||||
ways that a debug build cannot reproduce. Without the `ANDROID_RELEASE_*`
|
||||
variables below, drop `--release` and the debug APK is used instead. The matrix — phone and tablet layouts, rotation,
|
||||
process recreation, playback and background controls, Picture-in-Picture,
|
||||
process recreation, playback and background controls,
|
||||
download cancellation/resume, and an offline launch with networking disabled —
|
||||
is `scripts/android-acceptance.sh`; `--list` prints every case and what it
|
||||
needs, and `PSTR_ACCEPTANCE_ONLY=name` runs one. Five cases need a real share,
|
||||
given as `PSTR_ACCEPTANCE_SHARE_URL` and `PSTR_ACCEPTANCE_SHARE_PASSWORD`; they
|
||||
reach the device as instrumentation arguments and are stored nowhere. Without
|
||||
them those cases report `skip`. `picture-in-picture` is still `pending`, so the
|
||||
run tells you the matrix is incomplete — but no longer because of B40, which is
|
||||
fixed; the case now just needs writing. `docs/TESTING.md` has the layer-by-layer
|
||||
them those cases report `skip`. There is no Picture-in-Picture: it was removed
|
||||
after a PiP window that never sized its video right proved less use than
|
||||
background audio and the lock-screen transport. `docs/TESTING.md` has the layer-by-layer
|
||||
detail, including why lint runs against a baseline.
|
||||
|
||||
Requires JDK 17. `build-android.sh` locates it, because a newer JVM does not
|
||||
@@ -110,6 +110,6 @@ ABIs, plus a source archive and revision record, under
|
||||
packages the staged libraries, and compiles `pstr_mpv`, the JNI/EGL adapter. It
|
||||
feeds libmpv through the Rust stream C ABI; decrypted bytes remain in native
|
||||
memory. `PlaybackService` owns the mpv core for background audio and media
|
||||
controls, while the activity supplies the current `Surface` and enters PiP.
|
||||
controls, while the activity supplies the current `Surface`.
|
||||
Do not publish an Android binary until this exact pipeline is verified on real
|
||||
arm64 hardware and an x86_64 emulator.
|
||||
+14
-1
@@ -118,6 +118,18 @@ an edge case.
|
||||
including trashing the file of an installation quiet for 90 days once its
|
||||
history is merged into this one's. Folders of the account added as shares
|
||||
are only ever read.
|
||||
- The same file carries the rest of what makes every device look alike
|
||||
(`pstr-core::profile`): the shares, with each link and custom password so
|
||||
another device can open them; the playback, appearance and metadata settings
|
||||
except volume and mute; the TMDB key; hand-picked matches; per-show track
|
||||
choices. Each is a register merged per key, newer wins, a removed share being
|
||||
a register with no value. `sync-state.json` holds the registers as last
|
||||
synced, and a sync stamps whatever differs from it, so nothing that writes a
|
||||
setting has to know about sync. A first sync stamps what it finds at zero, so
|
||||
a fresh install's defaults never outrank the library everyone else agreed on.
|
||||
Automatic matches are not synced: every device with enrichment on finds them
|
||||
again. The front end crawls what arrived (`uncrawled_shares` covers a sync
|
||||
that ran in the background) and drops what was removed with its files.
|
||||
- A CAPTCHA in front of a sign-in is shown in a webview, because the page
|
||||
reports success by posting a message to its host. On the desktop that is a
|
||||
child process of the app (`pstr-app::verify`, wry): WebKitGTK on Linux,
|
||||
@@ -139,7 +151,8 @@ an edge case.
|
||||
| Watch state | same DB, own table | **No** — deliberately survives a recrawl and removing the share; with an account, mirrored to its Drive |
|
||||
| Proton session | OS credential store, `proton-account` | No — sign in again |
|
||||
| Sync identity | config dir, `sync.json` | A new id if missing — the old history file is still read |
|
||||
| Watch history in Drive | device `proton-stream`, `watch-history/<id>.json` | One file per installation; merged per episode, newer wins. A file unwritten for 90 days is retired by the next installation to sync, after merging it; a live installation whose file was retired writes it again |
|
||||
| Sync state | config dir, `sync-state.json` | Yes — a missing one is a first sync, which takes everything other devices have |
|
||||
| Watch history and profile in Drive | device `proton-stream`, `watch-history/<id>.json` | One file per installation; merged per episode and per register, newer wins. Holds share links and passwords, end-to-end encrypted by Drive. A file unwritten for 90 days is retired by the next installation to sync, after merging it; a live installation whose file was retired writes it again |
|
||||
| Block cache | cache dir, `blocks/` | Yes — must work correctly after deletion |
|
||||
| Poster thumbnails | cache dir, `thumbs/` | Yes — refetched per file, one small decrypt each; stored scaled to 640 px |
|
||||
| Provider artwork | cache dir, `posters/` | Yes — refetched by URL; stored scaled to 640 px |
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ Each of these is the platform's doing, not a choice to diverge.
|
||||
boundary and is queued again; it resumes from its `.part` file when the app next
|
||||
comes forward. "Download on Wi-Fi only" stops transfers on a cellular or
|
||||
otherwise expensive network rather than holding them back.
|
||||
- **Watch history syncs when the app leaves the screen**, inside the same grace, in
|
||||
- **Sync runs when the app leaves the screen**, inside the same grace, in
|
||||
place of Android's `WatchSyncWorker`, and every five minutes while it is open.
|
||||
- **Background audio off pauses** the episode when the app leaves the screen, where
|
||||
Android stops it.
|
||||
|
||||
+3
-4
@@ -255,10 +255,9 @@ three dispositions, and the third is the one to read:
|
||||
| `pending` | specified but not implemented. Never passes, and the summary reports the matrix as incomplete until it is gone |
|
||||
| `skip` | needs a share that was not configured |
|
||||
|
||||
Today `process-recreation` is `xfail` against B35, and `picture-in-picture` is
|
||||
`pending` against B40 — asserting today's behaviour would encode the bug rather
|
||||
than catch it. **The matrix is not complete, and the runner says so on every
|
||||
run.**
|
||||
Today `process-recreation` is `xfail` against B35 — asserting today's behaviour
|
||||
would encode the bug rather than catch it. The `picture-in-picture` case went
|
||||
with Picture-in-Picture itself, so nothing is `pending`.
|
||||
|
||||
The five live cases need a share, given as environment variables that reach the
|
||||
device as instrumentation arguments and are written nowhere:
|
||||
|
||||
Reference in new issue
Block a user