/* NCAkit - Main JavaScript */ // ============================================ // 🔐 LOGIN CREDENTIALS (Easy to find if forgotten!) // ============================================ const AUTH_CONFIG = { username: "robiul", password: "Robi1234", storageKey: "ncakit_logged_in" }; // ============================================ // AUTHENTICATION FUNCTIONS // ============================================ function checkAuth() { return localStorage.getItem(AUTH_CONFIG.storageKey) === "true"; } function showApp() { document.getElementById('loginOverlay').classList.add('hidden'); document.getElementById('mainApp').classList.remove('hidden'); } function showLogin() { document.getElementById('loginOverlay').classList.remove('hidden'); document.getElementById('mainApp').classList.add('hidden'); } function handleLogin(e) { e.preventDefault(); const username = document.getElementById('loginUsername').value.trim(); const password = document.getElementById('loginPassword').value; const errorEl = document.getElementById('loginError'); // Debug logging console.log('Login attempt:', { username, passwordLength: password.length }); console.log('Expected:', { username: AUTH_CONFIG.username, password: AUTH_CONFIG.password }); if (username === AUTH_CONFIG.username && password === AUTH_CONFIG.password) { console.log('Login SUCCESS!'); localStorage.setItem(AUTH_CONFIG.storageKey, "true"); errorEl.classList.add('hidden'); showApp(); } else { console.log('Login FAILED - credentials mismatch'); errorEl.textContent = "❌ Incorrect username or password"; errorEl.classList.remove('hidden'); } } function handleLogout() { localStorage.removeItem(AUTH_CONFIG.storageKey); showLogin(); // Clear form document.getElementById('loginUsername').value = ''; document.getElementById('loginPassword').value = ''; } // Wait for DOM to be ready document.addEventListener('DOMContentLoaded', function () { // ============================================ // AUTHENTICATION CHECK ON PAGE LOAD // ============================================ if (checkAuth()) { showApp(); } else { showLogin(); } // Login form handler document.getElementById('loginForm').addEventListener('submit', handleLogin); // Logout button handler document.getElementById('logoutBtn').addEventListener('click', handleLogout); // ============================================ // TAB SWITCHING // ============================================ document.querySelectorAll('.tab-btn').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); btn.classList.add('active'); document.getElementById(btn.dataset.tab + '-tab').classList.add('active'); }); }); // ============================================ // STORY REELS MODULE // ============================================ document.getElementById('storyForm').addEventListener('submit', async (e) => { e.preventDefault(); const status = document.getElementById('storyStatus'); status.className = 'status processing'; status.innerHTML = '⏳ Starting generation...'; status.classList.remove('hidden'); const data = { script: document.getElementById('storyScript').value, image_style: document.getElementById('storyStyle').value, voice: document.getElementById('storyVoice').value }; try { const res = await fetch('/api/story/story-reel', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await res.json(); if (result.job_id) { status.innerHTML = `✅ Job started! ID: ${result.job_id}
Checking status...`; pollStatus(result.job_id, 'story'); } } catch (err) { status.className = 'status error'; status.innerHTML = '❌ Error: ' + err.message; } }); // Generic poll status for story/video async function pollStatus(jobId, type) { const status = document.getElementById(type + 'Status'); const endpoint = type === 'story' ? '/api/story/story-reel/' : '/api/video/short-video/'; const check = async () => { try { const res = await fetch(endpoint + jobId + '/status'); const data = await res.json(); const progress = data.progress || 0; status.innerHTML = `
Status: ${data.status}
`; if (data.status === 'ready') { status.className = 'status success'; const downloadUrl = type === 'story' ? `/api/story/story-reel/${jobId}` : `/api/video/short-video/${jobId}`; status.innerHTML += `
📥 Download Video`; } else if (data.status === 'failed') { status.className = 'status error'; status.innerHTML = '❌ Failed: ' + (data.error || 'Unknown error'); } else { setTimeout(check, 2000); } } catch (err) { setTimeout(check, 3000); } }; check(); } // ============================================ // VIDEO CREATOR MODULE // ============================================ // Add scene document.getElementById('addScene').addEventListener('click', () => { const container = document.getElementById('scenesContainer'); const count = container.querySelectorAll('.scene-text').length + 1; container.innerHTML += `
`; }); // Video Form document.getElementById('videoForm').addEventListener('submit', async (e) => { e.preventDefault(); const status = document.getElementById('videoStatus'); status.className = 'status processing'; status.innerHTML = '⏳ Creating video...'; status.classList.remove('hidden'); const scenes = []; document.querySelectorAll('.scene-text').forEach((textarea, i) => { const keywords = document.querySelectorAll('.scene-keywords')[i]?.value || ''; scenes.push({ text: textarea.value, searchTerms: keywords.split(',').map(k => k.trim()).filter(k => k) }); }); const data = { scenes: scenes, config: { voice: document.getElementById('videoVoice').value, music: document.getElementById('videoMusic').value || null } }; try { const res = await fetch('/api/video/short-video', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await res.json(); if (result.videoId) { status.innerHTML = `✅ Video started! ID: ${result.videoId}`; pollStatus(result.videoId, 'video'); } } catch (err) { status.className = 'status error'; status.innerHTML = '❌ Error: ' + err.message; } }); // ============================================ // FACT IMAGE MODULE // ============================================ document.getElementById('factForm').addEventListener('submit', async (e) => { e.preventDefault(); const status = document.getElementById('factStatus'); status.className = 'status processing'; status.innerHTML = '⏳ Generating fact video...'; status.classList.remove('hidden'); const heading = document.getElementById('factHeading').value.trim(); const data = { model: document.getElementById('factModel').value, image_prompt: document.getElementById('factImagePrompt').value, fact_text: document.getElementById('factText').value, duration: parseInt(document.getElementById('factDuration').value) }; if (heading) { data.fact_heading = heading; data.heading_background = { enabled: true, color: document.getElementById('headingBgColor').value, padding: 22, corner_radius: parseInt(document.getElementById('headingBgRadius').value) }; } try { const res = await fetch('/api/fact-image/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await res.json(); if (res.ok && result.job_id) { status.innerHTML = `✅ Job started! ID: ${result.job_id}
Checking status...`; pollFactStatus(result.job_id); } else { status.className = 'status error'; // Show validation errors let errorMsg = result.detail || 'Unknown error'; if (Array.isArray(result.detail)) { errorMsg = result.detail.map(e => e.msg).join(', '); } status.innerHTML = '❌ Error: ' + errorMsg; } } catch (err) { status.className = 'status error'; status.innerHTML = '❌ Error: ' + err.message; } }); async function pollFactStatus(jobId) { const status = document.getElementById('factStatus'); const check = async () => { try { const res = await fetch(`/api/fact-image/${jobId}/status`); const data = await res.json(); const progress = data.progress || 0; status.innerHTML = `
Status: ${data.status}
`; if (data.status === 'ready') { status.className = 'status success'; status.innerHTML += `
📥 Download Video`; } else if (data.status === 'failed') { status.className = 'status error'; status.innerHTML = '❌ Failed: ' + (data.error || 'Unknown error'); } else { setTimeout(check, 2000); } } catch (err) { setTimeout(check, 3000); } }; check(); } // ============================================ // TRENDS MODULE // ============================================ document.getElementById('trendingForm').addEventListener('submit', async (e) => { e.preventDefault(); const results = document.getElementById('trendingResults'); results.innerHTML = '

⏳ Loading trends...

'; const data = { country: document.getElementById('trendCountry').value, limit: parseInt(document.getElementById('trendLimit').value) }; try { const res = await fetch('/api/trends/trending-now', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await res.json(); if (result.success) { let html = '
'; result.trends.forEach(t => { html += `
#${t.rank} ${t.topic}
`; }); html += '
'; results.innerHTML = html; } else { results.innerHTML = '

❌ Error: ' + (result.detail || 'Failed') + '

'; } } catch (err) { results.innerHTML = '

❌ Error: ' + err.message + '

'; } }); document.getElementById('keywordForm').addEventListener('submit', async (e) => { e.preventDefault(); const results = document.getElementById('keywordResults'); results.innerHTML = '

⏳ Analyzing keyword...

'; const data = { keyword: document.getElementById('researchKeyword').value, region: document.getElementById('researchRegion').value, timeframe: document.getElementById('researchTimeframe').value, category: document.getElementById('researchCategory').value, search_type: document.getElementById('researchType').value }; try { const res = await fetch('/api/trends/keyword-research', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await res.json(); if (result.success) { let html = '
'; html += '
'; html += '

📌 Related Topics

'; if (result.related_topics.top && result.related_topics.top.length > 0) { result.related_topics.top.slice(0, 10).forEach(t => { html += `
${t.topic} (${t.value})
`; }); } else { html += '

No data

'; } html += '
'; html += '
'; html += '

🔍 Related Queries

'; if (result.related_queries.top && result.related_queries.top.length > 0) { result.related_queries.top.slice(0, 10).forEach(q => { html += `
${q.query} (${q.value})
`; }); } else { html += '

No data

'; } html += '
'; html += '
'; results.innerHTML = html; } else { results.innerHTML = '

❌ Error: ' + (result.detail || 'Failed') + '

'; } } catch (err) { results.innerHTML = '

❌ Error: ' + err.message + '

'; } }); // ============================================ // QUIZ REEL MODULE // ============================================ let quizCount = 1; document.getElementById('addQuizBtn').addEventListener('click', () => { quizCount++; const container = document.getElementById('quizContainer'); const quizHtml = `

