mirror of
https://github.com/narrrl/proton-stream.git
synced 2026-11-04 00:47:00 +01:00
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.
46 lines
2.0 KiB
Rust
46 lines
2.0 KiB
Rust
//! Seekable, cached byte streams over Proton Drive revisions.
|
|
//!
|
|
//! Proton stores a file as a list of independently-encrypted 4 MiB blocks, each
|
|
//! a self-contained PGP packet under one session key. Nothing about that is
|
|
//! sequential — which is the fact this whole crate rests on. Seeking is not an
|
|
//! approximation of streaming here; block 400 costs exactly what block 0 costs.
|
|
//!
|
|
//! What sits between the SDK and the player:
|
|
//!
|
|
//! ```text
|
|
//! mpv demuxer VideoStream BlockRing → DiskCache → Proton
|
|
//! (small, forward, → byte range → → memory disk network
|
|
//! seeking reads) blocks (128 MiB) (4 GiB)
|
|
//! ```
|
|
//!
|
|
//! * [`StreamSource`] opens files and keeps the open ones in an LRU.
|
|
//! * [`VideoStream`] answers byte ranges, reading ahead of the player.
|
|
//! * The ring and the disk cache are byte-budgeted, shared across streams, and
|
|
//! keyed by revision id so a resealed file can never serve stale bytes.
|
|
//!
|
|
//! Only [`reader`] touches the SDK. Everything else runs against a
|
|
//! [`BlockSource`], which is what makes the caching and read-ahead testable
|
|
//! without an account, a network or a share.
|
|
#![forbid(unsafe_code)]
|
|
|
|
pub mod block;
|
|
pub mod disk;
|
|
pub mod error;
|
|
pub mod reader;
|
|
pub mod ring;
|
|
mod single_flight;
|
|
pub mod source;
|
|
pub mod stream;
|
|
|
|
pub use block::{BlockMap, BlockSource, MemoryBlocks, SharedBlocks};
|
|
pub use disk::{DiskCacheConfig, DiskStats};
|
|
pub use error::{Error, Result};
|
|
/// Re-exported because every caller of this crate names one — `VideoStream::uid`
|
|
/// returns it and `StreamSource::open` takes it — and taking a dependency on the
|
|
/// whole SDK for a newtype is not a reasonable price for that.
|
|
pub use proton_sdk::ids::NodeUid;
|
|
pub use reader::{FileBlocks, LibraryOpener, RevisionBlocks};
|
|
pub use ring::{DEFAULT_RING_BYTES, RingStats};
|
|
pub use source::{RevisionOpener, StreamConfig, StreamSource};
|
|
pub use stream::{COPY_BLOCKS_IN_FLIGHT, DEFAULT_READAHEAD_BLOCKS, StreamStats, VideoStream};
|