danicor commited on
Commit
3c29e42
·
verified ·
1 Parent(s): 6328fa1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +59 -154
app.py CHANGED
@@ -1,4 +1,4 @@
1
- from fastapi import FastAPI, File, UploadFile, HTTPException, BackgroundTasks, Query, Form
2
  from fastapi.responses import JSONResponse
3
  from fastapi.middleware.cors import CORSMiddleware
4
  import whisper
@@ -13,10 +13,8 @@ import sqlite3
13
  from datetime import datetime, timedelta
14
  from typing import Optional, Dict, Any
15
  from contextlib import asynccontextmanager
16
- import re
17
  import asyncio
18
  from concurrent.futures import ThreadPoolExecutor
19
- import threading
20
 
21
  # Configure logging
22
  logging.basicConfig(
@@ -294,7 +292,7 @@ async def background_transcription(file_path: str, file_hash: str, filename: str
294
 
295
  await update_processing_status(file_hash, status='processing', progress=10)
296
 
297
- # Transcribe audio in original language only
298
  loop = asyncio.get_event_loop()
299
  result = await loop.run_in_executor(
300
  executor,
@@ -319,8 +317,6 @@ async def background_transcription(file_path: str, file_hash: str, filename: str
319
  "from_cache": False
320
  }
321
 
322
- await update_processing_status(file_hash, progress=100)
323
-
324
  # Save to cache
325
  await save_to_cache(
326
  file_hash, filename, file_size,
@@ -358,12 +354,11 @@ async def root():
358
  processing_count = cursor.fetchone()[0] or 0
359
 
360
  return {
361
- "message": "Whisper Transcription API is running",
362
  "device": device,
363
  "cuda_available": torch.cuda.is_available(),
364
  "cached_files": cache_count,
365
- "currently_processing": processing_count,
366
- "whisper_loaded": whisper_model is not None
367
  }
368
  except Exception as e:
369
  logger.error(f"Error in root endpoint: {e}")
@@ -514,19 +509,13 @@ async def transcribe_audio(
514
  except Exception as e:
515
  logger.error(f"Error in immediate transcription: {e}")
516
  raise HTTPException(status_code=500, detail=f"Transcription failed: {str(e)}")
517
-
518
  finally:
519
- # Clean up temporary file and GPU memory
520
- try:
521
- if tmp_file_path and os.path.exists(tmp_file_path):
522
- os.unlink(tmp_file_path)
523
- if torch.cuda.is_available():
524
- torch.cuda.empty_cache()
525
- except Exception as e:
526
- logger.error(f"Error in cleanup: {e}")
527
 
528
  else:
529
- # Process larger files in background
530
  await add_processing_status(file_hash, file.filename, file_size, estimated_time)
531
 
532
  background_tasks.add_task(
@@ -535,163 +524,79 @@ async def transcribe_audio(
535
  )
536
 
537
  return JSONResponse({
538
- "status": "processing",
539
  "estimated_time": estimated_time,
540
  "file_hash": file_hash,
541
- "message": f"File is being processed. Estimated time: {estimated_time} minutes"
 
542
  })
543
 
544
  except HTTPException:
545
  raise
546
  except Exception as e:
547
- logger.error(f"Unexpected error in transcribe endpoint: {e}")
548
- raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
549
 
550
  finally:
551
- # Ensure temporary file is cleaned up in case of errors
552
- if tmp_file_path and os.path.exists(tmp_file_path):
553
  try:
554
  os.unlink(tmp_file_path)
555
  except Exception as e:
556
- logger.error(f"Error cleaning up temp file: {e}")
557
 
558
  @app.get("/status/{file_hash}")
559
- async def get_transcription_status(file_hash: str):
560
- """Get status of a transcription job"""
561
- try:
562
- # Check cache first
563
- cached_result = await get_from_cache(file_hash)
564
- if cached_result:
565
- return JSONResponse({
566
- "status": "completed",
567
- "result": cached_result
568
- })
569
-
570
- # Check processing status
571
- processing_status = await get_processing_status(file_hash)
572
- if processing_status:
573
- return JSONResponse(processing_status)
574
-
 
575
  return JSONResponse({
576
- "status": "not_found",
577
- "message": "No job found with this hash"
 
 
 
 
578
  })
579
- except Exception as e:
580
- logger.error(f"Error in status endpoint: {e}")
581
- raise HTTPException(status_code=500, detail="Internal server error")
582
-
583
- @app.delete("/cache/{file_hash}")
584
- async def delete_cached_transcription(file_hash: str):
585
- """Delete cached transcription"""
586
- try:
587
- with db_manager.get_connection() as conn:
588
- cursor = conn.cursor()
589
- cursor.execute(
590
- 'DELETE FROM cache WHERE file_hash = ?',
591
- (file_hash,)
592
- )
593
- deleted = cursor.rowcount
594
- conn.commit()
595
-
596
- return {"deleted": deleted > 0}
597
- except Exception as e:
598
- logger.error(f"Error deleting cache: {e}")
599
- raise HTTPException(status_code=500, detail="Internal server error")
600
-
601
- @app.get("/cache/stats")
602
- async def get_cache_stats():
603
- """Get cache statistics"""
604
- try:
605
- with db_manager.get_connection() as conn:
606
- cursor = conn.cursor()
607
-
608
- cursor.execute('SELECT COUNT(*) FROM cache')
609
- total = cursor.fetchone()[0] or 0
610
-
611
- cursor.execute('SELECT SUM(file_size) FROM cache')
612
- total_size = cursor.fetchone()[0] or 0
613
-
614
- cursor.execute('''
615
- SELECT COUNT(*) FROM cache
616
- WHERE last_accessed > datetime('now', '-7 days')
617
- ''')
618
- recent_access = cursor.fetchone()[0] or 0
619
-
620
- cursor.execute('''
621
- SELECT COUNT(*) FROM cache
622
- WHERE created_at > datetime('now', '-1 day')
623
- ''')
624
- today_added = cursor.fetchone()[0] or 0
625
-
626
- return {
627
- "total_files": total,
628
- "total_size_mb": total_size / (1024 * 1024),
629
- "files_accessed_last_7_days": recent_access,
630
- "files_added_today": today_added
631
- }
632
- except Exception as e:
633
- logger.error(f"Error getting cache stats: {e}")
634
- raise HTTPException(status_code=500, detail="Internal server error")
635
 
636
- @app.get("/system/health")
637
  async def health_check():
638
  """Health check endpoint"""
639
- try:
640
- status = {
641
- "status": "healthy",
642
- "whisper_loaded": whisper_model is not None,
643
- "device": device,
644
- "cuda_available": torch.cuda.is_available(),
645
- "timestamp": datetime.now().isoformat()
646
- }
647
-
648
- # Check database connection
649
- try:
650
- with db_manager.get_connection() as conn:
651
- cursor = conn.cursor()
652
- cursor.execute('SELECT 1')
653
- status["database"] = "connected"
654
- except:
655
- status["database"] = "disconnected"
656
- status["status"] = "degraded"
657
-
658
- return status
659
- except Exception as e:
660
- logger.error(f"Health check failed: {e}")
661
- return {"status": "unhealthy", "error": str(e)}
662
-
663
- @app.get("/system/memory")
664
- async def memory_usage():
665
- """Get memory usage information"""
666
- try:
667
- if torch.cuda.is_available():
668
- gpu_memory = torch.cuda.memory_allocated() / (1024 ** 3) # GB
669
- gpu_max = torch.cuda.max_memory_allocated() / (1024 ** 3)
670
- gpu_reserved = torch.cuda.memory_reserved() / (1024 ** 3)
671
- else:
672
- gpu_memory = gpu_max = gpu_reserved = 0
673
-
674
- import psutil
675
- process = psutil.Process()
676
- memory_info = process.memory_info()
677
-
678
- return {
679
- "gpu_memory_gb": round(gpu_memory, 2),
680
- "gpu_max_memory_gb": round(gpu_max, 2),
681
- "gpu_reserved_memory_gb": round(gpu_reserved, 2),
682
- "ram_used_mb": round(memory_info.rss / (1024 ** 2), 2),
683
- "ram_available_gb": round(psutil.virtual_memory().available / (1024 ** 3), 2)
684
- }
685
- except Exception as e:
686
- logger.error(f"Memory check failed: {e}")
687
- return {"error": "Memory information unavailable"}
688
 
689
  if __name__ == "__main__":
 
690
  uvicorn.run(
691
  app,
692
  host="0.0.0.0",
693
- port=8000,
694
- timeout_keep_alive=30,
695
- limit_concurrency=50,
696
- limit_max_requests=1000
 
 
697
  )
 
1
+ from fastapi import FastAPI, File, UploadFile, HTTPException, BackgroundTasks, Query
2
  from fastapi.responses import JSONResponse
3
  from fastapi.middleware.cors import CORSMiddleware
4
  import whisper
 
13
  from datetime import datetime, timedelta
14
  from typing import Optional, Dict, Any
15
  from contextlib import asynccontextmanager
 
16
  import asyncio
17
  from concurrent.futures import ThreadPoolExecutor
 
18
 
19
  # Configure logging
20
  logging.basicConfig(
 
292
 
293
  await update_processing_status(file_hash, status='processing', progress=10)
294
 
295
+ # Transcribe audio
296
  loop = asyncio.get_event_loop()
297
  result = await loop.run_in_executor(
298
  executor,
 
317
  "from_cache": False
318
  }
319
 
 
 
320
  # Save to cache
321
  await save_to_cache(
322
  file_hash, filename, file_size,
 
354
  processing_count = cursor.fetchone()[0] or 0
355
 
356
  return {
357
+ "message": "Whisper API is running",
358
  "device": device,
359
  "cuda_available": torch.cuda.is_available(),
360
  "cached_files": cache_count,
361
+ "currently_processing": processing_count
 
362
  }
363
  except Exception as e:
364
  logger.error(f"Error in root endpoint: {e}")
 
509
  except Exception as e:
510
  logger.error(f"Error in immediate transcription: {e}")
511
  raise HTTPException(status_code=500, detail=f"Transcription failed: {str(e)}")
 
512
  finally:
513
+ # Clean up GPU memory
514
+ if torch.cuda.is_available():
515
+ torch.cuda.empty_cache()
 
 
 
 
 
516
 
517
  else:
518
+ # Large file - process in background
519
  await add_processing_status(file_hash, file.filename, file_size, estimated_time)
520
 
521
  background_tasks.add_task(
 
524
  )
525
 
526
  return JSONResponse({
527
+ "status": "processing_started",
528
  "estimated_time": estimated_time,
529
  "file_hash": file_hash,
530
+ "message": f"Processing started. Estimated time: {estimated_time} minutes.",
531
+ "server_load": f"Processing slots: {5 - available_slots}/5"
532
  })
533
 
534
  except HTTPException:
535
  raise
536
  except Exception as e:
537
+ logger.error(f"Error in transcription endpoint: {str(e)}")
538
+ raise HTTPException(status_code=500, detail=f"Processing error: {str(e)}")
539
 
540
  finally:
541
+ # Clean up temporary file for small immediate processing
542
+ if tmp_file_path and os.path.exists(tmp_file_path) and file_size_mb < 5:
543
  try:
544
  os.unlink(tmp_file_path)
545
  except Exception as e:
546
+ logger.error(f"Error deleting temp file: {e}")
547
 
548
  @app.get("/status/{file_hash}")
549
+ async def check_status(file_hash: str):
550
+ """Check processing status for a file"""
551
+ # Check cache first
552
+ cached_result = await get_from_cache(file_hash)
553
+ if cached_result:
554
+ await remove_processing_status(file_hash)
555
+ cached_result.update({
556
+ "status": "completed",
557
+ "from_cache": True,
558
+ "message": "Processing completed and result is ready"
559
+ })
560
+ return JSONResponse(cached_result)
561
+
562
+ # Check processing status
563
+ processing_status = await get_processing_status(file_hash)
564
+ if processing_status:
565
+ remaining_time = max(0, processing_status['estimated_time'] - processing_status['elapsed_minutes'])
566
  return JSONResponse({
567
+ "status": processing_status['status'],
568
+ "progress": processing_status['progress'],
569
+ "elapsed_minutes": processing_status['elapsed_minutes'],
570
+ "estimated_time": processing_status['estimated_time'],
571
+ "remaining_time": remaining_time,
572
+ "message": f"Processing... about {remaining_time} minutes remaining"
573
  })
574
+
575
+ return JSONResponse({
576
+ "status": "not_found",
577
+ "message": "File not found in cache or processing queue"
578
+ }, status_code=404)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
579
 
580
+ @app.get("/health")
581
  async def health_check():
582
  """Health check endpoint"""
583
+ return {
584
+ "status": "healthy",
585
+ "timestamp": datetime.now().isoformat(),
586
+ "device": device,
587
+ "cuda_available": torch.cuda.is_available(),
588
+ "whisper_loaded": whisper_model is not None
589
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
590
 
591
  if __name__ == "__main__":
592
+ # Production-ready uvicorn configuration
593
  uvicorn.run(
594
  app,
595
  host="0.0.0.0",
596
+ port=7860,
597
+ timeout_keep_alive=300,
598
+ limit_concurrency=100,
599
+ limit_max_requests=1000,
600
+ log_config=None,
601
+ access_log=False
602
  )