Quiz ${quizCount}

`; container.insertAdjacentHTML('beforeend', quizHtml); }); document.getElementById('removeQuizBtn').addEventListener('click', () => { const container = document.getElementById('quizContainer'); const items = container.querySelectorAll('.quiz-item'); if (items.length > 1) { items[items.length - 1].remove(); quizCount--; } }); async function pollQuizStatus(jobId, statusDiv) { const checkStatus = async () => { try { const res = await fetch(`/api/quiz/${jobId}/status`); const status = await res.json(); if (status.status === 'ready') { statusDiv.className = 'status success'; statusDiv.innerHTML = `✅ Video Ready! Download Video`; return; } else if (status.status === 'failed') { statusDiv.className = 'status error'; statusDiv.innerHTML = `❌ Failed: ${status.error}`; return; } else { statusDiv.innerHTML = `⏳ ${status.status}... ${status.progress}%`; setTimeout(checkStatus, 2000); } } catch (err) { statusDiv.className = 'status error'; statusDiv.innerHTML = `❌ Error: ${err.message}`; } }; checkStatus(); } document.getElementById('quizForm').addEventListener('submit', async (e) => { e.preventDefault(); const status = document.getElementById('quizStatus'); status.className = 'status'; status.classList.remove('hidden'); status.innerHTML = '⏳ Starting quiz video generation...'; const quizItems = document.querySelectorAll('.quiz-item'); const quizzes = []; quizItems.forEach(item => { quizzes.push({ hook: item.querySelector('.quiz-hook').value || '', question: item.querySelector('.quiz-question').value, options: { A: item.querySelector('.quiz-option-a').value, B: item.querySelector('.quiz-option-b').value, C: item.querySelector('.quiz-option-c').value }, correct: item.querySelector('.quiz-correct').value, explain: item.querySelector('.quiz-explain').value || '' }); }); const data = { quizzes: quizzes, voice: document.getElementById('quizVoice').value }; try { const res = await fetch('/api/quiz/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await res.json(); if (result.job_id) { status.innerHTML = `⏳ Job started: ${result.job_id} (${quizzes.length} quizzes)`; pollQuizStatus(result.job_id, status); } else { status.className = 'status error'; status.innerHTML = `❌ Error: ${result.detail || 'Failed to start'}`; } } catch (err) { status.className = 'status error'; status.innerHTML = '❌ Error: ' + err.message; } }); // ============================================ // TEXT STORY MODULE // ============================================ document.getElementById('tsAddMessage').addEventListener('click', () => { const container = document.getElementById('tsMessagesContainer'); const count = container.querySelectorAll('.ts-message-item').length + 1; const html = `
`; container.insertAdjacentHTML('beforeend', html); }); document.getElementById('tsMessagesContainer').addEventListener('click', (e) => { if (e.target.classList.contains('ts-remove')) { const items = document.querySelectorAll('.ts-message-item'); if (items.length > 1) { e.target.closest('.ts-message-item').remove(); } } }); // Toggle Manual/AI mode - make global window.toggleTsMode = function () { const mode = document.querySelector('input[name="tsMode"]:checked').value; document.getElementById('tsAiSection').style.display = mode === 'ai' ? 'block' : 'none'; document.getElementById('tsManualSection').style.display = mode === 'manual' ? 'block' : 'none'; }; document.getElementById('textStoryForm').addEventListener('submit', async (e) => { e.preventDefault(); const status = document.getElementById('textStoryStatus'); status.className = 'status processing'; status.classList.remove('hidden'); const mode = document.querySelector('input[name="tsMode"]:checked').value; let messages = []; if (mode === 'ai') { const prompt = document.getElementById('tsAiPrompt').value.trim(); if (!prompt) { status.className = 'status error'; status.innerHTML = '❌ Please enter a prompt for AI!'; return; } status.innerHTML = '🤖 AI generating conversation...'; try { const aiRes = await fetch('/api/text-story/ai-generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: prompt, person_a_name: document.getElementById('tsPersonA').value || 'You', person_b_name: document.getElementById('tsPersonB').value || 'My Ex', message_count: parseInt(document.getElementById('tsAiMsgCount').value), tone: document.getElementById('tsAiTone').value }) }); const aiData = await aiRes.json(); if (aiData.messages) { messages = aiData.messages; status.innerHTML = `🤖 Generated ${messages.length} messages. Now creating video...`; } else { status.className = 'status error'; status.innerHTML = '❌ AI failed: ' + (aiData.detail || 'Unknown error'); return; } } catch (err) { status.className = 'status error'; status.innerHTML = '❌ AI Error: ' + err.message; return; } } else { const messageItems = document.querySelectorAll('.ts-message-item'); messageItems.forEach(item => { const sender = item.querySelector('.ts-sender').value; const text = item.querySelector('.ts-text').value.trim(); if (text) { messages.push({ sender, text }); } }); if (messages.length < 2) { status.className = 'status error'; status.innerHTML = '❌ Need at least 2 messages!'; return; } } status.innerHTML = '⏳ Starting video generation...'; const data = { person_a_name: document.getElementById('tsPersonA').value || 'You', person_b_name: document.getElementById('tsPersonB').value || 'My Ex', person_b_avatar: document.getElementById('tsAvatar').value || null, messages: messages, ending_text: document.getElementById('tsEnding').value || null, voice_a: document.getElementById('tsVoiceA').value, voice_b: document.getElementById('tsVoiceB').value }; try { const res = await fetch('/api/text-story/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await res.json(); if (result.job_id) { status.innerHTML = `⏳ Job started: ${result.job_id}`; pollTextStoryStatus(result.job_id); } else { status.className = 'status error'; status.innerHTML = `❌ Error: ${result.detail || 'Failed to start'}`; } } catch (err) { status.className = 'status error'; status.innerHTML = '❌ Error: ' + err.message; } }); async function pollTextStoryStatus(jobId) { const status = document.getElementById('textStoryStatus'); const poll = async () => { try { const res = await fetch(`/api/text-story/${jobId}/status`); const data = await res.json(); if (data.status === 'ready') { status.className = 'status success'; status.innerHTML = `✅ Video ready! 📥 Download`; } else if (data.status === 'failed') { status.className = 'status error'; status.innerHTML = '❌ Failed: ' + (data.error || 'Unknown error'); } else { const step = data.current_step || 'Processing'; status.innerHTML = `⏳ ${step} (${data.progress}%)`; setTimeout(poll, 2000); } } catch (err) { setTimeout(poll, 3000); } }; poll(); } // ============================================ // GEMINI CHATBOT // ============================================ const chatBtn = document.getElementById('chatBtn'); const chatModal = document.getElementById('chatModal'); const chatClose = document.getElementById('chatClose'); const chatForm = document.getElementById('chatForm'); const chatInput = document.getElementById('chatInput'); const chatMessages = document.getElementById('chatMessages'); if (chatBtn) { chatBtn.addEventListener('click', () => { chatModal.classList.toggle('hidden'); }); chatClose.addEventListener('click', () => { chatModal.classList.add('hidden'); }); chatForm.addEventListener('submit', async (e) => { e.preventDefault(); const message = chatInput.value.trim(); if (!message) return; addMessage(message, 'user'); chatInput.value = ''; const loadingId = addMessage('⏳ Thinking...', 'bot'); try { const res = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message }) }); const data = await res.json(); document.getElementById(loadingId).remove(); if (data.reply) { addMessage(data.reply, 'bot'); } else if (data.error) { addMessage('❌ ' + data.error, 'bot'); } } catch (err) { document.getElementById(loadingId).remove(); addMessage('❌ Error: ' + err.message, 'bot'); } }); function addMessage(text, type) { const id = 'msg-' + Date.now(); const div = document.createElement('div'); div.id = id; div.className = 'chat-message ' + type; div.textContent = text; chatMessages.appendChild(div); chatMessages.scrollTop = chatMessages.scrollHeight; return id; } } }); // End DOMContentLoaded