Alide21 commited on
Commit
a386a7b
·
verified ·
1 Parent(s): e7b1c1e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +35 -165
app.py CHANGED
@@ -7,215 +7,85 @@ import uuid
7
  from huggingface_hub import HfApi
8
  from concurrent.futures import ThreadPoolExecutor
9
 
10
- # --- الإعدادات والمصادر ---
11
  HF_TOKEN = os.getenv("HF_TOKEN")
12
  REPO_ID = "Alide21/speech_quran"
13
  api = HfApi()
14
 
15
- SURAH_NAMES = ["الفاتحة", "البقرة", "آل عمران", "النساء", "المائدة", "الأنعام", "الأعراف", "الأنفال", "التوبة", "يونس", "هود", "يوسف", "الرعد", "إبراهيم", "الحجر", "النحل", "الإسراء", "الكهف", "مريم", "طه", "الأنبياء", "الحج", "المؤمنون", "النور", "الفرقان", "الشعراء", "النمل", "القصص", "العنكبوت", "الروم", "لقمان", "السجدة", "الأحزاب", "سبأ", "فاطر", "يس", "الصافات", "ص", "الزمر", "غافر", "فصلت", "الشورى", "الزخرف", "الدخان", "الجاثية", "الأحقاف", "محمد", "الفتح", "الحجرات", "ق", "الذاريات", "الطور", "النجم", "القمر", "الرحمن", "الواقعة", "الحديد", "المجادلة", "الحشر", "الممتحنة", "الصف", "الجمعة", "المنافقون", "التغابن", "الطلاق", "التحريم", "الملك", "اللم", "الحاقة", "المعارج", "نوح", "الجن", "المزمل", "المدثر", "القيامة", "الإنسان", "المرسلات", "النبأ", "النازعات", "عبس", "التكوير", "الانفطار", "المطففين", "الانشقاق", "البروج", "الطارق", "الأعلى", "الغاشية", "الفجر", "البلد", "الشمس", "الليل", "الضحى", "الشرح", "التين", "العلق", "القدر", "البينة", "الزلزلة", "العاديات", "القارعة", "التكاثر", "العصر", "الهمزة", "الفيل", "قريش", "الماعون", "الكوثر", "الكافرون", "النصر", "المسد", "الإخلاص", "الفلق", "الناس"]
16
 
17
- # --- تحسين CSS ---
18
  custom_css = """
19
  @import url('https://fonts.googleapis.com/css2?family=Amiri:wght@400;700&family=Reem+Kufi:wght@400;700&family=Scheherazade+New:wght@400;700&display=swap');
20
-
21
- :root {
22
- --primary: #064635;
23
- --secondary: #519259;
24
- --accent: #F4BB44;
25
- --text-dark: #2C3333;
26
- --bg-light: #F0F2F5;
27
- }
28
-
29
- body {
30
- direction: rtl;
31
- background-color: var(--bg-light);
32
- }
33
-
34
- /* تحسين شكل الحاوية الرئيسية */
35
- .gradio-container {
36
- font-family: 'Reem Kufi', sans-serif !important;
37
- }
38
-
39
- /* الهيدر الأنيق */
40
- #header-section {
41
- background: linear-gradient(135deg, var(--primary) 0%, #0a6b51 100%);
42
- padding: 50px 20px;
43
- border-radius: 0 0 50px 50px;
44
- text-align: center;
45
- color: white;
46
- box-shadow: 0 10px 25px rgba(0,0,0,0.2);
47
- margin-bottom: 30px;
48
- }
49
-
50
- .main-title {
51
- font-size: 3rem !important;
52
- margin-bottom: 10px;
53
- font-weight: 700;
54
- text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
55
- }
56
-
57
- /* بطاقة الآية */
58
- .ayah-card {
59
- background: white;
60
- border-radius: 25px;
61
- padding: 40px;
62
- box-shadow: 0 15px 35px rgba(0,0,0,0.05);
63
- border: 1px solid rgba(0,0,0,0.05);
64
- text-align: center;
65
- transition: all 0.3s ease;
66
- }
67
-
68
- .ayah-text {
69
- font-family: 'Amiri', serif !important;
70
- font-size: 2.8rem !important;
71
- line-height: 1.8 !important;
72
- color: var(--primary);
73
- margin: 20px 0;
74
- padding: 20px;
75
- }
76
-
77
- /* الأزرار */
78
- .custom-btn {
79
- border-radius: 12px !important;
80
- font-weight: bold !important;
81
- transition: transform 0.2s !important;
82
- }
83
- .custom-btn:hover { transform: scale(1.02); }
84
-
85
- /* التنبيهات */
86
- .alert-box {
87
- padding: 15px;
88
- border-radius: 12px;
89
- margin: 10px 0;
90
- font-weight: bold;
91
- }
92
  .alert-success { background: #d4edda; color: #155724; border-right: 5px solid #28a745; }
93
  .alert-error { background: #f8d7da; color: #721c24; border-right: 5px solid #dc3545; }
94
-
95
- /* تخصيص المسافات */
96
- .gap-20 { gap: 20px; }
97
  """
