import type { WorkerJobRecord } from "@/lib/types"; type WorkerProgressShape = { phase?: string; message?: string; total_chunks?: number; processed_chunks?: number; total_batches?: number; current_batch?: number; batch_size?: number; percent?: number; }; function asRecord(value: unknown): Record | null { if (!value || typeof value !== "object" || Array.isArray(value)) { return null; } return value as Record; } function asNumber(value: unknown): number | null { if (typeof value === "number" && Number.isFinite(value)) { return value; } if (typeof value === "string") { const parsed = Number(value); return Number.isFinite(parsed) ? parsed : null; } return null; } function asText(value: unknown): string | null { return typeof value === "string" && value.trim() ? value.trim() : null; } export function getWorkerProgress( job: Pick, ): WorkerProgressShape | null { const result = asRecord(job.result); if (!result) { return null; } const nested = asRecord(result.progress); const source = nested ?? result; const percent = asNumber(source.percent); const totalChunks = asNumber(source.total_chunks); const processedChunks = asNumber(source.processed_chunks); const totalBatches = asNumber(source.total_batches); const currentBatch = asNumber(source.current_batch); const batchSize = asNumber(source.batch_size); const phase = asText(source.phase) ?? asText(result.phase) ?? undefined; const message = asText(source.message) ?? asText(result.message) ?? undefined; if ( percent === null && totalChunks === null && processedChunks === null && totalBatches === null && currentBatch === null && batchSize === null && !phase && !message ) { return null; } return { phase, message, total_chunks: totalChunks ?? undefined, processed_chunks: processedChunks ?? undefined, total_batches: totalBatches ?? undefined, current_batch: currentBatch ?? undefined, batch_size: batchSize ?? undefined, percent: percent ?? undefined, }; } export function formatWorkerProgress( job: Pick, ): string | null { const progress = getWorkerProgress(job); if (!progress) { return null; } const percentText = typeof progress.percent === "number" ? `${Math.max(0, Math.min(100, Math.round(progress.percent)))}%` : null; const chunkText = typeof progress.processed_chunks === "number" && typeof progress.total_chunks === "number" ? `${progress.processed_chunks}/${progress.total_chunks} 分块` : null; const batchText = typeof progress.current_batch === "number" && typeof progress.total_batches === "number" && progress.total_batches > 0 ? `第 ${progress.current_batch}/${progress.total_batches} 批` : null; const details = [percentText, chunkText, batchText] .filter(Boolean) .join(" · "); if (progress.message && details) { return `${progress.message} ${details}`; } return progress.message ?? (details || null); } export function getWorkerProgressPercent( job: Pick, ): number | null { const progress = getWorkerProgress(job); if (typeof progress?.percent !== "number") { return null; } return Math.max(0, Math.min(100, Math.round(progress.percent))); }