דילוג לתוכן
  • דף הבית
  • חוקי הפורום
  • המדריך לשימוש בפורום
  • פוסטים אחרונים
  • לא נפתר
  • תגיות
  • משתמשים
  • חיפוש גוגל בפורום
  • צור קשר
  • פופולרי
  • קבוצות
עיצובים
  • בהיר
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • כהה
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • ברירת מחדל (ללא עיצוב (ברירת מחדל))
  • ללא עיצוב (ברירת מחדל)
כיווץ
לוגו אתר
א

אריה AI

@אריה AI
הפסקת מעקב מעקב
אודות
פוסטים
2
נושאים
1
שיתופים
0
קבוצות
0
עוקבים
1
עוקב אחרי
6

פוסטים

פוסטים אחרונים הגבוה ביותר שנוי במחלוקת

  • VoiceMaster אתר קריינות משוכלל!!!
    א אריה AI

    לאחר זמן רב,
    שישבתי יומם ולילה לחשוב איך כל אדם שצריך איזשהו קריינות, יוכל ליצור אותה בקלות.
    לכן בניתי את האתר הזה
    קודם כל יש להיכנס ל-Google AI Studio וליצור מפתח API חדש ולעלות לאתר למעלה בצד שמאל.
    ואחרי זה תיצרו את הקריינות...

    או שתפתחו פנקס רשימות ואז תשמרו את זה על שם 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>
    

    בהצלחה!!!

    סאונד, מוזיקה וקריינות

  • VoiceMaster אתר קריינות משוכלל!!!
    א אריה AI

    @Bugs-Bunny בשמחה....

    סאונד, מוזיקה וקריינות
  • התחברות

  • אין לך חשבון עדיין? הרשמה

  • התחברו או הירשמו כדי לחפש.
  • פוסט ראשון
    פוסט אחרון
0
  • דף הבית
  • חוקי הפורום
  • המדריך לשימוש בפורום
  • פוסטים אחרונים
  • לא נפתר
  • תגיות
  • משתמשים
  • חיפוש גוגל בפורום
  • צור קשר
  • פופולרי
  • קבוצות