@@ -161,6 +161,7 @@ private void createDownloadFolder()
161161 private void newClient ( )
162162 {
163163 client = new WTelegram . Client ( Convert . ToInt32 ( GeneralConfigStatic . tlconfig ? . api_id ?? Environment . GetEnvironmentVariable ( "api_id" ) ) , GeneralConfigStatic . tlconfig ? . hash_id ?? Environment . GetEnvironmentVariable ( "hash_id" ) , UserService . USERDATAFOLDER + "/WTelegram.session" ) ;
164+ ApplyConfiguredParallelTransfers ( client ) ;
164165 if ( GeneralConfigStatic . config . ShouldShowLogInTerminal )
165166 {
166167 // WTelegram.Helpers.Log = (lvl, str) => Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [{"TDIWE!"[lvl]}] {str}");
@@ -171,6 +172,67 @@ private void newClient()
171172
172173 }
173174
175+ // WTelegramClient requests file parts through a per-client semaphore that
176+ // defaults to 2 parts in flight, capping transfers at ~1MB per round-trip.
177+ // Tracks the value applied to each client instance (main client and the
178+ // media-DC clients WTelegram creates internally, which do NOT inherit the
179+ // main client's ParallelTransfers).
180+ private static readonly ConditionalWeakTable < WTelegram . Client , StrongBox < int > > appliedParallelTransfers = new ( ) ;
181+ private const int WTELEGRAM_DEFAULT_PARALLEL_TRANSFERS = 2 ;
182+
183+ public static int GetConfiguredParallelTransfers ( )
184+ {
185+ return Math . Clamp ( GeneralConfigStatic . config ? . ParallelTransfers ?? 4 , 1 , 16 ) ;
186+ }
187+
188+ private static void ApplyConfiguredParallelTransfers ( WTelegram . Client c )
189+ {
190+ if ( c == null )
191+ return ;
192+ int desired = GetConfiguredParallelTransfers ( ) ;
193+ int delta ;
194+ lock ( appliedParallelTransfers )
195+ {
196+ StrongBox < int > applied = appliedParallelTransfers . GetOrCreateValue ( c ) ;
197+ if ( applied . Value == 0 )
198+ applied . Value = WTELEGRAM_DEFAULT_PARALLEL_TRANSFERS ;
199+ delta = desired - applied . Value ;
200+ if ( delta == 0 )
201+ return ;
202+ applied . Value = desired ;
203+ }
204+ try
205+ {
206+ // The ParallelTransfers setter adjusts the semaphore relative to its
207+ // CURRENT count, which is lower while parts are in flight. Applying
208+ // our delta on top of the current value keeps the configured maximum
209+ // correct even if a transfer is running on this client.
210+ c . ParallelTransfers = c . ParallelTransfers + delta ;
211+ }
212+ catch ( Exception )
213+ {
214+ // Never let a tuning failure break a transfer.
215+ }
216+ }
217+
218+ /// <summary>
219+ /// Resolves the client instance that WTelegram's DownloadFileAsync will use
220+ /// for the given file DC (dc_id == 0 means the main client) and applies the
221+ /// configured chunk parallelism to it before the transfer starts.
222+ /// </summary>
223+ private async Task PrepareTransferClientAsync ( int dcId )
224+ {
225+ try
226+ {
227+ WTelegram . Client c = dcId == 0 ? client : await client . GetClientForDC ( - dcId , true ) ;
228+ ApplyConfiguredParallelTransfers ( c ) ;
229+ }
230+ catch ( Exception ex )
231+ {
232+ _logger . LogDebug ( ex , "Could not prepare transfer client for DC {DcId}" , dcId ) ;
233+ }
234+ }
235+
174236 public async Task < User > CallQrGenerator ( Action < string > func , CancellationToken ct , bool logoutFirst = false )
175237 {
176238 return await client . LoginWithQRCode ( func , logoutFirst : logoutFirst , ct : ct ) ;
@@ -701,6 +763,7 @@ public async Task<Message> uploadFile(string chatId, Stream file, string fileNam
701763
702764 try
703765 {
766+ ApplyConfiguredParallelTransfers ( client ) ;
704767 var inputFile = await client . UploadFileAsync ( file , fileName , um . ProgressCallback ) ;
705768 var result = await client . SendMediaAsync ( peer , caption ?? fileName , inputFile , mimeType ) ;
706769 _logger . LogInformation ( "File upload completed - FileName: {FileName}, MessageId: {MessageId}" , fileName , result . id ) ;
@@ -1205,6 +1268,7 @@ public async Task<Stream> DownloadFileAndReturn(ChatMessages message, Stream ms
12051268 model . name = filename ;
12061269 _logger . LogInformation ( "Starting document download - FileName: {FileName}, Size: {SizeMB:F2}MB" , filename , document . size / ( 1024.0 * 1024.0 ) ) ;
12071270 MemoryStream dest = new MemoryStream ( ) ;
1271+ await PrepareTransferClientAsync ( document . dc_id ) ;
12081272 await client . DownloadFileAsync ( document , ms ?? dest , ( PhotoSizeBase ) null , model . ProgressCallback ) ;
12091273 _logger . LogInformation ( "Document download completed - FileName: {FileName}" , filename ) ;
12101274 return ms ?? dest ;
@@ -1253,6 +1317,7 @@ public async Task<Stream> DownloadFileAndReturnWithOffset(ChatMessages message,
12531317 if ( offset == 0 )
12541318 {
12551319 MemoryStream dest = new MemoryStream ( ) ;
1320+ await PrepareTransferClientAsync ( document . dc_id ) ;
12561321 await client . DownloadFileAsync ( document , ms ?? dest , ( PhotoSizeBase ) null , model . ProgressCallback ) ;
12571322 _logger . LogInformation ( "Document download completed - FileName: {FileName}" , filename ) ;
12581323 return ms ?? dest ;
@@ -1365,6 +1430,7 @@ public async Task<string> DownloadFile(ChatMessages message, string fileName = n
13651430 _tis . addToDownloadList ( model ) ;
13661431 _logger . LogInformation ( "Starting file download to disk - FileName: {FileName}, Size: {SizeMB:F2}MB" , filename , document . size / ( 1024.0 * 1024.0 ) ) ;
13671432 using var dest = new FileStream ( $ "{ Path . Combine ( folder != null ? folder : Path . Combine ( Environment . CurrentDirectory , "local" , "temp" ) , filename ) } ", FileMode . Create , FileAccess . Write ) ;
1433+ await PrepareTransferClientAsync ( document . dc_id ) ;
13681434 await client . DownloadFileAsync ( document , dest , ( PhotoSizeBase ) null , model . ProgressCallback ) ;
13691435 _logger . LogInformation ( "File download to disk completed - FileName: {FileName}" , filename ) ;
13701436 }
0 commit comments