mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-06-03 05:34:21 -04:00
Feature/image rework cleanup (#589)
* Added volume migrations. Added parser case for "Chapter 63 - The Promise Made for 520 Cenz.cbr" * Added some info statements for when full library scans occur. For image apis, return the name of the file to aid in caching. * When managing users, show the current logged in user at the top of the list. Added a message when no libraries have been setup but you are trying to add a user to a library.
This commit is contained in:
parent
fb5866133a
commit
a872165747
@ -237,6 +237,7 @@ namespace API.Tests.Parser
|
|||||||
[InlineData("Hentai Ouji to Warawanai Neko. - Vol. 06 Ch. 034.5", "34.5")]
|
[InlineData("Hentai Ouji to Warawanai Neko. - Vol. 06 Ch. 034.5", "34.5")]
|
||||||
[InlineData("Kimi no Koto ga Daidaidaidaidaisuki na 100-nin no Kanojo Chapter 1-10", "1-10")]
|
[InlineData("Kimi no Koto ga Daidaidaidaidaisuki na 100-nin no Kanojo Chapter 1-10", "1-10")]
|
||||||
[InlineData("Deku_&_Bakugo_-_Rising_v1_c1.1.cbz", "1.1")]
|
[InlineData("Deku_&_Bakugo_-_Rising_v1_c1.1.cbz", "1.1")]
|
||||||
|
[InlineData("Chapter 63 - The Promise Made for 520 Cenz.cbr", "63")]
|
||||||
public void ParseChaptersTest(string filename, string expected)
|
public void ParseChaptersTest(string filename, string expected)
|
||||||
{
|
{
|
||||||
Assert.Equal(expected, API.Parser.Parser.ParseChapter(filename));
|
Assert.Equal(expected, API.Parser.Parser.ParseChapter(filename));
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using System.IO;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using API.Extensions;
|
using API.Extensions;
|
||||||
using API.Interfaces;
|
using API.Interfaces;
|
||||||
@ -34,7 +36,7 @@ namespace API.Controllers
|
|||||||
var format = Path.GetExtension(path).Replace(".", "");
|
var format = Path.GetExtension(path).Replace(".", "");
|
||||||
|
|
||||||
Response.AddCacheHeader(path);
|
Response.AddCacheHeader(path);
|
||||||
return PhysicalFile(path, "image/" + format);
|
return PhysicalFile(path, "image/" + format, Path.GetFileName(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -50,7 +52,7 @@ namespace API.Controllers
|
|||||||
var format = Path.GetExtension(path).Replace(".", "");
|
var format = Path.GetExtension(path).Replace(".", "");
|
||||||
|
|
||||||
Response.AddCacheHeader(path);
|
Response.AddCacheHeader(path);
|
||||||
return PhysicalFile(path, "image/" + format);
|
return PhysicalFile(path, "image/" + format, Path.GetFileName(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -66,7 +68,7 @@ namespace API.Controllers
|
|||||||
var format = Path.GetExtension(path).Replace(".", "");
|
var format = Path.GetExtension(path).Replace(".", "");
|
||||||
|
|
||||||
Response.AddCacheHeader(path);
|
Response.AddCacheHeader(path);
|
||||||
return PhysicalFile(path, "image/" + format);
|
return PhysicalFile(path, "image/" + format, Path.GetFileName(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -82,7 +84,7 @@ namespace API.Controllers
|
|||||||
var format = Path.GetExtension(path).Replace(".", "");
|
var format = Path.GetExtension(path).Replace(".", "");
|
||||||
|
|
||||||
Response.AddCacheHeader(path);
|
Response.AddCacheHeader(path);
|
||||||
return PhysicalFile(path, "image/" + format);
|
return PhysicalFile(path, "image/" + format, Path.GetFileName(path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ namespace API.Controllers
|
|||||||
if (string.IsNullOrEmpty(path) || !System.IO.File.Exists(path)) return BadRequest($"No such image for page {page}");
|
if (string.IsNullOrEmpty(path) || !System.IO.File.Exists(path)) return BadRequest($"No such image for page {page}");
|
||||||
var format = Path.GetExtension(path).Replace(".", "");
|
var format = Path.GetExtension(path).Replace(".", "");
|
||||||
|
|
||||||
return PhysicalFile(path, "image/" + format);
|
return PhysicalFile(path, "image/" + format, Path.GetFileName(path));
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using API.Comparators;
|
||||||
using API.Helpers;
|
using API.Helpers;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@ -23,6 +24,8 @@ namespace API.Data
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class MigrateCoverImages
|
public static class MigrateCoverImages
|
||||||
{
|
{
|
||||||
|
private static readonly ChapterSortComparerZeroFirst ChapterSortComparerForInChapterSorting = new ();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Run first. Will extract byte[]s from DB and write them to the cover directory.
|
/// Run first. Will extract byte[]s from DB and write them to the cover directory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -140,6 +143,22 @@ namespace API.Data
|
|||||||
|
|
||||||
await context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
|
Console.WriteLine("Updating Volume entities");
|
||||||
|
var volumes = await context.Volume.Include(v => v.Chapters).ToListAsync();
|
||||||
|
foreach (var volume in volumes)
|
||||||
|
{
|
||||||
|
var firstChapter = volume.Chapters.OrderBy(x => double.Parse(x.Number), ChapterSortComparerForInChapterSorting).FirstOrDefault();
|
||||||
|
if (firstChapter == null) continue;
|
||||||
|
if (File.Exists(Path.Join(DirectoryService.CoverImageDirectory,
|
||||||
|
$"{ImageService.GetChapterFormat(firstChapter.Id, firstChapter.VolumeId)}.png")))
|
||||||
|
{
|
||||||
|
volume.CoverImage = $"{ImageService.GetChapterFormat(firstChapter.Id, firstChapter.VolumeId)}.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
Console.WriteLine("Updating Collection Tag entities");
|
Console.WriteLine("Updating Collection Tag entities");
|
||||||
var tags = await context.CollectionTag.ToListAsync();
|
var tags = await context.CollectionTag.ToListAsync();
|
||||||
foreach (var tag in tags)
|
foreach (var tag in tags)
|
||||||
@ -153,6 +172,7 @@ namespace API.Data
|
|||||||
}
|
}
|
||||||
|
|
||||||
await context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
Console.WriteLine("Cover Image Migration completed");
|
Console.WriteLine("Cover Image Migration completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,7 +448,7 @@ namespace API.Parser
|
|||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
// Hinowa ga CRUSH! 018 (2019) (Digital) (LuCaZ).cbz, Hinowa ga CRUSH! 018.5 (2019) (Digital) (LuCaZ).cbz
|
// Hinowa ga CRUSH! 018 (2019) (Digital) (LuCaZ).cbz, Hinowa ga CRUSH! 018.5 (2019) (Digital) (LuCaZ).cbz
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?!Vol)(?<Series>.*)\s(?<!vol\. )(?<Chapter>\d+(?:.\d+|-\d+)?)(?:\s\(\d{4}\))?(\b|_|-)",
|
@"^(?!Vol)(?<Series>.+?)\s(?<!vol\. )(?<Chapter>\d+(?:.\d+|-\d+)?)(?:\s\(\d{4}\))?(\b|_|-)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
// Tower Of God S01 014 (CBT) (digital).cbz
|
// Tower Of God S01 014 (CBT) (digital).cbz
|
||||||
|
@ -128,12 +128,13 @@ namespace API.Services.Tasks
|
|||||||
[AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
|
[AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
|
||||||
public async Task ScanLibraries()
|
public async Task ScanLibraries()
|
||||||
{
|
{
|
||||||
|
_logger.LogInformation("Starting Scan of All Libraries");
|
||||||
var libraries = await _unitOfWork.LibraryRepository.GetLibrariesAsync();
|
var libraries = await _unitOfWork.LibraryRepository.GetLibrariesAsync();
|
||||||
foreach (var lib in libraries)
|
foreach (var lib in libraries)
|
||||||
{
|
{
|
||||||
await ScanLibrary(lib.Id, false);
|
await ScanLibrary(lib.Id, false);
|
||||||
}
|
}
|
||||||
|
_logger.LogInformation("Scan of All Libraries Finished");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
<label attr.for="library-{{i}}" class="form-check-label">{{library.data.name}}</label>
|
<label attr.for="library-{{i}}" class="form-check-label">{{library.data.name}}</label>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="list-group-item" *ngIf="selectedLibraries.length === 0">
|
||||||
|
There are no libraries setup yet.
|
||||||
|
</li>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<li *ngFor="let member of members; let idx = index;" class="list-group-item">
|
<li *ngFor="let member of members; let idx = index;" class="list-group-item">
|
||||||
<div>
|
<div>
|
||||||
<h4>
|
<h4>
|
||||||
<i class="presence fa fa-circle" title="Active" aria-hidden="true" *ngIf="false && (presence.onlineUsers$ | async)?.includes(member.username)"></i><span id="member-name--{{idx}}">{{member.username | titlecase}} </span><span *ngIf="member.isAdmin" class="badge badge-pill badge-secondary">Admin</span>
|
<i class="presence fa fa-circle" title="Active" aria-hidden="true" *ngIf="false && (presence.onlineUsers$ | async)?.includes(member.username)"></i><span id="member-name--{{idx}}">{{member.username | titlecase}} </span><span *ngIf="member.username === loggedInUsername">(You)</span>
|
||||||
<div class="float-right" *ngIf="canEditMember(member)">
|
<div class="float-right" *ngIf="canEditMember(member)">
|
||||||
<button class="btn btn-danger mr-2" (click)="deleteUser(member)" placement="top" ngbTooltip="Delete User" attr.aria-label="Delete User {{member.username | titlecase}}"><i class="fa fa-trash" aria-hidden="true"></i></button>
|
<button class="btn btn-danger mr-2" (click)="deleteUser(member)" placement="top" ngbTooltip="Delete User" attr.aria-label="Delete User {{member.username | titlecase}}"><i class="fa fa-trash" aria-hidden="true"></i></button>
|
||||||
<button class="btn btn-secondary mr-2" (click)="updatePassword(member)" placement="top" ngbTooltip="Change Password" attr.aria-label="Change Password for {{member.username | titlecase}}"><i class="fa fa-key" aria-hidden="true"></i></button>
|
<button class="btn btn-secondary mr-2" (click)="updatePassword(member)" placement="top" ngbTooltip="Change Password" attr.aria-label="Change Password for {{member.username | titlecase}}"><i class="fa fa-key" aria-hidden="true"></i></button>
|
||||||
|
@ -53,7 +53,18 @@ export class ManageUsersComponent implements OnInit, OnDestroy {
|
|||||||
loadMembers() {
|
loadMembers() {
|
||||||
this.loadingMembers = true;
|
this.loadingMembers = true;
|
||||||
this.memberService.getMembers().subscribe(members => {
|
this.memberService.getMembers().subscribe(members => {
|
||||||
this.members = members.filter(member => member.username !== this.loggedInUsername);
|
this.members = members;
|
||||||
|
// Show logged in user at the top of the list
|
||||||
|
this.members.sort((a: Member, b: Member) => {
|
||||||
|
if (a.username === this.loggedInUsername) return 1;
|
||||||
|
if (b.username === this.loggedInUsername) return 1;
|
||||||
|
|
||||||
|
const nameA = a.username.toUpperCase();
|
||||||
|
const nameB = b.username.toUpperCase();
|
||||||
|
if (nameA < nameB) return -1;
|
||||||
|
if (nameA > nameB) return 1;
|
||||||
|
return 0;
|
||||||
|
})
|
||||||
this.loadingMembers = false;
|
this.loadingMembers = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user