melakio commited on
Commit
882232f
·
1 Parent(s): 6b662c2

improve khotba

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
index.js CHANGED
@@ -21,6 +21,21 @@ function removeHarakats(text) {
21
  return text.replace(/[ًٌٍَُِّْٰ]/g, '');
22
  }
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  // 1. Global Security
25
  fastify.register(rateLimit, {
26
  global: true,
@@ -158,29 +173,115 @@ fastify.post('/ai-mentor/chat', async (request, reply) => {
158
  }
159
  });
160
 
161
- const { GoogleGenerativeAI } = require('@google/generative-ai');
 
 
 
 
162
 
163
- // Initialize Gemini
164
- const API_KEY = process.env.GEMINI_API_KEY || 'AIzaSyAxh2hMGR9D2zNgu0uNIlVPNLcJSMJ-lvo';
165
- console.log('[Gemini] Initializing with key starting with:', API_KEY.substring(0, 10));
166
 
167
- const genAI = new GoogleGenerativeAI(API_KEY);
168
- const SYSTEM_INSTRUCTION = `Expert Islamic Scholar Translator.
169
- Task: Transcribe & translate Arabic audio to French.
170
- Context: Friday Sermons (Khotba). Use H_AR/H_FR to ensure continuity and correct audio corruption.
171
- Output: {"ar": "transcription", "fr": "translation"}. No extra text. JSON ONLY.`;
172
 
173
- const model = genAI.getGenerativeModel({
174
- model: 'gemini-2.5-flash-lite',
175
- systemInstruction: SYSTEM_INSTRUCTION,
176
- generationConfig: {
177
- responseMimeType: 'application/json',
178
- maxOutputTokens: 1024,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  }
180
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
 
182
  fastify.post('/khotba/translate', async (request, reply) => {
183
- console.log('[Khotba] Received translation request (Optimized)');
184
  try {
185
  const { audioData, mimeType, historyAr = '', historyFr = '' } = request.body;
186
 
@@ -189,75 +290,84 @@ fastify.post('/khotba/translate', async (request, reply) => {
189
  return reply.status(400).send({ error: 'Missing audioData' });
190
  }
191
 
192
- // Token Optimization: Truncate history to save input tokens
193
  const truncate = (str, limit) => {
194
  const words = str.split(/\s+/);
195
  if (words.length <= limit) return str;
196
  return '...' + words.slice(-limit).join(' ');
197
  };
198
-
199
  const trunkAr = truncate(historyAr, 80);
200
  const trunkFr = truncate(historyFr, 80);
201
 
202
- console.log(`[Khotba] Payload size: ${Math.round(audioData.length / 1024)}KB. History: ${trunkAr.length + trunkFr.length} chars.`);
203
-
204
- // Minimized prompt for faster/cheaper execution
205
- const userPrompt = `History:
206
- AR: ${trunkAr}
207
- FR: ${trunkFr}
208
- Process attached audio.`;
209
-
210
- console.log('[Gemini] Requesting translation...');
211
- const result = await model.generateContent([
212
- { text: userPrompt },
213
- {
214
- inlineData: {
215
- mimeType: mimeType || 'audio/webm',
216
- data: audioData
217
- }
218
- }
219
- ]);
220
-
221
- console.log('[Gemini] Response received');
222
-
223
- // Safety/Finish reasons
224
- const response = result.response;
225
- const candidate = response.candidates?.[0];
226
- if (candidate?.finishReason === 'SAFETY') {
227
- return reply.status(400).send({ error: 'Blocked by safety filters' });
 
 
228
  }
229
 
230
- try {
231
- // Native JSON mode: response.text() should be pure JSON
232
- const finalResult = JSON.parse(response.text());
233
- console.log('[Khotba] Success');
234
- return { response: finalResult };
235
- } catch (parseError) {
236
- console.error('[Khotba] JSON Parse Error:', parseError.message);
237
- // Fallback to manual extraction just in case
238
- const raw = response.text();
239
- const jsonMatch = raw.match(/\{[\s\S]*\}/);
240
- if (jsonMatch) {
241
- return { response: JSON.parse(jsonMatch[0]) };
242
- }
243
- throw parseError;
 
244
  }
 
 
 
 
 
 
 
 
 
 
245
  } catch (error) {
246
- console.error('[Khotba] Error caught:', error.message);
 
 
247
 
248
- if (error.status === 429) {
249
- const retryDelay = error.errorDetails?.find(d => d.retryDelay)?.retryDelay || '30s';
250
- console.warn(`[Khotba] Quota exceeded (429). Retry after: ${retryDelay}`);
251
  return reply.status(429).send({
252
- error: 'Quota exceeded (Free Plan). Please wait.',
253
- retryAfter: retryDelay
254
  });
255
  }
256
 
257
- return reply.status(500).send({
258
- error: error.message,
259
- stack: error.stack
260
- });
261
  }
262
  });
263
 
@@ -269,7 +379,9 @@ fastify.get('/tts', async (request, reply) => {
269
  const buffer = await response.arrayBuffer();
270
  reply.header('Content-Type', 'audio/wav');
271
  reply.send(Buffer.from(buffer));
272
- } catch (err) { reply.status(500).send({ error: 'TTS failed' }); }
 
 
273
  });
274
 
275
  // Server Start
 
21
  return text.replace(/[ًٌٍَُِّْٰ]/g, '');
22
  }
23
 
24
+ /**
25
+ * Filtre anti-hallucination pour le modèle Whisper.
26
+ * Whisper a tendance à "halluciner" du texte (ex: "Thank you", "[Musique]")
27
+ * lorsqu'il est confronté à du bruit de fond ou du silence absolu.
28
+ */
29
+ function isWhisperHallucination(text) {
30
+ if (!text) return true;
31
+ const PATTERNS = [
32
+ /^[\s\u200b-\u200d\ufeff]+$/, // Caractères invisibles
33
+ /^(thank you|thanks|music|♪|\.{3,}|…+|subtitles.*|subscribe.*)$/i, // Fillers classiques Whisper
34
+ /^(شكرًا|شكرا|موسيقى|🎵|\[موسيقى\]|\[صمت\]|\[ضوضاء\])$/i, // Fillers arabes
35
+ ];
36
+ return PATTERNS.some(p => p.test(text.trim()));
37
+ }
38
+
39
  // 1. Global Security
40
  fastify.register(rateLimit, {
41
  global: true,
 
173
  }
174
  });
175
 
176
+ // ── Groq Configuration ────────────────────────────────────────────────────
177
+ const GROQ_API_KEY = process.env.GROQ_API_KEY || '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';
178
+ console.log('[Groq] Initialized. Key prefix:', GROQ_API_KEY.substring(0, 10));
179
+
180
+
181
 
 
 
 
182
 
183
+ // Groq Whisper: transcribe audio buffer → Arabic text (uses axios — native fetch breaks multipart)
184
+ async function groqTranscribe(audioBuffer, mimeType, previousAr = '') {
185
+ const FormData = require('form-data');
186
+ const axios = require('axios');
187
+ const form = new FormData();
188
 
189
+ // Ext mapping for proper multipart processing
190
+ const extMap = {
191
+ 'audio/webm': 'audio.webm',
192
+ 'audio/ogg': 'audio.ogg',
193
+ 'audio/mp4': 'audio.mp4',
194
+ 'audio/mpeg': 'audio.mp3',
195
+ 'audio/wav': 'audio.wav',
196
+ 'audio/x-wav': 'audio.wav',
197
+ 'audio/webm;codecs=opus': 'audio.webm',
198
+ };
199
+ const filename = extMap[mimeType] || 'audio.webm';
200
+ const cleanMime = mimeType?.split(';')[0] || 'audio/webm';
201
+
202
+ // Clean prompt for continuity: strip harakats and keep it very short (< 150 chars)
203
+ // This fixes the Groq 400 'prompt length' error permanently.
204
+ const cleanPrompt = previousAr ? removeHarakats(previousAr).replace(/\s+/g, ' ').trim().slice(-150) : '';
205
+
206
+ form.append('file', audioBuffer, { filename, contentType: cleanMime });
207
+ form.append('model', 'whisper-large-v3-turbo');
208
+ form.append('language', 'ar');
209
+ form.append('response_format', 'verbose_json');
210
+ form.append('temperature', '0');
211
+ form.append('prompt', cleanPrompt);
212
+
213
+ const response = await axios.post(
214
+ 'https://api.groq.com/openai/v1/audio/transcriptions',
215
+ form,
216
+ { headers: { 'Authorization': `Bearer ${GROQ_API_KEY}`, ...form.getHeaders() } }
217
+ );
218
+
219
+ const result = response.data;
220
+
221
+ // S-Tier Audio Gate: Skip silent segments identified by model confidence
222
+ const noSpeechProb = result.segments?.[0]?.no_speech_prob ?? result.no_speech_prob ?? 0;
223
+ if (noSpeechProb > 0.6) {
224
+ console.log(`[Groq Whisper] 🔇 Silence detected (no_speech_prob=${noSpeechProb.toFixed(2)})`);
225
+ return { text: '', noSpeech: true };
226
  }
227
+ return { text: result.text?.trim() || '', noSpeech: false };
228
+ }
229
+
230
+ // DeepSeek S-Tier Literary Translation (Minhaj Salafi Expert)
231
+ async function deepseekTranslate(arabicText, historyAr, historyFr) {
232
+ const axios = require('axios');
233
+ const systemPrompt = `Tu es un Traducteur Islamique Senior (Minhaj Salafi). Précision théologique absolue requise. Tu traduis une Khoutba en direct.
234
+
235
+ ## RÈGLES CRITIQUES ANTI-HALLUCINATION
236
+ 1. INTERDICTION D'INVENTER : Ne jamais inventer de noms propres, de sujets ou de personnages (ex: "Nâjih") pour combler des silences ou lier des phrases.
237
+ 2. PRONOMS IMPLICITES : Si l'arabe utilise un verbe sans sujet explicite (ex: يتذكر), traduis fidèlement par le pronom correspondant ("qu'il se rappelle") ou une tournure impersonnelle. Ne nomme jamais le sujet s'il n'est pas prononcé.
238
+ 3. FIDÉLITÉ DES ACTEURS : Traduis le sens profond avec un vocabulaire soutenu, mais les acteurs de l'action doivent rester strictement identiques à l'audio (NEW_AR).
239
+
240
+ ## ÉTAPE 1 — CORRECTION ARABE
241
+ - NEW_AR provient de Whisper.
242
+ - Utilise H_AR pour corriger le sens par déduction contextuelle.
243
+ - Produis arabic_refined avec harakats systématiques sur les termes importants.
244
+
245
+ ## ÉTAPE 2 — TRADUCTION FRANÇAISE
246
+ - CONTINUITÉ : La traduction doit s'enchaîner avec H_FR sans inventer de liants artificiels.
247
+ - RHÉTORIQUE : Préserve les répétitions et l'impact oratoire.
248
+ - TERMES TECHNIQUES : Garde les termes islamiques (Taqwa, Tawhid, etc.) et explique brièvement si première occurrence.
249
+
250
+ ## FORMAT DE SORTIE (JSON strict uniquement)
251
+ {
252
+ "arabic_refined": "Texte arabe corrigé et voyellé",
253
+ "translation_fr": "Traduction française fidèle",
254
+ "technical_notes": "Corrections faites ou termes expliqués (ou null)"
255
+ }`;
256
+
257
+ const hasHistory = !!(historyAr || historyFr);
258
+ const userPrompt = hasHistory
259
+ ? `## CONTEXTE DU PRÊCHE\nH_AR: ${historyAr}\nH_FR: ${historyFr}\n\n## NOUVEAU PASSAGE\nNEW_AR: ${arabicText}`
260
+ : `## DÉBUT DU PRÊCHE\nNEW_AR: ${arabicText}`;
261
+
262
+ const response = await axios.post('https://api.deepseek.com/chat/completions', {
263
+ model: 'deepseek-chat',
264
+ messages: [
265
+ { role: 'system', content: systemPrompt },
266
+ { role: 'user', content: userPrompt }
267
+ ],
268
+ response_format: { type: 'json_object' },
269
+ temperature: 0.2,
270
+ max_tokens: 900
271
+ }, {
272
+ headers: {
273
+ 'Authorization': `Bearer ${process.env.DEEPSEEK_API_KEY}`,
274
+ 'Content-Type': 'application/json'
275
+ }
276
+ });
277
+
278
+ const content = response.data.choices[0].message.content;
279
+ return JSON.parse(content);
280
+ }
281
+
282
 
283
  fastify.post('/khotba/translate', async (request, reply) => {
284
+ console.log('[Khotba] Received translation request (Groq Whisper + DeepSeek)');
285
  try {
286
  const { audioData, mimeType, historyAr = '', historyFr = '' } = request.body;
287
 
 
290
  return reply.status(400).send({ error: 'Missing audioData' });
291
  }
292
 
293
+ // Truncate history to keep costs low
294
  const truncate = (str, limit) => {
295
  const words = str.split(/\s+/);
296
  if (words.length <= limit) return str;
297
  return '...' + words.slice(-limit).join(' ');
298
  };
 
299
  const trunkAr = truncate(historyAr, 80);
300
  const trunkFr = truncate(historyFr, 80);
301
 
302
+ // Decode base64 audio Buffer for Groq multipart upload
303
+ let audioBuffer;
304
+ if (Array.isArray(audioData)) {
305
+ const buffers = audioData.map(data => Buffer.from(data, 'base64'));
306
+ audioBuffer = Buffer.concat(buffers);
307
+ console.log(`[Khotba] Grouped ${audioData.length} chunks. Combined size: ${Math.round(audioBuffer.length / 1024)}KB`);
308
+ } else {
309
+ audioBuffer = Buffer.from(audioData, 'base64');
310
+ console.log(`[Khotba] Audio size: ${Math.round(audioBuffer.length / 1024)}KB | History: ${trunkAr.length + trunkFr.length} chars`);
311
+ }
312
+
313
+ // 🛡️ Gate 1: Minimum audio size (< 8KB = silence/init-header only, skip Groq entirely)
314
+ const MIN_AUDIO_BYTES = 8 * 1024;
315
+ if (audioBuffer.length < MIN_AUDIO_BYTES) {
316
+ console.log(`[Khotba] ⚡ Skip — audio too small (${Math.round(audioBuffer.length / 1024)}KB < 8KB), likely silence`);
317
+ return { response: { ar: '', fr: '', notes: null } };
318
+ }
319
+
320
+ // Step 1: Groq Whisper Large V3 Turbo → Arabic transcription
321
+ // Pass last sentence of historyAr as context prompt for cross-segment continuity
322
+ const lastSentenceAr = trunkAr.split(/[.!?؟،]/).filter(Boolean).pop()?.trim() || '';
323
+ console.log('[Groq Whisper] Transcribing with context prompt...');
324
+ const whisperResult = await groqTranscribe(audioBuffer, mimeType || 'audio/webm', lastSentenceAr);
325
+
326
+ // 🛡️ Gate 2: Whisper no_speech gate (model's own confidence)
327
+ if (whisperResult.noSpeech || !whisperResult.text) {
328
+ console.warn('[Khotba] 🔇 Whisper confirmed no speech — skipping DeepSeek');
329
+ return { response: { ar: '', fr: '', notes: null } };
330
  }
331
 
332
+ const arabicText = whisperResult.text;
333
+ console.log(`[Groq Whisper] Transcription: "${arabicText.substring(0, 80)}"`);
334
+
335
+ // 🛡️ Gate 3: Hallucination blocklist — Whisper known filler outputs on near-silence
336
+ if (isWhisperHallucination(arabicText)) {
337
+ console.warn(`[Khotba] 🔇 Hallucination filtered: "${arabicText}"`);
338
+ return { response: { ar: '', fr: '', notes: null } };
339
+ }
340
+
341
+ // Step 2: DeepSeek Chat → Semantic correction + French translation
342
+ console.log('[DeepSeek] Translating & Correcting (S-Tier Minhaj)...');
343
+ const translated = await deepseekTranslate(arabicText, trunkAr, trunkFr);
344
+
345
+ if (translated.technical_notes) {
346
+ console.log('[DeepSeek Note]:', translated.technical_notes);
347
  }
348
+
349
+ const finalResult = {
350
+ ar: translated.arabic_refined || arabicText,
351
+ fr: translated.translation_fr || '',
352
+ notes: translated.technical_notes || null
353
+ };
354
+
355
+ console.log('[Khotba] ✅ Done — ar:', finalResult.ar.substring(0, 40), '| fr:', finalResult.fr.substring(0, 40));
356
+ return { response: finalResult };
357
+
358
  } catch (error) {
359
+ const errMsg = error.response?.data?.error?.message || error.message || 'Unknown error';
360
+ const errStatus = error.response?.status || 500;
361
+ console.error('[Khotba] Error caught:', errMsg, '| HTTP status:', errStatus);
362
 
363
+ if (errStatus === 429 || errMsg.toLowerCase().includes('rate limit')) {
 
 
364
  return reply.status(429).send({
365
+ error: 'Rate limit reached. Please wait a moment.',
366
+ retryAfter: '10s'
367
  });
368
  }
369
 
370
+ return reply.status(500).send({ error: errMsg, stack: error.stack });
 
 
 
371
  }
372
  });
