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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +90 -127
app.py CHANGED
@@ -14,186 +14,149 @@ api = HfApi()
14
 
15
  SURAH_NAMES = ["الفاتحة", "البقرة", "آل عمران", "النساء", "المائدة", "الأنعام", "الأعراف", "الأنفال", "التوبة", "يونس", "هود", "يوسف", "الرعد", "إبراهيم", "الحجر", "النحل", "الإسراء", "الكهف", "مريم", "طه", "الأنبياء", "الحج", "المؤمنون", "النور", "الفرقان", "الشعراء", "النمل", "القصص", "العنكبوت", "الروم", "لقمان", "السجدة", "الأحزاب", "سبأ", "فاطر", "يس", "الصافات", "ص", "الزمر", "غافر", "فصلت", "الشورى", "الزخرف", "الدخان", "الجاثية", "الأحقاف", "محمد", "الفتح", "الحجرات", "ق", "الذاريات", "الطور", "النجم", "القمر", "الرحمن", "الواقعة", "الحديد", "المجادلة", "الحشر", "الممتحنة", "الصف", "الجمعة", "المنافقون", "التغابن", "الطلاق", "التحريم", "الملك", "القلم", "الحاقة", "المعارج", "نوح", "الجن", "المزمل", "المدثر", "القيامة", "الإنسان", "المرسلات", "النبأ", "النازعات", "عبس", "التكوير", "الانفطار", "المطففين", "الانشقاق", "البروج", "الطارق", "الأعلى", "الغاشية", "الفجر", "البلد", "الشمس", "الليل", "الضحى", "الشرح", "التين", "العلق", "القدر", "البينة", "الزلزلة", "العاديات", "القارعة", "التكاثر", "العصر", "الهمزة", "الفيل", "قريش", "الماعون", "الكوثر", "الكافرون", "النصر", "المسد", "الإخلاص", "الفلق", "الناس"]
16
 
17
- # --- التنسيق البصري المحسن ---
18
  custom_css = """
19
  @import url('https://fonts.googleapis.com/css2?family=Amiri:wght@400;700&family=Reem+Kufi:wght@400;700&display=swap');
 
 
20
 
21
- :root {
22
- --gold: #D4AF37;
23
- --dark-green: #064635;
24
- --emerald: #519259;
25
- }
26
-
27
- body { direction: rtl; background-color: #f4f7f6; }
28
-
29
- /* قسم الهيرو العلوي */
30
- .hero-container {
31
- background: linear-gradient(180deg, var(--dark-green) 0%, #0a6b51 100%);
32
- min-height: 70vh;
33
- display: flex;
34
- flex-direction: column;
35
- align-items: center;
36
- justify-content: center;
37
- color: white;
38
  text-align: center;
39
- border-radius: 0 0 80px 80px;
40
- box-shadow: 0 10px 30px rgba(0,0,0,0.2);
41
- padding: 20px;
42
- margin-bottom: 40px;
43
- }
44
-
45
- .nation-stats {
46
- background: rgba(255, 255, 255, 0.1);
47
- border: 2px solid var(--gold);
48
- padding: 30px 60px;
49
- border-radius: 30px;
50
- margin: 20px 0;
51
- backdrop-filter: blur(10px);
52
- }
53
-
54
- .stats-number {
55
- font-size: 5rem;
56
- font-weight: 800;
57
- color: var(--gold);
58
- line-height: 1;
59
- text-shadow: 0 0 20px rgba(212, 175, 55, 0.5);
60
  }
61
-
62
- .join-btn {
63
- background-color: var(--gold) !important;
64
- color: var(--dark-green) !important;
65
- font-size: 1.5rem !important;
66
- padding: 15px 40px !important;
67
- border-radius: 50px !important;
68
- font-weight: bold !important;
69
- text-decoration: none;
70
- transition: all 0.3s ease;
71
- cursor: pointer;
72
- border: none;
73
  }
74
-
75
- .join-btn:hover { transform: translateY(-5px); box-shadow: 0 5px 15px rgba(0,0,0,0.3); }
76
-
77
- /* بطاقة الآية */
78
  .ayah-card {
79
  background: white;
80
- border-radius: 30px;
81
- padding: 40px;
82
- box-shadow: 0 20px 40px rgba(0,0,0,0.05);
83
- border-top: 8px solid var(--emerald);
84
  }
85
-
86
  .ayah-text {
87
  font-family: 'Amiri', serif !important;
88
- font-size: 3rem !important;
89
  color: var(--dark-green);
90
- line-height: 2 !important;
 
91
  }
92
-
93
- .alert-box { padding: 15px; border-radius: 12px; font-weight: bold; text-align: center; }
94
- .alert-success { background: #d4edda; color: #155724; }
95
- .alert-error { background: #f8d7da; color: #721c24; }
96
  """
