On Deck tweaks + Bugfixes (#1141)

* Tweaked the On deck to only look for series that have progress in past 30 days. This number is just to test it out, it will be configurable later. Tweaked the layout of the dashboard to remove a redundant section.

* Fixed a bug where archives with __MACOSX/ inside would break the reader during flattening.

* Fixed a bug where confirm service rejection should have resolved as false.

* Fixed an issue with checking if server is accessible with loopback and local ips
This commit is contained in:
Joseph Milazzo 2022-03-08 17:33:58 -06:00 committed by GitHub
parent 5aa40142c4
commit b921a14e12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 81 additions and 16 deletions

View File

@ -208,6 +208,9 @@ namespace API.Tests.Parser
[InlineData("MACOSX/Love Hina/", false)]
[InlineData("._Love Hina/Love Hina/", true)]
[InlineData("@Recently-Snapshot/Love Hina/", true)]
[InlineData("@recycle/Love Hina/", true)]
[InlineData("@recycle/Love Hina/", true)]
[InlineData("E:/Test/__MACOSX/Love Hina/", true)]
public void HasBlacklistedFolderInPathTest(string inputPath, bool expected)
{
Assert.Equal(expected, HasBlacklistedFolderInPath(inputPath));

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Abstractions.TestingHelpers;
@ -755,6 +755,22 @@ namespace API.Tests.Services
Assert.True(fileSystem.Directory.Exists($"{testDirectory}subdir/"));
}
[Fact]
public void Flatten_ShouldFlatten_WithoutMacosx()
{
const string testDirectory = "/manga/";
var fileSystem = new MockFileSystem();
fileSystem.AddDirectory(testDirectory);
fileSystem.AddFile($"{testDirectory}data-1.jpg", new MockFileData("abc"));
fileSystem.AddFile($"{testDirectory}subdir/data-3.webp", new MockFileData("abc"));
fileSystem.AddFile($"{testDirectory}__MACOSX/data-4.webp", new MockFileData("abc"));
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), fileSystem);
ds.Flatten($"{testDirectory}");
Assert.Equal(2, ds.GetFiles(testDirectory).Count());
Assert.False(fileSystem.FileExists($"{testDirectory}data-4.webp"));
}
#endregion
#region CheckWriteAccess

View File

@ -597,7 +597,7 @@ public class SeriesRepository : ISeriesRepository
{
//var allSeriesWithProgress = await _context.AppUserProgresses.Select(p => p.SeriesId).ToListAsync();
//var allChapters = await GetChapterIdsForSeriesAsync(allSeriesWithProgress);
var cuttoffProgressPoint = DateTime.Now - TimeSpan.FromDays(30);
var query = (await CreateFilteredSearchQueryable(userId, libraryId, filter))
.Join(_context.AppUserProgresses, s => s.Id, progress => progress.SeriesId, (s, progress) =>
new
@ -606,12 +606,14 @@ public class SeriesRepository : ISeriesRepository
PagesRead = _context.AppUserProgresses.Where(s1 => s1.SeriesId == s.Id && s1.AppUserId == userId)
.Sum(s1 => s1.PagesRead),
progress.AppUserId,
LastReadingProgress = _context.AppUserProgresses.Where(p => p.Id == progress.Id && p.AppUserId == userId)
LastReadingProgress = _context.AppUserProgresses
.Where(p => p.Id == progress.Id && p.AppUserId == userId)
.Max(p => p.LastModified),
// This is only taking into account chapters that have progress on them, not all chapters in said series
LastChapterCreated = _context.Chapter.Where(c => progress.ChapterId == c.Id).Max(c => c.Created)
//LastChapterCreated = _context.Chapter.Where(c => allChapters.Contains(c.Id)).Max(c => c.Created)
});
})
.Where(d => d.LastReadingProgress >= cuttoffProgressPoint);
// I think I need another Join statement. The problem is the chapters are still limited to progress

View File

@ -1003,7 +1003,7 @@ namespace API.Parser
public static bool HasBlacklistedFolderInPath(string path)
{
return path.Contains("__MACOSX") || path.StartsWith("@Recently-Snapshot") || path.StartsWith("._");
return path.Contains("__MACOSX") || path.StartsWith("@Recently-Snapshot") || path.StartsWith("@recycle") || path.StartsWith("._");
}

View File

