mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
Implemented the ability to extract an archive image to a cache directory.
This commit is contained in:
parent
14ad2a3dd5
commit
7ab7e8acc4
3
.gitignore
vendored
3
.gitignore
vendored
@ -447,4 +447,5 @@ appsettings.json
|
|||||||
/API/kavita.db-shm
|
/API/kavita.db-shm
|
||||||
/API/kavita.db-wal
|
/API/kavita.db-wal
|
||||||
/API/Hangfire.db
|
/API/Hangfire.db
|
||||||
/API/Hangfire-log.db
|
/API/Hangfire-log.db
|
||||||
|
cache/
|
46
API/Controllers/ReaderController.cs
Normal file
46
API/Controllers/ReaderController.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using API.Entities;
|
||||||
|
using API.Interfaces;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace API.Controllers
|
||||||
|
{
|
||||||
|
public class ReaderController : BaseApiController
|
||||||
|
{
|
||||||
|
private readonly ISeriesRepository _seriesRepository;
|
||||||
|
private readonly IDirectoryService _directoryService;
|
||||||
|
|
||||||
|
public ReaderController(ISeriesRepository seriesRepository, IDirectoryService directoryService)
|
||||||
|
{
|
||||||
|
_seriesRepository = seriesRepository;
|
||||||
|
_directoryService = directoryService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("info")]
|
||||||
|
public async Task<ActionResult<int>> GetInformation(int volumeId)
|
||||||
|
{
|
||||||
|
Volume volume = await _seriesRepository.GetVolumeAsync(volumeId);
|
||||||
|
|
||||||
|
// Assume we always get first Manga File
|
||||||
|
if (volume == null || !volume.Files.Any())
|
||||||
|
{
|
||||||
|
return BadRequest("There are no files in the volume to read.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var filepath = volume.Files.ElementAt(0).FilePath;
|
||||||
|
|
||||||
|
var extractPath = _directoryService.ExtractArchive(filepath, volumeId);
|
||||||
|
if (string.IsNullOrEmpty(extractPath))
|
||||||
|
{
|
||||||
|
return BadRequest("There file is no longer there or has no images. Please rescan.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(_directoryService.ListFiles(extractPath).Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -76,5 +76,12 @@ namespace API.Data
|
|||||||
return await _context.Series.Where(x => x.Id == seriesId)
|
return await _context.Series.Where(x => x.Id == seriesId)
|
||||||
.ProjectTo<SeriesDto>(_mapper.ConfigurationProvider).SingleAsync();
|
.ProjectTo<SeriesDto>(_mapper.ConfigurationProvider).SingleAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Volume> GetVolumeAsync(int volumeId)
|
||||||
|
{
|
||||||
|
return await _context.Volume
|
||||||
|
.Include(vol => vol.Files)
|
||||||
|
.SingleOrDefaultAsync(vol => vol.Id == volumeId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,6 +16,8 @@ namespace API.Entities
|
|||||||
public uint RowVersion { get; set; }
|
public uint RowVersion { get; set; }
|
||||||
|
|
||||||
public ICollection<AppUserRole> UserRoles { get; set; }
|
public ICollection<AppUserRole> UserRoles { get; set; }
|
||||||
|
|
||||||
|
//public ICollection<SeriesProgress> SeriesProgresses { get; set; }
|
||||||
|
|
||||||
public void OnSavingChanges()
|
public void OnSavingChanges()
|
||||||
{
|
{
|
||||||
|
@ -4,8 +4,38 @@ namespace API.Interfaces
|
|||||||
{
|
{
|
||||||
public interface IDirectoryService
|
public interface IDirectoryService
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Lists out top-level folders for a given directory. Filters out System and Hidden folders.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rootPath">Absolute path of directory to scan.</param>
|
||||||
|
/// <returns>List of folder names</returns>
|
||||||
IEnumerable<string> ListDirectory(string rootPath);
|
IEnumerable<string> ListDirectory(string rootPath);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lists out top-level files for a given directory.
|
||||||
|
/// TODO: Implement ability to provide a filter for file types
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rootPath">Absolute path </param>
|
||||||
|
/// <returns>List of folder names</returns>
|
||||||
|
IEnumerable<string> ListFiles(string rootPath);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Given a library id, scans folders for said library. Parses files and generates DB updates. Will overwrite
|
||||||
|
/// cover images if forceUpdate is true.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="libraryId">Library to scan against</param>
|
||||||
|
/// <param name="forceUpdate">Force overwriting for cover images</param>
|
||||||
void ScanLibrary(int libraryId, bool forceUpdate);
|
void ScanLibrary(int libraryId, bool forceUpdate);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extracts an archive to a temp cache directory. Returns path to new directory. If temp cache directory already exists,
|
||||||
|
/// will return that without performing an extraction. Returns empty string if there are any invalidations which would
|
||||||
|
/// prevent operations to perform correctly (missing archivePath file, empty archive, etc).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="archivePath">A valid file to an archive file.</param>
|
||||||
|
/// <param name="volumeId">Id of volume being extracted.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
string ExtractArchive(string archivePath, int volumeId);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,6 +16,7 @@ namespace API.Interfaces
|
|||||||
Task<IEnumerable<VolumeDto>> GetVolumesDtoAsync(int seriesId);
|
Task<IEnumerable<VolumeDto>> GetVolumesDtoAsync(int seriesId);
|
||||||
IEnumerable<Volume> GetVolumes(int seriesId);
|
IEnumerable<Volume> GetVolumes(int seriesId);
|
||||||
Task<SeriesDto> GetSeriesDtoByIdAsync(int seriesId);
|
Task<SeriesDto> GetSeriesDtoByIdAsync(int seriesId);
|
||||||
|
|
||||||
|
Task<Volume> GetVolumeAsync(int volumeId);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@ -51,11 +52,6 @@ namespace API.Services
|
|||||||
reSearchPattern.IsMatch(Path.GetExtension(file)));
|
reSearchPattern.IsMatch(Path.GetExtension(file)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Lists out top-level folders for a given directory. Filters out System and Hidden folders.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="rootPath">Absolute path </param>
|
|
||||||
/// <returns>List of folder names</returns>
|
|
||||||
public IEnumerable<string> ListDirectory(string rootPath)
|
public IEnumerable<string> ListDirectory(string rootPath)
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(rootPath)) return ImmutableList<string>.Empty;
|
if (!Directory.Exists(rootPath)) return ImmutableList<string>.Empty;
|
||||||
@ -69,6 +65,12 @@ namespace API.Services
|
|||||||
return dirs;
|
return dirs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<string> ListFiles(string rootPath)
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(rootPath)) return ImmutableList<string>.Empty;
|
||||||
|
return Directory.GetFiles(rootPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Processes files found during a library scan. Generates a collection of series->volume->files for DB processing later.
|
/// Processes files found during a library scan. Generates a collection of series->volume->files for DB processing later.
|
||||||
@ -153,7 +155,7 @@ namespace API.Services
|
|||||||
{
|
{
|
||||||
new MangaFile()
|
new MangaFile()
|
||||||
{
|
{
|
||||||
FilePath = info.File
|
FilePath = info.FullFilePath
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -189,6 +191,7 @@ namespace API.Services
|
|||||||
|
|
||||||
public void ScanLibrary(int libraryId, bool forceUpdate)
|
public void ScanLibrary(int libraryId, bool forceUpdate)
|
||||||
{
|
{
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
var library = Task.Run(() => _libraryRepository.GetLibraryForIdAsync(libraryId)).Result;
|
var library = Task.Run(() => _libraryRepository.GetLibraryForIdAsync(libraryId)).Result;
|
||||||
_scannedSeries = new ConcurrentDictionary<string, ConcurrentBag<ParserInfo>>();
|
_scannedSeries = new ConcurrentDictionary<string, ConcurrentBag<ParserInfo>>();
|
||||||
_logger.LogInformation($"Beginning scan on {library.Name}");
|
_logger.LogInformation($"Beginning scan on {library.Name}");
|
||||||
@ -239,6 +242,35 @@ namespace API.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
_scannedSeries = null;
|
_scannedSeries = null;
|
||||||
|
Console.WriteLine("Processed {0} files in {1} milliseconds", library.Name, sw.ElapsedMilliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public string ExtractArchive(string archivePath, int volumeId)
|
||||||
|
{
|
||||||
|
if (!File.Exists(archivePath) || !Parser.Parser.IsArchive(archivePath))
|
||||||
|
{
|
||||||
|
_logger.LogError($"Archive {archivePath} could not be found.");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
var extractPath = Path.Join(Directory.GetCurrentDirectory(), $"../cache/{volumeId}/");
|
||||||
|
|
||||||
|
if (Directory.Exists(extractPath))
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"Archive {archivePath} has already been extracted. Returning existing folder.");
|
||||||
|
return extractPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
using ZipArchive archive = ZipFile.OpenRead(archivePath);
|
||||||
|
|
||||||
|
if (archive.Entries.Count <= 0) return "";
|
||||||
|
|
||||||
|
archive.ExtractToDirectory(extractPath);
|
||||||
|
_logger.LogInformation($"Extracting archive to {extractPath}");
|
||||||
|
|
||||||
|
return extractPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void TraverseTreeParallelForEach(string root, Action<string> action)
|
private static void TraverseTreeParallelForEach(string root, Action<string> action)
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.1 MiB |
Loading…
x
Reference in New Issue
Block a user