fixed ci, release, docs

This commit is contained in:
nvrl committed 2026-08-03 09:38:01 +02:00
1 parent 14c48428a3
commit 4c842402a6
9 files changed
+315 -44

No files matched your search

+51
View File
@@ -0,0 +1,51 @@
name: libmpv (Windows)
description: >
Provide libmpv's Windows development files. `libmpv2-sys` emits
`cargo:rustc-link-lib=mpv`, and mpv's Windows *player* build ships neither
mpv.lib nor libmpv-2.dll — they come from the separate mpv-dev archive. Sets
LIB for the linker, PATH so binaries find the DLL at run time, and
MPV_DEV_DIR for `scripts/build.ps1`.
outputs:
dir:
description: Directory holding libmpv-2.dll and mpv.lib.
value: ${{ steps.mpv.outputs.dir }}
runs:
using: composite
steps:
# lib.exe, for turning mpv.def into an import library.
- uses: ilammy/msvc-dev-cmd@v1
- id: mpv
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$feed = [xml](Invoke-WebRequest -UseBasicParsing `
'https://sourceforge.net/projects/mpv-player-windows/rss?path=/libmpv').Content
# The "v3" variants are x86-64-v3 builds; take the baseline one.
$item = $feed.rss.channel.item |
Where-Object { $_.link -match 'mpv-dev-x86_64-2[^/]*\.7z/download$' } |
Select-Object -First 1
if (-not $item) { throw 'no mpv-dev build in the SourceForge feed' }
$archive = Join-Path $env:RUNNER_TEMP 'mpv-dev.7z'
$dir = Join-Path $env:RUNNER_TEMP 'mpv-dev'
Invoke-WebRequest -UseBasicParsing $item.link -OutFile $archive
7z x $archive "-o$dir" -y | Out-Null
$dll = Get-ChildItem -Path $dir -Filter 'libmpv-2.dll' -Recurse |
Select-Object -First 1
if (-not $dll) { throw "libmpv-2.dll not found under $dir" }
$libDir = $dll.Directory.FullName
# /NAME matters: without it the import library references mpv.dll, and
# the file that actually ships is libmpv-2.dll.
lib.exe /def:"$libDir\mpv.def" /name:libmpv-2.dll `
/out:"$libDir\mpv.lib" /machine:x64 | Out-Null
if ($LASTEXITCODE -ne 0) { throw 'lib.exe failed to build mpv.lib' }
"dir=$libDir" | Out-File $env:GITHUB_OUTPUT -Append -Encoding utf8
"MPV_DEV_DIR=$libDir" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
"LIB=$libDir;$env:LIB" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
$libDir | Out-File $env:GITHUB_PATH -Append -Encoding utf8
+6
View File
@@ -34,6 +34,12 @@ jobs:
sudo apt-get update sudo apt-get update
sudo apt-get install -y libmpv-dev sudo apt-get install -y libmpv-dev
# Without this the test binaries do not link: there is no mpv.lib on the
# runner. See .github/actions/libmpv-windows.
- name: Install libmpv (Windows)
if: runner.os == 'Windows'
uses: ./.github/actions/libmpv-windows
- run: cargo fmt --all -- --check - run: cargo fmt --all -- --check
- run: cargo clippy --workspace --all-targets -- -D warnings - run: cargo clippy --workspace --all-targets -- -D warnings
+153
View File
@@ -0,0 +1,153 @@
name: Release
# A release is a `v<version>` tag on main. The tag is the trigger and the
# version of record: nothing here reads the branch, so the first job checks that
# the commit really is on main and that the tag, `Cargo.toml` and the PKGBUILD
# all say the same version — a tag that disagrees would otherwise publish
# artifacts named after the manifest, not after the tag.
#
# `workflow_dispatch` builds everything and publishes nothing, which is how to
# rehearse a release without burning a tag.
on:
push:
tags:
- 'v*'
workflow_dispatch:
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
jobs:
verify:
name: Tag and versions
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: The tag is on main
if: startsWith(github.ref, 'refs/tags/')
run: |
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
git merge-base --is-ancestor "$GITHUB_SHA" origin/main ||
{ echo "::error::$GITHUB_REF_NAME points at a commit that is not on main"; exit 1; }
- name: The tag, Cargo.toml and the PKGBUILD agree
if: startsWith(github.ref, 'refs/tags/')
run: |
tag="${GITHUB_REF_NAME#v}"
manifest="$(sed -n 's/^version = "\([^"]*\)"/\1/p' Cargo.toml | head -n1)"
pkgbuild="$(sed -n 's/^pkgver=//p' packaging/PKGBUILD | head -n1)"
echo "tag=$tag manifest=$manifest pkgbuild=$pkgbuild"
[ "$tag" = "$manifest" ] ||
{ echo "::error::tag $tag != Cargo.toml $manifest"; exit 1; }
[ "$tag" = "$pkgbuild" ] ||
{ echo "::error::tag $tag != PKGBUILD $pkgbuild"; exit 1; }
# A release must resolve the SDK from crates.io. The build scripts refuse
# to run with a patch pointing at a missing sibling checkout, but here the
# patch is wrong even when the path exists.
- name: No path patch on the SDK
run: |
! grep -q '^\[patch\.crates-io\]' Cargo.toml ||
{ echo "::error::Cargo.toml carries [patch.crates-io]; releases build against the registry"; exit 1; }
linux:
name: Linux artifacts
needs: verify
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
# Pinned: the MSRV the workspace declares.
toolchain: "1.96"
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
# rpm for rpmbuild; the .deb goes through dpkg-deb, which is preinstalled.
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y libmpv-dev rpm
- name: Gate
run: |
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace --locked
- name: Build tarball, .deb and .rpm
run: scripts/build.sh tarball deb rpm
- uses: actions/upload-artifact@v4
with:
name: linux
path: |
dist/*.tar.gz
dist/*.deb
dist/*.rpm
if-no-files-found: error
windows:
name: Windows artifacts
needs: verify
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: "1.96"
- uses: Swatinem/rust-cache@v2
# Sets MPV_DEV_DIR, which is what build.ps1 reads when -MpvDev is absent.
- name: Install libmpv
uses: ./.github/actions/libmpv-windows
# build.ps1 installs the WiX toolset itself if `wix` is missing, and the
# runner has the .NET SDK it needs for that.
- name: Build the portable .zip and the .msi
shell: pwsh
run: scripts\build.ps1 -Artifacts zip,msi
- uses: actions/upload-artifact@v4
with:
name: windows
path: |
dist/*.zip
dist/*.msi
if-no-files-found: error
publish:
name: Publish
if: startsWith(github.ref, 'refs/tags/')
needs: [linux, windows]
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true
- name: Checksums
run: |
cd artifacts
sha256sum * > SHA256SUMS
cat SHA256SUMS
- uses: softprops/action-gh-release@v2
with:
files: artifacts/*
generate_release_notes: true
fail_on_unmatched_files: true
Generated
+4
View File
@@ -4037,6 +4037,8 @@ checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5"
[[package]] [[package]]
name = "proton-drive-rs" name = "proton-drive-rs"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b162681255e296b48ef7261cad899ffb384756e866a1c6d7b7c791cbbc8af3ac"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"base64 0.22.1", "base64 0.22.1",
@@ -4062,6 +4064,8 @@ dependencies = [
[[package]] [[package]]
name = "proton-sdk" name = "proton-sdk"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32d9b94ec901edb0e849238ea833121f2fb173308b4927b8aa6b157643fa0a73"
dependencies = [ dependencies = [
"aes-gcm 0.11.0", "aes-gcm 0.11.0",
"async-trait", "async-trait",
+8 -6
View File
@@ -91,9 +91,11 @@ lru = "0.12"
# be tested against a fake without a network or an account. # be tested against a fake without a network or an account.
async-trait = "0.1" async-trait = "0.1"
# During development the SDK work for this app lives in the sibling repo and is # The SDK builds against the registry — 0.4.0 is published. To test an
# not published yet. Remove once 0.3.3 is on crates.io — releases must build # unpublished `../proton-sdk-rs` change against this app, add locally and do
# against the registry. # **not** commit it (CI has no sibling checkout, and the build scripts refuse to
[patch.crates-io] # run with a patch pointing at a missing path):
proton-sdk = { path = "../proton-sdk-rs/crates/proton-sdk" } #
proton-drive-rs = { path = "../proton-sdk-rs/crates/proton-drive-rs" } # [patch.crates-io]
# proton-sdk = { path = "../proton-sdk-rs/crates/proton-sdk" }
# proton-drive-rs = { path = "../proton-sdk-rs/crates/proton-drive-rs" }
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Nils Pukropp
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+24 -12
View File
@@ -18,23 +18,28 @@ is playable without downloading 2 GB of it.
## Status ## Status
Early, but it plays, and there is a window now. The SDK streaming surface, the Early, but it is an app now: the picture lands **inside the window**, not in an
catalog, the streaming layer and the player are done and verified against a real mpv window of its own. The SDK streaming surface, the catalog, the streaming
share; the app has a library wall, a page per title with seasons and episodes, layer and the player are done and verified against a real share, and the app has
resume-where-you-left-off, a transport bar and share management. The picture a library wall, a page per title with seasons and episodes,
still lands in mpv's own window — embedding it in the app's surface is next. See resume-where-you-left-off, an overlay with a chapter-marked seek bar, a skip
`docs/DEVELOPMENT.md`. 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`.
```bash ```bash
cargo run --release -p pstr-app # the app cargo run --release -p pstr-app # the app
``` ```
Working today, via the `pstr` CLI: The `pstr` CLI does the same work headlessly, and is what tells you which layer
is broken:
```bash ```bash
cargo run -p pstr-cli -- add --name anime 'https://drive.proton.me/urls/TOKEN#fragment' 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 -- crawl
cargo run -p pstr-cli -- list 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 -- play --file Frieren --disk-cache
cargo run --release -p pstr-cli -- bench --file Frieren --verify cargo run --release -p pstr-cli -- bench --file Frieren --verify
``` ```
@@ -54,12 +59,12 @@ against the SDK's own range download.
|---|---| |---|---|
| Topology | Fat client. No server; each viewer's app talks to Proton directly. | | Topology | Fat client. No server; each viewer's app talks to Proton directly. |
| Auth | Public links only — but several, merged into one library. | | Auth | Public links only — but several, merged into one library. |
| Player | Embedded libmpv: 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. | | 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. | | Transcoding | None. mpv demuxes and decodes natively. |
| UI | egui/eframe on glow; mpv renders into the same GL context. | | UI | egui/eframe on glow; mpv renders into the same GL context. |
| Themes | The shipped near-black palette plus Catppuccin Latte, Frappé, Macchiato and Mocha, each with a choice of eight accents — pink and sky among them — drawn as a gradient unless you turn that off. Picked on the Shares page, applied without a restart. | | Themes | The shipped near-black palette plus Catppuccin Latte, Frappé, Macchiato and Mocha, each with a choice of eight accents — pink and sky among them — drawn as a gradient unless you turn that off. Picked on the Shares 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. | | 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 and per-episode titles; a title the scorer refuses to guess about can be pinned by hand, and a pinned match is never overwritten. |
| Platforms | Linux and Windows. | | Platforms | Linux and Windows. The macOS packaging is written but has not been run on a Mac. |
## Building ## Building
@@ -69,8 +74,15 @@ Rust 2024, MSRV 1.96.
cargo build --release cargo build --release
``` ```
`pstr-player` needs libmpv development headers (`mpv` ≥ 2.0). On Windows, `pstr-player` needs libmpv development headers (`mpv` ≥ 2.0). On Windows those
`libmpv-2.dll` is bundled next to the executable. 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.
## Privacy ## Privacy
+23 -18
View File
@@ -4,34 +4,39 @@
| Layer | State | | Layer | State |
|---|---| |---|---|
| SDK streaming surface (`../proton-sdk-rs` 0.3.3) | **done, live-verified** — `open_revision` / `download_range` / thumbnails / `refresh_session` on the visitor path; node-key cache; chunked `enumerate_nodes`; paged `download_file_to` | | SDK streaming surface (`../proton-sdk-rs` 0.4.0, from crates.io) | **done, live-verified** — `open_revision` / `download_range` / thumbnails / `refresh_session` on the visitor path; node-key cache; chunked `enumerate_nodes`; paged `download_file_to` |
| `pstr-core` — shares, config, catalog, naming, library grouping | **done** — 67 offline tests | | `pstr-core` — shares, config, catalog, naming, library grouping | **done** — 106 offline tests |
| `pstr-cli` — add / crawl / list | **done** — verified against a real 354-node share: 310 playable files, 22 titles, correct seasons and episodes | | `pstr-cli` — add / shares / remove / crawl / list / metadata / play / bench | **done** — verified against a real 354-node share: 310 playable files, 22 titles, correct seasons and episodes |
| `pstr-stream` — block cache, read-ahead | **done, live-verified** — 68 offline tests; benchmarked against a real 761 MiB episode (see `docs/TESTING.md`) | | `pstr-stream` — block cache, read-ahead | **done, live-verified** — 68 offline tests; benchmarked against a real 761 MiB episode (see `docs/TESTING.md`) |
| `pstr-player` — libmpv `stream_cb` | **done, live-verified** — 16 offline tests; plays a real 1080p HEVC episode over a public link, seeks, resumes from the disk cache. The render API (embedding) is not started | | `pstr-player` — libmpv `stream_cb` + render API | **done, live-verified** — 33 offline tests; plays a real 1080p HEVC episode over a public link, seeks, resumes from the disk cache, and renders into the app's GL context |
| `pstr-app` — egui UI, poster grid | **done, offline-tested** — library wall, title pages, transport, share management; mpv still opens its own window | | `pstr-app` — egui UI, poster grid, embedded video | **done, offline-tested** — 15 offline tests; library wall, title pages, player page with overlay controls, themes, share management |
| Metadata providers (AniList / TMDB) | not started — and this is what the wall is waiting on: see the note on thumbnails below | | Metadata providers (AniList / TMDB) | **done** — 32 offline tests; posters, synopses, per-episode titles, hand-pinned matches. Opt-in, off by default |
| Packaging | **done, locally verified** — `scripts/build.sh` (tarball, `.deb`, `.rpm`, `.app`, `.dmg`) and `scripts/build.ps1` (portable `.zip`, WiX `.msi`), plus `packaging/PKGBUILD`. See `packaging/README.md`. The Windows and macOS halves are written but only the Linux ones have been run on this machine | | Packaging | **done, locally verified** — `scripts/build.sh` (tarball, `.deb`, `.rpm`, `.app`, `.dmg`) and `scripts/build.ps1` (portable `.zip`, WiX `.msi`), plus `packaging/PKGBUILD`. See `packaging/README.md`. The macOS half is written but has not been run on a Mac |
## Build order from here ## Build order from here
1. **`pstr-player`, step two.** `mpv_render_context_create` with 1. **Sidecar subtitle files** — see the "not done" note at the end of this
`MPV_RENDER_API_TYPE_OPENGL` against eframe's glow context, rendered inside an section. The work is in `pstr-core`: crawl them, and pair them to a video by
`egui::PaintCallback`; overlay controls. If this proves troublesome, the name.
standalone-window mode already built remains shippable. Nothing in 2. Packaging is in place (`packaging/README.md`); what is left there is running
`pstr-player` except `PlayerConfig`'s window options is about where pixels the macOS half on a real host, and a release workflow that drives every
land, so this does not reach the streaming path. platform.
2. Metadata providers, docs. Packaging is in place (`packaging/README.md`); what
is left there is running `scripts/build.ps1` and the macOS half on real Done: **`pstr-player` step two** — `mpv_render_context_create` with
hosts, and a release workflow that drives both. `MPV_RENDER_API_TYPE_OPENGL` against eframe's glow context
(`pstr-player/src/render.rs`), painted from a framebuffer this app owns
(`pstr-app/src/video.rs`) so the picture can sit in a rectangle rather than
filling the window. `VideoOutput::Embedded` is what the app asks for; the
standalone-window mode the CLI uses is unchanged, and nothing about it reaches
the streaming path.
Done: `pstr-player` step one — `mpv_stream_cb_add_ro`, mpv in its own window. Done: `pstr-player` step one — `mpv_stream_cb_add_ro`, mpv in its own window.
`pstr play --file <text>` plays a catalogued episode over a public link. `pstr play --file <text>` plays a catalogued episode over a public link.
Done: `pstr-app` step one — `cargo run -p pstr-app` opens a window with the Done: `pstr-app` step one — `cargo run -p pstr-app` opens a window with the
library wall, a title page per series, a transport bar and share management. It library wall, a title page per series, a transport bar and share management. It
plays through the same `Player` the CLI uses, so mpv still owns its own window; plays through the same `Player` the CLI uses — at that point still in mpv's own
the app's transport drives it from a command channel. Two pieces of that are window; the app's transport drives it from a command channel. Two pieces of that are
worth knowing before touching it: worth knowing before touching it:
- **Drawing never mutates.** Pages are handed `&`-state and push `Action`s onto - **Drawing never mutates.** Pages are handed `&`-state and push `Action`s onto
+25 -8
View File
@@ -128,12 +128,29 @@ The `.icns` is rasterized from `io.narl.proton-stream.svg` when `rsvg-convert` o
The Debian `control` file has no template — `scripts/build.sh` writes it, because The Debian `control` file has no template — `scripts/build.sh` writes it, because
almost every field in it is computed. almost every field in it is computed.
## Two things to know before releasing ## Releasing
- **The `[patch.crates-io]` block in `Cargo.toml` is development only.** It points A release is a `v<version>` tag on `main`; `.github/workflows/release.yml` does
at `../proton-sdk-rs`, so both build scripts refuse to start when it is present the rest. It runs the CI gate and `scripts/build.sh tarball deb rpm` on Linux,
and that checkout is missing. A release must build against the published `scripts\build.ps1 -Artifacts zip,msi` on Windows, and publishes both sets plus
crates — drop the block once the pinned proton-sdk is on crates.io. a `SHA256SUMS` to a GitHub release. So the steps are:
- **There is no `LICENSE` file in the repository root** even though `Cargo.toml`
declares MIT. Every packager warns and ships without the license text; the MSI ```bash
falls back to `windows/license.rtf`. Adding the file fixes all of them at once. # 1. bump Cargo.toml's workspace version *and* packaging/PKGBUILD's pkgver
# 2. commit on main, then
git tag -a v0.1.0 -m 'proton-stream 0.1.0'
git push origin main --follow-tags
```
The workflow refuses to publish a tag whose commit is not an ancestor of `main`,
or whose version disagrees with `Cargo.toml` or the PKGBUILD — the artifacts are
named from the manifest, so a drifted tag would ship a file called something
other than the tag. It also refuses a `Cargo.toml` carrying `[patch.crates-io]`:
a release builds against the published SDK crates, never a sibling checkout.
`workflow_dispatch` runs everything except the publish step, which is how to
rehearse without burning a tag.
Not covered by the workflow: the Arch package (`makepkg` compiles in place, so
it wants an Arch host) and the macOS `.app`/`.dmg` (no runner for it here yet).
Both are still `scripts/build.sh arch` and `scripts/build.sh dmg` by hand.