Disable Animations + Lots of bugfixes and Polish (#1561)
* Fixed inputs not showing inline validation due to a missing class * Fixed some checks * Increased the button size on manga reader (develop) * Migrated a type cast to a pure pipe * Sped up the check for if SendTo should render on the menu * Don't allow user to bookmark in bookmark mode * Fixed a bug where Scan Series would skip over Specials due to how new scan loop works. * Fixed scroll to top button persisting when navigating between pages * Edit Series modal now doesn't have a lock field for Series, which can't be locked as it is inheritently locked. Added some validation to ensure Name and SortName are required. * Fixed up some spacing * Fixed actionable menu not opening submenu on mobile * Cleaned up the layout of cover image on series detail * Show all volume or chapters (if only one volume) for cover selection on series * Don't open submenu to right if there is no space * Fixed up cover image not allowing custom saves of existing series/chapter/volume images. Fixed up logging so console output matches log file. * Implemented the ability to turn off css transitions in the UI. * Updated a note internally * Code smells * Added InstallId when pinging the email service to allow throughput tracking
This commit is contained in:
parent
ee7d109170
commit
28ab34c66d
59 changed files with 2103 additions and 444 deletions
|
@ -962,7 +962,7 @@ public class BookService : IBookService
|
|||
}
|
||||
catch (Exception)
|
||||
{
|
||||
/* Swallow exception. Some css don't have style rules ending in ; */
|
||||
//Swallow exception. Some css don't have style rules ending in ';'
|
||||
}
|
||||
|
||||
body = Regex.Replace(body, @"([\s:]0)(px|pt|%|em)", "$1");
|
||||
|
|
|
@ -46,6 +46,7 @@ public class EmailService : IEmailService
|
|||
_unitOfWork = unitOfWork;
|
||||
_downloadService = downloadService;
|
||||
|
||||
|
||||
FlurlHttp.ConfigureClient(DefaultApiUrl, cli =>
|
||||
cli.Settings.HttpClientFactory = new UntrustedCertClientFactory());
|
||||
}
|
||||
|
@ -126,15 +127,17 @@ public class EmailService : IEmailService
|
|||
return await SendEmailWithFiles(emailLink + "/api/sendto", data.FilePaths, data.DestinationEmail);
|
||||
}
|
||||
|
||||
private static async Task<bool> SendEmailWithGet(string url, int timeoutSecs = 30)
|
||||
private async Task<bool> SendEmailWithGet(string url, int timeoutSecs = 30)
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
|
||||
var response = await (url)
|
||||
.WithHeader("Accept", "application/json")
|
||||
.WithHeader("User-Agent", "Kavita")
|
||||
.WithHeader("x-api-key", "MsnvA2DfQqxSK5jh")
|
||||
.WithHeader("x-kavita-version", BuildInfo.Version)
|
||||
.WithHeader("x-kavita-installId", settings.InstallId)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithTimeout(TimeSpan.FromSeconds(timeoutSecs))
|
||||
.GetStringAsync();
|
||||
|
@ -152,15 +155,17 @@ public class EmailService : IEmailService
|
|||
}
|
||||
|
||||
|
||||
private static async Task<bool> SendEmailWithPost(string url, object data, int timeoutSecs = 30)
|
||||
private async Task<bool> SendEmailWithPost(string url, object data, int timeoutSecs = 30)
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
|
||||
var response = await (url)
|
||||
.WithHeader("Accept", "application/json")
|
||||
.WithHeader("User-Agent", "Kavita")
|
||||
.WithHeader("x-api-key", "MsnvA2DfQqxSK5jh")
|
||||
.WithHeader("x-kavita-version", BuildInfo.Version)
|
||||
.WithHeader("x-kavita-installId", settings.InstallId)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithTimeout(TimeSpan.FromSeconds(timeoutSecs))
|
||||
.PostJsonAsync(data);
|
||||
|
@ -182,10 +187,12 @@ public class EmailService : IEmailService
|
|||
{
|
||||
try
|
||||
{
|
||||
var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
|
||||
var response = await (url)
|
||||
.WithHeader("User-Agent", "Kavita")
|
||||
.WithHeader("x-api-key", "MsnvA2DfQqxSK5jh")
|
||||
.WithHeader("x-kavita-version", BuildInfo.Version)
|
||||
.WithHeader("x-kavita-installId", settings.InstallId)
|
||||
.WithTimeout(TimeSpan.FromSeconds(timeoutSecs))
|
||||
.PostMultipartAsync(mp =>
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using API.Data.Metadata;
|
||||
using API.Entities.Enums;
|
||||
using API.Parser;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
|
||||
namespace API.Services;
|
||||
|
||||
|
|
|
@ -150,7 +150,7 @@ public class LibraryWatcher : ILibraryWatcher
|
|||
|
||||
private void OnError(object sender, ErrorEventArgs e)
|
||||
{
|
||||
_logger.LogError(e.GetException(), "[LibraryWatcher] An error occured, likely too many watches occured at once. Restarting Watchers");
|
||||
_logger.LogError(e.GetException(), "[LibraryWatcher] An error occured, likely too many changes occured at once or the folder being watched was deleted. Restarting Watchers");
|
||||
Task.Run(RestartWatching);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using API.Entities.Enums;
|
||||
using API.Services;
|
||||
using API.Parser;
|
||||
|
||||
namespace API.Parser;
|
||||
namespace API.Services.Tasks.Scanner.Parser;
|
||||
|
||||
public interface IDefaultParser
|
||||
{
|
||||
|
@ -36,81 +36,81 @@ public class DefaultParser : IDefaultParser
|
|||
var fileName = _directoryService.FileSystem.Path.GetFileNameWithoutExtension(filePath);
|
||||
ParserInfo ret;
|
||||
|
||||
if (Services.Tasks.Scanner.Parser.Parser.IsEpub(filePath))
|
||||
if (Parser.IsEpub(filePath))
|
||||
{
|
||||
ret = new ParserInfo()
|
||||
ret = new ParserInfo
|
||||
{
|
||||
Chapters = Services.Tasks.Scanner.Parser.Parser.ParseChapter(fileName) ?? Services.Tasks.Scanner.Parser.Parser.ParseComicChapter(fileName),
|
||||
Series = Services.Tasks.Scanner.Parser.Parser.ParseSeries(fileName) ?? Services.Tasks.Scanner.Parser.Parser.ParseComicSeries(fileName),
|
||||
Volumes = Services.Tasks.Scanner.Parser.Parser.ParseVolume(fileName) ?? Services.Tasks.Scanner.Parser.Parser.ParseComicVolume(fileName),
|
||||
Chapters = Parser.ParseChapter(fileName) ?? Parser.ParseComicChapter(fileName),
|
||||
Series = Parser.ParseSeries(fileName) ?? Parser.ParseComicSeries(fileName),
|
||||
Volumes = Parser.ParseVolume(fileName) ?? Parser.ParseComicVolume(fileName),
|
||||
Filename = Path.GetFileName(filePath),
|
||||
Format = Services.Tasks.Scanner.Parser.Parser.ParseFormat(filePath),
|
||||
Format = Parser.ParseFormat(filePath),
|
||||
FullFilePath = filePath
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = new ParserInfo()
|
||||
ret = new ParserInfo
|
||||
{
|
||||
Chapters = type == LibraryType.Comic ? Services.Tasks.Scanner.Parser.Parser.ParseComicChapter(fileName) : Services.Tasks.Scanner.Parser.Parser.ParseChapter(fileName),
|
||||
Series = type == LibraryType.Comic ? Services.Tasks.Scanner.Parser.Parser.ParseComicSeries(fileName) : Services.Tasks.Scanner.Parser.Parser.ParseSeries(fileName),
|
||||
Volumes = type == LibraryType.Comic ? Services.Tasks.Scanner.Parser.Parser.ParseComicVolume(fileName) : Services.Tasks.Scanner.Parser.Parser.ParseVolume(fileName),
|
||||
Chapters = type == LibraryType.Comic ? Parser.ParseComicChapter(fileName) : Parser.ParseChapter(fileName),
|
||||
Series = type == LibraryType.Comic ? Parser.ParseComicSeries(fileName) : Parser.ParseSeries(fileName),
|
||||
Volumes = type == LibraryType.Comic ? Parser.ParseComicVolume(fileName) : Parser.ParseVolume(fileName),
|
||||
Filename = Path.GetFileName(filePath),
|
||||
Format = Services.Tasks.Scanner.Parser.Parser.ParseFormat(filePath),
|
||||
Format = Parser.ParseFormat(filePath),
|
||||
Title = Path.GetFileNameWithoutExtension(fileName),
|
||||
FullFilePath = filePath
|
||||
};
|
||||
}
|
||||
|
||||
if (Services.Tasks.Scanner.Parser.Parser.IsImage(filePath) && Services.Tasks.Scanner.Parser.Parser.IsCoverImage(filePath)) return null;
|
||||
if (Parser.IsCoverImage(filePath)) return null;
|
||||
|
||||
if (Services.Tasks.Scanner.Parser.Parser.IsImage(filePath))
|
||||
if (Parser.IsImage(filePath))
|
||||
{
|
||||
// Reset Chapters, Volumes, and Series as images are not good to parse information out of. Better to use folders.
|
||||
ret.Volumes = Services.Tasks.Scanner.Parser.Parser.DefaultVolume;
|
||||
ret.Chapters = Services.Tasks.Scanner.Parser.Parser.DefaultChapter;
|
||||
ret.Volumes = Parser.DefaultVolume;
|
||||
ret.Chapters = Parser.DefaultChapter;
|
||||
ret.Series = string.Empty;
|
||||
}
|
||||
|
||||
if (ret.Series == string.Empty || Services.Tasks.Scanner.Parser.Parser.IsImage(filePath))
|
||||
if (ret.Series == string.Empty || Parser.IsImage(filePath))
|
||||
{
|
||||
// Try to parse information out of each folder all the way to rootPath
|
||||
ParseFromFallbackFolders(filePath, rootPath, type, ref ret);
|
||||
}
|
||||
|
||||
var edition = Services.Tasks.Scanner.Parser.Parser.ParseEdition(fileName);
|
||||
var edition = Parser.ParseEdition(fileName);
|
||||
if (!string.IsNullOrEmpty(edition))
|
||||
{
|
||||
ret.Series = Services.Tasks.Scanner.Parser.Parser.CleanTitle(ret.Series.Replace(edition, ""), type is LibraryType.Comic);
|
||||
ret.Series = Parser.CleanTitle(ret.Series.Replace(edition, ""), type is LibraryType.Comic);
|
||||
ret.Edition = edition;
|
||||
}
|
||||
|
||||
var isSpecial = type == LibraryType.Comic ? Services.Tasks.Scanner.Parser.Parser.IsComicSpecial(fileName) : Services.Tasks.Scanner.Parser.Parser.IsMangaSpecial(fileName);
|
||||
var isSpecial = type == LibraryType.Comic ? Parser.IsComicSpecial(fileName) : Parser.IsMangaSpecial(fileName);
|
||||
// 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 (ret.Chapters == Services.Tasks.Scanner.Parser.Parser.DefaultChapter && ret.Volumes == Services.Tasks.Scanner.Parser.Parser.DefaultVolume && isSpecial)
|
||||
if (ret.Chapters == Parser.DefaultChapter && ret.Volumes == Parser.DefaultVolume && isSpecial)
|
||||
{
|
||||
ret.IsSpecial = true;
|
||||
ParseFromFallbackFolders(filePath, rootPath, type, ref ret); // NOTE: This can cause some complications, we should try to be a bit less aggressive to fallback to folder
|
||||
}
|
||||
|
||||
// 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 (Services.Tasks.Scanner.Parser.Parser.HasSpecialMarker(fileName))
|
||||
if (Parser.HasSpecialMarker(fileName))
|
||||
{
|
||||
ret.IsSpecial = true;
|
||||
ret.Chapters = Services.Tasks.Scanner.Parser.Parser.DefaultChapter;
|
||||
ret.Volumes = Services.Tasks.Scanner.Parser.Parser.DefaultVolume;
|
||||
ret.Chapters = Parser.DefaultChapter;
|
||||
ret.Volumes = Parser.DefaultVolume;
|
||||
|
||||
ParseFromFallbackFolders(filePath, rootPath, type, ref ret);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(ret.Series))
|
||||
{
|
||||
ret.Series = Services.Tasks.Scanner.Parser.Parser.CleanTitle(fileName, type is LibraryType.Comic);
|
||||
ret.Series = Parser.CleanTitle(fileName, type is LibraryType.Comic);
|
||||
}
|
||||
|
||||
// Pdfs may have .pdf in the series name, remove that
|
||||
if (Services.Tasks.Scanner.Parser.Parser.IsPdf(filePath) && ret.Series.ToLower().EndsWith(".pdf"))
|
||||
if (Parser.IsPdf(filePath) && ret.Series.ToLower().EndsWith(".pdf"))
|
||||
{
|
||||
ret.Series = ret.Series.Substring(0, ret.Series.Length - ".pdf".Length);
|
||||
}
|
||||
|
@ -127,35 +127,55 @@ public class DefaultParser : IDefaultParser
|
|||
/// <param name="ret">Expects a non-null ParserInfo which this method will populate</param>
|
||||
public void ParseFromFallbackFolders(string filePath, string rootPath, LibraryType type, ref ParserInfo ret)
|
||||
{
|
||||
var fallbackFolders = _directoryService.GetFoldersTillRoot(rootPath, filePath).ToList();
|
||||
var fallbackFolders = _directoryService.GetFoldersTillRoot(rootPath, filePath)
|
||||
.Where(f => !Parser.IsMangaSpecial(f))
|
||||
.ToList();
|
||||
|
||||
if (fallbackFolders.Count == 0)
|
||||
{
|
||||
var rootFolderName = _directoryService.FileSystem.DirectoryInfo.FromDirectoryName(rootPath).Name;
|
||||
var series = Parser.ParseSeries(rootFolderName);
|
||||
|
||||
if (string.IsNullOrEmpty(series))
|
||||
{
|
||||
ret.Series = Parser.CleanTitle(rootFolderName, type is LibraryType.Comic);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(series) && (string.IsNullOrEmpty(ret.Series) || !rootFolderName.Contains(ret.Series)))
|
||||
{
|
||||
ret.Series = series;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < fallbackFolders.Count; i++)
|
||||
{
|
||||
var folder = fallbackFolders[i];
|
||||
if (Services.Tasks.Scanner.Parser.Parser.IsMangaSpecial(folder)) continue;
|
||||
|
||||
var parsedVolume = type is LibraryType.Manga ? Services.Tasks.Scanner.Parser.Parser.ParseVolume(folder) : Services.Tasks.Scanner.Parser.Parser.ParseComicVolume(folder);
|
||||
var parsedChapter = type is LibraryType.Manga ? Services.Tasks.Scanner.Parser.Parser.ParseChapter(folder) : Services.Tasks.Scanner.Parser.Parser.ParseComicChapter(folder);
|
||||
var parsedVolume = type is LibraryType.Manga ? Parser.ParseVolume(folder) : Parser.ParseComicVolume(folder);
|
||||
var parsedChapter = type is LibraryType.Manga ? Parser.ParseChapter(folder) : Parser.ParseComicChapter(folder);
|
||||
|
||||
if (!parsedVolume.Equals(Services.Tasks.Scanner.Parser.Parser.DefaultVolume) || !parsedChapter.Equals(Services.Tasks.Scanner.Parser.Parser.DefaultChapter))
|
||||
if (!parsedVolume.Equals(Parser.DefaultVolume) || !parsedChapter.Equals(Parser.DefaultChapter))
|
||||
{
|
||||
if ((string.IsNullOrEmpty(ret.Volumes) || ret.Volumes.Equals(Services.Tasks.Scanner.Parser.Parser.DefaultVolume)) && !parsedVolume.Equals(Services.Tasks.Scanner.Parser.Parser.DefaultVolume))
|
||||
{
|
||||
ret.Volumes = parsedVolume;
|
||||
}
|
||||
if ((string.IsNullOrEmpty(ret.Chapters) || ret.Chapters.Equals(Services.Tasks.Scanner.Parser.Parser.DefaultChapter)) && !parsedChapter.Equals(Services.Tasks.Scanner.Parser.Parser.DefaultChapter))
|
||||
{
|
||||
ret.Chapters = parsedChapter;
|
||||
}
|
||||
if ((string.IsNullOrEmpty(ret.Volumes) || ret.Volumes.Equals(Parser.DefaultVolume)) && !parsedVolume.Equals(Parser.DefaultVolume))
|
||||
{
|
||||
ret.Volumes = parsedVolume;
|
||||
}
|
||||
if ((string.IsNullOrEmpty(ret.Chapters) || ret.Chapters.Equals(Parser.DefaultChapter)) && !parsedChapter.Equals(Parser.DefaultChapter))
|
||||
{
|
||||
ret.Chapters = parsedChapter;
|
||||
}
|
||||
}
|
||||
|
||||
// Generally users group in series folders. Let's try to parse series from the top folder
|
||||
if (!folder.Equals(ret.Series) && i == fallbackFolders.Count - 1)
|
||||
{
|
||||
var series = Services.Tasks.Scanner.Parser.Parser.ParseSeries(folder);
|
||||
var series = Parser.ParseSeries(folder);
|
||||
|
||||
if (string.IsNullOrEmpty(series))
|
||||
{
|
||||
ret.Series = Services.Tasks.Scanner.Parser.Parser.CleanTitle(folder, type is LibraryType.Comic);
|
||||
ret.Series = Parser.CleanTitle(folder, type is LibraryType.Comic);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -633,13 +633,11 @@ public class ProcessSeries : IProcessSeries
|
|||
|
||||
void AddGenre(Genre genre)
|
||||
{
|
||||
//chapter.Genres.Add(genre);
|
||||
GenreHelper.AddGenreIfNotExists(chapter.Genres, genre);
|
||||
}
|
||||
|
||||
void AddTag(Tag tag, bool added)
|
||||
{
|
||||
//chapter.Tags.Add(tag);
|
||||
TagHelper.AddTagIfNotExists(chapter.Tags, tag);
|
||||
}
|
||||
|
||||
|
|
|
@ -206,8 +206,6 @@ public class ScannerService : IScannerService
|
|||
var scanElapsedTime = await ScanFiles(library, new []{folderPath}, false, TrackFiles, true);
|
||||
_logger.LogInformation("ScanFiles for {Series} took {Time}", series.Name, scanElapsedTime);
|
||||
|
||||
//await Task.WhenAll(processTasks);
|
||||
|
||||
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress, MessageFactory.LibraryScanProgressEvent(library.Name, ProgressEventType.Ended, series.Name));
|
||||
|
||||
// Remove any parsedSeries keys that don't belong to our series. This can occur when users store 2 series in the same folder
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue