Video processing implemented.

This commit is contained in:
Alexander Shabarshov 2026-05-25 12:34:36 +01:00
parent 9cdf611ec8
commit af363ebb9a
18 changed files with 268 additions and 45 deletions

View File

@ -8,7 +8,9 @@ namespace Splitter_UI;
public partial class App : Application
{
private readonly ServiceProvider _provider;
private readonly ServiceProvider _provider = null!;
public App() { }
public App(ServiceProvider provider)
{

View File

@ -0,0 +1,15 @@
using System.Globalization;
using Avalonia.Data.Converters;
namespace Splitter_UI.Converters;
public sealed class BoolInvertConverter : IValueConverter
{
public static readonly BoolInvertConverter Instance = new();
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
=> value is bool b ? !b : value;
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> value is bool b ? !b : value;
}

View File

@ -1,6 +1,5 @@
using Avalonia.Data.Converters;
using Avalonia.Media;
using System;
using System.Globalization;
namespace Splitter_UI.Converters;

View File

@ -30,6 +30,7 @@ internal sealed class Program
services.AddTransient<PreviewPaneViewModel>();
services.AddTransient<InspectorPaneViewModel>();
services.AddSingleton<StatusBarViewModel>();
services.AddSingleton<ProgressViewModel>();
services.AddSingleton<LogPaneViewModel>(logPaveVM);
services.AddSingleton<ILogService>(logPaveVM);
@ -48,6 +49,7 @@ internal sealed class Program
};
});
services.AddSingleton<ILogger, GlobalLogger>();
services.AddSingleton<IJobProcessor, JobProcessor>();
// Domain services (your pipeline)
services.AddTransient<IFileProbeService, FileProbeService>();

View File

@ -1,16 +1,20 @@
namespace Splitter_UI.Services;
internal class GlobalLogger(ILogService _logService, StatusBarViewModel _statusBar) : ILogger
internal class GlobalLogger(ILogService _logService, StatusBarViewModel _statusBar, ProgressViewModel _progress) : ILogger
{
public void ClearProgress(string name, int progressLine)
{
if (progressLine == 0)
_statusBar.Percent = 0;
else
_progress.ClearProgress(name, progressLine-1);
}
public void DrawProgress(string name, int progressLine, double progress, TimeSpan eta, double speed)
{
if (progressLine == 0)
_statusBar.Percent = progress;
else
_progress.DrawProgress(name, progressLine - 1, progress, eta, speed);
}
public void Log(string prefix, ConsoleColor color, string msg)

View File

@ -39,4 +39,21 @@ public partial class FileListViewModel : ObservableObject
Selected = Files.LastOrDefault();
}
internal void DeleteSelected()
{
if (SelectedFiles.Any())
{
var toDelete = SelectedFiles.ToList();
foreach (var item in toDelete)
Files.Remove(item);
}
else if ( Selected != null)
{
var sel = Selected;
Files.Remove(sel);
}
Selected = Files.LastOrDefault();
}
}

View File

