aseelflihan commited on
Commit
33d3592
·
0 Parent(s):

Initial commit without node_modules

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +11 -0
  2. .gitignore +47 -0
  3. .kiro/specs/ai-questions/design.md +262 -0
  4. .kiro/specs/ai-questions/requirements.md +91 -0
  5. .kiro/specs/ai-questions/tasks.md +209 -0
  6. .kiro/specs/broadcast-export/design.md +274 -0
  7. .kiro/specs/broadcast-export/requirements.md +87 -0
  8. .kiro/specs/broadcast-export/tasks.md +194 -0
  9. .kiro/specs/model-management/design.md +225 -0
  10. .kiro/specs/model-management/requirements.md +62 -0
  11. .kiro/specs/model-management/tasks.md +103 -0
  12. Dockerfile +41 -0
  13. FIX_GOOGLE_ACCESS.md +27 -0
  14. GOOGLE_SETUP.md +47 -0
  15. GOOGLE_SETUP_EASY.md +82 -0
  16. GOOGLE_SETUP_SIMPLE.md +85 -0
  17. INTEGRATION_SOLUTION.md +75 -0
  18. PERFORMANCE_IMPROVEMENTS.md +76 -0
  19. QUICK_START.md +202 -0
  20. README.md +221 -0
  21. README_AR.md +134 -0
  22. SOLUTION_SUMMARY.md +69 -0
  23. SUMMARY_FIX_REPORT.md +169 -0
  24. TECHNICAL_IMPLEMENTATION.md +299 -0
  25. TROUBLESHOOTING.md +251 -0
  26. ai_questions.py +773 -0
  27. app.py +1812 -0
  28. app_config.py +59 -0
  29. app_launcher.py +43 -0
  30. audio_processor.py +391 -0
  31. check_credentials.py +118 -0
  32. comprehensive_test.py +225 -0
  33. credentials.json +16 -0
  34. custom_components/st-audiorec/.streamlit/config.toml +3 -0
  35. custom_components/st-audiorec/LICENCE +3 -0
  36. custom_components/st-audiorec/README.md +3 -0
  37. custom_components/st-audiorec/demo.py +3 -0
  38. custom_components/st-audiorec/setup.py +3 -0
  39. custom_components/st-audiorec/st_audiorec/__init__.py +3 -0
  40. custom_components/st-audiorec/st_audiorec/frontend/.prettierrc +3 -0
  41. custom_components/st-audiorec/st_audiorec/frontend/build/asset-manifest.json +3 -0
  42. custom_components/st-audiorec/st_audiorec/frontend/build/bootstrap.min.css +3 -0
  43. custom_components/st-audiorec/st_audiorec/frontend/build/index.html +3 -0
  44. custom_components/st-audiorec/st_audiorec/frontend/build/precache-manifest.30096e2fd9f149157a833e729e772f72.js +3 -0
  45. custom_components/st-audiorec/st_audiorec/frontend/build/service-worker.js +3 -0
  46. custom_components/st-audiorec/st_audiorec/frontend/build/static/js/2.ca2bba73.chunk.js +3 -0
  47. custom_components/st-audiorec/st_audiorec/frontend/build/static/js/2.ca2bba73.chunk.js.LICENSE.txt +3 -0
  48. custom_components/st-audiorec/st_audiorec/frontend/build/static/js/2.ca2bba73.chunk.js.map +3 -0
  49. custom_components/st-audiorec/st_audiorec/frontend/build/static/js/main.85742990.chunk.js +3 -0
  50. custom_components/st-audiorec/st_audiorec/frontend/build/static/js/main.85742990.chunk.js.map +3 -0
