diff --git a/TelegramBot/MediaGet.cs b/TelegramBot/MediaGet.cs index 49eb8c7..1fd315e 100644 --- a/TelegramBot/MediaGet.cs +++ b/TelegramBot/MediaGet.cs @@ -11,13 +11,13 @@ using System.Diagnostics; using System.Net; +using YoutubeDLSharp; +using YoutubeDLSharp.Options; namespace TelegramMediaRelayBot { public class MediaGet { - private static readonly string[] ColonSpaceSeparator = [": "]; - public static async Task?> DownloadMedia(ITelegramBotClient botClient, string videoUrl, Message statusMessage, CancellationToken cancellationToken) { try @@ -34,93 +34,111 @@ public class MediaGet } Log.Debug("Starting video download via yt-dlp..."); - + string tempDirPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(tempDirPath); - - if (Config.torEnabled) + try { - var proxy = new WebProxy($"socks5://{Config.torSocksHost}:{Config.torSocksPort}"); - var handler = new HttpClientHandler { Proxy = proxy, UseProxy = true }; - using var httpClient = new HttpClient(handler); - var result = await httpClient.GetStringAsync("https://check.torproject.org/api/ip"); - Log.Debug("Tor IP: " + result); - } + if (Config.torEnabled) + { + var proxy = new WebProxy($"socks5://{Config.torSocksHost}:{Config.torSocksPort}"); + var handler = new HttpClientHandler { Proxy = proxy, UseProxy = true }; + using var httpClient = new HttpClient(handler); + var result = await httpClient.GetStringAsync("https://check.torproject.org/api/ip"); + Log.Debug("Tor IP: " + result); + } - var startInfo = new ProcessStartInfo - { - FileName = "yt-dlp", - Arguments = $"--proxy \"{Config.proxy}\" -v -f mp4 --output \"{tempDirPath}/video.%(ext)s\" {videoUrl}", - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true - }; + var ytdl = new YoutubeDL + { + YoutubeDLPath = "yt-dlp", + OutputFolder = tempDirPath, + OutputFileTemplate = "video.%(ext)s", + OverwriteFiles = true + }; - using (Process process = new Process { StartInfo = startInfo }) - { - process.Start(); - Log.Debug("Process started."); + var overrideOptions = new OptionSet(); - List outputLines = new List(); - List errorLines = new List(); + string effectiveProxy = Config.proxy; + if (string.IsNullOrEmpty(effectiveProxy) && Config.torEnabled) + effectiveProxy = $"socks5://{Config.torSocksHost}:{Config.torSocksPort}"; - Task readOutputTask = ReadLinesAsync(process.StandardOutput, outputLines, botClient, statusMessage, cancellationToken); - Task readErrorTask = ReadLinesAsync(process.StandardError, errorLines, botClient, statusMessage, cancellationToken); + if (!string.IsNullOrEmpty(effectiveProxy)) + overrideOptions.Proxy = effectiveProxy; - await process.WaitForExitAsync(); + DateTime lastProgressUpdate = DateTime.MinValue; + var progress = new Progress(p => + { + if (DateTime.UtcNow - lastProgressUpdate < TimeSpan.FromMilliseconds(Config.videoGetDelay)) + return; - await Task.WhenAll(readOutputTask, readErrorTask); + lastProgressUpdate = DateTime.UtcNow; + int percent = (int)(p.Progress * 100); + string statusText = $"[download] {percent}%"; + if (!string.IsNullOrEmpty(p.DownloadSpeed)) + statusText += $" at {p.DownloadSpeed}"; + if (!string.IsNullOrEmpty(p.ETA)) + statusText += $" ETA {p.ETA}"; - string output = string.Join("\n", outputLines); - string error = string.Join("\n", errorLines); + if (Config.showVideoDownloadProgress) + Log.Debug($"Video download progress: {statusText}"); - if (process.ExitCode == 0) - { - try + _ = Task.Run(async () => { - string? downloadLine = output.Split('\n').FirstOrDefault(line => line.StartsWith("[download] Destination:")); - if (downloadLine == null) + try { - Log.Error("Could not find download destination in yt-dlp output."); - return null; + await botClient.EditMessageText( + statusMessage.Chat.Id, + statusMessage.MessageId, + statusText, + cancellationToken: cancellationToken); } - - string[] parts = downloadLine.Split(ColonSpaceSeparator, 2, StringSplitOptions.None); - if (parts.Length < 2) + catch (Exception ex) { - Log.Error("Download destination not found in yt-dlp output."); - return null; + Log.Debug(ex, "Error editing message."); } + }); + }); - string finalFilePath = parts[1].Trim(); - Log.Debug($"Final file path: {finalFilePath}"); + var downloadResult = await ytdl.RunVideoDownload( + videoUrl, + ct: cancellationToken, + progress: progress, + overrideOptions: overrideOptions); - if (System.IO.File.Exists(finalFilePath)) - { - List? videoBytes = new List { System.IO.File.ReadAllBytes(finalFilePath) }; - - System.IO.File.Delete(finalFilePath); - Directory.Delete(tempDirPath, recursive: true); + if (downloadResult.Success) + { + string filePath = downloadResult.Data; + Log.Debug($"Final file path: {filePath}"); - return videoBytes; - } - - Log.Error($"Final file does not exist: {finalFilePath}"); - } - catch (Exception ex) + if (System.IO.File.Exists(filePath)) { - Log.Error(ex, $"Error reading file: {ex.Message}"); + byte[] videoBytes = await System.IO.File.ReadAllBytesAsync(filePath, cancellationToken); + return new List { videoBytes }; } + + Log.Error($"Final file does not exist: {filePath}"); } else { - Log.Error("Video download failed: " + error); + string errors = string.Join("\n", downloadResult.ErrorOutput); + Log.Error("Video download failed: " + errors); } - - Directory.Delete(tempDirPath, recursive: true); + return null; } + catch (OperationCanceledException) + { + throw; + } + finally + { + if (Directory.Exists(tempDirPath)) + Directory.Delete(tempDirPath, recursive: true); + } + } + catch (OperationCanceledException) + { + throw; } catch (Exception ex) { diff --git a/TelegramMediaRelayBot.csproj b/TelegramMediaRelayBot.csproj index 2ba8f74..d6fe337 100644 --- a/TelegramMediaRelayBot.csproj +++ b/TelegramMediaRelayBot.csproj @@ -43,5 +43,6 @@ PreserveNewest + \ No newline at end of file