mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-05-24 00:52:23 -04:00
Added Hangfire and Hangfire SQLite extension for future task framework. Added a basic directory scanning implementation.
This commit is contained in:
parent
b899157015
commit
b3f210a8ab
@ -19,4 +19,8 @@
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.8.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Tasks" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -99,5 +99,16 @@ namespace API.Controllers
|
||||
|
||||
return BadRequest("Not Implemented");
|
||||
}
|
||||
|
||||
[Authorize(Policy = "RequireAdminRole")]
|
||||
[HttpGet("scan")]
|
||||
public async Task<ActionResult> ScanLibrary(int libraryId)
|
||||
{
|
||||
var library = await _libraryRepository.GetLibraryForIdAsync(libraryId);
|
||||
|
||||
_directoryService.ScanLibrary(library);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
@ -37,6 +37,15 @@ namespace API.Data
|
||||
.Include(f => f.Folders)
|
||||
.ProjectTo<LibraryDto>(_mapper.ConfigurationProvider).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<LibraryDto> GetLibraryForIdAsync(int libraryId)
|
||||
{
|
||||
return await _context.Library
|
||||
.Where(x => x.Id == libraryId)
|
||||
.Include(f => f.Folders)
|
||||
.ProjectTo<LibraryDto>(_mapper.ConfigurationProvider).SingleAsync();
|
||||
}
|
||||
|
||||
|
||||
public async Task<bool> LibraryExists(string libraryName)
|
||||
{
|
||||
|
@ -1,10 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using API.DTOs;
|
||||
using API.Entities;
|
||||
|
||||
namespace API.Interfaces
|
||||
{
|
||||
public interface IDirectoryService
|
||||
{
|
||||
IEnumerable<string> ListDirectory(string rootPath);
|
||||
|
||||
void ScanLibrary(LibraryDto library);
|
||||
}
|
||||
}
|
@ -17,6 +17,6 @@ namespace API.Interfaces
|
||||
/// <returns></returns>
|
||||
Task<bool> LibraryExists(string libraryName);
|
||||
|
||||
Task<IEnumerable<LibraryDto>> GetLibrariesForUserAsync(AppUser user);
|
||||
public Task<LibraryDto> GetLibraryForIdAsync(int libraryId);
|
||||
}
|
||||
}
|
@ -1,16 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using API.DTOs;
|
||||
using API.Interfaces;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Services
|
||||
{
|
||||
public class DirectoryService : IDirectoryService
|
||||
{
|
||||
/// <summary>
|
||||
private readonly ILogger<DirectoryService> _logger;
|
||||
|
||||
public DirectoryService(ILogger<DirectoryService> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists out top-level folders for a given directory. Filters out System and Hidden folders.
|
||||
/// </summary>
|
||||
/// <param name="rootPath">Absolute path </param>
|
||||
@ -27,5 +39,132 @@ namespace API.Services
|
||||
|
||||
return dirs;
|
||||
}
|
||||
|
||||
public void ScanLibrary(LibraryDto library)
|
||||
{
|
||||
foreach (var folderPath in library.Folders)
|
||||
{
|
||||
try {
|
||||
TraverseTreeParallelForEach(folderPath, (f) =>
|
||||
{
|
||||
// Exceptions are no-ops.
|
||||
try {
|
||||
// Do nothing with the data except read it.
|
||||
//byte[] data = File.ReadAllBytes(f);
|
||||
ProcessManga(f);
|
||||
}
|
||||
catch (FileNotFoundException) {}
|
||||
catch (IOException) {}
|
||||
catch (UnauthorizedAccessException) {}
|
||||
catch (SecurityException) {}
|
||||
// Display the filename.
|
||||
Console.WriteLine(f);
|
||||
});
|
||||
}
|
||||
catch (ArgumentException) {
|
||||
Console.WriteLine(@"The directory 'C:\Program Files' does not exist.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProcessManga(string filename)
|
||||
{
|
||||
Console.WriteLine($"Found {filename}");
|
||||
}
|
||||
|
||||
public static void TraverseTreeParallelForEach(string root, Action<string> action)
|
||||
{
|
||||
//Count of files traversed and timer for diagnostic output
|
||||
int fileCount = 0;
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
// Determine whether to parallelize file processing on each folder based on processor count.
|
||||
int procCount = System.Environment.ProcessorCount;
|
||||
|
||||
// Data structure to hold names of subfolders to be examined for files.
|
||||
Stack<string> dirs = new Stack<string>();
|
||||
|
||||
if (!Directory.Exists(root)) {
|
||||
throw new ArgumentException();
|
||||
}
|
||||
dirs.Push(root);
|
||||
|
||||
while (dirs.Count > 0) {
|
||||
string currentDir = dirs.Pop();
|
||||
string[] subDirs = {};
|
||||
string[] files = {};
|
||||
|
||||
try {
|
||||
subDirs = Directory.GetDirectories(currentDir);
|
||||
}
|
||||
// Thrown if we do not have discovery permission on the directory.
|
||||
catch (UnauthorizedAccessException e) {
|
||||
Console.WriteLine(e.Message);
|
||||
continue;
|
||||
}
|
||||
// Thrown if another process has deleted the directory after we retrieved its name.
|
||||
catch (DirectoryNotFoundException e) {
|
||||
Console.WriteLine(e.Message);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
files = Directory.GetFiles(currentDir);
|
||||
}
|
||||
catch (UnauthorizedAccessException e) {
|
||||
Console.WriteLine(e.Message);
|
||||
continue;
|
||||
}
|
||||
catch (DirectoryNotFoundException e) {
|
||||
Console.WriteLine(e.Message);
|
||||
continue;
|
||||
}
|
||||
catch (IOException e) {
|
||||
Console.WriteLine(e.Message);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Execute in parallel if there are enough files in the directory.
|
||||
// Otherwise, execute sequentially.Files are opened and processed
|
||||
// synchronously but this could be modified to perform async I/O.
|
||||
try {
|
||||
if (files.Length < procCount) {
|
||||
foreach (var file in files) {
|
||||
action(file);
|
||||
fileCount++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Parallel.ForEach(files, () => 0, (file, loopState, localCount) =>
|
||||
{ action(file);
|
||||
return (int) ++localCount;
|
||||
},
|
||||
(c) => {
|
||||
Interlocked.Add(ref fileCount, c);
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (AggregateException ae) {
|
||||
ae.Handle((ex) => {
|
||||
if (ex is UnauthorizedAccessException) {
|
||||
// Here we just output a message and go on.
|
||||
Console.WriteLine(ex.Message);
|
||||
return true;
|
||||
}
|
||||
// Handle other exceptions here if necessary...
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Push the subdirectories onto the stack for traversal.
|
||||
// This could also be done before handing the files.
|
||||
foreach (string str in subDirs)
|
||||
dirs.Push(str);
|
||||
}
|
||||
|
||||
// For diagnostic purposes.
|
||||
Console.WriteLine("Processed {0} files in {1} milliseconds", fileCount, sw.ElapsedMilliseconds);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user