using MediaToolkit; using MediaToolkit.Model; using MediaToolkit.Options; using RyzStudio.IO; using RyzStudio.Windows.Forms; using System; using System.Diagnostics; using System.Drawing; using System.IO; using System.Threading.Tasks; using System.Windows.Forms; namespace VideoPreview { public partial class MainForm : Form { protected readonly Random randy; protected OptionsForm optionsForm = null; protected bool isBusy = false; protected string videoFilename = null; protected TimeSpan videoDuration = TimeSpan.FromSeconds(0); public MainForm() { InitializeComponent(); button2.SetIcon("settings"); button3.SetIcon("refresh-cw"); button4.SetIcon("arrow-right-circle"); randy = new Random(); textBox1.TextBox.ReadOnly = true; textBox1.TextBox.BackColor = Color.White; textBox1.TextBox.TextChanged += textBox1_TextChanged; } protected override void OnLoad(EventArgs e) { base.OnLoad(e); textBox1_TextChanged(null, null); } protected override void OnFormClosing(FormClosingEventArgs e) { if (this.IsBusy) { e.Cancel = true; return; } base.OnFormClosing(e); } public bool IsBusy { get => isBusy; set { isBusy = value; ThreadControl.SetValue(pictureBox1, (isBusy ? UIcon.GetImage("loading_block") : null)); ThreadControl.SetEnable(textBox1, !isBusy); ThreadControl.SetEnable(button1, !isBusy); ThreadControl.SetEnable(button2, !isBusy); ThreadControl.SetEnable(button3, !isBusy); ThreadControl.SetEnable(button4, !isBusy); } } private void Form1_DragOver(object sender, DragEventArgs e) { e.Effect = (e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.Copy : DragDropEffects.None); } private async void Form1_DragDrop(object sender, DragEventArgs e) { string[] fileList = e.Data.GetData(DataFormats.FileDrop) as string[]; if (fileList == null) { return; } if (fileList.Length <= 0) { return; } await ReadVideoFile(fileList[0]); } private async void textBox1_TextChanged(object sender, EventArgs e) { bool isValidPath = !string.IsNullOrWhiteSpace(textBox1.Text); if (!isValidPath) { ThreadControl.SetEnable(button3, isValidPath); ThreadControl.SetEnable(button4, isValidPath); return; } await ReadVideoFile(textBox1.Text); } public AppSession CurrentSession { get; set; } = new AppSession(); #region main menu /// /// Refresh /// /// /// private void refreshToolStripMenuItem_Click(object sender, EventArgs e) => button3_MouseClick(null, null); /// /// Load next file /// /// /// private void loadNextFileToolStripMenuItem_Click(object sender, EventArgs e) => button4_MouseClick(null, null); /// /// Options /// /// /// private void optionsToolStripMenuItem_Click(object sender, EventArgs e) => button2_MouseClick(null, null); /// /// View help /// /// /// private void viewHelpToolStripMenuItem_Click(object sender, EventArgs e) { try { System.Diagnostics.Process.Start(new ProcessStartInfo() { FileName = AppResource.AppHelpURL, UseShellExecute = true }); } catch { // do nothing } } /// /// About /// /// /// private void aboutToolStripMenuItem_Click(object sender, EventArgs e) { MessageBox.Show(Application.ProductName + " v" + Application.ProductVersion, "About", MessageBoxButtons.OK, MessageBoxIcon.Information); } #endregion /// /// Refresh /// /// /// private async void button3_MouseClick(object sender, MouseEventArgs e) { if (this.IsBusy) return; if (string.IsNullOrWhiteSpace(textBox1.Text)) return; await ReadVideoFile(textBox1.Text); } /// /// Options /// /// /// private void button2_MouseClick(object sender, MouseEventArgs e) { if (this.IsBusy) { return; } if (optionsForm == null) optionsForm = new OptionsForm(this.CurrentSession); if (optionsForm.ShowDialog() == DialogResult.OK) { this.CurrentSession = optionsForm.Session; this.TopMost = this.CurrentSession.AlwaysOnTop; } } /// /// Next file /// /// /// private async void button4_MouseClick(object sender, MouseEventArgs e) { await Task.Run(() => { if (this.IsBusy) return; if (string.IsNullOrWhiteSpace(textBox1.Text)) return; string path = AccessibleDirectory.GetNextFile(textBox1.Text, "*.avi;*.mkv;*.mp4;*.ogm;*.mov;*.mpg;*.mpeg"); if (string.IsNullOrWhiteSpace(path)) return; textBox1.Text = path; }); } /// /// Close /// /// /// private void button1_MouseClick(object sender, MouseEventArgs e) { this.Close(); } protected TimeSpan CalcRandomTimeSpan(TimeSpan duration) { int mm = randy.Next(0, (int)Math.Floor(duration.TotalMinutes)); int ss = randy.Next(0, duration.Seconds); return new TimeSpan(0, mm, ss); } protected void Clear() { videoFilename = null; videoDuration = TimeSpan.FromSeconds(0); ThreadControl.SetText(this, AppResource.AppName); textBox1.Text = string.Empty; ThreadControl.SetText(label5, "-"); ThreadControl.SetText(label7, "-"); ThreadControl.SetText(label2, "-"); ThreadControl.Clear(flowLayoutPanel1); } protected Label CreateLabel(TimeSpan duration) { Label label = new Label(); label.Padding = new Padding(0); label.Margin = new Padding(0, 0, 0, 10); label.Text = string.Format("{0}h {1}m {2}s", duration.Hours, duration.Minutes, duration.Seconds); return label; } protected PictureBox CreatePictureBox(string filename, int width, int height) { PictureBox imageBox = new PictureBox() { BackColor = Color.White, Margin = new Padding(0, 0, 0, 0), Padding = new Padding(0), SizeMode = PictureBoxSizeMode.Zoom, Width = width, Height = height }; imageBox.Load(filename); return imageBox; } protected void DeleteFile(string filename) { try { File.Delete(filename); } catch { // do nothing } } protected decimal ParseFrameSizeRatio(string videoSize) { if (string.IsNullOrWhiteSpace(videoSize)) { return 0; } string[] parts = videoSize.Trim().Split("x"); if (parts.Length != 2) { return 0; } int w; if (!int.TryParse(parts[0], out w)) { return 0; } int h; if (!int.TryParse(parts[1], out h)) { return 0; } try { return decimal.Divide(w, h); } catch { return 0; } } protected async Task ReadVideoFile(string filename) { await Task.Run(() => { if (this.IsBusy) return; this.IsBusy = true; Clear(); string ffmpegPath = Path.GetDirectoryName(Application.ExecutablePath) + @"\ffmpeg.exe"; MediaFile inputFile = new MediaFile { Filename = filename }; using (Engine engine = new Engine(ffmpegPath)) { try { engine.GetMetadata(inputFile); } catch (Exception exc) { MessageBox.Show(exc.Message); this.IsBusy = false; return; } } if (inputFile.Metadata == null) { this.IsBusy = false; return; } videoFilename = filename; videoDuration = inputFile.Metadata.Duration; ThreadControl.SetText(this, Path.GetFileName(filename) + " - " + AppResource.AppName); textBox1.Text = videoFilename; ThreadControl.SetText(label5, inputFile.Metadata.VideoData.Format ?? string.Empty); ThreadControl.SetText(label7, (inputFile.Metadata.VideoData.FrameSize ?? string.Empty) + " @ " + inputFile.Metadata.VideoData.Fps.ToString() + "FPS"); ThreadControl.SetText(label2, string.Format("{0}h {1}m {2}s", videoDuration.Hours, videoDuration.Minutes, videoDuration.Seconds)); // calculate frame dimensions ratio decimal frameRatio = ParseFrameSizeRatio(inputFile.Metadata.VideoData.FrameSize); if (frameRatio <= 0) { this.IsBusy = false; return; } int thumbnailWidth = flowLayoutPanel1.ClientSize.Width - flowLayoutPanel1.Padding.Horizontal - SystemInformation.VerticalScrollBarWidth; int thumbnailHeight = (int)Math.Round(decimal.Divide((decimal)thumbnailWidth, frameRatio)); using (Engine engine = new Engine(ffmpegPath)) { for (int i = 0; i < this.CurrentSession.NoFrames; i++) { TimeSpan ts = CalcRandomTimeSpan(videoDuration); string thumbnailFilename = Path.ChangeExtension(Path.GetTempFileName(), "jpg"); ConversionOptions options = new ConversionOptions { Seek = ts, CustomWidth = thumbnailWidth, CustomHeight = thumbnailHeight }; engine.GetThumbnail(inputFile, new MediaFile { Filename = thumbnailFilename }, options); PictureBox imageBox = CreatePictureBox(thumbnailFilename, thumbnailWidth, thumbnailHeight); ThreadControl.Add(flowLayoutPanel1, imageBox); Label label = CreateLabel(ts); ThreadControl.Add(flowLayoutPanel1, label); // clean-up DeleteFile(thumbnailFilename); } } this.IsBusy = false; }); } } }