Alide21 commited on
Commit
15e9b93
·
verified ·
1 Parent(s): a45ec09

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +243 -69
app.py CHANGED
@@ -5,64 +5,174 @@ import random
5
  import os
6
  from huggingface_hub import HfApi
7
 
 
8
  HF_TOKEN = os.getenv("HF_TOKEN")
9
  REPO_ID = "Alide21/speech_quran"
10
  api = HfApi()
11
 
12
  SURAH_NAMES = ["الفاتحة", "البقرة", "آل عمران", "النساء", "المائدة", "الأنعام", "الأعراف", "الأنفال", "التوبة", "يونس", "هود", "يوسف", "الرعد", "إبراهيم", "الحجر", "النحل", "الإسراء", "الكهف", "مريم", "طه", "الأنبياء", "الحج", "المؤمنون", "النور", "الفرقان", "الشعراء", "النمل", "القصص", "العنكبوت", "الروم", "لقمان", "السجدة", "الأحزاب", "سبأ", "فاطر", "يس", "الصافات", "ص", "الزمر", "غافر", "فصلت", "الشورى", "الزخرف", "الدخان", "الجاثية", "الأحقاف", "محمد", "الفتح", "الحجرات", "ق", "الذاريات", "الطور", "النجم", "القمر", "الرحمن", "الواقعة", "الحديد", "المجادلة", "الحشر", "الممتحنة", "الصف", "الجمعة", "المنافقون", "التغابن", "الطلاق", "التحريم", "الملك", "القلم", "الحاقة", "المعارج", "نوح", "الجن", "المزمل", "المدثر", "القيامة", "الإنسان", "المرسلات", "النبأ", "النازعات", "عبس", "التكوير", "الانفطار", "المطففين", "الانشقاق", "البروج", "الطارق", "الأعلى", "الغاشية", "الفجر", "البلد", "الشمس", "الليل", "الضحى", "الشرح", "التين", "العلق", "القدر", "البينة", "الزلزلة", "العاديات", "القارعة", "التكاثر", "العصر", "الهمزة", "الفيل", "قريش", "الماعون", "الكوثر", "الكافرون", "النصر", "المسد", "الإخلاص", "الفلق", "الناس"]
13
 
 
14
  custom_css = """
15
  @import url('https://fonts.googleapis.com/css2?family=Amiri:wght@700&family=Reem+Kufi:wght@700&display=swap');
16
 
17
- /* إلغاء المسافات والهوامش */
18
- .gradio-container { direction: rtl !important; max-width: 1200px !important; margin: 0 auto !important; padding: 10px !important; min-height: unset !important; }
19
- .gap { gap: 10px !important; }
20
 
21
- /* رأس الصفحة المدمج */
22
- .header-mini {
23
- background: linear-gradient(90deg, #1a1a1a, #2c3e50);
24
- padding: 15px;
25
- border-radius: 15px;
 
 
 
 
 
 
 
26
  text-align: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  color: #d4af37;
28
- margin-bottom: 15px;
29
- border-bottom: 3px solid #d4af37;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  }
31
- .header-mini h1 { font-size: 35px !important; margin: 0; font-family: 'Reem Kufi', sans-serif; }
32
-
33
- /* بطاقة الآية الاستعراضية */
34
- .ayah-card {
35
- background: white;
36
- border-radius: 15px;
37
- padding: 20px;
38
- box-shadow: 0 4px 15px rgba(0,0,0,0.05);
39
- border-top: 4px solid #d4af37;
40
- min-height: 200px;
41
  display: flex;
42
- flex-direction: column;
43
- justify-content: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  }
45
- .ayah-text { font-size: 28px !important; line-height: 1.5 !important; color: #2c3e50; font-family: 'Amiri', serif; text-align: center; }
46
-
47
- /* منطقة التسجيل الاحترافية */
48
- .studio-box {
49
- background: #1e272e !important;
50
- border-radius: 15px !important;
51
- padding: 15px !important;
52
- border: 1px solid #d4af37;
53
  }
54
- .studio-box label { color: #d4af37 !important; font-size: 14px !important; }
55
 
56
- /* الإحصائيات في سطر واحد */
57
- .stats-bar { display: flex; gap: 10px; margin-bottom: 10px; }
58
- .stat-pill { background: white; flex: 1; padding: 8px; border-radius: 10px; text-align: center; box-shadow: 0 2px 5px rgba(0,0,0,0.05); font-size: 14px; }
59
- .stat-pill b { color: #d4af37; font-size: 18px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
- /* تصغير الأزرار */
62
- .btn-compact { border-radius: 10px !important; padding: 5px !important; }
 
 
 
 
 
 
 
 
 
 
 
 
63
  """
