• שדרוג אתר הניהול - חוו"ד המשתמשים

    נעוץ נעול
    1
    36 הצבעות
    1 פוסטים
    2k צפיות
    אין תגובות
  • תקנון קטגוריית עזרה הדדית

    נעוץ נעול הועבר
    1
    16 הצבעות
    1 פוסטים
    2k צפיות
    אין תגובות
  • מספר טלפון של מקליט ההודעה

    14
    1
    0 הצבעות
    14 פוסטים
    51 צפיות
    ר
    בדקתי גם אצלי זה קורה בשרת YMX, כנראה זה באג של השרת, השאלה היא מה אפשר לעשות לסדר את זה??
  • חדש!!! VoiceMaster אתר קריינות משוכלל!!!

    117
    12 הצבעות
    117 פוסטים
    3k צפיות
    א
    הספקתי לסדר ממש קצת מהבעיות באתר שלי, בעקבות שיש כאלה שבבין הזמנים יצטרכו את זה העליתי לכאן את הקובץ. אחד מהדברים שסודרו זה העיצוב שלא עובד בכל הדרכים... תפתחו פנקס רשימות ואז תשמרו את זה על שם index.html ותפתחו אותו... בהצלחה!!!! <!DOCTYPE html> <html lang="he" dir="rtl"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>VoiceMaster AI Studio ✨</title> <script src="https://cdn.tailwindcss.com"></script> <style> @import url('https://fonts.googleapis.com/css2?family=Assistant:wght@300;400;600;700;800&display=swap'); body { font-family: 'Assistant', sans-serif; background: #f1f5f9; transition: background-color 0.3s ease, color 0.3s ease; } .dark-mode { background: #020617; color: #f8fafc; } .card { background: white; border: 1px solid #e2e8f0; transition: all 0.3s ease; } .dark-mode .card { background: #1e293b; border-color: #334155; } .loading-spinner { border: 2px solid rgba(255,255,255,0.3); border-top: 2px solid currentColor; border-radius: 50%; width: 16px; height: 16px; animation: spin 0.8s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .ai-glow { box-shadow: 0 0 25px rgba(99, 102, 241, 0.15); } .feature-badge { background: linear-gradient(90deg, #4f46e5, #9333ea); color: white; padding: 2px 8px; border-radius: 99px; font-size: 10px; font-weight: bold; } /* התאמת רכיבי שמע למכשירים ניידים */ audio { max-width: 100%; height: 36px; } </style> </head> <body class="min-h-screen py-4 sm:py-8 px-3 sm:px-6 md:px-8 transition-colors duration-500" id="bodyTag"> <div class="max-w-7xl mx-auto"> <header class="flex flex-wrap justify-between items-center gap-4 mb-6 sm:mb-8 pb-4 border-b border-slate-200 dark:border-slate-800"> <div class="flex items-center gap-3 sm:gap-4"> <div class="bg-indigo-600 p-2.5 sm:p-3 rounded-2xl text-white shadow-xl flex-shrink-0"> <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 sm:h-8 sm:w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z" /> </svg> </div> <div> <h1 class="text-2xl sm:text-3xl font-black text-slate-800 dark:text-white tracking-tight leading-none uppercase">Voice<span class="text-indigo-600">Master</span></h1> <p class="text-[10px] sm:text-xs font-bold text-slate-400 uppercase tracking-widest mt-1">AI Smart Narration Hub</p> </div> </div> <div class="flex items-center gap-2"> <div class="relative"> <button id="apiKeyBtn" class="p-2.5 sm:p-3 rounded-xl card shadow-sm hover:scale-105 transition-all flex items-center gap-2 text-xs font-bold text-slate-600 dark:text-slate-300"> <svg class="w-4 h-4 sm:w-5 sm:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"/></svg> <span id="apiKeyStatus" class="hidden sm:inline">הגדר מפתח</span> </button> <div id="apiKeyPopover" class="hidden absolute left-0 sm:left-auto right-0 sm:right-0 mt-2 w-72 sm:w-80 p-4 card rounded-2xl shadow-2xl z-50"> <h4 class="font-bold text-sm mb-2 dark:text-white">Gemini API Key</h4> <input id="apiKeyInput" type="password" class="w-full p-2.5 text-xs bg-slate-50 dark:bg-slate-800 rounded-lg border border-slate-200 dark:border-slate-700 outline-none focus:ring-2 focus:ring-indigo-500 mb-2 dark:text-white" placeholder="AIzaSy..."> <div class="flex gap-2"> <button id="saveApiKey" class="flex-1 py-2 bg-indigo-600 text-white text-[11px] font-bold rounded-lg hover:bg-indigo-700 active:scale-95 transition-all">שמור מפתח</button> <button id="clearApiKey" class="flex-1 py-2 bg-red-50 text-red-600 text-[11px] font-bold rounded-lg hover:bg-red-100 active:scale-95 transition-all">מחק</button> </div> </div> </div> <button id="toggleDark" class="p-2.5 sm:p-3 rounded-xl card shadow-sm hover:scale-105 active:scale-95 transition-all"> <svg class="h-5 w-5 sm:h-6 sm:w-6 text-slate-600 dark:text-indigo-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/> </svg> </button> </div> </header> <div class="grid grid-cols-1 lg:grid-cols-12 gap-6 lg:gap-8"> <div class="lg:col-span-5 space-y-6"> <div class="card p-4 sm:p-6 rounded-2xl sm:rounded-[2rem] shadow-lg ai-glow border-indigo-100 dark:border-indigo-900/30"> <div class="flex items-center justify-between mb-3 sm:mb-4"> <h3 class="font-bold text-sm sm:text-base text-indigo-900 dark:text-indigo-300">יצירת תוכן חכמה</h3> </div> <div class="flex gap-2 mb-3 sm:mb-4"> <input id="promptInput" type="text" class="flex-grow p-3 bg-slate-50 dark:bg-slate-800 rounded-xl text-xs sm:text-sm border-none outline-none focus:ring-2 focus:ring-indigo-500 dark:text-white" placeholder="תאר מה לכתוב (למשל: פרסומת לשירות חדש)..."> <button id="magicWriteBtn" class="p-3 bg-indigo-600 text-white rounded-xl hover:bg-indigo-700 active:scale-95 transition-all flex items-center justify-center min-w-[48px] sm:min-w-[50px]"> <span id="magicIcon">✨</span> <div id="magicLoader" class="loading-spinner hidden"></div> </button> </div> <div class="grid grid-cols-2 sm:grid-cols-3 gap-2"> <button onclick="smartEdit('הפוך את הטקסט ליותר רגוע ונעים')" class="text-[11px] font-bold py-2 px-1 bg-slate-100 dark:bg-slate-800 rounded-lg hover:bg-indigo-50 dark:hover:bg-indigo-950/40 dark:text-slate-200 active:scale-95 transition-all truncate">✨ ריכוך</button> <button onclick="smartEdit('הפוך את הטקסט ליותר שיווקי ואנרגטי')" class="text-[11px] font-bold py-2 px-1 bg-slate-100 dark:bg-slate-800 rounded-lg hover:bg-indigo-50 dark:hover:bg-indigo-950/40 dark:text-slate-200 active:scale-95 transition-all truncate">✨ שיווקי</button> <button onclick="smartEdit('קצר את הטקסט ב-30% מבלי לאבד משמעות')" class="text-[11px] font-bold py-2 px-1 bg-slate-100 dark:bg-slate-800 rounded-lg hover:bg-indigo-50 dark:hover:bg-indigo-950/40 dark:text-slate-200 active:scale-95 transition-all truncate">✨ קיצור</button> <button onclick="translateText()" class="text-[11px] font-bold py-2 px-1 bg-indigo-50 dark:bg-indigo-900/40 text-indigo-700 dark:text-indigo-300 rounded-lg hover:bg-indigo-100 active:scale-95 transition-all truncate">✨ תרגם לאנגלית</button> <button onclick="generatePodcastScript()" class="text-[11px] font-bold py-2 px-1 bg-indigo-50 dark:bg-indigo-900/40 text-indigo-700 dark:text-indigo-300 rounded-lg hover:bg-indigo-100 active:scale-95 transition-all truncate">✨ תסריט פודקאסט</button> <button onclick="summarizeText()" class="text-[11px] font-bold py-2 px-1 bg-indigo-50 dark:bg-indigo-900/40 text-indigo-700 dark:text-indigo-300 rounded-lg hover:bg-indigo-100 active:scale-95 transition-all truncate">✨ סיכום נקודות</button> </div> </div> <div class="card p-4 sm:p-7 rounded-2xl sm:rounded-[2.5rem] shadow-xl border-none"> <div class="flex flex-wrap justify-between items-center gap-2 mb-2"> <label class="text-xs sm:text-sm font-bold text-slate-500 dark:text-slate-400">טקסט לקריינות</label> <button id="analyzeToneBtn" class="text-[10px] sm:text-[11px] font-bold text-indigo-600 bg-indigo-50 dark:bg-indigo-900/30 px-2.5 py-1 rounded-full hover:bg-indigo-100 active:scale-95 transition-all flex items-center gap-1"> <span>✨ התאם סגנון אוטומטית</span> <div id="toneLoader" class="loading-spinner hidden !w-3 !h-3"></div> </button> </div> <textarea id="textInput" rows="5" class="w-full p-3 sm:p-4 bg-slate-50 dark:bg-slate-800/50 rounded-xl sm:rounded-2xl outline-none border-2 border-transparent focus:border-indigo-500 transition-all text-base sm:text-lg dark:text-white resize-y" placeholder="הטקסט יופיע כאן..."></textarea> <div class="flex gap-2 mt-3 mb-5"> <button id="vocalizeBtn" class="flex-1 py-2.5 sm:py-3 bg-slate-50 dark:bg-slate-800 text-slate-600 dark:text-slate-300 rounded-xl text-xs font-bold hover:bg-indigo-50 dark:hover:bg-indigo-900/20 active:scale-95 transition-all flex items-center justify-center gap-2 border border-slate-100 dark:border-slate-700"> <span>✨ ניקוד אוטומטי</span> <div id="vocalizeLoader" class="loading-spinner hidden"></div> </button> </div> <div class="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4 mb-6"> <div> <label class="text-[10px] font-bold text-slate-400 uppercase mr-1 block mb-1">קול</label> <select id="voiceSelect" class="w-full p-3 bg-slate-100 dark:bg-slate-800 dark:text-white rounded-xl text-xs sm:text-sm font-bold border-none outline-none cursor-pointer"> <optgroup label="גברים (Male)"> <option value="Zephyr">Zephyr (קליל)</option> <option value="Fenrir">Fenrir (עמוק)</option> <option value="Puck">Puck (שובב)</option> <option value="Charon">Charon (סמכותי)</option> <option value="Orus">Orus (קלאסי)</option> <option value="Enceladus">Enceladus (צעיר)</option> <option value="Iapetus">Iapetus (חם)</option> <option value="Algieba">Algieba (נמרץ)</option> <option value="Rasalgethi">Rasalgethi (בוגר)</option> <option value="Achernar">Achernar (יציב)</option> <option value="Alnilam">Alnilam (בהיר)</option> <option value="Schedar">Schedar (נעים)</option> <option value="Achird">Achird (שידור)</option> <option value="Zubenelgenubi">Zubenelgenubi (עמוק מאוד)</option> <option value="Sadachbia">Sadachbia (רך)</option> <option value="Sadaltager">Sadaltager (טבעי)</option> <option value="Sulafat">Sulafat (עוצמתי)</option> </optgroup> <optgroup label="נשים (Female)"> <option value="Kore" selected>Kore (מקצועי)</option> <option value="Leda">Leda (עדין)</option> <option value="Aoede">Aoede (רך)</option> <option value="Callirrhoe">Callirrhoe (רהוט)</option> <option value="Autonoe">Autonoe (אנרגטי)</option> <option value="Umbriel">Umbriel (מסתורי)</option> <option value="Despina">Despina (מהיר)</option> <option value="Erinome">Erinome (ידידותי)</option> <option value="Algenib">Algenib (חדשותי)</option> <option value="Laomedeia">Laomedeia (מרגיע)</option> <option value="Gacrux">Gacrux (אלגנטי)</option> <option value="Pulcherrima">Pulcherrima (עשיר)</option> <option value="Vindemiatrix">Vindemiatrix (סבלני)</option> </optgroup> </select> </div> <div> <label class="text-[10px] font-bold text-slate-400 uppercase mr-1 block mb-1">סגנון והגשה</label> <select id="styleSelect" class="w-full p-3 bg-slate-100 dark:bg-slate-800 dark:text-white rounded-xl text-xs sm:text-sm font-bold border-none outline-none cursor-pointer"> <option value="natural">טבעי (Natural)</option> <option value="cheerful">שמח (Cheerful)</option> <option value="serious">רציני (Serious)</option> <option value="whisper">לחישה (Whisper)</option> <option value="excited">נרגש (Excited)</option> <option value="calm">רגוע (Calm)</option> <option value="authoritative">סמכותי (Authoritative)</option> <option value="friendly">ידידותי (Friendly)</option> <option value="professional">מקצועי (Professional)</option> <option value="storytelling">מספר סיפורים (Storytelling)</option> <option value="shouting">צעקה (Shouting)</option> <option value="terrified">מבועת (Terrified)</option> <option value="sad">עצוב (Sad)</option> <option value="angry">כועס (Angry)</option> <option value="hopeful">מלא תקווה (Hopeful)</option> </select> </div> </div> <button id="generateBtn" class="w-full bg-indigo-600 hover:bg-indigo-700 active:scale-[0.98] text-white font-bold py-3.5 sm:py-4 rounded-xl sm:rounded-2xl shadow-lg transition-all flex items-center justify-center gap-3"> <span id="btnText" class="text-sm sm:text-base">הפק קריינות עכשיו</span> <div id="btnLoader" class="loading-spinner hidden !border-t-white"></div> </button> </div> </div> <div class="lg:col-span-7"> <div class="flex justify-between items-center mb-4"> <h2 class="text-lg sm:text-xl font-bold dark:text-white">הקלטות אחרונות</h2> <button id="clearTracks" class="text-xs text-slate-400 hover:text-red-500 font-bold transition-colors">נקה הכל</button> </div> <div id="tracksContainer" class="space-y-3 sm:space-y-4"> <div id="noTracks" class="text-center py-16 sm:py-24 border-2 border-dashed border-slate-300 dark:border-slate-800 rounded-2xl sm:rounded-[2.5rem] text-slate-400 font-bold text-sm sm:text-base"> היסטוריית הקריינות שלך תישמר כאן </div> </div> </div> </div> </div> <div id="toast" class="fixed bottom-4 sm:bottom-8 left-4 sm:left-8 right-4 sm:right-auto px-4 sm:px-6 py-3 rounded-xl shadow-2xl opacity-0 transition-all pointer-events-none z-50 transform translate-y-4 flex items-center justify-center sm:justify-start gap-2 max-w-md"> <span id="toastIcon"></span> <span id="toastMessage" class="text-xs sm:text-sm font-semibold"></span> </div> <script> // Global State & Config let currentApiKey = localStorage.getItem('gemini_api_key') || ""; const modelText = "gemini-3.5-flash"; const modelAudio = "gemini-2.5-flash-preview-tts"; const els = { textInput: document.getElementById('textInput'), promptInput: document.getElementById('promptInput'), generateBtn: document.getElementById('generateBtn'), magicWriteBtn: document.getElementById('magicWriteBtn'), analyzeToneBtn: document.getElementById('analyzeToneBtn'), voiceSelect: document.getElementById('voiceSelect'), styleSelect: document.getElementById('styleSelect'), tracksContainer: document.getElementById('tracksContainer'), noTracks: document.getElementById('noTracks'), body: document.getElementById('bodyTag'), toast: document.getElementById('toast'), toastMessage: document.getElementById('toastMessage'), // API Key elements apiKeyBtn: document.getElementById('apiKeyBtn'), apiKeyPopover: document.getElementById('apiKeyPopover'), apiKeyInput: document.getElementById('apiKeyInput'), saveApiKey: document.getElementById('saveApiKey'), clearApiKey: document.getElementById('clearApiKey'), apiKeyStatus: document.getElementById('apiKeyStatus') }; // Initialize UI function updateApiStatus() { if (currentApiKey) { els.apiKeyStatus.innerText = "מפתח מוגדר ✅"; els.apiKeyStatus.classList.add('text-green-600'); els.apiKeyInput.value = currentApiKey; } else { els.apiKeyStatus.innerText = "הגדר מפתח ⚠️"; els.apiKeyStatus.classList.remove('text-green-600'); els.apiKeyInput.value = ""; } } updateApiStatus(); // API Key Popover Toggle els.apiKeyBtn.onclick = (e) => { e.stopPropagation(); els.apiKeyPopover.classList.toggle('hidden'); }; els.saveApiKey.onclick = () => { const key = els.apiKeyInput.value.trim(); if (key) { currentApiKey = key; localStorage.setItem('gemini_api_key', key); updateApiStatus(); els.apiKeyPopover.classList.add('hidden'); showToast("מפתח ה-API נשמר בהצלחה", "success"); } }; els.clearApiKey.onclick = () => { currentApiKey = ""; localStorage.removeItem('gemini_api_key'); updateApiStatus(); showToast("מפתח ה-API נמחק"); }; document.addEventListener('click', () => els.apiKeyPopover.classList.add('hidden')); els.apiKeyPopover.onclick = (e) => e.stopPropagation(); // Utility: API Caller with retry logic async function apiCall(model, payload, retries = 5) { if (!currentApiKey) { showToast("אנא הגדר מפתח API בתפריט למעלה"); els.apiKeyPopover.classList.remove('hidden'); throw new Error("Missing API Key"); } const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:${model.includes('tts') ? 'generateContent' : 'generateContent'}?key=${currentApiKey}`; for (let i = 0; i < retries; i++) { try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const data = await response.json(); if (response.ok) return data; if (response.status === 429) { await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000)); } else if (response.status === 403 || response.status === 401) { showToast("מפתח ה-API אינו תקין"); throw new Error("Invalid API Key"); } else { throw new Error(data.error?.message || "Communication Error"); } } catch (e) { if (i === retries - 1) throw e; } } } function showToast(msg, type = 'error') { els.toastMessage.innerText = msg; els.toast.className = `fixed bottom-4 sm:bottom-8 left-4 sm:left-8 right-4 sm:right-auto px-4 sm:px-6 py-3 rounded-xl shadow-2xl transition-all z-50 transform translate-y-0 opacity-100 ${type === 'error' ? 'bg-red-600' : 'bg-indigo-600'} text-white max-w-md flex items-center justify-center sm:justify-start gap-2`; setTimeout(() => { els.toast.classList.add('opacity-0', 'translate-y-4'); }, 4000); } // --- LLM Features --- // ✨ Magic Write els.magicWriteBtn.onclick = async () => { const prompt = els.promptInput.value.trim(); if (!prompt) return showToast("תאר מה לכתוב בתיבה"); toggleLoader('magic', true); try { const data = await apiCall(modelText, { contents: [{ parts: [{ text: `כתוב סקריפט קצר ומקצועי לקריינות בעברית עבור הנושא הבא: "${prompt}". החזר רק את הטקסט של הסקריפט ללא הסברים או כותרות.` }] }] }); const result = data.candidates?.[0]?.content?.parts?.[0]?.text; if (result) els.textInput.value = result.trim(); } catch (e) { if(e.message !== "Missing API Key") showToast(e.message); } toggleLoader('magic', false); }; // ✨ Smart Edit async function smartEdit(instruction) { const text = els.textInput.value.trim(); if (!text) return showToast("הכנס קודם טקסט לתיבה"); showToast("מעבד טקסט...", "success"); try { const data = await apiCall(modelText, { contents: [{ parts: [{ text: `${instruction} על הטקסט הבא: "${text}". החזר רק את הטקסט המעובד.` }] }] }); const result = data.candidates?.[0]?.content?.parts?.[0]?.text; if (result) els.textInput.value = result.trim(); } catch (e) { if(e.message !== "Missing API Key") showToast(e.message); } } // ✨ Translate async function translateText() { const text = els.textInput.value.trim(); if (!text) return showToast("אין טקסט לתרגום"); showToast("מתרגם לאנגלית...", "success"); try { const data = await apiCall(modelText, { contents: [{ parts: [{ text: `Translate the following text to fluent English suitable for narration: "${text}". Return only the translation.` }] }] }); const result = data.candidates?.[0]?.content?.parts?.[0]?.text; if (result) els.textInput.value = result.trim(); } catch (e) { if(e.message !== "Missing API Key") showToast(e.message); } } // ✨ Podcast Script async function generatePodcastScript() { const topic = els.promptInput.value.trim() || "Technology"; showToast("יוצר תסריט פודקאסט...", "success"); try { const data = await apiCall(modelText, { contents: [{ parts: [{ text: `Create a short podcast script dialogue between two people about: "${topic}". Return only the dialogue in Hebrew.` }] }] }); const result = data.candidates?.[0]?.content?.parts?.[0]?.text; if (result) els.textInput.value = result.trim(); } catch (e) { if(e.message !== "Missing API Key") showToast(e.message); } } // ✨ Summarize async function summarizeText() { const text = els.textInput.value.trim(); if (!text) return showToast("אין טקסט לסיכום"); showToast("מסכם לנקודות...", "success"); try { const data = await apiCall(modelText, { contents: [{ parts: [{ text: `Summarize the following text into 3 clear key points for narration in Hebrew: "${text}".` }] }] }); const result = data.candidates?.[0]?.content?.parts?.[0]?.text; if (result) els.textInput.value = result.trim(); } catch (e) { if(e.message !== "Missing API Key") showToast(e.message); } } // ✨ Tone Analysis els.analyzeToneBtn.onclick = async () => { const text = els.textInput.value.trim(); if (!text) return showToast("אין טקסט לניתוח"); toggleLoader('tone', true); try { const data = await apiCall(modelText, { contents: [{ parts: [{ text: `Analyze the sentiment of the following text and pick the best speaking style from this list: (natural, cheerful, serious, whisper, excited, calm, authoritative, friendly, professional, storytelling, shouting, terrified, sad, angry, hopeful). Return ONLY the English word. Text: "${text}"` }] }] }); const choice = data.candidates?.[0]?.content?.parts?.[0]?.text?.trim().toLowerCase(); if (choice) { const cleanChoice = choice.replace(/[^a-z]/g, ''); const option = Array.from(els.styleSelect.options).find(o => o.value === cleanChoice); if (option) { els.styleSelect.value = cleanChoice; showToast(`הסגנון הותאם ל: ${option.text}`, "success"); } } } catch (e) { if(e.message !== "Missing API Key") showToast(e.message); } toggleLoader('tone', false); }; // ✨ Vocalization (Nikud) document.getElementById('vocalizeBtn').onclick = async () => { const text = els.textInput.value.trim(); if (!text) return showToast("אין טקסט לניקוד"); toggleLoader('vocalize', true); try { const data = await apiCall(modelText, { contents: [{ parts: [{ text: `הוסף ניקוד דקדוקי מלא ומדויק לטקסט הבא בעברית: "${text}". החזר רק את הטקסט המנוקד.` }] }] }); const result = data.candidates?.[0]?.content?.parts?.[0]?.text; if (result) els.textInput.value = result.trim(); } catch (e) { if(e.message !== "Missing API Key") showToast(e.message); } toggleLoader('vocalize', false); }; // --- TTS Core --- els.generateBtn.onclick = async () => { const text = els.textInput.value.trim(); if (!text) return showToast("הכנס טקסט לקריינות"); toggleLoader('btn', true); try { const result = await apiCall(modelAudio, { contents: [{ parts: [{ text: `Say in a ${els.styleSelect.value} style: ${text}` }] }], generationConfig: { responseModalities: ["AUDIO"], speechConfig: { voiceConfig: { prebuiltVoiceConfig: { voiceName: els.voiceSelect.value } } } } }); const pcmData = result.candidates?.[0]?.content?.parts?.[0]?.inlineData?.data; const mimeType = result.candidates?.[0]?.content?.parts?.[0]?.inlineData?.mimeType || ""; const sampleRate = parseInt(mimeType.split('rate=')[1]) || 24000; if (pcmData) { const blob = pcmToWav(pcmData, sampleRate); addTrack(blob, text.substring(0, 30)); showToast("קריינות הופקה בהצלחה!", "success"); } } catch (e) { if(e.message !== "Missing API Key") showToast(e.message); } toggleLoader('btn', false); }; // --- Helpers --- function toggleLoader(key, show) { const l = document.getElementById(key + 'Loader'); const i = document.getElementById(key + 'Icon'); const t = document.getElementById(key + 'Text'); if (l) l.classList.toggle('hidden', !show); if (i) i.classList.toggle('hidden', show); if (t) t.innerText = show ? "מעבד..." : "הפק קריינות עכשיו"; if (key === 'btn') els.generateBtn.disabled = show; } function pcmToWav(base64, rate) { const binary = atob(base64); const bytes = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i); const header = new ArrayBuffer(44); const v = new DataView(header); const s = (off, str) => { for (let i = 0; i < str.length; i++) v.setUint8(off + i, str.charCodeAt(i)); }; s(0, 'RIFF'); v.setUint32(4, 36 + bytes.length, true); s(8, 'WAVE'); s(12, 'fmt '); v.setUint32(16, 16, true); v.setUint16(20, 1, true); v.setUint16(22, 1, true); v.setUint32(24, rate, true); v.setUint32(28, rate * 2, true); v.setUint16(32, 2, true); v.setUint16(34, 16, true); s(36, 'data'); v.setUint32(40, bytes.length, true); return new Blob([header, bytes], { type: 'audio/wav' }); } function addTrack(blob, title) { els.noTracks.classList.add('hidden'); const url = URL.createObjectURL(blob); const div = document.createElement('div'); div.className = "card p-3 sm:p-4 rounded-xl sm:rounded-2xl flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 shadow-sm border-slate-100 dark:border-slate-800"; div.innerHTML = ` <div class="flex-grow w-full sm:w-auto"> <div class="font-bold text-xs sm:text-sm truncate dark:text-white">${title}...</div> <div class="text-[10px] text-slate-400 mt-0.5">${els.voiceSelect.value} | ${els.styleSelect.value}</div> </div> <div class="flex items-center justify-between sm:justify-end w-full sm:w-auto gap-2 border-t sm:border-t-0 pt-2 sm:pt-0 border-slate-100 dark:border-slate-800"> <audio src="${url}" controls class="w-full sm:w-48"></audio> <a href="${url}" download="voice_${Date.now()}.wav" class="p-2 hover:bg-indigo-50 dark:hover:bg-slate-700 rounded-lg text-indigo-600 flex-shrink-0"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/></svg> </a> </div> `; els.tracksContainer.prepend(div); } document.getElementById('toggleDark').onclick = () => els.body.classList.toggle('dark-mode'); document.getElementById('clearTracks').onclick = () => { els.tracksContainer.querySelectorAll('.card').forEach(c => c.remove()); els.noTracks.classList.remove('hidden'); }; </script> </body> </html>
  • שלוחה להשמעת קבצים משתי שלוחות

    5
    0 הצבעות
    5 פוסטים
    12 צפיות
    נ
    @דודי-א כתב: שלום וברכה ראיתי שיש את האופציה אבל לא הבנתי בדיוק איך עושים את זה יש לי 2 שלוחות של השמעת קבצים ואני רוצה ששלוחה 3 תשמיע את הקבצים משתיהם ושתשמיע לפי סדר שהאחרון שהוקלט יושמע ראשון הבנתי שצריך להשתמש בהגדרה folder_to_play= אשמח להסבר מדויק תודה רבה https://f2.freeivr.co.il/post/56978
  • מה קרה שהתחלף קריין בכל המערכת

    4
    0 הצבעות
    4 פוסטים
    20 צפיות
    נ
    @חסיד-חזק יש לי שרת ישן ועים מעדקנים טקסת שם למעזין אז זה משנה קול
  • 0 הצבעות
    214 פוסטים
    6k צפיות
    ה
    עשיתי את כל ההגדרות ולא הצלחתי לרשום את המערכת
  • הודעה אישית / ניהולו 🪧📣👨‍💼

    לא נפתר
    25
    0 הצבעות
    25 פוסטים
    132 צפיות
    ש
    @פלוס תנסה להיכנס ישר ל B בלי לעבור ב A תבדוק אם זה משמיע לך הודעות של אותו מספר
  • נדרש לי שני קוי תוכן שנפתחו לפני יותר מחודש

    לא נפתר
    5
    0 הצבעות
    5 פוסטים
    41 צפיות
    נ
    @ARISH יש לי 079 זה טוב
  • איך אני עושה הודעת פתיחה זמנית

    30
    0 הצבעות
    30 פוסטים
    238 צפיות
    מ
    @שלוחה-404 כן אבל אין לי את המייל שלך אז אולי תשלח לי מייל ואז אוכל להשיב לך
  • מאזין שלא ביקר בשלוחה X ישמע הודעה מיוחדת

    10
    0 הצבעות
    10 פוסטים
    111 צפיות
    י
    @המומחה האם עכשיו אחרי ההסברה שלהם כן הסתדרת אם לא אני מביא לך את ההגדרות
  • מספר "הודעה כמות המספרים ששמעו..."

    17
    0 הצבעות
    17 פוסטים
    44 צפיות
    נ
    @המומחה סמן נשה כנפתר
  • פרסומת בקו ללא פרסומות

    7
    0 הצבעות
    7 פוסטים
    51 צפיות
    צ
    זה קרה לי בפעם הראשונה בשתי קוים שונים (אחד בכלל לא בשליטתי והוא קו של 1700), וגם בקו שזה קרה לי פעמיים זה היה באופן חד פעמי ממש כך שלא נראה שזה בעיה בתשלום עכשיו אני בדקתי ואני רואה שבאמת התשלום האחרון הוא מהעשירי ביוני, אולי זה הפשט
  • איפוס אקסס פילטר דרך הקלטה

    לא נפתר
    3
    0 הצבעות
    3 פוסטים
    12 צפיות
    ח
    @שלוחה-404 תודה רבה צודק מיליון אחוז
  • "למעבר לשירות טלזכור הקש 0" - גם אותכם זה מעצבן?

    23
    9 הצבעות
    23 פוסטים
    253 צפיות
    י
    @שלוחה-404 עריכה בפוסט הראשון: תודה רבה לכל המגיבים והמלייקים (שימו לב לכמות הלייקים!! ) מה יצא? בינתיים לא השתנה כלום.... אולי צריך לפנות לימות המשיח? הנושא כאן קצת גלש.... אז מה יצא עם הנושא שלשמו התכנסנו פה? מישהו כבר פנה לימות המשיח?
  • העתקת תוכן אוטומטי ממערכת אחד למערכת שני על ידי טוקן

    9
    0 הצבעות
    9 פוסטים
    42 צפיות
    נ
    @שטיינהרטר כתב: אני רוצה העתקת תוכן אוטומטי ממערכת אחד למערכת שני על ידי טוקן כלומר שכשמהו נכנס למערכת ב' המערכת ילך להעתיק לו חומר הנמצאת במערכת א' עשיתי את הקודים האלו אבל משום מה זה לא עובד יש לכם פיתרון? קודים לשלוחה 1 קבלת הקבצים והעברתם לשלוחה 2 שלוחת השמעת קבצים הגדרת סוג השלוחה כמודול להעתקת קבצים type=copy_file כתובת השרת של ימות המשיח לביצוע הורדת קבצים כאן כתבתי את הכתובת המפתח הסודי (Token) של קו א' שמאפשר לקו ב' לגשת לקבצים שלו ..............כאן כתבתי הטוקן הנתיב המדויק בקו א' ממנו נמשכים הקבצים (שלוחה 1) copy_path=ivr2/1 הנתיב בקו ב' אליו יועתקו הקבצים (תיקייה 2 שנמצאת שלב אחד מעל) target_path=/2 הוראה להעתיק את כל הקבצים שנמצאים בתיקיית המקור copy_all=yes הוראה לא להעתיק קובץ אם הוא כבר קיים בקו ב' (חוסך זמן ומונע כפילויות) copy_if_exists=no לאחר סיום בדיקת ההעתקה, המערכת תעביר את המתקשר אוטומטית לשלוחה 2 copy_done_go_to=/2 קודים לשלוחה 2 קבלת הקבצים והעשמעתם משלוחה 1 שלוחת קבלת קבציםטקסט מודגש הגדרת סוג השלוחה כשלוחה להשמעת קבצים type=playfile מיון הקבצים מהחדש ביותר לישנה ביותר (לפי סדר יורד של שמות הקבצים) playfile_sort=7 הגדרה המאפשרת למתקשר לשלוט על ההשמעה (למשל: 4 לחזור אחורה, 6 לדלג קדימה) control_playfile=yes הגדרה שאומרת למערכת שאם אין קבצים בשלוחה, היא תגיד "אין הודעות" במקום לשתוק say_no_files=yes זה מה שAI יצר https://share.gemini.google/CU1i0ByPaH4u
  • בעיית השמעת טקסט (TTS) בתוך פקודת read במודול API

    12
    0 הצבעות
    12 פוסטים
    73 צפיות
    ש
    @שמואל-ש. כתב: @שלוחה-404 כתב: @קו-הרהיטים אם זה מערכת YMX אין מה לעשות בדיקת שרת באתר הניהול בסוף הדף יש סוגריים קטנות שכתוב בתוכם איזה שרת אתה אני החלפתי קול ועזר לי אולי תועיל ותסביר באיזה שרת אתה ואיזה פקודה שמת שזה עזר לך ואיפוא שמת אותה?
  • מודל תור type=queue

    9
    0 הצבעות
    9 פוסטים
    91 צפיות
    ל
    @יעקב-יצחק עשיתי את זה בשלוחה SIP ולא עובד דווקא בשלוחה של ניתוב זה כן עובד
  • 0 הצבעות
    4 פוסטים
    23 צפיות
    Y
    @מתעניין זה לא עוזר כי שוב צריך ניתוק ולא יועיל מי שסיים לשמוע את ההודעות שתישמר עבורו ההודעה האחרונה לפני סוף ההודעות.
  • מחיקת שידור חי

    2
    0 הצבעות
    2 פוסטים
    35 צפיות
    ד
    יש לי את אותה שאלה... גם אי אפשר לחדש הפעלה של שידור ישן במצב כזה, מרגיש כמו שינוי יזום, בפרט כשאין התייחסות לנושא. אולי סגירה שקטה של המודול, מקווה שטעיתי...