danicor commited on
Commit
39e2fc4
·
verified ·
1 Parent(s): 1020694

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +158 -18
app.py CHANGED
@@ -2,14 +2,16 @@ import os
2
  import tempfile
3
  import time
4
  import json
 
 
5
  from pathlib import Path
6
  import uuid
7
  import logging
 
8
 
9
  import torch
10
  import yt_dlp as youtube_dl
11
  from flask import Flask, request, jsonify
12
- # from flask_cors import CORS
13
  from transformers import pipeline
14
  from transformers.pipelines.audio_utils import ffmpeg_read
15
  import ffmpeg
@@ -19,7 +21,6 @@ logging.basicConfig(level=logging.INFO)
19
  logger = logging.getLogger(__name__)
20
 
21
  app = Flask(__name__)
22
- # CORS(app)
23
 
24
  # Configuration
25
  MODEL_NAME = "openai/whisper-large-v3"
@@ -27,23 +28,112 @@ BATCH_SIZE = 8
27
  FILE_LIMIT_MB = 1000
28
  YT_LENGTH_LIMIT_S = 3600 # 1 hour limit for YouTube
29
  MAX_FILE_SIZE = FILE_LIMIT_MB * 1024 * 1024 # Convert to bytes
 
30
 
31
  # Device configuration
32
  device = 0 if torch.cuda.is_available() else "cpu"
33
  logger.info(f"Using device: {device}")
34
 
35
- # Initialize Whisper pipeline
36
- try:
37
- pipe = pipeline(
38
- task="automatic-speech-recognition",
39
- model=MODEL_NAME,
40
- chunk_length_s=30,
41
- device=device,
42
- )
43
- logger.info("Whisper model loaded successfully")
44
- except Exception as e:
45
- logger.error(f"Error loading Whisper model: {e}")
46
- raise
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
  # Supported languages for Whisper (99 languages)
49
  SUPPORTED_LANGUAGES = {
@@ -141,6 +231,9 @@ def download_youtube_audio(yt_url, output_path):
141
  def process_audio_file(file_path, task="transcribe", language="auto", return_timestamps=False):
142
  """Process audio file with Whisper"""
143
  try:
 
 
 
144
  # Read audio file
145
  with open(file_path, "rb") as f:
146
  inputs = f.read()
@@ -173,13 +266,53 @@ def process_audio_file(file_path, task="transcribe", language="auto", return_tim
173
  @app.route('/health', methods=['GET'])
174
  def health_check():
175
  """Health check endpoint"""
 
176
  return jsonify({
177
  "status": "healthy",
178
  "model": MODEL_NAME,
179
  "device": str(device),
 
 
180
  "supported_languages": list(SUPPORTED_LANGUAGES.keys())
181
  })
182
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  @app.route('/languages', methods=['GET'])
184
  def get_supported_languages():
185
  """Get list of supported languages"""
@@ -404,8 +537,15 @@ def get_extension_hooks():
404
  "description": "Extension hooks for plugins like CSS customization, myCred integration, etc."
405
  })
406
 
 
 
 
 
 
 
407
  if __name__ == '__main__':
408
- # Try to initialize model on startup
409
- logger.info("Starting Flask application...")
410
- initialize_model()
411
- app.run(host='0.0.0.0', port=7860, debug=False)
 
 
2
  import tempfile
3
  import time
4
  import json
5
+ import threading
6
+ import gc
7
  from pathlib import Path
8
  import uuid
9
  import logging
10
+ from datetime import datetime, timedelta
11
 
12
  import torch
13
  import yt_dlp as youtube_dl
14
  from flask import Flask, request, jsonify
 
15
  from transformers import pipeline
16
  from transformers.pipelines.audio_utils import ffmpeg_read
17
  import ffmpeg
 
21
  logger = logging.getLogger(__name__)
22
 
23
  app = Flask(__name__)
 
24
 
25
  # Configuration
26
  MODEL_NAME = "openai/whisper-large-v3"
 
28
  FILE_LIMIT_MB = 1000
29
  YT_LENGTH_LIMIT_S = 3600 # 1 hour limit for YouTube
30
  MAX_FILE_SIZE = FILE_LIMIT_MB * 1024 * 1024 # Convert to bytes
31
+ MODEL_TIMEOUT_MINUTES = 60 # مدت زمان نگهداری مدل در حافظه (به دقیقه)
32
 
33
  # Device configuration
34
  device = 0 if torch.cuda.is_available() else "cpu"
35
  logger.info(f"Using device: {device}")
36
 
