Update to 0.3.0.0:
Fix the way processes are handled in the application. Output the about information at startup. Output the downloading video on pressing go.
This commit is contained in:
parent
8508bcd3af
commit
c17822995e
@ -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
|
||||
}
|
||||
|
@ -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")]
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,9 @@
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="util\IProcessOuputReceiver.cs" />
|
||||
<Compile Include="util\MonitorableProcess.cs" />
|
||||
<Compile Include="util\OutputReceivers.cs" />
|
||||
<Compile Include="YTDL.cs" />
|
||||
<EmbeddedResource Include="Main.resx">
|
||||
<DependentUpon>Main.cs</DependentUpon>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
152
YTDLNetFrontEnd/YTDLNetFrontEnd/util/MonitorableProcess.cs
Normal file
152
YTDLNetFrontEnd/YTDLNetFrontEnd/util/MonitorableProcess.cs
Normal file
@ -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<IProcessOuputReceiver> outputReceivers = new List<IProcessOuputReceiver>();
|
||||
protected List<IProcessOuputReceiver> errorReceivers = new List<IProcessOuputReceiver>();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
56
YTDLNetFrontEnd/YTDLNetFrontEnd/util/OutputReceivers.cs
Normal file
56
YTDLNetFrontEnd/YTDLNetFrontEnd/util/OutputReceivers.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user