From 8f5f12ad30d5178befc8008716ae3e300f8a9b23 Mon Sep 17 00:00:00 2001 From: burdukov Date: Sun, 7 Jul 2024 00:48:54 +0500 Subject: [PATCH] feat: caching pages --- .../components/reader/reader.component.html | 16 ++-- .../app/components/reader/reader.component.ts | 91 +++++++++++++------ .../src/app/services/search.service.ts | 15 ++- 3 files changed, 87 insertions(+), 35 deletions(-) diff --git a/apps/NwaifuAnime/src/app/components/reader/reader.component.html b/apps/NwaifuAnime/src/app/components/reader/reader.component.html index c925604..d117c31 100644 --- a/apps/NwaifuAnime/src/app/components/reader/reader.component.html +++ b/apps/NwaifuAnime/src/app/components/reader/reader.component.html @@ -1,11 +1,11 @@ -

It's reader page

+

It's reader page

@if(pages.length > 0){ - -
- -

{{pages[currentPageIndex].slug}} / {{pages.length}}

- -
- } + +
+ +

{{pages[currentPageIndex]?.slug}} / {{pages.length}}

+ +
+}
\ No newline at end of file diff --git a/apps/NwaifuAnime/src/app/components/reader/reader.component.ts b/apps/NwaifuAnime/src/app/components/reader/reader.component.ts index c3a6f7c..a5d69dd 100644 --- a/apps/NwaifuAnime/src/app/components/reader/reader.component.ts +++ b/apps/NwaifuAnime/src/app/components/reader/reader.component.ts @@ -1,6 +1,7 @@ import { CommonModule } from "@angular/common"; -import { AfterViewInit, Component } from "@angular/core"; +import { AfterViewInit, Component, ElementRef, ViewChild } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; +import { Observable, map } from "rxjs"; import { Page } from "../../services/parsers/rulib/rulib.chapter.dto"; import { SearchService } from "../../services/search.service"; @@ -14,7 +15,8 @@ import { SearchService } from "../../services/search.service"; export class ReaderComponent implements AfterViewInit { pages: Page[] = []; currentPageIndex = 0; - cachedPages: { [key: number]: Page } = {}; + cachedPages: { [key: number]: Page & { imageData?: Uint8Array } } = {}; + @ViewChild('mangaImage') mangaImage!: ElementRef; constructor( private route: ActivatedRoute, @@ -38,44 +40,81 @@ export class ReaderComponent implements AfterViewInit { loadChapter(url: string, chapter: string, volume: string) { this.searchService.getChapter(url, chapter, volume).subscribe((data) => { this.pages = data.data.pages; - this.loadPage(0); + this.loadPage(0); // Загрузить первую страницу при открытии главы }); } loadPage(index: number) { if (index >= 0 && index < this.pages.length) { this.currentPageIndex = index; - this.cachePages(index); - - if (!this.cachedPages[index]) { - this.cachedPages[index] = { - ...this.pages[index], - url: this.searchService.getImageServer() + this.pages[index].url, - }; + this.cachePage(index); // Кэшируем текущую и соседние страницы + this.unloadCachedPages(index); // Сгружаем ненужные страницы из кэша + if (!this.cachedPages[index]?.imageData) { + // Если страница не закэширована, загружаем её + this.fetchAndCachePage(index).subscribe(() => { + this.updateImage(); + }); + } else { + // Если страница уже в кэше, просто обновляем изображение + this.updateImage(); } } } - cachePages(currentIndex: number) { - for (let i = 0; i <= currentIndex && i < this.pages.length; i++) { - if (!this.cachedPages[i]) { - this.cachedPages[i] = { - ...this.pages[i], - url: this.searchService.getImageServer() + this.pages[i].url, - }; - } - } - - for (let i = currentIndex + 1; i <= currentIndex + 3 && i < this.pages.length; i++) { - if (!this.cachedPages[i]) { - this.cachedPages[i] = { - ...this.pages[i], - url: this.searchService.getImageServer() + this.pages[i].url, - }; + // Кэширование текущей страницы и несколько соседних для ускорения процесса навигации по страницам + private cachePage(index: number) { + const startIndex = Math.max(0, index - 2); + const endIndex = Math.min(this.pages.length - 1, index + 2); + + for (let i = startIndex; i <= endIndex; i++) { + if (!this.isPageCached(i)) { + this.fetchAndCachePage(i).subscribe(() => { + if (i === this.currentPageIndex) { + this.updateImage(); + } + }); } } } + private isPageCached(index: number): boolean { + return !!this.cachedPages[index]?.imageData; + } + + // Загрузка и сохранение изображения в кэш + private fetchAndCachePage(index: number): Observable { + return this.searchService + .getImageData(this.searchService.getImageServer() + this.pages[index].url) + .pipe( + map((imageData) => { + this.cachedPages[index] = { + ...this.pages[index], + imageData, + }; + }) + ); + } + + // Выгрузка из кэша старых страниц + private unloadCachedPages(index: number) { + for (const key in this.cachedPages) { + const pageIndex = parseInt(key, 10); + if (index - pageIndex > 2) { + delete this.cachedPages[pageIndex]; + } + } + } + + // Обновляем изображение на странице + private updateImage() { + const currentPage = this.cachedPages[this.currentPageIndex]; + if (this.mangaImage?.nativeElement && currentPage?.imageData) { + const blob = new Blob([currentPage.imageData], { type: 'image/jpeg' }); + const urlCreator = window.URL || window.webkitURL; + this.mangaImage.nativeElement.src = urlCreator.createObjectURL(blob); + } + } + nextPage() { this.loadPage(this.currentPageIndex + 1); } diff --git a/apps/NwaifuAnime/src/app/services/search.service.ts b/apps/NwaifuAnime/src/app/services/search.service.ts index 8682415..b427bc0 100644 --- a/apps/NwaifuAnime/src/app/services/search.service.ts +++ b/apps/NwaifuAnime/src/app/services/search.service.ts @@ -5,12 +5,16 @@ import { IRulibChapterResult } from "./parsers/rulib/rulib.chapter.dto"; import { IRulibChaptersResult } from "./parsers/rulib/rulib.chapters.dto"; import { IRulibDetailResult } from "./parsers/rulib/rulib.detail.dto"; import { Datum } from "./parsers/rulib/rulib.search.dto"; +import { HttpClient } from "@angular/common/http"; @Injectable({ providedIn: "root" }) export class SearchService { private itemsTerm = new BehaviorSubject([]); currentItemsTerm = this.itemsTerm.asObservable(); - constructor(private parser: LibSocialParserService) {} + constructor( + private parser: LibSocialParserService, + private http: HttpClient, + ) {} search(query: string) { this.parser.searchManga(query).subscribe((data) => { @@ -45,4 +49,13 @@ export class SearchService { getImageServer() { return this.parser.imageServer; } + + getImageData(imageUrl: string): Observable{ + return this.http.get(imageUrl, {responseType: 'arraybuffer'}).pipe( + map((arrayBuffer: ArrayBuffer) => { + return new Uint8Array(arrayBuffer); + }) + ); + } + }