Adapt sign-in to the 0.8 account API and use what it adds:
- Key passphrases are KeyPassphrases now, wiped on drop. The stored
session keeps its JSON shape and converts on unlock.
- The mailbox password is checked with AccountClient::unlock(), one
core/v4/users request instead of loading every address, and a wrong
one is recognised as ProtonError::KeysLocked instead of any
InvalidOperation.
- KeysLocked from a resumed session counts as signed out: the mailbox
password changed, and only signing in again derives passphrases that
unlock the keys.
- A sign-in Proton answers with code 2028 (Blocked) says so, instead of
showing the server text that claims the app version is outdated.
Also picked up without code changes: a token refresh that outlives its
caller and is never replayed (0.8.2).
Moves to proton-sdk / proton-drive-rs 0.7.3.
The stored Proton session now holds, per account key, the passphrase derived
from the mailbox password at sign-in, and resumes through
`ProtonDriveClient::with_key_passphrases`. The mailbox password — on a
single-password account, the login password — and the key salts are no longer
stored. A session in the earlier shape, which only development builds of
1.2.0 wrote, reads as signed out: signing in again replaces it.
An account whose only second factor is a security key is refused at sign-in
with a reason, rather than asked for a code that can only fail as a wrong one.
The code prompts now say plainly that security keys are not supported.
A Proton account is now optional alongside public links. Signing in (password,
then a second-factor code and a mailbox password where the account has them;
a CAPTCHA gate is solved in the Android app) stores one session blob in the OS
credential store under `proton-account`, writing back every refreshed token
since refresh tokens are single-use. With an account, folders of the viewer's
own Drive — My Files, other devices, shared with me — can be added as shares
through the same `ShareClient` surface as a public link.
Watch history syncs through a `proton-stream` device in that Drive: one
`watch-history/<installation>.json` per installation, so no two devices ever
write the same file. Each sync reads the others and merges per episode, newer
`updated_at` wins, with a deterministic tie-break. It runs at launch, after
sign-in, whenever the player closes and every five minutes. A session that can
no longer refresh signs the app out instead of failing on every launch.
The CLI gains `login`, `logout`, `account`, `browse` and `sync`.
Also in this change:
- Android offline downloads no longer look stuck (B64): two transfer at once,
through a copy stream with no read-ahead, with a smoothed transfer rate.
- Choosing a metadata match by hand now enriches the title too, instead of
keeping the old match's episodes and artwork.
The catalog schema is unchanged.