diff --git a/TelegramDownloader/Controllers/FileController.cs b/TelegramDownloader/Controllers/FileController.cs index 8405d82..4300b1f 100644 --- a/TelegramDownloader/Controllers/FileController.cs +++ b/TelegramDownloader/Controllers/FileController.cs @@ -267,6 +267,19 @@ public async Task GetFile(string id, string? idChannel, string? i }; } + [Route("strm")] + public async Task GetStrm([FromQuery] string idChannel, [FromQuery] string path, [FromQuery] string host) + { + String zip = await _fs.CreateStrmFiles(path, idChannel, host); + + FileStream fs = new FileStream(zip, FileMode.Open); + + return new FileStreamResult(fs, "application/octet-stream") + { + FileDownloadName = Path.GetFileName(zip) + }; + } + [Route("GetFileStream/{idChannel}/{idFile}/{name}")] public async Task GetFileStream(string idChannel, string idFile, string name) { @@ -275,6 +288,7 @@ public async Task GetFileStream(string idChannel, string idFile, var request = HttpContext.Request; var rangeHeader = request.Headers["Range"].ToString(); + Console.WriteLine("range: ", rangeHeader); var file = await _fs.getItemById(idChannel, idFile); long totalLength = file.Size; diff --git a/TelegramDownloader/Controllers/WebDavController.cs b/TelegramDownloader/Controllers/WebDavController.cs new file mode 100644 index 0000000..3c7e708 --- /dev/null +++ b/TelegramDownloader/Controllers/WebDavController.cs @@ -0,0 +1,81 @@ +using Microsoft.AspNetCore.Mvc; +using TelegramDownloader.Data.db; +using TelegramDownloader.Data; +using TelegramDownloader.Models; +using System.Text.Json; + +namespace TelegramDownloader.Controllers +{ + [Route("api/nodes")] + [ApiController] + public class WebDavController : ControllerBase + { + IDbService _db { get; set; } + + public WebDavController(IDbService db) + { + _db = db; + } + [HttpGet] + public async Task> webDavPaths([FromQuery] string path, [FromQuery] string depth) + { + Console.WriteLine("Peticion de webDavPaths: " + path); + var isFile = Path.HasExtension(path); + if (!path.EndsWith("/") && !isFile) + path = path + "/"; + if (String.IsNullOrEmpty(path)) + throw new BadHttpRequestException("Path is null or empty"); + string channel = path.Split("/")[1]; + if (String.IsNullOrEmpty(channel)) + throw new BadHttpRequestException("Channel is empty"); + path = path.Remove(0, path.IndexOf("/", 1)); + List files = new List(); + if (isFile) + { + var bsonFile = await _db.getFileByPath(channel, path); + if (!(bsonFile == null) && bsonFile.IsFile) + { + WebDavFileModel file = bsonFile.toWebDavFileModel(channel); + files = new List(); + files.Add(file); + } + } + if ((!isFile) || files.Count == 0) + { + if (depth == "0") + { + var bsonFile = await _db.getFileByPath(channel, path[..^1]); + if (bsonFile != null) + { + WebDavFileModel file = bsonFile.toWebDavFileModel(channel); + files = new List(); + files.Add(file); + } + + } + else + files = (await _db.getAllFilesInDirectoryPath(channel, path)).Select(file => file.toWebDavFileModel()).ToList(); + } + + // Console.WriteLine(JsonSerializer.Serialize(files)); + return files; + } + + [HttpGet("meta")] + public async Task webDavMetadata([FromQuery] string path) + { + Console.WriteLine("Peticion de webDavMetadata: " + path); + if (String.IsNullOrEmpty(path)) + throw new Exception("Path is null or empty"); + string channel = path.Split("/")[1]; + if (String.IsNullOrEmpty(channel)) + throw new Exception("Channel is empty"); + path = path.Remove(0, path.IndexOf("/", 1)); + var bsonFile = await _db.getFileByPath(channel, path); + if (bsonFile == null || !bsonFile.IsFile) + throw new FileNotFoundException(); + WebDavFileModel files = bsonFile.toWebDavFileModel(channel); + return files; + } + } +} diff --git a/TelegramDownloader/Data/FileService.cs b/TelegramDownloader/Data/FileService.cs index 94c5b67..6fbf00b 100644 --- a/TelegramDownloader/Data/FileService.cs +++ b/TelegramDownloader/Data/FileService.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging; using MongoDB.Driver; using Newtonsoft.Json; +using SharpCompress.Archives; using SharpCompress.Common; using Syncfusion.Blazor.FileManager; using Syncfusion.Blazor.Inputs; @@ -17,6 +18,8 @@ using System.IO.Hashing; using System.Security.Cryptography; using System.Text.Json; +using System.Text.RegularExpressions; +using System.Threading.Channels; using System.Xml.Linq; using TelegramDownloader.Data.db; using TelegramDownloader.Models; @@ -28,7 +31,6 @@ namespace TelegramDownloader.Data public class FileService : IFileService { public static readonly List ImageExtensions = new List { ".JPG", ".JPEG", ".JPE", ".BMP", ".GIF", ".PNG" }; - public static readonly long MAXIMAGESIZE = 1024 * 1024 * 10; public static string IMGDIR = System.IO.Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "img", "telegram"); public static string LOCALDIR = System.IO.Path.Combine(Directory.GetCurrentDirectory(), "local"); public static string RELATIVELOCALDIR = System.IO.Path.Combine("local"); @@ -230,6 +232,7 @@ public class FileService : IFileService {"xyz", "chemical/x-xyz"}, {"zip", "application/zip"} }; + public static List refreshChannelList = new List(); private PhysicalFileProvider operation = new PhysicalFileProvider(); private ITelegramService _ts { get; set; } @@ -237,6 +240,7 @@ public class FileService : IFileService private ILogger _logger { get; set; } private TransactionInfoService _tis { get; set; } private ToastService _toastService { get; set; } + private static Mutex refreshMutex = new Mutex(); const int MaxSize = 1024 * 1024 * 1000; // 1GB @@ -321,6 +325,7 @@ public async Task> ShareFile(string dbName, string bs public async Task> itemDeleteAsync(string dbName, ItemsDeleteEventArgs args) { string[] names = args.Files.Select(x => x.Name).ToArray(); + bool isMyChannel = _ts.isMyChat(Convert.ToInt64(dbName)); // args.Response = await FileManagerService.Delete(args.Path, names, args.Files.ToArray()); foreach (Syncfusion.Blazor.FileManager.FileManagerDirectoryContent File in args.Files) { @@ -339,13 +344,13 @@ public async Task> ShareFile(string dbName, string bs { foreach (int id in child.ListMessageId) { - if (!await _db.existItemByTelegramId(dbName, id)) + if (isMyChannel && !await _db.existItemByTelegramId(dbName, id)) await _ts.deleteFile(dbName, id); } } else { - if (!await _db.existItemByTelegramId(dbName, (int)child.MessageId)) + if (isMyChannel && !await _db.existItemByTelegramId(dbName, (int)child.MessageId)) await _ts.deleteFile(dbName, (int)child.MessageId); } } @@ -364,13 +369,13 @@ public async Task> ShareFile(string dbName, string bs { foreach (int id in entry.ListMessageId) { - if (!await _db.existItemByTelegramId(dbName, id)) + if (isMyChannel && !await _db.existItemByTelegramId(dbName, id)) await _ts.deleteFile(dbName, id); } } else { - if (!await _db.existItemByTelegramId(dbName, (int)entry.MessageId)) + if (isMyChannel && !await _db.existItemByTelegramId(dbName, (int)entry.MessageId)) await _ts.deleteFile(dbName, (int)entry.MessageId); } } @@ -712,8 +717,8 @@ public async Task downloadFile(string dbName, List // if (targetPath == null) targetPath = "/"; //path.Replace("\\", "/"); foreach (var itemFile in files) { - - BsonFileManagerModel file = collectionId == null ? _db.getFileByPathSync(dbName, itemFile.FilterPath.Replace("\\", "/") + itemFile.Name) : _db.getFileByPathSync(dbName, itemFile.FilterPath.Replace("\\", "/") + itemFile.Name, collectionId); + var filterPath = itemFile.FilterPath == "Files/" ? "/" : itemFile.FilterPath; + BsonFileManagerModel file = collectionId == null ? _db.getFileByPathSync(dbName, filterPath.Replace("\\", "/") + itemFile.Name) : _db.getFileByPathSync(dbName, filterPath.Replace("\\", "/") + itemFile.Name, collectionId); string currentFilePath = currentTargetPath; if (!itemFile.IsFile) { @@ -732,20 +737,7 @@ public async Task downloadFile(string dbName, List throw new Exception($"{currentTargetPath} does not exist"); if (file.isSplit) { - int i = 1; - List splitPaths = new List(); - foreach (int messageId in file.ListMessageId) - { - string filePathPart = System.IO.Path.Combine(currentFilePath, $"({i})" + itemFile.Name); - await downloadFromTelegram(channelId == null ? dbName : channelId, messageId, filePathPart, file); - splitPaths.Add(filePathPart); - i++; - } - await mergeFileStreamAsync(splitPaths, System.IO.Path.Combine(currentFilePath, itemFile.Name)); - foreach (string filePath in splitPaths) - { - File.Delete(filePath); - } + downloadSplitFiles(itemFile, file, currentFilePath, channelId == null ? dbName : channelId); } else { @@ -768,6 +760,24 @@ public async Task downloadFile(string dbName, List } + private async Task downloadSplitFiles(FileManagerDirectoryContent itemFile, BsonFileManagerModel file, string currentFilePath, string dbName) + { + int i = 1; + List splitPaths = new List(); + foreach (int messageId in file.ListMessageId) + { + string filePathPart = System.IO.Path.Combine(currentFilePath, $"({i})" + itemFile.Name); + await downloadFromTelegram(dbName, messageId, filePathPart, file, true); + splitPaths.Add(filePathPart); + i++; + } + await mergeFileStreamAsync(splitPaths, System.IO.Path.Combine(currentFilePath, itemFile.Name)); + foreach (string filePath in splitPaths) + { + File.Delete(filePath); + } + } + private async Task downloadFromTelegramAndReturn(string dbName, int messageId, string destPath, MemoryStream ms = null) { TL.Message m = await _ts.getMessageFile(dbName, messageId); @@ -793,7 +803,7 @@ public static void functionCalll(string dbName, int messageId, string destPath) { } - private async Task downloadFromTelegram(string dbName, int messageId, string destPath, BsonFileManagerModel file = null) + private async Task downloadFromTelegram(string dbName, int messageId, string destPath, BsonFileManagerModel file = null, bool shouldWait = false) { DownloadModel model = new DownloadModel(); model.tis = _tis; @@ -810,9 +820,24 @@ private async Task downloadFromTelegram(string dbName, int messageId, string des } catch(Exception ex) { model.channelName = "Public or Shared"; } - + var tcs = new TaskCompletionSource(); + + EventHandler handler = null; + handler = (sender, args) => + { + if (model.state == StateTask.Completed) + { + model.EventStatechanged -= handler; + tcs.SetResult(true); + } + }; + + model.EventStatechanged += handler; model.callbacks.callback = async () => await DownloadFileNow(dbName, messageId, destPath, model); - _tis.addToPendingDownloadList(model); + _tis.addToPendingDownloadList(model, atFirst: shouldWait); + // Espera hasta que el estado sea "Completed" + if (shouldWait) + await tcs.Task; } public async Task DownloadFileFromChat(ChatMessages message, string fileName = null, string folder = null, DownloadModel model = null) @@ -828,7 +853,7 @@ public async Task DownloadFileFromChat(ChatMessages message, string fileName = n { model._size = document.size; } - if (message.user is Channel channel) + if (message.user is TL.Channel channel) { model.channelName = channel.Title; } @@ -1145,7 +1170,43 @@ public async Task AddUploadFileFromServer(string dbName, string currentPath, Lis _tis.CheckPendingUploadInfoTasks(); } + public async Task CreateStrmFiles(string path, string dbName, string host) + { + String basePath = Path.Combine(TEMPDIR, "Strm", dbName); + try + { + Directory.Delete(basePath, true); + } + catch (Exception ex) + { + } + + Directory.CreateDirectory(basePath); + List filesAndFolders = await _db.getAllChildFilesInDirectory(dbName, path); + foreach (BsonFileManagerModel file in filesAndFolders) + { + String filePath = Path.Combine(basePath, file.FilePath.Substring(path.Length)); + if (file.IsFile) + { + if (FileExtensionTypeTest.isVideoExtension(file.Type) || FileExtensionTypeTest.isAudioExtension(file.Type)) + { + string contenido = Path.Combine(host, "api/file/GetFileStream", dbName, file.Id, "file" + file.Type).Replace("\\", "/"); + string pattern = $@"\.({file.Type.Replace(".", "")})$"; + File.WriteAllText(Regex.Replace(filePath, pattern, ".strm"), contenido); + } + } else + { + Directory.CreateDirectory(filePath); + } + } + + string zipPath = $"{basePath}.zip"; + File.Delete(zipPath); + ZipFile.CreateFromDirectory(basePath, zipPath, CompressionLevel.Fastest, true); + Directory.Delete(basePath, true); + return zipPath; + } public async Task UploadFileFromServer(string dbName, string currentPath, List files, InfoDownloadTaksModel dm = null) // ItemsUploadedEventArgs args) { @@ -1155,44 +1216,6 @@ public async Task UploadFileFromServer(string dbName, string currentPath, List await UploadFileFromServer(dbName, currentPath, files); - // foreach (var file in files) - // { - // var filePath = file.IsFile ? file.FilterPath.Replace("\\", "/") + file.Name : file.FilterPath.Replace("\\", "/") + file.Name + "/"; - // string currentFilePath = System.IO.Path.Combine(LOCALDIR, filePath[0] == '/' ? filePath.Substring(1) : filePath).Replace("\\", "/"); - - // var fileInfo = new System.IO.FileInfo(currentFilePath); - // if (!file.IsFile) - // { - // var allFiles = new DirectoryInfo(currentFilePath).GetFiles("*.*", SearchOption.AllDirectories).Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden)); - // idt.total = allFiles.Count(); - // idt.totalSize += allFiles.Sum(x => x.Length); - // } else - // { - // idt.total++; - // idt.totalSize += fileInfo.Length; - // } - - // } - // TransactionInfoService ti = new TransactionInfoService(); - // ti.addToInfoDownloadTaskList(idt); - - - //} nm.sendEvent(new Notification($"Uploading files from folder {currentPath} to Telegram", "Telegram Upload", NotificationTypes.Info)); foreach (var file in files) { @@ -1203,24 +1226,6 @@ public async Task UploadFileFromServer(string dbName, string currentPath, List !x.Attributes.HasFlag(FileAttributes.Hidden)); - // idt.total = allFiles.Count(); - // idt.totalSize = allFiles.Sum(x => x.Length); - // idt.fromPath = currentFilePath; - // idt.toPath = Path.Combine(currentPath, file.Name); - // idt.executed = 0; - // idt.executedSize = 0; - // idt.isUpload = true; - // idt.file = file; - // idt.thread = Thread.CurrentThread; - // TransactionInfoService ti = new TransactionInfoService(); - // ti.addToInfoDownloadTaskList(idt); - //} BsonFileManagerModel model = new BsonFileManagerModel(); if (file.IsFile) @@ -1322,7 +1327,7 @@ public async Task UploadFileFromServer(string dbName, string currentPath, List file.Name.ToUpper().EndsWith(x)) && file.Size >= MAXIMAGESIZE) + if (ImageExtensions.Any(x => file.Name.ToUpper().EndsWith(x)) && file.Size >= (1024 * 1024 * GeneralConfigStatic.config.MaxImageUploadSizeInMb)) { m = await _ts.uploadFile(dbName, ms, file.Name, "application/octet-stream", um, caption: getCaption(filePath)); } @@ -1384,7 +1389,7 @@ public async Task UploadFileFromServer(string dbName, string currentPath, List args) + public async Task refreshChannelFIles(string channelId) { - // string currentPath = args.Path; - try + refreshMutex.WaitOne(); + refreshChannelList.Add(channelId); + refreshMutex.ReleaseMutex(); + DateTime init = DateTime.Now; + _logger.LogInformation($"Refresh channel with id: {channelId}"); + List telegramChatDocuments = (await _ts.searchAllChannelFiles(Convert.ToInt64(channelId))).Where(x => x.name != null).ToList(); + _logger.LogInformation($"Get the telegram files in: {(DateTime.Now - init).TotalSeconds} seconds for channel id:{channelId}"); + var nameCount = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var doc in telegramChatDocuments) { - //foreach (var file in args.Files) - //{ + var baseName = doc.name; + var name = baseName; + int count = 0; - //using (var filestream = new FileStream(Path.Combine(Environment.CurrentDirectory, "download","assss.jpg"), FileMode.Create, FileAccess.Write)) - //{ - // await file.File.OpenReadStream(maxAllowedSize: long.MaxValue).CopyToAsync(filestream); - //} - //MemoryStream ms = new MemoryStream(file.File.OpenReadStream(maxAllowedSize: long.MaxValue)); - //await file.File.OpenReadStream().CopyToAsync(ms); + while (nameCount.ContainsKey(name)) + { + count++; + name = $"{baseName}({count})"; + } + doc.name = name; + nameCount[name] = 1; + if (count == 0) + nameCount[baseName] = 1; + } + List presentIds = await _db.getAllIdsFromChannel(channelId); + var rootFolder = await _db.getRootFolder(channelId); + foreach (TelegramChatDocuments tcd in telegramChatDocuments) + { + if (!presentIds.Contains(tcd.id)) + { + BsonFileManagerModel model = new BsonFileManagerModel(); + model.Size = tcd.fileSize; + model.MessageId = tcd.id; + model.Name = tcd.name; + model.IsFile = true; + model.HasChild = false; + model.DateCreated = tcd.creationDate; + model.DateModified = tcd.modifiedDate; + model.FilterPath = "/"; + model.FilterId = rootFolder.Id + "/"; + model.ParentId = rootFolder.Id; + model.FilePath = "/" + tcd.name; + model.Type = tcd.extension; + model.isSplit = false; + model.isEncrypted = false; + await _db.createEntry(channelId, model); + } + } + refreshMutex.WaitOne(); + refreshChannelList.Remove(channelId); + refreshMutex.ReleaseMutex(); + _logger.LogInformation($"Finish Refresh channel with id: {channelId}"); + } + + public bool isChannelRefreshing(string channelId) + { + return refreshChannelList.Contains(channelId); + } + + public async Task UploadFile(string dbName, string currentPath, UploadFiles file) // ItemsUploadedEventArgs args) + { + try + { string currentFilePath = System.IO.Path.Combine(TEMPDIR, dbName + currentPath); await SaveToFile(file.File, currentFilePath); @@ -1547,33 +1604,6 @@ public async Task UploadFile(string dbName, string currentPath, UploadFiles file await _db.addBytesToFolder(dbName, model.ParentId, model.Size); GC.Collect(); - - - - - // var folders = (file.FileInfo.Name).Split('/'); - // if (folders.Length > 1) - // { - // for (var i = 0; i < folders.Length - 1; i++) - // { - // string newDirectoryPath = Path.Combine(FileManagerService.basePath + currentPath, folders[i]); - // if (Path.GetFullPath(newDirectoryPath) != (Path.GetDirectoryName(newDirectoryPath) + Path.DirectorySeparatorChar + folders[i])) - // { - // throw new UnauthorizedAccessException("Access denied for Directory-traversal"); - // } - // if (!Directory.Exists(newDirectoryPath)) - // { - // await FileManagerService.Create(currentPath, folders[i]); - // } - // currentPath += folders[i] + "/"; - // } - // } - // var fullName = Path.Combine((FileManagerService.contentRootPath + currentPath), file.File.Name); - // using (var filestream = new FileStream(fullName, FileMode.Create, FileAccess.Write)) - // { - // await file.File.OpenReadStream(long.MaxValue).CopyToAsync(filestream); - // } - //} } catch (Exception ex) { @@ -1646,15 +1676,6 @@ public async Task> getTelegramFolders(string dbName, } } return result; - //var root = listFolders.Where(x => string.IsNullOrEmpty(x.ParentId)).FirstOrDefault(); - //FolderModel folder = new FolderModel(); - //folder.FolderName = root.Name; - //folder.Id = "/"; - //folder.Expanded = true; - //folder.Folders = await getTelegramSubfolders(root.Id, listFolders.Where(x => !string.IsNullOrEmpty(x.ParentId)).ToList(), folder.Id); - //node.Add(folder); - //return node; - } private async Task> getTelegramSubfolders(string id, List listFolders, string prevPath) @@ -1712,27 +1733,6 @@ private async Task> AddChildRecords(string id, string parent if (fileDetails == null) fileDetails = new List(); FileManagerResponse response = new FileManagerResponse(); - //if (path == "/") - //{ - // List Data = await _db.getAllFiles(); - // string ParentId = Data - // .Where(x => string.IsNullOrEmpty(x.ParentId)) - // .Select(x => x.Id).First(); - // response.CWD = Data - // .Where(x => string.IsNullOrEmpty(x.ParentId)).First().toFileManagerContent(); - // response.Files = Data - // .Where(x => x.ParentId == ParentId).Select(x => x.toFileManagerContent()).ToList(); - //} - //else - //{ - //List Data = await _db.getAllFilesInDirectoryPath(path); - // var childItem = fileDetails.Count > 0 && fileDetails[0] != null ? fileDetails[0] : Data - // .Where(x => x.FilterPath == path).First().toFileManagerContent(); - // response.CWD = childItem; - // response.Files = Data - // .Where(x => x.ParentId == childItem.Id).Select(x => x.toFileManagerContent()).ToList(); - - //} if (path == "/") { List Data = collectionName == null ? await _db.getAllFiles(dbName) : await _db.getAllFiles(dbName, collectionName); diff --git a/TelegramDownloader/Data/IFileService.cs b/TelegramDownloader/Data/IFileService.cs index faeda77..e3455ce 100644 --- a/TelegramDownloader/Data/IFileService.cs +++ b/TelegramDownloader/Data/IFileService.cs @@ -25,6 +25,7 @@ public interface IFileService Task getItemById(string dbName, string id); Task> getTelegramFolders(string dbName, string? parentId = null); Task> GetTelegramFoldersExpando(string id, string parentId); + Task CreateStrmFiles(string path, string dbName, string host); Task importData(string dbName, string path, GenericNotificationProgressModel gnp); Task importSharedData(ShareFilesModel sfm, GenericNotificationProgressModel gnp); Task> itemDeleteAsync(string dbName, ItemsDeleteEventArgs args); @@ -34,5 +35,7 @@ public interface IFileService Task UploadFile(string dbName, string currentPath, UploadFiles file); Task UploadFileFromServer(string dbName, string currentPath, List files, InfoDownloadTaksModel dm = null); Task AddUploadFileFromServer(string dbName, string currentPath, List files, InfoDownloadTaksModel idt = null); + Task refreshChannelFIles(string channelId); + bool isChannelRefreshing(string channelId); } } \ No newline at end of file diff --git a/TelegramDownloader/Data/ITelegramService.cs b/TelegramDownloader/Data/ITelegramService.cs index 71fbdf4..cc83b81 100644 --- a/TelegramDownloader/Data/ITelegramService.cs +++ b/TelegramDownloader/Data/ITelegramService.cs @@ -35,6 +35,8 @@ public interface ITelegramService Task logOff(); Task sendVerificationCode(string vc); Task uploadFile(string chatId, Stream file, string fileName, string mimeType = null, UploadModel um = null, string caption = null); + Task> searchAllChannelFiles(long id); + bool isMyChat(long id); } } \ No newline at end of file diff --git a/TelegramDownloader/Data/TelegramService.cs b/TelegramDownloader/Data/TelegramService.cs index 92f8c92..7757910 100644 --- a/TelegramDownloader/Data/TelegramService.cs +++ b/TelegramDownloader/Data/TelegramService.cs @@ -3,9 +3,11 @@ using Microsoft.AspNetCore.Components.Forms; using Syncfusion.Blazor.Inputs; using Syncfusion.Blazor.Kanban.Internal; +using Syncfusion.Blazor.Schedule; using Syncfusion.Blazor.Sparkline.Internal; using System.Collections.Generic; using System.Reflection.Metadata; +using System.Threading.Channels; using System.Threading.Tasks; using TelegramDownloader.Data.db; using TelegramDownloader.Models; @@ -28,13 +30,14 @@ public class TelegramService : ITelegramService private static Messages_Chats chats = null; private static List favouriteChannels = new List(); private static Mutex mut = new Mutex(); + private ILogger _logger { get; set; } - - public TelegramService(TransactionInfoService tis, IDbService db) + public TelegramService(TransactionInfoService tis, IDbService db, ILogger logger) { _tis = tis; _db = db; + _logger = logger; // createDownloadFolder(); mut.WaitOne(); if (client == null) @@ -234,11 +237,26 @@ public bool isInChat(long id) public string getChatName(long id) { - List cm = new List(); + if (chats == null) + return ""; var peer = chats.chats[id]; return peer.Title; } + public bool isMyChat(long id) + { + if (chats == null) + return false; + var peer = chats.chats[id]; + if (peer is TL.Channel channel) + { + // Si es un canal, checkea la propiedad 'creator' + bool canPost = channel.admin_rights != null; + return canPost; + } + return true; + } + public async Task> GetFouriteChannels(bool mustRefresh = true) { @@ -509,6 +527,7 @@ public async Task> getAllFileMessages(long id) for (int offset_id = 0; ;) { var messages = await client.Messages_GetHistory(peer, offset_id); + Thread.Sleep(500); if (messages.Messages.Length == 0) break; foreach (MessageBase msgBase in messages.Messages) { @@ -529,6 +548,7 @@ public async Task> getAllFileMessages(long id) } } offset_id = messages.Messages[^1].ID; + Console.WriteLine("Get telegram files, offset_id: " + offset_id); } return cm; } @@ -650,20 +670,26 @@ public async Task DownloadFileStream(Message message, long offset, int l thumb_size = "" }; - //var file = await client.Invoke(new Upload_GetFile - //{ - // location = new InputDocumentFileLocation - // { - // id = inputFile.id, - // access_hash = inputFile.access_hash, - // file_reference = inputFile.file_reference, - // thumb_size = "" - // }, - // offset = currentOffset, - // limit = totalDownloadBytes - //}); - - var file = await client.Upload_GetFile(location, currentOffset, limit: totalDownloadBytes); + Upload_FileBase file = null; + try + { + file = await client.Upload_GetFile(location, currentOffset, limit: totalDownloadBytes); + } + catch (RpcException ex) when (ex.Code == 303 && ex.Message == "FILE_MIGRATE_X") + { + client = await client.GetClientForDC(-ex.X, true); + file = await client.Upload_GetFile(location, currentOffset, limit: totalDownloadBytes); + } + catch (RpcException ex) when (ex.Code == 400 && ex.Message == "OFFSET_INVALID") + { + _logger.LogError("OFFSET_INVALID", ex); + throw; + } + catch (Exception ex) + { + _logger.LogError("Download Error", ex); + throw; + } if (file is Upload_File uploadFile) { @@ -794,5 +820,89 @@ public async Task DownloadFile(ChatMessages message, string fileName = n return null; } + public async Task> searchAllChannelFiles(long id) + { + List telegramChatDocuments = new List(); + List result = await getAllFileMessages(id); + + foreach (var msg in result) + if (msg.message is Message msgBase) + { + if (msgBase.media is MessageMediaDocument mediaDoc && + mediaDoc.document is TL.Document doc) + { + TelegramChatDocuments tcd = new TelegramChatDocuments(); + tcd.id = msgBase.id; + tcd.documentType = DocumentType.Document; + tcd.name = doc.Filename; + tcd.fileSize = doc.size; + tcd.extension = Path.GetExtension(doc.Filename); + tcd.creationDate = msgBase.date; + tcd.modifiedDate = msgBase.edit_date < msgBase.date ? msgBase.date : msgBase.edit_date; + telegramChatDocuments.Add(tcd); + } + } + return telegramChatDocuments; + } + + //public async Task> searchAllChannelFiles(long id) + //{ + // InputPeer channel = chats.chats[id]; + // List telegramChatDocuments = new List(); + // for (int offset_id = 0; ;) + // { + // Messages_MessagesBase result = await client.Messages_Search( + // peer: channel, + // text: "", + // offset_id: offset_id, + // limit: 0); + // if (result.Messages.Length == 0) break; + // foreach (var msgBase in result.Messages.OfType()) + // { + // if (msgBase.media is MessageMediaDocument mediaDoc && + // mediaDoc.document is TL.Document doc) + // { + // TelegramChatDocuments tcd = new TelegramChatDocuments(); + // tcd.id = msgBase.id; + // tcd.documentType = DocumentType.Document; + // tcd.name = doc.Filename; + // tcd.extension = Path.GetExtension(doc.Filename); + // tcd.creationDate = msgBase.date; + // tcd.modifiedDate = msgBase.edit_date; + // telegramChatDocuments.Add(tcd); + // } + // } + // offset_id = result.Messages[^1].ID; + // } + + // for (int offset_id = 0; ;) + // { + // Messages_MessagesBase result = await client.Messages_Search( + // peer: channel, + // text: "", + // offset_id: offset_id, + // limit: 0); + // if (result.Messages.Length == 0) break; + // foreach (var msgBase in result.Messages.OfType()) + // { + // if (msgBase.media is MessageMediaPhoto mediaPhoto && + // mediaPhoto.photo is Photo photo) + // { + // TelegramChatDocuments tcd = new TelegramChatDocuments(); + // tcd.documentType = DocumentType.Photo; + // tcd.id = msgBase.id; + // // Nombre "falso" porque la foto no trae filename + // tcd.name = $"{photo.id}.jpg"; + // tcd.extension = ".jpg"; + // tcd.creationDate = msgBase.date; + // tcd.modifiedDate = msgBase.edit_date; + // telegramChatDocuments.Add(tcd); + // } + // } + // offset_id = result.Messages[^1].ID; + // } + + // return telegramChatDocuments; + //} } } diff --git a/TelegramDownloader/Data/db/DbService.cs b/TelegramDownloader/Data/db/DbService.cs index 85f6086..42370a7 100644 --- a/TelegramDownloader/Data/db/DbService.cs +++ b/TelegramDownloader/Data/db/DbService.cs @@ -6,6 +6,8 @@ using MongoDB.Driver.Linq; using System.Collections; using Syncfusion.Blazor.PivotView; +using Microsoft.Extensions.Options; +using Syncfusion.EJ2.Layouts; namespace TelegramDownloader.Data.db { @@ -34,15 +36,33 @@ public async Task getSession() public IMongoDatabase getDatabase(string dbName) { // currentDatabase = client.GetDatabase(dbName); + return client.GetDatabase(dbName); } public async Task createIndex(string dbName, string collectionName = "directory") { if (collectionName == null) collectionName = "directory"; - var options = new CreateIndexOptions() { Unique = true, Name = "uniquefile" }; - var indexKeysDefinition = Builders.IndexKeys.Ascending(x => x.FilePath); - await getDatabase(dbName).GetCollection(collectionName).Indexes.CreateOneAsync(indexKeysDefinition, options); + var collection = getDatabase(dbName).GetCollection(collectionName); + + var indexes = await collection.Indexes.ListAsync(); + var indexList = await indexes.ToListAsync(); + + bool existsFilePath = indexList.Any(i => i["name"] == "uniquefile"); + bool existsFilterPath = indexList.Any(i => i["name"] == "uniquefilterpath"); + + if (!existsFilePath) + { + var options = new CreateIndexOptions() { Unique = true, Name = "uniquefile" }; + var indexKeysDefinition = Builders.IndexKeys.Ascending(x => x.FilePath); + await collection.Indexes.CreateOneAsync(indexKeysDefinition, options); + } + if (!existsFilterPath) + { + var options2 = new CreateIndexOptions() { Unique = false, Name = "uniquefilterpath" }; + var indexKeysDefinition2 = Builders.IndexKeys.Ascending(x => x.FilterPath); + await collection.Indexes.CreateOneAsync(indexKeysDefinition2, options2); + } } public async Task CreateDatabase(string dbName = "default", string collection = "directory", bool CreateDefaultEntry = true) @@ -294,7 +314,7 @@ public async Task> getAllFiles(string dbName, string { if (collectionName == null) collectionName = "directory"; - return await (await getDatabase(dbName).GetCollection(collectionName).FindAsync(Builders.Filter.Eq(x => x.FilterPath, "/") | Builders.Filter.Eq(x => x.FilterId, ""))).ToListAsync(); + return await (await getDatabase(dbName).GetCollection(collectionName).FindAsync(Builders.Filter.Eq(x => x.FilterPath, "/") | Builders.Filter.Eq(x => x.FilterPath, "Files/") | Builders.Filter.Eq(x => x.FilterId, ""))).ToListAsync(); } public async Task> getAllFilesInDirectory(string dbName, string path, string collectionName = "directory") @@ -349,6 +369,30 @@ public async Task> getAllFolders(string dbName, strin //return await (await getDatabase(dbName).GetCollection(collectionName).FindAsync(Builders.Filter.Where(x => !x.IsFile && x.ParentId == parentId))).ToListAsync(); } + public async Task> getAllIdsFromChannel(string dbName, string collectionName = "directory") + { + if (collectionName == null) + collectionName = "directory"; + var collection = getDatabase(dbName).GetCollection(collectionName); + + var items = await collection + .Find(x => x.IsFile) + .Project(x => x.MessageId) + .ToListAsync(); + + var allMessageIds = new HashSet(); + foreach (var item in items) + { + if (item != null) + allMessageIds.Add(item.Value); + + //if (item != null) + // allMessageIds.UnionWith(item); + } + + return allMessageIds.OrderBy(x => x).ToList(); + } + public async Task> getAllFilesInDirectoryById(string dbName, string idFolder, string collectionName = "directory") { if (collectionName == null) @@ -486,6 +530,13 @@ public async Task> createEntry(string dbName, BsonFil return null; } + public async Task getRootFolder(string dbName, string collectionName = "directory") + { + if (collectionName == null) + collectionName = "directory"; + return await (await getDatabase(dbName).GetCollection(collectionName).FindAsync(Builders.Filter.Where(x => x.ParentId == "" && x.Name == "Files"))).FirstOrDefaultAsync(); + } + public async Task toBasonFile(string Path, string FolderName, FileManagerDirectoryContent ParentFolder) diff --git a/TelegramDownloader/Data/db/IDbService.cs b/TelegramDownloader/Data/db/IDbService.cs index 88db984..243657f 100644 --- a/TelegramDownloader/Data/db/IDbService.cs +++ b/TelegramDownloader/Data/db/IDbService.cs @@ -17,6 +17,7 @@ public interface IDbService Task copyItem(string dbName, string sourceId, FileManagerDirectoryContent target, string targetPath, bool isFile, string collectionName = "directory"); Task CreateDatabase(string dbName = "default", string collection = "directory", bool CreateDefaultEntry = true); Task> createEntry(string dbName, BsonFileManagerModel file, string collectionName = "directory", IClientSessionHandle? session = null); + Task getRootFolder(string dbName, string collectionName = "directory"); Task createIndex(string dbName, string collectionName = "directory"); Task deleteDatabase(string dbName = "default"); Task deleteEntry(string dbName, string id, string collectionName = "directory"); @@ -30,6 +31,7 @@ public interface IDbService Task> getAllFilesInDirectoryPath(string dbName, string path, string collectionName = "directory"); Task> getAllFilesInDirectoryPath2(string dbName, string path, string collectionName = "directory"); Task> getAllFolders(string dbName, string? parentId = null, string collectionName = "directory"); + Task> getAllIdsFromChannel(string dbName, string collectionName = "directory"); IMongoDatabase getDatabase(string dbName); Task getEntry(string dbName, string filterId, string name, string collectionName = "directory"); Task getFileById(string dbName, string id, string collectionName = "directory"); diff --git a/TelegramDownloader/Dockerfile b/TelegramDownloader/Dockerfile index fca714e..1682c40 100644 --- a/TelegramDownloader/Dockerfile +++ b/TelegramDownloader/Dockerfile @@ -21,5 +21,18 @@ RUN dotnet publish "./TelegramDownloader.csproj" -c $BUILD_CONFIGURATION -o /app FROM base AS final WORKDIR /app + +# Instala Python y pip +RUN apt-get update && \ + apt-get install -y python3 python3-pip python3-venv && \ + ln -s /usr/bin/python3 /usr/bin/python && \ + rm -rf /var/lib/apt/lists/* + +COPY ./WebDav /app/WebDav +RUN python3 -m venv /app/venv +RUN ln -sf /app/venv/bin/python /usr/bin/python +RUN /app/venv/bin/pip install --upgrade pip +RUN /app/venv/bin/pip install -r /app/WebDav/requirements.txt + COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "TelegramDownloader.dll"] diff --git a/TelegramDownloader/Models/DownloadModel.cs b/TelegramDownloader/Models/DownloadModel.cs index c1dcd32..4ee5453 100644 --- a/TelegramDownloader/Models/DownloadModel.cs +++ b/TelegramDownloader/Models/DownloadModel.cs @@ -119,6 +119,7 @@ public class DownloadModel { public Mutex mutex = new Mutex(); public event EventHandler EventChanged; + public event EventHandler EventStatechanged; public string id = Guid.NewGuid().ToString(); public string action { get; set; } = "Download"; public StateTask state { get; set; } = StateTask.Working; @@ -145,13 +146,14 @@ public DownloadModel(string? name = null) id = Guid.NewGuid().ToString() + ":" + name; } + // Fix for CS7036: Ensure the required "sender" parameter is passed when invoking the EventHandler. + public void ProgressCallback(long transmitted, long totalSize) { if (state == StateTask.Canceled) throw new Exception($"Canceled {name}"); if (state == StateTask.Paused) { - state = StateTask.Working; tis.deleteDownloadInList(this); throw new Exception($"Paused {name}"); @@ -165,11 +167,11 @@ public void ProgressCallback(long transmitted, long totalSize) if (transmitted == totalSize) { state = StateTask.Completed; + EventStatechanged?.Invoke(this, EventArgs.Empty); NotificationModel nm = new NotificationModel(); nm.sendEvent(new Notification($"Download {name} completed", "Download Completed", NotificationTypes.Success)); tis.CheckPendingDownloads(); } - } public void Cancel() diff --git a/TelegramDownloader/Models/FileModel.cs b/TelegramDownloader/Models/FileModel.cs index 10f37b4..d74906c 100644 --- a/TelegramDownloader/Models/FileModel.cs +++ b/TelegramDownloader/Models/FileModel.cs @@ -4,6 +4,8 @@ using TelegramDownloader.Data.db; using Syncfusion.EJ2.Linq; using TL; +using System.Text.Json.Serialization; +using TelegramDownloader.Data; namespace TelegramDownloader.Models { @@ -168,6 +170,42 @@ public FileDetails toFileDetails() }; } + public WebDavFileModel toWebDavFileModel(String? channel = null) + { + return this.IsFile + ? new WebDavFileModel() + { + name = this.Name, + is_dir = false, + file_id = this.Id, + content_type = FileService.getMimeType(this.Type), + content_length = this.Size, + channel = channel, + last_modified = this.DateModified + } + : new WebDavFileModel() + { + name = this.Name, + is_dir = true, + last_modified = this.DateModified + }; + } + } + + public class WebDavFileModel + { + public string name { get; set; } + public bool is_dir { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string file_id { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string content_type { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public long content_length { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? channel { get; set; } + public DateTime last_modified { get; set; } + } public class FileManagerModel diff --git a/TelegramDownloader/Models/GeneralConfig.cs b/TelegramDownloader/Models/GeneralConfig.cs index 9b04aca..9eb05f1 100644 --- a/TelegramDownloader/Models/GeneralConfig.cs +++ b/TelegramDownloader/Models/GeneralConfig.cs @@ -7,6 +7,8 @@ using Newtonsoft.Json; using Syncfusion.Blazor.Charts; using TelegramDownloader.Data; +using System.Diagnostics; +using TelegramDownloader.Services; namespace TelegramDownloader.Models { @@ -72,10 +74,15 @@ public class GeneralConfig public int SplitSize { get; set; } = 0; public int MaxSimultaneousDownloads = 1; public bool CheckHash { get; set; } = false; + /// + /// Maximum image upload size in megabytes. if 0, All images will be sent as a file + /// + public int MaxImageUploadSizeInMb { get; set; } = 10; public bool ShouldShowCaptionPath { get; set; } = false; public bool ShouldShowLogInTerminal { get; set; } = false; public bool ShouldShowPaginatedFileChannel { get; set; } = false; public List FavouriteChannels { get; set; } = new List(); + public WebDavModel webDav { get; set; } = new WebDavModel(); } @@ -86,4 +93,23 @@ public class TLConfig public string? mongo_connection_string { get; set; } public bool? avoid_checking_certificate { get; set; } } + + public class WebDavModel + { + public string Host { get; set; } = "127.0.0.1"; + public int PuertoEntrada { get; set; } = 8080; + public int PuertoSalida { get; set; } = 9081; + [BsonIgnore] + public WebbDavService? webDavService { get; set; } = new WebbDavService(); + + public void start() + { + webDavService.Start(port: PuertoEntrada, externalPort: PuertoSalida, host: Host); + } + + public void stop() + { + webDavService.Stop(); + } + } } diff --git a/TelegramDownloader/Models/LoginModel.cs b/TelegramDownloader/Models/LoginModel.cs index cad6ee7..7afeda8 100644 --- a/TelegramDownloader/Models/LoginModel.cs +++ b/TelegramDownloader/Models/LoginModel.cs @@ -32,4 +32,23 @@ public class ChatMessages public string videoThumb { get; set; } public bool isDocument { get; set; } } + + public class TelegramChatDocuments + { + public int id { get; set; } + public string name { get; set; } + public string extension { get; set; } + public DateTime creationDate { get; set; } + public DateTime modifiedDate { get; set; } + public long fileSize { get; set; } + public DocumentType documentType { get; set; } + } + + public enum DocumentType + { + Photo, + Video, + Audio, + Document + } } diff --git a/TelegramDownloader/Pages/Config.razor b/TelegramDownloader/Pages/Config.razor index 0390990..75eb010 100644 --- a/TelegramDownloader/Pages/Config.razor +++ b/TelegramDownloader/Pages/Config.razor @@ -80,6 +80,17 @@ +
+
+ +
+ + +
+ +
+
+
@if (TelegramService.isPremium) diff --git a/TelegramDownloader/Pages/FileManager.razor b/TelegramDownloader/Pages/FileManager.razor index 9bc58ef..009255b 100644 --- a/TelegramDownloader/Pages/FileManager.razor +++ b/TelegramDownloader/Pages/FileManager.razor @@ -3,7 +3,9 @@ @using TelegramDownloader.Data @using TelegramDownloader.Data.db @using TelegramDownloader.Models +@using TelegramDownloader.Pages.Modals.InfoModals @using TelegramDownloader.Pages.Partials +@using TelegramDownloader.Services @inject IFileService fs @inject ITelegramService ts @inject ILogger Logger @@ -12,11 +14,11 @@ @@ -27,8 +29,25 @@ Export Import + @if (!isMyChannel && showRefresh) + { + + + + Beta + Beta function + + Refresh data + + } + + @if (webDavService.IsRunning) + { + + } +