nvrl 8e42906a7f 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.
2026-10-09 03:15:39 +02:00
2026-10-07 01:55:22 +02:00
2026-10-07 01:55:22 +02:00
2026-10-07 01:55:22 +02:00
2026-08-03 09:38:01 +02:00

proton-stream

A Netflix-style cross-platform client for Proton Drive public links.

Paste a share URL and its password; get a browsable, streamable library. No Proton account, no server to host, no download step — episodes start playing in seconds and the seek bar works.

Built on proton-sdk-rs, a pure-Rust Proton Drive SDK.

Why it works

A Proton Drive file's content blocks are each a self-contained PGP packet decrypted under one shared session key. There is no sequential streaming session to resume, so seeking costs exactly the blocks the seek lands on — a 2 GB episode is playable without downloading 2 GB of it.

Status

Early, but it is an app now: the picture lands inside the window, not in an mpv window of its own. The SDK streaming surface, the catalog, the streaming layer and the player are done and verified against a real share, and the app has a library wall, a page per title with seasons and episodes, resume-where-you-left-off, an overlay with a chapter-marked seek bar, a skip button for openings and endings, next/previous episode with an "up next" countdown, optional metadata, themes and share management. What is left is mostly sidecar subtitle files (.srt/.ass beside the video are invisible to the crawl today) and running the macOS packaging on a real Mac. See docs/DEVELOPMENT.md.

A native Android 12+ client is under android/, sharing the Rust catalog and streaming implementation through UniFFI. It features a modern Material 3 interface, an immersive fullscreen player with auto-hiding controls, and offline downloads. Its toolchain, emulator, signing, and release instructions are in docs/ANDROID.md.

An iOS 17+ client under ios/ is a SwiftUI port of the Android one over the same Rust engine, built as an unsigned .ipa for SideStore or AltStore; see docs/IOS.md.

cargo run --release -p pstr-app          # the app
proton-stream 'https://drive.proton.me/urls/TOKEN#fragment'   # open the Add form with a link

One window at a time: a second launch brings the open window forward and hands it any share link it was given, then exits.

The pstr CLI does the same work headlessly, and is what tells you which layer is broken:

cargo run -p pstr-cli -- add --name anime 'https://drive.proton.me/urls/TOKEN#fragment'
cargo run -p pstr-cli -- crawl
cargo run -p pstr-cli -- list
cargo run -p pstr-cli -- metadata                      # opt-in; off unless configured
cargo run --release -p pstr-cli -- play --file Frieren --disk-cache
cargo run --release -p pstr-cli -- bench --file Frieren --verify

On a 761 MiB, 26-minute 1080p HEVC episode over a real password-protected link: 2.3 s to first frame, ~1.5 s to resume after dragging to an arbitrary point, 61 MiB fetched for the first 38 seconds of playback. Watch it again and the disk cache serves it with no network at all — 0.3 s to first frame.

bench measures the same file without a player, reporting cold seek latency, sustained throughput and peak memory, and --verify cross-checks a mid-file read against the SDK's own range download.

Design

Topology Fat client. No server; each viewer's app talks to Proton directly.
Auth Public links only — but several, merged into one library.
Player Embedded libmpv, drawing into the app's own GL surface: HEVC 10-bit, HDR and ASS subtitles with hardware decode. Volume, audio-track, subtitle and chapter pickers; skip-the-opening; next/previous episode and autoplay. The language picked is preferred for the next file too.
Transcoding None. mpv demuxes and decodes natively.
UI egui/eframe on glow; mpv renders into the same GL context.
Themes The shipped near-black palette, Catppuccin Latte, Frappé, Macchiato and Mocha, and a Persona 5 one — black, white and one loud red, which is what the Red accent is there for — each with a choice of nine accents, drawn as a gradient unless you turn that off. Picked on the Settings page, applied without a restart.
Metadata Filename parsing, with optional AniList/TMDB enrichment — off by default, because enabling it sends your library's titles to a third party. It brings posters, synopses, fanart and per-episode titles and synopses (for AniList, from ani.zip, which is sent the matched ids); a title the scorer refuses to guess about can be pinned by hand, and a pinned match is never overwritten. The library sorts by name, watch, add or air date, rating and popularity, folds a franchise into one tile, and shelves titles by director, studio and genre. See docs/METADATA.md.
Platforms Linux and Windows; macOS packaging is written but has not been run on a Mac. A native Android 12+ client is available featuring a Material 3 UI and immersive fullscreen playback, and an iOS 17+ port of it for sideloading.

Building

Rust 2024, MSRV 1.96.

cargo build --release

pstr-player needs libmpv development headers (mpv ≥ 2.0), and on Linux pstr-app needs WebKitGTK 4.1's, for the window Proton's CAPTCHA is shown in. On Windows the libmpv ones come from the separate mpv-dev archive rather than mpv's player build, and libmpv-2.dll ships next to the executable. The Proton SDK crates come from crates.io; nothing here builds against a sibling checkout.

Installable artifacts — tarball, .deb, .rpm, Arch package, .app/.dmg, and a portable .zip plus an .msi on Windows — are produced by scripts/build.sh and scripts\build.ps1; packaging/README.md has the per-platform detail.

Android builds use JDK 17, Android SDK 36/NDK 28, Gradle 8.13, cargo-ndk, and UniFFI. Run bash scripts/build-android.sh debug after installing that toolchain; see docs/ANDROID.md for exact setup and release signing.

iOS builds need macOS with Xcode, XcodeGen and the aarch64-apple-ios Rust target: scripts/build-ios.sh all writes dist/proton-stream-<version>-ios.ipa.

Privacy

  • The share URL fragment is the decryption password. It and any custom password live in the OS credential store — Secret Service on Linux, Credential Manager on Windows — never in a config file.
  • Nothing leaves your machine except requests to Proton, unless you turn metadata enrichment on. The player is fed through an in-process mpv stream callback, not a local HTTP server: there is no port for another process on the machine to ask for the plaintext, and mpv is handed an opaque token rather than a URL that would put a share secret in its log and window title.
  • The block cache holds decrypted content. It lives in your cache directory, can be deleted at any time the app is not running, and can be sized or emptied from Settings → Storage.
  • A share link given as a command-line argument is visible to other users of the machine in the process list while that launch runs, and may land in shell history. Pasting it into the window (Ctrl+V) does neither.
  • The desktop's media widget is handed the title, the episode and a file:// path to artwork already on disk — never a provider URL, so the shell never contacts a metadata provider on its own.

License

The shared Rust crates and desktop application are MIT licensed. The Android and iOS applications and their combined native distributions are GPL-3.0-or-later; see android/LICENSE.md, ios/LICENSE.md and the bundled-component notices. The desktop application embeds the Inter typeface, under the SIL Open Font License 1.1 (crates/pstr-app/assets/fonts/Inter-OFL.txt), and the Phosphor icon font, under MIT.

S
Description
No description provided
Readme MIT
57 MiB
0 Stars 1 Watchers 0 Forks
Languages
Rust 62.8%
Kotlin 18.7%
Swift 13.6%
Shell 2%
C++ 1.5%
Other 1.3%