Lukingrecap / templates /index.html
kc502
Full Release: Login System, Credit System, Free/Premium Locks, Split Workflow
dee5fc3
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lukingrecap Pro</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<style>
.btn-glossy { background: linear-gradient(to bottom, #d946ef, #9333ea); border-radius: 99px; padding: 8px 16px; font-weight: bold; transition: 0.2s; display: inline-block; }
.btn-glossy:active { transform: translateY(2px); }
.locked { opacity: 0.5; pointer-events: none; filter: grayscale(1); position: relative; }
.locked::after { content: '🔒 PREMIUM ONLY'; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: black; color: gold; padding: 5px; font-size: 10px; font-weight: bold; border-radius: 4px; white-space: nowrap; }
.tab-btn.active { color: #d946ef; border-bottom: 2px solid #d946ef; background: rgba(217,70,239,0.1); }
</style>
</head>
<body class="bg-gray-950 text-white min-h-screen font-sans flex flex-col">
<div id="loginModal" class="fixed inset-0 bg-black/95 z-50 flex items-center justify-center">
<div class="bg-gray-900 p-8 rounded-2xl border border-purple-500/30 text-center max-w-sm w-full relative">
<button onclick="showAdminLogin()" class="absolute top-2 right-2 text-gray-700 hover:text-gray-500 text-xs"><i class="fas fa-user-shield"></i></button>
<h1 class="text-3xl font-bold mb-2 bg-clip-text text-transparent bg-gradient-to-r from-pink-400 to-purple-500">LUKINGRECAP PRO</h1>
<p class="text-gray-400 text-sm mb-6">Enter your 8-digit access code</p>
<input type="text" id="accessCode" class="w-full bg-gray-800 border border-gray-700 rounded-lg p-3 text-center text-xl tracking-widest mb-4 focus:border-purple-500 outline-none" placeholder="12345678" maxlength="8">
<button onclick="doLogin()" class="btn-glossy w-full text-white">Login</button>
<p id="loginError" class="text-red-500 text-xs mt-3 hidden"></p>
</div>
</div>
<div id="adminModal" class="hidden fixed inset-0 bg-black/95 z-50 flex items-center justify-center">
<div class="bg-gray-900 p-6 rounded-xl border border-red-900 w-96">
<h2 class="text-red-500 font-bold mb-4">Admin Dashboard</h2>
<div id="adminAuth">
<input type="password" id="adminPass" class="w-full bg-gray-800 p-2 mb-2 rounded text-xs" placeholder="Admin Password">
<button onclick="doAdminLogin()" class="bg-red-700 w-full py-2 rounded text-xs font-bold">Access</button>
</div>
<div id="adminPanel" class="hidden space-y-4">
<div class="border-b border-gray-800 pb-2">
<h3 class="text-xs text-gray-400 mb-1">Generate New Code</h3>
<div class="flex gap-2">
<select id="genType" class="bg-gray-800 text-xs p-2 rounded"><option value="free">Free (100 Cr)</option><option value="premium">Premium (250 Cr)</option></select>
<button onclick="adminGen()" class="bg-blue-600 px-3 rounded text-xs">Create</button>
</div>
<div id="genCodeDisplay" class="mt-2 text-xl font-mono text-green-400 text-center tracking-widest bg-gray-800 p-2 rounded hidden"></div>
</div>
<div>
<h3 class="text-xs text-gray-400 mb-1">Add Credits</h3>
<input type="text" id="targetCode" class="w-full bg-gray-800 text-xs p-2 mb-1 rounded" placeholder="User 8-digit Code">
<div class="flex gap-2">
<input type="number" id="addAmount" class="w-20 bg-gray-800 text-xs p-2 rounded" placeholder="Amt">
<button onclick="adminAdd()" class="bg-green-600 flex-1 rounded text-xs">Add Credit</button>
</div>
</div>
<button onclick="location.reload()" class="text-gray-500 text-xs w-full mt-4">Close / Logout</button>
</div>
</div>
</div>
<div id="mainApp" class="hidden flex-1 flex flex-col max-w-4xl mx-auto w-full p-4">
<div class="bg-gray-900 rounded-xl p-3 mb-4 border border-gray-800 flex flex-wrap justify-between items-center gap-2">
<div class="flex items-center gap-3">
<div id="userBadge" class="px-2 py-1 rounded text-xs font-bold bg-gray-700 text-gray-300">GUEST</div>
<div class="text-xs text-gray-400">Credits: <span id="creditCount" class="text-white font-mono">0</span></div>
<div class="text-xs text-gray-400">Limit: <span id="dailyCount" class="text-white font-mono">0</span>/<span id="dailyMax">1</span></div>
</div>
<div class="flex items-center gap-3">
<div class="text-[10px] text-green-400"><span class="w-2 h-2 bg-green-500 rounded-full inline-block mr-1"></span>Active: <span id="activeUsers">1</span></div>
<button onclick="document.getElementById('settingsModal').classList.remove('hidden')" class="text-gray-400 hover:text-white"><i class="fas fa-cog"></i></button>
<button onclick="logout()" class="text-red-400 hover:text-red-300 text-xs uppercase font-bold">Exit</button>
</div>
</div>
<div class="flex bg-gray-900 rounded-lg p-1 mb-4 overflow-x-auto">
<button class="tab-btn flex-1 py-2 text-xs font-bold text-gray-400 uppercase active" onclick="switchTab('tab1', this)">1. Transcript</button>
<button class="tab-btn flex-1 py-2 text-xs font-bold text-gray-400 uppercase" onclick="switchTab('tab2', this)">2. Rewrite</button>
<button class="tab-btn flex-1 py-2 text-xs font-bold text-gray-400 uppercase" onclick="switchTab('tab3', this)">3. TTS</button>
<button class="tab-btn flex-1 py-2 text-xs font-bold text-gray-400 uppercase" onclick="switchTab('tab4', this)">4. Video</button>
<button class="tab-btn flex-1 py-2 text-xs font-bold text-gray-400 uppercase" onclick="switchTab('tab5', this)">5. Thumb</button>
<button class="tab-btn flex-1 py-2 text-xs font-bold text-gray-400 uppercase" onclick="switchTab('tab6', this)">6. SRT</button>
</div>
<div id="tab1" class="tab-content">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-gray-900 p-4 rounded-xl border border-gray-800">
<h3 class="text-sm font-bold text-purple-400 mb-2">1A: Video to MP3</h3>
<input type="file" id="videoToMp3File" class="w-full bg-gray-800 text-xs p-2 rounded mb-2" accept="video/*">
<button onclick="convertMp3()" class="btn-glossy w-full text-xs text-white">Convert</button>
<a id="mp3Download" class="hidden block text-center mt-2 text-xs text-green-400 underline">Download</a>
</div>
<div class="bg-gray-900 p-4 rounded-xl border border-gray-800">
<h3 class="text-sm font-bold text-pink-400 mb-2">1B: MP3 to Text</h3>
<input type="file" id="mp3ToTextFile" class="w-full bg-gray-800 text-xs p-2 rounded mb-2" accept="audio/*">
<button onclick="extractText()" class="btn-glossy w-full text-xs text-white">Extract</button>
<textarea id="rawText" class="w-full h-32 bg-gray-800 mt-2 text-xs p-2 rounded" placeholder="Result..."></textarea>
</div>
</div>
</div>
<div id="tab2" class="tab-content hidden"><div class="bg-gray-900 p-4 rounded-xl"><textarea id="rewriteInput" class="w-full h-32 bg-gray-800 text-xs p-2 rounded mb-2" placeholder="Paste Transcript..."></textarea><div class="flex gap-2"><input type="text" id="ownKeyGemini" class="flex-1 bg-gray-800 text-xs p-2 rounded border border-gray-700" placeholder="Optional: Own Gemini Key"><button onclick="doRewrite()" class="btn-glossy text-xs text-white px-6">Rewrite</button></div><textarea id="finalScript" class="w-full h-40 bg-gray-800 mt-2 text-xs p-2 rounded border border-purple-500/30" placeholder="Final Script..."></textarea></div></div>
<div id="tab3" class="tab-content hidden"><div class="bg-gray-900 p-4 rounded-xl"><div class="grid grid-cols-2 gap-2 mb-2"><select id="ttsModel" class="bg-gray-800 text-xs p-2 rounded"><option value="edge">Edge-TTS</option><option value="gemini">Gemini TTS (Own Key)</option></select><select id="ttsVoice" class="bg-gray-800 text-xs p-2 rounded"><option value="my-MM-ThihaNeural">Thiha</option><option value="my-MM-NilarNeural">Nilar</option></select></div><textarea id="ttsText" class="w-full h-32 bg-gray-800 text-xs p-2 rounded mb-2" placeholder="Script..."></textarea><button onclick="doTTS()" class="btn-glossy w-full text-xs text-white">Generate Audio</button><audio id="ttsAudio" controls class="w-full mt-3 h-8"></audio></div></div>
<div id="tab4" class="tab-content hidden"><div id="videoLock" class="bg-gray-900 p-4 rounded-xl"><div class="grid grid-cols-2 gap-2 mb-2"><input type="file" id="vidFile" class="bg-gray-800 text-xs p-1 rounded"><input type="file" id="audFile" class="bg-gray-800 text-xs p-1 rounded"></div><button onclick="doVideo()" class="btn-glossy w-full text-xs text-white">Render Video (20 Credits)</button><video id="finalVid" controls class="w-full mt-3 rounded border border-gray-700 max-h-60"></video></div></div>
<div id="tab5" class="tab-content hidden"><div id="thumbLock" class="bg-gray-900 p-4 rounded-xl text-center"><input type="file" id="thumbFile" class="bg-gray-800 text-xs p-2 rounded w-full mb-2" accept="image/*"><input type="text" id="thumbText" class="bg-gray-800 text-xs p-2 rounded w-full mb-2" placeholder="Title (Burmese)"><button onclick="doThumb()" class="btn-glossy text-xs text-white">Generate</button><img id="thumbResult" class="w-full mt-2 rounded shadow-lg"></div></div>
<div id="tab6" class="tab-content hidden"><div id="srtLock" class="bg-gray-900 p-4 rounded-xl text-center"><input type="file" id="srtFile" class="bg-gray-800 text-xs p-2 rounded w-full mb-2" accept=".srt"><button onclick="doSRT()" class="btn-glossy text-xs text-white">Translate SRT</button><textarea id="srtResult" class="w-full h-40 bg-gray-800 mt-2 text-xs p-2 rounded"></textarea></div></div>
</div>
<div id="settingsModal" class="hidden fixed inset-0 bg-black/80 z-50 flex items-center justify-center">
<div class="bg-gray-900 p-6 rounded-xl border border-gray-700 w-80">
<h3 class="font-bold text-white mb-4">Pricing Plans</h3>
<div class="space-y-2 text-xs text-gray-300">
<div class="p-2 border border-gray-700 rounded flex justify-between"><span>200 Credits</span><span class="text-green-400">3,000 Ks</span></div>
<div class="p-2 border border-gray-700 rounded flex justify-between"><span>300 Credits</span><span class="text-green-400">4,000 Ks</span></div>
</div>
<button onclick="document.getElementById('settingsModal').classList.add('hidden')" class="mt-4 w-full bg-gray-700 py-2 rounded text-xs">Close</button>
</div>
</div>
<script>
let USER = null; let ADMIN_KEY = null;
// AUTH & ADMIN
function showAdminLogin() { document.getElementById('loginModal').classList.add('hidden'); document.getElementById('adminModal').classList.remove('hidden'); }
async function doLogin() {
const code = document.getElementById('accessCode').value;
const res = await fetch('/api/login', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({code})});
const data = await res.json();
if(data.error) { document.getElementById('loginError').innerText=data.error; document.getElementById('loginError').classList.remove('hidden'); }
else { USER=data; localStorage.setItem('vfactory_code', code); initUI(); }
}
async function doAdminLogin() {
const pass = document.getElementById('adminPass').value;
const res = await fetch('/api/admin/login', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({password:pass})});
if(res.ok) { ADMIN_KEY=pass; document.getElementById('adminAuth').classList.add('hidden'); document.getElementById('adminPanel').classList.remove('hidden'); }
else alert("Wrong Password");
}
async function adminGen() {
const type = document.getElementById('genType').value;
const res = await fetch('/api/admin/generate', {method:'POST', headers:{'Content-Type':'application/json', 'X-Admin-Pass':ADMIN_KEY}, body:JSON.stringify({type})});
const data = await res.json();
const d = document.getElementById('genCodeDisplay'); d.innerText = data.code; d.classList.remove('hidden');
}
async function adminAdd() {
const code = document.getElementById('targetCode').value; const amt = document.getElementById('addAmount').value;
const res = await fetch('/api/admin/add_credit', {method:'POST', headers:{'Content-Type':'application/json', 'X-Admin-Pass':ADMIN_KEY}, body:JSON.stringify({code, amount:amt})});
const data = await res.json(); alert(data.status || data.error);
}
// UI & FUNCTIONS
function initUI() {
document.getElementById('loginModal').classList.add('hidden'); document.getElementById('mainApp').classList.remove('hidden');
document.getElementById('userBadge').innerText = USER.type.toUpperCase();
document.getElementById('userBadge').className = `px-2 py-1 rounded text-xs font-bold ${USER.type==='premium'?'bg-yellow-600 text-black':'bg-gray-700 text-gray-300'}`;
document.getElementById('creditCount').innerText = USER.credits; document.getElementById('dailyCount').innerText = USER.usage; document.getElementById('dailyMax').innerText = USER.type==='premium'?'5':'1';
if(USER.type==='free') { document.getElementById('videoLock').classList.add('locked'); document.getElementById('thumbLock').classList.add('locked'); document.getElementById('srtLock').classList.add('locked'); }
fetch('/api/stats').then(r=>r.json()).then(d=>document.getElementById('activeUsers').innerText=d.active_users);
}
function logout() { localStorage.removeItem('vfactory_code'); location.reload(); }
const savedCode = localStorage.getItem('vfactory_code'); if(savedCode) { document.getElementById('accessCode').value = savedCode; doLogin(); }
function switchTab(id, btn) { document.querySelectorAll('.tab-content').forEach(el=>el.classList.add('hidden')); document.getElementById(id).classList.remove('hidden'); document.querySelectorAll('.tab-btn').forEach(el=>el.classList.remove('active')); btn.classList.add('active'); }
// TOOL FUNCTIONS (Simplified calls)
async function convertMp3() { const f=document.getElementById('videoToMp3File').files[0]; const fd=new FormData(); fd.append('file',f); const r=await fetch('/api/convert_mp3',{method:'POST',headers:{'X-Access-Code':USER.code},body:fd}); const d=await r.json(); const l=document.getElementById('mp3Download'); l.href=d.audio_url; l.classList.remove('hidden'); }
async function extractText() { const f=document.getElementById('mp3ToTextFile').files[0]; const fd=new FormData(); fd.append('file',f); const r=await fetch('/api/transcribe_mp3',{method:'POST',headers:{'X-Access-Code':USER.code},body:fd}); const d=await r.json(); document.getElementById('rawText').value=d.transcript||d.error; }
async function doRewrite() { const t=document.getElementById('rewriteInput').value; const k=document.getElementById('ownKeyGemini').value; const r=await fetch('/api/rewrite',{method:'POST',headers:{'Content-Type':'application/json','X-Access-Code':USER.code},body:JSON.stringify({text:t,api_key:k})}); const d=await r.json(); document.getElementById('finalScript').value=d.script||d.error; }
async function doTTS() { const s=document.getElementById('ttsText').value; const k=document.getElementById('ttsModel').value==='gemini'?document.getElementById('ownKeyGemini').value:null; const r=await fetch('/api/tts',{method:'POST',headers:{'Content-Type':'application/json','X-Access-Code':USER.code},body:JSON.stringify({script:s,voice:document.getElementById('ttsVoice').value,speed:'+0%',pitch:'+0Hz',api_key:k})}); const d=await r.json(); document.getElementById('ttsAudio').src=d.audio_url; }
async function doVideo() { const v=document.getElementById('vidFile').files[0]; const a=document.getElementById('audFile').files[0]; const fd=new FormData(); fd.append('video',v); fd.append('audio',a); const r=await fetch('/api/process_video',{method:'POST',headers:{'X-Access-Code':USER.code},body:fd}); const d=await r.json(); document.getElementById('finalVid').src=d.video_url; }
async function doThumb() { const f=document.getElementById('thumbFile').files[0]; const t=document.getElementById('thumbText').value; const fd=new FormData(); fd.append('image',f); fd.append('title',t); const r=await fetch('/api/thumbnail',{method:'POST',headers:{'X-Access-Code':USER.code},body:fd}); const d=await r.json(); document.getElementById('thumbResult').src=d.url; }
async function doSRT() { const r=await fetch('/api/srt_translate',{method:'POST',headers:{'X-Access-Code':USER.code}}); const d=await r.json(); document.getElementById('srtResult').value=d.srt_content||d.error; }
</script>
</body>
</html>