Improve video info JSON viewer
This commit is contained in:
parent
23f9ab3399
commit
9bf4588c54
@ -368,7 +368,6 @@
|
|||||||
<TabItem Header="Подробная информация">
|
<TabItem Header="Подробная информация">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
@ -412,37 +411,6 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Border Grid.Row="1"
|
<Border Grid.Row="1"
|
||||||
BorderBrush="{DynamicResource Ui.Brush.BorderSubtle}"
|
|
||||||
BorderThickness="0,0,0,1"
|
|
||||||
Padding="0,0,0,8"
|
|
||||||
Margin="0,0,0,8">
|
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
|
||||||
<Button Style="{StaticResource UiButtonSecondary}" Margin="0,0,8,0" MinWidth="126" Command="{Binding ExpandAllCommand}">
|
|
||||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
|
||||||
<TextBlock Text="Развернуть всё" VerticalAlignment="Center" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
<Button Style="{StaticResource UiButtonSecondary}" Margin="0,0,8,0" MinWidth="126" Command="{Binding CollapseAllCommand}">
|
|
||||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
|
||||||
<TextBlock Text="Свернуть всё" VerticalAlignment="Center" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
<Button Style="{StaticResource UiButtonSecondary}" Margin="0,0,8,0" MinWidth="132" Command="{Binding CopyJsonCommand}">
|
|
||||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
|
||||||
<TextBlock Style="{StaticResource UiMdlGlyphButton}" Text="" />
|
|
||||||
<TextBlock Text="Копировать JSON" VerticalAlignment="Center" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
<Button Style="{StaticResource UiButtonSecondary}" MinWidth="130" Command="{Binding SaveJsonCommand}">
|
|
||||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
|
||||||
<TextBlock Style="{StaticResource UiMdlGlyphButton}" Text="" />
|
|
||||||
<TextBlock Text="Сохранить JSON" VerticalAlignment="Center" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<Border Grid.Row="2"
|
|
||||||
Padding="8,4,8,8"
|
Padding="8,4,8,8"
|
||||||
Background="{DynamicResource Ui.Brush.Surface}"
|
Background="{DynamicResource Ui.Brush.Surface}"
|
||||||
behaviors:VideoInfoDropTargetBehavior.IsEnabled="True">
|
behaviors:VideoInfoDropTargetBehavior.IsEnabled="True">
|
||||||
@ -463,43 +431,6 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.Resources>
|
|
||||||
<Style x:Key="VideoInfoTreeItemStyle" TargetType="TreeViewItem">
|
|
||||||
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
|
|
||||||
<Setter Property="Padding" Value="2,1" />
|
|
||||||
<Setter Property="Margin" Value="0,0,0,1" />
|
|
||||||
<Setter Property="Background" Value="Transparent" />
|
|
||||||
<Setter Property="Foreground" Value="{DynamicResource Ui.Brush.Text}" />
|
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
|
||||||
<EventSetter Event="PreviewMouseRightButtonDown" Handler="OnJsonTreeItemPreviewMouseRightButtonDown" />
|
|
||||||
<Setter Property="ContextMenu">
|
|
||||||
<Setter.Value>
|
|
||||||
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
|
||||||
<MenuItem Header="Копировать значение"
|
|
||||||
Command="{Binding DataContext.VideoInfo.CopyNodeValueCommand, Source={x:Reference RootWindow}}"
|
|
||||||
CommandParameter="{Binding}" />
|
|
||||||
<MenuItem Header="Копировать узел"
|
|
||||||
Command="{Binding DataContext.VideoInfo.CopyNodeLineCommand, Source={x:Reference RootWindow}}"
|
|
||||||
CommandParameter="{Binding}" />
|
|
||||||
<MenuItem Header="Копировать узел с дочерними элементами"
|
|
||||||
Command="{Binding DataContext.VideoInfo.CopyNodeWithChildrenCommand, Source={x:Reference RootWindow}}"
|
|
||||||
CommandParameter="{Binding}" />
|
|
||||||
<Separator />
|
|
||||||
<MenuItem Header="Копировать путь к узлу"
|
|
||||||
Command="{Binding DataContext.VideoInfo.CopyNodePathCommand, Source={x:Reference RootWindow}}"
|
|
||||||
CommandParameter="{Binding}" />
|
|
||||||
</ContextMenu>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
<Style.Triggers>
|
|
||||||
<Trigger Property="IsSelected" Value="True">
|
|
||||||
<Setter Property="Background" Value="{DynamicResource Ui.Brush.DataRowSelected}" />
|
|
||||||
<Setter Property="Foreground" Value="{DynamicResource Ui.Brush.Text}" />
|
|
||||||
<Setter Property="TextElement.Foreground" Value="{DynamicResource Ui.Brush.Text}" />
|
|
||||||
</Trigger>
|
|
||||||
</Style.Triggers>
|
|
||||||
</Style>
|
|
||||||
</Grid.Resources>
|
|
||||||
|
|
||||||
<Grid Grid.RowSpan="2">
|
<Grid Grid.RowSpan="2">
|
||||||
<Grid.Style>
|
<Grid.Style>
|
||||||
@ -541,9 +472,21 @@
|
|||||||
</TextBlock.Style>
|
</TextBlock.Style>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
|
|
||||||
<TreeView Grid.Row="1" ItemsSource="{Binding JsonNodes}" Margin="0" BorderThickness="0" Background="Transparent">
|
<ListBox Grid.Row="1"
|
||||||
<TreeView.Style>
|
ItemsSource="{Binding FormattedJsonLines}"
|
||||||
<Style TargetType="TreeView">
|
Margin="0"
|
||||||
|
BorderThickness="0"
|
||||||
|
Background="Transparent"
|
||||||
|
Foreground="{DynamicResource Ui.Brush.Text}"
|
||||||
|
ScrollViewer.CanContentScroll="True"
|
||||||
|
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||||
|
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||||
|
VirtualizingPanel.IsVirtualizing="True"
|
||||||
|
VirtualizingPanel.VirtualizationMode="Recycling"
|
||||||
|
VirtualizingPanel.ScrollUnit="Pixel"
|
||||||
|
SelectionMode="Extended">
|
||||||
|
<ListBox.Style>
|
||||||
|
<Style TargetType="ListBox">
|
||||||
<Setter Property="Visibility" Value="Visible" />
|
<Setter Property="Visibility" Value="Visible" />
|
||||||
<Style.Triggers>
|
<Style.Triggers>
|
||||||
<DataTrigger Binding="{Binding SelectedFilePath}" Value="">
|
<DataTrigger Binding="{Binding SelectedFilePath}" Value="">
|
||||||
@ -551,36 +494,36 @@
|
|||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
</Style.Triggers>
|
</Style.Triggers>
|
||||||
</Style>
|
</Style>
|
||||||
</TreeView.Style>
|
</ListBox.Style>
|
||||||
<TreeView.Resources>
|
<ListBox.ItemContainerStyle>
|
||||||
<HierarchicalDataTemplate DataType="{x:Type viewModels:JsonTreeNodeViewModel}" ItemsSource="{Binding Children}">
|
<Style TargetType="ListBoxItem">
|
||||||
<Border Background="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}, Path=Background}" Padding="2,1">
|
<Setter Property="Padding" Value="0" />
|
||||||
<StackPanel Orientation="Horizontal">
|
<Setter Property="Margin" Value="0" />
|
||||||
<TextBlock Text="{Binding Name}" />
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
<TextBlock Text=": " />
|
<Setter Property="Background" Value="Transparent" />
|
||||||
<TextBlock Text="{Binding Value}" Foreground="{DynamicResource Ui.Brush.Muted}" />
|
<Setter Property="HorizontalContentAlignment" Value="Left" />
|
||||||
</StackPanel>
|
<Setter Property="Focusable" Value="False" />
|
||||||
<Border.Style>
|
</Style>
|
||||||
<Style TargetType="Border">
|
</ListBox.ItemContainerStyle>
|
||||||
<Setter Property="Background" Value="Transparent" />
|
<ListBox.ItemTemplate>
|
||||||
<Style.Triggers>
|
<DataTemplate>
|
||||||
<Trigger Property="IsMouseOver" Value="True">
|
<TextBox Text="{Binding Mode=OneWay}"
|
||||||
<Setter Property="Background" Value="{DynamicResource Ui.Brush.DataRowHover}" />
|
BorderThickness="0"
|
||||||
</Trigger>
|
Background="Transparent"
|
||||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}, Path=IsSelected}" Value="True">
|
Foreground="{DynamicResource Ui.Brush.Text}"
|
||||||
<Setter Property="Background" Value="{DynamicResource Ui.Brush.DataRowSelected}" />
|
FontFamily="Consolas"
|
||||||
<Setter Property="TextElement.Foreground" Value="{DynamicResource Ui.Brush.Text}" />
|
FontSize="13"
|
||||||
</DataTrigger>
|
IsReadOnly="True"
|
||||||
</Style.Triggers>
|
IsUndoEnabled="False"
|
||||||
</Style>
|
IsInactiveSelectionHighlightEnabled="True"
|
||||||
</Border.Style>
|
AcceptsReturn="False"
|
||||||
</Border>
|
AcceptsTab="False"
|
||||||
</HierarchicalDataTemplate>
|
TextWrapping="NoWrap"
|
||||||
</TreeView.Resources>
|
HorizontalScrollBarVisibility="Disabled"
|
||||||
<TreeView.ItemContainerStyle>
|
VerticalScrollBarVisibility="Disabled" />
|
||||||
<StaticResource ResourceKey="VideoInfoTreeItemStyle" />
|
</DataTemplate>
|
||||||
</TreeView.ItemContainerStyle>
|
</ListBox.ItemTemplate>
|
||||||
</TreeView>
|
</ListBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@ -1,7 +1,4 @@
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using System.Windows.Media;
|
|
||||||
using EmbyToolbox.ViewModels;
|
using EmbyToolbox.ViewModels;
|
||||||
|
|
||||||
namespace EmbyToolbox;
|
namespace EmbyToolbox;
|
||||||
@ -13,19 +10,4 @@ public partial class MainWindow
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = new MainWindowViewModel();
|
DataContext = new MainWindowViewModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnJsonTreeItemPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
|
||||||
{
|
|
||||||
var dependencyObject = e.OriginalSource as DependencyObject;
|
|
||||||
while (dependencyObject is not null && dependencyObject is not TreeViewItem)
|
|
||||||
{
|
|
||||||
dependencyObject = VisualTreeHelper.GetParent(dependencyObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dependencyObject is TreeViewItem item)
|
|
||||||
{
|
|
||||||
item.IsSelected = true;
|
|
||||||
item.Focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
@ -23,7 +22,8 @@ public sealed class VideoInfoViewModel : INotifyPropertyChanged
|
|||||||
private string _selectedFilePath = string.Empty;
|
private string _selectedFilePath = string.Empty;
|
||||||
private string _analysisStateText = string.Empty;
|
private string _analysisStateText = string.Empty;
|
||||||
private string _errorMessage = string.Empty;
|
private string _errorMessage = string.Empty;
|
||||||
private string _rawJson = string.Empty;
|
private string _formattedJson = string.Empty;
|
||||||
|
private IReadOnlyList<string> _formattedJsonLines = Array.Empty<string>();
|
||||||
private string _summaryText = string.Empty;
|
private string _summaryText = string.Empty;
|
||||||
private bool _isBusy;
|
private bool _isBusy;
|
||||||
private bool _isVideoInfoDropHighlight;
|
private bool _isVideoInfoDropHighlight;
|
||||||
@ -44,41 +44,16 @@ public sealed class VideoInfoViewModel : INotifyPropertyChanged
|
|||||||
|
|
||||||
SelectFileCommand = new RelayCommand(ExecuteSelectFile);
|
SelectFileCommand = new RelayCommand(ExecuteSelectFile);
|
||||||
SelectSummaryFilesCommand = new RelayCommand(ExecuteSelectSummaryFiles);
|
SelectSummaryFilesCommand = new RelayCommand(ExecuteSelectSummaryFiles);
|
||||||
ExpandAllCommand = new RelayCommand(ExecuteExpandAll);
|
|
||||||
CollapseAllCommand = new RelayCommand(ExecuteCollapseAll);
|
|
||||||
CopyJsonCommand = new RelayCommand(ExecuteCopyJson, () => !string.IsNullOrWhiteSpace(_rawJson));
|
|
||||||
SaveJsonCommand = new RelayCommand(ExecuteSaveJson, () => !string.IsNullOrWhiteSpace(_rawJson));
|
|
||||||
CopySummaryCommand = new RelayCommand(ExecuteCopySummary, () => !string.IsNullOrWhiteSpace(SummaryText) && SummaryText != "Файл не проанализирован");
|
CopySummaryCommand = new RelayCommand(ExecuteCopySummary, () => !string.IsNullOrWhiteSpace(SummaryText) && SummaryText != "Файл не проанализирован");
|
||||||
SaveSummaryCommand = new RelayCommand(ExecuteSaveSummary, () => !string.IsNullOrWhiteSpace(SummaryText) && SummaryText != "Файл не проанализирован");
|
SaveSummaryCommand = new RelayCommand(ExecuteSaveSummary, () => !string.IsNullOrWhiteSpace(SummaryText) && SummaryText != "Файл не проанализирован");
|
||||||
CopyNodeValueCommand = new RelayCommand(ExecuteCopyNodeValue, CanCopyNodeValue);
|
|
||||||
CopyNodeLineCommand = new RelayCommand(ExecuteCopyNodeLine, CanCopyNodeLine);
|
|
||||||
CopyNodeWithChildrenCommand = new RelayCommand(ExecuteCopyNodeWithChildren, CanCopyNodeWithChildren);
|
|
||||||
CopyNodePathCommand = new RelayCommand(ExecuteCopyNodePath, CanCopyNodePath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableCollection<JsonTreeNodeViewModel> JsonNodes { get; } = new();
|
|
||||||
|
|
||||||
public ICommand SelectFileCommand { get; }
|
public ICommand SelectFileCommand { get; }
|
||||||
public ICommand SelectSummaryFilesCommand { get; }
|
public ICommand SelectSummaryFilesCommand { get; }
|
||||||
|
|
||||||
public ICommand ExpandAllCommand { get; }
|
|
||||||
|
|
||||||
public ICommand CollapseAllCommand { get; }
|
|
||||||
|
|
||||||
public ICommand CopyJsonCommand { get; }
|
|
||||||
|
|
||||||
public ICommand SaveJsonCommand { get; }
|
|
||||||
public ICommand CopySummaryCommand { get; }
|
public ICommand CopySummaryCommand { get; }
|
||||||
public ICommand SaveSummaryCommand { get; }
|
public ICommand SaveSummaryCommand { get; }
|
||||||
|
|
||||||
public ICommand CopyNodeValueCommand { get; }
|
|
||||||
|
|
||||||
public ICommand CopyNodeLineCommand { get; }
|
|
||||||
|
|
||||||
public ICommand CopyNodeWithChildrenCommand { get; }
|
|
||||||
|
|
||||||
public ICommand CopyNodePathCommand { get; }
|
|
||||||
|
|
||||||
public string SelectedFilePath
|
public string SelectedFilePath
|
||||||
{
|
{
|
||||||
get => _selectedFilePath;
|
get => _selectedFilePath;
|
||||||
@ -124,6 +99,37 @@ public sealed class VideoInfoViewModel : INotifyPropertyChanged
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string FormattedJson
|
||||||
|
{
|
||||||
|
get => _formattedJson;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (_formattedJson == value)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_formattedJson = value;
|
||||||
|
FormattedJsonLines = SplitTextLines(value);
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<string> FormattedJsonLines
|
||||||
|
{
|
||||||
|
get => _formattedJsonLines;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(_formattedJsonLines, value))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_formattedJsonLines = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsBusy
|
public bool IsBusy
|
||||||
{
|
{
|
||||||
get => _isBusy;
|
get => _isBusy;
|
||||||
@ -307,9 +313,8 @@ public sealed class VideoInfoViewModel : INotifyPropertyChanged
|
|||||||
|
|
||||||
private async Task AnalyzeAsync()
|
private async Task AnalyzeAsync()
|
||||||
{
|
{
|
||||||
JsonNodes.Clear();
|
|
||||||
ErrorMessage = string.Empty;
|
ErrorMessage = string.Empty;
|
||||||
_rawJson = string.Empty;
|
FormattedJson = string.Empty;
|
||||||
RaiseCommandStates();
|
RaiseCommandStates();
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(SelectedFilePath))
|
if (string.IsNullOrWhiteSpace(SelectedFilePath))
|
||||||
@ -335,8 +340,7 @@ public sealed class VideoInfoViewModel : INotifyPropertyChanged
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_rawJson = result.Json;
|
FormattedJson = FormatJsonOrFallback(result.Json);
|
||||||
BuildTree(_rawJson);
|
|
||||||
AnalysisStateText = "Готово";
|
AnalysisStateText = "Готово";
|
||||||
RaiseCommandStates();
|
RaiseCommandStates();
|
||||||
_logging.Info($"ffprobe завершен: {Path.GetFileName(SelectedFilePath)}", "video-info.ffprobe", command: result.Command, stdout: result.StdOut, stderr: result.StdErr);
|
_logging.Info($"ffprobe завершен: {Path.GetFileName(SelectedFilePath)}", "video-info.ffprobe", command: result.Command, stdout: result.StdOut, stderr: result.StdErr);
|
||||||
@ -427,125 +431,6 @@ public sealed class VideoInfoViewModel : INotifyPropertyChanged
|
|||||||
RaiseCommandStates();
|
RaiseCommandStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BuildTree(string json)
|
|
||||||
{
|
|
||||||
using var doc = JsonDocument.Parse(json);
|
|
||||||
JsonNodes.Clear();
|
|
||||||
|
|
||||||
if (doc.RootElement.ValueKind == JsonValueKind.Object)
|
|
||||||
{
|
|
||||||
foreach (var property in doc.RootElement.EnumerateObject())
|
|
||||||
{
|
|
||||||
JsonNodes.Add(CreateNode(property.Name, property.Value, null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
JsonNodes.Add(CreateNode("root", doc.RootElement, null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static JsonTreeNodeViewModel CreateNode(string name, JsonElement element, JsonTreeNodeViewModel? parent)
|
|
||||||
{
|
|
||||||
var node = new JsonTreeNodeViewModel(name, GetPreviewValue(element), element.GetRawText(), parent);
|
|
||||||
|
|
||||||
switch (element.ValueKind)
|
|
||||||
{
|
|
||||||
case JsonValueKind.Object:
|
|
||||||
foreach (var prop in element.EnumerateObject())
|
|
||||||
{
|
|
||||||
node.Children.Add(CreateNode(prop.Name, prop.Value, node));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case JsonValueKind.Array:
|
|
||||||
var index = 0;
|
|
||||||
foreach (var item in element.EnumerateArray())
|
|
||||||
{
|
|
||||||
node.Children.Add(CreateNode($"[{index}]", item, node));
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetPreviewValue(JsonElement element)
|
|
||||||
{
|
|
||||||
return element.ValueKind switch
|
|
||||||
{
|
|
||||||
JsonValueKind.Object => "{...}",
|
|
||||||
JsonValueKind.Array => "[...]",
|
|
||||||
JsonValueKind.String => element.GetString() ?? string.Empty,
|
|
||||||
JsonValueKind.Number => element.GetRawText(),
|
|
||||||
JsonValueKind.True => "true",
|
|
||||||
JsonValueKind.False => "false",
|
|
||||||
JsonValueKind.Null => "null",
|
|
||||||
_ => element.GetRawText()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ExecuteExpandAll()
|
|
||||||
{
|
|
||||||
SetExpandedState(JsonNodes, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ExecuteCollapseAll()
|
|
||||||
{
|
|
||||||
SetExpandedState(JsonNodes, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetExpandedState(IEnumerable<JsonTreeNodeViewModel> nodes, bool isExpanded)
|
|
||||||
{
|
|
||||||
foreach (var node in nodes)
|
|
||||||
{
|
|
||||||
node.IsExpanded = isExpanded;
|
|
||||||
SetExpandedState(node.Children, isExpanded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ExecuteCopyJson()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(_rawJson))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Clipboard.SetText(_rawJson);
|
|
||||||
_logging.Info("JSON скопирован в буфер обмена", "video-info.copy");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ExecuteSaveJson()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(_rawJson))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var defaultName = string.IsNullOrWhiteSpace(SelectedFilePath)
|
|
||||||
? "ffprobe.json"
|
|
||||||
: $"{Path.GetFileNameWithoutExtension(SelectedFilePath)}.ffprobe.json";
|
|
||||||
|
|
||||||
var dialog = new SaveFileDialog
|
|
||||||
{
|
|
||||||
Title = "Сохранить JSON ffprobe",
|
|
||||||
Filter = "JSON (*.json)|*.json|Все файлы|*.*",
|
|
||||||
FileName = defaultName,
|
|
||||||
InitialDirectory = _recentPaths.GetInitialDirectory(RecentPathScenario.SettingsOutputFolder),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (dialog.ShowDialog() != true)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
File.WriteAllText(dialog.FileName, _rawJson);
|
|
||||||
_recentPaths.RememberChosenFolder(
|
|
||||||
RecentPathScenario.SettingsOutputFolder,
|
|
||||||
Path.GetDirectoryName(dialog.FileName) ?? dialog.FileName);
|
|
||||||
_logging.Info($"JSON сохранен: {dialog.FileName}", "video-info.save");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ExecuteCopySummary()
|
private void ExecuteCopySummary()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(SummaryText) || SummaryText == "Файл не проанализирован")
|
if (string.IsNullOrWhiteSpace(SummaryText) || SummaryText == "Файл не проанализирован")
|
||||||
@ -594,105 +479,8 @@ public sealed class VideoInfoViewModel : INotifyPropertyChanged
|
|||||||
|
|
||||||
private void RaiseCommandStates()
|
private void RaiseCommandStates()
|
||||||
{
|
{
|
||||||
(CopyJsonCommand as RelayCommand)?.RaiseCanExecuteChanged();
|
|
||||||
(SaveJsonCommand as RelayCommand)?.RaiseCanExecuteChanged();
|
|
||||||
(CopySummaryCommand as RelayCommand)?.RaiseCanExecuteChanged();
|
(CopySummaryCommand as RelayCommand)?.RaiseCanExecuteChanged();
|
||||||
(SaveSummaryCommand as RelayCommand)?.RaiseCanExecuteChanged();
|
(SaveSummaryCommand as RelayCommand)?.RaiseCanExecuteChanged();
|
||||||
(CopyNodeValueCommand as RelayCommand)?.RaiseCanExecuteChanged();
|
|
||||||
(CopyNodeLineCommand as RelayCommand)?.RaiseCanExecuteChanged();
|
|
||||||
(CopyNodeWithChildrenCommand as RelayCommand)?.RaiseCanExecuteChanged();
|
|
||||||
(CopyNodePathCommand as RelayCommand)?.RaiseCanExecuteChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static JsonTreeNodeViewModel? AsNode(object? parameter)
|
|
||||||
{
|
|
||||||
return parameter as JsonTreeNodeViewModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CanCopyNodeValue(object? parameter)
|
|
||||||
{
|
|
||||||
var node = AsNode(parameter);
|
|
||||||
return node is not null && node.Children.Count == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CanCopyNodeLine(object? parameter)
|
|
||||||
{
|
|
||||||
return AsNode(parameter) is not null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CanCopyNodeWithChildren(object? parameter)
|
|
||||||
{
|
|
||||||
return AsNode(parameter) is not null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CanCopyNodePath(object? parameter)
|
|
||||||
{
|
|
||||||
return AsNode(parameter) is not null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ExecuteCopyNodeValue(object? parameter)
|
|
||||||
{
|
|
||||||
var node = AsNode(parameter);
|
|
||||||
if (node is null || node.Children.Count > 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Clipboard.SetText(node.Value);
|
|
||||||
_logging.Info("узел JSON скопирован", "video-info.copy");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ExecuteCopyNodeLine(object? parameter)
|
|
||||||
{
|
|
||||||
var node = AsNode(parameter);
|
|
||||||
if (node is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Clipboard.SetText($"{node.Name}: {node.Value}");
|
|
||||||
_logging.Info("узел JSON скопирован", "video-info.copy");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ExecuteCopyNodeWithChildren(object? parameter)
|
|
||||||
{
|
|
||||||
var node = AsNode(parameter);
|
|
||||||
if (node is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var formatted = FormatJsonOrFallback(node.SubtreeJson);
|
|
||||||
Clipboard.SetText(formatted);
|
|
||||||
_logging.Info("узел JSON скопирован", "video-info.copy");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ExecuteCopyNodePath(object? parameter)
|
|
||||||
{
|
|
||||||
var node = AsNode(parameter);
|
|
||||||
if (node is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Clipboard.SetText(BuildNodePath(node));
|
|
||||||
_logging.Info("узел JSON скопирован", "video-info.copy");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string BuildNodePath(JsonTreeNodeViewModel node)
|
|
||||||
{
|
|
||||||
if (node.Parent is null)
|
|
||||||
{
|
|
||||||
return node.Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
var parentPath = BuildNodePath(node.Parent);
|
|
||||||
if (node.Name.StartsWith("[", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
return $"{parentPath}{node.Name}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.IsNullOrWhiteSpace(parentPath) ? node.Name : $"{parentPath}.{node.Name}";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string FormatJsonOrFallback(string json)
|
private static string FormatJsonOrFallback(string json)
|
||||||
@ -707,5 +495,15 @@ public sealed class VideoInfoViewModel : INotifyPropertyChanged
|
|||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IReadOnlyList<string> SplitTextLines(string text)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(text))
|
||||||
|
{
|
||||||
|
return Array.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return text.ReplaceLineEndings("\n").Split('\n');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user