andevs commited on
Commit
0dcdbd4
Β·
verified Β·
1 Parent(s): 316ee25

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +162 -366
app.py CHANGED
@@ -7,16 +7,9 @@ import io
7
  import requests
8
  from PIL import Image
9
  import json
10
- import textwrap
11
  import time
12
  from datetime import datetime
13
- import tempfile
14
- import subprocess
15
- import shutil
16
- import re
17
- import base64
18
- from typing import Optional, List
19
- import hashlib
20
 
21
  app = FastAPI(title="CONVERTLY API", version="2.0.0")
22
 
@@ -29,14 +22,14 @@ app.add_middleware(
29
  )
30
 
31
  HF_TOKEN = os.getenv("HF_TOKEN", "")
32
- MAX_FILE_SIZE = 100 * 1024 * 1024
33
 
34
  # Allowed formats
35
  ALLOWED_FORMATS = {
36
  'document': ['pdf', 'docx', 'doc', 'txt', 'rtf', 'odt', 'html', 'md'],
37
  'image': ['png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp', 'tiff', 'ico', 'svg'],
38
- 'video': ['mp4', 'webm', 'avi', 'mov', 'mkv', 'wmv', 'flv', '3gp'],
39
- 'audio': ['mp3', 'wav', 'flac', 'aac', 'ogg', 'm4a', 'wma'],
40
  'archive': ['zip', 'rar', '7z', 'tar', 'gz'],
41
  'ebook': ['epub', 'mobi', 'azw3'],
42
  'data': ['json', 'csv', 'xml', 'yaml', 'sql'],
@@ -154,35 +147,22 @@ def create_clean_pdf(text_content: str) -> bytes:
154
  story = []
155
 
156
  if text_content:
157
- paragraphs = []
158
- raw_paragraphs = text_content.split('\n\n')
159
 
160
- for para in raw_paragraphs:
161
  cleaned = para.strip()
162
  if cleaned:
163
  cleaned = ' '.join(cleaned.split())
164
- paragraphs.append(cleaned)
165
-
166
- for i, para in enumerate(paragraphs):
167
- is_heading = len(para) < 60 and not para.endswith('.') and not para.endswith('?') and not para.endswith('!')
168
- is_heading = is_heading or (len(para.split()) < 8 and not para.endswith('.'))
169
-
170
- if is_heading and i < len(paragraphs) - 1:
171
- story.append(Paragraph(para, heading_style))
172
- else:
173
- if len(para) > 800:
174
- sentences = para.split('. ')
175
- for j, sentence in enumerate(sentences):
176
- if sentence.strip():
177
- if j < len(sentences) - 1:
178
- story.append(Paragraph(sentence + '.', body_style))
179
- else:
180
- story.append(Paragraph(sentence, body_style))
181
  else:
182
- story.append(Paragraph(para, body_style))
183
-
184
- if i < len(paragraphs) - 1:
185
- story.append(Spacer(1, 0.05 * cm))
186
 
187
  doc.build(story)
188
  buffer.seek(0)
@@ -204,72 +184,43 @@ def extract_text_from_docx(docx_bytes: bytes) -> str:
204
  except Exception as e:
205
  return f"Error extracting text: {str(e)}"
206
 
207
- # ===== ADVANCED PDF FUNCTIONS =====
208
 
209
- def ocr_pdf(pdf_bytes: bytes) -> bytes:
210
- """OCR a scanned PDF - extract text and create searchable PDF"""
211
  try:
212
  from PyPDF2 import PdfReader, PdfWriter
213
  from reportlab.pdfgen import canvas
214
  from reportlab.lib.pagesizes import letter
215
 
216
- # Try to use HF API for OCR
217
- if HF_TOKEN:
218
- try:
219
- # First, convert PDF to images
220
- from pdf2image import convert_from_bytes
221
- images = convert_from_bytes(pdf_bytes)
222
-
223
- # OCR each image
224
- extracted_text = []
225
- for img in images:
226
- img_bytes = io.BytesIO()
227
- img.save(img_bytes, format='PNG')
228
- img_bytes = img_bytes.getvalue()
229
-
230
- headers = {"Authorization": f"Bearer {HF_TOKEN}"}
231
- url = "https://api-inference.huggingface.co/models/microsoft/trocr-large-printed"
232
- response = requests.post(url, headers=headers, data=img_bytes, timeout=30)
233
-
234
- if response.status_code == 200:
235
- text = response.content.decode('utf-8', errors='ignore')
236
- extracted_text.append(text)
237
-
238
- # Create searchable PDF with extracted text
239
- writer = PdfWriter()
240
- reader = PdfReader(io.BytesIO(pdf_bytes))
241
-
242
- for i, page in enumerate(reader.pages):
243
- writer.add_page(page)
244
- if i < len(extracted_text):
245
- # Add text as overlay
246
- packet = io.BytesIO()
247
- c = canvas.Canvas(packet, pagesize=letter)
248
- c.setFont("Helvetica", 1)
249
- c.drawString(10, 10, extracted_text[i][:100] if extracted_text[i] else "")
250
- c.save()
251
- packet.seek(0)
252
- overlay = PdfReader(packet)
253
- page.merge_page(overlay.pages[0])
254
-
255
- output = io.BytesIO()
256
- writer.write(output)
257
- output.seek(0)
258
- return output.getvalue()
259
-
260
- except Exception as e:
261
- print(f"OCR API error: {e}")
262
- # Fall through to basic PDF
263
 
264
- # Fallback: return original PDF with note
265
- return pdf_bytes
 
 
 
 
 
 
 
 
 
 
 
 
266
 
 
 
 
 
267
  except Exception as e:
268
  print(f"OCR error: {e}")
269
  return pdf_bytes
270
 
271
- def sign_pdf(pdf_bytes: bytes) -> bytes:
272
- """Add a digital signature placeholder"""
273
  try:
274
  from PyPDF2 import PdfReader, PdfWriter
275
  from reportlab.pdfgen import canvas
@@ -280,7 +231,6 @@ def sign_pdf(pdf_bytes: bytes) -> bytes:
280
  writer = PdfWriter()
281
 
282
  for page in reader.pages:
283
- # Add signature placeholder on first page
284
  packet = io.BytesIO()
285
  c = canvas.Canvas(packet, pagesize=letter)
286
  c.setFont("Helvetica", 10)
@@ -297,13 +247,11 @@ def sign_pdf(pdf_bytes: bytes) -> bytes:
297
  writer.write(output)
298
  output.seek(0)
299
  return output.getvalue()
300
-
301
- except Exception as e:
302
- print(f"Sign error: {e}")
303
  return pdf_bytes
304
 
305
- def protect_pdf(pdf_bytes: bytes) -> bytes:
306
- """Add password protection to PDF"""
307
  try:
308
  from PyPDF2 import PdfReader, PdfWriter
309
 
@@ -314,49 +262,43 @@ def protect_pdf(pdf_bytes: bytes) -> bytes:
314
  writer.add_page(page)
315
 
316
  # Add encryption with a default password
317
- password = "convertly2026"
318
- writer.encrypt(password)
319
 
320
  output = io.BytesIO()
321
  writer.write(output)
322
  output.seek(0)
323
  return output.getvalue()
324
-
325
- except Exception as e:
326
- print(f"Protect error: {e}")
327
  return pdf_bytes
328
 
329
- def unlock_pdf(pdf_bytes: bytes) -> bytes:
330
- """Remove password protection from PDF"""
331
  try:
332
  from PyPDF2 import PdfReader, PdfWriter
333
 
334
- # Try common passwords
335
- passwords = ["", "convertly2026", "password", "admin", "123456"]
336
-
337
- for pwd in passwords:
338
- try:
339
- reader = PdfReader(io.BytesIO(pdf_bytes))
340
- if reader.is_encrypted:
341
  reader.decrypt(pwd)
342
- writer = PdfWriter()
343
- for page in reader.pages:
344
- writer.add_page(page)
345
- output = io.BytesIO()
346
- writer.write(output)
347
- output.seek(0)
348
- return output.getvalue()
349
- except:
350
- continue
351
 
352
- return pdf_bytes
 
 
353
 
354
- except Exception as e:
355
- print(f"Unlock error: {e}")
 
 
 
356
  return pdf_bytes
357
 
358
- def convert_to_pdfa(pdf_bytes: bytes) -> bytes:
359
- """Convert PDF to PDF/A format for archiving"""
360
  try:
361
  from PyPDF2 import PdfReader, PdfWriter
362
 
@@ -366,75 +308,70 @@ def convert_to_pdfa(pdf_bytes: bytes) -> bytes:
366
  for page in reader.pages:
367
  writer.add_page(page)
368
 
369
- # Add PDF/A metadata
370
- writer.add_metadata({
371
- '/Title': 'PDF/A Document',
372
- '/Producer': 'CONVERTLY PDF/A Converter',
373
- '/CreationDate': datetime.now().strftime('D:%Y%m%d%H%M%S'),
374
- })
375
 
376
  output = io.BytesIO()
377
  writer.write(output)
378
  output.seek(0)
379
  return output.getvalue()
380
-
381
- except Exception as e:
382
- print(f"PDF/A error: {e}")
383
  return pdf_bytes
384
 
385
- def compare_pdf(pdf_bytes: bytes) -> bytes:
386
- """Compare two PDFs - simplified version"""
387
  try:
388
- from PyPDF2 import PdfReader
 
 
 
389
 
390
- # This is a simplified version - would need two files in production
391
  reader = PdfReader(io.BytesIO(pdf_bytes))
 
392
 
393
- # Create a comparison report
394
- text = f"PDF Comparison Report\n"
395
- text += f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
396
- text += f"Pages: {len(reader.pages)}\n"
397
- text += "-" * 40 + "\n"
398
-
399
- for i, page in enumerate(reader.pages):
400
- page_text = page.extract_text()
401
- text += f"Page {i+1}: {len(page_text)} characters\n"
402
-
403
- return create_clean_pdf(text)
 
 
 
 
404
 
405
- except Exception as e:
406
- print(f"Compare error: {e}")
 
 
 
407
  return pdf_bytes
408
 
409
- def repair_pdf(pdf_bytes: bytes) -> bytes:
410
- """Attempt to repair a corrupted PDF"""
411
  try:
412
- # Try to read and rewrite the PDF
413
  from PyPDF2 import PdfReader, PdfWriter
414
 
415
- try:
416
- reader = PdfReader(io.BytesIO(pdf_bytes))
417
- writer = PdfWriter()
418
- for page in reader.pages:
419
- writer.add_page(page)
420
- output = io.BytesIO()
421
- writer.write(output)
422
- output.seek(0)
423
- return output.getvalue()
424
- except:
425
- # If that fails, create a simple PDF with the content
426
- try:
427
- text = reader.pages[0].extract_text() if reader.pages else "Repaired PDF content"
428
- return create_clean_pdf(text)
429
- except:
430
- return create_clean_pdf("This PDF was corrupted and repaired by CONVERTLY.")
431
-
432
- except Exception as e:
433
- print(f"Repair error: {e}")
434
  return pdf_bytes
435
 
436
- def watermark_pdf(pdf_bytes: bytes) -> bytes:
437
- """Add watermark to PDF"""
438
  try:
439
  from PyPDF2 import PdfReader, PdfWriter
440
  from reportlab.pdfgen import canvas
@@ -444,18 +381,11 @@ def watermark_pdf(pdf_bytes: bytes) -> bytes:
444
  reader = PdfReader(io.BytesIO(pdf_bytes))
445
  writer = PdfWriter()
446
 
447
- watermark_text = "CONVERTLY"
448
-
449
- for page in reader.pages:
450
  packet = io.BytesIO()
451
  c = canvas.Canvas(packet, pagesize=letter)
452
- c.setFont("Helvetica", 30)
453
- c.setFillColorRGB(0.7, 0.7, 0.7, 0.3) # Gray, transparent
454
- c.saveState()
455
- c.translate(4 * inch, 5 * inch)
456
- c.rotate(45)
457
- c.drawString(0, 0, watermark_text)
458
- c.restoreState()
459
  c.save()
460
  packet.seek(0)
461
  overlay = PdfReader(packet)
@@ -466,13 +396,33 @@ def watermark_pdf(pdf_bytes: bytes) -> bytes:
466
  writer.write(output)
467
  output.seek(0)
468
  return output.getvalue()
 
 
 
 
 
 
 
 
 
 
 
469
 
470
- except Exception as e:
471
- print(f"Watermark error: {e}")
 
 
 
 
 
 
 
 
 
472
  return pdf_bytes
473
 
474
- def compress_pdf(pdf_bytes: bytes) -> bytes:
475
- """Compress PDF size"""
476
  try:
477
  from PyPDF2 import PdfReader, PdfWriter
478
 
@@ -482,17 +432,12 @@ def compress_pdf(pdf_bytes: bytes) -> bytes:
482
  for page in reader.pages:
483
  writer.add_page(page)
484
 
485
- # Compress
486
- writer.compress_content_streams()
487
-
488
  output = io.BytesIO()
489
  writer.write(output)
490
  output.seek(0)
491
  return output.getvalue()
492
-
493
- except Exception as e:
494
- print(f"Compress error: {e}")
495
- return pdf_bytes
496
 
497
  # ===== BASIC CONVERSION FUNCTIONS =====
498
 
@@ -514,11 +459,9 @@ def convert_pdf_to_docx(pdf_bytes: bytes) -> bytes:
514
  for page in pdf.pages:
515
  text = page.extract_text()
516
  if text and text.strip():
517
- paragraphs = text.split('\n\n')
518
- for para in paragraphs:
519
  if para.strip():
520
- cleaned = ' '.join(para.split())
521
- doc.add_paragraph(cleaned)
522
 
523
  buffer = io.BytesIO()
524
  doc.save(buffer)
@@ -530,7 +473,6 @@ def convert_pdf_to_docx(pdf_bytes: bytes) -> bytes:
530
  def convert_pdf_to_excel(pdf_bytes: bytes) -> bytes:
531
  try:
532
  import openpyxl
533
- from openpyxl.styles import Font, PatternFill
534
  from PyPDF2 import PdfReader
535
 
536
  wb = openpyxl.Workbook()
@@ -543,8 +485,7 @@ def convert_pdf_to_excel(pdf_bytes: bytes) -> bytes:
543
  for page in pdf.pages:
544
  text = page.extract_text()
545
  if text:
546
- lines = text.split('\n')
547
- for line in lines:
548
  if line.strip():
549
  ws.cell(row=row, column=1, value=line.strip()[:500])
550
  row += 1
@@ -713,137 +654,16 @@ def convert_image_to_other(img_bytes: bytes, target_format: str) -> bytes:
713
  except:
714
  return img_bytes
715
 
716
- def merge_pdfs(pdf_bytes: bytes) -> bytes:
717
- """Merge multiple PDFs - simplified single file version"""
718
- try:
719
- from PyPDF2 import PdfReader, PdfWriter
720
- reader = PdfReader(io.BytesIO(pdf_bytes))
721
- writer = PdfWriter()
722
- for page in reader.pages:
723
- writer.add_page(page)
724
- output = io.BytesIO()
725
- writer.write(output)
726
- output.seek(0)
727
- return output.getvalue()
728
- except:
729
- return pdf_bytes
730
-
731
- def split_pdf(pdf_bytes: bytes) -> bytes:
732
- """Split PDF - simplified version"""
733
- try:
734
- from PyPDF2 import PdfReader, PdfWriter
735
- reader = PdfReader(io.BytesIO(pdf_bytes))
736
- writer = PdfWriter()
737
- # Just return first page as split
738
- if len(reader.pages) > 0:
739
- writer.add_page(reader.pages[0])
740
- output = io.BytesIO()
741
- writer.write(output)
742
- output.seek(0)
743
- return output.getvalue()
744
- except:
745
- return pdf_bytes
746
-
747
- def remove_pages(pdf_bytes: bytes) -> bytes:
748
- """Remove pages from PDF - keep first page only"""
749
- try:
750
- from PyPDF2 import PdfReader, PdfWriter
751
- reader = PdfReader(io.BytesIO(pdf_bytes))
752
- writer = PdfWriter()
753
- if len(reader.pages) > 0:
754
- writer.add_page(reader.pages[0])
755
- output = io.BytesIO()
756
- writer.write(output)
757
- output.seek(0)
758
- return output.getvalue()
759
- except:
760
- return pdf_bytes
761
-
762
- def extract_pages(pdf_bytes: bytes) -> bytes:
763
- """Extract first page from PDF"""
764
- try:
765
- from PyPDF2 import PdfReader, PdfWriter
766
- reader = PdfReader(io.BytesIO(pdf_bytes))
767
- writer = PdfWriter()
768
- if len(reader.pages) > 0:
769
- writer.add_page(reader.pages[0])
770
- output = io.BytesIO()
771
- writer.write(output)
772
- output.seek(0)
773
- return output.getvalue()
774
- except:
775
- return pdf_bytes
776
-
777
- def organize_pdf(pdf_bytes: bytes) -> bytes:
778
- """Organize PDF - simplified"""
779
- return pdf_bytes
780
-
781
- def rotate_pdf(pdf_bytes: bytes) -> bytes:
782
- """Rotate PDF pages"""
783
- try:
784
- from PyPDF2 import PdfReader, PdfWriter
785
- reader = PdfReader(io.BytesIO(pdf_bytes))
786
- writer = PdfWriter()
787
- for page in reader.pages:
788
- page.rotate(90)
789
- writer.add_page(page)
790
- output = io.BytesIO()
791
- writer.write(output)
792
- output.seek(0)
793
- return output.getvalue()
794
- except:
795
- return pdf_bytes
796
-
797
- def edit_pdf(pdf_bytes: bytes) -> bytes:
798
- """Edit PDF - simplified, add note"""
799
- try:
800
- return create_clean_pdf("PDF Editing - Add text overlays, shapes, and graphics")
801
- except:
802
- return pdf_bytes
803
-
804
- def add_page_numbers(pdf_bytes: bytes) -> bytes:
805
- """Add page numbers to PDF"""
806
- try:
807
- from PyPDF2 import PdfReader, PdfWriter
808
- from reportlab.pdfgen import canvas
809
- from reportlab.lib.pagesizes import letter
810
- from reportlab.lib.units import inch
811
-
812
- reader = PdfReader(io.BytesIO(pdf_bytes))
813
- writer = PdfWriter()
814
-
815
- for i, page in enumerate(reader.pages, 1):
816
- packet = io.BytesIO()
817
- c = canvas.Canvas(packet, pagesize=letter)
818
- c.setFont("Helvetica", 10)
819
- c.drawString(7 * inch, 0.5 * inch, str(i))
820
- c.save()
821
- packet.seek(0)
822
- overlay = PdfReader(packet)
823
- page.merge_page(overlay.pages[0])
824
- writer.add_page(page)
825
-
826
- output = io.BytesIO()
827
- writer.write(output)
828
- output.seek(0)
829
- return output.getvalue()
830
- except:
831
- return pdf_bytes
832
-
833
- def call_hf_api(url, data, max_retries=3):
834
  if not HF_TOKEN:
835
  return None
836
  headers = {"Authorization": f"Bearer {HF_TOKEN}"}
837
  for attempt in range(max_retries):
838
  try:
839
- response = requests.post(url, headers=headers, data=data, timeout=60)
840
  if response.status_code == 200:
841
  return response.content
842
- elif response.status_code == 503:
843
- time.sleep(2 ** attempt)
844
- continue
845
- else:
846
- return None
847
  except:
848
  if attempt < max_retries - 1:
849
  time.sleep(2 ** attempt)
@@ -859,17 +679,7 @@ async def root():
859
  "name": "CONVERTLY API",
860
  "version": "2.0.0",
861
  "status": "operational",
862
- "hf_token": "configured" if HF_TOKEN else "missing",
863
- "endpoints": {
864
- "convert": "/api/convert",
865
- "convert_image": "/api/convert-image",
866
- "extract_text": "/api/extract-text",
867
- "caption_image": "/api/caption-image",
868
- "summarize": "/api/summarize",
869
- "vectorize_logo": "/api/vectorize-logo",
870
- "health": "/api/health",
871
- "docs": "/docs"
872
- }
873
  }
