Version Update Modal Rework + A few bugfixes (#3664)
This commit is contained in:
parent
9fb3bdd548
commit
43d0d1277f
65 changed files with 1963 additions and 805 deletions
|
@ -86,7 +86,7 @@ public class BasicParser(IDirectoryService directoryService, IDefaultParser imag
|
|||
{
|
||||
ParseFromFallbackFolders(filePath, tempRootPath, type, ref ret);
|
||||
}
|
||||
|
||||
ret.Title = Parser.CleanSpecialTitle(fileName);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(ret.Series))
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
@ -44,87 +43,83 @@ public static partial class Parser
|
|||
"One Shot", "One-Shot", "Prologue", "TPB", "Trade Paper Back", "Omnibus", "Compendium", "Absolute", "Graphic Novel",
|
||||
"GN", "FCBD", "Giant Size");
|
||||
|
||||
private static readonly char[] LeadingZeroesTrimChars = new[] { '0' };
|
||||
private static readonly char[] LeadingZeroesTrimChars = ['0'];
|
||||
|
||||
private static readonly char[] SpacesAndSeparators = { '\0', '\t', '\r', ' ', '-', ','};
|
||||
private static readonly char[] SpacesAndSeparators = ['\0', '\t', '\r', ' ', '-', ','];
|
||||
|
||||
|
||||
private const string Number = @"\d+(\.\d)?";
|
||||
private const string NumberRange = Number + @"(-" + Number + @")?";
|
||||
|
||||
/// <summary>
|
||||
/// non greedy matching of a string where parenthesis are balanced
|
||||
/// non-greedy matching of a string where parenthesis are balanced
|
||||
/// </summary>
|
||||
public const string BalancedParen = @"(?:[^()]|(?<open>\()|(?<-open>\)))*?(?(open)(?!))";
|
||||
/// <summary>
|
||||
/// non greedy matching of a string where square brackets are balanced
|
||||
/// non-greedy matching of a string where square brackets are balanced
|
||||
/// </summary>
|
||||
public const string BalancedBracket = @"(?:[^\[\]]|(?<open>\[)|(?<-open>\]))*?(?(open)(?!))";
|
||||
/// <summary>
|
||||
/// Matches [Complete], release tags like [kmts] but not [ Complete ] or [kmts ]
|
||||
/// </summary>
|
||||
private const string TagsInBrackets = $@"\[(?!\s){BalancedBracket}(?<!\s)\]";
|
||||
/// <summary>
|
||||
/// Common regex patterns present in both Comics and Mangas
|
||||
/// </summary>
|
||||
private const string CommonSpecial = @"Specials?|One[- ]?Shot|Extra(?:\sChapter)?(?=\s)|Art Collection|Side Stories|Bonus";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Matches against font-family css syntax. Does not match if url import has data: starting, as that is binary data
|
||||
/// </summary>
|
||||
/// <remarks>See here for some examples https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face</remarks>
|
||||
public static readonly Regex FontSrcUrlRegex = new Regex(@"(?<Start>(?:src:\s?)?(?:url|local)\((?!data:)" + "(?:[\"']?)" + @"(?!data:))"
|
||||
+ "(?<Filename>(?!data:)[^\"']+?)" + "(?<End>[\"']?" + @"\);?)",
|
||||
public static readonly Regex FontSrcUrlRegex = new(@"(?<Start>(?:src:\s?)?(?:url|local)\((?!data:)" + "(?:[\"']?)" + @"(?!data:))"
|
||||
+ "(?<Filename>(?!data:)[^\"']+?)" + "(?<End>[\"']?" + @"\);?)",
|
||||
MatchOptions, RegexTimeout);
|
||||
/// <summary>
|
||||
/// https://developer.mozilla.org/en-US/docs/Web/CSS/@import
|
||||
/// </summary>
|
||||
public static readonly Regex CssImportUrlRegex = new Regex("(@import\\s([\"|']|url\\([\"|']))(?<Filename>[^'\"]+)([\"|']\\)?);",
|
||||
public static readonly Regex CssImportUrlRegex = new("(@import\\s([\"|']|url\\([\"|']))(?<Filename>[^'\"]+)([\"|']\\)?);",
|
||||
MatchOptions | RegexOptions.Multiline, RegexTimeout);
|
||||
/// <summary>
|
||||
/// Misc css image references, like background-image: url(), border-image, or list-style-image
|
||||
/// </summary>
|
||||
/// Original prepend: (background|border|list-style)-image:\s?)?
|
||||
public static readonly Regex CssImageUrlRegex = new Regex(@"(url\((?!data:).(?!data:))" + "(?<Filename>(?!data:)[^\"']*)" + @"(.\))",
|
||||
public static readonly Regex CssImageUrlRegex = new(@"(url\((?!data:).(?!data:))" + "(?<Filename>(?!data:)[^\"']*)" + @"(.\))",
|
||||
MatchOptions, RegexTimeout);
|
||||
|
||||
|
||||
private static readonly Regex ImageRegex = new Regex(ImageFileExtensions,
|
||||
private static readonly Regex ImageRegex = new(ImageFileExtensions,
|
||||
MatchOptions, RegexTimeout);
|
||||
private static readonly Regex ArchiveFileRegex = new Regex(ArchiveFileExtensions,
|
||||
private static readonly Regex ArchiveFileRegex = new(ArchiveFileExtensions,
|
||||
MatchOptions, RegexTimeout);
|
||||
private static readonly Regex ComicInfoArchiveRegex = new Regex(@"\.cbz|\.cbr|\.cb7|\.cbt",
|
||||
private static readonly Regex ComicInfoArchiveRegex = new(@"\.cbz|\.cbr|\.cb7|\.cbt",
|
||||
MatchOptions, RegexTimeout);
|
||||
private static readonly Regex XmlRegex = new Regex(XmlRegexExtensions,
|
||||
private static readonly Regex XmlRegex = new(XmlRegexExtensions,
|
||||
MatchOptions, RegexTimeout);
|
||||
private static readonly Regex BookFileRegex = new Regex(BookFileExtensions,
|
||||
private static readonly Regex BookFileRegex = new(BookFileExtensions,
|
||||
MatchOptions, RegexTimeout);
|
||||
private static readonly Regex CoverImageRegex = new Regex(@"(?<![[a-z]\d])(?:!?)(?<!back)(?<!back_)(?<!back-)(cover|folder)(?![\w\d])",
|
||||
private static readonly Regex CoverImageRegex = new(@"(?<!back[\s_-])(?<!\(back )(?<!back)(?:^|[^a-zA-Z0-9])(!?cover|folder)(?![a-zA-Z0-9]|s\b)",
|
||||
MatchOptions, RegexTimeout);
|
||||
|
||||
/// <summary>
|
||||
/// Normalize everything within Kavita. Some characters don't fall under Unicode, like full-width characters and need to be
|
||||
/// added on a case-by-case basis.
|
||||
/// </summary>
|
||||
private static readonly Regex NormalizeRegex = new Regex(@"[^\p{L}0-9\+!*!+]",
|
||||
private static readonly Regex NormalizeRegex = new(@"[^\p{L}0-9\+!*!+]",
|
||||
MatchOptions, RegexTimeout);
|
||||
|
||||
/// <summary>
|
||||
/// Supports Batman (2020) or Batman (2)
|
||||
/// </summary>
|
||||
private static readonly Regex SeriesAndYearRegex = new Regex(@"^\D+\s\((?<Year>\d+)\)$",
|
||||
private static readonly Regex SeriesAndYearRegex = new(@"^\D+\s\((?<Year>\d+)\)$",
|
||||
MatchOptions, RegexTimeout);
|
||||
|
||||
/// <summary>
|
||||
/// Recognizes the Special token only
|
||||
/// </summary>
|
||||
private static readonly Regex SpecialTokenRegex = new Regex(@"SP\d+",
|
||||
private static readonly Regex SpecialTokenRegex = new(@"SP\d+",
|
||||
MatchOptions, RegexTimeout);
|
||||
|
||||
|
||||
private static readonly Regex[] MangaVolumeRegex = new[]
|
||||
{
|
||||
private static readonly Regex[] MangaVolumeRegex =
|
||||
[
|
||||
// Thai Volume: เล่ม n -> Volume n
|
||||
new Regex(
|
||||
@"(เล่ม|เล่มที่)(\s)?(\.?)(\s|_)?(?<Volume>\d+(\-\d+)?(\.\d+)?)",
|
||||
|
@ -197,11 +192,11 @@ public static partial class Parser
|
|||
// Russian Volume: n Том -> Volume n
|
||||
new Regex(
|
||||
@"(\s|_)?(?<Volume>\d+(?:(\-)\d+)?)(\s|_)Том(а?)",
|
||||
MatchOptions, RegexTimeout),
|
||||
};
|
||||
MatchOptions, RegexTimeout)
|
||||
];
|
||||
|
||||
private static readonly Regex[] MangaSeriesRegex = new[]
|
||||
{
|
||||
private static readonly Regex[] MangaSeriesRegex =
|
||||
[
|
||||
// Thai Volume: เล่ม n -> Volume n
|
||||
new Regex(
|
||||
@"(?<Series>.+?)(เล่ม|เล่มที่)(\s)?(\.?)(\s|_)?(?<Volume>\d+(\-\d+)?(\.\d+)?)",
|
||||
|
@ -374,12 +369,12 @@ public static partial class Parser
|
|||
// Japanese Volume: n巻 -> Volume n
|
||||
new Regex(
|
||||
@"(?<Series>.+?)第(?<Volume>\d+(?:(\-)\d+)?)巻",
|
||||
MatchOptions, RegexTimeout),
|
||||
MatchOptions, RegexTimeout)
|
||||
|
||||
};
|
||||
];
|
||||
|
||||
private static readonly Regex[] ComicSeriesRegex = new[]
|
||||
{
|
||||
private static readonly Regex[] ComicSeriesRegex =
|
||||
[
|
||||
// Thai Volume: เล่ม n -> Volume n
|
||||
new Regex(
|
||||
@"(?<Series>.+?)(เล่ม|เล่มที่)(\s)?(\.?)(\s|_)?(?<Volume>\d+(\-\d+)?(\.\d+)?)",
|
||||
|
@ -467,11 +462,11 @@ public static partial class Parser
|
|||
// MUST BE LAST: Batman & Daredevil - King of New York
|
||||
new Regex(
|
||||
@"^(?<Series>.*)",
|
||||
MatchOptions, RegexTimeout),
|
||||
};
|
||||
MatchOptions, RegexTimeout)
|
||||
];
|
||||
|
||||
private static readonly Regex[] ComicVolumeRegex = new[]
|
||||
{
|
||||
private static readonly Regex[] ComicVolumeRegex =
|
||||
[
|
||||
// Thai Volume: เล่ม n -> Volume n
|
||||
new Regex(
|
||||
@"(เล่ม|เล่มที่)(\s)?(\.?)(\s|_)?(?<Volume>\d+(\-\d+)?(\.\d+)?)",
|
||||
|
@ -507,11 +502,11 @@ public static partial class Parser
|
|||
// Russian Volume: n Том -> Volume n
|
||||
new Regex(
|
||||
@"(\s|_)?(?<Volume>\d+(?:(\-)\d+)?)(\s|_)Том(а?)",
|
||||
MatchOptions, RegexTimeout),
|
||||
};
|
||||
MatchOptions, RegexTimeout)
|
||||
];
|
||||
|
||||
private static readonly Regex[] ComicChapterRegex = new[]
|
||||
{
|
||||
private static readonly Regex[] ComicChapterRegex =
|
||||
[
|
||||
// Thai Volume: บทที่ n -> Chapter n, ตอนที่ n -> Chapter n
|
||||
new Regex(
|
||||
@"(บทที่|ตอนที่)(\s)?(\.?)(\s|_)?(?<Chapter>\d+(\-\d+)?(\.\d+)?)",
|
||||
|
@ -576,11 +571,11 @@ public static partial class Parser
|
|||
// spawn-123, spawn-chapter-123 (from https://github.com/Girbons/comics-downloader)
|
||||
new Regex(
|
||||
@"^(?<Series>.+?)-(chapter-)?(?<Chapter>\d+)",
|
||||
MatchOptions, RegexTimeout),
|
||||
};
|
||||
MatchOptions, RegexTimeout)
|
||||
];
|
||||
|
||||
private static readonly Regex[] MangaChapterRegex = new[]
|
||||
{
|
||||
private static readonly Regex[] MangaChapterRegex =
|
||||
[
|
||||
// Thai Chapter: บทที่ n -> Chapter n, ตอนที่ n -> Chapter n, เล่ม n -> Volume n, เล่มที่ n -> Volume n
|
||||
new Regex(
|
||||
@"(?<Volume>((เล่ม|เล่มที่))?(\s|_)?\.?\d+)(\s|_)(บทที่|ตอนที่)\.?(\s|_)?(?<Chapter>\d+)",
|
||||
|
@ -645,8 +640,8 @@ public static partial class Parser
|
|||
// Russian Chapter: n Главa -> Chapter n
|
||||
new Regex(
|
||||
@"(?!Том)(?<!Том\.)\s\d+(\s|_)?(?<Chapter>\d+(?:\.\d+|-\d+)?)(\s|_)(Глава|глава|Главы|Глава)",
|
||||
MatchOptions, RegexTimeout),
|
||||
};
|
||||
MatchOptions, RegexTimeout)
|
||||
];
|
||||
|
||||
private static readonly Regex MangaEditionRegex = new Regex(
|
||||
// Tenjo Tenge {Full Contact Edition} v01 (2011) (Digital) (ASTC).cbz
|
||||
|
@ -661,25 +656,6 @@ public static partial class Parser
|
|||
MatchOptions, RegexTimeout
|
||||
);
|
||||
|
||||
private static readonly Regex MangaSpecialRegex = new Regex(
|
||||
// All Keywords, does not account for checking if contains volume/chapter identification. Parser.Parse() will handle.
|
||||
$@"\b(?:{CommonSpecial}|Omake)\b",
|
||||
MatchOptions, RegexTimeout
|
||||
);
|
||||
|
||||
private static readonly Regex ComicSpecialRegex = new Regex(
|
||||
// All Keywords, does not account for checking if contains volume/chapter identification. Parser.Parse() will handle.
|
||||
$@"\b(?:{CommonSpecial}|\d.+?(\W|-|^)Annual|Annual(\W|-|$|\s#)|Book \d.+?|Compendium(\W|-|$|\s.+?)|Omnibus(\W|-|$|\s.+?)|FCBD \d.+?|Absolute(\W|-|$|\s.+?)|Preview(\W|-|$|\s.+?)|Hors[ -]S[ée]rie|TPB|HS|THS)\b",
|
||||
MatchOptions, RegexTimeout
|
||||
);
|
||||
|
||||
private static readonly Regex EuropeanComicRegex = new Regex(
|
||||
// All Keywords, does not account for checking if contains volume/chapter identification. Parser.Parse() will handle.
|
||||
@"\b(?:Bd[-\s]Fr)\b",
|
||||
MatchOptions, RegexTimeout
|
||||
);
|
||||
|
||||
|
||||
// If SP\d+ is in the filename, we force treat it as a special regardless if volume or chapter might have been found.
|
||||
private static readonly Regex SpecialMarkerRegex = new Regex(
|
||||
@"SP\d+",
|
||||
|
@ -732,20 +708,6 @@ public static partial class Parser
|
|||
return HasSpecialMarker(filePath);
|
||||
}
|
||||
|
||||
private static bool IsMangaSpecial(string? filePath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filePath)) return false;
|
||||
return HasSpecialMarker(filePath);
|
||||
}
|
||||
|
||||
private static bool IsComicSpecial(string? filePath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filePath)) return false;
|
||||
return HasSpecialMarker(filePath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static string ParseMangaSeries(string filename)
|
||||
{
|
||||
foreach (var regex in MangaSeriesRegex)
|
||||
|
@ -932,22 +894,6 @@ public static partial class Parser
|
|||
return title;
|
||||
}
|
||||
|
||||
private static string RemoveMangaSpecialTags(string title)
|
||||
{
|
||||
return MangaSpecialRegex.Replace(title, string.Empty);
|
||||
}
|
||||
|
||||
private static string RemoveEuropeanTags(string title)
|
||||
{
|
||||
return EuropeanComicRegex.Replace(title, string.Empty);
|
||||
}
|
||||
|
||||
private static string RemoveComicSpecialTags(string title)
|
||||
{
|
||||
return ComicSpecialRegex.Replace(title, string.Empty);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Translates _ -> spaces, trims front and back of string, removes release groups
|
||||
|
@ -966,20 +912,6 @@ public static partial class Parser
|
|||
|
||||
title = RemoveEditionTagHolders(title);
|
||||
|
||||
// if (replaceSpecials)
|
||||
// {
|
||||
// if (isComic)
|
||||
// {
|
||||
// title = RemoveComicSpecialTags(title);
|
||||
// title = RemoveEuropeanTags(title);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// title = RemoveMangaSpecialTags(title);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
title = title.Trim(SpacesAndSeparators);
|
||||
|
||||
title = EmptySpaceRegex.Replace(title, " ");
|
||||
|
@ -1110,11 +1042,6 @@ public static partial class Parser
|
|||
{
|
||||
if (string.IsNullOrEmpty(name)) return name;
|
||||
var cleaned = SpecialTokenRegex.Replace(name.Replace('_', ' '), string.Empty).Trim();
|
||||
var lastIndex = cleaned.LastIndexOf('.');
|
||||
if (lastIndex > 0)
|
||||
{
|
||||
cleaned = cleaned.Substring(0, cleaned.LastIndexOf('.')).Trim();
|
||||
}
|
||||
|
||||
return string.IsNullOrEmpty(cleaned) ? name : cleaned;
|
||||
}
|
||||
|
@ -1132,7 +1059,7 @@ public static partial class Parser
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that a Path doesn't start with certain blacklisted folders, like __MACOSX, @Recently-Snapshot, etc and that if a full path, the filename
|
||||
/// Validates that a Path doesn't start with certain blacklisted folders, like __MACOSX, @Recently-Snapshot, etc. and that if a full path, the filename
|
||||
/// doesn't start with ._, which is a metadata file on MACOSX.
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
|
|
|
@ -51,7 +51,7 @@ public interface IVersionUpdaterService
|
|||
Task<UpdateNotificationDto?> CheckForUpdate();
|
||||
Task PushUpdate(UpdateNotificationDto update);
|
||||
Task<IList<UpdateNotificationDto>> GetAllReleases(int count = 0);
|
||||
Task<int> GetNumberOfReleasesBehind();
|
||||
Task<int> GetNumberOfReleasesBehind(bool stableOnly = false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -112,6 +112,10 @@ public partial class VersionUpdaterService : IVersionUpdaterService
|
|||
return dto;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will add any extra (nightly) updates from the latest stable. Does not back-fill anything prior to the latest stable.
|
||||
/// </summary>
|
||||
/// <param name="dtos"></param>
|
||||
private async Task EnrichWithNightlyInfo(List<UpdateNotificationDto> dtos)
|
||||
{
|
||||
var dto = dtos[0]; // Latest version
|
||||
|
@ -301,7 +305,7 @@ public partial class VersionUpdaterService : IVersionUpdaterService
|
|||
}
|
||||
|
||||
// If we're on a nightly build, enrich the information
|
||||
if (updateDtos.Count != 0 && BuildInfo.Version > new Version(updateDtos[0].UpdateVersion))
|
||||
if (updateDtos.Count != 0) // && BuildInfo.Version > new Version(updateDtos[0].UpdateVersion)
|
||||
{
|
||||
await EnrichWithNightlyInfo(updateDtos);
|
||||
}
|
||||
|
@ -397,22 +401,25 @@ public partial class VersionUpdaterService : IVersionUpdaterService
|
|||
}
|
||||
|
||||
|
||||
public async Task<int> GetNumberOfReleasesBehind()
|
||||
/// <summary>
|
||||
/// Returns the number of releases ahead of this install version. If this install version is on a nightly,
|
||||
/// then include nightly releases, otherwise only count Stable releases.
|
||||
/// </summary>
|
||||
/// <param name="stableOnly">Only count Stable releases </param>
|
||||
/// <returns></returns>
|
||||
public async Task<int> GetNumberOfReleasesBehind(bool stableOnly = false)
|
||||
{
|
||||
var updates = await GetAllReleases();
|
||||
|
||||
// If the user is on nightly, then we need to handle releases behind differently
|
||||
if (updates[0].IsPrerelease)
|
||||
if (!stableOnly && (updates[0].IsPrerelease || updates[0].IsOnNightlyInRelease))
|
||||
{
|
||||
return Math.Min(0, updates
|
||||
.TakeWhile(update => update.UpdateVersion != update.CurrentVersion)
|
||||
.Count() - 1);
|
||||
return updates.Count(u => u.IsReleaseNewer);
|
||||
}
|
||||
|
||||
return Math.Min(0, updates
|
||||
return updates
|
||||
.Where(update => !update.IsPrerelease)
|
||||
.TakeWhile(update => update.UpdateVersion != update.CurrentVersion)
|
||||
.Count());
|
||||
.Count(u => u.IsReleaseNewer);
|
||||
}
|
||||
|
||||
private UpdateNotificationDto? CreateDto(GithubReleaseMetadata? update)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue