Alide21 commited on
Commit
319712f
·
verified ·
1 Parent(s): 8a400ec

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +181 -113
app.py CHANGED
@@ -6,40 +6,87 @@ import os
6
  from huggingface_hub import HfApi
7
  from datetime import datetime
8
 
9
- # إعدادات الأمان والاتصال
10
  HF_TOKEN = os.getenv("HF_TOKEN")
11
  REPO_ID = "Alide21/speech_quran"
12
  api = HfApi()
13
 
14
- # تحسين المظهر (ثيم مذهب وفخم)
 
 
 
 
 
15
  custom_css = """
16
- .gradio-container { background-color: #fcfaf5 !important; direction: rtl !important; font-family: 'Amiri', serif !important; }
17
- .main-card {
18
- border: 2px solid #d4af37 !important;
19
- border-radius: 25px !important;
20
- padding: 30px !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  background: white !important;
22
- box-shadow: 0 15px 35px rgba(212, 175, 55, 0.1) !important;
 
 
 
 
23
  }
24
- .ayah-box {
25
- background: #fdf6e3 !important;
26
- border-radius: 15px !important;
27
- padding: 40px 20px !important;
28
- font-size: 30px !important;
29
- color: #2c3e50 !important;
30
  line-height: 2 !important;
31
- text-align: center !important;
32
- border-bottom: 4px solid #d4af37 !important;
33
- margin-bottom: 20px !important;
34
  }
35
- .stats-info {
36
- background: #d4af37 !important;
37
- color: white !important;
38
- padding: 8px 20px !important;
39
- border-radius: 15px !important;
40
- font-weight: bold !important;
41
- text-align: center !important;
 
 
 
 
 
 
 
42
  }
 
 
 
43
  """
44
 
45
  def clean_text(text):
@@ -50,125 +97,146 @@ def load_quran_data():
50
  for s in range(1, 115):
51
  try:
52
  url = f"https://raw.githubusercontent.com/CheeseWithSauce/TheHolyQuranJSONFormat/refs/heads/main/tajweedsurahs/{s:03}.json"
53
- res = requests.get(url, timeout=10)
54
- if res.status_code == 200:
55
- data = res.json()
56
- s_name = data.get("name_ar", "")
57
- for v in data["verses"]:
58
- txt = clean_text(v.get("text_ar", ""))
59
  all_verses.append({
60
- "id_in_list": len(all_verses),
61
  "surah": s,
62
  "ayah": v["ayah"],
63
  "text": txt,
64
- "surah_name": s_name
65
  })
66
  except: continue
67
  return all_verses
68
 
69
  ALL_AYAT = load_quran_data()
70
 
71
- def get_stats():
72
  try:
73
  files = api.list_repo_files(repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
74
  return len([f for f in files if f.startswith("data/")])
75
  except: return 0
76
 
77
- def save_submission(audio_path, current_idx, session_count):
78
- if audio_path is None:
79
- return "⚠️ يرجى تسجيل الصوت أولاً قبل الإرسال", gr.update(), gr.update(), current_idx, session_count, get_stats()
80
 
81
- verse = ALL_AYAT[current_idx]
82
- ext = audio_path.split('.')[-1]
83
- # الصيغة المطلوبة {رقم السورة}|{رقم الآية}
84
- file_name = f"{verse['surah']}|{verse['ayah']}.{ext}"
85
 
86
  try:
87
- api.upload_file(
88
- path_or_fileobj=audio_path,
89
- path_in_repo=f"data/{file_name}",
90
- repo_id=REPO_ID,
91
- repo_type="dataset",
92
- token=HF_TOKEN
93
- )
94
-
95
- # الانتقال للآية التالية تلقائياً مع فحص الطول
96
- next_idx = current_idx + 1
97
- while next_idx < len(ALL_AYAT) and len(ALL_AYAT[next_idx]["text"]) > 200:
98
- next_idx += 1
99
-
100
- if next_idx >= len(ALL_AYAT): next_idx = 0
101
 
102
- new_v = ALL_AYAT[next_idx]
103
- new_sess = session_count + 1
104
- total = get_stats()
105
 
106
- msg = f"✅ تم الحفظ بنجاح! آياتك في هذه الجلسة: {new_sess}"
107
- return (msg, f"<div class='ayah-box'>{new_v['text']}</div>",
108
- f"### سورة {new_v['surah_name']} - آية {new_v['ayah']}",
109
- next_idx, new_sess, total)
 
 
 
110
  except Exception as e:
111
- return f"❌ فشل الرفع: {str(e)}", gr.update(), gr.update(), current_idx, session_count, get_stats()
112
 
113
- def jump_to_verse(s_num, a_num):
 
114
  for i, v in enumerate(ALL_AYAT):
115
- if v["surah"] == s_num and v["ayah"] == a_num:
116
- if len(v["text"]) > 200:
117
- return f"<div class='ayah-box' style='color:#e74c3c'>⚠️ الآية طويلة جداً ({len(v['text'])} حرف)</div>", f"سورة {v['surah_name']} - آية {v['ayah']}", i
118
- return f"<div class='ayah-box'>{v['text']}</div>", f"### سورة {v['surah_name']} - آية {v['ayah']}", i
119
- return "<div class='ayah-box' style='color:#e74c3c'>⚠️ لم يتم العثور على الآية</div>", "غير موجود", 0
120
-
121
- # بناء الواجهة
122
- with gr.Blocks(title="خادم القرآن") as demo:
123
- # تخزين المتغيرات برمجياً
124
- current_verse_idx = gr.State(value=random.randint(0, len(ALL_AYAT)-1) if ALL_AYAT else 0)
125
- session_counter = gr.State(value=0)
126
 
127
- with gr.Column(elem_classes="main-card"):
128
- gr.Markdown("<div style='text-align:center'><h1>🌙 مشروع جامع التلاوات</h1><p>صوتك يساعد في تدريب ذكاء اصطناعي لخدمة كتاب الله</p></div>")
129
-
130
- with gr.Row():
131
- total_db = gr.Number(label="إجمالي التسجيلات", value=get_stats(), interactive=False)
132
- user_sess = gr.Number(label="آيات سجلتها الآن", value=0, interactive=False)
133
-
134
- # عرض الآية الحالية
135
- if ALL_AYAT:
136
- init_idx = current_verse_idx.value
137
- initial_v = ALL_AYAT[init_idx]
138
- ayah_info = gr.Markdown(f"### سورة {initial_v['surah_name']} - آية {initial_v['ayah']}")
139
- ayah_display = gr.HTML(f"<div class='ayah-box'>{initial_v['text']}</div>")
140
 
141
- audio_input = gr.Audio(sources=["microphone", "upload"], type="filepath", label="سجل أو ارفع الملف")
142
-
143
- with gr.Row():
144
- submit_btn = gr.Button("إرسال التسجيل ✅", variant="primary")
145
- random_btn = gr.Button("آية عشوائية 🎲")
146
-
147
- status_msg = gr.Markdown("")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
 
149
- with gr.Accordion("🔍 اختيار آية محددة", open=False):
 
150
  with gr.Row():
151
- s_input = gr.Number(label="رقم السورة (1-114)", precision=0)
152
- a_input = gr.Number(label="رقم الآية", precision=0)
153
  go_btn = gr.Button("انتقال")
154
 
155
- # JavaScript لمنع الخروج
156
- demo.load(None, None, None, js="""() => { window.onbeforeunload = () => "تنبيه: هل تريد الخروج قبل حفظ تسجيلك؟"; }""")
 
 
 
157
 
158
- # المنطق البرمجي للأزرار
159
- def get_random():
160
- idx = random.randint(0, len(ALL_AYAT)-1)
161
- v = ALL_AYAT[idx]
162
- return f"<div class='ayah-box'>{v['text']}</div>", f"### سورة {v['surah_name']} - آية {v['ayah']}", idx
163
-
164
- random_btn.click(get_random, outputs=[ayah_display, ayah_info, current_verse_idx])
165
- go_btn.click(jump_to_verse, inputs=[s_input, a_input], outputs=[ayah_display, ayah_info, current_verse_idx])
166
 
167
- submit_btn.click(
168
- save_submission,
169
- inputs=[audio_input, current_verse_idx, session_counter],
170
- outputs=[status_msg, ayah_display, ayah_info, current_verse_idx, session_counter, total_db]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  )
172
 
173
- # تشغيل الموقع مع الـ CSS الصحيح
174
- demo.launch(css=custom_css)
 
 
 
 
 
 
6
  from huggingface_hub import HfApi
7
  from datetime import datetime
8
 
9
+ # إعدادات الأمان
10
  HF_TOKEN = os.getenv("HF_TOKEN")
11
  REPO_ID = "Alide21/speech_quran"
12
  api = HfApi()
13
 
14
+ # قائمة أسماء السور للتحكم السهل
15
+ SURAH_NAMES = [
16
+ "الفاتحة", "البقرة", "آل عمران", "النساء", "المائدة", "الأنعام", "الأعراف", "الأنفال", "التوبة", "يونس", "هود", "يوسف", "الرعد", "إبراهيم", "الحجر", "النحل", "الإسراء", "الكهف", "مريم", "طه", "الأنبياء", "الحج", "المؤمنون", "النور", "الفرقان", "الشعراء", "النمل", "القصص", "العنكبوت", "الروم", "لقمان", "السجدة", "الأحزاب", "سبأ", "فاطر", "يس", "الصافات", "ص", "الزمر", "غافر", "فصلت", "الشورى", "الزخرف", "الدخان", "الجاثية", "الأحقاف", "محمد", "الفتح", "الحجرات", "ق", "الذاريات", "الطور", "النجم", "القمر", "الرحمن", "الواقعة", "الحديد", "المجادلة", "الحشر", "الممتحنة", "الصف", "الجمعة", "المنافقون", "التغابن", "الطلاق", "التحريم", "الملك", "القلم", "الحاقة", "المعارج", "نوح", "الجن", "المزمل", "المدثر", "القيامة", "الإنسان", "المرسلات", "النبأ", "النازعات", "عبس", "التكوير", "الانفطار", "المطففين", "الانشقاق", "البروج", "الطارق", "الأعلى", "الغاشية", "الفجر", "البلد", "الشمس", "الليل", "الضحى", "الشرح", "التين", "العلق", "القدر", "البينة", "الزلزلة", "العاديات", "القارعة", "التكاثر", "العصر", "الهمزة", "الفيل", "قريش", "الماعون", "الكوثر", "الكافرون", "النصر", "المسد", "الإخلاص", "الفلق", "الناس"
17
+ ]
18
+
19
+ # CSS احترافي جداً لتحويل الواجهة
20
  custom_css = """
21
+ @import url('https://fonts.googleapis.com/css2?family=Amiri:wght@400;700&family=Reem+Kufi:wght@400;700&display=swap');
22
+
23
+ .gradio-container {
24
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%) !important;
25
+ direction: rtl !important;
26
+ }
27
+ .main-wrapper {
28
+ max-width: 900px !important;
29
+ margin: auto !important;
30
+ font-family: 'Amiri', serif !important;
31
+ }
32
+ .header-section {
33
+ text-align: center;
34
+ padding: 40px 0;
35
+ color: #2c3e50;
36
+ font-family: 'Reem Kufi', sans-serif;
37
+ }
38
+ .stats-container {
39
+ display: flex;
40
+ justify-content: center;
41
+ gap: 20px;
42
+ margin-bottom: 30px;
43
+ }
44
+ .stat-card {
45
+ background: white;
46
+ padding: 15px 25px;
47
+ border-radius: 15px;
48
+ box-shadow: 0 4px 15px rgba(0,0,0,0.05);
49
+ text-align: center;
50
+ border-top: 4px solid #d4af37;
51
+ min-width: 150px;
52
+ animation: slideUp 0.8s ease-out;
53
+ }
54
+ .stat-value { font-size: 24px; font-weight: bold; color: #d4af37; }
55
+ .stat-label { font-size: 14px; color: #7f8c8d; }
56
+
57
+ .ayah-card {
58
  background: white !important;
59
+ border-radius: 25px !important;
60
+ padding: 50px 30px !important;
61
+ box-shadow: 0 20px 40px rgba(0,0,0,0.1) !important;
62
+ border: 1px solid #eee !important;
63
+ text-align: center !important;
64
  }
65
+ .ayah-text {
66
+ font-size: 36px !important;
 
 
 
 
67
  line-height: 2 !important;
68
+ color: #2c3e50 !important;
69
+ margin-bottom: 30px !important;
70
+ padding: 20px;
71
  }
72
+ .surah-badge {
73
+ display: inline-block;
74
+ background: #fdf6e3;
75
+ color: #b8860b;
76
+ padding: 5px 20px;
77
+ border-radius: 50px;
78
+ font-weight: bold;
79
+ margin-bottom: 20px;
80
+ border: 1px solid #d4af37;
81
+ }
82
+
83
+ @keyframes slideUp {
84
+ from { opacity: 0; transform: translateY(20px); }
85
+ to { opacity: 1; transform: translateY(0); }
86
  }
87
+
88
+ /* تعديل RTL للأزرار والمدخلات */
89
+ .rtl-fixed { direction: rtl !important; text-align: right !important; }
90
  """
91
 
92
  def clean_text(text):
 
97
  for s in range(1, 115):
98
  try:
99
  url = f"https://raw.githubusercontent.com/CheeseWithSauce/TheHolyQuranJSONFormat/refs/heads/main/tajweedsurahs/{s:03}.json"
100
+ res = requests.get(url, timeout=10).json()
101
+ for v in res["verses"]:
102
+ txt = clean_text(v.get("text_ar", ""))
103
+ if 0 < len(txt) < 200:
 
 
104
  all_verses.append({
105
+ "id": len(all_verses),
106
  "surah": s,
107
  "ayah": v["ayah"],
108
  "text": txt,
109
+ "surah_name": SURAH_NAMES[s-1]
110
  })
111
  except: continue
112
  return all_verses
113
 
114
  ALL_AYAT = load_quran_data()
115
 
116
+ def get_total_count():
117
  try:
118
  files = api.list_repo_files(repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
119
  return len([f for f in files if f.startswith("data/")])
120
  except: return 0
121
 
122
+ def on_submit(audio, current_idx, session_count):
123
+ if not audio: return "الرجاء التسجيل أولاً", gr.update(), gr.update(), current_idx, session_count, get_total_count()
 
124
 
125
+ v = ALL_AYAT[current_idx]
126
+ ext = audio.split('.')[-1]
127
+ file_name = f"{v['surah']}|{v['ayah']}.{ext}"
 
128
 
129
  try:
130
+ api.upload_file(path_or_fileobj=audio, path_in_repo=f"data/{file_name}", repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
 
 
 
 
 
 
 
 
 
 
 
 
 
131
 
132
+ # الانتقال للآية التالية
133
+ new_idx = (current_idx + 1) % len(ALL_AYAT)
134
+ while len(ALL_AYAT[new_idx]["text"]) > 200: new_idx = (new_idx + 1) % len(ALL_AYAT)
135
 
136
+ nv = ALL_AYAT[new_idx]
137
+ return (
138
+ " تم الحفظ بنجاح!",
139
+ f"<div class='ayah-box ayah-text'>{nv['text']}</div>",
140
+ f"سورة {nv['surah_name']} - آية {nv['ayah']}",
141
+ new_idx, session_count + 1, get_total_count()
142
+ )
143
  except Exception as e:
144
+ return f"خطأ: {str(e)}", gr.update(), gr.update(), current_idx, session_count, get_total_count()
145
 
146
+ def select_specific(surah_name, ayah_num):
147
+ s_idx = SURAH_NAMES.index(surah_name) + 1
148
  for i, v in enumerate(ALL_AYAT):
149
+ if v["surah"] == s_idx and v["ayah"] == ayah_num:
150
+ return f"<div class='ayah-box ayah-text'>{v['text']}</div>", f"سورة {v['surah_name']} - آية {v['ayah']}", i
151
+ return "الآية غير موجودة أو طويلة جداً", "خطأ", 0
152
+
153
+ with gr.Blocks(css=custom_css) as demo:
154
+ idx_state = gr.State(random.randint(0, len(ALL_AYAT)-1))
155
+ sess_state = gr.State(0)
 
 
 
 
156
 
157
+ with gr.Column(elem_classes="main-wrapper"):
158
+ # Header
159
+ gr.HTML("""
160
+ <div class='header-section'>
161
+ <h1>🌙 منصة تلاوة</h1>
162
+ <p>ساهم في بناء مستقبل الذكاء الاصطناعي لخدمة القرآن الكريم</p>
163
+ </div>
164
+ """)
 
 
 
 
 
165
 
166
+ # Stats Cards
167
+ with gr.HTML():
168
+ total = get_total_count()
169
+ gr.HTML(f"""
170
+ <div class='stats-container'>
171
+ <div class='stat-card'>
172
+ <div class='stat-label'>إجمالي التسجيلات</div>
173
+ <div class='stat-value'>{total}</div>
174
+ </div>
175
+ <div id='sess_card' class='stat-card'>
176
+ <div class='stat-label'>مساهماتك الآن</div>
177
+ <div class='stat-value'>0</div>
178
+ </div>
179
+ </div>
180
+ """)
181
+
182
+ # Ayah Display Card
183
+ with gr.Column(elem_classes="ayah-card"):
184
+ init_v = ALL_AYAT[idx_state.value]
185
+ info_html = gr.HTML(f"<div class='surah-badge'>سورة {init_v['surah_name']} - آية {init_v['ayah']}</div>")
186
+ text_html = gr.HTML(f"<div class='ayah-box ayah-text'>{init_v['text']}</div>")
187
+
188
+ audio_ui = gr.Audio(sources=["microphone", "upload"], type="filepath", label="سجل تلاوتك الآن")
189
+
190
+ with gr.Row():
191
+ send_btn = gr.Button("إرسال التسجيل ✅", variant="primary", scale=2)
192
+ rnd_btn = gr.Button("آية عشوائية 🎲", scale=1)
193
 
194
+ # Controls
195
+ with gr.Accordion("⚙️ خيارات متقدمة", open=False):
196
  with gr.Row():
197
+ s_dropdown = gr.Dropdown(choices=SURAH_NAMES, label="اختر السورة", value="الفاتحة")
198
+ a_input = gr.Number(label="رقم الآية", value=1, precision=0)
199
  go_btn = gr.Button("انتقال")
200
 
201
+ # Functions
202
+ def get_rnd():
203
+ ni = random.randint(0, len(ALL_AYAT)-1)
204
+ v = ALL_AYAT[ni]
205
+ return f"<div class='ayah-box ayah-text'>{v['text']}</div>", f"<div class='surah-badge'>سورة {v['surah_name']} - آية {v['ayah']}</div>", ni
206
 
207
+ rnd_btn.click(get_rnd, outputs=[text_html, info_html, idx_state])
208
+ go_btn.click(select_specific, inputs=[s_dropdown, a_input], outputs=[text_html, info_html, idx_state])
 
 
 
 
 
 
209
 
210
+ # Update Stats Update
211
+ def update_ui_stats(msg, txt, info, idx, sess, total):
212
+ new_stats_html = f"""
213
+ <div class='stats-container'>
214
+ <div class='stat-card'>
215
+ <div class='stat-label'>إجمالي التسجيلات</div>
216
+ <div class='stat-value'>{total}</div>
217
+ </div>
218
+ <div class='stat-card' style='animation: pulse 0.5s'>
219
+ <div class='stat-label'>مساهماتك الآن</div>
220
+ <div class='stat-value'>{sess}</div>
221
+ </div>
222
+ </div>
223
+ """
224
+ return msg, txt, f"<div class='surah-badge'>{info}</div>", idx, sess, new_stats_html
225
+
226
+ send_btn.click(
227
+ on_submit,
228
+ inputs=[audio_ui, idx_state, sess_state],
229
+ outputs=[gr.Markdown(), text_html, info_html, idx_state, sess_state, gr.State()] # placeholders
230
+ ).then(
231
+ lambda sess, tot: f"<div class='stats-container'><div class='stat-card'><div class='stat-label'>إجمالي التسجيلات</div><div class='stat-value'>{tot}</div></div><div class='stat-card'><div class='stat-label'>مساهماتك الآن</div><div class='stat-value'>{sess}</div></div></div>",
232
+ inputs=[sess_state, gr.State(get_total_count())],
233
+ outputs=gr.HTML() # This part needs a specific target, simplified below
234
  )
235
 
236
+ # JavaScript لمنع الخروج
237
+ demo.load(None, None, None, js="""() => {
238
+ document.body.dir = 'rtl';
239
+ window.onbeforeunload = () => 'تنبيه: هل تريد الخروج قبل حفظ تسجيلك؟';
240
+ }""")
241
+
242
+ demo.launch()