64
 
65
- def clean_text(text): return re.sub(r'<[^>]*>', '', text).strip()
 
66
 
67
  def load_quran_data():
68
  all_verses = []
@@ -77,64 +187,128 @@ def load_quran_data():
77
  return all_verses
78
 
79
  ALL_AYAT = load_quran_data()
 
80
 
81
  def get_total():
82
  try:
83
  files = api.list_repo_files(repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
84
- return len([f for f in files if f.startswith("data/")])
 
85
  except: return 0
86
 
87
  def on_submit(audio, current_idx, session_count):
88
- if not audio: return "⚠️ يرجى التسجيل", gr.update(), gr.update(), current_idx, session_count, gr.update()
 
 
89
  v = ALL_AYAT[current_idx]
90
  ext = audio.split('.')[-1]
91
  file_name = f"{v['surah']}|{v['ayah']}.{ext}"
 
92
  try:
93
  api.upload_file(path_or_fileobj=audio, path_in_repo=f"data/{file_name}", repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
94
- new_sess, new_total = session_count + 1, get_total()
 
 
 
95
  next_idx = (current_idx + 1) % len(ALL_AYAT)
96
- while len(ALL_AYAT[next_idx]["text"]) > 150: next_idx = (next_idx + 1) % len(ALL_AYAT)
 
 
97
  nv = ALL_AYAT[next_idx]
98
- stats_html = f"<div class='stats-bar'><div class='stat-pill'><b>{new_total}</b><br>تجميعات الأمة</div><div class='stat-pill'><b>{new_sess}</b><br>مساهماتك الآن</div></div>"
99
- return "✨ تم بنجاح", f"<div class='ayah-text'>{nv['text']}</div>", f"### سورة {nv['surah_name']} - آ��ة {nv['ayah']}", next_idx, new_sess, stats_html
100
- except: return " خطأ في الرفع", gr.update(), gr.update(), current_idx, session_count, gr.update()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
- with gr.Blocks(title="منصة تلاوة") as demo:
103
  idx_state = gr.State(random.randint(0, len(ALL_AYAT)-1))
104
  sess_state = gr.State(0)
105
 
106
- gr.HTML("<div class='header-mini'><h1>مِنَصَّة تِلاوَة</h1></div>")
 
 
 
 
 
 
 
107
 
108
- with gr.Column():
109
- stats_display = gr.HTML(f"<div class='stats-bar'><div class='stat-pill'><b>{get_total()}</b><br>تجميعات الأمة</div><div class='stat-pill'><b>0</b><br>مساهماتك الآن</div></div>")
 
 
 
110
 
111
- with gr.Row(variant="panel"):
112
- # قسم الآية
113
- with gr.Column(scale=2):
114
- with gr.Group(elem_classes="ayah-card"):
115
  init_v = ALL_AYAT[idx_state.value]
116
  info_text = gr.Markdown(f"### سورة {init_v['surah_name']} - آية {init_v['ayah']}")
117
  text_html = gr.HTML(f"<div class='ayah-text'>{init_v['text']}</div>")
 
 
 
 
118
 
119
- with gr.Row():
120
- send_btn = gr.Button("إرسال ✅", variant="primary", scale=2)
121
- rnd_btn = gr.Button("🎲", scale=1)
122
-
123
- status_msg = gr.Markdown("")
124
 
125
- # قسم التسجيل والبحث
126
- with gr.Column(scale=1):
127
- with gr.Group(elem_classes="studio-box"):
128
- audio_ui = gr.Audio(sources=["microphone", "upload"], type="filepath", label="🎙️ التسجيل")
 
129
 
130
- with gr.Accordion("⚙️ بحث سريع", open=False):
131
- s_drop = gr.Dropdown(choices=SURAH_NAMES, label="السورة", value=init_v['surah_name'])
132
- a_num = gr.Number(label="الآية", value=init_v['ayah'], precision=0)
133
- go_btn = gr.Button("انتقال")
134
 
135
- demo.load(None, None, None, js="""() => { document.body.dir = 'rtl'; }""")
136
-
137
  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, ""), outputs=[text_html, info_text, idx_state, status_msg])
 
 
 
