Alide21 commited on
Commit
5c5e4d4
·
verified ·
1 Parent(s): 1a3680a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +79 -42
app.py CHANGED
@@ -22,20 +22,21 @@ body { direction: rtl; background-color: #f9f9f9; }
22
 
23
  .hero-header {
24
  background: linear-gradient(135deg, var(--dark-green) 0%, #0a6b51 100%);
25
- padding: 30px 20px;
26
- border-radius: 0 0 40px 40px;
27
  text-align: center;
28
  color: white;
29
  box-shadow: 0 4px 15px rgba(0,0,0,0.1);
30
  }
31
- .real-count { font-size: 3.5rem; color: var(--gold); font-weight: bold; line-height: 1; }
32
  .instruction-box {
33
- background: #fff5e6;
34
- border-right: 5px solid #ff9800;
35
- padding: 15px;
36
  border-radius: 10px;
37
  margin: 15px 0;
38
  font-size: 0.95rem;
 
39
  }
40
  .ayah-card {
41
  background: white;
@@ -43,107 +44,141 @@ body { direction: rtl; background-color: #f9f9f9; }
43
  padding: 30px;
44
  box-shadow: 0 10px 25px rgba(0,0,0,0.05);
45
  border: 1px solid #eee;
 
46
  }
47
  .ayah-text {
48
  font-family: 'Amiri', serif !important;
49
- font-size: 2.4rem !important;
50
  color: var(--dark-green);
51
- line-height: 1.8 !important;
52
- text-align: center;
53
  }
54
  """
55
 
56
  # --- الوظائف المنطقية ---
57
  def get_real_total():
 
58
  try:
59
  files = api.list_repo_files(repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
60
- return len([f for f in files if f.startswith("data/")])
61
- except: return "..."
 
62
 
63
  INITIAL_TOTAL = get_real_total()
64
 
 
 
 
65
  def fetch_surah(s_idx):
66
  try:
67
  url = f"https://raw.githubusercontent.com/CheeseWithSauce/TheHolyQuranJSONFormat/refs/heads/main/tajweedsurahs/{s_idx:03}.json"
68
  res = requests.get(url, timeout=5).json()
69
- return [{"surah": s_idx, "ayah": v["ayah"], "text": re.sub(r'<[^>]*>', '', v.get("text_ar", "")).strip(), "surah_name": SURAH_NAMES[s_idx-1]} for v in res["verses"]]
70
  except: return []
71
 
 
72
  with ThreadPoolExecutor(max_workers=10) as executor:
73
  results = list(executor.map(fetch_surah, range(1, 115)))
74
  ALL_AYAT = [item for sublist in results for item in sublist]
75
 
76
  def jump_to_ayah(s_name, a_num):
77
- for i, v in enumerate(ALL_AYAT):
78
- if v['surah_name'] == s_name and v['ayah'] == int(a_num):
79
- return "", f"<div class='ayah-text'>{v['text']}</div>", f"### {v['surah_name']} | آية {v['ayah']}", i
80
- return "⚠️ لم يتم العثور على الآية", gr.update(), gr.update(), gr.update()
 
 
 
 
 
81
 
82
  def on_submit(audio, current_idx):
83
- if not audio: return "⚠️ يرجى التسجيل أولاً", gr.update(), gr.update(), current_idx
 
 
84
  v = ALL_AYAT[current_idx]
85
  try:
86
- api.upload_file(path_or_fileobj=audio, path_in_repo=f"data/s{v['surah']}_a{v['ayah']}_{uuid.uuid4().hex[:6]}.wav", repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
 
 
 
 
 
 
 
 
 
87
  next_idx = random.randint(0, len(ALL_AYAT)-1)
88
  nv = ALL_AYAT[next_idx]
89
- return "✅ تم الحفظ بنجاح", f"<div class='ayah-text'>{nv['text']}</div>", f"### {nv['surah_name']} | آية {nv['ayah']}", next_idx
90
- except Exception as e: return f"❌ خطأ: {str(e)}", gr.update(), gr.update(), current_idx
 
91
 
92
- # --- الواجهة ---
93
- with gr.Blocks(title="مشروع تصحيح التلاوة بالذكاء الاصطناعي") as demo:
94
  idx_state = gr.State(random.randint(0, len(ALL_AYAT)-1))
95
 
96
- # الهيدر المدمج
97
  gr.HTML(f"""
98
  <div class="hero-header">
99
- <h1 style="margin:0; font-size: 2rem;">مِنَصَّة التِّلَاوَة</h1>
100
- <p style="margin:5px 0 15px 0; opacity: 0.9;">مشروع بناء نموذج ذكاء اصطناعي لتصحيح التلاوة والتجويد</p>
101
- <div style="background: rgba(0,0,0,0.2); padding: 10px; border-radius: 15px; display: inline-block;">
102
- <span>مساهمات الأمة الحقيقية:</span>
103
  <div class="real-count">{INITIAL_TOTAL}</div>
104
  </div>
105
  </div>
106
  """)
107
 
108
- with gr.Column(elem_classes="gradio-container"):
109
- # تنبيه هام
110
  gr.HTML("""
111
  <div class="instruction-box">
112
- <strong>⚠️ تنبيه هام للجودة:</strong>
113
- يرجى قراءة الآية بشكل مستمر وبنفس واحد قدر الإمكان.
114
- <span style="color: #d32f2f;">يُمنع الوقف ثم البدء من مكان سابق في نفس التسجيل</span> لأن ذلك يفسد تدريب نموذج الذكاء الاصطناعي.
115
  </div>
116
  """)
117
 
118
  with gr.Row():
 
119
  with gr.Column(scale=2):
120
  with gr.Column(elem_classes="ayah-card"):
121
  v_init = ALL_AYAT[idx_state.value]
122
  info_text = gr.Markdown(f"### {v_init['surah_name']} | آية {v_init['ayah']}")
123
- text_html = gr.HTML(f<div class='ayah-text'>{v_init['text']}</div>")
124
  status_msg = gr.Markdown("")
125
 
126
  with gr.Row():
127
  send_btn = gr.Button("🎤 إرسال المساهمة", variant="primary")
128
- rnd_btn = gr.Button("🔄 آية أخرى", variant="secondary")
129
 
 
130
  with gr.Column(scale=1):
131
- gr.Markdown("### 🎙️ استوديو التسجيل")
132
- audio_ui = gr.Audio(sources=["microphone"], type="filepath", label=None)
133
-
134
  with gr.Group():
135
- gr.Markdown("### 🔍 الانتقال السريع")
136
- s_drop = gr.Dropdown(choices=SURAH_NAMES, label="اختر السورة", value="الفاتحة")
 
 
 
137
  a_num = gr.Number(label="رقم الآية", value=1, precision=0)
138
- go_btn = gr.Button("انتقل للآية")
 
 
 
 
 
 
 
 
139
 
140
- # الأحداث
141
  rnd_btn.click(
142
- lambda: (v:=ALL_AYAT[i:=random.randint(0,len(ALL_AYAT)-1)]) and (f"<div class='ayah-text'>{v['text']}</div>", f"### {v['surah_name']} | آية {v['ayah']}", i, ""),
143
  outputs=[text_html, info_text, idx_state, status_msg],
144
  show_progress="hidden"
145
  )
146
 
 
147
  go_btn.click(
148
  jump_to_ayah,
149
  inputs=[s_drop, a_num],
@@ -151,6 +186,7 @@ with gr.Blocks(title="مشروع تصحيح التلاوة بالذكاء الا
151
  show_progress="hidden"
152
  )
153
 
 
154
  send_btn.click(
155
  on_submit,
156
  inputs=[audio_ui, idx_state],
@@ -158,5 +194,6 @@ with gr.Blocks(title="مشروع تصحيح التلاوة بالذكاء الا
158
  show_progress="minimal"
159
  )
160
 
 
161
  if __name__ == "__main__":
162
  demo.launch(css=custom_css)
 
22
 
23
  .hero-header {
24
  background: linear-gradient(135deg, var(--dark-green) 0%, #0a6b51 100%);
25
+ padding: 25px 20px;
26
+ border-radius: 0 0 30px 30px;
27
  text-align: center;
28
  color: white;
29
  box-shadow: 0 4px 15px rgba(0,0,0,0.1);
30
  }
31
+ .real-count { font-size: 3rem; color: var(--gold); font-weight: bold; line-height: 1; margin-top: 5px; }
32
  .instruction-box {
33
+ background: #fff9e6;
34
+ border-right: 5px solid #ffa000;
35
+ padding: 12px 20px;
36
  border-radius: 10px;
37
  margin: 15px 0;
38
  font-size: 0.95rem;
39
+ line-height: 1.6;
40
  }
41
  .ayah-card {
42
  background: white;
 
44
  padding: 30px;
45
  box-shadow: 0 10px 25px rgba(0,0,0,0.05);
46
  border: 1px solid #eee;
47
+ text-align: center;
48
  }
49
  .ayah-text {
50
  font-family: 'Amiri', serif !important;
51
+ font-size: 2.3rem !important;
52
  color: var(--dark-green);
53
+ line-height: 1.7 !important;
54
+ margin: 15px 0;
55
  }
56
  """
57
 
58
  # --- الوظائف المنطقية ---
59
  def get_real_total():
60
+ """جلب عدد الملفات الحقيقي من Hugging Face"""
61
  try:
62
  files = api.list_repo_files(repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
63
+ count = len([f for f in files if f.startswith("data/")])
64
+ return count
65
+ except: return "جاري التحميل..."
66
 
67
  INITIAL_TOTAL = get_real_total()
68
 
69
+ def clean_text(text):
70
+ return re.sub(r'<[^>]*>', '', text).strip()
71
+
72
  def fetch_surah(s_idx):
73
  try:
74
  url = f"https://raw.githubusercontent.com/CheeseWithSauce/TheHolyQuranJSONFormat/refs/heads/main/tajweedsurahs/{s_idx:03}.json"
75
  res = requests.get(url, timeout=5).json()
76
+ return [{"surah": s_idx, "ayah": v["ayah"], "text": clean_text(v.get("text_ar", "")), "surah_name": SURAH_NAMES[s_idx-1]} for v in res["verses"]]
77
  except: return []
78
 
79
+ # تحميل البيانات بسرعة باستخدام التوازي
80
  with ThreadPoolExecutor(max_workers=10) as executor:
81
  results = list(executor.map(fetch_surah, range(1, 115)))
82
  ALL_AYAT = [item for sublist in results for item in sublist]
83
 
84
  def jump_to_ayah(s_name, a_num):
85
+ """منطق البحث عن آية محددة"""
86
+ try:
87
+ target_num = int(a_num)
88
+ for i, v in enumerate(ALL_AYAT):
89
+ if v['surah_name'] == s_name and v['ayah'] == target_num:
90
+ return "", f"<div class='ayah-text'>{v['text']}</div>", f"### {v['surah_name']} | آية {v['ayah']}", i
91
+ return "⚠️ الآية غير موجودة في هذه السورة", gr.update(), gr.update(), gr.update()
92
+ except:
93
+ return "⚠️ يرجى إدخال رقم آية صحيح", gr.update(), gr.update(), gr.update()
94
 
95
  def on_submit(audio, current_idx):
96
+ if not audio:
97
+ return "⚠️ يرجى تسجيل الصوت أولاً", gr.update(), gr.update(), current_idx
98
+
99
  v = ALL_AYAT[current_idx]
100
  try:
101
+ # رفع الملف
102
+ file_name = f"data/s{v['surah']}_a{v['ayah']}_{uuid.uuid4().hex[:6]}.wav"
103
+ api.upload_file(
104
+ path_or_fileobj=audio,
105
+ path_in_repo=file_name,
106
+ repo_id=REPO_ID,
107
+ repo_type="dataset",
108
+ token=HF_TOKEN
109
+ )
110
+ # اختيار آية عشوائية تالية
111
  next_idx = random.randint(0, len(ALL_AYAT)-1)
112
  nv = ALL_AYAT[next_idx]
113
+ return "✅ تم الحفظ بنجاح! جزاك الله خيراً", f"<div class='ayah-text'>{nv['text']}</div>", f"### {nv['surah_name']} | آية {nv['ayah']}", next_idx
114
+ except Exception as e:
115
+ return f"❌ خطأ في الرفع: {str(e)}", gr.update(), gr.update(), current_idx
116
 
117
+ # --- بناء الواجهة ---
118
+ with gr.Blocks(title="مشروع الذكاء الاصطناعي لتصحيح التلاوة") as demo:
119
  idx_state = gr.State(random.randint(0, len(ALL_AYAT)-1))
120
 
121
+ # الجزء العلوي (Hero)
122
  gr.HTML(f"""
123
  <div class="hero-header">
124
+ <h1 style="margin:0; font-size: 1.8rem;">مِنَصَّة التِّلَاوَة</h1>
125
+ <p style="margin:5px 0 10px 0; opacity: 0.85; font-size: 1rem;">مشروع تقني لبناء نموذج ذكاء اصطناعي لتصحيح التجويد وتلاوة القرآن</p>
126
+ <div style="background: rgba(255,255,255,0.1); padding: 8px 20px; border-radius: 12px; display: inline-block; border: 1px solid var(--gold);">
127
+ <span style="font-size: 0.9rem;">إجمالي المساهمات الحقيقية:</span>
128
  <div class="real-count">{INITIAL_TOTAL}</div>
129
  </div>
130
  </div>
131
  """)
132
 
133
+ with gr.Column(elem_id="main-container"):
134
+ # صندوق التعليمات
135
  gr.HTML("""
136
  <div class="instruction-box">
137
+ <strong>💡 تنبيه للجودة:</strong>
138
+ نحن نبني نموذجاً يفهم التلاوة الصحيحة، لذا يرجى عدم <b>"الوقف ثم إعادة الكلمات"</b> في نفس التسجيل.
139
+ اقرأ الآية بنَفَس واحد أو قف وقوفاً صحيحاً دون تكرار كلمات سابقة.
140
  </div>
141
  """)
142
 
143
  with gr.Row():
144
+ # قسم عرض الآية
145
  with gr.Column(scale=2):
146
  with gr.Column(elem_classes="ayah-card"):
147
  v_init = ALL_AYAT[idx_state.value]
148
  info_text = gr.Markdown(f"### {v_init['surah_name']} | آية {v_init['ayah']}")
149
+ text_html = gr.HTML(f"<div class='ayah-text'>{v_init['text']}</div>")
150
  status_msg = gr.Markdown("")
151
 
152
  with gr.Row():
153
  send_btn = gr.Button("🎤 إرسال المساهمة", variant="primary")
154
+ rnd_btn = gr.Button("🔄 آية عشوائية", variant="secondary")
155
 
156
+ # قسم التسجيل والبحث
157
  with gr.Column(scale=1):
 
 
 
158
  with gr.Group():
159
+ gr.Markdown("### 🎙️ استوديو التسجيل")
160
+ audio_ui = gr.Audio(sources=["microphone"], type="filepath", label=None)
161
+
162
+ with gr.Accordion("🔍 اختيار آية محددة", open=False):
163
+ s_drop = gr.Dropdown(choices=SURAH_NAMES, label="اسم السورة", value="الفاتحة")
164
  a_num = gr.Number(label="رقم الآية", value=1, precision=0)
165
+ go_btn = gr.Button("انتقل الآن", variant="secondary")
166
+
167
+ # --- إدارة الأحداث ---
168
+
169
+ # آية عشوائية
170
+ def get_next_random():
171
+ i = random.randint(0, len(ALL_AYAT)-1)
172
+ v = ALL_AYAT[i]
173
+ return f"<div class='ayah-text'>{v['text']}</div>", f"### {v['surah_name']} | آية {v['ayah']}", i, ""
174
 
 
175
  rnd_btn.click(
176
+ get_next_random,
177
  outputs=[text_html, info_text, idx_state, status_msg],
178
  show_progress="hidden"
179
  )
180
 
181
+ # الانتقال لآية محددة
182
  go_btn.click(
183
  jump_to_ayah,
184
  inputs=[s_drop, a_num],
 
186
  show_progress="hidden"
187
  )
188
 
189
+ # الإرسال
190
  send_btn.click(
191
  on_submit,
192
  inputs=[audio_ui, idx_state],
 
194
  show_progress="minimal"
195
  )
196
 
197
+ # تشغيل التطبيق
198
  if __name__ == "__main__":
199
  demo.launch(css=custom_css)