Snacks v2.3.1
Automated Video Library Encoder
Patch release focused on cluster reliability: AMD VAAPI calibration fixes, ASCII-safe cluster auth and filename headers, smarter VAAPI bitrate calibration with oscillation guard, and versioned Docker image tags.
Bug Fixes
AMD VAAPI Calibration
- Skip Intel-only Low Power mode on AMD -- VAAPI calibration previously tried
-low_power 1on every GPU. That entrypoint (VAEntrypointEncSliceLP) is Intel-only; AMD cards only implementVAEntrypointEncSlice, so the LP pass always failed before falling back. Calibration now detects the vendor and only attempts LP mode on Intel. - Drop redundant
hwuploadon hardware-decoded frames -- when the input is already being hardware-decoded, the frames live on the GPU. The encode pipeline was still appendingformat=nv12|vaapi,hwupload, which fails on AMD drivers because you can't upload a surface that's already a surface.hwuploadis now only inserted for software-decode -> hardware-encode paths. - Removed Intel-only
-low_power 1from the VAAPI encoder self-test -- the one-frame encoder capability probe no longer hard-codes an Intel flag, so AMD cards pass the test correctly.
Cluster Auth Header Encoding
- Shared secrets are now Base64-encoded on the wire -- HTTP headers must be ASCII, but
ClusterConfig.SharedSecretcan contain any Unicode character. Sending a non-ASCII secret raw produced anHttpRequestExceptionbefore the request ever left the client. Outbound cluster requests now Base64(UTF-8) the secret; the server decodes before the constant-time compare. Invalid Base64 returns401 Unauthorizedinstead of throwing. - Applied everywhere secrets are sent --
ClusterDiscoveryService,ClusterNodeJobService, andClusterFileTransferServiceall route through the sameEncodeSecretForHeaderhelper.
Filename Header Encoding
- Original filenames are URL-escaped in transit --
X-Original-FileNamewas being set from rawworkItem.FileName, which rejects non-ASCII characters at the HTTP layer. The sender nowUri.EscapeDataStrings the filename; the receiver unescapes it (falling back to the raw value on malformed input) before resolving the temp path.
VAAPI Bitrate Calibration Oscillation
- Bisection fallback when the QP predictor oscillates -- the log-based QP predictor assumes a constant ~0.72x bitrate change per +2 QP. Real content often breaks that assumption and the loop would bounce between two QPs that straddle the target, burning every iteration without converging. Calibration now records
QP -> peak kbpsfor every pass and, if the predictor picks a QP that's already been tested, bisects between the closest under- and over-target bracket. When adjacent QPs are exhausted, it stops early. - Best-observed QP is selected at the end -- instead of returning whatever QP the last iteration happened to land on (which may have been an untested guess), the final choice is now the lowest QP whose peak fits within the upper tolerance bound, falling back to the QP whose peak was closest to target.
Build / Packaging
- Docker images are now pushed with both
:latestand:{version}tags --build-and-export.battags and pushesderekshreds/snacks-dockerandderekshreds/snackswebwith both the movinglatesttag and a pinned version tag, so deployments can lock to a specific release.
Known Issues
- Sporadic progress updates on some items -- the bundled Jellyfin build of FFmpeg occasionally reports incorrect timestamps or omits frame counts entirely while encoding. When that happens, the progress bar for the affected item may appear to stall or update in jumps even though the encode is proceeding normally. This is an upstream FFmpeg behavior and does not affect output quality or completion.
Files Changed
Snacks/Controllers/ClusterController.cs-- unescapeX-Original-FileNameon receiveSnacks/Controllers/HomeController.cs-- version bump to 2.3.1Snacks/Services/ClusterAuthMiddleware.cs--EncodeSecretForHeaderhelper and Base64 decode of incomingX-Snacks-SecretSnacks/Services/ClusterDiscoveryService.cs-- Base64-encoded secret header, protocol version bump to 2.3.1Snacks/Services/ClusterFileTransferService.cs-- URL-escapeX-Original-FileName, Base64-encoded secret header, simplified authenticated-client reuseSnacks/Services/ClusterNodeJobService.cs-- Base64-encoded secret headerSnacks/Services/TranscodingService.cs-- AMD VAAPI LP skip, conditionalhwupload, oscillation-guarded QP calibration with best-observed selectionSnacks/Views/Shared/_Layout.cshtml-- version bumpREADME.md-- version bumpbuild-and-export.bat-- dual:latest+:2.3.1Docker tag pushelectron-app/package.json/package-lock.json-- version bump
Full documentation: README.md