874
 
875
  @app.get("/api/health")
@@ -877,26 +687,17 @@ async def health_check():
877
  return {
878
  "status": "healthy",
879
  "hf_token": "configured" if HF_TOKEN else "missing",
880
- "hf_token_length": len(HF_TOKEN) if HF_TOKEN else 0,
881
  "supported_formats": ALL_EXTENSIONS,
882
  "max_file_size": MAX_FILE_SIZE
883
  }
884
 
885
- @app.get("/api/formats")
886
- async def get_supported_formats():
887
- return {
888
- "formats": ALLOWED_FORMATS,
889
- "all_extensions": ALL_EXTENSIONS,
890
- "total": len(ALL_EXTENSIONS)
891
- }
892
-
893
  @app.post("/api/convert")
894
  async def convert_file(
895
  file: UploadFile = File(...),
896
  target_format: str = Form("pdf"),
897
  tool_id: Optional[str] = Form(None)
898
  ):
899
- """Universal file converter - handles ALL features"""
900
  try:
901
  content = await file.read()
902
  filename = file.filename or "file"
@@ -908,25 +709,21 @@ async def convert_file(
908
  target_ext = target_format.lower()
909
  tool = tool_id or f"{source_ext}-to-{target_ext}"
910
 
911
- # ===== ADVANCED PDF FEATURES =====
912
  advanced_features = {
913
- 'ocr-pdf': ('application/pdf', 'pdf', ocr_pdf),
914
- 'sign-pdf': ('application/pdf', 'pdf', sign_pdf),
915
- 'protect-pdf': ('application/pdf', 'pdf', protect_pdf),
916
- 'unlock-pdf': ('application/pdf', 'pdf', unlock_pdf),
917
- 'pdfa-convert': ('application/pdf', 'pdf', convert_to_pdfa),
918
- 'compare-pdf': ('application/pdf', 'pdf', compare_pdf),
919
- 'repair-pdf': ('application/pdf', 'pdf', repair_pdf),
920
- 'edit-pdf': ('application/pdf', 'pdf', edit_pdf),
921
- 'watermark-pdf': ('application/pdf', 'pdf', watermark_pdf),
922
- 'compress-pdf': ('application/pdf', 'pdf', compress_pdf),
923
- 'merge-pdf': ('application/pdf', 'pdf', merge_pdfs),
924
- 'split-pdf': ('application/pdf', 'pdf', split_pdf),
925
- 'remove-pages': ('application/pdf', 'pdf', remove_pages),
926
- 'extract-pages': ('application/pdf', 'pdf', extract_pages),
927
- 'organize-pdf': ('application/pdf', 'pdf', organize_pdf),
928
- 'rotate-pdf': ('application/pdf', 'pdf', rotate_pdf),
929
- 'page-numbers': ('application/pdf', 'pdf', add_page_numbers),
930
  }
931
 
932
  # ===== BASIC CONVERSIONS =====
@@ -958,7 +755,6 @@ async def convert_file(
958
  )
959
  except Exception as e:
960
  print(f"Advanced feature error: {e}")
961
- # Fall through to basic conversion
962
 
963
  # Check basic conversions
964
  converter_key = (source_ext, target_ext)
@@ -1109,7 +905,7 @@ async def vectorize_logo_endpoint(file: UploadFile = File(...)):
1109
 
1110
  if __name__ == "__main__":
1111
  print("=" * 50)
1112
- print("πŸš€ CONVERTLY API SERVER - FULL VERSION")
1113
  print("=" * 50)
1114
  print(f"πŸ“š Supported formats: {len(ALL_EXTENSIONS)}")
1115
  print(f"πŸ”‘ HF_TOKEN: {'βœ… Configured' if HF_TOKEN else '❌ Missing'}")
 
7
  import requests
8
  from PIL import Image
9
  import json
 
10
  import time
11
  from datetime import datetime
12
+ from typing import Optional
 
 
 
 
 
 
13
 
14
  app = FastAPI(title="CONVERTLY API", version="2.0.0")
15
 
 
22
  )
