mirror of
https://github.com/narrrl/proton-stream.git
synced 2026-11-03 16:41:26 +01:00
fix(core,app,android,cli,packaging): harden account sign-in and watch-history sync
- Desktop shows Proton's CAPTCHA instead of failing the sign-in: a wry webview in a child process of the app (`pstr-app::verify`), confined to https://*.proton.me, hands the token back and the sign-in is retried. Linux packages and CI gain WebKitGTK 4.1; the Windows zip and MSI ship WebView2Loader.dll, without which a -gnu build does not start. - The desktop keyring splits a secret too long for one entry across several, so a Proton session with many key salts survives Windows' 1280-unit cap. Short secrets stay one entry, so every existing secret still reads. - `WatchSync` runs against a small `HistoryDrive` trait and is tested end to end against an in-memory Drive: two devices, newer wins, no re-upload of an unchanged history, a missing file written again, an unreadable own file never overwritten, a newer-format file left alone. - An installation's file unwritten for 90 days is retired by the next device to sync, after its entries are merged and carried in that device's file. - Removing a share keeps its watch state: sync would bring it back from the other devices anyway, and re-adding the share picks up where it left off. - Watch history syncs once more on desktop exit (bounded to 3 s) and, on Android, through a WorkManager job enqueued when the app leaves the screen. - The second-factor prompts say security keys are not supported.
This commit is contained in:
1 parent
26cccd5c63
commit
d9ca8f8d60
28 files changed
+2475
-186
No files matched your search
@@ -513,19 +513,17 @@ impl Catalog {
|
||||
Ok(rows.collect::<std::result::Result<_, _>>()?)
|
||||
}
|
||||
|
||||
/// Drop everything a share contributed, watch positions included.
|
||||
/// Drop everything a share contributed except where it was watched.
|
||||
///
|
||||
/// Unlike a recrawl — which replaces rows and deliberately leaves watch
|
||||
/// state alone — this is the viewer saying they are done with the share.
|
||||
/// Keeping positions for files they can no longer reach would only leave
|
||||
/// the "continue watching" row pointing at nothing.
|
||||
/// Watch state stays, as through a recrawl. It is history, and not only
|
||||
/// this device's: with an account signed in, the next sync would bring it
|
||||
/// straight back from the other devices' files, so deleting it here would
|
||||
/// only make it flicker. Nothing shows it — the library pairs positions
|
||||
/// with files, and the files are gone — until the share is added again,
|
||||
/// when it picks up where it left off.
|
||||
pub fn remove_share(&mut self, share_id: &str) -> Result<()> {
|
||||
let tx = self.conn.transaction()?;
|
||||
tx.execute("DELETE FROM nodes WHERE share_id = ?1", params![share_id])?;
|
||||
tx.execute(
|
||||
"DELETE FROM watch_state WHERE share_id = ?1",
|
||||
params![share_id],
|
||||
)?;
|
||||
tx.execute(
|
||||
"DELETE FROM offline_files WHERE share_id = ?1",
|
||||
params![share_id],
|
||||
@@ -2430,6 +2428,38 @@ mod tests {
|
||||
assert_eq!(rows[0].link_id, "film");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn removing_a_share_keeps_its_watch_state_out_of_sight() {
|
||||
let mut catalog = Catalog::in_memory().expect("open");
|
||||
catalog
|
||||
.replace_share("s1", &[file("s1", "a", "Show S01E01.mkv")])
|
||||
.expect("crawl");
|
||||
let state = WatchState {
|
||||
position_secs: 120.0,
|
||||
duration_secs: Some(1440.0),
|
||||
watched: false,
|
||||
updated_at: 10,
|
||||
};
|
||||
catalog.set_watch_state("s1", "a", &state).expect("set");
|
||||
|
||||
catalog.remove_share("s1").expect("remove");
|
||||
let library = crate::library::Library::build(
|
||||
catalog.all_files().expect("files"),
|
||||
&catalog.all_watch_states().expect("watch"),
|
||||
);
|
||||
assert!(library.titles.is_empty());
|
||||
assert_eq!(catalog.watch_state("s1", "a").expect("read"), Some(state));
|
||||
|
||||
catalog
|
||||
.replace_share("s1", &[file("s1", "a", "Show S01E01.mkv")])
|
||||
.expect("re-add");
|
||||
let library = crate::library::Library::build(
|
||||
catalog.all_files().expect("files"),
|
||||
&catalog.all_watch_states().expect("watch"),
|
||||
);
|
||||
assert_eq!(library.titles[0].seasons[0].episodes[0].watch, Some(state));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merging_takes_only_states_newer_than_the_recorded_ones() {
|
||||
let mut catalog = Catalog::in_memory().expect("open");
|
||||
|
||||
Reference in new issue
Block a user