Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
00c0e91
feat: add download grid from channels
mateof Apr 24, 2025
8bad38f
feat: show channel files
mateof May 24, 2025
b0b0cc6
feat: add media streaming from telegram (#32)
mateof Sep 6, 2025
7f93450
feat: media stream for huge files (#34)
mateof Sep 9, 2025
15f240b
Merge branch 'main' into develop
mateof Sep 9, 2025
57f0290
Merge branch 'main' into develop
mateof Sep 9, 2025
d183622
feat: add webdav and export files to strm (#39)
mateof Sep 23, 2025
c5fc1a1
chore: dockerfile
mateof Sep 23, 2025
0b5b9af
chore: dockerfile change
mateof Sep 23, 2025
be4a2e1
chore: python virtual env
mateof Sep 23, 2025
aff3772
chore: add beta tags
mateof Sep 25, 2025
773751c
chore: log refresh
mateof Sep 25, 2025
3b11b98
Merge branch 'main' into develop
mateof Sep 25, 2025
d88af3f
feat: add download file before play
mateof Oct 25, 2025
7f0186f
chore: python docker repair
mateof Oct 26, 2025
ada83dd
feat: add virtual scroll config
mateof Oct 26, 2025
1fe6a30
chore: add mutex in tasks
mateof Oct 29, 2025
2906995
fix: repair strm
mateof Oct 31, 2025
9761906
fea: add preload files on streaming option
mateof Nov 7, 2025
ce5c42e
fix: solve download multi part file for stream
mateof Nov 7, 2025
10ef101
fix: multi download problem
mateof Nov 7, 2025
df4625b
chore: add getFileById on download
mateof Nov 8, 2025
95ee3c1
chore: add ogm as a video extension
mateof Nov 8, 2025
8e5323b
feat: improve streaming
mateof Nov 9, 2025
2c294a1
feat: download files v2
mateof Nov 9, 2025
9c2cefe
feat: add reverse proxy support over https
mateof Nov 13, 2025
6498b4d
chore: add local file manager routes dynamically
mateof Nov 13, 2025
2d85d14
chore: improve navbar
mateof Nov 15, 2025
c7e1d61
feat: add speedChart
mateof Dec 3, 2025
f566faf
feat: improve main window (#41)
mateof Dec 3, 2025
f5ac352
feat: remove id from task tables
mateof Dec 3, 2025
29a9636
fix: solve some errors 🚑
mateof Dec 5, 2025
95ed0cb
chore: improve shared file manager
mateof Dec 5, 2025
1c7dd4b
chore: add start time in download
mateof Dec 5, 2025
8a8adfe
chore: improve js and css
mateof Dec 6, 2025
ebe67ce
chore: improve events
mateof Dec 7, 2025
05a01ae
chore: navbar changes
mateof Dec 7, 2025
66ffc59
chore: change strm export
mateof Dec 8, 2025
66b7482
Merge remote-tracking branch 'origin/main' into develop
mateof Dec 8, 2025
9243ef3
feat: add new lazy DropDownTree
mateof Dec 9, 2025
1133146
feat: add custom mobile file manager (#43)
mateof Dec 10, 2025
f4296fc
feat: prevent load file manager
mateof Dec 10, 2025
11ff003
feat: add file type filter on mobile
mateof Dec 11, 2025
3dcfc0f
feat: add shared folder to mobile
mateof Dec 11, 2025
fc28e80
feat: add preload files
mateof Dec 11, 2025
a4a7c10
feat: slide bottom bar
mateof Dec 11, 2025
c7418e4
feat: bump .net to 10
mateof Dec 11, 2025
daae717
feat: versioning
mateof Dec 11, 2025
213762c
feat: improve version modal
mateof Dec 12, 2025
e049b01
feat: improve refresh data channel
mateof Dec 12, 2025
5d7bc8a
chore: improve audio and video modals
mateof Dec 12, 2025
3a2476f
feat: add audio playlist
mateof Dec 12, 2025
0b97f6a
feat: improve audio player
mateof Dec 12, 2025
88f3ad7
feat: improve configuration
mateof Dec 12, 2025
7687ade
feat: improve local file upload
mateof Dec 13, 2025
c6b0cf4
feat: improve top row
mateof Dec 13, 2025
7477b66
feat: improve offcanvas
mateof Dec 13, 2025
f98cd97
feat: improve task manager
mateof Dec 13, 2025
c2eb61e
chore: change dockerfile
mateof Dec 13, 2025
9741d40
fix: ensure is dispose
mateof Dec 13, 2025
e8795e1
chore: improve tasks list
mateof Dec 13, 2025
c5cc450
feat: tasks tab navigation
mateof Dec 13, 2025
5e4f361
feat: improve import modal
mateof Dec 13, 2025
50c610a
feat: add multiple audio files to playlist
mateof Dec 13, 2025
c70b3be
feat: devices compatibilities
mateof Dec 13, 2025
4bedb8d
fix: culture error solve
mateof Dec 13, 2025
035d74d
feat: improve video modal
mateof Dec 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Docker Image CI
name: Docker Image CI (Dev)

on:
push:
Expand All @@ -12,17 +12,30 @@ jobs:

steps:
- uses: actions/checkout@v4

- name: Generate version
id: version
run: |
# Generate dev version: 0.0.0-dev.YYYYMMDD.HHMMSS.shortsha
DATE_VERSION=$(date +%Y%m%d.%H%M%S)
SHORT_SHA=$(echo ${{ github.sha }} | cut -c1-7)
VERSION="0.0.0-dev.${DATE_VERSION}.${SHORT_SHA}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Generated version: $VERSION"

- name: 'Login to GitHub Container Registry'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{github.actor}}
password: ${{secrets.GHCR}}

- name: Build and Push the Docker image
run: |
date_ref=$(date +%s)
docker build ./TelegramDownloader -f ./TelegramDownloader/Dockerfile --tag ghcr.io/mateof/telegramfilemanager:dev_$date_ref
IMAGE_ID=$(docker images | grep ghcr.io/mateof/telegramfilemanager | awk '{print $3}')
docker tag $IMAGE_ID ghcr.io/mateof/telegramfilemanager:dev_latest
docker push ghcr.io/mateof/telegramfilemanager:dev_$date_ref
VERSION=${{ steps.version.outputs.version }}
docker build ./TelegramDownloader -f ./TelegramDownloader/Dockerfile \
--build-arg VERSION=$VERSION \
--tag ghcr.io/mateof/telegramfilemanager:dev_$VERSION \
--tag ghcr.io/mateof/telegramfilemanager:dev_latest
docker push ghcr.io/mateof/telegramfilemanager:dev_$VERSION
docker push ghcr.io/mateof/telegramfilemanager:dev_latest
22 changes: 17 additions & 5 deletions .github/workflows/release-docker-image.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Docker Image CI
name: Docker Image CI (Release)

on:
push:
Expand All @@ -12,16 +12,28 @@ jobs:

steps:
- uses: actions/checkout@v4

- name: Extract version from tag
id: version
run: |
# Remove 'v' prefix from tag (v1.2.3 -> 1.2.3)
VERSION=${GITHUB_REF_NAME#v}
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Extracted version: $VERSION"

- name: 'Login to GitHub Container Registry'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{github.actor}}
password: ${{secrets.GHCR}}

- name: Build and Push the Docker image
run: |
docker build ./TelegramDownloader -f ./TelegramDownloader/Dockerfile --tag ghcr.io/mateof/telegramfilemanager:latest --build-arg tag=${{github.ref_name}}
IMAGE_ID=$(docker images | grep ghcr.io/mateof/telegramfilemanager | awk '{print $3}')
docker tag $IMAGE_ID ghcr.io/mateof/telegramfilemanager:${{github.ref_name}}
docker push ghcr.io/mateof/telegramfilemanager:latest
VERSION=${{ steps.version.outputs.version }}
docker build ./TelegramDownloader -f ./TelegramDownloader/Dockerfile \
--build-arg VERSION=$VERSION \
--tag ghcr.io/mateof/telegramfilemanager:${{github.ref_name}} \
--tag ghcr.io/mateof/telegramfilemanager:latest
docker push ghcr.io/mateof/telegramfilemanager:${{github.ref_name}}
docker push ghcr.io/mateof/telegramfilemanager:latest
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ services:
api_id: ""
hash_id: ""
DOTNET_HOSTBUILDER__RELOADCONFIGONCHANGE: false
DOTNET_CLI_TELEMETRY_OPTOUT: true
restart: unless-stopped
depends_on:
- mongodb_container
Expand Down
88 changes: 83 additions & 5 deletions TelegramDownloader/Data/FileService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,52 @@ public async Task oneItemDeleteAsync(string dbName, Syncfusion.Blazor.FileManage

}

public async Task<FileManagerResponse<Syncfusion.Blazor.FileManager.FileManagerDirectoryContent>> CopyOrMoveItems(string dbName, Syncfusion.Blazor.FileManager.FileManagerDirectoryContent[] files, string targetPath, Syncfusion.Blazor.FileManager.FileManagerDirectoryContent targetData, bool isCopy)
{
try
{
FileManagerResponse<Syncfusion.Blazor.FileManager.FileManagerDirectoryContent> fm = new FileManagerResponse<Syncfusion.Blazor.FileManager.FileManagerDirectoryContent>();
var lista = new List<Syncfusion.Blazor.FileManager.FileManagerDirectoryContent>();
foreach (var item in files)
{
if (!item.IsFile)
{
var result = await copyAllDirectoryFiles(dbName, item.Id, targetData, targetPath + item.Name + "/");
lista.Add(result.toFileManagerContentInCopy());
if (!isCopy)
{
await _db.deleteEntry(dbName, item.Id);
}
}
else
{
var result = await _db.copyItem(dbName, item.Id, targetData, targetPath, item.IsFile);
lista.Add(result.toFileManagerContentInCopy());
if (!isCopy)
{
await _db.deleteEntry(dbName, item.Id);
}
}
await _db.addBytesToFolder(dbName, item.ParentId, item.Size);
}
fm.Files = lista;
return fm;
}
catch (Exception e)
{
_logger.LogError(e, "Error on CopyOrMoveItems");
if (e is MongoWriteException ex)
{
if (ex.WriteError.Category == ServerErrorCategory.DuplicateKey)
{
NotificationModel nm = new NotificationModel();
nm.sendEvent(new Notification("The file exist in the directory", "Duplicate", NotificationTypes.Error));
}
}
throw e;
}
}

private async Task<BsonFileManagerModel> copyAllDirectoryFiles(string dbName, string idfolder, Syncfusion.Blazor.FileManager.FileManagerDirectoryContent target, string targetPath, bool isCopy = true)
{
var result = await _db.copyItem(dbName, idfolder, target, targetPath, false);
Expand Down Expand Up @@ -1027,6 +1073,11 @@ public async Task importSharedData(ShareFilesModel sfm, GenericNotificationProgr
return (await _db.createEntry(dbName, await _db.toBasonFile(args.Path, args.FolderName, args.ParentFolder))).Select(x => x.toFileManagerContent()).ToList();
}

public async Task<List<Syncfusion.Blazor.FileManager.FileManagerDirectoryContent>> createFolder(string dbName, string path, string folderName, Syncfusion.Blazor.FileManager.FileManagerDirectoryContent? parentFolder)
{
return (await _db.createEntry(dbName, await _db.toBasonFile(path, folderName, parentFolder))).Select(x => x.toFileManagerContent()).ToList();
}

public async Task CreateDatabase(string id)
{
if (_ts.checkChannelExist(id))
Expand Down Expand Up @@ -1444,10 +1495,11 @@ public async Task UploadFileFromServer(string dbName, string currentPath, List<S
// if file or folder does not exist, it will be created
if ((await _db.getFileByPath(dbName, System.IO.Path.Combine(currentPath, file.Name))) == null)
await _db.createEntry(dbName, model);
if (!parent.HasChild)
await _db.setDirectoryHasChild(dbName, parent.Id);
if (file.IsFile)
{
await _db.addBytesToFolder(dbName, model.ParentId, model.Size);
await _db.setDirectoryHasChild(dbName, parent.Id);
if (idt != null) idt.AddOne(file.Size);
}
else
Expand Down Expand Up @@ -1535,6 +1587,7 @@ private string GetMimeType(string fileName)

public async Task refreshChannelFIles(string channelId, bool force = false)
{
int totalNewMessages = 0;
refreshMutex.WaitOne();
refreshChannelList.Add(channelId);
refreshMutex.ReleaseMutex();
Expand Down Expand Up @@ -1590,21 +1643,23 @@ public async Task refreshChannelFIles(string channelId, bool force = false)
model.isSplit = false;
model.isEncrypted = false;
await _db.createEntry(channelId, model);
totalNewMessages++;
}
}
refreshMutex.WaitOne();
refreshChannelList.Remove(channelId);
refreshMutex.ReleaseMutex();
_logger.LogInformation($"Finish Refresh channel with id: {channelId}");
_logger.LogInformation($"Finish Refresh channel with id: {channelId} with {totalNewMessages} new files added.");

// Fix for CS1739: Removed the invalid 'autoHide' parameter and replaced it with the correct property assignment.
ToastMessage tm = new ToastMessage
{
Type = ToastType.Success,
IconName = IconName.CheckCircle,
Title = "Refresh channel files",
Message = "Files channel has been refreshed",
Message = $"Files channel has been refreshed with {totalNewMessages} new files added.",
AutoHide = true

};
_toastService.Notify(tm);
}
Expand Down Expand Up @@ -1769,6 +1824,13 @@ public async Task<List<BsonFileManagerModel>> getTelegramFolders(string dbName,
return result;
}

public async Task<List<BsonFileManagerModel>> getTelegramFoldersByParentId(string dbName, string? parentId)
{
var result = await _db.getFoldersByParentId(dbName, parentId);

return result;
}

private async Task<List<FolderModel>> getTelegramSubfolders(string id, List<BsonFileManagerModel> listFolders, string prevPath)
{
List<FolderModel> node = new List<FolderModel>();
Expand Down Expand Up @@ -1841,9 +1903,20 @@ private async Task<List<ExpandoObject>> AddChildRecords(string id, string parent
var childItem = fileDetails.Count > 0 && fileDetails[0] != null ? fileDetails[0] : Data
.Where(x => x.FilePath + "/" == path).First().toFileManagerContent();
response.CWD = childItem;
response.Files = Data
.Where(x => x.ParentId == childItem.Id).Select(x => x.toFileManagerContent()).ToList();

// First try to find children in the query result (by ParentId)
var files = Data.Where(x => x.ParentId == childItem.Id).ToList();

// If no children found in query result, do a separate query by ParentId
// This handles the case where children's FilterPath format doesn't match the query path
if (files.Count == 0 && !string.IsNullOrEmpty(childItem.Id))
{
files = collectionName == null
? await _db.getFilesByParentId(dbName, childItem.Id)
: await _db.getFilesByParentId(dbName, childItem.Id, collectionName);
}

response.Files = files.Select(x => x.toFileManagerContent()).ToList();
}
await Task.Yield();
return response;
Expand Down Expand Up @@ -1980,5 +2053,10 @@ private async Task mergeFileStreamAsync(List<string> pathList, string destName)
}
_logger.LogInformation("File merge completed - DestName: {DestName}", destName);
}

public virtual Task<int> PreloadFilesToTemp(string channelId, List<Syncfusion.Blazor.FileManager.FileManagerDirectoryContent> items)
{
throw new NotImplementedException("PreloadFilesToTemp is implemented in FileServiceV2");
}
}
}
136 changes: 135 additions & 1 deletion TelegramDownloader/Data/FileServiceV2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,141 @@ public async Task DownloadFileNowV2(string dbName, int messageId, string destPat
{
await _ts.DownloadFileAndReturn(cm, ms: fs, model: model);
}


}

public override async Task<int> PreloadFilesToTemp(string channelId, List<FileManagerDirectoryContent> items)
{
_logger.LogInformation("Starting preload files to temp V2 - ChannelId: {ChannelId}, ItemsCount: {Count}", channelId, items.Count);
NotificationModel nm = new NotificationModel();
int preloadedCount = 0;
string tempPath = System.IO.Path.Combine(TEMPDIR, "_temp");
Directory.CreateDirectory(tempPath);

try
{
nm.sendEvent(new Notification($"Starting preload of {items.Count} items to cache", "Preload", NotificationTypes.Info));

// Collect all files to preload (including files inside folders)
var filesToPreload = new List<BsonFileManagerModel>();
await CollectFilesForPreloadV2(channelId, items, filesToPreload);

_logger.LogInformation("Collected {Count} files to preload", filesToPreload.Count);
nm.sendEvent(new Notification($"Found {filesToPreload.Count} files to preload", "Preload", NotificationTypes.Info));

foreach (var file in filesToPreload)
{
try
{
// Build the temp file name: {channelId}-{MessageId}-{fileName}
string tempFileName = $"{channelId}-{file.MessageId}-{file.Name}";
string tempFilePath = System.IO.Path.Combine(tempPath, tempFileName);

// Check if file already exists and is complete
if (File.Exists(tempFilePath))
{
var existingFile = new System.IO.FileInfo(tempFilePath);
if (existingFile.Length >= file.Size)
{
_logger.LogInformation("File already preloaded: {FileName}", file.Name);
preloadedCount++;
continue;
}
// File exists but incomplete, delete and re-download
File.Delete(tempFilePath);
}

// Download file to temp
_logger.LogInformation("Preloading file: {FileName} (MessageId: {MessageId})", file.Name, file.MessageId);

if (file.isSplit)
{
// Handle split files
await PreloadSplitFilesToTempV2(channelId, file, tempFilePath);
}
else
{
await downloadFromTelegramV2(channelId, (int)file.MessageId, tempFilePath, file);
}

preloadedCount++;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error preloading file: {FileName}", file.Name);
}
}

nm.sendEvent(new Notification($"Preload completed: {preloadedCount} files cached", "Preload", NotificationTypes.Success));
_logger.LogInformation("Preload completed - FilesPreloaded: {Count}", preloadedCount);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error during preload to temp");
nm.sendEvent(new Notification($"Error during preload: {ex.Message}", "Preload Error", NotificationTypes.Error));
}

return preloadedCount;
}

private async Task CollectFilesForPreloadV2(string channelId, List<FileManagerDirectoryContent> items, List<BsonFileManagerModel> filesToPreload)
{
foreach (var item in items)
{
if (item.IsFile)
{
// Get the file details from database
var dbFile = await _db.getFileById(channelId, item.Id);
if (dbFile != null && dbFile.MessageId != null)
{
filesToPreload.Add(dbFile);
}
}
else
{
// It's a folder - get all files inside recursively
await CollectFilesInFolderRecursiveV2(channelId, item.Id, filesToPreload);
}
}
}

private async Task CollectFilesInFolderRecursiveV2(string channelId, string folderId, List<BsonFileManagerModel> filesToPreload)
{
// Get all items in this folder
var children = await _db.getFilesByParentId(channelId, folderId);

foreach (var child in children)
{
if (child.IsFile)
{
if (child.MessageId != null)
{
filesToPreload.Add(child);
}
}
else
{
// Recurse into subfolder
await CollectFilesInFolderRecursiveV2(channelId, child.Id, filesToPreload);
}
}
}

private async Task PreloadSplitFilesToTempV2(string channelId, BsonFileManagerModel file, string destPath)
{
_logger.LogInformation("Preloading split file V2 - FileName: {FileName}, Parts: {Parts}", file.Name, file.ListMessageId?.Count ?? 0);

if (File.Exists(destPath))
{
File.Delete(destPath);
}

int i = 1;
foreach (int messageId in file.ListMessageId)
{
await downloadFromTelegramV2(channelId, messageId, destPath, file, true, destPath, i);
i++;
}
}

}
Expand Down
Loading