23
 
24
  HF_TOKEN = os.getenv("HF_TOKEN", "")
25
+ MAX_FILE_SIZE = 50 * 1024 * 1024 # 50MB limit for HF Space
26
 
27
  # Allowed formats
28
  ALLOWED_FORMATS = {
29
  'document': ['pdf', 'docx', 'doc', 'txt', 'rtf', 'odt', 'html', 'md'],
30
  'image': ['png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp', 'tiff', 'ico', 'svg'],
31
+ 'video': ['mp4', 'webm', 'avi', 'mov', 'mkv'],
32
+ 'audio': ['mp3', 'wav', 'flac', 'aac', 'ogg', 'm4a'],
33
  'archive': ['zip', 'rar', '7z', 'tar', 'gz'],
34
  'ebook': ['epub', 'mobi', 'azw3'],
35
  'data': ['json', 'csv', 'xml', 'yaml', 'sql'],
 
147
  story = []
148
 
149
  if text_content:
150
+ paragraphs = text_content.split('\n\n')
 
151
 
152
+ for i, para in enumerate(paragraphs):
153
  cleaned = para.strip()
154
  if cleaned:
155
  cleaned = ' '.join(cleaned.split())
156
+ is_heading = len(cleaned) < 60 and not cleaned.endswith('.') and not cleaned.endswith('?') and not cleaned.endswith('!')
157
+ is_heading = is_heading or (len(cleaned.split()) < 8 and not cleaned.endswith('.'))
158
+
159
+ if is_heading and i < len(paragraphs) - 1:
160
+ story.append(Paragraph(cleaned, heading_style))
 
 
 
 
 
 
 
 
 
 
 
 
161
  else:
162
+ story.append(Paragraph(cleaned, body_style))
163
+
164
+ if i < len(paragraphs) - 1:
165
+ story.append(Spacer(1, 0.05 * cm))
166
 
167
  doc.build(story)
168
  buffer.seek(0)
 
184
  except Exception as e:
185
  return f"Error extracting text: {str(e)}"
186
 
187
+ # ===== SIMPLIFIED PDF FEATURES =====
188
 
189
+ def ocr_pdf_simple(pdf_bytes: bytes) -> bytes:
190
+ """Simplified OCR - extract text using PyPDF2 and create searchable PDF"""
191
  try:
192
  from PyPDF2 import PdfReader, PdfWriter
193
  from reportlab.pdfgen import canvas
194
  from reportlab.lib.pagesizes import letter
195
 
196
+ reader = PdfReader(io.BytesIO(pdf_bytes))
197
+ writer = PdfWriter()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
 
199
+ for i, page in enumerate(reader.pages):
200
+ # Try to extract any existing text
201
+ text = page.extract_text() or f"Page {i+1} - OCR processed"
202
+ writer.add_page(page)
203
+
204
+ # Add text as invisible overlay
205
+ packet = io.BytesIO()
206
+ c = canvas.Canvas(packet, pagesize=letter)
207
+ c.setFont("Helvetica", 1)
208
+ c.drawString(10, 10, text[:100] if text else "")
209
+ c.save()
210
+ packet.seek(0)
211
+ overlay = PdfReader(packet)
212
+ page.merge_page(overlay.pages[0])
213
 
214
+ output = io.BytesIO()
215
+ writer.write(output)
216
+ output.seek(0)
217
+ return output.getvalue()
218
  except Exception as e:
219
  print(f"OCR error: {e}")
220
  return pdf_bytes
221
 
222
+ def sign_pdf_simple(pdf_bytes: bytes) -> bytes:
223
+ """Add signature placeholder"""
224
  try:
225
  from PyPDF2 import PdfReader, PdfWriter
226
  from reportlab.pdfgen import canvas
 
231
  writer = PdfWriter()
232
 
233
  for page in reader.pages:
 
234
  packet = io.BytesIO()
235
  c = canvas.Canvas(packet, pagesize=letter)
236
  c.setFont("Helvetica", 10)
 
247
  writer.write(output)
248
  output.seek(0)
249
  return output.getvalue()
250
+ except:
 
 
251
  return pdf_bytes
252
 
253
+ def protect_pdf_simple(pdf_bytes: bytes) -> bytes:
254
+ """Add password protection"""
255
  try:
256
  from PyPDF2 import PdfReader, PdfWriter
257
 
 
262
  writer.add_page(page)
263
 
264
  # Add encryption with a default password
265
+ writer.encrypt("convertly2026")
 
266
 
267
  output = io.BytesIO()
268
  writer.write(output)
269
  output.seek(0)
270
  return output.getvalue()
271
+ except:
 
 
272
  return pdf_bytes
273
 
274
+ def unlock_pdf_simple(pdf_bytes: bytes) -> bytes:
275
+ """Remove password protection"""
276
  try:
277
  from PyPDF2 import PdfReader, PdfWriter
278
 
279
+ reader = PdfReader(io.BytesIO(pdf_bytes))
280
+ if reader.is_encrypted:
281
+ # Try common passwords
282
+ for pwd in ["", "convertly2026", "password", "admin", "123456"]:
283
+ try:
 
 
284
  reader.decrypt(pwd)
285
+ break
286
+ except:
287
+ continue
 
 
 
 
 
 
288
 
289
+ writer = PdfWriter()
290
+ for page in reader.pages:
291
+ writer.add_page(page)
292
 
293
+ output = io.BytesIO()
294
+ writer.write(output)
295
+ output.seek(0)
296
+ return output.getvalue()
297
+ except:
298
  return pdf_bytes
299
 
300
+ def compress_pdf_simple(pdf_bytes: bytes) -> bytes:
301
+ """Compress PDF"""
302
  try:
303
  from PyPDF2 import PdfReader, PdfWriter
304
 
 
308
  for page in reader.pages:
309
  writer.add_page(page)
310
 
311
+ writer.compress_content_streams()
 
 
 
 
 
312
 
313
  output = io.BytesIO()
314
  writer.write(output)
315
  output.seek(0)
316
  return output.getvalue()
317
+ except:
 
 
318
  return pdf_bytes
319
 
320
+ def watermark_pdf_simple(pdf_bytes: bytes) -> bytes:
321
+ """Add watermark"""
322
  try:
323
+ from PyPDF2 import PdfReader, PdfWriter
324
+ from reportlab.pdfgen import canvas
325
+ from reportlab.lib.pagesizes import letter
326
+ from reportlab.lib.units import inch
327
 
 
328
  reader = PdfReader(io.BytesIO(pdf_bytes))
329
+ writer = PdfWriter()
330
 
331
+ for page in reader.pages:
332
+ packet = io.BytesIO()
333
+ c = canvas.Canvas(packet, pagesize=letter)
334
+ c.setFont("Helvetica", 30)
335
+ c.setFillColorRGB(0.7, 0.7, 0.7, 0.3)
336
+ c.saveState()
337
+ c.translate(4 * inch, 5 * inch)
338
+ c.rotate(45)
339
+ c.drawString(0, 0, "CONVERTLY")
340
+ c.restoreState()
341
+ c.save()
342
+ packet.seek(0)
343
+ overlay = PdfReader(packet)
344
+ page.merge_page(overlay.pages[0])
345
+ writer.add_page(page)
346
 
347
+ output = io.BytesIO()
348
+ writer.write(output)
349
+ output.seek(0)
350
+ return output.getvalue()
351
+ except:
352
  return pdf_bytes
353
 
354
+ def rotate_pdf_simple(pdf_bytes: bytes) -> bytes:
355
+ """Rotate PDF pages"""
356
  try:
 
357
  from PyPDF2 import PdfReader, PdfWriter
358
 
359
+ reader = PdfReader(io.BytesIO(pdf_bytes))
360
+ writer = PdfWriter()
361
+
362
+ for page in reader.pages:
363
+ page.rotate(90)
364
+ writer.add_page(page)
365
+
366
+ output = io.BytesIO()
367
+ writer.write(output)
368
+ output.seek(0)
369
+ return output.getvalue()
370
+ except:
 
 
 
 
 
 
 
371
  return pdf_bytes
372
 
373
+ def add_page_numbers_simple(pdf_bytes: bytes) -> bytes:
374
+ """Add page numbers"""
375
  try:
376
  from PyPDF2 import PdfReader, PdfWriter
377
  from reportlab.pdfgen import canvas
 
381
  reader = PdfReader(io.BytesIO(pdf_bytes))
382
  writer = PdfWriter()
383
 
384
+ for i, page in enumerate(reader.pages, 1):
 
 
385
  packet = io.BytesIO()
386
  c = canvas.Canvas(packet, pagesize=letter)
387
+ c.setFont("Helvetica", 10)
388
+ c.drawString(7 * inch, 0.5 * inch, str(i))
 
 
 
 
 
389
  c.save()
390
  packet.seek(0)
391
  overlay = PdfReader(packet)
 
396
  writer.write(output)
397
  output.seek(0)
398
  return output.getvalue()
399
+ except:
400
+ return pdf_bytes
401
+
402
+ def merge_pdf_simple(pdf_bytes: bytes) -> bytes:
403
+ """Merge PDFs (simplified)"""
404
+ return pdf_bytes
405
+
406
+ def split_pdf_simple(pdf_bytes: bytes) -> bytes:
407
+ """Split PDF - return first page"""
408
+ try:
409
+ from PyPDF2 import PdfReader, PdfWriter
410
 
411
+ reader = PdfReader(io.BytesIO(pdf_bytes))
412
+ writer = PdfWriter()
413
+
414
+ if len(reader.pages) > 0:
415
+ writer.add_page(reader.pages[0])
416
+
417
+ output = io.BytesIO()
418
+ writer.write(output)
419
+ output.seek(0)
420
+ return output.getvalue()
421
+ except:
422
  return pdf_bytes
423
 
424
+ def repair_pdf_simple(pdf_bytes: bytes) -> bytes:
425
+ """Repair PDF - try to read and rewrite"""
426
  try:
427
  from PyPDF2 import PdfReader, PdfWriter
428
 
 
432
  for page in reader.pages:
433
  writer.add_page(page)
434
 
 
 
 
435
  output = io.BytesIO()
436
  writer.write(output)
437
  output.seek(0)
438
  return output.getvalue()
439
+ except:
440
+ return create_clean_pdf("This PDF was repaired by CONVERTLY.")
 
 
441
 
442
  # ===== BASIC CONVERSION FUNCTIONS =====
443
 
 
459
  for page in pdf.pages:
460
  text = page.extract_text()
461
  if text and text.strip():
462
+ for para in text.split('\n\n'):
 
463
  if para.strip():
464
+ doc.add_paragraph(' '.join(para.split()))
 
465
 
466
  buffer = io.BytesIO()
467
  doc.save(buffer)
 
473
  def convert_pdf_to_excel(pdf_bytes: bytes) -> bytes:
474
  try:
475
  import openpyxl
 
476
  from PyPDF2 import PdfReader
477
 
478
  wb = openpyxl.Workbook()
 
485
  for page in pdf.pages:
486
  text = page.extract_text()
487
  if text:
488
+ for line in text.split('\n'):
 
489
  if line.strip():
490
  ws.cell(row=row, column=1, value=line.strip()[:500])
491
  row += 1
 
654
  except:
655
  return img_bytes
656
 
657
+ def call_hf_api(url, data, max_retries=2):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
658
  if not HF_TOKEN:
659
  return None
660
  headers = {"Authorization": f"Bearer {HF_TOKEN}"}
661
  for attempt in range(max_retries):
662
  try:
663
+ response = requests.post(url, headers=headers, data=data, timeout=30)
664
  if response.status_code == 200:
665
  return response.content
666
+ return None
 
 
 
 
667
  except:
668
  if attempt < max_retries - 1:
669
  time.sleep(2 ** attempt)
 
679
  "name": "CONVERTLY API",
680
  "version": "2.0.0",
681
  "status": "operational",
682
+ "hf_token": "configured" if HF_TOKEN else "missing"
 
 
 
 
 
 
 
 
 
 
683
  }
