Skip to content

Fix QuickFilters not loading from disk after restart (#1979) - #1980

Merged
rmcrackan merged 3 commits into
masterfrom
cursor/fix-quickfilters-not-loaded-c279
Aug 19, 2026
Merged

Fix QuickFilters not loading from disk after restart (#1979)#1980
rmcrackan merged 3 commits into
masterfrom
cursor/fix-quickfilters-not-loaded-c279

Conversation

@rmcrackan

@rmcrackan rmcrackan commented Aug 19, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #1979: quick filters saved to QuickFilters.json were no longer available after restarting the app, and adding a new filter after a restart overwrote the file.

Auditing the commit that caused it turned up a second, latent regression from the same migration cleanup, also fixed here (see below).

Root cause

Commit 065118c (first shipped in v13.7.6) deleted Migrations.migrate_to_v11_5_0 as a dead migration. Despite its name, that method ran on every startup from RunPostConfigMigrations and was the only code that read QuickFilters.json into QuickFilters.InMemoryState. With it gone, InMemoryState stayed null:

  • QuickFilters.Filters returned an empty enumerable, so the Quick Filters menu was empty after restart.
  • Add did InMemoryState ??= new() and then saved, overwriting the file with only the new filter.
  • Nothing was logged because every code path silently tolerated the null state.

Fix

  • QuickFilters now lazily loads its state from QuickFilters.json on first access (guarded by the existing lock), so it no longer depends on a startup hook that looks like removable migration code.
  • The pre-v11.5.0 format fallback (filters as plain strings without names) that the deleted migration provided is restored, so users upgrading straight from old versions keep their filters.
  • A missing or malformed file yields an empty state without throwing (malformed files log an error).
  • Side-effect fix: the "first filter is default" toggle can now be persisted even before any filter has been added in the session (the UseDefault setter previously no-oped while InMemoryState was null).
  • The QuickFilters locker is now a System.Threading.Lock, matching PersistentDictionary and LibationAvalonia.Program.

Second regression from the same cleanup: MaxSampleRate clamp

Commit 065118c also deleted migrate_to_v11_6_5, which clamped MaxSampleRate into LAME's supported [Hz_8000, Hz_48000] range on every startup (the fix for #1116). Its replacement, ValidateEnumSettings, only rejects values that fail to parse - but AAXClean.SampleRate defines Hz_7350, Hz_64000, Hz_88200 and Hz_96000, so a hand-edited or pre-v11.6.5 Settings.json could carry a valid-but-unsupported rate straight into the encoder via GetLameOptions. The clamp is restored in the MaxSampleRate property getter and setter (following the DailyDownloadLimitQuantity pattern), so it too no longer depends on a startup hook.

(The third deleted migration, migrate_to_v6_6_9's Serilog fixups, was left alone: its removal only softens log quality for configs that predate v6.6.9 and never ran a version in between, and force-rewriting outputTemplate clashed with the commit's intent of not clobbering hand-edited configs.)

Testing

  • New regression tests in Source/_Tests/LibationFileManager.Tests/QuickFiltersTests.cs: loading the exact JSON attached to the issue, the legacy string format, missing/malformed files, and restart-persistence scenarios (add -> reset -> still present; add after reset appends instead of overwriting; UseDefault persistence).
  • New tests in Source/_Tests/LibationFileManager.Tests/MaxSampleRateTests.cs: default, in-range round-trips, setter clamping, and getter clamping of hand-edited out-of-range values.
  • Full suite passes (dotnet test from Source/, all 8 projects, 1492 tests).
  • Manually verified in the Avalonia GUI on Linux with the seeded demo library: added the issue's filter !IsPodcast&!IsLiberated&!Absent, closed the app, relaunched, and the filter is present in the Quick Filters menu and applies correctly (Visible Books 30 -> 18).

Filter added in the first session:

Quick filter added before restart

After restarting the app, the menu still has it and clicking it filters the grid:

quick_filter_persists_after_restart.mp4

WinForms note: the fixes are entirely in shared LibationFileManager code and the solution (including LibationWinForms, which consumes MaxSampleRate in its settings dialog) compiles with 0 errors, but WinForms behaviour was not run on Windows.

To show artifacts inline, enable in settings.

Open in Web Open in Cursor 

Commit 065118c deleted Migrations.migrate_to_v11_5_0, which despite its
name ran on every startup and was the only code that read
QuickFilters.json into QuickFilters.InMemoryState. Since v13.7.6, saved
quick filters were never loaded after a restart, and adding a new filter
overwrote the file.

Make QuickFilters load its state lazily from disk on first access so it
no longer depends on a startup hook. Restore the pre-v11.5.0 format
fallback (plain string filters without names) that the deleted migration
provided, and add regression tests covering load formats and
restart persistence.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
@github-actions github-actions Bot added the exempt Excluded from automatic stale closure (e.g. maintainer-opened) label Aug 19, 2026
@rmcrackan
rmcrackan marked this pull request as ready for review August 19, 2026 19:59
cursoragent and others added 2 commits August 19, 2026 20:12
Matches the existing usage in PersistentDictionary and
LibationAvalonia.Program; the lock statement now emits
Lock.EnterScope() instead of Monitor.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
Commit 065118c also deleted migrate_to_v11_6_5, which clamped
MaxSampleRate into LAME's supported [Hz_8000, Hz_48000] range on every
startup (the fix for #1116). Its replacement, ValidateEnumSettings, only
rejects values that fail to parse - but AAXClean.SampleRate defines
Hz_7350, Hz_64000, Hz_88200 and Hz_96000, so a hand-edited or
pre-v11.6.5 Settings.json could carry a valid-but-unsupported rate
straight into the encoder.

Clamp in the property getter and setter instead of a startup hook,
following the DailyDownloadLimitQuantity pattern.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
@rmcrackan
rmcrackan merged commit 0b03be5 into master Aug 19, 2026
11 checks passed
@rmcrackan
rmcrackan deleted the cursor/fix-quickfilters-not-loaded-c279 branch August 19, 2026 20:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

exempt Excluded from automatic stale closure (e.g. maintainer-opened)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

QuickFilters not loaded after Restart

2 participants