@@ -27,10 +27,12 @@ public class TelegramService : ITelegramService
2727 private TransactionInfoService _tis { get ; set ; }
2828 private IDbService _db { get ; set ; }
2929 private ITaskPersistenceService _persistence { get ; set ; }
30- private static WTelegram . Client client = null ;
30+ private static WTelegram . Client client = null ; // Used for uploads and general operations
31+ private static WTelegram . Client downloadClient = null ; // Dedicated client for downloads
3132 private static Messages_Chats chats = null ;
3233 private static List < ChatViewBase > favouriteChannels = new List < ChatViewBase > ( ) ;
3334 private static Mutex mut = new Mutex ( ) ;
35+ private static Mutex downloadClientMut = new Mutex ( ) ;
3436 private ILogger < IFileService > _logger { get ; set ; }
3537
3638 // Event that fires when user successfully logs in
@@ -74,6 +76,76 @@ private void newClient()
7476
7577 }
7678
79+ /// <summary>
80+ /// Gets the dedicated download client, creating it if necessary.
81+ /// Uses a separate session file to allow independent bandwidth for downloads.
82+ /// </summary>
83+ private async Task < WTelegram . Client > GetDownloadClientAsync ( )
84+ {
85+ if ( downloadClient != null )
86+ return downloadClient ;
87+
88+ downloadClientMut . WaitOne ( ) ;
89+ try
90+ {
91+ if ( downloadClient != null )
92+ return downloadClient ;
93+
94+ var mainSessionPath = UserService . USERDATAFOLDER + "/WTelegram.session" ;
95+ var downloadSessionPath = UserService . USERDATAFOLDER + "/WTelegram_download.session" ;
96+
97+ // Copy the main session file if download session doesn't exist
98+ if ( ! File . Exists ( downloadSessionPath ) && File . Exists ( mainSessionPath ) )
99+ {
100+ File . Copy ( mainSessionPath , downloadSessionPath , overwrite : true ) ;
101+ _logger . LogInformation ( "Created download client session from main session" ) ;
102+ }
103+
104+ downloadClient = new WTelegram . Client (
105+ Convert . ToInt32 ( GeneralConfigStatic . tlconfig ? . api_id ?? Environment . GetEnvironmentVariable ( "api_id" ) ) ,
106+ GeneralConfigStatic . tlconfig ? . hash_id ?? Environment . GetEnvironmentVariable ( "hash_id" ) ,
107+ downloadSessionPath ) ;
108+
109+ // Login the download client (should auto-login from session)
110+ await downloadClient . LoginUserIfNeeded ( ) ;
111+ _logger . LogInformation ( "Download client initialized successfully - separate connection for downloads" ) ;
112+
113+ return downloadClient ;
114+ }
115+ catch ( Exception ex )
116+ {
117+ _logger . LogWarning ( ex , "Failed to create dedicated download client, falling back to main client" ) ;
118+ return client ;
119+ }
120+ finally
121+ {
122+ downloadClientMut . ReleaseMutex ( ) ;
123+ }
124+ }
125+
126+ /// <summary>
127+ /// Resets the download client (e.g., after logout)
128+ /// </summary>
129+ public static void ResetDownloadClient ( )
130+ {
131+ downloadClientMut . WaitOne ( ) ;
132+ try
133+ {
134+ downloadClient ? . Dispose ( ) ;
135+ downloadClient = null ;
136+
137+ var downloadSessionPath = UserService . USERDATAFOLDER + "/WTelegram_download.session" ;
138+ if ( File . Exists ( downloadSessionPath ) )
139+ {
140+ File . Delete ( downloadSessionPath ) ;
141+ }
142+ }
143+ finally
144+ {
145+ downloadClientMut . ReleaseMutex ( ) ;
146+ }
147+ }
148+
77149 public async Task < User > CallQrGenerator ( Action < string > func , CancellationToken ct , bool logoutFirst = false )
78150 {
79151 return await client . LoginWithQRCode ( func , logoutFirst : logoutFirst , ct : ct ) ;
@@ -219,6 +291,7 @@ public async Task logOff()
219291 await client . Auth_LogOut ( ) ;
220292 UserService . deleteUserDataToFile ( ) ;
221293 client . Dispose ( ) ;
294+ ResetDownloadClient ( ) ;
222295 newClient ( ) ;
223296 _logger . LogInformation ( "User logged off successfully" ) ;
224297 }
@@ -713,6 +786,10 @@ public async Task<Byte[]> DownloadFileStream(Message message, long offset, int l
713786 int totalLimit = limit ;
714787 long currentOffset = offset ;
715788 Byte [ ] response ;
789+
790+ // Use dedicated download client for independent bandwidth
791+ var dlClient = await GetDownloadClientAsync ( ) ;
792+
716793 if ( message is Message msg && msg . media is MessageMediaDocument doc )
717794 {
718795 try
@@ -725,7 +802,7 @@ public async Task<Byte[]> DownloadFileStream(Message message, long offset, int l
725802 if ( totalLimit >= FILESPLITSIZE )
726803 {
727804 totalDownloadBytes = FILESPLITSIZE ;
728-
805+
729806 }
730807 //else if (totalLimit >= 4096)
731808 //{
@@ -751,12 +828,13 @@ public async Task<Byte[]> DownloadFileStream(Message message, long offset, int l
751828 Upload_FileBase file = null ;
752829 try
753830 {
754- file = await client . Upload_GetFile ( location , currentOffset , limit : totalDownloadBytes ) ;
831+ file = await dlClient . Upload_GetFile ( location , currentOffset , limit : totalDownloadBytes ) ;
755832 }
756833 catch ( RpcException ex ) when ( ex . Code == 303 && ex . Message == "FILE_MIGRATE_X" )
757834 {
758- client = await client . GetClientForDC ( - ex . X , true ) ;
759- file = await client . Upload_GetFile ( location , currentOffset , limit : totalDownloadBytes ) ;
835+ downloadClient = await dlClient . GetClientForDC ( - ex . X , true ) ;
836+ dlClient = downloadClient ;
837+ file = await dlClient . Upload_GetFile ( location , currentOffset , limit : totalDownloadBytes ) ;
760838 }
761839 catch ( RpcException ex ) when ( ex . Code == 400 && ex . Message == "OFFSET_INVALID" )
762840 {
@@ -789,7 +867,7 @@ public async Task<Byte[]> DownloadFileStream(Message message, long offset, int l
789867 throw new InvalidOperationException ( "Unexpected file type returned." ) ;
790868 }
791869 }
792-
870+
793871 throw new ArgumentException ( "Invalid message or media type." ) ;
794872 }
795873
@@ -804,6 +882,9 @@ public async Task<Stream> DownloadFileAndReturn(ChatMessages message, Stream ms
804882 model . m = message ;
805883 model . channel = message . user ;
806884
885+ // Use dedicated download client for independent bandwidth
886+ var dlClient = await GetDownloadClientAsync ( ) ;
887+
807888 if ( message . message . media is MessageMediaDocument { document : TL . Document document } )
808889 {
809890
@@ -818,7 +899,7 @@ public async Task<Stream> DownloadFileAndReturn(ChatMessages message, Stream ms
818899 // using var fileStream = File.Create(filename);
819900 MemoryStream dest = new MemoryStream ( ) ;
820901 // using var dest = new FileStream($"{Path.Combine(folder != null ? folder : Path.Combine(Environment.CurrentDirectory, "download"), filename)}", FileMode.Create, FileAccess.Write);
821- await client . DownloadFileAsync ( document , ms ?? dest , ( PhotoSizeBase ) null , model . ProgressCallback ) ;
902+ await dlClient . DownloadFileAsync ( document , ms ?? dest , ( PhotoSizeBase ) null , model . ProgressCallback ) ;
822903 _logger . LogInformation ( "Document download completed - FileName: {FileName}" , filename ) ;
823904 return ms ?? dest ;
824905 }
@@ -833,7 +914,7 @@ public async Task<Stream> DownloadFileAndReturn(ChatMessages message, Stream ms
833914 _logger . LogInformation ( "Starting photo download - FileName: {FileName}" , filename ) ;
834915 MemoryStream dest = new MemoryStream ( ) ;
835916 // using var dest = new FileStream($"{Path.Combine(Environment.CurrentDirectory!, "wwwroot", "img", "telegram", filename)}", FileMode.Create, FileAccess.Write);
836- var type = await client . DownloadFileAsync ( photo , ms ?? dest , ( PhotoSizeBase ) null , model . ProgressCallback ) ;
917+ var type = await dlClient . DownloadFileAsync ( photo , ms ?? dest , ( PhotoSizeBase ) null , model . ProgressCallback ) ;
837918 dest . Close ( ) ; // necessary for the renaming
838919 _logger . LogInformation ( "Photo download completed - FileName: {FileName}" , filename ) ;
839920 return ms ?? dest ;
@@ -854,6 +935,9 @@ public async Task<Stream> DownloadFileAndReturnWithOffset(ChatMessages message,
854935 model . m = message ;
855936 model . channel = message . user ;
856937
938+ // Use dedicated download client for independent bandwidth
939+ var dlClient = await GetDownloadClientAsync ( ) ;
940+
857941 if ( message . message . media is MessageMediaDocument { document : TL . Document document } )
858942 {
859943 model . _size = document . size ;
@@ -870,7 +954,7 @@ public async Task<Stream> DownloadFileAndReturnWithOffset(ChatMessages message,
870954 {
871955 // No offset - use standard download
872956 MemoryStream dest = new MemoryStream ( ) ;
873- await client . DownloadFileAsync ( document , ms ?? dest , ( PhotoSizeBase ) null , model . ProgressCallback ) ;
957+ await dlClient . DownloadFileAsync ( document , ms ?? dest , ( PhotoSizeBase ) null , model . ProgressCallback ) ;
874958 _logger . LogInformation ( "Document download completed - FileName: {FileName}" , filename ) ;
875959 return ms ?? dest ;
876960 }
@@ -906,13 +990,14 @@ public async Task<Stream> DownloadFileAndReturnWithOffset(ChatMessages message,
906990 Upload_FileBase file = null ;
907991 try
908992 {
909- file = await client . Upload_GetFile ( location , currentOffset , limit : chunkSize ) ;
993+ file = await dlClient . Upload_GetFile ( location , currentOffset , limit : chunkSize ) ;
910994 }
911995 catch ( RpcException ex ) when ( ex . Code == 303 && ex . Message == "FILE_MIGRATE_X" )
912996 {
913997 _logger . LogWarning ( "DC migration required, switching to DC {DC}" , - ex . X ) ;
914- client = await client . GetClientForDC ( - ex . X , true ) ;
915- file = await client . Upload_GetFile ( location , currentOffset , limit : chunkSize ) ;
998+ downloadClient = await dlClient . GetClientForDC ( - ex . X , true ) ;
999+ dlClient = downloadClient ;
1000+ file = await dlClient . Upload_GetFile ( location , currentOffset , limit : chunkSize ) ;
9161001 }
9171002 catch ( RpcException ex ) when ( ex . Code == 400 && ex . Message == "OFFSET_INVALID" )
9181003 {
@@ -958,7 +1043,7 @@ public async Task<Stream> DownloadFileAndReturnWithOffset(ChatMessages message,
9581043 _logger . LogInformation ( "Photo download (no resume support) - PhotoId: {PhotoId}" , photo . id ) ;
9591044 var filename = $ "{ photo . id } .jpg";
9601045 MemoryStream dest = new MemoryStream ( ) ;
961- var type = await client . DownloadFileAsync ( photo , ms ?? dest , ( PhotoSizeBase ) null , model . ProgressCallback ) ;
1046+ var type = await dlClient . DownloadFileAsync ( photo , ms ?? dest , ( PhotoSizeBase ) null , model . ProgressCallback ) ;
9621047 dest . Close ( ) ;
9631048 return ms ?? dest ;
9641049 }
@@ -978,6 +1063,9 @@ public async Task<string> DownloadFile(ChatMessages message, string fileName = n
9781063 model . m = message ;
9791064 model . channel = message . user ;
9801065
1066+ // Use dedicated download client for independent bandwidth
1067+ var dlClient = await GetDownloadClientAsync ( ) ;
1068+
9811069 if ( message . message . media is MessageMediaDocument { document : TL . Document document } )
9821070 {
9831071
@@ -991,7 +1079,7 @@ public async Task<string> DownloadFile(ChatMessages message, string fileName = n
9911079 _logger . LogInformation ( "Starting file download to disk - FileName: {FileName}, Size: {SizeMB:F2}MB" , filename , document . size / ( 1024.0 * 1024.0 ) ) ;
9921080 // using var fileStream = File.Create(filename);
9931081 using var dest = new FileStream ( $ "{ Path . Combine ( folder != null ? folder : Path . Combine ( Environment . CurrentDirectory , "local" , "temp" ) , filename ) } ", FileMode . Create , FileAccess . Write ) ;
994- await client . DownloadFileAsync ( document , dest , ( PhotoSizeBase ) null , model . ProgressCallback ) ;
1082+ await dlClient . DownloadFileAsync ( document , dest , ( PhotoSizeBase ) null , model . ProgressCallback ) ;
9951083
9961084 _logger . LogInformation ( "File download to disk completed - FileName: {FileName}" , filename ) ;
9971085 }
@@ -1005,7 +1093,7 @@ public async Task<string> DownloadFile(ChatMessages message, string fileName = n
10051093 }
10061094 _logger . LogInformation ( "Starting photo download to disk - FileName: {FileName}" , filename ) ;
10071095 using var dest = new FileStream ( $ "{ Path . Combine ( Environment . CurrentDirectory ! , "wwwroot" , "img" , "telegram" , filename ) } ", FileMode . Create , FileAccess . Write ) ;
1008- var type = await client . DownloadFileAsync ( photo , dest , progress : model . ProgressCallback ) ;
1096+ var type = await dlClient . DownloadFileAsync ( photo , dest , progress : model . ProgressCallback ) ;
10091097 dest . Close ( ) ;
10101098 _logger . LogInformation ( "Photo download to disk completed - FileName: {FileName}" , filename ) ;
10111099 if ( type is not Storage_FileType . unknown and not Storage_FileType . partial )
0 commit comments