diff --git a/API.Tests/Services/ArchiveServiceTests.cs b/API.Tests/Services/ArchiveServiceTests.cs index fa20d7d87..de27464c9 100644 --- a/API.Tests/Services/ArchiveServiceTests.cs +++ b/API.Tests/Services/ArchiveServiceTests.cs @@ -152,16 +152,14 @@ namespace API.Tests.Services } - - // TODO: This is broken on GA due to DirectoryService.CoverImageDirectory - //[Theory] - [InlineData("v10.cbz", "v10.expected.jpg")] - [InlineData("v10 - with folder.cbz", "v10 - with folder.expected.jpg")] - [InlineData("v10 - nested folder.cbz", "v10 - nested folder.expected.jpg")] - [InlineData("macos_native.zip", "macos_native.jpg")] - [InlineData("v10 - duplicate covers.cbz", "v10 - duplicate covers.expected.jpg")] - [InlineData("sorting.zip", "sorting.expected.jpg")] - [InlineData("test.zip", "test.expected.jpg")] // https://github.com/kleisauke/net-vips/issues/155 + [Theory] + [InlineData("v10.cbz", "v10.expected.png")] + [InlineData("v10 - with folder.cbz", "v10 - with folder.expected.png")] + [InlineData("v10 - nested folder.cbz", "v10 - nested folder.expected.png")] + [InlineData("macos_native.zip", "macos_native.png")] + [InlineData("v10 - duplicate covers.cbz", "v10 - duplicate covers.expected.png")] + [InlineData("sorting.zip", "sorting.expected.png")] + [InlineData("test.zip", "test.expected.jpg")] public void GetCoverImage_Default_Test(string inputFile, string expectedOutputFile) { var ds = Substitute.For(_directoryServiceLogger, new FileSystem()); @@ -183,33 +181,33 @@ namespace API.Tests.Services Assert.Equal(expectedBytes, actual); - //_directoryService.ClearAndDeleteDirectory(outputDir); + _directoryService.ClearAndDeleteDirectory(outputDir); } - // TODO: This is broken on GA due to DirectoryService.CoverImageDirectory - //[Theory] - [InlineData("v10.cbz", "v10.expected.jpg")] - [InlineData("v10 - with folder.cbz", "v10 - with folder.expected.jpg")] - [InlineData("v10 - nested folder.cbz", "v10 - nested folder.expected.jpg")] - [InlineData("macos_native.zip", "macos_native.jpg")] - [InlineData("v10 - duplicate covers.cbz", "v10 - duplicate covers.expected.jpg")] - [InlineData("sorting.zip", "sorting.expected.jpg")] + [Theory] + [InlineData("v10.cbz", "v10.expected.png")] + [InlineData("v10 - with folder.cbz", "v10 - with folder.expected.png")] + [InlineData("v10 - nested folder.cbz", "v10 - nested folder.expected.png")] + [InlineData("macos_native.zip", "macos_native.png")] + [InlineData("v10 - duplicate covers.cbz", "v10 - duplicate covers.expected.png")] + [InlineData("sorting.zip", "sorting.expected.png")] public void GetCoverImage_SharpCompress_Test(string inputFile, string expectedOutputFile) { var imageService = new ImageService(Substitute.For>(), _directoryService); var archiveService = Substitute.For(_logger, new DirectoryService(_directoryServiceLogger, new FileSystem()), imageService); - var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ArchiveService/CoverImages"); - var expectedBytes = File.ReadAllBytes(Path.Join(testDirectory, expectedOutputFile)); + var testDirectory = API.Parser.Parser.NormalizePath(Path.GetFullPath(Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ArchiveService/CoverImages"))); var outputDir = Path.Join(testDirectory, "output"); _directoryService.ClearDirectory(outputDir); _directoryService.ExistOrCreate(outputDir); archiveService.Configure().CanOpen(Path.Join(testDirectory, inputFile)).Returns(ArchiveLibrary.SharpCompress); - var actualBytes = File.ReadAllBytes(archiveService.GetCoverImage(Path.Join(testDirectory, inputFile), - Path.GetFileNameWithoutExtension(inputFile) + "_output", outputDir)); + var coverOutputFile = archiveService.GetCoverImage(Path.Join(testDirectory, inputFile), + Path.GetFileNameWithoutExtension(inputFile), outputDir); + var actualBytes = File.ReadAllBytes(Path.Join(outputDir, coverOutputFile)); + var expectedBytes = File.ReadAllBytes(Path.Join(testDirectory, expectedOutputFile)); Assert.Equal(expectedBytes, actualBytes); _directoryService.ClearAndDeleteDirectory(outputDir); diff --git a/API.Tests/Services/Test Data/ArchiveService/CoverImages/macos_native.jpg b/API.Tests/Services/Test Data/ArchiveService/CoverImages/macos_native.jpg deleted file mode 100644 index 575b9e556..000000000 Binary files a/API.Tests/Services/Test Data/ArchiveService/CoverImages/macos_native.jpg and /dev/null differ diff --git a/API.Tests/Services/Test Data/ArchiveService/CoverImages/macos_native.png b/API.Tests/Services/Test Data/ArchiveService/CoverImages/macos_native.png new file mode 100644 index 000000000..05c47779f Binary files /dev/null and b/API.Tests/Services/Test Data/ArchiveService/CoverImages/macos_native.png differ diff --git a/API.Tests/Services/Test Data/ArchiveService/CoverImages/output/test2_output.png b/API.Tests/Services/Test Data/ArchiveService/CoverImages/output/test2_output.png deleted file mode 100644 index faa1b5d21..000000000 Binary files a/API.Tests/Services/Test Data/ArchiveService/CoverImages/output/test2_output.png and /dev/null differ diff --git a/API.Tests/Services/Test Data/ArchiveService/CoverImages/sorting.expected.jpg b/API.Tests/Services/Test Data/ArchiveService/CoverImages/sorting.expected.jpg deleted file mode 100644 index bd9d441cd..000000000 Binary files a/API.Tests/Services/Test Data/ArchiveService/CoverImages/sorting.expected.jpg and /dev/null differ diff --git a/API.Tests/Services/Test Data/ArchiveService/CoverImages/sorting.expected.png b/API.Tests/Services/Test Data/ArchiveService/CoverImages/sorting.expected.png new file mode 100644 index 000000000..665af5761 Binary files /dev/null and b/API.Tests/Services/Test Data/ArchiveService/CoverImages/sorting.expected.png differ diff --git a/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10 - duplicate covers.expected.jpg b/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10 - duplicate covers.expected.jpg deleted file mode 100644 index 51fd89ca0..000000000 Binary files a/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10 - duplicate covers.expected.jpg and /dev/null differ diff --git a/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10 - duplicate covers.expected.png b/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10 - duplicate covers.expected.png new file mode 100644 index 000000000..68bf77f0e Binary files /dev/null and b/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10 - duplicate covers.expected.png differ diff --git a/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10 - nested folder.expected.jpg b/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10 - nested folder.expected.jpg deleted file mode 100644 index 51fd89ca0..000000000 Binary files a/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10 - nested folder.expected.jpg and /dev/null differ diff --git a/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10 - nested folder.expected.png b/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10 - nested folder.expected.png new file mode 100644 index 000000000..68bf77f0e Binary files /dev/null and b/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10 - nested folder.expected.png differ diff --git a/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10 - with folder.expected.png b/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10 - with folder.expected.png new file mode 100644 index 000000000..9a6ada78e Binary files /dev/null and b/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10 - with folder.expected.png differ diff --git a/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10.expected.jpg b/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10.expected.jpg deleted file mode 100644 index 51fd89ca0..000000000 Binary files a/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10.expected.jpg and /dev/null differ diff --git a/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10.expected.png b/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10.expected.png new file mode 100644 index 000000000..68bf77f0e Binary files /dev/null and b/API.Tests/Services/Test Data/ArchiveService/CoverImages/v10.expected.png differ diff --git a/API/Controllers/ReaderController.cs b/API/Controllers/ReaderController.cs index ef291a66d..a8a34bdfd 100644 --- a/API/Controllers/ReaderController.cs +++ b/API/Controllers/ReaderController.cs @@ -63,7 +63,7 @@ namespace API.Controllers if (string.IsNullOrEmpty(path) || !System.IO.File.Exists(path)) return BadRequest($"No such image for page {page}"); var format = Path.GetExtension(path).Replace(".", ""); - Response.AddCacheHeader(path); + Response.AddCacheHeader(path, TimeSpan.FromMinutes(10).Seconds); return PhysicalFile(path, "image/" + format, Path.GetFileName(path)); } catch (Exception) diff --git a/API/Entities/Enums/LayoutMode.cs b/API/Entities/Enums/LayoutMode.cs index 58aaf30da..37fc69293 100644 --- a/API/Entities/Enums/LayoutMode.cs +++ b/API/Entities/Enums/LayoutMode.cs @@ -7,5 +7,7 @@ public enum LayoutMode [Description("Single")] Single = 1, [Description("Double")] - Double = 2 + Double = 2, + [Description("Double (manga)")] + DoubleReversed = 3 } diff --git a/API/Extensions/HttpExtensions.cs b/API/Extensions/HttpExtensions.cs index f0b3d5399..c468ef7ce 100644 --- a/API/Extensions/HttpExtensions.cs +++ b/API/Extensions/HttpExtensions.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; @@ -41,12 +42,17 @@ namespace API.Extensions /// /// /// - public static void AddCacheHeader(this HttpResponse response, string filename) + /// Maximum amount of seconds to set for Cache-Control + public static void AddCacheHeader(this HttpResponse response, string filename, int maxAge = 10) { - if (filename == null || filename.Length <= 0) return; + if (filename is not {Length: > 0}) return; var hashContent = filename + File.GetLastWriteTimeUtc(filename); using var sha1 = SHA256.Create(); response.Headers.Add("ETag", string.Concat(sha1.ComputeHash(Encoding.UTF8.GetBytes(hashContent)).Select(x => x.ToString("X2")))); + if (maxAge != 10) + { + response.Headers.CacheControl = $"max-age={maxAge}"; + } } } diff --git a/API/Services/CacheService.cs b/API/Services/CacheService.cs index c5396f4ed..ef2d3609a 100644 --- a/API/Services/CacheService.cs +++ b/API/Services/CacheService.cs @@ -142,7 +142,7 @@ namespace API.Services { foreach (var chapter in chapterIds) { - _directoryService.ClearDirectory(GetCachePath(chapter)); + _directoryService.ClearAndDeleteDirectory(GetCachePath(chapter)); } } diff --git a/UI/Web/src/app/_models/preferences/preferences.ts b/UI/Web/src/app/_models/preferences/preferences.ts index 89cdc8a61..7fbdaf185 100644 --- a/UI/Web/src/app/_models/preferences/preferences.ts +++ b/UI/Web/src/app/_models/preferences/preferences.ts @@ -34,4 +34,4 @@ export const readingDirections = [{text: 'Left to Right', value: ReadingDirectio export const scalingOptions = [{text: 'Automatic', value: ScalingOption.Automatic}, {text: 'Fit to Height', value: ScalingOption.FitToHeight}, {text: 'Fit to Width', value: ScalingOption.FitToWidth}, {text: 'Original', value: ScalingOption.Original}]; export const pageSplitOptions = [{text: 'Fit to Screen', value: PageSplitOption.FitSplit}, {text: 'Right to Left', value: PageSplitOption.SplitRightToLeft}, {text: 'Left to Right', value: PageSplitOption.SplitLeftToRight}, {text: 'No Split', value: PageSplitOption.NoSplit}]; export const readingModes = [{text: 'Left to Right', value: ReaderMode.LeftRight}, {text: 'Up to Down', value: ReaderMode.UpDown}, {text: 'Webtoon', value: ReaderMode.Webtoon}]; -export const layoutModes = [{text: 'Single', value: LayoutMode.Single}, {text: 'Double', value: LayoutMode.Double}]; +export const layoutModes = [{text: 'Single', value: LayoutMode.Single}, {text: 'Double', value: LayoutMode.Double}, {text: 'Double (Manga)', value: LayoutMode.DoubleReversed}]; diff --git a/UI/Web/src/app/manga-reader/_models/layout-mode.ts b/UI/Web/src/app/manga-reader/_models/layout-mode.ts index 814112d5e..75887f2a2 100644 --- a/UI/Web/src/app/manga-reader/_models/layout-mode.ts +++ b/UI/Web/src/app/manga-reader/_models/layout-mode.ts @@ -10,5 +10,8 @@ export enum LayoutMode { * Renders 2 pages side by side on the renderer. Cover images will not split and take up both panes. */ Double = 2, - + /** + * Renders 2 pages side by side on the renderer. Cover images will not split and take up both panes. This version reverses the order and is used for Manga only + */ + DoubleReversed = 3 } \ No newline at end of file diff --git a/UI/Web/src/app/manga-reader/_models/reader-enums.ts b/UI/Web/src/app/manga-reader/_models/reader-enums.ts index 37ab08054..f33059a96 100644 --- a/UI/Web/src/app/manga-reader/_models/reader-enums.ts +++ b/UI/Web/src/app/manga-reader/_models/reader-enums.ts @@ -3,7 +3,10 @@ export enum FITTING_OPTION { WIDTH = 'full-width', ORIGINAL = 'original' } - + +/** + * How to split a page into virutal pages. Only works with LayoutMode.Single + */ export enum SPLIT_PAGE_PART { NO_SPLIT = 'none', LEFT_PART = 'left', diff --git a/UI/Web/src/app/manga-reader/infinite-scroller/infinite-scroller.component.scss b/UI/Web/src/app/manga-reader/infinite-scroller/infinite-scroller.component.scss index 1880bb678..8dece9927 100644 --- a/UI/Web/src/app/manga-reader/infinite-scroller/infinite-scroller.component.scss +++ b/UI/Web/src/app/manga-reader/infinite-scroller/infinite-scroller.component.scss @@ -25,8 +25,10 @@ } } -.full-width { + +img, .full-width { width: 100% !important; + height: auto; } diff --git a/UI/Web/src/app/manga-reader/manga-reader.component.html b/UI/Web/src/app/manga-reader/manga-reader.component.html index 19575600c..ae6563fd9 100644 --- a/UI/Web/src/app/manga-reader/manga-reader.component.html +++ b/UI/Web/src/app/manga-reader/manga-reader.component.html @@ -5,7 +5,7 @@ Back - +
{{title}} (Incognito Mode:)
@@ -18,8 +18,9 @@ Keyboard Shortcuts Modal + - +
@@ -30,54 +31,57 @@
- +
-
-
- + - - - + + +
+ + + +
+
+ +
+
+
+
+ +
+
+
+
- +
-
-
- + + - -
-
- -
-
-
-
- -
-
-
- +
@@ -95,8 +99,6 @@
- -
@@ -130,7 +132,7 @@
 