@ -703,7 +703,7 @@ namespace API.Services
}
private void FlattenDirectory(IDirectoryInfo root, IDirectoryInfo directory, ref int directoryIndex)
private static void FlattenDirectory(IFileSystemInfo root, IDirectoryInfo directory, ref int directoryIndex)
{
if (!root.FullName.Equals(directory.FullName))
{
@ -725,6 +725,9 @@ namespace API.Services
foreach (var subDirectory in directory.EnumerateDirectories().OrderByNatural(d => d.FullName))
{
// We need to check if the directory is not a blacklisted (ie __MACOSX)
if (Parser.Parser.HasBlacklistedFolderInPath(subDirectory.FullName)) continue;
FlattenDirectory(root, subDirectory, ref directoryIndex);
}
}

View File

@ -1,4 +1,6 @@
using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using API.Data;
using API.DTOs.Email;
@ -40,14 +42,22 @@ public class EmailService : IEmailService
cli.Settings.HttpClientFactory = new UntrustedCertClientFactory());
}
/// <summary>
/// Test if this instance is accessible outside the network
/// </summary>
/// <remarks>This will do some basic filtering to auto return false if the emailUrl is a LAN ip</remarks>
/// <param name="emailUrl"></param>
/// <returns></returns>
public async Task<EmailTestResultDto> TestConnectivity(string emailUrl)
{
// FlurlHttp.ConfigureClient(emailUrl, cli =>
// cli.Settings.HttpClientFactory = new UntrustedCertClientFactory());
var result = new EmailTestResultDto();
try
{
if (IsLocalIpAddress(emailUrl))
{
result.Successful = false;
result.ErrorMessage = "This is a local IP address";
}
result.Successful = await SendEmailWithGet(emailUrl + "/api/email/test");
}
catch (KavitaException ex)
@ -72,6 +82,7 @@ public class EmailService : IEmailService
public async Task<bool> CheckIfAccessible(string host)
{
// This is the only exception for using the default because we need an external service to check if the server is accessible for emails
if (IsLocalIpAddress(host)) return false;
return await SendEmailWithGet(DefaultApiUrl + "/api/email/reachable?host=" + host);
}
@ -138,4 +149,34 @@ public class EmailService : IEmailService
return true;
}
private static bool IsLocalIpAddress(string url)
{
var host = url.Split(':')[0];
try
{
// get host IP addresses
var hostIPs = Dns.GetHostAddresses(host);
// get local IP addresses
var localIPs = Dns.GetHostAddresses(Dns.GetHostName());
// test if any host IP equals to any local IP or to localhost
foreach (var hostIp in hostIPs)
{
// is localhost
if (IPAddress.IsLoopback(hostIp)) return true;
// is local address
if (localIPs.Contains(hostIp))
{
return true;
}
}
}
catch
{
// ignored
}
return false;
}
}

View File

@ -12,25 +12,25 @@
</app-carousel-reel>
<!-- TODO: Refactor this so we can use series actions here -->
<app-carousel-reel [items]="recentlyUpdatedSeries" title="Recently Updated Series">
<app-carousel-reel [items]="recentlyUpdatedSeries" title="Recently Updated Series" (sectionClick)="handleSectionClick($event)">
<ng-template #carouselItem let-item let-position="idx">
<app-card-item [entity]="item" [title]="item.seriesName" [imageUrl]="imageService.getSeriesCoverImage(item.seriesId)"
[supressArchiveWarning]="true" (clicked)="handleRecentlyAddedChapterClick(item)" [count]="item.count"></app-card-item>
</ng-template>
</app-carousel-reel>
<app-carousel-reel [items]="recentlyAddedSeries" title="Recently Added Series" (sectionClick)="handleSectionClick($event)">
<app-carousel-reel [items]="recentlyAddedSeries" title="Newly Added Series">
<ng-template #carouselItem let-item let-position="idx">
<app-series-card [data]="item" [libraryId]="item.libraryId" (dataChanged)="loadRecentlyAddedSeries()"></app-series-card>
</ng-template>
</app-carousel-reel>
<app-carousel-reel [items]="recentlyAddedChapters" title="Recently Added" (sectionClick)="handleSectionClick($event)">
<!-- <app-carousel-reel [items]="recentlyAddedChapters" title="Recently Added" (sectionClick)="handleSectionClick($event)">
<ng-template #carouselItem let-item let-position="idx">
<app-card-item [entity]="item" [title]="item.title" [subtitle]="item.seriesName" [imageUrl]="imageService.getRecentlyAddedItem(item)"
[supressArchiveWarning]="true" (clicked)="handleRecentlyAddedChapterClick(item)"></app-card-item>
</ng-template>
</app-carousel-reel>
</app-carousel-reel> -->
<app-carousel-reel [items]="libraries" title="Libraries" (sectionClick)="handleSectionClick($event)">
<ng-template #carouselItem let-item let-position="idx">

View File

@ -138,7 +138,7 @@ export class LibraryComponent implements OnInit, OnDestroy {
handleSectionClick(sectionTitle: string) {
if (sectionTitle.toLowerCase() === 'collections') {
this.router.navigate(['collections']);
} else if (sectionTitle.toLowerCase() === 'recently added') {
} else if (sectionTitle.toLowerCase() === 'recently updated series') {
this.router.navigate(['recently-added']);
} else if (sectionTitle.toLowerCase() === 'on deck') {
this.router.navigate(['on-deck']);

View File

@ -41,7 +41,7 @@ export class ConfirmService {
return resolve(result);
});
modalRef.dismissed.pipe(take(1)).subscribe(() => {
return reject(false);
return resolve(false);
});
});
@ -65,7 +65,7 @@ export class ConfirmService {
return resolve(result);
});
modalRef.dismissed.pipe(take(1)).subscribe(() => {
return reject(false);
return resolve(false);
});
})
}