Fesaa c62b20f54b
BE Tech Debt (#4497)
Co-authored-by: Joseph Milazzo <joseph.v.milazzo@gmail.com>
Co-authored-by: Joe Milazzo <josephmajora@gmail.com>
2026-03-07 10:04:08 -08:00

137 lines
5.1 KiB
C#

using System.IO;
using Kavita.API.Services;
using Kavita.Models.Entities.Enums;
using Kavita.Models.Metadata;
using Kavita.Models.Parser;
namespace Kavita.Services.Scanner;
public class PdfParser(IDirectoryService directoryService) : DefaultParser(directoryService)
{
public override ParserInfo Parse(string filePath, string rootPath, string libraryRoot, LibraryType type, bool enableMetadata = true, ComicInfo comicInfo = null)
{
var fileName = directoryService.FileSystem.Path.GetFileNameWithoutExtension(filePath);
var ret = new ParserInfo
{
Filename = Path.GetFileName(filePath),
Format = Scanner.Parser.ParseFormat(filePath),
Title = Scanner.Parser.RemoveExtensionIfSupported(fileName)!,
FullFilePath = Scanner.Parser.NormalizePath(filePath),
Series = string.Empty,
ComicInfo = comicInfo,
Chapters = Scanner.Parser.ParseChapter(fileName, type)
};
if (type == LibraryType.Book)
{
ret.Chapters = Scanner.Parser.DefaultChapter;
}
ret.Series = Scanner.Parser.ParseSeries(fileName, type);
ret.Volumes = Scanner.Parser.ParseVolume(fileName, type);
if (ret.Series == string.Empty)
{
// Try to parse information out of each folder all the way to rootPath
ParseFromFallbackFolders(filePath, rootPath, type, ref ret);
}
var edition = Scanner.Parser.ParseEdition(fileName);
if (!string.IsNullOrEmpty(edition))
{
ret.Series = Scanner.Parser.CleanTitle(ret.Series.Replace(edition, string.Empty), type is LibraryType.Comic);
ret.Edition = edition;
}
var isSpecial = Scanner.Parser.IsSpecial(fileName, type);
// We must ensure that we can only parse a special out. As some files will have v20 c171-180+Omake and that
// could cause a problem as Omake is a special term, but there is valid volume/chapter information.
if (Scanner.Parser.IsDefaultChapter(ret.Chapters) && Scanner.Parser.IsLooseLeafVolume(ret.Volumes) && isSpecial)
{
ret.IsSpecial = true;
// NOTE: This can cause some complications, we should try to be a bit less aggressive to fallback to folder
ParseFromFallbackFolders(filePath, rootPath, type, ref ret);
}
// If we are a special with marker, we need to ensure we use the correct series name. we can do this by falling back to Folder name
if (Scanner.Parser.HasSpecialMarker(fileName))
{
ret.IsSpecial = true;
ret.SpecialIndex = Scanner.Parser.ParseSpecialIndex(fileName);
ret.Chapters = Scanner.Parser.DefaultChapter;
ret.Volumes = Scanner.Parser.SpecialVolume;
var tempRootPath = rootPath;
if (rootPath.EndsWith("Specials") || rootPath.EndsWith("Specials/"))
{
tempRootPath = rootPath.Replace("Specials", string.Empty).TrimEnd('/');
}
ParseFromFallbackFolders(filePath, tempRootPath, type, ref ret);
}
if (enableMetadata)
{
// Patch in other information from ComicInfo
UpdateFromComicInfo(ret);
if (comicInfo != null && !string.IsNullOrEmpty(comicInfo.Title))
{
ret.Title = comicInfo.Title.Trim();
}
}
if (Scanner.Parser.IsDefaultChapter(ret.Chapters) && Scanner.Parser.IsLooseLeafVolume(ret.Volumes) && type == LibraryType.Book)
{
ret.IsSpecial = true;
ret.Chapters = Scanner.Parser.DefaultChapter;
ret.Volumes = Scanner.Parser.SpecialVolume;
ParseFromFallbackFolders(filePath, rootPath, type, ref ret);
}
if (type == LibraryType.Book && comicInfo != null)
{
// For books, fall back to the Title for Series.
if (!string.IsNullOrEmpty(comicInfo.Series))
{
ret.Series = comicInfo.Series.Trim();
}
else if (!string.IsNullOrEmpty(comicInfo.Title))
{
ret.Series = comicInfo.Title.Trim();
}
}
if (string.IsNullOrEmpty(ret.Series))
{
ret.Series = Scanner.Parser.CleanTitle(fileName, type is LibraryType.Comic);
}
// Pdfs may have .pdf in the series name, remove that
if (Scanner.Parser.IsPdf(filePath) && ret.Series.ToLower().EndsWith(".pdf"))
{
ret.Series = ret.Series.Substring(0, ret.Series.Length - ".pdf".Length);
}
// v0.8.x: Introducing a change where Specials will go in a separate Volume with a reserved number
if (ret.IsSpecial)
{
ret.Volumes = $"{Scanner.Parser.SpecialVolumeNumber}";
}
return string.IsNullOrEmpty(ret.Series) ? null : ret;
}
/// <summary>
/// Only applicable for PDF files
/// </summary>
/// <param name="filePath"></param>
/// <param name="type"></param>
/// <returns></returns>
public override bool IsApplicable(string filePath, LibraryType type)
{
return Scanner.Parser.IsPdf(filePath);
}
}