bluenevus commited on
Commit
86821c0
·
verified ·
1 Parent(s): cdffd44

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +123 -547
app.py CHANGED
@@ -54,17 +54,7 @@ if not grok_api_key:
54
  server = flask.Flask(__name__)
55
  app = dash.Dash(__name__, server=server, external_stylesheets=[dbc.themes.BOOTSTRAP], suppress_callback_exceptions=True)
56
 
57
- session_data = defaultdict(lambda: {
58
- "audio_path": None,
59
- "transcript": None,
60
- "minutes": None,
61
- "temp_dir": None,
62
- "original_filename": None,
63
- "diarized_transcript": None,
64
- "diarization_done": False,
65
- "advanced_diarized_transcript": None,
66
- "preview_mode": "auto"
67
- })
68
  session_locks = defaultdict(threading.Lock)
69
 
70
  def get_session_dir(session_id):
@@ -163,186 +153,6 @@ def transcribe_audio(file_path):
163
  logging.error(f"An unexpected error occurred during transcription: {e}")
164
  return f"Error during transcription: An unexpected error occurred."
165
 
166
- def diarize_transcript_ai(transcript, model_name, session_id):
167
- logging.info(f"Starting AI diarization using {model_name} for session {session_id}")
168
- if not transcript or "Error:" in transcript:
169
- return "Error: Cannot diarize invalid or missing transcript."
170
- prompt = (
171
- "You are given a transcript of a conversation or meeting. "
172
- "Please analyze the text and assign speaker turns as Speaker 1, Speaker 2, etc. "
173
- "If a person introduces themselves by name, try to use their name as the speaker label. "
174
- "Otherwise, assign speakers based on changes in voice, speech patterns, or cues in the transcript. "
175
- "Format the output as alternating lines, each starting with the speaker label, for example:\n"
176
- "Speaker 1: Hello and welcome.\n"
177
- "Speaker 2: Thank you. My name is Alex.\n"
178
- "Alex: I have a question about...\n"
179
- "Speaker 1: Please go ahead.\n"
180
- "If unsure, use Speaker 1, Speaker 2, etc. Do not invent content.\n\n"
181
- f"Transcript:\n{transcript}\n\n"
182
- "Diarized Transcript:"
183
- )
184
- with session_locks[session_id]:
185
- try:
186
- if model_name == 'openai':
187
- if not openai.api_key: return "Error: OpenAI API key not configured."
188
- client = openai.OpenAI()
189
- response = client.chat.completions.create(
190
- model="gpt-3.5-turbo",
191
- messages=[
192
- {"role": "system", "content": "You are a professional assistant for meeting AI diarization."},
193
- {"role": "user", "content": prompt}
194
- ],
195
- timeout=120
196
- )
197
- logging.info(f"OpenAI diarization successful for session {session_id}")
198
- return response.choices[0].message.content
199
- elif model_name == 'gemini':
200
- if not genai: return "Error: Google Gemini API not configured or key missing."
201
- model = genai.GenerativeModel('gemini-1.5-flash-latest')
202
- response = model.generate_content(
203
- prompt,
204
- request_options={'timeout': 120}
205
- )
206
- logging.info(f"Gemini diarization successful for session {session_id}")
207
- if response.parts:
208
- return response.text
209
- else:
210
- logging.warning(f"Gemini response blocked or empty for diarization for session {session_id}. Reason: {response.prompt_feedback}")
211
- return f"Error: Gemini response blocked or empty. Reason: {response.prompt_feedback}"
212
- elif model_name == 'anthropic':
213
- if not anthropic: return "Error: Anthropic API not configured or key missing."
214
- response = anthropic.messages.create(
215
- model="claude-3-5-haiku-20241022",
216
- max_tokens=2000,
217
- messages=[
218
- {
219
- "role": "user",
220
- "content": prompt
221
- }
222
- ],
223
- timeout=120
224
- )
225
- logging.info(f"Anthropic diarization successful for session {session_id}")
226
- if response.content and isinstance(response.content, list) and hasattr(response.content[0], 'text'):
227
- return response.content[0].text
228
- else:
229
- logging.error(f"Could not extract content from Anthropic response (diarization): {response}")
230
- return "Error: Could not extract content from Anthropic response."
231
- elif model_name == 'grok':
232
- if not grok_api_key: return "Error: Grok API key (via Groq) not configured."
233
- groq_url = "https://api.groq.com/openai/v1/chat/completions"
234
- headers = {
235
- "Authorization": f"Bearer {grok_api_key}",
236
- "Content-Type": "application/json"
237
- }
238
- data = {
239
- "model": "grok-3-mini-fast-beta",
240
- "messages": [
241
- {"role": "system", "content": "You are a professional assistant for meeting AI diarization."},
242
- {"role": "user", "content": prompt}
243
- ],
244
- "max_tokens": 2000,
245
- "temperature": 0.7
246
- }
247
- response = requests.post(groq_url, headers=headers, json=data, timeout=120)
248
- response.raise_for_status()
249
- logging.info(f"Groq ({data['model']}) diarization successful for session {session_id}")
250
- return response.json()["choices"][0]["message"]["content"]
251
- else:
252
- logging.warning(f"Invalid model selection for diarization: {model_name}")
253
- return "Error: Invalid model selection"
254
- except Exception as e:
255
- logging.error(f"Error diarizing transcript with {model_name} for session {session_id}: {e}", exc_info=True)
256
- return f"Error diarizing transcript using {model_name}: An unexpected error occurred."
257
-
258
- def advanced_diarize_transcript_ai(transcript, model_name, session_id):
259
- logging.info(f"Starting advanced AI diarization using {model_name} for session {session_id}")
260
- if not transcript or "Error:" in transcript:
261
- return "Error: Cannot diarize invalid or missing transcript."
262
- prompt = (
263
- "Analyze the given transcript to identify distinct speakers without labeled identifiers. "
264
- "Create unique speaker embeddings based on individual speech patterns, vocabulary choices, and linguistic styles. "
265
- "Examine the context and content of each utterance to detect likely speaker changes. "
266
- "Recognize typical conversation structures and turn-taking behaviors to differentiate between speakers. "
267
- "Finally, use topic modeling to identify shifts in subject matter and areas of expertise, associating certain topics with specific speakers. "
268
- "Based on this analysis, assign speaker labels (e.g., Speaker 1, Speaker 2, name if given) to each utterance in the transcript.\n\n"
269
- f"Transcript:\n{transcript}\n\n"
270
- "Diarized Transcript:"
271
- )
272
- with session_locks[session_id]:
273
- try:
274
- if model_name == 'openai':
275
- if not openai.api_key: return "Error: OpenAI API key not configured."
276
- client = openai.OpenAI()
277
- response = client.chat.completions.create(
278
- model="gpt-3.5-turbo",
279
- messages=[
280
- {"role": "system", "content": "You are a professional assistant for meeting AI diarization."},
281
- {"role": "user", "content": prompt}
282
- ],
283
- timeout=120
284
- )
285
- logging.info(f"OpenAI advanced diarization successful for session {session_id}")
286
- return response.choices[0].message.content
287
- elif model_name == 'gemini':
288
- if not genai: return "Error: Google Gemini API not configured or key missing."
289
- model = genai.GenerativeModel('gemini-1.5-flash-latest')
290
- response = model.generate_content(
291
- prompt,
292
- request_options={'timeout': 120}
293
- )
294
- logging.info(f"Gemini advanced diarization successful for session {session_id}")
295
- if response.parts:
296
- return response.text
297
- else:
298
- logging.warning(f"Gemini response blocked or empty for advanced diarization for session {session_id}. Reason: {response.prompt_feedback}")
299
- return f"Error: Gemini response blocked or empty. Reason: {response.prompt_feedback}"
300
- elif model_name == 'anthropic':
301
- if not anthropic: return "Error: Anthropic API not configured or key missing."
302
- response = anthropic.messages.create(
303
- model="claude-3-5-haiku-20241022",
304
- max_tokens=2000,
305
- messages=[
306
- {
307
- "role": "user",
308
- "content": prompt
309
- }
310
- ],
311
- timeout=120
312
- )
313
- logging.info(f"Anthropic advanced diarization successful for session {session_id}")
314
- if response.content and isinstance(response.content, list) and hasattr(response.content[0], 'text'):
315
- return response.content[0].text
316
- else:
317
- logging.error(f"Could not extract content from Anthropic response (advanced diarization): {response}")
318
- return "Error: Could not extract content from Anthropic response."
319
- elif model_name == 'grok':
320
- if not grok_api_key: return "Error: Grok API key (via Groq) not configured."
321
- groq_url = "https://api.groq.com/openai/v1/chat/completions"
322
- headers = {
323
- "Authorization": f"Bearer {grok_api_key}",
324
- "Content-Type": "application/json"
325
- }
326
- data = {
327
- "model": "grok-3-mini-fast-beta",
328
- "messages": [
329
- {"role": "system", "content": "You are a professional assistant for meeting AI diarization."},
330
- {"role": "user", "content": prompt}
331
- ],
332
- "max_tokens": 2000,
333
- "temperature": 0.7
334
- }
335
- response = requests.post(groq_url, headers=headers, json=data, timeout=120)
336
- response.raise_for_status()
337
- logging.info(f"Groq ({data['model']}) advanced diarization successful for session {session_id}")
338
- return response.json()["choices"][0]["message"]["content"]
339
- else:
340
- logging.warning(f"Invalid model selection for advanced diarization: {model_name}")
341
- return "Error: Invalid model selection"
342
- except Exception as e:
343
- logging.error(f"Error in advanced diarization with {model_name} for session {session_id}: {e}", exc_info=True)
344
- return f"Error advanced diarizing transcript using {model_name}: An unexpected error occurred."
345
-
346
  def generate_minutes_ai(transcript, model_name, session_id):
