Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a99751cdf |
@@ -1,18 +0,0 @@
|
|||||||
node_modules
|
|
||||||
.git
|
|
||||||
.gitignore
|
|
||||||
.claude
|
|
||||||
.svelte-kit
|
|
||||||
build
|
|
||||||
dist
|
|
||||||
.env
|
|
||||||
.env.local
|
|
||||||
.vscode
|
|
||||||
.idea
|
|
||||||
target
|
|
||||||
*.apk
|
|
||||||
*.aab
|
|
||||||
*.log
|
|
||||||
coverage
|
|
||||||
src-tauri/gen
|
|
||||||
src-tauri/target
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# Local Android release signing.
|
|
||||||
#
|
|
||||||
# Copy to `.env` and fill in. `.env` is gitignored and is the single source of
|
|
||||||
# truth for local release signing — scripts/write-keystore-properties.sh reads
|
|
||||||
# it and regenerates src-tauri/gen/android/keystore.properties before every
|
|
||||||
# release build, because `tauri android init` overwrites that file.
|
|
||||||
#
|
|
||||||
# Only needed for `bun run android:build:release`. Debug builds sign with the
|
|
||||||
# local debug keystore and need nothing here.
|
|
||||||
#
|
|
||||||
# CI does not use this file: build-release.yml reconstructs the keystore from
|
|
||||||
# the ANDROID_KEYSTORE_BASE64 secret and writes the same properties itself.
|
|
||||||
|
|
||||||
# Key alias inside the keystore.
|
|
||||||
ANDROID_KEY_ALIAS=jellytau
|
|
||||||
|
|
||||||
# Absolute path to the .jks. Keep it outside the repo, or in the gitignored
|
|
||||||
# android-keystore/ directory.
|
|
||||||
ANDROID_KEYSTORE_FILE=/absolute/path/to/jellytau-release.jks
|
|
||||||
|
|
||||||
# Keystore and key passwords. These are secrets — never commit the filled-in .env.
|
|
||||||
ANDROID_KEYSTORE_PASSWORD=
|
|
||||||
ANDROID_KEY_PASSWORD=
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
name: Bug report
|
|
||||||
about: Something behaves incorrectly
|
|
||||||
title: ""
|
|
||||||
labels: ["bug"]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Security vulnerabilities do **not** go here — see
|
|
||||||
[SECURITY.md](../../SECURITY.md).
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: what-happened
|
|
||||||
attributes:
|
|
||||||
label: What happened
|
|
||||||
description: What you did, what you expected, and what you got instead.
|
|
||||||
placeholder: |
|
|
||||||
1. Opened an album from the Music library
|
|
||||||
2. Tapped the third track
|
|
||||||
3. Playback started from the first track instead
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: input
|
|
||||||
id: version
|
|
||||||
attributes:
|
|
||||||
label: JellyTau version
|
|
||||||
description: Settings scrolls to the bottom, or the filename you installed.
|
|
||||||
placeholder: "0.9.1"
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
id: platform
|
|
||||||
attributes:
|
|
||||||
label: Platform
|
|
||||||
options:
|
|
||||||
- Linux (AppImage)
|
|
||||||
- Linux (deb)
|
|
||||||
- Linux (rpm)
|
|
||||||
- Linux (Arch package)
|
|
||||||
- Windows
|
|
||||||
- Android
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
### Playback questions
|
|
||||||
|
|
||||||
If this involves playback, these three answers decide which of several
|
|
||||||
very different code paths you were on. "I don't know" is a fine answer.
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
id: source
|
|
||||||
attributes:
|
|
||||||
label: Was the media streaming or downloaded?
|
|
||||||
options:
|
|
||||||
- Streaming from the server
|
|
||||||
- Downloaded for offline use
|
|
||||||
- Not playback-related
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
id: transcode
|
|
||||||
attributes:
|
|
||||||
label: Was the server transcoding?
|
|
||||||
description: Jellyfin's dashboard shows this while something is playing.
|
|
||||||
options:
|
|
||||||
- Direct play
|
|
||||||
- Transcoding
|
|
||||||
- Don't know
|
|
||||||
- Not playback-related
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
id: kind
|
|
||||||
attributes:
|
|
||||||
label: Music or video?
|
|
||||||
options:
|
|
||||||
- Music
|
|
||||||
- Video (movie)
|
|
||||||
- Video (TV episode)
|
|
||||||
- Not playback-related
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: logs
|
|
||||||
attributes:
|
|
||||||
label: Logs
|
|
||||||
description: |
|
|
||||||
Android: `adb logcat | grep -i jellytau`.
|
|
||||||
Linux: run from a terminal, or `RUST_LOG=debug jellytau` for more.
|
|
||||||
In the app, `localStorage.setItem("jellytau:logLevel","debug")` in the
|
|
||||||
webview console turns the frontend up too.
|
|
||||||
render: shell
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: server
|
|
||||||
attributes:
|
|
||||||
label: Jellyfin server
|
|
||||||
description: Version, and anything unusual about the library layout.
|
|
||||||
placeholder: "10.9.11, series stored without season folders"
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
name: Feature request
|
|
||||||
about: Suggest something JellyTau should do
|
|
||||||
title: ""
|
|
||||||
labels: ["enhancement"]
|
|
||||||
body:
|
|
||||||
- type: textarea
|
|
||||||
id: problem
|
|
||||||
attributes:
|
|
||||||
label: What are you trying to do?
|
|
||||||
description: |
|
|
||||||
The situation, not the solution. "I listen to albums in a fixed order and
|
|
||||||
lose my place when I switch devices" tells us more than "add a sync
|
|
||||||
button", and often has a better answer than the one you had in mind.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: proposal
|
|
||||||
attributes:
|
|
||||||
label: What would you like it to do?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
id: platform
|
|
||||||
attributes:
|
|
||||||
label: Which platforms does this matter on?
|
|
||||||
multiple: true
|
|
||||||
options:
|
|
||||||
- Linux
|
|
||||||
- Windows
|
|
||||||
- Android
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: alternatives
|
|
||||||
attributes:
|
|
||||||
label: Anything you have tried, or how other clients handle it
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
## What and why
|
|
||||||
|
|
||||||
<!-- What changes, and the reason. The diff shows the what; the why is what
|
|
||||||
the commit log is for. -->
|
|
||||||
|
|
||||||
## How it was verified
|
|
||||||
|
|
||||||
<!-- What you actually ran or clicked. "Tests pass" on its own says little;
|
|
||||||
"played a transcoded episode on Android, seeked twice, backgrounded it"
|
|
||||||
says a lot. -->
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
- [ ] `bun run check`, `bun run test`, `bun run format:check`, `bun run lint`
|
|
||||||
- [ ] `cargo fmt`, `cargo clippy --all-targets -- -D warnings`, `cargo test`
|
|
||||||
- [ ] `bun run check:boundary` — no Jellyfin taxonomy in the frontend
|
|
||||||
- [ ] New requirement-implementing code carries a `TRACES:` comment, and every
|
|
||||||
ID it names exists in `docs/requirements.md` (`bun run traces:validate`)
|
|
||||||
- [ ] **Bug fix:** a test that reproduces it was written *first* and observed
|
|
||||||
failing before the fix
|
|
||||||
- [ ] Android source edits were made in `src-tauri/android/src` and synced with
|
|
||||||
`scripts/sync-android-sources.sh` (never edit `gen/` directly)
|
|
||||||
@@ -1,298 +0,0 @@
|
|||||||
name: '🏗️ Build and Test JellyTau'
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
paths-ignore:
|
|
||||||
- '**/*.md'
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
paths-ignore:
|
|
||||||
- '**/*.md'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
# Incremental state is never reused between CI runs -- pure disk cost.
|
|
||||||
CARGO_INCREMENTAL: 0
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
name: Run Tests
|
|
||||||
# A release push triggers build-release.yml on the tag, which runs this exact
|
|
||||||
# test suite itself — and on a single-slot runner the two ~1h workflows would
|
|
||||||
# otherwise serialize/contend. Skip the duplicate for chore(release) commits.
|
|
||||||
# (head_commit is absent on pull_request/workflow_dispatch; startsWith(null,…)
|
|
||||||
# is false there, so those events still run.)
|
|
||||||
if: "!startsWith(github.event.head_commit.message, 'chore(release)')"
|
|
||||||
runs-on: linux/amd64
|
|
||||||
container:
|
|
||||||
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Cache Rust dependencies
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
# Registry only -- never src-tauri/target. That directory is ~16 GB and
|
|
||||||
# was cached under five separate keys, which filled the runner's 74 GB
|
|
||||||
# disk at ~1.15 GB/day (23 GB in 20 days, measured Aug 2026).
|
|
||||||
# registry/src is omitted too: cargo re-extracts it for free from
|
|
||||||
# registry/cache (155 MB of .crate tarballs vs 1.1 GB extracted).
|
|
||||||
path: |
|
|
||||||
~/.cargo/registry/index
|
|
||||||
~/.cargo/registry/cache
|
|
||||||
~/.cargo/git/db
|
|
||||||
# One shared key across every job. The old per-job keys existed to stop
|
|
||||||
# debug/release target artifacts clobbering each other; with target no
|
|
||||||
# longer cached, registry contents are target-independent, so all jobs
|
|
||||||
# want the same crates. First job to finish saves; the rest restore.
|
|
||||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-cargo-registry-
|
|
||||||
|
|
||||||
- name: Cache Node dependencies
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.bun/install/cache
|
|
||||||
node_modules
|
|
||||||
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-bun-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
bun install
|
|
||||||
|
|
||||||
# Tripwire for domain-taxonomy leaks into the presentation layer (a
|
|
||||||
# multi-type includeItemTypes query defining a category in the frontend).
|
|
||||||
# See scripts/check-frontend-boundary.sh and
|
|
||||||
# docs/specs/scoped-search-boundary.md.
|
|
||||||
- name: Check frontend/backend boundary
|
|
||||||
run: bash scripts/check-frontend-boundary.sh
|
|
||||||
|
|
||||||
# The docs are the maintained source of truth for architecture and
|
|
||||||
# process, and they cross-reference each other heavily. A rename that
|
|
||||||
# misses a link turns a doc into a dead end silently. Pure shell + git —
|
|
||||||
# no tool is installed at job time.
|
|
||||||
- name: Check documentation links
|
|
||||||
run: bash scripts/check-doc-links.sh
|
|
||||||
|
|
||||||
# Formatting, linting and type-checking were all configured in this repo
|
|
||||||
# and enforced by nothing: .prettierrc described a tree where 199 files did
|
|
||||||
# not match it, eslint.config.js ran in no workflow and in no hook, and
|
|
||||||
# `bun run check` ran only in build-release.yml — i.e. a type error could
|
|
||||||
# sit on master until somebody cut a tag. These three steps are what make
|
|
||||||
# those configs load-bearing. All are project deps installed by
|
|
||||||
# `bun install`; nothing is fetched at job time.
|
|
||||||
# Cheap tripwire for a class of defect this repo kept hitting: tooling on
|
|
||||||
# a rarely-taken path. scripts/build-android.sh ran `npm install` on its
|
|
||||||
# clean-build branch -- in a bun project, ignoring bun.lock and
|
|
||||||
# re-resolving the tree, which is how the Tauri plugin crate/package
|
|
||||||
# versions drifted apart and broke a release build. It survived because
|
|
||||||
# clean builds are rare.
|
|
||||||
- name: Check build tooling
|
|
||||||
run: bash scripts/check-tooling.sh
|
|
||||||
|
|
||||||
- name: Check formatting
|
|
||||||
run: bun run format:check
|
|
||||||
|
|
||||||
# RATCHET — this number only ever goes DOWN. Same policy as MIN_THRESHOLD
|
|
||||||
# in traceability-check.yml and the coverage thresholds in
|
|
||||||
# vitest.config.ts. 159 is what the tree carried when the gate went in; the
|
|
||||||
# backlog is real findings (dead bindings, unkeyed {#each}, `any` at the
|
|
||||||
# IPC boundary) that eslint.config.js documents rule by rule, each parked
|
|
||||||
# at "warn" until its class is cleared and it can be promoted to "error".
|
|
||||||
# Lower this as you clear them. Never raise it to make a build pass.
|
|
||||||
- name: Lint
|
|
||||||
run: bun run lint -- --max-warnings=158
|
|
||||||
|
|
||||||
- name: Check TypeScript
|
|
||||||
run: |
|
|
||||||
bunx svelte-kit sync
|
|
||||||
bun run check
|
|
||||||
|
|
||||||
# Tauri refuses to build when a plugin's Rust crate and npm package are on
|
|
||||||
# different minor versions. Nothing here runs `tauri build` -- that only
|
|
||||||
# happens on a tag -- so a mismatch introduced on master stayed invisible
|
|
||||||
# until the release build, which is where it was found: v0.10.0 prep hit
|
|
||||||
# `tauri-plugin-log (v2.8.0) : @tauri-apps/plugin-log (v2.9.0)`. `cargo
|
|
||||||
# check`, clippy, the tests and svelte-check had all passed.
|
|
||||||
#
|
|
||||||
# `tauri info` performs the same comparison the bundler does, without a
|
|
||||||
# build. Grepping its output is crude, but the alternative is discovering
|
|
||||||
# this at tag time again.
|
|
||||||
- name: Check Tauri plugin versions match
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
if bunx tauri info 2>&1 | tee /tmp/tauri-info.txt | grep -q "version mismatched"; then
|
|
||||||
echo "::error::A Tauri plugin's Rust crate and npm package versions disagree."
|
|
||||||
echo "::error::The release build will refuse to start. Align them in"
|
|
||||||
echo "::error::src-tauri/Cargo.toml and package.json (both are pinned exactly)."
|
|
||||||
grep -A6 "version mismatched" /tmp/tauri-info.txt || true
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✅ Tauri plugin crate/package versions agree."
|
|
||||||
|
|
||||||
# Coverage rather than a bare `bun run test`: same suite, plus the
|
|
||||||
# thresholds in vitest.config.ts, so a large untested module or a deleted
|
|
||||||
# test fails here instead of being noticed months later.
|
|
||||||
- name: Run frontend tests
|
|
||||||
run: |
|
|
||||||
bunx svelte-kit sync
|
|
||||||
bun run test:coverage
|
|
||||||
|
|
||||||
# CLAUDE.md has required `cargo fmt` + `cargo clippy` before every commit
|
|
||||||
# for as long as the rule has existed, but nothing in CI checked either,
|
|
||||||
# so the requirement rested entirely on memory. Both components are baked
|
|
||||||
# into the builder image (Dockerfile.builder: `rustup component add
|
|
||||||
# rustfmt clippy`) — nothing is installed at job time.
|
|
||||||
- name: Check Rust formatting
|
|
||||||
run: |
|
|
||||||
cd src-tauri
|
|
||||||
cargo fmt --all -- --check
|
|
||||||
|
|
||||||
# Clippy is a hard gate. It was advisory while the tree carried a warning
|
|
||||||
# backlog; that backlog is gone (0 warnings on 1.97.1, the pinned
|
|
||||||
# toolchain), so a warning here is now new breakage rather than old noise.
|
|
||||||
#
|
|
||||||
# This only means anything because src-tauri/rust-toolchain.toml pins the
|
|
||||||
# compiler: clippy's lint set moves between releases, so an unpinned gate
|
|
||||||
# would fail on whatever the runner happened to install. The pin and this
|
|
||||||
# flag stand or fall together — if you unpin, drop this back to advisory.
|
|
||||||
- name: Run clippy
|
|
||||||
run: |
|
|
||||||
cd src-tauri
|
|
||||||
cargo clippy --all-targets -- -D warnings
|
|
||||||
|
|
||||||
- name: Run Rust tests
|
|
||||||
run: |
|
|
||||||
cd src-tauri
|
|
||||||
cargo test
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
# Fast per-commit Android compile check. This does NOT build a shippable APK:
|
|
||||||
# the full signed release APK is built only on tag pushes by build-release.yml
|
|
||||||
# (which runs sync-android-sources.sh + signing). Running the full bundle here
|
|
||||||
# too would duplicate a ~15min build and, without the sync step, produced an
|
|
||||||
# unsigned APK missing our custom sources/icons/proguard rules anyway.
|
|
||||||
# `cargo check` for the Android target (~1min) catches Android-specific Rust
|
|
||||||
# breakage without linking, bundling, or signing.
|
|
||||||
android-check:
|
|
||||||
name: Android Compile Check
|
|
||||||
runs-on: linux/amd64
|
|
||||||
needs: test
|
|
||||||
container:
|
|
||||||
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08
|
|
||||||
env:
|
|
||||||
ANDROID_HOME: /opt/android-sdk
|
|
||||||
ANDROID_SDK_ROOT: /opt/android-sdk
|
|
||||||
NDK_HOME: /opt/android-sdk/ndk/27.0.11902837
|
|
||||||
ANDROID_NDK_HOME: /opt/android-sdk/ndk/27.0.11902837
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Cache Rust dependencies
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
# Registry only -- never src-tauri/target. That directory is ~16 GB and
|
|
||||||
# was cached under five separate keys, which filled the runner's 74 GB
|
|
||||||
# disk at ~1.15 GB/day (23 GB in 20 days, measured Aug 2026).
|
|
||||||
# registry/src is omitted too: cargo re-extracts it for free from
|
|
||||||
# registry/cache (155 MB of .crate tarballs vs 1.1 GB extracted).
|
|
||||||
path: |
|
|
||||||
~/.cargo/registry/index
|
|
||||||
~/.cargo/registry/cache
|
|
||||||
~/.cargo/git/db
|
|
||||||
# One shared key across every job. The old per-job keys existed to stop
|
|
||||||
# debug/release target artifacts clobbering each other; with target no
|
|
||||||
# longer cached, registry contents are target-independent, so all jobs
|
|
||||||
# want the same crates. First job to finish saves; the rest restore.
|
|
||||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-cargo-registry-
|
|
||||||
|
|
||||||
- name: Cache Node dependencies
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.bun/install/cache
|
|
||||||
node_modules
|
|
||||||
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-bun-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: bun install
|
|
||||||
|
|
||||||
- name: Cargo check (aarch64-linux-android)
|
|
||||||
run: |
|
|
||||||
TC="$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin"
|
|
||||||
export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="$TC/aarch64-linux-android24-clang"
|
|
||||||
export CC_aarch64_linux_android="$TC/aarch64-linux-android24-clang"
|
|
||||||
export AR_aarch64_linux_android="$TC/llvm-ar"
|
|
||||||
cd src-tauri
|
|
||||||
cargo check --target aarch64-linux-android --lib
|
|
||||||
|
|
||||||
# Supply-chain gate. Until this job existed the project had no vulnerability
|
|
||||||
# scanning of any kind: nothing checked the ~500-crate Rust graph or the JS
|
|
||||||
# dependencies against a CVE feed, and nothing checked that everything we
|
|
||||||
# redistribute is licence-compatible with shipping JellyTau under MIT.
|
|
||||||
#
|
|
||||||
# The first run of this found eight vulnerabilities and one unsoundness
|
|
||||||
# (bytes, four in rustls-webpki, time, two in quick-xml, rand) — all fixed by
|
|
||||||
# `cargo update`, none of which anybody had reason to run.
|
|
||||||
#
|
|
||||||
# Runs in parallel with android-check rather than after `test`: a dependency
|
|
||||||
# advisory has nothing to do with whether the tests pass, and finding out
|
|
||||||
# sooner is the point.
|
|
||||||
security:
|
|
||||||
name: Supply Chain
|
|
||||||
runs-on: linux/amd64
|
|
||||||
container:
|
|
||||||
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Cache Rust dependencies
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cargo/registry/index
|
|
||||||
~/.cargo/registry/cache
|
|
||||||
~/.cargo/git/db
|
|
||||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-cargo-registry-
|
|
||||||
|
|
||||||
# cargo-deny is baked into the builder image. It fetches the RustSec
|
|
||||||
# advisory database at run time — that is *data*, like the crates
|
|
||||||
# `bun install` fetches, not a toolchain install, so the 🔴 rule in
|
|
||||||
# CLAUDE.md is not in play here.
|
|
||||||
#
|
|
||||||
# Config and every documented exception live in src-tauri/deny.toml.
|
|
||||||
# Vulnerabilities and unsoundness are hard failures with no override;
|
|
||||||
# unmaintained transitive crates that have no safe upgrade (Tauri's GTK3
|
|
||||||
# stack, the unic-* tables) are ignored there by ID, each with a reason.
|
|
||||||
- name: cargo-deny (advisories, licences, bans, sources)
|
|
||||||
run: |
|
|
||||||
cd src-tauri
|
|
||||||
cargo deny check
|
|
||||||
|
|
||||||
# Advisory for now, deliberately. The Rust graph was clean after one
|
|
||||||
# update pass, so gating it costs nothing; the JS graph has not been
|
|
||||||
# audited before and a first run that fails the build teaches everyone to
|
|
||||||
# ignore this job. Promote to a hard gate once the output is empty and
|
|
||||||
# stays empty — same approach that got clippy from advisory to -D warnings.
|
|
||||||
- name: bun audit (advisory)
|
|
||||||
run: |
|
|
||||||
bun install
|
|
||||||
bun audit || echo "::warning::bun audit reported findings — advisory for now, see CLAUDE.md"
|
|
||||||
@@ -1,707 +0,0 @@
|
|||||||
name: Build & Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- 'v*'
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
version:
|
|
||||||
description: 'Version to build (e.g., v1.0.0)'
|
|
||||||
required: false
|
|
||||||
|
|
||||||
env:
|
|
||||||
RUST_BACKTRACE: 1
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
# Incremental state is never reused between CI runs -- pure disk cost.
|
|
||||||
CARGO_INCREMENTAL: 0
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
name: Run Tests
|
|
||||||
runs-on: linux/amd64
|
|
||||||
container:
|
|
||||||
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Cache Rust dependencies
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
# Registry only -- never src-tauri/target. That directory is ~16 GB and
|
|
||||||
# was cached under five separate keys, which filled the runner's 74 GB
|
|
||||||
# disk at ~1.15 GB/day (23 GB in 20 days, measured Aug 2026).
|
|
||||||
# registry/src is omitted too: cargo re-extracts it for free from
|
|
||||||
# registry/cache (155 MB of .crate tarballs vs 1.1 GB extracted).
|
|
||||||
path: |
|
|
||||||
~/.cargo/registry/index
|
|
||||||
~/.cargo/registry/cache
|
|
||||||
~/.cargo/git/db
|
|
||||||
# One shared key across every job. The old per-job keys existed to stop
|
|
||||||
# debug/release target artifacts clobbering each other; with target no
|
|
||||||
# longer cached, registry contents are target-independent, so all jobs
|
|
||||||
# want the same crates. First job to finish saves; the rest restore.
|
|
||||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-cargo-registry-
|
|
||||||
|
|
||||||
- name: Cache Node dependencies
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.bun/install/cache
|
|
||||||
node_modules
|
|
||||||
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-bun-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: bun install
|
|
||||||
|
|
||||||
- name: Run frontend tests
|
|
||||||
run: |
|
|
||||||
bunx svelte-kit sync
|
|
||||||
bun run test --run
|
|
||||||
continue-on-error: false
|
|
||||||
|
|
||||||
# Same gate as build-and-test.yml. A release must not ship from a tree
|
|
||||||
# that would fail the per-commit checks. rustfmt/clippy come from the
|
|
||||||
# builder image; nothing is installed here.
|
|
||||||
- name: Check Rust formatting
|
|
||||||
run: |
|
|
||||||
cd src-tauri
|
|
||||||
cargo fmt --all -- --check
|
|
||||||
continue-on-error: false
|
|
||||||
|
|
||||||
# Advisory until the ~51 pre-existing warnings are cleared; see the longer
|
|
||||||
# note in build-and-test.yml. Tighten both to `-- -D warnings` together.
|
|
||||||
- name: Run clippy (advisory)
|
|
||||||
run: |
|
|
||||||
cd src-tauri
|
|
||||||
cargo clippy --all-targets
|
|
||||||
|
|
||||||
- name: Run Rust tests
|
|
||||||
run: bun run test:rust
|
|
||||||
continue-on-error: false
|
|
||||||
|
|
||||||
- name: Check TypeScript
|
|
||||||
run: bun run check
|
|
||||||
continue-on-error: false
|
|
||||||
|
|
||||||
build-linux:
|
|
||||||
name: Build Linux
|
|
||||||
runs-on: linux/amd64
|
|
||||||
needs: test
|
|
||||||
container:
|
|
||||||
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Cache Rust dependencies
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
# Registry only -- never src-tauri/target. That directory is ~16 GB and
|
|
||||||
# was cached under five separate keys, which filled the runner's 74 GB
|
|
||||||
# disk at ~1.15 GB/day (23 GB in 20 days, measured Aug 2026).
|
|
||||||
# registry/src is omitted too: cargo re-extracts it for free from
|
|
||||||
# registry/cache (155 MB of .crate tarballs vs 1.1 GB extracted).
|
|
||||||
path: |
|
|
||||||
~/.cargo/registry/index
|
|
||||||
~/.cargo/registry/cache
|
|
||||||
~/.cargo/git/db
|
|
||||||
# One shared key across every job. The old per-job keys existed to stop
|
|
||||||
# debug/release target artifacts clobbering each other; with target no
|
|
||||||
# longer cached, registry contents are target-independent, so all jobs
|
|
||||||
# want the same crates. First job to finish saves; the rest restore.
|
|
||||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-cargo-registry-
|
|
||||||
|
|
||||||
- name: Cache Node dependencies
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.bun/install/cache
|
|
||||||
node_modules
|
|
||||||
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-bun-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: bun install
|
|
||||||
|
|
||||||
# The Linux job previously had no version step at all, so a tagged release
|
|
||||||
# built Linux packages from whatever version happened to be committed.
|
|
||||||
- name: Set app version from tag
|
|
||||||
run: ./scripts/set-version.sh "${GITHUB_REF#refs/tags/}"
|
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
|
||||||
|
|
||||||
# TAURI_SKIP_UPDATER is gone: it was suppressing the updater artifacts
|
|
||||||
# (.AppImage.tar.gz + .sig) that the update manifest points at, back when
|
|
||||||
# there was no updater to feed. With the signing key present, `tauri build`
|
|
||||||
# emits and signs them.
|
|
||||||
#
|
|
||||||
# If TAURI_SIGNING_PRIVATE_KEY is ever absent the build fails loudly rather
|
|
||||||
# than quietly shipping an unsigned release that no client will accept --
|
|
||||||
# which is the behaviour we want.
|
|
||||||
# Same hazard as the Windows job: the bundle directory is never cleaned by
|
|
||||||
# cargo and the runner reuses src-tauri/target, while the copy step below
|
|
||||||
# globs bundle/deb/*.deb and friends. Windows is where this actually bit
|
|
||||||
# (v0.8.2 shipped thirteen stale installers), but only because Linux
|
|
||||||
# packaging is newer -- the glob is identical. Remove the directory so a
|
|
||||||
# stale artifact cannot exist to be copied.
|
|
||||||
- name: Clear previous bundle output
|
|
||||||
run: rm -rf src-tauri/target/release/bundle
|
|
||||||
|
|
||||||
- name: Build for Linux
|
|
||||||
run: bun run tauri build
|
|
||||||
env:
|
|
||||||
# linuxdeploy's bundled `strip` cannot parse the `.relr.dyn` section
|
|
||||||
# modern toolchains emit, and fails on every bundled library:
|
|
||||||
# strip: libzstd.so.1: unknown type [0x13] section `.relr.dyn'
|
|
||||||
# failed to bundle project `failed to run linuxdeploy`
|
|
||||||
# Ubuntu 23.10+ links with -z pack-relative-relocs by default, so this
|
|
||||||
# image hits it. Skipping strip is linuxdeploy's documented escape
|
|
||||||
# hatch; the cost is a larger AppImage. Found by building the target
|
|
||||||
# locally before tagging -- nothing in CI builds the app, so a release
|
|
||||||
# would have been the first time anyone discovered the AppImage target
|
|
||||||
# does not work.
|
|
||||||
NO_STRIP: "true"
|
|
||||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
|
||||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Prepare Linux artifacts
|
|
||||||
run: |
|
|
||||||
mkdir -p dist/linux
|
|
||||||
# Match by extension, not by product name. Bundle filenames follow
|
|
||||||
# `productName`, so renaming the app (jellytau -> JellyTau) made the
|
|
||||||
# old `jellytau_*.deb` glob match nothing — and because the copy was
|
|
||||||
# wrapped in `if [ -f ... ]`, the artifact simply vanished from the
|
|
||||||
# release with no error. Each bundle directory holds one file.
|
|
||||||
#
|
|
||||||
# `if [ -f "dir/"*.ext ]` was also wrong on its own terms: with more
|
|
||||||
# than one match `test` gets extra arguments and fails.
|
|
||||||
#
|
|
||||||
# No `shopt -s nullglob` here: the runner executes `run:` blocks with
|
|
||||||
# POSIX sh, where shopt does not exist -- it exited 127 and killed the
|
|
||||||
# step (which is why v0.9.0 and v0.9.1 built but never published).
|
|
||||||
# Without nullglob an unmatched pattern stays literal, so test each
|
|
||||||
# candidate instead. Same POSIX-only rule as traceability-check.yml.
|
|
||||||
#
|
|
||||||
# The .AppImage.tar.gz + .sig pair is what the updater downloads and
|
|
||||||
# verifies; the plain .AppImage is what a human downloads. Both ship.
|
|
||||||
for bundle in \
|
|
||||||
src-tauri/target/release/bundle/appimage/*.AppImage \
|
|
||||||
src-tauri/target/release/bundle/appimage/*.AppImage.tar.gz \
|
|
||||||
src-tauri/target/release/bundle/appimage/*.AppImage.tar.gz.sig \
|
|
||||||
src-tauri/target/release/bundle/deb/*.deb \
|
|
||||||
src-tauri/target/release/bundle/rpm/*.rpm; do
|
|
||||||
[ -e "$bundle" ] || continue
|
|
||||||
cp -v "$bundle" dist/linux/
|
|
||||||
done
|
|
||||||
|
|
||||||
# An AppImage that did not build means no updater artifact either, and
|
|
||||||
# the release notes have advertised an AppImage for months. Fail rather
|
|
||||||
# than publish a release whose manifest points at nothing.
|
|
||||||
if ! ls dist/linux/*.AppImage >/dev/null 2>&1; then
|
|
||||||
echo "::error::No AppImage produced -- check bundle.targets in tauri.conf.json"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# A release with no Linux package is a failure, not a quiet success.
|
|
||||||
if [ -z "$(ls -A dist/linux/)" ]; then
|
|
||||||
echo "::error::No Linux bundles found under src-tauri/target/release/bundle/"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
ls -lah dist/linux/
|
|
||||||
|
|
||||||
- name: Upload Linux build artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: jellytau-linux
|
|
||||||
path: dist/linux/
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
build-windows:
|
|
||||||
name: Build Windows
|
|
||||||
runs-on: linux/amd64
|
|
||||||
needs: test
|
|
||||||
# Cross-compiled from Linux via the official Tauri path (MSVC + cargo-xwin),
|
|
||||||
# baked into the builder image. No toolchain installs here — the image has
|
|
||||||
# cargo-xwin, clang/clang-cl, lld, llvm, nsis and the msvc target.
|
|
||||||
container:
|
|
||||||
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Cache Rust dependencies
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
# Registry only -- never src-tauri/target. That directory is ~16 GB and
|
|
||||||
# was cached under five separate keys, which filled the runner's 74 GB
|
|
||||||
# disk at ~1.15 GB/day (23 GB in 20 days, measured Aug 2026).
|
|
||||||
# registry/src is omitted too: cargo re-extracts it for free from
|
|
||||||
# registry/cache (155 MB of .crate tarballs vs 1.1 GB extracted).
|
|
||||||
path: |
|
|
||||||
~/.cargo/registry/index
|
|
||||||
~/.cargo/registry/cache
|
|
||||||
~/.cargo/git/db
|
|
||||||
# One shared key across every job. The old per-job keys existed to stop
|
|
||||||
# debug/release target artifacts clobbering each other; with target no
|
|
||||||
# longer cached, registry contents are target-independent, so all jobs
|
|
||||||
# want the same crates. First job to finish saves; the rest restore.
|
|
||||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-cargo-registry-
|
|
||||||
|
|
||||||
- name: Cache Windows CRT/SDK (cargo-xwin)
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: ~/.cache/cargo-xwin
|
|
||||||
# Contents track the xwin version baked into the builder image, not our
|
|
||||||
# lockfile -- keying this on Cargo.lock re-downloaded the whole SDK on
|
|
||||||
# every release bump. Bump the suffix by hand if the image's xwin moves.
|
|
||||||
key: ${{ runner.os }}-cargo-xwin-v1
|
|
||||||
|
|
||||||
- name: Cache Node dependencies
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.bun/install/cache
|
|
||||||
node_modules
|
|
||||||
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-bun-
|
|
||||||
|
|
||||||
# The tag is the single source of truth for a release version; the script
|
|
||||||
# stamps every file that carries it (package.json, tauri.conf.json,
|
|
||||||
# Cargo.toml, Cargo.lock). This step used to sed only tauri.conf.json, so
|
|
||||||
# the other three shipped whatever was committed.
|
|
||||||
- name: Set app version from tag
|
|
||||||
run: ./scripts/set-version.sh "${GITHUB_REF#refs/tags/}"
|
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
|
||||||
|
|
||||||
- name: Build Windows (NSIS installer + exe)
|
|
||||||
run: OUTPUT_DIR="$PWD/dist/windows" WIN_BUNDLES=nsis ./scripts/build-windows-cross.sh
|
|
||||||
env:
|
|
||||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
|
||||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
||||||
|
|
||||||
- name: List Windows artifacts
|
|
||||||
run: ls -lah dist/windows/
|
|
||||||
|
|
||||||
- name: Upload Windows build artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: jellytau-windows
|
|
||||||
path: dist/windows/
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
build-android:
|
|
||||||
name: Build Android
|
|
||||||
runs-on: linux/amd64
|
|
||||||
needs: test
|
|
||||||
container:
|
|
||||||
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08
|
|
||||||
env:
|
|
||||||
ANDROID_HOME: /opt/android-sdk
|
|
||||||
ANDROID_SDK_ROOT: /opt/android-sdk
|
|
||||||
ANDROID_NDK_HOME: /opt/android-sdk/ndk/27.0.11902837
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Cache Rust dependencies
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
# Registry only -- never src-tauri/target. That directory is ~16 GB and
|
|
||||||
# was cached under five separate keys, which filled the runner's 74 GB
|
|
||||||
# disk at ~1.15 GB/day (23 GB in 20 days, measured Aug 2026).
|
|
||||||
# registry/src is omitted too: cargo re-extracts it for free from
|
|
||||||
# registry/cache (155 MB of .crate tarballs vs 1.1 GB extracted).
|
|
||||||
path: |
|
|
||||||
~/.cargo/registry/index
|
|
||||||
~/.cargo/registry/cache
|
|
||||||
~/.cargo/git/db
|
|
||||||
# One shared key across every job. The old per-job keys existed to stop
|
|
||||||
# debug/release target artifacts clobbering each other; with target no
|
|
||||||
# longer cached, registry contents are target-independent, so all jobs
|
|
||||||
# want the same crates. First job to finish saves; the rest restore.
|
|
||||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-cargo-registry-
|
|
||||||
|
|
||||||
- name: Cache Node dependencies
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.bun/install/cache
|
|
||||||
node_modules
|
|
||||||
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-bun-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: bun install
|
|
||||||
|
|
||||||
# Stamp before `android init`: it derives its generated project (including
|
|
||||||
# the initial versionCode) from tauri.conf.json.
|
|
||||||
- name: Set app version from tag
|
|
||||||
run: ./scripts/set-version.sh "${GITHUB_REF#refs/tags/}"
|
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
|
||||||
|
|
||||||
- name: Initialize Android project
|
|
||||||
run: bun run tauri android init
|
|
||||||
|
|
||||||
# Re-run after init: tauri.properties only exists now, and its
|
|
||||||
# autogenerated versionCode (0.0.15 -> 15) is both tiny and NOT monotonic
|
|
||||||
# against the 1000 floor already shipped in the field. The script rewrites
|
|
||||||
# it as 1000 + major*10000 + minor*100 + patch. Runs unconditionally so
|
|
||||||
# untagged builds get a sane code too, derived from git describe.
|
|
||||||
- name: Pin a monotonic Android versionCode
|
|
||||||
run: ./scripts/set-version.sh "${GITHUB_REF#refs/tags/}"
|
|
||||||
|
|
||||||
- name: Sync custom Android sources & gradle config
|
|
||||||
run: ./scripts/sync-android-sources.sh
|
|
||||||
|
|
||||||
- name: Write signing keystore
|
|
||||||
run: |
|
|
||||||
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > "$RUNNER_TEMP/jellytau-release.jks"
|
|
||||||
cat > src-tauri/gen/android/keystore.properties <<EOF
|
|
||||||
storeFile=$RUNNER_TEMP/jellytau-release.jks
|
|
||||||
storePassword=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
|
|
||||||
keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}
|
|
||||||
keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# `--apk` is a boolean flag, not `--apk true`. tauri-cli took a value here
|
|
||||||
# until 2.10; from 2.11 the stray `true` is parsed as a positional and the
|
|
||||||
# command fails with "unexpected argument 'true' found" before building.
|
|
||||||
# This line and scripts/build-android.sh must agree.
|
|
||||||
- name: Build signed Android APK
|
|
||||||
run: bun run tauri android build --apk --target aarch64
|
|
||||||
|
|
||||||
- name: Collect & verify signed APK
|
|
||||||
run: |
|
|
||||||
mkdir -p dist/android
|
|
||||||
APK=$(find src-tauri/gen/android/app/build/outputs/apk -name '*-release.apk' | head -1)
|
|
||||||
if [ -z "$APK" ]; then echo "❌ No release APK produced"; exit 1; fi
|
|
||||||
cp "$APK" dist/android/jellytau-release.apk
|
|
||||||
APKSIGNER=$(find "$ANDROID_SDK_ROOT/build-tools" -name apksigner | sort -V | tail -1)
|
|
||||||
echo "🔏 Verifying signature with $APKSIGNER"
|
|
||||||
"$APKSIGNER" verify --print-certs dist/android/jellytau-release.apk
|
|
||||||
ls -lah dist/android/
|
|
||||||
|
|
||||||
- name: Upload Android build artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: jellytau-android
|
|
||||||
path: dist/android/
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
create-release:
|
|
||||||
name: Create Release
|
|
||||||
runs-on: linux/amd64
|
|
||||||
needs: [build-linux, build-windows, build-android]
|
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
|
||||||
container:
|
|
||||||
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Get version from tag
|
|
||||||
id: tag_name
|
|
||||||
run: |
|
|
||||||
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
|
||||||
echo "RELEASE_NAME=JellyTau ${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Download Linux artifacts
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: jellytau-linux
|
|
||||||
path: artifacts/linux/
|
|
||||||
|
|
||||||
- name: Download Windows artifacts
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: jellytau-windows
|
|
||||||
path: artifacts/windows/
|
|
||||||
|
|
||||||
- name: Download Android artifacts
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: jellytau-android
|
|
||||||
path: artifacts/android/
|
|
||||||
|
|
||||||
# Runs before the SBOM, the checksums and the upload -- everything
|
|
||||||
# downstream describes this set of files, so a stale artifact must be
|
|
||||||
# caught before it gets hashed into SHA256SUMS and published as though it
|
|
||||||
# belonged to this release.
|
|
||||||
#
|
|
||||||
# See the script for the eight months of releases that shipped their
|
|
||||||
# predecessors' Windows installers.
|
|
||||||
- name: Verify artifacts belong to this release
|
|
||||||
run: |
|
|
||||||
./scripts/check-release-artifacts.sh \
|
|
||||||
"${{ steps.tag_name.outputs.VERSION }}" \
|
|
||||||
artifacts/linux artifacts/windows artifacts/android
|
|
||||||
|
|
||||||
# Software Bill of Materials, one per half of the app. Without it there is
|
|
||||||
# no answer to "does this release contain <vulnerable crate>?" other than
|
|
||||||
# rebuilding the tag and re-resolving it. cargo-cyclonedx is in the builder
|
|
||||||
# image; the JS side is read straight from the lockfile bun install used.
|
|
||||||
- name: Generate SBOM
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
mkdir -p artifacts/sbom
|
|
||||||
cd src-tauri
|
|
||||||
cargo cyclonedx --format json
|
|
||||||
find . -maxdepth 2 -name "*.cdx.json" -exec cp -v {} ../artifacts/sbom/ \;
|
|
||||||
cd ..
|
|
||||||
bun install --frozen-lockfile
|
|
||||||
bun pm ls --all > artifacts/sbom/frontend-dependencies.txt
|
|
||||||
ls -lah artifacts/sbom/
|
|
||||||
|
|
||||||
# Checksums over everything being published. A release of unsigned Linux
|
|
||||||
# and Windows binaries with no checksum gives a user no way at all to tell
|
|
||||||
# a corrupted or substituted download from a good one — and the AppImage
|
|
||||||
# and NSIS installer are both fetched over plain HTTP redirects.
|
|
||||||
#
|
|
||||||
# Written with paths relative to the asset directory so `sha256sum -c
|
|
||||||
# SHA256SUMS` works in the directory a user downloaded into.
|
|
||||||
# The update manifest. Built before the checksums so latest.json is not
|
|
||||||
# itself hashed into SHA256SUMS (it is metadata about the release, not a
|
|
||||||
# download), and after the artifacts exist so the signatures can be read.
|
|
||||||
#
|
|
||||||
# Why a dedicated `updater` branch and a raw-file URL: this Gitea serves
|
|
||||||
# /releases/download/<tag>/<asset> but returns 404 for
|
|
||||||
# /releases/latest/download/<asset>, so there is no stable "latest release"
|
|
||||||
# URL to point a client at. The gitea-pages branch is force-pushed whole by
|
|
||||||
# publish-docs.yml, so hosting the manifest there would delete it on the
|
|
||||||
# next docs build. An orphan branch that only ever contains latest.json is
|
|
||||||
# the one location both stable and ours.
|
|
||||||
- name: Build update manifest (latest.json)
|
|
||||||
id: manifest
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
VERSION="${{ steps.tag_name.outputs.VERSION }}"
|
|
||||||
# The manifest carries the bare version; the tag carries the v prefix.
|
|
||||||
PLAIN="${VERSION#v}"
|
|
||||||
BASE="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/releases/download/${VERSION}"
|
|
||||||
|
|
||||||
# Tauri matches on "<os>-<arch>". We ship one desktop arch today.
|
|
||||||
APPIMAGE_SIG=""
|
|
||||||
NSIS_SIG=""
|
|
||||||
APPIMAGE_URL=""
|
|
||||||
NSIS_URL=""
|
|
||||||
|
|
||||||
for f in artifacts/linux/*.AppImage.tar.gz; do
|
|
||||||
[ -e "$f" ] || continue
|
|
||||||
APPIMAGE_URL="${BASE}/$(basename "$f")"
|
|
||||||
[ -e "$f.sig" ] && APPIMAGE_SIG="$(cat "$f.sig")"
|
|
||||||
done
|
|
||||||
|
|
||||||
for f in artifacts/windows/*-setup.exe; do
|
|
||||||
[ -e "$f" ] || continue
|
|
||||||
NSIS_URL="${BASE}/$(basename "$f")"
|
|
||||||
[ -e "$f.sig" ] && NSIS_SIG="$(cat "$f.sig")"
|
|
||||||
done
|
|
||||||
|
|
||||||
# A manifest with an empty signature is worse than no manifest: the
|
|
||||||
# client rejects it after downloading the whole payload.
|
|
||||||
if [ -z "$APPIMAGE_SIG" ] || [ -z "$NSIS_SIG" ]; then
|
|
||||||
echo "::error::Missing updater signature (appimage='$APPIMAGE_SIG' nsis='$NSIS_SIG')."
|
|
||||||
echo "::error::Check that TAURI_SIGNING_PRIVATE_KEY reached both desktop build jobs."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# What the in-app update prompt shows. Same reviewed source as the
|
|
||||||
# release body -- the CHANGELOG section for this version, not the
|
|
||||||
# traceability draft.
|
|
||||||
NOTES="$(awk -v ver="## $VERSION" '$0==ver{f=1;next} /^## /{if(f)exit} f' CHANGELOG.md | head -c 4000)"
|
|
||||||
[ -n "$NOTES" ] || NOTES="See the release page for details."
|
|
||||||
|
|
||||||
jq -n \
|
|
||||||
--arg version "$PLAIN" \
|
|
||||||
--arg notes "$NOTES" \
|
|
||||||
--arg pub_date "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
||||||
--arg lin_sig "$APPIMAGE_SIG" --arg lin_url "$APPIMAGE_URL" \
|
|
||||||
--arg win_sig "$NSIS_SIG" --arg win_url "$NSIS_URL" \
|
|
||||||
'{
|
|
||||||
version: $version,
|
|
||||||
notes: $notes,
|
|
||||||
pub_date: $pub_date,
|
|
||||||
platforms: {
|
|
||||||
"linux-x86_64": { signature: $lin_sig, url: $lin_url },
|
|
||||||
"windows-x86_64": { signature: $win_sig, url: $win_url }
|
|
||||||
}
|
|
||||||
}' > latest.json
|
|
||||||
|
|
||||||
echo "📄 latest.json:"
|
|
||||||
cat latest.json
|
|
||||||
|
|
||||||
- name: Publish latest.json to the updater branch
|
|
||||||
env:
|
|
||||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
||||||
AUTO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
TOKEN="${GITEA_TOKEN:-$AUTO_TOKEN}"
|
|
||||||
HOST="$(echo "$GITHUB_SERVER_URL" | sed -E 's#^https?://##')"
|
|
||||||
REMOTE="https://oauth2:${TOKEN}@${HOST}/${GITHUB_REPOSITORY}.git"
|
|
||||||
|
|
||||||
# Built in a scratch repo, NOT by switching branches in the checkout.
|
|
||||||
# `git checkout --orphan` here would leave every later step standing on
|
|
||||||
# a one-commit branch -- and the next step but one runs
|
|
||||||
# `bun run release:notes`, which resolves a commit range against the
|
|
||||||
# real history and would silently produce nothing.
|
|
||||||
WORK="$RUNNER_TEMP/updater-branch"
|
|
||||||
rm -rf "$WORK"
|
|
||||||
mkdir -p "$WORK"
|
|
||||||
cp latest.json "$WORK/latest.json"
|
|
||||||
cd "$WORK"
|
|
||||||
git init -q
|
|
||||||
git config user.email "ci@jellytau"
|
|
||||||
git config user.name "JellyTau CI"
|
|
||||||
git add latest.json
|
|
||||||
git commit -qm "chore(updater): manifest for ${{ steps.tag_name.outputs.VERSION }}"
|
|
||||||
echo "🚀 Force-pushing update manifest to the updater branch"
|
|
||||||
# Force-push: the branch holds exactly one file and no history worth
|
|
||||||
# keeping, same shape as publish-docs.yml's gitea-pages.
|
|
||||||
git push -f "$REMOTE" HEAD:refs/heads/updater
|
|
||||||
echo "✅ Served at ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/raw/branch/updater/latest.json"
|
|
||||||
|
|
||||||
- name: Generate SHA256SUMS
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
mkdir -p artifacts/release
|
|
||||||
find artifacts/linux artifacts/windows artifacts/android -type f -exec cp -v {} artifacts/release/ \;
|
|
||||||
cd artifacts/release
|
|
||||||
sha256sum * > SHA256SUMS
|
|
||||||
echo "🔐 Published checksums:"
|
|
||||||
cat SHA256SUMS
|
|
||||||
# Verify what we just wrote, so a broken checksum file fails the
|
|
||||||
# release rather than shipping and failing for users.
|
|
||||||
sha256sum -c SHA256SUMS
|
|
||||||
|
|
||||||
# The published body is the hand-written CHANGELOG.md section for this
|
|
||||||
# version. `bun run release:notes` is printed into the job log as a
|
|
||||||
# drafting aid, but is NOT published: CLAUDE.md is explicit that its
|
|
||||||
# output is "a reviewed draft, not a final changelog", and publishing it
|
|
||||||
# unreviewed proved the point -- a range containing a repo-wide prettier
|
|
||||||
# sweep resolved to nearly the whole requirement matrix and produced notes
|
|
||||||
# claiming one release had added the entire application.
|
|
||||||
#
|
|
||||||
# A missing CHANGELOG section fails the release. A release whose notes say
|
|
||||||
# nothing is worse than one that waits for a maintainer to write two
|
|
||||||
# sentences, and the checklist already requires that entry.
|
|
||||||
- name: Prepare release notes
|
|
||||||
id: release_notes
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
VERSION="${{ steps.tag_name.outputs.VERSION }}"
|
|
||||||
|
|
||||||
echo "📋 Traceability draft (for reference; not published):"
|
|
||||||
bun run release:notes 2>/dev/null || echo "(could not derive a draft)"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# The section between this version's heading and the next one.
|
|
||||||
CHANGES=$(awk -v ver="## $VERSION" '$0==ver{f=1;next} /^## /{if(f)exit} f' CHANGELOG.md)
|
|
||||||
if [ -z "$(echo "$CHANGES" | tr -d '[:space:]')" ]; then
|
|
||||||
echo "::error::CHANGELOG.md has no '## $VERSION' section."
|
|
||||||
echo "::error::Add the entry for this version and re-tag; see docs/release-checklist.md."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
{
|
|
||||||
echo "$CHANGES"
|
|
||||||
echo ""
|
|
||||||
echo "### Downloads"
|
|
||||||
echo ""
|
|
||||||
echo "| Platform | File |"
|
|
||||||
echo "|---|---|"
|
|
||||||
echo "| Linux (portable) | \`*.AppImage\` — \`chmod +x\` and run |"
|
|
||||||
echo "| Linux (Debian/Ubuntu) | \`*.deb\` — \`sudo dpkg -i\` |"
|
|
||||||
echo "| Linux (Fedora/openSUSE) | \`*.rpm\` — \`sudo rpm -i\` |"
|
|
||||||
echo "| Windows | \`*-setup.exe\` (NSIS). Unsigned — SmartScreen may warn on first run. |"
|
|
||||||
echo "| Android | \`*.apk\` sideload, or \`*.aab\` for Play Console |"
|
|
||||||
echo ""
|
|
||||||
echo "Desktop builds check for updates from here and can install a new"
|
|
||||||
echo "version in place, verifying its signature first."
|
|
||||||
echo ""
|
|
||||||
echo "### Verifying your download"
|
|
||||||
echo ""
|
|
||||||
echo "\`\`\`bash"
|
|
||||||
echo "sha256sum -c SHA256SUMS"
|
|
||||||
echo "\`\`\`"
|
|
||||||
echo ""
|
|
||||||
echo "\`SHA256SUMS\` covers every file in this release. An SBOM"
|
|
||||||
echo "(\`*.cdx.json\`, \`frontend-dependencies.txt\`) lists what went into it."
|
|
||||||
echo ""
|
|
||||||
echo "### Requirements"
|
|
||||||
echo ""
|
|
||||||
echo "- **Linux:** 64-bit, GLIBC 2.29+"
|
|
||||||
echo "- **Windows:** 64-bit Windows 10 or later"
|
|
||||||
echo "- **Android:** 8.0 or later, ~50 MB free"
|
|
||||||
echo ""
|
|
||||||
echo "---"
|
|
||||||
echo "Report a problem: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/issues"
|
|
||||||
} > release_notes.md
|
|
||||||
|
|
||||||
echo "📝 Release notes:"
|
|
||||||
cat release_notes.md
|
|
||||||
|
|
||||||
- name: Publish Gitea release & upload assets
|
|
||||||
env:
|
|
||||||
# GITEA_TOKEN (a PAT) is preferred; falls back to the auto-provided token.
|
|
||||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
||||||
AUTO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
command -v jq >/dev/null || { echo "❌ jq is required on the runner"; exit 1; }
|
|
||||||
VERSION="${{ steps.tag_name.outputs.VERSION }}"
|
|
||||||
API="${GITHUB_SERVER_URL}/api/v1"
|
|
||||||
REPO="${GITHUB_REPOSITORY}"
|
|
||||||
TOKEN="${GITEA_TOKEN:-$AUTO_TOKEN}"
|
|
||||||
case "$VERSION" in *rc*|*beta*|*alpha*) PRE=true;; *) PRE=false;; esac
|
|
||||||
|
|
||||||
PAYLOAD=$(jq -n \
|
|
||||||
--arg tag "$VERSION" \
|
|
||||||
--arg name "JellyTau $VERSION" \
|
|
||||||
--rawfile body release_notes.md \
|
|
||||||
--argjson pre "$PRE" \
|
|
||||||
'{tag_name:$tag, name:$name, body:$body, draft:false, prerelease:$pre}')
|
|
||||||
|
|
||||||
echo "📦 Creating release $VERSION on $REPO"
|
|
||||||
# -f drops on HTTP error; capture status so an existing release (409) is handled gracefully.
|
|
||||||
HTTP=$(curl -sS -o resp.json -w '%{http_code}' -X POST "$API/repos/$REPO/releases" \
|
|
||||||
-H "Authorization: token $TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "$PAYLOAD")
|
|
||||||
if [ "$HTTP" = "201" ]; then
|
|
||||||
RELEASE_ID=$(jq -r '.id' resp.json)
|
|
||||||
elif [ "$HTTP" = "409" ]; then
|
|
||||||
echo "ℹ️ Release $VERSION already exists; fetching its id to upload assets"
|
|
||||||
RELEASE_ID=$(curl -fsS "$API/repos/$REPO/releases/tags/$VERSION" \
|
|
||||||
-H "Authorization: token $TOKEN" | jq -r '.id')
|
|
||||||
else
|
|
||||||
echo "❌ Failed to create release (HTTP $HTTP):"; cat resp.json; exit 1
|
|
||||||
fi
|
|
||||||
echo "Release id=$RELEASE_ID"
|
|
||||||
|
|
||||||
# artifacts/release/ holds a copy of every platform artifact plus the
|
|
||||||
# SHA256SUMS generated over exactly that set, so the checksums describe
|
|
||||||
# precisely what is uploaded. artifacts/sbom/ rides along.
|
|
||||||
for f in artifacts/release/* artifacts/sbom/*; do
|
|
||||||
[ -f "$f" ] || continue
|
|
||||||
echo "⬆️ Uploading $(basename "$f")"
|
|
||||||
curl -fsS -X POST \
|
|
||||||
"$API/repos/$REPO/releases/$RELEASE_ID/assets?name=$(basename "$f")" \
|
|
||||||
-H "Authorization: token $TOKEN" \
|
|
||||||
-F "attachment=@$f" >/dev/null
|
|
||||||
done
|
|
||||||
echo "✅ Release $VERSION published with assets"
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
name: Publish Documentation
|
|
||||||
|
|
||||||
# Renders the markdown docs (docs/*.md) into an mdBook site, builds the Rust
|
|
||||||
# API reference with cargo doc, and force-pushes the combined output to the
|
|
||||||
# orphan `gitea-pages` branch that the Gitea Pages server serves.
|
|
||||||
#
|
|
||||||
# The published matrix is regenerated during the build, so it is never stale.
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
# Only one docs publish at a time; a newer push supersedes an in-flight run.
|
|
||||||
group: publish-docs
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish-docs:
|
|
||||||
name: Build & publish docs to gitea-pages
|
|
||||||
runs-on: linux/amd64
|
|
||||||
container:
|
|
||||||
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
# bun is baked into jellytau-builder (see Dockerfile.builder); no setup-bun
|
|
||||||
# action needed — fetching it stalls on this Gitea runner.
|
|
||||||
- name: Install dependencies
|
|
||||||
run: bun install
|
|
||||||
|
|
||||||
# mdBook is baked into jellytau-builder (Dockerfile.builder, MDBOOK_VERSION).
|
|
||||||
# It used to be curl'd from GitHub releases straight into /usr/local/bin
|
|
||||||
# right here, which was a toolchain install at job time — the exact thing
|
|
||||||
# CLAUDE.md's 🔴 rule forbids — and made every docs publish depend on
|
|
||||||
# GitHub's CDN answering. To move the version, bump it in the image.
|
|
||||||
- name: Confirm mdBook is present
|
|
||||||
run: mdbook --version
|
|
||||||
|
|
||||||
- name: Regenerate traceability matrix (keep published copy current)
|
|
||||||
run: bun run traces:markdown
|
|
||||||
|
|
||||||
- name: Assemble mdBook sources
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
# mdBook's src is docs/. Drop in the SUMMARY and the generated
|
|
||||||
# intro + API redirect pages (build artifacts, not committed).
|
|
||||||
cp docs-site/SUMMARY.md docs/SUMMARY.md
|
|
||||||
|
|
||||||
cat > docs/README.md <<'EOF'
|
|
||||||
# JellyTau Documentation
|
|
||||||
|
|
||||||
Cross-platform Jellyfin client — business logic in a Rust backend,
|
|
||||||
SvelteKit + TypeScript frontend, talking over Tauri v2 IPC.
|
|
||||||
|
|
||||||
- **[Requirements Specification](requirements.md)** — user, integration, and development requirements.
|
|
||||||
- **[Traceability Matrix](traceability.md)** — generated map from requirements to code (regenerated on every publish).
|
|
||||||
- **[Architecture](architecture/README.md)** — backend, frontend, data flow, platform backends.
|
|
||||||
- **[Rust API Reference](api/index.html)** — rustdoc for the `src-tauri` backend.
|
|
||||||
|
|
||||||
_This site is published automatically from `master` by the `publish-docs` CI job._
|
|
||||||
EOF
|
|
||||||
|
|
||||||
cat > docs/api-redirect.md <<'EOF'
|
|
||||||
# Rust API Reference
|
|
||||||
|
|
||||||
The full backend API reference is generated by `cargo doc` (rustdoc).
|
|
||||||
|
|
||||||
👉 **[Open the Rust API Reference](api/index.html)**
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Build mdBook site
|
|
||||||
run: mdbook build docs-site --dest-dir "$GITHUB_WORKSPACE/site"
|
|
||||||
|
|
||||||
- name: Build Rust API docs (cargo doc)
|
|
||||||
working-directory: src-tauri
|
|
||||||
# --no-deps keeps it to our own crate (fast, focused); document private
|
|
||||||
# items so internal modules/commands appear.
|
|
||||||
run: |
|
|
||||||
cargo doc --no-deps --document-private-items
|
|
||||||
# The backend modules/commands live in the LIB crate (jellytau_lib);
|
|
||||||
# the bin crate (jellytau) is a near-empty shim. Land on the lib.
|
|
||||||
echo '<meta http-equiv="refresh" content="0; url=jellytau_lib/index.html">' \
|
|
||||||
> target/doc/index.html
|
|
||||||
|
|
||||||
- name: Assemble published output
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
mkdir -p "$GITHUB_WORKSPACE/site/api"
|
|
||||||
cp -r src-tauri/target/doc/. "$GITHUB_WORKSPACE/site/api/"
|
|
||||||
# Disable Jekyll processing on the pages branch.
|
|
||||||
touch "$GITHUB_WORKSPACE/site/.nojekyll"
|
|
||||||
ls -la "$GITHUB_WORKSPACE/site"
|
|
||||||
|
|
||||||
- name: Push to gitea-pages branch
|
|
||||||
env:
|
|
||||||
# PAT preferred; falls back to the auto-provided token (same pattern
|
|
||||||
# as build-release.yml).
|
|
||||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
||||||
AUTO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
TOKEN="${GITEA_TOKEN:-$AUTO_TOKEN}"
|
|
||||||
REPO="${GITHUB_REPOSITORY}"
|
|
||||||
HOST="$(echo "$GITHUB_SERVER_URL" | sed -E 's#^https?://##')"
|
|
||||||
REMOTE="https://oauth2:${TOKEN}@${HOST}/${REPO}.git"
|
|
||||||
|
|
||||||
cd "$GITHUB_WORKSPACE/site"
|
|
||||||
git init -q
|
|
||||||
git config user.name "gitea-actions"
|
|
||||||
git config user.email "actions@gitea.tourolle.paris"
|
|
||||||
git checkout -q -b gitea-pages
|
|
||||||
git add -A
|
|
||||||
# POSIX sh has no ${VAR::N} substring expansion — cut instead.
|
|
||||||
SHORT_SHA="$(printf '%s' "$GITHUB_SHA" | cut -c1-8)"
|
|
||||||
git commit -q -m "docs: publish site from ${SHORT_SHA}"
|
|
||||||
echo "🚀 Force-pushing to gitea-pages"
|
|
||||||
git push -f "$REMOTE" gitea-pages
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
name: Traceability Validation
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- main
|
|
||||||
- develop
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- main
|
|
||||||
- develop
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
validate-traces:
|
|
||||||
runs-on: linux/amd64
|
|
||||||
name: Check Requirement Traces
|
|
||||||
container:
|
|
||||||
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
# bun is baked into jellytau-builder (see Dockerfile.builder); no setup-bun
|
|
||||||
# action needed — fetching it stalls on this Gitea runner.
|
|
||||||
- name: Install dependencies
|
|
||||||
run: bun install
|
|
||||||
|
|
||||||
- name: Extract traces
|
|
||||||
run: |
|
|
||||||
echo "🔍 Extracting requirement traces..."
|
|
||||||
bun run traces:json > traces-report.json
|
|
||||||
|
|
||||||
- name: Validate traces
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "📊 Validating requirement traceability..."
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Denominators come from docs/requirements.md at run time — NEVER
|
|
||||||
# hardcode them here. This step previously divided by frozen literals
|
|
||||||
# (UR/39, IR/24, DR/48, JA/3, total 114) while the file had grown to
|
|
||||||
# 211 requirements, so it reported 158% coverage and the threshold
|
|
||||||
# below could never trip. See docs/traceability-ci.md.
|
|
||||||
TOTAL_TRACES=$(jq '.totalTraces' traces-report.json)
|
|
||||||
COVERED=$(jq '.coverage.covered' traces-report.json)
|
|
||||||
TOTAL_REQS=$(jq '.coverage.total' traces-report.json)
|
|
||||||
COVERAGE=$(jq '.coverage.percent' traces-report.json)
|
|
||||||
|
|
||||||
echo "✅ TRACES Found: $TOTAL_TRACES"
|
|
||||||
echo ""
|
|
||||||
echo "📋 Coverage Summary (traced / defined):"
|
|
||||||
for T in UR IR DR JA; do
|
|
||||||
TRACED=$(jq --arg t "$T" '[.byType[$t][] | select(. != null)] | length' traces-report.json)
|
|
||||||
DEFINED=$(jq --arg t "$T" '.defined[$t]' traces-report.json)
|
|
||||||
echo " $T: $TRACED / $DEFINED"
|
|
||||||
done
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
echo "📈 Overall Coverage: $COVERED / $TOTAL_REQS ($COVERAGE%)"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Traced IDs that requirements.md does not define (typo, or a deleted
|
|
||||||
# requirement). These do not count toward coverage.
|
|
||||||
ORPHANED=$(jq -c '.coverage.orphaned' traces-report.json)
|
|
||||||
if [ "$ORPHANED" != "[]" ]; then
|
|
||||||
echo "⚠️ Traced but not defined in requirements.md: $ORPHANED"
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# A ratio above 100% means the computation is broken — the exact
|
|
||||||
# condition that hid the stale-denominator bug. Fail loudly.
|
|
||||||
if [ "$COVERAGE" -gt 100 ]; then
|
|
||||||
echo "❌ ERROR: Coverage ($COVERAGE%) exceeds 100% — the gate is miscomputing."
|
|
||||||
echo " Orphaned IDs: $ORPHANED"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Minimum coverage. RATCHET POLICY: this number only ever goes UP.
|
|
||||||
#
|
|
||||||
# It sits a few points under the coverage actually achieved, so a real
|
|
||||||
# regression trips it. It was 50 while true coverage was 86%, which
|
|
||||||
# meant nearly half the matrix could rot before CI said a word — a
|
|
||||||
# gate that cannot fail is not a gate.
|
|
||||||
#
|
|
||||||
# When coverage rises durably, raise this to just under the new figure
|
|
||||||
# (`bun run traces:coverage` prints it). Never lower it to make a red
|
|
||||||
# build pass — add the missing TRACES comments instead.
|
|
||||||
#
|
|
||||||
# Keep in sync with MIN_COVERAGE_PERCENT in scripts/extract-traces.ts;
|
|
||||||
# scripts/extract-traces.test.ts fails if the two drift apart.
|
|
||||||
MIN_THRESHOLD=89
|
|
||||||
if [ "$COVERAGE" -lt "$MIN_THRESHOLD" ]; then
|
|
||||||
echo "❌ ERROR: Coverage ($COVERAGE%) is below minimum threshold ($MIN_THRESHOLD%)"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "✅ Coverage is acceptable ($COVERAGE% >= $MIN_THRESHOLD%)"
|
|
||||||
|
|
||||||
# Every ID named by a TRACES comment must be defined as a table row in
|
|
||||||
# docs/requirements.md. The extractor used to accept any well-formed ID
|
|
||||||
# silently, so a typo or a rename that missed a call site passed CI
|
|
||||||
# unnoticed (DR-189 and UT-188 lived in three source files, defined
|
|
||||||
# nowhere, for months). This covers UT/IT too, which the coverage
|
|
||||||
# orphan list above deliberately ignores.
|
|
||||||
- name: Validate requirement IDs
|
|
||||||
run: bun run traces:validate
|
|
||||||
|
|
||||||
- name: Check modified files
|
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
run: |
|
|
||||||
echo "🔍 Checking modified files for traces..."
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Get changed files
|
|
||||||
CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD | grep -E '\.(ts|tsx|svelte|rs)$' || echo "")
|
|
||||||
|
|
||||||
if [ -z "$CHANGED" ]; then
|
|
||||||
echo "✅ No TypeScript/Rust files changed"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "📝 Changed files:"
|
|
||||||
echo "$CHANGED" | sed 's/^/ /'
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Check each file
|
|
||||||
# Pipe into the loop instead of a here-string (<<<) so this step works
|
|
||||||
# under POSIX sh/dash, not just bash. Use `case` instead of `[[ == ]]`
|
|
||||||
# for the same reason. The loop runs in a subshell (so a counter var
|
|
||||||
# wouldn't survive), so we record warnings in a temp file and count it
|
|
||||||
# afterwards.
|
|
||||||
MISSING_FILE=$(mktemp)
|
|
||||||
echo "$CHANGED" | while IFS= read -r file; do
|
|
||||||
# Skip test files
|
|
||||||
case "$file" in
|
|
||||||
*.test.*) continue ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -f "$file" ]; then
|
|
||||||
if ! grep -q "TRACES:" "$file"; then
|
|
||||||
echo "⚠️ Missing TRACES: $file"
|
|
||||||
echo "$file" >> "$MISSING_FILE"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
MISSING_TRACES=$(wc -l < "$MISSING_FILE" | tr -d ' ')
|
|
||||||
rm -f "$MISSING_FILE"
|
|
||||||
|
|
||||||
if [ "$MISSING_TRACES" -gt 0 ]; then
|
|
||||||
echo ""
|
|
||||||
echo "📝 Recommendation: Add TRACES comments to new/modified code"
|
|
||||||
echo " Format: // TRACES: UR-001, UR-002 | DR-003"
|
|
||||||
echo ""
|
|
||||||
echo "💡 For more info, see: scripts/README.md"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Generate full report
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
echo "📄 Generating full traceability report..."
|
|
||||||
bun run traces:markdown
|
|
||||||
|
|
||||||
- name: Display report summary
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
echo ""
|
|
||||||
echo "📊 Full Report Generated"
|
|
||||||
echo "📁 Location: docs/traceability.md"
|
|
||||||
echo ""
|
|
||||||
head -50 docs/traceability.md || true
|
|
||||||
|
|
||||||
- name: Save artifacts
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: traceability-reports
|
|
||||||
path: |
|
|
||||||
traces-report.json
|
|
||||||
docs/traceability.md
|
|
||||||
retention-days: 30
|
|
||||||
-67
@@ -1,67 +0,0 @@
|
|||||||
# OS files
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
|
|
||||||
# Node.js
|
|
||||||
node_modules
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
pnpm-debug.log*
|
|
||||||
|
|
||||||
# Use bun (see packageManager in package.json); ignore other package managers' lockfiles
|
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
|
||||||
pnpm-lock.yaml
|
|
||||||
|
|
||||||
# Build output
|
|
||||||
/build
|
|
||||||
/dist
|
|
||||||
/.svelte-kit
|
|
||||||
/package
|
|
||||||
|
|
||||||
# Environment variables
|
|
||||||
.env
|
|
||||||
.env.*
|
|
||||||
!.env.example
|
|
||||||
|
|
||||||
# Testing
|
|
||||||
coverage
|
|
||||||
.nyc_output
|
|
||||||
*.lcov
|
|
||||||
|
|
||||||
# Vitest
|
|
||||||
.vitest
|
|
||||||
|
|
||||||
# Vite
|
|
||||||
vite.config.js.timestamp-*
|
|
||||||
vite.config.ts.timestamp-*
|
|
||||||
|
|
||||||
# IDE
|
|
||||||
.idea
|
|
||||||
.vscode
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
*~
|
|
||||||
|
|
||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
|
|
||||||
# Android signing keystore (NEVER commit)
|
|
||||||
android-keystore/
|
|
||||||
|
|
||||||
# Local machine-specific Android NDK toolchain paths (do not commit)
|
|
||||||
src-tauri/.cargo/config.toml
|
|
||||||
|
|
||||||
# Docs site build artifacts (generated by the publish-docs CI job into docs/)
|
|
||||||
/docs/SUMMARY.md
|
|
||||||
/docs/README.md
|
|
||||||
/docs/api-redirect.md
|
|
||||||
/docs-site/book/
|
|
||||||
|
|
||||||
# Arch packaging build artifacts (vendored cargo cache, makepkg workdir, output package)
|
|
||||||
/.cargo-arch/
|
|
||||||
/packaging/arch/pkg/
|
|
||||||
/packaging/arch/src/
|
|
||||||
/packaging/arch/*.pkg.tar.zst
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
This file makes sure that Github Pages doesn't process mdBook's output.
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# Dependencies & build output
|
|
||||||
node_modules/
|
|
||||||
.svelte-kit/
|
|
||||||
|
|
||||||
# Scratch worktrees (git-ignored) — full checkouts of this repo
|
|
||||||
.claude/
|
|
||||||
build/
|
|
||||||
dist/
|
|
||||||
coverage/
|
|
||||||
/package/
|
|
||||||
|
|
||||||
# Rust backend (rustfmt owns this tree)
|
|
||||||
src-tauri/
|
|
||||||
|
|
||||||
# Generated by tauri-specta — regenerated on every Rust build, never hand-edited
|
|
||||||
src/lib/api/bindings.ts
|
|
||||||
|
|
||||||
# Lockfiles and generated data
|
|
||||||
bun.lock
|
|
||||||
*.lcov
|
|
||||||
|
|
||||||
# Generated docs (built by the publish-docs CI job)
|
|
||||||
docs/SUMMARY.md
|
|
||||||
docs/README.md
|
|
||||||
docs/api-redirect.md
|
|
||||||
docs-site/book/
|
|
||||||
|
|
||||||
# Hand-maintained Markdown (docs/, CHANGELOG.md, README.md, ...). Prettier
|
|
||||||
# reflows tables and wrapped prose, which would swamp real doc diffs and fight
|
|
||||||
# the hand-tuned layout of docs/requirements.md and docs/traceability.md
|
|
||||||
# (the latter is generated by scripts/extract-traces.ts).
|
|
||||||
**/*.md
|
|
||||||
|
|
||||||
# CI workflow YAML — formatting churn here would obscure real pipeline diffs.
|
|
||||||
.gitea/
|
|
||||||
-20
@@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/prettierrc",
|
|
||||||
"printWidth": 100,
|
|
||||||
"tabWidth": 2,
|
|
||||||
"useTabs": false,
|
|
||||||
"semi": true,
|
|
||||||
"singleQuote": false,
|
|
||||||
"quoteProps": "as-needed",
|
|
||||||
"trailingComma": "all",
|
|
||||||
"bracketSpacing": true,
|
|
||||||
"arrowParens": "always",
|
|
||||||
"endOfLine": "lf",
|
|
||||||
"plugins": ["prettier-plugin-svelte"],
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": "*.svelte",
|
|
||||||
"options": { "parser": "svelte" }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
-1432
File diff suppressed because it is too large
Load Diff
@@ -1,355 +0,0 @@
|
|||||||
# JellyTau
|
|
||||||
|
|
||||||
A cross-platform Jellyfin client. Business logic lives in a Rust backend
|
|
||||||
(`src-tauri/`); a SvelteKit + TypeScript frontend (`src/`) handles presentation
|
|
||||||
and talks to it over Tauri v2 IPC. Targets **Linux** (libmpv, WebKitGTK HTML5
|
|
||||||
`<video>` for transcoded playback) and **Android** (ExoPlayer).
|
|
||||||
|
|
||||||
Package manager is **bun**.
|
|
||||||
|
|
||||||
## Build / Run / Test
|
|
||||||
|
|
||||||
All routine tasks go through `package.json` scripts and helper scripts in
|
|
||||||
`scripts/`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun install # install deps
|
|
||||||
bun run dev # vite dev server (frontend)
|
|
||||||
bun run tauri dev # run the desktop app
|
|
||||||
|
|
||||||
bun run check # svelte-check (types)
|
|
||||||
bun run test # vitest (frontend unit/integration)
|
|
||||||
bun run test:rust # cargo test (scripts/test-rust.sh)
|
|
||||||
bun run test:all # full suite (scripts/test-all.sh)
|
|
||||||
bun run lint # eslint (src/, scripts/, root configs)
|
|
||||||
bun run format:check # prettier
|
|
||||||
|
|
||||||
# Android — canonical entry points (see scripts/):
|
|
||||||
bun run android:build # debug APK
|
|
||||||
bun run android:build:release # release APK
|
|
||||||
bun run android:deploy # install to connected device
|
|
||||||
bun run android:dev # build + deploy
|
|
||||||
bun run android:logs # logcat
|
|
||||||
```
|
|
||||||
|
|
||||||
The **debug** build type carries `applicationIdSuffix ".debug"`, so
|
|
||||||
`com.dtourolle.jellytau.debug` ("JellyTau Debug") installs *alongside* a release
|
|
||||||
build with its own data dir — never uninstall the release app to test a debug
|
|
||||||
one. `./scripts/build-and-deploy.sh release --device --debug` puts an
|
|
||||||
R8-minified *release* build in that same slot, signed with the local debug
|
|
||||||
keystore, for validating minification without the real key. Only the
|
|
||||||
applicationId is suffixed; Kotlin classes stay in the `namespace` package
|
|
||||||
`com.dtourolle.jellytau`, so JNI lookups and R8 keep rules are unaffected. See
|
|
||||||
[README_ANDROID_BUILD.md](src-tauri/android/README_ANDROID_BUILD.md).
|
|
||||||
|
|
||||||
CI runs on **Gitea Actions** (`.gitea/workflows/`), not GitHub. Use the `gh` CLI
|
|
||||||
only against the mirror if one exists; the canonical remote is
|
|
||||||
`gitea.tourolle.paris`.
|
|
||||||
|
|
||||||
> **🔴 CI installs no system tools.** Never add an `apt-get`, `rustup`,
|
|
||||||
> `sdkmanager`, mingw/nsis, or any other *toolchain/system-package* install to a
|
|
||||||
> CI workflow step. Every build, test, and packaging **tool** must already live
|
|
||||||
> in the Docker image the job runs in — the unified builder (`Dockerfile.builder`
|
|
||||||
> → `gitea.tourolle.paris/dtourolle/jellytau-builder`) for Android/Linux/Windows,
|
|
||||||
> or `Dockerfile.arch` for Arch. If a job needs a tool the image lacks, **add it
|
|
||||||
> to the image, rebuild + push it** (`scripts/build-builder-image.sh`), and use
|
|
||||||
> it from CI — do not install it at job time. This keeps builds reproducible and
|
|
||||||
> fast, and is why the packaging stages are thin `FROM ${BUILDER_IMAGE}` layers.
|
|
||||||
>
|
|
||||||
> `bun install` (fetching the project's own JS deps per the lockfile) is **not**
|
|
||||||
> a violation — that's project dependencies, not a toolchain. The rule is about
|
|
||||||
> system tools, not npm/bun/cargo *packages* declared by the project.
|
|
||||||
|
|
||||||
## Before Committing
|
|
||||||
|
|
||||||
- Frontend: `bun run check`, `bun run test`, `bun run format:check` and
|
|
||||||
`bun run lint` (0 errors; the warning count is a CI ratchet) must pass.
|
|
||||||
- Rust: `cd src-tauri && cargo fmt` then `cargo clippy`, plus `bun run test:rust`.
|
|
||||||
- **Boundary**: `bun run check:boundary` must pass — no domain taxonomy (Jellyfin
|
|
||||||
item-type category sets) leaked into the frontend. See below.
|
|
||||||
- **Traceability**: new requirement-implementing code must carry a `// TRACES:`
|
|
||||||
comment (see below).
|
|
||||||
- **Android source edits**: edit `src-tauri/android/src` (the canonical tree),
|
|
||||||
then run `scripts/sync-android-sources.sh` to sync into the `gen/` tree.
|
|
||||||
Never edit the generated `gen/` sources directly.
|
|
||||||
|
|
||||||
## Traceability (TRACES)
|
|
||||||
|
|
||||||
This project practices requirement-driven development: code that implements a
|
|
||||||
requirement is tagged with a `TRACES:` comment linking it to requirement IDs, and
|
|
||||||
an extraction tool builds the traceability matrix. **When you add or change code
|
|
||||||
that implements a requirement, add/update its TRACES comment.** Internal helpers
|
|
||||||
and requirement-less code stay untraced.
|
|
||||||
|
|
||||||
Format — `// TRACES: <URs> | <DRs> | <tests>`, e.g.:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
/// TRACES: UR-005 | DR-001
|
|
||||||
pub enum PlayerState { … }
|
|
||||||
```
|
|
||||||
```typescript
|
|
||||||
// TRACES: UR-005, UR-026 | DR-029
|
|
||||||
export function autoplayNextEpisode() { }
|
|
||||||
```
|
|
||||||
|
|
||||||
ID types: **UR** user requirement, **IR** integration, **DR** development, **JA**
|
|
||||||
Jellyfin API, **UT** unit test, **IT** integration test. Requirements are defined
|
|
||||||
in [docs/requirements.md](docs/requirements.md); the generated matrix is
|
|
||||||
[docs/traceability.md](docs/traceability.md).
|
|
||||||
|
|
||||||
Tooling:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun run traces # extract traces (default format)
|
|
||||||
bun run traces:json # JSON — e.g. | jq '.byType' or '.requirements."UR-005"'
|
|
||||||
bun run traces:markdown # regenerate docs/traceability.md
|
|
||||||
bun run traces:coverage # coverage gate — exits non-zero below the threshold
|
|
||||||
bun run traces:validate # dangling-ID gate — every traced ID must be defined
|
|
||||||
git diff --name-only | xargs grep -L "TRACES:" # find untraced changed files
|
|
||||||
```
|
|
||||||
|
|
||||||
Every ID a `TRACES:` comment names must exist as a table row in
|
|
||||||
`docs/requirements.md` — `traces:validate` fails otherwise, so a typo or a
|
|
||||||
rename that missed a call site can no longer pass silently.
|
|
||||||
|
|
||||||
**CI is Gitea Actions** (`.gitea/workflows/`, remote `gitea.tourolle.paris`), not
|
|
||||||
GitHub. `traceability-check.yml` fails the build if coverage drops below
|
|
||||||
**89%** (`MIN_THRESHOLD`, a *ratchet* — raise it as coverage climbs, never lower
|
|
||||||
it to make a build pass) or if any traced ID is undefined; `build-and-test.yml`
|
|
||||||
runs frontend tests **with coverage thresholds**, `bun run check`, `format:check`,
|
|
||||||
a `--max-warnings` eslint ratchet, Rust tests, `cargo fmt --check`, `cargo clippy
|
|
||||||
-D warnings`, and an Android `cargo check`. See
|
|
||||||
[docs/traceability-ci.md](docs/traceability-ci.md)
|
|
||||||
and [docs/traces-quick-ref.md](docs/traces-quick-ref.md).
|
|
||||||
|
|
||||||
### Traces drive release notes
|
|
||||||
|
|
||||||
Prefer traceability over raw commit subjects when writing release notes for
|
|
||||||
[docs/release-checklist.md](docs/release-checklist.md). Raw `git log` subjects are
|
|
||||||
noisy; the TRACES graph gives a semantic summary of *what capabilities* the
|
|
||||||
release touched.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun run release:notes # <latest tag>..HEAD
|
|
||||||
bun run release:notes v0.0.15..HEAD # explicit range
|
|
||||||
```
|
|
||||||
|
|
||||||
[scripts/release-notes.ts](scripts/release-notes.ts) resolves a commit range's
|
|
||||||
changed files → their `TRACES:` IDs → descriptions in
|
|
||||||
[docs/requirements.md](docs/requirements.md), then groups **UR** into *Features*
|
|
||||||
and **DR/IR** into *Improvements* (deduped, so many commits touching one
|
|
||||||
requirement collapse to one line). It also lists changed files that carry no
|
|
||||||
TRACES so nothing is silently dropped — those still need a manual line. Treat the
|
|
||||||
output as a reviewed draft, not a final changelog.
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
- **Rust backend** (`src-tauri/src/`) — all business logic: auth, catalog,
|
|
||||||
sessions, downloads, offline cache, playback control. Commands grouped by
|
|
||||||
domain in `src-tauri/src/commands/` (`auth.rs`, `catalog.rs`, `player/`,
|
|
||||||
`download/`, `offline.rs`, `sessions.rs`, …).
|
|
||||||
- **Svelte frontend** (`src/`) — presentation only. Stores in
|
|
||||||
`src/lib/stores/`, API wrappers in `src/lib/api/`, components in
|
|
||||||
`src/lib/components/`.
|
|
||||||
- **Playback layers** — Linux uses libmpv for direct playback and a WebKitGTK
|
|
||||||
HTML5 `<video>` element for HLS-transcoded (h264) streams; Android uses
|
|
||||||
ExoPlayer with a foreground media service + `MediaSessionCompat`.
|
|
||||||
- **tauri-specta** generates TypeScript bindings and typed events from the Rust
|
|
||||||
command/event definitions (registered via the Builder in `src-tauri/src/lib.rs`).
|
|
||||||
|
|
||||||
**Read the architecture docs before making structural changes** — they are the
|
|
||||||
canonical, maintained source; this file only summarizes. See
|
|
||||||
[docs/architecture/README.md](docs/architecture/README.md) and:
|
|
||||||
|
|
||||||
| Doc | Contents |
|
|
||||||
|-----|----------|
|
|
||||||
| [01-rust-backend.md](docs/architecture/01-rust-backend.md) | Player/session state machines, playback mode, queue, commands |
|
|
||||||
| [02-svelte-frontend.md](docs/architecture/02-svelte-frontend.md) | Stores, repository architecture, MiniPlayer, autoplay, nav guard |
|
|
||||||
| [03-data-flow.md](docs/architecture/03-data-flow.md) | Cache-first query flow, playback initiation, mode transfer |
|
|
||||||
| [04-type-sync-and-threading.md](docs/architecture/04-type-sync-and-threading.md) | **Rust↔TS type sync, the IPC camelCase convention + param table, locking** |
|
|
||||||
| [05-platform-backends.md](docs/architecture/05-platform-backends.md) | MpvBackend (Linux), ExoPlayerBackend (Android), MediaSession, HTML5 adapter |
|
|
||||||
| [06-downloads-and-offline.md](docs/architecture/06-downloads-and-offline.md) | Download manager/worker, smart cache, offline commands |
|
|
||||||
| [07-connectivity.md](docs/architecture/07-connectivity.md) | HTTP retry, ConnectivityMonitor, reachability model |
|
|
||||||
| [08-database-design.md](docs/architecture/08-database-design.md) | Tables, relationships, key queries |
|
|
||||||
| [09-security.md](docs/architecture/09-security.md) | Token storage, secure storage, network security |
|
|
||||||
|
|
||||||
Release process lives in [docs/release-checklist.md](docs/release-checklist.md)
|
|
||||||
and [docs/build/build-release.md](docs/build/build-release.md).
|
|
||||||
|
|
||||||
### Core principles (from the architecture docs)
|
|
||||||
|
|
||||||
- **Playback state is one-directional.** The player (ExoPlayer on Android, MPV on
|
|
||||||
Linux, session poller in remote mode) is the **authoritative source** of state
|
|
||||||
— position, pause, seeking, rate, track changes. The Svelte UI, OS
|
|
||||||
`MediaSession`/lockscreen, and MPRIS are **consumers**; they reflect what the
|
|
||||||
player reports and never determine it.
|
|
||||||
- **Unified player boundary.** UI controls playback *only* through the frontend
|
|
||||||
facade `src/lib/player/index.ts` (`playerController`) — never by calling
|
|
||||||
`commands.player*` directly. Webview HTML5 `<video>` reports its state back
|
|
||||||
into Rust via `src/lib/player/html5Adapter.ts` and the `player_report_*`
|
|
||||||
commands, so the controller stays the single source of truth in both native
|
|
||||||
and HTML5 modes.
|
|
||||||
- **Reachability from real traffic.** Server online/offline is derived from the
|
|
||||||
outcome of actual repository requests (reported to `ConnectivityMonitor`), not
|
|
||||||
a side-channel poller. The `/System/Info/Public` probe runs *only while
|
|
||||||
offline*, as a recovery detector.
|
|
||||||
- **Poison-tolerant locking.** Access shared `std::sync` state via the
|
|
||||||
`MutexSafe`/`RwLockSafe` helpers in `utils/lock.rs`, which recover a poisoned
|
|
||||||
lock instead of cascading a panic across the player.
|
|
||||||
- **Graceful backend init.** If a native player backend fails to initialize, the
|
|
||||||
app falls back to a no-op backend and emits `backend-init-failed` rather than
|
|
||||||
crashing.
|
|
||||||
- **Domain vocabulary lives in Rust.** The frontend is presentation-only and must
|
|
||||||
not encode Jellyfin's *taxonomy* — e.g. the set of item types that defines a
|
|
||||||
category like "Music". Send an opaque scope/enum across the boundary and let the
|
|
||||||
backend expand it. Single-type presentation (`itemType: "Movie"`, "this page
|
|
||||||
shows albums") is fine; a *category → set of types* mapping in `src/` is a leak.
|
|
||||||
`bun run check:boundary` is the tripwire; the real gate is the spec's layer
|
|
||||||
assignment. The canonical example lives in Rust:
|
|
||||||
`SearchScope::item_types()` in `repository/types.rs` expands an opaque scope the
|
|
||||||
frontend sends. See [scoped-search-boundary.md](docs/specs/scoped-search-boundary.md)
|
|
||||||
for the incident this rule came from — note the tripwire missed that leak for
|
|
||||||
months because the mapping was assigned to a named const rather than written
|
|
||||||
inline at the query, so **a green `check:boundary` is not proof**; it flags
|
|
||||||
item-type array literals only, not run-time-built sets or `switch`/`||`
|
|
||||||
taxonomy.
|
|
||||||
|
|
||||||
## Writing specs
|
|
||||||
|
|
||||||
New feature specs go in [docs/specs/](docs/specs/) — see its
|
|
||||||
[README](docs/specs/README.md) for the index and what is already built.
|
|
||||||
**Start from
|
|
||||||
[SPEC-TEMPLATE.md](docs/specs/SPEC-TEMPLATE.md)** — its "Layer assignment" section
|
|
||||||
forces each piece of *logic* to be placed in the correct layer (Rust = domain,
|
|
||||||
frontend = presentation) *with a reason*, which is what prevents boundary leaks.
|
|
||||||
Before accepting a spec, run it past
|
|
||||||
[SPEC-REVIEW-CHECKLIST.md](docs/specs/SPEC-REVIEW-CHECKLIST.md). Do **not** frame
|
|
||||||
a spec around "no Rust changes required" — correct layer placement is the goal,
|
|
||||||
not minimal backend churn.
|
|
||||||
|
|
||||||
### 🔴 A spec becomes an architecture doc when it ships
|
|
||||||
|
|
||||||
`docs/specs/` holds **only work that has not shipped**. There is no "Implemented"
|
|
||||||
resting state for a spec file: when the last acceptance criterion is met, fold
|
|
||||||
the design into [docs/architecture/](docs/architecture/README.md) and **delete
|
|
||||||
the spec in the same commit**.
|
|
||||||
|
|
||||||
This is not tidying. A directory that mixes promises with descriptions makes both
|
|
||||||
unreliable — you cannot tell from a file whether it describes the build or
|
|
||||||
proposes a change to it, and stale specs then quietly disagree with the code
|
|
||||||
while reading as authority.
|
|
||||||
|
|
||||||
- **Every spec names its destination up front** — the template's "Destination on
|
|
||||||
completion" line. Deciding at spec time which architecture doc will absorb it
|
|
||||||
is a design check in itself: a feature that fits no existing doc is usually a
|
|
||||||
feature whose layer assignment is unclear.
|
|
||||||
- **Carry the reasoning, not the plan.** The architecture doc gets the *why* a
|
|
||||||
future change still needs — invariants, rejected alternatives that would be
|
|
||||||
re-attempted, the defect a piece of code exists to prevent. Acceptance
|
|
||||||
criteria, phase breakdowns and migration steps die with the spec; git history
|
|
||||||
keeps them.
|
|
||||||
- **Deferred work outlives its spec.** Anything the spec listed as out-of-scope
|
|
||||||
and still worth doing goes beside the code it concerns, not into the void.
|
|
||||||
- **Rewrite inbound references before deleting** — source comments and CI
|
|
||||||
scripts cite spec paths, and `check-doc-links` only sees markdown.
|
|
||||||
- **Partially implemented is a real status.** A spec stays until *all* of it
|
|
||||||
ships, with the header naming what is left.
|
|
||||||
|
|
||||||
## Conventions
|
|
||||||
|
|
||||||
### Rust Backend
|
|
||||||
|
|
||||||
- Use `#[tauri::command]` for all IPC handlers.
|
|
||||||
- Prefer `async` commands for I/O-bound work.
|
|
||||||
- Return `Result<T, String>` from commands (the established convention here).
|
|
||||||
- Use `tauri::State<>` for shared state.
|
|
||||||
- Group related commands in domain modules under `commands/`.
|
|
||||||
- Use official Tauri plugins before writing custom native code.
|
|
||||||
|
|
||||||
### Frontend
|
|
||||||
|
|
||||||
- Use `invoke<T>()` from `@tauri-apps/api/core`, or the tauri-specta bindings.
|
|
||||||
- Define TS types matching the Rust structs; prefer the generated bindings.
|
|
||||||
- Handle IPC errors with try/catch.
|
|
||||||
- Use `@tauri-apps/api/path` for paths (never hardcode).
|
|
||||||
- Use `@tauri-apps/api/event` for backend→frontend events.
|
|
||||||
|
|
||||||
### 🔴 IPC parameter naming (Tauri v2)
|
|
||||||
|
|
||||||
The command **name** must match the Rust function name exactly
|
|
||||||
(`invoke("player_play_queue", …)`). But **parameter names do NOT** — Tauri v2's
|
|
||||||
`#[tauri::command]` macro auto-converts snake_case Rust params to **camelCase**
|
|
||||||
on the frontend:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn cmd(repository_handle: String) { … }
|
|
||||||
```
|
|
||||||
```typescript
|
|
||||||
await invoke("cmd", { repositoryHandle: "…" }); // camelCase, auto-converted
|
|
||||||
```
|
|
||||||
|
|
||||||
Nested struct fields need `#[serde(rename_all = "camelCase")]`; tagged unions use
|
|
||||||
`#[serde(tag = "type")]` and both sides must match the tag. Note: tauri-specta
|
|
||||||
tagged responses keep the Rust field names as-is (e.g. `new_url`, not `newUrl`).
|
|
||||||
|
|
||||||
### Events
|
|
||||||
|
|
||||||
- Backend events use **kebab-case** names (`download-event`, `search-event`).
|
|
||||||
- Emit from Rust via `emit(...)`; consume on the frontend via
|
|
||||||
`@tauri-apps/api/event` or the tauri-specta typed event bindings.
|
|
||||||
|
|
||||||
### Security
|
|
||||||
|
|
||||||
- Declare minimum permissions in `src-tauri/capabilities/`.
|
|
||||||
- Keep the CSP restrictive in `tauri.conf.json`.
|
|
||||||
- Validate all inputs in Rust command handlers.
|
|
||||||
- **Never read credentials** (tokens/keys from keyring, env, or stores) without
|
|
||||||
asking the user first.
|
|
||||||
|
|
||||||
## Gotchas (hard-won)
|
|
||||||
|
|
||||||
- **Never call sync/blocking APIs from event callbacks** that can re-enter the
|
|
||||||
player or hold a lock — it deadlocks. On Android, bind a locked
|
|
||||||
`AutoplayDecision` to a `let` *before* matching; a tokio `MutexGuard` held in
|
|
||||||
the `match` scrutinee deadlocks the `AdvanceToNext` arm.
|
|
||||||
- **VideoPlayer native mode**: no lifecycle calls after an `await` in `onMount`
|
|
||||||
(it flips to HTML5 mode and breaks Android seek).
|
|
||||||
- **Transcoded resume/seek**: `get_video_stream_url` must return the HLS
|
|
||||||
`master.m3u8`, not `stream.mp4`, or transcoded playback never starts.
|
|
||||||
- **Downloads** cap at 3 concurrent; the backend pump auto-starts pending rows.
|
|
||||||
Don't loop `startDownload` from the frontend.
|
|
||||||
- **Parallel Claude sessions**: the user may run concurrent sessions. Unexpected
|
|
||||||
file changes may be another session — check `git diff` before "repairing".
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
### 🔴 Bug fixes: failing test FIRST, then the fix
|
|
||||||
|
|
||||||
When fixing a bug, **write a test that reproduces it and watch it fail before
|
|
||||||
touching the fix.** Red → green, in that order:
|
|
||||||
|
|
||||||
1. Write a test that exercises the broken behavior and **run it — it must fail**,
|
|
||||||
proving the test actually catches the bug (a test that passes before the fix
|
|
||||||
proves nothing).
|
|
||||||
2. Apply the fix.
|
|
||||||
3. Re-run — the test now passes, and so does the rest of the suite.
|
|
||||||
|
|
||||||
Never fix first and backfill the test afterward: a test written against
|
|
||||||
already-fixed code can pass for the wrong reason and silently fails to guard the
|
|
||||||
regression. If the logic is buried in a component, extract the pure part into a
|
|
||||||
plain `.ts` module (e.g. `episodeStrip.ts`) so it can be unit-tested — the same
|
|
||||||
pattern as `TrackList.logic.test.ts`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Rust
|
|
||||||
cd src-tauri && cargo test
|
|
||||||
cd src-tauri && cargo test test_name # single test
|
|
||||||
|
|
||||||
# Frontend
|
|
||||||
bun run test
|
|
||||||
bun run test:coverage
|
|
||||||
|
|
||||||
# Tauri IPC param-naming integration tests (guard the camelCase rule):
|
|
||||||
bun run test -- tauriIntegration.test.ts
|
|
||||||
```
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
# Code of Conduct
|
|
||||||
|
|
||||||
## The short version
|
|
||||||
|
|
||||||
Be decent to people. Assume the person you are talking to is acting in good
|
|
||||||
faith and knows things you do not.
|
|
||||||
|
|
||||||
## What that means here
|
|
||||||
|
|
||||||
**Expected:**
|
|
||||||
|
|
||||||
- Criticise code, decisions and ideas — not the people who wrote them.
|
|
||||||
- Accept that "no" is a complete answer. This is a small project with a
|
|
||||||
maintainer who has finite time; a declined feature request is not a slight.
|
|
||||||
- Give people room to be new. Everyone was once confused by Tauri's IPC.
|
|
||||||
- Assume a bug report is someone trying to help, even when it arrives terse or
|
|
||||||
frustrated.
|
|
||||||
|
|
||||||
**Not accepted:**
|
|
||||||
|
|
||||||
- Harassment, personal attacks, or demeaning remarks — including about someone's
|
|
||||||
identity, background, or level of experience.
|
|
||||||
- Sexualised language or imagery, and unwelcome attention of any kind.
|
|
||||||
- Publishing someone's private information without their permission.
|
|
||||||
- Persistently derailing discussions, or badgering people who have already
|
|
||||||
answered you.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This applies in the issue tracker, pull requests, commit messages and any other
|
|
||||||
project space, and to anyone taking part — maintainer included.
|
|
||||||
|
|
||||||
## Reporting
|
|
||||||
|
|
||||||
Email **duncan@tourolle.paris**. Reports are read by the maintainer and handled
|
|
||||||
privately.
|
|
||||||
|
|
||||||
Responses range from a quiet word through to removing comments or blocking an
|
|
||||||
account, depending on what happened. If a report concerns the maintainer, and
|
|
||||||
that makes reporting to them pointless, you are free to say so publicly — a
|
|
||||||
project this size has no separate committee to appeal to, and pretending
|
|
||||||
otherwise would be dishonest.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
Adapted in spirit from the [Contributor Covenant](https://www.contributor-covenant.org),
|
|
||||||
shortened to what a single-maintainer project can actually honour.
|
|
||||||
-123
@@ -1,123 +0,0 @@
|
|||||||
# Contributing to JellyTau
|
|
||||||
|
|
||||||
Thanks for looking. This file is the short version of how the project is built
|
|
||||||
and what has to be true before a change lands. The long version lives in
|
|
||||||
[CLAUDE.md](CLAUDE.md) and [docs/architecture/](docs/architecture/README.md),
|
|
||||||
which are maintained rather than decorative — read them before a structural
|
|
||||||
change.
|
|
||||||
|
|
||||||
## Getting set up
|
|
||||||
|
|
||||||
Package manager is **bun**. You will also need a Rust toolchain (the exact
|
|
||||||
version is pinned in [src-tauri/rust-toolchain.toml](src-tauri/rust-toolchain.toml)
|
|
||||||
— rustup honours it automatically) and the Tauri Linux dependencies.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun install
|
|
||||||
bun run hooks:install # do this once: it enables the pre-commit gates
|
|
||||||
bun run tauri dev
|
|
||||||
```
|
|
||||||
|
|
||||||
`hooks:install` points `core.hooksPath` at [scripts/hooks/](scripts/hooks/), so
|
|
||||||
hook updates arrive with a `git pull` instead of needing a re-install.
|
|
||||||
|
|
||||||
## What has to pass
|
|
||||||
|
|
||||||
Everything below runs in CI, and the fast half runs in the pre-commit hook. None
|
|
||||||
of it is advisory:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun run check # svelte-check — 0 errors
|
|
||||||
bun run test # vitest
|
|
||||||
bun run format:check # prettier
|
|
||||||
bun run lint # eslint — 0 errors; the warning count is a ratchet
|
|
||||||
bun run check:boundary # no Jellyfin taxonomy in the frontend
|
|
||||||
bun run test:rust # cargo test
|
|
||||||
cd src-tauri && cargo fmt --all && cargo clippy --all-targets -- -D warnings
|
|
||||||
cd src-tauri && cargo deny check # advisories, licences, bans, sources
|
|
||||||
```
|
|
||||||
|
|
||||||
`bun run test:all` runs the whole set.
|
|
||||||
|
|
||||||
Several of these are **ratchets** — a number that only ever moves in the
|
|
||||||
improving direction:
|
|
||||||
|
|
||||||
| Ratchet | Where | Rule |
|
|
||||||
|---|---|---|
|
|
||||||
| eslint `--max-warnings` | [.gitea/workflows/build-and-test.yml](.gitea/workflows/build-and-test.yml) | only goes down |
|
|
||||||
| Coverage thresholds | [vitest.config.ts](vitest.config.ts) | only go up |
|
|
||||||
| Traceability coverage | [.gitea/workflows/traceability-check.yml](.gitea/workflows/traceability-check.yml) | only goes up |
|
|
||||||
|
|
||||||
Never relax one to make a build pass. Fix the thing it caught.
|
|
||||||
|
|
||||||
## The two rules that surprise people
|
|
||||||
|
|
||||||
**1. Bug fixes start with a failing test.** Write a test that reproduces the bug
|
|
||||||
and *watch it fail* before you touch the fix. A test written against
|
|
||||||
already-fixed code can pass for the wrong reason and guards nothing. If the logic
|
|
||||||
is trapped in a component, extract the pure part into a plain `.ts` module and
|
|
||||||
test that — see `episodeStrip.ts` or `TrackList.logic.ts` for the pattern.
|
|
||||||
|
|
||||||
**2. Domain vocabulary lives in Rust.** The frontend is presentation-only. It
|
|
||||||
must not encode Jellyfin's *taxonomy* — for example, the set of item types that
|
|
||||||
makes up a category like "Music". Send an opaque scope across the IPC boundary
|
|
||||||
and let the backend expand it. `bun run check:boundary` is a tripwire, not a
|
|
||||||
proof: it only flags item-type array literals, so a green run does not mean you
|
|
||||||
are clear. [docs/specs/scoped-search-boundary.md](docs/specs/scoped-search-boundary.md)
|
|
||||||
describes the leak that made this a rule.
|
|
||||||
|
|
||||||
## Traceability
|
|
||||||
|
|
||||||
Code that implements a requirement carries a `TRACES:` comment naming the
|
|
||||||
requirement IDs, and a tool builds the matrix from those comments:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
/// TRACES: UR-005 | DR-001
|
|
||||||
```
|
|
||||||
|
|
||||||
Every ID must exist as a row in [docs/requirements.md](docs/requirements.md) —
|
|
||||||
`bun run traces:validate` fails on a typo or a stale rename. Internal helpers and
|
|
||||||
requirement-less code stay untraced; do not sprinkle IDs to raise the number.
|
|
||||||
|
|
||||||
If you add a requirement, add its row. If you implement one, tag the code.
|
|
||||||
|
|
||||||
## Commits and pull requests
|
|
||||||
|
|
||||||
- Conventional-commit subjects: `fix(player): …`, `feat(updater): …`, `ci: …`.
|
|
||||||
- Explain **why** in the body, not what the diff already shows. The commit log
|
|
||||||
is the main record of why things are the way they are here, and it is used to
|
|
||||||
draft release notes.
|
|
||||||
- One concern per commit. A formatting sweep and a behaviour change in the same
|
|
||||||
commit is unreviewable.
|
|
||||||
- Rebase rather than merge-commit onto `master`.
|
|
||||||
|
|
||||||
## Specs
|
|
||||||
|
|
||||||
New features start from [docs/specs/SPEC-TEMPLATE.md](docs/specs/SPEC-TEMPLATE.md).
|
|
||||||
Its "Layer assignment" section is the point: each piece of logic gets placed in
|
|
||||||
Rust or the frontend *with a reason*. Review against
|
|
||||||
[docs/specs/SPEC-REVIEW-CHECKLIST.md](docs/specs/SPEC-REVIEW-CHECKLIST.md). Do
|
|
||||||
not frame a spec around "no Rust changes required" — correct placement is the
|
|
||||||
goal, not minimal backend churn.
|
|
||||||
|
|
||||||
## CI
|
|
||||||
|
|
||||||
CI is **Gitea Actions** (`.gitea/workflows/`), not GitHub.
|
|
||||||
|
|
||||||
🔴 **CI installs no system tools.** Every build, test and packaging tool must
|
|
||||||
already be in the Docker builder image. If a job needs a tool the image lacks,
|
|
||||||
add it to [Dockerfile.builder](Dockerfile.builder), rebuild and push the image,
|
|
||||||
and pin the new tag — do not `apt-get` it at job time. Details in
|
|
||||||
[docs/build/ci-operations.md](docs/build/ci-operations.md).
|
|
||||||
|
|
||||||
Fetching the project's own declared dependencies (`bun install`, cargo crates,
|
|
||||||
an advisory database) is not a toolchain install and is fine.
|
|
||||||
|
|
||||||
## Reporting bugs
|
|
||||||
|
|
||||||
Use the issue templates. For anything involving playback, include what the
|
|
||||||
platform was, whether the media was streaming or downloaded, and whether it was
|
|
||||||
transcoding — those three answers determine which of several code paths you were
|
|
||||||
actually on.
|
|
||||||
|
|
||||||
Security issues go to [SECURITY.md](SECURITY.md), not the tracker.
|
|
||||||
-156
@@ -1,156 +0,0 @@
|
|||||||
# Multi-stage build for JellyTau - Tauri Jellyfin client
|
|
||||||
#
|
|
||||||
# The desktop packaging stages (desktop-linux-build, windows-cross) build FROM
|
|
||||||
# the unified registry builder image, which carries every packaging tool. Declared
|
|
||||||
# here (before the first FROM) so it's in scope for those stages' FROM lines.
|
|
||||||
# Override for local iteration: --build-arg BUILDER_IMAGE=jellytau-builder:latest
|
|
||||||
ARG BUILDER_IMAGE=gitea.tourolle.paris/dtourolle/jellytau-builder:latest
|
|
||||||
|
|
||||||
FROM ubuntu:24.04 AS builder
|
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive \
|
|
||||||
ANDROID_HOME=/opt/android-sdk \
|
|
||||||
NDK_VERSION=27.0.11902837 \
|
|
||||||
SDK_VERSION=34 \
|
|
||||||
RUST_BACKTRACE=1 \
|
|
||||||
PATH="/root/.bun/bin:/root/.cargo/bin:$PATH" \
|
|
||||||
CARGO_HOME=/root/.cargo
|
|
||||||
|
|
||||||
# Install system dependencies
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
# Build essentials
|
|
||||||
build-essential \
|
|
||||||
curl \
|
|
||||||
wget \
|
|
||||||
git \
|
|
||||||
ca-certificates \
|
|
||||||
unzip \
|
|
||||||
# JDK for Android
|
|
||||||
openjdk-17-jdk-headless \
|
|
||||||
# Android build tools
|
|
||||||
android-sdk-platform-tools \
|
|
||||||
# Additional development tools
|
|
||||||
pkg-config \
|
|
||||||
libssl-dev \
|
|
||||||
libclang-dev \
|
|
||||||
llvm-dev \
|
|
||||||
# Tauri Linux desktop dependencies (needed for `cargo test` on the host target)
|
|
||||||
libglib2.0-dev \
|
|
||||||
libgtk-3-dev \
|
|
||||||
libwebkit2gtk-4.1-dev \
|
|
||||||
libjavascriptcoregtk-4.1-dev \
|
|
||||||
libsoup-3.0-dev \
|
|
||||||
librsvg2-dev \
|
|
||||||
libayatana-appindicator3-dev \
|
|
||||||
# mpv player library (linked via libmpv-sys)
|
|
||||||
libmpv-dev \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Install Node.js 20.x from NodeSource
|
|
||||||
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
|
|
||||||
apt-get install -y --no-install-recommends nodejs && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Install Bun
|
|
||||||
RUN curl -fsSL https://bun.sh/install | bash && \
|
|
||||||
ln -s /root/.bun/bin/bun /usr/local/bin/bun
|
|
||||||
|
|
||||||
# Install Rust using rustup
|
|
||||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && \
|
|
||||||
. $HOME/.cargo/env && \
|
|
||||||
rustup target add aarch64-linux-android && \
|
|
||||||
rustup target add armv7-linux-androideabi && \
|
|
||||||
rustup target add x86_64-linux-android
|
|
||||||
|
|
||||||
# Setup Android SDK
|
|
||||||
RUN mkdir -p $ANDROID_HOME && \
|
|
||||||
mkdir -p /root/.android && \
|
|
||||||
echo '### User Sources for `android` cmd line tool ###' > /root/.android/repositories.cfg && \
|
|
||||||
echo 'count=0' >> /root/.android/repositories.cfg
|
|
||||||
|
|
||||||
# Download and setup Android Command Line Tools
|
|
||||||
RUN wget -q https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O /tmp/cmdline-tools.zip && \
|
|
||||||
unzip -q /tmp/cmdline-tools.zip -d $ANDROID_HOME && \
|
|
||||||
rm /tmp/cmdline-tools.zip && \
|
|
||||||
mkdir -p $ANDROID_HOME/cmdline-tools/latest && \
|
|
||||||
mv $ANDROID_HOME/cmdline-tools/* $ANDROID_HOME/cmdline-tools/latest/ 2>/dev/null || true
|
|
||||||
|
|
||||||
# Setup Android SDK components
|
|
||||||
RUN $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --sdk_root=$ANDROID_HOME \
|
|
||||||
"platforms;android-$SDK_VERSION" \
|
|
||||||
"build-tools;34.0.0" \
|
|
||||||
"ndk;$NDK_VERSION" \
|
|
||||||
--channel=0 2>&1 | grep -v "Warning" || true
|
|
||||||
|
|
||||||
# Set NDK environment variable
|
|
||||||
ENV NDK_HOME=$ANDROID_HOME/ndk/$NDK_VERSION
|
|
||||||
|
|
||||||
# Create working directory
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy project files
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Install Node.js dependencies
|
|
||||||
RUN bun install
|
|
||||||
|
|
||||||
# Install Rust dependencies
|
|
||||||
RUN cd src-tauri && cargo fetch && cd ..
|
|
||||||
|
|
||||||
# Build stage - Tests
|
|
||||||
FROM builder AS test
|
|
||||||
WORKDIR /app
|
|
||||||
RUN echo "Running tests..." && \
|
|
||||||
bunx svelte-kit sync && \
|
|
||||||
bun run test && \
|
|
||||||
cd src-tauri && cargo test && cd .. && \
|
|
||||||
echo "All tests passed!"
|
|
||||||
|
|
||||||
# Build stage - APK
|
|
||||||
FROM builder AS android-build
|
|
||||||
WORKDIR /app
|
|
||||||
RUN cd src-tauri && cargo fetch && cd .. && \
|
|
||||||
echo "Building Android APK..." && \
|
|
||||||
bun run build && \
|
|
||||||
bun run tauri android build --apk true && \
|
|
||||||
echo "APK build complete!"
|
|
||||||
|
|
||||||
# Desktop packaging stages build FROM the unified registry builder image (see the
|
|
||||||
# BUILDER_IMAGE ARG at the top), which already carries every packaging tool
|
|
||||||
# (rpm/file for Linux, cargo-xwin + nsis + the x86_64-pc-windows-msvc rust
|
|
||||||
# target for Windows). ONE source of dependency truth, shared with CI — no
|
|
||||||
# per-stage apt/rustup here.
|
|
||||||
#
|
|
||||||
# NOTE: Windows uses the MSVC target via cargo-xwin, NOT mingw/GNU — the GNU
|
|
||||||
# toolchain cannot bundle an NSIS installer from Linux. See
|
|
||||||
# scripts/build-windows-cross.sh.
|
|
||||||
|
|
||||||
# Linux desktop packaging environment (deb + rpm; Arch is Dockerfile.arch).
|
|
||||||
# Thin layer over the builder — the actual build runs at container-run time on
|
|
||||||
# the bind-mounted source (see docker-compose.yml / scripts/build-desktop-linux.sh),
|
|
||||||
# matching the `dev` service model. Run standalone with:
|
|
||||||
# docker run --rm -v "$PWD:/app" -v "$PWD/dist:/app/dist" <img> \
|
|
||||||
# bash -c "OUTPUT_DIR=/app/dist scripts/build-desktop-linux.sh"
|
|
||||||
FROM ${BUILDER_IMAGE} AS desktop-linux-build
|
|
||||||
WORKDIR /app
|
|
||||||
CMD ["bash", "-c", "OUTPUT_DIR=/app/dist scripts/build-desktop-linux.sh"]
|
|
||||||
|
|
||||||
# Windows cross-compile environment (MSVC target via cargo-xwin). Video works via
|
|
||||||
# WebView2 and audio via the webview <audio> backend; NSIS installer is produced
|
|
||||||
# from Linux by cargo-xwin. Default bundles NSIS; override WIN_BUNDLES=none for
|
|
||||||
# exe-only. Build runs at container-run time like above.
|
|
||||||
FROM ${BUILDER_IMAGE} AS windows-cross
|
|
||||||
WORKDIR /app
|
|
||||||
CMD ["bash", "-c", "OUTPUT_DIR=/app/dist WIN_BUNDLES=${WIN_BUNDLES:-nsis} scripts/build-windows-cross.sh"]
|
|
||||||
|
|
||||||
# Final output stage
|
|
||||||
FROM ubuntu:24.04 AS final
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
android-sdk-platform-tools \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
COPY --from=android-build /app/src-tauri/gen/android/app/build/outputs/apk /app/apk
|
|
||||||
|
|
||||||
VOLUME ["/app/apk"]
|
|
||||||
CMD ["/bin/bash", "-c", "echo 'APK files are available in /app/apk' && ls -lh /app/apk/"]
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
# JellyTau Arch Linux package builder.
|
|
||||||
#
|
|
||||||
# Tauri has no pacman bundle target, so we build a real .pkg.tar.zst with makepkg
|
|
||||||
# from packaging/arch/PKGBUILD. makepkg refuses to run as root, so we create a
|
|
||||||
# non-root `builder` user with passwordless sudo (for `makepkg -s` pacman calls).
|
|
||||||
#
|
|
||||||
# docker build -f Dockerfile.arch -t jellytau-arch .
|
|
||||||
# docker run --rm -v "$PWD/dist:/out" jellytau-arch
|
|
||||||
FROM archlinux:latest
|
|
||||||
|
|
||||||
RUN pacman -Syu --noconfirm \
|
|
||||||
base-devel git sudo \
|
|
||||||
rust cargo nodejs \
|
|
||||||
webkit2gtk-4.1 mpv gtk3 libayatana-appindicator \
|
|
||||||
libsoup3 pkgconf openssl \
|
|
||||||
&& pacman -Scc --noconfirm
|
|
||||||
|
|
||||||
# Bun is not in the official repos; install the upstream binary.
|
|
||||||
RUN curl -fsSL https://bun.sh/install | bash && \
|
|
||||||
ln -s /root/.bun/bin/bun /usr/local/bin/bun
|
|
||||||
|
|
||||||
# Non-root build user with passwordless sudo for makepkg's dependency step.
|
|
||||||
RUN useradd -m builder && \
|
|
||||||
echo 'builder ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/builder && \
|
|
||||||
ln -sf /root/.bun/bin/bun /usr/local/bin/bun
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
COPY . .
|
|
||||||
RUN chown -R builder:builder /app
|
|
||||||
|
|
||||||
USER builder
|
|
||||||
ENV OUTPUT_DIR=/out
|
|
||||||
RUN mkdir -p /out
|
|
||||||
VOLUME ["/out"]
|
|
||||||
|
|
||||||
# Default: build the package. Output lands in /out (mount it to collect the pkg).
|
|
||||||
CMD ["bash", "-c", "OUTPUT_DIR=/out scripts/build-arch.sh"]
|
|
||||||
@@ -1,180 +0,0 @@
|
|||||||
# JellyTau Builder Image
|
|
||||||
# Pre-built image with all dependencies for building, testing, and packaging:
|
|
||||||
# - Android APK (SDK/NDK), Linux desktop (deb/rpm),
|
|
||||||
# - Windows cross via the official Tauri path: MSVC target + cargo-xwin + NSIS
|
|
||||||
# Arch packages build in a separate archlinux image (Dockerfile.arch) since
|
|
||||||
# makepkg is Arch-specific.
|
|
||||||
# Push to your registry: docker build -f Dockerfile.builder -t gitea.tourolle.paris/dtourolle/jellytau-builder:latest .
|
|
||||||
|
|
||||||
FROM ubuntu:24.04
|
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive \
|
|
||||||
ANDROID_HOME=/opt/android-sdk \
|
|
||||||
NDK_VERSION=27.0.11902837 \
|
|
||||||
SDK_VERSION=36 \
|
|
||||||
BUILD_TOOLS_VERSION=35.0.0 \
|
|
||||||
RUST_BACKTRACE=1 \
|
|
||||||
PATH="/root/.bun/bin:/root/.cargo/bin:$PATH" \
|
|
||||||
CARGO_HOME=/root/.cargo
|
|
||||||
|
|
||||||
# Install system dependencies
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
build-essential \
|
|
||||||
curl \
|
|
||||||
wget \
|
|
||||||
git \
|
|
||||||
ca-certificates \
|
|
||||||
unzip \
|
|
||||||
jq \
|
|
||||||
openjdk-17-jdk-headless \
|
|
||||||
pkg-config \
|
|
||||||
libssl-dev \
|
|
||||||
libclang-dev \
|
|
||||||
llvm-dev \
|
|
||||||
# Tauri Linux desktop dependencies (needed for `cargo test` on the host target)
|
|
||||||
libglib2.0-dev \
|
|
||||||
libgtk-3-dev \
|
|
||||||
libwebkit2gtk-4.1-dev \
|
|
||||||
libjavascriptcoregtk-4.1-dev \
|
|
||||||
libsoup-3.0-dev \
|
|
||||||
librsvg2-dev \
|
|
||||||
libayatana-appindicator3-dev \
|
|
||||||
# mpv player library (linked via libmpv-sys)
|
|
||||||
libmpv-dev \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Install Node.js 20.x from NodeSource
|
|
||||||
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
|
|
||||||
apt-get install -y --no-install-recommends nodejs && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Install Bun
|
|
||||||
RUN curl -fsSL https://bun.sh/install | bash && \
|
|
||||||
ln -s /root/.bun/bin/bun /usr/local/bin/bun
|
|
||||||
|
|
||||||
# Install Rust using rustup, pinned to an exact release.
|
|
||||||
#
|
|
||||||
# 🔴 RUST_VERSION must equal `channel` in src-tauri/rust-toolchain.toml.
|
|
||||||
#
|
|
||||||
# The two are a pair. rust-toolchain.toml is what makes a developer's `cargo
|
|
||||||
# clippy` agree with CI's; this line is what makes the image already contain that
|
|
||||||
# toolchain. If they drift, rustup silently downloads the pinned version the
|
|
||||||
# first time cargo runs inside a job — a toolchain install at job time, which
|
|
||||||
# CLAUDE.md's "🔴 CI installs no system tools" rule forbids (and which costs
|
|
||||||
# ~1min plus a network dependency on every build).
|
|
||||||
#
|
|
||||||
# 🔴 Changing this line does NOT change CI on its own: the image must be
|
|
||||||
# rebuilt and pushed (`scripts/build-builder-image.sh`) before the new pin is
|
|
||||||
# authoritative. Bump rust-toolchain.toml and this line together, rebuild, push,
|
|
||||||
# then merge.
|
|
||||||
#
|
|
||||||
# Was: `sh -s -- -y` (latest stable, whatever it happened to be on rebuild day).
|
|
||||||
ENV RUST_VERSION=1.97.1
|
|
||||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
|
|
||||||
sh -s -- -y --profile minimal --default-toolchain "$RUST_VERSION" && \
|
|
||||||
. $HOME/.cargo/env && \
|
|
||||||
rustup default "$RUST_VERSION" && \
|
|
||||||
rustup target add aarch64-linux-android && \
|
|
||||||
rustup target add armv7-linux-androideabi && \
|
|
||||||
rustup target add x86_64-linux-android && \
|
|
||||||
rustup component add rustfmt clippy && \
|
|
||||||
rustc --version && \
|
|
||||||
cargo clippy --version
|
|
||||||
|
|
||||||
# Setup Android SDK
|
|
||||||
RUN mkdir -p $ANDROID_HOME && \
|
|
||||||
mkdir -p /root/.android && \
|
|
||||||
echo '### User Sources for `android` cmd line tool ###' > /root/.android/repositories.cfg && \
|
|
||||||
echo 'count=0' >> /root/.android/repositories.cfg
|
|
||||||
|
|
||||||
# Download and setup Android Command Line Tools
|
|
||||||
RUN wget -q https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O /tmp/cmdline-tools.zip && \
|
|
||||||
unzip -q /tmp/cmdline-tools.zip -d $ANDROID_HOME && \
|
|
||||||
rm /tmp/cmdline-tools.zip && \
|
|
||||||
mkdir -p $ANDROID_HOME/cmdline-tools/latest && \
|
|
||||||
mv $ANDROID_HOME/cmdline-tools/* $ANDROID_HOME/cmdline-tools/latest/ 2>/dev/null || true
|
|
||||||
|
|
||||||
# Accept all SDK licenses up front so Gradle can install/use components non-interactively
|
|
||||||
RUN yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --sdk_root=$ANDROID_HOME --licenses > /dev/null
|
|
||||||
|
|
||||||
# Install Android SDK components (must match the compileSdk/targetSdk in the generated Gradle project)
|
|
||||||
RUN $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --sdk_root=$ANDROID_HOME \
|
|
||||||
"platform-tools" \
|
|
||||||
"platforms;android-$SDK_VERSION" \
|
|
||||||
"build-tools;$BUILD_TOOLS_VERSION" \
|
|
||||||
"ndk;$NDK_VERSION" \
|
|
||||||
--channel=0 2>&1 | grep -v "Warning" || true
|
|
||||||
|
|
||||||
# Set NDK environment variable
|
|
||||||
ENV NDK_HOME=$ANDROID_HOME/ndk/$NDK_VERSION
|
|
||||||
|
|
||||||
# Gradle distribution. `tauri android init` regenerates gen/android with a
|
|
||||||
# wrapper pointing at services.gradle.org, so every Android job would otherwise
|
|
||||||
# download ~130MB of Gradle at build time — slow, and a hard failure when the
|
|
||||||
# CDN hiccups ("Unexpected end of file from server"). Ship the distribution in
|
|
||||||
# the image instead; scripts/sync-android-sources.sh repoints the regenerated
|
|
||||||
# wrapper at this local copy. Keep GRADLE_VERSION in sync with the version
|
|
||||||
# Tauri's generated wrapper requests.
|
|
||||||
ENV GRADLE_VERSION=8.14.3 \
|
|
||||||
GRADLE_HOME=/opt/gradle/gradle-8.14.3
|
|
||||||
RUN mkdir -p /opt/gradle/dist && \
|
|
||||||
wget -q "https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip" \
|
|
||||||
-O "/opt/gradle/dist/gradle-${GRADLE_VERSION}-bin.zip" && \
|
|
||||||
unzip -q "/opt/gradle/dist/gradle-${GRADLE_VERSION}-bin.zip" -d /opt/gradle && \
|
|
||||||
"$GRADLE_HOME/bin/gradle" --version
|
|
||||||
ENV PATH="$GRADLE_HOME/bin:$PATH"
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Desktop packaging tools — kept in a trailing layer ON PURPOSE so that adding
|
|
||||||
# or changing a packaging tool doesn't invalidate the expensive apt/rust/Android
|
|
||||||
# layers above (a tool tweak becomes a ~1-2 min rebuild, not ~15). Covers Linux
|
|
||||||
# (deb/rpm) and Windows cross (MSVC via cargo-xwin + NSIS).
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
# Linux desktop packaging: rpmbuild for the .rpm bundle (deb needs nothing extra)
|
|
||||||
rpm \
|
|
||||||
file \
|
|
||||||
# Windows cross-compile (official Tauri path: MSVC target via cargo-xwin).
|
|
||||||
# clang provides clang-cl, the MSVC-compatible C compiler cc-rs uses to build
|
|
||||||
# C deps (bundled sqlite, ring, ...); lld = linker; llvm = llvm-lib/ar etc;
|
|
||||||
# nsis = installer generator.
|
|
||||||
clang \
|
|
||||||
lld \
|
|
||||||
llvm \
|
|
||||||
nsis \
|
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
|
||||||
# Ubuntu's clang package ships clang but NOT the clang-cl alias that cc-rs
|
|
||||||
# invokes for MSVC targets. clang-cl is the same binary in MSVC-compat mode,
|
|
||||||
# so provide it as a symlink.
|
|
||||||
&& ln -sf /usr/bin/clang /usr/local/bin/clang-cl
|
|
||||||
|
|
||||||
# Windows rust target + cargo-xwin (downloads the MSVC CRT/SDK at build time).
|
|
||||||
RUN . $HOME/.cargo/env && \
|
|
||||||
rustup target add x86_64-pc-windows-msvc && \
|
|
||||||
cargo install --locked cargo-xwin
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Supply-chain and docs tooling.
|
|
||||||
#
|
|
||||||
# cargo-deny — advisories/licences/bans/sources gate (src-tauri/deny.toml),
|
|
||||||
# run by the `security` job. It fetches the RustSec advisory
|
|
||||||
# database at run time; that is *data*, not a toolchain, so it
|
|
||||||
# does not breach the no-installs-in-CI rule.
|
|
||||||
# cargo-cyclonedx — SBOM for the Rust half of a release.
|
|
||||||
# mdbook — builds the docs site. It used to be curl'd from GitHub
|
|
||||||
# releases *inside* the job (publish-docs.yml), which was both a
|
|
||||||
# breach of that rule and a hard dependency on GitHub's CDN
|
|
||||||
# being up at publish time. Pinned to the version that job used.
|
|
||||||
ENV MDBOOK_VERSION=v0.4.40
|
|
||||||
RUN . $HOME/.cargo/env && \
|
|
||||||
cargo install --locked cargo-deny cargo-cyclonedx && \
|
|
||||||
wget -q "https://github.com/rust-lang/mdBook/releases/download/${MDBOOK_VERSION}/mdbook-${MDBOOK_VERSION}-x86_64-unknown-linux-gnu.tar.gz" \
|
|
||||||
-O /tmp/mdbook.tar.gz && \
|
|
||||||
tar -xzf /tmp/mdbook.tar.gz -C /usr/local/bin && \
|
|
||||||
rm /tmp/mdbook.tar.gz && \
|
|
||||||
cargo deny --version && \
|
|
||||||
cargo cyclonedx --version && \
|
|
||||||
mdbook --version
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
ENTRYPOINT ["/bin/bash"]
|
|
||||||
Vendored
+4
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 434 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2026 Duncan Tourolle
|
|
||||||
|
|
||||||
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.
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
<h1 align="center">
|
|
||||||
<img src="docs/assets/logo.png" alt="JellyTau logo" width="120" /><br />
|
|
||||||
JellyTau
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
A cross-platform Jellyfin client built with Tauri, SvelteKit, and TypeScript.
|
|
||||||
|
|
||||||
Business logic lives in a Rust backend; a UI-rich Svelte frontend handles
|
|
||||||
presentation and talks to it over Tauri's IPC. Targets Linux (libmpv) and
|
|
||||||
Android (ExoPlayer).
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
This project uses [bun](https://bun.sh) as its package manager.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Activate the Rust environment (fish shell)
|
|
||||||
source "$HOME/.cargo/env.fish"
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
bun install
|
|
||||||
|
|
||||||
# Run in development
|
|
||||||
bun run tauri dev
|
|
||||||
|
|
||||||
# Type-check the frontend
|
|
||||||
bun run check
|
|
||||||
|
|
||||||
# Build for Linux
|
|
||||||
bun run tauri build
|
|
||||||
|
|
||||||
# Build for Android
|
|
||||||
bun run tauri android build
|
|
||||||
```
|
|
||||||
|
|
||||||
For the full set of build, test, and Android helper scripts, see
|
|
||||||
[scripts/README.md](scripts/README.md).
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
| Topic | Location |
|
|
||||||
|-------|----------|
|
|
||||||
| Architecture overview & subsystem docs | [docs/architecture/](docs/architecture/) |
|
|
||||||
| Requirements, traceability & technical debt | [docs/requirements.md](docs/requirements.md) |
|
|
||||||
| Build & release process | [docs/build/build-release.md](docs/build/build-release.md) |
|
|
||||||
| Docker builds | [docs/build/docker.md](docs/build/docker.md) |
|
|
||||||
| Traceability tooling & CI | [docs/traceability.md](docs/traceability.md), [docs/traceability-ci.md](docs/traceability-ci.md) |
|
|
||||||
| Release checklist | [docs/release-checklist.md](docs/release-checklist.md) |
|
|
||||||
| UX flows | [docs/ux-flows.md](docs/ux-flows.md) |
|
|
||||||
| CI operations (builder image, secrets, runner) | [docs/build/ci-operations.md](docs/build/ci-operations.md) |
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
[CONTRIBUTING.md](CONTRIBUTING.md) covers the setup, the gates a change has to
|
|
||||||
pass, and the two rules that catch people out (bug fixes start with a failing
|
|
||||||
test; Jellyfin's taxonomy stays in Rust). Please also read the
|
|
||||||
[Code of Conduct](CODE_OF_CONDUCT.md).
|
|
||||||
|
|
||||||
Found a security problem? Do not open an issue — see [SECURITY.md](SECURITY.md).
|
|
||||||
|
|
||||||
## Verifying a download
|
|
||||||
|
|
||||||
Every release publishes `SHA256SUMS` covering all of its artifacts, plus an SBOM
|
|
||||||
of what went into the build:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sha256sum -c SHA256SUMS
|
|
||||||
```
|
|
||||||
|
|
||||||
Desktop builds update themselves from Settings → Updates, verifying each payload
|
|
||||||
against JellyTau's signing key before installing. Android installs are handled by
|
|
||||||
the system installer, so the app links to the releases page instead.
|
|
||||||
|
|
||||||
## Recommended IDE Setup
|
|
||||||
|
|
||||||
[VS Code](https://code.visualstudio.com/) +
|
|
||||||
[Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode) +
|
|
||||||
[Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) +
|
|
||||||
[rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer).
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MIT
|
|
||||||
-60
@@ -1,60 +0,0 @@
|
|||||||
# Security Policy
|
|
||||||
|
|
||||||
## Reporting a vulnerability
|
|
||||||
|
|
||||||
Email **duncan@tourolle.paris** with `[JellyTau security]` in the subject.
|
|
||||||
Please do **not** open a public issue for a vulnerability — JellyTau handles
|
|
||||||
Jellyfin credentials and media, and an unfixed issue in a public tracker is an
|
|
||||||
advisory for everyone running it.
|
|
||||||
|
|
||||||
Include what you have: what the problem is, how to reproduce it, the version and
|
|
||||||
platform, and what you think an attacker could do with it. A rough report is
|
|
||||||
worth more than a polished one that never gets sent.
|
|
||||||
|
|
||||||
You can expect an acknowledgement within a week. If a fix is warranted it will
|
|
||||||
ship in the next release, and you will be credited in the release notes unless
|
|
||||||
you would rather not be.
|
|
||||||
|
|
||||||
## Supported versions
|
|
||||||
|
|
||||||
JellyTau is a single-maintainer project without long-term support branches.
|
|
||||||
**Only the latest release receives fixes.** Desktop builds can update themselves
|
|
||||||
(Settings → Updates); on Android, install the latest APK from the releases page.
|
|
||||||
|
|
||||||
## What is in scope
|
|
||||||
|
|
||||||
The application and its build pipeline:
|
|
||||||
|
|
||||||
- The Tauri backend (`src-tauri/`) and the Svelte frontend (`src/`)
|
|
||||||
- Credential storage — the system keyring and its encrypted-file fallback
|
|
||||||
- The Android player service and its JNI bridge
|
|
||||||
- The loopback media server used for downloaded playback
|
|
||||||
- The release pipeline: artifact signing, the update manifest, the builder image
|
|
||||||
|
|
||||||
**Out of scope:** vulnerabilities in Jellyfin itself (report those to the
|
|
||||||
Jellyfin project), and issues that require an already-compromised device or a
|
|
||||||
malicious server the user deliberately configured and trusted.
|
|
||||||
|
|
||||||
## What the project already does
|
|
||||||
|
|
||||||
Not a guarantee, but so you know what has been considered:
|
|
||||||
|
|
||||||
- **Credentials** never go in plaintext config: the system keyring is used where
|
|
||||||
available, with an AES-GCM encrypted file as fallback (see
|
|
||||||
[docs/architecture/09-security.md](docs/architecture/09-security.md)).
|
|
||||||
- **The webview runs under a restrictive CSP**, and the asset protocol is scoped
|
|
||||||
to the thumbnail cache directory only.
|
|
||||||
- **Path confinement** is enforced on the cache and download roots — a
|
|
||||||
server-supplied id cannot decide where a file lands (DR-210, DR-211).
|
|
||||||
- **Queries and URLs bind or encode their inputs** rather than interpolating
|
|
||||||
them (DR-212).
|
|
||||||
- **Dependencies are scanned on every build** by `cargo deny` against the RustSec
|
|
||||||
advisory database, and licence-checked against an allow-list (DR-216).
|
|
||||||
- **Releases carry `SHA256SUMS` and an SBOM**, so you can verify a download and
|
|
||||||
find out what went into it.
|
|
||||||
- **Desktop updates are signature-verified** against a key held only in CI before
|
|
||||||
anything is installed (DR-217).
|
|
||||||
|
|
||||||
Windows installers are **not** Authenticode-signed — SmartScreen will warn on
|
|
||||||
first run. That is a cost and identity problem, not an oversight; verify the
|
|
||||||
download against `SHA256SUMS` instead.
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
|||||||
|
window.ALL_CRATES = ["jellytau","jellytau_lib"];
|
||||||
|
//{"start":21,"fragment_lengths":[10,15]}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Documentation for Rustdoc"><title>Help</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="./static.files/${f}">`).join(""))</script><link rel="stylesheet" href="./static.files/normalize-9960930a.css"><link rel="stylesheet" href="./static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="./" data-static-root-path="./static.files/" data-current-crate="jellytau" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="./static.files/storage-41dd4d93.js"></script><script defer src="./static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="./static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="./static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="./static.files/favicon-044be391.svg"></head><body class="rustdoc mod sys"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">All</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><a class="logo-container" href="./index.html"><img class="rust-logo" src="./static.files/rust-logo-9a9549ea.svg" alt="logo"></a><h2><a href="./index.html">Rustdoc</a><span class="version">1.97.1</span></h2></div><div class="version">(8bab26f4f 2026-07-14)</div><h2 class="location">Help</h2><div class="sidebar-elems"></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><h1>Rustdoc help</h1><span class="out-of-band"><a id="back" href="javascript:void(0)" onclick="history.back();">Back</a></span></div><noscript><section><p>You need to enable JavaScript to use keyboard commands or search.</p><p>For more information, browse the <a href="https://doc.rust-lang.org/1.97.1/rustdoc/">rustdoc handbook</a>.</p></section></noscript></section></div></main></body></html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<meta http-equiv="refresh" content="0; url=jellytau_lib/index.html">
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="List of all items in this crate"><title>List of all items in this crate</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="jellytau" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../static.files/storage-41dd4d93.js"></script><script defer src="../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc mod sys"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">All</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../jellytau/index.html">jellytau</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h3><a href="#functions">Crate Items</a></h3><ul class="block"><li><a href="#functions" title="Functions">Functions</a></li></ul></section><div id="rustdoc-modnav"></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><h1>List of all items</h1><rustdoc-toolbar></rustdoc-toolbar></div><h3 id="functions">Functions</h3><ul class="all-items"><li><a href="fn.main.html">main</a></li></ul></section></div></main></body></html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="API documentation for the Rust `main` fn in crate `jellytau`."><title>main in jellytau - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="jellytau" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">main</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../jellytau/index.html">jellytau</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">jellytau</a></div><h1>Function <span class="fn">main</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/jellytau/main.rs.html#4-6">Source</a> </span></div><pre class="rust item-decl"><code>pub(crate) fn main()</code></pre></section></div></main></body></html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="API documentation for the Rust `jellytau` crate."><title>jellytau - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="jellytau" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../static.files/storage-41dd4d93.js"></script><script defer src="../crates.js"></script><script defer src="../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc mod crate"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">Crate jellytau</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../jellytau/index.html">jellytau</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><ul class="block"><li><a id="all-types" href="all.html">All Items</a></li></ul><section id="rustdoc-toc"><h3><a href="#functions">Crate Items</a></h3><ul class="block"><li><a href="#functions" title="Functions">Functions</a></li></ul></section><div id="rustdoc-modnav"></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><h1>Crate <span>jellytau</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/jellytau/main.rs.html#2-6">Source</a> </span></div><h2 id="functions" class="section-header">Functions<a href="#functions" class="anchor">§</a></h2><dl class="item-table"><dt><a class="fn" href="fn.main.html" title="fn jellytau::main">main</a><span title="Restricted Visibility"> 🔒</span> </dt></dl></section></div></main></body></html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
window.SIDEBAR_ITEMS = {"fn":["main"]};
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="API documentation for the Rust `VERIFICATION_INTERVAL_MS` constant in crate `jellytau_lib`."><title>VERIFICATION_INTERVAL_MS in jellytau_lib::auth::session_verifier - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc constant"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">VERIFICATION_INTERVAL_MS</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>auth::<wbr>session_<wbr>verifier</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">auth</a>::<wbr><a href="index.html">session_verifier</a></div><h1>Constant <span class="constant">VERIFICATION_<wbr>INTERVAL_<wbr>MS</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/auth/session_verifier.rs.html#10">Source</a> </span></div><pre class="rust item-decl"><code>const VERIFICATION_INTERVAL_MS: <a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.u64.html">u64</a> = 300000;</code></pre></section></div></main></body></html>
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="API documentation for the Rust `session_verifier` mod in crate `jellytau_lib`."><title>jellytau_lib::auth::session_verifier - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="../sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc mod"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">Module session_verifier</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h2 class="location"><a href="#">Module session_<wbr>verifier</a></h2><h3><a href="#structs">Module Items</a></h3><ul class="block"><li><a href="#structs" title="Structs">Structs</a></li><li><a href="#enums" title="Enums">Enums</a></li><li><a href="#constants" title="Constants">Constants</a></li></ul></section><div id="rustdoc-modnav"><h2><a href="../index.html">In jellytau_<wbr>lib::<wbr>auth</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">auth</a></div><h1>Module <span>session_<wbr>verifier</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/auth/session_verifier.rs.html#1-170">Source</a> </span></div><h2 id="structs" class="section-header">Structs<a href="#structs" class="anchor">§</a></h2><dl class="item-table"><dt><a class="struct" href="struct.SessionVerifier.html" title="struct jellytau_lib::auth::session_verifier::SessionVerifier">Session<wbr>Verifier</a></dt><dd>Background session verifier</dd></dl><h2 id="enums" class="section-header">Enums<a href="#enums" class="anchor">§</a></h2><dl class="item-table"><dt><a class="enum" href="enum.SessionVerificationEvent.html" title="enum jellytau_lib::auth::session_verifier::SessionVerificationEvent">Session<wbr>Verification<wbr>Event</a></dt><dd>Session verification result event emitted to frontend</dd></dl><h2 id="constants" class="section-header">Constants<a href="#constants" class="anchor">§</a></h2><dl class="item-table"><dt><a class="constant" href="constant.VERIFICATION_INTERVAL_MS.html" title="constant jellytau_lib::auth::session_verifier::VERIFICATION_INTERVAL_MS">VERIFICATION_<wbr>INTERVAL_<wbr>MS</a><span title="Restricted Visibility"> 🔒</span> </dt></dl></section></div></main></body></html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
window.SIDEBAR_ITEMS = {"constant":["VERIFICATION_INTERVAL_MS"],"enum":["SessionVerificationEvent"],"struct":["SessionVerifier"]};
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
window.SIDEBAR_ITEMS = {"mod":["session_verifier"],"struct":["AuthManager","AuthResult","AuthenticateByNameResponse","JellyfinUser","PublicSystemInfo","ServerInfo","Session","User"]};
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,5 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Connect to a Jellyfin server and get server info"><title>auth_connect_to_server in jellytau_lib::commands::auth - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">auth_connect_to_server</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>auth</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">auth</a></div><h1>Function <span class="fn">auth_<wbr>connect_<wbr>to_<wbr>server</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/auth.rs.html#75-80">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn auth_connect_to_server(
|
||||||
|
server_url: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
|
||||||
|
auth_manager: State<'_, <a class="struct" href="struct.AuthManagerWrapper.html" title="struct jellytau_lib::commands::auth::AuthManagerWrapper">AuthManagerWrapper</a>>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="struct" href="../../auth/struct.ServerInfo.html" title="struct jellytau_lib::auth::ServerInfo">ServerInfo</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Connect to a Jellyfin server and get server info</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Get current session"><title>auth_get_session in jellytau_lib::commands::auth - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">auth_get_session</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>auth</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">auth</a></div><h1>Function <span class="fn">auth_<wbr>get_<wbr>session</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/auth.rs.html#171-175">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn auth_get_session(
|
||||||
|
auth_manager: State<'_, <a class="struct" href="struct.AuthManagerWrapper.html" title="struct jellytau_lib::commands::auth::AuthManagerWrapper">AuthManagerWrapper</a>>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="enum" href="https://doc.rust-lang.org/1.97.1/core/option/enum.Option.html" title="enum core::option::Option">Option</a><<a class="struct" href="../../auth/struct.Session.html" title="struct jellytau_lib::auth::Session">Session</a>>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Get current session</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Initialize the auth manager (call on app startup) Restores session from storage if available"><title>auth_initialize in jellytau_lib::commands::auth - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">auth_initialize</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>auth</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">auth</a></div><h1>Function <span class="fn">auth_<wbr>initialize</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/auth.rs.html#20-70">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn auth_initialize(
|
||||||
|
auth_manager: State<'_, <a class="struct" href="struct.AuthManagerWrapper.html" title="struct jellytau_lib::commands::auth::AuthManagerWrapper">AuthManagerWrapper</a>>,
|
||||||
|
database: State<'_, <a class="struct" href="../storage/struct.DatabaseWrapper.html" title="struct jellytau_lib::commands::storage::DatabaseWrapper">DatabaseWrapper</a>>,
|
||||||
|
credentials: State<'_, <a class="struct" href="../storage/struct.CredentialStoreWrapper.html" title="struct jellytau_lib::commands::storage::CredentialStoreWrapper">CredentialStoreWrapper</a>>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="enum" href="https://doc.rust-lang.org/1.97.1/core/option/enum.Option.html" title="enum core::option::Option">Option</a><<a class="struct" href="../../auth/struct.Session.html" title="struct jellytau_lib::auth::Session">Session</a>>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Initialize the auth manager (call on app startup)
|
||||||
|
Restores session from storage if available</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Login with username and password"><title>auth_login in jellytau_lib::commands::auth - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">auth_login</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>auth</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">auth</a></div><h1>Function <span class="fn">auth_<wbr>login</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/auth.rs.html#85-114">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn auth_login(
|
||||||
|
server_url: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
|
||||||
|
username: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
|
||||||
|
password: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
|
||||||
|
device_id: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
|
||||||
|
auth_manager: State<'_, <a class="struct" href="struct.AuthManagerWrapper.html" title="struct jellytau_lib::commands::auth::AuthManagerWrapper">AuthManagerWrapper</a>>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="struct" href="../../auth/struct.AuthResult.html" title="struct jellytau_lib::auth::AuthResult">AuthResult</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Login with username and password</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Logout (clear session and call Jellyfin logout endpoint)"><title>auth_logout in jellytau_lib::commands::auth - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">auth_logout</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>auth</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">auth</a></div><h1>Function <span class="fn">auth_<wbr>logout</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/auth.rs.html#142-166">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn auth_logout(
|
||||||
|
server_url: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
|
||||||
|
access_token: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
|
||||||
|
device_id: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
|
||||||
|
auth_manager: State<'_, <a class="struct" href="struct.AuthManagerWrapper.html" title="struct jellytau_lib::commands::auth::AuthManagerWrapper">AuthManagerWrapper</a>>,
|
||||||
|
session_verifier: State<'_, <a class="struct" href="struct.SessionVerifierWrapper.html" title="struct jellytau_lib::commands::auth::SessionVerifierWrapper">SessionVerifierWrapper</a>>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.unit.html">()</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Logout (clear session and call Jellyfin logout endpoint)</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Re-authenticate with password (when session expired)"><title>auth_reauthenticate in jellytau_lib::commands::auth - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">auth_reauthenticate</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>auth</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">auth</a></div><h1>Function <span class="fn">auth_<wbr>reauthenticate</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/auth.rs.html#244-282">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn auth_reauthenticate(
|
||||||
|
password: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
|
||||||
|
device_id: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
|
||||||
|
auth_manager: State<'_, <a class="struct" href="struct.AuthManagerWrapper.html" title="struct jellytau_lib::commands::auth::AuthManagerWrapper">AuthManagerWrapper</a>>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="struct" href="../../auth/struct.AuthResult.html" title="struct jellytau_lib::auth::AuthResult">AuthResult</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Re-authenticate with password (when session expired)</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Set current session (for restoration from storage)"><title>auth_set_session in jellytau_lib::commands::auth - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">auth_set_session</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>auth</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">auth</a></div><h1>Function <span class="fn">auth_<wbr>set_<wbr>session</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/auth.rs.html#180-195">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn auth_set_session(
|
||||||
|
session: <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/option/enum.Option.html" title="enum core::option::Option">Option</a><<a class="struct" href="../../auth/struct.Session.html" title="struct jellytau_lib::auth::Session">Session</a>>,
|
||||||
|
auth_manager: State<'_, <a class="struct" href="struct.AuthManagerWrapper.html" title="struct jellytau_lib::commands::auth::AuthManagerWrapper">AuthManagerWrapper</a>>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.unit.html">()</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Set current session (for restoration from storage)</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Start background session verification"><title>auth_start_verification in jellytau_lib::commands::auth - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">auth_start_verification</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>auth</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">auth</a></div><h1>Function <span class="fn">auth_<wbr>start_<wbr>verification</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/auth.rs.html#200-224">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn auth_start_verification(
|
||||||
|
device_id: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
|
||||||
|
app_handle: AppHandle,
|
||||||
|
auth_manager: State<'_, <a class="struct" href="struct.AuthManagerWrapper.html" title="struct jellytau_lib::commands::auth::AuthManagerWrapper">AuthManagerWrapper</a>>,
|
||||||
|
session_verifier: State<'_, <a class="struct" href="struct.SessionVerifierWrapper.html" title="struct jellytau_lib::commands::auth::SessionVerifierWrapper">SessionVerifierWrapper</a>>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.unit.html">()</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Start background session verification</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Stop background session verification"><title>auth_stop_verification in jellytau_lib::commands::auth - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">auth_stop_verification</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>auth</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">auth</a></div><h1>Function <span class="fn">auth_<wbr>stop_<wbr>verification</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/auth.rs.html#229-239">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn auth_stop_verification(
|
||||||
|
session_verifier: State<'_, <a class="struct" href="struct.SessionVerifierWrapper.html" title="struct jellytau_lib::commands::auth::SessionVerifierWrapper">SessionVerifierWrapper</a>>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.unit.html">()</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Stop background session verification</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Verify current session"><title>auth_verify_session in jellytau_lib::commands::auth - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">auth_verify_session</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>auth</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">auth</a></div><h1>Function <span class="fn">auth_<wbr>verify_<wbr>session</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/auth.rs.html#119-137">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn auth_verify_session(
|
||||||
|
server_url: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
|
||||||
|
user_id: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
|
||||||
|
access_token: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
|
||||||
|
device_id: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
|
||||||
|
auth_manager: State<'_, <a class="struct" href="struct.AuthManagerWrapper.html" title="struct jellytau_lib::commands::auth::AuthManagerWrapper">AuthManagerWrapper</a>>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.bool.html">bool</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Verify current session</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Authentication and session-lifecycle commands."><title>jellytau_lib::commands::auth - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="../sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc mod"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">Module auth</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h2 class="location"><a href="#">Module auth</a></h2><h3><a href="#structs">Module Items</a></h3><ul class="block"><li><a href="#structs" title="Structs">Structs</a></li><li><a href="#functions" title="Functions">Functions</a></li></ul></section><div id="rustdoc-modnav"><h2><a href="../index.html">In jellytau_<wbr>lib::<wbr>commands</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a></div><h1>Module <span>auth</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/auth.rs.html#1-476">Source</a> </span></div><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Authentication and session-lifecycle commands.</p>
|
||||||
|
<p>TRACES: UR-042 | IR-009, IR-014, JA-002 | DR-054</p>
|
||||||
|
</div></details><h2 id="structs" class="section-header">Structs<a href="#structs" class="anchor">§</a></h2><dl class="item-table"><dt><a class="struct" href="struct.AuthManagerWrapper.html" title="struct jellytau_lib::commands::auth::AuthManagerWrapper">Auth<wbr>Manager<wbr>Wrapper</a></dt><dd>Wrapper for AuthManager to manage in Tauri state</dd><dt><a class="struct" href="struct.SessionVerifierWrapper.html" title="struct jellytau_lib::commands::auth::SessionVerifierWrapper">Session<wbr>Verifier<wbr>Wrapper</a></dt><dd>Wrapper for SessionVerifier to manage in Tauri state</dd></dl><h2 id="functions" class="section-header">Functions<a href="#functions" class="anchor">§</a></h2><dl class="item-table"><dt><a class="fn" href="fn.auth_connect_to_server.html" title="fn jellytau_lib::commands::auth::auth_connect_to_server">auth_<wbr>connect_<wbr>to_<wbr>server</a></dt><dd>Connect to a Jellyfin server and get server info</dd><dt><a class="fn" href="fn.auth_get_session.html" title="fn jellytau_lib::commands::auth::auth_get_session">auth_<wbr>get_<wbr>session</a></dt><dd>Get current session</dd><dt><a class="fn" href="fn.auth_initialize.html" title="fn jellytau_lib::commands::auth::auth_initialize">auth_<wbr>initialize</a></dt><dd>Initialize the auth manager (call on app startup)
|
||||||
|
Restores session from storage if available</dd><dt><a class="fn" href="fn.auth_login.html" title="fn jellytau_lib::commands::auth::auth_login">auth_<wbr>login</a></dt><dd>Login with username and password</dd><dt><a class="fn" href="fn.auth_logout.html" title="fn jellytau_lib::commands::auth::auth_logout">auth_<wbr>logout</a></dt><dd>Logout (clear session and call Jellyfin logout endpoint)</dd><dt><a class="fn" href="fn.auth_reauthenticate.html" title="fn jellytau_lib::commands::auth::auth_reauthenticate">auth_<wbr>reauthenticate</a></dt><dd>Re-authenticate with password (when session expired)</dd><dt><a class="fn" href="fn.auth_set_session.html" title="fn jellytau_lib::commands::auth::auth_set_session">auth_<wbr>set_<wbr>session</a></dt><dd>Set current session (for restoration from storage)</dd><dt><a class="fn" href="fn.auth_start_verification.html" title="fn jellytau_lib::commands::auth::auth_start_verification">auth_<wbr>start_<wbr>verification</a></dt><dd>Start background session verification</dd><dt><a class="fn" href="fn.auth_stop_verification.html" title="fn jellytau_lib::commands::auth::auth_stop_verification">auth_<wbr>stop_<wbr>verification</a></dt><dd>Stop background session verification</dd><dt><a class="fn" href="fn.auth_verify_session.html" title="fn jellytau_lib::commands::auth::auth_verify_session">auth_<wbr>verify_<wbr>session</a></dt><dd>Verify current session</dd></dl></section></div></main></body></html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
window.SIDEBAR_ITEMS = {"fn":["auth_connect_to_server","auth_get_session","auth_initialize","auth_login","auth_logout","auth_reauthenticate","auth_set_session","auth_start_verification","auth_stop_verification","auth_verify_session"],"struct":["AuthManagerWrapper","SessionVerifierWrapper"]};
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Kebab-case, per the project’s event convention."><title>CATALOG_INDEX_EVENT in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc constant"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">CATALOG_INDEX_EVENT</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Constant <span class="constant">CATALOG_<wbr>INDEX_<wbr>EVENT</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#57">Source</a> </span></div><pre class="rust item-decl"><code>pub const CATALOG_INDEX_EVENT: &<a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.str.html">str</a> = "catalog-index-event";</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Kebab-case, per the project’s event convention.</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Delay before the first staleness check, to let sign-in complete and the repository be registered. Without it the first check runs against an empty repository manager and a fresh install would sit unindexed until the next tick."><title>CATALOG_INDEX_FIRST_CHECK in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc constant"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">CATALOG_INDEX_FIRST_CHECK</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Constant <span class="constant">CATALOG_<wbr>INDEX_<wbr>FIRST_<wbr>CHECK</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#54">Source</a> </span></div><pre class="rust item-decl"><code>const CATALOG_INDEX_FIRST_CHECK: <a class="struct" href="https://doc.rust-lang.org/1.97.1/core/time/struct.Duration.html" title="struct core::time::Duration">Duration</a>;</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Delay before the first staleness check, to let sign-in complete and the
|
||||||
|
repository be registered. Without it the first check runs against an empty
|
||||||
|
repository manager and a fresh install would sit unindexed until the next
|
||||||
|
tick.</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="How often the scheduler wakes to check staleness. Far shorter than the TTL because a tick is nearly free — one indexed `app_settings` lookup — and it is what makes the indexer responsive to events it cannot subscribe to: signing in, and coming back online. The TTL, not the tick, decides whether a crawl actually happens."><title>CATALOG_INDEX_TICK in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc constant"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">CATALOG_INDEX_TICK</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Constant <span class="constant">CATALOG_<wbr>INDEX_<wbr>TICK</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#48">Source</a> </span></div><pre class="rust item-decl"><code>const CATALOG_INDEX_TICK: <a class="struct" href="https://doc.rust-lang.org/1.97.1/core/time/struct.Duration.html" title="struct core::time::Duration">Duration</a>;</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>How often the scheduler wakes to <em>check</em> staleness. Far shorter than the TTL
|
||||||
|
because a tick is nearly free — one indexed <code>app_settings</code> lookup — and it is
|
||||||
|
what makes the indexer responsive to events it cannot subscribe to: signing
|
||||||
|
in, and coming back online. The TTL, not the tick, decides whether a crawl
|
||||||
|
actually happens.</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="How long an index stays fresh before a re-index is due."><title>CATALOG_INDEX_TTL in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc constant"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">CATALOG_INDEX_TTL</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Constant <span class="constant">CATALOG_<wbr>INDEX_<wbr>TTL</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#41">Source</a> </span></div><pre class="rust item-decl"><code>const CATALOG_INDEX_TTL: <a class="struct" href="https://doc.rust-lang.org/1.97.1/core/time/struct.Duration.html" title="struct core::time::Duration">Duration</a>;</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>How long an index stays fresh before a re-index is due.</p>
|
||||||
|
<p>This lives in Rust rather than being a frontend constant because it decides
|
||||||
|
<em>whether the local cache is authoritative</em> — the same class of decision as
|
||||||
|
<code>include_catalog_browse</code>, and squarely the “sync policy” the spec review
|
||||||
|
checklist keeps out of the presentation layer. If it later becomes
|
||||||
|
user-configurable it stays a Rust-owned setting edited through a command.</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Item types worth caching for offline browsing: containers the library landing pages render plus the playable leaves users queue for download. `MusicArtist` and `Playlist` are here because search groups results by them (UR-060’s Artists group). Without them in the crawl, the local index can never answer an artist query and those groups can only ever be filled by the server leg. Keep this in step with what `prune_stale_catalog` is allowed to sweep — the crawl is only authoritative for the types it asks for."><title>CATALOG_ITEM_TYPES in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc constant"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">CATALOG_ITEM_TYPES</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Constant <span class="constant">CATALOG_<wbr>ITEM_<wbr>TYPES</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#97-107">Source</a> </span></div><pre class="rust item-decl"><code>const CATALOG_ITEM_TYPES: &[&<a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.str.html">str</a>];</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Item types worth caching for offline browsing: containers the library
|
||||||
|
landing pages render plus the playable leaves users queue for download.
|
||||||
|
<code>MusicArtist</code> and <code>Playlist</code> are here because search groups results by them
|
||||||
|
(UR-060’s Artists group). Without them in the crawl, the local index can
|
||||||
|
never answer an artist query and those groups can only ever be filled by the
|
||||||
|
server leg. Keep this in step with what <code>prune_stale_catalog</code> is allowed to
|
||||||
|
sweep — the crawl is only authoritative for the types it asks for.</p>
|
||||||
|
<p>TRACES: UR-065, UR-060 | DR-111</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="app_settings key holding the RFC-3339 timestamp of the last successful full-catalog sync."><title>LAST_CATALOG_SYNC_KEY in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc constant"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">LAST_CATALOG_SYNC_KEY</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Constant <span class="constant">LAST_<wbr>CATALOG_<wbr>SYNC_<wbr>KEY</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#32">Source</a> </span></div><pre class="rust item-decl"><code>const LAST_CATALOG_SYNC_KEY: &<a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.str.html">str</a> = "last_catalog_sync";</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>app_settings key holding the RFC-3339 timestamp of the last successful
|
||||||
|
full-catalog sync.</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Jellyfin item types whose download is a video stream rather than an audio one. The download queue stores an opaque `media_type` (‘audio’/‘video’); this is where the taxonomy that produces it lives, so the frontend never has to know which item types are video."><title>VIDEO_ITEM_TYPES in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc constant"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">VIDEO_ITEM_TYPES</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Constant <span class="constant">VIDEO_<wbr>ITEM_<wbr>TYPES</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#115">Source</a> </span></div><pre class="rust item-decl"><code>const VIDEO_ITEM_TYPES: &[&<a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.str.html">str</a>];</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Jellyfin item types whose download is a <em>video</em> stream rather than an audio
|
||||||
|
one. The download queue stores an opaque <code>media_type</code> (‘audio’/‘video’); this
|
||||||
|
is where the taxonomy that produces it lives, so the frontend never has to
|
||||||
|
know which item types are video.</p>
|
||||||
|
<p>TRACES: UR-071 | DR-135</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Report the last-synced timestamp so the UI can show a hint / decide whether to trigger a fresh sync."><title>catalog_sync_status in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">catalog_sync_status</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Function <span class="fn">catalog_<wbr>sync_<wbr>status</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#432-450">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn catalog_sync_status(
|
||||||
|
db: State<'_, <a class="struct" href="../storage/struct.DatabaseWrapper.html" title="struct jellytau_lib::commands::storage::DatabaseWrapper">DatabaseWrapper</a>>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="struct" href="struct.CatalogSyncStatus.html" title="struct jellytau_lib::commands::catalog::CatalogSyncStatus">CatalogSyncStatus</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Report the last-synced timestamp so the UI can show a hint / decide whether
|
||||||
|
to trigger a fresh sync.</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Whether an index pass is due, given when one last completed."><title>index_is_due in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">index_is_due</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Function <span class="fn">index_<wbr>is_<wbr>due</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#282-299">Source</a> </span></div><pre class="rust item-decl"><code>pub(crate) fn index_is_due(
|
||||||
|
last_synced_at: <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/option/enum.Option.html" title="enum core::option::Option">Option</a><&<a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.str.html">str</a>>,
|
||||||
|
now: <a class="struct" href="https://docs.rs/chrono/latest/chrono/datetime/struct.DateTime.html" title="struct chrono::datetime::DateTime">DateTime</a><<a class="struct" href="https://docs.rs/chrono/latest/chrono/offset/utc/struct.Utc.html" title="struct chrono::offset::utc::Utc">Utc</a>>,
|
||||||
|
ttl: <a class="struct" href="https://doc.rust-lang.org/1.97.1/core/time/struct.Duration.html" title="struct core::time::Duration">Duration</a>,
|
||||||
|
) -> <a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.bool.html">bool</a></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Whether an index pass is due, given when one last completed.</p>
|
||||||
|
<p>Pure so the policy is unit-testable without a clock, a server, or a database.
|
||||||
|
<code>None</code> (never indexed) and an unparseable stored value both mean “due” — a
|
||||||
|
corrupt timestamp should trigger a re-index, not silently freeze the catalog.</p>
|
||||||
|
<p>TRACES: UR-065 | DR-109 | UT-115</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="One scheduler tick: check the preconditions, then index if due."><title>maybe_run_scheduled_pass in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">maybe_run_scheduled_pass</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Function <span class="fn">maybe_<wbr>run_<wbr>scheduled_<wbr>pass</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#347-426">Source</a> </span></div><pre class="rust item-decl"><code>async fn maybe_run_scheduled_pass(app: &AppHandle) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.unit.html">()</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>One scheduler tick: check the preconditions, then index if due.</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Read the last-sync timestamp straight from `app_settings`."><title>read_last_sync in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">read_last_sync</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Function <span class="fn">read_<wbr>last_<wbr>sync</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#302-316">Source</a> </span></div><pre class="rust item-decl"><code>async fn read_last_sync(db_service: &<a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/sync/struct.Arc.html" title="struct alloc::sync::Arc">Arc</a><<a class="struct" href="../../storage/db_service/struct.RusqliteService.html" title="struct jellytau_lib::storage::db_service::RusqliteService">RusqliteService</a>>) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/option/enum.Option.html" title="enum core::option::Option">Option</a><<a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Read the last-sync timestamp straight from <code>app_settings</code>.</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Requeue video downloads that were fetched as audio."><title>requeue_mistyped_video_downloads in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">requeue_mistyped_video_downloads</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Function <span class="fn">requeue_<wbr>mistyped_<wbr>video_<wbr>downloads</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#491-518">Source</a> </span></div><pre class="rust item-decl"><code>pub(crate) async fn requeue_mistyped_video_downloads(
|
||||||
|
db_service: &<a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/sync/struct.Arc.html" title="struct alloc::sync::Arc">Arc</a><<a class="struct" href="../../storage/db_service/struct.RusqliteService.html" title="struct jellytau_lib::storage::db_service::RusqliteService">RusqliteService</a>>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.usize.html">usize</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Requeue video downloads that were fetched as audio.</p>
|
||||||
|
<p>Before <a href="fn.resolve_pending_download_urls.html" title="fn jellytau_lib::commands::catalog::resolve_pending_download_urls"><code>resolve_pending_download_urls</code></a> consulted the item’s type, a row
|
||||||
|
with no <code>media_type</code> — which is every row queued from a media card, since
|
||||||
|
<code>download_item</code> does not record one — resolved against
|
||||||
|
<code>get_audio_stream_url</code>. A movie queued that way completed with an audio-only
|
||||||
|
transcode on disk, so playing it offline could only ever fail. Those rows are
|
||||||
|
identifiable after the fact (no <code>media_type</code>, but a video item), so reset them
|
||||||
|
to pending with no URL and let the resolver fetch the real video.</p>
|
||||||
|
<p>Rows carrying an explicit <code>media_type</code> were resolved correctly and are left
|
||||||
|
alone, as are genuine audio downloads.</p>
|
||||||
|
<p>Returns the number of rows requeued.</p>
|
||||||
|
<p>TRACES: UR-071 | DR-136 | UT-126</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Core of `resume_queued_downloads`, factored out for testing: select every `pending`/`stream_url IS NULL` row, resolve each via `resolve` (returning `None` leaves the row pending), and heal the row so the pump can start it. The `resolve` closure receives `(item_id, media_type, quality_preset)`."><title>resolve_pending_download_urls in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">resolve_pending_download_urls</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Function <span class="fn">resolve_<wbr>pending_<wbr>download_<wbr>urls</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#529-636">Source</a> </span></div><pre class="rust item-decl"><code>pub(crate) async fn resolve_pending_download_urls<F, Fut>(
|
||||||
|
db_service: &<a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/sync/struct.Arc.html" title="struct alloc::sync::Arc">Arc</a><<a class="struct" href="../../storage/db_service/struct.RusqliteService.html" title="struct jellytau_lib::storage::db_service::RusqliteService">RusqliteService</a>>,
|
||||||
|
target_dir: &<a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.str.html">str</a>,
|
||||||
|
only_ids: <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/option/enum.Option.html" title="enum core::option::Option">Option</a><&[<a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.i64.html">i64</a>]>,
|
||||||
|
resolve: F,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="struct" href="struct.ResumeQueuedResult.html" title="struct jellytau_lib::commands::catalog::ResumeQueuedResult">ResumeQueuedResult</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>><div class="where">where
|
||||||
|
F: <a class="trait" href="https://doc.rust-lang.org/1.97.1/core/ops/function/trait.Fn.html" title="trait core::ops::function::Fn">Fn</a>(<a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>) -> Fut,
|
||||||
|
Fut: <a class="trait" href="https://doc.rust-lang.org/1.97.1/core/future/future/trait.Future.html" title="trait core::future::future::Future">Future</a><Output = <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/option/enum.Option.html" title="enum core::option::Option">Option</a><<a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>>>,</div></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Core of <a href="fn.resume_queued_downloads.html" title="fn jellytau_lib::commands::catalog::resume_queued_downloads"><code>resume_queued_downloads</code></a>, factored out for testing: select every
|
||||||
|
<code>pending</code>/<code>stream_url IS NULL</code> row, resolve each via <code>resolve</code> (returning
|
||||||
|
<code>None</code> leaves the row pending), and heal the row so the pump can start it.
|
||||||
|
The <code>resolve</code> closure receives <code>(item_id, media_type, quality_preset)</code>.</p>
|
||||||
|
<p><code>only_ids</code> restricts the sweep to specific download rows. Reconnect passes
|
||||||
|
<code>None</code> and heals everything; a bulk enqueue (an album, say) passes the rows
|
||||||
|
it just created, so clicking download on one album cannot also start every
|
||||||
|
unrelated row that has been sitting pending.</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Resolve the stream URL for every download row that was queued while offline (`status = 'pending' AND stream_url IS NULL`), then pump the queue so they start. Call this on reconnect."><title>resume_queued_downloads in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">resume_queued_downloads</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Function <span class="fn">resume_<wbr>queued_<wbr>downloads</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#648-751">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn resume_queued_downloads(
|
||||||
|
repository: State<'_, <a class="struct" href="../repository/struct.RepositoryManagerWrapper.html" title="struct jellytau_lib::commands::repository::RepositoryManagerWrapper">RepositoryManagerWrapper</a>>,
|
||||||
|
db: State<'_, <a class="struct" href="../storage/struct.DatabaseWrapper.html" title="struct jellytau_lib::commands::storage::DatabaseWrapper">DatabaseWrapper</a>>,
|
||||||
|
download_manager: State<'_, <a class="struct" href="../download/struct.DownloadManagerWrapper.html" title="struct jellytau_lib::commands::download::DownloadManagerWrapper">DownloadManagerWrapper</a>>,
|
||||||
|
app: AppHandle,
|
||||||
|
handle: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="struct" href="struct.ResumeQueuedResult.html" title="struct jellytau_lib::commands::catalog::ResumeQueuedResult">ResumeQueuedResult</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Resolve the stream URL for every download row that was queued while offline
|
||||||
|
(<code>status = 'pending' AND stream_url IS NULL</code>), then pump the queue so they
|
||||||
|
start. Call this on reconnect.</p>
|
||||||
|
<p>Audio rows resolve via <code>get_audio_stream_url</code>; video rows (media_type =
|
||||||
|
‘video’) via the pure <code>get_video_download_url</code> builder using the row’s stored
|
||||||
|
<code>quality_preset</code> — mirroring <code>enqueue_video_downloads</code>. Rows whose URL can’t
|
||||||
|
be resolved are left pending (they retry on the next reconnect).</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="One full-catalog indexing pass, shared by the `sync_full_catalog` command and the background scheduler (DR-109) so there is exactly one implementation and one concurrency guard."><title>run_index_pass in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">run_index_pass</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Function <span class="fn">run_<wbr>index_<wbr>pass</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#165-273">Source</a> </span></div><pre class="rust item-decl"><code>pub(crate) async fn run_index_pass(
|
||||||
|
repo: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/sync/struct.Arc.html" title="struct alloc::sync::Arc">Arc</a><<a class="struct" href="../../repository/hybrid/struct.HybridRepository.html" title="struct jellytau_lib::repository::hybrid::HybridRepository">HybridRepository</a>>,
|
||||||
|
db_service: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/sync/struct.Arc.html" title="struct alloc::sync::Arc">Arc</a><<a class="struct" href="../../storage/db_service/struct.RusqliteService.html" title="struct jellytau_lib::storage::db_service::RusqliteService">RusqliteService</a>>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="struct" href="struct.CatalogSyncResult.html" title="struct jellytau_lib::commands::catalog::CatalogSyncResult">CatalogSyncResult</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>One full-catalog indexing pass, shared by the <a href="fn.sync_full_catalog.html" title="fn jellytau_lib::commands::catalog::sync_full_catalog"><code>sync_full_catalog</code></a> command
|
||||||
|
and the background scheduler (DR-109) so there is exactly one implementation
|
||||||
|
and one concurrency guard.</p>
|
||||||
|
<p>TRACES: UR-065 | DR-109, DR-110</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Control whether offline library queries reveal the full synced catalog (greyed-out, non-downloaded media) or only downloaded/local media."><title>set_show_server_catalog in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">set_show_server_catalog</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Function <span class="fn">set_<wbr>show_<wbr>server_<wbr>catalog</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#462-464">Source</a> </span></div><pre class="rust item-decl"><code>pub fn set_show_server_catalog(show: <a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.bool.html">bool</a>)</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Control whether offline library queries reveal the full synced catalog
|
||||||
|
(greyed-out, non-downloaded media) or only downloaded/local media.</p>
|
||||||
|
<p>The frontend calls this from the “Show all server media” toggle: pass <code>true</code>
|
||||||
|
when online, or when offline with the toggle on; pass <code>false</code> when offline
|
||||||
|
with the toggle off so library pages show downloaded media only. Fixes the
|
||||||
|
bug where offline library pages showed every server item regardless of the
|
||||||
|
toggle.</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Start the background catalog indexer."><title>spawn_catalog_indexer in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">spawn_catalog_indexer</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Function <span class="fn">spawn_<wbr>catalog_<wbr>indexer</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#328-344">Source</a> </span></div><pre class="rust item-decl"><code>pub fn spawn_catalog_indexer(app: AppHandle)</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Start the background catalog indexer.</p>
|
||||||
|
<p>Replaces the frontend’s startup-only <code>syncCatalog()</code> call: index freshness is
|
||||||
|
sync policy and belongs in Rust (see the layer assignment in
|
||||||
|
docs/architecture/03-data-flow.md, “Search Flow”). Ticks every
|
||||||
|
<a href="constant.CATALOG_INDEX_TICK.html" title="constant jellytau_lib::commands::catalog::CATALOG_INDEX_TICK"><code>CATALOG_INDEX_TICK</code></a> and
|
||||||
|
runs a pass when a repository exists, the server is reachable, and the index
|
||||||
|
is older than <a href="constant.CATALOG_INDEX_TTL.html" title="constant jellytau_lib::commands::catalog::CATALOG_INDEX_TTL"><code>CATALOG_INDEX_TTL</code></a>.</p>
|
||||||
|
<p>TRACES: UR-065 | DR-109, IR-030</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Walk every library on the server and persist all items to the offline cache so the full catalog is browsable offline (greyed out when not downloaded)."><title>sync_full_catalog in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">sync_full_catalog</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Function <span class="fn">sync_<wbr>full_<wbr>catalog</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#145-158">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn sync_full_catalog(
|
||||||
|
repository: State<'_, <a class="struct" href="../repository/struct.RepositoryManagerWrapper.html" title="struct jellytau_lib::commands::repository::RepositoryManagerWrapper">RepositoryManagerWrapper</a>>,
|
||||||
|
db: State<'_, <a class="struct" href="../storage/struct.DatabaseWrapper.html" title="struct jellytau_lib::commands::storage::DatabaseWrapper">DatabaseWrapper</a>>,
|
||||||
|
handle: <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="struct" href="struct.CatalogSyncResult.html" title="struct jellytau_lib::commands::catalog::CatalogSyncResult">CatalogSyncResult</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Walk every library on the server and persist all items to the offline cache
|
||||||
|
so the full catalog is browsable offline (greyed out when not downloaded).</p>
|
||||||
|
<p>Best-effort: a library that fails to fetch is counted and skipped rather than
|
||||||
|
aborting the whole sync. Runs libraries sequentially to avoid hammering the
|
||||||
|
server. Uses <code>Recursive=true</code> so a single request per library returns the
|
||||||
|
containers and their playable children.</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Tauri commands for the offline “browse & queue” feature."><title>jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="../sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc mod"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">Module catalog</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h2 class="location"><a href="#">Module catalog</a></h2><h3><a href="#structs">Module Items</a></h3><ul class="block"><li><a href="#structs" title="Structs">Structs</a></li><li><a href="#constants" title="Constants">Constants</a></li><li><a href="#statics" title="Statics">Statics</a></li><li><a href="#functions" title="Functions">Functions</a></li></ul></section><div id="rustdoc-modnav"><h2><a href="../index.html">In jellytau_<wbr>lib::<wbr>commands</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a></div><h1>Module <span>catalog</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#1-1147">Source</a> </span></div><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Tauri commands for the offline “browse & queue” feature.</p>
|
||||||
|
<p>TRACES: UR-002, UR-007, UR-024 | JA-004, JA-016 | DR-012, DR-027</p>
|
||||||
|
<p>Two backend pieces support browsing the full server catalog while offline
|
||||||
|
and queueing downloads that fire on reconnect:</p>
|
||||||
|
<ul>
|
||||||
|
<li><a href="fn.sync_full_catalog.html" title="fn jellytau_lib::commands::catalog::sync_full_catalog"><code>sync_full_catalog</code></a> walks every library while online and persists all
|
||||||
|
items to the offline cache so the whole catalog is browsable (greyed out)
|
||||||
|
offline. It reuses [<code>HybridRepository::cache_items_from_server</code>], which in
|
||||||
|
turn reuses <code>OfflineRepository::save_to_cache</code> (sets <code>synced_at</code>, which is
|
||||||
|
what <code>get_items</code> branch 3 serves offline).</li>
|
||||||
|
<li><a href="fn.resume_queued_downloads.html" title="fn jellytau_lib::commands::catalog::resume_queued_downloads"><code>resume_queued_downloads</code></a> resolves and pumps the <code>pending</code> download rows
|
||||||
|
that were queued offline (they have <code>stream_url IS NULL</code>), mirroring the
|
||||||
|
heal-and-pump pattern in <code>player_preload_upcoming</code>.</li>
|
||||||
|
</ul>
|
||||||
|
</div></details><h2 id="structs" class="section-header">Structs<a href="#structs" class="anchor">§</a></h2><dl class="item-table"><dt><a class="struct" href="struct.CatalogIndexEvent.html" title="struct jellytau_lib::commands::catalog::CatalogIndexEvent">Catalog<wbr>Index<wbr>Event</a></dt><dd>Progress of a background index pass, for the staleness hint in the UI.</dd><dt><a class="struct" href="struct.CatalogSyncResult.html" title="struct jellytau_lib::commands::catalog::CatalogSyncResult">Catalog<wbr>Sync<wbr>Result</a></dt><dt><a class="struct" href="struct.CatalogSyncStatus.html" title="struct jellytau_lib::commands::catalog::CatalogSyncStatus">Catalog<wbr>Sync<wbr>Status</a></dt><dt><a class="struct" href="struct.IndexPassGuard.html" title="struct jellytau_lib::commands::catalog::IndexPassGuard">Index<wbr>Pass<wbr>Guard</a><span title="Restricted Visibility"> 🔒</span> </dt><dd>Clears <a href="static.INDEX_IN_PROGRESS.html" title="static jellytau_lib::commands::catalog::INDEX_IN_PROGRESS"><code>INDEX_IN_PROGRESS</code></a> however the pass leaves — including on the <code>?</code>
|
||||||
|
early return when <code>get_libraries</code> fails, which a plain store at the end of
|
||||||
|
the function would leak.</dd><dt><a class="struct" href="struct.ResumeQueuedResult.html" title="struct jellytau_lib::commands::catalog::ResumeQueuedResult">Resume<wbr>Queued<wbr>Result</a></dt></dl><h2 id="constants" class="section-header">Constants<a href="#constants" class="anchor">§</a></h2><dl class="item-table"><dt><a class="constant" href="constant.CATALOG_INDEX_EVENT.html" title="constant jellytau_lib::commands::catalog::CATALOG_INDEX_EVENT">CATALOG_<wbr>INDEX_<wbr>EVENT</a></dt><dd>Kebab-case, per the project’s event convention.</dd><dt><a class="constant" href="constant.CATALOG_INDEX_FIRST_CHECK.html" title="constant jellytau_lib::commands::catalog::CATALOG_INDEX_FIRST_CHECK">CATALOG_<wbr>INDEX_<wbr>FIRST_<wbr>CHECK</a><span title="Restricted Visibility"> 🔒</span> </dt><dd>Delay before the first staleness check, to let sign-in complete and the
|
||||||
|
repository be registered. Without it the first check runs against an empty
|
||||||
|
repository manager and a fresh install would sit unindexed until the next
|
||||||
|
tick.</dd><dt><a class="constant" href="constant.CATALOG_INDEX_TICK.html" title="constant jellytau_lib::commands::catalog::CATALOG_INDEX_TICK">CATALOG_<wbr>INDEX_<wbr>TICK</a><span title="Restricted Visibility"> 🔒</span> </dt><dd>How often the scheduler wakes to <em>check</em> staleness. Far shorter than the TTL
|
||||||
|
because a tick is nearly free — one indexed <code>app_settings</code> lookup — and it is
|
||||||
|
what makes the indexer responsive to events it cannot subscribe to: signing
|
||||||
|
in, and coming back online. The TTL, not the tick, decides whether a crawl
|
||||||
|
actually happens.</dd><dt><a class="constant" href="constant.CATALOG_INDEX_TTL.html" title="constant jellytau_lib::commands::catalog::CATALOG_INDEX_TTL">CATALOG_<wbr>INDEX_<wbr>TTL</a><span title="Restricted Visibility"> 🔒</span> </dt><dd>How long an index stays fresh before a re-index is due.</dd><dt><a class="constant" href="constant.CATALOG_ITEM_TYPES.html" title="constant jellytau_lib::commands::catalog::CATALOG_ITEM_TYPES">CATALOG_<wbr>ITEM_<wbr>TYPES</a><span title="Restricted Visibility"> 🔒</span> </dt><dd>Item types worth caching for offline browsing: containers the library
|
||||||
|
landing pages render plus the playable leaves users queue for download.
|
||||||
|
<code>MusicArtist</code> and <code>Playlist</code> are here because search groups results by them
|
||||||
|
(UR-060’s Artists group). Without them in the crawl, the local index can
|
||||||
|
never answer an artist query and those groups can only ever be filled by the
|
||||||
|
server leg. Keep this in step with what <code>prune_stale_catalog</code> is allowed to
|
||||||
|
sweep — the crawl is only authoritative for the types it asks for.</dd><dt><a class="constant" href="constant.LAST_CATALOG_SYNC_KEY.html" title="constant jellytau_lib::commands::catalog::LAST_CATALOG_SYNC_KEY">LAST_<wbr>CATALOG_<wbr>SYNC_<wbr>KEY</a><span title="Restricted Visibility"> 🔒</span> </dt><dd>app_settings key holding the RFC-3339 timestamp of the last successful
|
||||||
|
full-catalog sync.</dd><dt><a class="constant" href="constant.VIDEO_ITEM_TYPES.html" title="constant jellytau_lib::commands::catalog::VIDEO_ITEM_TYPES">VIDEO_<wbr>ITEM_<wbr>TYPES</a><span title="Restricted Visibility"> 🔒</span> </dt><dd>Jellyfin item types whose download is a <em>video</em> stream rather than an audio
|
||||||
|
one. The download queue stores an opaque <code>media_type</code> (‘audio’/‘video’); this
|
||||||
|
is where the taxonomy that produces it lives, so the frontend never has to
|
||||||
|
know which item types are video.</dd></dl><h2 id="statics" class="section-header">Statics<a href="#statics" class="anchor">§</a></h2><dl class="item-table"><dt><a class="static" href="static.INDEX_IN_PROGRESS.html" title="static jellytau_lib::commands::catalog::INDEX_IN_PROGRESS">INDEX_<wbr>IN_<wbr>PROGRESS</a><span title="Restricted Visibility"> 🔒</span> </dt><dd>Guards against two passes running at once. Replaces the frontend’s
|
||||||
|
<code>syncInProgress</code> boolean in <code>offlineCatalog.ts</code>, which could not see a pass
|
||||||
|
started by the scheduler.</dd></dl><h2 id="functions" class="section-header">Functions<a href="#functions" class="anchor">§</a></h2><dl class="item-table"><dt><a class="fn" href="fn.catalog_sync_status.html" title="fn jellytau_lib::commands::catalog::catalog_sync_status">catalog_<wbr>sync_<wbr>status</a></dt><dd>Report the last-synced timestamp so the UI can show a hint / decide whether
|
||||||
|
to trigger a fresh sync.</dd><dt><a class="fn" href="fn.index_is_due.html" title="fn jellytau_lib::commands::catalog::index_is_due">index_<wbr>is_<wbr>due</a><span title="Restricted Visibility"> 🔒</span> </dt><dd>Whether an index pass is due, given when one last completed.</dd><dt><a class="fn" href="fn.maybe_run_scheduled_pass.html" title="fn jellytau_lib::commands::catalog::maybe_run_scheduled_pass">maybe_<wbr>run_<wbr>scheduled_<wbr>pass</a><span title="Restricted Visibility"> 🔒</span> </dt><dd>One scheduler tick: check the preconditions, then index if due.</dd><dt><a class="fn" href="fn.read_last_sync.html" title="fn jellytau_lib::commands::catalog::read_last_sync">read_<wbr>last_<wbr>sync</a><span title="Restricted Visibility"> 🔒</span> </dt><dd>Read the last-sync timestamp straight from <code>app_settings</code>.</dd><dt><a class="fn" href="fn.requeue_mistyped_video_downloads.html" title="fn jellytau_lib::commands::catalog::requeue_mistyped_video_downloads">requeue_<wbr>mistyped_<wbr>video_<wbr>downloads</a><span title="Restricted Visibility"> 🔒</span> </dt><dd>Requeue video downloads that were fetched as audio.</dd><dt><a class="fn" href="fn.resolve_pending_download_urls.html" title="fn jellytau_lib::commands::catalog::resolve_pending_download_urls">resolve_<wbr>pending_<wbr>download_<wbr>urls</a><span title="Restricted Visibility"> 🔒</span> </dt><dd>Core of <a href="fn.resume_queued_downloads.html" title="fn jellytau_lib::commands::catalog::resume_queued_downloads"><code>resume_queued_downloads</code></a>, factored out for testing: select every
|
||||||
|
<code>pending</code>/<code>stream_url IS NULL</code> row, resolve each via <code>resolve</code> (returning
|
||||||
|
<code>None</code> leaves the row pending), and heal the row so the pump can start it.
|
||||||
|
The <code>resolve</code> closure receives <code>(item_id, media_type, quality_preset)</code>.</dd><dt><a class="fn" href="fn.resume_queued_downloads.html" title="fn jellytau_lib::commands::catalog::resume_queued_downloads">resume_<wbr>queued_<wbr>downloads</a></dt><dd>Resolve the stream URL for every download row that was queued while offline
|
||||||
|
(<code>status = 'pending' AND stream_url IS NULL</code>), then pump the queue so they
|
||||||
|
start. Call this on reconnect.</dd><dt><a class="fn" href="fn.run_index_pass.html" title="fn jellytau_lib::commands::catalog::run_index_pass">run_<wbr>index_<wbr>pass</a><span title="Restricted Visibility"> 🔒</span> </dt><dd>One full-catalog indexing pass, shared by the <a href="fn.sync_full_catalog.html" title="fn jellytau_lib::commands::catalog::sync_full_catalog"><code>sync_full_catalog</code></a> command
|
||||||
|
and the background scheduler (DR-109) so there is exactly one implementation
|
||||||
|
and one concurrency guard.</dd><dt><a class="fn" href="fn.set_show_server_catalog.html" title="fn jellytau_lib::commands::catalog::set_show_server_catalog">set_<wbr>show_<wbr>server_<wbr>catalog</a></dt><dd>Control whether offline library queries reveal the full synced catalog
|
||||||
|
(greyed-out, non-downloaded media) or only downloaded/local media.</dd><dt><a class="fn" href="fn.spawn_catalog_indexer.html" title="fn jellytau_lib::commands::catalog::spawn_catalog_indexer">spawn_<wbr>catalog_<wbr>indexer</a></dt><dd>Start the background catalog indexer.</dd><dt><a class="fn" href="fn.sync_full_catalog.html" title="fn jellytau_lib::commands::catalog::sync_full_catalog">sync_<wbr>full_<wbr>catalog</a></dt><dd>Walk every library on the server and persist all items to the offline cache
|
||||||
|
so the full catalog is browsable offline (greyed out when not downloaded).</dd></dl></section></div></main></body></html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
window.SIDEBAR_ITEMS = {"constant":["CATALOG_INDEX_EVENT","CATALOG_INDEX_FIRST_CHECK","CATALOG_INDEX_TICK","CATALOG_INDEX_TTL","CATALOG_ITEM_TYPES","LAST_CATALOG_SYNC_KEY","VIDEO_ITEM_TYPES"],"fn":["catalog_sync_status","index_is_due","maybe_run_scheduled_pass","read_last_sync","requeue_mistyped_video_downloads","resolve_pending_download_urls","resume_queued_downloads","run_index_pass","set_show_server_catalog","spawn_catalog_indexer","sync_full_catalog"],"static":["INDEX_IN_PROGRESS"],"struct":["CatalogIndexEvent","CatalogSyncResult","CatalogSyncStatus","IndexPassGuard","ResumeQueuedResult"]};
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Guards against two passes running at once. Replaces the frontend’s `syncInProgress` boolean in `offlineCatalog.ts`, which could not see a pass started by the scheduler."><title>INDEX_IN_PROGRESS in jellytau_lib::commands::catalog - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc static"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">INDEX_IN_PROGRESS</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>catalog</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">catalog</a></div><h1>Static <span class="static">INDEX_<wbr>IN_<wbr>PROGRESS</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/catalog.rs.html#62">Source</a> </span></div><pre class="rust item-decl"><code>static INDEX_IN_PROGRESS: <a class="type" href="https://doc.rust-lang.org/1.97.1/core/sync/atomic/type.AtomicBool.html" title="type core::sync::atomic::AtomicBool">AtomicBool</a></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Guards against two passes running at once. Replaces the frontend’s
|
||||||
|
<code>syncInProgress</code> boolean in <code>offlineCatalog.ts</code>, which could not see a pass
|
||||||
|
started by the scheduler.</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,4 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Check if the server is currently reachable"><title>connectivity_check_server in jellytau_lib::commands::connectivity - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">connectivity_check_server</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>connectivity</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">connectivity</a></div><h1>Function <span class="fn">connectivity_<wbr>check_<wbr>server</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/connectivity.rs.html#15-20">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn connectivity_check_server(
|
||||||
|
state: State<'_, <a class="struct" href="struct.ConnectivityMonitorWrapper.html" title="struct jellytau_lib::commands::connectivity::ConnectivityMonitorWrapper">ConnectivityMonitorWrapper</a>>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.bool.html">bool</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Check if the server is currently reachable</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Get the current connectivity status"><title>connectivity_get_status in jellytau_lib::commands::connectivity - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">connectivity_get_status</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>connectivity</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">connectivity</a></div><h1>Function <span class="fn">connectivity_<wbr>get_<wbr>status</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/connectivity.rs.html#37-42">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn connectivity_get_status(
|
||||||
|
state: State<'_, <a class="struct" href="struct.ConnectivityMonitorWrapper.html" title="struct jellytau_lib::commands::connectivity::ConnectivityMonitorWrapper">ConnectivityMonitorWrapper</a>>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="struct" href="../../connectivity/struct.ConnectivityStatus.html" title="struct jellytau_lib::connectivity::ConnectivityStatus">ConnectivityStatus</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Get the current connectivity status</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Mark the server as reachable (called after successful API calls)"><title>connectivity_mark_reachable in jellytau_lib::commands::connectivity - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">connectivity_mark_reachable</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>connectivity</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">connectivity</a></div><h1>Function <span class="fn">connectivity_<wbr>mark_<wbr>reachable</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/connectivity.rs.html#69-75">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn connectivity_mark_reachable(
|
||||||
|
state: State<'_, <a class="struct" href="struct.ConnectivityMonitorWrapper.html" title="struct jellytau_lib::commands::connectivity::ConnectivityMonitorWrapper">ConnectivityMonitorWrapper</a>>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.unit.html">()</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Mark the server as reachable (called after successful API calls)</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Mark the server as unreachable (called after failed API calls)"><title>connectivity_mark_unreachable in jellytau_lib::commands::connectivity - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">connectivity_mark_unreachable</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.11.1</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>commands::<wbr>connectivity</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">commands</a>::<wbr><a href="index.html">connectivity</a></div><h1>Function <span class="fn">connectivity_<wbr>mark_<wbr>unreachable</span> <button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/commands/connectivity.rs.html#80-87">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn connectivity_mark_unreachable(
|
||||||
|
error: <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/option/enum.Option.html" title="enum core::option::Option">Option</a><<a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>>,
|
||||||
|
state: State<'_, <a class="struct" href="struct.ConnectivityMonitorWrapper.html" title="struct jellytau_lib::commands::connectivity::ConnectivityMonitorWrapper">ConnectivityMonitorWrapper</a>>,
|
||||||
|
) -> <a class="enum" href="https://doc.rust-lang.org/1.97.1/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.unit.html">()</a>, <a class="struct" href="https://doc.rust-lang.org/1.97.1/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>></code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Mark the server as unreachable (called after failed API calls)</p>
|
||||||
|
</div></details></section></div></main></body></html>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user