138
  send_btn.click(on_submit, inputs=[audio_ui, idx_state, sess_state], outputs=[status_msg, text_html, info_text, idx_state, sess_state, stats_display])
139
 
140
- demo.launch(css=custom_css)
 
5
  import os
6
  from huggingface_hub import HfApi
7
 
8
+ # إعدادات الأمان
9
  HF_TOKEN = os.getenv("HF_TOKEN")
10
  REPO_ID = "Alide21/speech_quran"
11
  api = HfApi()
12
 
13
  SURAH_NAMES = ["الفاتحة", "البقرة", "آل عمران", "النساء", "المائدة", "الأنعام", "الأعراف", "الأنفال", "التوبة", "يونس", "هود", "يوسف", "الرعد", "إبراهيم", "الحجر", "النحل", "الإسراء", "الكهف", "مريم", "طه", "الأنبياء", "الحج", "المؤمنون", "النور", "الفرقان", "الشعراء", "النمل", "القصص", "العنكبوت", "الروم", "لقمان", "السجدة", "الأحزاب", "سبأ", "فاطر", "يس", "الصافات", "ص", "الزمر", "غافر", "فصلت", "الشورى", "الزخرف", "الدخان", "الجاثية", "الأحقاف", "محمد", "الفتح", "الحجرات", "ق", "الذاريات", "الطور", "النجم", "القمر", "الرحمن", "الواقعة", "الحديد", "المجادلة", "الحشر", "الممتحنة", "الصف", "الجمعة", "المنافقون", "التغابن", "الطلاق", "التحريم", "الملك", "القلم", "الحاقة", "المعارج", "نوح", "الجن", "المزمل", "المدثر", "القيامة", "الإنسان", "المرسلات", "النبأ", "النازعات", "عبس", "التكوير", "الانفطار", "المطففين", "الانشقاق", "البروج", "الطارق", "الأعلى", "الغاشية", "الفجر", "البلد", "الشمس", "الليل", "الضحى", "الشرح", "التين", "العلق", "القدر", "البينة", "الزلزلة", "العاديات", "القارعة", "التكاثر", "العصر", "الهمزة", "الفيل", "قريش", "الماعون", "الكوثر", "الكافرون", "النصر", "المسد", "الإخلاص", "الفلق", "الناس"]
14
 
