424 lines
13 KiB
C#
424 lines
13 KiB
C#
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
|
|
|
|
/// <summary>
|
|
/// Refresh
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void refreshToolStripMenuItem_Click(object sender, EventArgs e) => button3_MouseClick(null, null);
|
|
|
|
/// <summary>
|
|
/// Load next file
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void loadNextFileToolStripMenuItem_Click(object sender, EventArgs e) => button4_MouseClick(null, null);
|
|
|
|
/// <summary>
|
|
/// Options
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void optionsToolStripMenuItem_Click(object sender, EventArgs e) => button2_MouseClick(null, null);
|
|
|
|
/// <summary>
|
|
/// View help
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void viewHelpToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
try
|
|
{
|
|
System.Diagnostics.Process.Start(new ProcessStartInfo()
|
|
{
|
|
FileName = AppResource.AppHelpURL,
|
|
UseShellExecute = true
|
|
});
|
|
}
|
|
catch
|
|
{
|
|
// do nothing
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// About
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
MessageBox.Show(Application.ProductName + " v" + Application.ProductVersion, "About", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Refresh
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private async void button3_MouseClick(object sender, MouseEventArgs e)
|
|
{
|
|
if (this.IsBusy) return;
|
|
if (string.IsNullOrWhiteSpace(textBox1.Text)) return;
|
|
|
|
await ReadVideoFile(textBox1.Text);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Options
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Next file
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
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;
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Close
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
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;
|
|
});
|
|
}
|
|
|
|
private void toolStripMenuItem3_Click(object sender, EventArgs e)
|
|
{
|
|
this.TopMost = !this.TopMost;
|
|
}
|
|
}
|
|
}
|