1111
1212using System . Diagnostics ;
1313using System . Net ;
14+ using YoutubeDLSharp ;
15+ using YoutubeDLSharp . Options ;
1416
1517namespace TelegramMediaRelayBot
1618{
1719 public class MediaGet
1820 {
19- private static readonly string [ ] ColonSpaceSeparator = [ ": " ] ;
20-
2121 public static async Task < List < byte [ ] > ? > DownloadMedia ( ITelegramBotClient botClient , string videoUrl , Message statusMessage , CancellationToken cancellationToken )
2222 {
2323 try
@@ -34,10 +34,10 @@ public class MediaGet
3434 }
3535
3636 Log . Debug ( "Starting video download via yt-dlp..." ) ;
37-
37+
3838 string tempDirPath = Path . Combine ( Path . GetTempPath ( ) , Guid . NewGuid ( ) . ToString ( ) ) ;
3939 Directory . CreateDirectory ( tempDirPath ) ;
40-
40+
4141 if ( Config . torEnabled )
4242 {
4343 var proxy = new WebProxy ( $ "socks5://{ Config . torSocksHost } :{ Config . torSocksPort } ") ;
@@ -47,80 +47,88 @@ public class MediaGet
4747 Log . Debug ( "Tor IP: " + result ) ;
4848 }
4949
50- var startInfo = new ProcessStartInfo
50+ var ytdl = new YoutubeDL
5151 {
52- FileName = "yt-dlp" ,
53- Arguments = $ "--proxy \" { Config . proxy } \" -v -f mp4 --output \" { tempDirPath } /video.%(ext)s\" { videoUrl } ",
54- RedirectStandardOutput = true ,
55- RedirectStandardError = true ,
56- UseShellExecute = false ,
57- CreateNoWindow = true
52+ YoutubeDLPath = "yt-dlp" ,
53+ OutputFolder = tempDirPath ,
54+ OutputFileTemplate = "video.%(ext)s" ,
55+ OverwriteFiles = true
5856 } ;
5957
60- using ( Process process = new Process { StartInfo = startInfo } )
58+ var overrideOptions = new OptionSet
6159 {
62- process . Start ( ) ;
63- Log . Debug ( "Process started." ) ;
64-
65- List < string > outputLines = new List < string > ( ) ;
66- List < string > errorLines = new List < string > ( ) ;
60+ Format = "mp4" ,
61+ Verbose = true
62+ } ;
6763
68- Task readOutputTask = ReadLinesAsync ( process . StandardOutput , outputLines , botClient , statusMessage , cancellationToken ) ;
69- Task readErrorTask = ReadLinesAsync ( process . StandardError , errorLines , botClient , statusMessage , cancellationToken ) ;
64+ if ( ! string . IsNullOrEmpty ( Config . proxy ) )
65+ {
66+ overrideOptions . Proxy = Config . proxy ;
67+ }
7068
71- await process . WaitForExitAsync ( ) ;
69+ DateTime lastProgressUpdate = DateTime . MinValue ;
70+ var progress = new Progress < DownloadProgress > ( p =>
71+ {
72+ if ( DateTime . UtcNow - lastProgressUpdate < TimeSpan . FromMilliseconds ( Config . videoGetDelay ) )
73+ return ;
7274
73- await Task . WhenAll ( readOutputTask , readErrorTask ) ;
75+ lastProgressUpdate = DateTime . UtcNow ;
76+ int percent = ( int ) ( p . Progress * 100 ) ;
77+ string statusText = $ "[download] { percent } %";
78+ if ( ! string . IsNullOrEmpty ( p . DownloadSpeed ) )
79+ statusText += $ " at { p . DownloadSpeed } ";
80+ if ( ! string . IsNullOrEmpty ( p . ETA ) )
81+ statusText += $ " ETA { p . ETA } ";
7482
75- string output = string . Join ( " \n " , outputLines ) ;
76- string error = string . Join ( " \n " , errorLines ) ;
83+ if ( Config . showVideoDownloadProgress )
84+ Log . Debug ( $ "Video download progress: { statusText } " ) ;
7785
78- if ( process . ExitCode == 0 )
86+ _ = Task . Run ( async ( ) =>
7987 {
8088 try
8189 {
82- string ? downloadLine = output . Split ( '\n ' ) . FirstOrDefault ( line => line . StartsWith ( "[download] Destination:" ) ) ;
83- if ( downloadLine == null )
84- {
85- Log . Error ( "Could not find download destination in yt-dlp output." ) ;
86- return null ;
87- }
88-
89- string [ ] parts = downloadLine . Split ( ColonSpaceSeparator , 2 , StringSplitOptions . None ) ;
90- if ( parts . Length < 2 )
91- {
92- Log . Error ( "Download destination not found in yt-dlp output." ) ;
93- return null ;
94- }
95-
96- string finalFilePath = parts [ 1 ] . Trim ( ) ;
97- Log . Debug ( $ "Final file path: { finalFilePath } ") ;
98-
99- if ( System . IO . File . Exists ( finalFilePath ) )
100- {
101- List < byte [ ] > ? videoBytes = new List < byte [ ] > { System . IO . File . ReadAllBytes ( finalFilePath ) } ;
102-
103- System . IO . File . Delete ( finalFilePath ) ;
104- Directory . Delete ( tempDirPath , recursive : true ) ;
105-
106- return videoBytes ;
107- }
108-
109- Log . Error ( $ "Final file does not exist: { finalFilePath } ") ;
90+ await botClient . EditMessageText (
91+ statusMessage . Chat . Id ,
92+ statusMessage . MessageId ,
93+ statusText ,
94+ cancellationToken : cancellationToken ) ;
11095 }
11196 catch ( Exception ex )
11297 {
113- Log . Error ( ex , $ "Error reading file: { ex . Message } ") ;
98+ Log . Debug ( ex , "Error editing message. " ) ;
11499 }
115- }
116- else
100+ } ) ;
101+ } ) ;
102+
103+ var downloadResult = await ytdl . RunVideoDownload (
104+ videoUrl ,
105+ format : "mp4" ,
106+ ct : cancellationToken ,
107+ progress : progress ,
108+ overrideOptions : overrideOptions ) ;
109+
110+ if ( downloadResult . Success )
111+ {
112+ string filePath = downloadResult . Data ;
113+ Log . Debug ( $ "Final file path: { filePath } ") ;
114+
115+ if ( System . IO . File . Exists ( filePath ) )
117116 {
118- Log . Error ( "Video download failed: " + error ) ;
117+ byte [ ] videoBytes = await System . IO . File . ReadAllBytesAsync ( filePath , cancellationToken ) ;
118+ Directory . Delete ( tempDirPath , recursive : true ) ;
119+ return new List < byte [ ] > { videoBytes } ;
119120 }
120-
121- Directory . Delete ( tempDirPath , recursive : true ) ;
122- return null ;
121+
122+ Log . Error ( $ "Final file does not exist: { filePath } ") ;
123+ }
124+ else
125+ {
126+ string errors = string . Join ( "\n " , downloadResult . ErrorOutput ) ;
127+ Log . Error ( "Video download failed: " + errors ) ;
123128 }
129+
130+ Directory . Delete ( tempDirPath , recursive : true ) ;
131+ return null ;
124132 }
125133 catch ( Exception ex )
126134 {
0 commit comments