Cargo.toml
Rama main · réplica de lectura del repositorio original.
[workspace]
resolver = "3"
members = [
"crates/ferrofin-util",
"crates/ferrofin-keyframes",
"crates/ferrofin-model",
"crates/ferrofin-common",
"crates/ferrofin-naming",
"crates/ferrofin-networking",
"crates/ferrofin-health",
"crates/ferrofin-db",
"crates/ferrofin-traits",
"crates/ferrofin-mediaencoding",
"crates/ferrofin-hls",
"crates/ferrofin-drawing",
"crates/ferrofin-providers",
"crates/ferrofin-livetv",
"crates/ferrofin-chromaprint",
"crates/ferrofin-core",
"crates/ferrofin-extensions",
"crates/ferrofin-wasm",
"crates/ferrofin-metrics",
"crates/ferrofin-api",
"apps/ferrofin-server",
# crates are added here as each PortJob's SCAFFOLD stage lands them.
# Dependency-ordered (bottom-up port waves):
# Wave 0: ferrofin-util, ferrofin-keyframes
# Wave 1: ferrofin-model
# Wave 2: ferrofin-common, ferrofin-naming, ferrofin-networking, ferrofin-health
# Wave 3: ferrofin-db Wave 4: ferrofin-traits
# Wave 5: ferrofin-mediaencoding, ferrofin-hls, ferrofin-drawing, ferrofin-providers, ferrofin-livetv(stub)
# Wave 6: ferrofin-core Wave 7: ferrofin-api Wave 8: apps/ferrofin-server
]
exclude = ["vendor/icu_collator"]
[workspace.package]
version = "0.4.1"
edition = "2024"
license = "GPL-3.0-only" # matches the GPL-3.0-only package metadata on the Jellyfin crates being ported
publish = false
repository = "https://github.com/mangoleaf/ferrofin"
[workspace.lints.rust]
missing_docs = "warn"
# `coverage_nightly` is set by cargo-llvm-cov (nightly) to gate the
# `#[coverage(off)]` carve-outs on un-mockable I/O modules (e.g. the real
# ffmpeg spawn in ferrofin-mediaencoding). Register it so stable builds don't warn.
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
[workspace.lints.clippy]
pedantic = { level = "warn", priority = -1 }
# product names in docs (SQLite, ffmpeg, DLNA, …) trip doc_markdown — allow it
doc_markdown = "allow"
# modules with children are self-named `foo.rs` beside `foo/`, never `foo/mod.rs`
# (integration-test helpers at `tests/<name>/mod.rs` are not flagged)
mod_module_files = "warn"
# nursery allocation lints (gl-1017): a clone whose original is never used again, a
# collect only iterated, counted or searched once, an eagerly built `or`/`unwrap_or` default
redundant_clone = "warn"
needless_collect = "warn"
or_fun_call = "warn"
[workspace.dependencies]
if-addrs = { version = "0.15.0", features = ["link-local"] }
# ── intra-workspace (path deps added as crates land) ───────────────
ferrofin-util = { path = "crates/ferrofin-util" }
ferrofin-keyframes = { path = "crates/ferrofin-keyframes" }
ferrofin-model = { path = "crates/ferrofin-model" }
ferrofin-common = { path = "crates/ferrofin-common" }
ferrofin-naming = { path = "crates/ferrofin-naming" }
ferrofin-networking = { path = "crates/ferrofin-networking" }
ferrofin-health = { path = "crates/ferrofin-health" }
ferrofin-db = { path = "crates/ferrofin-db" }
ferrofin-traits = { path = "crates/ferrofin-traits" }
ferrofin-mediaencoding = { path = "crates/ferrofin-mediaencoding" }
ferrofin-hls = { path = "crates/ferrofin-hls" }
ferrofin-drawing = { path = "crates/ferrofin-drawing" }
ferrofin-providers = { path = "crates/ferrofin-providers" }
ferrofin-livetv = { path = "crates/ferrofin-livetv" }
ferrofin-chromaprint = { path = "crates/ferrofin-chromaprint" }
ferrofin-core = { path = "crates/ferrofin-core" }
ferrofin-extensions = { path = "crates/ferrofin-extensions" }
ferrofin-wasm = { path = "crates/ferrofin-wasm" }
ferrofin-metrics = { path = "crates/ferrofin-metrics" }
ferrofin-api = { path = "crates/ferrofin-api" }
# ── http / async ───────────────────────────────────────────────────
axum = "0.8"
tower = "0.5"
tower-http = { version = "0.7.1", features = ["fs", "trace", "cors", "compression-gzip", "compression-br"] }
httpdate = "1.0.3"
# Decoders for the response-compression tests only (the server never decodes a
# response it just encoded). Streaming readers validate the wire response.
flate2 = "1.1.10"
zip = { version = "9", default-features = false, features = ["deflate"] } # .cbz/.epub archives (ferrofin-providers books); backup archives (ferrofin-api, which adds `time`)
tar = { version = "0.4.46", default-features = false } # .cbt comic covers (ferrofin-providers books); read-only, so no `xattr`
sevenz-rust2 = { version = "0.24.1", default-features = false, features = ["bzip2", "deflate", "ppmd"] } # .cb7 comic covers (ferrofin-providers books)
brotli = "9.0.0"
tokio = { version = "1", features = ["full"] }
socket2 = "0.6"
http-body = "1.0"
# The `Stream` trait `axum::body::Body::from_stream` takes — the progressive
# (tail-following) response bodies the Live TV live-stream/recording routes serve.
futures-core = "0.3.34"
# WebSocket client for the /socket integration tests only. Its version is
# independent of the WebSocket implementation used internally by axum.
tokio-tungstenite = "0.30.0"
futures-util = "0.3.34"
notify = "8.2"
# ── persistence: sqlx + SQLite (speed-first; see plan) ─────────────
# SQLite has no network transport, so no TLS; `macros` stays only because it carries `migrate!`.
sqlx = { version = "0.9.0", default-features = false, features = ["sqlite", "runtime-tokio", "macros", "migrate", "chrono", "uuid"] }
# Direct handle on the SQLite C API, pinned to the exact version sqlx resolves so
# cargo unifies them to ONE linked copy — a second copy would mean a second,
# unconfigured SQLite. Used only to call sqlite3_config() before any connection.
libsqlite3-sys = "0.37.0"
# ── serde / errors ─────────────────────────────────────────────────
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_urlencoded = "0.7"
# A map that keeps its insertion order on the wire: BaseItemDto.Trickplay lists
# its media sources in GetMediaSources order (ferrofin-model).
indexmap = { version = "2.14", features = ["serde"] }
# Edits config.toml in place, keeping its comments and layout: the settings
# page's save (ferrofin-server `settings::save`).
toml_edit = { version = "0.25.17", default-features = false, features = ["parse", "display"] }
# The `$.Field[0]` path in a body-binder rejection: serde's own error names the
# type, not the member that failed, and ASP.NET's ValidationProblemDetails keys
# its `errors` dictionary by path. ferrofin-api's JsonBody extractor only.
serde_path_to_error = "0.1"
form_urlencoded = "1.2.2"
secrecy = { version = "0.10", features = ["serde"] }
thiserror = "2.0.21"
anyhow = "1"
# ── allocator (server-only: glibc malloc convoyed under 64 threads) ─
# `background_threads` is load-bearing for memory, not speed: without it jemalloc
# only purges dirty pages while it is *being called*, so an idle server never
# returns anything and peak RSS becomes a permanent ratchet. See the comment on
# the `#[global_allocator]` in `apps/ferrofin-server/src/main.rs`.
tikv-jemallocator = { version = "0.7", features = ["unprefixed_malloc_on_supported_platforms", "background_threads"] }
# ── observability / net ────────────────────────────────────────────
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
tracing-appender = "0.2"
reqwest = { version = "0.13.5", default-features = false, features = ["json", "rustls", "stream", "query"] }
# The HTTPS listener (server only): rustls with the aws-lc-rs provider reqwest
# already builds, and Jellyfin's PKCS#12 (`.pfx`) certificates, read with both
# the modern (PBES2) and the legacy (PBES1, `pbes1`) encryptions.
rustls = { version = "0.23.46", default-features = false, features = ["aws_lc_rs", "logging", "std", "tls12"] }
tokio-rustls = { version = "0.26.6", default-features = false, features = ["aws_lc_rs", "logging", "tls12"] }
p12-keystore = "0.4.1"
# The LDAP-Auth extension's directory client: LDAP, LDAPS and StartTLS over the
# same rustls + aws-lc-rs stack (no default features, so neither native-tls nor
# ring comes in).
ldap3 = { version = "0.12.1", default-features = false, features = ["tls-rustls-aws-lc-rs"] }
# The system's trusted roots for an LDAP-Auth TLS configuration Ferrofin builds
# itself (a client certificate, or the default checks): what ldap3 reads too.
rustls-native-certs = "0.8.5"
# A certificate's validity period, for LDAP-Auth's check of a server certificate
# given as its own root CA (already built for ldap3 and p12-keystore).
x509-parser = "0.18.1"
# Self-signed certificates for the HTTPS tests (dev-dependency only).
rcgen = { version = "0.14.10", default-features = false, features = ["aws_lc_rs", "pem"] }
# ── openapi ────────────────────────────────────────────────────────
utoipa = "6.0.0"
# ── metrics + traces: OpenTelemetry ────────────────────────────────
# metrics → prometheus exposition (ferrofin-metrics only); traces → OTLP gRPC
# (ferrofin-server only). The 0.34 tracing-opentelemetry pairs with otel 0.33
# (the expected +1 offset); bump both together if otel ever moves.
opentelemetry = { version = "0.33.0", default-features = false, features = ["metrics", "trace"] }
opentelemetry_sdk = { version = "0.33.0", default-features = false, features = ["metrics", "trace"] }
opentelemetry-prometheus = "0.33.0"
# TLS for an https collector: aws-lc as everywhere else, roots from the system store.
opentelemetry-otlp = { version = "0.33.0", default-features = false, features = ["grpc-tonic", "trace", "tls-aws-lc", "tls-roots"] }
tracing-opentelemetry = "0.34.0"
# encode-only; ferrofin-metrics ONLY — the 0.32 exporter exposes no render helper,
# so `Registry` + `TextEncoder` come from a direct dep (dedupes with the exporter's ^0.14).
prometheus = { version = "0.14", default-features = false }
# ── WASM plugin host (Tier 1b — see docs/EXTENSIONS.md) ──
# Component-model runtime for sandboxed third-party plugins. Always compiled
# in (decision 2026-08-07); MSRV 1.94 ≤ our pinned toolchain.
# No default features: a guest proposal is on whenever its crate feature is,
# so the list is the sandbox's grant (`threads`, `gc` with exceptions,
# `component-model-async` and `stack-switching` stay off, and so do the
# profiling, cache, coredump and pooling extras). Only WASIp2 is linked.
wasmtime = { version = "49.0.5", default-features = false, features = [
"addr2line",
"anyhow",
"backtrace",
"component-model",
"cranelift",
"demangle",
"parallel-compilation",
"runtime",
"std",
] }
# Locked-down empty WASI ctx (no preopens/sockets) so std guests link.
wasmtime-wasi = { version = "49.0.5", default-features = false, features = ["p2"] }
wat = "1.259.0" # dev-only in practice: inline-WAT test fixtures (no committed .wasm)
# ── domain helpers ─────────────────────────────────────────────────
uuid = { version = "1.26.1", features = ["v4", "serde"] }
url = "2.5.8"
chrono = { version = "0.4", features = ["serde"] }
unicode-normalization = "0.1.25"
rand = "0.10.3" # ShuffleExtensions (ferrofin-util `shuffle`), password salts (ferrofin-common)
# Legacy text encodings (subtitles, playlist files) — C# `UtfUnknown`.
encoding_rs = "0.8.35"
chardetng = "0.1.17"
shlex = "2" # ffmpeg command strings → argv (ferrofin-mediaencoding `split_args`)
async-trait = "0.1.92"
libc = "0.2"
windows-sys = { version = "0.61.2", features = [
"Win32_Foundation",
"Win32_NetworkManagement_IpHelper",
"Win32_NetworkManagement_Ndis",
"Win32_Networking_WinSock",
"Win32_System_Threading",
] }
pbkdf2 = "0.13.0" # password hashing — matches Jellyfin's Rfc2898DeriveBytes.Pbkdf2
sha2 = "0.11.0" # PBKDF2-HMAC-SHA512 (default hash id)
sha1 = "0.11.0" # PBKDF2-HMAC-SHA1 (legacy "PBKDF2" hash id)
image = { version = "0.25", default-features = false, features = ["jpeg", "png", "webp", "gif", "bmp", "ico"] }
kamadak-exif = "0.6.2" # photo EXIF (ferrofin-drawing PhotoProvider)
webp = { version = "0.3", default-features = false } # LOSSY WebP encode via libwebp (BSD-3) — image-webp is lossless-only; Skia parity needs Google's encoder
jpeg-encoder = "0.7" # JPEG encode with 4:2:0 chroma subsampling — the `image` crate's encoder is hardcoded 4:4:4; Skia's SkJpegEncoder defaults to 4:2:0
jpeg-decoder = { version = "0.3.2", default-features = false } # DCT-scaled JPEG decode (1/2, 1/4, 1/8) for blur hashes and thumbnails, like Skia's GetScaledDimensions; zune-jpeg (the `image` crate's decoder) cannot scale. In maintenance mode upstream. No rayon: without it a decode wider than 128 px runs one short-lived thread per colour component, inside the encode permits that bound concurrent decodes to the cores (a failed spawn falls back to the full decode)
blurhash = "0.2" # ImageBlurHashes — functionally-valid hashes (not byte-identical to Jellyfin's Skia)
config = { version = "0.15.26", default-features = false, features = ["toml"] } # config.toml is TOML only
clap = { version = "4.6.7", features = ["derive"] }
clap_mangen = "0.3.3" # renders the .deb man page from the clap definition (test-maintained)
bitflags = "2.13.2"
# CRC-32 (reflected zlib polynomial) for the HDHomeRun control protocol's packet
# checksum — a port of `MediaBrowser.Common/Crc32.cs`, whose own tests pin
# 0x414fa339 for "The quick brown fox...". Already in the lock file as a
# transitive dependency of flate2/zip, so it adds no new compilation. The WASM
# host also uses it to tell whether a plugin's state file still holds the bytes
# its kept map came from.
crc32fast = "1.5.2"
regex = "1"
percent-encoding = "2.3.2"
# Base64 with .NET's rules (`Base64.IsValid`, `Convert.FromBase64String`) for
# LDAP-Auth's profile images. Already in the lock file as a transitive dependency.
base64 = "0.23.1"
# CLDR root collation — the port of .NET `StringComparison.InvariantCulture`
# used by the Live TV name sort. ICU4X 2.x is already in the tree (icu_normalizer
# 2.2 via idna), so this shares icu_collections/icu_provider/zerovec.
icu_collator = "2.3.1"
icu_casemap = "2.3.0"
icu_locale_core = "2.3.0"
icu_collections = "2.3.0"
icu_locale_fallback = "2.3.0"
icu_normalizer = "2.3.0"
icu_provider = "2.3.1"
zerovec = "0.11.8"
fancy-regex = "0.19.2" # for C# regexes that use lookaround
quick-xml = { version = "0.42.0", features = ["serialize"] } # NFO/XBMC metadata parsing (ferrofin-providers)
# Embedded lyric tags read from the file (ID3v2 SYLT, frame order), which
# ffprobe's tag map cannot give — the scan's port of ATL's `Track.Lyrics`.
lofty = "0.25.4"
# ── dev / test ─────────────────────────────────────────────────────
rstest = "0.27.0" # [Theory]/[InlineData] → #[case] transliteration
tempfile = "3" # temp SQLite files for ferrofin-db tests
# ── build profiles ─────────────────────────────────────────────────
# Release-speed binary with full debug info, for samply/perf/heaptrack
# symbolization (PLAN_OPTIMIZE_HOTSPOTS phase 0). Never shipped: release
# stays lean; this exists so profiling is one flag, not a Cargo.toml edit.
[profile.profiling]
inherits = "release"
debug = true
strip = false
# Exact ICU4X2.3.1 with a reviewed Jamo dispatch correction for search data.
[patch.crates-io]
icu_collator = { path = "vendor/icu_collator" }