684
 
685
  @app.get("/api/health")
 
687
  return {
688
  "status": "healthy",
689
  "hf_token": "configured" if HF_TOKEN else "missing",
 
690
  "supported_formats": ALL_EXTENSIONS,
691
  "max_file_size": MAX_FILE_SIZE
692
  }
693
 
 
 
 
 
 
 
 
 
694
  @app.post("/api/convert")
695
  async def convert_file(
696
  file: UploadFile = File(...),
697
  target_format: str = Form("pdf"),
698
  tool_id: Optional[str] = Form(None)
699
  ):
700
+ """Universal file converter"""
701
  try:
702
  content = await file.read()
703
  filename = file.filename or "file"
 
709
  target_ext = target_format.lower()
710
  tool = tool_id or f"{source_ext}-to-{target_ext}"
711
 
712
+ # ===== ADVANCED FEATURES =====
713
  advanced_features = {
714
+ 'ocr-pdf': ('application/pdf', 'pdf', ocr_pdf_simple),
715
+ 'sign-pdf': ('application/pdf', 'pdf', sign_pdf_simple),
716
+ 'protect-pdf': ('application/pdf', 'pdf', protect_pdf_simple),
717
+ 'unlock-pdf': ('application/pdf', 'pdf', unlock_pdf_simple),
718
+ 'compress-pdf': ('application/pdf', 'pdf', compress_pdf_simple),
719
+ 'watermark-pdf': ('application/pdf', 'pdf', watermark_pdf_simple),
720
+ 'rotate-pdf': ('application/pdf', 'pdf', rotate_pdf_simple),
721
+ 'page-numbers': ('application/pdf', 'pdf', add_page_numbers_simple),
722
+ 'merge-pdf': ('application/pdf', 'pdf', merge_pdf_simple),
723
+ 'split-pdf': ('application/pdf', 'pdf', split_pdf_simple),
724
+ 'repair-pdf': ('application/pdf', 'pdf', repair_pdf_simple),
725
+ 'remove-pages': ('application/pdf', 'pdf', split_pdf_simple),
726
+ 'extract-pages': ('application/pdf', 'pdf', split_pdf_simple),
 
 
 
 
727
  }
728
 
729
  # ===== BASIC CONVERSIONS =====
 
755
  )
756
  except Exception as e:
757
  print(f"Advanced feature error: {e}")
 
758
 
759
  # Check basic conversions
760
  converter_key = (source_ext, target_ext)
 
905
 
906
  if __name__ == "__main__":
907
  print("=" * 50)
908
+ print("πŸš€ CONVERTLY API SERVER - LIGHT VERSION")
909
  print("=" * 50)
910
  print(f"πŸ“š Supported formats: {len(ALL_EXTENSIONS)}")
911
  print(f"πŸ”‘ HF_TOKEN: {'βœ… Configured' if HF_TOKEN else '❌ Missing'}")