Phoe2004 commited on
Commit
2706d2f
ยท
verified ยท
1 Parent(s): 5940ae4

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +277 -125
index.html CHANGED
@@ -2,158 +2,310 @@
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width,initial-scale=1.0">
6
- <title>Recap Studio โ€” Login</title>
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
  <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
 
9
  <style>
10
  :root{
11
- --bg:#f5f6fa;--bg2:#fff;
12
- --border:#e2e4ee;
13
  --text:#1a1d2e;--muted:#6b6f8a;--muted2:#9ba1bc;
14
- --primary:#5b4cf5;--purple:#8b5cf6;
 
 
 
15
  --F:'Plus Jakarta Sans',sans-serif;
16
  }
17
  *{box-sizing:border-box;margin:0;padding:0}
18
- body{background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);color:var(--text);font-family:var(--F);min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px}
19
- .lw{width:100%;max-width:420px}
20
- .lb{text-align:center;margin-bottom:28px}
21
- .lb .ico{width:64px;height:64px;background:#fff;border-radius:18px;display:inline-flex;align-items:center;justify-content:center;font-size:1.7rem;margin-bottom:12px;box-shadow:0 8px 24px rgba(0,0,0,.15)}
22
- .lb h1{font-size:1.8rem;font-weight:800;color:#fff}
23
- .lb p{color:rgba(255,255,255,.75);font-size:.85rem;margin-top:4px}
24
- .lc{background:#fff;border-radius:20px;padding:30px;box-shadow:0 24px 60px rgba(0,0,0,.2)}
25
- .tabs{display:flex;gap:3px;background:var(--bg);border-radius:9px;padding:3px;margin-bottom:22px}
26
- .tab{flex:1;padding:9px;border:none;background:transparent;color:var(--muted);font-family:var(--F);font-size:.83rem;font-weight:600;border-radius:7px;cursor:pointer;transition:.2s}
27
- .tab.on{background:#fff;color:var(--primary);box-shadow:0 2px 8px rgba(0,0,0,.08)}
28
- .fi{margin-bottom:14px}
29
- .fi label{display:block;font-size:.72rem;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--muted);margin-bottom:6px}
30
- .inp{width:100%;padding:11px 13px;background:var(--bg);border:1.5px solid var(--border);border-radius:9px;color:var(--text);font-family:var(--F);font-size:.88rem;outline:none;transition:.2s}
31
- .inp:focus{border-color:var(--primary);background:#fff;box-shadow:0 0 0 3px rgba(91,76,245,.1)}
32
- .bp{width:100%;padding:13px;background:linear-gradient(135deg,var(--primary),var(--purple));border:none;border-radius:10px;color:#fff;font-family:var(--F);font-size:.9rem;font-weight:700;cursor:pointer;transition:.2s;margin-top:4px;text-align:center}
33
- .bp:hover{transform:translateY(-1px);box-shadow:0 6px 20px rgba(91,76,245,.35)}
34
- .am{font-size:.79rem;padding:9px 12px;border-radius:8px;margin-top:10px;display:none}
35
- .am-ok{background:#d1fae5;border:1px solid #6ee7b7;color:#065f46}
36
- .am-err{background:#fee2e2;border:1px solid #fca5a5;color:#991b1b}
37
- .divider{display:flex;align-items:center;gap:8px;margin:14px 0}
38
- .divider hr{flex:1;border:none;border-top:1px solid #e2e8f0}
39
- .divider span{color:#94a3b8;font-size:.75rem}
40
- .gbtn{background:#fff;color:#374151;border:1.5px solid #e2e8f0;display:flex;align-items:center;justify-content:center;gap:8px;text-decoration:none;font-weight:600;width:100%;padding:13px;border-radius:10px;font-family:var(--F);font-size:.9rem;cursor:pointer;transition:.2s}
41
- .gbtn:hover{background:#f8fafc;border-color:#cbd5e1}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  </style>
43
  </head>
44
  <body>
45
- <div class="lw">
46
- <div class="lb">
47
- <div class="ico">๐ŸŽฌ</div>
48
- <h1>Recap Studio</h1>
49
- <p>AI-powered video recap generator</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  </div>
51
- <div class="lc">
52
- <div class="tabs">
53
- <button class="tab on" id="tab-li" onclick="stab('li')">Login</button>
54
- <button class="tab" id="tab-re" onclick="stab('re')">Register</button>
 
 
 
 
 
 
 
 
 
 
 
55
  </div>
56
 
57
- <!-- LOGIN PANE -->
58
- <div id="pane-li">
59
- <div class="fi"><label>Username</label><input class="inp" id="l-u" placeholder="your username" autocomplete="username"></div>
60
- <div class="fi"><label>Password</label><input class="inp" id="l-p" type="password" placeholder="โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข"></div>
61
- <button class="bp" onclick="doLogin()">Sign In โ†’</button>
62
- <div id="google-btn-wrap" style="display:none">
63
- <div class="divider"><hr><span>or</span><hr></div>
64
- <button onclick="googleLogin()" class="gbtn">
65
- <svg width="18" height="18" viewBox="0 0 48 48"><path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/><path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/><path fill="#FBBC05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"/><path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.18 1.48-4.97 2.29-8.16 2.29-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/></svg>
66
- Continue with Google
67
- </button>
 
68
  </div>
69
  </div>
70
 
71
- <!-- REGISTER PANE -->
72
- <div id="pane-re" style="display:none">
73
- <div class="fi"><label>Username <span style="color:var(--muted2);text-transform:none;letter-spacing:0;font-weight:400">(optional)</span></label><input class="inp" id="r-u" placeholder="leave blank for random"></div>
74
- <div class="fi"><label>Password</label><input class="inp" id="r-p" type="password" placeholder="โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข"></div>
75
- <button class="bp" onclick="doReg()">Create Account โ†’</button>
 
 
76
  </div>
77
 
78
- <div id="am" class="am"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  </div>
80
  </div>
81
 
82
- <script>
83
- function stab(t){
84
- ['li','re'].forEach(x=>{
85
- document.getElementById('tab-'+x).classList.toggle('on',x===t);
86
- document.getElementById('pane-'+x).style.display=x===t?'':'none';
87
- });
88
- document.getElementById('am').style.display='none';
89
- }
90
- function sam(msg,ok){
91
- const e=document.getElementById('am');
92
- e.textContent=msg;
93
- e.className='am '+(ok?'am-ok':'am-err');
94
- e.style.display='block';
95
- }
96
 
97
- async function doLogin(){
98
- const u=document.getElementById('l-u').value.trim(),p=document.getElementById('l-p').value;
99
- if(!u){sam('โŒ Username required',false);return;}
100
- try{
101
- const r=await fetch('/api/login',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({username:u,password:p})});
102
- const d=await r.json();
103
- if(d.ok){
104
- sessionStorage.setItem('recap_user',JSON.stringify({u:u,coins:d.coins,is_admin:d.is_admin,name:u}));
105
- window.location.href='/app';
106
- } else {
107
- sam(d.msg||'โŒ Login failed',false);
108
- }
109
- }catch{sam('โŒ Connection error',false);}
110
- }
111
-
112
- async function doReg(){
113
- const u=document.getElementById('r-u').value.trim(),p=document.getElementById('r-p').value;
114
- try{
115
- const r=await fetch('/api/register',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({username:u,password:p})});
116
- const d=await r.json();
117
- if(d.ok){
118
- sam('โœ… '+d.username+' created!',true);
119
- setTimeout(()=>{
120
- sessionStorage.setItem('recap_user',JSON.stringify({u:d.username,coins:d.coins,is_admin:false,name:d.username}));
121
- window.location.href='/app';
122
- },700);
123
- } else {
124
- sam(d.msg||'โŒ Failed',false);
125
- }
126
- }catch{sam('โŒ Connection error',false);}
127
- }
128
 
129
- function googleLogin(){
130
- const width=500,height=600;
131
- const left=(screen.width-width)/2;
132
- const top=(screen.height-height)/2;
133
- window.open('/auth/google','Google Login',`width=${width},height=${height},left=${left},top=${top}`);
134
- }
135
 
136
- document.addEventListener('DOMContentLoaded',()=>{
137
- fetch('/api/auth/google_enabled').then(r=>r.json()).then(d=>{
138
- if(d.enabled) document.getElementById('google-btn-wrap').style.display='block';
139
- }).catch(()=>{});
140
- const sp=new URLSearchParams(location.search);
141
- if(sp.get('auth')==='google'){
142
- const u=sp.get('u'),n=sp.get('n'),c=parseInt(sp.get('c')||'0'),a=sp.get('a')==='1';
143
- if(u){
144
- sessionStorage.setItem('recap_user',JSON.stringify({u:u,coins:c,is_admin:a,name:n}));
145
- window.location.href='/app';
146
- }
147
- } else if(sp.get('auth_error')){
148
- sam('โŒ Google login failed: '+sp.get('auth_error'),false);
149
- history.replaceState({},'','/');
150
- }
151
  const saved=sessionStorage.getItem('recap_user');
152
- if(saved){
153
- try{ JSON.parse(saved); window.location.href='/app'; }catch{}
154
- }
155
  });
156
- document.addEventListener('keydown',e=>{if(e.key==='Enter'){if(document.getElementById('pane-li').style.display!=='none') doLogin();else doReg();}});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  </script>
158
  </body>
159
  </html>
 
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
6
+ <title>Recap Studio โ€” Create Viral Videos</title>
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
  <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
9
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
10
  <style>
11
  :root{
12
+ --bg:#f5f6fa;--bg2:#fff;--bg3:#f0f1f7;--bg4:#e8eaf2;
13
+ --border:#e2e4ee;--border2:#cdd0e0;
14
  --text:#1a1d2e;--muted:#6b6f8a;--muted2:#9ba1bc;
15
+ --primary:#5b4cf5;--primary2:#4a3de0;
16
+ --orange:#ff6b35;--orange2:#e85a25;
17
+ --green:#10b981;--red:#ef4444;--blue:#3b82f6;--purple:#8b5cf6;
18
+ --gold:#f59e0b;
19
  --F:'Plus Jakarta Sans',sans-serif;
20
  }
21
  *{box-sizing:border-box;margin:0;padding:0}
22
+ body{background:var(--bg);color:var(--text);font-family:var(--F);min-height:100vh}
23
+ #app-screen{display:block}
24
+
25
+ .hdr{display:flex;align-items:center;justify-content:space-between;padding:0 16px;height:56px;background:#fff;border-bottom:1px solid var(--border);position:sticky;top:0;z-index:100;box-shadow:0 1px 8px rgba(0,0,0,.06)}
26
+ .brand{display:flex;align-items:center;gap:9px;font-weight:800;font-size:1rem;color:var(--text)}
27
+ .brand-i{width:30px;height:30px;background:linear-gradient(135deg,var(--primary),var(--purple));border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:.75rem}
28
+ .hr{display:flex;align-items:center;gap:7px}
29
+ .cpill{display:flex;align-items:center;gap:5px;padding:5px 12px;background:linear-gradient(135deg,#fff7ed,#fef3c7);border:1.5px solid #fcd34d;border-radius:20px;font-size:.78rem;font-weight:700;color:#92400e;cursor:pointer;transition:.2s}
30
+ .cpill:hover{box-shadow:0 2px 8px rgba(245,158,11,.2)}
31
+ .hb{padding:7px 13px;border-radius:8px;border:1.5px solid var(--border);background:#fff;color:var(--muted);font-family:var(--F);font-size:.78rem;font-weight:600;cursor:pointer;transition:.2s}
32
+ .hb:hover{border-color:var(--primary);color:var(--primary)}
33
+ .hb-p{background:linear-gradient(135deg,var(--orange),var(--orange2));border-color:transparent;color:#fff}
34
+ .hb-p:hover{box-shadow:0 4px 14px rgba(255,107,53,.3);transform:translateY(-1px)}
35
+ .mbtn{width:33px;height:33px;border:1.5px solid var(--border);background:#fff;border-radius:8px;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;transition:.2s}
36
+ .mbtn span{display:block;width:15px;height:1.5px;background:var(--muted);border-radius:2px}
37
+ .dov{display:none;position:fixed;inset:0;z-index:200}
38
+ .dov.on{display:block}
39
+ .dbg{position:absolute;inset:0;background:rgba(0,0,0,.4);backdrop-filter:blur(3px)}
40
+ .drw{position:absolute;top:0;right:0;width:280px;height:100%;background:#fff;box-shadow:-8px 0 32px rgba(0,0,0,.12);display:flex;flex-direction:column;animation:sli .2s ease}
41
+ @keyframes sli{from{transform:translateX(100%)}to{transform:translateX(0)}}
42
+ .drw-h{padding:18px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between}
43
+ .drw-u{display:flex;align-items:center;gap:11px}
44
+ .av{width:40px;height:40px;border-radius:12px;background:linear-gradient(135deg,var(--primary),var(--purple));display:flex;align-items:center;justify-content:center;font-weight:700;font-size:.95rem;color:#fff}
45
+ .dn{font-weight:700;font-size:.9rem}
46
+ .drl{font-size:.68rem;color:var(--muted);margin-top:1px}
47
+ .dxbtn{width:28px;height:28px;border:1.5px solid var(--border);background:#fff;color:var(--muted);border-radius:7px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:.75rem}
48
+ .drw-s{display:grid;grid-template-columns:1fr 1fr;gap:8px;padding:14px 18px;border-bottom:1px solid var(--border)}
49
+ .ds{background:var(--bg);border-radius:10px;padding:12px;text-align:center}
50
+ .dsv{font-size:1.3rem;font-weight:800;color:var(--primary)}
51
+ .dsl{font-size:.63rem;color:var(--muted);margin-top:2px}
52
+ .drw-n{flex:1;padding:8px}
53
+ .dni{display:flex;align-items:center;gap:9px;padding:10px 13px;border-radius:9px;cursor:pointer;border:none;background:transparent;width:100%;text-align:left;font-family:var(--F);font-size:.83rem;font-weight:500;color:var(--text)}
54
+ .dni:hover{background:var(--bg)}
55
+ .dni i{width:17px;color:var(--muted)}
56
+ .dni-r{color:var(--red)}.dni-r i{color:var(--red)}
57
+ .drw-f{padding:12px 18px;border-top:1px solid var(--border);font-size:.66rem;color:var(--muted2);text-align:center}
58
+ .ab{max-width:900px;margin:0 auto;padding:18px 14px 70px}
59
+ .card{background:#fff;border:1px solid var(--border);border-radius:16px;padding:16px;margin-bottom:12px;box-shadow:0 1px 6px rgba(0,0,0,.04)}
60
+ .ch{display:flex;align-items:center;gap:8px;margin-bottom:13px}
61
+ .ci{width:28px;height:28px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:.74rem}
62
+ .ci-p{background:linear-gradient(135deg,#ede9fe,#ddd6fe);color:var(--primary)}
63
+ .ci-o{background:linear-gradient(135deg,#fff7ed,#fed7aa);color:var(--orange)}
64
+ .ci-g{background:linear-gradient(135deg,#d1fae5,#a7f3d0);color:var(--green)}
65
+ .ct{font-weight:800;font-size:.88rem}
66
+ .uw{position:relative}
67
+ .uinp{width:100%;padding:11px 100px 11px 42px;background:var(--bg);border:1.5px solid var(--border);border-radius:10px;font-family:var(--F);font-size:.86rem;outline:none}
68
+ .uinp:focus{border-color:var(--primary);background:#fff}
69
+ .uico{position:absolute;left:13px;top:50%;transform:translateY(-50%);color:var(--muted)}
70
+ .paste-btn{position:absolute;right:10px;top:50%;transform:translateY(-50%);padding:5px 11px;background:var(--primary);border:none;border-radius:7px;color:#fff;font-size:.72rem;font-weight:700;cursor:pointer}
71
+ .orsep{text-align:center;color:var(--muted2);font-size:.73rem;margin:10px 0;position:relative}
72
+ .orsep::before,.orsep::after{content:'';position:absolute;top:50%;width:44%;height:1px;background:var(--border)}
73
+ .orsep::before{left:0}.orsep::after{right:0}
74
+ .upz{border:2px dashed var(--border2);border-radius:10px;padding:14px;text-align:center;cursor:pointer;position:relative}
75
+ .upz:hover{border-color:var(--primary);background:#faf9ff}
76
+ .upz input{position:absolute;inset:0;opacity:0;cursor:pointer}
77
+ .upz-t{font-size:.78rem;color:var(--muted)}
78
+ .upz-n{font-size:.76rem;color:var(--green);margin-top:3px;display:none}
79
+ .sgrid{display:grid;grid-template-columns:1fr 1fr;gap:9px}
80
+ @media(max-width:460px){.sgrid{grid-template-columns:1fr}}
81
+ .si{background:var(--bg);border:1.5px solid var(--border);border-radius:10px;padding:11px 13px}
82
+ .sil{font-size:.65rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);margin-bottom:6px}
83
+ .ssel{width:100%;background:transparent;border:none;color:var(--text);font-family:var(--F);font-size:.84rem;font-weight:500;outline:none;cursor:pointer}
84
+ .vrow{display:flex;align-items:center;gap:7px}
85
+ .vrow .ssel{flex:1;background:var(--bg);border:1.5px solid var(--border);border-radius:9px;padding:9px 11px}
86
+ .pvbtn{width:34px;height:34px;background:#fff;border:1.5px solid var(--border);border-radius:9px;color:var(--muted);cursor:pointer}
87
+ .spd-row{display:flex;align-items:center;gap:9px;margin-top:9px}
88
+ .spd-row label{font-size:.7rem;font-weight:600;color:var(--muted);width:52px}
89
+ .spd-sl{flex:1;-webkit-appearance:none;height:4px;border-radius:2px;background:var(--bg4)}
90
+ .spd-sl::-webkit-slider-thumb{-webkit-appearance:none;width:14px;height:14px;border-radius:50%;background:var(--primary);cursor:pointer}
91
+ .spd-v{font-size:.73rem;color:var(--primary);font-weight:700;width:36px;text-align:right}
92
+ .egrid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px}
93
+ .icon-toggle{display:flex;align-items:center;justify-content:center;gap:5px;padding:8px 10px;background:var(--bg);border:1.5px solid var(--border);border-radius:8px;cursor:pointer;font-size:.79rem;font-weight:600;color:var(--muted);width:100%}
94
+ .icon-toggle.on{background:linear-gradient(135deg,#ede9fe,#e0e7ff);border-color:var(--primary);color:var(--primary)}
95
+ .btn-auto{width:100%;padding:15px;background:linear-gradient(135deg,var(--primary),var(--purple));border:none;border-radius:12px;color:#fff;font-family:var(--F);font-size:.94rem;font-weight:700;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:8px;box-shadow:0 4px 18px rgba(91,76,245,.3)}
96
+ .btn-auto:disabled{opacity:.5;cursor:not-allowed}
97
+ .pc{background:#fff;border:1px solid var(--border);border-radius:16px;padding:18px;margin-top:12px;display:none}
98
+ .ph{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}
99
+ .ptit{font-weight:800;font-size:.86rem}
100
+ .ptmr{font-size:.73rem;color:var(--primary);font-weight:700;background:#ede9fe;padding:3px 9px;border-radius:20px}
101
+ .pb-bg{height:6px;background:var(--bg4);border-radius:3px;overflow:hidden;margin-bottom:9px}
102
+ .pb{height:100%;background:linear-gradient(90deg,var(--primary),var(--orange));border-radius:3px;width:0%}
103
+ .pmsg{font-size:.8rem;color:var(--muted);font-weight:500}
104
+ .psteps{display:flex;gap:5px;margin-top:11px;flex-wrap:wrap}
105
+ .ps{font-size:.66rem;font-weight:600;padding:4px 10px;border-radius:20px;background:var(--bg);color:var(--muted2);border:1px solid var(--border)}
106
+ .ps.active{background:#ede9fe;color:var(--primary);border-color:#c4b5fd}
107
+ .ps.done{background:#d1fae5;color:#065f46;border-color:#6ee7b7}
108
+ .rc{background:#fff;border:1px solid var(--border);border-radius:16px;overflow:hidden;margin-top:12px;display:none}
109
+ .rvw{background:#000}
110
+ .rvw video{width:100%;display:block;max-height:380px;object-fit:contain}
111
+ .rb{padding:15px}
112
+ .rtm{font-size:.7rem;color:var(--muted);margin-bottom:8px;display:flex;align-items:center;gap:4px}
113
+ .rtit{font-weight:800;font-size:.94rem;margin-bottom:5px}
114
+ .rtag{font-size:.73rem;color:var(--primary);margin-bottom:12px}
115
+ .ract{display:grid;grid-template-columns:1fr 1fr;gap:8px}
116
+ .rb-btn{padding:11px;border-radius:9px;border:none;font-family:var(--F);font-size:.81rem;font-weight:700;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:6px}
117
+ .rb-g{background:#d1fae5;color:#065f46}
118
+ .rb-b{background:#dbeafe;color:#1d4ed8}
119
+ .admp{background:#fff;border:2px solid #fef3c7;border-radius:16px;padding:16px;margin-top:12px;display:none}
120
+ .admbadge{font-size:.63rem;font-weight:700;padding:3px 8px;background:#fef3c7;color:#92400e;border-radius:20px;margin-left:8px}
121
+ .admg{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin-bottom:11px}
122
+ .ai-row{display:flex;gap:5px}
123
+ .ai{flex:1;padding:8px 10px;background:var(--bg);border:1.5px solid var(--border);border-radius:8px;font-family:var(--F);font-size:.8rem;outline:none}
124
+ .abtn{padding:8px 12px;border-radius:8px;border:none;font-size:.76rem;font-weight:700;cursor:pointer}
125
+ .ab-g{background:#ede9fe;color:var(--primary)}
126
+ .ab-r{background:#fee2e2;color:#dc2626}
127
+ .ut{width:100%;border-collapse:collapse;font-size:.76rem;margin-top:10px}
128
+ .ut th{padding:7px 9px;text-align:left;font-size:.63rem;font-weight:700;color:var(--muted);border-bottom:2px solid var(--border)}
129
+ .ut td{padding:7px 9px;border-bottom:1px solid var(--bg4)}
130
+ #toast{position:fixed;bottom:22px;left:50%;transform:translateX(-50%);background:var(--text);border-radius:10px;padding:9px 16px;font-size:.8rem;color:#fff;z-index:9999;opacity:0;transition:.3s;pointer-events:none;white-space:nowrap}
131
+ #toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
132
+ @keyframes spin{to{transform:rotate(360deg)}}
133
+ .sp{display:inline-block;animation:spin .7s linear infinite}
134
+ .modal-ov{display:none;position:fixed;inset:0;z-index:300;align-items:flex-end;justify-content:center}
135
+ .modal-ov.on{display:flex}
136
+ .modal-bg{position:absolute;inset:0;background:rgba(0,0,0,.45);backdrop-filter:blur(3px)}
137
+ .modal-sheet{position:relative;width:100%;max-width:480px;background:#fff;border-radius:20px 20px 0 0;padding:24px 20px 36px;animation:slideUp .25s ease}
138
+ @keyframes slideUp{from{transform:translateY(100%)}to{transform:translateY(0)}}
139
+ .modal-sheet h3{font-size:1.1rem;font-weight:800;margin-bottom:4px}
140
+ .modal-sheet p{font-size:.8rem;color:var(--muted);margin-bottom:18px}
141
+ .pkg-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:16px}
142
+ .pkg-card{background:var(--bg);border:1.5px solid var(--border);border-radius:12px;padding:14px 12px;text-align:center;cursor:pointer;transition:.2s;position:relative}
143
+ .pkg-card:hover{border-color:var(--primary);background:#faf9ff}
144
+ .pkg-card.popular{border-color:var(--primary);background:#faf9ff}
145
+ .pkg-pop-badge{position:absolute;top:-9px;left:50%;transform:translateX(-50%);background:var(--primary);color:#fff;font-size:.6rem;font-weight:700;padding:2px 8px;border-radius:10px}
146
+ .pkg-emoji{font-size:1.5rem;margin-bottom:5px}
147
+ .pkg-name{font-size:.75rem;font-weight:700;margin-bottom:3px}
148
+ .pkg-coins{font-size:1.2rem;font-weight:800;color:var(--primary)}
149
+ .pkg-coins span{font-size:.7rem;font-weight:500;color:var(--muted)}
150
+ .pkg-contact{background:var(--bg);border:1.5px solid var(--border);border-radius:10px;padding:12px;text-align:center;font-size:.8rem}
151
+ .pkg-contact a{color:var(--primary);font-weight:600;text-decoration:none}
152
+ .modal-close{position:absolute;top:14px;right:16px;width:28px;height:28px;border:1.5px solid var(--border);background:#fff;border-radius:7px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:.75rem}
153
  </style>
154
  </head>
155
  <body>
156
+ <div id="app-screen">
157
+ <header class="hdr">
158
+ <div class="brand"><div class="brand-i">๐ŸŽฌ</div>Recap Studio</div>
159
+ <div class="hr">
160
+ <div class="cpill" id="cpill" onclick="openPkg()">๐Ÿช™ <span id="hcoins">0</span> coins</div>
161
+ <button class="hb hb-p" onclick="window.location.href='/payment'">Buy Coins</button>
162
+ <button class="mbtn" onclick="odw()"><span></span><span></span><span></span></button>
163
+ </div>
164
+ </header>
165
+
166
+ <div class="dov" id="dov">
167
+ <div class="dbg" onclick="cdw()"></div>
168
+ <div class="drw">
169
+ <div class="drw-h">
170
+ <div class="drw-u"><div class="av" id="dav">U</div><div><div class="dn" id="dun">โ€”</div><div class="drl" id="drl">Member</div></div></div>
171
+ <button class="dxbtn" onclick="cdw()"><i class="fas fa-times"></i></button>
172
+ </div>
173
+ <div class="drw-s">
174
+ <div class="ds"><div class="dsv" id="dcoins">0</div><div class="dsl">Coins</div></div>
175
+ <div class="ds"><div class="dsv" id="dvids">0</div><div class="dsl">Videos</div></div>
176
+ </div>
177
+ <div class="drw-n">
178
+ <button class="dni" onclick="cdw()"><i class="fas fa-home"></i>Home</button>
179
+ <button class="dni" onclick="window.location.href='/payment-history';cdw()"><i class="fas fa-history"></i>Payment History</button>
180
+ <button class="dni" id="adm-dlink" style="display:none" onclick="scrollToAdmin();cdw()"><i class="fas fa-crown"></i>Admin Panel</button>
181
+ <button class="dni dni-r" onclick="doLogout()"><i class="fas fa-sign-out-alt"></i>Logout</button>
182
+ </div>
183
+ <div class="drw-f">recap.psonline.shop</div>
184
+ </div>
185
  </div>
186
+
187
+ <div class="ab">
188
+ <div class="card">
189
+ <div class="ch"><div class="ci ci-p"><i class="fas fa-video"></i></div><div class="ct">Video Source</div></div>
190
+ <div class="uw">
191
+ <i class="fas fa-link uico"></i>
192
+ <input class="uinp" id="vurl" placeholder="Paste YouTube / TikTok / Facebook / Instagram linkโ€ฆ" oninput="onUrl(this.value)">
193
+ <button class="paste-btn" onclick="doPaste()"><i class="fas fa-paste"></i> Paste</button>
194
+ </div>
195
+ <div class="orsep">or upload file</div>
196
+ <div class="upz">
197
+ <input type="file" id="vfile" accept="video/*" onchange="onFile(this)">
198
+ <div class="upz-t"><i class="fas fa-cloud-upload-alt"></i> Click or drag video file here</div>
199
+ <div class="upz-n" id="upzn"></div>
200
+ </div>
201
  </div>
202
 
203
+ <div class="card">
204
+ <div class="ch"><div class="ci ci-o"><i class="fas fa-sliders-h"></i></div><div class="ct">Settings</div></div>
205
+ <div class="sgrid">
206
+ <div class="si"><div class="sil">Language</div><select class="ssel" id="vlang" onchange="onLang()"><option value="my">๐Ÿ‡ฒ๐Ÿ‡ฒ Myanmar</option><option value="th">๐Ÿ‡น๐Ÿ‡ญ Thai</option><option value="en">๐Ÿ‡ฌ๐Ÿ‡ง English</option></select></div>
207
+ <div class="si"><div class="sil">Content Type</div><select class="ssel" id="ctype"><option value="Movie Recap">๐ŸŽฌ Movie Recap</option><option value="Medical/Health">๐Ÿ’Š Medical</option></select></div>
208
+ <div class="si"><div class="sil">AI Model</div><select class="ssel" id="aimodel"><option value="Gemini">โœจ Gemini</option><option value="DeepSeek">๐Ÿค– DeepSeek</option></select></div>
209
+ <div class="si"><div class="sil">TTS Engine</div><select class="ssel" id="engine" onchange="onEng()"><option value="ms">๐ŸŽ™๏ธ Edge TTS</option><option value="gemini">๐Ÿ”ฎ Gemini TTS</option></select></div>
210
+ </div>
211
+ <div style="margin-top:10px">
212
+ <div class="sil" style="margin-bottom:6px">Voice</div>
213
+ <div class="vrow"><select class="ssel" id="vsel"></select><button class="pvbtn" id="pvbtn" onclick="doPrevVoice()"><i class="fas fa-play"></i></button></div>
214
+ <div class="spd-row"><label>Speed</label><input type="range" class="spd-sl" id="spd" min="-50" max="100" value="30" oninput="document.getElementById('spdv').textContent=this.value+'%'"><span class="spd-v" id="spdv">30%</span></div>
215
  </div>
216
  </div>
217
 
218
+ <div class="card">
219
+ <div class="ch"><div class="ci ci-g"><i class="fas fa-film"></i></div><div class="ct">Video Options</div></div>
220
+ <div class="egrid">
221
+ <div class="si"><div class="sil">Aspect Ratio</div><select class="ssel" id="crop"><option value="9:16">9:16 TikTok</option><option value="16:9">16:9 YouTube</option><option value="1:1">1:1 Square</option></select></div>
222
+ <div class="si"><div class="sil">Flip</div><button class="icon-toggle" id="btn-flip" onclick="toggleOpt('flip')"><i class="fas fa-arrows-alt-h"></i> Off</button></div>
223
+ <div class="si"><div class="sil">Color Boost</div><button class="icon-toggle" id="btn-col" onclick="toggleOpt('col')"><i class="fas fa-adjust"></i> Off</button></div>
224
+ </div>
225
  </div>
226
 
227
+ <button class="btn-auto" id="btnauto" onclick="doAuto()"><i class="fas fa-magic"></i> Auto Process <span style="opacity:.7;font-size:.78em">2 Coins</span></button>
228
+
229
+ <div class="pc" id="pc">
230
+ <div class="ph"><span class="ptit">โš™๏ธ Processingโ€ฆ</span><span class="ptmr" id="ptmr">โฑ 0s</span></div>
231
+ <div class="pb-bg"><div class="pb" id="pb"></div></div>
232
+ <div class="pmsg" id="pmsg">Startingโ€ฆ</div>
233
+ <div class="psteps"><span class="ps" id="ps-dl">๐Ÿ“ฅ Download</span><span class="ps" id="ps-tr">๐ŸŽ™๏ธ Transcribe</span><span class="ps" id="ps-ai">๐Ÿค– AI Script</span><span class="ps" id="ps-ts">๐Ÿ”Š TTS</span><span class="ps" id="ps-vd">๐ŸŽฌ Render</span></div>
234
+ </div>
235
+
236
+ <div class="rc" id="rc">
237
+ <div class="rvw"><video id="rvid" controls playsinline></video></div>
238
+ <div class="rb"><div class="rtm" id="rtm" style="display:none"><i class="fas fa-clock"></i><span></span></div><div class="rtit" id="rtit" style="display:none"></div><div class="rtag" id="rtag" style="display:none"></div><div class="ract"><button class="rb-btn rb-g" onclick="dlVideo()"><i class="fas fa-download"></i> Download MP4</button><button class="rb-btn rb-b" onclick="copyCap()"><i class="fas fa-copy"></i> Copy Caption</button></div></div>
239
+ </div>
240
+
241
+ <div class="admp" id="admp"><div class="ch"><div class="ci ci-p"><i class="fas fa-crown"></i></div><div class="ct">Admin Panel</div><span class="admbadge">Admin</span></div>
242
+ <div class="admg"><div><div class="sil">Add Coins</div><div class="ai-row"><input class="ai" id="au-add" placeholder="username"><input class="ai" id="an-add" type="number" placeholder="n" style="width:60px"><button class="abtn ab-g" onclick="admCoins('add')">Add</button></div></div>
243
+ <div><div class="sil">Set Coins</div><div class="ai-row"><input class="ai" id="au-set" placeholder="username"><input class="ai" id="an-set" type="number" placeholder="n" style="width:60px"><button class="abtn ab-g" onclick="admCoins('set')">Set</button></div></div>
244
+ <div><div class="sil">Create User</div><div class="ai-row"><input class="ai" id="au-new" placeholder="name (blank=auto)"><button class="abtn ab-g" onclick="admCreate()">Create</button></div><div id="au-res" style="font-size:.71rem;color:var(--green);margin-top:3px"></div></div>
245
+ <div><div class="sil">Delete User</div><div class="ai-row"><input class="ai" id="au-del" placeholder="username"><button class="abtn ab-r" onclick="admDel()">Delete</button></div></div></div>
246
+ <div id="admmsg" class="admmsg"></div>
247
+ <button style="padding:8px 14px;background:var(--bg);border:1.5px solid var(--border);border-radius:8px;font-size:.75rem;cursor:pointer;margin-top:4px" onclick="loadUsers()"><i class="fas fa-users"></i> Load Users</button>
248
+ <div id="utw" style="overflow-x:auto"></div>
249
+ </div>
250
  </div>
251
  </div>
252
 
253
+ <div class="modal-ov" id="pkg-modal"><div class="modal-bg" onclick="closePkg()"></div><div class="modal-sheet"><button class="modal-close" onclick="closePkg()"><i class="fas fa-times"></i></button><h3>๐Ÿ’ฐ Buy Coins</h3><p>Package แ€›แ€ฝแ€ฑแ€ธแ€•แ€ผแ€ฎแ€ธ Admin แ€€แ€ญแ€ฏ Telegram แ€™แ€พ แ€†แ€€แ€บแ€žแ€ฝแ€šแ€บแ€•แ€ซ</p><div class="pkg-grid"><div class="pkg-card" onclick="contactPkg(10,'10,000 MMK')"><div class="pkg-emoji">๐Ÿฅ‰</div><div class="pkg-name">Starter</div><div class="pkg-coins">10 <span>coins</span></div></div><div class="pkg-card popular" onclick="contactPkg(30,'28,000 MMK')"><div class="pkg-pop-badge">โญ Popular</div><div class="pkg-emoji">๐Ÿฅˆ</div><div class="pkg-name">Basic</div><div class="pkg-coins">30 <span>coins</span></div></div><div class="pkg-card" onclick="contactPkg(60,'58,000 MMK')"><div class="pkg-emoji">๐Ÿฅ‡</div><div class="pkg-name">Pro</div><div class="pkg-coins">60 <span>coins</span></div></div><div class="pkg-card" onclick="contactPkg(100,'95,000 MMK')"><div class="pkg-emoji">๐Ÿ’Ž</div><div class="pkg-name">Unlimited</div><div class="pkg-coins">100 <span>coins</span></div></div></div><div class="pkg-contact">Account: <b id="pkg-uname">โ€”</b> ยท <a href="https://t.me/PhoeShan2001" target="_blank">๐Ÿ“ฉ @PhoeShan2001 แ€€แ€ญแ€ฏ Message แ€•แ€ญแ€ฏแ€ทแ€›แ€”แ€บ</a></div></div></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
254
 
255
+ <div id="toast"></div>
256
+ <audio id="pva"></audio>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
257
 
258
+ <script>
259
+ let U='',COINS=0,ISADM=false,OUT_URL='',OUT_CAP='',OUT_TAGS='',SSE=null,TICK=null,T0=0,FLIP_ON=false,COL_ON=false;
260
+ const EV={my:[{id:'my-MM-ThihaNeural',name:'Thiha (Male)'},{id:'my-MM-NilarNeural',name:'Nilar (Female)'}],th:[{id:'th-TH-NiwatNeural',name:'Niwat'},{id:'th-TH-PremwadeeNeural',name:'Premwadee'}],en:[{id:'en-US-GuyNeural',name:'Guy'},{id:'en-US-JennyNeural',name:'Jenny'}]};
261
+ let GV=[];
 
 
262
 
263
+ addEventListener('DOMContentLoaded',()=>{
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  const saved=sessionStorage.getItem('recap_user');
265
+ if(!saved){window.location.href='/login';return;}
266
+ try{const sess=JSON.parse(saved);eApp(sess.u,sess.coins,sess.is_admin,sess.name);}catch{window.location.href='/login';}
267
+ onLang();fetch('/api/gemini_voices').then(r=>r.ok?r.json():null).then(d=>{if(d?.ok){GV=d.voices;onEng();}}).catch(()=>{});
268
  });
269
+
270
+ function eApp(u,coins,isAdm,displayName){U=u;COINS=coins;ISADM=isAdm;updC(coins);const dn=displayName||u;document.getElementById('dun').textContent=dn;document.getElementById('drl').textContent=isAdm?'๐Ÿ‘‘ Admin':'Member';document.getElementById('dav').textContent=dn[0].toUpperCase();if(isAdm){document.getElementById('admp').style.display='block';document.getElementById('adm-dlink').style.display='flex';document.getElementById('cpill').style.display='none';document.getElementById('dcoins').textContent='โˆž';}}
271
+ function doLogout(){U='';COINS=0;ISADM=false;sessionStorage.removeItem('recap_user');window.location.href='/login';}
272
+ function odw(){document.getElementById('dcoins').textContent=ISADM?'โˆž':COINS;document.getElementById('dov').classList.add('on');}
273
+ function cdw(){document.getElementById('dov').classList.remove('on');}
274
+ function scrollToAdmin(){document.getElementById('admp').scrollIntoView({behavior:'smooth'});}
275
+ function updC(n){if(n===-1||ISADM)return;COINS=n;document.getElementById('hcoins').textContent=n;}
276
+ function onLang(){renderV();}
277
+ function onEng(){renderV();const g=document.getElementById('engine').value==='gemini';document.getElementById('pvbtn').style.display=g?'none':'flex';}
278
+ function renderV(){const lang=document.getElementById('vlang').value,eng=document.getElementById('engine').value;const sel=document.getElementById('vsel');sel.innerHTML='';(eng==='gemini'?GV:(EV[lang]||EV.my)).forEach(v=>{const o=document.createElement('option');o.value=v.id;o.textContent=v.name||v.id;sel.appendChild(o);});}
279
+ async function doPrevVoice(){const voice=document.getElementById('vsel').value,speed=parseInt(document.getElementById('spd').value);const btn=document.getElementById('pvbtn');btn.innerHTML='<i class="fas fa-spinner sp"></i>';try{const r=await fetch('/api/preview_voice',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({voice,speed,engine:'ms'})});const d=await r.json();if(d.ok){const a=document.getElementById('pva');a.src=d.url;a.play();}else toast('โŒ Preview failed');}catch{toast('โŒ Preview failed');}btn.innerHTML='<i class="fas fa-play"></i>';}
280
+ function toggleOpt(k){if(k==='flip'){FLIP_ON=!FLIP_ON;const b=document.getElementById('btn-flip');b.className='icon-toggle'+(FLIP_ON?' on':'');b.innerHTML='<i class="fas fa-arrows-alt-h"></i> '+(FLIP_ON?'On':'Off');}else{COL_ON=!COL_ON;const b=document.getElementById('btn-col');b.className='icon-toggle'+(COL_ON?' on':'');b.innerHTML='<i class="fas fa-adjust"></i> '+(COL_ON?'On':'Off');}}
281
+ function onUrl(v){if(URL_DEBOUNCE)clearTimeout(URL_DEBOUNCE);if(v.startsWith('http')){URL_DEBOUNCE=setTimeout(()=>autoPreview(v),1200);}}
282
+ let URL_DEBOUNCE=null;
283
+ async function autoPreview(url){try{const r=await fetch('/api/thumb',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({url})});const d=await r.json();if(d.ok&&d.width>0){const sel=document.getElementById('crop');const ratio=d.width/d.height;if(ratio>0.9&&ratio<1.1)sel.value='1:1';else if(ratio<0.7)sel.value='9:16';else if(ratio>1.4)sel.value='16:9';else sel.value='9:16';}}catch(e){console.log(e)}}
284
+ function onFile(inp){const f=inp.files[0];if(!f)return;document.getElementById('upzn').textContent=f.name;document.getElementById('upzn').style.display='block';document.getElementById('vurl').value='';}
285
+ function hasVid(){return document.getElementById('vurl').value.trim()||(document.getElementById('vfile').files||[]).length>0;}
286
+ function bfd(){const fd=new FormData();fd.append('username',U);fd.append('video_url',document.getElementById('vurl').value.trim());fd.append('voice',document.getElementById('vsel').value);fd.append('engine',document.getElementById('engine').value);fd.append('speed',document.getElementById('spd').value);fd.append('watermark','');fd.append('crop',document.getElementById('crop').value);fd.append('flip',FLIP_ON?'1':'0');fd.append('color',COL_ON?'1':'0');fd.append('content_type',document.getElementById('ctype').value);fd.append('ai_model',document.getElementById('aimodel').value);fd.append('vo_lang',document.getElementById('vlang').value);const vf=document.getElementById('vfile');if(vf&&vf.files[0])fd.append('video_file',vf.files[0]);return fd;}
287
+ function fmt(s){const m=Math.floor(s/60);return m>0?m+'m '+(s%60)+'s':s+'s';}
288
+ function showProg(on){document.getElementById('pc').style.display=on?'block':'none';if(on){document.getElementById('pb').style.width='0%';document.getElementById('pmsg').textContent='โณ Processing...';document.getElementById('ptmr').textContent='โฑ 0s';['ps-dl','ps-tr','ps-ai','ps-ts','ps-vd'].forEach(id=>document.getElementById(id).className='ps');}}
289
+ function updSteps(pct){const steps={8:'ps-dl',20:'ps-tr',45:'ps-ai',65:'ps-ts',78:'ps-vd'};let hit=false;Object.entries(steps).forEach(([p,id])=>{const el=document.getElementById(id);if(pct>=parseInt(p)){el.className='ps done';}else if(!hit){el.className='ps active';hit=true;}else el.className='ps';});}
290
+ function startTick(){T0=Date.now();if(TICK)clearInterval(TICK);TICK=setInterval(()=>{document.getElementById('ptmr').textContent='โฑ '+fmt(Math.floor((Date.now()-T0)/1000));},1000);}
291
+ function stopTick(){if(TICK){clearInterval(TICK);TICK=null;}return Math.floor((Date.now()-T0)/1000);}
292
+ function _resetBtn(){const b=document.getElementById('btnauto');b.disabled=false;b.innerHTML='<i class="fas fa-magic"></i> Auto Process <span style="opacity:.7;font-size:.78em">2 Coins</span>';}
293
+ function startSSE(tid){if(SSE)SSE.close();SSE=new EventSource('/api/progress/'+tid);SSE.onmessage=e=>{try{const p=JSON.parse(e.data);document.getElementById('pb').style.width=(p.pct||0)+'%';document.getElementById('pmsg').textContent=p.msg||'';updSteps(p.pct||0);if(p.done){SSE.close();SSE=null;const el=stopTick();_resetBtn();if(p.output_url){updC(p.coins!=null?p.coins:0);showRes(p.output_url,p.title,p.hashtags,p.caption,el);}else{showProg(false);toast('โŒ output not found');}}else if(p.error){SSE.close();SSE=null;stopTick();showProg(false);_resetBtn();toast(p.msg||'โŒ Process failed');}}catch(ex){}};}
294
+ async function doAuto(){if(!hasVid()){toast('โŒ Select video or URL');return;}const btn=document.getElementById('btnauto');btn.disabled=true;btn.innerHTML='<i class="fas fa-spinner sp"></i> Processingโ€ฆ';showProg(true);startTick();const fd=bfd();const tid=uid8();fd.append('tid',tid);try{const ctrl=new AbortController(),tout=setTimeout(()=>ctrl.abort(),30000);const r=await fetch('/api/process_all',{method:'POST',body:fd,signal:ctrl.signal});clearTimeout(tout);if(!r.ok){stopTick();showProg(false);_resetBtn();toast('โŒ Server error');return;}const d=await r.json();if(!d.ok){stopTick();showProg(false);_resetBtn();toast(d.msg||'โŒ Process failed');return;}startSSE(tid);}catch(e){stopTick();showProg(false);_resetBtn();toast(e.name==='AbortError'?'โŒ Timeout':'โŒ '+e);}}
295
+ function showRes(url,title,tags,cap,el){showProg(false);OUT_URL=url;OUT_CAP=cap||title;OUT_TAGS=tags;const rc=document.getElementById('rc');const rvid=document.getElementById('rvid');rc.style.display='block';if(title){document.getElementById('rtit').textContent=title;document.getElementById('rtit').style.display='block';}if(tags){document.getElementById('rtag').textContent=tags;document.getElementById('rtag').style.display='block';}if(el>0){const rt=document.getElementById('rtm');rt.querySelector('span').textContent='Process time: '+fmt(el);rt.style.display='flex';}rc.scrollIntoView({behavior:'smooth'});let tries=0;const poll=setInterval(async()=>{tries++;try{const r=await fetch(url,{method:'HEAD'});if(r.ok){clearInterval(poll);rvid.src=url;toast('โœ… Video ready!');return;}}catch(e){}if(tries>=20){clearInterval(poll);rvid.src=url;toast('โœ… Done');}},1000);}
296
+ function dlVideo(){if(!OUT_URL)return;const a=document.createElement('a');a.href=OUT_URL;a.download='recap.mp4';a.click();}
297
+ function copyCap(){const t=[OUT_CAP,OUT_TAGS].filter(Boolean).join('\n\n');if(!t){toast('Nothing to copy');return;}navigator.clipboard.writeText(t).then(()=>toast('โœ… Copied!')).catch(()=>toast('โŒ Copy failed'));}
298
+ async function admCoins(act){const u=document.getElementById('au-'+act).value.trim(),n=parseInt(document.getElementById('an-'+act).value)||0;if(!u){toast('โŒ Enter username');return;}const r=await fetch('/api/admin/coins',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({caller:U,username:u,amount:n,action:act})});const d=await r.json();document.getElementById('admmsg').textContent=d.msg||'';}
299
+ async function admCreate(){const u=document.getElementById('au-new').value.trim();const r=await fetch('/api/admin/create_user',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({caller:U,username:u,coins:0})});const d=await r.json();document.getElementById('au-res').textContent=d.msg+(d.username?' โ†’ '+d.username:'');}
300
+ async function admDel(){const u=document.getElementById('au-del').value.trim();if(!u||!confirm('Delete '+u+'?'))return;const r=await fetch('/api/admin/delete_user',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({caller:U,username:u})});const d=await r.json();document.getElementById('admmsg').textContent=d.msg;if(d.ok)loadUsers();}
301
+ async function loadUsers(){const r=await fetch('/api/admin/users?caller='+encodeURIComponent(U));if(!r.ok)return;const d=await r.json();if(!d.ok)return;const w=document.getElementById('utw');if(!d.users.length){w.innerHTML='<div style="font-size:.75rem;color:var(--muted);padding:6px">No users</div>';return;}let h='<table class="ut"><tr><th>Username</th><th>Coins</th><th>Videos</th><th>Created</th><th></th></tr>';d.users.forEach(u=>{h+=`<tr><td>${u.username}</td><td>๐Ÿช™${u.coins}</td><td>${u.videos}</td><td>${u.created||''}</td><td><button class="delbtn" onclick="qDel('${u.username}')"><i class="fas fa-trash"></i></button></td></tr>`;});w.innerHTML=h+'</table>';}
302
+ async function qDel(u){if(!confirm('Delete '+u+'?'))return;const r=await fetch('/api/admin/delete_user',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({caller:U,username:u})});const d=await r.json();toast(d.msg);if(d.ok)loadUsers();}
303
+ function toast(m){const e=document.getElementById('toast');e.textContent=m;e.classList.add('show');setTimeout(()=>e.classList.remove('show'),2800);}
304
+ function openPkg(){document.getElementById('pkg-uname').textContent=U||'โ€”';document.getElementById('pkg-modal').classList.add('on');}
305
+ function closePkg(){document.getElementById('pkg-modal').classList.remove('on');}
306
+ function contactPkg(n,price){const msg=encodeURIComponent('แ€™แ€„แ€บแ€นแ€‚แ€œแ€ฌแ€•แ€ซ! Coin แ€แ€šแ€บแ€แ€ปแ€„แ€บแ€•แ€ซแ€แ€šแ€บ\nPackage: '+n+' coins ('+price+')\nAccount: '+U);window.open('https://t.me/PhoeShan2001?text='+msg,'_blank');}
307
+ function uid8(){return([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,c=>(c^crypto.getRandomValues(new Uint8Array(1))[0]&15>>c/4).toString(16)).substring(0,8);}
308
+ async function doPaste(){try{const t=await navigator.clipboard.readText();if(t){document.getElementById('vurl').value=t;onUrl(t);toast('โœ… Pasted!');}else toast('โŒ Clipboard empty');}catch(e){toast('โŒ Clipboard access denied');}}
309
  </script>
310
  </body>
311
  </html>