373
 
 
379
  const buffer = await response.arrayBuffer();
380
  reply.header('Content-Type', 'audio/wav');
381
  reply.send(Buffer.from(buffer));
382
+ } catch (err) {
383
+ reply.status(500).send({ error: 'TTS failed' });
384
+ }
385
  });
386
 
387
  // Server Start
package-lock.json CHANGED
@@ -11,11 +11,11 @@
11
  "dependencies": {
12
  "@fastify/cors": "^11.2.0",
13
  "@fastify/rate-limit": "^10.3.0",
14
- "@google/generative-ai": "^0.24.1",
15
  "axios": "^1.13.5",
16
  "dotenv": "^17.3.1",
17
  "duckdb": "^1.4.4",
18
  "fastify": "^5.7.4",
 
19
  "translate-google": "^1.5.0"
20
  }
21
  },
@@ -177,15 +177,6 @@
177
  "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==",
178
  "license": "MIT"
179
  },
180
- "node_modules/@google/generative-ai": {
181
- "version": "0.24.1",
182
- "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.24.1.tgz",
183
- "integrity": "sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q==",
184
- "license": "Apache-2.0",
185
- "engines": {
186
- "node": ">=18.0.0"
187
- }
188
- },
189
  "node_modules/@isaacs/fs-minipass": {
190
  "version": "4.0.1",
191
  "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
 
11
  "dependencies": {
12
  "@fastify/cors": "^11.2.0",
13
  "@fastify/rate-limit": "^10.3.0",
 
14
  "axios": "^1.13.5",
15
  "dotenv": "^17.3.1",
16
  "duckdb": "^1.4.4",
17
  "fastify": "^5.7.4",
18
+ "form-data": "^4.0.5",
19
  "translate-google": "^1.5.0"
20
  }
21
  },
 
