mirror of
https://github.com/narrrl/proton-stream.git
synced 2026-11-03 16:41:26 +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.
127 lines
5.6 KiB
TOML
127 lines
5.6 KiB
TOML
[workspace]
|
|
resolver = "2"
|
|
members = [
|
|
"crates/pstr-core",
|
|
"crates/pstr-stream",
|
|
"crates/pstr-meta",
|
|
"crates/pstr-player",
|
|
"crates/pstr-app",
|
|
"crates/pstr-cli",
|
|
"crates/pstr-android",
|
|
]
|
|
|
|
[workspace.package]
|
|
version = "1.1.3"
|
|
edition = "2024"
|
|
license = "MIT"
|
|
repository = "https://github.com/narrrl/proton-stream"
|
|
rust-version = "1.96"
|
|
authors = ["Nils Pukropp <contact@narl.io>"]
|
|
|
|
# Dependencies build optimized even in dev/test, for the same reason
|
|
# `proton-sdk-rs` does it: every CPU-bound thing this app does — rPGP block
|
|
# decryption, bcrypt, the S2K derivations — lives in those crates, and
|
|
# unoptimized rPGP is roughly two orders of magnitude slower. A debug build
|
|
# without this cannot keep up with playback at all. Workspace crates stay at
|
|
# `opt-level = 0` so incremental rebuilds and backtraces are unaffected.
|
|
[profile.dev.package."*"]
|
|
opt-level = 2
|
|
|
|
[workspace.dependencies]
|
|
proton-sdk = { version = "0.7.2" }
|
|
proton-drive-rs = { version = "0.7.2" }
|
|
|
|
# Internal crates.
|
|
pstr-core = { path = "crates/pstr-core" }
|
|
pstr-meta = { path = "crates/pstr-meta" }
|
|
pstr-stream = { path = "crates/pstr-stream" }
|
|
pstr-player = { path = "crates/pstr-player" }
|
|
|
|
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "fs", "io-util", "time"] }
|
|
futures = "0.3"
|
|
# The TLS features match `proton-sdk-rs`'s deliberately: cargo unifies features
|
|
# across the graph, and a second configuration here would link a second TLS
|
|
# stack into the same binary. `query` is the one addition — TMDB takes its
|
|
# search terms in the query string, and it is purely additive.
|
|
reqwest = { version = "0.13", default-features = false, features = ["json", "query", "rustls", "webpki-roots", "gzip"] }
|
|
serde = { version = "1", features = ["derive"] }
|
|
serde_json = "1"
|
|
thiserror = "2"
|
|
anyhow = "1"
|
|
tracing = "0.1"
|
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|
directories = "5"
|
|
# Local dates for the history page. Already in the tree through the SDK.
|
|
chrono = { version = "0.4", default-features = false, features = ["clock"] }
|
|
sha2 = "0.10"
|
|
# The id a watch-history file is named by. Already in the tree through the SDK.
|
|
getrandom = "0.3"
|
|
# `bundled` so a Windows build needs no system SQLite.
|
|
rusqlite = { version = "0.32", features = ["bundled"] }
|
|
# The share URL fragment and the custom password are both secrets; they go in
|
|
# the OS credential store (Secret Service on Linux, Credential Manager on
|
|
# Windows), never in the config file next to them.
|
|
#
|
|
# No platform features here on purpose. `keyring` selects its keystore by
|
|
# `cfg(all(target_os = ..., feature = ...))`, and with the matching feature off
|
|
# it falls back to `mock` — an in-memory store built fresh per `Entry`, so
|
|
# `set_password` succeeds, the next `Entry::new(..).get_password()` returns
|
|
# `NoEntry`, and nothing warns. Every consuming crate therefore enables the
|
|
# store for its target in a `[target.'cfg(...)'.dependencies]` block; see
|
|
# `crates/pstr-core/Cargo.toml`.
|
|
keyring = { version = "3" }
|
|
clap = { version = "4", features = ["derive"] }
|
|
# The UI. `glow` rather than `wgpu` because step two of the player hands mpv an
|
|
# `MPV_RENDER_API_TYPE_OPENGL` context, and that has to be the same context
|
|
# eframe is drawing into.
|
|
eframe = { version = "0.35", default-features = false, features = [
|
|
"default_fonts",
|
|
"glow",
|
|
"wayland",
|
|
"x11",
|
|
] }
|
|
egui = "0.35"
|
|
# The icons on buttons. Glyphs like ⏭ and 🔊 come out of whichever fallback
|
|
# font has them, at its own size and baseline; an icon font is one set.
|
|
egui-phosphor = { version = "0.13", default-features = false, features = ["regular"] }
|
|
# Tests only: reads the bundled fonts' character maps. The version epaint
|
|
# already builds, so it costs no extra compile.
|
|
skrifa = { version = "0.42", default-features = false, features = ["std"] }
|
|
# Poster thumbnails only: Proton renders them as JPEG, and a decoder that also
|
|
# pulls in every exotic format is weight this app has no use for.
|
|
image = { version = "0.25", default-features = false, features = ["jpeg", "png", "webp"] }
|
|
# Non-poisoning, deliberately: a panicking UI frame must not wedge the catalog
|
|
# behind a poisoned lock for the rest of the session. Same reason
|
|
# `proton-drive-linux` uses it.
|
|
parking_lot = "0.12"
|
|
# Media keys and the desktop's media widget: MPRIS on Linux, SMTC on Windows.
|
|
# `dbus` rather than `zbus` because the keyring's Secret Service store already
|
|
# links libdbus, so this adds no second D-Bus stack.
|
|
souvlaki = { version = "0.8", default-features = false, features = ["use_dbus"] }
|
|
# Holding the screensaver off while a film plays. mpv does that for a window of
|
|
# its own, but the picture here is drawn into ours, and mpv has no window to
|
|
# hold it for.
|
|
dbus = "0.9"
|
|
windows-sys = "0.61"
|
|
# Toasts on Windows. Linux notifications go over the `dbus` above.
|
|
tauri-winrt-notification = "0.8"
|
|
# The exe's own icon resource, which is what Explorer and a taskbar pin draw.
|
|
embed-resource = "3"
|
|
rpassword = "7"
|
|
libloading = "0.8"
|
|
lru = "0.18"
|
|
# `async fn` in traits is stable at MSRV 1.96 but is not dyn-compatible, and the
|
|
# stream layer needs a `dyn` block source so its cache, ring and read-ahead can
|
|
# be tested against a fake without a network or an account.
|
|
async-trait = "0.1"
|
|
uniffi = "0.32"
|
|
|
|
# The SDK builds against the registry — 0.4.0 is published. To test an
|
|
# unpublished `../proton-sdk-rs` change against this app, add locally and do
|
|
# **not** commit it (CI has no sibling checkout, and the build scripts refuse to
|
|
# run with a patch pointing at a missing path):
|
|
#
|
|
# [patch.crates-io]
|
|
# proton-sdk = { path = "../proton-sdk-rs/crates/proton-sdk" }
|
|
# proton-drive-rs = { path = "../proton-sdk-rs/crates/proton-drive-rs" }
|