66aeab282c
Build and Push Multi-Platform Images / build-and-push (push) Successful in 19s
This reverts commit 07dfd7e840.
124 lines
3.6 KiB
TypeScript
124 lines
3.6 KiB
TypeScript
import { Injectable, Logger } from '@nestjs/common';
|
|
import { ConfigService } from '@nestjs/config';
|
|
import * as path from 'path';
|
|
import * as fs from 'fs/promises';
|
|
import sharp from 'sharp';
|
|
|
|
const THUMBNAIL_WIDTH = 180;
|
|
|
|
@Injectable()
|
|
export class PageCacheService {
|
|
private readonly logger = new Logger(PageCacheService.name);
|
|
private readonly inboxRoot: string;
|
|
|
|
constructor(configService: ConfigService) {
|
|
this.inboxRoot = configService.get<string>(
|
|
'INBOX_DATA_DIR',
|
|
'/mnt/data/inbox',
|
|
);
|
|
}
|
|
|
|
documentDir(documentId: string): string {
|
|
return path.join(this.inboxRoot, documentId);
|
|
}
|
|
|
|
documentPdfPath(documentId: string): string {
|
|
return path.join(this.documentDir(documentId), 'document.pdf');
|
|
}
|
|
|
|
previewPath(documentId: string, page: number): string {
|
|
return path.join(this.documentDir(documentId), `page-${page}.preview.png`);
|
|
}
|
|
|
|
thumbnailPath(documentId: string, page: number): string {
|
|
return path.join(this.documentDir(documentId), `page-${page}.thumb.png`);
|
|
}
|
|
|
|
/**
|
|
* Übernimmt die bereits gerenderten 200-dpi-PNGs als preview.png und
|
|
* erzeugt parallel eine kleinere thumb.png pro Seite.
|
|
*/
|
|
async generate(documentId: string, renderedImages: string[]): Promise<void> {
|
|
const dir = this.documentDir(documentId);
|
|
await fs.mkdir(dir, { recursive: true });
|
|
|
|
for (let i = 0; i < renderedImages.length; i++) {
|
|
const page = i + 1;
|
|
const src = renderedImages[i];
|
|
const previewDest = this.previewPath(documentId, page);
|
|
const thumbDest = this.thumbnailPath(documentId, page);
|
|
|
|
try {
|
|
await fs.copyFile(src, previewDest);
|
|
await sharp(src)
|
|
.resize({ width: THUMBNAIL_WIDTH })
|
|
.png()
|
|
.toFile(thumbDest);
|
|
} catch (err: any) {
|
|
this.logger.warn(
|
|
`Seiten-Cache fehlgeschlagen (${documentId} Seite ${page}): ${err.message}`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Entfernt alle page-*.png-Dateien eines Dokuments (Original-PDF bleibt).
|
|
*/
|
|
async clear(documentId: string): Promise<void> {
|
|
const dir = this.documentDir(documentId);
|
|
let entries: string[];
|
|
try {
|
|
entries = await fs.readdir(dir);
|
|
} catch {
|
|
return;
|
|
}
|
|
|
|
for (const name of entries) {
|
|
if (!/^page-\d+\.(preview|thumb)\.png$/.test(name)) continue;
|
|
await fs.unlink(path.join(dir, name)).catch(() => undefined);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Verschiebt Page-Cache-Dateien nach dem Löschen einer Seite:
|
|
* page-N.*.png weg, page-(N+1..oldPageCount) rutschen um 1 nach vorne.
|
|
*/
|
|
async shiftAfterPageDelete(
|
|
documentId: string,
|
|
deletedPage: number,
|
|
oldPageCount: number,
|
|
): Promise<void> {
|
|
const dir = this.documentDir(documentId);
|
|
await fs
|
|
.unlink(path.join(dir, `page-${deletedPage}.thumb.png`))
|
|
.catch(() => undefined);
|
|
await fs
|
|
.unlink(path.join(dir, `page-${deletedPage}.preview.png`))
|
|
.catch(() => undefined);
|
|
|
|
for (let n = deletedPage + 1; n <= oldPageCount; n++) {
|
|
for (const variant of ['thumb', 'preview'] as const) {
|
|
const from = path.join(dir, `page-${n}.${variant}.png`);
|
|
const to = path.join(dir, `page-${n - 1}.${variant}.png`);
|
|
try {
|
|
await fs.rename(from, to);
|
|
} catch (err: any) {
|
|
this.logger.warn(
|
|
`Cache-Shift fehlgeschlagen (${documentId} Seite ${n} ${variant}): ${err.message}`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
async hasPreview(documentId: string, page: number): Promise<boolean> {
|
|
try {
|
|
await fs.access(this.previewPath(documentId, page));
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
}
|