Source: backend/models/modelFileReader.js

/**
 * @fileoverview Diese Datei enthält Funktionen zum Extrahieren von Textinhalten 
 * aus verschiedenen Dateiformaten. Sie unterstützt PDF, DOCX, Textdateien und Bilder (mittels OCR).
 * 
 * @author Miray
 * Die Funktionen wurden mit Unterstützung von KI-Tools angepasst und optimiert.
 * @module modelFileReader
 */

const { PythonShell } = require('python-shell');
const fs = require('fs').promises;
const path = require('path');
const mammoth = require('mammoth');
const { performOCR } = require('./modelOcr');
const { performance } = require('perf_hooks');

const pythonScriptPath = path.join(__dirname, 'pdf_extractor.py');

/**
 * Extrahiert Text aus einer PDF-Datei mithilfe eines externen Python-Skripts.
 * Führt mehrere Versuche durch, falls ein Fehler auftritt.
 *
 * @async
 * @function extractTextFromPDF
 * @param {Buffer} buffer - Der Dateiinhalt als Buffer.
 * @returns {Promise<string>} Der extrahierte Text aus dem PDF.
 * @throws {Error} Falls die Extraktion fehlschlägt oder das Python-Skript einen Fehler zurückgibt.
 * @example
 * const pdfBuffer = fs.readFileSync('document.pdf');
 * const text = await extractTextFromPDF(pdfBuffer);
 * console.log(text);
 */
async function extractTextFromPDF(buffer) {
    const maxRetries = 3;
    const retryDelay = 1000;

    for (let attempt = 1; attempt <= maxRetries; attempt++) {
        try {
            //console.log(`Attempt ${attempt} to extract text from PDF`);
            const pyshell = new PythonShell(pythonScriptPath, {
                mode: 'text',
                pythonOptions: ['-u'],
                stdinWithAck: true,
                encoding: 'utf-8'
            });

            const extractedText = await new Promise((resolve, reject) => {
                let textContent = '';
                pyshell.stdin.write(buffer.toString('base64'));
                pyshell.stdin.end();

                pyshell.on('message', (message) => {
                    textContent += message;
                });

                pyshell.end((err) => {
                    if (err) {
                        console.error(`Attempt ${attempt} failed:`, err);
                        reject(err);
                    } else {
                        resolve(textContent);
                    }
                });
            });

            if (extractedText.startsWith('ERROR:')) {
                throw new Error(extractedText);
            }

            return extractedText;
        } catch (error) {
            if (attempt === maxRetries) throw error;
            await new Promise(resolve => setTimeout(resolve, retryDelay));
        }
    }
}

/**
 * Extrahiert reinen Text aus einer DOCX-Datei.
 *
 * @async
 * @function extractTextFromDOCX
 * @param {Buffer} buffer - Der Dateiinhalt als Buffer.
 * @returns {Promise<string>} Der extrahierte Text aus dem DOCX-Dokument.
 * @example
 * const docxBuffer = fs.readFileSync('document.docx');
 * const text = await extractTextFromDOCX(docxBuffer);
 * console.log(text);
 */
async function extractTextFromDOCX(buffer) {
    const { value } = await mammoth.extractRawText({ buffer });
    return value;
}

const extractors = {
    'application/pdf': extractTextFromPDF,
    'application/vnd.openxmlformats-officedocument.wordprocessingml.document': extractTextFromDOCX,
    'text/plain': (buffer) => buffer.toString('utf-8'),
    'image/png': performOCR,
    'image/jpeg': performOCR
};

/**
 * Extrahiert den Textinhalt einer Datei basierend auf ihrem MIME-Typ.
 * Unterstützt PDF, DOCX, Textdateien und Bilder (OCR).
 *
 * @async
 * @function extractTextContent
 * @param {Buffer} buffer - Der Dateiinhalt als Buffer.
 * @param {string} mimetype - Der MIME-Typ der Datei.
 * @param {string} filename - Der Name der Datei (optional für Logging und OCR).
 * @returns {Promise<string>} Der extrahierte Textinhalt der Datei.
 * @throws {Error} Falls das Dateiformat nicht unterstützt wird oder die Extraktion fehlschlägt.
 * @example
 * const buffer = fs.readFileSync('image.png');
 * const text = await extractTextContent(buffer, 'image/png', 'image.png');
 * console.log(text);
 */
async function extractTextContent(buffer, mimetype, filename) {
    const startTime = performance.now();

    console.log(`Processing file: ${filename} (${mimetype})`);

    const extractor = extractors[mimetype];
    if (!extractor) {
        throw new Error('Unsupported file type');
    }

    let textContent;
    if (mimetype.startsWith('image/')) {
        const result = await extractor(buffer, filename);
        if (result.success) {
            textContent = result.text;
        } else {
            throw new Error(`OCR failed: ${result.error}`);
        }
    } else {
        textContent = await extractor(buffer, filename);
    }

    if (typeof textContent !== 'string') {
        throw new Error(`Unexpected result type from extractor: ${typeof textContent}`);
    }

    console.log(`Successfully extracted text from ${filename}`);
    console.log(`Preview of extracted text (first 1000 characters):`);
    console.log(textContent.substring(0, 1000));
    //console.log(`... (${textContent.length} total characters extracted)`);

    const endTime = performance.now();
    const processingTime = (endTime - startTime).toFixed(2);
    console.log(`Reading file time: ${processingTime} ms`);

    return textContent;
}

module.exports = { extractTextContent };