diff --git a/application/single_app/config.py b/application/single_app/config.py index 2303a89e9..9cbad1371 100644 --- a/application/single_app/config.py +++ b/application/single_app/config.py @@ -122,11 +122,49 @@ CLIENTS = {} CLIENTS_LOCK = threading.Lock() -ALLOWED_EXTENSIONS = { - 'txt', 'pdf', 'doc', 'docm', 'docx', 'xlsx', 'xlsm', 'xls', 'csv', 'pptx', 'html', 'jpg', 'jpeg', 'png', 'bmp', 'tiff', 'tif', 'heif', 'md', 'json', - 'mp4', 'mov', 'avi', 'mkv', 'flv', 'mxf', 'gxf', 'ts', 'ps', '3gp', '3gpp', 'mpg', 'wmv', 'asf', 'm4a', 'm4v', 'isma', 'ismv', - 'dvr-ms', 'wav', 'xml', 'yaml', 'yml', 'log' +# Base allowed extensions (always available) +BASE_ALLOWED_EXTENSIONS = {'txt', 'doc', 'docm', 'html', 'md', 'json', 'xml', 'yaml', 'yml', 'log'} +DOCUMENT_EXTENSIONS = {'pdf', 'docx', 'pptx', 'ppt'} +TABULAR_EXTENSIONS = {'csv', 'xlsx', 'xls', 'xlsm'} + +# Updates to image, video, or audio extensions should also be made in static/js/chat/chat-enhanced-citations.js if the new file types can be natively rendered in the browser. +IMAGE_EXTENSIONS = {'jpg', 'jpeg', 'png', 'bmp', 'tiff', 'tif', 'heif', 'heic'} + +# Optional extensions by feature +VIDEO_EXTENSIONS = { + 'mp4', 'mov', 'avi', 'mkv', 'flv', 'mxf', 'gxf', 'ts', 'ps', '3gp', '3gpp', + 'mpg', 'wmv', 'asf', 'm4v', 'isma', 'ismv', 'dvr-ms', 'webm', 'mpeg' } + +AUDIO_EXTENSIONS = {'mp3', 'wav', 'ogg', 'aac', 'flac', 'm4a'} + +def get_allowed_extensions(enable_video=False, enable_audio=False): + """ + Get allowed file extensions based on feature flags. + + Args: + enable_video: Whether video file support is enabled + enable_audio: Whether audio file support is enabled + + Returns: + set: Allowed file extensions + """ + extensions = BASE_ALLOWED_EXTENSIONS.copy() + extensions.update(DOCUMENT_EXTENSIONS) + extensions.update(IMAGE_EXTENSIONS) + extensions.update(TABULAR_EXTENSIONS) + + if enable_video: + extensions.update(VIDEO_EXTENSIONS) + + if enable_audio: + extensions.update(AUDIO_EXTENSIONS) + + return extensions + +ALLOWED_EXTENSIONS = get_allowed_extensions(enable_video=True, enable_audio=True) + +# Admin UI specific extensions (for logo/favicon uploads) ALLOWED_EXTENSIONS_IMG = {'png', 'jpg', 'jpeg'} MAX_CONTENT_LENGTH = 5000 * 1024 * 1024 # 5000 MB AKA 5 GB diff --git a/application/single_app/functions_documents.py b/application/single_app/functions_documents.py index 017b819fa..9ae01a622 100644 --- a/application/single_app/functions_documents.py +++ b/application/single_app/functions_documents.py @@ -4851,7 +4851,7 @@ def process_di_document(document_id, user_id, temp_file_path, original_filename, is_pdf = file_ext == '.pdf' is_word = file_ext in ('.docx', '.doc') is_ppt = file_ext in ('.pptx', '.ppt') - is_image = file_ext in ('.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif', '.heif') + is_image = file_ext in tuple('.' + ext for ext in IMAGE_EXTENSIONS) try: if is_pdf: @@ -5233,23 +5233,138 @@ def process_audio_document( print(f"[Debug] Transcribing chunk {idx}: {chunk_path}") # Get fresh config (tokens expire after ~1 hour) - speech_config = _get_speech_config(settings, endpoint, locale) + try: + speech_config = _get_speech_config(settings, endpoint, locale) + except Exception as e: + print(f"[Error] Failed to get speech config for chunk {idx}: {e}") + raise RuntimeError(f"Speech configuration failed for chunk {idx}: {e}") - audio_config = speechsdk.AudioConfig(filename=chunk_path) - speech_recognizer = speechsdk.SpeechRecognizer( - speech_config=speech_config, - audio_config=audio_config - ) + try: + audio_config = speechsdk.AudioConfig(filename=chunk_path) + except Exception as e: + print(f"[Error] Failed to load audio file {chunk_path}: {e}") + raise RuntimeError(f"Audio file loading failed: {e}") - result = speech_recognizer.recognize_once() - if result.reason == speechsdk.ResultReason.RecognizedSpeech: - print(f"[Debug] Recognized: {result.text}") - all_phrases.append(result.text) - elif result.reason == speechsdk.ResultReason.NoMatch: - print(f"[Warning] No speech in {chunk_path}") - elif result.reason == speechsdk.ResultReason.Canceled: - print(f"[Error] {result.cancellation_details.reason}: {result.cancellation_details.error_details}") - raise RuntimeError(f"Transcription canceled for {chunk_path}: {result.cancellation_details.error_details}") + try: + speech_recognizer = speechsdk.SpeechRecognizer( + speech_config=speech_config, + audio_config=audio_config + ) + except Exception as e: + print(f"[Error] Failed to create speech recognizer for chunk {idx}: {e}") + raise RuntimeError(f"Speech recognizer creation failed: {e}") + + # Use continuous recognition instead of recognize_once + all_results = [] + done = False + error_occurred = False + error_message = None + + def stop_cb(evt): + nonlocal done + print(f"[Debug] Session stopped for chunk {idx}") + done = True + + def recognized_cb(evt): + try: + if evt.result.reason == speechsdk.ResultReason.RecognizedSpeech: + all_results.append(evt.result.text) + print(f"[Debug] Recognized: {evt.result.text}") + elif evt.result.reason == speechsdk.ResultReason.NoMatch: + print(f"[Debug] No speech recognized in segment") + except Exception as e: + print(f"[Error] Error in recognized callback: {e}") + # Don't fail on individual recognition errors + + def canceled_cb(evt): + nonlocal done, error_occurred, error_message + print(f"[Debug] Recognition canceled for chunk {idx}: {evt.cancellation_details.reason}") + + if evt.cancellation_details.reason == speechsdk.CancellationReason.Error: + error_occurred = True + error_message = evt.cancellation_details.error_details + print(f"[Error] Recognition error: {error_message}") + elif evt.cancellation_details.reason == speechsdk.CancellationReason.EndOfStream: + print(f"[Debug] End of audio stream reached") + + done = True + + try: + # Connect callbacks + speech_recognizer.recognized.connect(recognized_cb) + speech_recognizer.session_stopped.connect(stop_cb) + speech_recognizer.canceled.connect(canceled_cb) + + # Start continuous recognition + print(f"[Debug] Starting continuous recognition for chunk {idx}") + speech_recognizer.start_continuous_recognition() + + # Wait for completion with timeout + import time + timeout_seconds = 600 # 10 minutes max per chunk + start_time = time.time() + + while not done: + if time.time() - start_time > timeout_seconds: + print(f"[Error] Recognition timeout for chunk {idx}") + error_occurred = True + error_message = f"Recognition timed out after {timeout_seconds} seconds" + break + time.sleep(0.5) + + # Stop recognition + try: + speech_recognizer.stop_continuous_recognition() + print(f"[Debug] Stopped continuous recognition for chunk {idx}") + except Exception as e: + print(f"[Warning] Error stopping recognition for chunk {idx}: {e}") + # Continue even if stop fails + + # Check for errors after completion + if error_occurred: + raise RuntimeError(f"Recognition failed for chunk {idx}: {error_message}") + + # Add all recognized phrases to the overall list + if all_results: + all_phrases.extend(all_results) + print(f"[Debug] Total phrases from chunk {idx}: {len(all_results)}") + else: + print(f"[Warning] No speech recognized in {chunk_path}") + # Continue to next chunk - empty result is not necessarily an error + + except RuntimeError as e: + # Re-raise runtime errors (these are our custom errors) + raise + except Exception as e: + print(f"[Error] Unexpected error during recognition for chunk {idx}: {e}") + raise RuntimeError(f"Recognition failed unexpectedly for chunk {idx}: {e}") + finally: + # Cleanup: disconnect callbacks and dispose recognizer + try: + speech_recognizer.recognized.disconnect_all() + speech_recognizer.session_stopped.disconnect_all() + speech_recognizer.canceled.disconnect_all() + except Exception as e: + print(f"[Warning] Error disconnecting callbacks for chunk {idx}: {e}") + + # # Get fresh config (tokens expire after ~1 hour) + # speech_config = _get_speech_config(settings, endpoint, locale) + + # audio_config = speechsdk.AudioConfig(filename=chunk_path) + # speech_recognizer = speechsdk.SpeechRecognizer( + # speech_config=speech_config, + # audio_config=audio_config + # ) + + # result = speech_recognizer.recognize_once() + # if result.reason == speechsdk.ResultReason.RecognizedSpeech: + # print(f"[Debug] Recognized: {result.text}") + # all_phrases.append(result.text) + # elif result.reason == speechsdk.ResultReason.NoMatch: + # print(f"[Warning] No speech in {chunk_path}") + # elif result.reason == speechsdk.ResultReason.Canceled: + # print(f"[Error] {result.cancellation_details.reason}: {result.cancellation_details.error_details}") + # raise RuntimeError(f"Transcription canceled for {chunk_path}: {result.cancellation_details.error_details}") else: # Use the fast-transcription API if not in sovereign or custom cloud @@ -5357,8 +5472,12 @@ def process_document_upload_background(document_id, user_id, temp_file_path, ori enable_extract_meta_data = settings.get('enable_extract_meta_data', False) # Used by DI flow max_file_size_bytes = settings.get('max_file_size_mb', 16) * 1024 * 1024 - video_extensions = ('.mp4', '.mov', '.avi', '.mkv', '.flv') - audio_extensions = ('.mp3', '.wav', '.ogg', '.aac', '.flac', '.m4a') + # Get allowed extensions from config.py to determine which processing function to call + tabular_extensions = tuple('.' + ext for ext in TABULAR_EXTENSIONS) + image_extensions = tuple('.' + ext for ext in IMAGE_EXTENSIONS) + di_supported_extensions = tuple('.' + ext for ext in DOCUMENT_EXTENSIONS | IMAGE_EXTENSIONS) + video_extensions = tuple('.' + ext for ext in VIDEO_EXTENSIONS) + audio_extensions = tuple('.' + ext for ext in AUDIO_EXTENSIONS) # --- Define update_document callback wrapper --- # This makes it easier to pass the update function to helpers without repeating args @@ -5402,8 +5521,6 @@ def update_doc_callback(**kwargs): # --- 1. Dispatch to appropriate handler based on file type --- # Note: .doc and .docm are handled separately by process_doc() using docx2txt - di_supported_extensions = ('.pdf', '.docx', '.pptx', '.ppt', '.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif', '.heif') - tabular_extensions = ('.csv', '.xlsx', '.xls', '.xlsm') is_group = group_id is not None @@ -5512,7 +5629,7 @@ def update_doc_callback(**kwargs): final_status = "Processing complete" if total_chunks_saved == 0: # Provide more specific status if no chunks were saved - if file_ext in ('.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif', '.heif'): + if file_ext in image_extensions: final_status = "Processing complete - no text found in image" elif file_ext in tabular_extensions: final_status = "Processing complete - no data rows found or file empty" diff --git a/application/single_app/route_backend_documents.py b/application/single_app/route_backend_documents.py index 072577d6c..31619f693 100644 --- a/application/single_app/route_backend_documents.py +++ b/application/single_app/route_backend_documents.py @@ -535,9 +535,23 @@ def api_patch_user_document(document_id): try: # Log the metadata update transaction if any fields were updated if updated_fields: - # Get document details for logging - doc = get_document(user_id, document_id) - if doc: + # Get document details for logging - handle tuple return + doc_response = get_document(user_id, document_id) + doc = None + + # Handle tuple return (response, status_code) + if isinstance(doc_response, tuple): + resp, status_code = doc_response + if hasattr(resp, "get_json"): + doc = resp.get_json() + else: + doc = resp + elif hasattr(doc_response, "get_json"): + doc = doc_response.get_json() + else: + doc = doc_response + + if doc and isinstance(doc, dict): log_document_metadata_update_transaction( user_id=user_id, document_id=document_id, diff --git a/application/single_app/route_backend_group_documents.py b/application/single_app/route_backend_group_documents.py index 194b5a6b9..68a1c0fa5 100644 --- a/application/single_app/route_backend_group_documents.py +++ b/application/single_app/route_backend_group_documents.py @@ -416,24 +416,43 @@ def api_patch_group_document(document_id): ) updated_fields['authors'] = authors_list - # Log the metadata update transaction if any fields were updated - if updated_fields: + # Save updates back to Cosmos + try: + # Log the metadata update transaction if any fields were updated + if updated_fields: + # Get document details for logging - handle tuple return # Get document details for logging - from functions_documents import get_document - doc = get_document(user_id, document_id, group_id=active_group_id) - if doc: - from functions_activity_logging import log_document_metadata_update_transaction - log_document_metadata_update_transaction( - user_id=user_id, - document_id=document_id, - workspace_type='group', - file_name=doc.get('file_name', 'Unknown'), - updated_fields=updated_fields, - file_type=doc.get('file_type'), - group_id=active_group_id - ) - - return jsonify({'message': 'Group document metadata updated successfully'}), 200 + from functions_documents import get_document + doc_response = get_document(user_id, document_id, group_id=active_group_id) + doc = None + + # Handle tuple return (response, status_code) + if isinstance(doc_response, tuple): + resp, status_code = doc_response + if hasattr(resp, "get_json"): + doc = resp.get_json() + else: + doc = resp + elif hasattr(doc_response, "get_json"): + doc = doc_response.get_json() + else: + doc = doc_response + + if doc and isinstance(doc, dict): + from functions_activity_logging import log_document_metadata_update_transaction + log_document_metadata_update_transaction( + user_id=user_id, + document_id=document_id, + workspace_type='group', + file_name=doc.get('file_name', 'Unknown'), + updated_fields=updated_fields, + file_type=doc.get('file_type'), + group_id=active_group_id + ) + + return jsonify({'message': 'Group document metadata updated successfully'}), 200 + except Exception as e: + return jsonify({'Error updating Group document metadata': str(e)}), 500 except Exception as e: return jsonify({'error': str(e)}), 500 diff --git a/application/single_app/route_backend_public_documents.py b/application/single_app/route_backend_public_documents.py index 9e228acd9..a209e9a20 100644 --- a/application/single_app/route_backend_public_documents.py +++ b/application/single_app/route_backend_public_documents.py @@ -299,25 +299,45 @@ def api_patch_public_document(doc_id): update_document(document_id=doc_id, public_workspace_id=active_ws, user_id=user_id, document_classification=data['document_classification']) updated_fields['document_classification'] = data['document_classification'] - # Log the metadata update transaction if any fields were updated - if updated_fields: - from functions_documents import get_document - from functions_activity_logging import log_document_metadata_update_transaction - doc = get_document(user_id, doc_id, public_workspace_id=active_ws) - if doc: - log_document_metadata_update_transaction( - user_id=user_id, - document_id=doc_id, - workspace_type='public', - file_name=doc.get('file_name', 'Unknown'), - updated_fields=updated_fields, - file_type=doc.get('file_type'), - public_workspace_id=active_ws - ) - - return jsonify({'message':'Metadata updated'}), 200 + # Save updates back to Cosmos + try: + # Log the metadata update transaction if any fields were updated + if updated_fields: + # Get document details for logging - handle tuple return + # Get document details for logging + from functions_documents import get_document + doc_response = get_document(user_id, doc_id, public_workspace_id=active_ws) + doc = None + + # Handle tuple return (response, status_code) + if isinstance(doc_response, tuple): + resp, status_code = doc_response + if hasattr(resp, "get_json"): + doc = resp.get_json() + else: + doc = resp + elif hasattr(doc_response, "get_json"): + doc = doc_response.get_json() + else: + doc = doc_response + + if doc and isinstance(doc, dict): + from functions_activity_logging import log_document_metadata_update_transaction + log_document_metadata_update_transaction( + user_id=user_id, + document_id=doc_id, + workspace_type='public', + file_name=doc.get('file_name', 'Unknown'), + updated_fields=updated_fields, + file_type=doc.get('file_type'), + public_workspace_id=active_ws + ) + + return jsonify({'message': 'Public document metadata updated successfully'}), 200 + except Exception as e: + return jsonify({'Error updating Public document metadata': str(e)}), 500 except Exception as e: - return jsonify({'error':str(e)}), 500 + return jsonify({'error': str(e)}), 500 @app.route('/api/public_documents/', methods=['DELETE']) @swagger_route(security=get_auth_security()) diff --git a/application/single_app/route_enhanced_citations.py b/application/single_app/route_enhanced_citations.py index 684559db7..c81ef2252 100644 --- a/application/single_app/route_enhanced_citations.py +++ b/application/single_app/route_enhanced_citations.py @@ -15,7 +15,7 @@ from functions_group import get_user_groups from functions_public_workspaces import get_user_visible_public_workspace_ids_from_settings from swagger_wrapper import swagger_route, get_auth_security -from config import CLIENTS, storage_account_user_documents_container_name, storage_account_group_documents_container_name, storage_account_public_documents_container_name +from config import CLIENTS, storage_account_user_documents_container_name, storage_account_group_documents_container_name, storage_account_public_documents_container_name, IMAGE_EXTENSIONS, VIDEO_EXTENSIONS, AUDIO_EXTENSIONS from functions_debug import debug_print def register_enhanced_citations_routes(app): @@ -49,9 +49,8 @@ def get_enhanced_citation_image(): # Check if it's an image file file_name = raw_doc['file_name'] ext = file_name.lower().split('.')[-1] if '.' in file_name else '' - image_extensions = ['jpg', 'jpeg', 'png', 'bmp', 'tiff', 'tif', 'heif'] - if ext not in image_extensions: + if ext not in IMAGE_EXTENSIONS: return jsonify({"error": "File is not an image"}), 400 # Serve the image content directly @@ -88,9 +87,8 @@ def get_enhanced_citation_video(): # Check if it's a video file file_name = raw_doc['file_name'] ext = file_name.lower().split('.')[-1] if '.' in file_name else '' - video_extensions = ['mp4', 'mov', 'avi', 'mkv', 'flv', 'webm', 'wmv'] - if ext not in video_extensions: + if ext not in VIDEO_EXTENSIONS: return jsonify({"error": "File is not a video"}), 400 # Serve the video content directly @@ -127,9 +125,8 @@ def get_enhanced_citation_audio(): # Check if it's an audio file file_name = raw_doc['file_name'] ext = file_name.lower().split('.')[-1] if '.' in file_name else '' - audio_extensions = ['mp3', 'wav', 'ogg', 'aac', 'flac', 'm4a'] - if ext not in audio_extensions: + if ext not in AUDIO_EXTENSIONS: return jsonify({"error": "File is not an audio file"}), 400 # Serve the audio content directly diff --git a/application/single_app/route_frontend_chats.py b/application/single_app/route_frontend_chats.py index af3ce9b19..8e34c0f43 100644 --- a/application/single_app/route_frontend_chats.py +++ b/application/single_app/route_frontend_chats.py @@ -131,9 +131,9 @@ def upload_file(): try: # Check if this is an image file - is_image_file = file_ext in ['.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif', '.heif'] + is_image_file = file_ext in IMAGE_EXTENSIONS - if file_ext in ['.pdf', '.docx', '.pptx', '.html', '.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif', '.heif']: + if file_ext in ['.pdf', '.docx', '.pptx', '.ppt', '.html'] or is_image_file: extracted_content_raw = extract_content_with_azure_di(temp_file_path) # Convert pages_data list to string @@ -209,7 +209,7 @@ def upload_file(): elif file_ext in ['.xml', '.yaml', '.yml', '.log']: # Handle XML, YAML, and LOG files as text for inline chat extracted_content = extract_text_file(temp_file_path) - elif file_ext in ['.csv', '.xls', '.xlsx', '.xlsm']: + elif file_ext in TABULAR_EXTENSIONS: extracted_content = extract_table_file(temp_file_path, file_ext) is_table = True else: @@ -685,8 +685,8 @@ def view_document(): is_pdf = file_ext == '.pdf' is_word = file_ext in ('.docx', '.doc', '.docm') is_ppt = file_ext in ('.pptx', '.ppt') - is_image = file_ext in ('.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif', '.gif', '.webp') # Added more image types - is_text = file_ext in ('.txt', '.md', '.csv', '.json', '.log', '.xml', '.yaml', '.yml', '.html', '.htm') # Common text-based types + is_image = file_ext.lstrip('.') in (IMAGE_EXTENSIONS | {'gif', 'webp'}) # Added more image types + is_text = file_ext.lstrip('.') in (BASE_ALLOWED_EXTENSIONS - {'doc', 'docm'}) # Common text-based types try: # Download the file to the specified location diff --git a/application/single_app/route_frontend_group_workspaces.py b/application/single_app/route_frontend_group_workspaces.py index 75996d840..850cc8d04 100644 --- a/application/single_app/route_frontend_group_workspaces.py +++ b/application/single_app/route_frontend_group_workspaces.py @@ -45,30 +45,13 @@ def group_workspaces(): ) legacy_count = legacy_docs_from_cosmos[0] if legacy_docs_from_cosmos else 0 - # Build allowed extensions string - allowed_extensions = [ - "txt", "pdf", "doc", "docm", "docx", "xlsx", "xls", "xlsm","csv", "pptx", "html", - "jpg", "jpeg", "png", "bmp", "tiff", "tif", "heif", "md", "json", - "xml", "yaml", "yml", "log" - ] - if enable_video_file_support in [True, 'True', 'true']: - allowed_extensions += ["mp4", "mov", "avi", "wmv", "mkv", "webm"] - if enable_audio_file_support in [True, 'True', 'true']: - allowed_extensions += ["mp3", "wav", "ogg", "aac", "flac", "m4a"] - allowed_extensions_str = "Allowed: " + ", ".join(allowed_extensions) - - # Build allowed extensions string - allowed_extensions = [ - "txt", "pdf", "doc", "docm", "docx", "xlsx", "xls", "xlsm","csv", "pptx", "html", - "jpg", "jpeg", "png", "bmp", "tiff", "tif", "heif", "md", "json", - "xml", "yaml", "yml", "log" - ] - if enable_video_file_support in [True, 'True', 'true']: - allowed_extensions += ["mp4", "mov", "avi", "wmv", "mkv", "webm"] - if enable_audio_file_support in [True, 'True', 'true']: - allowed_extensions += ["mp3", "wav", "ogg", "aac", "flac", "m4a"] + # Get allowed extensions from central function and build allowed extensions string + allowed_extensions = sorted(get_allowed_extensions( + enable_video=enable_video_file_support in [True, 'True', 'true'], + enable_audio=enable_audio_file_support in [True, 'True', 'true'] + )) allowed_extensions_str = "Allowed: " + ", ".join(allowed_extensions) - + return render_template( 'group_workspaces.html', settings=public_settings, diff --git a/application/single_app/route_frontend_public_workspaces.py b/application/single_app/route_frontend_public_workspaces.py index 102354449..05d5b982a 100644 --- a/application/single_app/route_frontend_public_workspaces.py +++ b/application/single_app/route_frontend_public_workspaces.py @@ -69,18 +69,13 @@ def public_workspaces(): enable_video_file_support = settings.get('enable_video_file_support', False) enable_audio_file_support = settings.get('enable_audio_file_support', False) - # Build allowed extensions string as in workspace.html - allowed_extensions = [ - "txt", "pdf", "doc", "docm", "docx", "xlsx", "xls", "xlsm","csv", "pptx", "html", - "jpg", "jpeg", "png", "bmp", "tiff", "tif", "heif", "md", "json", - "xml", "yaml", "yml", "log" - ] - if enable_video_file_support in [True, 'True', 'true']: - allowed_extensions += ["mp4", "mov", "avi", "wmv", "mkv", "webm"] - if enable_audio_file_support in [True, 'True', 'true']: - allowed_extensions += ["mp3", "wav", "ogg", "aac", "flac", "m4a"] + # Get allowed extensions from central function and build allowed extensions string + allowed_extensions = sorted(get_allowed_extensions( + enable_video=enable_video_file_support in [True, 'True', 'true'], + enable_audio=enable_audio_file_support in [True, 'True', 'true'] + )) allowed_extensions_str = "Allowed: " + ", ".join(allowed_extensions) - + return render_template( 'public_workspaces.html', settings=public_settings, diff --git a/application/single_app/route_frontend_workspace.py b/application/single_app/route_frontend_workspace.py index 47f121e08..2ca1aad98 100644 --- a/application/single_app/route_frontend_workspace.py +++ b/application/single_app/route_frontend_workspace.py @@ -43,18 +43,13 @@ def workspace(): ) legacy_count = legacy_docs_from_cosmos[0] if legacy_docs_from_cosmos else 0 - # Build allowed extensions string - allowed_extensions = [ - "txt", "pdf", "doc", "docm", "docx", "xlsx", "xls", "xlsm","csv", "pptx", "html", - "jpg", "jpeg", "png", "bmp", "tiff", "tif", "heif", "md", "json", - "xml", "yaml", "yml", "log" - ] - if enable_video_file_support in [True, 'True', 'true']: - allowed_extensions += ["mp4", "mov", "avi", "wmv", "mkv", "webm"] - if enable_audio_file_support in [True, 'True', 'true']: - allowed_extensions += ["mp3", "wav", "ogg", "aac", "flac", "m4a"] + # Get allowed extensions from central function and build allowed extensions string + allowed_extensions = sorted(get_allowed_extensions( + enable_video=enable_video_file_support in [True, 'True', 'true'], + enable_audio=enable_audio_file_support in [True, 'True', 'true'] + )) allowed_extensions_str = "Allowed: " + ", ".join(allowed_extensions) - + return render_template( 'workspace.html', settings=public_settings, diff --git a/application/single_app/static/js/chat/chat-enhanced-citations.js b/application/single_app/static/js/chat/chat-enhanced-citations.js index 18c75229c..dcda708be 100644 --- a/application/single_app/static/js/chat/chat-enhanced-citations.js +++ b/application/single_app/static/js/chat/chat-enhanced-citations.js @@ -15,8 +15,8 @@ export function getFileType(fileName) { const ext = fileName.toLowerCase().split('.').pop(); - const imageExtensions = ['jpg', 'jpeg', 'png', 'bmp', 'tiff', 'tif', 'heif']; - const videoExtensions = ['mp4', 'mov', 'avi', 'mkv', 'flv', 'webm', 'wmv']; + const imageExtensions = ['jpg', 'jpeg', 'png', 'bmp', 'tiff', 'tif']; + const videoExtensions = ['mp4', 'mov', 'avi', 'mkv', 'flv', 'webm', 'wmv', 'm4v', '3gp']; const audioExtensions = ['mp3', 'wav', 'ogg', 'aac', 'flac', 'm4a']; if (imageExtensions.includes(ext)) return 'image';