gfathertech commited on
Commit
aca8585
ยท
verified ยท
1 Parent(s): f582e08

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +45 -45
app.py CHANGED
@@ -15,17 +15,16 @@ app = FastAPI(title="Free MarkItDown Service")
15
  md = MarkItDown()
16
 
17
  IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".bmp", ".tiff", ".webp"}
 
18
 
19
  def run_free_ocr(file_path: str) -> str:
20
- """Fallback OCR pipeline using Tesseract (100% free)."""
21
  file_ext = os.path.splitext(file_path)[1].lower()
22
 
23
- # Image OCR
24
  if file_ext in IMAGE_EXTENSIONS:
25
  img = Image.open(file_path)
26
  return pytesseract.image_to_string(img)
27
 
28
- # PDF OCR (page by page)
29
  elif file_ext == ".pdf":
30
  pages = convert_from_path(file_path)
31
  extracted_text = []
@@ -37,14 +36,26 @@ def run_free_ocr(file_path: str) -> str:
37
 
38
  return ""
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  def process_input(source_path_or_url: str, original_filename: str = None) -> str:
41
- """Unified document converter with explicit fallback handling."""
42
  if not source_path_or_url or not source_path_or_url.strip():
43
  return "Please supply a valid file or URL."
44
 
45
  target = source_path_or_url.strip()
46
 
47
- # If it's a URL, let MarkItDown fetch and parse it directly
48
  if target.startswith("http://") or target.startswith("https://"):
49
  try:
50
  res = md.convert(target)
@@ -52,51 +63,41 @@ def process_input(source_path_or_url: str, original_filename: str = None) -> str
52
  except Exception as e:
53
  return f"URL Conversion Error: {str(e)}"
54
 
55
- # Identify true file extension
56
- ext = ""
57
- if original_filename:
58
- ext = os.path.splitext(original_filename)[1].lower()
59
- else:
60
- ext = os.path.splitext(target)[1].lower()
61
-
62
- # Ensure the file path on disk has the correct file extension
63
  working_path = target
64
- temp_renamed = None
65
  if ext and not target.lower().endswith(ext):
66
- temp_renamed = f"{target}{ext}"
67
- shutil.copyfile(target, temp_renamed)
68
- working_path = temp_renamed
69
 
70
- converted_docx = None
71
 
72
  try:
73
- # Route 1: Directly handle pure image files using local Tesseract OCR
74
  if ext in IMAGE_EXTENSIONS:
75
  return run_free_ocr(working_path)
76
 
77
- # Route 2: Convert legacy binary .doc files using LibreOffice
78
- if ext == ".doc":
79
- out_dir = "/tmp"
80
- subprocess.run(
81
- ["libreoffice", "--headless", "--convert-to", "docx", working_path, "--outdir", out_dir],
82
- check=True
83
- )
84
- converted_docx = os.path.join(out_dir, os.path.splitext(os.path.basename(working_path))[0] + ".docx")
85
- working_path = converted_docx
86
-
87
- # Route 3: Standard MarkItDown extraction
88
  result = md.convert(working_path)
89
  text_content = result.text_content if result else ""
90
 
91
- # Fallback to Tesseract OCR if MarkItDown extracts no text
92
- if not text_content or not text_content.strip():
93
  text_content = run_free_ocr(working_path)
94
 
95
  return text_content if text_content.strip() else "Unable to extract text from document."
96
 
97
- except (FileConversionException, Exception) as e:
98
- # If MarkItDown fails to parse the file format, run Tesseract OCR as final safety net
99
- print(f"MarkItDown failed on '{working_path}'. Attempting Tesseract OCR fallback...")
100
  try:
101
  ocr_text = run_free_ocr(working_path)
102
  if ocr_text and ocr_text.strip():
@@ -106,17 +107,17 @@ def process_input(source_path_or_url: str, original_filename: str = None) -> str
106
 
107
  print("--- CONVERSION ERROR ---")
108
  traceback.print_exc()
109
- return f"Conversion Error: {str(e)}"
110
 
111
  finally:
112
- # Clean up temporary renamed or converted files
113
- if temp_renamed and os.path.exists(temp_renamed):
114
- os.remove(temp_renamed)
115
- if converted_docx and os.path.exists(converted_docx):
116
- os.remove(converted_docx)
117
 
118
  # -------------------------------------------------------------
119
- # 1. FastAPI Endpoint (For TypeScript backend calls)
120
  # -------------------------------------------------------------
121
  @app.post("/convert")
122
  async def convert_file_api(file: UploadFile = None, url: str = None):
@@ -144,11 +145,11 @@ async def convert_file_api(file: UploadFile = None, url: str = None):
144
  os.remove(temp_path)
145
 
146
  # -------------------------------------------------------------
147
- # 2. Gradio Web Interface (For browser uploads)
148
  # -------------------------------------------------------------
149
  def gradio_handler(file_obj, url_input):
150
  if file_obj is not None:
151
- # Pass file_obj.name (disk path) along with original filename metadata
152
  orig_name = getattr(file_obj, "orig_name", file_obj.name)
153
  return process_input(file_obj.name, original_filename=orig_name)
154
  elif url_input and url_input.strip():
@@ -157,7 +158,6 @@ def gradio_handler(file_obj, url_input):
157
 
158
  with gr.Blocks(title="Free MarkItDown Tester") as demo:
159
  gr.Markdown("# ๐Ÿ“„ Free Document & URL Converter")
