Skip to content

Commit 4245542

Browse files
authored
internal: Develop to main (#42)
* feat: add download grid from channels chore: bump versions * feat: show channel files * feat: add media streaming from telegram (#32) * feat: media stream for huge files (#34) * feat: add webdav and export files to strm (#39) feat: improves fix: error on download root files fix: error on download splitted files * chore: dockerfile * chore: dockerfile change * chore: python virtual env * chore: add beta tags * chore: log refresh * feat: add download file before play * chore: python docker repair * feat: add virtual scroll config * chore: add mutex in tasks * fix: repair strm feat: add app controller * fea: add preload files on streaming option * fix: solve download multi part file for stream * fix: multi download problem * chore: add getFileById on download * chore: add ogm as a video extension * feat: improve streaming * feat: download files v2 * feat: add reverse proxy support over https feat: download sync file and return to front * chore: add local file manager routes dynamically * chore: improve navbar * feat: add speedChart chore: add more logs * feat: improve main window (#41) chore: add download and upload info * feat: remove id from task tables * fix: solve some errors 🚑 * chore: improve shared file manager * chore: add start time in download * chore: improve js and css * chore: improve events * chore: navbar changes * chore: change strm export
1 parent f1130a8 commit 4245542

43 files changed

Lines changed: 2231 additions & 435 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
TelegramDownloader/userData.json
2+
TelegramDownloader/.claude/
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using Microsoft.AspNetCore.Http;
2+
using Microsoft.AspNetCore.Mvc;
3+
using TelegramDownloader.Data.db;
4+
using TelegramDownloader.Data;
5+
using TelegramDownloader.Models;
6+
using TelegramDownloader.Services;
7+
using TL;
8+
9+
namespace TelegramDownloader.Controllers
10+
{
11+
[Route("api/app")]
12+
[ApiController]
13+
public class AppController : ControllerBase
14+
{
15+
IDbService _db { get; set; }
16+
ITelegramService _ts { get; set; }
17+
IFileService _fs { get; set; }
18+
19+
public AppController(IDbService db, ITelegramService ts, IFileService fs)
20+
{
21+
_fs = fs; ;
22+
_ts = ts;
23+
_db = db;
24+
25+
}
26+
27+
[HttpGet("ping")]
28+
public ActionResult Ping()
29+
{
30+
var result = new Dictionary<string, string>
31+
{
32+
{ "status", "connected" },
33+
{ "app", "TFM App" },
34+
{ "time", DateTimeOffset.Now.ToString("o") }
35+
};
36+
return Ok(result);
37+
}
38+
39+
[HttpGet("mychats")]
40+
public async Task<ActionResult> getMyChats()
41+
{
42+
List<ChatViewBase> mineChats = new List<ChatViewBase>();
43+
foreach (var chat in await _ts.getAllChats())
44+
{
45+
if (chat.chat is Channel chat1)
46+
{
47+
if (chat1.flags.HasFlag(Channel.Flags.creator))
48+
{
49+
mineChats.Add(chat);
50+
}
51+
}
52+
}
53+
return Ok(mineChats);
54+
}
55+
}
56+
}

TelegramDownloader/Controllers/FileController.cs

Lines changed: 116 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,11 @@
66
using Newtonsoft.Json.Serialization;
77

88
using Syncfusion.Blazor.FileManager;
9-
using Syncfusion.Blazor.Inputs;
109
using Syncfusion.EJ2.FileManager.Base;
1110
using Syncfusion.EJ2.FileManager.PhysicalFileProvider;
12-
using Syncfusion.EJ2.Notifications;
13-
using System;
14-
using System.Collections.Generic;
15-
using System.IO;
1611
using System.IO.Compression;
17-
using System.Linq;
1812
using System.Net;
19-
using System.Net.Http.Headers;
20-
using System.Runtime.Serialization.Formatters.Binary;
2113
using System.Web;
22-
using System.Xml.Linq;
2314
using TelegramDownloader.Data;
2415
using TelegramDownloader.Data.db;
2516
using TelegramDownloader.Models;
@@ -37,12 +28,15 @@ public class FileController : ControllerBase
3728
public PhysicalFileProvider operation;
3829
public string basePath;
3930
string root = FileService.RELATIVELOCALDIR;
31+
32+
private static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
4033
IDbService _db { get; set; }
4134
ITelegramService _ts { get; set; }
4235
IFileService _fs { get; set; }
4336
TransactionInfoService _tis { get; set; }
37+
private ILogger<IFileService> _logger { get; set; }
4438

45-
public FileController(IDbService db, ITelegramService ts, IFileService fs, TransactionInfoService tis)
39+
public FileController(IDbService db, ITelegramService ts, IFileService fs, TransactionInfoService tis, ILogger<IFileService> logger)
4640
{
4741
this.basePath = Environment.CurrentDirectory;
4842
if (!System.IO.Directory.Exists(Path.Combine(basePath, root)))
@@ -53,6 +47,7 @@ public FileController(IDbService db, ITelegramService ts, IFileService fs, Trans
5347
_ts = ts;
5448
_db = db;
5549
_tis = tis;
50+
_logger = logger;
5651

5752
this.operation = new PhysicalFileProvider();
5853
this.operation.RootFolder(Path.Combine(basePath, root));
@@ -248,9 +243,21 @@ public async Task<IActionResult> GetFile(string id, string? idChannel, string? i
248243
TL.Message idM = await _ts.getMessageFile(idChannel, Convert.ToInt32(idFile));
249244
ChatMessages cm = new ChatMessages();
250245
cm.message = idM;
251-
file = new FileStream(System.IO.Path.Combine(FileService.TEMPDIR, "_temp", $"{idChannel}-{idFile}-{id}"), FileMode.Create, FileAccess.ReadWrite);
246+
string filePath = System.IO.Path.Combine(FileService.TEMPDIR, "_temp", $"{idChannel}-{idFile}-{id}");
247+
file = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite);
252248
DownloadModel dm = new DownloadModel();
253249
dm.tis = _tis;
250+
dm.startDate = DateTime.Now;
251+
dm.path = filePath;
252+
if (cm.message is Message msgBase)
253+
{
254+
if (msgBase.media is MessageMediaDocument mediaDoc &&
255+
mediaDoc.document is TL.Document doc)
256+
{
257+
dm._size = doc.size;
258+
dm.name = doc.Filename;
259+
}
260+
}
254261
dm.channelName = _ts.getChatName(Convert.ToInt64(idChannel));
255262
_tis.addToDownloadList(dm);
256263
await _ts.DownloadFileAndReturn(cm, file, model: dm);
@@ -267,6 +274,100 @@ public async Task<IActionResult> GetFile(string id, string? idChannel, string? i
267274
};
268275
}
269276

277+
[Route("GetFileByTfmIdNow/{id}")]
278+
public async Task<IActionResult> GetFileByTfmIdNow(string id, [FromQuery] string idChannel, [FromQuery] string idFile)
279+
{
280+
var fileName = id;
281+
var mimeType = FileService.getMimeType(id.Split(".").Last());
282+
var dbFile = await _fs.getItemById(idChannel, idFile);
283+
if (dbFile == null)
284+
{
285+
return new ObjectResult("") { StatusCode = (int)HttpStatusCode.NotFound };
286+
}
287+
var file = _fs.ExistFileIntempFolder($"{idChannel}-{dbFile.MessageId}-{id}");
288+
if (file == null)
289+
{
290+
TL.Message idM = await _ts.getMessageFile(idChannel, Convert.ToInt32(dbFile.MessageId));
291+
ChatMessages cm = new ChatMessages();
292+
cm.message = idM;
293+
String filePath = System.IO.Path.Combine(FileService.TEMPDIR, "_temp", $"{idChannel}-{idFile}-{id}");
294+
file = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite);
295+
DownloadModel dm = new DownloadModel();
296+
dm.tis = _tis;
297+
dm.startDate = DateTime.Now;
298+
dm.path = filePath;
299+
dm.name = dbFile.Name;
300+
dm._size = dbFile.Size;
301+
dm.channelName = _ts.getChatName(Convert.ToInt64(idChannel));
302+
_tis.addToDownloadList(dm);
303+
await _ts.DownloadFileAndReturn(cm, file, model: dm);
304+
file.Position = 0;
305+
}
306+
307+
Response.Headers["Content-Disposition"] = $"inline; filename=\"{HttpUtility.UrlEncode(fileName)}\"";
308+
309+
return new FileStreamResult(file, mimeType);
310+
}
311+
312+
[Route("GetFileByTfmId/{id}")]
313+
public async Task<IActionResult> GetFileByTfmId(string id, [FromQuery] string idChannel, [FromQuery] string idFile)
314+
{
315+
var fileName = id;
316+
var mimeType = FileService.getMimeType(id.Split(".").Last());
317+
var dbFile = await _fs.getItemById(idChannel, idFile);
318+
if (dbFile == null)
319+
{
320+
return new ObjectResult("") { StatusCode = (int)HttpStatusCode.NotFound };
321+
}
322+
323+
dbFile.Name = $"{idChannel}-{(dbFile.MessageId != null ? dbFile.MessageId.ToString() : dbFile.Id)}-{id}";
324+
String path = Path.Combine(FileService.TEMPDIR, "_temp");
325+
String filePath = Path.Combine(path, dbFile.Name);
326+
var mutexState = semaphoreSlim.Wait(10000);
327+
FileStream? file = null;
328+
if (!mutexState)
329+
{
330+
_logger.LogWarning("Could not acquire download mutex in 10 seconds for file {fileName}", dbFile.Name);
331+
return StatusCode(500, "Could not acquire download mutex in 10 seconds");
332+
}
333+
334+
file = _fs.ExistFileIntempFolder(dbFile.Name);
335+
var isFileDownloaded = _tis.isFileDownloaded(filePath);
336+
if (!isFileDownloaded)
337+
if ((file == null) || (file != null && file.Length < dbFile.Size))
338+
{
339+
_logger.LogWarning("Stream Downloading file {fileName}", dbFile.Name);
340+
await _fs.downloadFile(idChannel, new List<Syncfusion.Blazor.FileManager.FileManagerDirectoryContent> { dbFile.toFileManagerContent() }, path);
341+
Thread.Sleep(4000);
342+
file = _fs.ExistFileIntempFolder(dbFile.Name);
343+
}
344+
345+
if (mutexState)
346+
semaphoreSlim.Release();
347+
348+
if (file == null)
349+
{
350+
return NotFound();
351+
}
352+
353+
Response.Headers["Content-Disposition"] = $"inline; filename=\"{HttpUtility.UrlEncode(fileName)}\"";
354+
355+
try
356+
{
357+
return new FileStreamResult(file, mimeType)
358+
{
359+
FileDownloadName = fileName,
360+
EnableRangeProcessing = true
361+
362+
};
363+
}
364+
catch (OperationCanceledException)
365+
{
366+
_logger.LogInformation("❌ El cliente cerró la conexión al descargar el archivo {fileName}.", fileName);
367+
return StatusCode(StatusCodes.Status499ClientClosedRequest);
368+
}
369+
}
370+
270371
[Route("strm")]
271372
public async Task<IActionResult> GetStrm([FromQuery] string idChannel, [FromQuery] string path, [FromQuery] string host)
272373
{
@@ -288,7 +389,7 @@ public async Task<IActionResult> GetFileStream(string idChannel, string idFile,
288389

289390
var request = HttpContext.Request;
290391
var rangeHeader = request.Headers["Range"].ToString();
291-
Console.WriteLine("range: ", rangeHeader);
392+
_logger.LogDebug("GetFileStream - Range: {Range}", rangeHeader);
292393

293394
var file = await _fs.getItemById(idChannel, idFile);
294395
long totalLength = file.Size;
@@ -319,7 +420,7 @@ public async Task<IActionResult> GetFileStream(string idChannel, string idFile,
319420

320421
if (from >= totalPartialFileLength)
321422
{
322-
Console.WriteLine("Take a file part: from: " + from + ", totalPartialFileLength: " + totalPartialFileLength);
423+
_logger.LogDebug("Take a file part - From: {From}, TotalPartialFileLength: {TotalPartialFileLength}", from, totalPartialFileLength);
323424
int filePart = 0;
324425
while(from >= totalPartialFileLength)
325426
{
@@ -339,7 +440,7 @@ public async Task<IActionResult> GetFileStream(string idChannel, string idFile,
339440
from = (from / 524288) * 524288;
340441
}
341442

342-
Console.WriteLine("Fom:" + from);
443+
_logger.LogDebug("Stream position - From: {From}", from);
343444

344445
if (to == 0)
345446
if (string.IsNullOrEmpty(rangeHeader) || from == 0)
@@ -417,116 +518,13 @@ public async Task<IActionResult> GetFileStream(string idChannel, string idFile,
417518
}
418519
catch (OperationCanceledException)
419520
{
420-
Console.WriteLine("❌ El cliente cerró la conexión.");
521+
_logger.LogInformation("Client closed connection during stream - File: {FileName}", fileName);
421522
}
422523

423524
return new EmptyResult();
424525

425526
}
426527

427-
[Route("GetFileStream2/{idChannel}/{idFile}/{name}")]
428-
public async Task<IActionResult> GetFileStream2(string idChannel, string idFile, string name)
429-
{
430-
var fileName = name;
431-
var mimeType = FileService.getMimeType(name.Split(".").Last());
432-
433-
var request = HttpContext.Request;
434-
var rangeHeader = request.Headers["Range"].ToString();
435-
436-
var file = await _fs.getItemById(idChannel, idFile);
437-
438-
// HttpResponseMessage fullResponse = new HttpResponseMessage(HttpStatusCode.OK); // Request.CreateResponse(HttpStatusCode.OK);
439-
TL.Message idM = await _ts.getMessageFile(idChannel, file.MessageId ?? file.ListMessageId.FirstOrDefault());
440-
//byte[] data = await _ts.DownloadFileStream(idM, 0, 512);
441-
//var stream = new MemoryStream(data);
442-
443-
// Supón que puedes obtener el tamaño total del archivo remoto
444-
long totalLength = (await _fs.getItemById(idChannel, idFile)).Size;
445-
446-
long from = 0;
447-
long initialFrom = 0;
448-
long to = 0;
449-
450-
451-
452-
if (!string.IsNullOrEmpty(rangeHeader) && rangeHeader.StartsWith("bytes="))
453-
{
454-
var range = rangeHeader.Replace("bytes=", "").Split('-');
455-
from = long.Parse(range[0]);
456-
initialFrom = from;
457-
if (range.Length > 1 && !string.IsNullOrEmpty(range[1]))
458-
to = long.Parse(range[1]);
459-
}
460-
461-
if (from > 0)
462-
{
463-
from = (from / 524288) * 524288;
464-
}
465-
466-
if (to == 0)
467-
to = (25 * 524288) + from;
468-
else
469-
to = ((to + 524288) / 524288) * 524288;
470-
471-
if (to > totalLength)
472-
{
473-
to = totalLength - 1;
474-
}
475-
476-
long length = to - from;
477-
478-
byte[] data = await _ts.DownloadFileStream(idM, from, (int)length);
479-
480-
long skipedBytes = initialFrom - from; // (data.Length + initialFrom) >= totalLength ? data.Length - (totalLength - initialFrom) : initialFrom - from;
481-
482-
if (skipedBytes < 0) skipedBytes = 0;
483-
484-
if (skipedBytes >= data.Length)
485-
return StatusCode(500, "No hay suficientes bytes en el bloque descargado");
486-
487-
long dataLength = data.Length - skipedBytes;
488-
Response.ContentLength = (dataLength + initialFrom) >= totalLength ? totalLength - initialFrom : dataLength;
489-
// Response.StatusCode = (int)HttpStatusCode.PartialContent; // 206
490-
Response.StatusCode = StatusCodes.Status206PartialContent;
491-
Response.ContentType = mimeType;
492-
Response.Headers.Add("Content-Range", $"bytes {initialFrom}-{(initialFrom + Response.ContentLength - 1)}/{totalLength}");
493-
Response.Headers.Add("Accept-Ranges", "bytes");
494-
495-
if (Request.Headers.ContainsKey("Range"))
496-
{
497-
Console.WriteLine("Range header recibido:");
498-
Console.WriteLine(Request.Headers["Range"]);
499-
}
500-
501-
foreach (var header in Response.Headers)
502-
{
503-
Console.WriteLine($"{header.Key}: {header.Value}");
504-
}
505-
506-
507-
508-
509-
var stream = new MemoryStream(data, (int)skipedBytes, (int)Response.ContentLength);
510-
511-
Console.WriteLine("Real Data length: " + stream.Length);
512-
513-
Console.WriteLine("---------------------------------------------------");
514-
515-
//return new FileStreamResult(stream, mimeType);
516-
var cancellationToken = HttpContext.RequestAborted;
517-
518-
await stream.CopyToAsync(Response.Body, 64 * 1024, cancellationToken);
519-
await Response.Body.FlushAsync(cancellationToken);
520-
521-
return new EmptyResult();
522-
// return File(stream, mimeType, enableRangeProcessing: false);
523-
//return new FileStreamResult(stream, mimeType)
524-
//{
525-
// EnableRangeProcessing = false
526-
//};
527-
528-
}
529-
530528
[Route("export/{id}")]
531529
public async Task<IActionResult> ExportDatabase(string id)
532530
{

0 commit comments

Comments
 (0)