347
  logging.info(f"Generating minutes using {model_name} for session {session_id}")
348
  if not transcript or "Error:" in transcript:
@@ -465,112 +275,87 @@ app.layout = dbc.Container([
465
  dcc.Download(id="download-transcript"),
466
  dcc.Download(id="download-audio"),
467
  dcc.Download(id="download-minutes"),
468
- dcc.Download(id="download-diarized"),
469
  dbc.Row([
470
- dbc.Col(
471
- dbc.Card(
472
- dbc.CardBody([
473
- html.H4("Controls", className="card-title"),
474
- html.Div("Upload meeting audio or video file:"),
475
- dcc.Upload(
476
- id='audio-uploader',
477
- children=html.Div([
478
- 'Drag and Drop or ',
479
- html.A('Select Audio/Video File')
480
- ]),
481
- style={
482
- 'width': '100%',
483
- 'height': '60px',
484
- 'lineHeight': '60px',
485
- 'borderWidth': '1px',
486
- 'borderStyle': 'dashed',
487
- 'borderRadius': '5px',
488
- 'textAlign': 'center',
489
- 'margin': '10px 0'
490
- },
491
- multiple=False,
492
- accept='audio/*,video/*'
493
- ),
494
- html.Div(id='upload-status', children='Status: Ready to Upload', className="mt-2"),
495
- html.Div(id='uploaded-filename', style={'fontWeight': 'bold', 'marginBottom': '8px'}),
496
- html.Hr(),
497
- html.H5("View Output", className="mt-2"),
498
- dbc.Button("View Original Transcript", id="nav-original-btn", color="tertiary", className="mb-2 w-100"),
499
- dbc.Button("View Diarized Transcript", id="nav-diarized-btn", color="tertiary", className="mb-2 w-100"),
500
- dbc.Button("View Minutes", id="nav-minutes-btn", color="tertiary", className="mb-2 w-100"),
501
- html.Hr(),
502
- html.H5("Select AI Model", className="mt-2"),
503
- dcc.Dropdown(
504
- id='model-selection',
505
- options=[
506
- {'label': 'OpenAI GPT-3.5 Turbo', 'value': 'openai', 'disabled': not openai.api_key},
507
- {'label': 'Google Gemini 1.5 Flash', 'value': 'gemini', 'disabled': not genai},
508
- {'label': 'Anthropic Claude 3.5 Haiku', 'value': 'anthropic', 'disabled': not anthropic},
509
- {'label': 'Grok 3 Mini', 'value': 'grok', 'disabled': not grok_api_key}
510
- ],
511
- value='openai' if openai.api_key else (
512
- 'gemini' if genai else (
513
- 'anthropic' if anthropic else (
514
- 'grok' if grok_api_key else None
515
- )
516
- )
517
- ),
518
- clearable=False,
519
- className="mt-2",
520
- disabled=not (openai.api_key or genai or anthropic or grok_api_key)
521
- ),
522
- dbc.Checkbox(
523
- id="diarize-checkbox",
524
- className="mt-3",
525
- value=False,
526
- label="Diarize Speakers (AI)",
527
- style={"fontWeight": "bold"}
528
- ),
529
- dbc.Button("Diarize Transcript (Simple)", id="diarize-btn", color="primary", className="mt-2 w-100", disabled=True),
530
- dbc.Button("Diarize Transcript (Advanced)", id="advanced-diarize-btn", color="secondary", className="mt-2 w-100", disabled=True),
531
- dbc.Button("Download Diarized Transcript (.docx)", id="download-diarized-btn", color="info", className="mt-2 w-100", disabled=True),
532
- dbc.Button("Generate Minutes", id="minutes-btn", color="secondary", className="mt-4 w-100", disabled=True),
533
- dbc.Button("Delete Session Data", id="delete-btn", color="warning", className="mt-4 w-100", disabled=True),
534
- ]),
535
- style={'height': '80vh', 'overflow-y': 'auto'}
536
- ), width=12, lg=4
537
- ),
538
- dbc.Col(
539
- dbc.Card(
540
- dbc.CardBody([
541
- dcc.Loading(
542
- id="loading",
543
- type="default",
544
- parent_style={'position': 'relative', 'height': '100%'},
545
- style={'position': 'absolute', 'top': '50%', 'left': '50%', 'transform': 'translate(-50%, -50%)', 'zIndex':'1000'},
546
- children=[
547
- html.Div([
548
- html.H4("Output", className="card-title"),
549
- html.Div(id="status", children="Status: Idle", className="mb-2"),
550
- html.H5("Transcript / Minutes"),
551
- html.Div(id="transcript-preview", style={
552
- "height": "400px",
553
- "overflow-y": "scroll",
554
- "border": "1px solid #ccc",
555
- "padding": "10px",
556
- "white-space": "pre-wrap",
557
- "word-wrap": "break-word",
558
- "background-color": "#f9f9f9"
559
- }),
560
- html.H5("Downloads", className="mt-3"),
561
- dbc.Row([
562
- dbc.Col(dbc.Button("Download Transcript (.docx)", id="download-transcript-btn", color="info", className="w-100 mb-2", disabled=True), width=12, md=4),
563
- dbc.Col(dbc.Button("Download Minutes (.docx)", id="download-minutes-btn", color="info", className="w-100 mb-2", disabled=True), width=12, md=4),
564
- dbc.Col(dbc.Button("Download Processed Audio", id="download-audio-btn", color="info", className="w-100 mb-2", disabled=True), width=12, md=4),
565
- ]),
566
- ])
567
- ]
568
- ),
569
- html.Div(id="loading-output", style={"height": "0px", "visibility": "hidden"}),
570
- ]),
571
- style={'height': '80vh', 'overflow-y': 'auto', 'position': 'relative'}
572
- ), width=12, lg=8
573
- ),
574
  ])
575
  ], fluid=True)