160
- gr.Markdown("Upload a document (`.pdf`, `.doc`, `.docx`, `.xlsx`, `.pptx`, `.png`, `.jpg`) or enter a public URL.")
161
 
162
  with gr.Row():
163
  with gr.Column():
 
15
  md = MarkItDown()
16
 
17
  IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".bmp", ".tiff", ".webp"}
18
+ LEGACY_OFFICE_EXTENSIONS = {".doc", ".xls", ".ppt", ".rtf"}
19
 
20
  def run_free_ocr(file_path: str) -> str:
21
+ """Fallback OCR pipeline using Tesseract."""
22
  file_ext = os.path.splitext(file_path)[1].lower()
23
 
 
24
  if file_ext in IMAGE_EXTENSIONS:
25
  img = Image.open(file_path)
26
  return pytesseract.image_to_string(img)
27
 
 
28
  elif file_ext == ".pdf":
29
  pages = convert_from_path(file_path)
30
  extracted_text = []
 
36
 
37
  return ""
38
 
39
+ def convert_legacy_office_file(file_path: str, ext: str) -> str:
40
+ """Converts legacy .doc, .xls, .ppt to modern XML format via LibreOffice."""
41
+ out_dir = "/tmp"
42
+ target_ext = "docx" if ext == ".doc" else "xlsx" if ext == ".xls" else "pptx"
43
+
44
+ subprocess.run(
45
+ ["libreoffice", "--headless", "--convert-to", target_ext, file_path, "--outdir", out_dir],
46
+ check=True
47
+ )
48
+
49
+ base_name = os.path.splitext(os.path.basename(file_path))[0]
50
+ return os.path.join(out_dir, f"{base_name}.{target_ext}")
51
+
52
  def process_input(source_path_or_url: str, original_filename: str = None) -> str:
 
53
  if not source_path_or_url or not source_path_or_url.strip():
54
  return "Please supply a valid file or URL."
55
 
56
  target = source_path_or_url.strip()
57
 
58
+ # If HTTP/HTTPS URL
59
  if target.startswith("http://") or target.startswith("https://"):
60
  try:
61
  res = md.convert(target)
 
63
  except Exception as e:
64
  return f"URL Conversion Error: {str(e)}"
65
 
66
+ # 1. Detect extension from the original filename (e.g., 'document.doc')
67
+ ext = os.path.splitext(original_filename or target)[1].lower()
68
+
69
+ # 2. FORCE the temp file on disk to have the real extension so MarkItDown knows what it is!
 
 
 
 
70
  working_path = target
71
+ temp_with_ext = None
72
  if ext and not target.lower().endswith(ext):
73
+ temp_with_ext = f"{target}{ext}"
74
+ shutil.copyfile(target, temp_with_ext)
75
+ working_path = temp_with_ext
76
 
77
+ temp_converted_office = None
78
 
79
  try:
80
+ # Route Images directly to Tesseract
81
  if ext in IMAGE_EXTENSIONS:
82
  return run_free_ocr(working_path)
83
 
84
+ # Route Legacy Office files (.doc) to LibreOffice first
85
+ if ext in LEGACY_OFFICE_EXTENSIONS:
86
+ temp_converted_office = convert_legacy_office_file(working_path, ext)
87
+ working_path = temp_converted_office
88
+
89
+ # Standard MarkItDown extraction
 
 
 
 
 
90
  result = md.convert(working_path)
91
  text_content = result.text_content if result else ""
92
 
93
+ # Fallback to Tesseract if extraction yielded blank text
94
+ if not text_content.strip():
95
  text_content = run_free_ocr(working_path)
96
 
97
  return text_content if text_content.strip() else "Unable to extract text from document."
98
 
99
+ except Exception as e:
100
+ print(f"MarkItDown failed on '{working_path}'. Executing Tesseract OCR fallback...")
 
101
  try:
102
  ocr_text = run_free_ocr(working_path)
103
  if ocr_text and ocr_text.strip():
 
107
 
108
  print("--- CONVERSION ERROR ---")
109
  traceback.print_exc()
110
+ return f"Conversion Error: File type '{ext}' could not be processed."
111
 
112
  finally:
113
+ # Cleanup temporary files
114
+ if temp_with_ext and os.path.exists(temp_with_ext):
115
+ os.remove(temp_with_ext)
116
+ if temp_converted_office and os.path.exists(temp_converted_office):
117
+ os.remove(temp_converted_office)
118
 
119
  # -------------------------------------------------------------
120
+ # FastAPI Endpoint
121
  # -------------------------------------------------------------
122
  @app.post("/convert")
123
  async def convert_file_api(file: UploadFile = None, url: str = None):
 
145
  os.remove(temp_path)
146
 
147
  # -------------------------------------------------------------
148
+ # Gradio Handler
149
  # -------------------------------------------------------------
150
  def gradio_handler(file_obj, url_input):
151
  if file_obj is not None:
152
+ # Extract the real filename (e.g. "my_doc.pdf" instead of "/tmp/gradio/xyz123")
153
  orig_name = getattr(file_obj, "orig_name", file_obj.name)
154
  return process_input(file_obj.name, original_filename=orig_name)
155
  elif url_input and url_input.strip():
 
158
 
159
  with gr.Blocks(title="Free MarkItDown Tester") as demo:
160
  gr.Markdown("# ๐Ÿ“„ Free Document & URL Converter")
 
161
 
162
  with gr.Row():
163
  with gr.Column():