2025-05-20 02:11:57 +02:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
|
using System.Deployment.Application;
|
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
|
|
namespace ytdlp_gui
|
|
|
|
|
|
{
|
|
|
|
|
|
public class YTdlpStartEventArgs : EventArgs
|
|
|
|
|
|
{
|
|
|
|
|
|
public YTdlpStartEventArgs(string provider)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.provider = provider;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public readonly string provider;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public class YTdlpProgressEventArgs : EventArgs
|
|
|
|
|
|
{
|
|
|
|
|
|
public YTdlpProgressEventArgs(string provider, string action, string file, float progress)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.provider = provider;
|
|
|
|
|
|
this.action = action;
|
|
|
|
|
|
this.file = file;
|
|
|
|
|
|
this.progress = progress;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public readonly string provider;
|
|
|
|
|
|
public readonly string action;
|
|
|
|
|
|
public readonly string file;
|
|
|
|
|
|
public readonly float progress;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public class YTdlpErrorEventArgs : EventArgs
|
|
|
|
|
|
{
|
|
|
|
|
|
public YTdlpErrorEventArgs(string provider, string error)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.provider = provider;
|
|
|
|
|
|
this.error = error;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public readonly string provider;
|
|
|
|
|
|
public readonly string error;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public class YTdlpFinishedEventArgs : EventArgs
|
|
|
|
|
|
{
|
|
|
|
|
|
public YTdlpFinishedEventArgs(string provider, string file, string[] files)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.provider = provider;
|
|
|
|
|
|
this.file = file;
|
|
|
|
|
|
this.files = files;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public readonly string provider;
|
|
|
|
|
|
public readonly string file;
|
|
|
|
|
|
public readonly string[] files;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public class YTdlpProcess
|
|
|
|
|
|
{
|
|
|
|
|
|
public YTdlpProcess(Process process)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.process = process;
|
|
|
|
|
|
|
|
|
|
|
|
ci = (CultureInfo)CultureInfo.CurrentCulture.Clone();
|
|
|
|
|
|
ci.NumberFormat.CurrencyDecimalSeparator = ".";
|
|
|
|
|
|
|
|
|
|
|
|
files = new List<string>();
|
|
|
|
|
|
|
2025-05-20 03:21:14 +02:00
|
|
|
|
this.already_downloaded_regex = new Regex(@"\[download\]\s+(.+) has already been downloaded");
|
2025-05-20 02:11:57 +02:00
|
|
|
|
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()
|
|
|
|
|
|
{
|
|
|
|
|
|
process.Start();
|
|
|
|
|
|
|
|
|
|
|
|
string line = null;
|
|
|
|
|
|
bool first = true;
|
|
|
|
|
|
while ((line = await process.StandardOutput.ReadLineAsync()) != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (first)
|
|
|
|
|
|
{
|
|
|
|
|
|
provider = (new Regex(@"\w+")).Match(line).Value;
|
|
|
|
|
|
first = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
line = line.Trim();
|
|
|
|
|
|
|
2025-05-20 03:21:14 +02:00
|
|
|
|
Match match = this.already_downloaded_regex.Match(line);
|
|
|
|
|
|
if (match.Success)
|
|
|
|
|
|
{
|
|
|
|
|
|
file = match.Groups[1].Value;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-20 02:11:57 +02:00
|
|
|
|
if (line.StartsWith("[info] "))
|
|
|
|
|
|
ProgInfo(line.Substring("[info] ".Length).TrimStart());
|
|
|
|
|
|
|
|
|
|
|
|
if (line.StartsWith("[download] "))
|
|
|
|
|
|
ProgDownload(line.Substring("[download] ".Length).TrimStart());
|
|
|
|
|
|
|
|
|
|
|
|
if (line.StartsWith("[Merger] "))
|
|
|
|
|
|
ProgMerger(line.Substring("[Merger] ".Length).TrimStart());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-20 03:31:46 +02:00
|
|
|
|
process.WaitForExit();
|
|
|
|
|
|
process.Close();
|
2025-05-20 02:11:57 +02:00
|
|
|
|
Finished?.Invoke(this, new YTdlpFinishedEventArgs(provider, File, files.ToArray()));
|
|
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected void ProgInfo(string line)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected void ProgDownload(string line)
|
|
|
|
|
|
{
|
|
|
|
|
|
const string dest_str = "Destination: ";
|
|
|
|
|
|
if (line.StartsWith(dest_str))
|
|
|
|
|
|
File = line.Substring(dest_str.Length);
|
|
|
|
|
|
|
|
|
|
|
|
Match match;
|
|
|
|
|
|
|
|
|
|
|
|
match = download_regex.Match(line);
|
|
|
|
|
|
if (match.Success)
|
|
|
|
|
|
{
|
|
|
|
|
|
GroupCollection groups = match.Groups;
|
|
|
|
|
|
float progress = ParseFloat( groups[1].Value ) / 100 ;
|
|
|
|
|
|
float total = ParseFloat( groups[2].Value ) ;
|
|
|
|
|
|
string total_unit = groups[3].Value ;
|
|
|
|
|
|
float speed = ParseFloat( groups[4].Value ) ;
|
|
|
|
|
|
string speed_unit = groups[5].Value ;
|
|
|
|
|
|
string estamated_time = groups[6].Value ;
|
|
|
|
|
|
|
|
|
|
|
|
YTdlpProgressEventArgs args = new YTdlpProgressEventArgs(
|
|
|
|
|
|
provider,
|
|
|
|
|
|
"download",
|
|
|
|
|
|
File,
|
|
|
|
|
|
progress
|
|
|
|
|
|
);
|
|
|
|
|
|
Progress?.Invoke(this, args);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
match = download_finish_regex.Match(line);
|
|
|
|
|
|
if (match.Success)
|
|
|
|
|
|
{
|
|
|
|
|
|
GroupCollection groups = match.Groups;
|
|
|
|
|
|
float progress = ParseFloat( groups[1].Value ) / 100 ;
|
|
|
|
|
|
float total = ParseFloat( groups[2].Value ) ;
|
|
|
|
|
|
string total_unit = groups[3].Value ;
|
|
|
|
|
|
string total_time = groups[4].Value ;
|
|
|
|
|
|
float speed = ParseFloat( groups[5].Value ) ;
|
|
|
|
|
|
string speed_unit = groups[6].Value ;
|
|
|
|
|
|
|
|
|
|
|
|
YTdlpProgressEventArgs args = new YTdlpProgressEventArgs(
|
|
|
|
|
|
provider,
|
|
|
|
|
|
"download",
|
|
|
|
|
|
File,
|
|
|
|
|
|
progress
|
|
|
|
|
|
);
|
|
|
|
|
|
Progress?.Invoke(this, args);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected void ProgMerger(string line)
|
|
|
|
|
|
{
|
|
|
|
|
|
Match match;
|
|
|
|
|
|
|
|
|
|
|
|
match = merge_regex.Match(line);
|
|
|
|
|
|
if (match.Success)
|
|
|
|
|
|
{
|
|
|
|
|
|
File = match.Groups[1].Value;
|
|
|
|
|
|
|
|
|
|
|
|
YTdlpProgressEventArgs args = new YTdlpProgressEventArgs(
|
|
|
|
|
|
provider,
|
|
|
|
|
|
"merge",
|
|
|
|
|
|
file,
|
|
|
|
|
|
1
|
|
|
|
|
|
);
|
|
|
|
|
|
Progress?.Invoke(this, args);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private float ParseFloat(string str)
|
|
|
|
|
|
{
|
|
|
|
|
|
return float.Parse(str, NumberStyles.Any, ci);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private readonly Process process;
|
|
|
|
|
|
private readonly CultureInfo ci;
|
2025-05-20 03:21:14 +02:00
|
|
|
|
private readonly Regex already_downloaded_regex;
|
2025-05-20 02:11:57 +02:00
|
|
|
|
private readonly Regex download_regex;
|
|
|
|
|
|
private readonly Regex download_finish_regex;
|
|
|
|
|
|
private readonly Regex merge_regex;
|
|
|
|
|
|
private string provider;
|
|
|
|
|
|
private string File {
|
|
|
|
|
|
get { return file; }
|
|
|
|
|
|
set { file = value; if (!files.Contains(value)) files.Add(value); }
|
|
|
|
|
|
}
|
|
|
|
|
|
private string file;
|
|
|
|
|
|
private List<string> files;
|
|
|
|
|
|
|
|
|
|
|
|
public event EventHandler<YTdlpStartEventArgs> Start;
|
|
|
|
|
|
public event EventHandler<YTdlpProgressEventArgs> Progress;
|
|
|
|
|
|
public event EventHandler<YTdlpErrorEventArgs> Error;
|
|
|
|
|
|
public event EventHandler<YTdlpFinishedEventArgs> Finished;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public class YTdlp : Program
|
|
|
|
|
|
{
|
|
|
|
|
|
public YTdlp() : base("yt-dlp")
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-20 02:28:22 +02:00
|
|
|
|
public YTdlpProcess Download(string url, bool audio_only, bool download_playlist, string workdir)
|
2025-05-20 02:11:57 +02:00
|
|
|
|
{
|
|
|
|
|
|
Process process = new Process();
|
|
|
|
|
|
process.StartInfo.FileName = program;
|
|
|
|
|
|
process.StartInfo.Arguments = String.Format(
|
2025-05-20 02:28:22 +02:00
|
|
|
|
"{0} {1} \"{2}\"",
|
|
|
|
|
|
audio_only ? "-f ba" : "",
|
2025-05-20 02:11:57 +02:00
|
|
|
|
download_playlist ? "--yes-playlist" : "--no-playlist",
|
|
|
|
|
|
url.Replace("\"", "\"\"\"")
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
process.StartInfo.UseShellExecute = false;
|
|
|
|
|
|
process.StartInfo.CreateNoWindow = true;
|
|
|
|
|
|
process.StartInfo.RedirectStandardOutput = true;
|
|
|
|
|
|
process.StartInfo.RedirectStandardError = true;
|
|
|
|
|
|
process.StartInfo.WorkingDirectory = workdir;
|
|
|
|
|
|
|
|
|
|
|
|
return new YTdlpProcess(process);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|