mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-05-24 00:52:23 -04:00
Implemented the ability to flatten directories, esp useful with nested folders in archives.
This commit is contained in:
parent
56e8a0059e
commit
7f404a0ce9
@ -19,8 +19,7 @@ namespace API.Tests
|
|||||||
[InlineData("[Suihei Kiki]_Kasumi_Otoko_no_Ko_[Taruby]_v1.1.zip", "1")]
|
[InlineData("[Suihei Kiki]_Kasumi_Otoko_no_Ko_[Taruby]_v1.1.zip", "1")]
|
||||||
public void ParseVolumeTest(string filename, string expected)
|
public void ParseVolumeTest(string filename, string expected)
|
||||||
{
|
{
|
||||||
var result = ParseVolume(filename);
|
Assert.Equal(expected, ParseVolume(filename));
|
||||||
Assert.Equal(expected, result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
@ -36,8 +35,7 @@ namespace API.Tests
|
|||||||
[InlineData("Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)", "Akame ga KILL! ZERO")]
|
[InlineData("Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)", "Akame ga KILL! ZERO")]
|
||||||
public void ParseSeriesTest(string filename, string expected)
|
public void ParseSeriesTest(string filename, string expected)
|
||||||
{
|
{
|
||||||
var result = ParseSeries(filename);
|
Assert.Equal(expected, ParseSeries(filename));
|
||||||
Assert.Equal(expected, result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
@ -53,8 +51,7 @@ namespace API.Tests
|
|||||||
[InlineData("Adding volume 1 with File: Ana Satsujin Vol. 1 Ch. 5 - Manga Box (gb).cbz", "5")]
|
[InlineData("Adding volume 1 with File: Ana Satsujin Vol. 1 Ch. 5 - Manga Box (gb).cbz", "5")]
|
||||||
public void ParseChaptersTest(string filename, string expected)
|
public void ParseChaptersTest(string filename, string expected)
|
||||||
{
|
{
|
||||||
var result = ParseChapter(filename);
|
Assert.Equal(expected, ParseChapter(filename));
|
||||||
Assert.Equal(expected, result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -91,6 +88,7 @@ namespace API.Tests
|
|||||||
[InlineData("test.cbr", true)]
|
[InlineData("test.cbr", true)]
|
||||||
[InlineData("test.zip", true)]
|
[InlineData("test.zip", true)]
|
||||||
[InlineData("test.rar", true)]
|
[InlineData("test.rar", true)]
|
||||||
|
[InlineData("test.rar.!qb", false)]
|
||||||
public void IsArchiveTest(string input, bool expected)
|
public void IsArchiveTest(string input, bool expected)
|
||||||
{
|
{
|
||||||
Assert.Equal(expected, IsArchive(input));
|
Assert.Equal(expected, IsArchive(input));
|
||||||
|
@ -30,7 +30,7 @@ namespace API.Comparators
|
|||||||
{
|
{
|
||||||
bool c1 = Char.IsDigit(s1, i1);
|
bool c1 = Char.IsDigit(s1, i1);
|
||||||
bool c2 = Char.IsDigit(s2, i2);
|
bool c2 = Char.IsDigit(s2, i2);
|
||||||
var r = 0; // temp result
|
int r; // temp result
|
||||||
if(!c1 && !c2)
|
if(!c1 && !c2)
|
||||||
{
|
{
|
||||||
bool letter1 = Char.IsLetter(s1, i1);
|
bool letter1 = Char.IsLetter(s1, i1);
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
using System.Linq;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using API.DTOs;
|
using API.DTOs;
|
||||||
using API.Entities;
|
|
||||||
using API.Interfaces;
|
using API.Interfaces;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
@ -1,11 +1,65 @@
|
|||||||
namespace API.Extensions
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace API.Extensions
|
||||||
{
|
{
|
||||||
public static class DirectoryInfoExtensions
|
public static class DirectoryInfoExtensions
|
||||||
{
|
{
|
||||||
public static void Empty(this System.IO.DirectoryInfo directory)
|
public static void Empty(this DirectoryInfo directory)
|
||||||
{
|
{
|
||||||
foreach(System.IO.FileInfo file in directory.EnumerateFiles()) file.Delete();
|
foreach(FileInfo file in directory.EnumerateFiles()) file.Delete();
|
||||||
foreach(System.IO.DirectoryInfo subDirectory in directory.EnumerateDirectories()) subDirectory.Delete(true);
|
foreach(DirectoryInfo subDirectory in directory.EnumerateDirectories()) subDirectory.Delete(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flattens all files in subfolders to the passed directory recursively.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// foo<para />
|
||||||
|
/// ├── 1.txt<para />
|
||||||
|
/// ├── 2.txt<para />
|
||||||
|
/// ├── 3.txt<para />
|
||||||
|
/// ├── 4.txt<para />
|
||||||
|
/// └── bar<para />
|
||||||
|
/// ├── 1.txt<para />
|
||||||
|
/// ├── 2.txt<para />
|
||||||
|
/// └── 5.txt<para />
|
||||||
|
///
|
||||||
|
/// becomes:<para />
|
||||||
|
/// foo<para />
|
||||||
|
/// ├── 1.txt<para />
|
||||||
|
/// ├── 2.txt<para />
|
||||||
|
/// ├── 3.txt<para />
|
||||||
|
/// ├── 4.txt<para />
|
||||||
|
/// ├── bar_1.txt<para />
|
||||||
|
/// ├── bar_2.txt<para />
|
||||||
|
/// └── bar_5.txt<para />
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="directory"></param>
|
||||||
|
public static void Flatten(this DirectoryInfo directory)
|
||||||
|
{
|
||||||
|
FlattenDirectory(directory, directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void FlattenDirectory(DirectoryInfo root, DirectoryInfo directory)
|
||||||
|
{
|
||||||
|
if (!root.FullName.Equals(directory.FullName)) // I might be able to replace this with root === directory
|
||||||
|
{
|
||||||
|
foreach (var file in directory.EnumerateFiles())
|
||||||
|
{
|
||||||
|
if (file.Directory == null) continue;
|
||||||
|
var newName = $"{file.Directory.Name}_{file.Name}";
|
||||||
|
var newPath = Path.Join(root.FullName, newName);
|
||||||
|
Console.WriteLine($"Renaming/Moving file to: {newPath}");
|
||||||
|
file.MoveTo(newPath);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var subDirectory in directory.EnumerateDirectories())
|
||||||
|
{
|
||||||
|
FlattenDirectory(root, subDirectory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,7 +4,6 @@ using API.DTOs;
|
|||||||
|
|
||||||
namespace API.Interfaces
|
namespace API.Interfaces
|
||||||
{
|
{
|
||||||
// TODO: Refactor this into IDiskService to encapsulate all disk based IO
|
|
||||||
public interface IDirectoryService
|
public interface IDirectoryService
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -42,7 +42,7 @@ namespace API.Services
|
|||||||
foreach (var file in volume.Files)
|
foreach (var file in volume.Files)
|
||||||
{
|
{
|
||||||
var extractPath = GetVolumeCachePath(volumeId, file);
|
var extractPath = GetVolumeCachePath(volumeId, file);
|
||||||
|
|
||||||
_directoryService.ExtractArchive(file.FilePath, extractPath);
|
_directoryService.ExtractArchive(file.FilePath, extractPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +137,7 @@ namespace API.Services
|
|||||||
{
|
{
|
||||||
FilePath = info.FullFilePath,
|
FilePath = info.FullFilePath,
|
||||||
Chapter = chapter,
|
Chapter = chapter,
|
||||||
|
Format = info.Format,
|
||||||
NumberOfPages = GetNumberOfPagesFromArchive(info.FullFilePath)
|
NumberOfPages = GetNumberOfPagesFromArchive(info.FullFilePath)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -284,7 +285,13 @@ namespace API.Services
|
|||||||
return Path.Join(Directory.GetCurrentDirectory(), $"../cache/{volumeId}/");
|
return Path.Join(Directory.GetCurrentDirectory(), $"../cache/{volumeId}/");
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ExtractArchive(string archivePath, int volumeId)
|
/// <summary>
|
||||||
|
/// TODO: Delete this method
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="archivePath"></param>
|
||||||
|
/// <param name="volumeId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private string ExtractArchive(string archivePath, int volumeId)
|
||||||
{
|
{
|
||||||
if (!File.Exists(archivePath) || !Parser.Parser.IsArchive(archivePath))
|
if (!File.Exists(archivePath) || !Parser.Parser.IsArchive(archivePath))
|
||||||
{
|
{
|
||||||
@ -302,10 +309,18 @@ namespace API.Services
|
|||||||
|
|
||||||
using ZipArchive archive = ZipFile.OpenRead(archivePath);
|
using ZipArchive archive = ZipFile.OpenRead(archivePath);
|
||||||
|
|
||||||
if (!archive.HasFiles()) return "";
|
// TODO: Throw error if we couldn't extract
|
||||||
|
var needsFlattening = archive.Entries.Count > 0 && !Path.HasExtension(archive.Entries.ElementAt(0).FullName);
|
||||||
|
if (!archive.HasFiles() && !needsFlattening) return "";
|
||||||
|
|
||||||
archive.ExtractToDirectory(extractPath);
|
archive.ExtractToDirectory(extractPath);
|
||||||
_logger.LogInformation($"Extracting archive to {extractPath}");
|
_logger.LogInformation($"Extracting archive to {extractPath}");
|
||||||
|
|
||||||
|
if (needsFlattening)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Extracted archive is nested in root folder, flattening...");
|
||||||
|
new DirectoryInfo(extractPath).Flatten();
|
||||||
|
}
|
||||||
|
|
||||||
return extractPath;
|
return extractPath;
|
||||||
}
|
}
|
||||||
@ -325,12 +340,18 @@ namespace API.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
using ZipArchive archive = ZipFile.OpenRead(archivePath);
|
using ZipArchive archive = ZipFile.OpenRead(archivePath);
|
||||||
|
// TODO: Throw error if we couldn't extract
|
||||||
if (!archive.HasFiles()) return "";
|
var needsFlattening = archive.Entries.Count > 0 && !Path.HasExtension(archive.Entries.ElementAt(0).FullName);
|
||||||
|
if (!archive.HasFiles() && !needsFlattening) return "";
|
||||||
|
|
||||||
archive.ExtractToDirectory(extractPath);
|
archive.ExtractToDirectory(extractPath);
|
||||||
_logger.LogDebug($"Extracting archive to {extractPath}");
|
_logger.LogDebug($"Extracting archive to {extractPath}");
|
||||||
|
|
||||||
|
if (!needsFlattening) return extractPath;
|
||||||
|
|
||||||
|
_logger.LogInformation("Extracted archive is nested in root folder, flattening...");
|
||||||
|
new DirectoryInfo(extractPath).Flatten();
|
||||||
|
|
||||||
return extractPath;
|
return extractPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,7 +364,6 @@ namespace API.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
using ZipArchive archive = ZipFile.OpenRead(archivePath);
|
using ZipArchive archive = ZipFile.OpenRead(archivePath);
|
||||||
Console.WriteLine();
|
|
||||||
return archive.Entries.Count(e => Parser.Parser.IsImage(e.FullName));
|
return archive.Entries.Count(e => Parser.Parser.IsImage(e.FullName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user