Alide21 commited on
Commit
5bf1034
·
verified ·
1 Parent(s): 829af70

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +29 -154
app.py CHANGED
@@ -13,232 +13,107 @@ api = HfApi()
13
 
14
  SURAH_NAMES = ["الفاتحة", "البقرة", "آل عمران", "النساء", "المائدة", "الأنعام", "الأعراف", "الأنفال", "التوبة", "يونس", "هود", "يوسف", "الرعد", "إبراهيم", "الحجر", "النحل", "الإسراء", "الكهف", "مريم", "طه", "الأنبياء", "الحج", "المؤمنون", "النور", "الفرقان", "الشعراء", "النمل", "القصص", "العنكبوت", "الروم", "لقمان", "السجدة", "الأحزاب", "سبأ", "فاطر", "يس", "الصافات", "ص", "الزمر", "غافر", "فصلت", "الشورى", "الزخرف", "الدخان", "الجاثية", "الأحقاف", "محمد", "الفتح", "الحجرات", "ق", "الذاريات", "الطور", "النجم", "القمر", "الرحمن", "الواقعة", "الحديد", "المجادلة", "الحشر", "الممتحنة", "الصف", "الجمعة", "المنافقون", "التغابن", "الطلاق", "التحريم", "الملك", "القلم", "الحاقة", "المعارج", "نوح", "الجن", "المزمل", "المدثر", "القيامة", "الإنسان", "المرسلات", "النبأ", "النازعات", "عبس", "التكوير", "الانفطار", "المطففين", "الانشقاق", "البروج", "الطارق", "الأعلى", "الغاشية", "الفجر", "البلد", "الشمس", "الليل", "الضحى", "الشرح", "التين", "العلق", "القدر", "البينة", "الزلزلة", "العاديات", "القارعة", "التكاثر", "العصر", "الهمزة", "الفيل", "قريش", "الماعون", "الكوثر", "الكافرون", "النصر", "المسد", "الإخلاص", "الفلق", "الناس"]
15
 
16
- # --- CSS الاحترافي المعدل ---
17
  custom_css = """
18
  @import url('https://fonts.googleapis.com/css2?family=Amiri:wght@400;700&family=Reem+Kufi:wght@700&display=swap');
19
-
20
- body {
21
- direction: rtl;
22
- background-color: #f8f9fa;
23
- }
24
-
25
- #hero-section {
26
- min-height: 60vh;
27
- display: flex;
28
- flex-direction: column;
29
- justify-content: center;
30
- align-items: center;
31
- text-align: center;
32
- background: linear-gradient(rgba(0,0,0,0.7), rgba(0,0,0,0.7)), url('https://images.unsplash.com/photo-1609599006353-e629aaabfeae?q=80&w=2070&auto=format&fit=crop');
33
- background-size: cover;
34
- background-position: center;
35
- color: white;
36
- padding: 40px 20px;
37
- border-radius: 0 0 50px 50px;
38
- }
39
-
40
- .main-title {
41
- font-family: 'Reem Kufi', sans-serif;
42
- font-size: 4rem;
43
- color: #f1c40f;
44
- margin-bottom: 20px;
45
- }
46
-
47
- .sub-title {
48
- font-family: 'Amiri', serif;
49
- font-size: 1.5rem;
50
- max-width: 800px;
51
- margin-bottom: 30px;
52
- }
53
-
54
- .card-box {
55
- background: white;
56
- border-radius: 15px;
57
- padding: 25px;
58
- box-shadow: 0 10px 25px rgba(0,0,0,0.05);
59
- margin-bottom: 20px;
60
- border-top: 5px solid #d4af37;
61
- }
62
-
63
- .ayah-text {
64
- font-family: 'Amiri', serif;
65
- font-size: 2.5rem;
66
- line-height: 1.8;
67
- color: #2c3e50;
68
- text-align: center;
69
- padding: 20px;
70
- }
71
-
72
- .studio-dark {
73
- background: #1a252f;
74
- color: white;
75
- border-radius: 15px;
76
- padding: 20px;
77
- }
78
-
79
  .alert-box { padding: 15px; border-radius: 8px; margin-top: 15px; text-align: center; font-weight: bold; }
80
  .alert-success { background: #d4edda; color: #155724; }
81
  .alert-error { background: #f8d7da; color: #721c24; }
82
  """