-
+
@@ -178,13 +180,11 @@
- - \ No newline at end of file + diff --git a/UI/Web/src/app/manga-reader/manga-reader.component.scss b/UI/Web/src/app/manga-reader/manga-reader.component.scss index b417f6f2c..65bef4e82 100644 --- a/UI/Web/src/app/manga-reader/manga-reader.component.scss +++ b/UI/Web/src/app/manga-reader/manga-reader.component.scss @@ -4,6 +4,9 @@ $side-width: 25%; $dash-width: 3px; $pointer-offset: 5px; +img { + user-select: none; +} @media(min-width: 600px) { .overlay .left .i { @@ -14,6 +17,36 @@ $pointer-offset: 5px; } } +.reading-area { + display: flex; + justify-content: center; + position: relative; +} + +.image-container { + text-align: center; + display: block; + height: 100vh; + + #image-1 { + &.double { + margin: 0 0 0 auto; + } + } + + &.reverse { + flex-direction: row-reverse; + overflow: unset; + justify-content: flex-end; + } + + #image-2 { + &.double { + margin: 0 auto 0 0; + } + } +} + canvas { position: absolute; } @@ -55,65 +88,62 @@ canvas { } // Fitting Options -// .full-height { -// position: absolute; -// margin: auto; -// top: 0px; -// left: 0; -// right: 0; -// bottom: 0px; -// height: 100%; -// } .full-height { width: auto; margin: 0 auto; - height: 100vh; + height: 100%; vertical-align: top; } .original { - position: absolute; - margin-left: auto; - margin-right: auto; - top: 0px; - bottom: 0px; - left: 0; - right: 0; + align-self: center; } .full-width { width: 100%; + align-self: center; &.double { - width: 50% - } + width: 50%; - .image-2 { - margin-left: 50%; + &.cover { + width: 100%; + } } } .center-double { - display: block; - margin-left: auto; - margin-right: auto; + display: flex; + overflow: unset; +} + +.fit-to-width-double-offset { + width: 100%; +} + +.original-double-offset { + width: 100%; } .fit-to-height-double-offset { - width: 50%; + position: absolute; + height: 100vh; + object-fit: scale-down; + top: 50%; + left: 50%; + transform: translate(-50%, 0%); + max-width: 100%; } - - .right { position: fixed; right: 0px; top: 0px; width: $side-width; - height: 100%; + height: 100vh; background: rgba(0, 0, 0, 0); z-index: 2; cursor: pointer; @@ -135,7 +165,7 @@ canvas { left: 0px; top: 0px; width: $side-width; - height: 100%; + height: 100vh; background: rgba(0, 0, 0, 0); z-index: 2; cursor: pointer; diff --git a/UI/Web/src/app/manga-reader/manga-reader.component.ts b/UI/Web/src/app/manga-reader/manga-reader.component.ts index eb14fbbaa..9ad50d2e8 100644 --- a/UI/Web/src/app/manga-reader/manga-reader.component.ts +++ b/UI/Web/src/app/manga-reader/manga-reader.component.ts @@ -20,7 +20,7 @@ import { ChangeContext, LabelType, Options } from '@angular-slider/ngx-slider'; import { trigger, state, style, transition, animate } from '@angular/animations'; import { ChapterInfo } from './_models/chapter-info'; import { FITTING_OPTION, PAGING_DIRECTION, SPLIT_PAGE_PART } from './_models/reader-enums'; -import { pageSplitOptions, scalingOptions } from '../_models/preferences/preferences'; +import { layoutModes, pageSplitOptions, scalingOptions } from '../_models/preferences/preferences'; import { ReaderMode } from '../_models/preferences/reader-mode'; import { MangaFormat } from '../_models/manga-format'; import { LibraryService } from '../_services/library.service'; @@ -107,6 +107,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { readerMode: ReaderMode = ReaderMode.LeftRight; pageSplitOptions = pageSplitOptions; + layoutModes = layoutModes; isLoading = true; @@ -118,7 +119,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { */ canvasImage = new Image(); /** - * Used soley for LayoutMode.Double rendering. Will always hold the next image in buffer. + * Used soley for LayoutMode.Double rendering. Will always hold the next image in buffer. */ canvasImage2 = new Image(); renderWithCanvas: boolean = false; // Dictates if we use render with canvas or with image @@ -256,8 +257,19 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { getPageUrl = (pageNum: number) => this.readerService.getPageUrl(this.chapterId, pageNum); + get PageNumber() { + return Math.max(Math.min(this.pageNum, this.maxPages - 1), 0); + } - get pageBookmarked() { + get ShouldRenderDoublePage() { + return this.layoutMode !== LayoutMode.Single && !this.isCoverImage(); + } + + get ShouldRenderReverseDouble() { + return (this.layoutMode === LayoutMode.DoubleReversed) && !this.isCoverImage(); + } + + get isCurrentPageBookmarked() { return this.bookmarks.hasOwnProperty(this.pageNum); } @@ -364,9 +376,15 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { this.generalSettingsForm.get('layoutMode')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe(val => { this.layoutMode = parseInt(val, 10); - if (this.layoutMode === LayoutMode.Double) { - // Update canvasImage2 - this.canvasImage2 = this.cachedImages.next(); + + if (this.layoutMode === LayoutMode.Single) { + this.generalSettingsForm.get('pageSplitOption')?.enable(); + + } else { + this.generalSettingsForm.get('pageSplitOption')?.setValue(PageSplitOption.FitSplit); + this.generalSettingsForm.get('pageSplitOption')?.disable(); + + this.canvasImage2 = this.cachedImages.peek(); } }); @@ -633,12 +651,12 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { } val = formControl?.value; - if (this.isCoverImage() && this.shouldRenderAsFitSplit()) { - // Rewriting to fit to width for this cover image - val = FITTING_OPTION.WIDTH; + + if (this.isCoverImage() && this.layoutMode !== LayoutMode.Single) { + return val + ' cover double'; } - if (!this.isCoverImage() && this.layoutMode === LayoutMode.Double) { + if (!this.isCoverImage() && this.layoutMode !== LayoutMode.Single) { return val + ' double'; } return val; @@ -783,7 +801,12 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { const notInSplit = this.currentImageSplitPart !== (this.isSplitLeftToRight() ? SPLIT_PAGE_PART.LEFT_PART : SPLIT_PAGE_PART.RIGHT_PART); - if ((this.pageNum + 1 >= this.maxPages && notInSplit) || this.isLoading) { + let pageAmount = (this.layoutMode !== LayoutMode.Single && !this.isCoverImage()) ? 2 : 1; + if (this.pageNum < 1) { + pageAmount = 1; + } + + if ((this.pageNum + pageAmount >= this.maxPages && notInSplit) || this.isLoading) { if (this.isLoading) { return; } @@ -794,12 +817,10 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { this.pagingDirection = PAGING_DIRECTION.FORWARD; if (this.isNoSplit() || notInSplit) { - this.setPageNum(this.pageNum + 1); + this.setPageNum(this.pageNum + pageAmount); + if (this.readerMode !== ReaderMode.Webtoon) { this.canvasImage = this.cachedImages.next(); - this.canvasImage2 = this.cachedImages.peek(2); - console.log('[nextPage] canvasImage: ', this.canvasImage); - console.log('[nextPage] canvasImage2: ', this.canvasImage2); } } @@ -816,6 +837,8 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { const notInSplit = this.currentImageSplitPart !== (this.isSplitLeftToRight() ? SPLIT_PAGE_PART.RIGHT_PART : SPLIT_PAGE_PART.LEFT_PART); + const pageAmount = (this.layoutMode !== LayoutMode.Single && !this.isCoverImage()) ? 2: 1; + console.log('pageAmt: ', pageAmount); if ((this.pageNum - 1 < 0 && notInSplit) || this.isLoading) { if (this.isLoading) { return; } @@ -827,11 +850,8 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { this.pagingDirection = PAGING_DIRECTION.BACKWARDS; if (this.isNoSplit() || notInSplit) { - this.setPageNum(this.pageNum - 1); + this.setPageNum(this.pageNum - pageAmount); this.canvasImage = this.cachedImages.prev(); - this.canvasImage2 = this.cachedImages.peek(-2); - console.log('[prevPage] canvasImage: ', this.canvasImage); - console.log('[prevPage] canvasImage2: ', this.canvasImage2); } if (this.readerMode !== ReaderMode.Webtoon) { @@ -945,12 +965,10 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { this.canvas.nativeElement.width = this.canvasImage.width / 2; this.ctx.drawImage(this.canvasImage, 0, 0, this.canvasImage.width, this.canvasImage.height, 0, 0, this.canvasImage.width, this.canvasImage.height); this.renderWithCanvas = true; - console.log('[Render] Canvas') } else if (needsSplitting && this.currentImageSplitPart === SPLIT_PAGE_PART.RIGHT_PART) { this.canvas.nativeElement.width = this.canvasImage.width / 2; this.ctx.drawImage(this.canvasImage, 0, 0, this.canvasImage.width, this.canvasImage.height, -this.canvasImage.width / 2, 0, this.canvasImage.width, this.canvasImage.height); this.renderWithCanvas = true; - console.log('[Render] Canvas') } else { this.renderWithCanvas = false; } @@ -1007,6 +1025,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { this.cachedImages.applyFor((item, internalIndex) => { const offsetIndex = this.pageNum + index; const urlPageNum = this.readerService.imageUrlToPageNum(item.src); + if (urlPageNum === offsetIndex) { index += 1; return; @@ -1016,23 +1035,27 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { index += 1; } }, this.cachedImages.size() - 3); + + //console.log('cachedImages: ', this.cachedImages.arr.map(img => this.readerService.imageUrlToPageNum(img.src) + ': ' + img.complete)); } loadPage() { if (!this.canvas || !this.ctx) { return; } this.isLoading = true; + this.canvasImage = this.cachedImages.current(); - this.canvasImage2 = this.cachedImages.next(); // TODO: Do I need this here? - console.log('[loadPage] canvasImage: ', this.canvasImage); - console.log('[loadPage] canvasImage2: ', this.canvasImage2); + + if (this.readerService.imageUrlToPageNum(this.canvasImage.src) !== this.pageNum || this.canvasImage.src === '' || !this.canvasImage.complete) { - this.canvasImage.src = this.readerService.getPageUrl(this.chapterId, this.pageNum); - this.canvasImage2.src = this.readerService.getPageUrl(this.chapterId, this.pageNum + 1); // TODO: I need to handle last page correctly + if (this.layoutMode === LayoutMode.Single) { + this.canvasImage.src = this.readerService.getPageUrl(this.chapterId, this.pageNum); + } else { + this.canvasImage.src = this.readerService.getPageUrl(this.chapterId, this.pageNum); + this.canvasImage2.src = this.readerService.getPageUrl(this.chapterId, this.pageNum + 1); // TODO: I need to handle last page correctly + } this.canvasImage.onload = () => this.renderPage(); - console.log('[loadPage] (after setting) canvasImage: ', this.canvasImage); - console.log('[loadPage] (after setting) canvasImage2: ', this.canvasImage2); } else { this.renderPage(); } @@ -1074,12 +1097,12 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { this.setPageNum(page); this.refreshSlider.emit(); - this.goToPageEvent.next(page); + this.goToPageEvent.next(page); this.render(); } setPageNum(pageNum: number) { - this.pageNum = pageNum; + this.pageNum = Math.max(pageNum, 0); if (this.pageNum >= this.maxPages - 10) { // Tell server to cache the next chapter @@ -1179,12 +1202,19 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { } updateForm() { + if ( this.readerMode === ReaderMode.Webtoon) { this.generalSettingsForm.get('fittingOption')?.disable() this.generalSettingsForm.get('pageSplitOption')?.disable(); + this.generalSettingsForm.get('layoutMode')?.disable(); } else { this.generalSettingsForm.get('fittingOption')?.enable() this.generalSettingsForm.get('pageSplitOption')?.enable(); + this.generalSettingsForm.get('layoutMode')?.enable(); + + if (this.layoutMode !== LayoutMode.Single) { + this.generalSettingsForm.get('pageSplitOption')?.disable(); + } } } @@ -1198,9 +1228,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { bookmarkPage() { const pageNum = this.pageNum; - // TODO: Handle LayoutMode.Double - - if (this.pageBookmarked) { + if (this.isCurrentPageBookmarked) { let apis = [this.readerService.unbookmark(this.seriesId, this.volumeId, this.chapterId, pageNum)]; if (this.layoutMode === LayoutMode.Double) apis.push(this.readerService.unbookmark(this.seriesId, this.volumeId, this.chapterId, pageNum + 1)); forkJoin(apis).pipe(take(1)).subscribe(() => {