github-actions[bot] commited on
Commit
ea14d5d
Β·
1 Parent(s): 4116bc7

Auto-deploy from GitHub: 07597185080a5be8bd74c45bc69a05d84f5e6604

Browse files
Files changed (3) hide show
  1. Dockerfile +2 -7
  2. app.py +145 -1
  3. worker.py +24 -11
Dockerfile CHANGED
@@ -25,10 +25,5 @@ RUN mkdir -p uploads temp_dir
25
  # Expose port
26
  EXPOSE 7860
27
 
28
- # Create a startup script
29
- RUN echo '#!/bin/bash\n\
30
- python worker.py &\n\
31
- python app.py' > /app/start.sh && chmod +x /app/start.sh
32
-
33
- # Run the application
34
- CMD ["/app/start.sh"]
 
25
  # Expose port
26
  EXPOSE 7860
27
 
28
+ # Run only the Flask app (worker starts automatically on first upload)
29
+ CMD ["python", "app.py"]
 
 
 
 
 
app.py CHANGED
@@ -5,6 +5,9 @@ import os
5
  import uuid
6
  from datetime import datetime
7
  from werkzeug.utils import secure_filename
 
 
 
8
 
9
  app = Flask(__name__)
10
  CORS(app)
@@ -15,6 +18,10 @@ ALLOWED_EXTENSIONS = {'wav', 'mp3', 'flac', 'ogg', 'm4a', 'aac'}
15
  os.makedirs(UPLOAD_FOLDER, exist_ok=True)
16
  os.makedirs('temp_dir', exist_ok=True)
17
 
 
 
 
 
18
  def init_db():
19
  conn = sqlite3.connect('audio_captions.db')
20
  c = conn.cursor()
@@ -32,6 +39,129 @@ def init_db():
32
  def allowed_file(filename):
33
  return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  @app.route('/')
36
  def index():
37
  return send_from_directory('.', 'index.html')
@@ -63,6 +193,9 @@ def upload_audio():
63
  conn.commit()
64
  conn.close()
