Downloading Enhancements (#2599)
This commit is contained in:
parent
e6f6090fcf
commit
70cb687ef6
15 changed files with 139 additions and 45 deletions
|
|
@ -118,7 +118,7 @@ public class DownloadController : BaseApiController
|
|||
return await _accountService.HasDownloadPermission(user);
|
||||
}
|
||||
|
||||
private ActionResult GetFirstFileDownload(IEnumerable<MangaFile> files)
|
||||
private PhysicalFileResult GetFirstFileDownload(IEnumerable<MangaFile> files)
|
||||
{
|
||||
var (zipFile, contentType, fileDownloadName) = _downloadService.GetFirstFileDownload(files);
|
||||
return PhysicalFile(zipFile, contentType, Uri.EscapeDataString(fileDownloadName), true);
|
||||
|
|
@ -150,31 +150,40 @@ public class DownloadController : BaseApiController
|
|||
|
||||
private async Task<ActionResult> DownloadFiles(ICollection<MangaFile> files, string tempFolder, string downloadName)
|
||||
{
|
||||
var username = User.GetUsername();
|
||||
var filename = Path.GetFileNameWithoutExtension(downloadName);
|
||||
try
|
||||
{
|
||||
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress,
|
||||
MessageFactory.DownloadProgressEvent(User.GetUsername(),
|
||||
Path.GetFileNameWithoutExtension(downloadName), 0F, "started"));
|
||||
MessageFactory.DownloadProgressEvent(username,
|
||||
filename, $"Downloading {filename}", 0F, "started"));
|
||||
if (files.Count == 1)
|
||||
{
|
||||
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress,
|
||||
MessageFactory.DownloadProgressEvent(User.GetUsername(),
|
||||
Path.GetFileNameWithoutExtension(downloadName), 1F, "ended"));
|
||||
MessageFactory.DownloadProgressEvent(username,
|
||||
filename, $"Downloading {filename}",1F, "ended"));
|
||||
return GetFirstFileDownload(files);
|
||||
}
|
||||
|
||||
var filePath = _archiveService.CreateZipForDownload(files.Select(c => c.FilePath), tempFolder);
|
||||
var filePath = _archiveService.CreateZipFromFoldersForDownload(files.Select(c => c.FilePath).ToList(), tempFolder, ProgressCallback);
|
||||
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress,
|
||||
MessageFactory.DownloadProgressEvent(User.GetUsername(),
|
||||
Path.GetFileNameWithoutExtension(downloadName), 1F, "ended"));
|
||||
MessageFactory.DownloadProgressEvent(username,
|
||||
filename, "Download Complete", 1F, "ended"));
|
||||
return PhysicalFile(filePath, DefaultContentType, Uri.EscapeDataString(downloadName), true);
|
||||
|
||||
async Task ProgressCallback(Tuple<string, float> progressInfo)
|
||||
{
|
||||
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress,
|
||||
MessageFactory.DownloadProgressEvent(username, filename, $"Extracting {Path.GetFileNameWithoutExtension(progressInfo.Item1)}",
|
||||
Math.Clamp(progressInfo.Item2, 0F, 1F)));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "There was an exception when trying to download files");
|
||||
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress,
|
||||
MessageFactory.DownloadProgressEvent(User.GetUsername(),
|
||||
Path.GetFileNameWithoutExtension(downloadName), 1F, "ended"));
|
||||
filename, "Download Complete", 1F, "ended"));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
|
@ -216,12 +225,12 @@ public class DownloadController : BaseApiController
|
|||
|
||||
var filename = $"{series!.Name} - Bookmarks.zip";
|
||||
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress,
|
||||
MessageFactory.DownloadProgressEvent(username, Path.GetFileNameWithoutExtension(filename), 0F));
|
||||
MessageFactory.DownloadProgressEvent(username, Path.GetFileNameWithoutExtension(filename), $"Downloading {filename}",0F));
|
||||
var seriesIds = string.Join("_", downloadBookmarkDto.Bookmarks.Select(b => b.SeriesId).Distinct());
|
||||
var filePath = _archiveService.CreateZipForDownload(files,
|
||||
$"download_{userId}_{seriesIds}_bookmarks");
|
||||
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress,
|
||||
MessageFactory.DownloadProgressEvent(username, Path.GetFileNameWithoutExtension(filename), 1F));
|
||||
MessageFactory.DownloadProgressEvent(username, Path.GetFileNameWithoutExtension(filename), $"Downloading {filename}", 1F));
|
||||
|
||||
|
||||
return PhysicalFile(filePath, DefaultContentType, System.Web.HttpUtility.UrlEncode(filename), true);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using System.Diagnostics;
|
|||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.Serialization;
|
||||
using API.Archive;
|
||||
|
|
@ -30,13 +31,21 @@ public interface IArchiveService
|
|||
ArchiveLibrary CanOpen(string archivePath);
|
||||
bool ArchiveNeedsFlattening(ZipArchive archive);
|
||||
/// <summary>
|
||||
/// Creates a zip file form the listed files and outputs to the temp folder.
|
||||
/// Creates a zip file form the listed files and outputs to the temp folder. This will combine into one zip of multiple zips.
|
||||
/// </summary>
|
||||
/// <param name="files">List of files to be zipped up. Should be full file paths.</param>
|
||||
/// <param name="tempFolder">Temp folder name to use for preparing the files. Will be created and deleted</param>
|
||||
/// <returns>Path to the temp zip</returns>
|
||||
/// <exception cref="KavitaException"></exception>
|
||||
string CreateZipForDownload(IEnumerable<string> files, string tempFolder);
|
||||
/// <summary>
|
||||
/// Creates a zip file form the listed files and outputs to the temp folder. This will extract each archive and combine them into one zip.
|
||||
/// </summary>
|
||||
/// <param name="files">List of files to be zipped up. Should be full file paths.</param>
|
||||
/// <param name="tempFolder">Temp folder name to use for preparing the files. Will be created and deleted</param>
|
||||
/// <returns>Path to the temp zip</returns>
|
||||
/// <exception cref="KavitaException"></exception>
|
||||
string CreateZipFromFoldersForDownload(IList<string> files, string tempFolder, Func<Tuple<string, float>, Task> progressCallback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -322,6 +331,54 @@ public class ArchiveService : IArchiveService
|
|||
return zipPath;
|
||||
}
|
||||
|
||||
public string CreateZipFromFoldersForDownload(IList<string> files, string tempFolder, Func<Tuple<string, float>, Task> progressCallback)
|
||||
{
|
||||
var dateString = DateTime.UtcNow.ToShortDateString().Replace("/", "_");
|
||||
|
||||
var potentialExistingFile = _directoryService.FileSystem.FileInfo.New(Path.Join(_directoryService.TempDirectory, $"kavita_{tempFolder}_{dateString}.zip"));
|
||||
if (potentialExistingFile.Exists)
|
||||
{
|
||||
// A previous download exists, just return it immediately
|
||||
return potentialExistingFile.FullName;
|
||||
}
|
||||
|
||||
// Extract all the files to a temp directory and create zip on that
|
||||
var tempLocation = Path.Join(_directoryService.TempDirectory, $"{tempFolder}_{dateString}");
|
||||
var totalFiles = files.Count + 1;
|
||||
var count = 1f;
|
||||
try
|
||||
{
|
||||
_directoryService.ExistOrCreate(tempLocation);
|
||||
foreach (var path in files)
|
||||
{
|
||||
var tempPath = Path.Join(tempLocation, _directoryService.FileSystem.Path.GetFileNameWithoutExtension(_directoryService.FileSystem.FileInfo.New(path).Name));
|
||||
_directoryService.ExistOrCreate(tempPath);
|
||||
progressCallback(Tuple.Create(_directoryService.FileSystem.FileInfo.New(path).Name, (1.0f * totalFiles) / count));
|
||||
ExtractArchive(path, tempPath);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new KavitaException("bad-copy-files-for-download");
|
||||
}
|
||||
|
||||
var zipPath = Path.Join(_directoryService.TempDirectory, $"kavita_{tempFolder}_{dateString}.zip");
|
||||
try
|
||||
{
|
||||
ZipFile.CreateFromDirectory(tempLocation, zipPath);
|
||||
// Remove the folder as we have the zip
|
||||
_directoryService.ClearAndDeleteDirectory(tempLocation);
|
||||
}
|
||||
catch (AggregateException ex)
|
||||
{
|
||||
_logger.LogError(ex, "There was an issue creating temp archive");
|
||||
throw new KavitaException("generic-create-temp-archive");
|
||||
}
|
||||
|
||||
return zipPath;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Test if the archive path exists and an archive
|
||||
|
|
@ -477,7 +534,7 @@ public class ArchiveService : IArchiveService
|
|||
{
|
||||
if (!IsValidArchive(archivePath)) return;
|
||||
|
||||
if (Directory.Exists(extractPath)) return;
|
||||
if (_directoryService.FileSystem.Directory.Exists(extractPath)) return;
|
||||
|
||||
if (!_directoryService.FileSystem.File.Exists(archivePath))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -376,13 +376,13 @@ public static class MessageFactory
|
|||
};
|
||||
}
|
||||
|
||||
public static SignalRMessage DownloadProgressEvent(string username, string downloadName, float progress, string eventType = "updated")
|
||||
public static SignalRMessage DownloadProgressEvent(string username, string downloadName, string subtitle, float progress, string eventType = "updated")
|
||||
{
|
||||
return new SignalRMessage()
|
||||
{
|
||||
Name = DownloadProgress,
|
||||
Title = $"Downloading {downloadName}",
|
||||
SubTitle = $"Preparing {username.SentenceCase()} the download of {downloadName}",
|
||||
Title = $"Preparing {username.SentenceCase()} the download of {downloadName}",
|
||||
SubTitle = subtitle,
|
||||
EventType = eventType,
|
||||
Progress = ProgressType.Determinate,
|
||||
Body = new
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue