Added Hangfire and Hangfire SQLite extension for future task framework. Added a basic directory scanning implementation.

This commit is contained in:
Joseph Milazzo 2020-12-26 09:08:27 -06:00
parent b899157015
commit b3f210a8ab
6 changed files with 169 additions and 2 deletions

View File

@ -19,4 +19,8 @@
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.8.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Tasks" />
</ItemGroup>
</Project>

View File

@ -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();
}
}
}

View File

@ -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)
{

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}