using System.Collections.ObjectModel; using System.IO; using System.Windows; using Azaion.Annotator.DTO; using Azaion.Annotator.Extensions; using LibVLCSharp.Shared; using MediatR; using Microsoft.WindowsAPICodePack.Dialogs; using Newtonsoft.Json; using Point = System.Windows.Point; using Size = System.Windows.Size; namespace Azaion.Annotator; public partial class MainWindow { private readonly LibVLC _libVLC; private readonly MediaPlayer _mediaPlayer; private readonly IMediator _mediator; private readonly FormState _formState; private readonly Config _config; private readonly HelpWindow _helpWindow; public ObservableCollection AnnotationClasses { get; set; } = new(); private bool _suspendLayout; public Dictionary> Annotations { get; set; } = new(); public Dictionary> AnnotationResults { get; set; } = new(); public MainWindow(LibVLC libVLC, MediaPlayer mediaPlayer, IMediator mediator, FormState formState, Config config, HelpWindow helpWindow) { InitializeComponent(); _libVLC = libVLC; _mediaPlayer = mediaPlayer; _mediator = mediator; _formState = formState; _config = config; _helpWindow = helpWindow; VideoView.Loaded += VideoView_Loaded; Closed += OnFormClosed; } private void VideoView_Loaded(object sender, RoutedEventArgs e) { Core.Initialize(); InitControls(); _suspendLayout = true; Left = _config.WindowLocation.X; Top = _config.WindowLocation.Y; Width = _config.WindowSize.Width; Height = _config.WindowSize.Height; _suspendLayout = false; ReloadFiles(); if (_config.AnnotationClasses.Count == 0) _config.AnnotationClasses.Add(new AnnotationClass(0)); AnnotationClasses = new ObservableCollection(_config.AnnotationClasses); LvClasses.ItemsSource = AnnotationClasses; LvClasses.SelectedIndex = 0; if (LvFiles.Items.IsEmpty) BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.Initial]); if (_config.ShowHelpOnStart) _helpWindow.Show(); } public void BlinkHelp(string helpText, int times = 2) { _ = Task.Run(async () => { for (int i = 0; i < times; i++) { Dispatcher.Invoke(() => StatusHelp.Text = helpText); await Task.Delay(200); Dispatcher.Invoke(() => StatusHelp.Text = ""); await Task.Delay(200); } Dispatcher.Invoke(() => StatusHelp.Text = helpText); }); } private void InitControls() { VideoView.MediaPlayer = _mediaPlayer; _mediaPlayer.Playing += (sender, args) => { uint vw = 0, vh = 0; _mediaPlayer.Size(0, ref vw, ref vh); _formState.CurrentVideoSize = new Size(vw, vh); _formState.CurrentVideoLength = TimeSpan.FromMilliseconds(_mediaPlayer.Length); }; LvFiles.MouseDoubleClick += async (_, _) => await _mediator.Publish(new PlaybackControlEvent(PlaybackControlEnum.Play)); LvClasses.SelectionChanged += (_, _) => { var selectedClass = (AnnotationClass)LvClasses.SelectedItem; Editor.CurrentAnnClass = selectedClass; _mediator.Publish(new AnnClassSelectedEvent(selectedClass)); }; _mediaPlayer.PositionChanged += (o, args) => { Dispatcher.Invoke(() => VideoSlider.Value = _mediaPlayer.Position * VideoSlider.Maximum); Dispatcher.Invoke(() => StatusClock.Text = $"{TimeSpan.FromMilliseconds(_mediaPlayer.Time):mm\\:ss} / {_formState.CurrentVideoLength:mm\\:ss}"); var time = TimeSpan.FromMilliseconds(_mediaPlayer.Time); if (!Annotations.TryGetValue(time, out var annotations)) return; foreach (var ann in annotations) { var annClass = _config.AnnotationClasses[ann.ClassNumber]; var annInfo = new CanvasLabel(ann, Editor.RenderSize, _formState.CurrentVideoSize); Dispatcher.Invoke(() => Editor.CreateAnnotation(annClass, time, annInfo)); } Dispatcher.Invoke(() => Editor.ClearExpiredAnnotations(time)); }; VideoSlider.ValueChanged += (value, newValue) => _mediaPlayer.Position = (float)(newValue / VideoSlider.Maximum); VideoSlider.KeyDown += (sender, args) => _mediator.Publish(new KeyEvent(sender, args)); Volume.ValueChanged += (_, newValue) => _mediator.Publish(new VolumeChangedEvent((int)newValue)); SizeChanged += (sender, args) => { if (!_suspendLayout) _config.WindowSize = args.NewSize; }; LocationChanged += (_, _) => { if (!_suspendLayout) _config.WindowLocation = new Point(Left, Top); }; Editor.FormState = _formState; Editor.Mediator = _mediator; } public void LoadExistingAnnotations() { Annotations = LoadAnnotations(); _formState.AnnotationResults = LoadAnnotationResults(); } private Dictionary> LoadAnnotations() { var labelDir = new DirectoryInfo(_config.LabelsDirectory); if (!labelDir.Exists) return new Dictionary>(); var labelFiles = labelDir.GetFiles($"{_formState.VideoName}_*"); return labelFiles.Select(x => { var name = Path.GetFileNameWithoutExtension(x.Name); return new { Name = x.FullName, Time = _formState.GetTime(name) }; }).ToDictionary(f => f.Time!.Value, f => { var str = File.ReadAllText(f.Name); return str.Split(Environment.NewLine).Select(YoloLabel.Parse) .Where(x => x != null) .ToList(); })!; } private List LoadAnnotationResults() { var resultDir = new DirectoryInfo(_config!.ResultsDirectory); if (!resultDir.Exists) Directory.CreateDirectory(_config!.ResultsDirectory); var resultsFile = resultDir.GetFiles($"{_formState!.CurrentFile}*").FirstOrDefault(); List results; if (resultsFile == null) { results = Annotations.Select(anns => new AnnotationResult(anns.Key, _formState.GetTimeName(anns.Key), anns.Value)) .ToList(); File.WriteAllText($"{_config.ResultsDirectory}/{_formState.VideoName}.json", JsonConvert.SerializeObject(results, Formatting.Indented)); } else results = JsonConvert.DeserializeObject>(File.ReadAllText(File.ReadAllText(resultsFile.FullName)))!; return results; } private void ReloadFiles() { var dir = new DirectoryInfo(_config.VideosDirectory); if (!dir.Exists) return; var files = dir.GetFiles("mp4", "mov").Select(x => { _mediaPlayer.Media = new Media(_libVLC, x.FullName); return new VideoFileInfo { Name = x.Name, Path = x.FullName, Duration = TimeSpan.FromMilliseconds(_mediaPlayer.Media.Duration) }; }).ToList(); LvFiles.ItemsSource = new ObservableCollection(files); TbFolder.Text = _config.VideosDirectory; BlinkHelp(files.Count == 0 ? HelpTexts.HelpTextsDict[HelpTextEnum.Initial] : HelpTexts.HelpTextsDict[HelpTextEnum.PlayVideo]); } private void OnFormClosed(object? sender, EventArgs e) { _mediaPlayer.Stop(); _mediaPlayer.Dispose(); _libVLC.Dispose(); _config.AnnotationClasses = AnnotationClasses.ToList(); _config.Save(); Application.Current.Shutdown(); } // private void AddClassBtnClick(object sender, RoutedEventArgs e) // { // LvClasses.IsReadOnly = false; // AnnotationClasses.Add(new AnnotationClass(AnnotationClasses.Count)); // LvClasses.SelectedIndex = AnnotationClasses.Count - 1; // } private void OpenFolderItemClick(object sender, RoutedEventArgs e) => OpenFolder(); private void OpenFolderButtonClick(object sender, RoutedEventArgs e) => OpenFolder(); private void OpenFolder() { var dlg = new CommonOpenFileDialog { Title = "Open Video folder", IsFolderPicker = true, InitialDirectory = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory) }; if (dlg.ShowDialog() != CommonFileDialogResult.Ok) return; if (!string.IsNullOrEmpty(dlg.FileName)) _config.VideosDirectory = dlg.FileName; ReloadFiles(); } private void PlayClick(object sender, RoutedEventArgs e) => _mediator.Publish(new PlaybackControlEvent(PlaybackControlEnum.Play)); private void PauseClick(object sender, RoutedEventArgs e)=> _mediator.Publish(new PlaybackControlEvent(PlaybackControlEnum.Pause)); private void StopClick(object sender, RoutedEventArgs e) => _mediator.Publish(new PlaybackControlEvent(PlaybackControlEnum.Stop)); private void PreviousFrameClick(object sender, RoutedEventArgs e) => _mediator.Publish(new PlaybackControlEvent(PlaybackControlEnum.PreviousFrame)); private void NextFrameClick(object sender, RoutedEventArgs e) => _mediator.Publish(new PlaybackControlEvent(PlaybackControlEnum.NextFrame)); private void SaveAnnotationsClick(object sender, RoutedEventArgs e) => _mediator.Publish(new PlaybackControlEvent(PlaybackControlEnum.SaveAnnotations)); private void RemoveSelectedClick(object sender, RoutedEventArgs e) => _mediator.Publish(new PlaybackControlEvent(PlaybackControlEnum.RemoveSelectedAnns)); private void RemoveAllClick(object sender, RoutedEventArgs e) => _mediator.Publish(new PlaybackControlEvent(PlaybackControlEnum.RemoveAllAnns)); private void TurnOffVolume(object sender, RoutedEventArgs e) => _mediator.Publish(new PlaybackControlEvent(PlaybackControlEnum.TurnOffVolume)); private void TurnOnVolume(object sender, RoutedEventArgs e) => _mediator.Publish(new PlaybackControlEvent(PlaybackControlEnum.TurnOnVolume)); private void OpenHelpWindowClick(object sender, RoutedEventArgs e) { _helpWindow.Show(); _helpWindow.Activate(); } }