Skip to content

Commit 9c5cb4c

Browse files
committed
feat: add multiclient to upload and download
1 parent 0dea419 commit 9c5cb4c

1 file changed

Lines changed: 103 additions & 15 deletions

File tree

TelegramDownloader/Data/TelegramService.cs

Lines changed: 103 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)