@ -16,6 +16,13 @@ public partial class InspectorPaneViewModel : ObservableObject
"face", "body", "none"
];
[RelayCommand]
private void TransformAll()
{
_ = _main.Start();
}
[RelayCommand]
private void ApplyOverrides()
{
@ -43,12 +50,16 @@ public partial class InspectorPaneViewModel : ObservableObject
public IRelayCommand RotateLeftCommand { get; }
public IRelayCommand RotateRightCommand { get; }
private MainViewModel _main = null!;
public InspectorPaneViewModel()
{
RotateLeftCommand = new RelayCommand(() => AdjustRotation(-90));
RotateRightCommand = new RelayCommand(() => AdjustRotation(+90));
}
public void SetMain(MainViewModel main) => _main = main;
private void AdjustRotation(int delta)
{
if ( Selected == null)

View File

@ -11,7 +11,9 @@ namespace Splitter_UI.ViewModels;
public partial class JobViewModel : ObservableObject
{
private SingleJob Job { get; }
public SingleJob GetJob() => Job;
[ObservableProperty] private VideoInfo? _probe;
[ObservableProperty] private PreviewData? _preview = new(null, [], null, new(0.5f, 0.5f));
[ObservableProperty] private Bitmap? _thumbnail;
@ -366,4 +368,5 @@ public partial class JobViewModel : ObservableObject
{
Task.Run(CreatePreview);
}
}

View File

@ -1,4 +1,4 @@
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.ComponentModel;
namespace Splitter_UI.ViewModels;
@ -9,41 +9,74 @@ public partial class MainViewModel : ViewModelBase
public InspectorPaneViewModel Inspector { get; }
public StatusBarViewModel StatusBar { get; }
public LogPaneViewModel LogPane { get; }
public ProgressViewModel Progress { get; }
private IJobProcessor _processor = null!;
[ObservableProperty] private bool _transformMode = false;
private ILogger _logger;
public MainViewModel(
IFileJobFactory fileJobFactory,
IAutoDecisionService autoDecisionService,
FileListViewModel fileListVM,
PreviewPaneViewModel ppVM,
InspectorPaneViewModel iVM,
LogPaneViewModel lpVM,
StatusBarViewModel sbVM
StatusBarViewModel sbVM,
ProgressViewModel pVM,
IJobProcessor processor,
ILogger logger
)
{
FileList = new FileListViewModel(fileJobFactory, autoDecisionService);
Preview = ppVM;
Inspector = iVM;
LogPane = lpVM;
StatusBar = sbVM;
// Wire selection → preview + inspector
FileList = fileListVM;
Preview = ppVM;
Inspector = iVM;
LogPane = lpVM;
StatusBar = sbVM;
Progress = pVM;
_processor = processor;
_logger = logger;
// Wire selection -> preview + inspector
FileList.SelectedFileChanged += file =>
{
Preview.Selected = file;
Inspector.Selected = file;
};
Inspector.SetMain(this);
Inspector.Files = FileList.Files;
}
[RelayCommand]
private void Start()
public async Task Start()
{
StatusBar.StatusText = "Processing…";
// call IProcessingService here
try
{
StatusBar.StatusText = "Processing…";
StatusBar.Percent = 0;
TransformMode = true;
var files = FileList.Files.ToList();
var jobs = new List<SingleTask>();
foreach (var file in files)
{
var fileJobs = await _processor.GenerateJobs(file.GetJob(), false);
jobs.AddRange(fileJobs);
}
await _processor.ProcessJobs(jobs, false);
}
catch (Exception ex)
{
// Handle exception
StatusBar.StatusText = "Error occurred…";
_logger.LogError($"Error: {ex.Message}");
}
finally
{
StatusBar.StatusText = "Ready…";
StatusBar.Percent = 0;
TransformMode = false;
}
}
[RelayCommand]
private void Stop()
{
StatusBar.StatusText = "Stopped";
}
}

View File

@ -0,0 +1,43 @@
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
namespace Splitter_UI.ViewModels;
public record ProgressInfo(string Name, int ProgressLine, double Progress, TimeSpan Eta, double Speed);
public partial class ProgressViewModel : ObservableObject
{
[ObservableProperty] private int _numberOfProcesses = 0;
public ObservableCollection<ProgressInfo> Processes { get; } = [];
private Lock _lock = new();
public void ClearProgress(string name, int progressLine)
{
lock (_lock)
{
if (progressLine < 0 || progressLine > Processes.Count)
return;
NumberOfProcesses -= 1;
Processes[progressLine] = new ProgressInfo("", progressLine, 0, TimeSpan.Zero, 0);
}
}
public void DrawProgress(string name, int progressLine, double progress, TimeSpan eta, double speed)
{
lock (_lock)
{
if (progressLine < 0)
return;
while (Processes.Count <= progressLine)
{
Processes.Add(new ProgressInfo("", Processes.Count, 0, TimeSpan.Zero, 0));
}
if (Processes[progressLine].Name == "")
NumberOfProcesses += 1;
Processes[progressLine] = new ProgressInfo(name, progressLine, progress, eta, speed);
}
}
}

View File

@ -10,6 +10,4 @@ public partial class StatusBarViewModel : ObservableObject
[ObservableProperty]
private double _percent;
[ObservableProperty]
private string _threadInfo = "Threads: 0/0";
}

View File

@ -5,7 +5,9 @@
xmlns:views="clr-namespace:Splitter_UI.Views"
xmlns:conv="clr-namespace:Splitter_UI.Converters"
x:Class="Splitter_UI.Views.FileListView"
x:DataType="vm:FileListViewModel">
x:DataType="vm:FileListViewModel"
KeyDown="OnKeyDown"
Focusable="True">
<UserControl.Resources>
<conv:ZeroToBoolConverter x:Key="ZeroToBoolConverter"/>

View File