97
 
98
- # --- المنطق الخلفي ---
99
- def clean_text(text): return re.sub(r'<[^>]*>', '', text).strip()
 
 
 
 
 
 
100
 
101
  def fetch_surah(s_idx):
102
  try:
103
  url = f"https://raw.githubusercontent.com/CheeseWithSauce/TheHolyQuranJSONFormat/refs/heads/main/tajweedsurahs/{s_idx:03}.json"
104
  res = requests.get(url, timeout=5).json()
105
- 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"]]
106
  except: return []
107
 
108
  with ThreadPoolExecutor(max_workers=10) as executor:
109
  results = list(executor.map(fetch_surah, range(1, 115)))
110
  ALL_AYAT = [item for sublist in results for item in sublist]
111
 
112
- def on_submit(audio, current_idx, session_count):
113
- if not audio: return "<div class='alert-box alert-error'>⚠️ يرجى تسجيل الصوت أولاً</div>", gr.update(), gr.update(), current_idx, session_count
 
 
 
 
 
 
114
  v = ALL_AYAT[current_idx]
115
  try:
116
  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)
117
  next_idx = random.randint(0, len(ALL_AYAT)-1)
118
  nv = ALL_AYAT[next_idx]
119
- return "<div class='alert-box alert-success'>✅ تم حفظ مساهمتك بنجاح!</div>", f"<div class='ayah-text'>{nv['text']}</div>", f"### {nv['surah_name']} | آية {nv['ayah']}", next_idx, session_count + 1
120
- except Exception as e: return f"<div class='alert-box alert-error'>❌ خطأ: {str(e)}</div>", gr.update(), gr.update(), current_idx, session_count
121
 
122
- # --- بناء الواجهة ---
123
- with gr.Blocks(title="مِنَصَّة التِّلَاوَة") as demo:
124
  idx_state = gr.State(random.randint(0, len(ALL_AYAT)-1))
125
- sess_state = gr.State(0)
126
-
127
- # 1. الجزء العلوي الجذاب (Hero Section)
128
- gr.HTML("""
129
- <div class="hero-container">
130
- <h1 style="font-size: 3.5rem; margin-bottom: 0;">مِنَصَّة التِّلَاوَة الجماعية</h1>
131
- <p style="font-size: 1.4rem; opacity: 0.8;">كن جزءاً من أعظم مشروع تقني لخدمة القرآن الكريم</p>
132
-
133
- <div class="nation-stats">
134
- <div style="font-size: 1.2rem; color: #eee;">إجمالي مساهمات الأمة حتى الآن</div>
135
- <div class="stats-number">100</div>
136
- <div style="font-size: 1.1rem; color: var(--gold);">مساهمة فقط! هدفنا المليون</div>
137
  </div>
138
-
139
- <button class="join-btn" onclick="document.getElementById('main-area').scrollIntoView({behavior: 'smooth'})">
140
- شارك الآن ⬇️
141
- </button>
142
  </div>
143
  """)
144
 
145
- # 2. منطقة العمل الرئيسية
146
- with gr.Column(elem_id="main-area", elem_classes="gradio-container"):
 
 
 
 
 
 
 
 
147
  with gr.Row():
148
- with gr.Column(scale=3):
149
  with gr.Column(elem_classes="ayah-card"):
150
- curr_v = ALL_AYAT[idx_state.value]
151
- info_text = gr.Markdown(f"### {curr_v['surah_name']} | آية {curr_v['ayah']}")
152
- text_html = gr.HTML(f"<div class='ayah-text'>{curr_v['text']}</div>")
153
- status_msg = gr.HTML("")
154
 
155
  with gr.Row():
156
- # إضافة show_progress="hidden" لإخفاء العداد المزعج
157
- send_btn = gr.Button("🎤 إرسال التسجيل", variant="primary")
158
  rnd_btn = gr.Button("🔄 آية أخرى", variant="secondary")
