Skip to content

Commit 7e4f6f4

Browse files
authored
develop to main (#80)
* feat: add order and search in url * feat: add preload data channels at startup * feat: add more url filrters * fix: modify actual telegram size
2 parents 40985f9 + 435fa3b commit 7e4f6f4

13 files changed

Lines changed: 407 additions & 53 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
TelegramDownloader/userData.json
22
TelegramDownloader/.claude/
3+
.claude/

TelegramDownloader/Data/FileService.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1408,7 +1408,8 @@ public async Task UploadFileFromServer(string dbName, string currentPath, List<S
14081408
if (GeneralConfigStatic.config.EnableMemorySplitUpload)
14091409
{
14101410
// Memory-based splitting - read chunks directly without creating temp files
1411-
long chunkSizeBytes = (long)GeneralConfigStatic.config.MemorySplitSizeGB * 1024L * 1024L * 1024L;
1411+
// Use MaxSize (Telegram's actual limit per GB unit) instead of real GB
1412+
long chunkSizeBytes = (long)GeneralConfigStatic.config.MemorySplitSizeGB * MaxSize;
14121413
int totalChunks = (int)Math.Ceiling((double)fileInfo.Length / chunkSizeBytes);
14131414

14141415
_logger.LogInformation("Using memory-based split for {FileName} - {TotalChunks} chunks of {ChunkSizeGB} GB",

TelegramDownloader/Data/ITelegramService.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public interface ITelegramService
2929
Task<List<ChatViewBase>> getAllChats();
3030
Task<List<ChatViewBase>> getAllSavedChats();
3131
Task<ChatsWithFolders> getChatsWithFolders();
32+
Task PreloadChannelsAsync();
3233
Task<List<ChatMessages>> getAllMessages(long id, Boolean onlyFiles = false);
3334
Task<GridDataProviderResult<ChatMessages>> getPaginatedMessages(long id, int page, int size, Boolean onlyFiles = false);
3435
Task<List<ChatMessages>> getAllMediaMessages(long id, Boolean onlyFiles = false);

TelegramDownloader/Data/TelegramService.cs

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,24 @@ public TelegramService(TransactionInfoService tis, IDbService db, ILogger<IFileS
5050
{
5151
if (client == null && HasValidCredentials())
5252
{
53-
newClient();
53+
try
54+
{
55+
newClient();
56+
}
57+
catch (WTelegram.WTException ex) when (ex.Message.Contains("session file"))
58+
{
59+
// Session file is corrupted - delete it and create a new one
60+
_logger.LogWarning("Session file corrupted during startup, deleting: {Message}", ex.Message);
61+
var sessionPath = UserService.USERDATAFOLDER + "/WTelegram.session";
62+
if (File.Exists(sessionPath))
63+
{
64+
File.Delete(sessionPath);
65+
_logger.LogInformation("Deleted corrupted session file: {Path}", sessionPath);
66+
}
67+
// Create new client with fresh session
68+
newClient();
69+
_logger.LogInformation("Telegram client initialized with new session");
70+
}
5471
}
5572
else if (!HasValidCredentials())
5673
{
@@ -100,8 +117,25 @@ public void InitializeClient()
100117
if (client == null) // Double-check after acquiring lock
101118
{
102119
_logger.LogInformation("Initializing Telegram client after setup");
103-
newClient();
104-
_logger.LogInformation("Telegram client initialized successfully");
120+
try
121+
{
122+
newClient();
123+
_logger.LogInformation("Telegram client initialized successfully");
124+
}
125+
catch (WTelegram.WTException ex) when (ex.Message.Contains("session file"))
126+
{
127+
// Session file is corrupted - delete it and create a new one
128+
_logger.LogWarning("Session file corrupted, deleting and creating new session: {Message}", ex.Message);
129+
var sessionPath = UserService.USERDATAFOLDER + "/WTelegram.session";
130+
if (File.Exists(sessionPath))
131+
{
132+
File.Delete(sessionPath);
133+
_logger.LogInformation("Deleted corrupted session file: {Path}", sessionPath);
134+
}
135+
// Create new client with fresh session
136+
newClient();
137+
_logger.LogInformation("Telegram client initialized with new session");
138+
}
105139
}
106140
}
107141
catch (Exception ex)
@@ -599,6 +633,43 @@ public async Task<ChatsWithFolders> getChatsWithFolders()
599633
return result;
600634
}
601635

636+
/// <summary>
637+
/// Preloads channels and configuration at startup if user is logged in.
638+
/// This ensures channels are available for API calls without requiring UI access first.
639+
/// </summary>
640+
public async Task PreloadChannelsAsync()
641+
{
642+
if (!checkUserLogin())
643+
{
644+
_logger.LogInformation("PreloadChannelsAsync: User not logged in, skipping preload");
645+
return;
646+
}
647+
648+
try
649+
{
650+
_logger.LogInformation("PreloadChannelsAsync: Starting channel preload...");
651+
652+
// Load all channels into cache
653+
var channels = await getAllChats();
654+
_logger.LogInformation("PreloadChannelsAsync: Loaded {Count} channels", channels.Count);
655+
656+
// Also preload folders
657+
var folders = await getChatsWithFolders();
658+
_logger.LogInformation("PreloadChannelsAsync: Loaded {FolderCount} folders with {UngroupedCount} ungrouped chats",
659+
folders.Folders.Count, folders.UngroupedChats.Count);
660+
661+
// Preload favourites
662+
var favourites = await GetFouriteChannels(true);
663+
_logger.LogInformation("PreloadChannelsAsync: Loaded {Count} favourite channels", favourites.Count);
664+
665+
_logger.LogInformation("PreloadChannelsAsync: Channel preload completed successfully");
666+
}
667+
catch (Exception ex)
668+
{
669+
_logger.LogError(ex, "PreloadChannelsAsync: Error preloading channels");
670+
}
671+
}
672+
602673
public async Task<Message> uploadFile(string chatId, Stream file, string fileName, string mimeType = null, UploadModel um = null, string caption = null)
603674
{
604675
_logger.LogInformation("Starting file upload - FileName: {FileName}, Size: {SizeMB:F2}MB, ChatId: {ChatId}",

TelegramDownloader/Models/GeneralConfig.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ public static async Task SaveChanges(IDbService db, GeneralConfig gc)
2525
gc.MemorySplitSizeGB = gc.SplitSize;
2626
}
2727

28-
// Ensure MemorySplitSizeGB is within valid range (1-4 GB)
28+
// Ensure MemorySplitSizeGB is within valid range based on Telegram limits
29+
// Premium: max 4, Non-premium: max 2 (same as Telegram file size limits)
30+
int maxAllowedSize = TelegramService.isPremium ? 4 : 2;
2931
if (gc.MemorySplitSizeGB < 1) gc.MemorySplitSizeGB = 1;
30-
if (gc.MemorySplitSizeGB > 4) gc.MemorySplitSizeGB = 4;
32+
if (gc.MemorySplitSizeGB > maxAllowedSize) gc.MemorySplitSizeGB = maxAllowedSize;
3133

3234
await db.SaveConfig(gc);
3335
config = gc;
@@ -131,8 +133,10 @@ public class GeneralConfig
131133
public bool EnableMemorySplitUpload { get; set; } = false;
132134

133135
/// <summary>
134-
/// Size in GB for each memory chunk when uploading large files (1-4 GB).
136+
/// Size in GB for each memory chunk when uploading large files.
137+
/// Premium accounts: 1-4 GB, Non-premium: 1-2 GB.
135138
/// Only used when EnableMemorySplitUpload is true.
139+
/// Note: Uses Telegram's actual size limits (1GB = 1024*1024*1000 bytes, not 1024^3).
136140
/// </summary>
137141
public int MemorySplitSizeGB { get; set; } = 2;
138142

TelegramDownloader/Pages/Config.razor

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,15 @@
345345
Memory Chunk Size
346346
</div>
347347
<div class="config-item-description">
348-
Size of each chunk when splitting in memory (1-4 GB). Must beSplit Size.
348+
Size of each chunk when splitting in memory. Must beSplit Size.
349+
@if (TelegramService.isPremium)
350+
{
351+
<span class="badge bg-warning text-dark ms-2">Premium: 1-4 GB</span>
352+
}
353+
else
354+
{
355+
<span class="badge bg-secondary ms-2">1-2 GB</span>
356+
}
349357
</div>
350358
</div>
351359
<div class="config-item-control">
@@ -1156,8 +1164,10 @@
11561164

11571165
private int GetMaxMemorySplitSize()
11581166
{
1159-
// MemorySplitSizeGB must be <= SplitSize and <= 4
1160-
return Math.Min(Model?.SplitSize ?? 4, 4);
1167+
// MemorySplitSizeGB must be <= SplitSize and <= Telegram's max file size
1168+
// Premium: max 4, Non-premium: max 2
1169+
int telegramMaxSize = TelegramService.isPremium ? 4 : 2;
1170+
return Math.Min(Model?.SplitSize ?? telegramMaxSize, telegramMaxSize);
11611171
}
11621172

11631173
public void Dispose()

TelegramDownloader/Pages/Partials/impl/FileManagerImpl.razor

Lines changed: 96 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,10 @@
102102
OnPathChanged="OnMobilePathChanged"
103103
OnFilterChanged="OnMobileFilterChanged"
104104
InitialSearch="@_initialSearch"
105-
InitialFilters="@_initialFilters" />
105+
InitialFilters="@_initialFilters"
106+
InitialSortBy="@_initialSortBy"
107+
InitialSortAscending="@_initialSortAscending"
108+
InitialPage="@_initialPage" />
106109
</div>
107110
}
108111

@@ -172,18 +175,30 @@
172175
private bool _firstRenderComplete = false;
173176
private bool _needsRefresh = false;
174177

175-
// URL state for filters and search
178+
// URL state for filters, search, sorting, and pagination
176179
private string _initialSearch = string.Empty;
177180
private HashSet<string> _initialFilters = new();
181+
private string _initialSortBy = "Name";
182+
private bool _initialSortAscending = true;
183+
private int _initialPage = 1;
178184
private string _currentSearch = string.Empty;
179185
private HashSet<string> _currentFilters = new();
186+
private string _currentSortBy = "Name";
187+
private bool _currentSortAscending = true;
188+
private int _currentPage = 1;
180189

181190
protected override async Task OnParametersSetAsync()
182191
{
183192
string currentBsiId = bsi?.Id;
184193
bool idChanged = _previousId != id;
185194
bool bsiChanged = isShared && _previousBsiId != currentBsiId && currentBsiId != null;
186195

196+
// Parse URL params early so they're available before first render
197+
if (!_firstRenderComplete)
198+
{
199+
ParseUrlParameters();
200+
}
201+
187202
if (idChanged || bsiChanged)
188203
{
189204
_previousId = id;
@@ -198,6 +213,59 @@
198213
}
199214
}
200215

216+
private void ParseUrlParameters()
217+
{
218+
try
219+
{
220+
var uri = new Uri(MyNavigationManager.Uri);
221+
var query = System.Web.HttpUtility.ParseQueryString(uri.Query);
222+
var searchFromUrl = query["search"];
223+
var filtersFromUrl = query["filters"];
224+
var sortByFromUrl = query["sortBy"];
225+
var sortAscFromUrl = query["sortAsc"];
226+
227+
// Parse initial search
228+
if (!string.IsNullOrEmpty(searchFromUrl))
229+
{
230+
_initialSearch = Uri.UnescapeDataString(searchFromUrl);
231+
_currentSearch = _initialSearch;
232+
}
233+
234+
// Parse initial filters (comma-separated)
235+
if (!string.IsNullOrEmpty(filtersFromUrl))
236+
{
237+
var filterList = Uri.UnescapeDataString(filtersFromUrl).Split(',', StringSplitOptions.RemoveEmptyEntries);
238+
_initialFilters = new HashSet<string>(filterList);
239+
_currentFilters = new HashSet<string>(filterList);
240+
}
241+
242+
// Parse initial sort
243+
if (!string.IsNullOrEmpty(sortByFromUrl))
244+
{
245+
_initialSortBy = Uri.UnescapeDataString(sortByFromUrl);
246+
_currentSortBy = _initialSortBy;
247+
}
248+
249+
if (!string.IsNullOrEmpty(sortAscFromUrl))
250+
{
251+
_initialSortAscending = sortAscFromUrl.ToLower() != "false";
252+
_currentSortAscending = _initialSortAscending;
253+
}
254+
255+
// Parse initial page
256+
var pageFromUrl = query["page"];
257+
if (!string.IsNullOrEmpty(pageFromUrl) && int.TryParse(pageFromUrl, out int page) && page > 0)
258+
{
259+
_initialPage = page;
260+
_currentPage = page;
261+
}
262+
}
263+
catch (Exception ex)
264+
{
265+
Logger.LogError(ex, "Error parsing URL parameters");
266+
}
267+
}
268+
201269
protected override async Task OnAfterRenderAsync(bool firstRender)
202270
{
203271
if (firstRender)
@@ -261,23 +329,6 @@
261329
var uri = new Uri(MyNavigationManager.Uri);
262330
var query = System.Web.HttpUtility.ParseQueryString(uri.Query);
263331
var pathFromUrl = query["path"];
264-
var searchFromUrl = query["search"];
265-
var filtersFromUrl = query["filters"];
266-
267-
// Parse initial search
268-
if (!string.IsNullOrEmpty(searchFromUrl))
269-
{
270-
_initialSearch = Uri.UnescapeDataString(searchFromUrl);
271-
_currentSearch = _initialSearch;
272-
}
273-
274-
// Parse initial filters (comma-separated)
275-
if (!string.IsNullOrEmpty(filtersFromUrl))
276-
{
277-
var filterList = Uri.UnescapeDataString(filtersFromUrl).Split(',', StringSplitOptions.RemoveEmptyEntries);
278-
_initialFilters = new HashSet<string>(filterList);
279-
_currentFilters = new HashSet<string>(filterList);
280-
}
281332

282333
// Decode the path and ensure it ends with /
283334
string decodedPath = "/";
@@ -310,7 +361,9 @@
310361
{
311362
try
312363
{
313-
UpdateUrlWithState(path, _currentSearch, _currentFilters);
364+
// Reset page to 1 when navigating to a different folder
365+
_currentPage = 1;
366+
UpdateUrlWithState(path, _currentSearch, _currentFilters, _currentSortBy, _currentSortAscending, _currentPage);
314367
}
315368
catch (Exception ex)
316369
{
@@ -324,18 +377,21 @@
324377
{
325378
_currentSearch = args.SearchText;
326379
_currentFilters = args.TypeFilters;
380+
_currentSortBy = args.SortBy;
381+
_currentSortAscending = args.SortAscending;
382+
_currentPage = args.CurrentPage;
327383

328384
// Get current path from mobile file manager
329385
var currentPath = mobileFileManager?.Path ?? "/";
330-
UpdateUrlWithState(currentPath, _currentSearch, _currentFilters);
386+
UpdateUrlWithState(currentPath, _currentSearch, _currentFilters, _currentSortBy, _currentSortAscending, _currentPage);
331387
}
332388
catch (Exception ex)
333389
{
334390
Logger.LogError(ex, "Error updating URL with filters");
335391
}
336392
}
337393

338-
private void UpdateUrlWithState(string path, string search, HashSet<string> filters)
394+
private void UpdateUrlWithState(string path, string search, HashSet<string> filters, string sortBy = "Name", bool sortAscending = true, int page = 1)
339395
{
340396
var baseUri = MyNavigationManager.Uri.Split('?')[0];
341397
var queryParams = new List<string>();
@@ -358,6 +414,24 @@
358414
queryParams.Add($"filters={Uri.EscapeDataString(string.Join(",", filters))}");
359415
}
360416

417+
// Add sort if not default
418+
if (sortBy != "Name")
419+
{
420+
queryParams.Add($"sortBy={Uri.EscapeDataString(sortBy)}");
421+
}
422+
423+
// Add sort direction if descending
424+
if (!sortAscending)
425+
{
426+
queryParams.Add("sortAsc=false");
427+
}
428+
429+
// Add page if not first page
430+
if (page > 1)
431+
{
432+
queryParams.Add($"page={page}");
433+
}
434+
361435
var newUrl = queryParams.Count > 0 ? $"{baseUri}?{string.Join("&", queryParams)}" : baseUri;
362436
MyNavigationManager.NavigateTo(newUrl, forceLoad: false, replace: true);
363437
}

0 commit comments

Comments
 (0)