refactor(previews): streamline preview generation and update image variant to 400px

Remove temporary working directory usage and in-place file writes from preview
generation, simplifying logic for both PDF and EPUB previews. Change default
preview image variant from 480px to 400px width, updating related constants and
cache schema version to maintain consistency across client and server code.
This commit is contained in:
Richard R 2026-05-18 12:37:26 -06:00
parent 8af4e857b7
commit 90e7caac43
3 changed files with 14 additions and 31 deletions

View file

@ -8,7 +8,7 @@ import { documentPreviewFallbackUrl, documentPreviewPresignUrl } from '@/lib/cli
const inMemoryPreviewUrlCache = new Map<string, string>(); const inMemoryPreviewUrlCache = new Map<string, string>();
const inFlightPreviewPrime = new Map<string, Promise<string | null>>(); const inFlightPreviewPrime = new Map<string, Promise<string | null>>();
const PREVIEW_CACHE_SCHEMA_VERSION = 3; const PREVIEW_CACHE_SCHEMA_VERSION = 4;
function revokeIfBlobUrl(url: string | null | undefined): void { function revokeIfBlobUrl(url: string | null | undefined): void {
if (!url) return; if (!url) return;

View file

@ -10,10 +10,10 @@ import { getS3Client, getS3Config, getS3ProxyClient } from '@/lib/server/storage
const SAFE_NAMESPACE_REGEX = /^[a-zA-Z0-9._-]{1,128}$/; const SAFE_NAMESPACE_REGEX = /^[a-zA-Z0-9._-]{1,128}$/;
const DEFAULT_NAMESPACE_SEGMENT = '_default'; const DEFAULT_NAMESPACE_SEGMENT = '_default';
export const DOCUMENT_PREVIEW_VARIANT = 'card-480-jpeg'; export const DOCUMENT_PREVIEW_VARIANT = 'card-400-jpeg';
export const DOCUMENT_PREVIEW_FILE_NAME = 'card-480.jpg'; export const DOCUMENT_PREVIEW_FILE_NAME = 'card-400.jpg';
export const DOCUMENT_PREVIEW_CONTENT_TYPE = 'image/jpeg'; export const DOCUMENT_PREVIEW_CONTENT_TYPE = 'image/jpeg';
export const DOCUMENT_PREVIEW_WIDTH = 480; export const DOCUMENT_PREVIEW_WIDTH = 400;
function sanitizeNamespace(namespace: string | null): string | null { function sanitizeNamespace(namespace: string | null): string | null {
if (!namespace) return null; if (!namespace) return null;

View file

@ -1,7 +1,4 @@
import { randomUUID } from 'crypto'; import { randomUUID } from 'crypto';
import { mkdtemp, rm, writeFile } from 'fs/promises';
import { tmpdir } from 'os';
import { join } from 'path';
import { and, eq, inArray, lt, or, sql } from 'drizzle-orm'; import { and, eq, inArray, lt, or, sql } from 'drizzle-orm';
import { db } from '@/db'; import { db } from '@/db';
import { documentPreviews } from '@/db/schema'; import { documentPreviews } from '@/db/schema';
@ -290,31 +287,17 @@ async function markPreviewFailed(docId: string, namespaceKey: string, error: unk
} }
async function generateAndStorePreview(doc: PreviewSourceDocument, namespace: string | null): Promise<void> { async function generateAndStorePreview(doc: PreviewSourceDocument, namespace: string | null): Promise<void> {
let workDir: string | null = null; const sourceBytes = await getDocumentBlob(doc.id, namespace);
try { let rendered;
const sourceBytes = await getDocumentBlob(doc.id, namespace); if (doc.type === 'pdf') {
workDir = await mkdtemp(join(tmpdir(), 'openreader-preview-')); rendered = await renderPdfFirstPageToJpeg(sourceBytes, DOCUMENT_PREVIEW_WIDTH);
const sourcePath = join(workDir, 'source'); } else if (doc.type === 'epub') {
await writeFile(sourcePath, sourceBytes); rendered = await renderEpubCoverToJpeg(sourceBytes, DOCUMENT_PREVIEW_WIDTH);
} else {
let rendered; throw new Error(`Unsupported preview type: ${doc.type}`);
if (doc.type === 'pdf') {
rendered = await renderPdfFirstPageToJpeg(sourceBytes, DOCUMENT_PREVIEW_WIDTH);
} else if (doc.type === 'epub') {
rendered = await renderEpubCoverToJpeg(sourceBytes, DOCUMENT_PREVIEW_WIDTH);
} else {
throw new Error(`Unsupported preview type: ${doc.type}`);
}
// Replace-in-place semantics: clear old preview objects for this document
// prefix before writing the current variant so stale files don't linger.
await deleteDocumentPreviewArtifacts(doc.id, namespace).catch(() => undefined);
await putDocumentPreviewBuffer(doc.id, rendered.bytes, namespace);
} finally {
if (workDir) {
await rm(workDir, { recursive: true, force: true }).catch(() => {});
}
} }
// Hot path: overwrite current variant key directly to avoid prefix list/delete latency.
await putDocumentPreviewBuffer(doc.id, rendered.bytes, namespace);
} }
function pendingResult(status: PreviewStatus, lastError: string | null): EnsureDocumentPreviewResult { function pendingResult(status: PreviewStatus, lastError: string | null): EnsureDocumentPreviewResult {