diff --git a/API.Tests/Services/SeriesServiceTests.cs b/API.Tests/Services/SeriesServiceTests.cs
index 1ab48ed3e..1af97deb2 100644
--- a/API.Tests/Services/SeriesServiceTests.cs
+++ b/API.Tests/Services/SeriesServiceTests.cs
@@ -343,7 +343,7 @@ public class SeriesServiceTests : AbstractDbTest
Assert.True(result);
- var ratings = (await _unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Ratings))
+ var ratings = (await _unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Ratings))!
.Ratings;
Assert.NotEmpty(ratings);
Assert.Equal(3, ratings.First().Rating);
@@ -780,7 +780,7 @@ public class SeriesServiceTests : AbstractDbTest
{
var series = CreateSeriesMock();
- var firstChapter = SeriesService.GetFirstChapterForMetadata(series, true);
+ var firstChapter = SeriesService.GetFirstChapterForMetadata(series);
Assert.Same("1", firstChapter.Range);
}
@@ -789,7 +789,7 @@ public class SeriesServiceTests : AbstractDbTest
{
var series = CreateSeriesMock();
- var firstChapter = SeriesService.GetFirstChapterForMetadata(series, false);
+ var firstChapter = SeriesService.GetFirstChapterForMetadata(series);
Assert.Same("1", firstChapter.Range);
}
@@ -808,10 +808,35 @@ public class SeriesServiceTests : AbstractDbTest
new ChapterBuilder("1.2").WithFiles(files).WithPages(1).Build(),
};
- var firstChapter = SeriesService.GetFirstChapterForMetadata(series, false);
+ var firstChapter = SeriesService.GetFirstChapterForMetadata(series);
Assert.Same("1.1", firstChapter.Range);
}
+ [Fact]
+ public void GetFirstChapterForMetadata_NonBook_ShouldReturnChapter1_WhenFirstVolumeIs3()
+ {
+ var file = new MangaFileBuilder("Test.cbz", MangaFormat.Archive, 1).Build();
+
+ var series = new SeriesBuilder("Test")
+ .WithVolume(new VolumeBuilder("0")
+ .WithChapter(new ChapterBuilder("1").WithPages(1).WithFile(file).Build())
+ .WithChapter(new ChapterBuilder("2").WithPages(1).WithFile(file).Build())
+ .Build())
+ .WithVolume(new VolumeBuilder("2")
+ .WithChapter(new ChapterBuilder("21").WithPages(1).WithFile(file).Build())
+ .WithChapter(new ChapterBuilder("22").WithPages(1).WithFile(file).Build())
+ .Build())
+ .WithVolume(new VolumeBuilder("3")
+ .WithChapter(new ChapterBuilder("31").WithPages(1).WithFile(file).Build())
+ .WithChapter(new ChapterBuilder("32").WithPages(1).WithFile(file).Build())
+ .Build())
+ .Build();
+ series.Library = new LibraryBuilder("Test LIb", LibraryType.Book).Build();
+
+ var firstChapter = SeriesService.GetFirstChapterForMetadata(series);
+ Assert.Same("1", firstChapter.Range);
+ }
+
#endregion
#region SeriesRelation
diff --git a/API/Controllers/LibraryController.cs b/API/Controllers/LibraryController.cs
index e20349b49..ae569f272 100644
--- a/API/Controllers/LibraryController.cs
+++ b/API/Controllers/LibraryController.cs
@@ -50,22 +50,28 @@ public class LibraryController : BaseApiController
///
/// Creates a new Library. Upon library creation, adds new library to all Admin accounts.
///
- ///
+ ///
///
[Authorize(Policy = "RequireAdminRole")]
[HttpPost("create")]
- public async Task AddLibrary(CreateLibraryDto createLibraryDto)
+ public async Task AddLibrary(UpdateLibraryDto dto)
{
- if (await _unitOfWork.LibraryRepository.LibraryExists(createLibraryDto.Name))
+ if (await _unitOfWork.LibraryRepository.LibraryExists(dto.Name))
{
return BadRequest("Library name already exists. Please choose a unique name to the server.");
}
var library = new Library
{
- Name = createLibraryDto.Name,
- Type = createLibraryDto.Type,
- Folders = createLibraryDto.Folders.Select(x => new FolderPath {Path = x}).ToList()
+ Name = dto.Name,
+ Type = dto.Type,
+ Folders = dto.Folders.Select(x => new FolderPath {Path = x}).Distinct().ToList(),
+ FolderWatching = dto.FolderWatching,
+ IncludeInDashboard = dto.IncludeInDashboard,
+ IncludeInRecommended = dto.IncludeInRecommended,
+ IncludeInSearch = dto.IncludeInSearch,
+ ManageCollections = dto.ManageCollections,
+ ManageReadingLists = dto.ManageReadingLists,
};
_unitOfWork.LibraryRepository.Add(library);
@@ -359,7 +365,7 @@ public class LibraryController : BaseApiController
var originalFolders = library.Folders.Select(x => x.Path).ToList();
library.Name = newName;
- library.Folders = dto.Folders.Select(s => new FolderPath() {Path = s}).ToList();
+ library.Folders = dto.Folders.Select(s => new FolderPath() {Path = s}).Distinct().ToList();
var typeUpdate = library.Type != dto.Type;
var folderWatchingUpdate = library.FolderWatching != dto.FolderWatching;
diff --git a/API/Services/ReadingListService.cs b/API/Services/ReadingListService.cs
index b987be1a1..fccb3d2cd 100644
--- a/API/Services/ReadingListService.cs
+++ b/API/Services/ReadingListService.cs
@@ -157,19 +157,19 @@ public class ReadingListService : IReadingListService
readingList.CoverImageLocked = dto.CoverImageLocked;
- if (NumberHelper.IsValidMonth(dto.StartingMonth))
+ if (NumberHelper.IsValidMonth(dto.StartingMonth) || dto.StartingMonth == 0)
{
readingList.StartingMonth = dto.StartingMonth;
}
- if (NumberHelper.IsValidYear(dto.StartingYear))
+ if (NumberHelper.IsValidYear(dto.StartingYear) || dto.StartingYear == 0)
{
readingList.StartingYear = dto.StartingYear;
}
- if (NumberHelper.IsValidMonth(dto.EndingMonth))
+ if (NumberHelper.IsValidMonth(dto.EndingMonth) || dto.EndingMonth == 0)
{
readingList.EndingMonth = dto.EndingMonth;
}
- if (NumberHelper.IsValidYear(dto.EndingYear))
+ if (NumberHelper.IsValidYear(dto.EndingYear) || dto.EndingYear == 0)
{
readingList.EndingYear = dto.EndingYear;
}
diff --git a/API/Services/SeriesService.cs b/API/Services/SeriesService.cs
index 5be0ebfd4..f8616e651 100644
--- a/API/Services/SeriesService.cs
+++ b/API/Services/SeriesService.cs
@@ -48,14 +48,25 @@ public class SeriesService : ISeriesService
///
/// Returns the first chapter for a series to extract metadata from (ie Summary, etc)
///
- ///
- ///
+ /// The full series with all volumes and chapters on it
///
- public static Chapter? GetFirstChapterForMetadata(Series series, bool isBookLibrary)
+ public static Chapter? GetFirstChapterForMetadata(Series series)
{
- return series.Volumes.OrderBy(v => v.Number, ChapterSortComparer.Default)
+ var sortedVolumes = series.Volumes.OrderBy(v => v.Number, ChapterSortComparer.Default);
+ var minVolumeNumber = sortedVolumes
+ .Where(v => v.Number != 0)
+ .MinBy(v => v.Number);
+
+ var minChapter = series.Volumes
.SelectMany(v => v.Chapters.OrderBy(c => float.Parse(c.Number), ChapterSortComparer.Default))
.FirstOrDefault();
+
+ if (minVolumeNumber != null && minChapter != null && float.Parse(minChapter.Number) > minVolumeNumber.Number)
+ {
+ return minVolumeNumber.Chapters.MinBy(c => float.Parse(c.Number), ChapterSortComparer.Default);
+ }
+
+ return minChapter;
}
public async Task UpdateSeriesMetadata(UpdateSeriesMetadataDto updateSeriesMetadataDto)
diff --git a/API/Services/Tasks/Scanner/ProcessSeries.cs b/API/Services/Tasks/Scanner/ProcessSeries.cs
index bedd27877..710d40a72 100644
--- a/API/Services/Tasks/Scanner/ProcessSeries.cs
+++ b/API/Services/Tasks/Scanner/ProcessSeries.cs
@@ -266,8 +266,7 @@ public class ProcessSeries : IProcessSeries
public void UpdateSeriesMetadata(Series series, Library library)
{
series.Metadata ??= new SeriesMetadataBuilder().Build();
- var isBook = library.Type == LibraryType.Book;
- var firstChapter = SeriesService.GetFirstChapterForMetadata(series, isBook);
+ var firstChapter = SeriesService.GetFirstChapterForMetadata(series);
var firstFile = firstChapter?.Files.FirstOrDefault();
if (firstFile == null) return;
diff --git a/UI/Web/src/_manga-reader-common.scss b/UI/Web/src/_manga-reader-common.scss
index a7dfb4f19..123b925a6 100644
--- a/UI/Web/src/_manga-reader-common.scss
+++ b/UI/Web/src/_manga-reader-common.scss
@@ -15,7 +15,7 @@ img {
&.full-height {
height: calc(100vh); // We need to - $scrollbarHeight when there is a horizontal scroll on macos
- display: flex;
+ display: flex;
align-content: center;
}
@@ -42,13 +42,13 @@ img {
}
.full-width {
- width: 100%;
margin: 0 auto;
vertical-align: top;
max-width: fit-content;
}
.fit-to-screen.full-width {
+ width: 100%;
max-height: calc(var(--vh)*100);
}
}
diff --git a/UI/Web/src/app/reading-list/_components/reading-list-detail/reading-list-detail.component.ts b/UI/Web/src/app/reading-list/_components/reading-list-detail/reading-list-detail.component.ts
index 7e50fcf1a..1c42011b2 100644
--- a/UI/Web/src/app/reading-list/_components/reading-list-detail/reading-list-detail.component.ts
+++ b/UI/Web/src/app/reading-list/_components/reading-list-detail/reading-list-detail.component.ts
@@ -49,7 +49,7 @@ export class ReadingListDetailComponent implements OnInit {
constructor(private route: ActivatedRoute, private router: Router, private readingListService: ReadingListService,
private actionService: ActionService, private actionFactoryService: ActionFactoryService, public utilityService: UtilityService,
- public imageService: ImageService, private accountService: AccountService, private toastr: ToastrService,
+ public imageService: ImageService, private accountService: AccountService, private toastr: ToastrService,
private confirmService: ConfirmService, private libraryService: LibraryService, private readerService: ReaderService,
private readonly cdRef: ChangeDetectorRef) {}
@@ -65,7 +65,7 @@ export class ReadingListDetailComponent implements OnInit {
this.readingListImage = this.imageService.randomize(this.imageService.getReadingListCoverImage(this.listId));
forkJoin([
- this.libraryService.getLibraries(),
+ this.libraryService.getLibraries(),
this.readingListService.getReadingList(this.listId)
]).subscribe(results => {
const libraries = results[0];
@@ -90,7 +90,7 @@ export class ReadingListDetailComponent implements OnInit {
if (user) {
this.isAdmin = this.accountService.hasAdminRole(user);
this.hasDownloadingRole = this.accountService.hasDownloadRole(user);
-
+
this.actions = this.actionFactoryService.getReadingListActions(this.handleReadingListActionCallback.bind(this))
.filter(action => this.readingListService.actionListFilter(action, readingList, this.isAdmin));
this.cdRef.markForCheck();
@@ -123,18 +123,20 @@ export class ReadingListDetailComponent implements OnInit {
this.router.navigate(this.readerService.getNavigationArray(item.libraryId, item.seriesId, item.chapterId, item.seriesFormat), {queryParams: params});
}
- handleReadingListActionCallback(action: ActionItem, readingList: ReadingList) {
+ async handleReadingListActionCallback(action: ActionItem, readingList: ReadingList) {
switch(action.action) {
case Action.Delete:
- this.deleteList(readingList);
+ await this.deleteList(readingList);
break;
case Action.Edit:
this.actionService.editReadingList(readingList, (readingList: ReadingList) => {
// Reload information around list
- this.readingList = readingList;
- this.readingListSummary = (this.readingList.summary === null ? '' : this.readingList.summary).replace(/\n/g, '
');
- this.readingListImage = this.imageService.randomize(this.imageService.getReadingListCoverImage(this.listId));
- this.cdRef.markForCheck();
+ this.readingListService.getReadingList(this.listId).subscribe(rl => {
+ this.readingList = rl;
+ this.readingListSummary = (this.readingList.summary === null ? '' : this.readingList.summary).replace(/\n/g, '
');
+ this.readingListImage = this.imageService.randomize(this.imageService.getReadingListCoverImage(this.listId));
+ this.cdRef.markForCheck();
+ })
});
break;
}
@@ -181,7 +183,7 @@ export class ReadingListDetailComponent implements OnInit {
if (!this.readingList) return;
const firstItem = this.items[0];
this.router.navigate(
- this.readerService.getNavigationArray(firstItem.libraryId, firstItem.seriesId, firstItem.chapterId, firstItem.seriesFormat),
+ this.readerService.getNavigationArray(firstItem.libraryId, firstItem.seriesId, firstItem.chapterId, firstItem.seriesFormat),
{queryParams: {readingListId: this.readingList.id, inconitoMode: inconitoMode}});
}
@@ -198,7 +200,7 @@ export class ReadingListDetailComponent implements OnInit {
}
this.router.navigate(
- this.readerService.getNavigationArray(currentlyReadingChapter.libraryId, currentlyReadingChapter.seriesId, currentlyReadingChapter.chapterId, currentlyReadingChapter.seriesFormat),
+ this.readerService.getNavigationArray(currentlyReadingChapter.libraryId, currentlyReadingChapter.seriesId, currentlyReadingChapter.chapterId, currentlyReadingChapter.seriesFormat),
{queryParams: {readingListId: this.readingList.id, inconitoMode: inconitoMode}});
}
diff --git a/openapi.json b/openapi.json
index 3ac94ed06..ac2a6cea6 100644
--- a/openapi.json
+++ b/openapi.json
@@ -7,7 +7,7 @@
"name": "GPL-3.0",
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
},
- "version": "0.7.2.26"
+ "version": "0.7.2.28"
},
"servers": [
{
@@ -2129,17 +2129,17 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/CreateLibraryDto"
+ "$ref": "#/components/schemas/UpdateLibraryDto"
}
},
"text/json": {
"schema": {
- "$ref": "#/components/schemas/CreateLibraryDto"
+ "$ref": "#/components/schemas/UpdateLibraryDto"
}
},
"application/*+json": {
"schema": {
- "$ref": "#/components/schemas/CreateLibraryDto"
+ "$ref": "#/components/schemas/UpdateLibraryDto"
}
}
}
@@ -11191,31 +11191,6 @@
},
"additionalProperties": false
},
- "CreateLibraryDto": {
- "required": [
- "folders",
- "name",
- "type"
- ],
- "type": "object",
- "properties": {
- "name": {
- "minLength": 1,
- "type": "string"
- },
- "type": {
- "$ref": "#/components/schemas/LibraryType"
- },
- "folders": {
- "minItems": 1,
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- },
- "additionalProperties": false
- },
"CreateReadingListDto": {
"type": "object",
"properties": {