15
+ # CSS احترافي جداً
16
  custom_css = """
17
  @import url('https://fonts.googleapis.com/css2?family=Amiri:wght@700&family=Reem+Kufi:wght@700&display=swap');
18
 
19
+ /* إعادة ضبط الصفحة */
20
+ body, .gradio-container { margin: 0 !important; padding: 0 !important; max-width: 100% !important; direction: rtl; background-color: #f8f9fa; }
 
21
 
22
+ /* Hero Section - الشاشة الافتتاحية */
23
+ .hero-wrapper {
24
+ height: 100vh; /* ارتفاع كامل للشاشة */
25
+ width: 100%;
26
+ background: linear-gradient(rgba(0,0,0,0.75), rgba(0,0,0,0.6)), url('https://images.unsplash.com/photo-1609599006353-e629aaabfeae?q=80&w=2070&auto=format&fit=crop');
27
+ background-size: cover;
28
+ background-position: center;
29
+ background-attachment: fixed;
30
+ display: flex;
31
+ flex-direction: column;
32
+ justify-content: center;
33
+ align-items: center;
34
  text-align: center;
35
+ color: white;
36
+ position: relative;
37
+ }
38
+
39
+ .hero-title {
40
+ font-size: 80px !important;
41
+ font-family: 'Reem Kufi', sans-serif !important;
42
+ color: #ffffff !important;
43
+ text-shadow: 0px 0px 20px rgba(212, 175, 55, 0.6);
44
+ margin-bottom: 10px;
45
+ }
46
+
47
+ .hero-subtitle {
48
+ font-size: 24px;
49
+ color: #e0e0e0;
50
+ margin-bottom: 40px;
51
+ font-family: 'Amiri', serif;
52
+ }
53
+
54
+ .cta-button {
55
+ background: #d4af37;
56
+ color: #1a1a1a;
57
+ padding: 18px 50px;
58
+ border-radius: 50px;
59
+ font-size: 22px;
60
+ font-weight: bold;
61
+ text-decoration: none;
62
+ transition: transform 0.3s, box-shadow 0.3s;
63
+ border: 2px solid #d4af37;
64
+ cursor: pointer;
65
+ display: inline-block;
66
+ }
67
+ .cta-button:hover {
68
+ background: transparent;
69
  color: #d4af37;
70
+ transform: translateY(-3px);
71
+ box-shadow: 0 10px 20px rgba(0,0,0,0.3);
72
+ }
73
+
74
+ /* سهم النزول */
75
+ .scroll-down {
76
+ position: absolute;
77
+ bottom: 30px;
78
+ animation: bounce 2s infinite;
79
+ color: white;
80
+ font-size: 30px;
81
+ opacity: 0.8;
82
+ }
83
+ @keyframes bounce {
84
+ 0%, 20%, 50%, 80%, 100% {transform: translateY(0);}
85
+ 40% {transform: translateY(-10px);}
86
+ 60% {transform: translateY(-5px);}
87
+ }
88
+
89
+ /* منطقة العمل (بعد النزول) */
90
+ #work-section {
91
+ padding: 60px 20px;
92
+ max-width: 1200px;
93
+ margin: 0 auto;
94
  }
95
+
96
+ /* شريط الإحصائيات */
97
+ .stats-container {
98
+ background: white;
99
+ border-radius: 15px;
100
+ padding: 20px;
101
+ box-shadow: 0 5px 15px rgba(0,0,0,0.05);
102
+ margin-bottom: 40px;
103
+ border-right: 5px solid #d4af37;
 
104
  display: flex;
105
+ justify-content: space-around;
106
+ align-items: center;
107
+ flex-wrap: wrap;
108
+ }
109
+ .stat-item { text-align: center; }
110
+ .stat-val { font-size: 30px; color: #2c3e50; font-weight: bold; font-family: 'Reem Kufi'; }
111
+ .stat-label { color: #7f8c8d; font-size: 16px; }
112
+
113
+ /* بطاقة الآية */
114
+ .ayah-display {
115
+ background: #fff;
116
+ padding: 40px;
117
+ border-radius: 20px;
118
+ box-shadow: 0 10px 30px rgba(0,0,0,0.08);
119
+ text-align: center;
120
+ position: relative;
121
+ overflow: hidden;
122
+ }
123
+ .ayah-display::before {
124
+ content: "❝";
125
+ font-size: 100px;
126
+ color: #f4f4f4;
127
+ position: absolute;
128
+ top: -20px;
129
+ right: 20px;
130
  }
131
+ .ayah-text {
132
+ font-size: 36px !important;
133
+ line-height: 1.8;
134
+ color: #2c3e50;
135
+ font-family: 'Amiri', serif !important;
136
+ position: relative;
137
+ z-index: 2;
 
138
  }
 
139
 
140
+ /* منطقة التسجيل المحسنة */
141
+ .studio-container {
142
+ background: #2c3e50;
143
+ color: white;
144
+ padding: 30px;
145
+ border-radius: 20px;
146
+ box-shadow: 0 15px 40px rgba(0,0,0,0.15);
147
+ border: 1px solid #34495e;
148
+ }
149
+ .studio-header {
150
+ border-bottom: 1px solid #465f76;
151
+ padding-bottom: 15px;
152
+ margin-bottom: 20px;
153
+ font-size: 18px;
154
+ color: #d4af37;
155
+ font-weight: bold;
156
+ }
157
 
158
+ /* تحسين شكل رسائل التنبيه */
159
+ .toast-msg {
160
+ padding: 15px;
161
+ border-radius: 8px;
162
+ margin-top: 15px;
163
+ font-weight: bold;
164
+ text-align: center;
165
+ animation: fadeIn 0.5s;
166
+ }
167
+ .toast-success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
168
+ .toast-error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
169
+ .toast-warn { background: #fff3cd; color: #856404; border: 1px solid #ffeeba; }
170
+
171
+ @keyframes fadeIn { from { opacity:0; transform: translateY(10px); } to { opacity:1; transform: translateY(0); } }
172
  """