83
 
84
- # --- الدوال البرمجية ---
85
  def clean_text(text):
86
  return re.sub(r'<[^>]*>', '', text).strip()
87
 
88
  def load_quran_data():
89
  all_verses = []
90
- # لتقليل وقت التحميل في البداية، يمكن تحميل أول 10 سور أو استخدام كاش
91
  for s in range(1, 115):
92
  try:
93
  url = f"https://raw.githubusercontent.com/CheeseWithSauce/TheHolyQuranJSONFormat/refs/heads/main/tajweedsurahs/{s:03}.json"
94
  res = requests.get(url, timeout=5).json()
95
  for v in res["verses"]:
96
  txt = clean_text(v.get("text_ar", ""))
97
- all_verses.append({
98
- "surah": s,
99
- "ayah": v["ayah"],
100
- "text": txt,
101
- "surah_name": SURAH_NAMES[s-1]
102
- })
103
  except: continue
104
  return all_verses
105
 
106
  ALL_AYAT = load_quran_data()
107
- TARGET_GOAL = 1000
108
 
109
  def get_stats_html(user_session_count):
110
- try:
111
- files = api.list_repo_files(repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
112
- total_count = len([f for f in files if f.startswith("data/")])
113
- except:
114
- total_count = 0
115
-
116
- percent = min((total_count / TARGET_GOAL) * 100, 100)
117
- return f"""
118
- <div style="background: white; padding: 20px; border-radius: 15px; box-shadow: 0 4px 10px rgba(0,0,0,0.05); margin-bottom: 20px;">
119
- <div style="display:flex; justify-content:space-between; margin-bottom:10px; direction: rtl;">
120
- <span style="font-weight:bold; color:#d4af37;">🎯 الهدف: {TARGET_GOAL} تلاوة</span>
121
- <span style="font-weight:bold; color:#2c3e50;">الإجمالي الحالي: {total_count}</span>
122
- </div>
123
- <div style="width:100%; background-color:#eee; border-radius:10px; height:12px; overflow:hidden;">
124
- <div style="width:{percent}%; background-color:#d4af37; height:100%;"></div>
125
- </div>
126
- <div style="margin-top:10px; text-align:right; color:#777; font-size:14px;">
127
- مساهماتك الآن: <b>{user_session_count}</b>
128
- </div>
129
- </div>
130
- """
131
 
132
  def on_submit(audio, current_idx, session_count):
133
  if not audio:
134
  return "<div class='alert-box alert-error'>⚠️ يرجى تسجيل الصوت أولاً</div>", gr.update(), gr.update(), current_idx, session_count, gr.update()
135
 
136
  v = ALL_AYAT[current_idx]
137
- # إضافة UUID لمنع استبدال الملفات بنفس السورة والآية
138
  unique_id = str(uuid.uuid4())[:8]
139
  ext = audio.split('.')[-1]
140
- file_path_in_repo = f"data/s{v['surah']}_a{v['ayah']}_{unique_id}.{ext}"
141
 
142
  try:
143
- api.upload_file(
144
- path_or_fileobj=audio,
145
- path_in_repo=file_path_in_repo,
146
- repo_id=REPO_ID,
147
- repo_type="dataset",
148
- token=HF_TOKEN
149
- )
150
  new_sess = session_count + 1
151
-
152
- # اختيار الآية التالية
153
  next_idx = (current_idx + 1) % len(ALL_AYAT)
154
  nv = ALL_AYAT[next_idx]
155
-
156
- return (
157
- "<div class='alert-box alert-success'>✅ تم الحفظ بنجاح، كتب الله أجرك.</div>",
158
- f"<div class='ayah-text'>{nv['text']}</div>",
159
- f"### سورة {nv['surah_name']} - آية {nv['ayah']}",
160
- next_idx,
161
- new_sess,
162
- get_stats_html(new_sess)
163
- )
164
  except Exception as e:
165
  return f"<div class='alert-box alert-error'>❌ خطأ: {str(e)}</div>", gr.update(), gr.update(), current_idx, session_count, gr.update()
166
 
167
  def jump_logic(s_name, a_num):
168
  try:
169
  s_idx = SURAH_NAMES.index(s_name) + 1
170
- target_idx = next((i for i, v in enumerate(ALL_AYAT) if v["surah"] == s_idx and v["ayah"] == int(a_num)), None)
171
-
172
- if target_idx is None:
173
- return "<div class='alert-box alert-error'>⚠️ الآية غير موجودة</div>", gr.update(), gr.update(), 0
174
-
175
- target = ALL_AYAT[target_idx]
176
- return "", f"<div class='ayah-text'>{target['text']}</div>", f"### سورة {target['surah_name']} - آية {target['ayah']}", target_idx
177
  except:
178
- return "<div class='alert-box alert-error'> حدث خطأ في البحث</div>", gr.update(), gr.update(), 0
179
 
180
  # --- بناء الواجهة ---
181
- with gr.Blocks(title="منصة تلاوة مفتوحة", css=custom_css) as demo:
182
  idx_state = gr.State(random.randint(0, len(ALL_AYAT)-1))
183
  sess_state = gr.State(0)
184
 
185
- gr.HTML(f"""
186
  <div id="hero-section">
187
  <h1 class="main-title">مِنَصَّة تِلاوَة</h1>
188
- <p class="sub-title">ساهم في بناء مستقبل التقنيات الإسلامية بصوتك</p>
189
  </div>
190
  """)
191
 
192
- with gr.Container():
193
  stats_display = gr.HTML(get_stats_html(0))
194
 
195
  with gr.Row():
196
- # الجزء الأيمن (عرض الآية)
197
  with gr.Column(scale=3):
198
- with gr.Div(elem_classes="card-box"):
199
  curr_v = ALL_AYAT[idx_state.value]
200
  info_text = gr.Markdown(f"### سورة {curr_v['surah_name']} - آية {curr_v['ayah']}")
201
  text_html = gr.HTML(f"<div class='ayah-text'>{curr_v['text']}</div>")
202
 
203
  with gr.Row():
204
- send_btn = gr.Button("إرسال التسجيل ✅", variant="primary")
205
  rnd_btn = gr.Button("آية عشوائية 🎲")
206
-
207
  status_msg = gr.HTML("")
208
 
209
- # الجزء الأيسر (التحكم)
210
  with gr.Column(scale=2):
211
- with gr.Div(elem_classes="studio-dark"):
212
  gr.Markdown("### 🎙️ سجل تلاوتك")
213
  audio_ui = gr.Audio(sources=["microphone"], type="filepath", label=None)
214
 
215
  with gr.Accordion("🔍 بحث سريع", open=False):
216
  s_drop = gr.Dropdown(choices=SURAH_NAMES, label="السورة")
217
- a_num = gr.Number(label="رقم الآية", value=1, precision=0)
218
  go_btn = gr.Button("انتقال")
219
 
220
- # الأحداث (Events)
221
- rnd_btn.click(
222
- fn=lambda: (
223
- f"<div class='ayah-text'>{ALL_AYAT[ni:=random.randint(0, len(ALL_AYAT)-1)]['text']}</div>",
224
- f"### سورة {ALL_AYAT[ni]['surah_name']} - آية {ALL_AYAT[ni]['ayah']}",
225
- ni,
226
- ""
227
- ),
228
- outputs=[text_html, info_text, idx_state, status_msg]
229
- )
230
-
231
- go_btn.click(
232
- fn=jump_logic,
233
- inputs=[s_drop, a_num],
234
- outputs=[status_msg, text_html, info_text, idx_state]
235
- )
236
-
237
- send_btn.click(
238
- fn=on_submit,
239
- inputs=[audio_ui, idx_state, sess_state],
240
- outputs=[status_msg, text_html, info_text, idx_state, sess_state, stats_display]
241
- )
242
 
243
- if __name__ == "__main__":
244
- demo.launch()
 
13
 
14
  SURAH_NAMES = ["الفاتحة", "البقرة", "آل عمران", "النساء", "المائدة", "الأنعام", "الأعراف", "الأنفال", "التوبة", "يونس", "هود", "يوسف", "الرعد", "إبراهيم", "الحجر", "النحل", "الإسراء", "الكهف", "مريم", "طه", "الأنبياء", "الحج", "المؤمنون", "النور", "الفرقان", "الشعراء", "النمل", "القصص", "العنكبوت", "الروم", "لقمان", "السجدة", "الأحزاب", "سبأ", "فاطر", "يس", "الصافات", "ص", "الزمر", "غافر", "فصلت", "الشورى", "الزخرف", "الدخان", "الجاثية", "الأحقاف", "محمد", "الفتح", "الحجرات", "ق", "الذاريات", "الطور", "النجم", "القمر", "الرحمن", "الواقعة", "الحديد", "المجادلة", "الحشر", "الممتحنة", "الصف", "الجمعة", "المنافقون", "التغابن", "الطلاق", "التحريم", "الملك", "القلم", "الحاقة", "المعارج", "نوح", "الجن", "المزمل", "المدثر", "القيامة", "الإنسان", "المرسلات", "النبأ", "النازعات", "عبس", "التكوير", "الانفطار", "المطففين", "الانشقاق", "البروج", "الطارق", "الأعلى", "الغاشية", "الفجر", "البلد", "الشمس", "الليل", "الضحى", "الشرح", "التين", "العلق", "القدر", "البينة", "الزلزلة", "العاديات", "القارعة", "التكاثر", "العصر", "الهمزة", "الفيل", "قريش", "الماعون", "الكوثر", "الكافرون", "النصر", "المسد", "الإخلاص", "الفلق", "الناس"]
15
 
 
16
  custom_css = """
17
  @import url('https://fonts.googleapis.com/css2?family=Amiri:wght@400;700&family=Reem+Kufi:wght@700&display=swap');
18
+ body { direction: rtl; background-color: #f8f9fa; }
19
+ #hero-section { min-height: 40vh; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; background: linear-gradient(rgba(0,0,0,0.7), rgba(0,0,0,0.7)), url('https://images.unsplash.com/photo-1609599006353-e629aaabfeae?q=80&w=2070&auto=format&fit=crop'); background-size: cover; background-position: center; color: white; padding: 40px 20px; border-radius: 0 0 50px 50px; }
20
+ .main-title { font-family: 'Reem Kufi', sans-serif; font-size: 3.5rem; color: #f1c40f; margin-bottom: 10px; }
21
+ .card-box { background: white; border-radius: 15px; padding: 25px; box-shadow: 0 10px 25px rgba(0,0,0,0.05); margin-bottom: 20px; border-top: 5px solid #d4af37; }
22
+ .ayah-text { font-family: 'Amiri', serif; font-size: 2.2rem; line-height: 1.8; color: #2c3e50; text-align: center; }
23
+ .studio-dark { background: #1a252f; color: white; border-radius: 15px; padding: 20px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  .alert-box { padding: 15px; border-radius: 8px; margin-top: 15px; text-align: center; font-weight: bold; }
25
  .alert-success { background: #d4edda; color: #155724; }
26
  .alert-error { background: #f8d7da; color: #721c24; }
27
  """
28
 
 
29
  def clean_text(text):
30
  return re.sub(r'<[^>]*>', '', text).strip()
31
 
32
  def load_quran_data():
33
  all_verses = []
34
+ # لتحسين السرعة، سنحمل عينة من السور أولاً
35
  for s in range(1, 115):
36
  try:
37
  url = f"https://raw.githubusercontent.com/CheeseWithSauce/TheHolyQuranJSONFormat/refs/heads/main/tajweedsurahs/{s:03}.json"
38
  res = requests.get(url, timeout=5).json()
39
  for v in res["verses"]:
40
  txt = clean_text(v.get("text_ar", ""))
41
+ all_verses.append({"surah": s, "ayah": v["ayah"], "text": txt, "surah_name": SURAH_NAMES[s-1]})
 
 
 
 
 
42
  except: continue
43
  return all_verses
44
 
45
  ALL_AYAT = load_quran_data()
 
46
 
47
  def get_stats_html(user_session_count):
48
+ return f"<div style='text-align:center; padding:10px; background:#fff; border-radius:10px; margin-bottom:20px; border:1px solid #ddd;'>عدد تلاواتك في هذه الجلسة: <b>{user_session_count}</b></div>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
  def on_submit(audio, current_idx, session_count):
51
  if not audio:
52
  return "<div class='alert-box alert-error'>⚠️ يرجى تسجيل الصوت أولاً</div>", gr.update(), gr.update(), current_idx, session_count, gr.update()
53
 
54
  v = ALL_AYAT[current_idx]
 
55
  unique_id = str(uuid.uuid4())[:8]
56
  ext = audio.split('.')[-1]
57
+ file_name = f"s{v['surah']}_a{v['ayah']}_{unique_id}.{ext}"
58
 
59
  try:
60
+ api.upload_file(path_or_fileobj=audio, path_in_repo=f"data/{file_name}", repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
 
 
 
 
 
 
61
  new_sess = session_count + 1
 
 
62
  next_idx = (current_idx + 1) % len(ALL_AYAT)
63
  nv = ALL_AYAT[next_idx]
64
+ return "<div class='alert-box alert-success'>✅ تم الحفظ بنجاح</div>", f"<div class='ayah-text'>{nv['text']}</div>", f"### سورة {nv['surah_name']} - آية {nv['ayah']}", next_idx, new_sess, get_stats_html(new_sess)
 
 
 
 
 
 
 
 
65
  except Exception as e:
66
  return f"<div class='alert-box alert-error'>❌ خطأ: {str(e)}</div>", gr.update(), gr.update(), current_idx, session_count, gr.update()
67
 
68
  def jump_logic(s_name, a_num):
69
  try:
70
  s_idx = SURAH_NAMES.index(s_name) + 1
71
+ t_idx = next((i for i, v in enumerate(ALL_AYAT) if v["surah"] == s_idx and v["ayah"] == int(a_num)), 0)
72
+ target = ALL_AYAT[t_idx]
73
+ return "", f"<div class='ayah-text'>{target['text']}</div>", f"### سورة {target['surah_name']} - آية {target['ayah']}", t_idx
 
 
 
 
74
  except:
75
+ return "<div class='alert-box alert-error'>⚠️ لم يتم العثور على الآية</div>", gr.update(), gr.update(), 0
76
 
77
  # --- بناء الواجهة ---
78
+ with gr.Blocks(title="منصة تلاوة مفتوحة") as demo:
79
  idx_state = gr.State(random.randint(0, len(ALL_AYAT)-1))
80
  sess_state = gr.State(0)
81
 
82
+ gr.HTML("""
83
  <div id="hero-section">
84
  <h1 class="main-title">مِنَصَّة تِلاوَة</h1>
85
+ <p style="font-size: 1.2rem;">مشروع لجمع بيانات القرآن الكريم الصوتية</p>
86
  </div>
87
  """)
88
 
89
+ with gr.Column(variant="panel"): # بديل gr.Container
90
  stats_display = gr.HTML(get_stats_html(0))
91
 
92
  with gr.Row():
 
93
  with gr.Column(scale=3):
94
+ with gr.Group():
95
  curr_v = ALL_AYAT[idx_state.value]
96
  info_text = gr.Markdown(f"### سورة {curr_v['surah_name']} - آية {curr_v['ayah']}")
97
  text_html = gr.HTML(f"<div class='ayah-text'>{curr_v['text']}</div>")
98
 
99
  with gr.Row():
100
+ send_btn = gr.Button("إرسال ✅", variant="primary")
101
  rnd_btn = gr.Button("آية عشوائية 🎲")
 
102
  status_msg = gr.HTML("")
103
 
 
104
  with gr.Column(scale=2):
105
+ with gr.Group():
106
  gr.Markdown("### 🎙️ سجل تلاوتك")
107
  audio_ui = gr.Audio(sources=["microphone"], type="filepath", label=None)
108
 
109
  with gr.Accordion("🔍 بحث سريع", open=False):
110
  s_drop = gr.Dropdown(choices=SURAH_NAMES, label="السورة")
111
+ a_num = gr.Number(label="الآية", value=1, precision=0)
112
  go_btn = gr.Button("انتقال")
113
 
114
+ rnd_btn.click(lambda: (f"<div class='ayah-text'>{ALL_AYAT[ni:=random.randint(0, len(ALL_AYAT)-1)]['text']}</div>", f"### سورة {ALL_AYAT[ni]['surah_name']} - آية {ALL_AYAT[ni]['ayah']}", ni, ""), None, [text_html, info_text, idx_state, status_msg])
115
+ go_btn.click(jump_logic, [s_drop, a_num], [status_msg, text_html, info_text, idx_state])
116
+ send_btn.click(on_submit, [audio_ui, idx_state, sess_state], [status_msg, text_html, info_text, idx_state, sess_state, stats_display])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
+ # تشغيل مع نقل الـ CSS لهنا (حسب متطلبات الإصدار 6)
119
+ demo.launch(css=custom_css)