Files
clipforge/ClipForge/TrimmerWindow.xaml.cs
Blade / SCOPEDD c1a425a7ad Add project files.
2026-02-22 11:31:35 -05:00

196 lines
6.2 KiB
C#

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Windows.Media.Core;
using Windows.Media.Playback;
namespace ClipForge
{
public sealed partial class TrimmerWindow : Window
{
private string _clipPath;
private TimeSpan _duration;
private TimeSpan _inPoint;
private TimeSpan _outPoint;
private bool _isPlaying;
private bool _scrubbing;
private MediaPlayer _mediaPlayer;
private DispatcherTimer _timer;
public TrimmerWindow(string clipPath)
{
this.InitializeComponent();
_clipPath = clipPath;
this.AppWindow.Resize(new Windows.Graphics.SizeInt32(800, 560));
this.AppWindow.SetPresenter(
Microsoft.UI.Windowing.AppWindowPresenterKind.Overlapped);
LoadVideo();
}
private void LoadVideo()
{
_mediaPlayer = new MediaPlayer();
_mediaPlayer.Source = MediaSource.CreateFromUri(
new Uri(_clipPath));
_mediaPlayer.MediaOpened += OnMediaOpened;
MediaPlayer.SetMediaPlayer(_mediaPlayer);
// Timer to update scrubber position
_timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(250)
};
_timer.Tick += OnTimerTick;
_timer.Start();
}
private void OnMediaOpened(MediaPlayer sender, object args)
{
_duration = sender.NaturalDuration;
_outPoint = _duration;
DispatcherQueue.TryEnqueue(() =>
{
DurationLabel.Text = FormatTime(_duration);
OutPointLabel.Text = FormatTime(_duration);
ScrubberSlider.Maximum = _duration.TotalSeconds;
});
}
private void OnTimerTick(object? sender, object e)
{
if (_scrubbing || !_isPlaying) return;
ScrubberSlider.Value = _mediaPlayer.Position.TotalSeconds;
}
private void ScrubberSlider_ValueChanged(object sender,
Microsoft.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
{
if (_mediaPlayer == null) return;
_scrubbing = true;
_mediaPlayer.Position = TimeSpan.FromSeconds(e.NewValue);
_scrubbing = false;
}
private void PlayButton_Click(object sender, RoutedEventArgs e)
{
if (_isPlaying)
{
_mediaPlayer.Pause();
PlayButton.Content = new TextBlock
{
Text = "\uE768",
FontFamily = new Microsoft.UI.Xaml.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 14
};
_isPlaying = false;
}
else
{
_mediaPlayer.Play();
PlayButton.Content = new TextBlock
{
Text = "\uE769",
FontFamily = new Microsoft.UI.Xaml.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 14
};
_isPlaying = true;
}
}
private void SetInPoint_Click(object sender, RoutedEventArgs e)
{
_inPoint = _mediaPlayer.Position;
InPointLabel.Text = FormatTime(_inPoint);
}
private void SetOutPoint_Click(object sender, RoutedEventArgs e)
{
_outPoint = _mediaPlayer.Position;
OutPointLabel.Text = FormatTime(_outPoint);
}
private async void ExportTrim_Click(object sender, RoutedEventArgs e)
{
if (_inPoint >= _outPoint)
{
var dialog = new ContentDialog
{
Title = "Invalid Range",
Content = "In point must be before out point.",
CloseButtonText = "OK",
XamlRoot = this.Content.XamlRoot
};
await dialog.ShowAsync();
return;
}
_mediaPlayer.Pause();
_isPlaying = false;
var outputDir = Path.GetDirectoryName(_clipPath)!;
var fileName = Path.GetFileNameWithoutExtension(_clipPath);
var outputPath = Path.Combine(outputDir,
$"{fileName}_trimmed_{DateTime.Now:HHmmss}.mp4");
await TrimWithFfmpegAsync(_clipPath, outputPath,
_inPoint, _outPoint);
var successDialog = new ContentDialog
{
Title = "Export Complete",
Content = $"Saved as {Path.GetFileName(outputPath)}",
CloseButtonText = "OK",
XamlRoot = this.Content.XamlRoot
};
await successDialog.ShowAsync();
this.Close();
}
private async Task TrimWithFfmpegAsync(
string input, string output,
TimeSpan inPoint, TimeSpan outPoint)
{
var ffmpegPath = Path.Combine(AppContext.BaseDirectory, "ffmpeg.exe");
if (!File.Exists(ffmpegPath)) return;
var start = inPoint.ToString(@"hh\:mm\:ss\.fff");
var duration = (outPoint - inPoint).ToString(@"hh\:mm\:ss\.fff");
var args = $"-ss {start} -i \"{input}\" -t {duration} -c copy \"{output}\"";
var process = new System.Diagnostics.Process
{
StartInfo = new System.Diagnostics.ProcessStartInfo
{
FileName = ffmpegPath,
Arguments = args,
UseShellExecute = false,
CreateNoWindow = true
}
};
process.Start();
await Task.Run(() => process.WaitForExit());
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
_mediaPlayer?.Pause();
_timer?.Stop();
this.Close();
}
private static string FormatTime(TimeSpan t)
{
return t.TotalHours >= 1
? t.ToString(@"h\:mm\:ss")
: t.ToString(@"m\:ss");
}
}
}