177
  "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==",
178
  "license": "MIT"
179
  },
 
 
 
 
 
 
 
 
 
180
  "node_modules/@isaacs/fs-minipass": {
181
  "version": "4.0.1",
182
  "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
package.json CHANGED
@@ -15,11 +15,11 @@
15
  "dependencies": {
16
  "@fastify/cors": "^11.2.0",
17
  "@fastify/rate-limit": "^10.3.0",
18
- "@google/generative-ai": "^0.24.1",
19
  "axios": "^1.13.5",
20
  "dotenv": "^17.3.1",
21
  "duckdb": "^1.4.4",
22
  "fastify": "^5.7.4",
 
23
  "translate-google": "^1.5.0"
24
  }
25
  }
 
15
  "dependencies": {
16
  "@fastify/cors": "^11.2.0",
17
  "@fastify/rate-limit": "^10.3.0",
 
18
  "axios": "^1.13.5",
19
  "dotenv": "^17.3.1",
20
  "duckdb": "^1.4.4",
21
  "fastify": "^5.7.4",
22
+ "form-data": "^4.0.5",
23
  "translate-google": "^1.5.0"
24
  }
25
  }
start_all.sh CHANGED
@@ -1,5 +1,4 @@
1
  #!/bin/bash
2
- <<<<<<< HEAD
3
  # ─────────────────────────────────────────────────────────────────────────────
