Snacks v2.3.0
Automated Video Library Encoder
Minor release introducing per-folder and per-node encoding overrides, a custom modal system replacing Bootstrap modals, master node self-card with local encoding pause, pre-dispatch busy checks to prevent double-encoding on worker nodes, race condition fixes in the cluster job pipeline, and static file cache-busting.
New Features
Per-Folder Encoding Overrides
- Watched folders now support encoding overrides -- each directory in the auto-scan list can carry an optional
EncoderOptionsOverridethat overrides global encoder settings (codec, bitrate, hardware acceleration, etc.) for files in that folder. Overrides are configured via a new settings gear icon next to each watched folder in the Auto Scan tab. WatchedFoldermodel replaces plain string paths --AutoScanConfig.Directoriesis now aList<WatchedFolder>instead ofList<string>. A customWatchedFolderListConverterhandles backward-compatible deserialization of legacy string arrays, auto-promoting them toWatchedFolderobjects on load.- Longest-prefix folder matching --
FindFolderOverrideresolves the most specific watched folder for a given file path, so nested watched folders inherit the correct overrides.
Per-Node Encoding Overrides
- Node-level encoding settings -- each cluster node can have its own
EncoderOptionsOverrideand 4K dispatch routing rules (Only4K,Exclude4K). Settings are persisted tonode-settings.jsonand survive node disconnect/reconnect. - Three-tier override merge -- encoding options resolve as global -> folder -> node (most specific wins) via
EncoderOptionsOverride.ApplyOverrides. Cluster dispatch now callsResolveOptionsForJobto merge all three tiers before dispatching or retrying. - 4K dispatch routing --
ScoreNodechecks node-levelOnly4K/Exclude4Kconstraints, returning a disqualifying score for mismatched jobs. The master's own node settings also feed aSetLocalSkipPredicateon the transcoding service, so the master skips locally ineligible items and leaves them for remote dispatch.
Master Node Self-Card
- Master node visible in the cluster panel -- the cluster panel now renders a "self-card" for the local machine alongside remote worker cards. Displays role, OS, GPU vendor, local job counts (completed/failed), and a settings button.
- Local encoding pause/resume -- masters can pause local encoding while continuing to dispatch to remote workers. A new
SetLocalEncodingPausedendpoint and UI button toggle this independently of the global queue pause. - Local job counters --
TranscodingServicetracksLocalCompletedJobsandLocalFailedJobsvia atomic increments, exposed throughGetClusterStatus.
Override Dialog UI
- Shared override dialog -- a new
overrideDialogmodal (rendered above the settings modal via a higher z-index) provides a unified form for both folder and node encoding overrides. Checkboxes toggle each field independently; unchecked fields inherit from the parent tier. - Node-specific 4K dispatch rules -- the override dialog shows
Only4K/Exclude4Kcheckboxes when editing node settings, with mutual exclusivity enforced in both the frontend and backend.
Custom Modal System
snacks-modal-backdropreplaces Bootstrap modals -- the Library and Settings modals now use a custom CSS/JS modal system with.openclass toggling instead of Bootstrap'sdata-bs-toggle/data-bs-dismiss. Provides consistent dark-theme styling, proper backdrop click-to-close, Escape key handling (topmost modal closes first), and eliminates Bootstrap modal z-index and scroll-lock issues.- Animation support -- modals use
snacksBackdropInandsnacksDialogInkeyframe animations for smooth fade + slide-in transitions.
Pre-Dispatch Busy Check
- Node heartbeat check before dispatch -- before uploading a file to a worker, the master now queries the node's heartbeat endpoint to verify it's actually idle. If the node reports busy (e.g., still retrying a failed encode the master already re-queued), the job is re-queued instead of dispatched. Prevents double-encoding that overwhelms hardware encoders.
Hardware Detection Broadcast
HardwareDetectedSignalR event -- after hardware acceleration detection completes on startup, aHardwareDetectedevent is broadcast to all connected clients. The frontend reloads workers and re-renders the cluster panel so GPU capabilities appear without a manual refresh.
Bug Fixes
Race Conditions
- Job start lock prevents concurrent encoding --
ClusterNodeJobService.StartEncodingAsyncnow acquires a_jobStartLockaround the_currentRemoteJobnull-check and slot claim. Previously, two near-simultaneousReceiveFilecompletions could both pass the guard and start encoding concurrently, overwhelming the hardware encoder. - Validation failures release the job slot -- all early-return paths in
StartEncodingAsync(file not found, size mismatch, corrupt header, existing output) now clear_currentRemoteJobbefore returning, preventing the node from appearing permanently busy after a validation failure. - Failure cleanup before master notification -- on encoding failure, the worker now clears
_currentRemoteJob, disposes the CTS, and unregisters callbacks before reporting failure to the master. Previously, the master would immediately dispatch new work that arrived while the old state was still set.
Idle Grace Period
- Idle heartbeat threshold raised from 3 to 10 -- the idle grace counter before re-queuing a job on an apparently-idle node was increased from 3 to 10 heartbeats, reducing false re-queues during encoding startup or brief idle gaps between job phases.
Caching
- Static file cache-busting --
Program.csnow configuresUseStaticFileswithCache-Control: no-cache, no-store, must-revalidateheaders, ensuring browsers always fetch the latest JS/CSS after deployments. - Electron cache clearing on startup -- the Electron app now calls
session.defaultSession.clearCache()and clearscachestorage/serviceworkersstorage onready, so the desktop wrapper always loads fresh frontend assets.
Layout
- Responsive main content margins -- the hardcoded
style="margin-top: 80px"on the main container is replaced with a.main-contentCSS class using64pxon mobile and76pxon desktop via a media query.
UI Improvements
Styling
- Enhanced tab styling -- nav tabs get smooth transition effects, hover/focus highlights, and an active state with an inset bottom border in the primary color.
- Form focus effects -- form controls and selects gain a primary-colored border, glow shadow, and subtle upward translate on focus.
- Button hover effects -- all buttons lift with
translateY(-2px)and gain a medium shadow on hover, pressing back down on:active. - Encoder panel hover -- encoder settings panels highlight with the primary border color and glow on hover.
- Active tab background fix -- active nav tab background now uses
--bg-surfaceconsistently instead of--bg-card.
Modals
- Log modal centered -- the log/detail modal dialog now uses
modal-dialog-centeredfor vertical centering. - Folder override indicator -- watched folders with custom encoding overrides show a sliders icon next to the folder name in the auto-scan directory list.
Files Changed
New Files
Snacks/Models/EncoderOptionsOverride.cs-- nullable overlay model for encoding settings with three-tier merge logicSnacks/Models/NodeSettings.cs-- per-node settings model andNodeSettingsConfigcontainerSnacks/Models/WatchedFolder.cs-- watched folder model with encoding overrides and backward-compatible JSON converter
Modified Files
Snacks/Controllers/HomeController.cs-- version bump,SetLocalEncodingPausedendpoint,GetNodeSettings/SaveNodeSettings/DeleteNodeSettingsendpoints,SaveFolderSettingsendpoint, expandedGetClusterStatusresponse with self capabilities and local job countersSnacks/Models/AutoScanConfig.cs--Directorieschanged fromList<string>toList<WatchedFolder>withWatchedFolderListConverterSnacks/Program.cs-- static file cache-busting headersSnacks/Services/AutoScanService.cs-- folder override resolver,SaveFolderSettings,FindFolderOverridewith longest-prefix matching, per-folder options applied during scanSnacks/Services/ClusterNodeJobService.cs--_jobStartLockfor concurrent job guard, validation failure slot cleanup, failure cleanup before master notificationSnacks/Services/ClusterService.cs--NodeSettingsConfigpersistence,SetLocalEncodingEnabled,FolderOverrideResolverdelegate, three-tier option resolution in dispatch, pre-dispatch busy check,ScoreNode4K routing constraints,UpdateLocalSkipPredicate, idle grace threshold raised to 10Snacks/Services/TranscodingService.cs--_shouldSkipLocalpredicate for local queue filtering,LocalCompletedJobs/LocalFailedJobscounters,HardwareDetectedSignalR broadcast,SetLocalSkipPredicateSnacks/Views/Home/Index.cshtml-- custom modal markup for library/settings, override dialog HTML with per-field toggle checkboxesSnacks/Views/Shared/_Layout.cshtml-- version bump, custom modal open buttons,.main-contentclassSnacks/wwwroot/css/site.css--.main-contentresponsive margins, enhanced tab/form/button/encoder-panel styling, fullsnacks-modal-*custom modal CSS with animationsSnacks/wwwroot/js/transcoding.js-- custom modal open/close logic, Escape key handling, backdrop click-to-close,HardwareDetectedhandler, master self-card rendering, local encoding pause button, node/folder settings dialogs, override form helpers, folder override indicatorelectron-app/main.js-- session cache clearing on startupelectron-app/package.json-- version bumpelectron-app/package-lock.json-- version bumprun-electron-dev.bat-- clearsbin/Releaseandobj/Releasebefore publishREADME.md-- version bump
Full documentation: README.md