File size: 6,875 Bytes
5e28e09
 
78ad12f
5e28e09
 
 
5530d18
2b7e158
 
94ae36a
2b7e158
964278d
2b7e158
 
94ae36a
 
2b7e158
94ae36a
 
2b7e158
94ae36a
5e28e09
9e8b5e4
 
 
7bbe6c6
29d547d
5e28e09
 
32b0a3f
 
71c6d61
 
 
9406f46
71c6d61
 
2b7e158
9406f46
2b7e158
 
964278d
 
 
 
 
5e28e09
94ae36a
9406f46
7569d3f
2b7e158
5e28e09
964278d
 
 
2b7e158
964278d
9406f46
7569d3f
5e28e09
94ae36a
 
9406f46
94ae36a
 
964278d
32b0a3f
29d547d
964278d
 
 
 
 
5e28e09
cf5e475
7bbe6c6
9e8b5e4
 
 
a67820e
ddb1cd6
a67820e
2b7e158
29d547d
2b7e158
29d547d
9e8b5e4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7bbe6c6
964278d
32b0a3f
7bbe6c6
9406f46
32b0a3f
 
 
 
964278d
 
 
 
 
32b0a3f
9e8b5e4
964278d
7bbe6c6
32b0a3f
 
 
964278d
 
32b0a3f
964278d
 
 
 
9e8b5e4
9406f46
964278d
 
7bbe6c6
5e28e09
 
 
 
 
2b7e158
5530d18
29d547d
a67820e
2b7e158
5530d18
 
 
32b0a3f
 
 
5e28e09
7569d3f
9e8b5e4
94ae36a
5e28e09
 
2b7e158
9e8b5e4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import os
import shutil
import subprocess
import requests
import gradio as gr
from urllib.parse import urlparse
import time
import re

def sanear_nombre(nombre):
    nombre_base = os.path.splitext(nombre)[0]
    return re.sub(r"[^\w\-_.]", "_", nombre_base)

def obtener_ruta_segura(base_dir, nombre_archivo):
    nombre_base = sanear_nombre(nombre_archivo)
    ruta_final = os.path.join(base_dir, nombre_base)
    contador = 1
    while os.path.exists(ruta_final + ".pdf"):
        ruta_final = os.path.join(base_dir, f"{nombre_base}_{contador}")
        contador += 1
    return ruta_final + ".pdf"

def ejecutar_marker(cmd):
    result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=900)
    return result