4
  # ArabApp Dev Server Launcher
5
  # Starts all required backend services:
@@ -16,46 +15,36 @@ echo ""
16
  # ── Service 1: Quran-MD Database (port 3002) ──────────────────────────────
17
  echo "▶ Starting Quran-MD audio database (port 3002)..."
18
  if lsof -ti:3002 > /dev/null 2>&1; then
19
- echo " Already running on port 3002"
20
- else
21
- cd "$SCRIPT_DIR"
22
- node index.js &
23
- NODE_PID=$!
24
- echo " ✓ Quran-MD server started (PID: $NODE_PID)"
25
  fi
26
 
 
 
 
 
 
27
  # ── Service 2: Arabic AI TTS (port 3003) ─────────────────────────────────
28
  echo "▶ Starting Arabic AI TTS (port 3003)..."
29
  if lsof -ti:3003 > /dev/null 2>&1; then
30
- echo " Already running on port 3003"
 
 
 
 
 
 
 
 
31
  else
32
- VENV_PYTHON="$SCRIPT_DIR/tts_venv/bin/python3"
33
- if [ ! -f "$VENV_PYTHON" ]; then
34
- echo " ⚠️ TTS venv not found. TTS will fallback to browser synthesis."
35
- echo " To install: cd server && python3 -m venv tts_venv"
36
- echo " Then: tts_venv/bin/pip install git+https://github.com/nipponjo/tts_arabic.git flask"
37
- else
38
- "$VENV_PYTHON" "$SCRIPT_DIR/tts_server.py" &
39
- TTS_PID=$!
40
- echo " ✓ AI TTS server started (PID: $TTS_PID)"
41
- echo " ⏳ Warming up TTS models (~10s)..."
42
- fi
43
  fi
 
44
 
45
  echo ""
46
  echo "════════════════════════════"
47
- echo "Services ready:"
48
- echo " 🎵 Quran Audio: http://localhost:3002/audio/<word>"
49
- echo " 🤖 AI TTS: http://localhost:3003/tts?text=<arabic>"
50
- echo " ❤️ Health: http://localhost:3003/health"
51
- echo ""
52
  echo "Press Ctrl+C to stop"
53
 
54
- # Keep script alive (optional: remove if running via npm scripts)
55
  wait
56
- =======
57
- # Start Python TTS Sidecar
58
- python3 tts_server.py &
59
- # Start Node Server
60
- node index.js
61
- >>>>>>> efdfee7 (feat: S-Tier Smart Scroll & Anti-Duplicate Surgical Cleanup v5.4)
 
1
  #!/bin/bash
 
2
  # ─────────────────────────────────────────────────────────────────────────────
3
  # ArabApp Dev Server Launcher
4
  # Starts all required backend services:
 
15
  # ── Service 1: Quran-MD Database (port 3002) ──────────────────────────────
16
  echo "▶ Starting Quran-MD audio database (port 3002)..."
17
  if lsof -ti:3002 > /dev/null 2>&1; then
18
+ echo " ! Port 3002 busy. Killing old instance..."
19
+ lsof -ti:3002 | xargs kill -9
 
 
 
 
20
  fi
21
 
22
+ cd "$SCRIPT_DIR"
23
+ node index.js &
24
+ NODE_PID=$!
25
+ echo " ✓ Quran-MD server started (PID: $NODE_PID)"
26
+
27
  # ── Service 2: Arabic AI TTS (port 3003) ─────────────────────────────────
28
  echo "▶ Starting Arabic AI TTS (port 3003)..."
29
  if lsof -ti:3003 > /dev/null 2>&1; then
30
+ echo " ! Port 3003 busy. Killing old instance..."
31
+ lsof -ti:3003 | xargs kill -9
32
+ fi
33
+
34
+ VENV_PYTHON="$SCRIPT_DIR/tts_venv/bin/python3"
35
+ if [ ! -f "$VENV_PYTHON" ]; then
36
+ echo " ⚠️ TTS venv not found. Running with system python if available..."
37
+ python3 "$SCRIPT_DIR/tts_server.py" &
38
+ TTS_PID=$!
39
  else
40
+ "$VENV_PYTHON" "$SCRIPT_DIR/tts_server.py" &
41
+ TTS_PID=$!
 
 
 
 
 
 
 
 
 
42
  fi
43
+ echo " ✓ AI TTS server started (PID: $TTS_PID)"
44
 
45
  echo ""
46
  echo "════════════════════════════"
47
+ echo "Services ready on ports 3002 and 3003"
 
 
 
 
48
  echo "Press Ctrl+C to stop"
49
 
 
50
  wait
 
 
 
 
 
 
