github derekshreds/Snacks v2.3.0
Snacks v2.3.0

10 hours ago

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 EncoderOptionsOverride that 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.
  • WatchedFolder model replaces plain string paths -- AutoScanConfig.Directories is now a List<WatchedFolder> instead of List<string>. A custom WatchedFolderListConverter handles backward-compatible deserialization of legacy string arrays, auto-promoting them to WatchedFolder objects on load.
  • Longest-prefix folder matching -- FindFolderOverride resolves 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 EncoderOptionsOverride and 4K dispatch routing rules (Only4K, Exclude4K). Settings are persisted to node-settings.json and survive node disconnect/reconnect.
  • Three-tier override merge -- encoding options resolve as global -> folder -> node (most specific wins) via EncoderOptionsOverride.ApplyOverrides. Cluster dispatch now calls ResolveOptionsForJob to merge all three tiers before dispatching or retrying.
  • 4K dispatch routing -- ScoreNode checks node-level Only4K/Exclude4K constraints, returning a disqualifying score for mismatched jobs. The master's own node settings also feed a SetLocalSkipPredicate on 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 SetLocalEncodingPaused endpoint and UI button toggle this independently of the global queue pause.
  • Local job counters -- TranscodingService tracks LocalCompletedJobs and LocalFailedJobs via atomic increments, exposed through GetClusterStatus.

Override Dialog UI

  • Shared override dialog -- a new overrideDialog modal (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 / Exclude4K checkboxes when editing node settings, with mutual exclusivity enforced in both the frontend and backend.

Custom Modal System

  • snacks-modal-backdrop replaces Bootstrap modals -- the Library and Settings modals now use a custom CSS/JS modal system with .open class toggling instead of Bootstrap's data-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 snacksBackdropIn and snacksDialogIn keyframe 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

  • HardwareDetected SignalR event -- after hardware acceleration detection completes on startup, a HardwareDetected event 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.StartEncodingAsync now acquires a _jobStartLock around the _currentRemoteJob null-check and slot claim. Previously, two near-simultaneous ReceiveFile completions 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 _currentRemoteJob before 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.cs now configures UseStaticFiles with Cache-Control: no-cache, no-store, must-revalidate headers, ensuring browsers always fetch the latest JS/CSS after deployments.
  • Electron cache clearing on startup -- the Electron app now calls session.defaultSession.clearCache() and clears cachestorage/serviceworkers storage on ready, 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-content CSS class using 64px on mobile and 76px on 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-surface consistently instead of --bg-card.

Modals

  • Log modal centered -- the log/detail modal dialog now uses modal-dialog-centered for 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 logic
  • Snacks/Models/NodeSettings.cs -- per-node settings model and NodeSettingsConfig container
  • Snacks/Models/WatchedFolder.cs -- watched folder model with encoding overrides and backward-compatible JSON converter

Modified Files

  • Snacks/Controllers/HomeController.cs -- version bump, SetLocalEncodingPaused endpoint, GetNodeSettings/SaveNodeSettings/DeleteNodeSettings endpoints, SaveFolderSettings endpoint, expanded GetClusterStatus response with self capabilities and local job counters
  • Snacks/Models/AutoScanConfig.cs -- Directories changed from List<string> to List<WatchedFolder> with WatchedFolderListConverter
  • Snacks/Program.cs -- static file cache-busting headers
  • Snacks/Services/AutoScanService.cs -- folder override resolver, SaveFolderSettings, FindFolderOverride with longest-prefix matching, per-folder options applied during scan
  • Snacks/Services/ClusterNodeJobService.cs -- _jobStartLock for concurrent job guard, validation failure slot cleanup, failure cleanup before master notification
  • Snacks/Services/ClusterService.cs -- NodeSettingsConfig persistence, SetLocalEncodingEnabled, FolderOverrideResolver delegate, three-tier option resolution in dispatch, pre-dispatch busy check, ScoreNode 4K routing constraints, UpdateLocalSkipPredicate, idle grace threshold raised to 10
  • Snacks/Services/TranscodingService.cs -- _shouldSkipLocal predicate for local queue filtering, LocalCompletedJobs/LocalFailedJobs counters, HardwareDetected SignalR broadcast, SetLocalSkipPredicate
  • Snacks/Views/Home/Index.cshtml -- custom modal markup for library/settings, override dialog HTML with per-field toggle checkboxes
  • Snacks/Views/Shared/_Layout.cshtml -- version bump, custom modal open buttons, .main-content class
  • Snacks/wwwroot/css/site.css -- .main-content responsive margins, enhanced tab/form/button/encoder-panel styling, full snacks-modal-* custom modal CSS with animations
  • Snacks/wwwroot/js/transcoding.js -- custom modal open/close logic, Escape key handling, backdrop click-to-close, HardwareDetected handler, master self-card rendering, local encoding pause button, node/folder settings dialogs, override form helpers, folder override indicator
  • electron-app/main.js -- session cache clearing on startup
  • electron-app/package.json -- version bump
  • electron-app/package-lock.json -- version bump
  • run-electron-dev.bat -- clears bin/Release and obj/Release before publish
  • README.md -- version bump

Full documentation: README.md

Don't miss a new Snacks release

NewReleases is sending notifications on new releases.