hamza2923 commited on
Commit
071a691
·
verified ·
1 Parent(s): e56406d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +195 -195
app.py CHANGED
@@ -1,196 +1,196 @@
1
- from flask import Flask, request, jsonify, Response
2
- from faster_whisper import WhisperModel
3
- import torch
4
- import io
5
- import time
6
- import datetime
7
- from threading import Semaphore
8
- import os
9
- from werkzeug.utils import secure_filename
10
- import tempfile
11
- from moviepy.editor import VideoFileClip
12
- import firebase_admin
13
- from firebase_admin import credentials, messaging # Added for FCM
14
-
15
- app = Flask(__name__)
16
-
17
- # Configuration
18
- MAX_CONCURRENT_REQUESTS = 2
19
- MAX_FILE_DURATION = 60 * 30
20
- TEMPORARY_FOLDER = tempfile.gettempdir()
21
- ALLOWED_AUDIO_EXTENSIONS = {'mp3', 'wav', 'ogg', 'm4a', 'flac', 'aac', 'wma', 'opus', 'aiff'}
22
- ALLOWED_VIDEO_EXTENSIONS = {'mp4', 'avi', 'mov', 'mkv', 'webm', 'flv', 'wmv', 'mpeg', 'mpg', '3gp'}
23
- ALLOWED_EXTENSIONS = ALLOWED_AUDIO_EXTENSIONS.union(ALLOWED_VIDEO_EXTENSIONS)
24
-
25
-
26
- # Initialize Firebase Admin SDK using environment variables
27
- firebase_credentials = {
28
- "type": "service_account",
29
- "project_id": os.getenv("FIREBASE_PROJECT_ID"),
30
- "private_key_id": os.getenv("FIREBASE_PRIVATE_KEY_ID"),
31
- "private_key": os.getenv("FIREBASE_PRIVATE_KEY").replace("\\n", "\n"),
32
- "client_email": os.getenv("FIREBASE_CLIENT_EMAIL"),
33
- "client_id": os.getenv("FIREBASE_CLIENT_ID"),
34
- "auth_uri": "https://accounts.google.com/o/oauth2/auth",
35
- "token_uri": "https://oauth2.googleapis.com/token",
36
- "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
37
- "client_x509_cert_url": f"https://www.googleapis.com/robot/v1/metadata/x509/{os.getenv('FIREBASE_CLIENT_EMAIL')}"
38
- }
39
- cred = credentials.Certificate(firebase_credentials)
40
- firebase_admin.initialize_app(cred)
41
-
42
- # Device check for faster-whisper
43
- device = "cuda" if torch.cuda.is_available() else "cpu"
44
- compute_type = "float16" if device == "cuda" else "int8"
45
- print(f"Using device: {device} with compute_type: {compute_type}")
46
-
47
- # Faster Whisper setup
48
- beamsize = 2
49
- wmodel = WhisperModel(
50
- "guillaumekln/faster-whisper-small",
51
- device=device,
52
- compute_type=compute_type,
53
- download_root="./model_cache"
54
- )
55
-
56
- # Concurrency control
57
- request_semaphore = Semaphore(MAX_CONCURRENT_REQUESTS)
58
- active_requests = 0
59
-
60
- def allowed_file(filename):
61
- return '.' in filename and \
62
- filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
63
-
64
- def cleanup_temp_files(*file_paths):
65
- for file_path in file_paths:
66
- try:
67
- if file_path and os.path.exists(file_path):
68
- os.remove(file_path)
69
- except Exception as e:
70
- print(f"Error cleaning up temp file {file_path}: {str(e)}")
71
-
72
- def extract_audio_from_video(video_path, output_audio_path):
73
- try:
74
- video = VideoFileClip(video_path)
75
- if video.duration > MAX_FILE_DURATION:
76
- video.close()
77
- raise ValueError(f"Video duration exceeds {MAX_FILE_DURATION} seconds")
78
- video.audio.write_audiofile(output_audio_path)
79
- video.close()
80
- return output_audio_path
81
- except Exception as e:
82
- raise Exception(f"Failed to extract audio from video: {str(e)}")
83
-
84
- def send_fcm_data_message(fcm_token, transcription, file_type, created_date, transcription_name):
85
- """Send an FCM message with transcription details and a notification"""
86
- try:
87
- message = messaging.Message(
88
- notification=messaging.Notification(
89
- title=transcription_name,
90
- body="Successfully downloaded"
91
- ),
92
- data={
93
- 'transcription': transcription,
94
- 'file_type': file_type,
95
- 'created_date': created_date,
96
- 'transcription_name': transcription_name
97
- },
98
- token=fcm_token
99
- )
100
- response = messaging.send(message)
101
- print(f"FCM message sent: {response}")
102
- return True
103
- except Exception as e:
104
- print(f"Error sending FCM message: {str(e)}")
105
- return False
106
-
107
- @app.route("/health", methods=["GET"])
108
- def health_check():
109
- return jsonify({
110
- 'status': 'API is running',
111
- 'timestamp': datetime.datetime.now().isoformat(),
112
- 'device': device,
113
- 'compute_type': compute_type,
114
- 'active_requests': active_requests,
115
- 'max_duration_supported': MAX_FILE_DURATION,
116
- 'supported_formats': list(ALLOWED_EXTENSIONS)
117
- })
118
-
119
- @app.route("/status/busy", methods=["GET"])
120
- def server_busy():
121
- is_busy = active_requests >= MAX_CONCURRENT_REQUESTS
122
- return jsonify({
123
- 'is_busy': is_busy,
124
- 'active_requests': active_requests,
125
- 'max_capacity': MAX_CONCURRENT_REQUESTS
126
- })
127
-
128
- @app.route("/whisper_transcribe", methods=["POST"])
129
- def transcribe():
130
- global active_requests
131
-
132
- if not request_semaphore.acquire(blocking=False):
133
- return jsonify({'error': 'Server busy'}), 503
134
-
135
- active_requests += 1
136
- start_time = time.time()
137
- temp_file_path = None
138
- temp_audio_path = None
139
-
140
- try:
141
- if 'file' not in request.files or 'fcm_token' not in request.form:
142
- return jsonify({'error': 'Missing file or FCM token'}), 400
143
-
144
- file = request.files['file']
145
- fcm_token = request.form['fcm_token']
146
- created_date = request.form['created_date']
147
- transcription_name = request.form['transcription_name']
148
- if not (file and allowed_file(file.filename)):
149
- return jsonify({'error': f'Invalid file format. Supported: {", ".join(ALLOWED_EXTENSIONS)}'}), 400
150
-
151
- # Save uploaded file
152
- temp_file_path = os.path.join(TEMPORARY_FOLDER, secure_filename(file.filename))
153
- file.save(temp_file_path)
154
-
155
- # Handle video/audio
156
- file_extension = file.filename.rsplit('.', 1)[1].lower()
157
- if file_extension in ALLOWED_VIDEO_EXTENSIONS:
158
- temp_audio_path = os.path.join(TEMPORARY_FOLDER, f"temp_audio_{int(time.time())}.wav")
159
- extract_audio_from_video(temp_file_path, temp_audio_path)
160
- transcription_file = temp_audio_path
161
- else:
162
- transcription_file = temp_file_path
163
-
164
- # Transcribe
165
- segments, _ = wmodel.transcribe(
166
- transcription_file,
167
- beam_size=beamsize,
168
- vad_filter=True,
169
- without_timestamps=True,
170
- compression_ratio_threshold=2.4,
171
- word_timestamps=False
172
- )
173
-
174
- full_text = " ".join(segment.text for segment in segments)
175
- file_type = 'video' if file_extension in ALLOWED_VIDEO_EXTENSIONS else 'audio'
176
-
177
- # Send FCM data message
178
- # Send FCM data message
179
- send_fcm_data_message(fcm_token, full_text, file_type, created_date, transcription_name)
180
-
181
- return jsonify({}), 200
182
-
183
- except Exception as e:
184
- return jsonify({'error': str(e)}), 500
185
-
186
- finally:
187
- cleanup_temp_files(temp_file_path, temp_audio_path)
188
- active_requests -= 1
189
- request_semaphore.release()
190
- print(f"Processed in {time.time()-start_time:.2f}s (Active: {active_requests})")
191
-
192
- if __name__ == "__main__":
193
- if not os.path.exists(TEMPORARY_FOLDER):
194
- os.makedirs(TEMPORARY_FOLDER)
195
-
196
  app.run(host="0.0.0.0", port=7860, threaded=True)
 
