diff --git a/YTDLNetFrontEnd/YTDLNetFrontEnd/Main.cs b/YTDLNetFrontEnd/YTDLNetFrontEnd/Main.cs
index 94edb17..fb51606 100644
--- a/YTDLNetFrontEnd/YTDLNetFrontEnd/Main.cs
+++ b/YTDLNetFrontEnd/YTDLNetFrontEnd/Main.cs
@@ -9,12 +9,13 @@ using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Threading.Tasks;
+using com.captainalm.YTDLNetFrontEnd.util;
namespace com.captainalm.YTDLNetFrontEnd
{
public partial class Main : Form
{
- private Process theProcess;
+ private MonitorableProcess theProcess;
public Main()
{
@@ -33,12 +34,14 @@ namespace com.captainalm.YTDLNetFrontEnd
}
Environment.CurrentDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyVideos);
textBoxSaveDir.Text = Environment.GetFolderPath(Environment.SpecialFolder.MyVideos);
+ textBoxOutput.AppendText("Starting: " + ProductName + " : " + ProductVersion + Environment.NewLine);
+ textBoxOutput.AppendText("Copyright: (C) " + this.CompanyName + " : BSD-3-Clause License" + Environment.NewLine);
}
private void buttonGo_Click(object sender, EventArgs e)
{
if (!backgroundWorkerMain.IsBusy) backgroundWorkerMain.RunWorkerAsync(BWArg.Go);
-
+
}
private void buttonInstall_Click(object sender, EventArgs e)
@@ -82,17 +85,28 @@ namespace com.captainalm.YTDLNetFrontEnd
{
case BWArg.Install:
setEnableButtons(false);
-
- var loctxt =
- YTDL.executeInstall((YTDL.getInstalled() != ApplicationType.Unavailable) ? YTDL.getInstalled() :
- (((Control.ModifierKeys & Keys.Shift) == Keys.Shift) ? ApplicationType.YoutubeDL : ApplicationType.YT_DLP),
- YTDL.getInstalled() != ApplicationType.Unavailable) +
- Environment.NewLine;
- this.Invoke(new Action(() =>
- {
- textBoxOutput.AppendText(loctxt);
- }));
+ using (var locpro =
+ YTDL.executeInstall((YTDL.getInstalled() != ApplicationType.Unavailable) ? YTDL.getInstalled() :
+ (((Control.ModifierKeys & Keys.Shift) == Keys.Shift) ? ApplicationType.YoutubeDL : ApplicationType.YT_DLP),
+ YTDL.getInstalled() != ApplicationType.Unavailable))
+ {
+ locpro.addOuputReceiver(new OutputReceiverTextbox(textBoxOutput));
+ locpro.addErrorReceiver(new OutputReaderPrefixed(textBoxOutput, "Error: "));
+ try
+ {
+ locpro.start();
+ locpro.waitForExit();
+ }
+ catch (Exception ex)
+ {
+ this.Invoke(new Action(() =>
+ {
+ textBoxOutput.AppendText("Could not start python, is python installed?" + Environment.NewLine
+ + ex.GetType().FullName + " : " + ex.Message + Environment.NewLine);
+ }));
+ }
+ }
if (YTDL.getInstalled() != ApplicationType.Unavailable)
{
@@ -110,8 +124,9 @@ namespace com.captainalm.YTDLNetFrontEnd
if (theProcess != null)
{
- if (!theProcess.HasExited) theProcess.WaitForExit();
- theProcess.Close();
+ if (((Control.ModifierKeys & Keys.Shift) == Keys.Shift)) theProcess.kill();
+ theProcess.waitForExit();
+ theProcess.close();
}
var theTarget = "";
@@ -120,18 +135,33 @@ namespace com.captainalm.YTDLNetFrontEnd
{
theTarget = textBoxEntry.Text;
textBoxEntry.Text = "";
+ textBoxOutput.AppendText("Downloading: " + theTarget + Environment.NewLine);
}));
theProcess = YTDL.executeApplication(theTarget);
if (theProcess != null)
{
- theProcess.BeginOutputReadLine();
- theProcess.BeginErrorReadLine();
- theProcess.OutputDataReceived += theProcess_OutputDataReceived;
- theProcess.ErrorDataReceived += theProcess_ErrorDataReceived;
- theProcess.Exited += theProcess_Exited;
- theProcess.EnableRaisingEvents = true;
- theProcess.WaitForExit();
+ theProcess.addOuputReceiver(new OutputReceiverTextbox(textBoxOutput));
+ theProcess.addErrorReceiver(new OutputReaderPrefixed(textBoxOutput, "Error: "));
+ try
+ {
+ theProcess.start();
+ }
+ catch (Exception ex)
+ {
+ this.Invoke(new Action(() =>
+ {
+ textBoxOutput.AppendText("Could not start python, is python and the downloader installed?" + Environment.NewLine
+ + ex.GetType().FullName + " : " + ex.Message + Environment.NewLine);
+ }));
+ }
+ }
+ else
+ {
+ this.Invoke(new Action(() =>
+ {
+ textBoxOutput.AppendText("Could not start python, is python and the downloader installed?" + Environment.NewLine);
+ }));
}
setEnableButtons(true);
@@ -141,43 +171,8 @@ namespace com.captainalm.YTDLNetFrontEnd
}
}
- void theProcess_Exited(object sender, EventArgs e)
+ private enum BWArg
{
- try
- {
- theProcess.CancelOutputRead();
- }
- catch (InvalidOperationException ex)
- {
- }
- try
- {
- theProcess.CancelErrorRead();
- }
- catch (InvalidOperationException ex)
- {
- }
- }
-
- void theProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
- {
- if (e.Data == null || e.Data.Equals("")) return;
- this.Invoke(new Action(() =>
- {
- textBoxOutput.AppendText("Error: " + e.Data + Environment.NewLine);
- }));
- }
-
- void theProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
- {
- if (e.Data == null || e.Data.Equals("")) return;
- this.Invoke(new Action(() =>
- {
- textBoxOutput.AppendText(e.Data + Environment.NewLine);
- }));
- }
-
- private enum BWArg {
Install,
Go
}
diff --git a/YTDLNetFrontEnd/YTDLNetFrontEnd/Properties/AssemblyInfo.cs b/YTDLNetFrontEnd/YTDLNetFrontEnd/Properties/AssemblyInfo.cs
index e6b3f09..6a4e6ea 100644
--- a/YTDLNetFrontEnd/YTDLNetFrontEnd/Properties/AssemblyInfo.cs
+++ b/YTDLNetFrontEnd/YTDLNetFrontEnd/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("0.2.0.0")]
-[assembly: AssemblyFileVersion("0.2.0.0")]
+[assembly: AssemblyVersion("0.3.0.0")]
+[assembly: AssemblyFileVersion("0.3.0.0")]
diff --git a/YTDLNetFrontEnd/YTDLNetFrontEnd/YTDL.cs b/YTDLNetFrontEnd/YTDLNetFrontEnd/YTDL.cs
index 779b384..3c4c0a7 100644
--- a/YTDLNetFrontEnd/YTDLNetFrontEnd/YTDL.cs
+++ b/YTDLNetFrontEnd/YTDLNetFrontEnd/YTDL.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
+using com.captainalm.YTDLNetFrontEnd.util;
namespace com.captainalm.YTDLNetFrontEnd
{
@@ -12,7 +13,7 @@ namespace com.captainalm.YTDLNetFrontEnd
{
private static string[] pathLocs = Environment.GetEnvironmentVariable("PATH").Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
- public static Process executeApplication(string target)
+ public static MonitorableProcess executeApplication(string target)
{
var packageName = "";
switch (getInstalled())
@@ -29,10 +30,10 @@ namespace com.captainalm.YTDLNetFrontEnd
var pyProSet = new ProcessStartInfo(findExecutableInPath("python"), "-m " + packageName + " --hls-prefer-native " +
((getInstalled() == ApplicationType.YT_DLP) ? "-N 16 " : "") + "\"" + target + "\"")
{ UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8 };
- return Process.Start(pyProSet);
+ return new MonitorableProcess(pyProSet);
}
- public static string executeInstall(ApplicationType package, bool update)
+ public static MonitorableProcess executeInstall(ApplicationType package, bool update)
{
var packageName = "";
switch (package)
@@ -44,17 +45,11 @@ namespace com.captainalm.YTDLNetFrontEnd
packageName = "yt-dlp";
break;
default:
- return "Invalid Package Name";
+ return null;
}
var pipProSet = new ProcessStartInfo(findExecutableInPath("python"), "-m pip install " + packageName + ((update) ? " --upgrade" : ""))
{ UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8 };
- using (var pipPro = Process.Start(pipProSet))
- {
- var rpote = new ReadProcessOutputToEnd(pipPro);
- pipPro.WaitForExit();
- if (!rpote.getError().Equals("")) return rpote.getError();
- return rpote.getOutput();
- }
+ return new MonitorableProcess(pipProSet);
}
private static ApplicationType installed;
@@ -66,11 +61,13 @@ namespace com.captainalm.YTDLNetFrontEnd
{
var pipProSet = new ProcessStartInfo(findExecutableInPath("python"), "-m pip freeze")
{ UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8 };
- using (var pipPro = Process.Start(pipProSet))
+ using (var pipPro = new MonitorableProcess(pipProSet))
{
- var rpote = new ReadProcessOutputToEnd(pipPro);
- pipPro.WaitForExit();
- var theList = rpote.getOutput();
+ var outputReader = new OutputReceiverReader();
+ pipPro.addOuputReceiver(outputReader);
+ pipPro.start();
+ pipPro.waitForExit();
+ var theList = outputReader.getData();
if (theList.Contains("yt-dlp")) installed = ApplicationType.YT_DLP;
else if (theList.Contains("youtube-dl")) installed = ApplicationType.YoutubeDL;
else installed = ApplicationType.Unavailable;
@@ -103,68 +100,4 @@ namespace com.captainalm.YTDLNetFrontEnd
YoutubeDL = 1,
YT_DLP = 2
}
-
- class ReadProcessOutputToEnd : IDisposable
- {
- Process theProcess;
- string tout = "";
- string terr = "";
-
-
- public ReadProcessOutputToEnd(Process processIn)
- {
- theProcess = processIn;
- theProcess.BeginOutputReadLine();
- theProcess.BeginErrorReadLine();
- theProcess.OutputDataReceived += theProcess_OutputDataReceived;
- theProcess.ErrorDataReceived += theProcess_ErrorDataReceived;
- theProcess.Exited += theProcess_Exited;
- theProcess.EnableRaisingEvents = true;
- }
-
- void theProcess_Exited(object sender, EventArgs e)
- {
- try
- {
- theProcess.CancelOutputRead();
- }
- catch (InvalidOperationException ex)
- {
- }
- try
- {
- theProcess.CancelErrorRead();
- }
- catch (InvalidOperationException ex)
- {
- }
- }
-
- void theProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
- {
- if (e.Data == null || e.Data.Equals("")) return;
- terr += e.Data + Environment.NewLine;
- }
-
- void theProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
- {
- if (e.Data == null || e.Data.Equals("")) return;
- tout += e.Data + Environment.NewLine;
- }
-
- public string getOutput()
- {
- return tout;
- }
-
- public string getError()
- {
- return terr;
- }
-
- public void Dispose()
- {
- theProcess.Close();
- }
- }
}
diff --git a/YTDLNetFrontEnd/YTDLNetFrontEnd/YTDLNetFrontEnd.csproj b/YTDLNetFrontEnd/YTDLNetFrontEnd/YTDLNetFrontEnd.csproj
index a059135..84edb74 100644
--- a/YTDLNetFrontEnd/YTDLNetFrontEnd/YTDLNetFrontEnd.csproj
+++ b/YTDLNetFrontEnd/YTDLNetFrontEnd/YTDLNetFrontEnd.csproj
@@ -55,6 +55,9 @@
+
+
+
Main.cs
diff --git a/YTDLNetFrontEnd/YTDLNetFrontEnd/util/IProcessOuputReceiver.cs b/YTDLNetFrontEnd/YTDLNetFrontEnd/util/IProcessOuputReceiver.cs
new file mode 100644
index 0000000..f5d4d23
--- /dev/null
+++ b/YTDLNetFrontEnd/YTDLNetFrontEnd/util/IProcessOuputReceiver.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace com.captainalm.YTDLNetFrontEnd.util
+{
+ public interface IProcessOuputReceiver
+ {
+ void receiveData(string dataIn);
+ }
+}
diff --git a/YTDLNetFrontEnd/YTDLNetFrontEnd/util/MonitorableProcess.cs b/YTDLNetFrontEnd/YTDLNetFrontEnd/util/MonitorableProcess.cs
new file mode 100644
index 0000000..3db19c3
--- /dev/null
+++ b/YTDLNetFrontEnd/YTDLNetFrontEnd/util/MonitorableProcess.cs
@@ -0,0 +1,152 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+
+namespace com.captainalm.YTDLNetFrontEnd.util
+{
+ public class MonitorableProcess : IDisposable
+ {
+ protected ProcessStartInfo startInfo;
+ protected Process representation;
+ protected List outputReceivers = new List();
+ protected List errorReceivers = new List();
+
+ private object inputSLock = new object();
+ private object outputSLock = new object();
+ private object processSLock = new object();
+
+ public MonitorableProcess(ProcessStartInfo startInfo, bool redirectInput = false)
+ {
+ this.startInfo = startInfo;
+ this.startInfo.UseShellExecute = false;
+ this.startInfo.RedirectStandardInput = redirectInput;
+ }
+
+ public void addOuputReceiver(IProcessOuputReceiver processOuput)
+ {
+ lock (outputSLock)
+ {
+ lock (processSLock)
+ {
+ if (representation == null) startInfo.RedirectStandardOutput = true;
+ }
+ outputReceivers.Add(processOuput);
+ }
+ }
+
+ public void removeOutputReceiver(IProcessOuputReceiver processOuput)
+ {
+ lock (outputSLock)
+ {
+ outputReceivers.Remove(processOuput);
+ }
+ }
+
+ public void addErrorReceiver(IProcessOuputReceiver processError)
+ {
+ lock (outputSLock)
+ {
+ lock (processSLock)
+ {
+ if (representation == null) startInfo.RedirectStandardError = true;
+ }
+ errorReceivers.Add(processError);
+ }
+ }
+
+ public void removeErrorReceiver(IProcessOuputReceiver processError)
+ {
+ lock (outputSLock)
+ {
+ errorReceivers.Remove(processError);
+ }
+ }
+
+ public void writeData(string inputData)
+ {
+ lock (processSLock)
+ {
+ if (!startInfo.RedirectStandardInput || representation == null || representation.HasExited) return;
+ lock (inputSLock)
+ {
+ representation.StandardInput.Write(inputData);
+ }
+ }
+ }
+
+ public Process start()
+ {
+ lock (processSLock)
+ {
+ representation = Process.Start(startInfo);
+ if (startInfo.RedirectStandardOutput)
+ {
+ representation.BeginOutputReadLine();
+ representation.OutputDataReceived += representation_OutputDataReceived;
+ }
+ if (startInfo.RedirectStandardError)
+ {
+ representation.BeginErrorReadLine();
+ representation.ErrorDataReceived += representation_ErrorDataReceived;
+ }
+ representation.Exited += representation_Exited;
+ representation.EnableRaisingEvents = true;
+ if (startInfo.RedirectStandardInput) representation.StandardInput.AutoFlush = true;
+ }
+ return representation;
+ }
+
+ void representation_Exited(object sender, EventArgs e)
+ {
+ try
+ {
+ representation.CancelOutputRead();
+ }
+ catch (InvalidOperationException ex)
+ {
+ }
+ try
+ {
+ representation.CancelErrorRead();
+ }
+ catch (InvalidOperationException ex)
+ {
+ }
+ }
+
+ protected void representation_OutputDataReceived(object sender, DataReceivedEventArgs e)
+ {
+ foreach (var c in outputReceivers) if (e.Data != null && !e.Data.Equals("")) lock (outputSLock) c.receiveData(e.Data);
+ }
+
+ void representation_ErrorDataReceived(object sender, DataReceivedEventArgs e)
+ {
+ foreach (var c in errorReceivers) if (e.Data != null && !e.Data.Equals("")) lock (outputSLock) c.receiveData(e.Data);
+ }
+
+ public void waitForExit()
+ {
+ if (representation == null) return;
+ if (!representation.HasExited) representation.WaitForExit();
+ }
+
+ void IDisposable.Dispose()
+ {
+ close();
+ }
+
+ public void close()
+ {
+ if (representation == null) return;
+ waitForExit();
+ representation.Close();
+ }
+
+ public void kill()
+ {
+ if (representation != null && !representation.HasExited) representation.Kill();
+ }
+ }
+}
diff --git a/YTDLNetFrontEnd/YTDLNetFrontEnd/util/OutputReceivers.cs b/YTDLNetFrontEnd/YTDLNetFrontEnd/util/OutputReceivers.cs
new file mode 100644
index 0000000..8e2c78f
--- /dev/null
+++ b/YTDLNetFrontEnd/YTDLNetFrontEnd/util/OutputReceivers.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Windows.Forms;
+namespace com.captainalm.YTDLNetFrontEnd.util
+{
+ public class OutputReceiverReader : IProcessOuputReceiver
+ {
+ private string theOutput = "";
+
+ public void receiveData(string dataIn)
+ {
+ theOutput += dataIn;
+ }
+
+ public string getData()
+ {
+ return theOutput.TrimEnd(Environment.NewLine.ToCharArray());
+ }
+ }
+
+ public class OutputReceiverTextbox : IProcessOuputReceiver
+ {
+ protected TextBox theOutputTextbox;
+
+ public OutputReceiverTextbox(TextBox tboxIn)
+ {
+ theOutputTextbox = tboxIn;
+ }
+
+ public virtual void receiveData(string dataIn)
+ {
+ if (theOutputTextbox.InvokeRequired)
+ {
+ theOutputTextbox.Invoke(new Action(() =>
+ {
+ theOutputTextbox.AppendText(dataIn + Environment.NewLine);
+ }));
+ }
+ }
+ }
+
+ public class OutputReaderPrefixed : OutputReceiverTextbox
+ {
+ protected string prefix;
+
+ public OutputReaderPrefixed(TextBox tboxIn, string prefix)
+ : base(tboxIn)
+ {
+ this.prefix = prefix;
+ }
+
+ public override void receiveData(string dataIn)
+ {
+ base.receiveData(prefix + dataIn);
+ }
+ }
+}
\ No newline at end of file