37
+ # Global model management
38
+ class ModelManager:
39
+ def __init__(self):
40
+ self.pipe = None
41
+ self.last_used = None
42
+ self.model_lock = threading.Lock()
43
+ self.cleanup_timer = None
44
+ self.is_loading = False
45
+
46
+ def load_model(self):
47
+ """بارگذاری مدل در صورت عدم وجود"""
48
+ with self.model_lock:
49
+ if self.pipe is not None:
50
+ self.last_used = datetime.now()
51
+ return self.pipe
52
+
53
+ if self.is_loading:
54
+ # اگر مدل در حال بارگذاری است، منتظر بمانید
55
+ while self.is_loading:
56
+ time.sleep(0.5)
57
+ return self.pipe
58
+
59
+ try:
60
+ self.is_loading = True
61
+ logger.info("Loading Whisper model...")
62
+
63
+ self.pipe = pipeline(
64
+ task="automatic-speech-recognition",
65
+ model=MODEL_NAME,
66
+ chunk_length_s=30,
67
+ device=device,
68
+ )
69
+
70
+ self.last_used = datetime.now()
71
+ self.start_cleanup_timer()
72
+ logger.info("Whisper model loaded successfully")
73
+
74
+ except Exception as e:
75
+ logger.error(f"Error loading Whisper model: {e}")
76
+ self.pipe = None
77
+ raise
78
+ finally:
79
+ self.is_loading = False
80
+
81
+ return self.pipe
82
+
83
+ def get_model(self):
84
+ """دریافت مدل (با بارگذاری در صورت نیاز)"""
85
+ if self.pipe is None:
86
+ return self.load_model()
87
+
88
+ self.last_used = datetime.now()
89
+ return self.pipe
90
+
91
+ def cleanup_model(self):
92
+ """پاکسازی مدل از حافظه"""
93
+ with self.model_lock:
94
+ if self.pipe is not None:
95
+ logger.info("Cleaning up model from memory...")
96
+ del self.pipe
97
+ self.pipe = None
98
+
99
+ # پاکسازی کش CUDA در صورت استفاده از GPU
100
+ if torch.cuda.is_available():
101
+ torch.cuda.empty_cache()
102
+
103
+ # فراخوانی garbage collector
104
+ gc.collect()
105
+ logger.info("Model cleanup completed")
106
+
107
+ if self.cleanup_timer:
108
+ self.cleanup_timer.cancel()
109
+ self.cleanup_timer = None
110
+
111
+ def start_cleanup_timer(self):
112
+ """شروع تایمر پاکسازی"""
113
+ if self.cleanup_timer:
114
+ self.cleanup_timer.cancel()
115
+
116
+ self.cleanup_timer = threading.Timer(
117
+ MODEL_TIMEOUT_MINUTES * 60,
118
+ self.check_and_cleanup
119
+ )
120
+ self.cleanup_timer.start()
121
+
122
+ def check_and_cleanup(self):
123
+ """بررسی و پاکسازی مدل در صورت عدم استفاده"""
124
+ with self.model_lock:
125
+ if self.last_used and self.pipe:
126
+ time_diff = datetime.now() - self.last_used
127
+ if time_diff > timedelta(minutes=MODEL_TIMEOUT_MINUTES):
128
+ self.cleanup_model()
129
+ else:
130
+ # اگر هنوز زمان پاکسازی نرسیده، دوباره تایمر را تنظیم کنید
131
+ remaining_time = MODEL_TIMEOUT_MINUTES * 60 - time_diff.total_seconds()
132
+ self.cleanup_timer = threading.Timer(remaining_time, self.check_and_cleanup)
133
+ self.cleanup_timer.start()
134
+
135
+ # Global model manager instance
136
+ model_manager = ModelManager()
137
 
138
  # Supported languages for Whisper (99 languages)
139
  SUPPORTED_LANGUAGES = {
 
231
  def process_audio_file(file_path, task="transcribe", language="auto", return_timestamps=False):
232
  """Process audio file with Whisper"""
233
  try:
234
+ # دریافت مدل (با lazy loading)
235
+ pipe = model_manager.get_model()
236
+
237
  # Read audio file
238
  with open(file_path, "rb") as f:
239
  inputs = f.read()
 
266
  @app.route('/health', methods=['GET'])
267
  def health_check():
268
  """Health check endpoint"""
269
+ model_status = "loaded" if model_manager.pipe is not None else "not_loaded"
270
  return jsonify({
271
  "status": "healthy",
272
  "model": MODEL_NAME,
273
  "device": str(device),
274
+ "model_status": model_status,
275
+ "model_timeout_minutes": MODEL_TIMEOUT_MINUTES,
276
  "supported_languages": list(SUPPORTED_LANGUAGES.keys())
277
  })
278
 
279
+ @app.route('/model/status', methods=['GET'])
280
+ def model_status():
281
+ """وضعیت مدل را بررسی کنید"""
282
+ is_loaded = model_manager.pipe is not None
283
+ last_used = model_manager.last_used.isoformat() if model_manager.last_used else None
284
+
285
+ return jsonify({
286
+ "model_loaded": is_loaded,
287
+ "last_used": last_used,
288
+ "timeout_minutes": MODEL_TIMEOUT_MINUTES,
289
+ "is_loading": model_manager.is_loading
290
+ })
291
+
292
+ @app.route('/model/preload', methods=['POST'])
293
+ def preload_model():
294
+ """پیش‌بارگذاری مدل"""
295
+ try:
296
+ model_manager.get_model()
297
+ return jsonify({
298
+ "success": True,
299
+ "message": "Model preloaded successfully"
300
+ })
301
+ except Exception as e:
302
+ return jsonify({
303
+ "success": False,
304
+ "error": str(e)
305
+ }), 500
306
+
307
+ @app.route('/model/unload', methods=['POST'])
308
+ def unload_model():
309
+ """پاکسازی دستی مدل"""
310
+ model_manager.cleanup_model()
311
+ return jsonify({
312
+ "success": True,
313
+ "message": "Model unloaded from memory"
314
+ })
315
+
316
  @app.route('/languages', methods=['GET'])
317
  def get_supported_languages():
318
  """Get list of supported languages"""
 
537
  "description": "Extension hooks for plugins like CSS customization, myCred integration, etc."
538
  })
539
 
540
+ # Cleanup on app shutdown
541
+ @app.teardown_appcontext
542
+ def cleanup_model_on_shutdown(exception):
543
+ """پاکسازی مدل هنگام خروج از برنامه"""
544
+ model_manager.cleanup_model()
545
+
546
  if __name__ == '__main__':
547
+ try:
548
+ app.run(host='0.0.0.0', port=7860, debug=False)
549
+ finally:
550
+ # پاکسازی نهایی
551
+ model_manager.cleanup_model()