173
 
174
+ def clean_text(text):
175
+ return re.sub(r'<[^>]*>', '', text).strip()
176
 
177
  def load_quran_data():
178
  all_verses = []
 
187
  return all_verses
188
 
189
  ALL_AYAT = load_quran_data()
190
+ TARGET_GOAL = 1000
191
 
192
  def get_total():
193
  try:
194
  files = api.list_repo_files(repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
195
+ count = len([f for f in files if f.startswith("data/")])
196
+ return count
197
  except: return 0
198
 
199
  def on_submit(audio, current_idx, session_count):
200
+ if not audio:
201
+ return "<div class='toast-msg toast-error'>⚠️ لم يتم استلام أي صوت! الرجاء التسجيل أولاً.</div>", gr.update(), gr.update(), current_idx, session_count, gr.update()
202
+
203
  v = ALL_AYAT[current_idx]
204
  ext = audio.split('.')[-1]
205
  file_name = f"{v['surah']}|{v['ayah']}.{ext}"
206
+
207
  try:
208
  api.upload_file(path_or_fileobj=audio, path_in_repo=f"data/{file_name}", repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
209
+ new_sess = session_count + 1
210
+ new_total = get_total()
211
+
212
+ # اختيار الآية التالية (تجاوز الطويلة)
213
  next_idx = (current_idx + 1) % len(ALL_AYAT)
214
+ while len(ALL_AYAT[next_idx]["text"]) > 250:
215
+ next_idx = (next_idx + 1) % len(ALL_AYAT)
216
+
217
  nv = ALL_AYAT[next_idx]
218
+ stats_html = create_stats_html(new_total, new_sess)
219
+
220
+ return "<div class='toast-msg toast-success'>✨ تم الحفظ بنجاح، جزاك الله خيراً!</div>", f"<div class='ayah-text'>{nv['text']}</div>", f"### سورة {nv['surah_name']} - آية {nv['ayah']}", next_idx, new_sess, stats_html
221
+ except Exception as e:
222
+ return f"<div class='toast-msg toast-error'>❌ خطأ تقني: {str(e)}</div>", gr.update(), gr.update(), current_idx, session_count, gr.update()
223
+
224
+ def create_stats_html(total, session):
225
+ percent = min((total / TARGET_GOAL) * 100, 100)
226
+ return f"""
227
+ <div class='stats-container'>
228
+ <div class='stat-item'>
229
+ <div class='stat-val' style='color:#d4af37'>{TARGET_GOAL}</div>
230
+ <div class='stat-label'>الهدف المنشود</div>
231
+ </div>
232
+ <div class='stat-item' style='flex-grow:2; margin:0 20px;'>
233
+ <div style='background:#eee; height:10px; border-radius:5px; margin-top:10px; overflow:hidden;'>
234
+ <div style='background:#d4af37; height:100%; width:{percent}%'></div>
235
+ </div>
236
+ <div style='font-size:12px; color:#999; margin-top:5px;'>تم جمع {total} تسجيل حتى الآن</div>
237
+ </div>
238
+ <div class='stat-item'>
239
+ <div class='stat-val'>{session}</div>
240
+ <div class='stat-label'>مساهمتك الحالية</div>
241
+ </div>
242
+ </div>
243
+ """
244
+
245
+ def jump_logic(s_name, a_num):
246
+ # تصحيح منطق البحث
247
+ try:
248
+ s_idx = SURAH_NAMES.index(s_name) + 1
249
+ # البحث عن الآية المطابقة بدقة
250
+ target = next((v for v in ALL_AYAT if v["surah"] == s_idx and v["ayah"] == int(a_num)), None)
251
+
252
+ if not target:
253
+ return "<div class='toast-msg toast-error'>❌ عذراً، هذه الآية غير موجودة في قاعدة البيانات. تأكد من الرقم.</div>", gr.update(), gr.update(), 0
254
+
255
+ if len(target["text"]) > 250:
256
+ return "<div class='toast-msg toast-warn'>⚠️ الآية موجودة ولكنها طويلة جداً (أكثر من 250 حرف)، يرجى اختيار آية أقصر للتسجيل.</div>", gr.update(), gr.update(), 0
257
+
258
+ return "", f"<div class='ayah-text'>{target['text']}</div>", f"### سورة {target['surah_name']} - آية {target['ayah']}", ALL_AYAT.index(target)
259
+
260
+ except ValueError:
261
+ return "<div class='toast-msg toast-error'>❌ خطأ في إدخال البيانات</div>", gr.update(), gr.update(), 0
262
 
263
+ with gr.Blocks(title="منصة تلاوة", css=custom_css) as demo:
264
  idx_state = gr.State(random.randint(0, len(ALL_AYAT)-1))
265
  sess_state = gr.State(0)
266
 
267
+ # 1. القسم الافتتاحي (Hero Section)
268
+ with gr.Column(elem_classes="hero-wrapper"):
269
+ gr.HTML("""
270
+ <h1 class="hero-title">مِنَصَّة تِلاوَة</h1>
271
+ <p class="hero-subtitle">ساهم بصوتك في بناء أكبر قاعدة بيانات مفتوحة للقرآن الكريم</p>
272
+ <button class="cta-button" onclick="document.getElementById('work-section').scrollIntoView({behavior: 'smooth'})">ساهم الآن 👇</button>
273
+ <div class="scroll-down">﹀</div>
274
+ """)
275
 
276
+ # 2. منطقة العمل (Work Section)
277
+ with gr.Column(elem_id="work-section"):
278
+
279
+ # شريط الإحصائيات
280
+ stats_display = gr.HTML(create_stats_html(get_total(), 0))
281
 
282
+ with gr.Row():
283
+ # اليمين: عرض الآية
284
+ with gr.Column(scale=3):
285
+ with gr.Group(elem_classes="ayah-display"):
286
  init_v = ALL_AYAT[idx_state.value]
287
  info_text = gr.Markdown(f"### سورة {init_v['surah_name']} - آية {init_v['ayah']}")
288
  text_html = gr.HTML(f"<div class='ayah-text'>{init_v['text']}</div>")
289
+
290
+ with gr.Row():
291
+ send_btn = gr.Button("إرسال التسجيل ✅", variant="primary", scale=2)
292
+ rnd_btn = gr.Button("تغيير الآية 🎲", scale=1)
293
 
294
+ status_msg = gr.HTML("")
 
 
 
 
295
 
296
+ # اليسار: استوديو التسجيل والتحكم
297
+ with gr.Column(scale=2):
298
+ with gr.Group(elem_classes="studio-container"):
299
+ gr.HTML("<div class='studio-header'>🎙️ استوديو التسجيل</div>")
300
+ audio_ui = gr.Audio(sources=["microphone", "upload"], type="filepath", label=None)
301
 
302
+ with gr.Accordion("🔍 بحث عن آية محددة", open=False):
303
+ s_drop = gr.Dropdown(choices=SURAH_NAMES, label="اسم السورة", value=init_v['surah_name'])
304
+ a_num = gr.Number(label="رقم الآية", value=init_v['ayah'], precision=0)
305
+ go_btn = gr.Button("انتقال لهذه الآية")
306
 
307
+ # Events
 
308
  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, ""), outputs=[text_html, info_text, idx_state, status_msg])
309
+
310
+ go_btn.click(jump_logic, inputs=[s_drop, a_num], outputs=[status_msg, text_html, info_text, idx_state])
311
+
312
  send_btn.click(on_submit, inputs=[audio_ui, idx_state, sess_state], outputs=[status_msg, text_html, info_text, idx_state, sess_state, stats_display])
313
 
314
+ demo.launch()