98
 
99
- # --- منطق البيانات ---
100
- def clean_text(text):
101
- return re.sub(r'<[^>]*>', '', text).strip()
102
 
103
  def fetch_surah(s_idx):
104
  try:
105
  url = f"https://raw.githubusercontent.com/CheeseWithSauce/TheHolyQuranJSONFormat/refs/heads/main/tajweedsurahs/{s_idx:03}.json"
106
  res = requests.get(url, timeout=5).json()
107
  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"]]
108
- except:
109
- return []
110
-
111
- def load_quran_data():
112
- all_verses = []
113
- # استخدام الـ Threading لتحميل البيانات بسرعة بدلاً من الانتظار التسلسلي
114
- with ThreadPoolExecutor(max_workers=10) as executor:
115
- results = list(executor.map(fetch_surah, range(1, 115)))
116
- for r in results:
117
- all_verses.extend(r)
118
- return all_verses
119
 
120
- ALL_AYAT = load_quran_data()
 
 
121
 
122
- # --- الوظائف المساعدة ---
123
  def get_stats_html(count):
124
- return f"""
125
- <div style="background: white; padding: 20px; border-radius: 15px; text-align: center; box-shadow: 0 4px 12px rgba(0,0,0,0.05);">
126
- <div style="font-size: 0.9rem; color: #666;">تلاواتك في هذه الجلسة</div>
127
- <div style="font-size: 2.5rem; color: var(--secondary); font-weight: bold;">{count}</div>
128
- <div style="font-size: 0.8rem; color: #aaa; margin-top: 5px;">جزاك الله خيراً على مساهمتك</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
- unique_id = str(uuid.uuid4())[:8]
138
- file_name = f"s{v['surah']}_a{v['ayah']}_{unique_id}.wav"
139
-
140
  try:
141
- api.upload_file(
142
- path_or_fileobj=audio,
143
- path_in_repo=f"data/{file_name}",
144
- repo_id=REPO_ID,
145
- repo_type="dataset",
146
- token=HF_TOKEN
147
- )
148
  new_sess = session_count + 1
149
- next_idx = random.randint(0, len(ALL_AYAT)-1) # الانتقال لآية عشوائية بعد النجاح
150
  nv = ALL_AYAT[next_idx]
151
-
152
- success_msg = f"<div class='alert-box alert-success'> تم حفظ التلاوة بنجاح! (آية {v['ayah']} - {v['surah_name']})</div>"
153
- return success_msg, f"<div class='ayah-text'>{nv['text']}</div>", f"### {nv['surah_name']} | آية {nv['ayah']}", next_idx, new_sess, get_stats_html(new_sess)
154
- except Exception as e:
155
- return f"<div class='alert-box alert-error'>❌ فشل الرفع: {str(e)}</div>", gr.update(), gr.update(), current_idx, session_count, gr.update()
156
 
157
- def jump_to_ayah(s_name, a_num):
158
- try:
159
- s_idx = SURAH_NAMES.index(s_name) + 1
160
- t_idx = next((i for i, v in enumerate(ALL_AYAT) if v["surah"] == s_idx and v["ayah"] == int(a_num)), None)
161
- if t_idx is not None:
162
- target = ALL_AYAT[t_idx]
163
- return "", f"<div class='ayah-text'>{target['text']}</div>", f"### {target['surah_name']} | آية {target['ayah']}", t_idx
164
- return "<div class='alert-box alert-error'>⚠️ لم نجد هذه الآية في السورة المختارة</div>", gr.update(), gr.update(), 0
165
- except:
166
- return "<div class='alert-box alert-error'>⚠️ حدث خطأ في البحث</div>", gr.update(), gr.update(), 0
167
-
168
- # --- بناء الواجهة ---
169
- with gr.Blocks(css=custom_css, title="منصة تلاوة") as demo:
170
  idx_state = gr.State(random.randint(0, len(ALL_AYAT)-1))
171
  sess_state = gr.State(0)
172
 
173
- # الهيدر
174
- gr.HTML("""
175
- <div id="header-section">
176
- <h1 class="main-title">مِنَصَّة التِّلَاوَة</h1>
177
- <p style="font-size: 1.2rem; opacity: 0.9;">ساهم في بناء أول قاعدة بيانات صوتية مفتوحة المصدر للقرآن الكريم</p>
178
- </div>
179
- """)
180
 
181
- with gr.Row(elem_classes="gap-20"):
182
- # العمود الأيمن: العرض
183
  with gr.Column(scale=3):
184
- with gr.Div(elem_classes="ayah-card"):
 
185
  curr_v = ALL_AYAT[idx_state.value]
186
  info_text = gr.Markdown(f"### {curr_v['surah_name']} | آية {curr_v['ayah']}")
187
  text_html = gr.HTML(f"<div class='ayah-text'>{curr_v['text']}</div>")
188
-
189
  status_msg = gr.HTML("")
190
-
191
  with gr.Row():
192
- send_btn = gr.Button("🎤 إرسال التلاوة الآن", variant="primary", elem_classes="custom-btn")
193
- rnd_btn = gr.Button("🔄 آية أخرى", variant="secondary", elem_classes="custom-btn")
194
 
195
- # العمود الأيسر: التحكم والإحصائيات
196
  with gr.Column(scale=1):
197
  stats_display = gr.HTML(get_stats_html(0))
198
-
199
- with gr.Group():
200
- gr.Markdown("### 🎙️ استوديو التسجيل")
201
- audio_ui = gr.Audio(sources=["microphone"], type="filepath", label=None)
202
-
203
- with gr.Accordion("🔍 انتقل لآية محددة", open=False):
204
  s_drop = gr.Dropdown(choices=SURAH_NAMES, label="السورة", value="الفاتحة")
205
  a_num = gr.Number(label="رقم الآية", value=1, precision=0)
206
- go_btn = gr.Button("انتقال", variant="secondary")
207
-
208
- # التذييل
209
- gr.HTML("""
210
- <div style="text-align: center; margin-top: 50px; padding: 20px; color: #666; border-top: 1px solid #ddd;">
211
- <p>© 2026 مشروع تلاوة - صدقة جارية لكل من ساهم بقراءته</p>
212
- </div>
213
- """)
214
 
215
- # الأحداث
216
  rnd_btn.click(lambda: (f"<div class='ayah-text'>{ALL_AYAT[i:=random.randint(0,len(ALL_AYAT)-1)]['text']}</div>", f"### {ALL_AYAT[i]['surah_name']} | آية {ALL_AYAT[i]['ayah']}", i, ""), None, [text_html, info_text, idx_state, status_msg])
217
- go_btn.click(jump_to_ayah, [s_drop, a_num], [status_msg, text_html, info_text, idx_state])
218
  send_btn.click(on_submit, [audio_ui, idx_state, sess_state], [status_msg, text_html, info_text, idx_state, sess_state, stats_display])
219
 
 
220
  if __name__ == "__main__":
221
- demo.launch()
 
7
  from huggingface_hub import HfApi
8
  from concurrent.futures import ThreadPoolExecutor
9
 
10
+ # --- الإعدادات (نفس الكود السابق) ---
11
  HF_TOKEN = os.getenv("HF_TOKEN")
12
  REPO_ID = "Alide21/speech_quran"
13
  api = HfApi()
14
 
15
+ SURAH_NAMES = ["الفاتحة", "البقرة", "آل عمران", "النساء", "المائدة", "الأنعام", "الأعراف", "الأنفال", "التوبة", "يونس", "هود", "يوسف", "الرعد", "إبراهيم", "الحجر", "النحل", "الإسراء", "الكهف", "مريم", "طه", "الأنبياء", "الحج", "المؤمنون", "النور", "الفرقان", "الشعراء", "النمل", "القصص", "العنكبوت", "الروم", "لقمان", "السجدة", "الأحزاب", "سبأ", "فاطر", "يس", "الصافات", "ص", "الزمر", "غافر", "فصلت", "الشورى", "الزخرف", "الدخان", "الجاثية", "الأحقاف", "محمد", "الفتح", "الحجرات", "ق", "الذاريات", "الطور", "النجم", "القمر", "الرحمن", "الواقعة", "الحديد", "المجادلة", "الحشر", "الممتحنة", "الصف", "الجمعة", "المنافقون", "التغابن", "الطلاق", "التحريم", "الملك", "القلم", "الحاقة", "المعارج", "نوح", "الجن", "المزمل", "المدثر", "القيامة", "الإنسان", "المرسلات", "النبأ", "النازعات", "عبس", "التكوير", "الانفطار", "المطففين", "الانشقاق", "البروج", "الطارق", "الأعلى", "الغاشية", "الفجر", "البلد", "الشمس", "الليل", "الضحى", "الشرح", "التين", "العلق", "القدر", "البينة", "الزلزلة", "العاديات", "القارعة", "التكاثر", "العصر", "الهمزة", "الفيل", "قريش", "الماعون", "الكوثر", "الكافرون", "النصر", "المسد", "الإخلاص", "الفلق", "الناس"]
16
 
17
+ # --- التنسيقات (نفس الـ CSS الرائع) ---
18
  custom_css = """
