Compare commits
2 Commits
feature/ma
...
8f5f12ad30
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f5f12ad30 | |||
| 6bad651312 |
@@ -1,6 +1,11 @@
|
|||||||
<h1>It's reader page</h1>
|
<h1>It's reader page</h1>
|
||||||
<div class="flex flex-col items-center">
|
<div class="flex flex-col items-center">
|
||||||
@for (page of pages; track $index) {
|
@if(pages.length > 0){
|
||||||
<img [src]="page.url" [alt]="page.slug" />
|
<img #mangaImage [alt]="cachedPages[currentPageIndex]?.slug" />
|
||||||
}
|
<div class="flex items-center justify-center space-x-4 mb-10">
|
||||||
|
<button (click)="prevPage()" [disabled]="currentPageIndex === 0" class="p-3 text-white bg-slate-600 w-[100px] mt-5 rounded-lg">←</button>
|
||||||
|
<p>{{pages[currentPageIndex]?.slug}} / {{pages.length}}</p>
|
||||||
|
<button (click)="nextPage()" [disabled]="currentPageIndex === pages.length - 1" class="p-3 text-white bg-slate-600 w-[100px] mt-5 rounded-lg">→</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { CommonModule } from "@angular/common";
|
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 { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
import { Observable, map } from "rxjs";
|
||||||
import { Page } from "../../services/parsers/rulib/rulib.chapter.dto";
|
import { Page } from "../../services/parsers/rulib/rulib.chapter.dto";
|
||||||
import { SearchService } from "../../services/search.service";
|
import { SearchService } from "../../services/search.service";
|
||||||
|
|
||||||
@@ -13,6 +14,10 @@ import { SearchService } from "../../services/search.service";
|
|||||||
})
|
})
|
||||||
export class ReaderComponent implements AfterViewInit {
|
export class ReaderComponent implements AfterViewInit {
|
||||||
pages: Page[] = [];
|
pages: Page[] = [];
|
||||||
|
currentPageIndex = 0;
|
||||||
|
cachedPages: { [key: number]: Page & { imageData?: Uint8Array } } = {};
|
||||||
|
@ViewChild('mangaImage') mangaImage!: ElementRef<HTMLImageElement>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
@@ -25,14 +30,96 @@ export class ReaderComponent implements AfterViewInit {
|
|||||||
const chapter = params["chapter"];
|
const chapter = params["chapter"];
|
||||||
const volume = params["volume"];
|
const volume = params["volume"];
|
||||||
if (url && chapter && volume) {
|
if (url && chapter && volume) {
|
||||||
this.searchService.getChapter(url, chapter, volume).subscribe((data) => {
|
this.loadChapter(url, chapter, volume);
|
||||||
this.pages = data.data.pages.map((page) => {
|
|
||||||
return { ...page, url: this.searchService.getImageServer() + page.url };
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
this.router.navigate(["/"]);
|
this.router.navigate(["/"]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadChapter(url: string, chapter: string, volume: string) {
|
||||||
|
this.searchService.getChapter(url, chapter, volume).subscribe((data) => {
|
||||||
|
this.pages = data.data.pages;
|
||||||
|
this.loadPage(0); // Загрузить первую страницу при открытии главы
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPage(index: number) {
|
||||||
|
if (index >= 0 && index < this.pages.length) {
|
||||||
|
this.currentPageIndex = index;
|
||||||
|
this.cachePage(index); // Кэшируем текущую и соседние страницы
|
||||||
|
this.unloadCachedPages(index); // Сгружаем ненужные страницы из кэша
|
||||||
|
if (!this.cachedPages[index]?.imageData) {
|
||||||
|
// Если страница не закэширована, загружаем её
|
||||||
|
this.fetchAndCachePage(index).subscribe(() => {
|
||||||
|
this.updateImage();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Если страница уже в кэше, просто обновляем изображение
|
||||||
|
this.updateImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Кэширование текущей страницы и несколько соседних для ускорения процесса навигации по страницам
|
||||||
|
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<void> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
prevPage() {
|
||||||
|
this.loadPage(this.currentPageIndex - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,12 +5,16 @@ import { IRulibChapterResult } from "./parsers/rulib/rulib.chapter.dto";
|
|||||||
import { IRulibChaptersResult } from "./parsers/rulib/rulib.chapters.dto";
|
import { IRulibChaptersResult } from "./parsers/rulib/rulib.chapters.dto";
|
||||||
import { IRulibDetailResult } from "./parsers/rulib/rulib.detail.dto";
|
import { IRulibDetailResult } from "./parsers/rulib/rulib.detail.dto";
|
||||||
import { Datum } from "./parsers/rulib/rulib.search.dto";
|
import { Datum } from "./parsers/rulib/rulib.search.dto";
|
||||||
|
import { HttpClient } from "@angular/common/http";
|
||||||
|
|
||||||
@Injectable({ providedIn: "root" })
|
@Injectable({ providedIn: "root" })
|
||||||
export class SearchService {
|
export class SearchService {
|
||||||
private itemsTerm = new BehaviorSubject<Datum[]>([]);
|
private itemsTerm = new BehaviorSubject<Datum[]>([]);
|
||||||
currentItemsTerm = this.itemsTerm.asObservable();
|
currentItemsTerm = this.itemsTerm.asObservable();
|
||||||
constructor(private parser: LibSocialParserService) {}
|
constructor(
|
||||||
|
private parser: LibSocialParserService,
|
||||||
|
private http: HttpClient,
|
||||||
|
) {}
|
||||||
|
|
||||||
search(query: string) {
|
search(query: string) {
|
||||||
this.parser.searchManga(query).subscribe((data) => {
|
this.parser.searchManga(query).subscribe((data) => {
|
||||||
@@ -45,4 +49,13 @@ export class SearchService {
|
|||||||
getImageServer() {
|
getImageServer() {
|
||||||
return this.parser.imageServer;
|
return this.parser.imageServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getImageData(imageUrl: string): Observable<Uint8Array>{
|
||||||
|
return this.http.get(imageUrl, {responseType: 'arraybuffer'}).pipe(
|
||||||
|
map((arrayBuffer: ArrayBuffer) => {
|
||||||
|
return new Uint8Array(arrayBuffer);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user