mohamedlabed commited on
Commit
d1984e5
·
verified ·
1 Parent(s): 661e282

Upload 4 files

Browse files
README.md - مساعد الدروس بالذكاء الاصطناعي ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # README.md - مساعد الدروس بالذكاء الاصطناعي
2
+
3
+ [![Open In Spaces](https://huggingface.co/datasets/huggingface/badges/raw/main/open-in-hf-spaces-sm.svg)](https://huggingface.co/spaces)
4
+
5
+ ## نظرة عامة
6
+
7
+ هذا التطبيق هو مساعد تعليمي يستخدم الذكاء الاصطناعي لمساعدة الطلاب على تحويل الدروس المسجلة (صوت أو فيديو) باللغة العربية إلى:
8
+ - نص مكتوب
9
+ - ملخص للدرس
10
+ - أسئلة للفهم والمراجعة
11
+ - خريطة ذهنية للمفاهيم الرئيسية
12
+
13
+ ## النماذج المستخدمة
14
+
15
+ - **التعرف على الكلام:** `Salama1429/KalemaTech-Arabic-STT-ASR-based-on-Whisper-Small` (نموذج متخصص للغة العربية)
16
+ - **تلخيص النصوص:** `malmarjeh/t5-arabic-text-summarization`
17
+
18
+ ## كيفية الاستخدام
19
+
20
+ 1. ارفع ملف صوتي أو فيديو يحتوي على درس باللغة العربية
21
+ 2. انقر على زر "معالجة الدرس"
22
+ 3. انتظر حتى تكتمل المعالجة
23
+ 4. استعرض النتائج في علامات التبويب المختلفة
24
+
25
+ ## ملاحظات
26
+
27
+ - يعمل التطبيق بشكل أفضل مع ملفات صوتية/فيديو ذات جودة جيدة
28
+ - قد تستغرق معالجة الملفات الكبيرة وقتاً أطول
29
+ - يتم تحميل النماذج عند أول تشغيل، مما قد يستغرق بعض الوقت
30
+
31
+ ## التطوير
32
+
33
+ تم تطوير هذا التطبيق باستخدام:
34
+ - Python
35
+ - Hugging Face Transformers
36
+ - Gradio
37
+ - FFmpeg
38
+
39
+ ## الترخيص
40
+
41
+ هذا المشروع متاح تحت ترخيص MIT.
app.py ADDED
@@ -0,0 +1,311 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Educational AI Assistant for Arabic Lessons - Hugging Face Spaces Version
4
+ Spécialement optimisée pour la reconnaissance vocale arabe
5
+ """
6
+
7
+ import gradio as gr
8
+ import torch
9
+ import os
10
+ import tempfile
11
+ import warnings
12
+ import gc
13
+ import time
14
+
15
+ warnings.filterwarnings("ignore")
16
+
17
+ # Fonction pour libérer la mémoire GPU
18
+ def clear_gpu_memory():
19
+ """Libère la mémoire GPU."""
20
+ if torch.cuda.is_available():
21
+ torch.cuda.empty_cache()
22
+ gc.collect()
23
+ print("Mémoire GPU libérée")
24
+
25
+ # Fonction pour extraire l'audio d'une vidéo
26
+ def extract_audio(video_path):
27
+ """Extrait l'audio d'un fichier vidéo."""
28
+ try:
29
+ import ffmpeg
30
+ audio_path = tempfile.mktemp(suffix=".wav")
31
+ print(f"Extraction de l'audio depuis {video_path} vers {audio_path}")
32
+
33
+ # Commande ffmpeg simplifiée
34
+ (ffmpeg
35
+ .input(video_path)
36
+ .output(audio_path,
37
+ acodec='pcm_s16le', # Format PCM non compressé
38
+ ac=1, # Mono
39
+ ar='16k') # 16kHz (optimal pour Whisper)
40
+ .run(capture_stdout=True, capture_stderr=True, overwrite_output=True))
41
+
42
+ print("Extraction audio réussie")
43
+ return audio_path
44
+ except Exception as e:
45
+ print(f"Erreur lors de l'extraction audio: {e}")
46
+ return None
47
+
48
+ # Fonction pour transcrire l'audio
49
+ def transcribe_audio(audio_path, progress=None):
50
+ """Transcrit l'audio arabe."""
51
+ if not audio_path:
52
+ return "Veuillez télécharger un fichier audio ou vidéo."
53
+
54
+ if progress:
55
+ progress(0.3, "Chargement du modèle ASR spécialisé pour l'arabe...")
56
+
57
+ try:
58
+ from transformers import pipeline
59
+
60
+ # Utiliser un modèle spécialisé pour l'arabe
61
+ model_name = "Salama1429/KalemaTech-Arabic-STT-ASR-based-on-Whisper-Small"
62
+
63
+ # Déterminer l'appareil à utiliser
64
+ device = "cuda:0" if torch.cuda.is_available() else "cpu"
65
+ print(f"Utilisation de l'appareil: {device}")
66
+
67
+ # Paramètres ultra-légers pour éviter les problèmes de mémoire
68
+ asr = pipeline(
69
+ "automatic-speech-recognition",
70
+ model=model_name,
71
+ chunk_length_s=10, # Chunks courts
72
+ batch_size=4, # Petit batch
73
+ device=device,
74
+ )
75
+
76
+ if progress:
77
+ progress(0.5, "Transcription en cours...")
78
+
79
+ print(f"Transcription du fichier audio: {audio_path}")
80
+ result = asr(audio_path, generate_kwargs={"language": "arabic"})
81
+
82
+ # Vérification robuste du résultat
83
+ if result and isinstance(result, dict) and "text" in result:
84
+ transcription = result["text"]
85
+ print("Transcription réussie")
86
+ else:
87
+ print("Format de résultat inattendu")
88
+ transcription = ""
89
+
90
+ # Nettoyage des fichiers temporaires
91
+ if "temp" in audio_path and os.path.exists(audio_path):
92
+ try:
93
+ os.remove(audio_path)
94
+ print(f"Fichier audio temporaire supprimé: {audio_path}")
95
+ except Exception as e:
96
+ print(f"Erreur lors de la suppression du fichier temporaire: {e}")
97
+
98
+ return transcription if transcription else "La transcription a échoué ou l'audio était silencieux."
99
+
100
+ except Exception as e:
101
+ print(f"Erreur pendant la transcription: {e}")
102
+ # Nettoyage des fichiers temporaires en cas d'erreur
103
+ if "temp" in audio_path and os.path.exists(audio_path):
104
+ try:
105
+ os.remove(audio_path)
106
+ except:
107
+ pass
108
+
109
+ return f"La transcription a échoué: {str(e)}"
110
+
111
+ # Fonction pour résumer le texte
112
+ def summarize_text(text, progress=None):
113
+ """Résume le texte arabe en entrée."""
114
+ if not text or not isinstance(text, str) or "transcription a échoué" in text.lower():
115
+ return "Veuillez fournir un texte à résumer (transcription nécessaire d'abord)."
116
+
117
+ if progress:
118
+ progress(0.6, "Chargement du modèle de résumé...")
119
+
120
+ try:
121
+ from transformers import pipeline
122
+
123
+ # Libérer la mémoire avant de charger un nouveau modèle
124
+ clear_gpu_memory()
125
+
126
+ # Utiliser un modèle de résumé pour l'arabe
127
+ model_name = "malmarjeh/t5-arabic-text-summarization"
128
+ device = "cuda:0" if torch.cuda.is_available() else "cpu"
129
+
130
+ summarizer = pipeline("summarization", model=model_name, device=device)
131
+
132
+ if progress:
133
+ progress(0.7, "Génération du résumé...")
134
+
135
+ print("Résumé du texte en cours...")
136
+
137
+ # Vérifier que le texte n'est pas vide
138
+ if not text.strip():
139
+ return "Le texte à résumer est vide."
140
+
141
+ # Limiter la longueur du texte pour éviter les problèmes de mémoire
142
+ max_input_length = 1024
143
+ if len(text) > max_input_length:
144
+ text = text[:max_input_length]
145
+ print(f"Texte tronqué à {max_input_length} caractères pour éviter les problèmes de mémoire")
146
+
147
+ # Vérification robuste du résultat
148
+ summary_result = summarizer(text, max_length=150, min_length=30, do_sample=False)
149
+
150
+ if summary_result and isinstance(summary_result, list) and len(summary_result) > 0 and 'summary_text' in summary_result[0]:
151
+ summary = summary_result[0]['summary_text']
152
+ print("Résumé réussi")
153
+ return summary
154
+ else:
155
+ print("Format de résultat de résumé inattendu")
156
+ return "Le résumé a échoué en raison d'un format de résultat inattendu."
157
+ except Exception as e:
158
+ print(f"Erreur pendant le résumé: {e}")
159
+ return f"Le résumé a échoué: {str(e)}"
160
+
161
+ # Fonction pour générer des questions
162
+ def generate_questions(text, progress=None):
163
+ """Génère des questions basées sur le texte arabe."""
164
+ if not text or not isinstance(text, str) or "transcription a échoué" in text.lower():
165
+ return "Veuillez fournir un texte pour générer des questions."
166
+
167
+ if progress:
168
+ progress(0.8, "Génération des questions...")
169
+
170
+ # Version simplifiée avec des questions génériques
171
+ questions = """1. ما هي الفكرة الرئيسية للدرس؟
172
+ 2. ما هي أهم المفاهيم التي تم شرحها في هذا الدرس؟
173
+ 3. كيف يمكن تطبيق المعلومات الواردة في هذا الدرس في الحياة العملية؟
174
+ 4. ما هي العلاقة بين المفاهيم المختلفة التي تم تقديمها؟
175
+ 5. لو طلب منك تلخيص هذا الدرس في ثلاث نقاط، ماذا ستكتب؟"""
176
+
177
+ return questions
178
+
179
+ # Fonction pour générer une carte mentale
180
+ def generate_mind_map(text, progress=None):
181
+ """Génère une représentation de carte mentale simple."""
182
+ if not text or not isinstance(text, str) or "transcription a échoué" in text.lower():
183
+ return "Veuillez fournir un texte pour générer une carte mentale."
184
+
185
+ if progress:
186
+ progress(0.9, "Création de la carte mentale...")
187
+
188
+ # Version ultra-simplifiée avec vérification des indices
189
+ lines = text.split('.')
190
+
191
+ # Création d'une structure Markdown basique
192
+ markdown_map = "# خريطة ذهنية للدرس\n\n"
193
+
194
+ # Titre principal - avec vérification que lines n'est pas vide
195
+ if lines and len(lines) > 0 and lines[0].strip():
196
+ main_idea = lines[0].strip()[:100] + ("..." if len(lines[0]) > 100 else "")
197
+ markdown_map += f"## الموضوع الرئيسي\n- {main_idea}\n\n"
198
+ else:
199
+ markdown_map += f"## الموضوع الرئيسي\n- (لم يتم العثور على موضوع رئيسي)\n\n"
200
+
201
+ # Points principaux (limités à 3 pour simplicité)
202
+ markdown_map += "## النقاط الرئيسية\n\n"
203
+
204
+ # Vérifier s'il y a des lignes supplémentaires
205
+ if len(lines) > 1:
206
+ count = 0
207
+ for line in lines[1:]:
208
+ if line and line.strip() and len(line.strip()) > 15:
209
+ point_text = line.strip()[:80] + ("..." if len(line) > 80 else "")
210
+ markdown_map += f"### نقطة {count+1}\n- {point_text}\n\n"
211
+ count += 1
212
+ if count >= 3:
213
+ break
214
+
215
+ # Si aucun point n'a été trouvé, ajouter un message
216
+ if "### نقطة" not in markdown_map:
217
+ markdown_map += "### ملاحظة\n- لم يتم العثور على نقاط رئيسية كافية في النص\n\n"
218
+
219
+ return markdown_map
220
+
221
+ # Fonction principale de traitement
222
+ def process_lesson(file_obj, progress=gr.Progress()):
223
+ """Traite le fichier audio/vidéo avec une approche ultra-légère."""
224
+ if file_obj is None:
225
+ return "Veuillez télécharger un fichier.", "", "", ""
226
+
227
+ try:
228
+ progress(0.1, "Préparation du traitement...")
229
+
230
+ file_path = file_obj.name
231
+ file_name, file_extension = os.path.splitext(file_path)
232
+ file_extension = file_extension.lower()
233
+
234
+ audio_path = None
235
+ progress(0.2, "Préparation du fichier...")
236
+
237
+ # Traitement du fichier selon son type
238
+ if file_extension in ['.mp4', '.mov', '.avi', '.mkv']:
239
+ audio_path = extract_audio(file_path)
240
+ if not audio_path:
241
+ return "Échec de l'extraction audio.", "", "", ""
242
+ elif file_extension in ['.mp3', '.wav', '.ogg', '.flac', '.m4a']:
243
+ audio_path = file_path
244
+ else:
245
+ return f"Type de fichier non pris en charge: {file_extension}. Veuillez télécharger un fichier audio ou vidéo.", "", "", ""
246
+
247
+ # 1. Transcription
248
+ transcription = transcribe_audio(audio_path, progress)
249
+
250
+ # Libérer la mémoire après transcription
251
+ clear_gpu_memory()
252
+
253
+ # 2. Résumé (seulement si la transcription a réussi)
254
+ if isinstance(transcription, str) and "échoué" not in transcription.lower():
255
+ summary = summarize_text(transcription, progress)
256
+ else:
257
+ summary = "Impossible de générer un résumé car la transcription a échoué."
258
+
259
+ # Libérer la mémoire après résumé
260
+ clear_gpu_memory()
261
+
262
+ # 3. Génération de questions (simplifiée)
263
+ questions = generate_questions(transcription, progress)
264
+
265
+ # 4. Génération de la carte mentale (simplifiée)
266
+ mind_map = generate_mind_map(summary, progress)
267
+
268
+ progress(1.0, "Terminé!")
269
+ return transcription, summary, questions, mind_map
270
+
271
+ except Exception as e:
272
+ print(f"Erreur générale: {e}")
273
+ return f"Une erreur s'est produite: {str(e)}", "", "", ""
274
+
275
+ # Interface Gradio simplifiée
276
+ def create_interface():
277
+ with gr.Blocks(theme=gr.themes.Soft(), title="مساعد الدروس بالذكاء الاصطناعي") as demo:
278
+ gr.Markdown("## مساعد الدروس بالذكاء الاصطناعي")
279
+ gr.Markdown("قم برفع ملف صوتي أو فيديو لدرس باللغة العربية، وسيقوم التطبيق بتحويله إلى نص، تلخيصه، توليد أسئلة، وإنشاء خريطة ذهنية.")
280
+
281
+ with gr.Row():
282
+ input_file = gr.File(label="رفع ملف الدرس (صوت أو فيديو)", file_types=["audio", "video"])
283
+
284
+ process_button = gr.Button("معالجة الدرس")
285
+
286
+ with gr.Tabs():
287
+ with gr.TabItem("النص الكامل"):
288
+ output_transcription = gr.Textbox(label="النص المكتوب للدرس", lines=15, interactive=False)
289
+ with gr.TabItem("الملخص"):
290
+ output_summary = gr.Textbox(label="ملخص الدرس", lines=10, interactive=False)
291
+ with gr.TabItem("أسئلة الفهم"):
292
+ output_questions = gr.Textbox(label="أسئلة مقترحة", lines=10, interactive=False)
293
+ with gr.TabItem("الخريطة الذهنية"):
294
+ output_mindmap = gr.Markdown(label="خريطة ذهنية (بصيغة ماركداون)")
295
+
296
+ process_button.click(
297
+ fn=process_lesson,
298
+ inputs=input_file,
299
+ outputs=[output_transcription, output_summary, output_questions, output_mindmap]
300
+ )
301
+
302
+ gr.Markdown("--- Developed with AI ---")
303
+
304
+ return demo
305
+
306
+ # Point d'entrée principal pour Hugging Face Spaces
307
+ demo = create_interface()
308
+
309
+ # Cette ligne est nécessaire pour Hugging Face Spaces
310
+ if __name__ == "__main__":
311
+ demo.launch()
packages.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ffmpeg
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ gradio>=3.50.2
2
+ transformers>=4.30.0
3
+ torch>=2.0.0
4
+ ffmpeg-python>=0.2.0
5
+ datasets>=2.14.0
6
+ sentencepiece>=0.1.99
7
+ protobuf>=4.25.0