venv/bin/Activate.ps1 ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <#
2
+ .Synopsis
3
+ Activate a Python virtual environment for the current PowerShell session.
4
+
5
+ .Description
6
+ Pushes the python executable for a virtual environment to the front of the
7
+ $Env:PATH environment variable and sets the prompt to signify that you are
8
+ in a Python virtual environment. Makes use of the command line switches as
9
+ well as the `pyvenv.cfg` file values present in the virtual environment.
10
+
11
+ .Parameter VenvDir
12
+ Path to the directory that contains the virtual environment to activate. The
13
+ default value for this is the parent of the directory that the Activate.ps1
14
+ script is located within.
15
+
16
+ .Parameter Prompt
17
+ The prompt prefix to display when this virtual environment is activated. By
18
+ default, this prompt is the name of the virtual environment folder (VenvDir)
19
+ surrounded by parentheses and followed by a single space (ie. '(.venv) ').
20
+
21
+ .Example
22
+ Activate.ps1
23
+ Activates the Python virtual environment that contains the Activate.ps1 script.
24
+
25
+ .Example
26
+ Activate.ps1 -Verbose
27
+ Activates the Python virtual environment that contains the Activate.ps1 script,
28
+ and shows extra information about the activation as it executes.
29
+
30
+ .Example
31
+ Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
32
+ Activates the Python virtual environment located in the specified location.
33
+
34
+ .Example
35
+ Activate.ps1 -Prompt "MyPython"
36
+ Activates the Python virtual environment that contains the Activate.ps1 script,
37
+ and prefixes the current prompt with the specified string (surrounded in
38
+ parentheses) while the virtual environment is active.
39
+
40
+ .Notes
41
+ On Windows, it may be required to enable this Activate.ps1 script by setting the
42
+ execution policy for the user. You can do this by issuing the following PowerShell
43
+ command:
44
+
45
+ PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
46
+
47
+ For more information on Execution Policies:
48
+ https://go.microsoft.com/fwlink/?LinkID=135170
49
+
50
+ #>
51
+ Param(
52
+ [Parameter(Mandatory = $false)]
53
+ [String]
54
+ $VenvDir,
55
+ [Parameter(Mandatory = $false)]
56
+ [String]
57
+ $Prompt
58
+ )
59
+
60
+ <# Function declarations --------------------------------------------------- #>
61
+
62
+ <#
63
+ .Synopsis
64
+ Remove all shell session elements added by the Activate script, including the
65
+ addition of the virtual environment's Python executable from the beginning of
66
+ the PATH variable.
67
+
68
+ .Parameter NonDestructive
69
+ If present, do not remove this function from the global namespace for the
70
+ session.
71
+
72
+ #>
73
+ function global:deactivate ([switch]$NonDestructive) {
74
+ # Revert to original values
75
+
76
+ # The prior prompt:
77
+ if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
78
+ Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
79
+ Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
80
+ }
81
+
82
+ # The prior PYTHONHOME:
83
+ if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
84
+ Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
85
+ Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
86
+ }
87
+
88
+ # The prior PATH:
89
+ if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
90
+ Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
91
+ Remove-Item -Path Env:_OLD_VIRTUAL_PATH
92
+ }
93
+
94
+ # Just remove the VIRTUAL_ENV altogether:
95
+ if (Test-Path -Path Env:VIRTUAL_ENV) {
96
+ Remove-Item -Path env:VIRTUAL_ENV
97
+ }
98
+
99
+ # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
100
+ if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
101
+ Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
102
+ }
103
+
104
+ # Leave deactivate function in the global namespace if requested:
105
+ if (-not $NonDestructive) {
106
+ Remove-Item -Path function:deactivate
107
+ }
108
+ }
109
+
110
+ <#
111
+ .Description
112
+ Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
113
+ given folder, and returns them in a map.
114
+
115
+ For each line in the pyvenv.cfg file, if that line can be parsed into exactly
116
+ two strings separated by `=` (with any amount of whitespace surrounding the =)
117
+ then it is considered a `key = value` line. The left hand string is the key,
118
+ the right hand is the value.
119
+
120
+ If the value starts with a `'` or a `"` then the first and last character is
121
+ stripped from the value before being captured.
122
+
123
+ .Parameter ConfigDir
124
+ Path to the directory that contains the `pyvenv.cfg` file.
125
+ #>
126
+ function Get-PyVenvConfig(
127
+ [String]
128
+ $ConfigDir
129
+ ) {
130
+ Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
131
+
132
+ # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
133
+ $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
134
+
135
+ # An empty map will be returned if no config file is found.
136
+ $pyvenvConfig = @{ }
137
+
138
+ if ($pyvenvConfigPath) {
139
+
140
+ Write-Verbose "File exists, parse `key = value` lines"
141
+ $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
142
+
143
+ $pyvenvConfigContent | ForEach-Object {
144
+ $keyval = $PSItem -split "\s*=\s*", 2
145
+ if ($keyval[0] -and $keyval[1]) {
146
+ $val = $keyval[1]
147
+
148
+ # Remove extraneous quotations around a string value.
149
+ if ("'""".Contains($val.Substring(0, 1))) {
150
+ $val = $val.Substring(1, $val.Length - 2)
151
+ }
152
+
153
+ $pyvenvConfig[$keyval[0]] = $val
154
+ Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
155
+ }
156
+ }
157
+ }
158
+ return $pyvenvConfig
159
+ }
160
+
161
+
162
+ <# Begin Activate script --------------------------------------------------- #>
163
+
164
+ # Determine the containing directory of this script
165
+ $VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
166
+ $VenvExecDir = Get-Item -Path $VenvExecPath
167
+
168
+ Write-Verbose "Activation script is located in path: '$VenvExecPath'"
169
+ Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
170
+ Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
171
+
172
+ # Set values required in priority: CmdLine, ConfigFile, Default
173
+ # First, get the location of the virtual environment, it might not be
174
+ # VenvExecDir if specified on the command line.
175
+ if ($VenvDir) {
176
+ Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
177
+ }
178
+ else {
179
+ Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
180
+ $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
181
+ Write-Verbose "VenvDir=$VenvDir"
182
+ }
183
+
184
+ # Next, read the `pyvenv.cfg` file to determine any required value such
185
+ # as `prompt`.
186
+ $pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
187
+
188
+ # Next, set the prompt from the command line, or the config file, or
189
+ # just use the name of the virtual environment folder.
190
+ if ($Prompt) {
191
+ Write-Verbose "Prompt specified as argument, using '$Prompt'"
192
+ }
193
+ else {
194
+ Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
195
+ if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
196
+ Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
197
+ $Prompt = $pyvenvCfg['prompt'];
198
+ }
199
+ else {
200
+ Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virutal environment)"
201
+ Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
202
+ $Prompt = Split-Path -Path $venvDir -Leaf
203
+ }
204
+ }
205
+
206
+ Write-Verbose "Prompt = '$Prompt'"
207
+ Write-Verbose "VenvDir='$VenvDir'"
208
+
209
+ # Deactivate any currently active virtual environment, but leave the
210
+ # deactivate function in place.
211
+ deactivate -nondestructive
212
+
213
+ # Now set the environment variable VIRTUAL_ENV, used by many tools to determine
214
+ # that there is an activated venv.
215
+ $env:VIRTUAL_ENV = $VenvDir
216
+
217
+ if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
218
+
219
+ Write-Verbose "Setting prompt to '$Prompt'"
220
+
221
+ # Set the prompt to include the env name
222
+ # Make sure _OLD_VIRTUAL_PROMPT is global
223
+ function global:_OLD_VIRTUAL_PROMPT { "" }
224
+ Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
225
+ New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
226
+
227
+ function global:prompt {
228
+ Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
229
+ _OLD_VIRTUAL_PROMPT
230
+ }
231
+ }
232
+
233
+ # Clear PYTHONHOME
234
+ if (Test-Path -Path Env:PYTHONHOME) {
235
+ Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
236
+ Remove-Item -Path Env:PYTHONHOME
237
+ }
238
+
239
+ # Add the venv to the PATH
240
+ Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
241
+ $Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
venv/bin/activate ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file must be used with "source bin/activate" *from bash*
2
+ # you cannot run it directly
3
+
4
+ deactivate () {
5
+ # reset old environment variables
6
+ if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
7
+ PATH="${_OLD_VIRTUAL_PATH:-}"
8
+ export PATH
9
+ unset _OLD_VIRTUAL_PATH
10
+ fi
11
+ if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
12
+ PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
13
+ export PYTHONHOME
14
+ unset _OLD_VIRTUAL_PYTHONHOME
15
+ fi
16
+
17
+ # This should detect bash and zsh, which have a hash command that must
18
+ # be called to get it to forget past commands. Without forgetting
19
+ # past commands the $PATH changes we made may not be respected
20
+ if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
21
+ hash -r 2> /dev/null
22
+ fi
23
+
24
+ if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
25
+ PS1="${_OLD_VIRTUAL_PS1:-}"
26
+ export PS1
27
+ unset _OLD_VIRTUAL_PS1
28
+ fi
29
+
30
+ unset VIRTUAL_ENV
31
+ if [ ! "${1:-}" = "nondestructive" ] ; then
32
+ # Self destruct!
33
+ unset -f deactivate
34
+ fi
35
+ }
36
+
37
+ # unset irrelevant variables
38
+ deactivate nondestructive
39
+
40
+ VIRTUAL_ENV="/Users/user/Project/arabApp/server/venv"
41
+ export VIRTUAL_ENV
42
+
43
+ _OLD_VIRTUAL_PATH="$PATH"
44
+ PATH="$VIRTUAL_ENV/bin:$PATH"
45
+ export PATH
46
+
47
+ # unset PYTHONHOME if set
48
+ # this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
49
+ # could use `if (set -u; : $PYTHONHOME) ;` in bash
50
+ if [ -n "${PYTHONHOME:-}" ] ; then
51
+ _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
52
+ unset PYTHONHOME
53
+ fi
54
+
55
+ if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
56
+ _OLD_VIRTUAL_PS1="${PS1:-}"
57
+ PS1="(venv) ${PS1:-}"
58
+ export PS1
59
+ fi
60
+
61
+ # This should detect bash and zsh, which have a hash command that must
62
+ # be called to get it to forget past commands. Without forgetting
63
+ # past commands the $PATH changes we made may not be respected
64
+ if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
65
+ hash -r 2> /dev/null
66
+ fi
venv/bin/activate.csh ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file must be used with "source bin/activate.csh" *from csh*.
2
+ # You cannot run it directly.
3
+ # Created by Davide Di Blasi <davidedb@gmail.com>.
4
+ # Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
5
+
6
+ alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate'
7
+
8
+ # Unset irrelevant variables.
9
+ deactivate nondestructive
10
+
11
+ setenv VIRTUAL_ENV "/Users/user/Project/arabApp/server/venv"
12
+
13
+ set _OLD_VIRTUAL_PATH="$PATH"
14
+ setenv PATH "$VIRTUAL_ENV/bin:$PATH"
15
+
16
+
17
+ set _OLD_VIRTUAL_PROMPT="$prompt"
18
+
19
+ if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
20
+ set prompt = "(venv) $prompt"
21
+ endif
22
+
23
+ alias pydoc python -m pydoc
24
+
25
+ rehash
venv/bin/activate.fish ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file must be used with "source <venv>/bin/activate.fish" *from fish*
2
+ # (https://fishshell.com/); you cannot run it directly.
3
+
4
+ function deactivate -d "Exit virtual environment and return to normal shell environment"
5
+ # reset old environment variables
6
+ if test -n "$_OLD_VIRTUAL_PATH"
7
+ set -gx PATH $_OLD_VIRTUAL_PATH
8
+ set -e _OLD_VIRTUAL_PATH
9
+ end
10
+ if test -n "$_OLD_VIRTUAL_PYTHONHOME"
11
+ set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
12
+ set -e _OLD_VIRTUAL_PYTHONHOME
13
+ end
14
+
15
+ if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
16
+ functions -e fish_prompt
17
+ set -e _OLD_FISH_PROMPT_OVERRIDE
18
+ functions -c _old_fish_prompt fish_prompt
19
+ functions -e _old_fish_prompt
20
+ end
21
+
22
+ set -e VIRTUAL_ENV
23
+ if test "$argv[1]" != "nondestructive"
24
+ # Self-destruct!
25
+ functions -e deactivate
26
+ end
27
+ end
28
+
29
+ # Unset irrelevant variables.
30
+ deactivate nondestructive
31
+
32
+ set -gx VIRTUAL_ENV "/Users/user/Project/arabApp/server/venv"
33
+
34
+ set -gx _OLD_VIRTUAL_PATH $PATH
35
+ set -gx PATH "$VIRTUAL_ENV/bin" $PATH
36
+
37
+ # Unset PYTHONHOME if set.
38
+ if set -q PYTHONHOME
39
+ set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
40
+ set -e PYTHONHOME
41
+ end
42
+
43
+ if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
44
+ # fish uses a function instead of an env var to generate the prompt.
45
+
46
+ # Save the current fish_prompt function as the function _old_fish_prompt.
47
+ functions -c fish_prompt _old_fish_prompt
48
+
49
+ # With the original prompt function renamed, we can override with our own.
50
+ function fish_prompt
51
+ # Save the return status of the last command.
52
+ set -l old_status $status
53
+
54
+ # Output the venv prompt; color taken from the blue of the Python logo.
55
+ printf "%s%s%s" (set_color 4B8BBE) "(venv) " (set_color normal)
56
+
57
+ # Restore the return status of the previous command.
58
+ echo "exit $old_status" | .
59
+ # Output the original/"old" prompt.
60
+ _old_fish_prompt
61
+ end
62
+
63
+ set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
64
+ end
venv/bin/coloredlogs ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from coloredlogs.cli import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
venv/bin/convert-caffe2-to-onnx ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from caffe2.python.onnx.bin.conversion import caffe2_to_onnx
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(caffe2_to_onnx())
venv/bin/convert-onnx-to-caffe2 ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from caffe2.python.onnx.bin.conversion import onnx_to_caffe2
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(onnx_to_caffe2())
venv/bin/docutils ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from docutils.__main__ import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
venv/bin/f2py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from numpy.f2py.f2py2e import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
venv/bin/fastapi ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from fastapi.cli import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
venv/bin/flask ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from flask.cli import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
venv/bin/funasr ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from funasr.bin.inference import main_hydra
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main_hydra())
venv/bin/funasr-export ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from funasr.bin.export import main_hydra
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main_hydra())
venv/bin/funasr-jsonl2scp ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from funasr.datasets.audio_datasets.jsonl2scp import main_hydra
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main_hydra())
venv/bin/funasr-scp2jsonl ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from funasr.datasets.audio_datasets.scp2jsonl import main_hydra
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main_hydra())
venv/bin/funasr-sensevoice2jsonl ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from funasr.datasets.audio_datasets.sensevoice2jsonl import main_hydra
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main_hydra())
venv/bin/funasr-train ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from funasr.bin.train import main_hydra
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main_hydra())
venv/bin/funasr-train-ds ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from funasr.bin.train_ds import main_hydra
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main_hydra())
venv/bin/gdown ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from gdown.__main__ import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
venv/bin/humanfriendly ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from humanfriendly.cli import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
venv/bin/isympy ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from isympy import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
venv/bin/jp.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+
3
+ import sys
4
+ import json
5
+ import argparse
6
+ from pprint import pformat
7
+
8
+ import jmespath
9
+ from jmespath import exceptions
10
+
11
+
12
+ def main():
13
+ parser = argparse.ArgumentParser()
14
+ parser.add_argument('expression')
15
+ parser.add_argument('-f', '--filename',
16
+ help=('The filename containing the input data. '
17
+ 'If a filename is not given then data is '
18
+ 'read from stdin.'))
19
+ parser.add_argument('--ast', action='store_true',
20
+ help=('Pretty print the AST, do not search the data.'))
21
+ args = parser.parse_args()
22
+ expression = args.expression
23
+ if args.ast:
24
+ # Only print the AST
25
+ expression = jmespath.compile(args.expression)
26
+ sys.stdout.write(pformat(expression.parsed))
27
+ sys.stdout.write('\n')
28
+ return 0
29
+ if args.filename:
30
+ with open(args.filename, 'r') as f:
31
+ data = json.load(f)
32
+ else:
33
+ data = sys.stdin.read()
34
+ data = json.loads(data)
35
+ try:
36
+ sys.stdout.write(json.dumps(
37
+ jmespath.search(expression, data), indent=4, ensure_ascii=False))
38
+ sys.stdout.write('\n')
39
+ except exceptions.ArityError as e:
40
+ sys.stderr.write("invalid-arity: %s\n" % e)
41
+ return 1
42
+ except exceptions.JMESPathTypeError as e:
43
+ sys.stderr.write("invalid-type: %s\n" % e)
44
+ return 1
45
+ except exceptions.UnknownFunctionError as e:
46
+ sys.stderr.write("unknown-function: %s\n" % e)
47
+ return 1
48
+ except exceptions.ParseError as e:
49
+ sys.stderr.write("syntax-error: %s\n" % e)
50
+ return 1
51
+
52
+
53
+ if __name__ == '__main__':
54
+ sys.exit(main())
venv/bin/jsonl2scp ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from funasr.datasets.audio_datasets.jsonl2scp import main_hydra
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main_hydra())
venv/bin/keyring ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from keyring.cli import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
venv/bin/markdown-it ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from markdown_it.cli.parse import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
venv/bin/modelscope ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from modelscope.cli.cli import run_cmd
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(run_cmd())
venv/bin/normalizer ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from charset_normalizer.cli import cli_detect
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(cli_detect())
venv/bin/numba ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ # -*- coding: UTF-8 -*-
3
+ from __future__ import print_function, division, absolute_import
4
+
5
+ from numba.misc.numba_entry import main
6
+
7
+ if __name__ == "__main__":
8
+ main()
venv/bin/onnxruntime_test ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from onnxruntime.tools.onnxruntime_test import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
venv/bin/pip ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ # -*- coding: utf-8 -*-
3
+ import re
4
+ import sys
5
+ from pip._internal.cli.main import main
6
+ if __name__ == '__main__':
7
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
8
+ sys.exit(main())
venv/bin/pip3 ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ # -*- coding: utf-8 -*-
3
+ import re
4
+ import sys
5
+ from pip._internal.cli.main import main
6
+ if __name__ == '__main__':
7
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
8
+ sys.exit(main())
venv/bin/pip3.9 ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ # -*- coding: utf-8 -*-
3
+ import re
4
+ import sys
5
+ from pip._internal.cli.main import main
6
+ if __name__ == '__main__':
7
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
8
+ sys.exit(main())
venv/bin/pygmentize ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from pygments.cmdline import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
venv/bin/pygrun ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ __author__ = 'jszheng'
3
+ import optparse
4
+ import sys
5
+ import os
6
+ import importlib
7
+ from antlr4 import *
8
+
9
+
10
+ # this is a python version of TestRig
11
+ def beautify_lisp_string(in_string):
12
+ indent_size = 3
13
+ add_indent = ' '*indent_size
14
+ out_string = in_string[0] # no indent for 1st (
15
+ indent = ''
16
+ for i in range(1, len(in_string)):
17
+ if in_string[i] == '(' and in_string[i+1] != ' ':
18
+ indent += add_indent
19
+ out_string += "\n" + indent + '('
20
+ elif in_string[i] == ')':
21
+ out_string += ')'
22
+ if len(indent) > 0:
23
+ indent = indent.replace(add_indent, '', 1)
24
+ else:
25
+ out_string += in_string[i]
26
+ return out_string
27
+
28
+
29
+ if __name__ == '__main__':
30
+
31
+ #############################################################
32
+ # parse options
33
+ # not support -gui -encoding -ps
34
+ #############################################################
35
+ usage = "Usage: %prog [options] Grammar_Name Start_Rule"
36
+ parser = optparse.OptionParser(usage=usage)
37
+ # parser.add_option('-t', '--tree',
38
+ # dest="out_file",
39
+ # default="default.out",
40
+ # help='set output file name',
41
+ # )
42
+ parser.add_option('-t', '--tree',
43
+ default=False,
44
+ action='store_true',
45
+ help='Print AST tree'
46
+ )
47
+ parser.add_option('-k', '--tokens',
48
+ dest="token",
49
+ default=False,
50
+ action='store_true',
51
+ help='Show Tokens'
52
+ )
53
+ parser.add_option('-s', '--sll',
54
+ dest="sll",
55
+ default=False,
56
+ action='store_true',
57
+ help='Show SLL'
58
+ )
59
+ parser.add_option('-d', '--diagnostics',
60
+ dest="diagnostics",
61
+ default=False,
62
+ action='store_true',
63
+ help='Enable diagnostics error listener'
64
+ )
65
+ parser.add_option('-a', '--trace',
66
+ dest="trace",
67
+ default=False,
68
+ action='store_true',
69
+ help='Enable Trace'
70
+ )
71
+
72
+ options, remainder = parser.parse_args()
73
+ if len(remainder) < 2:
74
+ print('ERROR: You have to provide at least 2 arguments!')
75
+ parser.print_help()
76
+ exit(1)
77
+ else:
78
+ grammar = remainder.pop(0)
79
+ start_rule = remainder.pop(0)
80
+ file_list = remainder
81
+
82
+ #############################################################
83
+ # check and load antlr generated files
84
+ #############################################################
85
+ # dynamic load the module and class
86
+ lexerName = grammar + 'Lexer'
87
+ parserName = grammar + 'Parser'
88
+ # check if the generate file exist
89
+ lexer_file = lexerName + '.py'
90
+ parser_file = parserName + '.py'
91
+ if not os.path.exists(lexer_file):
92
+ print("[ERROR] Can't find lexer file {}!".format(lexer_file))
93
+ print(os.path.realpath('.'))
94
+ exit(1)
95
+ if not os.path.exists(parser_file):
96
+ print("[ERROR] Can't find parser file {}!".format(lexer_file))
97
+ print(os.path.realpath('.'))
98
+ exit(1)
99
+
100
+ # current directory is where the generated file loaded
101
+ # the script might be in different place.
102
+ sys.path.append('.')
103
+ # print(sys.path)
104
+
105
+ # print("Load Lexer {}".format(lexerName))
106
+ module_lexer = __import__(lexerName, globals(), locals(), lexerName)
107
+ class_lexer = getattr(module_lexer, lexerName)
108
+ # print(class_lexer)
109
+
110
+ # print("Load Parser {}".format(parserName))
111
+ module_parser = __import__(parserName, globals(), locals(), parserName)
112
+ class_parser = getattr(module_parser, parserName)
113
+ # print(class_parser)
114
+
115
+ #############################################################
116
+ # main process steps.
117
+ #############################################################
118
+ def process(input_stream, class_lexer, class_parser):
119
+ lexer = class_lexer(input_stream)
120
+ token_stream = CommonTokenStream(lexer)
121
+ token_stream.fill()
122
+ if options.token: # need to show token
123
+ for tok in token_stream.tokens:
124
+ print(tok)
125
+ if start_rule == 'tokens':
126
+ return
127
+
128
+ parser = class_parser(token_stream)
129
+
130
+ if options.diagnostics:
131
+ parser.addErrorListener(DiagnosticErrorListener())
132
+ parser._interp.predictionMode = PredictionMode.LL_EXACT_AMBIG_DETECTION
133
+ if options.tree:
134
+ parser.buildParseTrees = True
135
+ if options.sll:
136
+ parser._interp.predictionMode = PredictionMode.SLL
137
+ #parser.setTokenStream(token_stream)
138
+ parser.setTrace(options.trace)
139
+ if hasattr(parser, start_rule):
140
+ func_start_rule = getattr(parser, start_rule)
141
+ parser_ret = func_start_rule()
142
+ if options.tree:
143
+ lisp_tree_str = parser_ret.toStringTree(recog=parser)
144
+ print(beautify_lisp_string(lisp_tree_str))
145
+ else:
146
+ print("[ERROR] Can't find start rule '{}' in parser '{}'".format(start_rule, parserName))
147
+
148
+ #############################################################
149
+ # use stdin if not provide file as input stream
150
+ #############################################################
151
+ if len(file_list) == 0:
152
+ input_stream = InputStream(sys.stdin.read())
153
+ process(input_stream, class_lexer, class_parser)
154
+ exit(0)
155
+
156
+ #############################################################
157
+ # iterate all input file
158
+ #############################################################
159
+ for file_name in file_list:
160
+ if os.path.exists(file_name) and os.path.isfile(file_name):
161
+ input_stream = FileStream(file_name)
162
+ process(input_stream, class_lexer, class_parser)
163
+ else:
164
+ print("[ERROR] file {} not exist".format(os.path.normpath(file_name)))
venv/bin/python ADDED
@@ -0,0 +1 @@
 
 
1
+ python3
venv/bin/python3 ADDED
@@ -0,0 +1 @@
 
 
1
+ /Applications/Xcode.app/Contents/Developer/usr/bin/python3
venv/bin/python3.9 ADDED
@@ -0,0 +1 @@
 
 
1
+ python3
venv/bin/rst2html ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from docutils.core import rst2html
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(rst2html())
venv/bin/rst2html4 ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from docutils.core import rst2html4
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(rst2html4())
venv/bin/rst2html5 ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from docutils.core import rst2html5
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(rst2html5())
venv/bin/rst2latex ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from docutils.core import rst2latex
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(rst2latex())
venv/bin/rst2man ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from docutils.core import rst2man
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(rst2man())
venv/bin/rst2odt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from docutils.core import rst2odt
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(rst2odt())
venv/bin/rst2pseudoxml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from docutils.core import rst2pseudoxml
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(rst2pseudoxml())
venv/bin/rst2s5 ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from docutils.core import rst2s5
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(rst2s5())
venv/bin/rst2xetex ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/Users/user/Project/arabApp/server/venv/bin/python3
2
+ import sys
3
+ from docutils.core import rst2xetex
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(rst2xetex())