2 months ago
59e1f11Adds a persistent per-app volume rule alongside the existing hard mute, with two modes: - Launch: write the volume once per app launch, then leave it alone. The user stays free to change it for the rest of the session. - Lock: hold the volume at the rule value, reverting changes from the app, the Windows mixer, or another tool. The two lists are merged into a single AppRuleEntriesJson key, one entry per app carrying both HardMuted and the volume rule, so the settings page can show one editable row per app. HardMutedAppEntriesJson is migrated once on load and left in place so downgrading still works; settings import also accepts the old key and merges it rather than replacing the list. Implementation notes: - Rules are applied after the group merge in DeviceViewModel.AddSession, not before. The merge copies the existing group's volume and mute onto the new session and would otherwise overwrite them. This also fixes a latent bug where a hard mute could be cleared by that merge. - Mute is re-asserted after the volume write: AudioDeviceSession's Volume setter clears IsMuted, so writing a launch volume would undo a hard mute. - Launch keys on process id (LaunchVolumeTracker) because a session can re-enter the collection without the app restarting: a device move and back, an unhide, or a DeviceViewModel rebuild all call AddSession again. Applying on every AddSession would reset the volume mid-session. Tracker state is per-run, which is what lets a rule still apply after a reboot when the app wins the startup race and is already playing. - Rule enforcement writes through a no-undo path so repeated Lock corrections don't flood the undo history. - The watcher that Lock needs also re-asserts hard mute, closing a pre-existing gap where an app that unmuted itself stayed unmuted. Verified at runtime: Launch applies on start and holds when raised manually, then reapplies on the next launch; Lock reverts external changes from both directions without oscillating; hard mute and a volume rule coexist; export/import round-trips and legacy imports merge.
Parent7afe27a