diff --git a/AppResource.Designer.cs b/AppResource.Designer.cs index 0f0121e..a8c3e5c 100644 --- a/AppResource.Designer.cs +++ b/AppResource.Designer.cs @@ -60,6 +60,15 @@ namespace VideoPreview { } } + /// + /// Looks up a localized string similar to Video Preview. + /// + internal static string AppName { + get { + return ResourceManager.GetString("AppName", resourceCulture); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/AppResource.resx b/AppResource.resx index 499f667..b87f38c 100644 --- a/AppResource.resx +++ b/AppResource.resx @@ -117,6 +117,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Video Preview + Resources\loading-block.gif;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/MainForm.Designer.cs b/MainForm.Designer.cs index 13c008c..513e7f5 100644 --- a/MainForm.Designer.cs +++ b/MainForm.Designer.cs @@ -42,6 +42,7 @@ namespace VideoPreview this.button2 = new RyzStudio.Windows.ThemedForms.TButton(); this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); this.button3 = new RyzStudio.Windows.ThemedForms.TButton(); + this.button4 = new RyzStudio.Windows.ThemedForms.TButton(); this.label4 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.label5 = new System.Windows.Forms.Label(); @@ -164,7 +165,7 @@ namespace VideoPreview this.textBox1.Name = "textBox1"; this.textBox1.NormalImage = ((System.Drawing.Image)(resources.GetObject("textBox1.NormalImage"))); this.textBox1.Padding = new System.Windows.Forms.Padding(10, 10, 9, 9); - this.textBox1.Size = new System.Drawing.Size(294, 35); + this.textBox1.Size = new System.Drawing.Size(286, 35); this.textBox1.SubmitButton = null; this.textBox1.TabIndex = 16; this.textBox1.UseSystemPasswordChar = false; @@ -173,14 +174,14 @@ namespace VideoPreview // this.button2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.button2.BackColor = System.Drawing.Color.Transparent; - this.button2.DefaultImage = null; - this.button2.DownImage = null; + this.button2.DefaultImage = global::VideoPreview.UIResource.cog2; + this.button2.DownImage = global::VideoPreview.UIResource.cog; this.button2.IsSelected = false; this.button2.LabelText = ""; this.button2.Location = new System.Drawing.Point(12, 705); this.button2.Margin = new System.Windows.Forms.Padding(10); this.button2.Name = "button2"; - this.button2.OverImage = null; + this.button2.OverImage = global::VideoPreview.UIResource.cog; this.button2.Padding = new System.Windows.Forms.Padding(4, 4, 3, 3); this.button2.Size = new System.Drawing.Size(37, 37); this.button2.TabIndex = 17; @@ -191,20 +192,38 @@ namespace VideoPreview // this.button3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.button3.BackColor = System.Drawing.Color.Transparent; - this.button3.DefaultImage = null; - this.button3.DownImage = null; + this.button3.DefaultImage = global::VideoPreview.UIResource.refresh2; + this.button3.DownImage = global::VideoPreview.UIResource.refresh; this.button3.IsSelected = false; this.button3.LabelText = ""; this.button3.Location = new System.Drawing.Point(410, 9); this.button3.Margin = new System.Windows.Forms.Padding(10); this.button3.Name = "button3"; - this.button3.OverImage = null; + this.button3.OverImage = global::VideoPreview.UIResource.refresh; this.button3.Padding = new System.Windows.Forms.Padding(4, 4, 3, 3); this.button3.Size = new System.Drawing.Size(35, 35); this.button3.TabIndex = 23; this.toolTip1.SetToolTip(this.button3, "Refresh"); this.button3.MouseClick += new System.Windows.Forms.MouseEventHandler(this.button3_MouseClick); // + // button4 + // + this.button4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.button4.BackColor = System.Drawing.Color.Transparent; + this.button4.DefaultImage = global::VideoPreview.UIResource.arrow_right2; + this.button4.DownImage = global::VideoPreview.UIResource.arrow_right; + this.button4.IsSelected = false; + this.button4.LabelText = ""; + this.button4.Location = new System.Drawing.Point(53, 705); + this.button4.Margin = new System.Windows.Forms.Padding(10); + this.button4.Name = "button4"; + this.button4.OverImage = global::VideoPreview.UIResource.arrow_right; + this.button4.Padding = new System.Windows.Forms.Padding(4, 4, 3, 3); + this.button4.Size = new System.Drawing.Size(37, 37); + this.button4.TabIndex = 24; + this.toolTip1.SetToolTip(this.button4, "Next"); + this.button4.MouseClick += new System.Windows.Forms.MouseEventHandler(this.button4_MouseClick); + // // label4 // this.label4.AutoSize = true; @@ -262,6 +281,7 @@ namespace VideoPreview this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(250)))), ((int)(((byte)(250)))), ((int)(((byte)(250))))); this.ClientSize = new System.Drawing.Size(464, 761); + this.Controls.Add(this.button4); this.Controls.Add(this.button3); this.Controls.Add(this.label6); this.Controls.Add(this.label7); @@ -305,6 +325,7 @@ namespace VideoPreview private System.Windows.Forms.Label label6; private System.Windows.Forms.Label label7; private RyzStudio.Windows.ThemedForms.TButton button3; + private RyzStudio.Windows.ThemedForms.TButton button4; } } diff --git a/MainForm.cs b/MainForm.cs index 7d6cd61..de8f890 100644 --- a/MainForm.cs +++ b/MainForm.cs @@ -1,10 +1,13 @@ using MediaToolkit; using MediaToolkit.Model; using MediaToolkit.Options; +using RyzStudio.IO; using RyzStudio.Windows.Forms; using System; +using System.Collections.Generic; using System.Drawing; using System.IO; +using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; @@ -30,11 +33,6 @@ namespace VideoPreview textBox1.InnerTextBox.ReadOnly = true; textBox1.InnerTextBox.BackColor = Color.White; textBox1.InnerTextBox.TextChanged += textBox1_TextChanged; - - button2.DefaultImage = UIResource.cog2; - button2.DownImage = button2.OverImage = UIResource.cog; - button3.DefaultImage = UIResource.refresh2; - button3.DownImage = button3.OverImage = UIResource.refresh; } protected override void OnFormClosing(FormClosingEventArgs e) @@ -64,118 +62,6 @@ namespace VideoPreview } - //private void button1_Click(object sender, EventArgs e) - //{ - // //string searchPattern = "*.avi|*.mp4|*.ogm|*.mkv"; - - // //string[] parts = searchPattern.Split(',', ';', '|'); - - - // //var inputFile = new MediaFile { Filename = videoFilename }; - // ////var outputFile = new MediaFile { Filename = @"N:\#\invincible.2021.s01e02.here.goes.nothing.720p.webrip.hevc.x265.mkv.jpg" }; - - // //using (var engine = new Engine()) - // //{ - // // engine.GetMetadata(inputFile); - - // // // Saves the frame located on the 15th second of the video. - // // var options = new ConversionOptions { Seek = TimeSpan.FromSeconds(150) }; - // // engine.GetThumbnail(inputFile, outputFile, options); - // //} - - - // //int i = 0; - - // //FFmpegLoader.FFmpegPath = @"L:\repos\WinFormsApp1\bin\Debug\net5.0-windows"; - - // //MediaFile file = MediaFile.Open(@"N:\#\invincible.2021.s01e03.who.you.calling.ugly.720p.webrip.hevc.x265.mkv"); - - // //FFMediaToolkit.Graphics.ImageData img; - // //if (file.Video.TryGetNextFrame(out img)) - // //{ - // // //img..ToBitmap() - // //} - - // //while (file.Video.TryGetNextFrame(out var imageData)) - // //{ - // // imageData.ToBitmap().Save($@"C:\images\frame_{i++}.png"); - // // // See the #Usage details for example .ToBitmap() implementation - // // // The .Save() method may be different depending on your graphics library - // //} - - // //MessageBox.Show("!"); - //} - - //private async void button2_Click(object sender, EventArgs e) - //{ - // //await Task.Run(() => - // //{ - // // if (string.IsNullOrWhiteSpace(textBox1.Text)) return; - // // if (!Directory.Exists(textBox1.Text)) return; - - // // ThreadControl.Clear(richTextBox1); - - // // //string[] extensionFilters = new string[] { ".avi", ".mp4", ".ogm", ".mkv" }; - - // // ThreadControl.SetText(textBox1, @"C:\Windows"); - - // // List fileList = RyzStudio.IO.SmarterFileSystem.GetFiles(textBox1.Text, "*.avi|*.mp4|*.ogm|*.mkv", SearchOption.AllDirectories); - - // // //string[] fileList = Directory.GetFiles(textBox1.Text, "*.avi|*.mp4|*.ogm|*.mkv", SearchOption.TopDirectoryOnly); - // // foreach (string item in fileList) - // // { - // // ThreadControl.AddLine(richTextBox1, item); - - - // // //if (IsMatchExtension(item, extensionFilters)) - // // //{ - // // // ThreadControl.AddLine(richTextBox1, item); - - // // //} - - - // // } - - - // // MessageBox.Show("!"); - - // //}); - //} - - //private bool IsMatchExtension(string filename, string[] extensionFilters) - //{ - // string ext = Path.GetExtension(filename)?.ToLower(); - - // foreach (var item in extensionFilters) - // { - // if (ext.Equals(item)) - // { - // return true; - // } - // } - - // return false; - //} - - - //private string[] Split(string lines) - //{ - // string[] rs = new string[0]; - - // foreach (string item in lines.Split(',', ';', '|')) - // { - // if (string.IsNullOrWhiteSpace(item)) - // { - // continue; - // } - - // Array.Resize(ref rs, (rs.Length + 1)); - // rs[(rs.Length - 1)] = item?.Trim(); - // } - - // return rs; - //} - private void Form1_DragOver(object sender, DragEventArgs e) { e.Effect = (e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.Copy : DragDropEffects.None); @@ -206,11 +92,24 @@ namespace VideoPreview public AppSession CurrentSession { get; set; } = new AppSession(); - private void button1_MouseClick(object sender, MouseEventArgs e) + /// + /// Refresh + /// + /// + /// + private async void button3_MouseClick(object sender, MouseEventArgs e) { - this.Close(); + 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) @@ -227,9 +126,33 @@ namespace VideoPreview } } - private async void button3_MouseClick(object sender, MouseEventArgs e) + /// + /// Next file + /// + /// + /// + private async void button4_MouseClick(object sender, MouseEventArgs e) { - await ReadVideoFile(textBox1.Text); + 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(); } @@ -246,6 +169,7 @@ namespace VideoPreview videoFilename = null; videoDuration = TimeSpan.FromSeconds(0); + ThreadControl.SetText(this, AppResource.AppName); textBox1.Text = string.Empty; ThreadControl.SetText(label5, "-"); ThreadControl.SetText(label7, "-"); @@ -291,37 +215,6 @@ namespace VideoPreview } } - //protected string GetTempFolder() - //{ - // string path = Path.Combine(Application.StartupPath, "tmp"); - // if (Directory.Exists(path)) - // { - // return path; - // } - - // try - // { - // Directory.CreateDirectory(path); - // } - // catch - // { - // // do nothing - // } - - // if (Directory.Exists(path)) - // { - // return path; - // } - - // path = Path.GetTempPath(); - // if (Directory.Exists(path)) - // { - // return path; - // } - - // return null; - //} - protected decimal ParseFrameSizeRatio(string videoSize) { if (string.IsNullOrWhiteSpace(videoSize)) @@ -361,10 +254,7 @@ namespace VideoPreview { await Task.Run(() => { - if (this.IsBusy) - { - return; - } + if (this.IsBusy) return; this.IsBusy = true; @@ -375,7 +265,7 @@ namespace VideoPreview Filename = filename }; - using (Engine engine = new Engine()) + using (Engine engine = new Engine(Path.GetDirectoryName(Application.ExecutablePath) + @"\ffmpeg.exe")) { try { @@ -393,6 +283,8 @@ namespace VideoPreview 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"); diff --git a/Resources/UI/arrow_right.png b/Resources/UI/arrow_right.png new file mode 100644 index 0000000..96c8e26 Binary files /dev/null and b/Resources/UI/arrow_right.png differ diff --git a/Resources/UI/arrow_right2.png b/Resources/UI/arrow_right2.png new file mode 100644 index 0000000..3398182 Binary files /dev/null and b/Resources/UI/arrow_right2.png differ diff --git a/RyzStudio/IO/AccessibleDirectory.cs b/RyzStudio/IO/AccessibleDirectory.cs new file mode 100644 index 0000000..a4af53d --- /dev/null +++ b/RyzStudio/IO/AccessibleDirectory.cs @@ -0,0 +1,381 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace RyzStudio.IO +{ + public class AccessibleDirectory + { + + public static List GetFiles(string path, string pattern, bool searchTopOnly) + { + List fileList = new List(); + List directoryList = new List(); + + if (string.IsNullOrWhiteSpace(pattern)) pattern = "*"; + + directoryList.Add(path); + + while (true) + { + if (directoryList.Count <= 0) + { + break; + } + + string directory = directoryList.First(); + directoryList.RemoveAt(0); + + if (IsDirectoryAccessible(directory)) + { + IEnumerable searchDirList = new List(); + + try + { + searchDirList = Directory.EnumerateDirectories(directory, "*", SearchOption.TopDirectoryOnly); + } + catch (Exception) + { + continue; + } + + if (!searchTopOnly) + { + foreach (string item in searchDirList) + { + if (!IsDirectoryAccessible(item)) + { + continue; + } + + directoryList.Add(item); + } + } + + foreach (string item in Directory.EnumerateFiles(directory, "*", SearchOption.TopDirectoryOnly)) + { + if (!MatchFileSearchPattern(pattern, Path.GetFileName(item))) + { + continue; + } + + if (!IsFileAccessible(item)) + { + continue; + } + + fileList.Add(item); + } + } + } + + return fileList; + } + + public static IEnumerable EnumerateFiles(string path, string pattern, bool searchTopOnly) + { + List directoryList = new List(); + + if (string.IsNullOrWhiteSpace(pattern)) pattern = "*"; + + directoryList.Add(path); + + while (true) + { + if (directoryList.Count <= 0) + { + yield break; + } + + string directory = directoryList.First(); + directoryList.RemoveAt(0); + + if (IsDirectoryAccessible(directory)) + { + IEnumerable searchDirList = new List(); + + try + { + searchDirList = Directory.EnumerateDirectories(directory, "*", SearchOption.TopDirectoryOnly); + } + catch (Exception) + { + continue; + } + + if (!searchTopOnly) + { + foreach (string item in searchDirList) + { + if (!IsDirectoryAccessible(item)) + { + continue; + } + + directoryList.Add(item); + } + } + + foreach (string item in Directory.EnumerateFiles(directory, "*", SearchOption.TopDirectoryOnly)) + { + if (!MatchFileSearchPattern(pattern, Path.GetFileName(item))) + { + continue; + } + + if (!IsFileAccessible(item)) + { + continue; + } + + yield return item; + } + } + } + } + + public static void GetFiles(string path, string pattern, bool searchTopOnly, Func callback) + { + List directoryList = new List(); + ulong searchCount = 0; + + if (string.IsNullOrWhiteSpace(pattern)) pattern = "*"; + + directoryList.Add(path); + searchCount++; + + while (true) + { + if (directoryList.Count <= 0) + { + break; + } + + string directory = directoryList.First(); + directoryList.RemoveAt(0); + + callback(null, searchCount, directoryList.Count); + + if (IsDirectoryAccessible(directory)) + { + IEnumerable searchDirList = new List(); + + try + { + searchDirList = Directory.EnumerateDirectories(directory, "*", SearchOption.TopDirectoryOnly); + } + catch (Exception) + { + continue; + } + + if (!searchTopOnly) + { + foreach (string item in searchDirList) + { + if (!IsDirectoryAccessible(item)) + { + continue; + } + + directoryList.Add(item); + searchCount++; + + callback(null, searchCount, directoryList.Count); + } + } + + foreach (string item in Directory.EnumerateFiles(directory, "*", SearchOption.TopDirectoryOnly)) + { + if (!MatchFileSearchPattern(pattern, Path.GetFileName(item))) + { + continue; + } + + if (!IsFileAccessible(item)) + { + continue; + } + + callback(item, searchCount, directoryList.Count); + } + } + } + } + + public static string GetNextFile(string filepath, string pattern) + { + if (string.IsNullOrWhiteSpace(filepath)) return null; + + string path = Path.GetDirectoryName(filepath); + if (string.IsNullOrWhiteSpace(path)) return null; + + string filename = Path.GetFileName(filepath); + if (string.IsNullOrWhiteSpace(filename)) return null; + + List fileList = AccessibleDirectory.GetFiles(path, pattern, true); + if (fileList == null) return null; + if (fileList.Count <= 0) return null; + if (fileList.Count(x => (Path.GetFileName(x).Equals(filename))) < 0) return null; + + int pos = fileList.FindIndex(x => Path.GetFileName(x).Equals(filename)); + if (pos < 0) return null; + if (pos >= (fileList.Count - 1)) return null; + + return fileList[(pos + 1)]; + } + + public static bool IsAccessible(string path) + { + if (string.IsNullOrWhiteSpace(path)) return false; + + if (File.Exists(path)) return IsFileAccessible(path); + if (Directory.Exists(path)) return IsDirectoryAccessible(path); + + return false; + } + + public static bool IsFileAccessible(string filename) + { + if (string.IsNullOrWhiteSpace(filename)) return false; + if (!File.Exists(filename)) return false; + + try + { + FileInfo fi = new FileInfo(filename); + fi.GetAccessControl(); + } + catch + { + return false; + } + + return true; + } + + public static bool IsDirectoryAccessible(string path) + { + if (string.IsNullOrWhiteSpace(path)) return false; + if (!Directory.Exists(path)) return false; + + try + { + DirectoryInfo di = new DirectoryInfo(path); + di.GetAccessControl(); + } + catch + { + return false; + } + + return true; + } + + public static bool MatchFileSearchPattern(string pattern, string subject) + { + if (string.IsNullOrWhiteSpace(pattern)) + { + return false; + } + + Func matchPattern = (pattern, subject) => + { + string[] parts = pattern.Split('*'); + if (parts.Length <= 1) + { + return subject.Equals(pattern, StringComparison.CurrentCultureIgnoreCase); + } + + int pos = 0; + + for (int i = 0; i < parts.Length; i++) + { + if (i <= 0) + { + // first + pos = subject.IndexOf(parts[i], pos, StringComparison.CurrentCultureIgnoreCase); + if (pos != 0) + { + return false; + } + } + else if (i >= (parts.Length - 1)) + { + // last + if (!subject.EndsWith(parts[i], StringComparison.CurrentCultureIgnoreCase)) + { + return false; + } + } + else + { + pos = subject.IndexOf(parts[i], pos, StringComparison.CurrentCultureIgnoreCase); + if (pos < 0) + { + return false; + } + + pos += parts[i].Length; + } + } + + return true; + }; + + Func matchAllPattern = (pattern, subject) => + { + int wildcardCount = pattern.Count(x => x.Equals('*')); + if (wildcardCount <= 0) + { + return subject.Equals(pattern, StringComparison.CurrentCultureIgnoreCase); + } + else if (wildcardCount == 1) + { + string newWildcardPattern = pattern.Replace("*", ""); + + if (pattern.StartsWith("*")) + { + return subject.EndsWith(newWildcardPattern, StringComparison.CurrentCultureIgnoreCase); + } + else if (pattern.EndsWith("*")) + { + return subject.StartsWith(newWildcardPattern, StringComparison.CurrentCultureIgnoreCase); + } + else + { + return matchPattern(pattern, subject); + } + } + else + { + return matchPattern(pattern, subject); + } + }; + + if (pattern.Contains(';')) + { + string[] parts = pattern.Split(';'); + for (int i=0; i + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap arrow_right { + get { + object obj = ResourceManager.GetObject("arrow_right", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap arrow_right2 { + get { + object obj = ResourceManager.GetObject("arrow_right2", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/UIResource.resx b/UIResource.resx index 0c7337a..9281e5c 100644 --- a/UIResource.resx +++ b/UIResource.resx @@ -118,6 +118,12 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Resources\UI\arrow_right.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\UI\arrow_right2.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + Resources\UI\cog.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/VideoPreview.csproj b/VideoPreview.csproj index 0834cea..1859923 100644 --- a/VideoPreview.csproj +++ b/VideoPreview.csproj @@ -11,7 +11,7 @@ Ray Lam favicon.ico true - 0.1.0.097 + 0.1.1.008 @@ -61,6 +61,7 @@ +