@fdm-monster/server
Version:
FDM Monster is a bulk OctoPrint, Klipper, PrusaLink and BambuLab manager to set up, configure and monitor 3D printers. Our aim is to provide neat overview over your farm.
90 lines (89 loc) • 3.84 kB
JavaScript
import { PrintJob } from "../entities/print-job.entity.js";
//#region src/tasks/print-job-analysis.task.ts
/**
* Background task to analyze pending print jobs
* Runs periodically to extract metadata from uploaded files
*/
var PrintJobAnalysisTask = class PrintJobAnalysisTask {
logger;
printJobRepository;
constructor(loggerFactory, printJobService, fileAnalysisService, fileStorageService, typeormService) {
this.printJobService = printJobService;
this.fileAnalysisService = fileAnalysisService;
this.fileStorageService = fileStorageService;
this.logger = loggerFactory(PrintJobAnalysisTask.name);
this.printJobRepository = typeormService.getDataSource().getRepository(PrintJob);
}
async run() {
try {
const pendingJobs = await this.printJobRepository.find({
where: [{
analysisState: "NOT_ANALYZED",
status: "PENDING"
}, { analysisState: "ANALYZING" }],
take: 10
});
if (pendingJobs.length === 0) return;
this.logger.log(`Found ${pendingJobs.length} print job(s) to analyze`);
for (const job of pendingJobs) try {
await this.analyzeJob(job);
} catch (error) {
this.logger.error(`Failed to analyze job ${job.id}: ${job.fileName}`, error);
job.analysisState = "FAILED";
job.statusReason = `Analysis failed: ${error instanceof Error ? error.message : "Unknown error"}`;
await this.printJobRepository.save(job);
}
this.logger.log(`Completed analysis of ${pendingJobs.length} print job(s)`);
} catch (error) {
this.logger.error("Failed to run print job analysis task", error);
}
}
async analyzeJob(job) {
this.logger.log(`Analyzing print job ${job.id}: ${job.fileName}`);
job.analysisState = "ANALYZING";
await this.printJobRepository.save(job);
if (!job.fileStorageId) throw new Error("Job has no fileStorageId - cannot analyze");
const cachedMetadata = await this.fileStorageService.loadMetadata(job.fileStorageId);
let metadata;
let thumbnails = [];
if (cachedMetadata) {
this.logger.log(`Using cached metadata for job ${job.id} (storageId: ${job.fileStorageId})`);
metadata = cachedMetadata;
thumbnails = [];
} else {
const filePath = await this.resolveFilePath(job);
if (!filePath) throw new Error("File path could not be resolved");
if (!await this.fileAnalysisService.needsAnalysis(filePath)) throw new Error(`File not found: ${filePath}`);
const analysisResult = await this.fileAnalysisService.analyzeFile(filePath);
metadata = analysisResult.metadata;
thumbnails = analysisResult.thumbnails;
let thumbnailMetadata = [];
if (thumbnails && thumbnails.length > 0) {
thumbnailMetadata = await this.fileStorageService.saveThumbnails(job.fileStorageId, thumbnails);
this.logger.log(`Saved ${thumbnailMetadata.length} thumbnail(s) for job ${job.id}`);
}
const fileHash = job.fileHash || void 0;
await this.fileStorageService.saveMetadata(job.fileStorageId, metadata, fileHash, job.fileName, thumbnailMetadata);
this.logger.log(`Cached metadata JSON for job ${job.id}`);
}
await this.printJobService.handleFileAnalyzed(job.id, metadata, thumbnails);
this.logger.log(`Successfully analyzed job ${job.id}: ${job.fileName}`);
}
async resolveFilePath(job) {
if (job.fileStorageId) try {
if (!await this.fileStorageService.fileExists(job.fileStorageId)) {
this.logger.warn(`File ${job.fileStorageId} not found in storage for job ${job.id}`);
return null;
}
return this.fileStorageService.getFilePath(job.fileStorageId);
} catch (error) {
this.logger.error(`Failed to resolve file path for job ${job.id}: ${error}`);
return null;
}
this.logger.debug(`Job ${job.id} has no fileStorageId - cannot analyze remotely stored file`);
return null;
}
};
//#endregion
export { PrintJobAnalysisTask };
//# sourceMappingURL=print-job-analysis.task.js.map