576
 
@@ -607,81 +392,38 @@ def manage_session_id(existing_session_id):
607
  return final_session_id
608
 
609
  @app.callback(
610
- [
611
- Output("status", "children"),
612
- Output("transcript-preview", "children"),
613
- Output("minutes-btn", "disabled"),
614
- Output("download-transcript-btn", "disabled"),
615
- Output("download-minutes-btn", "disabled"),
616
- Output("download-audio-btn", "disabled"),
617
- Output("delete-btn", "disabled"),
618
- Output("loading-output", "children"),
619
- Output("upload-status", "children"),
620
- Output("diarize-btn", "disabled"),
621
- Output("download-diarized-btn", "disabled"),
622
- Output("uploaded-filename", "children"),
623
- Output("advanced-diarize-btn", "disabled"),
624
- ],
625
- [
626
- Input('audio-uploader', 'contents'),
627
- Input("minutes-btn", "n_clicks"),
628
- Input("delete-btn", "n_clicks"),
629
- Input("diarize-btn", "n_clicks"),
630
- Input("diarize-checkbox", "value"),
631
- Input("nav-original-btn", "n_clicks"),
632
- Input("nav-diarized-btn", "n_clicks"),
633
- Input("nav-minutes-btn", "n_clicks"),
634
- Input("advanced-diarize-btn", "n_clicks"),
635
- ],
636
- [
637
- State("session-id", "data"),
638
- State("model-selection", "value"),
639
- State("transcript-preview", "children"),
640
- State('audio-uploader', 'filename'),
641
- State("diarize-checkbox", "value")
642
- ],
643
  prevent_initial_call=True
644
  )
645
- def handle_actions(
646
- upload_contents, minutes_clicks, delete_clicks, diarize_clicks, diarize_checkbox_val,
647
- nav_original_clicks, nav_diarized_clicks, nav_minutes_clicks, advanced_diarize_clicks,
648
- session_id, selected_model, existing_preview, filename, diarize_checkbox_val2
649
- ):
650
- diarize_checkbox = diarize_checkbox_val if diarize_checkbox_val is not None else diarize_checkbox_val2
651
  if not session_id:
652
  logging.warning("Session ID missing in handle_actions.")
653
- return "Status: Error - Session ID missing", "", True, True, True, True, True, None, "Status: Error", True, True, "", True
654
  ctx = dash.callback_context
655
  triggered_id = ctx.triggered_id if hasattr(ctx, 'triggered_id') else (ctx.triggered[0]['prop_id'].split('.')[0] if ctx.triggered else None)
656
  current_transcript = session_data[session_id].get("transcript", "")
657
  current_minutes = session_data[session_id].get("minutes", "")
658
  current_audio_path = session_data[session_id].get("audio_path", None)
659
  original_filename = session_data[session_id].get("original_filename", None)
660
- diarized_transcript = session_data[session_id].get("diarized_transcript", None)
661
- diarization_done = session_data[session_id].get("diarization_done", False)
662
- advanced_diarized_transcript = session_data[session_id].get("advanced_diarized_transcript", None)
663
- preview_mode = session_data[session_id].get("preview_mode", "auto")
664
- output_text = ""
665
- # Preview mode logic
666
- if preview_mode == "original":
667
- output_text = current_transcript if current_transcript else "No transcript available."
668
- elif preview_mode == "diarized":
669
- output_text = advanced_diarized_transcript if advanced_diarized_transcript else (diarized_transcript if diarized_transcript else "No diarized transcript available.")
670
- elif preview_mode == "minutes":
671
- output_text = current_minutes if current_minutes else "No minutes available."
672
- else:
673
- output_text = current_minutes if current_minutes else (
674
- advanced_diarized_transcript if advanced_diarized_transcript else (
675
- diarized_transcript if diarize_checkbox and diarized_transcript else (
676
- current_transcript if current_transcript else "Upload an audio or video file to begin."
677
- )
678
- )
679
- )
680
  status_msg = "Status: Idle"