@ -21,6 +21,14 @@ public partial class FileListView : UserControl
InitializeComponent();
}
private void OnKeyDown(object? sender, KeyEventArgs e)
{
if (e.Key == Key.Delete)
{
if (DataContext is FileListViewModel vm)
vm.DeleteSelected();
}
}
private void OnDragEnter(object? sender, DragEventArgs e)
{
IsDragActive = true;

View File

@ -147,10 +147,21 @@ x:DataType="vm:InspectorPaneViewModel">
<TextBox Text="{Binding Selected.PassthroughText}" Width="260"/>
</StackPanel>
<Button Content="Apply to Selected"
Command="{Binding ApplyOverridesCommand}"
HorizontalAlignment="Right"
Margin="0,10,0,0"/>
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Right"
Spacing="8"
Margin="0,10,0,0">
<Button Content="Apply to Selected"
Command="{Binding ApplyOverridesCommand}"/>
<Button Content="Transform all"
Background="#AA0000"
Foreground="White"
Command="{Binding TransformAllCommand}"/>
</StackPanel>
</StackPanel>
</ScrollViewer>

View File

@ -3,12 +3,18 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:views="clr-namespace:Splitter_UI.Views"
xmlns:vm="clr-namespace:Splitter_UI.ViewModels"
xmlns:conv="clr-namespace:Splitter_UI.Converters"
x:Class="Splitter_UI.Views.MainWindow"
x:DataType="vm:MainViewModel"
x:Name="Root"
Width="1400"
Height="950"
Title="Splitter UI">
<Window.Resources>
<conv:BoolInvertConverter x:Key="BoolInvertConverter"/>
</Window.Resources>
<DockPanel>
<!-- Status Bar -->
@ -19,21 +25,28 @@
<views:LogPane DockPanel.Dock="Bottom" Height="150"
DataContext="{Binding LogPane}" />
<!-- Main Content -->
<Grid ColumnDefinitions="2*,3*,430">
<Grid>
<!-- Main Content -->
<Grid ColumnDefinitions="2*,3*,430" IsVisible="{Binding TransformMode, Converter={StaticResource BoolInvertConverter}}">
<!-- File List -->
<views:FileListView Grid.Column="0"
DataContext="{Binding FileList}" />
<!-- File List -->
<views:FileListView Grid.Column="0"
DataContext="{Binding FileList}" />
<!-- Preview -->
<views:PreviewPane Grid.Column="1"
DataContext="{Binding Preview}" />
<!-- Preview -->
<views:PreviewPane Grid.Column="1"
DataContext="{Binding Preview}" />
<!-- Inspector -->
<views:InspectorPane Grid.Column="2"
DataContext="{Binding Inspector}" />
<!-- Inspector -->
<views:InspectorPane Grid.Column="2"
DataContext="{Binding Inspector}" />
</Grid>
<!-- Progress view (replaces entire grid) -->
<views:ProgressView
DataContext="{Binding Progress}"
IsVisible="{Binding #Root.DataContext.TransformMode}"/>
</Grid>
</DockPanel>

View File

@ -0,0 +1,50 @@
<UserControl
x:Class="Splitter_UI.Views.ProgressView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Splitter_UI.ViewModels"
x:DataType="vm:ProgressViewModel">
<Border Background="#111" Padding="8">
<ItemsControl ItemsSource="{Binding Processes}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:ProgressInfo">
<Grid ColumnDefinitions="2*,3*,Auto,Auto"
Margin="0,2">
<!-- Name -->
<TextBlock Grid.Column="0"
Text="{Binding Name}"
VerticalAlignment="Center"
FontSize="12"/>
<!-- Progress bar -->
<ProgressBar Grid.Column="1"
Height="12"
Minimum="0"
Maximum="1"
Value="{Binding Progress}"
Margin="8,0"/>
<!-- ETA -->
<TextBlock Grid.Column="2"
Width="70"
Text="{Binding Eta, StringFormat={}{0:hh\\:mm\\:ss}}"
VerticalAlignment="Center"
Margin="12,0"
FontSize="12"/>
<!-- Speed -->
<TextBlock Grid.Column="3"
Width="70"
Text="{Binding Speed, StringFormat={}{0:0.00}}"
VerticalAlignment="Center"
Margin="12,0"
FontSize="12"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</UserControl>

View File

@ -0,0 +1,12 @@
using Avalonia.Controls;
namespace Splitter_UI.Views;
public partial class ProgressView : UserControl
{
public ProgressView()
{
InitializeComponent();
}
}

View File

@ -12,13 +12,13 @@
Text="{Binding StatusText}" />
<ProgressBar Grid.Column="1"
Width="200" Height="16"
Width="200"
Height="16"
Minimum="0"
Maximum="1"
VerticalAlignment="Center"
Value="{Binding Percent}" />
<TextBlock Grid.Column="2"
VerticalAlignment="Center"
Text="{Binding ThreadInfo}" />
</Grid>
</Border>
</UserControl>