using System; using System.IO; using System.Threading.Tasks; namespace ClipForge { public class ThumbnailService { private string _ffmpegPath; public ThumbnailService() { _ffmpegPath = Path.Combine(AppContext.BaseDirectory, "ffmpeg.exe"); } public async Task GenerateThumbnailAsync(ClipFile clip) { if (!File.Exists(_ffmpegPath)) return; if (!File.Exists(clip.Path)) return; if (File.Exists(clip.ThumbnailPath)) return; try { // Extract frame at 1 second mark var args = $"-ss 00:00:01 -i \"{clip.Path}\" " + $"-vframes 1 -q:v 2 " + $"-vf scale=220:124:force_original_aspect_ratio=increase," + $"crop=220:124 " + $"\"{clip.ThumbnailPath}\""; var process = new System.Diagnostics.Process { StartInfo = new System.Diagnostics.ProcessStartInfo { FileName = _ffmpegPath, Arguments = args, UseShellExecute = false, CreateNoWindow = true, RedirectStandardError = true } }; process.Start(); await Task.Run(() => process.WaitForExit(5000)); // Notify the clip that thumbnail is ready if (File.Exists(clip.ThumbnailPath)) { clip.ThumbnailPath = clip.ThumbnailPath; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine( $"Thumbnail error for {clip.Title}: {ex.Message}"); } } public async Task GenerateAllThumbnailsAsync( System.Collections.ObjectModel.ObservableCollection clips) { foreach (var clip in clips) { await GenerateThumbnailAsync(clip); } } public void DeleteThumbnail(ClipFile clip) { if (File.Exists(clip.ThumbnailPath)) File.Delete(clip.ThumbnailPath); } } }