Enhance playback settings management in PLib video library manager. Introduce PlaybackOptions for volume and mute settings, integrating them into AppSettings and IAppSettingsStore. Update VideoPlayerViewModel to persist playback state and adjust UI bindings in VideoPlayerView for volume control. Revise README.md to document new playback settings functionality.

This commit is contained in:
Leonid Pershin
2026-08-09 06:46:33 +03:00
parent b13d0148df
commit a938a48de9
14 changed files with 399 additions and 251 deletions
@@ -2,7 +2,6 @@ using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using NSubstitute;
using PLib.Application.Library;
using PLib.Desktop.Services;
@@ -59,6 +58,21 @@ public sealed class SettingsViewModelTests
outcome.RescanRequired.ShouldBeTrue();
}
[Fact]
public async Task Saving_the_panel_keeps_the_settings_it_does_not_edit()
{
// The panel writes the whole file, so anything outside its own screens — the
// playback volume, for one — has to be carried through untouched.
await SaveAsync(
new LibraryOptions { Folders = [@"C: ideos"] },
viewModel => viewModel.ThumbnailWidth = 720,
new PlaybackOptions { Volume = 0.42, IsMuted = true });
Written.ThumbnailWidth.ShouldBe(720);
Written.Volume.ShouldBe(0.42);
Written.IsMuted.ShouldBeTrue();
}
[Fact]
public async Task Cancelling_writes_nothing()
{
@@ -69,18 +83,20 @@ public sealed class SettingsViewModelTests
await viewModel.CancelCommand.Execute().FirstAsync().ToTask(TestContext.Current.CancellationToken);
(await closed).Saved.ShouldBeFalse();
_store.ReceivedCalls().ShouldBeEmpty();
_store.ReceivedCalls().ShouldNotContain(call => call.GetMethodInfo().Name == nameof(IAppSettingsStore.SaveAsync));
}
private AppSettings Written =>
(AppSettings)_store.ReceivedCalls().Single(call => call.GetMethodInfo().Name == nameof(IAppSettingsStore.SaveAsync))
(AppSettings)_store.ReceivedCalls()
.Single(call => call.GetMethodInfo().Name == nameof(IAppSettingsStore.SaveAsync))
.GetArguments()[0]!;
private async Task<(SettingsViewModel ViewModel, SettingsDialogOutcome Outcome)> SaveAsync(
LibraryOptions options,
Action<SettingsViewModel>? edit = null)
Action<SettingsViewModel>? edit = null,
PlaybackOptions? playback = null)
{
var viewModel = Create(options);
var viewModel = Create(options, playback);
var closed = viewModel.Closed.FirstAsync().ToTask();
edit?.Invoke(viewModel);
@@ -89,19 +105,16 @@ public sealed class SettingsViewModelTests
return (viewModel, await closed);
}
private SettingsViewModel Create(LibraryOptions options) => new(
Substitute.For<IServiceScopeFactory>(),
Monitor(options),
Monitor(new AppearanceOptions()),
_store,
Substitute.For<IFolderPicker>(),
Substitute.For<IThemeService>(),
NullLogger<SettingsViewModel>.Instance);
private static IOptionsMonitor<T> Monitor<T>(T value)
private SettingsViewModel Create(LibraryOptions options, PlaybackOptions? playback = null)
{
var monitor = Substitute.For<IOptionsMonitor<T>>();
monitor.CurrentValue.Returns(value);
return monitor;
_store.Current.Returns(
AppSettings.From(options, new AppearanceOptions(), playback ?? new PlaybackOptions()));
return new SettingsViewModel(
Substitute.For<IServiceScopeFactory>(),
_store,
Substitute.For<IFolderPicker>(),
Substitute.For<IThemeService>(),
NullLogger<SettingsViewModel>.Instance);
}
}