159
 
160
  with gr.Column(scale=1):
161
- gr.Markdown("### 🎙️ سجل تلاوتك")
162
  audio_ui = gr.Audio(sources=["microphone"], type="filepath", label=None)
163
 
164
- with gr.Accordion("🔍 اختيار آية محددة", open=False):
165
- s_drop = gr.Dropdown(choices=SURAH_NAMES, label="السورة", value="الفاتحة")
166
- a_num = gr.Number(label="الآية", value=1, precision=0)
167
- go_btn = gr.Button("انتقال")
168
-
169
- # --- ربط الأحداث مع تعطيل شريط التقدم ---
170
- def get_random_ayah():
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_random_ayah,
177
- None,
178
- [text_html, info_text, idx_state, status_msg],
179
- show_progress="hidden" # هذا هو السر لإخفاء عداد الثواني
180
- )
181
-
182
- send_btn.click(
183
- on_submit,
184
- [audio_ui, idx_state, sess_state],
185
- [status_msg, text_html, info_text, idx_state, sess_state],
186
- show_progress="minimal"
187
  )
188
 
189
  go_btn.click(
190
- lambda s, n: next(((f"<div class='ayah-text'>{v['text']}</div>", f"### {v['surah_name']} | آية {v['ayah']}", i, "")
191
- for i, v in enumerate(ALL_AYAT) if v['surah_name'] == s and v['ayah'] == n),
192
- (gr.update(), gr.update(), 0, "الآية غير موجودة")),
193
- [s_drop, a_num],
194
- [text_html, info_text, idx_state, status_msg],
195
  show_progress="hidden"
196
  )
197
 
 
 
 
 
 
 
 
198
  if __name__ == "__main__":
199
  demo.launch(css=custom_css)
 
14
 
15
  SURAH_NAMES = ["الفاتحة", "البقرة", "آل عمران", "النساء", "المائدة", "الأنعام", "الأعراف", "الأنفال", "التوبة", "يونس", "هود", "يوسف", "الرعد", "إبراهيم", "الحجر", "النحل", "الإسراء", "الكهف", "مريم", "طه", "الأنبياء", "الحج", "المؤمنون", "النور", "الفرقان", "الشعراء", "النمل", "القصص", "العنكبوت", "الروم", "لقمان", "السجدة", "الأحزاب", "سبأ", "فاطر", "يس", "الصافات", "ص", "الزمر", "غافر", "فصلت", "الشورى", "الزخرف", "الدخان", "الجاثية", "الأحقاف", "محمد", "الفتح", "الحجرات", "ق", "الذاريات", "الطور", "النجم", "القمر", "الرحمن", "الواقعة", "الحديد", "المجادلة", "الحشر", "الممتحنة", "الصف", "الجمعة", "المنافقون", "التغابن", "الطلاق", "التحريم", "الملك", "القلم", "الحاقة", "المعارج", "نوح", "الجن", "المزمل", "المدثر", "القيامة", "الإنسان", "المرسلات", "النبأ", "النازعات", "عبس", "التكوير", "الانفطار", "المطففين", "الانشقاق", "البروج", "الطارق", "الأعلى", "الغاشية", "الفجر", "البلد", "الشمس", "الليل", "الضحى", "الشرح", "التين", "العلق", "القدر", "البينة", "الزلزلة", "العاديات", "القارعة", "التكاثر", "العصر", "الهمزة", "الفيل", "قريش", "الماعون", "الكوثر", "الكافرون", "النصر", "المسد", "الإخلاص", "الفلق", "الناس"]
16
 
17
+ # --- التنسيق البصري ---
18
  custom_css = """
19
  @import url('https://fonts.googleapis.com/css2?family=Amiri:wght@400;700&family=Reem+Kufi:wght@400;700&display=swap');
20
+ :root { --gold: #D4AF37; --dark-green: #064635; }
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;
42
+ border-radius: 20px;
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],
150
+ outputs=[status_msg, text_html, info_text, idx_state],
 
 
151
  show_progress="hidden"
152
  )
153
 
154
+ send_btn.click(
155
+ on_submit,
156
+ inputs=[audio_ui, idx_state],
157
+ outputs=[status_msg, text_html, info_text, idx_state],
158
+ show_progress="minimal"
159
+ )
160
+
161
  if __name__ == "__main__":
162
  demo.launch(css=custom_css)