def procesar_pdf(pdf_file=None, url_pdf=None, formatos=[], verbose="auto"):
    output_base = "./marker_output"
    os.makedirs(output_base, exist_ok=True)
    estado = ""

    try:
        subprocess.run(["marker_single", "--help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    except FileNotFoundError:
        yield "Error: el comando 'marker_single' no está disponible.\nVerifica que marker-pdf esté en requirements.txt", None, None
        return

    if not formatos:
        yield "Debes seleccionar al menos un formato de salida.", None, None
        return

    try:
        if url_pdf:
            parsed = urlparse(url_pdf)
            nombre_pdf = parsed.path.split("/")[-1] or "documento"
            ruta_pdf = obtener_ruta_segura(output_base, nombre_pdf)
            response = requests.get(url_pdf)
            if response.status_code != 200:
                yield "No se pudo descargar el PDF desde la URL proporcionada.", None, None
                return
            with open(ruta_pdf, "wb") as f:
                f.write(response.content)
        elif pdf_file:
            nombre_pdf = pdf_file.name or "documento"
            ruta_pdf = obtener_ruta_segura(output_base, nombre_pdf)
            shutil.copyfile(pdf_file.name, ruta_pdf)
        else:
            yield "No se proporcionó ni archivo ni URL.", None, None
            return

        with open(ruta_pdf, "rb") as f:
            if f.read(4) != b"%PDF":
                yield "El archivo proporcionado no es un PDF válido.", None, None
                return

        if os.path.getsize(ruta_pdf) > 3 * 1024 * 1024:
            estado += "Aviso: el PDF es grande y puede tardar más de lo normal.\n"

        nombre_sin_ext = os.path.splitext(os.path.basename(ruta_pdf))[0]
        carpeta_salida = os.path.join(output_base, nombre_sin_ext)
        if os.path.exists(carpeta_salida):
            shutil.rmtree(carpeta_salida)
        os.makedirs(carpeta_salida, exist_ok=True)

        for fmt in formatos:
            estado += f"\nProcesando formato '{fmt}'..."
            yield estado, None, None

            # Primer intento sin OCR (si aplica)
            if fmt == "md":
                cmd = ["marker_single", ruta_pdf, "--output_format", "markdown", "--disable_ocr", "--output_dir", carpeta_salida]
            elif fmt == "md + ocr":
                cmd = ["marker_single", ruta_pdf, "--output_format", "markdown", "--output_dir", carpeta_salida]
            else:
                cmd = ["marker_single", ruta_pdf, "--output_format", fmt, "--output_dir", carpeta_salida]

            result = ejecutar_marker(cmd)

            md_path = os.path.join(carpeta_salida, f"{nombre_sin_ext}.md")
            if fmt == "md" and not os.path.exists(md_path):
                estado += "\nNo se generó el archivo Markdown en el primer intento."
                estado += f"\n\n---\nSalida del comando marker_single:\n{result.stderr.strip()}\n"
                estado += "\nReintentando con OCR..."
                yield estado, None, None

                start_time = time.time()
                cmd = ["marker_single", ruta_pdf, "--output_format", "markdown", "--output_dir", carpeta_salida]
                result = ejecutar_marker(cmd)

                if not os.path.exists(md_path):
                    estado += "\nEl reintento con OCR también falló."
                    estado += f"\n\n---\nSalida del reintento:\n{result.stderr.strip()}"
                    yield estado, None, None
                    return

            elif result.returncode != 0:
                estado += f"\n\nError ejecutando marker_single:\n{result.stderr.strip()}"
                yield estado, None, None
                return

        if not os.path.exists(md_path):
            estado += "\nNo se generó el archivo Markdown. Puede que el PDF no contenga texto reconocible."
            yield estado, None, None
            return

        with open(md_path, "r", encoding="utf-8") as f:
            md_content = f.read()

        palabras = len(md_content.split())
        caracteres = len(md_content)
        figuras = len([f for f in os.listdir(carpeta_salida) if f.lower().endswith(".jpeg")])
        tablas = len(os.listdir(os.path.join(carpeta_salida, "tables"))) if os.path.exists(os.path.join(carpeta_salida, "tables")) else 0
        tamaño_kb = os.path.getsize(md_path) / 1024
        tiempo_total = round(time.time() - start_time, 1)

        resumen = f"\n\n---\nResumen del procesamiento:\n"
        resumen += f"- Palabras: {palabras}\n"
        resumen += f"- Caracteres: {caracteres}\n"
        resumen += f"- Tamaño Markdown: {tamaño_kb:.2f} KB\n"
        resumen += f"- Imágenes extraídas: {figuras}\n"
        resumen += f"- Tablas detectadas: {tablas}\n"
        resumen += f"- Tiempo total: {tiempo_total} s"

        zip_path = os.path.join(output_base, f"{nombre_sin_ext}.zip")
        shutil.make_archive(base_name=zip_path.replace(".zip", ""), format="zip", root_dir=carpeta_salida)

        estado += "\n\nProcesamiento finalizado correctamente."
        yield estado + resumen, gr.update(value=md_path, visible=True), gr.update(value=zip_path, visible=True)

    except Exception as e:
        yield f"Error general inesperado: {str(e)}", None, None

demo = gr.Interface(
    fn=procesar_pdf,
    inputs=[
        gr.File(label="Sube un PDF (opcional)", file_types=[".pdf"]),
        gr.Textbox(label="O introduce una URL directa", placeholder="https://arxiv.org/pdf/..."),
        gr.CheckboxGroup(
            choices=["md", "md + ocr", "html", "json"],
            value=["md"],
            label="Formatos de salida (elige al menos uno)"
        )
    ],
    outputs=[
        gr.Textbox(label="Estado del procesamiento", lines=12),
        gr.File(label="Descargar Markdown (.md)", visible=False),
        gr.File(label="Descargar ZIP completo", visible=False)
    ],
    title="Marker PDF",
    description="Convierte artículos científicos en Markdown, HTML o JSON. Reintenta automáticamente con OCR si es necesario.",
    flagging_mode="never"
)

if __name__ == "__main__":
    demo.launch()