.gitattributes ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ custom_components/st-audiorec/** filter=lfs diff=lfs merge=lfs -text
2
+ *.wav filter=lfs diff=lfs merge=lfs -text
3
+ *.mp3 filter=lfs diff=lfs merge=lfs -text
4
+ *.ogg filter=lfs diff=lfs merge=lfs -text
5
+ *.pt filter=lfs diff=lfs merge=lfs -text
6
+ *.pth filter=lfs diff=lfs merge=lfs -text
7
+ *.onnx filter=lfs diff=lfs merge=lfs -text
8
+ *.bin filter=lfs diff=lfs merge=lfs -text
9
+ *.tar.gz filter=lfs diff=lfs merge=lfs -text
10
+ *.zip filter=lfs diff=lfs merge=lfs -text
11
+ custom_components/** filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.pyo
5
+ *.pyd
6
+ *.pyc
7
+ *.log
8
+ *.sqlite3
9
+ *.db
10
+ venv/
11
+ .venv/
12
+ env/
13
+ ENV/
14
+ env.bak/
15
+ pip-wheel-metadata/
16
+ dist/
17
+ *.egg-info/
18
+
19
+ # Editor / OS
20
+ .vscode/
21
+ .idea/
22
+ *.swp
23
+ .DS_Store
24
+ Thumbs.db
25
+
26
+ # Node / frontend
27
+ node_modules/
28
+ npm-debug.log*
29
+ yarn-error.log*
30
+ package-lock.json
31
+ .pnpm-debug.log
32
+
33
+ # Specific frontend inside your component
34
+ custom_components/st-audiorec/st_audiorec/frontend/node_modules/
35
+
36
+ # Virtual env / credentials
37
+ *.env
38
+ .env.*
39
+
40
+ # Hugging Face / caches
41
+ .cache/
42
+ .hf/
43
+
44
+ # IDE metadata
45
+ *.sublime-workspace
46
+ *.sublime-project
47
+ custom_components/st-audiorec/st_audiorec/frontend/node_modules
.kiro/specs/ai-questions/design.md ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Design Document
2
+
3
+ ## Overview
4
+
5
+ The AI Questions feature integrates with the existing SyncMaster broadcast system to provide interactive AI-powered questioning capabilities. Students can select any text segment from broadcast content and engage in natural language conversations with AI to better understand the material. The system leverages the existing Gemini AI infrastructure and maintains the current multilingual support.
6
+
7
+ ## Architecture
8
+
9
+ ### High-Level Architecture
10
+
11
+ ```
12
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
13
+ │ Broadcast UI │ │ Question Engine│ │ AI Service │
14
+ │ │ │ │ │ │
15
+ │ - Text Selection│───▶│ - Context Prep │───▶│ - Gemini AI │
16
+ │ - Ask AI Button │ │ - Question Proc │ │ - Translation │
17
+ │ - Response Area │◀───│ - Response Format│◀───│ - Conversation │
18
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
19
+ │ │ │
20
+ │ │ │
21
+ ▼ ▼ ▼
22
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
23
+ │ Session State │ │ Question History│ │ Context Manager │
24
+ │ │ │ │ │ │
25
+ │ - Selected Text │ │ - Q&A Pairs │ │ - Text Context │
26
+ │ - Active Conv │ │ - Timestamps │ │ - Conversation │
27
+ │ - UI Language │ │ - User Prefs │ │ - Memory Mgmt │
28
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
29
+ ```
30
+
31
+ ### Component Integration
32
+
33
+ The AI Questions feature integrates seamlessly with existing components:
34
+ - **Broadcast System**: Uses existing `broadcast_segments` for text selection
35
+ - **Translation System**: Leverages current `translator.py` for AI responses
36
+ - **UI System**: Extends Streamlit interface with question components
37
+
38
+ ## Components and Interfaces
39
+
40
+ ### 1. Question Engine (`ai_questions.py`)
41
+
42
+ **Primary Class: `AIQuestionEngine`**
43
+
44
+ ```python
45
+ class AIQuestionEngine:
46
+ def __init__(self, translator_instance):
47
+ self.translator = translator_instance
48
+ self.conversation_history = {}
49
+ self.question_templates = {
50
+ 'ar': [
51
+ "اشرح هذا النص",
52
+ "أعطني أمثلة على هذا",
53
+ "ما معنى هذا؟",
54
+ "كيف يُستخدم هذا؟",
55
+ "ما أهمية هذا؟"
56
+ ],
57
+ 'en': [
58
+ "Explain this text",
59
+ "Give me examples of this",
60
+ "What does this mean?",
61
+ "How is this used?",
62
+ "Why is this important?"
63
+ ]
64
+ }
65
+
66
+ def process_question(self, selected_text, question, ui_language='ar'):
67
+ """Process user question about selected text"""
68
+
69
+ def get_question_templates(self, ui_language='ar'):
70
+ """Get pre-defined question templates"""
71
+
72
+ def format_ai_response(self, response, ui_language='ar'):
73
+ """Format AI response for display"""
74
+
75
+ def save_conversation(self, text_id, question, answer):
76
+ """Save Q&A pair to conversation history"""
77
+
78
+ def get_conversation_history(self, text_id):
79
+ """Retrieve conversation history for specific text"""
80
+ ```
81
+
82
+ ### 2. UI Components (Extended `app.py`)
83
+
84
+ **Text Selection Interface**
85
+ - Click-to-select functionality for broadcast segments
86
+ - Visual highlighting of selected text
87
+ - Context-aware "Ask AI" button activation
88
+
89
+ **Question Input Modal**
90
+ - Template selection buttons
91
+ - Free-form question input
92
+ - Context display showing selected text
93
+ - Submit and cancel options
94
+
95
+ **Response Display Area**
96
+ - Formatted AI responses
97
+ - Conversation history
98
+ - Copy functionality
99
+ - Follow-up question options
100
+
101
+ ### 3. Context Manager
102
+
103
+ **Text Context Preparation**
104
+ ```python
105
+ @dataclass
106
+ class QuestionContext:
107
+ selected_text: str
108
+ segment_info: Dict[str, Any] # timestamp, translations, etc.
109
+ conversation_id: str
110
+ ui_language: str
111
+ previous_questions: List[Dict[str, str]]
112
+ ```
113
+
114
+ **Conversation Management**
115
+ - Maintains context across multiple questions
116
+ - Manages conversation threads per text segment
117
+ - Handles context window limitations
118
+ - Provides conversation persistence
119
+
120
+ ## Data Models
121
+
122
+ ### Question Session
123
+ ```python
124
+ @dataclass
125
+ class QuestionSession:
126
+ session_id: str
127
+ selected_text: str
128
+ segment_id: str
129
+ start_timestamp: int
130
+ ui_language: str
131
+ conversation: List[QAPair]
132
+ created_at: datetime
133
+ ```
134
+
135
+ ### Q&A Pair
136
+ ```python
137
+ @dataclass
138
+ class QAPair:
139
+ question: str
140
+ answer: str
141
+ timestamp: datetime
142
+ question_type: str # 'template' or 'custom'
143
+ response_time_ms: int
144
+ ```
145
+
146
+ ### Text Selection
147
+ ```python
148
+ @dataclass
149
+ class TextSelection:
150
+ text: str
151
+ segment_id: str
152
+ start_ms: int
153
+ end_ms: int
154
+ translations: Dict[str, str]
155
+ selection_timestamp: int
156
+ ```
157
+
158
+ ## Error Handling
159
+
160
+ ### AI Service Error Handling
161
+
162
+ 1. **Service Unavailability**
163
+ - Graceful degradation when Gemini AI is unavailable
164
+ - Clear error messages to users
165
+ - Retry mechanisms for transient failures
166
+
167
+ 2. **Response Quality Issues**
168
+ - Validation of AI responses
169
+ - Fallback to simpler question processing
170
+ - User feedback mechanisms for poor responses
171
+
172
+ 3. **Context Management Errors**
173
+ - Handling of oversized context windows
174
+ - Conversation history cleanup
175
+ - Memory management for long sessions
176
+
177
+ ### User Experience Error Handling
178
+
179
+ ```python
180
+ def handle_question_error(self, error_type, context):
181
+ """Handle various question processing errors"""
182
+ error_messages = {
183
+ 'ai_unavailable': {
184
+ 'ar': 'خدمة الذكاء الاصطناعي غير متاحة حالياً. يرجى المحاولة لاحقاً.',
185
+ 'en': 'AI service is currently unavailable. Please try again later.'
186
+ },
187
+ 'invalid_selection': {
188
+ 'ar': 'يرجى تحديد نص صالح قبل طرح السؤال.',
189
+ 'en': 'Please select valid text before asking a question.'
190
+ },
191
+ 'processing_timeout': {
192
+ 'ar': 'انتهت مهلة معالجة السؤال. يرجى المحاولة مرة أخرى.',
193
+ 'en': 'Question processing timed out. Please try again.'
194
+ }
195
+ }
196
+ ```
197
+
198
+ ## Testing Strategy
199
+
200
+ ### Unit Testing
201
+ - **Question Processing Tests**: Various question types and text selections
202
+ - **Context Management Tests**: Conversation history and memory management
203
+ - **AI Integration Tests**: Response formatting and error handling
204
+ - **UI Component Tests**: Text selection and modal interactions
205
+
206
+ ### Integration Testing
207
+ - **End-to-End Question Flow**: From text selection to AI response display
208
+ - **Multi-language Testing**: Arabic and English question processing
209
+ - **Conversation Continuity**: Follow-up questions and context maintenance
210
+ - **Performance Testing**: Response times and memory usage
211
+
212
+ ### User Acceptance Testing
213
+ - **Student Workflow Testing**: Real-world usage scenarios
214
+ - **Question Quality Testing**: Relevance and accuracy of AI responses
215
+ - **Interface Usability Testing**: Ease of text selection and question input
216
+ - **Accessibility Testing**: Screen reader compatibility and keyboard navigation
217
+
218
+ ## Implementation Phases
219
+
220
+ ### Phase 1: Core Question Engine
221
+ - Implement `AIQuestionEngine` class
222
+ - Add basic question processing with Gemini AI
223
+ - Create question templates for both languages
224
+ - Integrate with existing translator system
225
+
226
+ ### Phase 2: UI Integration
227
+ - Add text selection functionality to broadcast segments
228
+ - Implement "Ask AI" button and modal interface
229
+ - Create question input and response display components
230
+ - Add visual feedback for text selection
231
+
232
+ ### Phase 3: Conversation Management
233
+ - Implement conversation history tracking
234
+ - Add follow-up question capabilities
235
+ - Create context management for multi-turn conversations
236
+ - Add conversation persistence across sessions
237
+
238
+ ### Phase 4: Advanced Features
239
+ - Add copy/export functionality for Q&A pairs
240
+ - Implement conversation search and filtering
241
+ - Add question analytics and usage tracking
242
+ - Create advanced question templates and suggestions
243
+
244
+ ## Performance Considerations
245
+
246
+ ### Response Time Optimization
247
+ - Asynchronous AI request processing
248
+ - Response caching for common questions
249
+ - Progressive loading for long conversations
250
+ - Optimized context preparation
251
+
252
+ ### Memory Management
253
+ - Efficient conversation history storage
254
+ - Automatic cleanup of old conversations
255
+ - Streaming responses for long AI answers
256
+ - Optimized text selection handling
257
+
258
+ ### Scalability
259
+ - Support for multiple concurrent question sessions
260
+ - Efficient handling of large broadcast segments
261
+ - Configurable conversation history limits
262
+ - Resource usage monitoring and optimization
.kiro/specs/ai-questions/requirements.md ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Requirements Document
2
+
3
+ ## Introduction
4
+
5
+ This feature adds AI-powered question generation functionality to the SyncMaster broadcast system. Students can select any text segment from the broadcast and ask the AI to generate questions, explanations, or provide additional information about specific content. This enhances the learning experience by allowing interactive exploration of lecture content through intelligent questioning.
6
+
7
+ ## Requirements
8
+
9
+ ### Requirement 1
10
+
11
+ **User Story:** As a student reviewing broadcast content, I want to select any text segment and ask AI questions about it, so that I can better understand specific concepts or get clarification on unclear points.
12
+
13
+ #### Acceptance Criteria
14
+
15
+ 1. WHEN the user clicks on any broadcast segment THEN the system SHALL highlight the selected text
16
+ 2. WHEN text is selected THEN the system SHALL display a "❓ Ask AI" button
17
+ 3. WHEN the user clicks "Ask AI" THEN the system SHALL open a question input interface
18
+ 4. WHEN the user submits a question THEN the system SHALL send the selected text and question to the AI service
19
+ 5. IF no text is selected THEN the "Ask AI" button SHALL be disabled with explanatory tooltip
20
+
21
+ ### Requirement 2
22
+
23
+ **User Story:** As a student, I want to ask different types of questions about the selected content, so that I can get explanations, examples, or deeper insights about the material.
24
+
25
+ #### Acceptance Criteria
26
+
27
+ 1. WHEN the question interface opens THEN the system SHALL provide quick question templates
28
+ 2. WHEN templates are provided THEN they SHALL include: "Explain this", "Give examples", "What does this mean?", "How is this used?"
29
+ 3. WHEN the user selects a template THEN the system SHALL auto-fill the question input
30
+ 4. WHEN the user types a custom question THEN the system SHALL accept free-form text input
31
+ 5. WHEN processing the question THEN the system SHALL include the selected text as context for the AI
32
+
33
+ ### Requirement 3
34
+
35
+ **User Story:** As a student, I want to receive AI-generated answers in my preferred language, so that I can understand the explanations clearly.
36
+
37
+ #### Acceptance Criteria
38
+
39
+ 1. WHEN generating AI responses THEN the system SHALL use the current UI language setting
40
+ 2. WHEN the UI is in Arabic THEN AI responses SHALL be in Arabic
41
+ 3. WHEN the UI is in English THEN AI responses SHALL be in English
42
+ 4. WHEN the selected text is in a different language THEN the AI SHALL provide context-aware responses
43
+ 5. IF language detection fails THEN the system SHALL default to the UI language
44
+
45
+ ### Requirement 4
46
+
47
+ **User Story:** As a student, I want to see the AI's answer clearly formatted and easy to read, so that I can quickly understand the information provided.
48
+
49
+ #### Acceptance Criteria
50
+
51
+ 1. WHEN the AI responds THEN the answer SHALL be displayed in a dedicated response area
52
+ 2. WHEN displaying the response THEN the system SHALL show the original selected text for reference
53
+ 3. WHEN formatting the response THEN the system SHALL use clear typography and spacing
54
+ 4. WHEN the response is long THEN the system SHALL provide scrollable content area
55
+ 5. WHEN multiple questions are asked THEN the system SHALL maintain a conversation history
56
+
57
+ ### Requirement 5
58
+
59
+ **User Story:** As a student, I want to ask follow-up questions about the same content, so that I can have a natural conversation with the AI about the topic.
60
+
61
+ #### Acceptance Criteria
62
+
63
+ 1. WHEN an AI response is displayed THEN the system SHALL provide an option to ask follow-up questions
64
+ 2. WHEN asking follow-up questions THEN the system SHALL maintain context from previous questions
65
+ 3. WHEN the conversation continues THEN the system SHALL display the full conversation thread
66
+ 4. WHEN starting a new question on different content THEN the system SHALL start a fresh conversation
67
+ 5. IF the conversation becomes too long THEN the system SHALL provide option to clear history
68
+
69
+ ### Requirement 6
70
+
71
+ **User Story:** As a student, I want to copy or save the AI's answers, so that I can include them in my notes or study materials.
72
+
73
+ #### Acceptance Criteria
74
+
75
+ 1. WHEN an AI response is displayed THEN the system SHALL provide a "Copy" button
76
+ 2. WHEN the user clicks "Copy" THEN the response text SHALL be copied to clipboard
77
+ 3. WHEN copying THEN the system SHALL include both the original question and AI answer
78
+ 4. WHEN multiple Q&A pairs exist THEN the user SHALL be able to copy individual answers or the entire conversation
79
+ 5. WHEN copying THEN the system SHALL format the text appropriately for pasting into documents
80
+
81
+ ### Requirement 7
82
+
83
+ **User Story:** As a student, I want the AI question feature to work seamlessly with the existing broadcast interface, so that my workflow is not disrupted.
84
+
85
+ #### Acceptance Criteria
86
+
87
+ 1. WHEN the question interface is open THEN the broadcast content SHALL remain visible
88
+ 2. WHEN asking questions THEN the broadcast playback SHALL not be interrupted
89
+ 3. WHEN switching between segments THEN any open question interface SHALL adapt to the new selection
90
+ 4. WHEN the broadcast is updated with new segments THEN the question feature SHALL work with new content
91
+ 5. IF the AI service is unavailable THEN the system SHALL display appropriate error messages and fallback options
.kiro/specs/ai-questions/tasks.md ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Implementation Plan
2
+
3
+ - [x] 1. Set up AI question engine infrastructure
4
+
5
+
6
+ - Create ai_questions.py module with AIQuestionEngine class
7
+ - Implement basic question processing using existing Gemini AI integration
8
+ - Add question templates for Arabic and English languages
9
+ - Create data models for question sessions and Q&A pairs
10
+ - _Requirements: 1.1, 2.1, 3.1_
11
+
12
+
13
+
14
+ - [ ] 2. Implement text selection functionality
15
+ - [ ] 2.1 Add clickable text selection to broadcast segments
16
+ - Modify broadcast segment display to make text selectable
17
+ - Implement visual highlighting for selected text
18
+ - Add session state management for selected text
19
+ - Create text selection validation and error handling
20
+
21
+ - Write unit tests for text selection functionality
22
+ - _Requirements: 1.1, 1.5_
23
+
24
+ - [ ] 2.2 Create "Ask AI" button with conditional display
25
+ - Add "Ask AI" button that appears when text is selected
26
+ - Implement button state management (enabled/disabled)
27
+ - Add tooltips and help text for button functionality
28
+ - Create button styling consistent with existing UI
29
+
30
+ - Test button behavior with different text selections
31
+ - _Requirements: 1.2, 1.5_
32
+
33
+ - [ ] 3. Build question input interface
34
+ - [ ] 3.1 Create question input modal with templates
35
+ - Implement modal dialog for question input
36
+ - Add pre-defined question templates with quick selection
37
+
38
+ - Create free-form text input for custom questions
39
+ - Display selected text context in the modal
40
+ - Add submit and cancel functionality
41
+ - _Requirements: 2.1, 2.2, 2.3, 2.4_
42
+
43
+ - [ ] 3.2 Implement question processing and AI integration
44
+ - Connect question input to AIQuestionEngine
45
+ - Add context preparation including selected text and metadata
46
+
47
+ - Implement AI request processing with error handling
48
+ - Add loading indicators during AI processing
49
+ - Create timeout handling for long AI responses
50
+ - _Requirements: 1.4, 2.5, 7.5_
51
+
52
+ - [ ] 4. Create AI response display system
53
+ - [x] 4.1 Build response display area with formatting
54
+
55
+ - Create dedicated area for displaying AI responses
56
+ - Implement proper text formatting and typography
57
+ - Add scrollable content area for long responses
58
+ - Display original selected text for reference
59
+ - Create responsive design for different screen sizes
60
+ - _Requirements: 4.1, 4.2, 4.3, 4.4_
61
+
62
+ - [x] 4.2 Add conversation history management
63
+
64
+ - Implement conversation thread display
65
+ - Add conversation history storage in session state
66
+ - Create conversation navigation and scrolling
67
+ - Add conversation clearing functionality
68
+ - Test conversation persistence across UI interactions
69
+ - _Requirements: 5.1, 5.2, 5.3, 5.4, 5.5_
70
+
71
+
72
+ - [ ] 5. Implement multilingual support
73
+ - [ ] 5.1 Add language-aware AI response generation
74
+ - Configure AI responses based on UI language setting
75
+ - Implement language detection for selected text
76
+ - Add context-aware response generation
77
+ - Create fallback mechanisms for language detection failures
78
+ - Test multilingual question processing
79
+ - _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5_
80
+
81
+
82
+ - [ ] 5.2 Create multilingual question templates
83
+ - Implement Arabic question templates
84
+ - Add English question templates
85
+ - Create template selection based on UI language
86
+ - Add template customization and expansion
87
+ - Test template functionality in both languages
88
+ - _Requirements: 2.1, 2.2, 2.3_
89
+
90
+ - [ ] 6. Add copy and export functionality
91
+ - [ ] 6.1 Implement response copying features
92
+ - Add "Copy" button for individual AI responses
93
+ - Implement clipboard integration for copying text
94
+ - Create formatted copying including questions and answers
95
+ - Add copy confirmation feedback to users
96
+ - Test copying functionality across different browsers
97
+
98
+
99
+ - _Requirements: 6.1, 6.2, 6.3_
100
+
101
+ - [ ] 6.2 Create conversation export capabilities
102
+ - Add functionality to copy entire conversations
103
+ - Implement formatted export for study materials
104
+ - Create export options for individual Q&A pairs
105
+ - Add export formatting for different use cases
106
+ - Test export functionality with various conversation lengths
107
+ - _Requirements: 6.4, 6.5_
108
+
109
+ - [ ] 7. Integrate with existing broadcast system
110
+ - [ ] 7.1 Ensure seamless broadcast integration
111
+ - Integrate question interface with existing broadcast UI
112
+ - Maintain broadcast visibility during question sessions
113
+ - Ensure broadcast playback is not interrupted by questions
114
+ - Add smooth transitions between broadcast and question modes
115
+ - Test integration with all existing broadcast features
116
+ - _Requirements: 7.1, 7.2_
117
+
118
+ - [ ] 7.2 Handle dynamic broadcast updates
119
+ - Adapt question interface to new broadcast segments
120
+ - Update text selection when broadcast content changes
121
+ - Maintain question sessions across broadcast updates
122
+ - Handle segment deletion and modification gracefully
123
+ - Test with real-time broadcast updates
124
+ - _Requirements: 7.3, 7.4_
125
+
126
+ - [ ] 8. Implement error handling and fallbacks
127
+ - [ ] 8.1 Add comprehensive error handling
128
+ - Implement AI service unavailability handling
129
+ - Add user-friendly error messages in both languages
130
+ - Create retry mechanisms for failed AI requests
131
+ - Add fallback options when AI service fails
132
+ - Test error scenarios and recovery mechanisms
133
+ - _Requirements: 7.5_
134
+
135
+ - [ ] 8.2 Create robust conversation management
136
+ - Add conversation cleanup for memory management
137
+ - Implement conversation size limits and warnings
138
+ - Create automatic conversation archiving
139
+ - Add conversation recovery after errors
140
+ - Test conversation stability under various conditions
141
+ - _Requirements: 5.5_
142
+
143
+ - [ ] 9. Add advanced question features
144
+ - [ ] 9.1 Implement follow-up question capabilities
145
+ - Add "Ask follow-up" functionality to responses
146
+ - Maintain conversation context across multiple questions
147
+ - Create intelligent context summarization for long conversations
148
+ - Add conversation branching for different topics
149
+ - Test follow-up question accuracy and relevance
150
+ - _Requirements: 5.1, 5.2, 5.3_
151
+
152
+ - [ ] 9.2 Create question suggestion system
153
+ - Implement AI-powered question suggestions based on selected text
154
+ - Add smart question recommendations
155
+ - Create question difficulty levels (basic, intermediate, advanced)
156
+ - Add question categorization (explanation, examples, application)
157
+ - Test suggestion quality and relevance
158
+ - _Requirements: 2.1, 2.2_
159
+
160
+ - [ ] 10. Optimize performance and user experience
161
+ - [ ] 10.1 Implement performance optimizations
162
+ - Add asynchronous processing for AI requests
163
+ - Implement response caching for common questions
164
+ - Create progressive loading for long conversations
165
+ - Add memory optimization for conversation history
166
+ - Test performance with large broadcast segments
167
+ - _Requirements: 4.4, 5.5_
168
+
169
+ - [ ] 10.2 Enhance user experience features
170
+ - Add keyboard shortcuts for common actions
171
+ - Implement drag-and-drop text selection
172
+ - Create question history search functionality
173
+ - Add question bookmarking and favorites
174
+ - Test accessibility features and screen reader compatibility
175
+ - _Requirements: 7.1, 7.2_
176
+
177
+ - [ ] 11. Create comprehensive testing suite
178
+ - [ ] 11.1 Write unit tests for question engine
179
+ - Create tests for AIQuestionEngine class methods
180
+ - Add tests for question processing and formatting
181
+ - Write tests for conversation management
182
+ - Create tests for error handling scenarios
183
+ - Implement test data fixtures for various question types
184
+ - _Requirements: All requirements validation_
185
+
186
+ - [ ] 11.2 Implement integration tests
187
+ - Create end-to-end tests for complete question flow
188
+ - Add tests for multilingual question processing
189
+ - Write tests for UI component interactions
190
+ - Create tests for broadcast system integration
191
+ - Implement performance tests for AI response times
192
+ - _Requirements: All requirements validation_
193
+
194
+ - [ ] 12. Final integration and polish
195
+ - [ ] 12.1 Complete system integration
196
+ - Ensure seamless integration with all existing features
197
+ - Test compatibility with export functionality
198
+ - Verify proper session state management
199
+ - Add configuration options for question features
200
+ - Create deployment-ready code with proper error handling
201
+ - _Requirements: All requirements_
202
+
203
+ - [ ] 12.2 Create user documentation and help
204
+ - Write user guide for AI question features
205
+ - Create in-app help and tooltips
206
+ - Add troubleshooting documentation
207
+ - Create video tutorials for complex workflows
208
+ - Document keyboard shortcuts and advanced features
209
+ - _Requirements: 7.5_
.kiro/specs/broadcast-export/design.md ADDED
@@ -0,0 +1,274 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Design Document
2
+
3
+ ## Overview
4
+
5
+ The broadcast export feature extends SyncMaster Enhanced with the ability to export lecture content from a specific timestamp. The system will integrate seamlessly with the existing broadcast functionality, providing students with formatted documents containing original text, translations, and AI-generated summaries. The design prioritizes simplicity, reliability, and multilingual support.
6
+
7
+ ## Architecture
8
+
9
+ ### High-Level Architecture
10
+
11
+ ```
12
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
13
+ │ Streamlit UI │ │ Export Engine │ │ File Generators│
14
+ │ │ │ │ │ │
15
+ │ - Export Button │───▶│ - Data Filter │───▶│ - Word Exporter │
16
+ │ - Preview Modal │ │ - Content Prep │ │ - GDocs Exporter│
17
+ │ - Download Link │◀───│ - Format Router │◀───│ - Summary Gen │
18
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
19
+ │ │ │
20
+ │ │ │
21
+ ▼ ▼ ▼
22
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
23
+ │ Session State │ │ Broadcast Data │ │ External APIs │
24
+ │ │ │ │ │ │
25
+ │ - Export Time │ │ - Segments │ │ - Google Docs │
26
+ │ - UI Language │ │ - Translations │ │ - Gemini AI │
27
+ │ - Export Config │ │ - Timestamps │ │ - File System │
28
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
29
+ ```
30
+
31
+ ### Component Integration
32
+
33
+ The export feature integrates with existing SyncMaster components:
34
+ - **Broadcast System**: Uses `st.session_state.broadcast_segments` for data
35
+ - **Translation System**: Leverages existing `translator.py` for summaries
36
+ - **UI System**: Extends current Streamlit interface with export controls
37
+
38
+ ## Components and Interfaces
39
+
40
+ ### 1. Export Engine (`exporter.py`)
41
+
42
+ **Primary Class: `BroadcastExporter`**
43
+
44
+ ```python
45
+ class BroadcastExporter:
46
+ def __init__(self, translator_instance):
47
+ self.translator = translator_instance
48
+ self.supported_formats = ['word', 'google_docs']
49
+
50
+ def filter_segments_from_timestamp(self, segments, export_timestamp):
51
+ """Filter broadcast segments from export timestamp"""
52
+
53
+ def prepare_export_content(self, segments, include_summary=True):
54
+ """Prepare structured content for export"""
55
+
56
+ def export_to_word(self, content, filename):
57
+ """Generate Word document"""
58
+
59
+ def export_to_google_docs(self, content, title):
60
+ """Create Google Docs document"""
61
+
62
+ def generate_export_summary(self, segments, target_language='ar'):
63
+ """Generate summary for export content"""
64
+ ```
65
+
66
+ ### 2. UI Components (Extended `app.py`)
67
+
68
+ **Export Button Integration**
69
+ - Location: Within the broadcast expander section
70
+ - Trigger: Records current timestamp and opens export modal
71
+ - State Management: Uses session state for export configuration
72
+
73
+ **Export Modal**
74
+ - Preview content display
75
+ - Format selection (Word/Google Docs)
76
+ - Export confirmation/cancellation
77
+ - Progress indication during export
78
+
79
+ ### 3. Document Generators
80
+
81
+ **Word Document Structure:**
82
+ ```
83
+ Title: محاضرة - [Date/Time]
84
+ Export Time: [Timestamp]
85
+ ═══════════════════════════════════
86
+
87
+ 📻 البرودكاست المُصدر
88
+ ═══════════════════════════════════
89
+
90
+ [Segment 1: Time Range]
91
+ Original: [Text]
92
+ Translation: [Text]
93
+
94
+ [Segment 2: Time Range]
95
+ Original: [Text]
96
+ Translation: [Text]
97
+
98
+ 📝 الملخص
99
+ ═══════════════════════════════════
100
+ [AI-Generated Summary]
101
+ ```
102
+
103
+ **Google Docs Integration:**
104
+ - Uses Google Docs API v1
105
+ - Creates shareable documents
106
+ - Applies consistent formatting
107
+ - Handles authentication via service account
108
+
109
+ ## Data Models
110
+
111
+ ### Export Configuration
112
+ ```python
113
+ @dataclass
114
+ class ExportConfig:
115
+ export_timestamp: int # Unix timestamp in milliseconds
116
+ format_type: str # 'word' or 'google_docs'
117
+ include_summary: bool # Whether to include AI summary
118
+ ui_language: str # 'ar' or 'en' for interface
119
+ target_language: str # Translation language for summary
120
+ ```
121
+
122
+ ### Export Content
123
+ ```python
124
+ @dataclass
125
+ class ExportContent:
126
+ title: str
127
+ export_time: str
128
+ segments: List[BroadcastSegment]
129
+ summary: Optional[str]
130
+ metadata: Dict[str, Any]
131
+ ```
132
+
133
+ ### Broadcast Segment (Extended)
134
+ ```python
135
+ @dataclass
136
+ class BroadcastSegment:
137
+ id: str
138
+ start_ms: int
139
+ end_ms: int
140
+ text: str
141
+ translations: Dict[str, str] # language_code -> translated_text
142
+ timestamp_formatted: str # Human-readable time range
143
+ ```
144
+
145
+ ## Error Handling
146
+
147
+ ### Export Process Error Handling
148
+
149
+ 1. **Timestamp Validation**
150
+ - Verify export timestamp is valid
151
+ - Handle edge cases (no segments after timestamp)
152
+ - Provide user feedback for empty exports
153
+
154
+ 2. **Document Generation Errors**
155
+ - Word document creation failures
156
+ - Google Docs API errors
157
+ - File system permission issues
158
+ - Network connectivity problems
159
+
160
+ 3. **Summary Generation Errors**
161
+ - AI service unavailability
162
+ - Empty content handling
163
+ - Fallback to export without summary
164
+
165
+ 4. **User Experience Error Handling**
166
+ - Clear error messages in user's language
167
+ - Graceful degradation (Word fallback for Google Docs)
168
+ - Retry mechanisms for transient failures
169
+
170
+ ### Error Recovery Strategies
171
+
172
+ ```python
173
+ def export_with_fallback(self, content, format_type):
174
+ """Export with automatic fallback handling"""
175
+ try:
176
+ if format_type == 'google_docs':
177
+ return self.export_to_google_docs(content)
178
+ except GoogleDocsError:
179
+ # Fallback to Word export
180
+ return self.export_to_word(content)
181
+ except Exception as e:
182
+ # Log error and provide user feedback
183
+ return self.handle_export_error(e)
184
+ ```
185
+
186
+ ## Testing Strategy
187
+
188
+ ### Unit Testing
189
+ - **Export Engine Tests**: Data filtering, content preparation, format generation
190
+ - **Document Generator Tests**: Word document structure, Google Docs API integration
191
+ - **Error Handling Tests**: Various failure scenarios and recovery mechanisms
192
+
193
+ ### Integration Testing
194
+ - **End-to-End Export Flow**: From button click to document download
195
+ - **Multi-language Testing**: Arabic and English interface testing
196
+ - **Cross-format Testing**: Consistency between Word and Google Docs exports
197
+
198
+ ### User Acceptance Testing
199
+ - **Student Workflow Testing**: Real lecture scenario testing
200
+ - **Performance Testing**: Export speed with large broadcast segments
201
+ - **Accessibility Testing**: Screen reader compatibility, keyboard navigation
202
+
203
+ ### Test Data Scenarios
204
+ ```python
205
+ # Test scenarios for broadcast segments
206
+ test_scenarios = [
207
+ "empty_broadcast", # No segments to export
208
+ "single_segment", # One segment after timestamp
209
+ "multiple_segments", # Multiple segments with translations
210
+ "mixed_languages", # Segments in different languages
211
+ "large_content", # Performance testing with many segments
212
+ "special_characters", # Unicode and RTL text handling
213
+ ]
214
+ ```
215
+
216
+ ## Implementation Phases
217
+
218
+ ### Phase 1: Core Export Engine
219
+ - Implement `BroadcastExporter` class
220
+ - Add timestamp filtering functionality
221
+ - Create basic Word document generation
222
+ - Integrate with existing broadcast data
223
+
224
+ ### Phase 2: UI Integration
225
+ - Add export button to broadcast section
226
+ - Implement export modal with preview
227
+ - Add progress indicators and user feedback
228
+ - Handle multilingual UI elements
229
+
230
+ ### Phase 3: Advanced Features
231
+ - Google Docs integration
232
+ - Enhanced document formatting
233
+ - Summary generation for export content
234
+ - Error handling and fallback mechanisms
235
+
236
+ ### Phase 4: Testing and Optimization
237
+ - Comprehensive testing suite
238
+ - Performance optimization
239
+ - User experience refinements
240
+ - Documentation and help content
241
+
242
+ ## Security Considerations
243
+
244
+ ### Data Privacy
245
+ - Export content remains on user's device or chosen cloud service
246
+ - No intermediate storage of sensitive lecture content
247
+ - Google Docs integration uses user's own Google account
248
+
249
+ ### API Security
250
+ - Google Docs API authentication via OAuth 2.0
251
+ - Secure handling of API credentials
252
+ - Rate limiting and quota management
253
+
254
+ ### File Security
255
+ - Generated Word documents include no executable content
256
+ - Sanitization of user input in document titles
257
+ - Secure temporary file handling during generation
258
+
259
+ ## Performance Considerations
260
+
261
+ ### Export Speed Optimization
262
+ - Lazy loading of large broadcast segments
263
+ - Asynchronous document generation
264
+ - Progress feedback for long-running exports
265
+
266
+ ### Memory Management
267
+ - Streaming document generation for large content
268
+ - Cleanup of temporary files and resources
269
+ - Efficient handling of multilingual text encoding
270
+
271
+ ### Scalability
272
+ - Support for exports with hundreds of broadcast segments
273
+ - Optimized data structures for large lecture sessions
274
+ - Configurable export limits to prevent system overload
.kiro/specs/broadcast-export/requirements.md ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Requirements Document
2
+
3
+ ## Introduction
4
+
5
+ This feature adds export functionality to the SyncMaster Enhanced application, specifically for the broadcast section. The feature allows students to export their lecture content (original text, translations, and summaries) from a specific point in time when they press the export button, rather than exporting the entire broadcast history. This enables students to capture and save important lecture segments for later review in Word or Google Docs format.
6
+
7
+ ## Requirements
8
+
9
+ ### Requirement 1
10
+
11
+ **User Story:** As a student using SyncMaster during a lecture, I want to export the broadcast content from a specific moment, so that I can save important lecture segments for later review.
12
+
13
+ #### Acceptance Criteria
14
+
15
+ 1. WHEN the user clicks the export button THEN the system SHALL record the current timestamp as the export starting point
16
+ 2. WHEN exporting THEN the system SHALL include only broadcast segments that occurred after the export timestamp
17
+ 3. WHEN exporting THEN the system SHALL include both original text and translations for each segment
18
+ 4. IF no segments exist after the export timestamp THEN the system SHALL display a message indicating no content to export
19
+
20
+ ### Requirement 2
21
+
22
+ **User Story:** As a student, I want to export my lecture content to Word format, so that I can easily review and edit the content offline.
23
+
24
+ #### Acceptance Criteria
25
+
26
+ 1. WHEN the user selects Word export THEN the system SHALL generate a .docx file
27
+ 2. WHEN generating the Word document THEN the system SHALL include a header with lecture title and export timestamp
28
+ 3. WHEN generating the Word document THEN the system SHALL format the content with clear sections for broadcast segments and summary
29
+ 4. WHEN the Word document is generated THEN the system SHALL provide a download link to the user
30
+ 5. IF the Word generation fails THEN the system SHALL display an error message and suggest alternative export options
31
+
32
+ ### Requirement 3
33
+
34
+ **User Story:** As a student, I want to export my lecture content to Google Docs, so that I can access and share it from anywhere.
35
+
36
+ #### Acceptance Criteria
37
+
38
+ 1. WHEN the user selects Google Docs export THEN the system SHALL create a new Google Docs document
39
+ 2. WHEN creating the Google Docs document THEN the system SHALL use the same formatting structure as Word export
40
+ 3. WHEN the Google Docs document is created THEN the system SHALL provide a shareable link to the user
41
+ 4. IF Google Docs integration is not available THEN the system SHALL fall back to Word export
42
+ 5. WHEN Google Docs export fails THEN the system SHALL display an error message with troubleshooting steps
43
+
44
+ ### Requirement 4
45
+
46
+ **User Story:** As a student, I want the exported content to include the Arabic summary, so that I can have a comprehensive review document.
47
+
48
+ #### Acceptance Criteria
49
+
50
+ 1. WHEN exporting THEN the system SHALL include the current Arabic summary if available
51
+ 2. WHEN no Arabic summary exists THEN the system SHALL generate a new summary based on the exported segments
52
+ 3. WHEN generating a new summary THEN the system SHALL use the same AI translation service as the main application
53
+ 4. IF summary generation fails THEN the system SHALL export without the summary section and notify the user
54
+
55
+ ### Requirement 5
56
+
57
+ **User Story:** As a student, I want to preview the export content before downloading, so that I can verify it contains the information I need.
58
+
59
+ #### Acceptance Criteria
60
+
61
+ 1. WHEN the user clicks export THEN the system SHALL display a preview modal showing the content to be exported
62
+ 2. WHEN showing the preview THEN the system SHALL display the number of segments and estimated document length
63
+ 3. WHEN in preview mode THEN the user SHALL be able to confirm or cancel the export
64
+ 4. WHEN the user confirms export THEN the system SHALL proceed with the selected format
65
+ 5. WHEN the user cancels export THEN the system SHALL close the preview without creating any files
66
+
67
+ ### Requirement 6
68
+
69
+ **User Story:** As a student, I want the export feature to work in both Arabic and English interfaces, so that I can use it regardless of my language preference.
70
+
71
+ #### Acceptance Criteria
72
+
73
+ 1. WHEN the interface language is Arabic THEN all export UI elements SHALL be displayed in Arabic
74
+ 2. WHEN the interface language is English THEN all export UI elements SHALL be displayed in English
75
+ 3. WHEN exporting THEN the document structure SHALL adapt to the interface language while preserving content languages
76
+ 4. WHEN displaying error messages THEN they SHALL be shown in the current interface language
77
+
78
+ ### Requirement 7
79
+
80
+ **User Story:** As a student, I want the export button to be easily accessible in the broadcast section, so that I can quickly export content during live lectures.
81
+
82
+ #### Acceptance Criteria
83
+
84
+ 1. WHEN viewing the broadcast section THEN the export button SHALL be prominently displayed
85
+ 2. WHEN the broadcast section is collapsed THEN the export button SHALL remain visible
86
+ 3. WHEN clicking the export button THEN the system SHALL respond within 2 seconds
87
+ 4. WHEN no broadcast segments exist THEN the export button SHALL be disabled with an explanatory tooltip
.kiro/specs/broadcast-export/tasks.md ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Implementation Plan
2
+
3
+ - [x] 1. Set up export infrastructure and dependencies
4
+
5
+
6
+ - Install required Python packages for document generation (python-docx, google-api-python-client)
7
+ - Update requirements.txt with new dependencies
8
+ - Create basic project structure for export functionality
9
+ - _Requirements: 1.1, 2.1, 3.1_
10
+
11
+ - [ ] 2. Implement core export engine
12
+ - [x] 2.1 Create BroadcastExporter class with timestamp filtering
13
+
14
+
15
+
16
+ - Write BroadcastExporter class in new exporter.py file
17
+ - Implement filter_segments_from_timestamp method to filter segments by export time
18
+ - Add prepare_export_content method to structure data for export
19
+ - Create unit tests for timestamp filtering logic
20
+ - _Requirements: 1.1, 1.2, 1.3_
21
+
22
+ - [ ] 2.2 Implement Word document generation functionality
23
+ - Add export_to_word method using python-docx library
24
+ - Create document template with proper Arabic/English formatting
25
+ - Implement structured content layout (header, segments, summary sections)
26
+ - Add proper RTL text support for Arabic content
27
+ - Write unit tests for Word document generation
28
+ - _Requirements: 2.1, 2.2, 2.3, 6.3_
29
+
30
+ - [ ] 2.3 Add export content preparation and formatting
31
+ - Implement content structuring for both original text and translations
32
+ - Add timestamp formatting for human-readable time ranges
33
+ - Create multilingual document headers and section titles
34
+ - Handle special characters and Unicode text properly
35
+ - Write tests for content preparation logic
36
+ - _Requirements: 1.3, 2.3, 6.1, 6.2_
37
+
38
+
39
+ - [ ] 3. Integrate export functionality with existing UI
40
+ - [ ] 3.1 Add export button to broadcast section
41
+
42
+
43
+ - Modify app.py to add export button in broadcast expander
44
+ - Implement export timestamp recording when button is clicked
45
+ - Add button state management (enabled/disabled based on content)
46
+ - Create multilingual button text and tooltips
47
+ - _Requirements: 7.1, 7.2, 7.4, 6.1, 6.2_
48
+
49
+ - [ ] 3.2 Create export preview modal interface
50
+ - Implement export preview modal using Streamlit components
51
+ - Add content preview showing segments count and estimated length
52
+ - Create format selection interface (Word/Google Docs options)
53
+ - Add confirm/cancel buttons with proper event handling
54
+ - Write UI tests for modal functionality
55
+ - _Requirements: 5.1, 5.2, 5.3, 5.4, 5.5_
56
+
57
+ - [ ] 3.3 Implement export progress and feedback system
58
+ - Add progress indicators during export generation
59
+ - Create success/error message display system
60
+ - Implement download link generation for completed exports
61
+ - Add multilingual error messages and user feedback
62
+ - Handle export cancellation and cleanup
63
+ - _Requirements: 7.3, 6.4, 2.4_
64
+
65
+ - [ ] 4. Add summary generation for export content
66
+ - [ ] 4.1 Integrate AI summary generation for export segments
67
+ - Extend exporter.py with generate_export_summary method
68
+ - Use existing translator.py functionality for summary generation
69
+ - Implement summary generation based on filtered segments only
70
+ - Add fallback handling when summary generation fails
71
+ - Create tests for summary integration
72
+ - _Requirements: 4.1, 4.2, 4.3, 4.4_
73
+
74
+ - [ ] 4.2 Handle summary inclusion in export documents
75
+ - Modify Word document generation to include summary section
76
+ - Add conditional summary inclusion based on user preferences
77
+ - Implement proper formatting for Arabic summary text
78
+ - Handle cases where summary is unavailable or generation fails
79
+ - Test summary formatting in exported documents
80
+ - _Requirements: 4.1, 4.4, 6.3_
81
+
82
+ - [ ] 5. Implement Google Docs integration
83
+ - [x] 5.1 Set up Google Docs API integration
84
+
85
+
86
+
87
+ - Create Google Docs API service setup and authentication
88
+ - Implement OAuth 2.0 flow for user authorization
89
+ - Add Google API credentials management
90
+ - Create basic Google Docs document creation functionality
91
+ - Write integration tests for Google API connectivity
92
+ - _Requirements: 3.1, 3.2_
93
+
94
+ - [ ] 5.2 Implement Google Docs export functionality
95
+ - Add export_to_google_docs method to BroadcastExporter
96
+ - Implement document formatting using Google Docs API
97
+ - Add proper RTL text support for Arabic content in Google Docs
98
+ - Create shareable link generation for exported documents
99
+ - Handle Google Docs API errors and rate limiting
100
+ - _Requirements: 3.1, 3.2, 3.3, 6.3_
101
+
102
+ - [ ] 5.3 Add Google Docs fallback and error handling
103
+ - Implement automatic fallback to Word export when Google Docs fails
104
+ - Add user-friendly error messages for Google Docs issues
105
+ - Create retry mechanisms for transient API failures
106
+ - Handle authentication errors and re-authorization flow
107
+ - Test fallback scenarios and error recovery
108
+ - _Requirements: 3.4, 3.5_
109
+
110
+ - [ ] 6. Implement comprehensive error handling
111
+ - [ ] 6.1 Add export validation and error prevention
112
+ - Implement pre-export validation (timestamp, content availability)
113
+ - Add user input sanitization for document titles and content
114
+ - Create validation for export configuration parameters
115
+ - Handle edge cases (empty segments, invalid timestamps)
116
+ - Write comprehensive validation tests
117
+ - _Requirements: 1.4, 7.4_
118
+
119
+ - [ ] 6.2 Create robust error recovery system
120
+ - Implement try-catch blocks for all export operations
121
+ - Add logging for debugging export failures
122
+ - Create user-friendly error messages in multiple languages
123
+ - Implement cleanup procedures for failed exports
124
+ - Add error reporting and diagnostics functionality
125
+ - _Requirements: 2.5, 3.5, 6.4_
126
+
127
+ - [ ] 7. Add multilingual support and localization
128
+ - [ ] 7.1 Implement Arabic interface support for export features
129
+ - Add Arabic translations for all export UI elements
130
+ - Create RTL-compatible export modal layout
131
+ - Implement Arabic document templates and formatting
132
+ - Add Arabic error messages and user feedback
133
+ - Test Arabic interface functionality thoroughly
134
+ - _Requirements: 6.1, 6.4_
135
+
136
+ - [ ] 7.2 Ensure consistent multilingual document generation
137
+ - Implement language-aware document formatting
138
+ - Add proper font selection for Arabic and English text
139
+ - Create consistent styling across different languages
140
+ - Handle mixed-language content in exports
141
+ - Test document generation with various language combinations
142
+ - _Requirements: 6.3_
143
+
144
+ Auto-process snapshots (keeps recording)
145
+ - [ ] 8. Create comprehensive testing suite
146
+ - [ ] 8.1 Write unit tests for export functionality
147
+ - Create tests for BroadcastExporter class methods
148
+ - Add tests for timestamp filtering and content preparation
149
+ - Write tests for Word document generation
150
+ - Create tests for error handling and edge cases
151
+ - Implement test data fixtures for various scenarios
152
+ - _Requirements: All requirements validation_
153
+
154
+ - [ ] 8.2 Implement integration tests for complete export flow
155
+ - Create end-to-end tests from button click to document download
156
+ - Add tests for multilingual export scenarios
157
+ - Write tests for Google Docs integration (with mocking)
158
+ - Create performance tests for large broadcast segments
159
+ - Implement user workflow simulation tests
160
+ - _Requirements: All requirements validation_
161
+
162
+ - [ ] 9. Optimize performance and user experience
163
+ - [ ] 9.1 Implement export performance optimizations
164
+ - Add asynchronous processing for large exports
165
+ - Implement memory-efficient document generation
166
+ - Create progress tracking for long-running exports
167
+ - Add export size limits and warnings
168
+ - Optimize data structures for large segment collections
169
+ - _Requirements: 7.3_
170
+
171
+ - [ ] 9.2 Enhance user experience and accessibility
172
+ - Add keyboard navigation support for export interface
173
+ - Implement screen reader compatibility
174
+ - Create helpful tooltips and user guidance
175
+ - Add export history and recent exports tracking
176
+ - Implement user preferences for export settings
177
+ - _Requirements: 5.1, 7.1, 7.2_
178
+
179
+ - [ ] 10. Final integration and documentation
180
+ - [ ] 10.1 Complete integration with main application
181
+ - Ensure seamless integration with existing broadcast functionality
182
+ - Test compatibility with all existing features
183
+ - Verify proper session state management
184
+ - Add configuration options for export features
185
+ - Create deployment-ready code with proper error handling
186
+ - _Requirements: All requirements_
187
+
188
+ - [ ] 10.2 Create user documentation and help content
189
+ - Write user guide for export functionality
190
+ - Create troubleshooting documentation
191
+ - Add inline help text and tooltips
192
+ - Create video tutorials or screenshots for complex workflows
193
+ - Document API integration requirements for Google Docs
194
+ - _Requirements: 3.5, 6.4_
.kiro/specs/model-management/design.md ADDED
@@ -0,0 +1,225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Model Management System Design
2
+
3
+ ## Overview
4
+
5
+ The Model Management System provides a centralized, robust approach to managing AI model configurations across multiple providers (Gemini, OpenRouter, Groq). It includes automatic validation, fallback mechanisms, health monitoring, and user-friendly status reporting.
6
+
7
+ ## Architecture
8
+
9
+ ### Core Components
10
+
11
+ 1. **ModelManager**: Central coordinator for all model operations
12
+ 2. **ModelValidator**: Validates model configurations against provider APIs
13
+ 3. **ModelHealthMonitor**: Monitors model availability and performance
14
+ 4. **FallbackChain**: Manages automatic fallback to alternative models
15
+ 5. **ModelConfigStore**: Centralized configuration management
16
+ 6. **StatusReporter**: Provides real-time status information to users
17
+
18
+ ### Component Interactions
19
+
20
+ ```
21
+ User Request → ModelManager → ModelValidator → Provider API
22
+
23
+ FallbackChain → Alternative Models
24
+
25
+ StatusReporter → User Feedback
26
+ ```
27
+
28
+ ## Components and Interfaces
29
+
30
+ ### ModelManager
31
+
32
+ **Purpose**: Central coordinator for all AI model operations
33
+
34
+ **Key Methods**:
35
+ - `get_available_model(provider, task_type)`: Returns best available model for a task
36
+ - `execute_with_fallback(prompt, preferences)`: Executes request with automatic fallback
37
+ - `get_system_status()`: Returns current status of all models
38
+ - `refresh_configurations()`: Reloads and validates all configurations
39
+
40
+ **Interfaces**:
41
+ - Input: User requests, configuration updates
42
+ - Output: Model responses, status information, error messages
43
+
44
+ ### ModelValidator
45
+
46
+ **Purpose**: Validates model configurations against provider APIs
47
+
48
+ **Key Methods**:
49
+ - `validate_model(provider, model_id)`: Tests if a model is valid and accessible
50
+ - `validate_all_models()`: Validates all configured models
51
+ - `suggest_alternatives(invalid_model)`: Suggests working alternatives for invalid models
52
+ - `update_model_status(model, status)`: Updates model availability status
53
+
54
+ **Validation Process**:
55
+ 1. Send minimal test request to provider API
56
+ 2. Check response for success/error patterns
57
+ 3. Update model status based on results
58
+ 4. Log validation results with timestamps
59
+
60
+ ### ModelHealthMonitor
61
+
62
+ **Purpose**: Continuously monitors model health and availability
63
+
64
+ **Key Methods**:
65
+ - `start_monitoring()`: Begins periodic health checks
66
+ - `check_model_health(model)`: Performs health check on specific model
67
+ - `handle_model_failure(model, error)`: Responds to model failures
68
+ - `get_health_report()`: Returns comprehensive health status
69
+
70
+ **Monitoring Strategy**:
71
+ - Periodic health checks every 5 minutes
72
+ - Immediate checks after failures
73
+ - Exponential backoff for failed models
74
+ - Automatic recovery detection
75
+
76
+ ### FallbackChain
77
+
78
+ **Purpose**: Manages automatic fallback to alternative models
79
+
80
+ **Fallback Priority**:
81
+ 1. **Primary Models**: User-configured preferred models
82
+ 2. **Secondary Models**: Validated working alternatives
83
+ 3. **Emergency Models**: Always-available simple response system
84
+
85
+ **Fallback Logic**:
86
+ ```
87
+ Primary Model → Secondary Models → Emergency Response
88
+ ↓ ↓ ↓
89
+ Full AI Reduced AI Rule-based
90
+ Response Response Response
91
+ ```
92
+
93
+ ### ModelConfigStore
94
+
95
+ **Purpose**: Centralized configuration management
96
+
97
+ **Configuration Structure**:
98
+ ```json
99
+ {
100
+ "providers": {
101
+ "openrouter": {
102
+ "api_key": "...",
103
+ "models": {
104
+ "primary": "meta-llama/llama-3.2-3b-instruct:free",
105
+ "fallbacks": [
106
+ "meta-llama/llama-3.1-8b-instruct:free",
107
+ "google/gemma-2-9b-it:free"
108
+ ]
109
+ }
110
+ },
111
+ "groq": {
112
+ "api_key": "...",
113
+ "models": {
114
+ "primary": "llama-3.3-70b-versatile",
115
+ "fallbacks": ["mixtral-8x7b-32768"]
116
+ }
117
+ }
118
+ }
119
+ }
120
+ ```
121
+
122
+ ## Data Models
123
+
124
+ ### ModelStatus
125
+ ```python
126
+ @dataclass
127
+ class ModelStatus:
128
+ provider: str
129
+ model_id: str
130
+ status: str # 'available', 'unavailable', 'quota_exceeded', 'error'
131
+ last_checked: datetime
132
+ error_message: Optional[str]
133
+ response_time_ms: Optional[int]
134
+ success_rate: float
135
+ ```
136
+
137
+ ### ProviderConfig
138
+ ```python
139
+ @dataclass
140
+ class ProviderConfig:
141
+ name: str
142
+ api_key: str
143
+ base_url: str
144
+ primary_models: List[str]
145
+ fallback_models: List[str]
146
+ timeout_seconds: int = 30
147
+ retry_attempts: int = 3
148
+ ```
149
+
150
+ ### ValidationResult
151
+ ```python
152
+ @dataclass
153
+ class ValidationResult:
154
+ model_id: str
155
+ is_valid: bool
156
+ error_message: Optional[str]
157
+ suggested_alternatives: List[str]
158
+ validation_timestamp: datetime
159
+ ```
160
+
161
+ ## Error Handling
162
+
163
+ ### Error Categories
164
+
165
+ 1. **Configuration Errors**: Invalid API keys, malformed model IDs
166
+ 2. **Network Errors**: Timeout, connection failures
167
+ 3. **Provider Errors**: Rate limits, quota exceeded, model unavailable
168
+ 4. **Validation Errors**: Model not found, unsupported parameters
169
+
170
+ ### Error Response Strategy
171
+
172
+ 1. **Immediate Fallback**: Switch to next available model
173
+ 2. **User Notification**: Inform user of fallback with clear messaging
174
+ 3. **Automatic Recovery**: Retry failed models after cooldown period
175
+ 4. **Graceful Degradation**: Provide simple responses when all AI fails
176
+
177
+ ### Error Logging
178
+
179
+ - Structured logging with error categories
180
+ - Performance metrics tracking
181
+ - User-friendly error messages
182
+ - Detailed technical logs for debugging
183
+
184
+ ## Testing Strategy
185
+
186
+ ### Unit Tests
187
+ - Model validation logic
188
+ - Fallback chain behavior
189
+ - Configuration parsing
190
+ - Error handling scenarios
191
+
192
+ ### Integration Tests
193
+ - End-to-end model requests
194
+ - Provider API interactions
195
+ - Fallback mechanisms
196
+ - Configuration updates
197
+
198
+ ### Performance Tests
199
+ - Response time monitoring
200
+ - Concurrent request handling
201
+ - Memory usage optimization
202
+ - Fallback performance impact
203
+
204
+ ### User Acceptance Tests
205
+ - Model status visibility
206
+ - Error message clarity
207
+ - Fallback transparency
208
+ - Configuration management UI
209
+
210
+ ## Implementation Phases
211
+
212
+ ### Phase 1: Core Infrastructure
213
+ - ModelManager implementation
214
+ - Basic validation system
215
+ - Simple fallback mechanism
216
+
217
+ ### Phase 2: Advanced Features
218
+ - Health monitoring
219
+ - Comprehensive error handling
220
+ - Performance optimization
221
+
222
+ ### Phase 3: User Experience
223
+ - Status dashboard
224
+ - Configuration UI
225
+ - Advanced monitoring features
.kiro/specs/model-management/requirements.md ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Requirements Document
2
+
3
+ ## Introduction
4
+
5
+ This feature aims to create a robust model management system that prevents invalid model configurations, provides automatic fallback mechanisms, and ensures reliable AI service availability. The system should handle model validation, automatic updates, and graceful error handling to prevent service disruptions.
6
+
7
+ ## Requirements
8
+
9
+ ### Requirement 1
10
+
11
+ **User Story:** As a system administrator, I want automatic model validation so that invalid model configurations are detected and corrected before they cause service failures.
12
+
13
+ #### Acceptance Criteria
14
+
15
+ 1. WHEN the system starts THEN it SHALL validate all configured AI models against their respective service APIs
16
+ 2. WHEN an invalid model is detected THEN the system SHALL automatically replace it with a known working fallback model
17
+ 3. WHEN model validation fails THEN the system SHALL log the error and continue with fallback models
18
+ 4. WHEN a model becomes unavailable THEN the system SHALL automatically switch to the next available model in the priority list
19
+
20
+ ### Requirement 2
21
+
22
+ **User Story:** As a developer, I want centralized model configuration management so that all AI services use consistent and validated model settings.
23
+
24
+ #### Acceptance Criteria
25
+
26
+ 1. WHEN the application initializes THEN it SHALL load model configurations from a centralized configuration system
27
+ 2. WHEN model configurations are updated THEN all AI services SHALL automatically use the new configurations
28
+ 3. WHEN a service requests a model THEN the system SHALL provide the most appropriate available model based on priority and availability
29
+ 4. WHEN model configurations are invalid THEN the system SHALL provide clear error messages and suggested fixes
30
+
31
+ ### Requirement 3
32
+
33
+ **User Story:** As an end user, I want transparent model status information so that I understand which AI services are available and their current status.
34
+
35
+ #### Acceptance Criteria
36
+
37
+ 1. WHEN I access the application THEN I SHALL see the current status of all AI models (available, unavailable, quota exceeded, etc.)
38
+ 2. WHEN a model fails THEN I SHALL receive a clear notification about the fallback being used
39
+ 3. WHEN model status changes THEN the UI SHALL update to reflect the current availability
40
+ 4. WHEN I encounter an AI error THEN I SHALL receive helpful information about alternative options
41
+
42
+ ### Requirement 4
43
+
44
+ **User Story:** As a system operator, I want automatic model health monitoring so that model issues are detected and resolved proactively.
45
+
46
+ #### Acceptance Criteria
47
+
48
+ 1. WHEN the system is running THEN it SHALL periodically check the health of all configured AI models
49
+ 2. WHEN a model health check fails THEN the system SHALL attempt to use alternative models
50
+ 3. WHEN all models for a service fail THEN the system SHALL provide graceful degradation with simple responses
51
+ 4. WHEN model health is restored THEN the system SHALL automatically resume using the preferred models
52
+
53
+ ### Requirement 5
54
+
55
+ **User Story:** As a developer, I want comprehensive error handling and logging so that model-related issues can be quickly diagnosed and resolved.
56
+
57
+ #### Acceptance Criteria
58
+
59
+ 1. WHEN a model error occurs THEN the system SHALL log detailed error information including model name, error type, and suggested resolution
60
+ 2. WHEN fallback models are used THEN the system SHALL log the fallback chain and reasons for each fallback
61
+ 3. WHEN model configurations are automatically corrected THEN the system SHALL log the changes made
62
+ 4. WHEN users encounter model errors THEN they SHALL receive user-friendly error messages with actionable guidance
.kiro/specs/model-management/tasks.md ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Implementation Plan
2
+
3
+ - [ ] 1. Create core model management infrastructure
4
+ - Implement ModelStatus and ProviderConfig data classes
5
+ - Create ModelConfigStore for centralized configuration management
6
+ - Set up basic logging and error handling framework
7
+ - _Requirements: 2.1, 2.2, 5.1_
8
+
9
+ - [ ] 2. Implement ModelValidator component
10
+ - Create ModelValidator class with validation methods
11
+ - Implement validate_model() method for individual model testing
12
+ - Add validate_all_models() method for batch validation
13
+ - Create suggest_alternatives() method for fallback recommendations
14
+ - Write unit tests for validation logic
15
+ - _Requirements: 1.1, 1.2, 1.3_
16
+
17
+ - [ ] 3. Build FallbackChain system
18
+ - Implement FallbackChain class with priority-based model selection
19
+ - Create fallback logic that tries primary, secondary, and emergency models
20
+ - Add automatic model switching when failures occur
21
+ - Implement graceful degradation to simple responses
22
+ - Write tests for fallback scenarios
23
+ - _Requirements: 1.4, 4.2, 4.3_
24
+
25
+ - [ ] 4. Create ModelManager central coordinator
26
+ - Implement ModelManager class as main interface
27
+ - Add get_available_model() method for model selection
28
+ - Create execute_with_fallback() method for request handling
29
+ - Implement get_system_status() for status reporting
30
+ - Add refresh_configurations() for dynamic config updates
31
+ - Write integration tests for ModelManager
32
+ - _Requirements: 2.3, 3.2, 4.1_
33
+
34
+ - [ ] 5. Implement ModelHealthMonitor
35
+ - Create ModelHealthMonitor class for continuous monitoring
36
+ - Add periodic health check functionality (5-minute intervals)
37
+ - Implement check_model_health() for individual model testing
38
+ - Create handle_model_failure() for failure response
39
+ - Add exponential backoff for failed models
40
+ - Write tests for health monitoring scenarios
41
+ - _Requirements: 4.1, 4.2, 4.4_
42
+
43
+ - [ ] 6. Build StatusReporter for user feedback
44
+ - Create StatusReporter class for user-facing status information
45
+ - Implement real-time status updates for UI
46
+ - Add user-friendly error message generation
47
+ - Create status dashboard data formatting
48
+ - Implement notification system for model changes
49
+ - Write tests for status reporting functionality
50
+ - _Requirements: 3.1, 3.2, 3.3, 3.4_
51
+
52
+ - [ ] 7. Integrate with existing AI services
53
+ - Update AITranslator to use ModelManager
54
+ - Modify AIQuestionEngine to use new model management
55
+ - Update all AI service calls to use execute_with_fallback()
56
+ - Replace hardcoded model configurations with centralized config
57
+ - Add model status display to existing UI components
58
+ - _Requirements: 2.2, 2.3, 3.1_
59
+
60
+ - [ ] 8. Implement comprehensive error handling
61
+ - Add structured error logging with categories
62
+ - Create user-friendly error messages for each error type
63
+ - Implement automatic error recovery mechanisms
64
+ - Add performance metrics tracking
65
+ - Create error reporting dashboard
66
+ - Write tests for all error scenarios
67
+ - _Requirements: 5.1, 5.2, 5.3, 5.4_
68
+
69
+ - [ ] 9. Add configuration management features
70
+ - Create configuration validation on startup
71
+ - Implement automatic model configuration updates
72
+ - Add configuration backup and restore functionality
73
+ - Create configuration migration tools for updates
74
+ - Add configuration validation UI
75
+ - Write tests for configuration management
76
+ - _Requirements: 2.1, 2.4, 1.2_
77
+
78
+ - [ ] 10. Create monitoring and analytics
79
+ - Implement performance metrics collection
80
+ - Add model usage statistics tracking
81
+ - Create health monitoring dashboard
82
+ - Add alerting for critical model failures
83
+ - Implement trend analysis for model performance
84
+ - Write tests for monitoring functionality
85
+ - _Requirements: 4.1, 4.4, 5.1_
86
+
87
+ - [ ] 11. Build user interface components
88
+ - Create model status display widget
89
+ - Add model selection interface for users
90
+ - Implement error notification system
91
+ - Create configuration management UI
92
+ - Add health monitoring dashboard
93
+ - Write UI tests for all components
94
+ - _Requirements: 3.1, 3.2, 3.3, 3.4_
95
+
96
+ - [ ] 12. Implement final integration and testing
97
+ - Integrate all components into main application
98
+ - Run comprehensive end-to-end tests
99
+ - Perform load testing with multiple concurrent requests
100
+ - Test all fallback scenarios under various failure conditions
101
+ - Validate user experience with real-world usage patterns
102
+ - Create deployment and maintenance documentation
103
+ - _Requirements: All requirements validation_
Dockerfile ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # -- Dockerfile for Streamlit app --
3
+ #
4
+
5
+ # Base image
6
+ FROM python:3.9-slim
7
+
8
+ # Set working directory
9
+ WORKDIR /app
10
+
11
+ # Install system dependencies (including ffmpeg)
12
+ RUN apt-get update && apt-get install -y \
13
+ build-essential \
14
+ ffmpeg \
15
+ && rm -rf /var/lib/apt/lists/*
16
+
17
+ # Copy requirements file
18
+ COPY requirements.txt ./requirements.txt
19
+
20
+ # Install Python dependencies
21
+ RUN pip install --no-cache-dir --upgrade pip
22
+ RUN pip install --no-cache-dir -r requirements.txt
23
+
24
+ # Copy the entire app
25
+ COPY . .
26
+
27
+ # Create .streamlit directory and set permissions
28
+ RUN mkdir -p /app/.streamlit && \
29
+ chmod -R 755 /app/.streamlit
30
+
31
+ # Set environment variable for Streamlit config
32
+ ENV STREAMLIT_CONFIG_DIR=/app/.streamlit
33
+
34
+ # Expose the port that Streamlit runs on
35
+ EXPOSE 8501
36
+
37
+ # Add a health check
38
+ HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
39
+
40
+ # Command to run the app
41
+ ENTRYPOINT ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]
FIX_GOOGLE_ACCESS.md ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🔧 حل مشكلة Google Access
2
+
3
+ ## 🎯 المشكلة:
4
+ ```
5
+ Error 403: access_denied
6
+ SyncMaster Export has not completed the Google verification process
7
+ ```
8
+
9
+ ## ✅ الحل السريع:
10
+
11
+ ### 1️⃣ إضافة نفسك كـ Test User:
12
+ 1. اذهب إلى [Google Cloud Console](https://console.cloud.google.com/)
13
+ 2. اختر مشروع `syncmaster-export`
14
+ 3. اذهب إلى: **APIs & Services** → **OAuth consent screen**
15
+ 4. اضغط على **"ADD USERS"** في قسم "Test users"
16
+ 5. أضف إيميل Google الخاص بك
17
+ 6. اضغط **"SAVE"**
18
+
19
+ ### 2️⃣ أو: نشر التطبيق للعامة (أسرع):
20
+ 1. في نفس صفحة **OAuth consent screen**
21
+ 2. اضغط **"PUBLISH APP"**
22
+ 3. اضغط **"CONFIRM"**
23
+ 4. الآن يمكن لأي شخص استخدام التطبيق
24
+
25
+ ## 🔧 حل مشكلة المنفذ:
26
+
27
+ المشكلة أن Streamlit يستخدم المنفذ 8501، لذلك سنغير المنفذ للمصادقة.
GOOGLE_SETUP.md ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # إعداد Google Docs للتصدير المباشر
2
+
3
+ ## الخطوات المطلوبة:
4
+
5
+ ### 1. إنشاء مشروع Google Cloud
6
+ 1. اذهب إلى [Google Cloud Console](https://console.cloud.google.com/)
7
+ 2. أنشئ مشروع جديد أو اختر مشروع موجود
8
+ 3. اكتب اسم المشروع (مثل: "SyncMaster Export")
9
+
10
+ ### 2. تفعيل Google Docs API
11
+ 1. في القائمة الجانبية، اذهب إلى "APIs & Services" > "Library"
12
+ 2. ابحث عن "Google Docs API"
13
+ 3. اضغط على "Enable"
14
+
15
+ ### 3. إنشاء بيانات الاعتماد
16
+ 1. اذهب إلى "APIs & Services" > "Credentials"
17
+ 2. اضغط على "Create Credentials" > "OAuth 2.0 Client ID"
18
+ 3. إذا لم تكن قد أعددت شاشة الموافقة، ستحتاج لإعدادها:
19
+ - اختر "External" للمستخدمين العاديين
20
+ - املأ المعلومات المطلوبة (اسم التطبيق، إيميل الدعم)
21
+ - أضف نطاقات Google Docs
22
+ 4. اختر "Desktop application" كنوع التطبيق
23
+ 5. اكتب اسم العميل (مثل: "SyncMaster Desktop")
24
+ 6. اضغط "Create"
25
+
26
+ ### 4. تحميل ملف بيانات الاعتماد
27
+ 1. بعد إنشاء بيانات الاعتماد، اضغط على أيقونة التحميل
28
+ 2. احفظ الملف باسم `credentials.json`
29
+ 3. ضع الملف في نفس مجلد التطبيق
30
+
31
+ ### 5. تشغيل التطبيق
32
+ 1. شغل التطبيق: `streamlit run app.py`
33
+ 2. اضغط على زر "📤 تصدير إلى Google Docs"
34
+ 3. ستفتح نافذة متصفح للمصادقة مع Google
35
+ 4. سجل دخول بحساب Google الخاص بك
36
+ 5. امنح الصلاحيات المطلوبة
37
+ 6. ارجع للتطبيق وستجد رابط المستند الجديد
38
+
39
+ ## ملاحظات مهمة:
40
+ - يتم حفظ بيانات المصادقة في ملف `token.json` لاستخدامها لاحقاً
41
+ - لا تشارك ملفات `credentials.json` أو `token.json` مع أحد
42
+ - يمكنك إلغاء الصلاحيات من إعدادات حساب Google في أي وقت
43
+
44
+ ## استكشاف الأخطاء:
45
+ - إذا ظهر خطأ "credentials.json not found"، تأكد من وضع الملف في المجلد الصحيح
46
+ - إذا فشلت المصادقة، احذف ملف `token.json` وحاول مرة أخرى
47
+ - تأكد من تفعيل Google Docs API في مشروعك
GOOGLE_SETUP_EASY.md ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🚀 إعداد Google Docs - الطريقة السهلة
2
+
3
+ ## 📝 الإجابات على أسئلتك:
4
+
5
+ ### ❓ ماذا أدخل في هذه الحقول؟
6
+ ```
7
+ Authorised JavaScript origins: اتركه فارغ (لا تدخل شيء)
8
+ Authorised redirect URIs: اتركه فارغ (لا تدخل شيء)
9
+ ```
10
+ **السبب**: نحن ننشئ تطبيق Desktop وليس Web، لذلك لا نحتاج هذه الحقول.
11
+
12
+ ### ❓ لم أستطع الوصول إلى صفحة Scopes؟
13
+ **الحل**: لا تقلق! يمكنك تخطي إضافة الـ Scopes يدوياً. التطبيق سيطلبها تلقائياً.
14
+
15
+ ---
16
+
17
+ ## 🎯 الطريقة المبسطة (5 دقائق فقط):
18
+
19
+ ### 1️⃣ اذهب إلى Google Cloud Console
20
+ 🔗 **الرابط**: https://console.cloud.google.com/
21
+
22
+ ### 2️⃣ أنشئ مشروع جديد
23
+ - اضغط "Select a project" → "NEW PROJECT"
24
+ - اسم المشروع: `SyncMaster`
25
+ - اضغط "CREATE"
26
+
27
+ ### 3️⃣ فعّل Google Docs API
28
+ - من القائمة الجانبية: "APIs & Services" → "Library"
29
+ - ابحث عن: `Google Docs API`
30
+ - اضغط على النتيجة الأولى → "ENABLE"
31
+
32
+ ### 4️⃣ إعداد OAuth Consent Screen (مبسط)
33
+ - اذهب إلى: "APIs & Services" → "OAuth consent screen"
34
+ - اختر "External" → "CREATE"
35
+ - املأ فقط:
36
+ - **App name**: `SyncMaster`
37
+ - **User support email**: إيميلك
38
+ - **Developer contact information**: إيميلك
39
+ - اضغط "SAVE AND CONTINUE" في جميع الصفحات (لا تغير شيء آخر)
40
+
41
+ ### 5️⃣ إنشاء Client ID
42
+ - اذهب إلى: "APIs & Services" → "Credentials"
43
+ - اضغط "+ CREATE CREDENTIALS" → "OAuth 2.0 Client ID"
44
+ - اختر "Desktop application"
45
+ - الاسم: `SyncMaster Desktop`
46
+ - **اترك جميع الحقول الأخرى فارغة**
47
+ - اضغط "CREATE"
48
+
49
+ ### 6️⃣ تحميل الملف
50
+ - ستظهر نافذة منبثقة
51
+ - اضغط "DOWNLOAD JSON"
52
+ - احفظ الملف باسم `credentials.json`
53
+ - ضعه في مجلد التطبيق (نفس مكان app.py)
54
+
55
+ ### 7️⃣ اختبار
56
+ ```bash
57
+ python check_credentials.py
58
+ ```
59
+ يجب أن ترى: ✅ ملف بيانات الاعتماد صحيح!
60
+
61
+ ---
62
+
63
+ ## 🎉 الآن جرب الزر!
64
+ - شغل التطبيق
65
+ - اضغط "📤 تصدير إلى Google Docs"
66
+ - ستفتح نافذة متصفح
67
+ - سجل دخول بحساب Google
68
+ - اضغط "Allow" لمنح الصلاحيات
69
+ - ستحصل على رابط المستند!
70
+
71
+ ---
72
+
73
+ ## 🔧 إذا ظهر تحذير "App isn't verified":
74
+ هذا طبيعي! اضغط:
75
+ 1. "Advanced"
76
+ 2. "Go to SyncMaster (unsafe)"
77
+ 3. "Allow"
78
+
79
+ ---
80
+
81
+ ## 📞 مازلت تواجه مشاكل؟
82
+ أرسل لي لقطة شاشة من الخطأ وسأساعدك فوراً!
GOOGLE_SETUP_SIMPLE.md ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🚀 إعداد Google Docs - دليل مبسط
2
+
3
+ ## ❌ المشكلة الحالية:
4
+ ```
5
+ Error 401: invalid_client
6
+ The OAuth client was not found.
7
+ ```
8
+
9
+ ## ✅ الحل السريع:
10
+
11
+ ### الخطوة 1: اذهب إلى Google Cloud Console
12
+ 🔗 **الرابط المباشر**: https://console.cloud.google.com/
13
+
14
+ ### الخطوة 2: إنشاء مشروع جديد
15
+ 1. اضغط على "Select a project" في الأعلى
16
+ 2. اضغط على "NEW PROJECT"
17
+ 3. اكتب اسم المشروع: `SyncMaster Export`
18
+ 4. اضغط "CREATE"
19
+
20
+ ### الخطوة 3: تفعيل Google Docs API
21
+ 1. في القائمة الجانبية ← "APIs & Services" ← "Library"
22
+ 2. ابحث عن: `Google Docs API`
23
+ 3. اضغط على النتيجة الأولى
24
+ 4. اضغط "ENABLE"
25
+
26
+ ### الخطوة 4: إعداد OAuth Consent Screen
27
+ 1. اذهب إلى "APIs & Services" ← "OAuth consent screen"
28
+ 2. اختر "External"
29
+ 3. اضغط "CREATE"
30
+ 4. املأ المعلومات المطلوبة:
31
+ - **App name**: `SyncMaster Export`
32
+ - **User support email**: إيميلك
33
+ - **Developer contact information**: إيميلك
34
+ 5. اضغط "SAVE AND CONTINUE"
35
+ 6. **في صفحة "Scopes"**:
36
+ - **لا تضيف أي Scopes يدوياً**
37
+ - فقط اضغط "SAVE AND CONTINUE" مباشرة
38
+ - (التطبيق سيطلب الصلاحيات تلقائياً عند الاستخدام)
39
+ 7. في صفحة "Test users": اضغط "SAVE AND CONTINUE"
40
+ 8. في صفحة "Summary": اضغط "BACK TO DASHBOARD"
41
+
42
+ ### الخطوة 5: إنشاء OAuth 2.0 Client ID
43
+ 1. اذهب إلى "APIs & Services" ← "Credentials"
44
+ 2. اضغط "+ CREATE CREDENTIALS" ← "OAuth 2.0 Client ID"
45
+ 3. اختر "Desktop application"
46
+ 4. اكتب الاسم: `SyncMaster Desktop`
47
+ 5. **اترك الحقول فارغة**:
48
+ - **Authorised JavaScript origins**: اتركه فارغ (لا تدخل شيء)
49
+ - **Authorised redirect URIs**: اتركه فارغ (لا تدخل شيء)
50
+ 6. اضغط "CREATE"
51
+
52
+ ### الخطوة 6: تحميل ملف البيانات
53
+ 1. ستظهر نافذة منبثقة مع Client ID و Client Secret
54
+ 2. اضغط "DOWNLOAD JSON"
55
+ 3. احفظ الملف باسم `credentials.json`
56
+ 4. انسخ الملف إلى مجلد التطبيق (نفس مجلد app.py)
57
+
58
+ ### الخطوة 7: اختبار التطبيق
59
+ 1. احذف ملف `token.json` إذا كان موجوداً
60
+ 2. شغل التطبيق
61
+ 3. اضغط على زر "📤 تصدير إلى Google Docs"
62
+ 4. ستفتح نافذة متصفح للمصادقة
63
+ 5. سجل دخول بحساب Google
64
+ 6. اضغط "Allow" لمنح الصلاحيات
65
+
66
+ ## 🎯 نصائح مهمة:
67
+ - **استخدم نفس حساب Google** الذي أنشأت به المشروع
68
+ - **لا تشارك ملف credentials.json** مع أحد
69
+ - **إذا ظهر تحذير "App isn't verified"** اضغط "Advanced" ثم "Go to SyncMaster Export (unsafe)"
70
+
71
+ ## 🔧 استكشاف الأخطاء:
72
+
73
+ ### إذا ظهر "invalid_client":
74
+ - تأكد من استبدال ملف `credentials.json` بالملف الحقيقي من Google
75
+ - تأكد من أن الملف في نفس مجلد `app.py`
76
+
77
+ ### إذا ظهر "access_denied":
78
+ - تأكد من الضغط على "Allow" في صفحة المصادقة
79
+ - تأكد من تسجيل الدخول بنفس حساب Google الذي أنشأت به المشروع
80
+
81
+ ### إذا ظهر "redirect_uri_mismatch":
82
+ - احذف ملف `token.json` وحاول مرة أخرى
83
+
84
+ ## 📞 إذا احتجت مساعدة:
85
+ أرسل لي لقطة شاشة من الخطأ وسأساعدك في حله!
INTEGRATION_SOLUTION.md ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SyncMaster - Integrated Setup
2
+
3
+ ## 🚀 التشغيل المبسط (HuggingFace Ready)
4
+
5
+ الآن يمكنك تشغيل التطبيق بأمر واحد فقط:
6
+
7
+ ```bash
8
+ npm run dev
9
+ ```
10
+
11
+ أو
12
+
13
+ ```bash
14
+ npm start
15
+ ```
16
+
17
+ ## 🔧 كيف تم حل المشكلة
18
+
19
+ ### المشكلة السابقة:
20
+ - كان يتطلب تشغيل `python recorder_server.py` و `npm run dev` بشكل منفصل
21
+ - غير مناسب للنشر على HuggingFace أو المنصات السحابية
22
+
23
+ ### الحل الجديد:
24
+ 1. **خادم متكامل**: تم إنشاء `integrated_server.py` الذي يشغل خادم التسجيل تلقائياً
25
+ 2. **نقطة دخول موحدة**: ملف `main.py` يبدأ كل شيء معاً
26
+ 3. **تكوين ذكي**: يكتشف البيئة تلقائياً (محلي أو سحابي)
27
+
28
+ ## 📁 الملفات الجديدة
29
+
30
+ - `integrated_server.py` - يدير خادم التسجيل المدمج
31
+ - `main.py` - نقطة الدخول الرئيسية
32
+ - `app_config.py` - إعدادات التطبيق
33
+ - `startup.py` - مُشغل متقدم للتطوير
34
+
35
+ ## 🎯 للاستخدام العادي
36
+
37
+ ```bash
38
+ # تشغيل التطبيق (يشمل خادم التسجيل)
39
+ npm run dev
40
+
41
+ # أو استخدام Python مباشرة
42
+ streamlit run main.py
43
+ ```
44
+
45
+ ## ⚙️ للتطوير المتقدم
46
+
47
+ ```bash
48
+ # تشغيل الخوادم بشكل منفصل (للتطوير)
49
+ npm run dev-separate
50
+ ```
51
+
52
+ ## 🌐 للنشر على HuggingFace
53
+
54
+ فقط ارفع المشروع واستخدم:
55
+ - **Command**: `npm run start`
56
+ - **Port**: `5050`
57
+
58
+ سيتم تشغيل خادم التسجيل تلقائياً في الخلفية!
59
+
60
+ ## ✅ اختبار النظام
61
+
62
+ ```bash
63
+ python integrated_server.py
64
+ ```
65
+
66
+ ## 🎉 النتيجة
67
+
68
+ - **✅ تشغيل بأمر واحد فقط**
69
+ - **✅ جاهز للنشر على HuggingFace**
70
+ - **✅ يعمل محلياً وسحابياً**
71
+ - **✅ لا حاجة لتشغيل أوامر متعددة**
72
+
73
+ ---
74
+
75
+ المشكلة محلولة! الآن يمكنك استخدام `npm run dev` فقط وسيعمل كل شيء تلقائياً 🎊
PERFORMANCE_IMPROVEMENTS.md ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🚀 تحسينات الأداء - مشكلة الشاشة البيضاء محلولة
2
+
3
+ ## 🔍 التحليل والمشكلة:
4
+ كانت المشكلة أن خادم التسجيل يبدأ **بشكل متزامن** عند تحميل الصفحة، مما يسبب:
5
+ - ⏰ تأخير في التحميل (نصف ثانية إلى ثانية)
6
+ - ⚪ شاشة بيضاء أثناء انتظار بدء الخادم
7
+ - 🐌 تجربة مستخدم بطيئة
8
+
9
+ ## ✅ الحلول المطبقة:
10
+
11
+ ### 1. **تشغيل غير متزامن للخادم**
12
+ ```python
13
+ # بدلاً من:
14
+ ensure_recorder_server() # يحجب الواجهة
15
+
16
+ # الآن:
17
+ recorder_thread = threading.Thread(target=start_recorder_async, daemon=True)
18
+ recorder_thread.start() # لا يحجب الواجهة
19
+ ```
20
+
21
+ ### 2. **تسريع فحص الاستجابة**
22
+ ```python
23
+ # قبل: timeout=3 ثوان
24
+ # الآن: timeout=0.5 ثانية
25
+ response = requests.get(url, timeout=0.5)
26
+ ```
27
+
28
+ ### 3. **تحسين انتظار بدء الخادم**
29
+ ```python
30
+ # قبل: sleep(1) × 10 مرات = 10 ثوان
31
+ # الآن: sleep(0.5) × 15 مرة = 7.5 ثانية
32
+ time.sleep(0.5)
33
+ ```
34
+
35
+ ### 4. **تحسين CSS لمنع الفلاش**
36
+ ```css
37
+ .main .block-container {
38
+ animation: fadeIn 0.2s ease-in-out;
39
+ }
40
+ .stSpinner { display: none !important; }
41
+ ```
42
+
43
+ ### 5. **فحص ذكي للخادم**
44
+ ```python
45
+ # فحص سريع أولاً
46
+ if integrated_server.is_server_responding():
47
+ return True # خروج فوري إذا كان يعمل
48
+ ```
49
+
50
+ ## 📊 النتائج:
51
+
52
+ ### قبل التحسين:
53
+ - ⏱️ **تحميل الصفحة**: 1+ ثانية
54
+ - ⚪ **شاشة بيضاء**: نعم
55
+ - 🔄 **تأخير ملحوظ**: نعم
56
+
57
+ ### بعد التحسين:
58
+ - ⏱️ **تحميل الصفحة**: 0.008-0.023 ثانية
59
+ - ⚪ **شاشة بيضاء**: لا
60
+ - ⚡ **تحميل فوري**: نعم
61
+
62
+ ## 🎯 التحسينات الإضافية:
63
+
64
+ 1. **عدم عرض رسائل تحميل غير ضرورية**
65
+ 2. **بدء الخادم في الخلفية فقط عند الحاجة**
66
+ 3. **تقليل عدد رسائل السجل**
67
+ 4. **تحسين CSS للانتقالات السلسة**
68
+
69
+ ## 🚀 النتيجة النهائية:
70
+
71
+ ✅ **لا مزيد من الشاشة البيضاء**
72
+ ✅ **تحميل فوري للمحتوى**
73
+ ✅ **تجربة مستخدم سلسة**
74
+ ✅ **أداء ممتاز (0.008 ثانية)**
75
+
76
+ **المشكلة محلولة تماماً!** 🎊
QUICK_START.md ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🎯 دليل الإصلاح والتشغيل السريع - SyncMaster Enhanced
2
+ # Quick Fix and Startup Guide - SyncMaster Enhanced
3
+
4
+ ## ✅ النظام جاهز للعمل! / System Ready!
5
+
6
+ تم اختبار جميع المكونات بنجاح ✅ All components tested successfully
7
+
8
+ ## 🚀 طرق التشغيل / Startup Methods
9
+
10
+ ### 1. التشغيل التلقائي المتقدم / Advanced Auto-Start (موصى به / Recommended)
11
+ ```bash
12
+ python start_debug.py
13
+ ```
14
+ **المزايا / Benefits:**
15
+ - فحص تلقائي للمشاكل / Automatic problem detection
16
+ - إصلاح تضارب المنافذ / Port conflict resolution
17
+ - رسائل خطأ واضحة / Clear error messages
18
+ - تشغيل آمن / Safe startup
19
+
20
+ ### 2. التشغيل اليدوي / Manual Startup
21
+ ```bash
22
+ # النافذة الأولى / First Terminal
23
+ python recorder_server.py
24
+
25
+ # النافذة الثانية / Second Terminal
26
+ streamlit run app.py --server.port 8501
27
+ ```
28
+
29
+ ### 3. التشغيل السريع / Quick Start (Windows)
30
+ ```bash
31
+ start_enhanced.bat
32
+ ```
33
+
34
+ ## 🌐 الروابط / URLs
35
+
36
+ بعد التشغيل الناجح / After successful startup:
37
+
38
+ - **🎙️ واجهة التسجيل / Recording Interface**: http://localhost:5001
39
+ - **💻 التطبيق الرئيسي / Main Application**: http://localhost:8501
40
+ - **🔄 فحص حالة الخادم / Server Status**: http://localhost:5001/record
41
+
42
+ ## 📋 خطوات الاستخدام / Usage Steps
43
+
44
+ ### للطلاب الجدد / For New Users:
45
+
46
+ #### 1. إعداد اللغة / Language Setup
47
+ - اختر اللغة المفضلة (عربي/English)
48
+ - فعّل الترجمة التلقائية
49
+ - اختر اللغة المستهدفة
50
+
51
+ #### 2. التسجيل / Recording
52
+ - اذهب لتبويب "🎙️ Record Audio"
53
+ - اضغط "Start Recording" / "بدء التسجيل"
54
+ - تحدث بوضوح
55
+ - استخدم "Mark Important" للنقاط المهمة
56
+ - اضغط "Stop" عند الانتهاء
57
+
58
+ #### 3. المعالجة / Processing
59
+ - اضغط "Extract Text" / "استخراج النص"
60
+ - انتظر المعالجة (قد تستغرق دقائق)
61
+ - راجع النص الأصلي والمترجم
62
+
63
+ #### 4. الحفظ / Saving
64
+ - انسخ النص المطلوب
65
+ - احفظ ملف JSON للمراجعة لاحقاً
66
+
67
+ ## 🔧 استكشاف الأخطاء / Troubleshooting
68
+
69
+ ### المشكلة الأكثر شيوعاً / Most Common Issue:
70
+ ```
71
+ Error: Failed to fetch
72
+ POST http://localhost:5001/record net::ERR_CONNECTION_REFUSED
73
+ ```
74
+
75
+ ### الحل السريع / Quick Fix:
76
+ ```bash
77
+ # 1. أوقف جميع العمليات / Stop all processes
78
+ taskkill /f /im python.exe
79
+
80
+ # 2. شغّل الاختبار / Run test
81
+ python test_system.py
82
+
83
+ # 3. شغّل النظام / Start system
84
+ python start_debug.py
85
+ ```
86
+
87
+ ### إذا لم يعمل / If Still Not Working:
88
+ ```bash
89
+ # فحص المنافذ / Check ports
90
+ netstat -an | findstr :5001
91
+ netstat -an | findstr :8501
92
+
93
+ # إعادة تثبيت التبعيات / Reinstall dependencies
94
+ pip install --upgrade -r requirements.txt
95
+ ```
96
+
97
+ ## 💡 نصائح مهمة / Important Tips
98
+
99
+ ### للحصول على أفضل النتائج / For Best Results:
100
+
101
+ #### جودة التسجيل / Recording Quality:
102
+ - استخدم سماعة رأس بميكروفون
103
+ - اجلس في مكان هادئ
104
+ - تحدث بوضوح وبطء نسبي
105
+ - تجنب الضوضاء الخلفية
106
+
107
+ #### إعدادات الترجمة / Translation Settings:
108
+ - **للطلاب العرب**: فعّل الترجمة للإنجليزية لفهم المصطلحات التقنية
109
+ - **للطلاب الدوليين**: استخدم الترجمة للغتك الأم
110
+ - **للمحاضرات المختلطة**: راجع النص بكلا اللغتين
111
+
112
+ #### استخدام العلامات / Using Markers:
113
+ - ضع علامة عند المفاهيم الجديدة
114
+ - اعلم النقاط المهمة للامتحان
115
+ - استخدم العلامات للتنظيم
116
+
117
+ ## 📱 متطلبات النظام / System Requirements
118
+
119
+ ### الحد الأدنى / Minimum:
120
+ - Python 3.8+
121
+ - 4 GB RAM
122
+ - اتصال إنترنت للترجمة
123
+ - مساحة 1 GB على القرص الصلب
124
+
125
+ ### الموصى به / Recommended:
126
+ - Python 3.10+
127
+ - 8 GB RAM
128
+ - اتصال إنترنت سريع
129
+ - SSD للتخزين
130
+ - ميكروفون عالي الجودة
131
+
132
+ ## 🌟 ميزات متقدمة / Advanced Features
133
+
134
+ ### اختصارات لوحة المفاتيح / Keyboard Shortcuts:
135
+ - **Space**: بدء/إيقاف التسجيل
136
+ - **M**: وضع علامة مهمة
137
+ - **P**: إيقاف مؤقت/استئناف
138
+ - **R**: إعادة تسجيل
139
+
140
+ ### واجهة برمجة التطبيقات / API Features:
141
+ - ترجمة نصوص مستقلة
142
+ - معالجة مجمعة للملفات
143
+ - كشف اللغة التلقائي
144
+ - تخصيص إعدادات الصوت
145
+
146
+ ## 📞 الدعم التقني / Technical Support
147
+
148
+ ### أدوات التشخيص / Diagnostic Tools:
149
+ ```bash
150
+ # اختبار شامل / Complete test
151
+ python test_system.py
152
+
153
+ # فحص الاتصال / Connection test
154
+ python -c "import requests; print(requests.get('http://localhost:5001/record').status_code)"
155
+
156
+ # اختبار الترجمة / Translation test
157
+ python -c "from translator import AITranslator; t=AITranslator(); print(t.translate_text('Hello', 'ar'))"
158
+ ```
159
+
160
+ ### ملفات السجل / Log Files:
161
+ - تحقق من console المتصفح (F12)
162
+ - راجع سجلات الطرفية
163
+ - ابحث عن ملفات tmp*.json
164
+
165
+ ## 🎓 للمدرسين والمحاضرين / For Teachers and Lecturers
166
+
167
+ ### إعدادات الفصل / Classroom Setup:
168
+ - تأكد من إذن التسجيل
169
+ - وضح للطلاب كيفية الاستخدام
170
+ - اقترح جلسات تدريبية
171
+
172
+ ### نصائح للمحاضرات / Lecture Tips:
173
+ - تحدث بوضوح
174
+ - اكرر المصطلحات المهمة
175
+ - استخدم فترات صمت قصيرة
176
+ - اشرح بعدة لغات إذا أمكن
177
+
178
+ ---
179
+
180
+ ## 🎉 مبروك! / Congratulations!
181
+
182
+ **النظام جاهز للاستخدام! / System is ready to use!**
183
+
184
+ ```bash
185
+ # للبدء الآن / To start now:
186
+ python start_debug.py
187
+ ```
188
+
189
+ **استمتع بتجربة تعليمية محسنة مع SyncMaster! 🚀**
190
+ **Enjoy an enhanced learning experience with SyncMaster! 🚀**
191
+
192
+ ---
193
+
194
+ ### 📋 Checklist
195
+
196
+ - ✅ Python مثبت / Python installed
197
+ - ✅ التبعيات مثبتة / Dependencies installed
198
+ - ✅ مفتاح API مُعد / API key configured
199
+ - ✅ اختبار النظام نجح / System test passed
200
+ - ✅ جاهز للاستخدام / Ready to use
201
+
202
+ **🎯 التالي: python start_debug.py**
README.md ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: SyncMaster Enhanced
3
+ emoji: 🚀
4
+ colorFrom: red
5
+ colorTo: red
6
+ sdk: docker
7
+ app_port: 8501
8
+ tags:
9
+ - streamlit
10
+ - ai-translation
11
+ - speech-to-text
12
+ - multilingual
13
+ - education
14
+ pinned: false
15
+ short_description: AI-powered audio transcription
16
+ license: mit
17
+ ---
18
+
19
+ # SyncMaster Enhanced - AI-Powered Audio Transcription & Translation
20
+
21
+ > **🌟 New: Enhanced with AI Translation Support for International Students**
22
+ > **جديد: محسن مع دعم الترجمة بالذكاء الاصطناعي للطلاب الدوليين**
23
+
24
+ SyncMaster is an intelligent audio-text synchronization platform specifically designed for international students in universities. It provides real-time audio recording, AI-powered transcription, and automatic translation to help students better understand and review their lectures.
25
+
26
+ ## ✨ Key Features
27
+
28
+ ### 🌐 Multi-Language Support
29
+ - **Full Arabic Interface**: Complete Arabic UI for better accessibility
30
+ - **AI-Powered Translation**: Automatic translation to Arabic, English, French, and Spanish
31
+ - **Language Detection**: Automatically detects the source language
32
+ - **Academic Context**: Specialized translation for academic content
33
+
34
+ ### 🎙️ Enhanced Recording
35
+ - **Browser-based Recording**: Record directly from your web browser
36
+ - **Real-time Audio Visualization**: Visual feedback during recording
37
+ - **Important Markers**: Mark important points during lectures
38
+ - **Pause/Resume**: Full control over recording sessions
39
+
40
+ ### 🤖 AI Technology
41
+ - **Gemini AI Integration**: Accurate transcription using Google's Gemini AI
42
+ - **Advanced Translation**: Context-aware translation for educational content
43
+ - **Parallel Processing**: Fast and efficient audio processing
44
+
45
+ ### 📱 Student-Friendly Features
46
+ - **Responsive Design**: Works on desktop, tablet, and mobile
47
+ - **Keyboard Shortcuts**: Quick access to common functions
48
+ - **Accessibility**: Screen reader support and RTL language support
49
+ - **Offline Capability**: Process recordings without constant internet
50
+
51
+ ## 🚀 Quick Start
52
+
53
+ ### For International Students:
54
+
55
+ 1. **Setup**:
56
+ ```bash
57
+ # Clone or download the project
58
+ # Install Python 3.8+
59
+ python setup_enhanced.py
60
+ ```
61
+
62
+ 2. **Run**:
63
+ ```bash
64
+ # Windows
65
+ start_enhanced.bat
66
+
67
+ # Linux/Mac
68
+ python setup_enhanced.py
69
+ ```
70
+
71
+ 3. **Configure**:
72
+ - Add your Gemini API key to `.env` file
73
+ - Choose your preferred language (Arabic/English)
74
+ - Enable translation and select target language
75
+
76
+ ### API Key Setup:
77
+ 1. Get a free Gemini API key from [Google AI Studio](https://makersuite.google.com/app/apikey)
78
+ 2. Add it to your `.env` file:
79
+ ```
80
+ GEMINI_API_KEY=your_api_key_here
81
+ ```
82
+
83
+ ## 📖 Usage Guide
84
+
85
+ ### Recording Lectures:
86
+ 1. Go to the **Record Audio** tab
87
+ 2. Click **Start Recording**
88
+ 3. Use **Mark Important** for key points
89
+ 4. Click **Stop** when finished
90
+ 5. Click **Extract Text** to process
91
+
92
+ ### Translation:
93
+ 1. Enable translation in settings
94
+ 2. Select target language
95
+ 3. Process your audio
96
+ 4. Review both original and translated text
97
+
98
+ ### Export Options:
99
+ - Copy text for notes
100
+ - Save as files for later review
101
+ - Generate synchronized videos (coming soon)
102
+
103
+ ## 🎓 For Students
104
+
105
+ ### Arabic Students (للطلاب العرب):
106
+ - استخدم الواجهة العربية لسهولة الاستخدام
107
+ - فعّل الترجمة للإنجليزية لفهم المصطلحات التقنية
108
+ - ضع علامات على المفاهيم الجديدة أثناء المحاضرة
109
+
110
+ ### International Students:
111
+ - Use translation to your native language for better understanding
112
+ - Mark important concepts during lectures
113
+ - Review both original and translated text together
114
+
115
+ ## ⌨️ Keyboard Shortcuts
116
+ - **Space**: Start/Stop recording
117
+ - **M**: Mark important point
118
+ - **P**: Pause/Resume
119
+ - **R**: Re-record
120
+
121
+ ## 🔧 Technical Requirements
122
+
123
+ ### System Requirements:
124
+ - Python 3.8 or higher
125
+ - Modern web browser (Chrome, Firefox, Safari, Edge)
126
+ - Microphone access for recording
127
+ - Internet connection for AI processing
128
+
129
+ ### Dependencies:
130
+ - Streamlit (Web interface)
131
+ - Google Generative AI (Transcription & Translation)
132
+ - Flask (Recording server)
133
+ - LibROSA (Audio processing)
134
+
135
+ ## 📱 Browser Compatibility
136
+
137
+ | Browser | Recording | Translation | UI |
138
+ |---------|-----------|-------------|----|
139
+ | Chrome | ✅ | ✅ | ✅ |
140
+ | Firefox | ✅ | ✅ | ✅ |
141
+ | Safari | ✅ | ✅ | ✅ |
142
+ | Edge | ✅ | ✅ | ✅ |
143
+
144
+ ## 🛠️ Troubleshooting
145
+
146
+ ### Common Issues:
147
+
148
+ **Microphone not working:**
149
+ - Grant microphone permission to your browser
150
+ - Check system audio settings
151
+ - Try a different browser
152
+
153
+ **Translation errors:**
154
+ - Check internet connection
155
+ - Verify Gemini API key
156
+ - Try processing again
157
+
158
+ **Poor transcription quality:**
159
+ - Ensure clear audio recording
160
+ - Reduce background noise
161
+ - Speak clearly and at moderate pace
162
+
163
+ ## 🔮 Roadmap
164
+
165
+ ### Coming Soon:
166
+ - **Smart Content Analysis**: Automatic extraction of key concepts
167
+ - **Study Cards**: Generate flashcards from lectures
168
+ - **Platform Integration**: Connect with Moodle, Canvas, etc.
169
+ - **Collaborative Features**: Share recordings with classmates
170
+ - **Advanced Analytics**: Learning progress tracking
171
+
172
+ ## 📚 Documentation
173
+
174
+ - [**Arabic Guide**](README_AR.md) - دليل باللغة العربية
175
+ - [**API Documentation**](docs/api.md) - Technical API reference
176
+ - [**Troubleshooting**](docs/troubleshooting.md) - Detailed problem solving
177
+
178
+ ## 🤝 Contributing
179
+
180
+ We welcome contributions from the international student community:
181
+
182
+ 1. Fork the repository
183
+ 2. Create a feature branch
184
+ 3. Add your improvements
185
+ 4. Submit a pull request
186
+
187
+ ### Areas for Contribution:
188
+ - Additional language support
189
+ - UI improvements
190
+ - Mobile optimization
191
+ - Documentation translation
192
+
193
+ ## 📄 License
194
+
195
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
196
+
197
+ ## 🙏 Acknowledgments
198
+
199
+ - Google Gemini AI for transcription and translation
200
+ - Streamlit team for the amazing web framework
201
+ - International student community for feedback and testing
202
+
203
+ ## 📞 Support
204
+
205
+ For technical support or questions:
206
+ - Check the browser console (F12) for error details
207
+ - Review log files in the application directory
208
+ - Ensure all dependencies are up to date
209
+
210
+ ---
211
+
212
+ **Made with ❤️ for international students worldwide**
213
+ **صُنع بـ ❤️ للطلاب الدوليين حول العالم**
214
+
215
+ ---
216
+
217
+ ### Quick Links:
218
+ - 🚀 [Quick Start Guide](docs/quickstart.md)
219
+ - 🌐 [Arabic Documentation](README_AR.md)
220
+ - 🎓 [Student Guide](docs/student-guide.md)
221
+ - 🔧 [Technical Setup](docs/technical-setup.md)
README_AR.md ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SyncMaster - دليل المستخدم للطلاب الأجانب
2
+
3
+ ## 🎯 نظرة عامة
4
+ SyncMaster هو تطبيق ذكي مطور خصيصاً للطلاب الأجانب في الجامعات لتسجيل المحاضرات وتحويلها إلى نص مكتوب مع ترجمة فورية باستخدام الذكاء الاصطناعي.
5
+
6
+ ## ✨ الميزات الجديدة
7
+
8
+ ### 🌐 دعم متعدد اللغات
9
+ - **واجهة عربية كاملة**: تم تطوير واجهة باللغة العربية لتسهيل الاستخدام
10
+ - **ترجمة فورية**: ترجمة النص المنسوخ إلى العربية والإنجليزية والفرنسية والإسبانية
11
+ - **كشف اللغة التلقائي**: يتعرف النظام على لغة المحاضرة تلقائياً
12
+
13
+ ### 🎙️ ميزات التسجيل المحسنة
14
+ - **تسجيل مباشر**: تسجيل المحاضرات مباشرة من المتصفح
15
+ - **علامات مهمة**: وضع علامات على النقاط المهمة أثناء التسجيل
16
+ - **مؤشر مستوى الصوت**: عرض مرئي لمستوى الصوت
17
+ - **إيقاف مؤقت واستئناف**: تحكم كامل في التسجيل
18
+
19
+ ### 🤖 ذكاء اصطناعي متطور
20
+ - **نسخ دقيق**: استخدام Gemini AI لنسخ دقيق للمحاضرات
21
+ - **ترجمة محسنة**: ترجمة متخصصة للمحتوى الأكاديمي
22
+ - **معالجة متوازية**: معالجة سريعة وفعالة
23
+
24
+ ## 🚀 كيفية الاستخدام
25
+
26
+ ### الخطوة 1: إعداد اللغة
27
+ 1. اختر لغة الواجهة من القائمة العلوية (العربية/English)
28
+ 2. فعّل الترجمة التلقائية
29
+ 3. اختر اللغة المستهدفة للترجمة
30
+
31
+ ### الخطوة 2: التسجيل
32
+ 1. اضغط على تبويب "🎙️ Record Audio"
33
+ 2. اضغط "Start Recording" لبدء التسجيل
34
+ 3. استخدم "Mark Important" لوضع علامات على النقاط المهمة
35
+ 4. اضغط "Stop" لإنهاء التسجيل
36
+
37
+ ### الخطوة 3: المعالجة والترجمة
38
+ 1. اضغط "Extract Text" لبدء المعالجة
39
+ 2. انتظر حتى يكتمل النسخ والترجمة
40
+ 3. راجع النص الأصلي والمترجم
41
+
42
+ ### الخطوة 4: التصدير
43
+ 1. احفظ النتائج أو انسخها
44
+ 2. استخدم الملف المحفوظ للمراجعة لاحقاً
45
+
46
+ ## ⌨️ اختصارات لوحة المفاتيح
47
+ - **Space**: بدء/إيقاف التسجيل
48
+ - **M**: وضع علامة مهمة
49
+ - **P**: إيقاف مؤقت/استئناف
50
+ - **R**: إعادة تسجيل
51
+
52
+ ## 📱 نصائح للطلاب الأجانب
53
+
54
+ ### للطلاب العرب:
55
+ - استخدم الواجهة العربية للسهولة
56
+ - فعّل الترجمة للإنجليزية لفهم المصطلحات التقنية
57
+ - ضع علامات على المفاهيم الجديدة
58
+
59
+ ### للطلاب الدوليين:
60
+ - استخدم الترجمة إلى لغتك الأم للفهم الأفضل
61
+ - اعتمد على العلامات المهمة للمراجعة السريعة
62
+ - راجع النص المترجم والأصلي معاً
63
+
64
+ ## 🔧 إعدادات متقدمة
65
+
66
+ ### جودة التسجيل:
67
+ - **عالية**: للمحاضرات المهمة (320 kbps)
68
+ - **متوسطة**: للاستخدام العادي (192 kbps)
69
+ - **منخفضة**: لتوفير المساحة (128 kbps)
70
+
71
+ ### إعدادات الترجمة:
72
+ - **Arabic**: للطلاب العرب
73
+ - **English**: للمحتوى الدولي
74
+ - **French**: للطلاب الفرنكوفونيين
75
+ - **Spanish**: للطلاب الناطقين بالإسبانية
76
+
77
+ ## 🛠️ استكشاف الأخطاء
78
+
79
+ ### مشاكل الميكروفون:
80
+ 1. تأكد من إعطاء إذن الميكروفون للمتصفح
81
+ 2. تحقق من إعدادات الصوت في النظام
82
+ 3. جرب متصفح آخر إذا لزم الأمر
83
+
84
+ ### مشاكل الترجمة:
85
+ 1. تأكد من اتصال الإنترنت
86
+ 2. تحقق من صحة مفتاح API
87
+ 3. جرب إعادة المعالجة
88
+
89
+ ### مشاكل في النسخ:
90
+ 1. تأكد من وضوح الصوت
91
+ 2. قلل الضوضاء في الخلفية
92
+ 3. تحدث بوضوح وبطء نسبياً
93
+
94
+ ## 📞 الدعم التقني
95
+
96
+ ### الحصول على المساعدة:
97
+ - تحقق من console المتصفح (F12) للأخطاء
98
+ - راجع ملفات السجل في مجلد التطبيق
99
+ - تأكد من تحديث جميع المكتبات
100
+
101
+ ### نصائح للأداء الأفضل:
102
+ - استخدم Chrome أو Firefox للتوافق الأفضل
103
+ - أغلق التطبيقات الأخرى أثناء التسجيل
104
+ - تأكد من مساحة كافية على القرص الصلب
105
+
106
+ ## 🎓 نصائح أكاديمية
107
+
108
+ ### للمحاضرات:
109
+ - اجلس في مقدمة القاعة للصوت الأوضح
110
+ - استخدم علامات المحاضر المهمة كدليل
111
+ - راجع الترجمة مع زملاء الدراسة
112
+
113
+ ### للمذاكرة:
114
+ - استخدم النص المترجم للمراجعة السريعة
115
+ - ابحث عن المفاهيم المترجمة في مصادر إضافية
116
+ - اربط النص الأصلي بالترجمة لتحسين اللغة
117
+
118
+ ## 🔮 ميزات قادمة
119
+
120
+ ### التحديثات المخططة:
121
+ - **تحليل المحتوى**: استخراج النقاط الرئيسية تلقائياً
122
+ - **بطاقات المراجعة**: إنشاء بطاقات دراسة من المحاضرات
123
+ - **التكامل مع المنصات**: ربط مع Moodle وCanvas
124
+ - **المشاركة التعاونية**: مشاركة المحاضرات مع الزملاء
125
+
126
+ ---
127
+
128
+ ## 📄 إخلاء المسؤولية
129
+
130
+ هذا التطبيق مخصص للاستخدام التعليمي. تأكد من الحصول على إذن المحاضر قبل تسجيل المحاضرات. النسخ والترجمة قد يحتويان على أخطاء، لذا راجعهما دائماً.
131
+
132
+ ---
133
+
134
+ **نتمنى لك تجربة تعليمية ممتازة مع SyncMaster! 🎓✨**
SOLUTION_SUMMARY.md ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🎉 تم حل المشكلة بنجاح!
2
+
3
+ ## ✅ ملخص الحل
4
+
5
+ تم حل مشكلة "system offline" في ميزة Lecture Recorder بنجاح. الآن يمكنك تشغيل التطبيق بأمر واحد فقط:
6
+
7
+ ```bash
8
+ npm run dev
9
+ ```
10
+
11
+ ## 🔧 التغييرات التي تمت
12
+
13
+ ### 1. ملفات جديدة تم إنشاؤها:
14
+ - `integrated_server.py` - خادم متكامل للتسجيل
15
+ - `main.py` - نقطة دخول بسيطة ومدمجة
16
+ - `app_config.py` - إعدادات التطبيق
17
+ - `startup.py` - مُشغل متقدم للتطوير
18
+
19
+ ### 2. ملفات تم تعديلها:
20
+ - `app.py` - إضافة استيراد الخادم المدمج
21
+ - `package.json` - تحديث أوامر التشغيل
22
+
23
+ ## 🚀 كيفية الاستخدام
24
+
25
+ ### للاستخدام العادي:
26
+ ```bash
27
+ npm run dev
28
+ ```
29
+
30
+ ### للنشر على HuggingFace:
31
+ ```bash
32
+ npm start
33
+ ```
34
+
35
+ ### للتطوير المتقدم (خوادم منفصلة):
36
+ ```bash
37
+ npm run dev-separate
38
+ ```
39
+
40
+ ## ✨ المميزات الجديدة
41
+
42
+ 1. **🎯 تشغيل موحد**: أمر واحد فقط لتشغيل كل شيء
43
+ 2. **☁️ جاهز للسحابة**: يعمل تلقائياً على HuggingFace و Railway
44
+ 3. **🔧 تكوين ذكي**: يكتشف البيئة ويتكيف معها
45
+ 4. **🛡️ معالجة أخطاء محسنة**: تشغيل احتياطي في حالة فشل الطريقة الأولى
46
+ 5. **📊 مراقبة الحالة**: فحص تلقائي لحالة الخوادم
47
+
48
+ ## 🧪 اختبار النظام
49
+
50
+ تم اختبار النظام وأظهر النتائج التالية:
51
+ - ✅ خادم التسجيل يبدأ تلقائياً
52
+ - ✅ Streamlit يعمل على المنفذ 5050
53
+ - ✅ خادم التسجيل يعمل على المنفذ 5001
54
+ - ✅ التكامل بين الخوادم يعمل بنجاح
55
+
56
+ ## 🎊 النتيجة النهائية
57
+
58
+ **المشكلة محلولة تماماً!**
59
+
60
+ لن تحتاج بعد الآن إلى:
61
+ - ❌ تشغيل `python recorder_server.py` منفصلاً
62
+ - ❌ القلق بشأن "system offline"
63
+ - ❌ تشغيل أوامر متعددة
64
+
65
+ فقط استخدم `npm run dev` وسيعمل كل شيء تلقائياً! 🚀
66
+
67
+ ---
68
+
69
+ **جاهز للنشر على HuggingFace الآن!** 🌟
SUMMARY_FIX_REPORT.md ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # حل مشكلة زر التلخيص - تقرير الإصلاح النهائي 🎉
2
+
3
+ ## 📋 ملخص المشكلة
4
+ كان زر "Generate Smart Lecture Summary" لا يعمل في تلخيص النص المستخرج من الذكاء الاصطناعي بعد جلبه من الصوت، مع ظهور خطأ CORS:
5
+
6
+ ```
7
+ Access to fetch at 'http://localhost:5001/summarize' from origin 'http://localhost:5054' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.
8
+ ```
9
+
10
+ ## 🔍 التشخيص المنجز
11
+ تم إنشاء نظام تشخيص شامل كشف عن:
12
+
13
+ ### 1. مشكلة CORS الرئيسية ❌
14
+ - **المشكلة**: الخادم يرسل `'*, *'` بدلاً من `'*'`
15
+ - **السبب**: تكرار إعدادات CORS - مرة من `flask-cors` ومرة يدوياً في كل endpoint
16
+ - **النتيجة**: تكرار header `Access-Control-Allow-Origin`
17
+
18
+ ### 2. مكتبة مفقودة ❌
19
+ - **المشكلة**: `google-generativeai` غير مثبتة
20
+ - **التأثير**: فشل في وظيفة التلخيص
21
+
22
+ ## ✅ الحلول المطبقة
23
+
24
+ ### 1. إصلاح مشكلة CORS
25
+ #### أ. تبسيط إعداد CORS في `recorder_server.py`:
26
+ ```python
27
+ # قبل الإصلاح - إعداد معقد
28
+ CORS(app, resources={
29
+ r"/record": {"origins": "*"},
30
+ r"/translate": {"origins": "*"},
31
+ r"/languages": {"origins": "*"},
32
+ r"/ui-translations/*": {"origins": "*"},
33
+ r"/notes": {"origins": "*"},
34
+ r"/notes/*": {"origins": "*"},
35
+ r"/summarize": {"origins": "*"}
36
+ })
37
+
38
+ # بعد الإصلاح - إعداد مبسط وصحيح
39
+ CORS(app,
40
+ origins="*",
41
+ methods=['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
42
+ allow_headers=['Content-Type', 'Authorization']
43
+ )
44
+ ```
45
+
46
+ #### ب. إزالة الإعدادات اليدوية المكررة:
47
+ ```python
48
+ # قبل الإصلاح - إعداد يدوي مكرر
49
+ if request.method == 'OPTIONS':
50
+ headers = {
51
+ 'Access-Control-Allow-Origin': '*',
52
+ 'Access-Control-Allow-Methods': 'POST, OPTIONS',
53
+ 'Access-Control-Allow-Headers': 'Content-Type',
54
+ }
55
+ return ('', 204, headers)
56
+
57
+ # بعد الإصلاح - تبسيط
58
+ if request.method == 'OPTIONS':
59
+ return '', 204 # flask-cors ستتولى الأمر
60
+ ```
61
+
62
+ ### 2. تثبيت المكتبات المفقودة
63
+ ```bash
64
+ pip install google-generativeai
65
+ ```
66
+
67
+ ### 3. تحسين معالجة الأخطاء في JavaScript
68
+ تم تحديث دالة `generateSummary()` في `templates/recorder.html`:
69
+ - رسائل خطأ محددة باللغة العربية
70
+ - معالجة أفضل لتنسيقات الاستجابة المختلفة
71
+ - تشخيص أوضح للمشاكل
72
+
73
+ ### 4. تحسين دالة عرض النتائج
74
+ تم تحديث `displaySummaryResults()` للتعامل مع:
75
+ - تنسيقات مختلفة للاستجابة (نص أو كائن)
76
+ - عرض محتوى احتياطي في حالة عدم وجود المحتوى المتوقع
77
+
78
+ ## 🧪 أدوات التشخيص المُنشأة
79
+
80
+ ### 1. `diagnose_summary.py`
81
+ نظام تشخيص شامل يفحص:
82
+ - حالة العمليات والمنافذ
83
+ - إعدادات CORS
84
+ - وظيفة التلخيص
85
+ - المكتبات المطلوبة
86
+
87
+ ### 2. `test_summary_button.py`
88
+ اختبار مبسط ومباشر لزر التلخيص
89
+
90
+ ### 3. `test_summarize.py`
91
+ اختبار أساسي لـ endpoint التلخيص
92
+
93
+ ## 📊 نتائج الاختبار النهائية ✅
94
+
95
+ ```
96
+ 🎉 جميع الاختبارات نجحت!
97
+ ✅ زر التلخيص يعمل بشكل صحيح
98
+
99
+ 📊 ملخص التشخيص:
100
+ 📦 المكتبات: ✅ موجودة
101
+ 🔧 عملية Python: ✅ تعمل
102
+ 🌐 المنفذ 5001: ✅ مفتوح
103
+ 🔧 CORS: ✅ صحيح
104
+ 🤖 التلخيص: ✅ يعمل
105
+ ```
106
+
107
+ ## 🔧 الملفات المُعدّلة
108
+
109
+ ### 1. `recorder_server.py`
110
+ - إصلاح إعدادات CORS
111
+ - إزالة التكرار في headers
112
+ - تبسيط معالجة OPTIONS requests
113
+
114
+ ### 2. `templates/recorder.html`
115
+ - تحسين دالة `generateSummary()`
116
+ - تحسين دالة `displaySummaryResults()`
117
+ - رسائل خطأ أوضح
118
+
119
+ ### 3. ملفات التشخيص الجديدة
120
+ - `diagnose_summary.py`
121
+ - `test_summary_button.py`
122
+ - `test_summarize.py`
123
+
124
+ ## 🚀 كيفية التحقق من الحل
125
+
126
+ ### 1. تشغيل الخادم:
127
+ ```bash
128
+ python recorder_server.py
129
+ ```
130
+
131
+ ### 2. تشغيل التشخيص:
132
+ ```bash
133
+ python diagnose_summary.py
134
+ ```
135
+
136
+ ### 3. اختبار زر التلخيص:
137
+ ```bash
138
+ python test_summary_button.py
139
+ ```
140
+
141
+ ### 4. اختبار من الواجهة:
142
+ 1. افتح `http://localhost:5054`
143
+ 2. سجل صوت أو ادخل نص
144
+ 3. اضغط زر "🤖 Generate Smart Lecture Summary"
145
+ 4. تأكد من ظهور الملخص
146
+
147
+ ## 💡 نصائح للمستقبل
148
+
149
+ ### 1. تجنب تكرار CORS
150
+ - استخدم إعداد CORS واحد فقط
151
+ - لا تضع إعدادات يدوية إضافية
152
+
153
+ ### 2. مراقبة التبعيات
154
+ - تأكد من تثبيت جميع المكتبات المطلوبة
155
+ - استخدم `requirements.txt` محدث
156
+
157
+ ### 3. استخدام أدوات التشخيص
158
+ - شغل `diagnose_summary.py` عند مواجهة مشاكل
159
+ - يوفر تشخيص سريع وشامل
160
+
161
+ ## 🎯 الخلاصة
162
+
163
+ تم حل مشكلة زر التلخيص بنجاح من خلال:
164
+ 1. ✅ إصلاح مشكلة CORS المزدوجة
165
+ 2. ✅ تثبيت المكتبات المفقودة
166
+ 3. ✅ تحسين معالجة الأخطاء
167
+ 4. ✅ إنشاء أدوات تشخيص شاملة
168
+
169
+ **النتيجة: زر التلخيص يعمل بشكل مثالي الآن! 🎉**
TECHNICAL_IMPLEMENTATION.md ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SyncMaster Enhanced - Technical Implementation Summary
2
+
3
+ ## 🎯 Summary of Enhancements
4
+
5
+ This document outlines the comprehensive improvements made to SyncMaster to support AI-powered translation for international students.
6
+
7
+ ## 🔧 New Components Added
8
+
9
+ ### 1. `translator.py` - AI Translation Engine
10
+ ```python
11
+ class AITranslator:
12
+ - translate_text(text, target_language='ar', source_language='auto')
13
+ - detect_language(text)
14
+ - translate_ui_elements(ui_dict, target_language='ar')
15
+ - batch_translate(texts, target_language='ar')
16
+ ```
17
+
18
+ **Features:**
19
+ - Gemini AI-powered translation
20
+ - Academic content optimization
21
+ - Multi-language support (Arabic, English, French, Spanish)
22
+ - Batch processing capabilities
23
+ - Context-aware translation
24
+
25
+ ### 2. Enhanced `audio_processor.py`
26
+ ```python
27
+ class AudioProcessor:
28
+ - get_word_timestamps_with_translation(audio_file_path, target_language='ar')
29
+ - batch_translate_transcription(audio_file_path, target_languages)
30
+ - _create_translated_timestamps(original_timestamps, original_text, translated_text)
31
+ ```
32
+
33
+ **New Features:**
34
+ - Integrated translation with transcription
35
+ - Proportional timestamp mapping for translated text
36
+ - Multi-language processing
37
+ - Enhanced error handling and logging
38
+
39
+ ### 3. Updated `recorder_server.py`
40
+ ```python
41
+ @app.route('/record', methods=['POST'])
42
+ def record():
43
+ # Enhanced with translation parameters:
44
+ # - target_language
45
+ # - enable_translation
46
+ # - comprehensive response with both original and translated text
47
+
48
+ @app.route('/translate', methods=['POST'])
49
+ def translate_text():
50
+ # Standalone translation endpoint
51
+
52
+ @app.route('/languages', methods=['GET'])
53
+ def get_supported_languages():
54
+ # Get list of supported languages
55
+
56
+ @app.route('/ui-translations/<language>', methods=['GET'])
57
+ def get_ui_translations(language):
58
+ # Get UI translations for specific language
59
+ ```
60
+
61
+ ### 4. Enhanced `templates/recorder.html`
62
+ **New Features:**
63
+ - Multi-language interface (English/Arabic)
64
+ - RTL support for Arabic
65
+ - Translation toggle controls
66
+ - Target language selection
67
+ - Enhanced visual design
68
+ - Keyboard shortcuts
69
+ - Accessibility improvements
70
+
71
+ **UI Improvements:**
72
+ - Modern gradient design
73
+ - Responsive layout for mobile devices
74
+ - Real-time language switching
75
+ - Visual feedback for translation status
76
+ - Better error messaging
77
+
78
+ ### 5. Updated `app.py` - Main Application
79
+ **Enhancements:**
80
+ - Language selection in sidebar
81
+ - Translation settings integration
82
+ - Enhanced processing workflow
83
+ - Bilingual interface support
84
+ - Improved user experience flow
85
+
86
+ ## 🌐 Multi-Language Support Implementation
87
+
88
+ ### UI Translation System
89
+ ```python
90
+ UI_TRANSLATIONS = {
91
+ 'en': { /* English translations */ },
92
+ 'ar': { /* Arabic translations */ }
93
+ }
94
+ ```
95
+
96
+ ### Dynamic Language Switching
97
+ - Client-side language detection
98
+ - Server-side translation API
99
+ - Real-time UI updates
100
+ - RTL text direction support
101
+
102
+ ### Translation Workflow
103
+ 1. **Audio Recording** → Record with language preferences
104
+ 2. **Transcription** → AI-powered speech-to-text
105
+ 3. **Language Detection** → Automatic source language identification
106
+ 4. **Translation** → Context-aware AI translation
107
+ 5. **Presentation** → Side-by-side original and translated text
108
+
109
+ ## 🚀 API Enhancements
110
+
111
+ ### Recording Endpoint (`/record`)
112
+ **Request Parameters:**
113
+ ```json
114
+ {
115
+ "audio_data": "binary_audio_file",
116
+ "markers": "[timestamp_array]",
117
+ "target_language": "ar|en|fr|es",
118
+ "enable_translation": "true|false"
119
+ }
120
+ ```
121
+
122
+ **Response Format:**
123
+ ```json
124
+ {
125
+ "success": true,
126
+ "original_text": "Original transcription",
127
+ "translated_text": "Translated text",
128
+ "file_path": "path/to/saved/file.json",
129
+ "markers": [timestamps],
130
+ "target_language": "ar",
131
+ "translation_enabled": true,
132
+ "translation_success": true,
133
+ "language_detected": "en"
134
+ }
135
+ ```
136
+
137
+ ### Translation Endpoint (`/translate`)
138
+ **Request:**
139
+ ```json
140
+ {
141
+ "text": "Text to translate",
142
+ "target_language": "ar",
143
+ "source_language": "auto"
144
+ }
145
+ ```
146
+
147
+ **Response:**
148
+ ```json
149
+ {
150
+ "success": true,
151
+ "original_text": "Original text",
152
+ "translated_text": "النص المترجم",
153
+ "source_language": "en",
154
+ "target_language": "ar"
155
+ }
156
+ ```
157
+
158
+ ## 📱 Frontend Enhancements
159
+
160
+ ### JavaScript Features
161
+ ```javascript
162
+ // Language Management
163
+ async function loadTranslations(language)
164
+ function applyTranslations()
165
+ function changeLanguage()
166
+
167
+ // Enhanced Recording
168
+ function displayResults(result)
169
+ function displayMarkers(markers)
170
+ function showMessage(message, type)
171
+
172
+ // Keyboard Shortcuts
173
+ document.addEventListener('keydown', handleKeyboardShortcuts)
174
+ ```
175
+
176
+ ### CSS Improvements
177
+ ```css
178
+ /* RTL Support */
179
+ html[dir="rtl"] { direction: rtl; }
180
+
181
+ /* Modern Design */
182
+ :root {
183
+ --primary-color: #4A90E2;
184
+ --success-color: #50C878;
185
+ /* ... more color variables */
186
+ }
187
+
188
+ /* Responsive Design */
189
+ @media (max-width: 768px) {
190
+ /* Mobile optimizations */
191
+ }
192
+ ```
193
+
194
+ ## 🔒 Security & Performance
195
+
196
+ ### Security Measures
197
+ - Input validation for all API endpoints
198
+ - CORS configuration for cross-origin requests
199
+ - Secure file handling with temporary files
200
+ - API key protection in environment variables
201
+
202
+ ### Performance Optimizations
203
+ - Parallel processing for audio and translation
204
+ - Efficient memory management
205
+ - Chunked audio processing
206
+ - Client-side caching for translations
207
+
208
+ ## 📊 File Structure Changes
209
+
210
+ ```
211
+ SyncMaster - Copy (2)/
212
+ ├── translator.py # NEW: AI Translation engine
213
+ ├── audio_processor.py # ENHANCED: With translation support
214
+ ├── recorder_server.py # ENHANCED: Additional endpoints
215
+ ├── app.py # ENHANCED: Multi-language support
216
+ ├── templates/
217
+ │ └── recorder.html # ENHANCED: Multi-language UI
218
+ ├── README_AR.md # NEW: Arabic documentation
219
+ ├── setup_enhanced.py # NEW: Enhanced setup script
220
+ ├── start_enhanced.bat # NEW: Quick start script
221
+ ├── requirements.txt # UPDATED: Additional dependencies
222
+ └── .env # UPDATED: Additional configuration
223
+ ```
224
+
225
+ ## 🎓 Educational Features
226
+
227
+ ### For International Students
228
+ 1. **Language Barrier Reduction**: Real-time translation of lectures
229
+ 2. **Better Comprehension**: Side-by-side original and translated text
230
+ 3. **Cultural Adaptation**: Interface in native language
231
+ 4. **Academic Context**: Specialized translation for educational content
232
+
233
+ ### For Arabic Students
234
+ 1. **Native Interface**: Complete Arabic UI
235
+ 2. **Technical Term Translation**: English technical terms with Arabic explanations
236
+ 3. **Reading Direction**: Proper RTL text display
237
+ 4. **Cultural Context**: Academic content adapted for Arabic speakers
238
+
239
+ ## 🔧 Installation & Setup
240
+
241
+ ### Enhanced Setup Process
242
+ 1. **Automated Installation**: `python setup_enhanced.py`
243
+ 2. **Dependency Management**: Automatic package installation
244
+ 3. **Configuration Validation**: Environment file checking
245
+ 4. **Service Management**: Automatic server startup
246
+
247
+ ### Quick Start Options
248
+ - **Windows**: `start_enhanced.bat`
249
+ - **Cross-platform**: `python setup_enhanced.py`
250
+ - **Manual**: Individual component startup
251
+
252
+ ## 📈 Testing & Quality Assurance
253
+
254
+ ### Translation Quality
255
+ - Academic content optimization
256
+ - Technical term preservation
257
+ - Context-aware translation
258
+ - Fallback mechanisms
259
+
260
+ ### User Experience Testing
261
+ - Multi-language interface testing
262
+ - Mobile responsiveness
263
+ - Accessibility compliance
264
+ - Performance optimization
265
+
266
+ ## 🔮 Future Enhancements
267
+
268
+ ### Planned Features
269
+ 1. **Advanced Translation**: Subject-specific terminology
270
+ 2. **Collaboration Tools**: Shared study sessions
271
+ 3. **Learning Analytics**: Progress tracking
272
+ 4. **Platform Integration**: LMS connectivity
273
+ 5. **Offline Support**: Local processing capabilities
274
+
275
+ ### Technical Roadmap
276
+ 1. **Model Optimization**: Faster processing
277
+ 2. **Caching System**: Reduced API calls
278
+ 3. **Advanced UI**: More interactive features
279
+ 4. **Mobile App**: Native mobile application
280
+
281
+ ---
282
+
283
+ ## 📞 Technical Support
284
+
285
+ ### Debugging Features
286
+ - Comprehensive logging system
287
+ - Browser console integration
288
+ - Error message localization
289
+ - Performance monitoring
290
+
291
+ ### Troubleshooting Resources
292
+ - Detailed error messages
293
+ - Multi-language support documentation
294
+ - Community forum integration
295
+ - Technical FAQ
296
+
297
+ ---
298
+
299
+ **This enhanced version of SyncMaster represents a significant advancement in making educational technology accessible to international students worldwide.**
TROUBLESHOOTING.md ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🛠️ دليل استكشاف الأخطاء - SyncMaster Enhanced
2
+ # Troubleshooting Guide - SyncMaster Enhanced
3
+
4
+ ## 🔍 الأخطاء الشائعة وحلولها / Common Errors and Solutions
5
+
6
+ ### 1. خطأ الاتصال بالخادم / Server Connection Error
7
+ ```
8
+ Error: Failed to fetch
9
+ POST http://localhost:5001/record net::ERR_CONNECTION_REFUSED
10
+ ```
11
+
12
+ **الأسباب المحتملة / Possible Causes:**
13
+ - الخادم غير يعمل / Server not running
14
+ - منفذ 5001 مستخدم من برنامج آخر / Port 5001 used by another application
15
+ - جدار حماية يحجب الاتصال / Firewall blocking connection
16
+
17
+ **الحلول / Solutions:**
18
+
19
+ #### أ) تشغيل اختبار النظام / Run System Test:
20
+ ```bash
21
+ python test_system.py
22
+ ```
23
+
24
+ #### ب) تشغيل الخادم يدوياً / Start Server Manually:
25
+ ```bash
26
+ # إيقاف جميع العمليات / Stop all processes
27
+ taskkill /f /im python.exe
28
+
29
+ # تشغيل الخادم / Start server
30
+ python recorder_server.py
31
+ ```
32
+
33
+ #### ج) استخدام البدء المتقدم / Use Debug Startup:
34
+ ```bash
35
+ python start_debug.py
36
+ ```
37
+
38
+ #### د) فحص المنافذ / Check Ports:
39
+ ```bash
40
+ # Windows
41
+ netstat -an | findstr :5001
42
+
43
+ # Linux/Mac
44
+ lsof -i :5001
45
+ ```
46
+
47
+ ### 2. مشكلة مفتاح API / API Key Issues
48
+ ```
49
+ ERROR: GEMINI_API_KEY not found in environment variables
50
+ ```
51
+
52
+ **الحل / Solution:**
53
+ 1. تأكد من وجود ملف `.env`:
54
+ ```bash
55
+ # إنشاء ملف .env / Create .env file
56
+ echo GEMINI_API_KEY=your_actual_api_key_here > .env
57
+ ```
58
+
59
+ 2. احصل على مفتاح API من:
60
+ - [Google AI Studio](https://makersuite.google.com/app/apikey)
61
+
62
+ 3. أضف المفتاح إلى `.env`:
63
+ ```
64
+ GEMINI_API_KEY=AIzaSyAS7JtrXjlNjyuo3RG5z6rkwocCwFy1YuA
65
+ ```
66
+
67
+ ### 3. مشاكل الصوت / Audio Issues
68
+ ```
69
+ UserWarning: PySoundFile failed. Trying audioread instead.
70
+ ```
71
+
72
+ **الحلول / Solutions:**
73
+
74
+ #### أ) تثبيت SoundFile مرة أخرى / Reinstall SoundFile:
75
+ ```bash
76
+ pip uninstall soundfile
77
+ pip install soundfile
78
+ ```
79
+
80
+ #### ب) تثبيت FFmpeg (إذا لزم الأمر) / Install FFmpeg if needed:
81
+ ```bash
82
+ # Windows (using chocolatey)
83
+ choco install ffmpeg
84
+
85
+ # Or download from: https://ffmpeg.org/download.html
86
+ ```
87
+
88
+ #### ج) فحص تنسيق الملف / Check Audio Format:
89
+ - استخدم WAV بدلاً من MP3
90
+ - تأكد من جودة التسجيل
91
+
92
+ ### 4. مشاكل الترجمة / Translation Issues
93
+ ```
94
+ WARNING: Gemini returned empty translation response
95
+ ```
96
+
97
+ **الحلول / Solutions:**
98
+
99
+ #### أ) فحص اتصال الإنترنت / Check Internet Connection:
100
+ ```bash
101
+ ping google.com
102
+ ```
103
+
104
+ #### ب) اختبار مفتاح API / Test API Key:
105
+ ```python
106
+ python test_system.py
107
+ ```
108
+
109
+ #### ج) تغيير النموذج / Change Model:
110
+ - إذا فشل `gemini-2.5-flash`، جرب `gemini-1.5-flash`
111
+
112
+ ### 5. مشاكل الواجهة / UI Issues
113
+
114
+ #### أ) الواجهة لا تحمّل / Interface Won't Load:
115
+ ```bash
116
+ # تحقق من المنفذ / Check port
117
+ python -c "import socket; s=socket.socket(); s.bind(('',8501)); print('Port 8501 available')"
118
+
119
+ # تشغيل على منفذ مختلف / Run on different port
120
+ streamlit run app.py --server.port 8502
121
+ ```
122
+
123
+ #### ب) مشاكل اللغة العربية / Arabic Language Issues:
124
+ - تأكد من دعم المتصفح للـ RTL
125
+ - استخدم Chrome أو Firefox للأفضل
126
+
127
+ ### 6. مشاكل الأداء / Performance Issues
128
+
129
+ #### أ) بطء في المعالجة / Slow Processing:
130
+ - تحقق من سرعة الإنترنت
131
+ - قلل حجم الملف الصوتي
132
+ - استخدم جودة أقل للتسجيل
133
+
134
+ #### ب) استهلاك ذاكرة عالي / High Memory Usage:
135
+ ```bash
136
+ # إعادة تشغيل النظام / Restart system
137
+ python start_debug.py
138
+ ```
139
+
140
+ ## 🔧 أدوات التشخيص / Diagnostic Tools
141
+
142
+ ### 1. اختبار شامل / Complete Test:
143
+ ```bash
144
+ python test_system.py
145
+ ```
146
+
147
+ ### 2. فحص المنافذ / Port Check:
148
+ ```python
149
+ python -c "
150
+ import socket
151
+ ports = [5001, 8501, 8502]
152
+ for port in ports:
153
+ try:
154
+ s = socket.socket()
155
+ s.bind(('localhost', port))
156
+ s.close()
157
+ print(f'Port {port}: Available ✅')
158
+ except:
159
+ print(f'Port {port}: Busy ❌')
160
+ "
161
+ ```
162
+
163
+ ### 3. فحص التبعيات / Dependencies Check:
164
+ ```bash
165
+ pip list | grep -E "(streamlit|flask|librosa|soundfile|google-generativeai)"
166
+ ```
167
+
168
+ ### 4. فحص العمليات / Process Check:
169
+ ```bash
170
+ # Windows
171
+ tasklist | findstr python
172
+
173
+ # Linux/Mac
174
+ ps aux | grep python
175
+ ```
176
+
177
+ ## 📱 نصائح لحل المشاكل / Troubleshooting Tips
178
+
179
+ ### للطلاب الجدد / For New Users:
180
+ 1. **ابدأ بالاختبار الشامل / Start with system test**:
181
+ ```bash
182
+ python test_system.py
183
+ ```
184
+
185
+ 2. **استخدم البدء المتقدم / Use debug startup**:
186
+ ```bash
187
+ python start_debug.py
188
+ ```
189
+
190
+ 3. **تحقق من المتطلبات / Check requirements**:
191
+ - Python 3.8+
192
+ - مفتاح Gemini API صالح
193
+ - اتصال إنترنت مستقر
194
+
195
+ ### للطلاب المتقدمين / For Advanced Users:
196
+ 1. **مراجعة السجلات / Check logs**:
197
+ - افتح console المتصفح (F12)
198
+ - راجع سجلات الطرفية
199
+
200
+ 2. **تخصيص الإعدادات / Customize settings**:
201
+ - غير المنافذ في حالة التضارب
202
+ - عدّل إعدادات الصوت
203
+
204
+ 3. **التشخيص المتقدم / Advanced diagnostics**:
205
+ ```python
206
+ # اختبار الاتصال / Test connection
207
+ import requests
208
+ response = requests.get('http://localhost:5001/record')
209
+ print(response.status_code, response.text)
210
+ ```
211
+
212
+ ## 🆘 طلب المساعدة / Getting Help
213
+
214
+ ### معلومات مطلوبة / Required Information:
215
+ 1. نظام التشغيل / Operating System
216
+ 2. إصدار Python / Python Version
217
+ 3. نتائج `python test_system.py`
218
+ 4. رسائل الخطأ الكاملة / Complete error messages
219
+ 5. سجلات الطرفية / Terminal logs
220
+
221
+ ### خطوات الإبلاغ / Reporting Steps:
222
+ 1. شغّل الاختبار الشامل
223
+ 2. احفظ النتائج
224
+ 3. صوّر رسائل الخطأ
225
+ 4. اذكر الخطوات التي أدت للمشكلة
226
+
227
+ ---
228
+
229
+ ## 🎯 Quick Fix Commands / أوامر الإصلاح السريع
230
+
231
+ ```bash
232
+ # إعادة تعيين كامل / Complete Reset
233
+ taskkill /f /im python.exe
234
+ python test_system.py
235
+ python start_debug.py
236
+
237
+ # إصلاح التبعيات / Fix Dependencies
238
+ pip install --upgrade -r requirements.txt
239
+
240
+ # إصلاح المنافذ / Fix Ports
241
+ python start_debug.py
242
+
243
+ # اختبار الترجمة / Test Translation
244
+ python -c "from translator import AITranslator; t=AITranslator(); print(t.translate_text('Hello', 'ar'))"
245
+ ```
246
+
247
+ ---
248
+
249
+ **تذكر: معظم المشاكل تُحل بإعادة تشغيل النظام وتشغيل الاختبار الشامل! 🔄**
250
+
251
+ **Remember: Most issues are solved by restarting and running the system test! 🔄**
ai_questions.py ADDED
@@ -0,0 +1,773 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ai_questions.py - AI-Powered Question Engine for SyncMaster
2
+
3
+ import time
4
+ import hashlib
5
+ from datetime import datetime
6
+ from typing import Dict, List, Optional, Tuple, Any
7
+ from dataclasses import dataclass, field
8
+ import streamlit as st
9
+
10
+ @dataclass
11
+ class QAPair:
12
+ """Represents a question-answer pair"""
13
+ question: str
14
+ answer: str
15
+ timestamp: datetime
16
+ question_type: str # 'template' or 'custom'
17
+ response_time_ms: int
18
+ model_used: str = "Unknown" # Which AI model was used
19
+
20
+ @dataclass
21
+ class QuestionSession:
22
+ """Represents a question session for a specific text segment"""
23
+ session_id: str
24
+ selected_text: str
25
+ segment_id: str
26
+ start_timestamp: int
27
+ ui_language: str
28
+ conversation: List[QAPair] = field(default_factory=list)
29
+ created_at: datetime = field(default_factory=datetime.now)
30
+
31
+ @dataclass
32
+ class TextSelection:
33
+ """Represents selected text from broadcast"""
34
+ text: str
35
+ segment_id: str
36
+ start_ms: int
37
+ end_ms: int
38
+ translations: Dict[str, str] = field(default_factory=dict)
39
+ selection_timestamp: int = field(default_factory=lambda: int(time.time() * 1000))
40
+
41
+ class AIQuestionEngine:
42
+ """
43
+ AI-powered question engine for interactive learning
44
+ """
45
+
46
+ def __init__(self, translator_instance=None):
47
+ self.translator = translator_instance
48
+ self.conversation_history: Dict[str, QuestionSession] = {}
49
+ self.model_usage_stats = {
50
+ 'Gemini AI': 0,
51
+ 'Groq AI': 0,
52
+ 'OpenRouter AI': 0,
53
+ 'Simple Response': 0
54
+ }
55
+
56
+ # Question templates in multiple languages
57
+ self.question_templates = {
58
+ 'ar': [
59
+ "اشرح هذا النص بالتفصيل",
60
+ "أعطني أمثلة عملية على هذا",
61
+ "ما معنى هذا المصطلح؟",
62
+ "كيف يُستخدم هذا في الواقع؟",
63
+ "ما أهمية هذا الموضوع؟",
64
+ "ما هي النقاط الرئيسية هنا؟",
65
+ "اربط هذا بمفاهيم أخرى",
66
+ "ما هي التطبيقات العملية؟"
67
+ ],
68
+ 'en': [
69
+ "Explain this text in detail",
70
+ "Give me practical examples of this",
71
+ "What does this term mean?",
72
+ "How is this used in practice?",
73
+ "Why is this topic important?",
74
+ "What are the key points here?",
75
+ "Connect this to other concepts",
76
+ "What are the practical applications?"
77
+ ],
78
+ 'fr': [
79
+ "Expliquez ce texte en détail",
80
+ "Donnez-moi des exemples pratiques",
81
+ "Que signifie ce terme?",
82
+ "Comment cela est-il utilisé en pratique?",
83
+ "Pourquoi ce sujet est-il important?",
84
+ "Quels sont les points clés ici?",
85
+ "Reliez cela à d'autres concepts",
86
+ "Quelles sont les applications pratiques?"
87
+ ],
88
+ 'es': [
89
+ "Explica este texto en detalle",
90
+ "Dame ejemplos prácticos de esto",
91
+ "¿Qué significa este término?",
92
+ "¿Cómo se usa esto en la práctica?",
93
+ "¿Por qué es importante este tema?",
94
+ "¿Cuáles son los puntos clave aquí?",
95
+ "Conecta esto con otros conceptos",
96
+ "¿Cuáles son las aplicaciones prácticas?"
97
+ ],
98
+ 'de': [
99
+ "Erkläre diesen Text im Detail",
100
+ "Gib mir praktische Beispiele dafür",
101
+ "Was bedeutet dieser Begriff?",
102
+ "Wie wird das in der Praxis verwendet?",
103
+ "Warum ist dieses Thema wichtig?",
104
+ "Was sind die wichtigsten Punkte hier?",
105
+ "Verbinde das mit anderen Konzepten",
106
+ "Was sind die praktischen Anwendungen?"
107
+ ],
108
+ 'zh': [
109
+ "详细解释这段文字",
110
+ "给我一些实际例子",
111
+ "这个术语是什么意思?",
112
+ "这在实践中如何使用?",
113
+ "为什么这个话题很重要?",
114
+ "这里的要点是什么?",
115
+ "将此与其他概念联系起来",
116
+ "实际应用有哪些?"
117
+ ]
118
+ }
119
+
120
+ # Response formatting templates
121
+ self.response_templates = {
122
+ 'ar': {
123
+ 'context_intro': "بناءً على النص المحدد:",
124
+ 'explanation_intro': "الشرح:",
125
+ 'examples_intro': "أمثلة:",
126
+ 'importance_intro': "الأهمية:",
127
+ 'applications_intro': "التطبيقات:",
128
+ 'error_message': "عذراً، حدث خطأ في معالجة سؤالك. يرجى المحاولة مرة أخرى."
129
+ },
130
+ 'en': {
131
+ 'context_intro': "Based on the selected text:",
132
+ 'explanation_intro': "Explanation:",
133
+ 'examples_intro': "Examples:",
134
+ 'importance_intro': "Importance:",
135
+ 'applications_intro': "Applications:",
136
+ 'error_message': "Sorry, there was an error processing your question. Please try again."
137
+ }
138
+ }
139
+
140
+ def get_question_templates(self, ui_language: str = 'ar') -> List[str]:
141
+ """Get pre-defined question templates for the specified language"""
142
+ return self.question_templates.get(ui_language, self.question_templates['ar'])
143
+
144
+ def create_session_id(self, selected_text: str, segment_id: str) -> str:
145
+ """Create a unique session ID for a text selection"""
146
+ content = f"{selected_text}_{segment_id}_{int(time.time())}"
147
+ return hashlib.md5(content.encode()).hexdigest()[:12]
148
+
149
+ def process_question(self,
150
+ selected_text: str,
151
+ question: str,
152
+ segment_info: Dict[str, Any],
153
+ ui_language: str = 'ar',
154
+ session_id: Optional[str] = None,
155
+ preferred_model: str = 'auto') -> Tuple[Optional[str], Optional[str], str]:
156
+ """
157
+ Process a user question about selected text
158
+
159
+ Args:
160
+ selected_text: The text the user selected
161
+ question: The user's question
162
+ segment_info: Information about the broadcast segment
163
+ ui_language: UI language ('ar' or 'en')
164
+ session_id: Existing session ID or None for new session
165
+
166
+ Returns:
167
+ Tuple of (answer, error_message, session_id)
168
+ """
169
+
170
+ if not self.translator:
171
+ error_msg = self.response_templates[ui_language]['error_message']
172
+ return None, error_msg, session_id or ""
173
+
174
+ try:
175
+ # Create or get session
176
+ if not session_id:
177
+ session_id = self.create_session_id(selected_text, segment_info.get('id', ''))
178
+ session = QuestionSession(
179
+ session_id=session_id,
180
+ selected_text=selected_text,
181
+ segment_id=segment_info.get('id', ''),
182
+ start_timestamp=segment_info.get('start_ms', 0),
183
+ ui_language=ui_language
184
+ )
185
+ self.conversation_history[session_id] = session
186
+ else:
187
+ session = self.conversation_history.get(session_id)
188
+ if not session:
189
+ # Session not found, create new one
190
+ session = QuestionSession(
191
+ session_id=session_id,
192
+ selected_text=selected_text,
193
+ segment_id=segment_info.get('id', ''),
194
+ start_timestamp=segment_info.get('start_ms', 0),
195
+ ui_language=ui_language
196
+ )
197
+ self.conversation_history[session_id] = session
198
+
199
+ # Prepare context for AI
200
+ context = self._prepare_question_context(selected_text, question, session, ui_language)
201
+
202
+ # Get AI response with preferred model
203
+ start_time = time.time()
204
+ ai_response, error, model_used = self.get_ai_response_with_model(context, ui_language, preferred_model)
205
+ response_time = int((time.time() - start_time) * 1000)
206
+
207
+ if ai_response:
208
+ # Format response
209
+ formatted_response = self.format_ai_response(ai_response, ui_language)
210
+
211
+ # Save to conversation history with model info
212
+ question_type = 'template' if question in self.get_question_templates(ui_language) else 'custom'
213
+ qa_pair = QAPair(
214
+ question=question,
215
+ answer=formatted_response,
216
+ timestamp=datetime.now(),
217
+ question_type=question_type,
218
+ response_time_ms=response_time
219
+ )
220
+ # Add model info to the QA pair
221
+ qa_pair.model_used = model_used
222
+ session.conversation.append(qa_pair)
223
+
224
+ return formatted_response, None, session_id, model_used
225
+ else:
226
+ error_msg = error or self.response_templates[ui_language]['error_message']
227
+ return None, error_msg, session_id, None
228
+
229
+ except Exception as e:
230
+ error_msg = f"{self.response_templates[ui_language]['error_message']} ({str(e)})"
231
+ return None, error_msg, session_id or ""
232
+
233
+ def _prepare_question_context(self,
234
+ selected_text: str,
235
+ question: str,
236
+ session: QuestionSession,
237
+ ui_language: str) -> str:
238
+ """Prepare context for AI question processing"""
239
+
240
+ templates = self.response_templates[ui_language]
241
+
242
+ # Build context with conversation history
243
+ context_parts = []
244
+
245
+ # Add selected text context
246
+ context_parts.append(f"{templates['context_intro']}")
247
+ context_parts.append(f'"{selected_text}"')
248
+ context_parts.append("")
249
+
250
+ # Add conversation history if exists
251
+ if session.conversation:
252
+ context_parts.append("Previous conversation:")
253
+ for qa in session.conversation[-3:]: # Last 3 Q&A pairs for context
254
+ context_parts.append(f"Q: {qa.question}")
255
+ context_parts.append(f"A: {qa.answer[:200]}...") # Truncate long answers
256
+ context_parts.append("")
257
+
258
+ # Add current question
259
+ context_parts.append(f"Current question: {question}")
260
+ context_parts.append("")
261
+
262
+ # Add instructions based on language
263
+ language_instructions = {
264
+ 'ar': """
265
+ أجب على السؤال بناءً على النص المحدد. اجعل إجابتك:
266
+ - واضحة ومفهومة
267
+ - مرتبطة بالنص المحدد
268
+ - تحتوي على أمثلة عملية إذا كان ذلك مناسباً
269
+ - باللغة العربية الفصحى
270
+ - منظمة ومنسقة بشكل جيد
271
+
272
+ إذا كان السؤال يطلب شرحاً، قدم شرحاً مفصلاً.
273
+ إذا كان يطلب أمثلة، قدم أمثلة واقعية ومفيدة.
274
+ إذا كان يطلب التوضيح، اشرح المفاهيم بطريقة بسيطة.
275
+ """,
276
+ 'en': """
277
+ Answer the question based on the selected text. Make your answer:
278
+ - Clear and understandable
279
+ - Related to the selected text
280
+ - Include practical examples when appropriate
281
+ - In English
282
+ - Well-organized and formatted
283
+
284
+ If the question asks for explanation, provide detailed explanation.
285
+ If it asks for examples, provide real-world, helpful examples.
286
+ If it asks for clarification, explain concepts in simple terms.
287
+ """,
288
+ 'fr': """
289
+ Répondez à la question basée sur le texte sélectionné. Rendez votre réponse:
290
+ - Claire et compréhensible
291
+ - Liée au texte sélectionné
292
+ - Incluez des exemples pratiques si approprié
293
+ - En français
294
+ - Bien organisée et formatée
295
+
296
+ Si la question demande une explication, fournissez une explication détaillée.
297
+ Si elle demande des exemples, fournissez des exemples réels et utiles.
298
+ Si elle demande des clarifications, expliquez les concepts en termes simples.
299
+ """,
300
+ 'es': """
301
+ Responde la pregunta basada en el texto seleccionado. Haz que tu respuesta sea:
302
+ - Clara y comprensible
303
+ - Relacionada con el texto seleccionado
304
+ - Incluye ejemplos prácticos cuando sea apropiado
305
+ - En español
306
+ - Bien organizada y formateada
307
+
308
+ Si la pregunta pide explicación, proporciona explicación detallada.
309
+ Si pide ejemplos, proporciona ejemplos reales y útiles.
310
+ Si pide aclaración, explica conceptos en términos simples.
311
+ """,
312
+ 'de': """
313
+ Beantworte die Frage basierend auf dem ausgewählten Text. Mache deine Antwort:
314
+ - Klar und verständlich
315
+ - Bezogen auf den ausgewählten Text
316
+ - Enthalte praktische Beispiele wenn angemessen
317
+ - Auf Deutsch
318
+ - Gut organisiert und formatiert
319
+
320
+ Wenn die Frage nach Erklärung fragt, gib detaillierte Erklärung.
321
+ Wenn sie nach Beispielen fragt, gib reale und hilfreiche Beispiele.
322
+ Wenn sie nach Klarstellung fragt, erkläre Konzepte in einfachen Begriffen.
323
+ """,
324
+ 'zh': """
325
+ 根据选定的文本回答问题。让你的回答:
326
+ - 清晰易懂
327
+ - 与选定文本相关
328
+ - 适当时包含实际例子
329
+ - 用中文
330
+ - 组织良好且格式化
331
+
332
+ 如果问题要求解释,提供详细解释。
333
+ 如果要求例子,提供真实有用的例子。
334
+ 如果要求澄清,用简单术语解释概念。
335
+ """
336
+ }
337
+
338
+ instructions = language_instructions.get(ui_language, language_instructions['en'])
339
+
340
+ context_parts.append(instructions)
341
+
342
+ return "\n".join(context_parts)
343
+
344
+ def _get_ai_response(self, context: str, ui_language: str) -> Tuple[Optional[str], Optional[str], Optional[str]]:
345
+ """Get AI response using multiple AI services with fallback
346
+
347
+ Returns:
348
+ Tuple of (response_text, error_message, model_used)
349
+ """
350
+
351
+ # Model 1: Try Gemini first
352
+ try:
353
+ if hasattr(self.translator, 'model') and self.translator.model:
354
+ response = self.translator.model.generate_content(context)
355
+ if response and hasattr(response, 'text') and response.text:
356
+ self.model_usage_stats['Gemini AI'] += 1
357
+ return response.text.strip(), None, "Gemini AI"
358
+ except Exception as e:
359
+ error_msg = str(e)
360
+ print(f"Gemini AI failed: {error_msg}") # Debug log
361
+ # Continue to next model instead of returning error immediately
362
+
363
+ # Model 2: Try Groq
364
+ try:
365
+ if hasattr(self.translator, '_groq_complete'):
366
+ response, error = self.translator._groq_complete(context)
367
+ if response and response.strip():
368
+ self.model_usage_stats['Groq AI'] += 1
369
+ return response.strip(), None, "Groq AI"
370
+ print(f"Groq failed: {error}") # Debug log
371
+ except Exception as e:
372
+ print(f"Groq exception: {str(e)}") # Debug log
373
+
374
+ # Model 3: Try OpenRouter
375
+ try:
376
+ if hasattr(self.translator, '_openrouter_complete'):
377
+ response, error = self.translator._openrouter_complete(context)
378
+ if response and response.strip():
379
+ self.model_usage_stats['OpenRouter AI'] += 1
380
+ return response.strip(), None, "OpenRouter AI"
381
+ print(f"OpenRouter failed: {error}") # Debug log
382
+ except Exception as e:
383
+ print(f"OpenRouter exception: {str(e)}") # Debug log
384
+
385
+ # Fallback: Simple rule-based response
386
+ simple_response, _ = self._generate_simple_response(context, ui_language)
387
+ self.model_usage_stats['Simple Response'] += 1
388
+ return simple_response, None, "Simple Response (AI services unavailable)"
389
+
390
+ def _try_fallback_services(self, context: str, ui_language: str) -> Tuple[Optional[str], Optional[str]]:
391
+ """Try fallback AI services when Gemini is unavailable"""
392
+
393
+ # Try OpenRouter (free models)
394
+ if hasattr(self.translator, '_openrouter_complete'):
395
+ try:
396
+ response, error = self.translator._openrouter_complete(context)
397
+ if response:
398
+ return response.strip(), None
399
+ except Exception:
400
+ pass
401
+
402
+ # Try Groq (free tier)
403
+ if hasattr(self.translator, '_groq_complete'):
404
+ try:
405
+ response, error = self.translator._groq_complete(context)
406
+ if response:
407
+ return response.strip(), None
408
+ except Exception:
409
+ pass
410
+
411
+ # Fallback to simple rule-based responses
412
+ return self._generate_simple_response(context, ui_language)
413
+
414
+ def _generate_simple_response(self, context: str, ui_language: str) -> Tuple[Optional[str], Optional[str]]:
415
+ """Generate simple rule-based response when AI services are unavailable"""
416
+
417
+ # Extract the question from context
418
+ lines = context.split('\n')
419
+ question = ""
420
+ selected_text = ""
421
+
422
+ for line in lines:
423
+ if line.startswith('Current question:'):
424
+ question = line.replace('Current question:', '').strip()
425
+ elif line.startswith('"') and line.endswith('"'):
426
+ selected_text = line.strip('"')
427
+
428
+ if not question or not selected_text:
429
+ return None, "Could not process question"
430
+
431
+ # Simple rule-based responses
432
+ if ui_language == 'ar':
433
+ responses = self._get_arabic_simple_responses(question, selected_text)
434
+ else:
435
+ responses = self._get_english_simple_responses(question, selected_text)
436
+
437
+ return responses
438
+
439
+ def _get_arabic_simple_responses(self, question: str, selected_text: str) -> Tuple[str, None]:
440
+ """Generate simple Arabic responses based on question patterns"""
441
+
442
+ question_lower = question.lower()
443
+
444
+ if any(word in question_lower for word in ['اشرح', 'شرح', 'وضح']):
445
+ response = f"""بناءً على النص المحدد:
446
+ "{selected_text}"
447
+
448
+ هذا النص يتحدث عن موضوع مهم يحتاج إلى فهم عميق. النقاط الرئيسية تشمل المفاهيم والأفكار المطروحة في النص.
449
+
450
+ للحصول على شرح أكثر تفصيلاً، يُنصح بمراجعة مصادر إضافية أو طرح أسئلة أكثر تحديداً.
451
+
452
+ ملاحظة: هذه إجابة مبسطة بسبب عدم توفر خدمة الذكاء الاصطناعي حالياً."""
453
+
454
+ elif any(word in question_lower for word in ['أمثلة', 'مثال', 'تطبيق']):
455
+ response = f"""أمثلة على النص المحدد:
456
+ "{selected_text}"
457
+
458
+ يمكن تطبيق هذا المفهوم في عدة مجالات:
459
+ • في الحياة العملية
460
+ • في الدراسة والتعلم
461
+ • في المشاريع والأعمال
462
+
463
+ للحصول على أمثلة أكثر تفصيلاً، يُنصح بالبحث في مصادر متخصصة.
464
+
465
+ ملاحظة: هذه إجابة مبسطة بسبب عدم توفر خدمة الذكاء الاصطناعي حالياً."""
466
+
467
+ elif any(word in question_lower for word in ['معنى', 'تعريف', 'ما هو']):
468
+ response = f"""معنى النص المحدد:
469
+ "{selected_text}"
470
+
471
+ هذا النص يشير إلى مفهوم أو فكرة معينة تحتاج إلى تفسير. المعنى العام يتعلق بالموضوع المطروح في السياق.
472
+
473
+ للحصول على تعريف أكثر دقة، يُنصح بمراجعة المصادر المتخصصة.
474
+
475
+ ملاحظة: هذه إجابة مبسطة بسبب عدم توفر خدمة الذكاء الاصطناعي حالياً."""
476
+
477
+ else:
478
+ response = f"""بخصوص سؤالك حول:
479
+ "{selected_text}"
480
+
481
+ هذا موضوع مهم يستحق الدراسة والتفكير. النص المحدد يحتوي على معلومات قيمة يمكن الاستفادة منها.
482
+
483
+ للحصول على إجابة أكثر تفصيلاً، يُنصح بـ:
484
+ • مراجعة مصادر إضافية
485
+ • طرح أسئلة أكثر تحديداً
486
+ • البحث في المراجع المتخصصة
487
+
488
+ ملاحظة: هذه إجابة مبسطة بسبب عدم توفر خدمة الذكاء الاصطناعي حالياً."""
489
+
490
+ return response, None
491
+
492
+ def _get_english_simple_responses(self, question: str, selected_text: str) -> Tuple[str, None]:
493
+ """Generate simple English responses based on question patterns"""
494
+
495
+ question_lower = question.lower()
496
+
497
+ if any(word in question_lower for word in ['explain', 'clarify', 'describe']):
498
+ response = f"""Based on the selected text:
499
+ "{selected_text}"
500
+
501
+ This text discusses an important topic that requires deep understanding. The main points include the concepts and ideas presented in the text.
502
+
503
+ For a more detailed explanation, it's recommended to consult additional sources or ask more specific questions.
504
+
505
+ Note: This is a simplified response due to AI service being temporarily unavailable."""
506
+
507
+ elif any(word in question_lower for word in ['example', 'application', 'use']):
508
+ response = f"""Examples related to the selected text:
509
+ "{selected_text}"
510
+
511
+ This concept can be applied in several areas:
512
+ • In practical life
513
+ • In study and learning
514
+ • In projects and work
515
+
516
+ For more detailed examples, it's recommended to search specialized sources.
517
+
518
+ Note: This is a simplified response due to AI service being temporarily unavailable."""
519
+
520
+ elif any(word in question_lower for word in ['mean', 'definition', 'what is']):
521
+ response = f"""Meaning of the selected text:
522
+ "{selected_text}"
523
+
524
+ This text refers to a specific concept or idea that needs interpretation. The general meaning relates to the topic presented in the context.
525
+
526
+ For a more precise definition, it's recommended to consult specialized sources.
527
+
528
+ Note: This is a simplified response due to AI service being temporarily unavailable."""
529
+
530
+ else:
531
+ response = f"""Regarding your question about:
532
+ "{selected_text}"
533
+
534
+ This is an important topic worth studying and thinking about. The selected text contains valuable information that can be beneficial.
535
+
536
+ For a more detailed answer, it's recommended to:
537
+ • Consult additional sources
538
+ • Ask more specific questions
539
+ • Search specialized references
540
+
541
+ Note: This is a simplified response due to AI service being temporarily unavailable."""
542
+
543
+ return response, None
544
+
545
+ def format_ai_response(self, response: str, ui_language: str) -> str:
546
+ """Format AI response for better display"""
547
+
548
+ # Clean up response
549
+ response = response.strip()
550
+
551
+ # Remove markdown artifacts
552
+ response = response.replace('**', '')
553
+ response = response.replace('```', '')
554
+ response = response.replace('`', '')
555
+
556
+ # Remove extra whitespace
557
+ lines = [line.strip() for line in response.split('\n')]
558
+ response = '\n'.join(line for line in lines if line)
559
+
560
+ return response
561
+
562
+ def get_conversation_history(self, session_id: str) -> Optional[QuestionSession]:
563
+ """Get conversation history for a specific session"""
564
+ return self.conversation_history.get(session_id)
565
+
566
+ def clear_conversation(self, session_id: str) -> bool:
567
+ """Clear conversation history for a specific session"""
568
+ if session_id in self.conversation_history:
569
+ del self.conversation_history[session_id]
570
+ return True
571
+ return False
572
+
573
+ def get_all_sessions(self) -> Dict[str, QuestionSession]:
574
+ """Get all active question sessions"""
575
+ return self.conversation_history.copy()
576
+
577
+ def get_model_usage_stats(self) -> Dict[str, int]:
578
+ """Get usage statistics for different AI models"""
579
+ return self.model_usage_stats.copy()
580
+
581
+ def check_model_availability(self) -> Dict[str, Dict[str, Any]]:
582
+ """Check availability status of all AI models"""
583
+ models_status = {}
584
+
585
+ # Check Gemini
586
+ try:
587
+ if hasattr(self.translator, 'model') and self.translator.model:
588
+ # Try a minimal test
589
+ test_response = self.translator.model.generate_content("Hi")
590
+ models_status['Gemini AI'] = {
591
+ 'status': 'available',
592
+ 'icon': '✅',
593
+ 'message': 'متاح' if st.session_state.get('language', 'ar') == 'ar' else 'Available',
594
+ 'color': 'green'
595
+ }
596
+ else:
597
+ models_status['Gemini AI'] = {
598
+ 'status': 'unavailable',
599
+ 'icon': '❌',
600
+ 'message': 'غير متاح' if st.session_state.get('language', 'ar') == 'ar' else 'Unavailable',
601
+ 'color': 'red'
602
+ }
603
+ except Exception as e:
604
+ error_str = str(e)
605
+ if "429" in error_str or "quota" in error_str.lower():
606
+ models_status['Gemini AI'] = {
607
+ 'status': 'quota_exceeded',
608
+ 'icon': '⚠️',
609
+ 'message': 'انتهت الحصة' if st.session_state.get('language', 'ar') == 'ar' else 'Quota exceeded',
610
+ 'color': 'orange'
611
+ }
612
+ else:
613
+ models_status['Gemini AI'] = {
614
+ 'status': 'error',
615
+ 'icon': '❌',
616
+ 'message': 'خطأ مؤقت' if st.session_state.get('language', 'ar') == 'ar' else 'Temporary error',
617
+ 'color': 'red'
618
+ }
619
+
620
+ # Check Groq
621
+ try:
622
+ if hasattr(self.translator, '_groq_complete') and hasattr(self.translator, 'groq_api_key') and self.translator.groq_api_key:
623
+ models_status['Groq AI'] = {
624
+ 'status': 'available',
625
+ 'icon': '✅',
626
+ 'message': 'متاح' if st.session_state.get('language', 'ar') == 'ar' else 'Available',
627
+ 'color': 'green'
628
+ }
629
+ else:
630
+ models_status['Groq AI'] = {
631
+ 'status': 'not_configured',
632
+ 'icon': '⚙️',
633
+ 'message': 'غير مُعد' if st.session_state.get('language', 'ar') == 'ar' else 'Not configured',
634
+ 'color': 'gray'
635
+ }
636
+ except Exception:
637
+ models_status['Groq AI'] = {
638
+ 'status': 'error',
639
+ 'icon': '❌',
640
+ 'message': 'خطأ' if st.session_state.get('language', 'ar') == 'ar' else 'Error',
641
+ 'color': 'red'
642
+ }
643
+
644
+ # Check OpenRouter
645
+ try:
646
+ if hasattr(self.translator, '_openrouter_complete') and hasattr(self.translator, 'openrouter_api_key') and self.translator.openrouter_api_key:
647
+ models_status['OpenRouter AI'] = {
648
+ 'status': 'available',
649
+ 'icon': '✅',
650
+ 'message': 'متاح' if st.session_state.get('language', 'ar') == 'ar' else 'Available',
651
+ 'color': 'green'
652
+ }
653
+ else:
654
+ models_status['OpenRouter AI'] = {
655
+ 'status': 'not_configured',
656
+ 'icon': '⚙️',
657
+ 'message': 'غير مُعد' if st.session_state.get('language', 'ar') == 'ar' else 'Not configured',
658
+ 'color': 'gray'
659
+ }
660
+ except Exception:
661
+ models_status['OpenRouter AI'] = {
662
+ 'status': 'error',
663
+ 'icon': '❌',
664
+ 'message': 'خطأ' if st.session_state.get('language', 'ar') == 'ar' else 'Error',
665
+ 'color': 'red'
666
+ }
667
+
668
+ # Simple Response is always available
669
+ models_status['Simple Response'] = {
670
+ 'status': 'available',
671
+ 'icon': '🛡️',
672
+ 'message': 'متاح دائماً' if st.session_state.get('language', 'ar') == 'ar' else 'Always available',
673
+ 'color': 'blue'
674
+ }
675
+
676
+ return models_status
677
+
678
+ def get_ai_response_with_model(self, context: str, ui_language: str, preferred_model: str = 'auto') -> Tuple[Optional[str], Optional[str], Optional[str]]:
679
+ """Get AI response using a specific model or auto-selection"""
680
+
681
+ if preferred_model == 'auto':
682
+ return self._get_ai_response(context, ui_language)
683
+
684
+ # Try specific model first
685
+ if preferred_model == 'Gemini AI':
686
+ try:
687
+ if hasattr(self.translator, 'model') and self.translator.model:
688
+ response = self.translator.model.generate_content(context)
689
+ if response and hasattr(response, 'text') and response.text:
690
+ self.model_usage_stats['Gemini AI'] += 1
691
+ return response.text.strip(), None, "Gemini AI"
692
+ except Exception as e:
693
+ return None, f"Gemini AI error: {str(e)}", None
694
+
695
+ elif preferred_model == 'Groq AI':
696
+ try:
697
+ if hasattr(self.translator, '_groq_complete'):
698
+ response, error = self.translator._groq_complete(context)
699
+ if response and response.strip():
700
+ self.model_usage_stats['Groq AI'] += 1
701
+ return response.strip(), None, "Groq AI"
702
+ else:
703
+ return None, f"Groq AI error: {error}", None
704
+ except Exception as e:
705
+ return None, f"Groq AI error: {str(e)}", None
706
+
707
+ elif preferred_model == 'OpenRouter AI':
708
+ try:
709
+ if hasattr(self.translator, '_openrouter_complete'):
710
+ response, error = self.translator._openrouter_complete(context)
711
+ if response and response.strip():
712
+ self.model_usage_stats['OpenRouter AI'] += 1
713
+ return response.strip(), None, "OpenRouter AI"
714
+ else:
715
+ return None, f"OpenRouter AI error: {error}", None
716
+ except Exception as e:
717
+ return None, f"OpenRouter AI error: {str(e)}", None
718
+
719
+ elif preferred_model == 'Simple Response':
720
+ simple_response, _ = self._generate_simple_response(context, ui_language)
721
+ self.model_usage_stats['Simple Response'] += 1
722
+ return simple_response, None, "Simple Response"
723
+
724
+ # If preferred model fails, fall back to auto-selection
725
+ return self._get_ai_response(context, ui_language)
726
+
727
+ def format_conversation_for_export(self, session_id: str, ui_language: str = 'ar') -> Optional[str]:
728
+ """Format conversation for export/copying"""
729
+
730
+ session = self.conversation_history.get(session_id)
731
+ if not session:
732
+ return None
733
+
734
+ export_lines = []
735
+
736
+ # Header
737
+ if ui_language == 'ar':
738
+ export_lines.append("محادثة الذكاء الاصطناعي")
739
+ export_lines.append(f"النص المحدد: {session.selected_text}")
740
+ export_lines.append(f"التاريخ: {session.created_at.strftime('%Y-%m-%d %H:%M:%S')}")
741
+ else:
742
+ export_lines.append("AI Conversation")
743
+ export_lines.append(f"Selected Text: {session.selected_text}")
744
+ export_lines.append(f"Date: {session.created_at.strftime('%Y-%m-%d %H:%M:%S')}")
745
+
746
+ export_lines.append("=" * 50)
747
+ export_lines.append("")
748
+
749
+ # Q&A pairs
750
+ for i, qa in enumerate(session.conversation, 1):
751
+ if ui_language == 'ar':
752
+ export_lines.append(f"السؤال {i}: {qa.question}")
753
+ export_lines.append(f"الإجابة {i}: {qa.answer}")
754
+ else:
755
+ export_lines.append(f"Question {i}: {qa.question}")
756
+ export_lines.append(f"Answer {i}: {qa.answer}")
757
+
758
+ export_lines.append("-" * 30)
759
+ export_lines.append("")
760
+
761
+ return "\n".join(export_lines)
762
+
763
+ # Global instance
764
+ ai_question_engine = None
765
+
766
+ def get_ai_question_engine():
767
+ """Get or create AI question engine instance"""
768
+ global ai_question_engine
769
+ if ai_question_engine is None:
770
+ from translator import get_translator
771
+ translator = get_translator()
772
+ ai_question_engine = AIQuestionEngine(translator)
773
+ return ai_question_engine
app.py ADDED
@@ -0,0 +1,1812 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py - Refactored to eliminate recorder_server.py dependency
2
+
3
+ import streamlit as st
4
+ import os
5
+ import tempfile
6
+ import json
7
+ from pathlib import Path
8
+ import time
9
+ import traceback
10
+ import streamlit.components.v1 as components
11
+ import hashlib
12
+ from datetime import datetime
13
+ # from st_audiorec import st_audiorec # Import the new recorder component - OLD
14
+ # Reduce metrics/usage writes that can cause permission errors on hosted environments
15
+ try:
16
+ st.set_option('browser.gatherUsageStats', False)
17
+ except Exception:
18
+ pass
19
+
20
+ # Robust component declaration: prefer local build, else fall back to pip package
21
+ parent_dir = os.path.dirname(os.path.abspath(__file__))
22
+ build_dir = os.path.join(parent_dir, "custom_components/st-audiorec/st_audiorec/frontend/build")
23
+
24
+ def st_audiorec(key=None):
25
+ """Return audio recorder component value, trying local build first, then pip package fallback."""
26
+ try:
27
+ if os.path.isdir(build_dir):
28
+ _component_func = components.declare_component("st_audiorec", path=build_dir)
29
+ return _component_func(key=key, default=0)
30
+ # Fallback to pip-installed component if available
31
+ try:
32
+ from st_audiorec import st_audiorec as st_audiorec_pkg
33
+ return st_audiorec_pkg(key=key)
34
+ except Exception:
35
+ st.warning("Audio recorder component is unavailable on this deployment (missing local build and pip fallback).")
36
+ return None
37
+ except Exception:
38
+ # Final safety net
39
+ st.warning("Failed to initialize audio recorder component.")
40
+ return None
41
+
42
+ # --- Critical Imports and Initial Checks ---
43
+ AUDIO_PROCESSOR_CLASS = None
44
+ IMPORT_ERROR_TRACEBACK = None
45
+ try:
46
+ from audio_processor import AudioProcessor
47
+ AUDIO_PROCESSOR_CLASS = AudioProcessor
48
+ except Exception:
49
+ IMPORT_ERROR_TRACEBACK = traceback.format_exc()
50
+
51
+ from video_generator import VideoGenerator
52
+ from mp3_embedder import MP3Embedder
53
+ from utils import format_timestamp
54
+ from translator import get_translator, UI_TRANSLATIONS
55
+ from exporter import BroadcastExporter, ExportConfig
56
+ from google_docs_config import google_docs_manager
57
+ from ai_questions import get_ai_question_engine, TextSelection
58
+ from style_fixes import apply_custom_styling, create_broadcast_bubble, create_white_container, create_processing_result_container
59
+ import requests
60
+ from dotenv import load_dotenv
61
+
62
+ # --- API Key Check ---
63
+ def check_api_key():
64
+ """Check for Gemini API key and display instructions if not found."""
65
+ load_dotenv()
66
+ if not os.getenv("GEMINI_API_KEY"):
67
+ st.error("🔴 FATAL ERROR: GEMINI_API_KEY is not set!")
68
+ st.info("To fix this, please follow these steps:")
69
+ st.markdown("""
70
+ 1. **Find the file named `.env.example`** in the `syncmaster2` directory.
71
+ 2. **Rename it to `.env`**.
72
+ 3. **Open the `.env` file** with a text editor.
73
+ 4. **Get your free API key** from [Google AI Studio](https://aistudio.google.com/app/apikey).
74
+ 5. **Paste your key** into the file, replacing `"PASTE_YOUR_GEMINI_API_KEY_HERE"`.
75
+ 6. **Save the file and restart the application.**
76
+ """)
77
+ return False
78
+ return True
79
+
80
+ # --- Summary Helper (robust to cached translator without summarize_text) ---
81
+ def generate_summary(text: str, target_language: str = 'ar'):
82
+ """Generate a concise summary in target_language, with graceful fallback.
83
+
84
+ If summarize_text is unavailable (cached instance), fall back to Arabic summary
85
+ then translate to the target language if needed.
86
+ """
87
+ tr = get_translator()
88
+ try:
89
+ if hasattr(tr, 'summarize_text') and callable(getattr(tr, 'summarize_text')):
90
+ s, err = tr.summarize_text(text or '', target_language=target_language)
91
+ if s:
92
+ return s, None
93
+ # Fallback path: Arabic summary first
94
+ s_ar, err_ar = tr.summarize_text_arabic(text or '')
95
+ if target_language and target_language != 'ar' and s_ar:
96
+ tx, err_tx = tr.translate_text(s_ar, target_language=target_language)
97
+ if tx:
98
+ return tx, None
99
+ return s_ar, err_tx
100
+ return s_ar, err_ar
101
+ except Exception as e:
102
+ return None, str(e)
103
+
104
+ # --- Page Configuration ---
105
+ st.set_page_config(
106
+ page_title="SyncMaster - AI Audio-Text Synchronization",
107
+ page_icon="🎵",
108
+ layout="wide"
109
+ )
110
+
111
+ # --- Browser Console Logging Utility ---
112
+ def log_to_browser_console(messages):
113
+ """Injects JavaScript to log messages to the browser's console."""
114
+ if isinstance(messages, str):
115
+ messages = [messages]
116
+ escaped_messages = [json.dumps(str(msg)) for msg in messages]
117
+ js_code = f"""
118
+ <script>
119
+ (function() {{
120
+ const logs = [{', '.join(escaped_messages)}];
121
+ console.group("Backend Logs from SyncMaster");
122
+ logs.forEach(log => {{
123
+ const content = String(log);
124
+ if (content.includes('--- ERROR') || content.includes('--- FATAL')) {{
125
+ console.error(log);
126
+ }} else if (content.includes('--- WARNING')) {{
127
+ console.warn(log);
128
+ }} else if (content.includes('--- DEBUG')) {{
129
+ console.debug(log);
130
+ }} else {{
131
+ console.log(log);
132
+ }}
133
+ }});
134
+ console.groupEnd();
135
+ }})();
136
+ </script>
137
+ """
138
+ components.html(js_code, height=0, scrolling=False)
139
+
140
+ # --- AI Models Reset Function ---
141
+ def reset_ai_models():
142
+ """Reset all AI models and clear cache completely"""
143
+
144
+ # Clear ALL session state keys that might contain cached AI instances
145
+ keys_to_clear = []
146
+ for key in list(st.session_state.keys()):
147
+ if any(term in key.lower() for term in ['translator', 'ai', 'question', 'model', 'engine', 'processing']):
148
+ keys_to_clear.append(key)
149
+
150
+ for key in keys_to_clear:
151
+ del st.session_state[key]
152
+
153
+ # Force reload environment variables
154
+ load_dotenv(override=True)
155
+
156
+ # Clear Python module cache completely
157
+ import sys
158
+ import importlib
159
+
160
+ modules_to_reload = ['translator', 'ai_questions', 'exporter']
161
+ for module_name in modules_to_reload:
162
+ if module_name in sys.modules:
163
+ try:
164
+ # Delete from sys.modules first
165
+ del sys.modules[module_name]
166
+ except:
167
+ pass
168
+
169
+ # Clear any global instances
170
+ try:
171
+ import translator
172
+ if hasattr(translator, 'translator_instance'):
173
+ translator.translator_instance = None
174
+ except:
175
+ pass
176
+
177
+ try:
178
+ import ai_questions
179
+ if hasattr(ai_questions, 'ai_question_engine'):
180
+ ai_questions.ai_question_engine = None
181
+ except:
182
+ pass
183
+
184
+ # --- Session State Initialization ---
185
+ def initialize_session_state():
186
+ """Initializes the session state variables if they don't exist."""
187
+ if 'step' not in st.session_state:
188
+ st.session_state.step = 1
189
+ if 'audio_data' not in st.session_state:
190
+ st.session_state.audio_data = None
191
+ if 'language' not in st.session_state:
192
+ st.session_state.language = 'en'
193
+ if 'enable_translation' not in st.session_state:
194
+ st.session_state.enable_translation = True
195
+ if 'target_language' not in st.session_state:
196
+ st.session_state.target_language = 'ar'
197
+ if 'transcription_data' not in st.session_state:
198
+ st.session_state.transcription_data = None
199
+ if 'edited_text' not in st.session_state:
200
+ st.session_state.edited_text = ""
201
+ if 'video_style' not in st.session_state:
202
+ st.session_state.video_style = {
203
+ 'animation_style': 'Karaoke Style', 'text_color': '#FFFFFF',
204
+ 'highlight_color': '#FFD700', 'background_color': '#000000',
205
+ 'font_family': 'Arial', 'font_size': 48
206
+ }
207
+ if 'new_recording' not in st.session_state:
208
+ st.session_state.new_recording = None
209
+ # Transcript feed (prepend latest) and dedupe set
210
+ if 'transcript_feed' not in st.session_state:
211
+ st.session_state.transcript_feed = [] # list of {id, ts, text}
212
+ if 'transcript_ids' not in st.session_state:
213
+ st.session_state.transcript_ids = set()
214
+ # Incremental broadcast state
215
+ if 'broadcast_segments' not in st.session_state:
216
+ st.session_state.broadcast_segments = [] # [{id, recording_id, start_ms, end_ms, checksum, text}]
217
+ if 'lastFetchedEnd_ms' not in st.session_state:
218
+ st.session_state.lastFetchedEnd_ms = 0
219
+ # Broadcast translation language (separate from general UI translation target)
220
+ if 'broadcast_translation_lang' not in st.session_state:
221
+ # Default broadcast translation target to Arabic
222
+ st.session_state.broadcast_translation_lang = 'ar'
223
+ if 'summary_language' not in st.session_state:
224
+ # Default summary language to Arabic
225
+ st.session_state.summary_language = 'ar'
226
+ # Auto-generate Arabic summary toggle
227
+ if 'auto_generate_summary' not in st.session_state:
228
+ st.session_state.auto_generate_summary = True
229
+ # Export functionality state
230
+ if 'export_timestamp' not in st.session_state:
231
+ st.session_state.export_timestamp = None
232
+ if 'show_export_modal' not in st.session_state:
233
+ st.session_state.show_export_modal = False
234
+ if 'export_format' not in st.session_state:
235
+ st.session_state.export_format = 'word'
236
+ # AI Questions functionality state
237
+ if 'selected_text' not in st.session_state:
238
+ st.session_state.selected_text = None
239
+ if 'selected_segment_id' not in st.session_state:
240
+ st.session_state.selected_segment_id = None
241
+ if 'show_question_modal' not in st.session_state:
242
+ st.session_state.show_question_modal = False
243
+ if 'current_question_session' not in st.session_state:
244
+ st.session_state.current_question_session = None
245
+ if 'preferred_ai_model' not in st.session_state:
246
+ st.session_state.preferred_ai_model = 'auto'
247
+ if 'preferred_answer_language' not in st.session_state:
248
+ st.session_state.preferred_answer_language = 'auto'
249
+ # Background processing state
250
+ if 'processing_queue' not in st.session_state:
251
+ st.session_state.processing_queue = []
252
+ if 'processing_results' not in st.session_state:
253
+ st.session_state.processing_results = {}
254
+ if 'processing_status' not in st.session_state:
255
+ st.session_state.processing_status = {}
256
+
257
+ # --- Background Audio Processing Function ---
258
+ def queue_audio_processing(audio_bytes, original_filename="recorded_audio.wav"):
259
+ """Queue audio for background processing"""
260
+ import uuid
261
+
262
+ # Generate unique ID for this processing task
263
+ task_id = str(uuid.uuid4())[:8]
264
+
265
+ # Add to processing queue
266
+ task = {
267
+ 'id': task_id,
268
+ 'audio_bytes': audio_bytes,
269
+ 'filename': original_filename,
270
+ 'timestamp': time.time(),
271
+ 'status': 'queued'
272
+ }
273
+
274
+ st.session_state.processing_queue.append(task)
275
+ st.session_state.processing_status[task_id] = 'queued'
276
+
277
+ # Show immediate feedback
278
+ st.info(f"🔄 {'تم إضافة التسجيل للمعالجة' if st.session_state.language == 'ar' else 'Audio queued for processing'} (ID: {task_id})")
279
+
280
+ return task_id
281
+
282
+ def process_queued_audio():
283
+ """Process queued audio in background"""
284
+ if not st.session_state.processing_queue:
285
+ return
286
+
287
+ # Process first item in queue
288
+ task = st.session_state.processing_queue[0]
289
+ task_id = task['id']
290
+
291
+ # Update status
292
+ st.session_state.processing_status[task_id] = 'processing'
293
+ task['status'] = 'processing'
294
+
295
+ # Show processing status
296
+ with st.status(f"🔄 {'معالجة التسجيل' if st.session_state.language == 'ar' else 'Processing audio'} {task_id}...", expanded=False):
297
+ try:
298
+ # Process the audio
299
+ result = run_audio_processing_sync(task['audio_bytes'], task['filename'])
300
+
301
+ if result:
302
+ # Store result
303
+ st.session_state.processing_results[task_id] = result
304
+ st.session_state.processing_status[task_id] = 'completed'
305
+ task['status'] = 'completed'
306
+
307
+ st.success(f"✅ {'تم الانتهاء من المعالجة' if st.session_state.language == 'ar' else 'Processing completed'} {task_id}")
308
+ else:
309
+ st.session_state.processing_status[task_id] = 'failed'
310
+ task['status'] = 'failed'
311
+ st.error(f"❌ {'فشلت المعالجة' if st.session_state.language == 'ar' else 'Processing failed'} {task_id}")
312
+
313
+ except Exception as e:
314
+ st.session_state.processing_status[task_id] = 'failed'
315
+ task['status'] = 'failed'
316
+ st.error(f"❌ {'خطأ في المعالجة' if st.session_state.language == 'ar' else 'Processing error'} {task_id}: {str(e)}")
317
+
318
+ # Remove from queue
319
+ st.session_state.processing_queue.pop(0)
320
+
321
+ # --- Centralized Audio Processing Function ---
322
+ def run_audio_processing(audio_bytes, original_filename="recorded_audio.wav"):
323
+ """Main audio processing function with background support"""
324
+
325
+ # Check if background processing is enabled
326
+ if st.session_state.get('background_processing', True):
327
+ return queue_audio_processing(audio_bytes, original_filename)
328
+ else:
329
+ return run_audio_processing_sync(audio_bytes, original_filename)
330
+
331
+ def run_audio_processing_sync(audio_bytes, original_filename="recorded_audio.wav"):
332
+ """
333
+ A single, robust function to handle all audio processing.
334
+ Takes audio bytes as input and returns the processed data.
335
+ """
336
+ # This function is the classic, non-Custom path; ensure editor sections are enabled
337
+ st.session_state['_custom_active'] = False
338
+ if not audio_bytes:
339
+ st.error("No audio data provided to process.")
340
+ return
341
+
342
+ tmp_file_path = None
343
+ log_to_browser_console("--- INFO: Starting unified audio processing. ---")
344
+
345
+ try:
346
+ with tempfile.NamedTemporaryFile(delete=False, suffix=Path(original_filename).suffix) as tmp_file:
347
+ tmp_file.write(audio_bytes)
348
+ tmp_file_path = tmp_file.name
349
+
350
+ processor = AUDIO_PROCESSOR_CLASS()
351
+ result_data = None
352
+ full_text = ""
353
+ word_timestamps = []
354
+
355
+ # Determine which processing path to take
356
+ if st.session_state.enable_translation:
357
+ with st.spinner("⏳ Performing AI Transcription & Translation... please wait."):
358
+ result_data, processor_logs = processor.get_word_timestamps_with_translation(
359
+ tmp_file_path,
360
+ st.session_state.target_language,
361
+ )
362
+
363
+ log_to_browser_console(processor_logs)
364
+
365
+ if not result_data or not result_data.get("original_text"):
366
+ st.warning(
367
+ "Could not generate transcription with translation. Check browser console (F12) for logs."
368
+ )
369
+ return
370
+
371
+ st.session_state.transcription_data = {
372
+ "text": result_data["original_text"],
373
+ "translated_text": result_data["translated_text"],
374
+ "word_timestamps": result_data["word_timestamps"],
375
+ "audio_bytes": audio_bytes,
376
+ "original_suffix": Path(original_filename).suffix,
377
+ "translation_success": result_data.get("translation_success", False),
378
+ "detected_language": result_data.get("language_detected", "unknown"),
379
+ }
380
+ # Update transcript feed (prepend, dedupe by digest)
381
+ try:
382
+ digest = hashlib.md5(audio_bytes).hexdigest()
383
+ except Exception:
384
+ digest = f"snap-{int(time.time()*1000)}"
385
+ if digest not in st.session_state.transcript_ids:
386
+ st.session_state.transcript_ids.add(digest)
387
+ st.session_state.transcript_feed.insert(
388
+ 0,
389
+ {
390
+ "id": digest,
391
+ "ts": int(time.time() * 1000),
392
+ "text": result_data["original_text"],
393
+ },
394
+ )
395
+ # Rebuild edited_text with newest first
396
+ st.session_state.edited_text = "\n\n".join(
397
+ [s["text"] for s in st.session_state.transcript_feed]
398
+ )
399
+
400
+ else: # Standard processing without translation
401
+ with st.spinner("⏳ Performing AI Transcription... please wait."):
402
+ word_timestamps, processor_logs = processor.get_word_timestamps(
403
+ tmp_file_path
404
+ )
405
+
406
+ log_to_browser_console(processor_logs)
407
+
408
+ if not word_timestamps:
409
+ st.warning(
410
+ "Could not generate timestamps. Check browser console (F12) for logs."
411
+ )
412
+ return
413
+
414
+ full_text = " ".join([d["word"] for d in word_timestamps])
415
+ st.session_state.transcription_data = {
416
+ "text": full_text,
417
+ "word_timestamps": word_timestamps,
418
+ "audio_bytes": audio_bytes,
419
+ "original_suffix": Path(original_filename).suffix,
420
+ "translation_success": False,
421
+ }
422
+ # Update transcript feed (prepend, dedupe by digest)
423
+ try:
424
+ digest = hashlib.md5(audio_bytes).hexdigest()
425
+ except Exception:
426
+ digest = f"snap-{int(time.time()*1000)}"
427
+ if digest not in st.session_state.transcript_ids:
428
+ st.session_state.transcript_ids.add(digest)
429
+ st.session_state.transcript_feed.insert(
430
+ 0, {"id": digest, "ts": int(time.time() * 1000), "text": full_text}
431
+ )
432
+ # Rebuild edited_text with newest first
433
+ st.session_state.edited_text = "\n\n".join(
434
+ [s["text"] for s in st.session_state.transcript_feed]
435
+ )
436
+
437
+ st.session_state.step = 1 # Keep it on the same step
438
+
439
+ # Return result for background processing
440
+ return {
441
+ 'original_text': result_data.get("original_text") if result_data else full_text,
442
+ 'translated_text': result_data.get("translated_text") if result_data else None,
443
+ 'detected_language': result_data.get("language_detected") if result_data else "unknown",
444
+ 'translation_success': result_data.get("translation_success", False) if result_data else False,
445
+ 'word_timestamps': result_data.get("word_timestamps") if result_data else word_timestamps
446
+ }
447
+
448
+ except Exception as e:
449
+ st.error("An unexpected error occurred during audio processing!")
450
+ st.exception(e)
451
+ log_to_browser_console(f"--- FATAL ERROR in run_audio_processing: {traceback.format_exc()} ---")
452
+ return None
453
+ finally:
454
+ if tmp_file_path and os.path.exists(tmp_file_path):
455
+ os.unlink(tmp_file_path)
456
+
457
+
458
+ # --- Main Application Logic ---
459
+ def main():
460
+ # Apply custom styling first
461
+ apply_custom_styling()
462
+
463
+ # Force reload environment variables
464
+ load_dotenv(override=True)
465
+
466
+ # Clear AI models cache on first run or if there's an issue
467
+ if 'app_initialized' not in st.session_state:
468
+ reset_ai_models()
469
+ st.session_state.app_initialized = True
470
+
471
+ initialize_session_state()
472
+
473
+ st.markdown("""
474
+ <style>
475
+ .main .block-container { animation: fadeIn 0.2s ease-in-out; }
476
+ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
477
+ .block-container { padding-top: 1rem; }
478
+ </style>
479
+ """, unsafe_allow_html=True)
480
+
481
+ with st.sidebar:
482
+ st.markdown("## 🌐 Language Settings")
483
+ language_options = {'English': 'en', 'العربية': 'ar'}
484
+ selected_lang_display = st.selectbox(
485
+ "Interface Language",
486
+ options=list(language_options.keys()),
487
+ index=0 if st.session_state.language == 'en' else 1
488
+ )
489
+ st.session_state.language = language_options[selected_lang_display]
490
+
491
+ st.markdown("## 🔤 Translation Settings")
492
+ st.session_state.enable_translation = st.checkbox(
493
+ "Enable AI Translation" if st.session_state.language == 'en' else "تفعيل الترجمة بالذكاء الاصطناعي",
494
+ value=st.session_state.enable_translation,
495
+ help="Automatically translate transcribed text" if st.session_state.language == 'en' else "ترجمة النص تلقائياً"
496
+ )
497
+
498
+ if st.session_state.enable_translation:
499
+ target_lang_options = {
500
+ 'Arabic (العربية)': 'ar', 'English': 'en', 'French (Français)': 'fr', 'Spanish (Español)': 'es'
501
+ }
502
+ selected_target = st.selectbox(
503
+ "Target Language" if st.session_state.language == 'en' else "اللغة المستهدفة",
504
+ options=list(target_lang_options.keys()), index=0
505
+ )
506
+ st.session_state.target_language = target_lang_options[selected_target]
507
+ # Auto summary toggle
508
+ st.session_state.auto_generate_summary = st.checkbox(
509
+ "Auto-generate Arabic summary" if st.session_state.language == 'en' else "توليد الملخص العربي تلقائياً",
510
+ value=st.session_state.auto_generate_summary
511
+ )
512
+
513
+ # Google Account Status
514
+ st.markdown("## 🔐 Google Account")
515
+ if google_docs_manager.is_authenticated():
516
+ st.success("✅ متصل" if st.session_state.language == 'ar' else "✅ Connected")
517
+ else:
518
+ st.info("🔒 غير متصل" if st.session_state.language == 'ar' else "🔒 Not connected")
519
+
520
+ # AI Questions Status
521
+ st.markdown("## 🤖 AI Questions")
522
+
523
+ # Show preferred model
524
+ if st.session_state.preferred_ai_model != 'auto':
525
+ st.info(f"🎯 {'النموذج المفضل' if st.session_state.language == 'ar' else 'Preferred Model'}: {st.session_state.preferred_ai_model}")
526
+ else:
527
+ st.info("🔄 " + ("تلقائي" if st.session_state.language == 'ar' else "Auto selection"))
528
+
529
+ # Model reset button
530
+ if st.button("🔄 " + ("إعادة تعيين النماذج" if st.session_state.language == 'ar' else "Reset AI Models"), help="إعادة تحميل نماذج الذكاء الاصطناعي" if st.session_state.language == 'ar' else "Reload AI models"):
531
+ reset_ai_models()
532
+ st.success("✅ " + ("تم إعادة تعيين النماذج" if st.session_state.language == 'ar' else "AI models reset successfully"))
533
+ st.rerun()
534
+
535
+ # Show preferred answer language
536
+ answer_lang = st.session_state.get('preferred_answer_language', 'auto')
537
+ if answer_lang != 'auto':
538
+ lang_names = {'ar': '🇸🇦 العربية', 'en': '🇺🇸 English', 'fr': '🇫🇷 Français', 'es': '🇪🇸 Español', 'de': '🇩🇪 Deutsch', 'zh': '🇨🇳 中文'}
539
+ lang_display = lang_names.get(answer_lang, answer_lang)
540
+ st.info(f"🌐 {'لغة الإجابة' if st.session_state.language == 'ar' else 'Answer Language'}: {lang_display}")
541
+ else:
542
+ current_ui_lang = "🇸🇦 العربية" if st.session_state.language == 'ar' else "🇺🇸 English"
543
+ st.info(f"🌐 {'لغة الإجابة' if st.session_state.language == 'ar' else 'Answer Language'}: {current_ui_lang} ({'تلقائي' if st.session_state.language == 'ar' else 'Auto'})")
544
+
545
+ # Test AI services availability
546
+ translator = get_translator()
547
+ services_status = {}
548
+
549
+ # Test Gemini
550
+ try:
551
+ if hasattr(translator, 'model') and translator.model:
552
+ test_response = translator.model.generate_content("Test")
553
+ services_status['Gemini'] = "✅"
554
+ else:
555
+ services_status['Gemini'] = "❌"
556
+ except Exception as e:
557
+ error_str = str(e)
558
+ if "429" in error_str or "quota" in error_str.lower():
559
+ services_status['Gemini'] = "⚠️"
560
+ else:
561
+ services_status['Gemini'] = "❌"
562
+
563
+ # Test Groq
564
+ try:
565
+ if hasattr(translator, '_groq_complete') and translator.groq_api_key:
566
+ services_status['Groq'] = "✅"
567
+ else:
568
+ services_status['Groq'] = "❌"
569
+ except Exception:
570
+ services_status['Groq'] = "❌"
571
+
572
+ # Test OpenRouter
573
+ try:
574
+ if hasattr(translator, '_openrouter_complete') and translator.openrouter_api_key:
575
+ services_status['OpenRouter'] = "✅"
576
+ else:
577
+ services_status['OpenRouter'] = "❌"
578
+ except Exception:
579
+ services_status['OpenRouter'] = "❌"
580
+
581
+ # Display services status
582
+ st.markdown("**" + ("حالة النماذج" if st.session_state.language == 'ar' else "Models Status") + ":**")
583
+ for service, status in services_status.items():
584
+ if status == "✅":
585
+ st.success(f"{status} {service}")
586
+ elif status == "⚠️":
587
+ st.warning(f"{status} {service} (حد يومي)" if st.session_state.language == 'ar' else f"{status} {service} (quota)")
588
+ else:
589
+ st.error(f"{status} {service}")
590
+
591
+ # Overall status
592
+ available_count = sum(1 for status in services_status.values() if status == "✅")
593
+ if available_count > 0:
594
+ st.info(f"🤖 {available_count}/3 " + ("نماذج متاحة" if st.session_state.language == 'ar' else "models available"))
595
+ else:
596
+ st.warning("⚠️ جميع النماذج غير متاحة" if st.session_state.language == 'ar' else "⚠️ All models unavailable")
597
+
598
+ # Show conversation status and usage stats
599
+ question_engine = get_ai_question_engine()
600
+ usage_stats = question_engine.get_model_usage_stats()
601
+ total_questions = sum(usage_stats.values())
602
+
603
+ if st.session_state.current_question_session:
604
+ session = question_engine.get_conversation_history(st.session_state.current_question_session)
605
+ if session and session.conversation:
606
+ st.info(f"💬 {len(session.conversation)} " + ("أسئلة نشطة" if st.session_state.language == 'ar' else "active questions"))
607
+ else:
608
+ st.info("🤖 جاهز للأسئلة" if st.session_state.language == 'ar' else "🤖 Ready for questions")
609
+ else:
610
+ st.info("🤖 جاهز للأسئلة" if st.session_state.language == 'ar' else "🤖 Ready for questions")
611
+
612
+ # Show usage statistics
613
+ if total_questions > 0:
614
+ with st.expander("📊 إحصائيات الاستخدام" if st.session_state.language == 'ar' else "📊 Usage Statistics"):
615
+ st.write(f"**{'إجمالي الأسئلة' if st.session_state.language == 'ar' else 'Total Questions'}: {total_questions}**")
616
+ for model, count in usage_stats.items():
617
+ if count > 0:
618
+ percentage = (count / total_questions) * 100
619
+ st.write(f"• {model}: {count} ({percentage:.1f}%)")
620
+ else:
621
+ st.caption("📊 لا توجد إحصائيات بعد" if st.session_state.language == 'ar' else "📊 No statistics yet")
622
+
623
+ # Quick model switcher
624
+ st.markdown("**" + ("تبديل سريع للنموذج" if st.session_state.language == 'ar' else "Quick Model Switch") + "**")
625
+
626
+ question_engine = get_ai_question_engine()
627
+ models_status = question_engine.check_model_availability()
628
+
629
+ # Create buttons for each available model
630
+ cols = st.columns(2)
631
+ model_names = ['auto', 'Gemini AI', 'Groq AI', 'OpenRouter AI']
632
+
633
+ for i, model in enumerate(model_names):
634
+ with cols[i % 2]:
635
+ if model == 'auto':
636
+ button_text = "🔄 تلقائي" if st.session_state.language == 'ar' else "🔄 Auto"
637
+ is_current = st.session_state.preferred_ai_model == 'auto'
638
+ else:
639
+ status_info = models_status.get(model, {})
640
+ icon = status_info.get('icon', '❓')
641
+ button_text = f"{icon} {model.split()[0]}" # Show first word + icon
642
+ is_current = st.session_state.preferred_ai_model == model
643
+
644
+ button_type = "primary" if is_current else "secondary"
645
+
646
+ if st.button(button_text, key=f"switch_{model}", type=button_type, use_container_width=True):
647
+ st.session_state.preferred_ai_model = model
648
+ st.rerun()
649
+
650
+ # Quick language switcher
651
+ st.markdown("**" + ("تبديل سريع للغة" if st.session_state.language == 'ar' else "Quick Language Switch") + "**")
652
+
653
+ language_buttons = {
654
+ 'auto': "🔄 تلقائي" if st.session_state.language == 'ar' else "🔄 Auto",
655
+ 'ar': "🇸🇦 عربي",
656
+ 'en': "🇺🇸 EN",
657
+ 'fr': "🇫🇷 FR",
658
+ 'es': "🇪🇸 ES"
659
+ }
660
+
661
+ cols_lang = st.columns(3)
662
+ for i, (lang_code, button_text) in enumerate(language_buttons.items()):
663
+ with cols_lang[i % 3]:
664
+ is_current_lang = st.session_state.get('preferred_answer_language', 'auto') == lang_code
665
+ button_type_lang = "primary" if is_current_lang else "secondary"
666
+
667
+ if st.button(button_text, key=f"switch_lang_{lang_code}", type=button_type_lang, use_container_width=True):
668
+ st.session_state.preferred_answer_language = lang_code
669
+ st.rerun()
670
+
671
+ # Processing status
672
+ if st.session_state.processing_queue or st.session_state.processing_results:
673
+ st.markdown("## 🔄 " + ("حالة المعالجة" if st.session_state.language == 'ar' else "Processing Status"))
674
+
675
+ # Queue status
676
+ if st.session_state.processing_queue:
677
+ queue_count = len(st.session_state.processing_queue)
678
+ st.warning(f"⏳ {queue_count} " + ("في الانتظار" if st.session_state.language == 'ar' else "in queue"))
679
+
680
+ # Results count
681
+ if st.session_state.processing_results:
682
+ results_count = len(st.session_state.processing_results)
683
+ st.success(f"✅ {results_count} " + ("مكتمل" if st.session_state.language == 'ar' else "completed"))
684
+
685
+ # Clear all button
686
+ if st.button("🗑️ " + ("مسح الكل" if st.session_state.language == 'ar' else "Clear All")):
687
+ st.session_state.processing_queue = []
688
+ st.session_state.processing_results = {}
689
+ st.session_state.processing_status = {}
690
+ st.rerun()
691
+
692
+ st.title("🎵 SyncMaster")
693
+ if st.session_state.language == 'ar':
694
+ st.markdown("### منصة المزامنة الذكية بين الصوت والنص")
695
+ else:
696
+ st.markdown("### The Intelligent Audio-Text Synchronization Platform")
697
+
698
+ # Simplified interface - removed step indicators as requested
699
+ # Global settings for long recording retention and custom snapshot duration
700
+ with st.expander("⚙️ Recording Settings (Snapshots)", expanded=False):
701
+ st.session_state.setdefault('retention_minutes', 30)
702
+ # 0 means: use full buffer by default for Custom
703
+ st.session_state.setdefault('custom_snapshot_seconds', 0)
704
+ # Auto-Custom interval seconds (for frontend auto trigger)
705
+ st.session_state.setdefault('auto_custom_interval_sec', 10)
706
+ # Auto-start incremental snapshots when recording begins
707
+ st.session_state.setdefault('auto_start_custom', False)
708
+ st.session_state.retention_minutes = st.number_input("Retention window (minutes)", min_value=5, max_value=240, value=st.session_state.retention_minutes)
709
+ st.session_state.custom_snapshot_seconds = st.number_input("Custom snapshot (seconds; 0 = full buffer)", min_value=0, max_value=3600, value=st.session_state.custom_snapshot_seconds)
710
+ st.session_state.auto_custom_interval_sec = st.number_input("Auto Custom interval (seconds)", min_value=1, max_value=3600, value=st.session_state.auto_custom_interval_sec, help="How often to auto-trigger the same Custom action while recording.")
711
+ st.session_state.auto_start_custom = st.checkbox("Auto-start incremental snapshots on record", value=st.session_state.auto_start_custom, help="Start sending Custom intervals automatically as soon as you start recording.")
712
+ # Inject globals into the page for the component to pick up
713
+ components.html(f"""
714
+ <script>
715
+ window.ST_AREC_RETENTION_MINUTES = {int(st.session_state.retention_minutes)};
716
+ window.ST_AREC_CUSTOM_SNAPSHOT_SECONDS = {int(st.session_state.custom_snapshot_seconds)};
717
+ window.ST_AREC_LAST_FETCHED_END_MS = {int(st.session_state.get('lastFetchedEnd_ms', 0))};
718
+ window.ST_AREC_CUSTOM_AUTO_INTERVAL_SECONDS = {int(st.session_state.get('auto_custom_interval_sec', 10))};
719
+ window.ST_AREC_AUTO_START = {str(bool(st.session_state.get('auto_start_custom', True))).lower()};
720
+ console.log('Recorder config', window.ST_AREC_RETENTION_MINUTES, window.ST_AREC_CUSTOM_SNAPSHOT_SECONDS);
721
+ </script>
722
+ """, height=0)
723
+
724
+ if AUDIO_PROCESSOR_CLASS is None:
725
+ st.error("Fatal Error: The application could not start correctly.")
726
+ st.subheader("An error occurred while trying to import `AudioProcessor`:")
727
+ st.code(IMPORT_ERROR_TRACEBACK, language="python")
728
+ st.stop()
729
+
730
+ step_1_upload_and_process()
731
+
732
+ # Process background queue
733
+ if st.session_state.get('background_processing', True) and st.session_state.processing_queue:
734
+ process_queued_audio()
735
+
736
+ # Show processing results optionally
737
+ if st.session_state.get('show_processing_results', False):
738
+ show_processing_results()
739
+ elif st.session_state.processing_results:
740
+ # Show a button to view results if there are any
741
+ if st.button("📝 " + ("عرض نتائج المعالجة" if st.session_state.language == 'ar' else "Show Processing Results") + f" ({len(st.session_state.processing_results)})", type="secondary"):
742
+ st.session_state.show_processing_results = True
743
+ st.rerun()
744
+
745
+ # Note: step_2_review_and_customize removed as requested
746
+ # Results are now shown in show_processing_results() function
747
+
748
+ # AI Question modal (show outside of other components)
749
+ if st.session_state.show_question_modal:
750
+ show_question_modal()
751
+
752
+ # Export modal (show outside of other components)
753
+ if st.session_state.show_export_modal:
754
+ show_export_modal()
755
+
756
+ # --- Show Processing Results ---
757
+ def show_processing_results():
758
+ """Show processing results in the same page"""
759
+
760
+ if not st.session_state.processing_results:
761
+ return
762
+
763
+ st.markdown("---")
764
+
765
+ # Header with results count and close button
766
+ col_header, col_close = st.columns([4, 1])
767
+
768
+ with col_header:
769
+ results_count = len(st.session_state.processing_results)
770
+ st.subheader(f"📝 {'نتائج المعالجة' if st.session_state.language == 'ar' else 'Processing Results'} ({results_count})")
771
+
772
+ with col_close:
773
+ if st.button("❌ " + ("إخفاء" if st.session_state.language == 'ar' else "Hide"), key="hide_results"):
774
+ st.session_state.show_processing_results = False
775
+ st.rerun()
776
+
777
+ if results_count > 1:
778
+ # Show all results in one view option
779
+ show_all = st.checkbox(
780
+ "عرض جميع النتائج مجمعة" if st.session_state.language == 'ar' else "Show all results combined",
781
+ help="عرض جميع النصوص والترجمات في مكان واحد" if st.session_state.language == 'ar' else "Display all texts and translations in one place"
782
+ )
783
+
784
+ if show_all:
785
+ # Combined view
786
+ st.markdown("### " + ("النصوص الأصلية مجمعة" if st.session_state.language == 'ar' else "Combined Original Texts"))
787
+ combined_original = "\n\n".join([result.get('original_text', '') for result in st.session_state.processing_results.values() if result.get('original_text')])
788
+ if combined_original:
789
+ st.write(combined_original)
790
+
791
+ if st.button("📋 " + ("نسخ جميع النصوص" if st.session_state.language == 'ar' else "Copy All Texts")):
792
+ st.code(combined_original, language=None)
793
+
794
+ st.markdown("### " + ("الترجمات مجمعة" if st.session_state.language == 'ar' else "Combined Translations"))
795
+ combined_translation = "\n\n".join([result.get('translated_text', '') for result in st.session_state.processing_results.values() if result.get('translated_text')])
796
+ if combined_translation:
797
+ st.write(combined_translation)
798
+
799
+ if st.button("📋 " + ("نسخ جميع الترجمات" if st.session_state.language == 'ar' else "Copy All Translations")):
800
+ st.code(combined_translation, language=None)
801
+
802
+ st.markdown("---")
803
+
804
+ # Show results for each completed processing
805
+ for task_id, result in st.session_state.processing_results.items():
806
+ with st.expander(f"🎵 {'التسجيل' if st.session_state.language == 'ar' else 'Recording'} {task_id}", expanded=True):
807
+
808
+ # Original text in white container
809
+ if result.get('original_text'):
810
+ original_title = "النص الأصلي" if st.session_state.language == 'ar' else "Original Text"
811
+ original_container = create_white_container(original_title, result['original_text'], "📝")
812
+ st.markdown(original_container, unsafe_allow_html=True)
813
+
814
+ # Translation in white container
815
+ if result.get('translated_text'):
816
+ translation_title = "الترجمة" if st.session_state.language == 'ar' else "Translation"
817
+ translation_container = create_white_container(translation_title, result['translated_text'], "🌐")
818
+ st.markdown(translation_container, unsafe_allow_html=True)
819
+
820
+ # Language info
821
+ if result.get('detected_language'):
822
+ st.caption(f"🌐 {'اللغة المكتشفة' if st.session_state.language == 'ar' else 'Detected language'}: {result['detected_language']}")
823
+
824
+ # Action buttons
825
+ col1, col2, col3 = st.columns(3)
826
+
827
+ with col1:
828
+ if st.button(f"📋 {'نسخ النص' if st.session_state.language == 'ar' else 'Copy Text'}", key=f"copy_original_{task_id}"):
829
+ st.code(result.get('original_text', ''), language=None)
830
+ st.success("✅ " + ("تم تنسيق النص للنسخ" if st.session_state.language == 'ar' else "Text formatted for copying"))
831
+
832
+ with col2:
833
+ if result.get('translated_text') and st.button(f"📋 {'نسخ الترجمة' if st.session_state.language == 'ar' else 'Copy Translation'}", key=f"copy_translation_{task_id}"):
834
+ st.code(result.get('translated_text', ''), language=None)
835
+ st.success("✅ " + ("تم تنسيق الترجمة للنسخ" if st.session_state.language == 'ar' else "Translation formatted for copying"))
836
+
837
+ with col3:
838
+ if st.button(f"🗑️ {'حذف' if st.session_state.language == 'ar' else 'Delete'}", key=f"delete_{task_id}"):
839
+ del st.session_state.processing_results[task_id]
840
+ if task_id in st.session_state.processing_status:
841
+ del st.session_state.processing_status[task_id]
842
+ st.rerun()
843
+
844
+ # --- Step 1: Upload and Process ---
845
+ def step_1_upload_and_process():
846
+ st.header("🎵 " + ("مصدر الصوت" if st.session_state.language == 'ar' else "Audio Source"))
847
+
848
+ upload_tab, record_tab = st.tabs(["📤 Upload a File", "🎙️ Record Audio"])
849
+
850
+ with upload_tab:
851
+ st.subheader("Upload an existing audio file")
852
+ uploaded_file = st.file_uploader("Choose an audio file", type=['mp3', 'wav', 'm4a'], help="Supported formats: MP3, WAV, M4A")
853
+ if uploaded_file:
854
+ st.session_state.audio_data = uploaded_file.getvalue()
855
+ st.success(f"File ready for processing: {uploaded_file.name}")
856
+ st.audio(st.session_state.audio_data)
857
+ if st.button("🚀 Start AI Processing", type="primary", use_container_width=True):
858
+ run_audio_processing(st.session_state.audio_data, uploaded_file.name)
859
+ if st.session_state.audio_data:
860
+ if st.button("🔄 Use a Different File"):
861
+ reset_session()
862
+ st.rerun()
863
+
864
+ with record_tab:
865
+ st.subheader("Record audio directly from your microphone")
866
+
867
+ # Recording instructions
868
+ # Recording instructions with improved controls
869
+ if st.session_state.language == 'ar':
870
+ st.info("🎙️ **تحكم بسيط في التسجيل:**\n- اضغط الميكروفون لبدء التسجيل\n- اضغط مرة أخرى للتوقف\n- استخدم الأزرار أدناه للتحكم الإضافي")
871
+ else:
872
+ st.info("🎙️ **Simple Recording Controls:**\n- Click microphone to start recording\n- Click again to stop\n- Use buttons below for additional control")
873
+
874
+ # Recording control buttons
875
+ col_record_info, col_record_controls = st.columns([2, 1])
876
+
877
+ with col_record_info:
878
+ # This will show recording status
879
+ pass
880
+
881
+ with col_record_controls:
882
+ # Recording control buttons in a more compact layout
883
+ col_pause, col_resume = st.columns(2)
884
+
885
+ with col_pause:
886
+ if st.button("⏸️ " + ("إيقاف مؤقت" if st.session_state.language == 'ar' else "Pause"),
887
+ help="إيقاف مؤقت للتسجيل" if st.session_state.language == 'ar' else "Pause recording",
888
+ use_container_width=True):
889
+ st.info("💡 " + ("استخدم زر الميكروفون للإيقاف المؤقت" if st.session_state.language == 'ar' else "Use microphone button to pause"))
890
+
891
+ with col_resume:
892
+ if st.button("▶️ " + ("استئناف" if st.session_state.language == 'ar' else "Resume"),
893
+ help="استئناف التسجيل" if st.session_state.language == 'ar' else "Resume recording",
894
+ use_container_width=True):
895
+ st.info("💡 " + ("استخدم زر الميكروفون للاستئناف" if st.session_state.language == 'ar' else "Use microphone button to resume"))
896
+
897
+ # Recording status
898
+ recording_status_placeholder = st.empty()
899
+
900
+ # Use the audio recorder component
901
+ wav_audio_data = st_audiorec()
902
+
903
+ # Show recording status and controls
904
+ if wav_audio_data:
905
+ # Check if wav_audio_data is bytes or dict
906
+ if isinstance(wav_audio_data, bytes):
907
+ # Simple bytes data - show audio player
908
+ recording_status_placeholder.success("🎵 " + ("تسجيل جاهز للمعالجة" if st.session_state.language == 'ar' else "Recording ready for processing"))
909
+
910
+ # Recording controls
911
+ col_play, col_clear = st.columns(2)
912
+
913
+ with col_play:
914
+ st.audio(wav_audio_data, format='audio/wav')
915
+
916
+ with col_clear:
917
+ if st.button("🗑️ " + ("مسح التسجيل" if st.session_state.language == 'ar' else "Clear Recording")):
918
+ st.rerun()
919
+
920
+ elif isinstance(wav_audio_data, dict):
921
+ # Dict data - handle interval processing
922
+ recording_status_placeholder.info("🔄 " + ("معالجة المقاطع..." if st.session_state.language == 'ar' else "Processing intervals..."))
923
+
924
+ # Google Docs Export and Logout Buttons
925
+ col_export, col_logout = st.columns([3, 1])
926
+
927
+ with col_export:
928
+ export_button_text = "📤 تصدير إلى Google Docs" if st.session_state.language == 'ar' else "📤 Export to Google Docs"
929
+ if st.button(export_button_text, type="primary", use_container_width=True):
930
+ export_to_google_docs_directly()
931
+
932
+ with col_logout:
933
+ # Check if user is authenticated
934
+ if google_docs_manager.is_authenticated():
935
+ logout_text = "🚪 خروج" if st.session_state.language == 'ar' else "🚪 Logout"
936
+ if st.button(logout_text, use_container_width=True, help="تسجيل الخروج من Google" if st.session_state.language == 'ar' else "Logout from Google"):
937
+ logout_from_google()
938
+ else:
939
+ # Show login status
940
+ login_status = "غير متصل" if st.session_state.language == 'ar' else "Not logged in"
941
+ st.caption(f"🔒 {login_status}")
942
+
943
+ # Processing settings
944
+ st.markdown("**" + ("إعدادات المعالجة" if st.session_state.language == 'ar' else "Processing Settings") + "**")
945
+
946
+ # Auto-process toggle (changed default to False for better UX)
947
+ st.session_state.setdefault('auto_process_snapshots', False)
948
+ auto_process = st.checkbox(
949
+ "معالجة تلقائية للمقاطع" if st.session_state.language == 'ar' else "Auto-process snapshots",
950
+ key='auto_process_snapshots',
951
+ help="عند التفعيل، يتم معالجة المقاطع تلقائياً أثناء التسجيل" if st.session_state.language == 'ar' else "When enabled, snapshots are processed automatically during recording"
952
+ )
953
+
954
+ # Background processing toggle
955
+ st.session_state.setdefault('background_processing', True)
956
+ background_mode = st.checkbox(
957
+ "معالجة في الخلفية" if st.session_state.language == 'ar' else "Background processing",
958
+ key='background_processing',
959
+ value=True,
960
+ help="يسمح بالاستمرار في استخدام التطبيق أثناء المعالجة" if st.session_state.language == 'ar' else "Allows continued use of the app during processing"
961
+ )
962
+
963
+ if wav_audio_data:
964
+ # Two possible payload shapes: raw bytes array (legacy) or interval payload dict
965
+ if isinstance(wav_audio_data, dict) and wav_audio_data.get('type') in ('interval_wav', 'no_new'):
966
+ payload = wav_audio_data
967
+ # Mark Custom interval flow active so Step 2 editor/style can be hidden
968
+ st.session_state['_custom_active'] = True
969
+ if payload['type'] == 'no_new':
970
+ st.info("No new audio chunks yet.")
971
+ elif payload['type'] == 'interval_wav':
972
+ # Extract interval audio
973
+ b = bytes(payload['bytes'])
974
+ sr = int(payload.get('sr', 16000))
975
+ start_ms = int(payload['start_ms'])
976
+ end_ms = int(payload['end_ms'])
977
+ # Dedupe/trim logic
978
+ if end_ms <= start_ms:
979
+ st.warning("The received interval is empty.")
980
+ else:
981
+ # Prevent overlap with prior segment
982
+ last_end = st.session_state.lastFetchedEnd_ms or 0
983
+ eff_start_ms = max(start_ms, last_end)
984
+ if eff_start_ms < end_ms:
985
+ # If there is overlap, trim the audio bytes accordingly (assumes WAV PCM16 mono header 44 bytes)
986
+ try:
987
+ delta_ms = eff_start_ms - start_ms
988
+ if delta_ms > 0:
989
+ if len(b) >= 44 and b[0:4] == b'RIFF' and b[8:12] == b'WAVE':
990
+ bytes_per_sample = 2 # PCM16 mono
991
+ drop_samples = int(sr * (delta_ms / 1000.0))
992
+ drop_bytes = drop_samples * bytes_per_sample
993
+ data_size = int.from_bytes(b[40:44], 'little') if len(b) >= 44 else len(b) - 44
994
+ pcm = b[44:]
995
+ if drop_bytes < len(pcm):
996
+ pcm_trim = pcm[drop_bytes:]
997
+ else:
998
+ pcm_trim = b''
999
+ new_data_size = len(pcm_trim)
1000
+ # Rebuild header sizes
1001
+ header = bytearray(b[:44])
1002
+ # ChunkSize at offset 4 = 36 + Subchunk2Size
1003
+ (36 + new_data_size).to_bytes(4, 'little')
1004
+ header[4:8] = (36 + new_data_size).to_bytes(4, 'little')
1005
+ # Subchunk2Size at offset 40
1006
+ header[40:44] = new_data_size.to_bytes(4, 'little')
1007
+ b = bytes(header) + pcm_trim
1008
+ else:
1009
+ # Not a recognizable WAV header; keep as-is
1010
+ pass
1011
+ except Exception as _:
1012
+ pass
1013
+ # Compute checksum
1014
+ digest = hashlib.md5(b).hexdigest()
1015
+ # Skip if identical checksum and same window
1016
+ exists = any(s.get('checksum') == digest and s.get('start_ms') == eff_start_ms and s.get('end_ms') == end_ms for s in st.session_state.broadcast_segments)
1017
+ if not exists:
1018
+ # Show spinner during extraction so the user sees a waiting icon until text appears
1019
+ with st.spinner("⏳ Extracting text from interval..."):
1020
+ # Run standard pipeline to get text (no translation to keep it light)
1021
+ # Reuse run_audio_processing internals via a temp path
1022
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.wav') as tf:
1023
+ tf.write(b)
1024
+ tmp_path = tf.name
1025
+ try:
1026
+ processor = AUDIO_PROCESSOR_CLASS()
1027
+ word_timestamps, processor_logs, model_used = processor.get_word_timestamps(tmp_path)
1028
+ full_text = " ".join([d['word'] for d in word_timestamps]) if word_timestamps else ""
1029
+ # Fallback: if timestamps extraction yielded no words, try plain transcription
1030
+ if not full_text:
1031
+ plain_text, err, fallback_model = processor.transcribe_audio(tmp_path)
1032
+ if plain_text:
1033
+ full_text = plain_text.strip()
1034
+ model_used = fallback_model
1035
+ finally:
1036
+ if os.path.exists(tmp_path): os.unlink(tmp_path)
1037
+
1038
+ # Append segment immediately with only the original text
1039
+ seg = {
1040
+ 'id': digest,
1041
+ 'recording_id': payload.get('session_id', 'local'),
1042
+ 'start_ms': eff_start_ms,
1043
+ 'end_ms': end_ms,
1044
+ 'checksum': digest,
1045
+ 'text': full_text,
1046
+ 'translations': {},
1047
+ 'transcription_model': model_used,
1048
+ }
1049
+ st.session_state.broadcast_segments.append(seg)
1050
+ st.session_state.broadcast_segments.sort(key=lambda s: s['start_ms'])
1051
+ st.session_state.lastFetchedEnd_ms = end_ms
1052
+ if full_text:
1053
+ if digest not in st.session_state.transcript_ids:
1054
+ st.session_state.transcript_ids.add(digest)
1055
+ st.session_state.transcript_feed.insert(
1056
+ 0,
1057
+ {
1058
+ "id": digest,
1059
+ "ts": int(time.time() * 1000),
1060
+ "text": full_text,
1061
+ },
1062
+ )
1063
+ st.session_state.edited_text = "\n\n".join(
1064
+ [s["text"] for s in st.session_state.transcript_feed]
1065
+ )
1066
+ st.success(f"Added new segment: {eff_start_ms/1000:.2f}s → {end_ms/1000:.2f}s")
1067
+
1068
+ # Now, asynchronously update translation and summary after segment is added
1069
+ def update_translation_and_summary():
1070
+ try:
1071
+ if full_text and st.session_state.get('enable_translation', True):
1072
+ translator = get_translator()
1073
+ sel_lang = st.session_state.get('broadcast_translation_lang', 'ar')
1074
+ tx, _ = translator.translate_text(full_text, target_language=sel_lang)
1075
+ if tx:
1076
+ seg['translations'][sel_lang] = tx
1077
+ except Exception:
1078
+ pass
1079
+ # Update summary
1080
+ if st.session_state.get('auto_generate_summary', True):
1081
+ try:
1082
+ source_text = " \n".join([s.get('text', '') for s in st.session_state.broadcast_segments if s.get('text')])
1083
+ if source_text.strip():
1084
+ summary, _ = generate_summary(source_text, target_language=st.session_state.get('summary_language', 'ar'))
1085
+ if summary:
1086
+ st.session_state.arabic_explanation = summary
1087
+ except Exception:
1088
+ pass
1089
+ import threading
1090
+ threading.Thread(target=update_translation_and_summary, daemon=True).start()
1091
+ else:
1092
+ st.info("Duplicate segment ignored.")
1093
+ else:
1094
+ st.info("No new parts after the last point.")
1095
+ else:
1096
+ # Legacy: treat as full wav bytes
1097
+ bytes_data = bytes(wav_audio_data)
1098
+ # This is not the Custom interval mode
1099
+ st.session_state['_custom_active'] = False
1100
+ st.session_state.audio_data = bytes_data
1101
+ st.audio(bytes_data)
1102
+ digest = hashlib.md5(bytes_data).hexdigest()
1103
+ last_digest = st.session_state.get('_last_component_digest')
1104
+ if st.session_state.auto_process_snapshots and digest != last_digest:
1105
+ st.session_state['_last_component_digest'] = digest
1106
+ task_id = run_audio_processing(bytes_data, "snapshot.wav")
1107
+ if task_id:
1108
+ st.success(f"🔄 {'تم إضافة المقطع للمعالجة' if st.session_state.language == 'ar' else 'Snapshot queued for processing'}")
1109
+ else:
1110
+ # Simple single button for processing
1111
+ if st.button("📝 " + ("استخراج النص" if st.session_state.language == 'ar' else "Extract Text"), type="primary", use_container_width=True):
1112
+ st.session_state['_last_component_digest'] = digest
1113
+ task_id = run_audio_processing(bytes_data, "recorded_audio.wav")
1114
+ if task_id:
1115
+ st.success(f"✅ {'تم إضافة التسجيل للمعالجة' if st.session_state.language == 'ar' else 'Audio queued for processing'}")
1116
+
1117
+ # Simplified: removed external live slice server UI to avoid complexity
1118
+
1119
+ # Always show Broadcast view in Step 1 as well (regardless of transcription_data)
1120
+ with st.expander("📻 Broadcast (latest first)", expanded=True):
1121
+ # Language selector for broadcast translations
1122
+ try:
1123
+ translator = get_translator()
1124
+ langs = translator.get_supported_languages()
1125
+ codes = list(langs.keys())
1126
+ labels = ["detect language — Arabic (العربية)"] + [f"{code} — {langs[code]}" for code in codes]
1127
+ current = st.session_state.get('broadcast_translation_lang', 'ar')
1128
+ # If not set, default to 'detect'
1129
+ if current not in codes and current != 'detect':
1130
+ current = 'detect'
1131
+ default_index = 0 if current == 'detect' else (codes.index(current) + 1 if current in codes else 1)
1132
+ sel_label = st.selectbox("Broadcast translation language", labels, index=default_index)
1133
+ if sel_label.startswith("detect language"):
1134
+ sel_code = 'detect'
1135
+ else:
1136
+ sel_code = sel_label.split(' — ')[0]
1137
+ st.session_state.broadcast_translation_lang = sel_code
1138
+ except Exception:
1139
+ sel_code = st.session_state.get('broadcast_translation_lang', 'ar')
1140
+
1141
+ if st.session_state.broadcast_segments:
1142
+ for s in sorted(st.session_state.broadcast_segments, key=lambda s: s['start_ms'], reverse=True):
1143
+ # Create unique segment ID
1144
+ segment_id = s.get('id', f"seg_{s['start_ms']}_{s['end_ms']}")
1145
+
1146
+ # Original text with selection capability
1147
+ original_text = s.get('text', '')
1148
+ if original_text:
1149
+ # Check if this segment is selected
1150
+ is_selected = (st.session_state.selected_segment_id == segment_id)
1151
+
1152
+ # Create timestamp for bubble
1153
+ timestamp = f"{s['start_ms']/1000:.1f}s → {s['end_ms']/1000:.1f}s"
1154
+
1155
+ # Create columns for bubble and ask button
1156
+ col_bubble, col_ask = st.columns([5, 1])
1157
+
1158
+ with col_bubble:
1159
+ # Display text as chat bubble
1160
+ bubble_html = create_broadcast_bubble(original_text, timestamp, is_selected)
1161
+ st.markdown(bubble_html, unsafe_allow_html=True)
1162
+
1163
+ if is_selected:
1164
+ st.success("🔍 " + ("هذا النص محدد للأسئلة" if st.session_state.language == 'ar' else "This text is selected for questions"))
1165
+
1166
+ with col_ask:
1167
+ # Ask AI button
1168
+ ask_button_text = "🤖 اسأل" if st.session_state.language == 'ar' else "🤖 Ask"
1169
+ button_type = "primary" if not is_selected else "secondary"
1170
+ if st.button(ask_button_text, key=f"ask_{segment_id}", type=button_type, use_container_width=True, help="اسأل الذكاء الاصطناعي عن هذا النص" if st.session_state.language == 'ar' else "Ask AI about this text"):
1171
+ # Select this text and open question modal
1172
+ st.session_state.selected_text = original_text
1173
+ st.session_state.selected_segment_id = segment_id
1174
+ st.session_state.show_question_modal = True
1175
+ st.rerun()
1176
+
1177
+ # Show model used for transcription
1178
+ model_note = s.get('transcription_model', None)
1179
+ if model_note:
1180
+ st.caption(f"Model used: {model_note}")
1181
+
1182
+ # Ensure and show translation in selected language
1183
+ if s.get('text') and st.session_state.get('enable_translation', True):
1184
+ if 'translations' not in s or not isinstance(s.get('translations'), dict):
1185
+ s['translations'] = {}
1186
+ # Detect language and translate if 'detect' is selected
1187
+ if sel_code == 'detect':
1188
+ # Use detected language from segment if available, else fallback to 'ar'
1189
+ detected_lang = s.get('detected_language', None)
1190
+ target_lang = 'ar' # Always translate to Arabic in detect mode
1191
+ if target_lang not in s['translations']:
1192
+ try:
1193
+ tx, _ = get_translator().translate_text(s.get('text', ''), target_language=target_lang)
1194
+ if tx:
1195
+ s['translations'][target_lang] = tx
1196
+ except Exception:
1197
+ pass
1198
+ if s['translations'].get(target_lang):
1199
+ st.caption(f"Translation (AR):")
1200
+ st.write(s['translations'][target_lang])
1201
+ else:
1202
+ if sel_code not in s['translations']:
1203
+ try:
1204
+ tx, _ = get_translator().translate_text(s.get('text', ''), target_language=sel_code)
1205
+ if tx:
1206
+ s['translations'][sel_code] = tx
1207
+ except Exception:
1208
+ pass
1209
+ if s['translations'].get(sel_code):
1210
+ st.caption(f"Translation ({sel_code.upper()}):")
1211
+ st.write(s['translations'][sel_code])
1212
+ st.divider()
1213
+ else:
1214
+ st.caption("No segments yet. Use the Custom button while recording.")
1215
+
1216
+
1217
+
1218
+ # --- Google Logout Function ---
1219
+ def logout_from_google():
1220
+ """Logout from Google account"""
1221
+ try:
1222
+ success = google_docs_manager.logout()
1223
+
1224
+ if success:
1225
+ st.success("تم تسجيل الخروج بنجاح!" if st.session_state.language == 'ar' else "Successfully logged out!")
1226
+ st.info("يمكنك الآن تسجيل الدخول بحساب آخر" if st.session_state.language == 'ar' else "You can now login with a different account")
1227
+ # Force rerun to update UI
1228
+ time.sleep(1)
1229
+ st.rerun()
1230
+ else:
1231
+ st.error("خطأ في تسجيل الخروج" if st.session_state.language == 'ar' else "Error during logout")
1232
+
1233
+ except Exception as e:
1234
+ st.error(f"خطأ غير متوقع: {str(e)}" if st.session_state.language == 'ar' else f"Unexpected error: {str(e)}")
1235
+
1236
+ # --- Direct Google Docs Export Function ---
1237
+ def export_to_google_docs_directly():
1238
+ """Export broadcast segments directly to Google Docs without conditions"""
1239
+
1240
+ try:
1241
+ # Get current segments (all segments, not filtered by timestamp)
1242
+ segments = st.session_state.broadcast_segments or []
1243
+
1244
+ # Show progress
1245
+ with st.spinner("جاري ��لتصدير إلى Google Docs..." if st.session_state.language == 'ar' else "Exporting to Google Docs..."):
1246
+ # Export directly
1247
+ doc_url, error = google_docs_manager.export_broadcast_to_docs(
1248
+ segments,
1249
+ ui_language=st.session_state.language
1250
+ )
1251
+
1252
+ if doc_url and not error:
1253
+ st.success("تم إنشاء المستند بنجاح!" if st.session_state.language == 'ar' else "Document created successfully!")
1254
+
1255
+ # Show clickable link
1256
+ if st.session_state.language == 'ar':
1257
+ st.markdown(f"🔗 [فتح المستند في Google Docs]({doc_url})")
1258
+ st.info("💡 نصيحة: اضغط على الرابط أعلاه لفتح المستند في تبويب جديد")
1259
+ else:
1260
+ st.markdown(f"🔗 [Open Document in Google Docs]({doc_url})")
1261
+ st.info("💡 Tip: Click the link above to open the document in a new tab")
1262
+
1263
+ # Also show the URL for copying
1264
+ st.code(doc_url, language=None)
1265
+
1266
+ # Show current user info
1267
+ if google_docs_manager.is_authenticated():
1268
+ st.caption("✅ متصل بحساب Google" if st.session_state.language == 'ar' else "✅ Connected to Google account")
1269
+
1270
+ else:
1271
+ error_msg = error or "Unknown error occurred"
1272
+ st.error(f"خطأ في التصدير: {error_msg}" if st.session_state.language == 'ar' else f"Export error: {error_msg}")
1273
+
1274
+ # Show setup instructions if credentials are missing
1275
+ if "credentials" in error_msg.lower() or "authentication" in error_msg.lower():
1276
+ st.info("📋 يرجى مراجعة ملف GOOGLE_SETUP.md لإعداد Google Docs" if st.session_state.language == 'ar' else "📋 Please check GOOGLE_SETUP.md for Google Docs setup instructions")
1277
+
1278
+ except Exception as e:
1279
+ st.error(f"خطأ غير متوقع: {str(e)}" if st.session_state.language == 'ar' else f"Unexpected error: {str(e)}")
1280
+
1281
+ # --- AI Question Modal Function ---
1282
+ def show_question_modal():
1283
+ """Display AI question modal for selected text"""
1284
+
1285
+ if not st.session_state.selected_text:
1286
+ st.session_state.show_question_modal = False
1287
+ return
1288
+
1289
+ # Get AI question engine
1290
+ question_engine = get_ai_question_engine()
1291
+
1292
+ # Modal header
1293
+ st.subheader("🤖 اسأل الذكاء الاصطناعي" if st.session_state.language == 'ar' else "🤖 Ask AI")
1294
+
1295
+ # Show selected text
1296
+ with st.expander("النص المحدد" if st.session_state.language == 'ar' else "Selected Text", expanded=True):
1297
+ st.write(f"📝 {st.session_state.selected_text}")
1298
+
1299
+ # Show conversation history if exists
1300
+ if st.session_state.current_question_session:
1301
+ session = question_engine.get_conversation_history(st.session_state.current_question_session)
1302
+ if session and session.conversation:
1303
+ with st.expander(f"💬 تاريخ المحادثة ({len(session.conversation)} أسئلة)" if st.session_state.language == 'ar' else f"💬 Conversation History ({len(session.conversation)} questions)", expanded=False):
1304
+ for i, qa in enumerate(session.conversation, 1):
1305
+ st.markdown(f"**{i}. {qa.question}**")
1306
+ st.write(qa.answer)
1307
+
1308
+ # Show timing and model info
1309
+ model_info = getattr(qa, 'model_used', 'Unknown')
1310
+ model_color = "green" if "Gemini" in model_info else "orange" if "Groq" in model_info or "OpenRouter" in model_info else "red"
1311
+
1312
+ caption_text = f"⏱️ {qa.timestamp.strftime('%H:%M:%S')} - {qa.response_time_ms}ms"
1313
+ model_text = f"🔧 {model_info}"
1314
+
1315
+ st.caption(caption_text)
1316
+ st.markdown(f"<small style='color: {model_color}'>{model_text}</small>", unsafe_allow_html=True)
1317
+
1318
+ if i < len(session.conversation):
1319
+ st.divider()
1320
+
1321
+ # Model selection (moved to top)
1322
+ st.markdown("**" + ("اختيار النموذج" if st.session_state.language == 'ar' else "Model Selection") + "**")
1323
+
1324
+ # Get model availability
1325
+ models_status = question_engine.check_model_availability()
1326
+
1327
+ # Create model options with status indicators
1328
+ model_options = {}
1329
+ for model_name, status_info in models_status.items():
1330
+ display_name = f"{status_info['icon']} {model_name} - {status_info['message']}"
1331
+ model_options[display_name] = model_name
1332
+
1333
+ # Add auto option
1334
+ auto_text = "🔄 تلقائي (أفضل نموذج متاح)" if st.session_state.language == 'ar' else "🔄 Auto (Best available model)"
1335
+ model_options = {auto_text: 'auto', **model_options}
1336
+
1337
+ # Find current selection index
1338
+ current_model = st.session_state.get('preferred_ai_model', 'auto')
1339
+ current_index = 0
1340
+ for i, (display_name, model_name) in enumerate(model_options.items()):
1341
+ if model_name == current_model:
1342
+ current_index = i
1343
+ break
1344
+
1345
+ # Model selector
1346
+ selected_model_display = st.selectbox(
1347
+ "النموذج المفضل" if st.session_state.language == 'ar' else "Preferred Model",
1348
+ options=list(model_options.keys()),
1349
+ index=current_index,
1350
+ help="اختر النموذج المفضل للإجابة" if st.session_state.language == 'ar' else "Choose preferred model for answering"
1351
+ )
1352
+
1353
+ selected_model = model_options[selected_model_display]
1354
+ st.session_state.preferred_ai_model = selected_model
1355
+
1356
+ # Show model status details
1357
+ if selected_model != 'auto':
1358
+ status_info = models_status[selected_model]
1359
+ if status_info['status'] != 'available':
1360
+ if status_info['status'] == 'quota_exceeded':
1361
+ st.warning("⚠️ هذا النموذج استنفد حصته اليومية" if st.session_state.language == 'ar' else "⚠️ This model has exceeded its daily quota")
1362
+ elif status_info['status'] == 'not_configured':
1363
+ st.info("ℹ️ هذا النموذج غير مُعد - سيتم استخدام البديل" if st.session_state.language == 'ar' else "ℹ️ This model is not configured - fallback will be used")
1364
+ else:
1365
+ st.error("❌ هذا النموذج غير متاح حالياً" if st.session_state.language == 'ar' else "❌ This model is currently unavailable")
1366
+
1367
+ # Language selection for answers
1368
+ st.markdown("**" + ("لغة الإجابة" if st.session_state.language == 'ar' else "Answer Language") + "**")
1369
+
1370
+ # Language options
1371
+ language_options = {
1372
+ "🔄 تلقائي (حسب لغة الواجهة)" if st.session_state.language == 'ar' else "🔄 Auto (Interface language)": 'auto',
1373
+ "🇸🇦 العربية": 'ar',
1374
+ "🇺🇸 English": 'en',
1375
+ "🇫🇷 Français": 'fr',
1376
+ "🇪🇸 Español": 'es',
1377
+ "🇩🇪 Deutsch": 'de',
1378
+ "🇨🇳 中文": 'zh'
1379
+ }
1380
+
1381
+ # Find current language selection
1382
+ current_lang = st.session_state.get('preferred_answer_language', 'auto')
1383
+ current_lang_index = 0
1384
+ for i, (display_name, lang_code) in enumerate(language_options.items()):
1385
+ if lang_code == current_lang:
1386
+ current_lang_index = i
1387
+ break
1388
+
1389
+ # Language selector
1390
+ selected_language_display = st.selectbox(
1391
+ "لغة الإجابة المفضلة" if st.session_state.language == 'ar' else "Preferred Answer Language",
1392
+ options=list(language_options.keys()),
1393
+ index=current_lang_index,
1394
+ help="اختر اللغة التي تريد الحصول على الإجابة بها" if st.session_state.language == 'ar' else "Choose the language for AI responses"
1395
+ )
1396
+
1397
+ selected_answer_language = language_options[selected_language_display]
1398
+ st.session_state.preferred_answer_language = selected_answer_language
1399
+
1400
+ # Show language info
1401
+ if selected_answer_language == 'auto':
1402
+ current_ui_lang = "العربية" if st.session_state.language == 'ar' else "English"
1403
+ st.caption(f"ℹ️ سيتم استخدام لغة الواجهة الحالية: {current_ui_lang}" if st.session_state.language == 'ar' else f"ℹ️ Will use current interface language: {current_ui_lang}")
1404
+ else:
1405
+ lang_names = {'ar': 'العربية', 'en': 'English', 'fr': 'Français', 'es': 'Español', 'de': 'Deutsch', 'zh': '中文'}
1406
+ selected_lang_name = lang_names.get(selected_answer_language, selected_answer_language)
1407
+ st.caption(f"ℹ️ الإجابات ستكون باللغة: {selected_lang_name}" if st.session_state.language == 'ar' else f"ℹ️ Answers will be in: {selected_lang_name}")
1408
+
1409
+ # Question templates
1410
+ st.markdown("**" + ("قوالب الأسئلة السريعة" if st.session_state.language == 'ar' else "Quick Question Templates") + "**")
1411
+
1412
+ templates = question_engine.get_question_templates(st.session_state.language)
1413
+
1414
+ # Display templates as buttons in columns
1415
+ cols = st.columns(2)
1416
+ for i, template in enumerate(templates[:6]): # Show first 6 templates
1417
+ with cols[i % 2]:
1418
+ if st.button(template, key=f"template_{i}", use_container_width=True):
1419
+ # Process template question
1420
+ process_ai_question(template, is_template=True, preferred_model=selected_model, answer_language=selected_answer_language)
1421
+ return
1422
+
1423
+ # Custom question input
1424
+ st.markdown("**" + ("أو اكتب سؤالك الخاص" if st.session_state.language == 'ar' else "Or Write Your Own Question") + "**")
1425
+
1426
+ custom_question = st.text_area(
1427
+ "سؤالك" if st.session_state.language == 'ar' else "Your Question",
1428
+ placeholder="اكتب سؤالك هنا..." if st.session_state.language == 'ar' else "Type your question here...",
1429
+ height=100
1430
+ )
1431
+
1432
+ # Action buttons
1433
+ col_ask, col_cancel = st.columns(2)
1434
+
1435
+ with col_ask:
1436
+ if st.button("🚀 اسأل" if st.session_state.language == 'ar' else "🚀 Ask", type="primary", disabled=not custom_question.strip()):
1437
+ if custom_question.strip():
1438
+ process_ai_question(custom_question.strip(), is_template=False, preferred_model=selected_model, answer_language=selected_answer_language)
1439
+ return
1440
+
1441
+ with col_cancel:
1442
+ if st.button("❌ إلغاء" if st.session_state.language == 'ar' else "❌ Cancel"):
1443
+ st.session_state.show_question_modal = False
1444
+ st.session_state.selected_text = None
1445
+ st.session_state.selected_segment_id = None
1446
+ st.rerun()
1447
+
1448
+ def process_ai_question(question: str, is_template: bool = False, preferred_model: str = 'auto', answer_language: str = 'auto'):
1449
+ """Process AI question and show response"""
1450
+
1451
+ question_engine = get_ai_question_engine()
1452
+
1453
+ # Prepare segment info
1454
+ segment_info = {
1455
+ 'id': st.session_state.selected_segment_id,
1456
+ 'start_ms': 0, # We'll get this from the actual segment if needed
1457
+ 'end_ms': 0
1458
+ }
1459
+
1460
+ # Show processing indicator
1461
+ with st.spinner("جاري معالجة سؤالك..." if st.session_state.language == 'ar' else "Processing your question..."):
1462
+ # Determine answer language
1463
+ if answer_language == 'auto':
1464
+ answer_lang = st.session_state.language
1465
+ else:
1466
+ answer_lang = answer_language
1467
+
1468
+ # Process question
1469
+ result = question_engine.process_question(
1470
+ selected_text=st.session_state.selected_text,
1471
+ question=question,
1472
+ segment_info=segment_info,
1473
+ ui_language=answer_lang, # Use selected answer language
1474
+ session_id=st.session_state.current_question_session,
1475
+ preferred_model=preferred_model
1476
+ )
1477
+
1478
+ # Handle different return formats for backward compatibility
1479
+ if len(result) == 4:
1480
+ answer, error, session_id, model_used = result
1481
+ else:
1482
+ answer, error, session_id = result
1483
+ model_used = "Unknown"
1484
+
1485
+ # Update session ID
1486
+ st.session_state.current_question_session = session_id
1487
+
1488
+ if answer:
1489
+ # Check response type and model fallback
1490
+ is_simple_response = "ملاحظة: هذه إجابة مبسطة" in answer or "Note: This is a simplified response" in answer
1491
+ preferred_model = st.session_state.get('preferred_ai_model', 'auto')
1492
+ model_fallback = preferred_model != 'auto' and preferred_model != model_used
1493
+
1494
+ if is_simple_response:
1495
+ st.warning("⚠️ خدمة الذكاء الاصطناعي غير متاحة حالياً - إجابة مبسطة" if st.session_state.language == 'ar' else "⚠️ AI service temporarily unavailable - simplified response")
1496
+ elif model_fallback:
1497
+ st.info(f"ℹ️ النموذج المفضل ({preferred_model}) غير متاح - تم استخدام {model_used}" if st.session_state.language == 'ar' else f"ℹ️ Preferred model ({preferred_model}) unavailable - used {model_used}")
1498
+ else:
1499
+ st.success("تم الحصول على الإجابة!" if st.session_state.language == 'ar' else "Got the answer!")
1500
+
1501
+ # Display Q&A
1502
+ st.markdown("### " + ("السؤال" if st.session_state.language == 'ar' else "Question"))
1503
+ st.write(f"❓ {question}")
1504
+
1505
+ st.markdown("### " + ("الإجابة" if st.session_state.language == 'ar' else "Answer"))
1506
+ st.write(f"🤖 {answer}")
1507
+
1508
+ # Show which model was used with enhanced styling
1509
+ if model_used:
1510
+ # Get model status for better display
1511
+ question_engine = get_ai_question_engine()
1512
+ models_status = question_engine.check_model_availability()
1513
+
1514
+ model_info = models_status.get(model_used, {})
1515
+ icon = model_info.get('icon', '🤖')
1516
+ color = model_info.get('color', 'gray')
1517
+
1518
+ # Show if user's preferred model was used or fallback occurred
1519
+ preferred_model = st.session_state.get('preferred_ai_model', 'auto')
1520
+ if preferred_model != 'auto' and preferred_model != model_used:
1521
+ fallback_msg = " (تم التبديل للبديل)" if st.session_state.language == 'ar' else " (fallback used)"
1522
+ color = "orange"
1523
+ else:
1524
+ fallback_msg = ""
1525
+
1526
+ model_display = f"{icon} {model_used}{fallback_msg}"
1527
+
1528
+ # Show answer language info
1529
+ answer_lang = st.session_state.get('preferred_answer_language', 'auto')
1530
+ if answer_lang == 'auto':
1531
+ lang_display = "تلقائي" if st.session_state.language == 'ar' else "Auto"
1532
+ lang_flag = "🔄"
1533
+ else:
1534
+ lang_flags = {'ar': '🇸🇦', 'en': '🇺🇸', 'fr': '🇫🇷', 'es': '🇪🇸', 'de': '🇩🇪', 'zh': '🇨🇳'}
1535
+ lang_names = {'ar': 'العربية', 'en': 'English', 'fr': 'Français', 'es': 'Español', 'de': 'Deutsch', 'zh': '中文'}
1536
+ lang_flag = lang_flags.get(answer_lang, '🌐')
1537
+ lang_display = lang_names.get(answer_lang, answer_lang)
1538
+
1539
+ info_text = f"🔧 {'النموذج' if st.session_state.language == 'ar' else 'Model'}: {model_display} | 🌐 {'اللغة' if st.session_state.language == 'ar' else 'Language'}: {lang_flag} {lang_display}"
1540
+
1541
+ st.markdown(f"<div style='background-color: rgba(0,0,0,0.1); padding: 8px; border-radius: 5px; margin: 5px 0;'><small style='color: {color}'>{info_text}</small></div>", unsafe_allow_html=True)
1542
+
1543
+ # Show additional help for simple responses
1544
+ if is_simple_response:
1545
+ with st.expander("💡 نصائح للحصول على إجابات أفضل" if st.session_state.language == 'ar' else "💡 Tips for better answers"):
1546
+ if st.session_state.language == 'ar':
1547
+ st.markdown("""
1548
+ **لماذا الإجابة مبسطة؟**
1549
+ - تم استنفاد الحد اليومي لخدمة Gemini AI المجانية (50 طلب/يوم)
1550
+ - النظام يستخدم إجابات مبسطة كبديل مؤقت
1551
+
1552
+ **للحصول على إجابات أفضل:**
1553
+ - حاول مرة أخرى غداً (يتم تجديد الحد اليومي)
1554
+ - اطرح أسئلة أكثر تحديداً
1555
+ - ابحث في مصادر إضافية للموضوع
1556
+ """)
1557
+ else:
1558
+ st.markdown("""
1559
+ **Why is the answer simplified?**
1560
+ - Daily limit for free Gemini AI service exceeded (50 requests/day)
1561
+ - System is using simplified responses as temporary fallback
1562
+
1563
+ **For better answers:**
1564
+ - Try again tomorrow (daily limit resets)
1565
+ - Ask more specific questions
1566
+ - Search additional sources for the topic
1567
+ """)
1568
+
1569
+
1570
+ # Action buttons for the response
1571
+ col_copy, col_follow, col_close = st.columns(3)
1572
+
1573
+ with col_copy:
1574
+ if st.button("📋 نسخ" if st.session_state.language == 'ar' else "📋 Copy"):
1575
+ # Format for copying
1576
+ copy_text = f"السؤال: {question}\nالإجابة: {answer}" if st.session_state.language == 'ar' else f"Question: {question}\nAnswer: {answer}"
1577
+ st.code(copy_text, language=None)
1578
+ st.success("تم تنسيق النص للنسخ أعلاه" if st.session_state.language == 'ar' else "Text formatted for copying above")
1579
+
1580
+ with col_follow:
1581
+ if st.button("➕ سؤال متابعة" if st.session_state.language == 'ar' else "➕ Follow-up"):
1582
+ # Keep modal open for follow-up question
1583
+ st.rerun()
1584
+
1585
+ with col_close:
1586
+ if st.button("✅ إغلاق" if st.session_state.language == 'ar' else "✅ Close"):
1587
+ st.session_state.show_question_modal = False
1588
+ st.session_state.selected_text = None
1589
+ st.session_state.selected_segment_id = None
1590
+ st.rerun()
1591
+
1592
+ else:
1593
+ # Show error
1594
+ st.error(f"خطأ: {error}" if st.session_state.language == 'ar' else f"Error: {error}")
1595
+
1596
+ # Retry and close buttons
1597
+ col_retry, col_close = st.columns(2)
1598
+
1599
+ with col_retry:
1600
+ if st.button("🔄 إعادة المحاولة" if st.session_state.language == 'ar' else "🔄 Retry"):
1601
+ process_ai_question(question, is_template)
1602
+ return
1603
+
1604
+ with col_close:
1605
+ if st.button("❌ إغلاق" if st.session_state.language == 'ar' else "❌ Close"):
1606
+ st.session_state.show_question_modal = False
1607
+ st.session_state.selected_text = None
1608
+ st.session_state.selected_segment_id = None
1609
+ st.rerun()
1610
+
1611
+ # --- Export Modal Function ---
1612
+ def show_export_modal():
1613
+ """Display export modal with preview and options"""
1614
+
1615
+ # Initialize Google Docs auth
1616
+ if 'google_auth' not in st.session_state:
1617
+ st.session_state.google_auth = GoogleDocsAuth()
1618
+
1619
+ google_auth = st.session_state.google_auth
1620
+
1621
+ # Filter segments from export timestamp
1622
+ if not st.session_state.export_timestamp or not st.session_state.broadcast_segments:
1623
+ st.session_state.show_export_modal = False
1624
+ return
1625
+
1626
+ # Get segments after export timestamp
1627
+ filtered_segments = []
1628
+ for segment in st.session_state.broadcast_segments:
1629
+ if segment.get('start_ms', 0) >= st.session_state.export_timestamp:
1630
+ filtered_segments.append(segment)
1631
+
1632
+ # Sort by start time (oldest first for export)
1633
+ filtered_segments.sort(key=lambda s: s.get('start_ms', 0))
1634
+
1635
+ if not filtered_segments:
1636
+ st.warning("لا توجد مقاطع جديدة للتصدير منذ الضغط على الزر" if st.session_state.language == 'ar' else "No new segments to export since button press")
1637
+ if st.button("إغلاق" if st.session_state.language == 'ar' else "Close"):
1638
+ st.session_state.show_export_modal = False
1639
+ st.rerun()
1640
+ return
1641
+
1642
+ # Export preview
1643
+ st.subheader("📋 معاينة التصدير" if st.session_state.language == 'ar' else "📋 Export Preview")
1644
+
1645
+ export_time = datetime.fromtimestamp(st.session_state.export_timestamp / 1000)
1646
+ st.info(f"{'المقاطع من وقت' if st.session_state.language == 'ar' else 'Segments from'}: {export_time.strftime('%H:%M:%S')}")
1647
+ st.info(f"{'عدد المقاطع' if st.session_state.language == 'ar' else 'Number of segments'}: {len(filtered_segments)}")
1648
+
1649
+ # Show preview of segments
1650
+ with st.expander("معاينة المحتوى" if st.session_state.language == 'ar' else "Content Preview", expanded=False):
1651
+ for i, segment in enumerate(filtered_segments[:3]): # Show first 3 segments
1652
+ start_time = segment.get('start_ms', 0) / 1000
1653
+ end_time = segment.get('end_ms', 0) / 1000
1654
+ st.markdown(f"**[{start_time:.2f}s → {end_time:.2f}s]**")
1655
+ st.write(segment.get('text', '')[:100] + "..." if len(segment.get('text', '')) > 100 else segment.get('text', ''))
1656
+ if i < 2 and i < len(filtered_segments) - 1:
1657
+ st.divider()
1658
+
1659
+ if len(filtered_segments) > 3:
1660
+ st.caption(f"... {'و' if st.session_state.language == 'ar' else 'and'} {len(filtered_segments) - 3} {'مقاطع أخرى' if st.session_state.language == 'ar' else 'more segments'}")
1661
+
1662
+ # Export options
1663
+ col1, col2 = st.columns(2)
1664
+
1665
+ with col1:
1666
+ format_options = {
1667
+ "📄 Word Document": "word",
1668
+ "📝 Google Docs": "google_docs"
1669
+ }
1670
+ selected_format = st.selectbox(
1671
+ "تنسيق التصدير" if st.session_state.language == 'ar' else "Export Format",
1672
+ options=list(format_options.keys()),
1673
+ index=0
1674
+ )
1675
+ st.session_state.export_format = format_options[selected_format]
1676
+
1677
+ with col2:
1678
+ include_summary = st.checkbox(
1679
+ "تضمين الملخص" if st.session_state.language == 'ar' else "Include Summary",
1680
+ value=True
1681
+ )
1682
+
1683
+ # Google Docs authentication section
1684
+ if st.session_state.export_format == 'google_docs':
1685
+ st.markdown("---")
1686
+ if google_auth.is_authenticated():
1687
+ st.success("✅ " + ("متصل بـ Google Docs" if st.session_state.language == 'ar' else "Connected to Google Docs"))
1688
+ col_logout, col_info = st.columns([1, 2])
1689
+ with col_logout:
1690
+ if st.button("🚪 " + ("تسجيل خروج" if st.session_state.language == 'ar' else "Logout")):
1691
+ google_auth.logout()
1692
+ st.rerun()
1693
+ with col_info:
1694
+ st.caption("سيتم إنشاء المستند في حسابك على Google" if st.session_state.language == 'ar' else "Document will be created in your Google account")
1695
+ else:
1696
+ st.warning("🔐 " + ("يجب تسجيل الدخول إلى Google Docs أولاً" if st.session_state.language == 'ar' else "Please authenticate with Google Docs first"))
1697
+
1698
+ # Handle OAuth callback
1699
+ auth_code = st.query_params.get("code")
1700
+ if auth_code:
1701
+ success, message = google_auth.handle_auth_callback(auth_code)
1702
+ if success:
1703
+ st.success(message)
1704
+ # Clear the code from URL
1705
+ st.query_params.clear()
1706
+ st.rerun()
1707
+ else:
1708
+ st.error(message)
1709
+
1710
+ # Show authentication button
1711
+ auth_url, error = google_auth.get_auth_url()
1712
+ if auth_url:
1713
+ st.markdown(f"""
1714
+ <a href="{auth_url}" target="_blank">
1715
+ <button style="background-color: #4285f4; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer;">
1716
+ 🔗 {"تسجيل الدخول إلى Google" if st.session_state.language == 'ar' else "Sign in with Google"}
1717
+ </button>
1718
+ </a>
1719
+ """, unsafe_allow_html=True)
1720
+ st.caption("سيتم فتح نافذة جديدة للمصادقة" if st.session_state.language == 'ar' else "A new window will open for authentication")
1721
+ else:
1722
+ st.error(f"خطأ في إعداد Google API: {error}" if st.session_state.language == 'ar' else f"Google API setup error: {error}")
1723
+ st.info("يرجى إعداد GOOGLE_CLIENT_ID و GOOGLE_CLIENT_SECRET في متغيرات البيئة" if st.session_state.language == 'ar' else "Please set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET environment variables")
1724
+
1725
+ # Export buttons
1726
+ col_export, col_cancel = st.columns(2)
1727
+
1728
+ with col_export:
1729
+ # Disable export button if Google Docs is selected but not authenticated
1730
+ export_disabled = (st.session_state.export_format == 'google_docs' and not google_auth.is_authenticated())
1731
+ export_button_text = "🚀 تصدير" if st.session_state.language == 'ar' else "🚀 Export"
1732
+
1733
+ if st.button(export_button_text, type="primary", disabled=export_disabled):
1734
+ perform_export(filtered_segments, include_summary, google_auth)
1735
+
1736
+ with col_cancel:
1737
+ if st.button("❌ إلغاء" if st.session_state.language == 'ar' else "❌ Cancel"):
1738
+ st.session_state.show_export_modal = False
1739
+ st.rerun()
1740
+
1741
+ # --- Export Execution Function ---
1742
+ def perform_export(segments, include_summary=True, google_auth=None):
1743
+ """Perform the actual export operation"""
1744
+
1745
+ try:
1746
+ # Initialize exporter
1747
+ translator = get_translator()
1748
+ exporter = BroadcastExporter(translator)
1749
+
1750
+ # Create export configuration
1751
+ config = ExportConfig(
1752
+ export_timestamp=st.session_state.export_timestamp,
1753
+ format_type=st.session_state.export_format,
1754
+ include_summary=include_summary,
1755
+ ui_language=st.session_state.language,
1756
+ target_language=st.session_state.get('broadcast_translation_lang', 'ar')
1757
+ )
1758
+
1759
+ # Prepare export content
1760
+ content = exporter.prepare_export_content(segments, config)
1761
+
1762
+ # Show progress
1763
+ with st.spinner("جاري التصدير..." if st.session_state.language == 'ar' else "Exporting..."):
1764
+ # Perform export with fallback
1765
+ result, error = exporter.export_with_fallback(content, config, google_auth)
1766
+
1767
+ if result and not error:
1768
+ if config.format_type == 'word':
1769
+ # Provide download link for Word document
1770
+ with open(result, 'rb') as file:
1771
+ st.download_button(
1772
+ label="📥 تحميل الملف" if st.session_state.language == 'ar' else "📥 Download File",
1773
+ data=file.read(),
1774
+ file_name=os.path.basename(result),
1775
+ mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
1776
+ )
1777
+ st.success("تم إنشاء الملف بنجاح!" if st.session_state.language == 'ar' else "File created successfully!")
1778
+ else:
1779
+ # Google Docs URL
1780
+ st.success("تم إنشاء المستند بنجاح!" if st.session_state.language == 'ar' else "Document created successfully!")
1781
+ st.markdown(f"[فتح في Google Docs]({result})" if st.session_state.language == 'ar' else f"[Open in Google Docs]({result})")
1782
+ else:
1783
+ st.error(f"خطأ في التصدير: {error}" if st.session_state.language == 'ar' else f"Export error: {error}")
1784
+
1785
+ except Exception as e:
1786
+ st.error(f"خطأ غير متوقع: {str(e)}" if st.session_state.language == 'ar' else f"Unexpected error: {str(e)}")
1787
+
1788
+ # Close modal after export attempt
1789
+ if st.button("إغلاق" if st.session_state.language == 'ar' else "Close"):
1790
+ st.session_state.show_export_modal = False
1791
+ st.rerun()
1792
+
1793
+ # Note: external live slice helper removed to keep the app simple and fully local
1794
+
1795
+ # --- Step 2: Review and Customize (REMOVED) ---
1796
+ # This section was removed as requested by user to simplify the interface
1797
+ # Results are now shown directly in show_processing_results() function
1798
+
1799
+ def reset_session():
1800
+ """Resets the session state by clearing specific keys and re-initializing."""
1801
+ log_to_browser_console("--- INFO: Resetting session state. ---")
1802
+ keys_to_clear = ['step', 'audio_data', 'transcription_data', 'edited_text', 'video_style', 'new_recording']
1803
+ for key in keys_to_clear:
1804
+ if key in st.session_state:
1805
+ del st.session_state[key]
1806
+ initialize_session_state()
1807
+
1808
+ # --- Entry Point ---
1809
+ if __name__ == "__main__":
1810
+ if check_api_key():
1811
+ initialize_session_state()
1812
+ main()
app_config.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Configuration Module for SyncMaster
3
+ إعدادات التطبيق الأساسية
4
+ """
5
+
6
+ import os
7
+ import logging
8
+
9
+ # Configure logging
10
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
11
+
12
+ class AppConfig:
13
+ """Configuration class for SyncMaster application"""
14
+
15
+ # Server settings
16
+ STREAMLIT_PORT = int(os.getenv('STREAMLIT_PORT', 5050))
17
+ RECORDER_PORT = int(os.getenv('RECORDER_PORT', 5001))
18
+
19
+ # Development vs Production
20
+ IS_PRODUCTION = os.getenv('SPACE_ID') is not None or os.getenv('RAILWAY_ENVIRONMENT') is not None
21
+
22
+ # Host settings
23
+ if IS_PRODUCTION:
24
+ STREAMLIT_HOST = "0.0.0.0"
25
+ RECORDER_HOST = "0.0.0.0"
26
+ else:
27
+ STREAMLIT_HOST = "localhost"
28
+ RECORDER_HOST = "localhost"
29
+
30
+ # Integration settings
31
+ USE_INTEGRATED_SERVER = IS_PRODUCTION or os.getenv('USE_INTEGRATED_SERVER', 'true').lower() == 'true'
32
+
33
+ # Logging
34
+ LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO')
35
+
36
+ @classmethod
37
+ def get_streamlit_url(cls):
38
+ """Get the Streamlit application URL"""
39
+ return f"http://{cls.STREAMLIT_HOST}:{cls.STREAMLIT_PORT}"
40
+
41
+ @classmethod
42
+ def get_recorder_url(cls):
43
+ """Get the recorder server URL"""
44
+ return f"http://{cls.RECORDER_HOST}:{cls.RECORDER_PORT}"
45
+
46
+ @classmethod
47
+ def log_config(cls):
48
+ """Log current configuration"""
49
+ logging.info("📋 SyncMaster Configuration:")
50
+ logging.info(f" • Production Mode: {cls.IS_PRODUCTION}")
51
+ logging.info(f" • Integrated Server: {cls.USE_INTEGRATED_SERVER}")
52
+ logging.info(f" • Streamlit: {cls.get_streamlit_url()}")
53
+ logging.info(f" • Recorder: {cls.get_recorder_url()}")
54
+
55
+ # Initialize configuration
56
+ config = AppConfig()
57
+
58
+ if __name__ == "__main__":
59
+ config.log_config()
app_launcher.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ App Launcher - يشغل التطبيق مع الخادم المدمج
4
+ """
5
+
6
+ import os
7
+ import sys
8
+
9
+ # Add current directory to path
10
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
11
+
12
+ # Force start integrated server
13
+ print("🚀 Starting integrated recorder server...")
14
+ try:
15
+ from integrated_server import integrated_server
16
+
17
+ # Force start the server
18
+ if not integrated_server.is_running:
19
+ result = integrated_server.start_recorder_server()
20
+ if result:
21
+ print("✅ Recorder server started successfully")
22
+ else:
23
+ print("⚠️ Warning: Could not start recorder server")
24
+ else:
25
+ print("✅ Recorder server already running")
26
+
27
+ except Exception as e:
28
+ print(f"❌ Error starting recorder server: {e}")
29
+
30
+ print("📱 Loading main application...")
31
+
32
+ # Execute the app.py content directly
33
+ if __name__ == "__main__":
34
+ # If running directly, execute app.py
35
+ exec(open('app.py').read())
36
+ else:
37
+ # If imported by Streamlit, import and execute
38
+ try:
39
+ exec(open('app.py').read())
40
+ print("✅ Application loaded successfully")
41
+ except Exception as e:
42
+ print(f"❌ Error loading application: {e}")
43
+ raise
audio_processor.py ADDED
@@ -0,0 +1,391 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # audio_processor.py - Enhanced with AI Translation Support
2
+
3
+ import os
4
+ from dotenv import load_dotenv
5
+ import tempfile
6
+ from typing import List, Dict, Optional, Tuple
7
+ import json
8
+ import traceback
9
+
10
+ # --- DEFINITIVE NUMBA FIX ---
11
+ # This MUST be done BEFORE importing librosa
12
+ os.environ["NUMBA_CACHE_DIR"] = "/tmp"
13
+
14
+ # Now, import librosa safely
15
+ import librosa
16
+ # --- END OF FIX ---
17
+
18
+ import google.generativeai as genai
19
+ from translator import AITranslator
20
+ import requests
21
+ from google.api_core import exceptions as google_exceptions
22
+
23
+ class AudioProcessor:
24
+ def __init__(self):
25
+ self.translator = None
26
+ self.init_error = None
27
+ self._initialize_translator()
28
+
29
+ def _initialize_translator(self):
30
+ """Initialize AI translator for multi-language support"""
31
+ try:
32
+ self.translator = AITranslator()
33
+ if self.translator.init_error:
34
+ print(f"--- WARNING: Translator has initialization error: {self.translator.init_error} ---")
35
+ except Exception as e:
36
+ print(f"--- WARNING: Translator initialization failed: {str(e)} ---")
37
+ self.translator = None
38
+
39
+ def transcribe_audio(self, audio_file_path: str) -> Tuple[Optional[str], Optional[str], Optional[str]]:
40
+ """
41
+ Transcribes audio. Returns (text, error_message).
42
+ Uses Gemini first (if available), then falls back to Groq Whisper.
43
+ """
44
+ if not os.path.exists(audio_file_path):
45
+ return None, f"--- ERROR: Audio file for transcription not found at: {audio_file_path} ---", None
46
+
47
+ # Try Gemini first if available
48
+ gemini_err = None
49
+ try:
50
+ if self.translator and self.translator.model:
51
+ audio_file = genai.upload_file(path=audio_file_path)
52
+ prompt = (
53
+ "You are an ASR system. Transcribe the audio accurately. "
54
+ "Auto-detect the spoken language and return ONLY the verbatim transcript in that same language. "
55
+ "Do not translate. Do not add labels or timestamps."
56
+ )
57
+ response = self.translator.model.generate_content([prompt, audio_file])
58
+ if response and hasattr(response, 'text') and response.text:
59
+ return response.text.strip(), None, "Gemini"
60
+ else:
61
+ gemini_err = "--- WARNING: Gemini returned an empty response for transcription. ---"
62
+ except google_exceptions.ResourceExhausted:
63
+ gemini_err = "--- QUOTA ERROR: You have exceeded the daily free usage limit for the AI service. Please wait for your quota to reset (usually within 24 hours) or upgrade your Google AI plan. ---"
64
+ except Exception:
65
+ gemini_err = f"--- FATAL ERROR during Gemini transcription: {traceback.format_exc()} ---"
66
+
67
+ # Fallback: Groq Whisper
68
+ text, groq_err = self._transcribe_with_groq(audio_file_path)
69
+ if text:
70
+ return text, None, "Groq Whisper"
71
+
72
+ # If all failed
73
+ combined_err = groq_err or gemini_err or "--- ERROR: No transcription provider available. ---"
74
+ return None, combined_err, None
75
+
76
+ def _transcribe_with_groq(self, audio_file_path: str) -> Tuple[Optional[str], Optional[str]]:
77
+ """Transcribe using Groq Whisper-compatible endpoint. Returns (text, error)."""
78
+ try:
79
+ load_dotenv()
80
+ groq_key = os.getenv("GROQ_API_KEY")
81
+ if not groq_key:
82
+ return None, "--- ERROR: GROQ_API_KEY not set. ---"
83
+ model = os.getenv("GROQ_WHISPER_MODEL", "whisper-large-v3")
84
+ url = "https://api.groq.com/openai/v1/audio/transcriptions"
85
+ headers = {"Authorization": f"Bearer {groq_key}"}
86
+ # Guess mime type by extension
87
+ filename = os.path.basename(audio_file_path)
88
+ mime = "audio/wav"
89
+ if filename.lower().endswith(".mp3"):
90
+ mime = "audio/mpeg"
91
+ elif filename.lower().endswith(".m4a"):
92
+ mime = "audio/mp4"
93
+ data = {
94
+ "model": model,
95
+ "response_format": "json",
96
+ }
97
+ with open(audio_file_path, "rb") as f:
98
+ files = {"file": (filename, f, mime)}
99
+ resp = requests.post(url, headers=headers, files=files, data=data, timeout=60)
100
+ if not resp.ok:
101
+ try:
102
+ err = resp.json()
103
+ except Exception:
104
+ err = {"error": resp.text}
105
+ return None, f"--- ERROR: Groq transcription error {resp.status_code}: {err} ---"
106
+ out = resp.json()
107
+ text = out.get("text")
108
+ if not text:
109
+ return None, "--- ERROR: Groq transcription returned no text. ---"
110
+ return text.strip(), None
111
+ except Exception:
112
+ return None, f"--- FATAL ERROR during Groq transcription: {traceback.format_exc()} ---"
113
+
114
+ def get_audio_duration(self, audio_file_path: str) -> Tuple[Optional[float], Optional[str]]:
115
+ """
116
+ Gets audio duration. Returns (duration, error_message).
117
+ """
118
+ try:
119
+ if not os.path.exists(audio_file_path):
120
+ return None, f"--- ERROR: Audio file for duration not found at: {audio_file_path} ---"
121
+
122
+ duration = librosa.get_duration(path=audio_file_path)
123
+ if duration is None or duration < 0.1:
124
+ return None, f"--- ERROR: librosa returned an invalid duration: {duration}s ---"
125
+ return duration, None
126
+ except Exception as e:
127
+ error_msg = f"--- FATAL ERROR getting audio duration with librosa: {traceback.format_exc()} ---"
128
+ return None, error_msg
129
+
130
+ def get_word_timestamps(self, audio_file_path: str) -> Tuple[List[Dict], List[str], Optional[str]]:
131
+ """
132
+ Generates timestamps. Returns (timestamps, log_messages).
133
+ """
134
+ logs = ["--- INFO: Starting get_word_timestamps... ---"]
135
+
136
+ transcription, error, model_used = self.transcribe_audio(audio_file_path)
137
+ if error:
138
+ logs.append(error)
139
+ return [], logs, model_used
140
+ logs.append(f"--- DEBUG: Transcription successful. Text: '{transcription[:50]}...'")
141
+
142
+ audio_duration, error = self.get_audio_duration(audio_file_path)
143
+ if error:
144
+ logs.append(error)
145
+ return [], logs, model_used
146
+ logs.append(f"--- DEBUG: Audio duration successful. Duration: {audio_duration:.2f}s")
147
+
148
+ words = transcription.split()
149
+ if not words:
150
+ logs.append("--- WARNING: Transcription resulted in zero words. ---")
151
+ return [], logs, model_used
152
+
153
+ logs.append(f"--- INFO: Distributing {len(words)} words across the duration. ---")
154
+ word_timestamps = []
155
+ total_words = len(words)
156
+ usable_duration = max(0, audio_duration - 1.0)
157
+
158
+ for i, word in enumerate(words):
159
+ start_time = 0.5 + (i * (usable_duration / total_words))
160
+ end_time = 0.5 + ((i + 1) * (usable_duration / total_words))
161
+ word_timestamps.append({'word': word.strip(), 'start': round(start_time, 3), 'end': round(end_time, 3)})
162
+
163
+ logs.append(f"--- SUCCESS: Generated {len(word_timestamps)} word timestamps. ---")
164
+ return word_timestamps, logs, model_used
165
+
166
+ def get_word_timestamps_with_translation(self, audio_file_path: str, target_language: str = 'ar') -> Tuple[Dict, List[str]]:
167
+ """
168
+ Enhanced function that provides both transcription and translation
169
+
170
+ Args:
171
+ audio_file_path: Path to audio file
172
+ target_language: Target language for translation ('ar' for Arabic)
173
+
174
+ Returns:
175
+ Tuple of (result_dict, log_messages)
176
+ result_dict contains: {
177
+ 'original_text': str,
178
+ 'translated_text': str,
179
+ 'word_timestamps': List[Dict],
180
+ 'translated_timestamps': List[Dict],
181
+ 'language_detected': str,
182
+ 'target_language': str
183
+ }
184
+ """
185
+ logs = ["--- INFO: Starting enhanced transcription with translation... ---"]
186
+
187
+ # Get original transcription and timestamps
188
+ word_timestamps, transcription_logs, model_used = self.get_word_timestamps(audio_file_path)
189
+ logs.extend(transcription_logs)
190
+
191
+ if not word_timestamps:
192
+ # Fallback: try plain transcription (Gemini → Groq) then synthesize timestamps
193
+ logs.append("--- INFO: Falling back to plain transcription because timestamps are empty. ---")
194
+ plain_text, err, model_used_fallback = self.transcribe_audio(audio_file_path)
195
+ if model_used_fallback:
196
+ model_used = model_used_fallback
197
+ if not plain_text:
198
+ logs.append(err or "--- ERROR: Plain transcription fallback failed ---")
199
+ return {}, logs
200
+ logs.append(f"--- SUCCESS: Plain transcription fallback succeeded. Model: {model_used}")
201
+ # Synthesize naive word-level timestamps across duration
202
+ try:
203
+ duration, derr = self.get_audio_duration(audio_file_path)
204
+ if derr:
205
+ logs.append(derr)
206
+ duration = 0.0
207
+ words = plain_text.split()
208
+ if not words:
209
+ logs.append("--- WARNING: Fallback transcription produced zero words. ---")
210
+ return {}, logs
211
+ if duration and duration > 0.1:
212
+ usable_duration = max(0, duration - 1.0)
213
+ start_offset = 0.5
214
+ else:
215
+ # If duration not available, assume ~0.4s per word
216
+ usable_duration = 0.4 * max(1, len(words))
217
+ start_offset = 0.0
218
+ word_timestamps = []
219
+ total_words = len(words)
220
+ for i, w in enumerate(words):
221
+ start_time = start_offset + (i * (usable_duration / total_words))
222
+ end_time = start_offset + ((i + 1) * (usable_duration / total_words))
223
+ word_timestamps.append({'word': w.strip(), 'start': round(start_time, 3), 'end': round(end_time, 3)})
224
+ logs.append(f"--- INFO: Synthesized {len(word_timestamps)} timestamps from fallback transcript. ---")
225
+ except Exception:
226
+ logs.append(f"--- FATAL ERROR synthesizing timestamps: {traceback.format_exc()} ---")
227
+ return {}, logs
228
+
229
+ # Extract original text
230
+ original_text = " ".join([d['word'] for d in word_timestamps])
231
+ logs.append(f"--- INFO: Original transcription: '{original_text[:50]}...' ---")
232
+
233
+ # Initialize result dictionary
234
+ result = {
235
+ 'original_text': original_text,
236
+ 'translated_text': '',
237
+ 'word_timestamps': word_timestamps,
238
+ 'translated_timestamps': [],
239
+ 'language_detected': 'unknown',
240
+ 'target_language': target_language,
241
+ 'translation_success': False,
242
+ 'transcription_model': model_used
243
+ }
244
+
245
+ # Check if translator is available
246
+ if not self.translator:
247
+ logs.append("--- WARNING: Translator not available, returning original text only ---")
248
+ result['translated_text'] = original_text
249
+ return result, logs
250
+
251
+ try:
252
+ # Translate the text
253
+ translated_text, translation_error = self.translator.translate_text(
254
+ original_text,
255
+ target_language=target_language
256
+ )
257
+
258
+ if translated_text:
259
+ result['translated_text'] = translated_text
260
+ result['translation_success'] = True
261
+ logs.append(f"--- SUCCESS: Translation completed: '{translated_text[:50]}...' ---")
262
+
263
+ # Create translated timestamps by mapping words
264
+ translated_timestamps = self._create_translated_timestamps(
265
+ word_timestamps,
266
+ original_text,
267
+ translated_text
268
+ )
269
+ result['translated_timestamps'] = translated_timestamps
270
+ logs.append(f"--- INFO: Created {len(translated_timestamps)} translated timestamps ---")
271
+
272
+ else:
273
+ logs.append(f"--- ERROR: Translation failed: {translation_error} ---")
274
+ result['translated_text'] = original_text # Fallback to original
275
+ result['translated_timestamps'] = word_timestamps # Use original timestamps
276
+
277
+ except Exception as e:
278
+ error_msg = f"--- FATAL ERROR during translation process: {traceback.format_exc()} ---"
279
+ logs.append(error_msg)
280
+ result['translated_text'] = original_text # Fallback
281
+ result['translated_timestamps'] = word_timestamps
282
+
283
+ return result, logs
284
+
285
+ def _create_translated_timestamps(self, original_timestamps: List[Dict], original_text: str, translated_text: str) -> List[Dict]:
286
+ """
287
+ Create timestamps for translated text by proportional mapping
288
+
289
+ Args:
290
+ original_timestamps: Original word timestamps
291
+ original_text: Original transcribed text
292
+ translated_text: Translated text
293
+
294
+ Returns:
295
+ List of translated word timestamps
296
+ """
297
+ try:
298
+ translated_words = translated_text.split()
299
+ if not translated_words:
300
+ return []
301
+
302
+ # Get total duration from original timestamps
303
+ if not original_timestamps:
304
+ return []
305
+
306
+ start_time = original_timestamps[0]['start']
307
+ end_time = original_timestamps[-1]['end']
308
+ total_duration = end_time - start_time
309
+
310
+ # Create proportional timestamps for translated words
311
+ translated_timestamps = []
312
+ word_count = len(translated_words)
313
+
314
+ for i, word in enumerate(translated_words):
315
+ # Calculate proportional timing
316
+ word_start = start_time + (i * total_duration / word_count)
317
+ word_end = start_time + ((i + 1) * total_duration / word_count)
318
+
319
+ translated_timestamps.append({
320
+ 'word': word.strip(),
321
+ 'start': round(word_start, 3),
322
+ 'end': round(word_end, 3)
323
+ })
324
+
325
+ return translated_timestamps
326
+
327
+ except Exception as e:
328
+ print(f"--- ERROR creating translated timestamps: {str(e)} ---")
329
+ return []
330
+
331
+ def batch_translate_transcription(self, audio_file_path: str, target_languages: List[str]) -> Tuple[Dict, List[str]]:
332
+ """
333
+ Transcribe audio and translate to multiple languages
334
+
335
+ Args:
336
+ audio_file_path: Path to audio file
337
+ target_languages: List of target language codes
338
+
339
+ Returns:
340
+ Tuple of (results_dict, log_messages)
341
+ """
342
+ logs = ["--- INFO: Starting batch translation process... ---"]
343
+
344
+ # Get original transcription
345
+ word_timestamps, transcription_logs = self.get_word_timestamps(audio_file_path)
346
+ logs.extend(transcription_logs)
347
+
348
+ if not word_timestamps:
349
+ return {}, logs
350
+
351
+ original_text = " ".join([d['word'] for d in word_timestamps])
352
+
353
+ # Initialize results
354
+ results = {
355
+ 'original': {
356
+ 'text': original_text,
357
+ 'timestamps': word_timestamps,
358
+ 'language': 'detected'
359
+ },
360
+ 'translations': {}
361
+ }
362
+
363
+ # Translate to each target language
364
+ if self.translator:
365
+ for lang_code in target_languages:
366
+ try:
367
+ translated_text, error = self.translator.translate_text(original_text, lang_code)
368
+ if translated_text:
369
+ translated_timestamps = self._create_translated_timestamps(
370
+ word_timestamps, original_text, translated_text
371
+ )
372
+ results['translations'][lang_code] = {
373
+ 'text': translated_text,
374
+ 'timestamps': translated_timestamps,
375
+ 'success': True
376
+ }
377
+ logs.append(f"--- SUCCESS: Translation to {lang_code} completed ---")
378
+ else:
379
+ results['translations'][lang_code] = {
380
+ 'text': original_text,
381
+ 'timestamps': word_timestamps,
382
+ 'success': False,
383
+ 'error': error
384
+ }
385
+ logs.append(f"--- ERROR: Translation to {lang_code} failed: {error} ---")
386
+ except Exception as e:
387
+ logs.append(f"--- FATAL ERROR translating to {lang_code}: {str(e)} ---")
388
+ else:
389
+ logs.append("--- WARNING: Translator not available for batch translation ---")
390
+
391
+ return results, logs
check_credentials.py ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # check_credentials.py - أداة فحص ملف بيانات الاعتماد
3
+
4
+ import os
5
+ import json
6
+
7
+ def check_credentials():
8
+ """فحص ملف credentials.json والتأكد من صحته"""
9
+
10
+ print("🔍 فحص ملف بيانات الاعتماد...")
11
+ print("=" * 50)
12
+
13
+ # فحص وجود الملف
14
+ if not os.path.exists('credentials.json'):
15
+ print("❌ ملف credentials.json غير موجود!")
16
+ print("💡 تأكد من وضع الملف في نفس مجلد app.py")
17
+ return False
18
+
19
+ print("✅ ملف credentials.json موجود")
20
+
21
+ # فحص محتوى الملف
22
+ try:
23
+ with open('credentials.json', 'r') as f:
24
+ creds = json.load(f)
25
+
26
+ print("✅ الملف يحتوي على JSON صحيح")
27
+
28
+ # فحص البنية
29
+ if 'installed' not in creds:
30
+ print("❌ الملف لا يحتوي على قسم 'installed'")
31
+ return False
32
+
33
+ installed = creds['installed']
34
+
35
+ # فحص الحقول المطلوبة
36
+ required_fields = ['client_id', 'client_secret', 'auth_uri', 'token_uri']
37
+ missing_fields = []
38
+
39
+ for field in required_fields:
40
+ if field not in installed:
41
+ missing_fields.append(field)
42
+
43
+ if missing_fields:
44
+ print(f"❌ الحقول المفقودة: {', '.join(missing_fields)}")
45
+ return False
46
+
47
+ # فحص إذا كانت البيانات وهمية
48
+ client_id = installed.get('client_id', '')
49
+ client_secret = installed.get('client_secret', '')
50
+
51
+ if client_id.startswith('YOUR_CLIENT_ID'):
52
+ print("❌ client_id لا يزال يحتوي على القيمة الافتراضية!")
53
+ print("💡 يجب استبدال الملف بملف حقيقي من Google Cloud Console")
54
+ return False
55
+
56
+ if client_secret.startswith('YOUR_CLIENT_SECRET'):
57
+ print("❌ client_secret لا يزال يحتوي على القيمة الافتراضية!")
58
+ print("💡 يجب استبدال الملف بملف حقيقي من Google Cloud Console")
59
+ return False
60
+
61
+ print("✅ جميع الحقول المطلوبة موجودة")
62
+ print(f"✅ Client ID: {client_id[:20]}...")
63
+ print(f"✅ Project ID: {installed.get('project_id', 'غير محدد')}")
64
+
65
+ return True
66
+
67
+ except json.JSONDecodeError:
68
+ print("❌ الملف لا يحتوي على JSON صحيح!")
69
+ return False
70
+ except Exception as e:
71
+ print(f"❌ خطأ في قراءة الملف: {e}")
72
+ return False
73
+
74
+ def check_token():
75
+ """فحص ملف token.json إذا كان موجوداً"""
76
+
77
+ print("\n🔍 فحص ملف المصادقة...")
78
+ print("=" * 50)
79
+
80
+ if os.path.exists('token.json'):
81
+ print("✅ ملف token.json موجود")
82
+ try:
83
+ with open('token.json', 'r') as f:
84
+ token = json.load(f)
85
+
86
+ if 'token' in token:
87
+ print("✅ يحتوي على رمز مصادقة")
88
+
89
+ if 'refresh_token' in token:
90
+ print("✅ يحتوي على رمز التحديث")
91
+
92
+ if 'expiry' in token:
93
+ print(f"⏰ تاريخ انتهاء الصلاحية: {token['expiry']}")
94
+
95
+ except Exception as e:
96
+ print(f"⚠️ مشكلة في ملف token.json: {e}")
97
+ print("💡 يمكنك حذف الملف وإعادة المصادقة")
98
+ else:
99
+ print("ℹ️ ملف token.json غير موجود (طبيعي في أول استخدام)")
100
+
101
+ def main():
102
+ print("🚀 أداة فحص بيانات الاعتماد لـ Google Docs")
103
+ print("=" * 60)
104
+
105
+ creds_ok = check_credentials()
106
+ check_token()
107
+
108
+ print("\n" + "=" * 60)
109
+
110
+ if creds_ok:
111
+ print("🎉 ملف بيانات الاعتماد صحيح!")
112
+ print("💡 يمكنك الآن استخدام زر التصدير")
113
+ else:
114
+ print("❌ يجب إصلاح ملف بيانات الاعتماد أولاً")
115
+ print("📋 راجع ملف GOOGLE_SETUP_SIMPLE.md للحصول على التعليمات")
116
+
117
+ if __name__ == "__main__":
118
+ main()
comprehensive_test.py ADDED
@@ -0,0 +1,225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ اختبار شامل للتحقق من إصلاح جميع المشاكل
5
+ """
6
+
7
+ import requests
8
+ import json
9
+ import time
10
+
11
+ def test_server_health():
12
+ """اختبار صحة الخادم"""
13
+ print("🏥 اختبار صحة الخادم...")
14
+
15
+ try:
16
+ response = requests.get('http://localhost:5001/record', timeout=5)
17
+ if response.status_code == 200:
18
+ data = response.json()
19
+ print(f"✅ الخادم يعمل: {data.get('message')}")
20
+ return True
21
+ else:
22
+ print(f"❌ مشكلة في الخادم: {response.status_code}")
23
+ return False
24
+ except Exception as e:
25
+ print(f"❌ لا يمكن الوصول للخادم: {e}")
26
+ return False
27
+
28
+ def test_cors_headers():
29
+ """اختبار CORS headers"""
30
+ print("\n🔧 اختبار CORS headers...")
31
+
32
+ try:
33
+ # اختبار OPTIONS request
34
+ response = requests.options('http://localhost:5001/summarize', timeout=5)
35
+
36
+ print(f"Status Code: {response.status_code}")
37
+
38
+ # فحص CORS headers
39
+ cors_origin = response.headers.get('Access-Control-Allow-Origin')
40
+ cors_methods = response.headers.get('Access-Control-Allow-Methods')
41
+ cors_headers = response.headers.get('Access-Control-Allow-Headers')
42
+
43
+ print(f"CORS Origin: '{cors_origin}'")
44
+ print(f"CORS Methods: '{cors_methods}'")
45
+ print(f"CORS Headers: '{cors_headers}'")
46
+
47
+ # التحقق من عدم وجود قيم مكررة
48
+ if cors_origin and ',' in cors_origin and cors_origin.count('*') > 1:
49
+ print("❌ مشكلة: CORS Origin يحتوي على قيم مكررة!")
50
+ return False
51
+ elif cors_origin == '*':
52
+ print("✅ CORS Origin صحيح")
53
+ return True
54
+ else:
55
+ print(f"⚠️ CORS Origin غير متوقع: {cors_origin}")
56
+ return False
57
+
58
+ except Exception as e:
59
+ print(f"❌ خطأ في اختبار CORS: {e}")
60
+ return False
61
+
62
+ def test_summarization():
63
+ """اختبار وظيفة التلخيص"""
64
+ print("\n🤖 اختبار وظيفة التلخيص...")
65
+
66
+ test_data = {
67
+ "text": "Hello, how are you? What are you doing today? Tell me about your work and your plans.",
68
+ "language": "arabic",
69
+ "type": "full"
70
+ }
71
+
72
+ try:
73
+ response = requests.post(
74
+ 'http://localhost:5001/summarize',
75
+ json=test_data,
76
+ headers={'Content-Type': 'application/json'},
77
+ timeout=30
78
+ )
79
+
80
+ print(f"Status Code: {response.status_code}")
81
+
82
+ if response.status_code == 200:
83
+ data = response.json()
84
+ if data.get('success'):
85
+ print("✅ التلخيص نجح!")
86
+ summary = data.get('summary', '')
87
+ print(f"الملخص: {summary[:100]}...")
88
+ return True
89
+ else:
90
+ print(f"❌ فشل التلخيص: {data.get('error')}")
91
+ return False
92
+ else:
93
+ print(f"❌ خطأ HTTP: {response.status_code}")
94
+ print(f"الرد: {response.text}")
95
+ return False
96
+
97
+ except Exception as e:
98
+ print(f"❌ خطأ في اختبار التلخيص: {e}")
99
+ return False
100
+
101
+ def test_javascript_syntax():
102
+ """اختبار صيغة JavaScript"""
103
+ print("\n📝 اختبار صيغة JavaScript...")
104
+
105
+ try:
106
+ with open('templates/recorder.html', 'r', encoding='utf-8') as f:
107
+ content = f.read()
108
+
109
+ # فحص بسيط للأقواس
110
+ js_start = content.find('<script>')
111
+ js_end = content.find('</script>')
112
+
113
+ if js_start == -1 or js_end == -1:
114
+ print("❌ لا يمكن العثور على JavaScript")
115
+ return False
116
+
117
+ js_content = content[js_start:js_end]
118
+
119
+ # عد الأقواس
120
+ open_braces = js_content.count('{')
121
+ close_braces = js_content.count('}')
122
+
123
+ print(f"أقواس فتح: {open_braces}")
124
+ print(f"أقواس إغلاق: {close_braces}")
125
+
126
+ if open_braces == close_braces:
127
+ print("✅ الأقواس متوازنة")
128
+
129
+ # فحص للكلمات المفتاحية الأساسية
130
+ if 'function' in js_content and 'async function' in js_content:
131
+ print("✅ الدوال موجودة")
132
+ return True
133
+ else:
134
+ print("⚠️ لا يمكن العثور على الدوال")
135
+ return False
136
+ else:
137
+ print(f"❌ الأقواس غير متوازنة! الفرق: {open_braces - close_braces}")
138
+ return False
139
+
140
+ except Exception as e:
141
+ print(f"❌ خطأ في فحص JavaScript: {e}")
142
+ return False
143
+
144
+ def test_translation_endpoints():
145
+ """اختبار endpoints الترجمة"""
146
+ print("\n🌐 اختبار endpoints الترجمة...")
147
+
148
+ try:
149
+ # اختبار قائمة اللغات
150
+ response = requests.get('http://localhost:5001/languages', timeout=5)
151
+ if response.status_code == 200:
152
+ print("✅ endpoint اللغات يعمل")
153
+ else:
154
+ print(f"⚠️ مشكلة في endpoint اللغات: {response.status_code}")
155
+
156
+ # اختبار UI translations
157
+ response = requests.get('http://localhost:5001/ui-translations/en', timeout=5)
158
+ if response.status_code == 200:
159
+ print("✅ endpoint UI translations يعمل")
160
+ else:
161
+ print(f"⚠️ مشكلة في endpoint UI translations: {response.status_code}")
162
+
163
+ return True
164
+
165
+ except Exception as e:
166
+ print(f"❌ خطأ في اختبار endpoints الترجمة: {e}")
167
+ return False
168
+
169
+ def comprehensive_test():
170
+ """اختبار شامل لجميع الوظائف"""
171
+ print("🚀 بدء الاختبار الشامل")
172
+ print("=" * 60)
173
+
174
+ tests = [
175
+ ("صحة الخادم", test_server_health),
176
+ ("CORS Headers", test_cors_headers),
177
+ ("وظيفة التلخيص", test_summarization),
178
+ ("صيغة JavaScript", test_javascript_syntax),
179
+ ("endpoints الترجمة", test_translation_endpoints)
180
+ ]
181
+
182
+ results = []
183
+
184
+ for test_name, test_func in tests:
185
+ print(f"\n🧪 اختبار: {test_name}")
186
+ print("-" * 40)
187
+
188
+ try:
189
+ result = test_func()
190
+ results.append((test_name, result))
191
+
192
+ if result:
193
+ print(f"✅ {test_name}: نجح")
194
+ else:
195
+ print(f"❌ {test_name}: فشل")
196
+
197
+ except Exception as e:
198
+ print(f"❌ {test_name}: خطأ - {e}")
199
+ results.append((test_name, False))
200
+
201
+ # النتائج النهائية
202
+ print("\n" + "=" * 60)
203
+ print("📊 ملخص نتائج الاختبار:")
204
+ print("=" * 60)
205
+
206
+ passed = 0
207
+ total = len(results)
208
+
209
+ for test_name, result in results:
210
+ status = "✅ نجح" if result else "❌ فشل"
211
+ print(f" {test_name}: {status}")
212
+ if result:
213
+ passed += 1
214
+
215
+ print(f"\nالنتيجة النهائية: {passed}/{total} اختبارات نجحت")
216
+
217
+ if passed == total:
218
+ print("🎉 جميع الاختبارات نجحت! النظام يعمل بشكل مثالي")
219
+ return True
220
+ else:
221
+ print(f"⚠️ {total - passed} اختبارات فشلت - هناك مشاكل تحتاج إصلاح")
222
+ return False
223
+
224
+ if __name__ == "__main__":
225
+ comprehensive_test()
credentials.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "web": {
3
+ "client_id": "739771741359-gk2mkvimn063a2msd6hmkkksn17iamre.apps.googleusercontent.com",
4
+ "project_id": "syncmaster-export",
5
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
6
+ "token_uri": "https://oauth2.googleapis.com/token",
7
+ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
8
+ "client_secret": "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",
9
+ "redirect_uris": [
10
+ "http://localhost:8502",
11
+ "http://localhost:8502/",
12
+ "http://127.0.0.1:8502",
13
+ "http://127.0.0.1:8502/"
14
+ ]
15
+ }
16
+ }
custom_components/st-audiorec/.streamlit/config.toml ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8eec7cce049f088766524596c7dd6229756df0d6331c8cfab099df7d2ebc9d5d
3
+ size 662
custom_components/st-audiorec/LICENCE ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:784d3a6fdb08d429f5de43125e9962e780d34cdf9b5f14b681c9a2d8e905bfec
3
+ size 1080
custom_components/st-audiorec/README.md ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:54716e3eaf4e6047fb50d99176bd2c6b24124b978af2eb30e44a8c6a74cdb9c0
3
+ size 1993
custom_components/st-audiorec/demo.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:efbe93ef9297821f3a1e1f1bdf0f75fb4a4351b154439d01d9ea0cbd49b996b8
3
+ size 2430
custom_components/st-audiorec/setup.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:492fd555416f2807df31dc43e10b9d3cd8d5c18636c586c7f37988e1d8b854c1
3
+ size 786
custom_components/st-audiorec/st_audiorec/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3f714847f1f4a2490e5dd80bc1eeb7b6fcb7850a3e682b491a28dd30fead486c
3
+ size 1622
custom_components/st-audiorec/st_audiorec/frontend/.prettierrc ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3375a44313ae0b6753868a7ae00dc03f618b0c23785b15980482e6b9457ca0f8
3
+ size 72
custom_components/st-audiorec/st_audiorec/frontend/build/asset-manifest.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d923ea03d2475f02335299c4a15a1ca84291e9cbbcd558e6abdb318abe5ccc6f
3
+ size 859
custom_components/st-audiorec/st_audiorec/frontend/build/bootstrap.min.css ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f396767523d7b7ce621d90aae93cbbd7a516275898efd19020be38aa5ae85d5c
3
+ size 206913
custom_components/st-audiorec/st_audiorec/frontend/build/index.html ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e007dc7fb036886292b996d076d57c6eb6eedf32b8e2c5489ad9f6d29d59f088
3
+ size 2175
custom_components/st-audiorec/st_audiorec/frontend/build/precache-manifest.30096e2fd9f149157a833e729e772f72.js ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e318206d88822468d480fd4047df1439dd2dadb500d846636314b39afabb8af4
3
+ size 564
custom_components/st-audiorec/st_audiorec/frontend/build/service-worker.js ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8243bfeb139d7acdf475a748daa48068cde6e5d62a4c2242326e3d6bbfbc6d78
3
+ size 1183
custom_components/st-audiorec/st_audiorec/frontend/build/static/js/2.ca2bba73.chunk.js ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8810a6d23c8292fa11953f5c2c762e6bd658f11316f12879d0a6d4e05f7df5a1
3
+ size 465885
custom_components/st-audiorec/st_audiorec/frontend/build/static/js/2.ca2bba73.chunk.js.LICENSE.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:83bbf722e5b20cfb2920ac1c234ffa5ccde3baa9d8d5a87b4cc90f81ef649a47
3
+ size 1653
custom_components/st-audiorec/st_audiorec/frontend/build/static/js/2.ca2bba73.chunk.js.map ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:25cecefabe1287205f5f99bfd33159566c8d97bc56dadba39d70fcaf160c7998
3
+ size 1634044
custom_components/st-audiorec/st_audiorec/frontend/build/static/js/main.85742990.chunk.js ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:28cf7e640dbe5e67fdde3e5e4eea1d9053901a0612be430efdd2968509feb279
3
+ size 13457
custom_components/st-audiorec/st_audiorec/frontend/build/static/js/main.85742990.chunk.js.map ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b9c4877643f7494e0fea5996bc57d8667c1258cb58311d1d530a957303ffd698
3
+ size 38454