Added playlist support
This commit is contained in:
61
Download.Designer.cs
generated
61
Download.Designer.cs
generated
@ -28,24 +28,26 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
this.lblStatus = new System.Windows.Forms.Label();
|
this.lblDownload = new System.Windows.Forms.Label();
|
||||||
this.btnCancel = new System.Windows.Forms.Button();
|
this.btnCancel = new System.Windows.Forms.Button();
|
||||||
this.progressBar = new System.Windows.Forms.ProgressBar();
|
this.pbDownload = new System.Windows.Forms.ProgressBar();
|
||||||
|
this.pbConvertion = new System.Windows.Forms.ProgressBar();
|
||||||
|
this.lblConvertion = new System.Windows.Forms.Label();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// lblStatus
|
// lblDownload
|
||||||
//
|
//
|
||||||
this.lblStatus.AutoSize = true;
|
this.lblDownload.AutoSize = true;
|
||||||
this.lblStatus.Location = new System.Drawing.Point(13, 13);
|
this.lblDownload.Location = new System.Drawing.Point(13, 13);
|
||||||
this.lblStatus.Name = "lblStatus";
|
this.lblDownload.Name = "lblDownload";
|
||||||
this.lblStatus.Size = new System.Drawing.Size(139, 25);
|
this.lblDownload.Size = new System.Drawing.Size(139, 25);
|
||||||
this.lblStatus.TabIndex = 0;
|
this.lblDownload.TabIndex = 0;
|
||||||
this.lblStatus.Text = "Connecting...";
|
this.lblDownload.Text = "Connecting...";
|
||||||
//
|
//
|
||||||
// btnCancel
|
// btnCancel
|
||||||
//
|
//
|
||||||
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Abort;
|
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Abort;
|
||||||
this.btnCancel.Location = new System.Drawing.Point(625, 41);
|
this.btnCancel.Location = new System.Drawing.Point(899, 127);
|
||||||
this.btnCancel.Name = "btnCancel";
|
this.btnCancel.Name = "btnCancel";
|
||||||
this.btnCancel.Size = new System.Drawing.Size(110, 40);
|
this.btnCancel.Size = new System.Drawing.Size(110, 40);
|
||||||
this.btnCancel.TabIndex = 7;
|
this.btnCancel.TabIndex = 7;
|
||||||
@ -53,21 +55,38 @@
|
|||||||
this.btnCancel.UseVisualStyleBackColor = true;
|
this.btnCancel.UseVisualStyleBackColor = true;
|
||||||
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
||||||
//
|
//
|
||||||
// progressBar
|
// pbDownload
|
||||||
//
|
//
|
||||||
this.progressBar.Location = new System.Drawing.Point(18, 41);
|
this.pbDownload.Location = new System.Drawing.Point(18, 41);
|
||||||
this.progressBar.Name = "progressBar";
|
this.pbDownload.Name = "pbDownload";
|
||||||
this.progressBar.Size = new System.Drawing.Size(601, 40);
|
this.pbDownload.Size = new System.Drawing.Size(875, 40);
|
||||||
this.progressBar.TabIndex = 8;
|
this.pbDownload.TabIndex = 8;
|
||||||
|
//
|
||||||
|
// pbConvertion
|
||||||
|
//
|
||||||
|
this.pbConvertion.Location = new System.Drawing.Point(18, 127);
|
||||||
|
this.pbConvertion.Name = "pbConvertion";
|
||||||
|
this.pbConvertion.Size = new System.Drawing.Size(875, 40);
|
||||||
|
this.pbConvertion.TabIndex = 10;
|
||||||
|
//
|
||||||
|
// lblConvertion
|
||||||
|
//
|
||||||
|
this.lblConvertion.AutoSize = true;
|
||||||
|
this.lblConvertion.Location = new System.Drawing.Point(13, 99);
|
||||||
|
this.lblConvertion.Name = "lblConvertion";
|
||||||
|
this.lblConvertion.Size = new System.Drawing.Size(0, 25);
|
||||||
|
this.lblConvertion.TabIndex = 9;
|
||||||
//
|
//
|
||||||
// Download
|
// Download
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 25F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 25F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.ClientSize = new System.Drawing.Size(747, 102);
|
this.ClientSize = new System.Drawing.Size(1021, 183);
|
||||||
this.Controls.Add(this.progressBar);
|
this.Controls.Add(this.pbConvertion);
|
||||||
|
this.Controls.Add(this.lblConvertion);
|
||||||
|
this.Controls.Add(this.pbDownload);
|
||||||
this.Controls.Add(this.btnCancel);
|
this.Controls.Add(this.btnCancel);
|
||||||
this.Controls.Add(this.lblStatus);
|
this.Controls.Add(this.lblDownload);
|
||||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
|
||||||
this.Name = "Download";
|
this.Name = "Download";
|
||||||
this.Text = "Downloading";
|
this.Text = "Downloading";
|
||||||
@ -80,8 +99,10 @@
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private System.Windows.Forms.Label lblStatus;
|
private System.Windows.Forms.Label lblDownload;
|
||||||
private System.Windows.Forms.Button btnCancel;
|
private System.Windows.Forms.Button btnCancel;
|
||||||
private System.Windows.Forms.ProgressBar progressBar;
|
private System.Windows.Forms.ProgressBar pbDownload;
|
||||||
|
private System.Windows.Forms.ProgressBar pbConvertion;
|
||||||
|
private System.Windows.Forms.Label lblConvertion;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
75
Download.cs
75
Download.cs
@ -5,6 +5,7 @@ using System.Data;
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
using System.Runtime.Remoting.Contexts;
|
using System.Runtime.Remoting.Contexts;
|
||||||
using System.Security.AccessControl;
|
using System.Security.AccessControl;
|
||||||
using System.Security.Policy;
|
using System.Security.Policy;
|
||||||
@ -25,22 +26,34 @@ namespace ytdlp_gui
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Download_Load(object sender, EventArgs e)
|
private async void Download_Load(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
process.Start += (_sender, start_event) =>
|
process.Start += (_sender, start_event) =>
|
||||||
{
|
{
|
||||||
lblStatus.Text = String.Format(
|
lblDownload.Text = String.Format(
|
||||||
"Connecting to %s...",
|
"Connecting to %s...",
|
||||||
start_event.provider
|
start_event.provider
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
process.Error += (_sender, error_event) =>
|
||||||
|
{
|
||||||
|
MessageBox.Show(this, error_event.error, "yt-dlp error", MessageBoxButtons.OK, MessageBoxIcon.Error );
|
||||||
|
this.Close();
|
||||||
|
};
|
||||||
|
|
||||||
process.Progress += (_sender, prog_event) =>
|
process.Progress += (_sender, prog_event) =>
|
||||||
{
|
{
|
||||||
int progress = (int)(prog_event.progress * 100 + 0.5);
|
int progress = (int)(prog_event.progress * 100 + 0.5);
|
||||||
this.Text = "Downloading - " + progress.ToString() + "%";
|
this.Text = String.Format(
|
||||||
lblStatus.Text = prog_event.file;
|
"Downloading {0}/{1} {2}% - {3}",
|
||||||
progressBar.Value = progress;
|
prog_event.playlist_current,
|
||||||
|
prog_event.playlist_total,
|
||||||
|
progress,
|
||||||
|
prog_event.file
|
||||||
|
);
|
||||||
|
lblDownload.Text = this.Text;
|
||||||
|
pbDownload.Value = progress;
|
||||||
};
|
};
|
||||||
|
|
||||||
process.Finished += (_sender, finish_event) =>
|
process.Finished += (_sender, finish_event) =>
|
||||||
@ -66,33 +79,41 @@ namespace ytdlp_gui
|
|||||||
{
|
{
|
||||||
System.IO.File.Move(filePath, newFilePath);
|
System.IO.File.Move(filePath, newFilePath);
|
||||||
System.IO.File.SetLastWriteTime(newFilePath, DateTime.Now);
|
System.IO.File.SetLastWriteTime(newFilePath, DateTime.Now);
|
||||||
this.Close();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
newFile = Path.ChangeExtension(newFile, convert_to);
|
||||||
newFilePath = Path.ChangeExtension(newFilePath, convert_to);
|
newFilePath = Path.ChangeExtension(newFilePath, convert_to);
|
||||||
convertion = (new FFmpeg()).Convert(filePath, newFilePath);
|
FFmpegProcess convertion = (new FFmpeg()).Convert(filePath, newFilePath);
|
||||||
|
|
||||||
convertion.Progress += (_ffmpeg, progress_event) =>
|
convertion.Progress += (_ffmpeg, prog_event) =>
|
||||||
{
|
{
|
||||||
int progress = (int)(progress_event.progress * 100 + 0.5);
|
int progress = (int)(prog_event.progress * 100 + 0.5);
|
||||||
this.Text = "Converting - " + progress.ToString() + "%";
|
// this.Text = "Converting - " + progress.ToString() + "%";
|
||||||
lblStatus.Text = "Converting to " + convert_to;
|
lblConvertion.Text = String.Format(
|
||||||
progressBar.Value = progress;
|
"Converting {0}/{1} {2}% - {3}",
|
||||||
|
convertion_current,
|
||||||
|
convertion_total,
|
||||||
|
progress,
|
||||||
|
newFile
|
||||||
|
);
|
||||||
|
pbConvertion.Value = progress;
|
||||||
};
|
};
|
||||||
|
|
||||||
convertion.Finished += (_ffmpeg, _ffmpeg_e) =>
|
convertion.Finished += (_ffmpeg, _ffmpeg_e) =>
|
||||||
{
|
{
|
||||||
System.IO.File.Delete(filePath);
|
System.IO.File.Delete(filePath);
|
||||||
this.Close();
|
triggerConvertionQueue();
|
||||||
};
|
};
|
||||||
|
|
||||||
convertion.Run().ContinueWith(t => { MessageBox.Show(t.Exception.ToString(), "Error"); }, TaskContinuationOptions.OnlyOnFaulted); ;
|
convertion_queue.Enqueue(convertion);
|
||||||
|
convertion_total += 1;
|
||||||
|
triggerConvertionQueue();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Can run in background :)
|
// Can run in background :)
|
||||||
process.Run().ContinueWith(t => { MessageBox.Show(t.Exception.ToString(), "Error"); }, TaskContinuationOptions.OnlyOnFaulted);
|
_ = await process.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnCancel_Click(object sender, EventArgs e)
|
private void btnCancel_Click(object sender, EventArgs e)
|
||||||
@ -102,12 +123,30 @@ namespace ytdlp_gui
|
|||||||
|
|
||||||
private void Download_FormClosing(object sender, FormClosingEventArgs e)
|
private void Download_FormClosing(object sender, FormClosingEventArgs e)
|
||||||
{
|
{
|
||||||
if (convertion != null)
|
process.Cancel();
|
||||||
convertion.Cancel();
|
foreach (FFmpegProcess conversion in convertion_queue)
|
||||||
|
conversion.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async void triggerConvertionQueue()
|
||||||
|
{
|
||||||
|
if (converting)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (convertion_queue.Count() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
converting = true;
|
||||||
|
convertion_current += 1;
|
||||||
|
await convertion_queue.Dequeue().Run();
|
||||||
|
converting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readonly YTdlpProcess process;
|
protected readonly YTdlpProcess process;
|
||||||
protected FFmpegProcess convertion;
|
protected int convertion_current = 0;
|
||||||
|
protected int convertion_total = 0;
|
||||||
|
protected Queue<FFmpegProcess> convertion_queue = new Queue<FFmpegProcess>();
|
||||||
|
private bool converting = false;
|
||||||
protected readonly string workdir;
|
protected readonly string workdir;
|
||||||
protected readonly string convert_to;
|
protected readonly string convert_to;
|
||||||
}
|
}
|
||||||
|
|||||||
31
FFmpeg.cs
31
FFmpeg.cs
@ -29,9 +29,6 @@ namespace ytdlp_gui
|
|||||||
this.output_file = output_file;
|
this.output_file = output_file;
|
||||||
ci = (CultureInfo)CultureInfo.CurrentCulture.Clone();
|
ci = (CultureInfo)CultureInfo.CurrentCulture.Clone();
|
||||||
ci.NumberFormat.CurrencyDecimalSeparator = ".";
|
ci.NumberFormat.CurrencyDecimalSeparator = ".";
|
||||||
|
|
||||||
this.header_regex = new Regex(@"Duration: (\d+):(\d+):(\d+)\.(\d+)");
|
|
||||||
this.progress_regex = new Regex(@"time=(\d+):(\d+):(\d+)\.(\d+)");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~FFmpegProcess()
|
~FFmpegProcess()
|
||||||
@ -39,7 +36,7 @@ namespace ytdlp_gui
|
|||||||
Cancel();
|
Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> Run()
|
public async Task Run()
|
||||||
{
|
{
|
||||||
if (File.Exists(output_file))
|
if (File.Exists(output_file))
|
||||||
System.IO.File.Delete(output_file);
|
System.IO.File.Delete(output_file);
|
||||||
@ -71,37 +68,33 @@ namespace ytdlp_gui
|
|||||||
+ int.Parse(groups[2].Value) * 60
|
+ int.Parse(groups[2].Value) * 60
|
||||||
+ int.Parse(groups[3].Value);
|
+ int.Parse(groups[3].Value);
|
||||||
|
|
||||||
Progress?.Invoke(this, new FFmpegProgressEventArgs((float)progress / duration));
|
Progress?.Invoke(this, new FFmpegProgressEventArgs(Math.Min((float)progress / duration, 1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stop)
|
||||||
|
return;
|
||||||
|
|
||||||
process.WaitForExit();
|
process.WaitForExit();
|
||||||
process.Close();
|
|
||||||
Finished?.Invoke(this, new EventArgs());
|
Finished?.Invoke(this, new EventArgs());
|
||||||
|
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Cancel()
|
public void Cancel()
|
||||||
{
|
{
|
||||||
bool finished = true;
|
stop = true;
|
||||||
try // to kill the process before deleting the file
|
if (!process.HasExited)
|
||||||
{
|
|
||||||
finished = process.HasExited;
|
|
||||||
process.Kill();
|
process.Kill();
|
||||||
process.WaitForExit();
|
process.WaitForExit();
|
||||||
process.Close();
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
// if (!finished && File.Exists(output_file))
|
|
||||||
// System.IO.File.Delete(output_file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly Process process;
|
private readonly Process process;
|
||||||
private readonly string output_file;
|
private readonly string output_file;
|
||||||
private readonly CultureInfo ci;
|
private readonly CultureInfo ci;
|
||||||
private readonly Regex header_regex;
|
private readonly Regex header_regex = new Regex(@"Duration: (\d+):(\d+):(\d+)\.(\d+)");
|
||||||
private readonly Regex progress_regex;
|
private readonly Regex progress_regex = new Regex(@"time=(\d+):(\d+):(\d+)\.(\d+)");
|
||||||
|
private bool stop = false;
|
||||||
public event EventHandler<FFmpegProgressEventArgs> Progress;
|
public event EventHandler<FFmpegProgressEventArgs> Progress;
|
||||||
public event EventHandler Finished;
|
public event EventHandler Finished;
|
||||||
}
|
}
|
||||||
@ -124,7 +117,7 @@ namespace ytdlp_gui
|
|||||||
|
|
||||||
process.StartInfo.UseShellExecute = false;
|
process.StartInfo.UseShellExecute = false;
|
||||||
process.StartInfo.CreateNoWindow = true;
|
process.StartInfo.CreateNoWindow = true;
|
||||||
process.StartInfo.RedirectStandardOutput = true;
|
// process.StartInfo.RedirectStandardOutput = true;
|
||||||
process.StartInfo.RedirectStandardError = true;
|
process.StartInfo.RedirectStandardError = true;
|
||||||
|
|
||||||
return new FFmpegProcess(process, to);
|
return new FFmpegProcess(process, to);
|
||||||
|
|||||||
57
Main.Designer.cs
generated
57
Main.Designer.cs
generated
@ -31,9 +31,9 @@
|
|||||||
this.label1 = new System.Windows.Forms.Label();
|
this.label1 = new System.Windows.Forms.Label();
|
||||||
this.textUrl = new System.Windows.Forms.TextBox();
|
this.textUrl = new System.Windows.Forms.TextBox();
|
||||||
this.checkDownloadPlaylist = new System.Windows.Forms.CheckBox();
|
this.checkDownloadPlaylist = new System.Windows.Forms.CheckBox();
|
||||||
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
this.groupPlaylist = new System.Windows.Forms.GroupBox();
|
||||||
this.textPlaylistFolder = new System.Windows.Forms.TextBox();
|
this.textPlaylistFolder = new System.Windows.Forms.TextBox();
|
||||||
this.label2 = new System.Windows.Forms.Label();
|
this.lblPlaylistFolder = new System.Windows.Forms.Label();
|
||||||
this.checkPlaylistFolder = new System.Windows.Forms.CheckBox();
|
this.checkPlaylistFolder = new System.Windows.Forms.CheckBox();
|
||||||
this.groupBox2 = new System.Windows.Forms.GroupBox();
|
this.groupBox2 = new System.Windows.Forms.GroupBox();
|
||||||
this.label3 = new System.Windows.Forms.Label();
|
this.label3 = new System.Windows.Forms.Label();
|
||||||
@ -42,7 +42,7 @@
|
|||||||
this.btnDownload = new System.Windows.Forms.Button();
|
this.btnDownload = new System.Windows.Forms.Button();
|
||||||
this.btnCancel = new System.Windows.Forms.Button();
|
this.btnCancel = new System.Windows.Forms.Button();
|
||||||
this.checkAudioOnly = new System.Windows.Forms.CheckBox();
|
this.checkAudioOnly = new System.Windows.Forms.CheckBox();
|
||||||
this.groupBox1.SuspendLayout();
|
this.groupPlaylist.SuspendLayout();
|
||||||
this.groupBox2.SuspendLayout();
|
this.groupBox2.SuspendLayout();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
@ -65,42 +65,44 @@
|
|||||||
// checkDownloadPlaylist
|
// checkDownloadPlaylist
|
||||||
//
|
//
|
||||||
this.checkDownloadPlaylist.AutoSize = true;
|
this.checkDownloadPlaylist.AutoSize = true;
|
||||||
this.checkDownloadPlaylist.Enabled = false;
|
|
||||||
this.checkDownloadPlaylist.Location = new System.Drawing.Point(25, 178);
|
this.checkDownloadPlaylist.Location = new System.Drawing.Point(25, 178);
|
||||||
this.checkDownloadPlaylist.Name = "checkDownloadPlaylist";
|
this.checkDownloadPlaylist.Name = "checkDownloadPlaylist";
|
||||||
this.checkDownloadPlaylist.Size = new System.Drawing.Size(212, 29);
|
this.checkDownloadPlaylist.Size = new System.Drawing.Size(212, 29);
|
||||||
this.checkDownloadPlaylist.TabIndex = 2;
|
this.checkDownloadPlaylist.TabIndex = 2;
|
||||||
this.checkDownloadPlaylist.Text = "Download playlist";
|
this.checkDownloadPlaylist.Text = "Download playlist";
|
||||||
this.checkDownloadPlaylist.UseVisualStyleBackColor = true;
|
this.checkDownloadPlaylist.UseVisualStyleBackColor = true;
|
||||||
|
this.checkDownloadPlaylist.CheckedChanged += new System.EventHandler(this.checkDownloadPlaylist_CheckedChanged);
|
||||||
//
|
//
|
||||||
// groupBox1
|
// groupPlaylist
|
||||||
//
|
//
|
||||||
this.groupBox1.Controls.Add(this.textPlaylistFolder);
|
this.groupPlaylist.Controls.Add(this.textPlaylistFolder);
|
||||||
this.groupBox1.Controls.Add(this.label2);
|
this.groupPlaylist.Controls.Add(this.lblPlaylistFolder);
|
||||||
this.groupBox1.Controls.Add(this.checkPlaylistFolder);
|
this.groupPlaylist.Controls.Add(this.checkPlaylistFolder);
|
||||||
this.groupBox1.Enabled = false;
|
this.groupPlaylist.Enabled = false;
|
||||||
this.groupBox1.Location = new System.Drawing.Point(18, 58);
|
this.groupPlaylist.Location = new System.Drawing.Point(18, 58);
|
||||||
this.groupBox1.Name = "groupBox1";
|
this.groupPlaylist.Name = "groupPlaylist";
|
||||||
this.groupBox1.Size = new System.Drawing.Size(373, 108);
|
this.groupPlaylist.Size = new System.Drawing.Size(373, 108);
|
||||||
this.groupBox1.TabIndex = 3;
|
this.groupPlaylist.TabIndex = 3;
|
||||||
this.groupBox1.TabStop = false;
|
this.groupPlaylist.TabStop = false;
|
||||||
this.groupBox1.Text = "Playlist";
|
this.groupPlaylist.Text = "Playlist";
|
||||||
//
|
//
|
||||||
// textPlaylistFolder
|
// textPlaylistFolder
|
||||||
//
|
//
|
||||||
|
this.textPlaylistFolder.Enabled = false;
|
||||||
this.textPlaylistFolder.Location = new System.Drawing.Point(94, 60);
|
this.textPlaylistFolder.Location = new System.Drawing.Point(94, 60);
|
||||||
this.textPlaylistFolder.Name = "textPlaylistFolder";
|
this.textPlaylistFolder.Name = "textPlaylistFolder";
|
||||||
this.textPlaylistFolder.Size = new System.Drawing.Size(273, 31);
|
this.textPlaylistFolder.Size = new System.Drawing.Size(273, 31);
|
||||||
this.textPlaylistFolder.TabIndex = 2;
|
this.textPlaylistFolder.TabIndex = 2;
|
||||||
//
|
//
|
||||||
// label2
|
// lblPlaylistFolder
|
||||||
//
|
//
|
||||||
this.label2.AutoSize = true;
|
this.lblPlaylistFolder.AutoSize = true;
|
||||||
this.label2.Location = new System.Drawing.Point(7, 67);
|
this.lblPlaylistFolder.Enabled = false;
|
||||||
this.label2.Name = "label2";
|
this.lblPlaylistFolder.Location = new System.Drawing.Point(7, 67);
|
||||||
this.label2.Size = new System.Drawing.Size(80, 25);
|
this.lblPlaylistFolder.Name = "lblPlaylistFolder";
|
||||||
this.label2.TabIndex = 1;
|
this.lblPlaylistFolder.Size = new System.Drawing.Size(80, 25);
|
||||||
this.label2.Text = "Name: ";
|
this.lblPlaylistFolder.TabIndex = 1;
|
||||||
|
this.lblPlaylistFolder.Text = "Name: ";
|
||||||
//
|
//
|
||||||
// checkPlaylistFolder
|
// checkPlaylistFolder
|
||||||
//
|
//
|
||||||
@ -111,6 +113,7 @@
|
|||||||
this.checkPlaylistFolder.TabIndex = 0;
|
this.checkPlaylistFolder.TabIndex = 0;
|
||||||
this.checkPlaylistFolder.Text = "Save to folder";
|
this.checkPlaylistFolder.Text = "Save to folder";
|
||||||
this.checkPlaylistFolder.UseVisualStyleBackColor = true;
|
this.checkPlaylistFolder.UseVisualStyleBackColor = true;
|
||||||
|
this.checkPlaylistFolder.CheckedChanged += new System.EventHandler(this.checkPlaylistFolder_CheckedChanged);
|
||||||
//
|
//
|
||||||
// groupBox2
|
// groupBox2
|
||||||
//
|
//
|
||||||
@ -201,7 +204,7 @@
|
|||||||
this.Controls.Add(this.btnCancel);
|
this.Controls.Add(this.btnCancel);
|
||||||
this.Controls.Add(this.btnDownload);
|
this.Controls.Add(this.btnDownload);
|
||||||
this.Controls.Add(this.groupBox2);
|
this.Controls.Add(this.groupBox2);
|
||||||
this.Controls.Add(this.groupBox1);
|
this.Controls.Add(this.groupPlaylist);
|
||||||
this.Controls.Add(this.checkDownloadPlaylist);
|
this.Controls.Add(this.checkDownloadPlaylist);
|
||||||
this.Controls.Add(this.textUrl);
|
this.Controls.Add(this.textUrl);
|
||||||
this.Controls.Add(this.label1);
|
this.Controls.Add(this.label1);
|
||||||
@ -211,8 +214,8 @@
|
|||||||
this.Name = "Main";
|
this.Name = "Main";
|
||||||
this.Text = "Youtube Downloader";
|
this.Text = "Youtube Downloader";
|
||||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Main_FormClosing);
|
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Main_FormClosing);
|
||||||
this.groupBox1.ResumeLayout(false);
|
this.groupPlaylist.ResumeLayout(false);
|
||||||
this.groupBox1.PerformLayout();
|
this.groupPlaylist.PerformLayout();
|
||||||
this.groupBox2.ResumeLayout(false);
|
this.groupBox2.ResumeLayout(false);
|
||||||
this.groupBox2.PerformLayout();
|
this.groupBox2.PerformLayout();
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
@ -225,10 +228,10 @@
|
|||||||
private System.Windows.Forms.Label label1;
|
private System.Windows.Forms.Label label1;
|
||||||
private System.Windows.Forms.TextBox textUrl;
|
private System.Windows.Forms.TextBox textUrl;
|
||||||
private System.Windows.Forms.CheckBox checkDownloadPlaylist;
|
private System.Windows.Forms.CheckBox checkDownloadPlaylist;
|
||||||
private System.Windows.Forms.GroupBox groupBox1;
|
private System.Windows.Forms.GroupBox groupPlaylist;
|
||||||
private System.Windows.Forms.CheckBox checkPlaylistFolder;
|
private System.Windows.Forms.CheckBox checkPlaylistFolder;
|
||||||
private System.Windows.Forms.TextBox textPlaylistFolder;
|
private System.Windows.Forms.TextBox textPlaylistFolder;
|
||||||
private System.Windows.Forms.Label label2;
|
private System.Windows.Forms.Label lblPlaylistFolder;
|
||||||
private System.Windows.Forms.GroupBox groupBox2;
|
private System.Windows.Forms.GroupBox groupBox2;
|
||||||
private System.Windows.Forms.ComboBox comboConvertFormat;
|
private System.Windows.Forms.ComboBox comboConvertFormat;
|
||||||
private System.Windows.Forms.CheckBox checkConvert;
|
private System.Windows.Forms.CheckBox checkConvert;
|
||||||
|
|||||||
21
Main.cs
21
Main.cs
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -30,10 +31,17 @@ namespace ytdlp_gui
|
|||||||
string user = Environment.GetEnvironmentVariable("USERNAME");
|
string user = Environment.GetEnvironmentVariable("USERNAME");
|
||||||
string workdir = @"C:\Users\" + user + @"\Downloads\";
|
string workdir = @"C:\Users\" + user + @"\Downloads\";
|
||||||
|
|
||||||
|
if (checkDownloadPlaylist.Checked && checkPlaylistFolder.Checked)
|
||||||
|
{
|
||||||
|
workdir = Path.Combine(workdir, textPlaylistFolder.Text);
|
||||||
|
if (!Directory.Exists(workdir))
|
||||||
|
System.IO.Directory.CreateDirectory(workdir);
|
||||||
|
}
|
||||||
|
|
||||||
YTdlpProcess process = ytdlp.Download(
|
YTdlpProcess process = ytdlp.Download(
|
||||||
textUrl.Text,
|
textUrl.Text,
|
||||||
checkAudioOnly.Checked,
|
checkAudioOnly.Checked,
|
||||||
false,
|
checkDownloadPlaylist.Checked,
|
||||||
workdir
|
workdir
|
||||||
);
|
);
|
||||||
Download download = new Download(
|
Download download = new Download(
|
||||||
@ -75,6 +83,17 @@ namespace ytdlp_gui
|
|||||||
comboConvertFormat.Enabled = checkConvert.Checked;
|
comboConvertFormat.Enabled = checkConvert.Checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkDownloadPlaylist_CheckedChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
groupPlaylist.Enabled = checkDownloadPlaylist.Checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkPlaylistFolder_CheckedChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
textPlaylistFolder.Enabled = checkPlaylistFolder.Checked;
|
||||||
|
lblPlaylistFolder.Enabled = checkPlaylistFolder.Checked;
|
||||||
|
}
|
||||||
|
|
||||||
protected List<Download> downloads;
|
protected List<Download> downloads;
|
||||||
protected YTdlp ytdlp;
|
protected YTdlp ytdlp;
|
||||||
}
|
}
|
||||||
|
|||||||
43
ProcessUtil.cs
Normal file
43
ProcessUtil.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Management;
|
||||||
|
|
||||||
|
namespace ytdlp_gui
|
||||||
|
{
|
||||||
|
internal static class ProcessUtil
|
||||||
|
{
|
||||||
|
/// https://stackoverflow.com/questions/5901679/kill-process-tree-programmatically-in-c-sharp#answer-10402906
|
||||||
|
/// <summary>
|
||||||
|
/// Kill a process, and all of its children, grandchildren, etc.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pid">Process ID.</param>
|
||||||
|
public static void KillProcessAndChildren(int pid)
|
||||||
|
{
|
||||||
|
// Cannot close 'system idle process'.
|
||||||
|
if (pid == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ManagementObjectSearcher searcher = new ManagementObjectSearcher
|
||||||
|
("Select * From Win32_Process Where ParentProcessID=" + pid);
|
||||||
|
ManagementObjectCollection moc = searcher.Get();
|
||||||
|
foreach (ManagementObject mo in moc)
|
||||||
|
{
|
||||||
|
KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"]));
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Process proc = Process.GetProcessById(pid);
|
||||||
|
proc.Kill();
|
||||||
|
}
|
||||||
|
catch (ArgumentException)
|
||||||
|
{
|
||||||
|
// Process already exited.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
127
YTdlp.cs
127
YTdlp.cs
@ -3,9 +3,12 @@ using System.Collections.Generic;
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Deployment.Application;
|
using System.Deployment.Application;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Drawing.Printing;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -24,18 +27,31 @@ namespace ytdlp_gui
|
|||||||
|
|
||||||
public class YTdlpProgressEventArgs : EventArgs
|
public class YTdlpProgressEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public YTdlpProgressEventArgs(string provider, string action, string file, float progress)
|
public YTdlpProgressEventArgs(
|
||||||
{
|
string provider,
|
||||||
|
string action,
|
||||||
|
string file,
|
||||||
|
float progress,
|
||||||
|
string playlist,
|
||||||
|
int playlist_current,
|
||||||
|
int playlist_total
|
||||||
|
){
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.action = action;
|
this.action = action;
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.progress = progress;
|
this.progress = progress;
|
||||||
|
this.playlist = playlist;
|
||||||
|
this.playlist_current = playlist_current;
|
||||||
|
this.playlist_total = playlist_total;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly string provider;
|
public readonly string provider;
|
||||||
public readonly string action;
|
public readonly string action;
|
||||||
public readonly string file;
|
public readonly string file;
|
||||||
public readonly float progress;
|
public readonly float progress;
|
||||||
|
public readonly string playlist;
|
||||||
|
public readonly int playlist_current;
|
||||||
|
public readonly int playlist_total;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class YTdlpErrorEventArgs : EventArgs
|
public class YTdlpErrorEventArgs : EventArgs
|
||||||
@ -74,21 +90,20 @@ namespace ytdlp_gui
|
|||||||
ci.NumberFormat.CurrencyDecimalSeparator = ".";
|
ci.NumberFormat.CurrencyDecimalSeparator = ".";
|
||||||
|
|
||||||
files = new List<string>();
|
files = new List<string>();
|
||||||
|
|
||||||
this.already_downloaded_regex = new Regex(@"\[download\]\s+(.+) has already been downloaded");
|
|
||||||
this.download_regex = new Regex(@"([0-9.]+)%\s+of\s+([0-9.]+)(\w+)\s+at\s+([0-9.]+)(\w+\/\w+)\sETA\s(\d+:\d+)");
|
|
||||||
this.download_finish_regex = new Regex(@"([0-9.]+)%\s+of\s+([0-9.]+)(\w+)\s+in\s+(\d+:\d+:\d+)\s+at\s+([0-9.]+)(\w+\/\w+)");
|
|
||||||
this.merge_regex = new Regex(@"Merging formats into ""(.+)""");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> Run()
|
public async Task<string[]> Run()
|
||||||
{
|
{
|
||||||
process.Start();
|
process.Start();
|
||||||
|
|
||||||
string line = null;
|
string stdout = "";
|
||||||
|
int playlist_last = playlist_current;
|
||||||
|
string line;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
while ((line = await process.StandardOutput.ReadLineAsync()) != null)
|
while ((line = await process.StandardOutput.ReadLineAsync()) != null)
|
||||||
{
|
{
|
||||||
|
Console.WriteLine(line);
|
||||||
|
stdout += line;
|
||||||
if (first)
|
if (first)
|
||||||
{
|
{
|
||||||
provider = (new Regex(@"\w+")).Match(line).Value;
|
provider = (new Regex(@"\w+")).Match(line).Value;
|
||||||
@ -96,12 +111,14 @@ namespace ytdlp_gui
|
|||||||
}
|
}
|
||||||
line = line.Trim();
|
line = line.Trim();
|
||||||
|
|
||||||
|
/*
|
||||||
Match match = this.already_downloaded_regex.Match(line);
|
Match match = this.already_downloaded_regex.Match(line);
|
||||||
if (match.Success)
|
if (match.Success)
|
||||||
{
|
{
|
||||||
file = match.Groups[1].Value;
|
file = match.Groups[1].Value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
if (line.StartsWith("[info] "))
|
if (line.StartsWith("[info] "))
|
||||||
ProgInfo(line.Substring("[info] ".Length).TrimStart());
|
ProgInfo(line.Substring("[info] ".Length).TrimStart());
|
||||||
@ -111,14 +128,46 @@ namespace ytdlp_gui
|
|||||||
|
|
||||||
if (line.StartsWith("[Merger] "))
|
if (line.StartsWith("[Merger] "))
|
||||||
ProgMerger(line.Substring("[Merger] ".Length).TrimStart());
|
ProgMerger(line.Substring("[Merger] ".Length).TrimStart());
|
||||||
|
|
||||||
|
if (line.StartsWith("[youtube:tab] "))
|
||||||
|
ProgYoutube(line.Substring("[youtube:tab] ".Length).TrimStart());
|
||||||
|
|
||||||
|
if (playlist_last != playlist_current)
|
||||||
|
{
|
||||||
|
playlist_last = playlist_current;
|
||||||
|
if (File != null)
|
||||||
|
Finished?.Invoke(this, new YTdlpFinishedEventArgs(provider, File, files.ToArray()));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stop)
|
||||||
|
return new string[0];
|
||||||
|
|
||||||
process.WaitForExit();
|
process.WaitForExit();
|
||||||
process.Close();
|
if (process.ExitCode == 0)
|
||||||
|
{
|
||||||
|
if (File != null)
|
||||||
Finished?.Invoke(this, new YTdlpFinishedEventArgs(provider, File, files.ToArray()));
|
Finished?.Invoke(this, new YTdlpFinishedEventArgs(provider, File, files.ToArray()));
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
Error?.Invoke(this, new YTdlpErrorEventArgs(provider, stdout));
|
||||||
|
|
||||||
|
return files.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Cancel()
|
||||||
|
{
|
||||||
|
stop = true;
|
||||||
|
if (!process.HasExited) try
|
||||||
|
{
|
||||||
|
ProcessUtil.KillProcessAndChildren(process.Id);
|
||||||
|
}
|
||||||
|
catch (Exception) { /* We tried ¯\_(ツ)_/¯ */ }
|
||||||
|
process.WaitForExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
private static extern int GetParentProcessId(int processId);
|
||||||
|
|
||||||
protected void ProgInfo(string line)
|
protected void ProgInfo(string line)
|
||||||
{
|
{
|
||||||
@ -147,7 +196,10 @@ namespace ytdlp_gui
|
|||||||
provider,
|
provider,
|
||||||
"download",
|
"download",
|
||||||
File,
|
File,
|
||||||
progress
|
progress,
|
||||||
|
playlist,
|
||||||
|
playlist_current,
|
||||||
|
playlist_total
|
||||||
);
|
);
|
||||||
Progress?.Invoke(this, args);
|
Progress?.Invoke(this, args);
|
||||||
}
|
}
|
||||||
@ -167,10 +219,21 @@ namespace ytdlp_gui
|
|||||||
provider,
|
provider,
|
||||||
"download",
|
"download",
|
||||||
File,
|
File,
|
||||||
progress
|
progress,
|
||||||
|
playlist,
|
||||||
|
playlist_current,
|
||||||
|
playlist_total
|
||||||
);
|
);
|
||||||
Progress?.Invoke(this, args);
|
Progress?.Invoke(this, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match = download_playlist_item_regex.Match(line);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
GroupCollection groups = match.Groups;
|
||||||
|
playlist_current = int.Parse(groups[1].Value);
|
||||||
|
playlist_total = int.Parse(groups[2].Value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ProgMerger(string line)
|
protected void ProgMerger(string line)
|
||||||
@ -186,12 +249,30 @@ namespace ytdlp_gui
|
|||||||
provider,
|
provider,
|
||||||
"merge",
|
"merge",
|
||||||
file,
|
file,
|
||||||
1
|
1,
|
||||||
|
playlist,
|
||||||
|
playlist_current,
|
||||||
|
playlist_total
|
||||||
);
|
);
|
||||||
Progress?.Invoke(this, args);
|
Progress?.Invoke(this, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void ProgYoutube(string line)
|
||||||
|
{
|
||||||
|
Match match;
|
||||||
|
|
||||||
|
match = download_playlist_regex.Match(line);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
playlist = match.Groups[1].Value;
|
||||||
|
// How much we downloading
|
||||||
|
playlist_total = int.Parse(match.Groups[2].Value);
|
||||||
|
// How much there is
|
||||||
|
// playlist_total = int.Parse(match.Groups[3].Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private float ParseFloat(string str)
|
private float ParseFloat(string str)
|
||||||
{
|
{
|
||||||
return float.Parse(str, NumberStyles.Any, ci);
|
return float.Parse(str, NumberStyles.Any, ci);
|
||||||
@ -199,11 +280,17 @@ namespace ytdlp_gui
|
|||||||
|
|
||||||
private readonly Process process;
|
private readonly Process process;
|
||||||
private readonly CultureInfo ci;
|
private readonly CultureInfo ci;
|
||||||
private readonly Regex already_downloaded_regex;
|
private readonly Regex download_playlist_regex = new Regex(@"Playlist (.+):\s+Downloading\s+(\d+)\s+items\s+of\s+(\d+)");
|
||||||
private readonly Regex download_regex;
|
private readonly Regex download_playlist_item_regex = new Regex(@"Downloading\s+item\s(\d+)\s+of\s+(\d+)");
|
||||||
private readonly Regex download_finish_regex;
|
private readonly Regex already_downloaded_regex = new Regex(@"\[download\]\s+(.+) has already been downloaded");
|
||||||
private readonly Regex merge_regex;
|
private readonly Regex download_regex = new Regex(@"([0-9.]+)%\s+of\s*~?\s+([0-9.]+)(\w+)\s+at\s+([0-9.]+)(\w+\/\w+)\sETA\s(\d+:\d+)");
|
||||||
|
private readonly Regex download_finish_regex = new Regex(@"([0-9.]+)%\s+of\s+([0-9.]+)(\w+)\s+in\s+(\d+:\d+:\d+)\s+at\s+([0-9.]+)(\w+\/\w+)");
|
||||||
|
private readonly Regex merge_regex = new Regex(@"Merging formats into ""(.+)""");
|
||||||
private string provider;
|
private string provider;
|
||||||
|
private string playlist;
|
||||||
|
private int playlist_current = 1;
|
||||||
|
private int playlist_total = 1;
|
||||||
|
private bool stop = false;
|
||||||
private string File {
|
private string File {
|
||||||
get { return file; }
|
get { return file; }
|
||||||
set { file = value; if (!files.Contains(value)) files.Add(value); }
|
set { file = value; if (!files.Contains(value)) files.Add(value); }
|
||||||
@ -237,7 +324,7 @@ namespace ytdlp_gui
|
|||||||
process.StartInfo.UseShellExecute = false;
|
process.StartInfo.UseShellExecute = false;
|
||||||
process.StartInfo.CreateNoWindow = true;
|
process.StartInfo.CreateNoWindow = true;
|
||||||
process.StartInfo.RedirectStandardOutput = true;
|
process.StartInfo.RedirectStandardOutput = true;
|
||||||
process.StartInfo.RedirectStandardError = true;
|
// process.StartInfo.RedirectStandardError = true;
|
||||||
process.StartInfo.WorkingDirectory = workdir;
|
process.StartInfo.WorkingDirectory = workdir;
|
||||||
|
|
||||||
return new YTdlpProcess(process);
|
return new YTdlpProcess(process);
|
||||||
|
|||||||
@ -35,6 +35,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Management" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
@ -60,6 +61,7 @@
|
|||||||
<DependentUpon>Main.cs</DependentUpon>
|
<DependentUpon>Main.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="App.cs" />
|
<Compile Include="App.cs" />
|
||||||
|
<Compile Include="ProcessUtil.cs" />
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="YTdlp.cs" />
|
<Compile Include="YTdlp.cs" />
|
||||||
|
|||||||
Reference in New Issue
Block a user