using System;
using API.Data.Metadata;
using API.Entities.Enums;
using API.Services.Tasks.Scanner.Parser;
namespace API.Services;
public interface IReadingItemService
{
ComicInfo? GetComicInfo(string filePath);
int GetNumberOfPages(string filePath, MangaFormat format);
string GetCoverImage(string filePath, string fileName, MangaFormat format, bool saveAsWebP);
void Extract(string fileFilePath, string targetDirectory, MangaFormat format, int imageCount = 1);
ParserInfo? ParseFile(string path, string rootPath, LibraryType type);
}
public class ReadingItemService : IReadingItemService
{
private readonly IArchiveService _archiveService;
private readonly IBookService _bookService;
private readonly IImageService _imageService;
private readonly IDirectoryService _directoryService;
private readonly IDefaultParser _defaultParser;
public ReadingItemService(IArchiveService archiveService, IBookService bookService, IImageService imageService, IDirectoryService directoryService)
{
_archiveService = archiveService;
_bookService = bookService;
_imageService = imageService;
_directoryService = directoryService;
_defaultParser = new DefaultParser(directoryService);
}
///
/// Gets the ComicInfo for the file if it exists. Null otherwise.
///
/// Fully qualified path of file
///
public ComicInfo? GetComicInfo(string filePath)
{
if (Parser.IsEpub(filePath))
{
return _bookService.GetComicInfo(filePath);
}
if (Parser.IsComicInfoExtension(filePath))
{
return _archiveService.GetComicInfo(filePath);
}
return null;
}
///
/// Processes files found during a library scan.
///
/// Path of a file
///
/// Library type to determine parsing to perform
public ParserInfo? ParseFile(string path, string rootPath, LibraryType type)
{
var info = Parse(path, rootPath, type);
if (info == null)
{
return null;
}
// This catches when original library type is Manga/Comic and when parsing with non
if (Parser.IsEpub(path) && Parser.ParseVolume(info.Series) != Parser.DefaultVolume) // Shouldn't this be info.Volume != DefaultVolume?
{
var hasVolumeInTitle = !Parser.ParseVolume(info.Title)
.Equals(Parser.DefaultVolume);
var hasVolumeInSeries = !Parser.ParseVolume(info.Series)
.Equals(Parser.DefaultVolume);
if (string.IsNullOrEmpty(info.ComicInfo?.Volume) && hasVolumeInTitle && (hasVolumeInSeries || string.IsNullOrEmpty(info.Series)))
{
// This is likely a light novel for which we can set series from parsed title
info.Series = Parser.ParseSeries(info.Title);
info.Volumes = Parser.ParseVolume(info.Title);
}
else
{
var info2 = _defaultParser.Parse(path, rootPath, LibraryType.Book);
info.Merge(info2);
}
}
// This is first time ComicInfo is called
info.ComicInfo = GetComicInfo(path);
if (info.ComicInfo == null) return info;
if (!string.IsNullOrEmpty(info.ComicInfo.Volume))
{
info.Volumes = info.ComicInfo.Volume;
}
if (!string.IsNullOrEmpty(info.ComicInfo.Series))
{
info.Series = info.ComicInfo.Series.Trim();
}
if (!string.IsNullOrEmpty(info.ComicInfo.Number))
{
info.Chapters = info.ComicInfo.Number;
}
// Patch is SeriesSort from ComicInfo
if (!string.IsNullOrEmpty(info.ComicInfo.TitleSort))
{
info.SeriesSort = info.ComicInfo.TitleSort.Trim();
}
if (!string.IsNullOrEmpty(info.ComicInfo.Format) && Parser.HasComicInfoSpecial(info.ComicInfo.Format))
{
info.IsSpecial = true;
info.Chapters = Parser.DefaultChapter;
info.Volumes = Parser.DefaultVolume;
}
if (!string.IsNullOrEmpty(info.ComicInfo.SeriesSort))
{
info.SeriesSort = info.ComicInfo.SeriesSort.Trim();
}
if (!string.IsNullOrEmpty(info.ComicInfo.LocalizedSeries))
{
info.LocalizedSeries = info.ComicInfo.LocalizedSeries.Trim();
}
return info;
}
///
///
///
///
///
///
public int GetNumberOfPages(string filePath, MangaFormat format)
{
switch (format)
{
case MangaFormat.Archive:
{
return _archiveService.GetNumberOfPagesFromArchive(filePath);
}
case MangaFormat.Pdf:
case MangaFormat.Epub:
{
return _bookService.GetNumberOfPages(filePath);
}
case MangaFormat.Image:
{
return 1;
}
case MangaFormat.Unknown:
default:
return 0;
}
}
public string GetCoverImage(string filePath, string fileName, MangaFormat format, bool saveAsWebP)
{
if (string.IsNullOrEmpty(filePath) || string.IsNullOrEmpty(fileName))
{
return string.Empty;
}
return format switch
{
MangaFormat.Epub => _bookService.GetCoverImage(filePath, fileName, _directoryService.CoverImageDirectory, saveAsWebP),
MangaFormat.Archive => _archiveService.GetCoverImage(filePath, fileName, _directoryService.CoverImageDirectory, saveAsWebP),
MangaFormat.Image => _imageService.GetCoverImage(filePath, fileName, _directoryService.CoverImageDirectory, saveAsWebP),
MangaFormat.Pdf => _bookService.GetCoverImage(filePath, fileName, _directoryService.CoverImageDirectory, saveAsWebP),
_ => string.Empty
};
}
///
/// Extracts the reading item to the target directory using the appropriate method
///
/// File to extract
/// Where to extract to. Will be created if does not exist
/// Format of the File
/// If the file is of type image, pass number of files needed. If > 0, will copy the whole directory.
///
public void Extract(string fileFilePath, string targetDirectory, MangaFormat format, int imageCount = 1)
{
switch (format)
{
case MangaFormat.Archive:
_archiveService.ExtractArchive(fileFilePath, targetDirectory);
break;
case MangaFormat.Image:
_imageService.ExtractImages(fileFilePath, targetDirectory, imageCount);
break;
case MangaFormat.Pdf:
_bookService.ExtractPdfImages(fileFilePath, targetDirectory);
break;
case MangaFormat.Unknown:
case MangaFormat.Epub:
break;
default:
throw new ArgumentOutOfRangeException(nameof(format), format, null);
}
}
///
/// Parses information out of a file. If file is a book (epub), it will use book metadata regardless of LibraryType
///
///
///
///
///
private ParserInfo? Parse(string path, string rootPath, LibraryType type)
{
return Parser.IsEpub(path) ? _bookService.ParseInfo(path) : _defaultParser.Parse(path, rootPath, type);
}
}