Stage 5's first half. Sibling of `qbz-plex` and deliberately shaped like it, so `qbz-source`'s JellyfinSource will read like PlexSource: frontend-agnostic, HTTP + DTOs, no cache schema and no settings store. Three measured facts shape the file, and each one is a comment because each one is a decision somebody could otherwise undo: - `?static=true` is bit-perfect. Verified by md5 against the file on the server's own disk, not inferred from a content type. `is_direct_ response` encodes the tell a transcode leaves — chunked with no Content-Length — so a server-side policy silently resampling a hi-res track is caught rather than played. - Quality costs 45.8 s per 4924 tracks and there is no cheaper path. `Fields=MediaStreams` trims 29% of the bytes and saves nothing: the cost is server-side media-info hydration. The scan will own that knowingly, which is why `track_count` exists to give a progress denominator. - Cover urls need no credentials, so unlike a Plex thumb they are STABLE and their cache key can be the url itself. `stream_url` / `image_url` are free functions rather than methods. Two reasons and both are load-bearing: `Source::artwork_token` may not touch the network by contract, and a `reqwest::Client` cannot even be CONSTRUCTED without a rustls provider — which a unit test does not have. The mapper picks the audio stream BY TYPE, not by position: an embedded cover rides in the same MediaStreams array as an `mjpeg` with BitDepth 8, and taking [0] would badge a 24-bit FLAC as 8-bit. A lossy row keeps an absent bit depth rather than defaulting to 16, which would badge every MP3 as CD quality. 10 unit tests, plus 5 LIVE tests that skip unless QBZ_JELLYFIN_URL names a server. The unit tests prove the JSON shape is understood; only the live ones can prove the endpoint still exists, the token still opens it, and the stream is still direct. Against Jellyfin 10.11.11: 5/5 — 500-row page with 221 real bit depths of 4924 total, a Range request answered 206 with a `fLaC` bitstream, a tokenless cover, and a future-dated delta returning zero. cc
25 lines
1000 B
TOML
25 lines
1000 B
TOML
[package]
|
|
name = "qbz-jellyfin"
|
|
version.workspace = true
|
|
edition.workspace = true
|
|
license.workspace = true
|
|
description = "Jellyfin integration for QBZ: HTTP client, JSON metadata, bit-perfect stream URLs (frontend-agnostic)"
|
|
|
|
[dependencies]
|
|
# HTTP client for the Jellyfin server API.
|
|
reqwest = { workspace = true, features = ["json"] }
|
|
|
|
# Jellyfin speaks JSON end to end (Plex speaks XML — that is the whole
|
|
# difference between this crate's parsing and `qbz-plex`'s).
|
|
serde = { workspace = true }
|
|
serde_json = { workspace = true }
|
|
|
|
[dev-dependencies]
|
|
tokio = { workspace = true }
|
|
# reqwest is built with `rustls-tls-webpki-roots-no-provider`, so a
|
|
# `reqwest::Client` cannot be CONSTRUCTED until a process-level CryptoProvider
|
|
# is installed. The app does that in `qbz_app::install_crypto_provider`; a test
|
|
# binary has no app, so the live harness installs it itself (same shape as
|
|
# `qbz-offline-cache/tests/purchase_download_harness.rs`).
|
|
rustls = { version = "0.23", features = ["aws_lc_rs"] }
|