1
+ from flask import Flask, request, jsonify, Response
2
+ from faster_whisper import WhisperModel
3
+ import torch
4
+ import io
5
+ import time
6
+ import datetime
7
+ from threading import Semaphore
8
+ import os
9
+ from werkzeug.utils import secure_filename
10
+ import tempfile
11
+ from moviepy.editor import VideoFileClip
12
+ import firebase_admin
13
+ from firebase_admin import credentials, messaging # Added for FCM
14
+
15
+ app = Flask(__name__)
16
+
17
+ # Configuration
18
+ MAX_CONCURRENT_REQUESTS = 1
19
+ MAX_FILE_DURATION = 60 * 30
20
+ TEMPORARY_FOLDER = tempfile.gettempdir()
21
+ ALLOWED_AUDIO_EXTENSIONS = {'mp3', 'wav', 'ogg', 'm4a', 'flac', 'aac', 'wma', 'opus', 'aiff'}
22
+ ALLOWED_VIDEO_EXTENSIONS = {'mp4', 'avi', 'mov', 'mkv', 'webm', 'flv', 'wmv', 'mpeg', 'mpg', '3gp'}
23
+ ALLOWED_EXTENSIONS = ALLOWED_AUDIO_EXTENSIONS.union(ALLOWED_VIDEO_EXTENSIONS)
24
+
25
+
26
+ # Initialize Firebase Admin SDK using environment variables
27
+ firebase_credentials = {
28
+ "type": "service_account",
29
+ "project_id": os.getenv("FIREBASE_PROJECT_ID"),
30
+ "private_key_id": os.getenv("FIREBASE_PRIVATE_KEY_ID"),
31
+ "private_key": os.getenv("FIREBASE_PRIVATE_KEY").replace("\\n", "\n"),
32
+ "client_email": os.getenv("FIREBASE_CLIENT_EMAIL"),
33
+ "client_id": os.getenv("FIREBASE_CLIENT_ID"),
34
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
35
+ "token_uri": "https://oauth2.googleapis.com/token",
36
+ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
37
+ "client_x509_cert_url": f"https://www.googleapis.com/robot/v1/metadata/x509/{os.getenv('FIREBASE_CLIENT_EMAIL')}"
38
+ }
39
+ cred = credentials.Certificate(firebase_credentials)
40
+ firebase_admin.initialize_app(cred)
41
+
42
+ # Device check for faster-whisper
43
+ device = "cuda" if torch.cuda.is_available() else "cpu"
44
+ compute_type = "float16" if device == "cuda" else "int8"
45
+ print(f"Using device: {device} with compute_type: {compute_type}")
46
+
47
+ # Faster Whisper setup
48
+ beamsize = 2
49
+ wmodel = WhisperModel(
50
+ "guillaumekln/faster-whisper-small",
51
+ device=device,
52
+ compute_type=compute_type,
53
+ download_root="./model_cache"
54
+ )
55
+
56
+ # Concurrency control
57
+ request_semaphore = Semaphore(MAX_CONCURRENT_REQUESTS)
58
+ active_requests = 0
59
+
60
+ def allowed_file(filename):
61
+ return '.' in filename and \
62
+ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
63
+
64
+ def cleanup_temp_files(*file_paths):
65
+ for file_path in file_paths:
66
+ try:
67
+ if file_path and os.path.exists(file_path):
68
+ os.remove(file_path)
69
+ except Exception as e:
70
+ print(f"Error cleaning up temp file {file_path}: {str(e)}")
71
+
72
+ def extract_audio_from_video(video_path, output_audio_path):
73
+ try:
74
+ video = VideoFileClip(video_path)
75
+ if video.duration > MAX_FILE_DURATION:
76
+ video.close()
77
+ raise ValueError(f"Video duration exceeds {MAX_FILE_DURATION} seconds")
78
+ video.audio.write_audiofile(output_audio_path)
79
+ video.close()
80
+ return output_audio_path
81
+ except Exception as e:
82
+ raise Exception(f"Failed to extract audio from video: {str(e)}")
83
+
84
+ def send_fcm_data_message(fcm_token, transcription, file_type, created_date, transcription_name):
85
+ """Send an FCM message with transcription details and a notification"""
86
+ try:
87
+ message = messaging.Message(
88
+ notification=messaging.Notification(
89
+ title=transcription_name,
90
+ body="Successfully downloaded"
91
+ ),
92
+ data={
93
+ 'transcription': transcription,
94
+ 'file_type': file_type,
95
+ 'created_date': created_date,
96
+ 'transcription_name': transcription_name
97
+ },
98
+ token=fcm_token
99
+ )
100
+ response = messaging.send(message)
101
+ print(f"FCM message sent: {response}")
102
+ return True
103
+ except Exception as e:
104
+ print(f"Error sending FCM message: {str(e)}")
105
+ return False
106
+
107
+ @app.route("/health", methods=["GET"])
108
+ def health_check():
109
+ return jsonify({
110
+ 'status': 'API is running',
111
+ 'timestamp': datetime.datetime.now().isoformat(),
112
+ 'device': device,
113
+ 'compute_type': compute_type,
114
+ 'active_requests': active_requests,
115
+ 'max_duration_supported': MAX_FILE_DURATION,
116
+ 'supported_formats': list(ALLOWED_EXTENSIONS)
117
+ })
118
+
119
+ @app.route("/status/busy", methods=["GET"])
120
+ def server_busy():
121
+ is_busy = active_requests >= MAX_CONCURRENT_REQUESTS
122
+ return jsonify({
123
+ 'is_busy': is_busy,
124
+ 'active_requests': active_requests,
125
+ 'max_capacity': MAX_CONCURRENT_REQUESTS
126
+ })
127
+
128
+ @app.route("/whisper_transcribe", methods=["POST"])
129
+ def transcribe():
130
+ global active_requests
131
+
132
+ if not request_semaphore.acquire(blocking=False):
133
+ return jsonify({'error': 'Server busy'}), 503
134
+
135
+ active_requests += 1
136
+ start_time = time.time()
137
+ temp_file_path = None
138
+ temp_audio_path = None
139
+
140
+ try:
141
+ if 'file' not in request.files or 'fcm_token' not in request.form:
142
+ return jsonify({'error': 'Missing file or FCM token'}), 400
143
+
144
+ file = request.files['file']
145
+ fcm_token = request.form['fcm_token']
146
+ created_date = request.form['created_date']
147
+ transcription_name = request.form['transcription_name']
148
+ if not (file and allowed_file(file.filename)):
149
+ return jsonify({'error': f'Invalid file format. Supported: {", ".join(ALLOWED_EXTENSIONS)}'}), 400
150
+
151
+ # Save uploaded file
152
+ temp_file_path = os.path.join(TEMPORARY_FOLDER, secure_filename(file.filename))
153
+ file.save(temp_file_path)
154
+
155
+ # Handle video/audio
156
+ file_extension = file.filename.rsplit('.', 1)[1].lower()
157
+ if file_extension in ALLOWED_VIDEO_EXTENSIONS:
158
+ temp_audio_path = os.path.join(TEMPORARY_FOLDER, f"temp_audio_{int(time.time())}.wav")
159
+ extract_audio_from_video(temp_file_path, temp_audio_path)
160
+ transcription_file = temp_audio_path
161
+ else:
162
+ transcription_file = temp_file_path
163
+
164
+ # Transcribe
165
+ segments, _ = wmodel.transcribe(
166
+ transcription_file,
167
+ beam_size=beamsize,
168
+ vad_filter=True,
169
+ without_timestamps=True,
170
+ compression_ratio_threshold=2.4,
171
+ word_timestamps=False
172
+ )
173
+
174
+ full_text = " ".join(segment.text for segment in segments)
175
+ file_type = 'video' if file_extension in ALLOWED_VIDEO_EXTENSIONS else 'audio'
176
+
177
+ # Send FCM data message
178
+ # Send FCM data message
179
+ send_fcm_data_message(fcm_token, full_text, file_type, created_date, transcription_name)
180
+
181
+ return jsonify({}), 200
182
+
183
+ except Exception as e:
184
+ return jsonify({'error': str(e)}), 500
185
+
186
+ finally:
187
+ cleanup_temp_files(temp_file_path, temp_audio_path)
188
+ active_requests -= 1
189
+ request_semaphore.release()
190
+ print(f"Processed in {time.time()-start_time:.2f}s (Active: {active_requests})")
191
+
192
+ if __name__ == "__main__":
193
+ if not os.path.exists(TEMPORARY_FOLDER):
194
+ os.makedirs(TEMPORARY_FOLDER)
195
+
196
  app.run(host="0.0.0.0", port=7860, threaded=True)