mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 22:46:30 +00:00
I like it move it move it
This commit is contained in:
@@ -0,0 +1,265 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using Azaion.Annotator.DTO;
|
||||
using Azaion.Annotator.Extensions;
|
||||
using LibVLCSharp.Shared;
|
||||
using MediatR;
|
||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
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;
|
||||
private readonly TimeSpan _annotationTime = TimeSpan.FromSeconds(1);
|
||||
|
||||
public ObservableCollection<AnnotationClass> AnnotationClasses { get; set; }
|
||||
private bool _suspendLayout;
|
||||
|
||||
public Dictionary<string, List<AnnotationInfo>> Annotations { get; set; } = new();
|
||||
|
||||
public bool ShowHelpOnStart { get; set; }
|
||||
|
||||
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<AnnotationClass>(_config.AnnotationClasses);
|
||||
LvClasses.ItemsSource = AnnotationClasses;
|
||||
LvClasses.SelectedIndex = 0;
|
||||
|
||||
if (LvFiles.Items.IsEmpty)
|
||||
BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.Initial]);
|
||||
|
||||
ShowHelpOnStart = _config.ShowHelpOnStart;
|
||||
if (ShowHelpOnStart)
|
||||
_helpWindow.Show();
|
||||
}
|
||||
|
||||
public void BlinkHelp(string helpText, int times = 3)
|
||||
{
|
||||
_ = 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 curTime = _formState.GetTimeName(TimeSpan.FromMilliseconds(_mediaPlayer.Time));
|
||||
if (!Annotations.TryGetValue(curTime, out var annotationInfos))
|
||||
return;
|
||||
|
||||
var annotations = annotationInfos.Select(info =>
|
||||
{
|
||||
var annClass = _config.AnnotationClasses[info.ClassNumber];
|
||||
var annInfo = info.ToCanvasCoordinates(Editor.RenderSize, _formState.CurrentVideoSize);
|
||||
var annotation = Dispatcher.Invoke(() => Editor.CreateAnnotation(annClass, annInfo));
|
||||
return annotation;
|
||||
}).ToList();
|
||||
|
||||
//remove annotations: either in 1 sec, either earlier if there is next annotation in a dictionary
|
||||
var strs = curTime.Split("_");
|
||||
var timeStr = strs.LastOrDefault();
|
||||
var ts = TimeSpan.FromMilliseconds(int.Parse(timeStr)*100);
|
||||
var timeSpanRemove = Enumerable.Range(1, ((int)_annotationTime.TotalMilliseconds / 100) - 1)
|
||||
.Select(x =>
|
||||
{
|
||||
var time = TimeSpan.FromMilliseconds(x * 100);
|
||||
var fName = _formState.GetTimeName(ts.Add(time));
|
||||
return Annotations.ContainsKey(fName) ? time : (TimeSpan?)null;
|
||||
}).FirstOrDefault(x => x != null) ?? _annotationTime;
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(timeSpanRemove);
|
||||
Dispatcher.Invoke(() => Editor.RemoveAnnotations(annotations));
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
videoSlider.ValueChanged += (value, newValue) =>
|
||||
_mediaPlayer.Position = (float)(newValue / videoSlider.Maximum);
|
||||
|
||||
videoSlider.KeyDown += (sender, args) => _mediator.Publish(new KeyEvent(sender, args));
|
||||
|
||||
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()
|
||||
{
|
||||
var dirInfo = new DirectoryInfo(_config.LabelsDirectory);
|
||||
if (!dirInfo.Exists)
|
||||
return;
|
||||
|
||||
var files = dirInfo.GetFiles($"{_formState.VideoName}_*");
|
||||
Annotations = files.ToDictionary(f => Path.GetFileNameWithoutExtension(f.Name), f =>
|
||||
{
|
||||
var str = File.ReadAllText(f.FullName);
|
||||
return str.Split(Environment.NewLine).Select(AnnotationInfo.Parse)
|
||||
.Where(x => x != null)
|
||||
.ToList();
|
||||
})!;
|
||||
}
|
||||
|
||||
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<VideoFileInfo>(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(Assembly.GetExecutingAssembly().Location)
|
||||
};
|
||||
if (dlg.ShowDialog() != CommonFileDialogResult.Ok)
|
||||
return;
|
||||
|
||||
_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 OpenHelpWindowClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_helpWindow.Show();
|
||||
_helpWindow.Activate();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user