19
  @import url('https://fonts.googleapis.com/css2?family=Amiri:wght@400;700&family=Reem+Kufi:wght@400;700&family=Scheherazade+New:wght@400;700&display=swap');
20
+ :root { --primary: #064635; --secondary: #519259; --accent: #F4BB44; --bg-light: #F0F2F5; }
21
+ body { direction: rtl; background-color: var(--bg-light); }
22
+ #header-section { background: linear-gradient(135deg, var(--primary) 0%, #0a6b51 100%); padding: 50px 20px; border-radius: 0 0 50px 50px; text-align: center; color: white; margin-bottom: 30px; }
23
+ .ayah-card { background: white; border-radius: 25px; padding: 40px; box-shadow: 0 15px 35px rgba(0,0,0,0.05); text-align: center; }
24
+ .ayah-text { font-family: 'Amiri', serif !important; font-size: 2.5rem !important; line-height: 1.8 !important; color: var(--primary); }
25
+ .alert-box { padding: 15px; border-radius: 12px; margin: 10px 0; font-weight: bold; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  .alert-success { background: #d4edda; color: #155724; border-right: 5px solid #28a745; }
27
  .alert-error { background: #f8d7da; color: #721c24; border-right: 5px solid #dc3545; }
 
 
 
28
  """
29
 
30
+ # --- الوظائف المنطقية ---
31
+ def clean_text(text): return re.sub(r'<[^>]*>', '', text).strip()
 
32
 
33
  def fetch_surah(s_idx):
34
  try:
35
  url = f"https://raw.githubusercontent.com/CheeseWithSauce/TheHolyQuranJSONFormat/refs/heads/main/tajweedsurahs/{s_idx:03}.json"
36
  res = requests.get(url, timeout=5).json()
37
  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"]]
38
+ except: return []
 
 
 
 
 
 
 
 
 
 
39
 
40
+ with ThreadPoolExecutor(max_workers=10) as executor:
41
+ results = list(executor.map(fetch_surah, range(1, 115)))
42
+ ALL_AYAT = [item for sublist in results for item in sublist]
43
 
 
44
  def get_stats_html(count):
45
+ return f"<div style='background: white; padding: 15px; border-radius: 15px; text-align: center;'>جلسة تلاوة: <b style='color:var(--secondary)'>{count}</b></div>"
 
 
 
 
 
 
46
 
47
  def on_submit(audio, current_idx, session_count):
48
+ if not audio: return "<div class='alert-box alert-error'>⚠️ سجل تلاوتك أولاً</div>", gr.update(), gr.update(), current_idx, session_count, gr.update()
 
 
49
  v = ALL_AYAT[current_idx]
 
 
 
50
  try:
51
+ 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)
 
 
 
 
 
 
52
  new_sess = session_count + 1
53
+ next_idx = random.randint(0, len(ALL_AYAT)-1)
54
  nv = ALL_AYAT[next_idx]
55
+ return f"<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)
56
+ except Exception as e: return f"<div class='alert-box alert-error'> خطأ: {str(e)}</div>", gr.update(), gr.update(), current_idx, session_count, gr.update()
 
 
 
57
 
58
+ # --- بناء الواجهة المحسنة لـ Gradio 6.0 ---
59
+ with gr.Blocks(title="منصة تلاوة") as demo: # لاحظ: حذفنا css من هنا
 
 
 
 
 
 
 
 
 
 
 
60
  idx_state = gr.State(random.randint(0, len(ALL_AYAT)-1))
61
  sess_state = gr.State(0)
62
 
63
+ gr.HTML('<div id="header-section"><h1>مِنَصَّة التِّلَاوَة</h1><p>بناء قاعدة بيانات صوتية للقرآن الكريم</p></div>')
 
 
 
 
 
 
64
 
65
+ with gr.Row():
 
66
  with gr.Column(scale=3):
67
+ # تم استبدال gr.Div بـ gr.Column مع الكلاس المناسب
68
+ with gr.Column(elem_classes="ayah-card"):
69
  curr_v = ALL_AYAT[idx_state.value]
70
  info_text = gr.Markdown(f"### {curr_v['surah_name']} | آية {curr_v['ayah']}")
71
  text_html = gr.HTML(f"<div class='ayah-text'>{curr_v['text']}</div>")
 
72
  status_msg = gr.HTML("")
 
73
  with gr.Row():
74
+ send_btn = gr.Button("🎤 إرسال التلاوة", variant="primary")
75
+ rnd_btn = gr.Button("🔄 آية عشوائية", variant="secondary")
76
 
 
77
  with gr.Column(scale=1):
78
  stats_display = gr.HTML(get_stats_html(0))
79
+ audio_ui = gr.Audio(sources=["microphone"], type="filepath", label="سجل هنا")
80
+ with gr.Accordion("🔍 بحث", open=False):
 
 
 
 
81
  s_drop = gr.Dropdown(choices=SURAH_NAMES, label="السورة", value="الفاتحة")
82
  a_num = gr.Number(label="رقم الآية", value=1, precision=0)
83
+ go_btn = gr.Button("انتقال")
 
 
 
 
 
 
 
84
 
85
+ # ربط الأحداث
86
  rnd_btn.click(lambda: (f"<div class='ayah-text'>{ALL_AYAT[i:=random.randint(0,len(ALL_AYAT)-1)]['text']}</div>", f"### {ALL_AYAT[i]['surah_name']} | آية {ALL_AYAT[i]['ayah']}", i, ""), None, [text_html, info_text, idx_state, status_msg])
 
87
  send_btn.click(on_submit, [audio_ui, idx_state, sess_state], [status_msg, text_html, info_text, idx_state, sess_state, stats_display])
88
 
89
+ # تشغيل التطبي�� مع تمرير الـ CSS هنا (قاعدة Gradio 6.0)
90
  if __name__ == "__main__":
91
+ demo.launch(css=custom_css)