Improve video info JSON viewer
This commit is contained in:
parent
23f9ab3399
commit
9bf4588c54
@ -368,7 +368,6 @@
|
||||
<TabItem Header="Подробная информация">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
@ -412,37 +411,6 @@
|
||||
</Grid>
|
||||
|
||||
<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"
|
||||
Background="{DynamicResource Ui.Brush.Surface}"
|
||||
behaviors:VideoInfoDropTargetBehavior.IsEnabled="True">
|
||||
@ -463,43 +431,6 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</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.Style>
|
||||
@ -541,9 +472,21 @@
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
|
||||
<TreeView Grid.Row="1" ItemsSource="{Binding JsonNodes}" Margin="0" BorderThickness="0" Background="Transparent">
|
||||
<TreeView.Style>
|
||||
<Style TargetType="TreeView">
|
||||
<ListBox Grid.Row="1"
|
||||
ItemsSource="{Binding FormattedJsonLines}"
|
||||
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" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding SelectedFilePath}" Value="">
|
||||
@ -551,36 +494,36 @@
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TreeView.Style>
|
||||
<TreeView.Resources>
|
||||
<HierarchicalDataTemplate DataType="{x:Type viewModels:JsonTreeNodeViewModel}" ItemsSource="{Binding Children}">
|
||||
<Border Background="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}, Path=Background}" Padding="2,1">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
<TextBlock Text=": " />
|
||||
<TextBlock Text="{Binding Value}" Foreground="{DynamicResource Ui.Brush.Muted}" />
|
||||
</StackPanel>
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
</ListBox.Style>
|
||||
<ListBox.ItemContainerStyle>
|
||||
<Style TargetType="ListBoxItem">
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource Ui.Brush.DataRowHover}" />
|
||||
</Trigger>
|
||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}, Path=IsSelected}" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource Ui.Brush.DataRowSelected}" />
|
||||
<Setter Property="TextElement.Foreground" Value="{DynamicResource Ui.Brush.Text}" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left" />
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
</Style>
|
||||
</Border.Style>
|
||||
</Border>
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.Resources>
|
||||
<TreeView.ItemContainerStyle>
|
||||
<StaticResource ResourceKey="VideoInfoTreeItemStyle" />
|
||||
</TreeView.ItemContainerStyle>
|
||||
</TreeView>
|
||||
</ListBox.ItemContainerStyle>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBox Text="{Binding Mode=OneWay}"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
Foreground="{DynamicResource Ui.Brush.Text}"
|
||||
FontFamily="Consolas"
|
||||
FontSize="13"
|
||||
IsReadOnly="True"
|
||||
IsUndoEnabled="False"
|
||||
IsInactiveSelectionHighlightEnabled="True"
|
||||
AcceptsReturn="False"
|
||||
AcceptsTab="False"
|
||||
TextWrapping="NoWrap"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Disabled" />
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using EmbyToolbox.ViewModels;
|
||||
|
||||
namespace EmbyToolbox;
|
||||
@ -13,19 +10,4 @@ public partial class MainWindow
|
||||
InitializeComponent();
|
||||
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.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
@ -23,7 +22,8 @@ public sealed class VideoInfoViewModel : INotifyPropertyChanged
|
||||
private string _selectedFilePath = string.Empty;
|
||||
private string _analysisStateText = 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 bool _isBusy;
|
||||
private bool _isVideoInfoDropHighlight;
|
||||
@ -44,41 +44,16 @@ public sealed class VideoInfoViewModel : INotifyPropertyChanged
|
||||
|
||||
SelectFileCommand = new RelayCommand(ExecuteSelectFile);
|
||||
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 != "Файл не проанализирован");
|
||||
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 SelectSummaryFilesCommand { get; }
|
||||
|
||||
public ICommand ExpandAllCommand { get; }
|
||||
|
||||
public ICommand CollapseAllCommand { get; }
|
||||
|
||||
public ICommand CopyJsonCommand { get; }
|
||||
|
||||
public ICommand SaveJsonCommand { get; }
|
||||
public ICommand CopySummaryCommand { get; }
|
||||
public ICommand SaveSummaryCommand { get; }
|
||||
|
||||
public ICommand CopyNodeValueCommand { get; }
|
||||
|
||||
public ICommand CopyNodeLineCommand { get; }
|
||||
|
||||
public ICommand CopyNodeWithChildrenCommand { get; }
|
||||
|
||||
public ICommand CopyNodePathCommand { get; }
|
||||
|
||||
public string 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
|
||||
{
|
||||
get => _isBusy;
|
||||
@ -307,9 +313,8 @@ public sealed class VideoInfoViewModel : INotifyPropertyChanged
|
||||
|
||||
private async Task AnalyzeAsync()
|
||||
{
|
||||
JsonNodes.Clear();
|
||||
ErrorMessage = string.Empty;
|
||||
_rawJson = string.Empty;
|
||||
FormattedJson = string.Empty;
|
||||
RaiseCommandStates();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(SelectedFilePath))
|
||||
@ -335,8 +340,7 @@ public sealed class VideoInfoViewModel : INotifyPropertyChanged
|
||||
|
||||
try
|
||||
{
|
||||
_rawJson = result.Json;
|
||||
BuildTree(_rawJson);
|
||||
FormattedJson = FormatJsonOrFallback(result.Json);
|
||||
AnalysisStateText = "Готово";
|
||||
RaiseCommandStates();
|
||||
_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();
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(SummaryText) || SummaryText == "Файл не проанализирован")
|
||||
@ -594,105 +479,8 @@ public sealed class VideoInfoViewModel : INotifyPropertyChanged
|
||||
|
||||
private void RaiseCommandStates()
|
||||
{
|
||||
(CopyJsonCommand as RelayCommand)?.RaiseCanExecuteChanged();
|
||||
(SaveJsonCommand as RelayCommand)?.RaiseCanExecuteChanged();
|
||||
(CopySummaryCommand 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)
|
||||
@ -707,5 +495,15 @@ public sealed class VideoInfoViewModel : INotifyPropertyChanged
|
||||
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