Files
bookish/bookish/services/converter.py
T

148 lines
4.5 KiB
Python

import os
import logging
import pypandoc
from config import SUPPORTED_FORMATS
from bookish.services.sse_manager import update_progress, mark_complete, mark_error
logger = logging.getLogger(__name__)
def get_format_from_ext(filename):
ext = os.path.splitext(filename)[1].lower().lstrip(".")
if ext in SUPPORTED_FORMATS:
return ext
# mappa alias comuni
alias = {"markdown": "md", "text": "txt", "latex": "latex", "tex": "latex"}
return alias.get(ext, ext)
def run_conversion(input_path, output_path, input_format, output_format, task_id, filename):
"""
Esegue la conversione con Pandoc, aggiornando il progresso via SSE.
Per alcuni formati speciali delega a formatter.py.
"""
try:
update_progress(task_id, 10, "analyzing")
_validate_pandoc_format(input_format, output_format)
update_progress(task_id, 30, "converting")
if input_format == "docx" and output_format == "pdf":
_convert_docx_to_pdf(input_path, output_path, task_id)
elif output_format == "pdf":
_convert_to_pdf(input_path, output_path, input_format, task_id)
else:
_convert_with_pandoc(input_path, output_path, input_format, output_format, task_id)
update_progress(task_id, 90, "finalizing")
update_progress(task_id, 100)
mark_complete(task_id, os.path.basename(output_path))
except Exception as e:
logger.exception("Conversione fallita: %s -> %s (%s)", input_format, output_format, filename)
mark_error(task_id, str(e))
def _validate_pandoc_format(input_format, output_format):
"""Verifica che Pandoc supporti input/output format."""
try:
readers = pypandoc.get_pandoc_formats()[0]
writers = pypandoc.get_pandoc_formats()[1]
except Exception as e:
raise RuntimeError(f"Errore Pandoc: {e}. Installalo con: pip install pypandoc-binary")
pandoc_input = _to_pandoc_format(input_format)
pandoc_output = _to_pandoc_format(output_format)
if pandoc_input not in readers:
raise ValueError(f"Formato input '{input_format}' non supportato da Pandoc")
if pandoc_output not in writers:
raise ValueError(f"Formato output '{output_format}' non supportato da Pandoc")
def _to_pandoc_format(fmt):
mapping = {
"md": "markdown",
"latex": "latex",
"tex": "latex",
"html": "html",
"epub": "epub",
"pdf": "pdf",
"docx": "docx",
"txt": "plain",
"fb2": "fb2",
"mobi": "mobi",
}
return mapping.get(fmt, fmt)
def _convert_with_pandoc(input_path, output_path, input_format, output_format, task_id):
extra_args = ["--wrap=preserve"]
if output_format == "epub":
extra_args += ["--metadata", "title=Converted Document"]
if output_format == "html":
extra_args += ["--self-contained", "--mathjax"]
pandoc_input = _to_pandoc_format(input_format)
pandoc_output = _to_pandoc_format(output_format)
def callback(percent):
scaled = 30 + int(percent * 0.5)
update_progress(task_id, min(scaled, 85))
pypandoc.convert_file(
input_path,
pandoc_output,
format=pandoc_input,
outputfile=output_path,
extra_args=extra_args,
)
def _convert_to_pdf(input_path, output_path, input_format, task_id):
"""Converte in PDF usando Pandoc con WeasyPrint."""
from bookish.services.formatter import html_to_pdf
update_progress(task_id, 40, "converting")
html_path = output_path.replace(".pdf", ".html")
pandoc_input = _to_pandoc_format(input_format)
pypandoc.convert_file(
input_path,
"html",
format=pandoc_input,
outputfile=html_path,
extra_args=["--wrap=preserve", "--self-contained"],
)
update_progress(task_id, 65, "converting")
html_to_pdf(html_path, output_path)
if os.path.exists(html_path):
os.remove(html_path)
def _convert_docx_to_pdf(input_path, output_path, task_id):
"""DOCX → PDF via Pandoc → HTML → WeasyPrint."""
from bookish.services.formatter import html_to_pdf
update_progress(task_id, 40, "converting")
html_path = output_path.replace(".pdf", ".html")
pypandoc.convert_file(
input_path,
"html",
format="docx",
outputfile=html_path,
extra_args=["--wrap=preserve"],
)
update_progress(task_id, 65, "converting")
html_to_pdf(html_path, output_path)
if os.path.exists(html_path):
os.remove(html_path)