681
  if current_minutes and "Error:" not in current_minutes:
682
  status_msg = "Status: Session restored. Minutes loaded."
683
- elif (diarize_checkbox or advanced_diarized_transcript) and (advanced_diarized_transcript or diarized_transcript) and "Error:" not in (advanced_diarized_transcript or diarized_transcript or ""):
684
- status_msg = "Status: Session restored. Diarized transcript loaded."
685
  elif current_transcript and "Error:" not in current_transcript:
686
  status_msg = "Status: Session restored. Transcript loaded. Ready for Minutes Generation."
687
  elif current_audio_path and os.path.exists(current_audio_path):
@@ -693,18 +435,12 @@ def handle_actions(
693
  dl_minutes_disabled = not bool(current_minutes and "Error:" not in current_minutes)
694
  dl_audio_disabled = not bool(current_audio_path and os.path.exists(current_audio_path))
695
  delete_disabled = not bool(session_data.get(session_id, {}).get("temp_dir"))
696
- diarize_disabled = not bool(current_transcript and "Error:" not in current_transcript)
697
- dl_diarized_disabled = not bool((advanced_diarized_transcript or diarized_transcript) and "Error:" not in (advanced_diarized_transcript or diarized_transcript or ""))
698
- uploaded_filename_text = original_filename if original_filename else ""
699
- advanced_diarize_disabled = diarize_disabled
700
  loading_output = None
701
  upload_status_msg = f"Status: {'Loaded: ' + original_filename if original_filename else 'Ready to Upload'}"
702
  start_time = time.time()
703
- # File upload logic
704
  if triggered_id == 'audio-uploader' and upload_contents is not None and filename is not None:
705
  logging.info(f"File uploaded for session {session_id}, filename: {filename}")
706
  session_data[session_id]["original_filename"] = filename
707
- uploaded_filename_text = filename
708
  upload_status_msg = f"Status: Processing Uploaded File ({filename})..."
709
  status_msg = "Status: Processing Upload..."
710
  loading_output = "Processing Upload..."
@@ -719,18 +455,12 @@ def handle_actions(
719
  session_data[session_id]["transcript"] = None
720
  session_data[session_id]["minutes"] = None
721
  session_data[session_id]["original_filename"] = None
722
- session_data[session_id]["diarized_transcript"] = None
723
- session_data[session_id]["diarization_done"] = False
724
- session_data[session_id]["advanced_diarized_transcript"] = None
725
  minutes_disabled = True
726
  dl_transcript_disabled = True
727
  dl_minutes_disabled = True
728
  dl_audio_disabled = True
729
  delete_disabled = False
730
- diarize_disabled = True
731
- dl_diarized_disabled = True
732
- advanced_diarize_disabled = True
733
- return status_msg, output_text, minutes_disabled, dl_transcript_disabled, dl_minutes_disabled, dl_audio_disabled, delete_disabled, None, upload_status_msg, diarize_disabled, dl_diarized_disabled, uploaded_filename_text, advanced_diarize_disabled
734
  safe_upload_filename = f"uploaded_file{f_ext}"
735
  upload_file_path = os.path.join(session_dir, safe_upload_filename)
736
  saved_upload_path = save_base64_data(upload_contents, upload_file_path)
@@ -762,13 +492,7 @@ def handle_actions(
762
  dl_minutes_disabled = True
763
  dl_audio_disabled = True
764
  delete_disabled = False
765
- diarize_disabled = True
766
- dl_diarized_disabled = True
767
- session_data[session_id]["diarized_transcript"] = None
768
- session_data[session_id]["diarization_done"] = False
769
- session_data[session_id]["advanced_diarized_transcript"] = None
770
- advanced_diarize_disabled = True
771
- return status_msg, output_text, minutes_disabled, dl_transcript_disabled, dl_minutes_disabled, dl_audio_disabled, delete_disabled, None, upload_status_msg, diarize_disabled, dl_diarized_disabled, uploaded_filename_text, advanced_diarize_disabled
772
  else:
773
  audio_path_for_transcription = saved_upload_path
774
  session_data[session_id]["audio_path"] = saved_upload_path
@@ -781,9 +505,6 @@ def handle_actions(
781
  transcript_text = transcribe_audio(audio_path_for_transcription)
782
  session_data[session_id]["transcript"] = transcript_text
783
  session_data[session_id]["minutes"] = None
784
- session_data[session_id]["diarized_transcript"] = None
785
- session_data[session_id]["diarization_done"] = False
786
- session_data[session_id]["advanced_diarized_transcript"] = None
787
  if "Error:" in transcript_text:
788
  status_msg = f"Status: Transcription Failed - {transcript_text}"
789
  output_text = transcript_text
@@ -792,9 +513,6 @@ def handle_actions(
792
  dl_minutes_disabled = True
793
  delete_disabled = False
794
  upload_status_msg = f"Status: Transcription Failed. ({filename})"
795
- diarize_disabled = True
796
- dl_diarized_disabled = True
797
- advanced_diarize_disabled = True
798
  else:
799
  status_msg = "Status: Transcription Complete. Ready for Minutes Generation."
800
  output_text = transcript_text
@@ -803,9 +521,6 @@ def handle_actions(
803
  dl_minutes_disabled = True
804
  delete_disabled = False
805
  upload_status_msg = f"Status: Processed & Transcribed: {filename}"
806
- diarize_disabled = False
807
- dl_diarized_disabled = True
808
- advanced_diarize_disabled = False
809
  processing_time = time.time() - start_time
810
  logging.info(f"File processing and transcription took {processing_time:.2f} seconds for session {session_id}")
811
  else:
@@ -819,28 +534,15 @@ def handle_actions(
819
  dl_minutes_disabled = True
820
  dl_audio_disabled = True
821
  delete_disabled = False
822
- diarize_disabled = True
823
- dl_diarized_disabled = True
824
- session_data[session_id]["diarized_transcript"] = None
825
- session_data[session_id]["diarization_done"] = False
826
- session_data[session_id]["advanced_diarized_transcript"] = None
827
- advanced_diarize_disabled = True
828
  elif triggered_id == "minutes-btn" and minutes_clicks:
829
  logging.info(f"Generate Minutes button clicked for session {session_id}")
830
- transcript_to_use = None
831
- if preview_mode == "diarized":
832
- transcript_to_use = advanced_diarized_transcript if advanced_diarized_transcript else diarized_transcript
833
- elif diarize_checkbox and (advanced_diarized_transcript or diarized_transcript):
834
- transcript_to_use = advanced_diarized_transcript if advanced_diarized_transcript else diarized_transcript
835
- else:
836
- transcript_to_use = current_transcript
837
- if transcript_to_use and "Error:" not in transcript_to_use:
838
  status_msg = f"Status: Generating Minutes ({selected_model})..."
839
  loading_output = "Generating Minutes..."
840
- minutes_text = generate_minutes_ai(transcript_to_use, selected_model, session_id)
841
  session_data[session_id]["minutes"] = minutes_text
842
  output_text = minutes_text
843
- session_data[session_id]["preview_mode"] = "minutes"
844
  if "Error:" in minutes_text:
845
  status_msg = f"Status: Minutes Generation Failed - {minutes_text}"
846
  dl_minutes_disabled = True
@@ -854,77 +556,10 @@ def handle_actions(
854
  dl_audio_disabled = not bool(session_data.get(session_id, {}).get("audio_path") and os.path.exists(session_data.get(session_id, {}).get("audio_path", "")))
855
  delete_disabled = False
856
  upload_status_msg = f"Status: Processed & Transcribed: {session_data[session_id].get('original_filename', 'File')}"
857
- diarize_disabled = not bool(session_data[session_id].get("transcript") and "Error:" not in session_data[session_id].get("transcript"))
858
- dl_diarized_disabled = not bool((session_data[session_id].get("advanced_diarized_transcript") or session_data[session_id].get("diarized_transcript")) and "Error:" not in (session_data[session_id].get("advanced_diarized_transcript") or session_data[session_id].get("diarized_transcript") or ""))
859
- advanced_diarize_disabled = diarize_disabled
860
  else:
861
  status_msg = "Status: Cannot generate minutes - No valid transcript available."
862
  output_text = existing_preview
863
  minutes_disabled = True
864
- elif triggered_id == "diarize-btn" and diarize_clicks:
865
- logging.info(f"Diarize button clicked for session {session_id}")
866
- current_transcript = session_data[session_id].get("transcript", "")
867
- if current_transcript and "Error:" not in current_transcript:
868
- status_msg = f"Status: Diarizing Transcript ({selected_model})..."
869
- loading_output = "Diarizing..."
870
- diarized_text = diarize_transcript_ai(current_transcript, selected_model, session_id)
871
- session_data[session_id]["diarized_transcript"] = diarized_text
872
- session_data[session_id]["diarization_done"] = "Error:" not in diarized_text
873
- output_text = diarized_text
874
- session_data[session_id]["preview_mode"] = "diarized"
875
- if "Error:" in diarized_text:
876
- status_msg = f"Status: Diarization Failed - {diarized_text}"
877
- dl_diarized_disabled = True
878
- else:
879
- status_msg = "Status: Diarization Complete."
880
- dl_diarized_disabled = False
881
- diarize_disabled = False
882
- advanced_diarize_disabled = False
883
- else:
884
- status_msg = "Status: Cannot diarize - No valid transcript available."
885
- output_text = existing_preview
886
- diarize_disabled = True
887
- dl_diarized_disabled = True
888
- advanced_diarize_disabled = True
889
- elif triggered_id == "advanced-diarize-btn" and advanced_diarize_clicks:
890
- logging.info(f"Advanced Diarize button clicked for session {session_id}")
891
- current_transcript = session_data[session_id].get("transcript", "")
892
- if current_transcript and "Error:" not in current_transcript:
893
- status_msg = f"Status: Advanced Diarizing Transcript ({selected_model})..."
894
- loading_output = "Advanced Diarizing..."
895
- adv_diarized_text = advanced_diarize_transcript_ai(current_transcript, selected_model, session_id)
896
- session_data[session_id]["advanced_diarized_transcript"] = adv_diarized_text
897
- output_text = adv_diarized_text
898
- session_data[session_id]["preview_mode"] = "diarized"
899
- if "Error:" in adv_diarized_text:
900
- status_msg = f"Status: Advanced Diarization Failed - {adv_diarized_text}"
901
- dl_diarized_disabled = True
902
- else:
903
- status_msg = "Status: Advanced Diarization Complete."
904
- dl_diarized_disabled = False
905
- diarize_disabled = False
906
- advanced_diarize_disabled = False
907
- else:
908
- status_msg = "Status: Cannot advanced diarize - No valid transcript available."
909
- output_text = existing_preview
910
- diarize_disabled = True
911
- dl_diarized_disabled = True
912
- advanced_diarize_disabled = True
913
- elif triggered_id == "nav-original-btn" and nav_original_clicks:
914
- logging.info(f"Nav: View Original Transcript for session {session_id}")
915
- output_text = current_transcript if current_transcript else "No transcript available."
916
- session_data[session_id]["preview_mode"] = "original"
917
- status_msg = "Status: Viewing Original Transcript."
918
- elif triggered_id == "nav-diarized-btn" and nav_diarized_clicks:
919
- logging.info(f"Nav: View Diarized Transcript for session {session_id}")
920
- output_text = advanced_diarized_transcript if advanced_diarized_transcript else (diarized_transcript if diarized_transcript else "No diarized transcript available.")
921
- session_data[session_id]["preview_mode"] = "diarized"
922
- status_msg = "Status: Viewing Diarized Transcript."
923
- elif triggered_id == "nav-minutes-btn" and nav_minutes_clicks:
924
- logging.info(f"Nav: View Minutes for session {session_id}")
925
- output_text = current_minutes if current_minutes else "No minutes available."
926
- session_data[session_id]["preview_mode"] = "minutes"
927
- status_msg = "Status: Viewing Minutes."
928
  elif triggered_id == "delete-btn" and delete_clicks:
929
  logging.info(f"Delete button clicked for session {session_id}")
930
  cleanup_session(session_id)
@@ -935,49 +570,24 @@ def handle_actions(
935
  dl_minutes_disabled = True
936
  dl_audio_disabled = True
937
  delete_disabled = True
938
- diarize_disabled = True
939
- dl_diarized_disabled = True
940
- advanced_diarize_disabled = True
941
  upload_status_msg = "Status: Ready to Upload"
942
- uploaded_filename_text = ""
943
  else:
944
- loaded_audio_path = session_data.get(session_id, {}).get("audio_path")
945
- loaded_transcript = session_data.get(session_id, {}).get("transcript")
946
- loaded_minutes = session_data.get(session_id, {}).get("minutes")
947
- loaded_diarized = session_data.get(session_id, {}).get("diarized_transcript")
948
- loaded_adv_diarized = session_data.get(session_id, {}).get("advanced_diarized_transcript")
949
- temp_dir_exists = bool(session_data.get(session_id, {}).get("temp_dir"))
950
- loaded_original_filename = session_data.get(session_id, {}).get("original_filename")
951
- dl_audio_disabled = not (loaded_audio_path and os.path.exists(loaded_audio_path))
952
- minutes_disabled = not (loaded_transcript and "Error:" not in loaded_transcript)
953
- dl_transcript_disabled = not (loaded_transcript and "Error:" not in loaded_transcript)
954
- dl_minutes_disabled = not (loaded_minutes and "Error:" not in loaded_minutes)
955
- diarize_disabled = not (loaded_transcript and "Error:" not in loaded_transcript)
956
- advanced_diarize_disabled = diarize_disabled
957
- dl_diarized_disabled = not ((loaded_adv_diarized or loaded_diarized) and "Error:" not in (loaded_adv_diarized or loaded_diarized or ""))
958
- delete_disabled = not (loaded_audio_path or loaded_transcript or loaded_minutes or loaded_diarized or temp_dir_exists or loaded_original_filename or loaded_adv_diarized)
959
- if loaded_original_filename and dl_audio_disabled and not loaded_transcript:
960
- upload_status_msg = f"Status: Error processing {loaded_original_filename}?"
961
- elif loaded_audio_path and os.path.exists(loaded_audio_path):
962
- upload_status_msg = f"Status: Processed audio loaded ({loaded_original_filename or 'previous file'})."
963
- else:
964
- upload_status_msg = "Status: Ready to Upload"
965
- uploaded_filename_text = loaded_original_filename if loaded_original_filename else ""
966
- pmode = session_data[session_id].get("preview_mode", "auto")
967
- if pmode == "original":
968
- output_text = loaded_transcript if loaded_transcript else "No transcript available."
969
- elif pmode == "diarized":
970
- output_text = loaded_adv_diarized if loaded_adv_diarized else (loaded_diarized if loaded_diarized else "No diarized transcript available.")
971
- elif pmode == "minutes":
972
- output_text = loaded_minutes if loaded_minutes else "No minutes available."
973
- else:
974
- output_text = loaded_minutes if loaded_minutes else (
975
- loaded_adv_diarized if loaded_adv_diarized else (
976
- loaded_diarized if diarize_checkbox and loaded_diarized else (
977
- loaded_transcript if loaded_transcript else "Upload an audio or video file to begin."
978
- )
979
- )
980
- )
981
  return (
982
  status_msg,
983
  output_text,
@@ -987,11 +597,7 @@ def handle_actions(
987
  dl_audio_disabled,
988
  delete_disabled,
989
  loading_output,
990
- upload_status_msg,
991
- diarize_disabled,
992
- dl_diarized_disabled,
993
- uploaded_filename_text,
994
- advanced_diarize_disabled
995
  )
996
 
997
  @app.callback(
@@ -1068,37 +674,7 @@ def download_audio_file(n_clicks, session_id):
1068
  logging.error(f"Processed audio file not found at path {audio_path} for session {session_id}")
1069
  return None
1070
 
1071
- @app.callback(
1072
- Output("download-diarized", "data"),
1073
- Input("download-diarized-btn", "n_clicks"),
1074
- State("session-id", "data"),
1075
- prevent_initial_call=True,
1076
- )
1077
- def download_diarized_file(n_clicks, session_id):
1078
- diarized = None
1079
- if session_id and session_data.get(session_id, {}).get("advanced_diarized_transcript"):
1080
- diarized = session_data[session_id]["advanced_diarized_transcript"]
1081
- elif session_id and session_data.get(session_id, {}).get("diarized_transcript"):
1082
- diarized = session_data[session_id]["diarized_transcript"]
1083
- else:
1084
- logging.warning(f"Download diarized transcript requested but no data found for session {session_id}.")
1085
- return None
1086
- if "Error:" in diarized:
1087
- logging.warning(f"Attempted to download diarized transcript containing an error for session {session_id}.")
1088
- return None
1089
- session_dir = get_session_dir(session_id)
1090
- diarized_filename = os.path.join(session_dir, f"diarized_{uuid.uuid4()}.docx")
1091
- saved_doc_path = save_to_word(diarized, diarized_filename)
1092
- if saved_doc_path:
1093
- logging.info(f"Sending diarized transcript file: {saved_doc_path}")
1094
- original_filename_base = os.path.splitext(session_data[session_id].get("original_filename", "meeting"))[0]
1095
- download_filename = f"{original_filename_base}_diarized.docx"
1096
- return dcc.send_file(saved_doc_path, filename=download_filename)
1097
- else:
1098
- logging.error(f"Failed to create Word document for diarized transcript download for session {session_id}")
1099
- return dcc.send_data_frame(lambda: diarized, "meeting_diarized.txt")
1100
-
1101
  if __name__ == '__main__':
1102
  print("Starting the Dash application...")
1103
- app.run(debug=True, host='0.0.0.0', port=7860)
1104
  print("Dash application has finished running.")
 
54
  server = flask.Flask(__name__)
55
  app = dash.Dash(__name__, server=server, external_stylesheets=[dbc.themes.BOOTSTRAP], suppress_callback_exceptions=True)
56
 
57
+ session_data = defaultdict(lambda: {"audio_path": None, "transcript": None, "minutes": None, "temp_dir": None, "original_filename": None})
 
 
 
 
 
 
 
 
 
 
58
  session_locks = defaultdict(threading.Lock)
59
 
60
  def get_session_dir(session_id):
 
153
  logging.error(f"An unexpected error occurred during transcription: {e}")
154
  return f"Error during transcription: An unexpected error occurred."
155
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  def generate_minutes_ai(transcript, model_name, session_id):
157
  logging.info(f"Generating minutes using {model_name} for session {session_id}")
158
  if not transcript or "Error:" in transcript:
 
275
  dcc.Download(id="download-transcript"),
276
  dcc.Download(id="download-audio"),
277
  dcc.Download(id="download-minutes"),
 
278
  dbc.Row([
279
+ dbc.Col(html.H1("AI Meeting Assistant", className="text-center my-4"), width=12)
280
+ ]),
281
+ dbc.Row([
282
+ dbc.Col(dbc.Card(
283
+ dbc.CardBody([
284
+ html.H4("Controls", className="card-title"),
285
+ html.Div("Upload meeting audio or video file:"),
286
+ dcc.Upload(
287
+ id='audio-uploader',
288
+ children=html.Div([
289
+ 'Drag and Drop or ',
290
+ html.A('Select Audio/Video File')
291
+ ]),
292
+ style={
293
+ 'width': '100%',
294
+ 'height': '60px',
295
+ 'lineHeight': '60px',
296
+ 'borderWidth': '1px',
297
+ 'borderStyle': 'dashed',
298
+ 'borderRadius': '5px',
299
+ 'textAlign': 'center',
300
+ 'margin': '10px 0'
301
+ },
302
+ multiple=False,
303
+ accept='audio/*,video/*'
304
+ ),
305
+ html.Div(id='upload-status', children='Status: Ready to Upload', className="mt-2"),
306
+ dbc.Button("Generate Minutes", id="minutes-btn", color="secondary", className="mt-3 w-100", disabled=True),
307
+ html.H5("Select AI Model", className="mt-4"),
308
+ dcc.Dropdown(
309
+ id='model-selection',
310
+ options=[
311
+ {'label': 'OpenAI GPT-3.5 Turbo', 'value': 'openai', 'disabled': not openai.api_key},
312
+ {'label': 'Google Gemini 1.5 Flash', 'value': 'gemini', 'disabled': not genai},
313
+ {'label': 'Anthropic Claude 3.5 Haiku', 'value': 'anthropic', 'disabled': not anthropic},
314
+ {'label': 'Grok 3 Mini', 'value': 'grok', 'disabled': not grok_api_key}
315
+ ],
316
+ value='openai' if openai.api_key else ('gemini' if genai else ('anthropic' if anthropic else ('grok' if grok_api_key else None))),
317
+ clearable=False,
318
+ className="mt-2",
319
+ disabled=not (openai.api_key or genai or anthropic or grok_api_key)
320
+ ),
321
+ dbc.Button("Delete Session Data", id="delete-btn", color="warning", className="mt-4 w-100", disabled=True),
322
+ ]),
323
+ style={'height': '80vh', 'overflow-y': 'auto'}
324
+ ), width=12, lg=4),
325
+ dbc.Col(dbc.Card(
326
+ dbc.CardBody([
327
+ dcc.Loading(
328
+ id="loading",
329
+ type="default",
330
+ parent_style={'position': 'relative', 'height': '100%'},
331
+ style={'position': 'absolute', 'top': '50%', 'left': '50%', 'transform': 'translate(-50%, -50%)', 'zIndex':'1000'},
332
+ children=[
333
+ html.Div([
334
+ html.H4("Output", className="card-title"),
335
+ html.Div(id="status", children="Status: Idle", className="mb-2"),
336
+ html.H5("Transcript / Minutes"),
337
+ html.Div(id="transcript-preview", style={
338
+ "height": "400px",
339
+ "overflow-y": "scroll",
340
+ "border": "1px solid #ccc",
341
+ "padding": "10px",
342
+ "white-space": "pre-wrap",
343
+ "word-wrap": "break-word",
344
+ "background-color": "#f9f9f9"
345
+ }),
346
+ html.H5("Downloads", className="mt-3"),
347
+ dbc.Row([
348
+ dbc.Col(dbc.Button("Download Transcript (.docx)", id="download-transcript-btn", color="info", className="w-100 mb-2", disabled=True), width=12, md=4),
349
+ dbc.Col(dbc.Button("Download Minutes (.docx)", id="download-minutes-btn", color="info", className="w-100 mb-2", disabled=True), width=12, md=4),
350
+ dbc.Col(dbc.Button("Download Processed Audio", id="download-audio-btn", color="info", className="w-100 mb-2", disabled=True), width=12, md=4),
351
+ ]),
352
+ ])
353
+ ]
354
+ ),
355
+ html.Div(id="loading-output", style={"height": "0px", "visibility": "hidden"}),
356
+ ]),
357
+ style={'height': '80vh', 'overflow-y': 'auto', 'position': 'relative'}
358
+ ), width=12, lg=8),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
  ])
360
  ], fluid=True)
361
 
 
392
  return final_session_id
393
 
394
  @app.callback(
395
+ [Output("status", "children"),
396
+ Output("transcript-preview", "children"),
397
+ Output("minutes-btn", "disabled"),
398
+ Output("download-transcript-btn", "disabled"),
399
+ Output("download-minutes-btn", "disabled"),
400
+ Output("download-audio-btn", "disabled"),
401
+ Output("delete-btn", "disabled"),
402
+ Output("loading-output", "children"),
403
+ Output("upload-status", "children")],
404
+ [Input('audio-uploader', 'contents'),
405
+ Input("minutes-btn", "n_clicks"),
406
+ Input("delete-btn", "n_clicks")],
407
+ [State("session-id", "data"),
408
+ State("model-selection", "value"),
409
+ State("transcript-preview", "children"),
410
+ State('audio-uploader', 'filename')],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
411
  prevent_initial_call=True
412
  )
413
+ def handle_actions(upload_contents, minutes_clicks, delete_clicks, session_id, selected_model, existing_preview, filename):
 
 
 
 
 
414
  if not session_id:
415
  logging.warning("Session ID missing in handle_actions.")
416
+ return "Status: Error - Session ID missing", "", True, True, True, True, True, None, "Status: Error"
417
  ctx = dash.callback_context
418
  triggered_id = ctx.triggered_id if hasattr(ctx, 'triggered_id') else (ctx.triggered[0]['prop_id'].split('.')[0] if ctx.triggered else None)
419
  current_transcript = session_data[session_id].get("transcript", "")
420
  current_minutes = session_data[session_id].get("minutes", "")
421
  current_audio_path = session_data[session_id].get("audio_path", None)
422
  original_filename = session_data[session_id].get("original_filename", None)
423
+ output_text = current_minutes if current_minutes else (current_transcript if current_transcript else "Upload an audio or video file to begin.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
424
  status_msg = "Status: Idle"
425
  if current_minutes and "Error:" not in current_minutes:
426
  status_msg = "Status: Session restored. Minutes loaded."
 
 
427
  elif current_transcript and "Error:" not in current_transcript:
428
  status_msg = "Status: Session restored. Transcript loaded. Ready for Minutes Generation."
429
  elif current_audio_path and os.path.exists(current_audio_path):
 
435
  dl_minutes_disabled = not bool(current_minutes and "Error:" not in current_minutes)
436
  dl_audio_disabled = not bool(current_audio_path and os.path.exists(current_audio_path))
437
  delete_disabled = not bool(session_data.get(session_id, {}).get("temp_dir"))
 
 
 
 
438
  loading_output = None
439
  upload_status_msg = f"Status: {'Loaded: ' + original_filename if original_filename else 'Ready to Upload'}"
440
  start_time = time.time()
 
441
  if triggered_id == 'audio-uploader' and upload_contents is not None and filename is not None:
442
  logging.info(f"File uploaded for session {session_id}, filename: {filename}")
443
  session_data[session_id]["original_filename"] = filename
 
444
  upload_status_msg = f"Status: Processing Uploaded File ({filename})..."
445
  status_msg = "Status: Processing Upload..."
446
  loading_output = "Processing Upload..."
 
455
  session_data[session_id]["transcript"] = None
456
  session_data[session_id]["minutes"] = None
457
  session_data[session_id]["original_filename"] = None
 
 
 
458
  minutes_disabled = True
459
  dl_transcript_disabled = True
460
  dl_minutes_disabled = True
461
  dl_audio_disabled = True
462
  delete_disabled = False
463
+ return status_msg, output_text, minutes_disabled, dl_transcript_disabled, dl_minutes_disabled, dl_audio_disabled, delete_disabled, None, upload_status_msg
 
 
 
464
  safe_upload_filename = f"uploaded_file{f_ext}"
465
  upload_file_path = os.path.join(session_dir, safe_upload_filename)
466
  saved_upload_path = save_base64_data(upload_contents, upload_file_path)
 
492
  dl_minutes_disabled = True
493
  dl_audio_disabled = True
494
  delete_disabled = False
495
+ return status_msg, output_text, minutes_disabled, dl_transcript_disabled, dl_minutes_disabled, dl_audio_disabled, delete_disabled, None, upload_status_msg
 
 
 
 
 
 
496
  else:
497
  audio_path_for_transcription = saved_upload_path
498
  session_data[session_id]["audio_path"] = saved_upload_path
 
505
  transcript_text = transcribe_audio(audio_path_for_transcription)
506
  session_data[session_id]["transcript"] = transcript_text
507
  session_data[session_id]["minutes"] = None
 
 
 
508
  if "Error:" in transcript_text:
509
  status_msg = f"Status: Transcription Failed - {transcript_text}"
510
  output_text = transcript_text
 
513
  dl_minutes_disabled = True
514
  delete_disabled = False
515
  upload_status_msg = f"Status: Transcription Failed. ({filename})"
 
 
 
516
  else:
517
  status_msg = "Status: Transcription Complete. Ready for Minutes Generation."
518
  output_text = transcript_text
 
521
  dl_minutes_disabled = True
522
  delete_disabled = False
523
  upload_status_msg = f"Status: Processed & Transcribed: {filename}"
 
 
 
524
  processing_time = time.time() - start_time
525
  logging.info(f"File processing and transcription took {processing_time:.2f} seconds for session {session_id}")
526
  else:
 
534
  dl_minutes_disabled = True
535
  dl_audio_disabled = True
536
  delete_disabled = False
 
 
 
 
 
 
537
  elif triggered_id == "minutes-btn" and minutes_clicks:
538
  logging.info(f"Generate Minutes button clicked for session {session_id}")
539
+ current_transcript = session_data[session_id].get("transcript", "")
540
+ if current_transcript and "Error:" not in current_transcript:
 
 
 
 
 
 
541
  status_msg = f"Status: Generating Minutes ({selected_model})..."
542
  loading_output = "Generating Minutes..."
543
+ minutes_text = generate_minutes_ai(current_transcript, selected_model, session_id)
544
  session_data[session_id]["minutes"] = minutes_text
545
  output_text = minutes_text
 
546
  if "Error:" in minutes_text:
547
  status_msg = f"Status: Minutes Generation Failed - {minutes_text}"
548
  dl_minutes_disabled = True
 
556
  dl_audio_disabled = not bool(session_data.get(session_id, {}).get("audio_path") and os.path.exists(session_data.get(session_id, {}).get("audio_path", "")))
557
  delete_disabled = False
558
  upload_status_msg = f"Status: Processed & Transcribed: {session_data[session_id].get('original_filename', 'File')}"
 
 
 
559
  else:
560
  status_msg = "Status: Cannot generate minutes - No valid transcript available."
561
  output_text = existing_preview
562
  minutes_disabled = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
563
  elif triggered_id == "delete-btn" and delete_clicks:
564
  logging.info(f"Delete button clicked for session {session_id}")
565
  cleanup_session(session_id)
 
570
  dl_minutes_disabled = True
571
  dl_audio_disabled = True
572
  delete_disabled = True
 
 
 
573
  upload_status_msg = "Status: Ready to Upload"
 
574
  else:
575
+ loaded_audio_path = session_data.get(session_id, {}).get("audio_path")
576
+ loaded_transcript = session_data.get(session_id, {}).get("transcript")
577
+ loaded_minutes = session_data.get(session_id, {}).get("minutes")
578
+ temp_dir_exists = bool(session_data.get(session_id, {}).get("temp_dir"))
579
+ loaded_original_filename = session_data.get(session_id, {}).get("original_filename")
580
+ dl_audio_disabled = not (loaded_audio_path and os.path.exists(loaded_audio_path))
581
+ minutes_disabled = not (loaded_transcript and "Error:" not in loaded_transcript)
582
+ dl_transcript_disabled = not (loaded_transcript and "Error:" not in loaded_transcript)
583
+ dl_minutes_disabled = not (loaded_minutes and "Error:" not in loaded_minutes)
584
+ delete_disabled = not (loaded_audio_path or loaded_transcript or loaded_minutes or temp_dir_exists or loaded_original_filename)
585
+ if loaded_original_filename and dl_audio_disabled and not loaded_transcript:
586
+ upload_status_msg = f"Status: Error processing {loaded_original_filename}?"
587
+ elif loaded_audio_path and os.path.exists(loaded_audio_path):
588
+ upload_status_msg = f"Status: Processed audio loaded ({loaded_original_filename or 'previous file'})."
589
+ else:
590
+ upload_status_msg = "Status: Ready to Upload"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
591
  return (
592
  status_msg,
593
  output_text,
 
597
  dl_audio_disabled,
598
  delete_disabled,
599
  loading_output,
600
+ upload_status_msg
 
 
 
 
601
  )
602
 
603
  @app.callback(
 
674
  logging.error(f"Processed audio file not found at path {audio_path} for session {session_id}")
675
  return None
676
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
677
  if __name__ == '__main__':
678
  print("Starting the Dash application...")
679
+ app.run(debug=False, host='0.0.0.0', port=7860)
680
  print("Dash application has finished running.")