65
 
 
 
 
66
  return jsonify({
67
  'id': file_id,
68
  'filename': filename,
@@ -115,10 +248,21 @@ def get_file(file_id):
115
 
116
  @app.route('/health', methods=['GET'])
117
  def health():
118
- return jsonify({'status': 'healthy', 'service': 'audio-caption-generator'})
 
 
 
 
119
 
120
  if __name__ == '__main__':
121
  init_db()
 
 
 
 
 
 
 
122
  # Use PORT environment variable for Hugging Face compatibility
123
  port = int(os.environ.get('PORT', 7860))
124
  app.run(debug=False, host='0.0.0.0', port=port)
 
5
  import uuid
6
  from datetime import datetime
7
  from werkzeug.utils import secure_filename
8
+ import threading
9
+ import subprocess
10
+ import time
11
 
12
  app = Flask(__name__)
13
  CORS(app)
 
18
  os.makedirs(UPLOAD_FOLDER, exist_ok=True)
19
  os.makedirs('temp_dir', exist_ok=True)
20
 
21
+ # Worker state
22
+ worker_thread = None
23
+ worker_running = False
24
+
25
  def init_db():
26
  conn = sqlite3.connect('audio_captions.db')
27
  c = conn.cursor()
 
39
  def allowed_file(filename):
40
  return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
41
 
42
+ def start_worker():
43
+ """Start the worker thread if not already running"""
44
+ global worker_thread, worker_running
45
+
46
+ if not worker_running:
47
+ worker_running = True
48
+ worker_thread = threading.Thread(target=worker_loop, daemon=True)
49
+ worker_thread.start()
50
+ print("βœ… Worker thread started")
51
+
52
+ def worker_loop():
53
+ """Main worker loop that processes audio files"""
54
+ print("πŸ€– STT Worker started. Monitoring for new audio files...")
55
+
56
+ CWD = "./"
57
+ PYTHON_PATH = "stt-transcribe"
58
+ STT_MODEL_NAME = "fasterwhispher"
59
+ POLL_INTERVAL = 3 # seconds
60
+
61
+ import shlex
62
+ import json
63
+
64
+ while worker_running:
65
+ try:
66
+ # Get next unprocessed file
67
+ conn = sqlite3.connect('audio_captions.db')
68
+ conn.row_factory = sqlite3.Row
69
+ c = conn.cursor()
70
+ c.execute('''SELECT * FROM audio_files
71
+ WHERE status = 'not_started'
72
+ ORDER BY created_at ASC
73
+ LIMIT 1''')
74
+ row = c.fetchone()
75
+ conn.close()
76
+
77
+ if row:
78
+ file_id = row['id']
79
+ filepath = row['filepath']
80
+ filename = row['filename']
81
+
82
+ print(f"\n{'='*60}")
83
+ print(f"🎡 Processing: {filename}")
84
+ print(f"πŸ“ ID: {file_id}")
85
+ print(f"{'='*60}")
86
+
87
+ # Update status to processing
88
+ update_status(file_id, 'processing')
89
+
90
+ try:
91
+ # Run STT command
92
+ print(f"πŸ”„ Running STT on: {os.path.abspath(filepath)}")
93
+ command = f"""cd {CWD} && {PYTHON_PATH} --input {shlex.quote(os.path.abspath(filepath))} --model {STT_MODEL_NAME}"""
94
+
95
+ subprocess.run(
96
+ command,
97
+ shell=True,
98
+ executable="/bin/bash",
99
+ check=True,
100
+ cwd=CWD,
101
+ env={
102
+ **os.environ,
103
+ 'PYTHONUNBUFFERED': '1',
104
+ 'CUDA_LAUNCH_BLOCKING': '1',
105
+ 'USE_CPU_IF_POSSIBLE': 'true'
106
+ }
107
+ )
108
+
109
+ # Read transcription result
110
+ output_path = f'{CWD}/temp_dir/output_transcription.json'
111
+ with open(output_path, 'r') as file:
112
+ result = json.loads(file.read().strip())
113
+
114
+ # Extract caption text
115
+ caption = result.get('text', '') or result.get('transcription', '') or str(result)
116
+
117
+ print(f"βœ… Successfully processed: {filename}")
118
+ print(f"πŸ“„ Caption preview: {caption[:100]}...")
119
+
120
+ # Update database with success
121
+ update_status(file_id, 'completed', caption=caption)
122
+
123
+ # Delete the audio file after successful processing
124
+ if os.path.exists(filepath):
125
+ os.remove(filepath)
126
+ print(f"πŸ—‘οΈ Deleted audio file: {filepath}")
127
+
128
+ except Exception as e:
129
+ print(f"❌ Failed to process: {filename}")
130
+ print(f"Error: {str(e)}")
131
+ update_status(file_id, 'failed', error=str(e))
132
+
133
+ # Don't delete file on failure (for debugging)
134
+ # Optionally delete after some time or manual review
135
+
136
+ else:
137
+ # No files to process, sleep for a bit
138
+ time.sleep(POLL_INTERVAL)
139
+
140
+ except Exception as e:
141
+ print(f"⚠️ Worker error: {str(e)}")
142
+ time.sleep(POLL_INTERVAL)
143
+
144
+ def update_status(file_id, status, caption=None, error=None):
145
+ """Update the status of a file in the database"""
146
+ conn = sqlite3.connect('audio_captions.db')
147
+ c = conn.cursor()
148
+
149
+ if status == 'completed':
150
+ c.execute('''UPDATE audio_files
151
+ SET status = ?, caption = ?, processed_at = ?
152
+ WHERE id = ?''',
153
+ (status, caption, datetime.now().isoformat(), file_id))
154
+ elif status == 'failed':
155
+ c.execute('''UPDATE audio_files
156
+ SET status = ?, caption = ?, processed_at = ?
157
+ WHERE id = ?''',
158
+ (status, f"Error: {error}", datetime.now().isoformat(), file_id))
159
+ else:
160
+ c.execute('UPDATE audio_files SET status = ? WHERE id = ?', (status, file_id))
161
+
162
+ conn.commit()
163
+ conn.close()
164
+
165
  @app.route('/')
166
  def index():
167
  return send_from_directory('.', 'index.html')
 
193
  conn.commit()
194
  conn.close()
195
 
196
+ # Start worker on first upload
197
+ start_worker()
198
+
199
  return jsonify({
200
  'id': file_id,
201
  'filename': filename,
 
248
 
249
  @app.route('/health', methods=['GET'])
250
  def health():
251
+ return jsonify({
252
+ 'status': 'healthy',
253
+ 'service': 'audio-caption-generator',
254
+ 'worker_running': worker_running
255
+ })
256
 
257
  if __name__ == '__main__':
258
  init_db()
259
+ print("\n" + "="*60)
260
+ print("πŸš€ Audio Caption Generator API Server")
261
+ print("="*60)
262
+ print("πŸ“Œ Worker will start automatically on first upload")
263
+ print("πŸ—‘οΈ Audio files will be deleted after successful processing")
264
+ print("="*60 + "\n")
265
+
266
  # Use PORT environment variable for Hugging Face compatibility
267
  port = int(os.environ.get('PORT', 7860))
268
  app.run(debug=False, host='0.0.0.0', port=port)
worker.py CHANGED
@@ -9,12 +9,12 @@ from datetime import datetime
9
  CWD = "./"
10
  PYTHON_PATH = "stt-transcribe"
11
  STT_MODEL_NAME = "fasterwhispher"
12
- POLL_INTERVAL = 5 # seconds
13
 
14
  def process_audio(file_id, filepath):
15
  """Process audio file using STT and return the transcription"""
16
  try:
17
- print(f"Processing file: {filepath}")
18
 
19
  # Run STT command
20
  command = f"""cd {CWD} && {PYTHON_PATH} --input {shlex.quote(os.path.abspath(filepath))} --model {STT_MODEL_NAME}"""
@@ -44,7 +44,7 @@ def process_audio(file_id, filepath):
44
  return caption, None
45
 
46
  except Exception as e:
47
- print(f"Error processing file {file_id}: {str(e)}")
48
  return None, str(e)
49
 
50
  def update_status(file_id, status, caption=None, error=None):
@@ -70,7 +70,8 @@ def update_status(file_id, status, caption=None, error=None):
70
 
71
  def worker_loop():
72
  """Main worker loop that processes audio files"""
73
- print("STT Worker started. Polling for new audio files...")
 
74
 
75
  while True:
76
  try:
@@ -91,8 +92,8 @@ def worker_loop():
91
  filename = row['filename']
92
 
93
  print(f"\n{'='*60}")
94
- print(f"Processing: {filename}")
95
- print(f"ID: {file_id}")
96
  print(f"{'='*60}")
97
 
98
  # Update status to processing
@@ -102,24 +103,36 @@ def worker_loop():
102
  caption, error = process_audio(file_id, filepath)
103
 
104
  if caption:
105
- print(f"βœ“ Successfully processed: {filename}")
106
- print(f"Caption: {caption[:100]}...")
107
  update_status(file_id, 'completed', caption=caption)
 
 
 
 
 
108
  else:
109
- print(f"βœ— Failed to process: {filename}")
110
  print(f"Error: {error}")
111
  update_status(file_id, 'failed', error=error)
 
112
  else:
113
  # No files to process, sleep for a bit
114
  time.sleep(POLL_INTERVAL)
115
 
116
  except Exception as e:
117
- print(f"Worker error: {str(e)}")
118
  time.sleep(POLL_INTERVAL)
119
 
120
  if __name__ == '__main__':
121
  # Initialize database if it doesn't exist
122
  if not os.path.exists('audio_captions.db'):
123
- print("Database not found. Please run app.py first to initialize.")
124
  else:
 
 
 
 
 
 
125
  worker_loop()
 
9
  CWD = "./"
10
  PYTHON_PATH = "stt-transcribe"
11
  STT_MODEL_NAME = "fasterwhispher"
12
+ POLL_INTERVAL = 3 # seconds
13
 
14
  def process_audio(file_id, filepath):
15
  """Process audio file using STT and return the transcription"""
16
  try:
17
+ print(f"πŸ”„ Running STT on: {os.path.abspath(filepath)}")
18
 
19
  # Run STT command
20
  command = f"""cd {CWD} && {PYTHON_PATH} --input {shlex.quote(os.path.abspath(filepath))} --model {STT_MODEL_NAME}"""
 
44
  return caption, None
45
 
46
  except Exception as e:
47
+ print(f"❌ Error processing file {file_id}: {str(e)}")
48
  return None, str(e)
49
 
50
  def update_status(file_id, status, caption=None, error=None):
 
70
 
71
  def worker_loop():
72
  """Main worker loop that processes audio files"""
73
+ print("πŸ€– STT Worker started. Monitoring for new audio files...")
74
+ print("πŸ—‘οΈ Audio files will be deleted after successful processing\n")
75
 
76
  while True:
77
  try:
 
92
  filename = row['filename']
93
 
94
  print(f"\n{'='*60}")
95
+ print(f"🎡 Processing: {filename}")
96
+ print(f"πŸ“ ID: {file_id}")
97
  print(f"{'='*60}")
98
 
99
  # Update status to processing
 
103
  caption, error = process_audio(file_id, filepath)
104
 
105
  if caption:
106
+ print(f"βœ… Successfully processed: {filename}")
107
+ print(f"πŸ“„ Caption preview: {caption[:100]}...")
108
  update_status(file_id, 'completed', caption=caption)
109
+
110
+ # Delete the audio file after successful processing
111
+ if os.path.exists(filepath):
112
+ os.remove(filepath)
113
+ print(f"πŸ—‘οΈ Deleted audio file: {filepath}")
114
  else:
115
+ print(f"❌ Failed to process: {filename}")
116
  print(f"Error: {error}")
117
  update_status(file_id, 'failed', error=error)
118
+ # Don't delete file on failure (for debugging)
119
  else:
120
  # No files to process, sleep for a bit
121
  time.sleep(POLL_INTERVAL)
122
 
123
  except Exception as e:
124
+ print(f"⚠️ Worker error: {str(e)}")
125
  time.sleep(POLL_INTERVAL)
126
 
127
  if __name__ == '__main__':
128
  # Initialize database if it doesn't exist
129
  if not os.path.exists('audio_captions.db'):
130
+ print("❌ Database not found. Please run app.py first to initialize.")
131
  else:
132
+ print("\n" + "="*60)
133
+ print("πŸš€ Starting STT Worker (Standalone Mode)")
134
+ print("="*60)
135
+ print("⚠️ Note: Worker is now embedded in app.py")
136
+ print("⚠️ This standalone mode is for testing/debugging only")
137
+ print("="*60 + "\n")
138
  worker_loop()