358 lines
14 KiB
C#
358 lines
14 KiB
C#
using System.Collections.ObjectModel;
|
|
using System.ComponentModel;
|
|
using System.Globalization;
|
|
using System.Runtime.CompilerServices;
|
|
using EmbyToolbox.Models;
|
|
using EmbyToolbox.Services;
|
|
|
|
namespace EmbyToolbox.ViewModels;
|
|
|
|
public sealed class AddFilesSettingsViewModel : INotifyPropertyChanged
|
|
{
|
|
private readonly IReadOnlyList<ConversionProfilePresetRow> _profiles;
|
|
private readonly Action<AddFilesOptions> _onAdd;
|
|
private readonly Action _onCancel;
|
|
private readonly ProfileOverrideBuilder _builder = new();
|
|
private ConversionProfileSettingsEntry _sourceProfile = new();
|
|
private ConversionProfilePresetRow? _selectedProfile;
|
|
private string _container = string.Empty;
|
|
private string _video = string.Empty;
|
|
private string _pixelFormat = string.Empty;
|
|
private string _resolution = string.Empty;
|
|
private string _fps = string.Empty;
|
|
private string _videoBitrateMode = VideoBitratePolicy.Auto;
|
|
private string _videoBitrateCustomMbps = string.Empty;
|
|
private string _audio = string.Empty;
|
|
private string _audioBitrate = string.Empty;
|
|
private bool _subtitles;
|
|
private bool _externalTracks;
|
|
private bool _externalSubtitles;
|
|
private bool _fonts;
|
|
private bool _removeForeignTracks;
|
|
private bool _disableSubtitleDefault;
|
|
private string _validationMessage = string.Empty;
|
|
private bool _isValid;
|
|
|
|
public AddFilesSettingsViewModel(
|
|
IReadOnlyList<ConversionProfilePresetRow> profiles,
|
|
ConversionFormOptions formOptions,
|
|
AddFilesSettingsState? previousState,
|
|
string fallbackProfileName,
|
|
bool fallbackDisableSubtitleDefault,
|
|
bool fallbackRemoveForeignTracks,
|
|
Action<AddFilesOptions> onAdd,
|
|
Action onCancel)
|
|
{
|
|
_profiles = profiles;
|
|
_onAdd = onAdd;
|
|
_onCancel = onCancel;
|
|
Profiles = new ObservableCollection<ConversionProfilePresetRow>(profiles);
|
|
ContainerOptions = formOptions.ContainerOptions;
|
|
VideoCodecOptions = formOptions.VideoCodecOptions;
|
|
PixelFormatOptions = formOptions.PixelFormatOptions;
|
|
ResolutionOptions = formOptions.ResolutionOptions;
|
|
FpsOptions = formOptions.FpsOptions;
|
|
AudioCodecOptions = formOptions.AudioCodecOptions;
|
|
AudioBitrateOptions = formOptions.AudioBitrateKbps;
|
|
VideoBitrateOptions = VideoBitratePolicy.UiOptions;
|
|
YesNoOptions = ["Да", "Нет"];
|
|
AddCommand = new RelayCommand(ExecuteAdd, CanAdd);
|
|
CancelCommand = new RelayCommand(() => _onCancel());
|
|
|
|
var profileName = string.IsNullOrWhiteSpace(previousState?.ProfileName)
|
|
? fallbackProfileName
|
|
: previousState!.ProfileName;
|
|
_selectedProfile = Profiles.FirstOrDefault(p => p.Profile.Equals(profileName, StringComparison.OrdinalIgnoreCase))
|
|
?? Profiles.FirstOrDefault(p => p.Profile.Equals("Emby", StringComparison.OrdinalIgnoreCase))
|
|
?? Profiles.FirstOrDefault();
|
|
|
|
if (_selectedProfile is not null)
|
|
{
|
|
LoadProfile(_selectedProfile.ToSettingsEntry(), resetUserValues: true);
|
|
}
|
|
|
|
if (previousState is not null)
|
|
{
|
|
ApplyState(previousState);
|
|
}
|
|
else
|
|
{
|
|
_disableSubtitleDefault = fallbackDisableSubtitleDefault;
|
|
_removeForeignTracks = fallbackRemoveForeignTracks;
|
|
}
|
|
|
|
Validate();
|
|
}
|
|
|
|
public ObservableCollection<ConversionProfilePresetRow> Profiles { get; }
|
|
public IReadOnlyList<string> ContainerOptions { get; }
|
|
public IReadOnlyList<string> VideoCodecOptions { get; }
|
|
public IReadOnlyList<string> PixelFormatOptions { get; }
|
|
public IReadOnlyList<string> ResolutionOptions { get; }
|
|
public IReadOnlyList<string> FpsOptions { get; }
|
|
public IReadOnlyList<string> AudioCodecOptions { get; }
|
|
public IReadOnlyList<string> AudioBitrateOptions { get; }
|
|
public IReadOnlyList<string> VideoBitrateOptions { get; }
|
|
public IReadOnlyList<string> YesNoOptions { get; }
|
|
|
|
public RelayCommand AddCommand { get; }
|
|
public RelayCommand CancelCommand { get; }
|
|
|
|
public ConversionProfilePresetRow? SelectedProfile
|
|
{
|
|
get => _selectedProfile;
|
|
set
|
|
{
|
|
if (ReferenceEquals(_selectedProfile, value))
|
|
{
|
|
return;
|
|
}
|
|
|
|
_selectedProfile = value;
|
|
OnPropertyChanged();
|
|
if (value is not null)
|
|
{
|
|
LoadProfile(value.ToSettingsEntry(), resetUserValues: true);
|
|
}
|
|
}
|
|
}
|
|
|
|
public string Container { get => _container; set => SetField(ref _container, value); }
|
|
public string Video { get => _video; set => SetField(ref _video, value); }
|
|
public string PixelFormat { get => _pixelFormat; set => SetField(ref _pixelFormat, value); }
|
|
public string Resolution { get => _resolution; set => SetField(ref _resolution, value); }
|
|
public string Fps { get => _fps; set => SetField(ref _fps, value); }
|
|
public string VideoBitrateMode
|
|
{
|
|
get => _videoBitrateMode;
|
|
set
|
|
{
|
|
if (SetField(ref _videoBitrateMode, string.IsNullOrWhiteSpace(value) ? VideoBitratePolicy.Auto : value))
|
|
{
|
|
OnPropertyChanged(nameof(IsVideoBitrateCustomVisible));
|
|
}
|
|
}
|
|
}
|
|
|
|
public string VideoBitrateCustomMbps { get => _videoBitrateCustomMbps; set => SetField(ref _videoBitrateCustomMbps, value); }
|
|
public string Audio { get => _audio; set => SetField(ref _audio, value); }
|
|
public string AudioBitrate { get => _audioBitrate; set => SetField(ref _audioBitrate, value); }
|
|
public bool Subtitles { get => _subtitles; set => SetField(ref _subtitles, value); }
|
|
public bool ExternalTracks { get => _externalTracks; set => SetField(ref _externalTracks, value); }
|
|
public bool ExternalSubtitles { get => _externalSubtitles; set => SetField(ref _externalSubtitles, value); }
|
|
public bool Fonts { get => _fonts; set => SetField(ref _fonts, value); }
|
|
public bool RemoveForeignTracks { get => _removeForeignTracks; set => SetField(ref _removeForeignTracks, value); }
|
|
public bool DisableSubtitleDefault { get => _disableSubtitleDefault; set => SetField(ref _disableSubtitleDefault, value); }
|
|
public bool IsVideoBitrateCustomVisible => string.Equals(VideoBitrateMode, VideoBitratePolicy.Custom, StringComparison.Ordinal);
|
|
public bool HasValidationMessage => !string.IsNullOrWhiteSpace(ValidationMessage);
|
|
|
|
public string ValidationMessage
|
|
{
|
|
get => _validationMessage;
|
|
private set
|
|
{
|
|
if (_validationMessage == value)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_validationMessage = value;
|
|
OnPropertyChanged();
|
|
OnPropertyChanged(nameof(HasValidationMessage));
|
|
}
|
|
}
|
|
|
|
public AddFilesSettingsState CaptureState() =>
|
|
new AddFilesSettingsState
|
|
{
|
|
ProfileName = SelectedProfile?.Profile ?? "Emby",
|
|
ChangedFields = BuildEffectiveSettings().ChangedFields.ToArray(),
|
|
Container = Container,
|
|
Video = Video,
|
|
PixelFormat = PixelFormat,
|
|
Resolution = Resolution,
|
|
Fps = Fps,
|
|
VideoBitrateMode = VideoBitrateMode,
|
|
VideoBitrateCustomMbps = VideoBitrateCustomMbps,
|
|
Audio = Audio,
|
|
AudioBitrate = AudioBitrate,
|
|
Subtitles = Subtitles,
|
|
ExternalTracks = ExternalTracks,
|
|
ExternalSubtitles = ExternalSubtitles,
|
|
Fonts = Fonts,
|
|
RemoveForeignTracks = RemoveForeignTracks,
|
|
DisableSubtitleDefault = DisableSubtitleDefault
|
|
};
|
|
|
|
private void LoadProfile(ConversionProfileSettingsEntry profile, bool resetUserValues)
|
|
{
|
|
_sourceProfile = ProfileOverrideBuilder.Clone(profile);
|
|
if (!resetUserValues)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Container = profile.Container;
|
|
Video = profile.Video;
|
|
PixelFormat = profile.PixelFormat;
|
|
Resolution = profile.Resolution;
|
|
Fps = profile.Fps;
|
|
VideoBitrateMode = VideoBitratePolicy.NormalizeMode(profile.VideoBitrateMode);
|
|
VideoBitrateCustomMbps = profile.VideoBitrateMbps?.ToString("0.###", CultureInfo.InvariantCulture) ?? string.Empty;
|
|
Audio = profile.Audio;
|
|
AudioBitrate = profile.Bitrate;
|
|
Subtitles = IsYes(profile.Subtitles);
|
|
ExternalTracks = IsYes(profile.ExternalTracks);
|
|
ExternalSubtitles = IsYes(profile.ExternalSubtitles);
|
|
Fonts = IsYes(profile.Fonts);
|
|
}
|
|
|
|
private void ApplyState(AddFilesSettingsState state)
|
|
{
|
|
var changed = state.ChangedFields.ToHashSet(StringComparer.Ordinal);
|
|
if (changed.Contains("Container")) Container = state.Container;
|
|
if (changed.Contains("Video")) Video = state.Video;
|
|
if (changed.Contains("PixelFormat")) PixelFormat = state.PixelFormat;
|
|
if (changed.Contains("Resolution")) Resolution = state.Resolution;
|
|
if (changed.Contains("Fps")) Fps = state.Fps;
|
|
if (changed.Contains("VideoBitrateMode")) VideoBitrateMode = state.VideoBitrateMode;
|
|
if (changed.Contains("VideoBitrateMbps")) VideoBitrateCustomMbps = state.VideoBitrateCustomMbps;
|
|
if (changed.Contains("Audio")) Audio = state.Audio;
|
|
if (changed.Contains("Bitrate")) AudioBitrate = state.AudioBitrate;
|
|
if (changed.Contains("Subtitles")) Subtitles = state.Subtitles;
|
|
if (changed.Contains("ExternalTracks")) ExternalTracks = state.ExternalTracks;
|
|
if (changed.Contains("ExternalSubtitles")) ExternalSubtitles = state.ExternalSubtitles;
|
|
if (changed.Contains("Fonts")) Fonts = state.Fonts;
|
|
RemoveForeignTracks = state.RemoveForeignTracks;
|
|
DisableSubtitleDefault = state.DisableSubtitleDefault;
|
|
}
|
|
|
|
private bool CanAdd() => _isValid;
|
|
|
|
private void ExecuteAdd()
|
|
{
|
|
if (!Validate())
|
|
{
|
|
return;
|
|
}
|
|
|
|
var effective = BuildEffectiveSettings();
|
|
_onAdd(
|
|
new AddFilesOptions
|
|
{
|
|
Profile = effective.SourceProfileName,
|
|
DisableSubtitleDefault = DisableSubtitleDefault,
|
|
RemoveForeignAudioAndSubtitles = RemoveForeignTracks,
|
|
EffectiveSettings = effective
|
|
});
|
|
}
|
|
|
|
private EffectiveProfileSettings BuildEffectiveSettings()
|
|
{
|
|
var form = new ConversionProfileSettingsEntry
|
|
{
|
|
Profile = SelectedProfile?.Profile ?? "Emby",
|
|
Container = Container,
|
|
Video = Video,
|
|
PixelFormat = PixelFormat,
|
|
Resolution = Resolution,
|
|
Fps = Fps,
|
|
Audio = Audio,
|
|
Bitrate = AudioBitrate,
|
|
VideoBitrateMode = VideoBitrateMode,
|
|
VideoBitrateMbps = IsVideoBitrateCustomVisible && TryParseCustomVideoBitrate(VideoBitrateCustomMbps, out var mbps) ? mbps : null,
|
|
Subtitles = ToYesNo(Subtitles),
|
|
ExternalTracks = ToYesNo(ExternalTracks),
|
|
ExternalSubtitles = ToYesNo(ExternalSubtitles),
|
|
Fonts = ToYesNo(Fonts)
|
|
};
|
|
|
|
return _builder.Build(_sourceProfile, form, SelectedProfile?.Profile ?? "Emby");
|
|
}
|
|
|
|
private bool Validate()
|
|
{
|
|
if (SelectedProfile is null)
|
|
{
|
|
ValidationMessage = "Выберите профиль.";
|
|
_isValid = false;
|
|
AddCommand.RaiseCanExecuteChanged();
|
|
return false;
|
|
}
|
|
|
|
if (IsVideoBitrateCustomVisible && !TryParseCustomVideoBitrate(VideoBitrateCustomMbps, out _))
|
|
{
|
|
ValidationMessage = "Видеобитрейт должен быть числом больше 0.";
|
|
_isValid = false;
|
|
AddCommand.RaiseCanExecuteChanged();
|
|
return false;
|
|
}
|
|
|
|
ValidationMessage = string.Empty;
|
|
_isValid = true;
|
|
AddCommand.RaiseCanExecuteChanged();
|
|
return true;
|
|
}
|
|
|
|
private bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
|
|
{
|
|
if (EqualityComparer<T>.Default.Equals(field, value))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
field = value;
|
|
OnPropertyChanged(propertyName);
|
|
Validate();
|
|
return true;
|
|
}
|
|
|
|
private static bool TryParseCustomVideoBitrate(string? raw, out double mbps)
|
|
{
|
|
mbps = 0;
|
|
if (string.IsNullOrWhiteSpace(raw))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return double.TryParse(raw.Trim().Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out mbps)
|
|
&& mbps > 0;
|
|
}
|
|
|
|
private static bool IsYes(string? value) =>
|
|
value is not null
|
|
&& (value.Equals("Да", StringComparison.OrdinalIgnoreCase)
|
|
|| value.Equals("Yes", StringComparison.OrdinalIgnoreCase)
|
|
|| value.Equals("true", StringComparison.OrdinalIgnoreCase));
|
|
|
|
private static string ToYesNo(bool value) => value ? "Да" : "Нет";
|
|
|
|
public event PropertyChangedEventHandler? PropertyChanged;
|
|
|
|
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
|
{
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|
}
|
|
}
|
|
|
|
public sealed class AddFilesSettingsState
|
|
{
|
|
public string ProfileName { get; init; } = "Emby";
|
|
public IReadOnlyList<string> ChangedFields { get; init; } = [];
|
|
public string Container { get; init; } = string.Empty;
|
|
public string Video { get; init; } = string.Empty;
|
|
public string PixelFormat { get; init; } = string.Empty;
|
|
public string Resolution { get; init; } = string.Empty;
|
|
public string Fps { get; init; } = string.Empty;
|
|
public string VideoBitrateMode { get; init; } = VideoBitratePolicy.Auto;
|
|
public string VideoBitrateCustomMbps { get; init; } = string.Empty;
|
|
public string Audio { get; init; } = string.Empty;
|
|
public string AudioBitrate { get; init; } = string.Empty;
|
|
public bool Subtitles { get; init; }
|
|
public bool ExternalTracks { get; init; }
|
|
public bool ExternalSubtitles { get; init; }
|
|
public bool Fonts { get; init; }
|
|
public bool RemoveForeignTracks { get; init; }
|
|
public bool DisableSubtitleDefault { get; init; }
|
|
}
|