| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width,initial-scale=1.0"> |
| <title>Recap Studio</title> |
| <link rel="manifest" href="/manifest.json"> |
| <meta name="theme-color" content="#1a1a2e"> |
| <link rel="apple-touch-icon" href="https://raw.githubusercontent.com/Shangyi69/psonlineshop/main/logo.png"> |
| <link rel="preconnect" href="https://fonts.googleapis.com"> |
| <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=Noto+Sans+Myanmar:wght@400;700&display=swap" rel="stylesheet"> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css"> |
| <style> |
| :root{ |
| --bg:#f5f6fa;--bg2:#fff;--bg3:#f0f1f7;--bg4:#e8eaf2; |
| --border:#e2e4ee;--border2:#cdd0e0; |
| --text:#1a1d2e;--muted:#6b6f8a;--muted2:#9ba1bc; |
| --primary:#5b4cf5;--primary2:#4a3de0; |
| --orange:#ff6b35;--orange2:#e85a25; |
| --green:#10b981;--red:#ef4444;--blue:#3b82f6;--purple:#8b5cf6; |
| --gold:#f59e0b; |
| --F:'Plus Jakarta Sans',sans-serif; |
| } |
| *{box-sizing:border-box;margin:0;padding:0} |
| body{background:var(--bg);color:var(--text);font-family:var(--F);min-height:100vh} |
| #login-screen{display:flex;align-items:center;justify-content:center;min-height:100vh;padding:20px; |
| background:linear-gradient(135deg,#1a0533 0%,#0d1b4b 40%,#0a2a1a 100%); |
| position:relative;overflow:hidden} |
| #login-screen::before{content:'';position:absolute;inset:0; |
| background:radial-gradient(ellipse at 20% 50%,rgba(139,92,246,.25) 0%,transparent 60%), |
| radial-gradient(ellipse at 80% 20%,rgba(59,130,246,.2) 0%,transparent 50%), |
| radial-gradient(ellipse at 60% 80%,rgba(16,185,129,.15) 0%,transparent 50%); |
| pointer-events:none} |
| #app-screen{display:none} |
|
|
| /* ── LOGIN GLASS ── */ |
| .lw{width:100%;max-width:420px;position:relative;z-index:1} |
| .lb{text-align:center;margin-bottom:24px} |
| .lb .ico{width:68px;height:68px;background:rgba(255,255,255,.15);backdrop-filter:blur(12px); |
| border:1.5px solid rgba(255,255,255,.3);border-radius:20px; |
| display:inline-flex;align-items:center;justify-content:center;font-size:1.8rem; |
| margin-bottom:14px;box-shadow:0 8px 32px rgba(0,0,0,.3)} |
| .lb h1{font-size:1.9rem;font-weight:800;color:#fff;letter-spacing:-.02em} |
| .lb p{color:rgba(255,255,255,.6);font-size:.84rem;margin-top:5px} |
| .lc{background:rgba(255,255,255,.08);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px); |
| border:1px solid rgba(255,255,255,.15);border-radius:24px;padding:28px; |
| box-shadow:0 32px 64px rgba(0,0,0,.4),inset 0 1px 0 rgba(255,255,255,.1)} |
| .tabs{display:flex;gap:3px;background:rgba(0,0,0,.2);border-radius:10px;padding:3px;margin-bottom:20px} |
| .tab{flex:1;padding:9px;border:none;background:transparent;color:rgba(255,255,255,.5); |
| font-family:var(--F);font-size:.83rem;font-weight:600;border-radius:8px;cursor:pointer;transition:.2s} |
| .tab.on{background:rgba(255,255,255,.15);color:#fff;box-shadow:0 2px 8px rgba(0,0,0,.2)} |
| .fi{margin-bottom:13px} |
| .fi label{display:block;font-size:.7rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase; |
| color:rgba(255,255,255,.5);margin-bottom:6px} |
| .inp{width:100%;padding:11px 14px;background:rgba(255,255,255,.08); |
| border:1px solid rgba(255,255,255,.15);border-radius:10px; |
| color:#fff;font-family:var(--F);font-size:.88rem;outline:none;transition:.2s} |
| .inp::placeholder{color:rgba(255,255,255,.3)} |
| .inp:focus{border-color:rgba(139,92,246,.7);background:rgba(255,255,255,.12); |
| box-shadow:0 0 0 3px rgba(139,92,246,.2)} |
| .bp{width:100%;padding:13px; |
| background:linear-gradient(135deg,#7c3aed,#5b4cf5); |
| border:none;border-radius:11px;color:#fff;font-family:var(--F); |
| font-size:.9rem;font-weight:700;cursor:pointer;transition:.2s;margin-top:4px; |
| box-shadow:0 4px 20px rgba(124,58,237,.4)} |
| .bp:hover{transform:translateY(-1px);box-shadow:0 8px 28px rgba(124,58,237,.5)} |
| .am{font-size:.79rem;padding:9px 12px;border-radius:9px;margin-top:10px;display:none} |
| .am-ok{background:rgba(16,185,129,.2);border:1px solid rgba(16,185,129,.4);color:#6ee7b7} |
| .am-err{background:rgba(239,68,68,.15);border:1px solid rgba(239,68,68,.3);color:#fca5a5} |
| /* Google button */ |
| .ldiv{display:flex;align-items:center;gap:8px;margin:14px 0} |
| .ldiv hr{flex:1;border:none;border-top:1px solid rgba(255,255,255,.12)} |
| .ldiv span{color:rgba(255,255,255,.35);font-size:.73rem;font-weight:500} |
| .gbtn{display:flex;align-items:center;justify-content:center;gap:10px;width:100%; |
| padding:12px;background:rgba(255,255,255,.95);border:none;border-radius:11px; |
| color:#1a1d2e;font-family:var(--F);font-size:.88rem;font-weight:600; |
| text-decoration:none;cursor:pointer;transition:.2s; |
| box-shadow:0 2px 12px rgba(0,0,0,.2)} |
| .gbtn:hover{background:#fff;transform:translateY(-1px);box-shadow:0 6px 20px rgba(0,0,0,.25)} |
| .lfoot{text-align:center;padding:14px 0 0;font-size:.7rem;color:rgba(255,255,255,.3)} |
| .lfoot a{color:rgba(255,255,255,.4);text-decoration:none} |
| .lfoot a:hover{color:rgba(255,255,255,.7)} |
| @keyframes floatOrb{0%,100%{transform:translate(0,0) scale(1)}33%{transform:translate(20px,-20px) scale(1.05)}66%{transform:translate(-15px,15px) scale(.97)}} |
| @keyframes logoPop{0%{transform:scale(.5);opacity:0}100%{transform:scale(1);opacity:1}} |
| @keyframes srtprog{0%{width:20%}100%{width:80%}} |
| @keyframes pandaBob{0%,100%{transform:translateY(0)}50%{transform:translateY(-6px)}} |
| @keyframes earWiggle{0%,100%{transform:rotate(-38deg)}50%{transform:rotate(-28deg)}} |
| @keyframes earWiggleR{0%,100%{transform:rotate(38deg)}50%{transform:rotate(28deg)}} |
| @keyframes eyeBlink{0%,90%,100%{transform:scaleY(1)}95%{transform:scaleY(0.1)}} |
| @keyframes eyeLook{0%,100%{transform:translateX(0)}40%{transform:translateX(3px)}70%{transform:translateX(-3px)}} |
|
|
| /* ── PANDA CHARACTER ── */ |
| .panda-wrap{position:relative;display:flex;justify-content:center;margin-bottom:-22px;z-index:10;animation:pandaBob 3s ease-in-out infinite} |
| .panda{position:relative;width:100px;height:90px} |
| /* ears */ |
| .panda .ear{position:absolute;width:28px;height:28px;background:#1a1a1a;border-radius:50%;top:4px} |
| .panda .ear-l{left:10px;transform:rotate(-38deg);transform-origin:bottom right;animation:earWiggle 3s ease-in-out infinite} |
| .panda .ear-r{right:10px;transform:rotate(38deg);transform-origin:bottom left;animation:earWiggleR 3s ease-in-out infinite} |
| /* head */ |
| .panda .head{position:absolute;width:80px;height:74px;background:#fff;border-radius:50%;left:10px;top:10px;box-shadow:0 4px 12px rgba(0,0,0,.2)} |
| /* eye patches */ |
| .panda .patch{position:absolute;width:24px;height:20px;background:#1a1a1a;border-radius:50%;top:18px} |
| .panda .patch-l{left:14px;transform:rotate(20deg)} |
| .panda .patch-r{right:14px;transform:rotate(-20deg)} |
| /* eyes */ |
| .panda .eye{position:absolute;width:10px;height:10px;background:#fff;border-radius:50%;top:22px;animation:eyeBlink 4s ease-in-out infinite} |
| .panda .eye-l{left:17px} |
| .panda .eye-r{right:17px} |
| .panda .pupil{position:absolute;width:6px;height:6px;background:#1a1a1a;border-radius:50%;top:2px;left:2px;animation:eyeLook 5s ease-in-out infinite} |
| /* nose */ |
| .panda .nose{position:absolute;width:16px;height:10px;background:#ffb8c0;border-radius:50%;left:50%;transform:translateX(-50%);top:38px} |
| /* mouth */ |
| .panda .mouth{position:absolute;width:20px;height:10px;border-bottom:2.5px solid #555;border-left:2.5px solid #555;border-right:2.5px solid #555;border-radius:0 0 12px 12px;left:50%;transform:translateX(-50%);top:46px} |
| /* blush */ |
| .panda .blush{position:absolute;width:22px;height:16px;background:#ffb8c0;border-radius:50%;opacity:.6;top:36px} |
| .panda .blush-l{left:8px;transform:rotate(25deg)} |
| .panda .blush-r{right:8px;transform:rotate(-25deg)} |
| /* arms gripping card sides */ |
| .panda .arm{position:absolute;width:22px;height:36px;background:#1a1a1a;border-radius:12px;bottom:-10px;transition:all .4s cubic-bezier(.34,1.2,.64,1)} |
| .panda .arm-l{left:-4px;transform:rotate(-20deg);transform-origin:top center} |
| .panda .arm-r{right:-4px;transform:rotate(20deg);transform-origin:top center} |
| /* covering eyes state */ |
| .panda.peek-off .arm-l{left:10px;bottom:38px;transform:rotate(30deg);transform-origin:bottom center} |
| .panda.peek-off .arm-r{right:10px;bottom:38px;transform:rotate(-30deg);transform-origin:bottom center} |
| .panda.peek-off .eye-l,.panda.peek-off .eye-r{transform:scaleY(0.05)} |
| .panda .eye{position:absolute;width:10px;height:10px;background:#fff;border-radius:50%;top:22px;animation:eyeBlink 4s ease-in-out infinite;transition:transform .3s ease} |
| .panda.peek-off .eye-l,.panda.peek-off .eye-r{animation:none} |
|
|
| /* ── HEADER ── */ |
| .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)} |
| .brand{display:flex;align-items:center;gap:9px;font-weight:800;font-size:1rem;color:var(--text)} |
| .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} |
| .hr{display:flex;align-items:center;gap:7px} |
| .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} |
| .cpill:hover{box-shadow:0 2px 8px rgba(245,158,11,.2)} |
| .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} |
| .hb:hover{border-color:var(--primary);color:var(--primary)} |
| .hb-p{background:linear-gradient(135deg,var(--orange),var(--orange2));border-color:transparent;color:#fff} |
| .hb-p:hover{box-shadow:0 4px 14px rgba(255,107,53,.3);color:#fff;transform:translateY(-1px)} |
| .huser{display:flex;align-items:center;gap:6px;padding:4px 10px 4px 5px;background:#f8f7ff;border:1.5px solid var(--border);border-radius:20px;cursor:pointer;transition:.2s;max-width:160px} |
| .huser:hover{border-color:var(--primary);background:#ede9fe} |
| .huser-av{width:24px;height:24px;border-radius:8px;background:linear-gradient(135deg,var(--primary),var(--purple));display:flex;align-items:center;justify-content:center;font-weight:700;font-size:.65rem;color:#fff;flex-shrink:0} |
| .huser-name{font-size:.72rem;font-weight:700;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis} |
| .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} |
| .mbtn:hover{border-color:var(--primary)} |
| .mbtn span{display:block;width:15px;height:1.5px;background:var(--muted);border-radius:2px} |
| .mbtn:hover span{background:var(--primary)} |
|
|
| /* ── DRAWER ── */ |
| .dov{display:none;position:fixed;inset:0;z-index:200} |
| .dov.on{display:block} |
| .dbg{position:absolute;inset:0;background:rgba(0,0,0,.4);backdrop-filter:blur(3px)} |
| .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} |
| @keyframes sli{from{transform:translateX(100%)}to{transform:translateX(0)}} |
| .drw-h{padding:18px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between} |
| .drw-u{display:flex;align-items:center;gap:11px} |
| .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} |
| .dn{font-weight:700;font-size:.9rem} |
| .drl{font-size:.68rem;color:var(--muted);margin-top:1px} |
| .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;transition:.2s} |
| .dxbtn:hover{border-color:var(--red);color:var(--red)} |
| .drw-s{display:grid;grid-template-columns:1fr 1fr;gap:8px;padding:14px 18px;border-bottom:1px solid var(--border)} |
| .ds{background:var(--bg);border-radius:10px;padding:12px;text-align:center} |
| .dsv{font-size:1.3rem;font-weight:800;color:var(--primary)} |
| .dsl{font-size:.63rem;color:var(--muted);margin-top:2px} |
| .drw-n{flex:1;padding:8px} |
| .dni{display:flex;align-items:center;gap:9px;padding:10px 13px;border-radius:9px;cursor:pointer;transition:.15s;border:none;background:transparent;width:100%;text-align:left;font-family:var(--F);font-size:.83rem;font-weight:500;color:var(--text)} |
| .dni:hover{background:var(--bg)} |
| .dni i{width:17px;font-size:.82rem;color:var(--muted)} |
| .dni-r{color:var(--red)}.dni-r i{color:var(--red)} |
| .drw-f{padding:12px 18px;border-top:1px solid var(--border);font-size:.66rem;color:var(--muted2);text-align:center} |
|
|
| /* ── BODY ── */ |
| .ab{max-width:900px;margin:0 auto;padding:18px 14px 70px} |
|
|
| /* ── CARD ── */ |
| .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)} |
| .ch{display:flex;align-items:center;gap:8px;margin-bottom:13px} |
| .ci{width:28px;height:28px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:.74rem} |
| .ci-p{background:linear-gradient(135deg,#ede9fe,#ddd6fe);color:var(--primary)} |
| .ci-o{background:linear-gradient(135deg,#fff7ed,#fed7aa);color:var(--orange)} |
| .ci-g{background:linear-gradient(135deg,#d1fae5,#a7f3d0);color:var(--green)} |
| .ci-b{background:linear-gradient(135deg,#dbeafe,#bfdbfe);color:var(--blue)} |
| .ci-pu{background:linear-gradient(135deg,#ede9fe,#c4b5fd);color:var(--purple)} |
| .ct{font-weight:800;font-size:.88rem;color:var(--text)} |
|
|
| /* ── URL ── */ |
| .uw{position:relative} |
| .uinp{width:100%;padding:11px 100px 11px 42px;background:var(--bg);border:1.5px solid var(--border);border-radius:10px;color:var(--text);font-family:var(--F);font-size:.86rem;outline:none;transition:.2s} |
| .uinp:focus{border-color:var(--primary);background:#fff;box-shadow:0 0 0 3px rgba(91,76,245,.08)} |
| .uico{position:absolute;left:13px;top:50%;transform:translateY(-50%);color:var(--muted);font-size:.82rem;pointer-events:none} |
| .ubadge{position:absolute;right:80px;top:50%;transform:translateY(-50%);font-size:.63rem;font-weight:700;padding:3px 8px;border-radius:20px;display:none} |
| .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-family:var(--F);font-size:.72rem;font-weight:700;cursor:pointer;transition:.2s;display:flex;align-items:center;gap:4px} |
| .paste-btn:hover{background:var(--primary2);box-shadow:0 2px 8px rgba(91,76,245,.3)} |
| .b-yt{background:#fee2e2;color:#dc2626;border:1px solid #fca5a5} |
| .b-tt{background:#f3f4f6;color:#374151;border:1px solid #d1d5db} |
| .b-ig{background:#fdf2f8;color:#be185d;border:1px solid #fbcfe8} |
| .b-fb{background:#eff6ff;color:#1d4ed8;border:1px solid #bfdbfe} |
| .orsep{text-align:center;color:var(--muted2);font-size:.73rem;margin:10px 0;position:relative} |
| .orsep::before,.orsep::after{content:'';position:absolute;top:50%;width:44%;height:1px;background:var(--border)} |
| .orsep::before{left:0}.orsep::after{right:0} |
| .upz{border:2px dashed var(--border2);border-radius:10px;padding:14px;text-align:center;cursor:pointer;transition:.2s;position:relative} |
| .upz:hover{border-color:var(--primary);background:#faf9ff} |
| .upz input{position:absolute;inset:0;opacity:0;cursor:pointer} |
| .upz-t{font-size:.78rem;color:var(--muted)} |
| .upz-n{font-size:.76rem;color:var(--green);margin-top:3px;display:none;font-weight:600} |
| .prev-loading{display:none;margin-top:8px;padding:9px 13px;background:linear-gradient(135deg,#ede9fe,#e0e7ff);border-radius:9px;font-size:.78rem;color:var(--primary);font-weight:600;align-items:center;gap:7px} |
| .prev-loading.on{display:flex} |
|
|
| /* ── SETTINGS ── */ |
| .sgrid{display:grid;grid-template-columns:1fr 1fr;gap:9px} |
| @media(max-width:460px){.sgrid{grid-template-columns:1fr}} |
| .si{background:var(--bg);border:1.5px solid var(--border);border-radius:10px;padding:11px 13px;transition:.2s} |
| .si:focus-within{border-color:var(--primary);background:#fff} |
| .sil{font-size:.65rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);margin-bottom:6px} |
| .ssel{width:100%;background:transparent;border:none;color:var(--text);font-family:var(--F);font-size:.84rem;font-weight:500;outline:none;cursor:pointer} |
| .ssel option{background:#fff;color:var(--text)} |
| .vrow{display:flex;align-items:center;gap:7px} |
| .vrow .ssel{flex:1;background:var(--bg);border:1.5px solid var(--border);border-radius:9px;padding:9px 11px} |
| .pvbtn{width:34px;height:34px;flex-shrink:0;background:#fff;border:1.5px solid var(--border);border-radius:9px;color:var(--muted);cursor:pointer;transition:.2s;display:flex;align-items:center;justify-content:center;font-size:.78rem} |
| .pvbtn:hover{border-color:var(--green);color:var(--green);box-shadow:0 2px 8px rgba(16,185,129,.15)} |
| .spd-row{display:flex;align-items:center;gap:9px;margin-top:9px} |
| .spd-row label{font-size:.7rem;font-weight:600;color:var(--muted);white-space:nowrap;width:52px} |
| .spd-sl{flex:1;-webkit-appearance:none;height:4px;border-radius:2px;background:var(--bg4);outline:none;cursor:pointer} |
| .spd-sl::-webkit-slider-thumb{-webkit-appearance:none;width:14px;height:14px;border-radius:50%;background:var(--primary);cursor:pointer;box-shadow:0 0 0 3px rgba(91,76,245,.15)} |
| .spd-v{font-size:.73rem;color:var(--primary);font-weight:700;width:36px;text-align:right} |
|
|
| /* ── VIDEO OPTIONS ── */ |
| .egrid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px} |
| @media(max-width:420px){.egrid{grid-template-columns:1fr 1fr}} |
| .ei{background:var(--bg);border:1.5px solid var(--border);border-radius:10px;padding:10px 12px;transition:.2s} |
| .ei:focus-within{border-color:var(--primary)} |
| .eil{font-size:.63rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);margin-bottom:5px} |
| .esel{width:100%;background:transparent;border:none;color:var(--text);font-family:var(--F);font-size:.81rem;font-weight:500;outline:none;cursor:pointer} |
| .esel option{background:#fff} |
| .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;transition:.2s;font-size:.79rem;font-weight:600;color:var(--muted);font-family:var(--F);width:100%} |
| .icon-toggle:hover{border-color:var(--primary);color:var(--primary)} |
| .icon-toggle.on{background:linear-gradient(135deg,#ede9fe,#e0e7ff);border-color:var(--primary);color:var(--primary)} |
| .winp{width:100%;padding:9px 11px;background:var(--bg);border:1.5px solid var(--border);border-radius:9px;color:var(--text);font-family:var(--F);font-size:.81rem;font-weight:500;outline:none;transition:.2s} |
| .winp:focus{border-color:var(--primary);background:#fff} |
| .winp::placeholder{color:var(--muted2)} |
| /* ── WATERMARK TOGGLE ── */ |
| .wm-row{display:flex;align-items:center;justify-content:space-between;margin-top:12px;border-top:1px solid var(--border);padding-top:12px} |
| .wm-lbl{font-size:.67rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);display:flex;align-items:center;gap:6px} |
| .wm-wrap{margin-top:8px;display:none} |
| .wm-toggle{padding:5px 13px;border-radius:20px;border:1.5px solid var(--border);background:var(--bg);color:var(--muted);font-family:var(--F);font-size:.75rem;font-weight:700;cursor:pointer;transition:.2s} |
| .wm-toggle.on{background:#fffbeb;border-color:#fcd34d;color:#92400e} |
|
|
| /* ── OVERLAY (REMOVED: watermark, logo, blur boxes) ── */ |
| /* Only subtitle settings remain */ |
| .canvas-wrap{position:relative;background:#111;border-radius:10px;overflow:hidden;margin-top:10px;min-height:200px} |
| .canvas-bg-blur{position:absolute;inset:0;background-size:cover;background-position:center;filter:blur(18px) brightness(.6);transform:scale(1.1);pointer-events:none;display:none;z-index:0} |
| .canvas-video-wrap{position:relative;z-index:1} |
| .canvas-wrap video{width:100%;display:block;height:auto} |
| .canvas-ph{display:flex;align-items:center;justify-content:center;height:200px;color:rgba(255,255,255,.3);font-size:.78rem;flex-direction:column;gap:6px;pointer-events:none} |
| .canvas-ph i{font-size:1.8rem;opacity:.4} |
|
|
| /* Overlay buttons hidden */ |
| .ov-controls{display:grid;grid-template-columns:repeat(4,1fr);gap:7px;margin-top:10px} |
| .ov-btn{padding:9px 5px;background:var(--bg);border:1.5px solid var(--border);border-radius:9px;color:var(--muted);font-family:var(--F);font-size:.71rem;font-weight:600;cursor:pointer;transition:.2s;text-align:center;display:flex;flex-direction:column;align-items:center;gap:4px} |
| .ov-btn i{font-size:.88rem} |
| .ov-btn:hover{border-color:var(--primary);color:var(--primary);background:#faf9ff} |
| .ov-btn.on-wm {background:#fffbeb;border-color:#fcd34d;color:#92400e} |
| .ov-btn.on-logo{background:#ecfdf5;border-color:#6ee7b7;color:#065f46} |
| .ov-btn.on-sub {background:#eff6ff;border-color:#93c5fd;color:#1d4ed8} |
| .ov-btn.on-blur{background:#fef2f2;border-color:#fca5a5;color:#991b1b} |
| /* Hide watermark, logo, blur buttons */ |
| /* ov-wm, ov-logo — visible as canvas overlay buttons */ |
|
|
| .sub-settings{background:var(--bg);border:1.5px solid var(--border);border-radius:10px;padding:11px;margin-top:9px;display:none} |
| .sub-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px} |
| .sub-inp{width:100%;padding:7px 9px;background:#fff;border:1.5px solid var(--border);border-radius:7px;color:var(--text);font-family:var(--F);font-size:.79rem;font-weight:500;outline:none;transition:.2s} |
| .sub-inp:focus{border-color:var(--blue)} |
| .slbl{font-size:.64rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--muted);margin-bottom:4px} |
|
|
| .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;transition:.2s;display:flex;align-items:center;justify-content:center;gap:8px;box-shadow:0 4px 18px rgba(91,76,245,.3)} |
| .btn-auto:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 8px 28px rgba(91,76,245,.4)} |
| .btn-auto:disabled{opacity:.5;cursor:not-allowed;transform:none;box-shadow:none} |
|
|
| /* ── PROGRESS ── */ |
| .pc{background:#fff;border:1px solid var(--border);border-radius:16px;padding:18px;margin-top:12px;display:none;box-shadow:var(--shadow)} |
| .ph{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px} |
| .ptit{font-weight:800;font-size:.86rem;color:var(--text)} |
| .ptmr{font-size:.73rem;color:var(--primary);font-variant-numeric:tabular-nums;font-weight:700;background:linear-gradient(135deg,#ede9fe,#e0e7ff);padding:3px 9px;border-radius:20px} |
| .pb-bg{height:6px;background:var(--bg4);border-radius:3px;overflow:hidden;margin-bottom:9px} |
| .pb{height:100%;background:linear-gradient(90deg,var(--primary),var(--orange));border-radius:3px;width:0%;transition:width .4s ease} |
| .pmsg{font-size:.8rem;color:var(--muted);font-weight:500} |
| .psteps{display:flex;gap:5px;margin-top:11px;flex-wrap:wrap} |
| .ps{font-size:.66rem;font-weight:600;padding:4px 10px;border-radius:20px;background:var(--bg);color:var(--muted2);border:1px solid var(--border);transition:.3s} |
| .ps.active{background:linear-gradient(135deg,#ede9fe,#e0e7ff);color:var(--primary);border-color:#c4b5fd} |
| .ps.done{background:linear-gradient(135deg,#d1fae5,#a7f3d0);color:#065f46;border-color:#6ee7b7} |
|
|
| /* ── RESULT ── */ |
| .rc{background:#fff;border:1px solid var(--border);border-radius:16px;overflow:hidden;margin-top:12px;display:none;box-shadow:var(--shadow)} |
| .rvw{background:#000} |
| .rvw video{width:100%;display:block;max-height:380px;object-fit:contain} |
| .rb{padding:15px} |
| .rtm{font-size:.7rem;color:var(--muted);margin-bottom:8px;display:flex;align-items:center;gap:4px;font-weight:500} |
| .rtit{font-weight:800;font-size:.94rem;margin-bottom:5px;line-height:1.3;color:var(--text)} |
| .rtag{font-size:.73rem;color:var(--primary);line-height:1.6;margin-bottom:12px;font-weight:500} |
| .ract{display:grid;grid-template-columns:1fr 1fr;gap:8px} |
| .rb-btn{padding:11px;border-radius:9px;border:none;font-family:var(--F);font-size:.81rem;font-weight:700;cursor:pointer;transition:.2s;display:flex;align-items:center;justify-content:center;gap:6px} |
| .rb-g{background:linear-gradient(135deg,#d1fae5,#a7f3d0);color:#065f46} |
| .rb-g:hover{transform:translateY(-1px);box-shadow:0 4px 12px rgba(16,185,129,.2)} |
| .rb-b{background:linear-gradient(135deg,#dbeafe,#bfdbfe);color:#1d4ed8} |
| .rb-b:hover{transform:translateY(-1px);box-shadow:0 4px 12px rgba(59,130,246,.2)} |
|
|
| /* ── ADMIN ── */ |
| .admp{background:#fff;border:2px solid #fef3c7;border-radius:16px;padding:16px;margin-top:12px;display:none;box-shadow:0 2px 12px rgba(245,158,11,.1)} |
| .admbadge{font-size:.63rem;font-weight:700;padding:3px 8px;background:linear-gradient(135deg,#fef3c7,#fde68a);color:#92400e;border-radius:20px;border:1px solid #fcd34d} |
| .admg{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin-bottom:11px} |
| @media(max-width:460px){.admg{grid-template-columns:1fr}} |
| .ai-row{display:flex;gap:5px} |
| .ai{flex:1;padding:8px 10px;background:var(--bg);border:1.5px solid var(--border);border-radius:8px;color:var(--text);font-family:var(--F);font-size:.8rem;font-weight:500;outline:none} |
| .ai:focus{border-color:var(--primary)} |
| .ai::placeholder{color:var(--muted2)} |
| .abtn{padding:8px 12px;border-radius:8px;border:none;font-family:var(--F);font-size:.76rem;font-weight:700;cursor:pointer;transition:.2s;white-space:nowrap} |
| .ab-g{background:linear-gradient(135deg,#ede9fe,#ddd6fe);color:var(--primary)} |
| .ab-g:hover{background:linear-gradient(135deg,#ddd6fe,#c4b5fd)} |
| .ab-r{background:linear-gradient(135deg,#fee2e2,#fecaca);color:#dc2626} |
| .ab-r:hover{background:linear-gradient(135deg,#fecaca,#fca5a5)} |
| .admmsg{font-size:.76rem;margin-top:7px;min-height:18px;color:var(--green);font-weight:600} |
| .adm-act-btn{padding:7px 13px;background:var(--bg);border:1.5px solid var(--border);border-radius:8px;color:var(--muted);font-family:var(--F);font-size:.75rem;font-weight:600;cursor:pointer;transition:.2s} |
| .adm-act-btn:hover{border-color:var(--primary);color:var(--primary)} |
| /* pending payment card */ |
| .adm-pay-card{background:var(--bg);border:1.5px solid var(--border);border-radius:12px;padding:12px 14px;margin-bottom:8px} |
| .adm-pay-card-top{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px} |
| .adm-pay-card-user{font-weight:700;font-size:.85rem;color:var(--text)} |
| .adm-pay-card-coins{font-size:.78rem;font-weight:700;color:var(--primary)} |
| .adm-pay-card-info{font-size:.72rem;color:var(--muted);margin-bottom:10px;line-height:1.6} |
| .adm-pay-card-btns{display:flex;gap:7px} |
| .adm-pay-approve{flex:1;padding:8px;background:linear-gradient(135deg,#059669,#10b981);border:none;border-radius:8px;color:#fff;font-family:var(--F);font-size:.78rem;font-weight:700;cursor:pointer;transition:.2s} |
| .adm-pay-approve:hover{transform:translateY(-1px);box-shadow:0 4px 12px rgba(16,185,129,.35)} |
| .adm-pay-reject{padding:8px 14px;background:rgba(239,68,68,.1);border:1.5px solid rgba(239,68,68,.25);border-radius:8px;color:#ef4444;font-family:var(--F);font-size:.78rem;font-weight:700;cursor:pointer;transition:.2s} |
| .adm-pay-reject:hover{background:rgba(239,68,68,.18)} |
| .adm-pay-slip-btn{padding:8px 12px;background:rgba(91,76,245,.1);border:1.5px solid rgba(91,76,245,.25);border-radius:8px;color:var(--primary);font-family:var(--F);font-size:.78rem;font-weight:700;cursor:pointer;transition:.2s} |
| .adm-pay-slip-btn:hover{background:rgba(91,76,245,.18)} |
| .adm-slip-img{width:100%;max-height:180px;object-fit:contain;border-radius:10px;margin-top:8px;display:none;border:1px solid var(--border)} |
| .ut{width:100%;border-collapse:collapse;font-size:.76rem;margin-top:10px} |
| .ut th{padding:7px 9px;text-align:left;font-size:.63rem;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);border-bottom:2px solid var(--border)} |
| .ut td{padding:7px 9px;border-bottom:1px solid var(--bg4);color:var(--text);font-weight:500} |
| .ut tr:last-child td{border-bottom:none} |
| .delbtn{background:transparent;border:none;color:var(--muted2);cursor:pointer;padding:2px 5px;border-radius:5px;transition:.2s;font-size:.73rem} |
| .delbtn:hover{color:var(--red);background:#fee2e2} |
|
|
| #toast{position:fixed;bottom:22px;left:50%;transform:translateX(-50%) translateY(14px);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;max-width:88vw;box-shadow:0 8px 28px rgba(0,0,0,.25);font-weight:600} |
| #toast.show{opacity:1;transform:translateX(-50%) translateY(0)} |
| @keyframes spin{to{transform:rotate(360deg)}} |
| .sp{display:inline-block;animation:spin .7s linear infinite} |
|
|
| .modal-ov{display:none;position:fixed;inset:0;z-index:300;align-items:flex-end;justify-content:center} |
| .modal-ov.on{display:flex} |
| .modal-bg{position:absolute;inset:0;background:rgba(0,0,0,.45);backdrop-filter:blur(3px)} |
| .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;box-shadow:0 -8px 40px rgba(0,0,0,.15)} |
| @keyframes slideUp{from{transform:translateY(100%)}to{transform:translateY(0)}} |
| .modal-sheet h3{font-size:1.1rem;font-weight:800;margin-bottom:4px;color:var(--text)} |
| .modal-sheet p{font-size:.8rem;color:var(--muted);margin-bottom:18px} |
| .pkg-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:16px} |
| .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} |
| .pkg-card:hover{border-color:var(--primary);background:#faf9ff;transform:translateY(-2px);box-shadow:0 4px 16px rgba(91,76,245,.12)} |
| .pkg-card.popular{border-color:var(--primary);background:linear-gradient(135deg,#faf9ff,#ede9fe)} |
| .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;white-space:nowrap} |
| .pkg-emoji{font-size:1.5rem;margin-bottom:5px} |
| .pkg-name{font-size:.75rem;font-weight:700;color:var(--text);margin-bottom:3px} |
| .pkg-coins{font-size:1.2rem;font-weight:800;color:var(--primary)} |
| .pkg-coins span{font-size:.7rem;font-weight:500;color:var(--muted)} |
| .pkg-contact{background:var(--bg);border:1.5px solid var(--border);border-radius:10px;padding:12px;text-align:center;font-size:.8rem;color:var(--muted)} |
| .pkg-contact b{color:var(--text)} |
| .pkg-price{font-size:.72rem;font-weight:700;color:var(--green);margin-top:3px} |
| .pkg-contact a{color:var(--primary);font-weight:600;text-decoration:none} |
| .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;color:var(--muted);transition:.2s} |
| .modal-close:hover{border-color:var(--red);color:var(--red)} |
|
|
| /* ══ PAYMENT MODAL ══ */ |
| .pov{display:none;position:fixed;inset:0;z-index:400; |
| background:rgba(8,8,20,.8);backdrop-filter:blur(14px); |
| align-items:flex-end;justify-content:center} |
| .pov.on{display:flex} |
|
|
| @keyframes sheetUp{from{transform:translateY(100%);opacity:0}to{transform:translateY(0);opacity:1}} |
| @keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}} |
| @keyframes shimmer{0%{background-position:200% center}100%{background-position:-200% center}} |
| @keyframes pulse-glow{0%,100%{box-shadow:0 0 0 0 rgba(139,92,246,.3)}50%{box-shadow:0 0 0 8px rgba(139,92,246,0)}} |
|
|
| .psheet{ |
| position:relative;width:100%;max-width:520px; |
| background:linear-gradient(165deg,#0f0f1e 0%,#13112b 60%,#0e1628 100%); |
| border-radius:28px 28px 0 0; |
| padding:0 0 44px;max-height:93vh;overflow-y:auto; |
| box-shadow:0 -4px 60px rgba(91,76,245,.25),0 -1px 0 rgba(255,255,255,.06); |
| animation:sheetUp .32s cubic-bezier(.34,1.1,.64,1)} |
| .psheet::-webkit-scrollbar{width:3px} |
| .psheet::-webkit-scrollbar-track{background:transparent} |
| .psheet::-webkit-scrollbar-thumb{background:rgba(139,92,246,.3);border-radius:10px} |
|
|
| /* header */ |
| .psheet-hdr{ |
| padding:20px 22px 0; |
| display:flex;align-items:center;justify-content:space-between; |
| position:sticky;top:0;z-index:2; |
| background:linear-gradient(165deg,#0f0f1e,#13112b); |
| padding-bottom:16px; |
| border-bottom:1px solid rgba(255,255,255,.06)} |
| .psheet-hdr h2{font-size:1.05rem;font-weight:800;color:#fff;display:flex;align-items:center;gap:8px} |
| .psheet-hdr h2 span{ |
| display:inline-flex;align-items:center;justify-content:center; |
| width:32px;height:32px;background:linear-gradient(135deg,#7c3aed,#5b4cf5); |
| border-radius:10px;font-size:.9rem} |
| .pclose{ |
| width:32px;height:32px;border:1px solid rgba(255,255,255,.12); |
| background:rgba(255,255,255,.06);border-radius:9px;cursor:pointer; |
| display:flex;align-items:center;justify-content:center; |
| font-size:.8rem;color:rgba(255,255,255,.5);transition:.2s} |
| .pclose:hover{background:rgba(239,68,68,.15);border-color:rgba(239,68,68,.4);color:#f87171} |
|
|
| /* tab2 */ |
| .tab2{display:flex;gap:6px;margin:18px 22px 0} |
| .tab2 button{ |
| flex:1;padding:10px;border:1px solid rgba(255,255,255,.08); |
| background:rgba(255,255,255,.04); |
| color:rgba(255,255,255,.4);font-family:var(--F);font-size:.8rem;font-weight:600; |
| border-radius:10px;cursor:pointer;transition:.2s} |
| .tab2 button.on{ |
| border-color:rgba(139,92,246,.5); |
| background:linear-gradient(135deg,rgba(124,58,237,.2),rgba(91,76,245,.15)); |
| color:#c4b5fd} |
|
|
| /* ── MODE TOGGLE ── */ |
| .mode-bar{display:flex;gap:0;background:#f0f1f7;border-bottom:1px solid var(--border);padding:0} |
| .mode-btn{flex:1;padding:11px 8px;border:none;background:transparent;font-family:var(--F);font-size:.82rem;font-weight:700;color:var(--muted);cursor:pointer;transition:.2s;display:flex;align-items:center;justify-content:center;gap:6px;border-bottom:2px solid transparent} |
| .mode-btn.on{background:#fff;color:var(--primary);border-bottom-color:var(--primary)} |
| .mode-btn:hover:not(.on){background:rgba(91,76,245,.05);color:var(--primary)} |
| /* ── SRT SECTION ── */ |
| .srt-drop{border:2px dashed var(--border2);border-radius:10px;padding:20px;text-align:center;cursor:pointer;transition:.2s;position:relative;background:var(--bg)} |
| .srt-drop:hover{border-color:var(--primary);background:#faf9ff} |
| .srt-drop input{position:absolute;inset:0;opacity:0;cursor:pointer;width:100%;height:100%} |
| .srt-drop-t{font-size:.82rem;color:var(--muted);margin-top:6px} |
| .srt-step{display:flex;align-items:center;gap:10px;padding:12px 14px;background:var(--bg);border:1.5px solid var(--border);border-radius:10px;margin-bottom:8px;transition:.2s} |
| .srt-step.done{border-color:var(--green);background:#f0fdf4} |
| .srt-step.active{border-color:var(--primary);background:#faf9ff} |
| .srt-step-n{width:26px;height:26px;border-radius:50%;background:var(--border);color:var(--muted);font-size:.72rem;font-weight:800;display:flex;align-items:center;justify-content:center;flex-shrink:0} |
| .srt-step.done .srt-step-n{background:var(--green);color:#fff} |
| .srt-step.active .srt-step-n{background:var(--primary);color:#fff} |
| .srt-step-t{font-size:.82rem;font-weight:600;color:var(--text)} |
| .srt-step-s{font-size:.72rem;color:var(--muted);margin-top:1px} |
| .srt-preview{background:#1a1d2e;border-radius:10px;padding:12px;max-height:200px;overflow-y:auto;font-size:.73rem;color:#c4b5fd;font-family:monospace;line-height:1.6;white-space:pre-wrap;word-break:break-word} |
| .srt-dl-btn{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;padding:13px;background:linear-gradient(135deg,#10b981,#059669);border:none;border-radius:11px;color:#fff;font-family:var(--F);font-size:.9rem;font-weight:700;cursor:pointer;transition:.2s;margin-top:8px} |
| .srt-dl-btn:hover{transform:translateY(-1px);box-shadow:0 6px 20px rgba(16,185,129,.35)} |
| .srt-burn-btn{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;padding:13px;background:linear-gradient(135deg,var(--primary),var(--purple));border:none;border-radius:11px;color:#fff;font-family:var(--F);font-size:.9rem;font-weight:700;cursor:pointer;transition:.2s;margin-top:8px} |
| .srt-burn-btn:hover{transform:translateY(-1px);box-shadow:0 6px 20px rgba(91,76,245,.35)} |
| .srt-burn-btn:disabled{opacity:.4;cursor:not-allowed;transform:none} |
|
|
|
|
| .ppkg{ |
| border:1.5px solid rgba(255,255,255,.07); |
| border-radius:16px;padding:14px 10px 12px;cursor:pointer; |
| transition:all .22s cubic-bezier(.34,1.1,.64,1); |
| background:rgba(255,255,255,.04); |
| text-align:center;position:relative;overflow:hidden} |
| .ppkg::before{ |
| content:'';position:absolute;inset:0; |
| background:radial-gradient(ellipse at 50% 0%,rgba(139,92,246,.12),transparent 70%); |
| opacity:0;transition:.2s} |
| .ppkg:hover{border-color:rgba(139,92,246,.4);transform:translateY(-3px); |
| box-shadow:0 8px 24px rgba(91,76,245,.2);background:rgba(139,92,246,.08)} |
| .ppkg:hover::before{opacity:1} |
| .ppkg.sel{ |
| border-color:rgba(167,139,250,.6); |
| background:linear-gradient(145deg,rgba(124,58,237,.18),rgba(91,76,245,.12)); |
| box-shadow:0 0 0 3px rgba(139,92,246,.15),0 8px 28px rgba(91,76,245,.25)} |
| .ppkg.sel::before{opacity:1} |
|
|
| .ppkg.pop{border-color:rgba(245,158,11,.35);background:rgba(245,158,11,.05)} |
| .ppkg.pop::after{ |
| content:'🔥 Popular';position:absolute;top:-1px;left:50%;transform:translateX(-50%); |
| background:linear-gradient(90deg,#f59e0b,#fbbf24);color:#1a0a00; |
| font-size:.58rem;font-weight:800;padding:3px 10px;border-radius:0 0 10px 10px; |
| white-space:nowrap;letter-spacing:.03em} |
| .ppkg.pop:hover{border-color:rgba(245,158,11,.6);box-shadow:0 8px 24px rgba(245,158,11,.2)} |
| .ppkg.pop.sel{border-color:#f59e0b;box-shadow:0 0 0 3px rgba(245,158,11,.15),0 8px 28px rgba(245,158,11,.2)} |
|
|
| .ppkg-icon{font-size:1.6rem;margin-bottom:6px;display:block; |
| filter:drop-shadow(0 2px 8px rgba(0,0,0,.3))} |
| .ppkg-c{ |
| font-size:1.8rem;font-weight:800; |
| background:linear-gradient(135deg,#c4b5fd,#a78bfa); |
| -webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text; |
| line-height:1;margin-bottom:2px} |
| .ppkg.pop .ppkg-c{ |
| background:linear-gradient(135deg,#fde68a,#fbbf24); |
| -webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text} |
| .ppkg-u{font-size:.65rem;color:rgba(255,255,255,.4);text-transform:uppercase;letter-spacing:.08em;margin-bottom:8px} |
| .ppkg-p{ |
| font-size:.78rem;font-weight:700; |
| background:rgba(255,255,255,.08);border-radius:6px; |
| padding:4px 8px;color:rgba(255,255,255,.7);display:inline-block} |
| .ppkg.pop .ppkg-p{background:rgba(245,158,11,.15);color:#fbbf24} |
|
|
| /* selected badge */ |
| .ppkg.sel .ppkg-c{background:linear-gradient(135deg,#e9d5ff,#c4b5fd); |
| -webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text} |
|
|
| /* pinfo */ |
| .pinfo{ |
| margin:14px 22px 0;padding:10px 14px; |
| background:rgba(139,92,246,.1);border:1px solid rgba(139,92,246,.2); |
| border-radius:10px;font-size:.78rem;color:#c4b5fd; |
| text-align:center;min-height:36px;display:flex;align-items:center;justify-content:center; |
| font-weight:600} |
|
|
| /* payment info box */ |
| .pkbz{ |
| margin:14px 22px 0; |
| background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08); |
| border-radius:14px;padding:14px;overflow:hidden;position:relative} |
| .pkbz::before{ |
| content:'';position:absolute;top:0;left:0;right:0;height:1px; |
| background:linear-gradient(90deg,transparent,rgba(139,92,246,.4),transparent)} |
| .pkbz-t{ |
| font-size:.68rem;font-weight:700;text-transform:uppercase;letter-spacing:.1em; |
| color:rgba(255,255,255,.35);margin-bottom:10px;display:flex;align-items:center;gap:6px} |
| .pkbz-t::after{content:'';flex:1;height:1px;background:rgba(255,255,255,.06)} |
| .pkbz-row{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px} |
| .pkbz-row:last-child{margin-bottom:0} |
| .pkbz-lbl{font-size:.7rem;color:rgba(255,255,255,.35);font-weight:500} |
| .pkbz-val{font-size:.88rem;font-weight:700;color:rgba(255,255,255,.9)} |
| .pcopy{ |
| background:rgba(139,92,246,.12);border:1px solid rgba(139,92,246,.25); |
| color:#a78bfa;font-size:.65rem;padding:4px 10px;border-radius:6px; |
| cursor:pointer;font-family:var(--F);font-weight:700;transition:.2s} |
| .pcopy:hover{background:rgba(139,92,246,.25);border-color:rgba(167,139,250,.5);color:#c4b5fd} |
|
|
| /* slip upload */ |
| .pslip-label{ |
| font-size:.72rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em; |
| color:rgba(255,255,255,.4);margin:14px 22px 8px;display:block} |
| .pslip-drop{ |
| margin:0 22px;border:1.5px dashed rgba(139,92,246,.3);border-radius:14px; |
| padding:24px 20px;text-align:center;cursor:pointer;transition:.25s; |
| position:relative;overflow:hidden; |
| background:rgba(139,92,246,.04)} |
| .pslip-drop:hover{border-color:rgba(167,139,250,.6);background:rgba(139,92,246,.09)} |
| .pslip-drop input{position:absolute;inset:0;opacity:0;cursor:pointer;width:100%;height:100%} |
| .pslip-drop .pi{font-size:2rem;margin-bottom:8px;display:block; |
| filter:drop-shadow(0 2px 8px rgba(139,92,246,.4))} |
| .pslip-drop .pt{font-size:.75rem;color:rgba(255,255,255,.35);line-height:1.5} |
| .pslip-preview{width:100%;max-height:160px;object-fit:contain;border-radius:10px;display:none;margin-top:10px; |
| box-shadow:0 4px 20px rgba(0,0,0,.4)} |
|
|
| /* submit button */ |
| .psubmit{ |
| display:none;width:calc(100% - 44px);margin:16px 22px 0; |
| padding:15px;border:none;border-radius:14px; |
| background:linear-gradient(135deg,#7c3aed,#5b4cf5,#4f46e5); |
| background-size:200% auto; |
| color:#fff;font-family:var(--F);font-size:.92rem;font-weight:800; |
| cursor:pointer;transition:.3s;letter-spacing:.01em; |
| box-shadow:0 4px 24px rgba(91,76,245,.35)} |
| .psubmit.on{display:block} |
| .psubmit:hover:not(:disabled){ |
| background-position:right center; |
| transform:translateY(-2px);box-shadow:0 8px 32px rgba(91,76,245,.5)} |
| .psubmit:disabled{opacity:.4;cursor:not-allowed;transform:none} |
|
|
| /* history tab */ |
| .pmeth-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:9px;padding:16px 22px 0} |
| .pmeth{border:1.5px solid rgba(255,255,255,.09);border-radius:14px;padding:14px 8px 11px; |
| cursor:pointer;text-align:center;transition:.22s;background:rgba(255,255,255,.04)} |
| .pmeth:hover{border-color:rgba(139,92,246,.45);background:rgba(139,92,246,.09);transform:translateY(-2px)} |
| .pmeth.sel{border-color:#a78bfa;background:rgba(124,58,237,.18);box-shadow:0 0 0 3px rgba(139,92,246,.15)} |
| .pmeth-icon{font-size:1.5rem;display:block;margin-bottom:6px;width:44px;height:44px;object-fit:contain;margin-left:auto;margin-right:auto;border-radius:10px} |
| .pmeth-name{font-size:.65rem;font-weight:700;color:rgba(255,255,255,.7);line-height:1.3} |
| .pmeth-sub{font-size:.6rem;color:rgba(255,255,255,.35);margin-top:2px} |
| .pinfo-box{margin:12px 22px 0;padding:12px 14px;background:rgba(255,255,255,.04); |
| border:1px solid rgba(255,255,255,.09);border-radius:13px} |
| .pinfo-row{display:flex;align-items:center;justify-content:space-between;padding:5px 0; |
| border-bottom:1px solid rgba(255,255,255,.05)} |
| .pinfo-row:last-child{border-bottom:none} |
| .pinfo-lbl{font-size:.69rem;color:rgba(255,255,255,.35);font-weight:500} |
| .pinfo-val{font-size:.86rem;font-weight:700;color:rgba(255,255,255,.88)} |
| .pcopy{background:rgba(139,92,246,.12);border:1px solid rgba(139,92,246,.25); |
| color:#a78bfa;font-size:.62rem;padding:3px 9px;border-radius:6px; |
| cursor:pointer;font-family:var(--F);font-weight:700;transition:.2s} |
| .pcopy:hover{background:rgba(139,92,246,.28);color:#c4b5fd} |
| .pstep-lbl{font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.1em; |
| color:rgba(255,255,255,.3);padding:16px 22px 0;display:flex;align-items:center;gap:8px} |
| .pstep-lbl::after{content:'';flex:1;height:1px;background:rgba(255,255,255,.06)} |
| .pstep-num{width:18px;height:18px;border-radius:50%;background:rgba(139,92,246,.35); |
| color:#c4b5fd;font-size:.6rem;font-weight:800;display:inline-flex;align-items:center;justify-content:center} |
| .buy-now-btn{display:block;width:calc(100% - 44px);margin:14px 22px 0; |
| padding:14px;border:none;border-radius:14px; |
| background:linear-gradient(135deg,#7c3aed,#5b4cf5); |
| color:#fff;font-family:var(--F);font-size:.9rem;font-weight:800; |
| cursor:pointer;transition:.3s;box-shadow:0 4px 20px rgba(91,76,245,.35)} |
| .buy-now-btn:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 8px 28px rgba(91,76,245,.5)} |
| .buy-now-btn:disabled{opacity:.35;cursor:not-allowed} |
| .pay-step{display:none}.pay-step.on{display:block} |
| .pback-btn{background:none;border:1px solid rgba(255,255,255,.1);color:rgba(255,255,255,.5); |
| font-size:.75rem;padding:6px 14px;border-radius:8px;cursor:pointer;font-family:var(--F); |
| margin:12px 22px 0;display:inline-flex;align-items:center;gap:6px;transition:.2s} |
| .pback-btn:hover{border-color:rgba(255,255,255,.25);color:rgba(255,255,255,.8)} |
| .ph-card{ |
| background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.07); |
| border-radius:14px;padding:14px 16px;margin-bottom:9px; |
| animation:fadeIn .2s ease both} |
| .ph-top{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:8px} |
| .ph-coins{font-size:1.05rem;font-weight:800; |
| background:linear-gradient(135deg,#c4b5fd,#a78bfa); |
| -webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text} |
| .ph-badge{font-size:.65rem;padding:4px 10px;border-radius:20px;font-weight:700;letter-spacing:.03em} |
| .ph-badge.pending{background:rgba(251,191,36,.12);color:#fbbf24;border:1px solid rgba(251,191,36,.25)} |
| .ph-badge.approved{background:rgba(52,211,153,.12);color:#34d399;border:1px solid rgba(52,211,153,.25)} |
| .ph-badge.rejected{background:rgba(239,68,68,.12);color:#f87171;border:1px solid rgba(239,68,68,.25)} |
| .ph-meta{font-size:.69rem;color:rgba(255,255,255,.3)} |
| .ph-empty{text-align:center;padding:36px 0;color:rgba(255,255,255,.25);font-size:.85rem} |
| .ph-list-wrap{padding:0 22px} |
| </style> |
| |
| <script src="https://telegram.org/js/telegram-web-app.js"></script> |
| </head> |
| <body> |
|
|
| |
| <div id="login-screen"> |
| |
| <div style="position:absolute;width:300px;height:300px;border-radius:50%;background:radial-gradient(circle,rgba(139,92,246,.3),transparent);top:-80px;left:-80px;animation:floatOrb 8s ease-in-out infinite;pointer-events:none"></div> |
| <div style="position:absolute;width:250px;height:250px;border-radius:50%;background:radial-gradient(circle,rgba(59,130,246,.25),transparent);bottom:-60px;right:-60px;animation:floatOrb 10s ease-in-out infinite reverse;pointer-events:none"></div> |
| <div style="position:absolute;width:180px;height:180px;border-radius:50%;background:radial-gradient(circle,rgba(16,185,129,.2),transparent);top:40%;right:10%;animation:floatOrb 12s ease-in-out infinite .5s;pointer-events:none"></div> |
|
|
| <div class="lw"> |
| |
| <div class="lb"> |
| <h1 style="font-size:2.1rem;background:linear-gradient(135deg,#fff 0%,rgba(196,181,253,.9) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text">Recap Studio</h1> |
| <p style="color:rgba(255,255,255,.5);font-size:.85rem;margin-top:6px;letter-spacing:.02em">AI-powered video recap generator</p> |
| </div> |
| |
| <div class="panda-wrap"> |
| <div class="panda"> |
| <div class="ear ear-l"></div> |
| <div class="ear ear-r"></div> |
| <div class="head"> |
| <div class="patch patch-l"></div> |
| <div class="patch patch-r"></div> |
| <div class="eye eye-l"><div class="pupil"></div></div> |
| <div class="eye eye-r"><div class="pupil"></div></div> |
| <div class="nose"></div> |
| <div class="mouth"></div> |
| <div class="blush blush-l"></div> |
| <div class="blush blush-r"></div> |
| </div> |
| <div class="arm arm-l"></div> |
| <div class="arm arm-r"></div> |
| </div> |
| </div> |
| |
| <div class="lc" style="position:relative;padding-top:32px"> |
| <div class="tabs"> |
| <button class="tab on" id="tab-li" onclick="stab('li')">Login</button> |
| <button class="tab" id="tab-re" onclick="stab('re')">Register</button> |
| </div> |
| <div id="pane-li"> |
| <div class="fi"> |
| <label>USERNAME</label> |
| <div style="position:relative"> |
| <span style="position:absolute;left:13px;top:50%;transform:translateY(-50%);color:rgba(255,255,255,.35);font-size:.85rem"><i class="fas fa-user"></i></span> |
| <input class="inp" id="l-u" placeholder="your username" autocomplete="off" style="padding-left:36px"> |
| </div> |
| </div> |
| <div class="fi"> |
| <label>PASSWORD</label> |
| <div style="position:relative"> |
| <span style="position:absolute;left:13px;top:50%;transform:translateY(-50%);color:rgba(255,255,255,.35);font-size:.85rem"><i class="fas fa-lock"></i></span> |
| <input class="inp" id="l-p" type="password" placeholder="••••••••" style="padding-left:36px"> |
| </div> |
| </div> |
| <button class="bp" onclick="doLogin()" style="margin-top:8px;letter-spacing:.03em;font-size:.95rem"> |
| Sign In <i class="fas fa-arrow-right" style="font-size:.8rem"></i> |
| </button> |
| <div id="google-wrap" style="display:none"> |
| <div class="ldiv"><hr><span>or</span><hr></div> |
| <a href="/auth/google" class="gbtn"> |
| <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> |
| Continue with Google |
| </a> |
| </div> |
| </div> |
| <div id="pane-re" style="display:none"> |
| <div class="fi"> |
| <label>USERNAME <span style="color:rgba(255,255,255,.35);text-transform:none;letter-spacing:0;font-weight:400;font-size:.68rem">(optional)</span></label> |
| <div style="position:relative"> |
| <span style="position:absolute;left:13px;top:50%;transform:translateY(-50%);color:rgba(255,255,255,.35);font-size:.85rem"><i class="fas fa-user"></i></span> |
| <input class="inp" id="r-u" placeholder="leave blank for random" autocomplete="off" style="padding-left:36px"> |
| </div> |
| </div> |
| <div class="fi"> |
| <label>PASSWORD</label> |
| <div style="position:relative"> |
| <span style="position:absolute;left:13px;top:50%;transform:translateY(-50%);color:rgba(255,255,255,.35);font-size:.85rem"><i class="fas fa-lock"></i></span> |
| <input class="inp" id="r-p" type="password" placeholder="••••••••" style="padding-left:36px"> |
| </div> |
| </div> |
| <button class="bp" onclick="doReg()" style="margin-top:8px;letter-spacing:.03em;font-size:.95rem"> |
| Create Account <i class="fas fa-arrow-right" style="font-size:.8rem"></i> |
| </button> |
| </div> |
| <div id="am" class="am"></div> |
| </div> |
| |
| <div style="display:flex;justify-content:space-between;padding:0 28px;margin-top:-4px;position:relative;z-index:11"> |
| <div style="width:34px;height:28px;background:#1a1a1a;border-radius:50%;position:relative"> |
| <span style="position:absolute;width:10px;height:10px;background:#2a2a2a;border-radius:50%;top:-3px;left:2px"></span> |
| <span style="position:absolute;width:10px;height:10px;background:#2a2a2a;border-radius:50%;top:-3px;left:14px"></span> |
| <span style="position:absolute;width:10px;height:10px;background:#2a2a2a;border-radius:50%;top:-3px;right:2px"></span> |
| </div> |
| <div style="width:34px;height:28px;background:#1a1a1a;border-radius:50%;position:relative"> |
| <span style="position:absolute;width:10px;height:10px;background:#2a2a2a;border-radius:50%;top:-3px;left:2px"></span> |
| <span style="position:absolute;width:10px;height:10px;background:#2a2a2a;border-radius:50%;top:-3px;left:14px"></span> |
| <span style="position:absolute;width:10px;height:10px;background:#2a2a2a;border-radius:50%;top:-3px;right:2px"></span> |
| </div> |
| </div> |
| <div class="lfoot"> |
| <a href="/terms">Terms of Service</a> · <a href="/privacy">Privacy Policy</a> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="app-screen"> |
| <header class="hdr"> |
| <div class="brand"><div class="brand-i"><img src="https://raw.githubusercontent.com/Shangyi69/psonlineshop/main/logo.png" alt="Recap Studio" style="width:100%;height:100%;object-fit:contain;border-radius:8px"></div>Recap Studio</div> |
| <div class="hr"> |
| <div class="cpill" id="cpill" onclick="openPay()">🪙 <span id="hcoins">0</span> coins</div> |
| <button class="hb hb-p" onclick="openPay()">Buy Coins</button> |
| <button class="mbtn" onclick="odw()"><span></span><span></span><span></span></button> |
| </div> |
| </header> |
|
|
| |
| <div class="dov" id="dov"> |
| <div class="dbg" onclick="cdw()"></div> |
| <div class="drw"> |
| <div class="drw-h"> |
| <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> |
| <button class="dxbtn" onclick="cdw()"><i class="fas fa-times"></i></button> |
| </div> |
| <div class="drw-s"> |
| <div class="ds"><div class="dsv" id="dcoins">0</div><div class="dsl">Coins</div></div> |
| <div class="ds"><div class="dsv" id="dvids">0</div><div class="dsl">Videos</div></div> |
| </div> |
| <div class="drw-n"> |
| <button class="dni" onclick="cdw()"><i class="fas fa-home"></i>Home</button> |
| <button class="dni" onclick="cdw();openVideoHistory()"><i class="fas fa-film"></i>Video History</button> |
| <button class="dni" onclick="cdw();openPay();setTimeout(()=>showPayTab('his'),200)"><i class="fas fa-receipt"></i>Order History</button> |
| <button class="dni" id="adm-dlink" style="display:none" onclick="scrollToAdmin();cdw()"><i class="fas fa-crown"></i>Admin Panel</button> |
| <a href="https://t.me/recapstudio" target="_blank" class="dni" style="text-decoration:none"><i class="fab fa-telegram" style="color:#29b6f6"></i>Telegram Channel</a> |
| <button class="dni dni-r" onclick="doLogout()"><i class="fas fa-sign-out-alt"></i>Logout</button> |
| </div> |
| <div class="drw-f">recap.psonline.shop</div> |
| </div> |
| </div> |
|
|
| |
| <div class="mode-bar"> |
| <button class="mode-btn on" id="mode-recap" onclick="switchMode('recap')"> |
| <i class="fas fa-magic"></i> MOVIE RECAP |
| </button> |
| <button class="mode-btn" id="mode-srt" onclick="switchMode('srt')"> |
| <i class="fas fa-closed-captioning"></i> TRANSLATE SRT |
| </button> |
| </div> |
|
|
| |
| <div id="section-recap"> |
| <div class="ab"> |
|
|
| |
| <div class="card"> |
| <div class="ch"><div class="ci ci-p"><i class="fas fa-video"></i></div><div class="ct">Video Source</div></div> |
| <div class="uw"> |
| <i class="fas fa-link uico"></i> |
| <input class="uinp" id="vurl" placeholder="Paste YouTube / TikTok / Facebook / Instagram link…" oninput="onUrl(this.value)" style="padding-right:80px"> |
| <span class="ubadge" id="ubadge"></span> |
| <button class="paste-btn" onclick="doPaste()" title="Paste from clipboard"><i class="fas fa-paste"></i> Paste</button> |
| </div> |
| <div class="prev-loading" id="prev-loading"><i class="fas fa-spinner sp"></i> Loading thumbnail…</div> |
| <div class="orsep">or upload file</div> |
| <div class="upz"> |
| <input type="file" id="vfile" accept="video/*" onchange="onFile(this)"> |
| <div class="upz-t"><i class="fas fa-cloud-upload-alt" style="margin-right:6px;font-size:1.1rem"></i>Click or drag video file here</div> |
| <div class="upz-n" id="upzn"></div> |
| </div> |
| </div> |
|
|
| |
| <div class="card"> |
| <div class="ch"><div class="ci ci-o"><i class="fas fa-sliders-h"></i></div><div class="ct">Settings</div></div> |
| <div class="sgrid"> |
| <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> |
| <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> |
| <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> |
| <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> |
| <div class="si" id="emotion-wrap" style="display:none"> |
| <div class="sil">Voice Emotion</div> |
| <select class="ssel" id="tts-emotion"> |
| <option value="">Normal</option> |
| <option value="[excitedly]">Excited 😊</option> |
| <option value="[sadly]">Sad 😢</option> |
| <option value="[whispers]">Whisper 🤫</option> |
| <option value="[shouting]">Shouting 📢</option> |
| <option value="[storytelling]">Storytelling 📖</option> |
| <option value="[angrily]">Angry 😠</option> |
| <option value="[fearfully]">Fearful 😨</option> |
| <option value="[in a spooky horror voice]">Horror 👻</option> |
| <option value="[in a funny and playful voice]">Funny 😂</option> |
| </select> |
| </div> |
| </div> |
| <div style="margin-top:10px"> |
| <div class="sil" style="margin-bottom:6px">Voice</div> |
| <div class="vrow"> |
| <select class="ssel" id="vsel"></select> |
| <button class="pvbtn" id="pvbtn" onclick="doPrevVoice()" title="Preview Voice"><i class="fas fa-play"></i></button> |
| </div> |
| <div style="margin-top:10px"> |
| <button onclick="toggleAdvanced()" id="adv-btn" style="background:none;border:none;color:var(--muted);font-size:.72rem;font-weight:600;cursor:pointer;display:flex;align-items:center;gap:5px;padding:0;font-family:var(--F)"> |
| <i class="fas fa-chevron-right" id="adv-icon" style="font-size:.6rem;transition:.2s"></i> Advanced |
| </button> |
| <div id="adv-wrap" style="display:none;margin-top:8px"> |
| <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> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="card"> |
| <div class="ch"><div class="ci ci-g"><i class="fas fa-film"></i></div><div class="ct">Video Options</div></div> |
| <div class="egrid" style="grid-template-columns:1fr 1fr;gap:8px"> |
| <div class="ei"><div class="eil">Aspect Ratio</div> |
| <select class="esel" id="crop" onchange="syncCanvasInner(document.getElementById('pvid'));_filterSubSize();_syncSubPreview();"> |
| <option value="original" selected>Original</option> |
| <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> |
| <div class="ei"><div class="eil">Flip</div> |
| <button class="icon-toggle" id="btn-flip" onclick="toggleOpt('flip')"><i class="fas fa-arrows-alt-h"></i> Off</button> |
| </div> |
| <div class="ei"><div class="eil">Color Boost</div> |
| <button class="icon-toggle" id="btn-col" onclick="toggleOpt('col')"><i class="fas fa-adjust"></i> Off</button> |
| </div> |
| <div class="ei"><div class="eil">Background Music</div> |
| <button class="icon-toggle" id="btn-bgm" onclick="toggleBgm()"><i class="fas fa-music"></i> Off</button> |
| </div> |
| <div class="ei" style="grid-column:1/-1;padding:0;border:none;background:none"> |
| <div id="bgm-wrap" style="display:none"> |
| <label style="display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.05);border:1px dashed rgba(168,85,247,.3);border-radius:9px;padding:10px 12px;cursor:pointer"> |
| <i class="fas fa-cloud-upload-alt" style="color:#a855f7;font-size:1.1rem"></i> |
| <span id="bgm-name" style="color:rgba(255,255,255,.5);font-size:.82rem">Click to upload MP3 / WAV</span> |
| <input type="file" id="bgm-file" accept=".mp3,.wav,audio/*" style="display:none" onchange="onBgmFile(this)"> |
| </label> |
| </div> |
| </div> |
| |
|
|
| </div> |
|
|
| </div> |
|
|
|
|
| |
| <div class="card" style="padding:0;overflow:hidden"> |
| <div class="canvas-wrap" id="canvas-wrap" style="margin-top:0;border-radius:0"> |
| <div class="canvas-bg-blur" id="canvas-bg-blur"></div> |
| <div class="canvas-video-wrap" id="canvas-video-wrap" style="position:relative"> |
| <video id="pvid" controls playsinline style="display:none;width:100%;height:auto;display:block"></video> |
| <img id="pthumb" style="display:none;width:100%;height:auto;object-fit:cover;display:block" alt="thumbnail"> |
| <div id="canvas-inner" class="canvas-inner" style="position:absolute;inset:0;pointer-events:none;z-index:10"> |
| <div id="db-blur" style="display:none;position:absolute;left:20px;top:80px;width:160px;height:90px;pointer-events:auto;cursor:move;border:2px dashed rgba(255,100,100,.85);background:rgba(0,0,0,.25);border-radius:6px;box-sizing:border-box;touch-action:none"> |
| <div style="position:absolute;top:2px;left:0;right:0;text-align:center;font-size:.58rem;font-weight:700;color:rgba(255,160,160,.9);letter-spacing:.05em;pointer-events:none;user-select:none">BLUR ZONE</div> |
| <div class="rh" style="position:absolute;right:0;bottom:0;width:18px;height:18px;cursor:se-resize;background:rgba(255,100,100,.6);border-radius:3px 0 4px 0;display:flex;align-items:center;justify-content:center"> |
| <i class="fas fa-expand-arrows-alt" style="font-size:.5rem;color:#fff;pointer-events:none"></i> |
| </div> |
| </div> |
| <div id="db-wm" style="display:none;position:absolute;left:10px;bottom:40px;width:180px;height:36px;pointer-events:auto;cursor:move;border:2px dashed rgba(251,191,36,.85);background:rgba(0,0,0,.22);border-radius:6px;box-sizing:border-box;touch-action:none"> |
| <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;pointer-events:none"> |
| <span id="db-wm-txt" style="font-size:.8rem;font-weight:700;color:#fef9c3;text-shadow:0 1px 3px rgba(0,0,0,.9);white-space:nowrap;overflow:hidden;max-width:92%;pointer-events:none">Watermark</span> |
| </div> |
| <div class="rh" style="position:absolute;right:0;bottom:0;width:16px;height:16px;cursor:se-resize;background:rgba(251,191,36,.6);border-radius:3px 0 4px 0;display:flex;align-items:center;justify-content:center"> |
| <i class="fas fa-expand-arrows-alt" style="font-size:.45rem;color:#fff;pointer-events:none"></i> |
| </div> |
| </div> |
| <div id="db-logo" style="display:none;position:absolute;right:10px;top:10px;width:72px;height:72px;pointer-events:auto;cursor:move;border:2px dashed rgba(52,211,153,.85);background:rgba(0,0,0,.22);border-radius:6px;box-sizing:border-box;touch-action:none;background-size:contain;background-repeat:no-repeat;background-position:center"> |
| <div id="db-logo-lbl" style="position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;pointer-events:none"> |
| <i class="fas fa-image" style="font-size:.9rem;color:rgba(52,211,153,.8)"></i> |
| <span style="font-size:.52rem;font-weight:700;color:rgba(52,211,153,.8)">LOGO</span> |
| </div> |
| <div class="rh" style="position:absolute;right:0;bottom:0;width:16px;height:16px;cursor:se-resize;background:rgba(52,211,153,.6);border-radius:3px 0 4px 0;display:flex;align-items:center;justify-content:center"> |
| <i class="fas fa-expand-arrows-alt" style="font-size:.45rem;color:#fff;pointer-events:none"></i> |
| </div> |
| </div> |
| <div id="db-sub" style="display:none;position:absolute;left:4%;right:4%;pointer-events:none;border:2px dashed rgba(56,189,248,.7);border-radius:8px;background:rgba(0,180,255,.08);box-sizing:border-box;transition:top .15s"><div style="display:flex;align-items:center;justify-content:center;gap:6px;padding:10px 8px;pointer-events:none"><i class="fas fa-closed-captioning" style="color:rgba(56,189,248,.8);font-size:.75rem;flex-shrink:0"></i><span id="db-sub-label" style="font-size:.72rem;font-weight:600;color:rgba(56,189,248,.9);white-space:nowrap">Subtitles will appear here</span></div></div> |
| </div> |
| </div> |
| <div id="canvas-ph" class="canvas-ph"><i class="fas fa-film"></i><span>Preview appears after URL is pasted</span></div> |
| </div> |
|
|
| |
| <div style="background:linear-gradient(135deg,#0e2233,#0a1f2e);padding:10px 12px"> |
|
|
| |
| <div style="margin-bottom:2px"> |
| <div style="font-size:.62rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:#94a3b8;margin-bottom:8px;display:flex;align-items:center;gap:5px"> |
| <i class="fas fa-layer-group" style="color:#a78bfa"></i>Canvas Overlays |
| </div> |
|
|
| |
| <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:6px;margin-bottom:10px"> |
|
|
| |
| <div style="background:rgba(251,191,36,.05);border:1.5px solid rgba(251,191,36,.18);border-radius:10px;padding:8px 6px;display:flex;flex-direction:column;align-items:center;gap:5px"> |
| <div style="width:30px;height:30px;border-radius:8px;background:rgba(251,191,36,.12);display:flex;align-items:center;justify-content:center"> |
| <i class="fas fa-stamp" style="color:rgba(251,191,36,.9);font-size:.85rem"></i> |
| </div> |
| <div style="font-size:.62rem;font-weight:700;color:rgba(251,191,36,.8);text-align:center">Watermark</div> |
| <button id="ov-wm" onclick="ovToggle('wm')" style="width:100%;padding:4px 0;font-size:.68rem;font-weight:700;font-family:var(--F);border-radius:6px;border:1.5px solid rgba(251,191,36,.35);background:rgba(251,191,36,.07);color:rgba(251,191,36,.8);cursor:pointer;transition:.2s"> |
| Off |
| </button> |
| </div> |
|
|
| |
| <div style="background:rgba(52,211,153,.05);border:1.5px solid rgba(52,211,153,.18);border-radius:10px;padding:8px 6px;display:flex;flex-direction:column;align-items:center;gap:5px"> |
| <div style="width:30px;height:30px;border-radius:8px;background:rgba(52,211,153,.12);display:flex;align-items:center;justify-content:center"> |
| <i class="fas fa-image" style="color:rgba(52,211,153,.9);font-size:.85rem"></i> |
| </div> |
| <div style="font-size:.62rem;font-weight:700;color:rgba(52,211,153,.8);text-align:center">Logo</div> |
| <button id="ov-logo" onclick="ovToggle('logo')" style="width:100%;padding:4px 0;font-size:.68rem;font-weight:700;font-family:var(--F);border-radius:6px;border:1.5px solid rgba(52,211,153,.35);background:rgba(52,211,153,.07);color:rgba(52,211,153,.8);cursor:pointer;transition:.2s"> |
| Off |
| </button> |
| </div> |
|
|
| |
| <div style="background:rgba(239,68,68,.04);border:1.5px solid rgba(252,165,165,.18);border-radius:10px;padding:8px 6px;display:flex;flex-direction:column;align-items:center;gap:5px"> |
| <div style="width:30px;height:30px;border-radius:8px;background:rgba(239,68,68,.1);display:flex;align-items:center;justify-content:center"> |
| <i class="fas fa-eraser" style="color:rgba(252,165,165,.9);font-size:.85rem"></i> |
| </div> |
| <div style="font-size:.62rem;font-weight:700;color:rgba(252,165,165,.8);text-align:center">Blur</div> |
| <button id="ov-blur" onclick="ovToggle('blur')" style="width:100%;padding:4px 0;font-size:.68rem;font-weight:700;font-family:var(--F);border-radius:6px;border:1.5px solid rgba(252,165,165,.35);background:rgba(239,68,68,.07);color:rgba(252,165,165,.8);cursor:pointer;transition:.2s"> |
| Off |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div id="ov-wm-wrap" style="display:none;margin-bottom:8px;padding:10px 12px;background:rgba(251,191,36,.05);border:1px solid rgba(251,191,36,.22);border-radius:10px"> |
| <div style="font-size:.58rem;font-weight:700;color:rgba(251,191,36,.7);letter-spacing:.06em;text-transform:uppercase;margin-bottom:6px"> |
| <i class="fas fa-stamp" style="margin-right:4px"></i>Watermark Text |
| </div> |
| <input id="wm-text2" class="winp" placeholder="@YourPage သို့မဟုတ် Recap Studio" maxlength="40" |
| oninput="document.getElementById('db-wm-txt').textContent=this.value||'Watermark'" |
| style="width:100%;font-size:.8rem;background:rgba(255,255,255,.06);border-color:rgba(251,191,36,.3);color:#fef9c3;box-sizing:border-box"> |
| <div style="font-size:.6rem;color:rgba(251,191,36,.4);margin-top:5px;display:flex;align-items:center;gap:4px"> |
| <i class="fas fa-hand-pointer"></i>Preview canvas ပေါ်မှာ drag ပြီး position ချိန်ပါ |
| </div> |
| </div> |
|
|
| |
| <div id="ov-logo-wrap" style="display:none;margin-bottom:8px;padding:10px 12px;background:rgba(52,211,153,.05);border:1px solid rgba(52,211,153,.22);border-radius:10px"> |
| <div style="font-size:.58rem;font-weight:700;color:rgba(52,211,153,.7);letter-spacing:.06em;text-transform:uppercase;margin-bottom:6px"> |
| <i class="fas fa-image" style="margin-right:4px"></i>Logo Image |
| </div> |
| <label style="display:flex;align-items:center;gap:8px;background:rgba(255,255,255,.04);border:1px dashed rgba(52,211,153,.3);border-radius:8px;padding:8px 10px;cursor:pointer;transition:.2s"> |
| <i class="fas fa-cloud-upload-alt" style="color:rgba(52,211,153,.7);font-size:1rem"></i> |
| <span id="logo-name" style="color:rgba(52,211,153,.55);font-size:.78rem;flex:1">Click to upload PNG / JPG</span> |
| <input type="file" id="logo-file2" accept="image/*" style="display:none" onchange="onLogoFile(this)"> |
| </label> |
| <div style="font-size:.6rem;color:rgba(52,211,153,.4);margin-top:5px;display:flex;align-items:center;gap:4px"> |
| <i class="fas fa-hand-pointer"></i>Canvas ပေါ်မှာ drag / resize ပြီး position ချိန်ပါ |
| </div> |
| </div> |
|
|
| |
| <div id="ov-blur-hint" style="display:none;margin-bottom:8px;padding:10px 12px;background:rgba(239,68,68,.04);border:1px solid rgba(239,68,68,.2);border-radius:10px"> |
| <div style="font-size:.6rem;color:rgba(252,165,165,.7);display:flex;align-items:center;gap:5px"> |
| <i class="fas fa-hand-pointer"></i>Preview canvas ပေါ်မှာ blur zone ကို drag နှင့် resize လုပ်ပါ |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div style="height:1px;background:rgba(255,255,255,.07);margin:6px 0 8px"></div> |
|
|
| |
| <div> |
| <div style="display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:6px"> |
| <div style="font-size:.65rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:#38bdf8;display:flex;align-items:center;gap:5px"> |
| <i class="fas fa-closed-captioning"></i>Subtitles (Auto) |
| </div> |
| <button class="wm-toggle" id="btn-sub" onclick="toggleSub()" style="border-color:#38bdf8;background:rgba(56,189,248,.08);color:#38bdf8;padding:3px 12px;font-size:.72rem">Off</button> |
| </div> |
| <div id="sub-wrap" style="display:none;margin-top:8px"> |
| <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:6px;margin-bottom:8px"> |
| <div> |
| <div style="font-size:.58rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:rgba(56,189,248,.6);margin-bottom:3px">Font Size</div> |
| <select class="esel" id="sub-size" onchange="_syncSubPreview()" style="color:#e0f2fe;background:rgba(255,255,255,.06);border:1px solid rgba(56,189,248,.2);border-radius:6px;padding:4px 6px;width:100%;font-size:.75rem"> |
| <option value="0.0350">XSmall</option> |
| <option value="0.0469" selected>Small</option> |
| <option value="0.0625">Medium</option> |
| <option value="0.0781">Large</option> |
| </select> |
| </div> |
| <div> |
| <div style="font-size:.58rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:rgba(56,189,248,.6);margin-bottom:3px">Color</div> |
| <select class="esel" id="sub-color" onchange="_syncSubPreview()" style="color:#e0f2fe;background:rgba(255,255,255,.06);border:1px solid rgba(56,189,248,.2);border-radius:6px;padding:4px 6px;width:100%;font-size:.75rem"> |
| <option value="white" selected>⬜ White</option> |
| <option value="yellow">🟡 Yellow</option> |
| <option value="cyan">🔵 Cyan</option> |
| <option value="green">🟢 Green</option> |
| <option value="orange">🟠 Orange</option> |
| <option value="pink">🩷 Pink</option> |
| <option value="red">🔴 Red</option> |
| <option value="lime">💚 Lime</option> |
| <option value="hotpink">💗 Hot Pink</option> |
| <option value="gold">✨ Gold</option> |
| <option value="violet">💜 Violet</option> |
| <option value="deepskyblue">🩵 Sky Blue</option> |
| <option value="coral">🪸 Coral</option> |
| </select> |
| </div> |
| <div> |
| <div style="font-size:.58rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:rgba(56,189,248,.6);margin-bottom:3px">Style</div> |
| <select class="esel" id="sub-style" onchange="_syncSubPreview()" style="color:#e0f2fe;background:rgba(255,255,255,.06);border:1px solid rgba(56,189,248,.2);border-radius:6px;padding:4px 6px;width:100%;font-size:.75rem"> |
| <option value="outline" selected>Outline</option> |
| <option value="box">Box</option> |
| <option value="shadow">Shadow</option> |
| <option value="glow">Glow</option> |
| <option value="stroke">Stroke</option> |
| <option value="plain">Plain</option> |
| </select> |
| </div> |
| </div> |
| <div> |
| <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:3px"> |
| <div style="font-size:.58rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:rgba(56,189,248,.6)">Position</div> |
| <span id="sub-pos-lbl" style="font-size:.68rem;font-weight:700;color:#38bdf8">90%</span> |
| </div> |
| <input type="range" id="sub-pos" min="0" max="95" value="90" |
| oninput="document.getElementById('sub-pos-lbl').textContent=this.value+'%';_syncSubPreview()" |
| style="width:100%;accent-color:#38bdf8;cursor:pointer"> |
| <div style="display:flex;justify-content:space-between;font-size:.58rem;color:rgba(56,189,248,.35);margin-top:1px"><span>Top</span><span>Bottom</span></div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div style="margin-bottom:12px"> |
| <button class="btn-auto" id="btnauto" onclick="doAuto()"> |
| <i class="fas fa-magic"></i> Auto Process |
| <span style="opacity:.7;font-size:.78em;background:rgba(255,255,255,.2);padding:2px 8px;border-radius:10px">1 Coin</span> |
| </button> |
| </div> |
|
|
| |
| <div class="pc" id="pc"> |
| <div class="ph"><span class="ptit">⚙️ Processing…</span><span class="ptmr" id="ptmr">⏱ 0s</span></div> |
| <div class="pb-bg"><div class="pb" id="pb"></div></div> |
| <div class="pmsg" id="pmsg">Starting…</div> |
| <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> |
| </div> |
|
|
| |
| <div id="live-outputs" style="display:none;flex-direction:column;gap:8px;margin-top:8px"> |
| |
| <div id="out-transcript" style="display:none;background:rgba(99,102,241,.07);border:1px solid rgba(99,102,241,.25);border-radius:12px;overflow:hidden"> |
| <div onclick="toggleOut('transcript')" style="display:flex;align-items:center;justify-content:space-between;padding:9px 12px;cursor:pointer;user-select:none"> |
| <span style="font-size:.72rem;font-weight:700;color:#a5b4fc;display:flex;align-items:center;gap:6px"><i class="fas fa-microphone-alt"></i> Whisper Transcript <span id="out-tr-lang" style="font-size:.62rem;opacity:.7"></span></span> |
| <i class="fas fa-chevron-down" id="out-tr-chv" style="color:#a5b4fc;font-size:.65rem;transition:.2s"></i> |
| </div> |
| <div id="out-tr-body" style="padding:0 12px 10px;display:none"> |
| <div id="out-tr-text" style="font-size:.75rem;color:#cbd5e1;line-height:1.6;max-height:120px;overflow-y:auto;white-space:pre-wrap;background:rgba(0,0,0,.2);border-radius:8px;padding:8px 10px"></div> |
| </div> |
| </div> |
| |
| <div id="out-script" style="display:none;background:rgba(16,185,129,.07);border:1px solid rgba(16,185,129,.25);border-radius:12px;overflow:hidden"> |
| <div onclick="toggleOut('script')" style="display:flex;align-items:center;justify-content:space-between;padding:9px 12px;cursor:pointer;user-select:none"> |
| <span style="font-size:.72rem;font-weight:700;color:#6ee7b7;display:flex;align-items:center;gap:6px"><i class="fas fa-robot"></i> AI Script (Myanmar)</span> |
| <i class="fas fa-chevron-down" id="out-sc-chv" style="color:#6ee7b7;font-size:.65rem;transition:.2s"></i> |
| </div> |
| <div id="out-sc-body" style="padding:0 12px 10px;display:none"> |
| <div id="out-sc-text" style="font-size:.75rem;color:#cbd5e1;line-height:1.7;max-height:150px;overflow-y:auto;white-space:pre-wrap;background:rgba(0,0,0,.2);border-radius:8px;padding:8px 10px;font-family:'NotoSansMyanmar',sans-serif"></div> |
| </div> |
| </div> |
| |
| <div id="out-tts" style="display:none;background:rgba(245,158,11,.07);border:1px solid rgba(245,158,11,.25);border-radius:12px;overflow:hidden"> |
| <div onclick="toggleOut('tts')" style="display:flex;align-items:center;justify-content:space-between;padding:9px 12px;cursor:pointer;user-select:none"> |
| <span style="font-size:.72rem;font-weight:700;color:#fcd34d;display:flex;align-items:center;gap:6px"><i class="fas fa-volume-up"></i> TTS Audio Preview</span> |
| <i class="fas fa-chevron-down" id="out-tts-chv" style="color:#fcd34d;font-size:.65rem;transition:.2s"></i> |
| </div> |
| <div id="out-tts-body" style="padding:0 12px 10px;display:none"> |
| <audio id="out-tts-audio" controls style="width:100%;border-radius:8px;accent-color:#f59e0b"></audio> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="rc" id="rc"> |
| <div class="rvw"><video id="rvid" controls playsinline></video></div> |
| <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> |
| </div> |
|
|
| |
| <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> |
| <div class="admg"> |
| <div><div class="sil" style="margin-bottom:5px">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> |
| <div><div class="sil" style="margin-bottom:5px">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> |
| <div><div class="sil" style="margin-bottom:5px">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;font-weight:600"></div> |
| </div> |
| <div><div class="sil" style="margin-bottom:5px">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> |
| <div id="admmsg" class="admmsg"></div> |
| <div style="display:flex;gap:8px;margin-top:8px;flex-wrap:wrap"> |
| <button class="adm-act-btn" onclick="loadUsers()"><i class="fas fa-users"></i> Users</button> |
| <button class="adm-act-btn" style="background:rgba(239,68,68,.1);border-color:rgba(239,68,68,.3);color:#ef4444" onclick="loadPendingPayments()"><i class="fas fa-clock"></i> Pending Payments <span id="adm-pending-count" style="background:#ef4444;color:#fff;border-radius:20px;padding:1px 7px;font-size:.62rem;margin-left:4px;display:none">0</span></button> |
| <button class="adm-act-btn" style="background:rgba(99,102,241,.1);border-color:rgba(99,102,241,.3);color:#6366f1" onclick="openBroadcast()"><i class="fas fa-bullhorn"></i> Broadcast</button> |
| </div> |
| <div id="utw" style="overflow-x:auto;margin-top:8px"></div> |
| |
| <div id="adm-pending-wrap" style="display:none;margin-top:10px"> |
| <div style="font-size:.72rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);margin-bottom:8px;display:flex;align-items:center;justify-content:space-between"> |
| <span>💰 Pending Payments</span> |
| <button onclick="loadPendingPayments()" style="background:none;border:none;color:var(--primary);font-size:.75rem;cursor:pointer;font-family:var(--F);font-weight:600"><i class="fas fa-sync-alt"></i> Refresh</button> |
| </div> |
| <div id="adm-pending-list"></div> |
| </div> |
| </div> |
|
|
| </div> |
| </div> |
|
|
| |
| <div id="section-srt" style="display:none"> |
| <div class="ab"> |
|
|
| |
| <div class="card" style="padding:0;overflow:hidden" id="srt-step1-card"> |
|
|
| |
| <div style="padding:10px 14px 0;display:flex;align-items:center;gap:8px"> |
| <div style="width:22px;height:22px;border-radius:50%;background:linear-gradient(135deg,#7c3aed,#5b4cf5);display:flex;align-items:center;justify-content:center;font-size:.7rem;font-weight:800;color:#fff;flex-shrink:0">1</div> |
| <span style="font-size:.78rem;font-weight:700;color:var(--text)">Video ထည့်ပါ</span> |
| </div> |
|
|
| |
| <div style="padding:8px 12px 0"> |
| <div class="uw"> |
| <i class="fas fa-link uico"></i> |
| <input class="uinp" id="srt-vurl" placeholder="YouTube / TikTok / XHS / Facebook URL…" oninput="onSrtUrl(this.value)" style="padding-right:80px"> |
| <span class="ubadge" id="srt-ubadge"></span> |
| <button class="paste-btn" onclick="srtPaste()"><i class="fas fa-paste"></i> Paste</button> |
| </div> |
| <div class="prev-loading" id="srt-prev-loading" style="display:none"><i class="fas fa-spinner sp"></i> Loading…</div> |
| <div class="orsep" style="margin:6px 0">or upload file</div> |
| <div class="upz" style="margin-bottom:8px"> |
| <input type="file" id="srt-vfile" accept="video/*" onchange="onSrtVFile(this)"> |
| <div class="upz-t"><i class="fas fa-cloud-upload-alt" style="margin-right:6px;font-size:1.1rem"></i>Click or drag video file here</div> |
| <div class="upz-n" id="srt-upzn"></div> |
| </div> |
| </div> |
|
|
| |
| <div style="padding:10px 12px 12px"> |
| <button class="btn-auto" id="srt-gen-btn" onclick="doGenerateSrt()" style="background:linear-gradient(135deg,#7c3aed,#5b4cf5)"> |
| <i class="fas fa-robot"></i> Generate Myanmar SRT |
| <span style="opacity:.7;font-size:.78em;background:rgba(255,255,255,.2);padding:2px 8px;border-radius:10px">1 Coin</span> |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div class="pc" id="srt-gen-pc" style="display:none"> |
| <div class="ph"><span class="ptit">🤖 SRT ထုတ်နေသည်…</span><span class="ptmr" id="srt-gen-ptmr">⏱ 0s</span></div> |
| <div class="pb-bg"><div class="pb" id="srt-gen-pb"></div></div> |
| <div class="pmsg" id="srt-gen-pmsg">Starting…</div> |
| <div class="psteps"> |
| <span class="ps" id="srt-ps-dl">📥 Download</span> |
| <span class="ps" id="srt-ps-ai">🤖 Gemini SRT</span> |
| </div> |
| </div> |
|
|
| |
| <div id="srt-step2-wrap" style="display:none"> |
|
|
| |
| <div style="display:flex;align-items:center;gap:8px;padding:4px 4px 0"> |
| <div style="width:22px;height:22px;border-radius:50%;background:linear-gradient(135deg,#0ea5e9,#6366f1);display:flex;align-items:center;justify-content:center;font-size:.7rem;font-weight:800;color:#fff;flex-shrink:0">2</div> |
| <span style="font-size:.78rem;font-weight:700;color:var(--text)">SRT စစ်ဆေးပြီး Render လုပ်ပါ</span> |
| </div> |
|
|
| |
| <div class="card" style="padding:12px"> |
| <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px"> |
| <div style="font-size:.65rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:#94a3b8;display:flex;align-items:center;gap:5px"> |
| <i class="fas fa-closed-captioning" style="color:#a78bfa"></i>Myanmar SRT Preview |
| <span id="srt-block-count" style="background:rgba(167,139,250,.15);color:#a78bfa;border-radius:10px;padding:1px 7px;font-size:.62rem"></span> |
| </div> |
| <button onclick="doRegenSrt()" style="padding:5px 10px;background:rgba(251,191,36,.1);border:1px solid rgba(251,191,36,.3);border-radius:7px;color:#fbbf24;font-family:var(--F);font-size:.7rem;font-weight:700;cursor:pointer;display:flex;align-items:center;gap:4px;transition:.2s"> |
| <i class="fas fa-redo"></i> Regenerate |
| </button> |
| </div> |
| |
| <div id="srt-first-sub-wrap" style="display:none;margin-bottom:8px;padding:7px 10px;background:rgba(167,139,250,.08);border:1px solid rgba(167,139,250,.2);border-radius:8px"> |
| <div style="font-size:.58rem;font-weight:700;color:rgba(167,139,250,.6);text-transform:uppercase;letter-spacing:.06em;margin-bottom:3px">First Subtitle</div> |
| <div id="srt-first-sub-txt" style="font-size:.82rem;color:#e0f2fe;font-weight:500"></div> |
| </div> |
| |
| <div class="srt-preview" id="srt-preview-box" style="max-height:180px"></div> |
| </div> |
|
|
| |
| <div style="margin-bottom:12px;margin-top:4px"> |
| <button class="btn-auto" id="srt-burn-btn" onclick="doProcessSrt()" style="background:linear-gradient(135deg,#0ea5e9,#6366f1)"> |
| <i class="fas fa-film"></i> Render Video |
| </button> |
| </div> |
|
|
| |
| <div class="pc" id="srt-pc" style="display:none"> |
| <div class="ph"><span class="ptit">⚙️ Render လုပ်နေသည်…</span><span class="ptmr" id="srt-ptmr">⏱ 0s</span></div> |
| <div class="pb-bg"><div class="pb" id="srt-pb"></div></div> |
| <div class="pmsg" id="srt-pmsg">Starting…</div> |
| <div class="psteps"> |
| <span class="ps" id="srt-ps-burn">🎬 Render</span> |
| </div> |
| </div> |
|
|
| |
| <div class="rc" id="srt-rc" style="display:none"> |
| <div class="rvw"><video id="srt-rvid" controls playsinline></video></div> |
| <div class="rb"> |
| <div class="ract"> |
| <button class="rb-btn rb-g" onclick="srtDlVideo()"><i class="fas fa-download"></i> Download MP4</button> |
| <button class="rb-btn" style="background:var(--bg);border:1.5px solid var(--border);color:var(--text)" onclick="srtReset()"><i class="fas fa-redo"></i> Reset</button> |
| </div> |
| </div> |
| </div> |
|
|
| </div> |
|
|
| |
| <div class="card" style="padding:0;overflow:hidden"> |
|
|
| |
| <div class="canvas-wrap" id="srt-canvas-wrap" style="margin-top:0;border-radius:0;position:relative"> |
| <div class="canvas-bg-blur" id="srt-canvas-bg-blur"></div> |
| <div class="canvas-video-wrap" id="srt-canvas-video-wrap" style="position:relative;min-height:130px;border-radius:10px;overflow:hidden"> |
| <video id="srt-pvid" controls playsinline style="width:100%;height:auto;max-height:480px;object-fit:contain;display:none"></video> |
| <img id="srt-pthumb" style="display:none;width:100%;height:auto;object-fit:cover;display:block;border-radius:10px;transition:transform .3s ease" alt=""> |
| |
| <div id="srt-canvas-inner" class="canvas-inner" style="position:absolute;inset:0;pointer-events:none;z-index:10"> |
| |
| <div id="srt-db-blur" style="display:none;position:absolute;left:20px;top:80px;width:160px;height:90px;pointer-events:auto;cursor:move;border:2px dashed rgba(255,100,100,.85);background:rgba(0,0,0,.25);border-radius:6px;box-sizing:border-box;touch-action:none"> |
| <div style="position:absolute;top:2px;left:0;right:0;text-align:center;font-size:.58rem;font-weight:700;color:rgba(255,160,160,.9);letter-spacing:.05em;pointer-events:none;user-select:none">BLUR ZONE</div> |
| <div class="rh" style="position:absolute;right:0;bottom:0;width:18px;height:18px;cursor:se-resize;background:rgba(255,100,100,.6);border-radius:3px 0 4px 0;display:flex;align-items:center;justify-content:center"> |
| <i class="fas fa-expand-arrows-alt" style="font-size:.5rem;color:#fff;pointer-events:none"></i> |
| </div> |
| </div> |
| |
| <div id="srt-db-wm" style="display:none;position:absolute;left:10px;bottom:40px;width:180px;height:36px;pointer-events:auto;cursor:move;border:2px dashed rgba(251,191,36,.85);background:rgba(0,0,0,.22);border-radius:6px;box-sizing:border-box;touch-action:none"> |
| <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;pointer-events:none"> |
| <span id="srt-db-wm-txt" style="font-size:.8rem;font-weight:700;color:#fef9c3;text-shadow:0 1px 3px rgba(0,0,0,.9);white-space:nowrap;overflow:hidden;max-width:92%;pointer-events:none">Watermark</span> |
| </div> |
| <div class="rh" style="position:absolute;right:0;bottom:0;width:16px;height:16px;cursor:se-resize;background:rgba(251,191,36,.6);border-radius:3px 0 4px 0;display:flex;align-items:center;justify-content:center"> |
| <i class="fas fa-expand-arrows-alt" style="font-size:.45rem;color:#fff;pointer-events:none"></i> |
| </div> |
| </div> |
| |
| <div id="srt-db-logo" style="display:none;position:absolute;right:10px;top:10px;width:72px;height:72px;pointer-events:auto;cursor:move;border:2px dashed rgba(52,211,153,.85);background:rgba(0,0,0,.22);border-radius:6px;box-sizing:border-box;touch-action:none;background-size:contain;background-repeat:no-repeat;background-position:center"> |
| <div id="srt-db-logo-lbl" style="position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;pointer-events:none"> |
| <i class="fas fa-image" style="font-size:.9rem;color:rgba(52,211,153,.8)"></i> |
| <span style="font-size:.52rem;font-weight:700;color:rgba(52,211,153,.8)">LOGO</span> |
| </div> |
| <div class="rh" style="position:absolute;right:0;bottom:0;width:16px;height:16px;cursor:se-resize;background:rgba(52,211,153,.6);border-radius:3px 0 4px 0;display:flex;align-items:center;justify-content:center"> |
| <i class="fas fa-expand-arrows-alt" style="font-size:.45rem;color:#fff;pointer-events:none"></i> |
| </div> |
| </div> |
| |
| <div id="srt-sub-overlay" style="display:none;position:absolute;left:0;right:0;width:100%;text-align:center;pointer-events:none;top:85%;transform:translateY(-50%)"> |
| <span id="srt-sub-preview-span" style="display:inline-block;padding:0 8px;line-height:1.25;font-weight:700;font-family:'Noto Sans Myanmar','Padauk',sans-serif;color:#fff;text-shadow:0 0 4px #000,0 1px 6px #000,-1px -1px 0 #000,1px 1px 0 #000;border-radius:4px;max-width:95%;font-size:.9rem;pointer-events:none"> စာတန်းထိုး</span> |
| </div> |
| </div> |
| </div> |
| <div id="srt-canvas-ph" style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:130px;color:rgba(255,255,255,.3);font-size:.78rem;gap:6px"> |
| <i class="fas fa-film" style="font-size:1.8rem;opacity:.4"></i> |
| <span>Preview appears after URL is pasted</span> |
| </div> |
| </div> |
|
|
| |
| <div style="background:linear-gradient(135deg,#0e2233,#0a1f2e);padding:10px 12px"> |
|
|
| |
| <div style="margin-bottom:10px"> |
| <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:3px"> |
| <div style="font-size:.58rem;font-weight:700;text-transform:uppercase;color:#64748b">Subtitle Position</div> |
| <span id="srt-sub-pos-lbl" style="font-size:.68rem;font-weight:700;color:#38bdf8">90%</span> |
| </div> |
| <input type="range" id="srt-sub-pos" min="5" max="95" value="90" |
| oninput="document.getElementById('srt-sub-pos-lbl').textContent=this.value+'%';syncSrtSubPreview()" |
| style="width:100%;accent-color:#38bdf8;cursor:pointer"> |
| <div style="display:flex;justify-content:space-between;font-size:.58rem;color:#475569"><span>Top (5%)</span><span>Bottom (90%)</span></div> |
| </div> |
|
|
| |
| <div style="height:1px;background:rgba(255,255,255,.07);margin:0 0 10px"></div> |
|
|
| |
| <div style="font-size:.62rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:#94a3b8;margin-bottom:8px;display:flex;align-items:center;gap:5px"> |
| <i class="fas fa-layer-group" style="color:#a78bfa"></i>Canvas Overlays |
| </div> |
| <div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:5px;margin-bottom:10px"> |
|
|
| |
| <div style="background:rgba(251,191,36,.05);border:1.5px solid rgba(251,191,36,.18);border-radius:10px;padding:8px 4px;display:flex;flex-direction:column;align-items:center;gap:4px"> |
| <div style="width:26px;height:26px;border-radius:7px;background:rgba(251,191,36,.12);display:flex;align-items:center;justify-content:center"> |
| <i class="fas fa-stamp" style="color:rgba(251,191,36,.9);font-size:.8rem"></i> |
| </div> |
| <div style="font-size:.58rem;font-weight:700;color:rgba(251,191,36,.8);text-align:center">Watermark</div> |
| <button id="srt-ov-wm" onclick="srtOvToggle('wm')" style="width:100%;padding:3px 0;font-size:.62rem;font-weight:700;font-family:var(--F);border-radius:6px;border:1.5px solid rgba(251,191,36,.35);background:rgba(251,191,36,.07);color:rgba(251,191,36,.8);cursor:pointer;transition:.2s">Off</button> |
| </div> |
|
|
| |
| <div style="background:rgba(52,211,153,.05);border:1.5px solid rgba(52,211,153,.18);border-radius:10px;padding:8px 4px;display:flex;flex-direction:column;align-items:center;gap:4px"> |
| <div style="width:26px;height:26px;border-radius:7px;background:rgba(52,211,153,.12);display:flex;align-items:center;justify-content:center"> |
| <i class="fas fa-image" style="color:rgba(52,211,153,.9);font-size:.8rem"></i> |
| </div> |
| <div style="font-size:.58rem;font-weight:700;color:rgba(52,211,153,.8);text-align:center">Logo</div> |
| <button id="srt-ov-logo" onclick="srtOvToggle('logo')" style="width:100%;padding:3px 0;font-size:.62rem;font-weight:700;font-family:var(--F);border-radius:6px;border:1.5px solid rgba(52,211,153,.35);background:rgba(52,211,153,.07);color:rgba(52,211,153,.8);cursor:pointer;transition:.2s">Off</button> |
| </div> |
|
|
| |
| <div style="background:rgba(239,68,68,.04);border:1.5px solid rgba(252,165,165,.18);border-radius:10px;padding:8px 4px;display:flex;flex-direction:column;align-items:center;gap:4px"> |
| <div style="width:26px;height:26px;border-radius:7px;background:rgba(239,68,68,.1);display:flex;align-items:center;justify-content:center"> |
| <i class="fas fa-eraser" style="color:rgba(252,165,165,.9);font-size:.8rem"></i> |
| </div> |
| <div style="font-size:.58rem;font-weight:700;color:rgba(252,165,165,.8);text-align:center">Blur</div> |
| <button id="srt-ov-blur" onclick="srtOvToggle('blur')" style="width:100%;padding:3px 0;font-size:.62rem;font-weight:700;font-family:var(--F);border-radius:6px;border:1.5px solid rgba(252,165,165,.35);background:rgba(239,68,68,.07);color:rgba(252,165,165,.8);cursor:pointer;transition:.2s">Off</button> |
| </div> |
|
|
| |
| <div style="display:none;background:rgba(139,92,246,.05);border:1.5px solid rgba(139,92,246,.18);border-radius:10px;padding:8px 4px;flex-direction:column;align-items:center;gap:4px"> |
| <div style="width:26px;height:26px;border-radius:7px;background:rgba(139,92,246,.12);display:flex;align-items:center;justify-content:center"> |
| <i class="fas fa-search-plus" style="color:rgba(167,139,250,.9);font-size:.8rem"></i> |
| </div> |
| <div style="font-size:.58rem;font-weight:700;color:rgba(167,139,250,.8);text-align:center">G.Zoom</div> |
| <button id="srt-ov-zoom" onclick="srtOvToggle('zoom')" style="width:100%;padding:3px 0;font-size:.62rem;font-weight:700;font-family:var(--F);border-radius:6px;border:1.5px solid rgba(139,92,246,.35);background:rgba(139,92,246,.07);color:rgba(167,139,250,.8);cursor:pointer;transition:.2s">Off</button> |
| </div> |
| </div> |
|
|
| |
| |
| <div id="srt-ov-wm-wrap" style="display:none;margin-bottom:8px;padding:10px 12px;background:rgba(251,191,36,.05);border:1px solid rgba(251,191,36,.22);border-radius:10px"> |
| <div style="font-size:.58rem;font-weight:700;color:rgba(251,191,36,.7);letter-spacing:.06em;text-transform:uppercase;margin-bottom:6px"> |
| <i class="fas fa-stamp" style="margin-right:4px"></i>Watermark Text |
| </div> |
| <input id="srt-wm-text" class="winp" placeholder="@YourPage သို့မဟုတ် Channel Name" maxlength="40" |
| oninput="document.getElementById('srt-db-wm-txt').textContent=this.value||'Watermark'" |
| style="width:100%;font-size:.8rem;background:rgba(255,255,255,.06);border-color:rgba(251,191,36,.3);color:#fef9c3;box-sizing:border-box"> |
| <div style="font-size:.6rem;color:rgba(251,191,36,.4);margin-top:5px;display:flex;align-items:center;gap:4px"> |
| <i class="fas fa-hand-pointer"></i>Preview canvas ပေါ်မှာ drag ပြီး position ချိန်ပါ |
| </div> |
| </div> |
|
|
| |
| <div id="srt-ov-logo-wrap" style="display:none;margin-bottom:8px;padding:10px 12px;background:rgba(52,211,153,.05);border:1px solid rgba(52,211,153,.22);border-radius:10px"> |
| <div style="font-size:.58rem;font-weight:700;color:rgba(52,211,153,.7);letter-spacing:.06em;text-transform:uppercase;margin-bottom:6px"> |
| <i class="fas fa-image" style="margin-right:4px"></i>Logo Image |
| </div> |
| <label style="display:flex;align-items:center;gap:8px;background:rgba(255,255,255,.04);border:1px dashed rgba(52,211,153,.3);border-radius:8px;padding:8px 10px;cursor:pointer;transition:.2s"> |
| <i class="fas fa-cloud-upload-alt" style="color:rgba(52,211,153,.7);font-size:1rem"></i> |
| <span id="srt-logo-name" style="color:rgba(52,211,153,.55);font-size:.78rem;flex:1">Click to upload PNG / JPG</span> |
| <input type="file" id="srt-logo-file" accept="image/*" style="display:none" onchange="onSrtLogoFile(this)"> |
| </label> |
| <div style="font-size:.6rem;color:rgba(52,211,153,.4);margin-top:5px;display:flex;align-items:center;gap:4px"> |
| <i class="fas fa-hand-pointer"></i>Canvas ပေါ်မှာ drag / resize ပြီး position ချိန်ပါ |
| </div> |
| </div> |
|
|
| |
| <div id="srt-ov-blur-hint" style="display:none;margin-bottom:8px;padding:10px 12px;background:rgba(239,68,68,.04);border:1px solid rgba(239,68,68,.2);border-radius:10px"> |
| <div style="font-size:.6rem;color:rgba(252,165,165,.7);display:flex;align-items:center;gap:5px"> |
| <i class="fas fa-hand-pointer"></i>Preview canvas ပေါ်မှာ blur zone ကို drag နှင့် resize လုပ်ပါ |
| </div> |
| </div> |
|
|
| |
|
|
| |
| <div style="height:1px;background:rgba(255,255,255,.07);margin:4px 0 8px"></div> |
|
|
| |
| <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:5px;margin-bottom:8px"> |
| <button id="srt-btn-flip" onclick="srtToggleOpt('flip')" style="padding:6px 2px;font-size:.68rem;font-weight:700;font-family:var(--F);border-radius:7px;border:1.5px solid rgba(255,255,255,.12);background:rgba(255,255,255,.05);color:rgba(255,255,255,.55);cursor:pointer;transition:.2s"><i class="fas fa-arrows-alt-h"></i> Flip</button> |
| <button id="srt-btn-col" onclick="srtToggleOpt('col')" style="padding:6px 2px;font-size:.68rem;font-weight:700;font-family:var(--F);border-radius:7px;border:1.5px solid rgba(255,255,255,.12);background:rgba(255,255,255,.05);color:rgba(255,255,255,.55);cursor:pointer;transition:.2s"><i class="fas fa-adjust"></i> Color</button> |
| <button id="srt-ov-audio" onclick="srtOvToggle('audio')" style="padding:6px 2px;font-size:.68rem;font-weight:700;font-family:var(--F);border-radius:7px;border:1.5px solid rgba(59,130,246,.35);background:rgba(59,130,246,.07);color:rgba(96,165,250,.8);cursor:pointer;transition:.2s"><i class="fas fa-volume-up"></i> Audio</button> |
| </div> |
|
|
| |
| <div style="height:1px;background:rgba(255,255,255,.07);margin:4px 0 8px"></div> |
|
|
| |
| |
| <div style="font-size:.62rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:#94a3b8;margin-bottom:8px;display:flex;align-items:center;gap:5px"> |
| <i class="fas fa-closed-captioning" style="color:#60a5fa"></i>Subtitle Settings |
| </div> |
| <div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-bottom:8px"> |
| <div style="background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:8px;padding:7px 9px"> |
| <div style="font-size:.58rem;font-weight:700;text-transform:uppercase;color:#64748b;margin-bottom:4px">Color</div> |
| <select id="srt-sub-color" onchange="syncSrtSubPreview()" style="width:100%;background:transparent;border:none;color:#e0f2fe;font-family:var(--F);font-size:.78rem;font-weight:500;outline:none;cursor:pointer"> |
| <option value="white" selected>⬜ White</option> |
| <option value="yellow">🟡 Yellow</option> |
| <option value="cyan">🔵 Cyan</option> |
| <option value="green">🟢 Green</option> |
| <option value="orange">🟠 Orange</option> |
| <option value="pink">🩷 Pink</option> |
| <option value="red">🔴 Red</option> |
| <option value="lime">💚 Lime</option> |
| <option value="hotpink">💗 Hot Pink</option> |
| <option value="gold">✨ Gold</option> |
| <option value="violet">💜 Violet</option> |
| <option value="deepskyblue">🩵 Sky Blue</option> |
| <option value="coral">🪸 Coral</option> |
| </select> |
| </div> |
| <div style="background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:8px;padding:7px 9px"> |
| <div style="font-size:.58rem;font-weight:700;text-transform:uppercase;color:#64748b;margin-bottom:4px">Font Size</div> |
| <select id="srt-sub-size" onchange="syncSrtSubPreview()" style="width:100%;background:transparent;border:none;color:#e0f2fe;font-family:var(--F);font-size:.78rem;font-weight:500;outline:none;cursor:pointer"> |
| <option value="0.0350">XSmall</option> |
| <option value="0.0469" selected>Small</option> |
| <option value="0.0625">Medium</option> |
| <option value="0.0781">Large</option> |
| </select> |
| </div> |
| <div style="background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:8px;padding:7px 9px"> |
| <div style="font-size:.58rem;font-weight:700;text-transform:uppercase;color:#64748b;margin-bottom:4px">Style</div> |
| <select id="srt-sub-style" onchange="syncSrtSubPreview()" style="width:100%;background:transparent;border:none;color:#e0f2fe;font-family:var(--F);font-size:.78rem;font-weight:500;outline:none;cursor:pointer"> |
| <option value="outline" selected>Outline</option> |
| <option value="box">Box</option> |
| <option value="shadow">Shadow</option> |
| <option value="glow">Glow</option> |
| <option value="stroke">Stroke</option> |
| <option value="plain">Plain</option> |
| </select> |
| </div> |
| <div style="background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:8px;padding:7px 9px"> |
| <div style="font-size:.58rem;font-weight:700;text-transform:uppercase;color:#64748b;margin-bottom:4px">Aspect Ratio</div> |
| <select id="srt-crop" onchange="onSrtCropChange()" style="width:100%;background:transparent;border:none;color:#e0f2fe;font-family:var(--F);font-size:.78rem;font-weight:500;outline:none;cursor:pointer"> |
| <option value="original" selected>Original</option> |
| <option value="9:16">9:16</option> |
| <option value="16:9">16:9</option> |
| <option value="1:1">1:1</option> |
| </select> |
| </div> |
| </div> |
|
|
| </div> |
| </div> |
|
|
| </div> |
| </div> |
|
|
|
|
| </div> |
|
|
| <div id="toast"></div> |
| <audio id="pva"></audio> |
|
|
| <script> |
| |
| let U='',COINS=0,ISADM=false; |
| let OUT_URL='',OUT_CAP='',OUT_TAGS=''; |
| let SSE=null,TICK=null,T0=0,COIN_POLL=null; |
| let FLIP_ON=false,COL_ON=false,BGM_ON=false,SUB_ON=false; |
| function toggleAdvanced(){ |
| const w=document.getElementById('adv-wrap'); |
| const ic=document.getElementById('adv-icon'); |
| const open=w.style.display==='none'; |
| w.style.display=open?'block':'none'; |
| ic.style.transform=open?'rotate(90deg)':'rotate(0deg)'; |
| } |
| function toggleSub(){ |
| SUB_ON=!SUB_ON; |
| const btn=document.getElementById('btn-sub'); |
| const wrap=document.getElementById('sub-wrap'); |
| btn.textContent=SUB_ON?'On':'Off'; |
| btn.className='wm-toggle'+(SUB_ON?' on':''); |
| btn.style.borderColor=SUB_ON?'#38bdf8':''; |
| wrap.style.display=SUB_ON?'block':'none'; |
| |
| _syncSubPreview(); |
| } |
| function _syncSubPreview(){ |
| const box=document.getElementById('db-sub'); |
| if(!box)return; |
| if(!SUB_ON){box.style.display='none';return;} |
| |
| const posVal=parseInt((document.getElementById('sub-pos')||{value:85}).value)||85; |
| |
| const ci=document.getElementById('canvas-inner'); |
| if(!ci){return;} |
| const ciR=ci.getBoundingClientRect(); |
| |
| const topPx=ciR.height*posVal/100; |
| box.style.top=topPx+'px'; |
| box.style.transform='translateY(-50%)'; |
| box.style.display='block'; |
| } |
| let CACHE_KEY=''; |
| let URL_DEBOUNCE=null; |
| const OV={sub:{on:false}}; |
| |
| const EV={ |
| my:[ |
| {id:'my-MM-ThihaNeural', name:'Thiha (Male)'}, |
| {id:'my-MM-NilarNeural', name:'Nilar (Female)'}, |
| {id:'fr-FR-RemyMultilingualNeural', name:'Remy (Male)'}, |
| |
| {id:'en-US-AndrewMultilingualNeural', name:'Andrew (Male)'}, |
| {id:'en-US-AvaMultilingualNeural', name:'Ava (Female)'}, |
| {id:'en-US-BrianMultilingualNeural', name:'Brian (Male)'}, |
| {id:'en-US-EmmaMultilingualNeural', name:'Emma (Female)'}, |
| {id:'en-US-JennyMultilingualNeural', name:'Jenny (Female)'}, |
| {id:'en-US-RyanMultilingualNeural', name:'Ryan (Male)'}, |
| {id:'zh-CN-XiaoxiaoMultilingualNeural', name:'Xiaoxiao (Female)'}, |
| {id:'zh-CN-YunxiMultilingualNeural', name:'Yunxi (Male)'}, |
| {id:'de-DE-FlorianMultilingualNeural', name:'Florian (Male)'}, |
| {id:'de-DE-SeraphinaMultilingualNeural',name:'Seraphina (Female)'}, |
| {id:'fr-FR-VivienneMultilingualNeural', name:'Vivienne (Female)'} |
| ], |
| th:[ |
| {id:'th-TH-NiwatNeural', name:'Niwat (Male)'}, |
| {id:'th-TH-PremwadeeNeural', name:'Premwadee (Female)'}, |
| {id:'th-TH-AcharaNeural', name:'Achara (Female)'}, |
| |
| {id:'en-US-AndrewMultilingualNeural', name:'Andrew (Male)'}, |
| {id:'en-US-AvaMultilingualNeural', name:'Ava (Female)'}, |
| {id:'en-US-BrianMultilingualNeural', name:'Brian (Male)'}, |
| {id:'en-US-EmmaMultilingualNeural', name:'Emma (Female)'}, |
| {id:'en-US-JennyMultilingualNeural', name:'Jenny (Female)'}, |
| {id:'en-US-RyanMultilingualNeural', name:'Ryan (Male)'}, |
| {id:'zh-CN-XiaoxiaoMultilingualNeural', name:'Xiaoxiao (Female)'}, |
| {id:'zh-CN-YunxiMultilingualNeural', name:'Yunxi (Male)'}, |
| {id:'de-DE-FlorianMultilingualNeural', name:'Florian (Male)'}, |
| {id:'de-DE-SeraphinaMultilingualNeural',name:'Seraphina (Female)'}, |
| {id:'fr-FR-RemyMultilingualNeural', name:'Remy (Male)'}, |
| {id:'fr-FR-VivienneMultilingualNeural', name:'Vivienne (Female)'} |
| ], |
| en:[ |
| {id:'en-US-GuyNeural', name:'Guy – US (Male)'}, |
| {id:'en-US-JennyNeural', name:'Jenny – US (Female)'}, |
| {id:'en-US-AriaNeural', name:'Aria – US (Female)'}, |
| {id:'en-US-DavisNeural', name:'Davis – US (Male)'}, |
| {id:'en-US-JasonNeural', name:'Jason – US (Male)'}, |
| {id:'en-US-SaraNeural', name:'Sara – US (Female)'}, |
| {id:'en-US-TonyNeural', name:'Tony – US (Male)'}, |
| {id:'en-US-AndrewNeural', name:'Andrew – US (Male)'}, |
| {id:'en-US-EmmaNeural', name:'Emma – US (Female)'}, |
| {id:'en-GB-RyanNeural', name:'Ryan – GB (Male)'}, |
| {id:'en-GB-SoniaNeural', name:'Sonia – GB (Female)'}, |
| {id:'en-GB-LibbyNeural', name:'Libby – GB (Female)'}, |
| {id:'en-AU-NatashaNeural', name:'Natasha – AU (Female)'}, |
| {id:'en-AU-WilliamNeural', name:'William – AU (Male)'}, |
| {id:'en-IN-NeerjaNeural', name:'Neerja – IN (Female)'}, |
| {id:'en-IN-PrabhatNeural', name:'Prabhat – IN (Male)'}, |
| |
| {id:'en-US-AndrewMultilingualNeural', name:'Andrew (Male)'}, |
| {id:'en-US-AvaMultilingualNeural', name:'Ava (Female)'}, |
| {id:'en-US-BrianMultilingualNeural', name:'Brian (Male)'}, |
| {id:'en-US-EmmaMultilingualNeural', name:'Emma (Female)'}, |
| {id:'en-US-JennyMultilingualNeural', name:'Jenny (Female)'}, |
| {id:'en-US-RyanMultilingualNeural', name:'Ryan (Male)'}, |
| {id:'zh-CN-XiaoxiaoMultilingualNeural', name:'Xiaoxiao (Female)'}, |
| {id:'zh-CN-YunxiMultilingualNeural', name:'Yunxi (Male)'}, |
| {id:'de-DE-FlorianMultilingualNeural', name:'Florian (Male)'}, |
| {id:'de-DE-SeraphinaMultilingualNeural',name:'Seraphina (Female)'}, |
| {id:'fr-FR-RemyMultilingualNeural', name:'Remy (Male)'}, |
| {id:'fr-FR-VivienneMultilingualNeural', name:'Vivienne (Female)'} |
| ] |
| }; |
| let GV=[]; |
| |
| |
| addEventListener('DOMContentLoaded', async ()=>{ |
| |
| renderV(); |
| window.addEventListener('resize',()=>{const v=document.getElementById('pvid');if(v&&v.src&&v.videoWidth)syncCanvasInner(v);}); |
| fetch('/api/gemini_voices').then(r=>r.ok?r.json():null).then(d=>{if(d?.ok){GV=d.voices;if(document.getElementById('engine').value==='gemini')renderV();}}).catch(()=>{}); |
| initDrag(); |
| initSrtDrag(); |
| |
| |
| const saved=sessionStorage.getItem('recap_user'); |
| if(saved){try{const s=JSON.parse(saved);if(s.u){eApp(s.u,s.coins,s.is_admin,s.name);return;}}catch(e){}} |
| |
| |
| await new Promise(r=>setTimeout(r,150)); |
| const tg=window.Telegram?.WebApp; |
| const tgUser=tg?.initDataUnsafe?.user; |
| const inTelegram=(tg?.initData||'').length>0||(tgUser&&tgUser.id); |
| |
| if(inTelegram&&tgUser&&tgUser.id){ |
| tg.ready(); tg.expand(); |
| try{ |
| const r=await fetch('/api/auth/telegram',{ |
| method:'POST',headers:{'Content-Type':'application/json'}, |
| body:JSON.stringify({telegram_id:String(tgUser.id),telegram_username:tgUser.username||tgUser.first_name||''}) |
| }); |
| const d=await r.json(); |
| if(d.ok){ |
| const dn=tgUser.first_name||tgUser.username||d.username; |
| sessionStorage.setItem('recap_user',JSON.stringify({u:d.username,coins:d.coins,is_admin:d.is_admin,name:dn,tg_id:String(tgUser.id)})); |
| eApp(d.username,d.coins,d.is_admin,dn); return; |
| } |
| }catch(e){console.error('TG auth error',e);} |
| document.getElementById('login-screen').style.display='flex'; return; |
| } |
| |
| |
| document.getElementById('login-screen').style.display='flex'; |
| fetch('/api/auth/google_enabled').then(r=>r.json()).then(d=>{if(d.enabled)document.getElementById('google-wrap').style.display='block';}).catch(()=>{}); |
| document.addEventListener('keydown',e=>{if(e.key==='Enter'&&document.getElementById('login-screen').style.display!=='none'){if(document.getElementById('pane-li').style.display!=='none')doLogin();else doReg();}}); |
| const sp=new URLSearchParams(location.search); |
| if(sp.get('auth')==='google'){const u=sp.get('u'),n=sp.get('n'),c=parseInt(sp.get('c')||'0'),a=sp.get('a')==='1';if(u){sessionStorage.setItem('recap_user',JSON.stringify({u,coins:c,is_admin:a,name:n}));history.replaceState({},'','/');eApp(u,c,a,n);return;}} |
| else if(sp.get('auth_error')){history.replaceState({},'','/');sam('❌ Google login failed: '+sp.get('auth_error'),false);} |
| }); |
| |
| |
| function stab(t){['li','re'].forEach(x=>{document.getElementById('tab-'+x).classList.toggle('on',x===t);document.getElementById('pane-'+x).style.display=x===t?'':'none';});document.getElementById('am').style.display='none';} |
| function sam(msg,ok){const e=document.getElementById('am');e.textContent=msg;e.className='am '+(ok?'am-ok':'am-err');e.style.display='block';} |
| async function doLogin(){ |
| const u=document.getElementById('l-u').value.trim(),p=document.getElementById('l-p').value; |
| if(!u){sam('❌ Username required',false);return;} |
| const btn=document.querySelector('#pane-li .bp'); |
| if(btn){btn.disabled=true;btn.textContent='⏳ နေပါ...';} |
| try{ |
| const r=await fetch('/api/login',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({username:u,password:p})}); |
| const d=await r.json(); |
| if(d.ok){ |
| sessionStorage.setItem('recap_user',JSON.stringify({u,coins:d.coins,is_admin:d.is_admin})); |
| eApp(u,d.coins,d.is_admin); |
| } else { |
| sam(d.msg||'❌ Login failed',false); |
| if(btn){btn.disabled=false;btn.textContent='Login';} |
| } |
| } catch{ |
| sam('❌ Connection error',false); |
| if(btn){btn.disabled=false;btn.textContent='Login';} |
| } |
| } |
| async function doReg(){ |
| const u=document.getElementById('r-u').value.trim(),p=document.getElementById('r-p').value; |
| const btn=document.querySelector('#pane-re .bp'); |
| if(btn){btn.disabled=true;btn.textContent='⏳ နေပါ...';} |
| try{ |
| const r=await fetch('/api/register',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({username:u,password:p})}); |
| const d=await r.json(); |
| if(d.ok){ |
| sam('✅ '+d.username+' created!',true); |
| setTimeout(()=>{sessionStorage.setItem('recap_user',JSON.stringify({u:d.username,coins:d.coins,is_admin:false}));eApp(d.username,d.coins,false);},700); |
| } else { |
| sam(d.msg||'❌ Failed',false); |
| if(btn){btn.disabled=false;btn.textContent='Register';} |
| } |
| } catch{ |
| sam('❌ Connection error',false); |
| if(btn){btn.disabled=false;btn.textContent='Register';} |
| } |
| } |
| async function fetchAndUpdateCoins(){ |
| if(!U||ISADM)return; |
| try{ |
| const r=await fetch('/api/coins?username='+encodeURIComponent(U)+'&_='+Date.now()); |
| const d=await r.json(); |
| if(d.ok){ |
| if(d.coins!==COINS){ |
| const diff=d.coins-COINS; |
| updC(d.coins); |
| |
| try{ |
| const s=JSON.parse(sessionStorage.getItem('recap_user')||'{}'); |
| s.coins=d.coins; |
| sessionStorage.setItem('recap_user',JSON.stringify(s)); |
| }catch(e){} |
| if(diff>0)toast('🎉 +'+diff+' Coins ရောက်ပြီ! လက်ကျန်: '+d.coins); |
| } |
| } |
| }catch(e){} |
| } |
| function startCoinPoll(){ |
| if(COIN_POLL)clearInterval(COIN_POLL); |
| fetchAndUpdateCoins(); |
| COIN_POLL=setInterval(fetchAndUpdateCoins,5000); |
| } |
| function stopCoinPoll(){if(COIN_POLL){clearInterval(COIN_POLL);COIN_POLL=null;}} |
| function eApp(u,coins,isAdm,displayName){ |
| U=u;COINS=coins;ISADM=isAdm; |
| document.getElementById('login-screen').style.display='none'; |
| document.getElementById('app-screen').style.display='block'; |
| const dn=displayName||u; |
| updC(coins); |
| document.getElementById('dun').textContent=dn; |
| document.getElementById('drl').textContent=isAdm?'👑 Admin':'Member'; |
| document.getElementById('dav').textContent=dn[0].toUpperCase(); |
| document.getElementById('dcoins').textContent=isAdm?'∞':coins; |
| fetch('/api/config').then(r=>r.json()).then(d=>{if(d.admin_tg)window._ADMIN_TG=d.admin_tg;}).catch(()=>{}); |
| 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='∞';} |
| else{startCoinPoll();} |
| renderV(); |
| window.addEventListener('resize',()=>{const v=document.getElementById('pvid');if(v.src&&v.videoWidth)syncCanvasInner(v);}); |
| fetch('/api/gemini_voices').then(r=>r.ok?r.json():null).then(d=>{if(d?.ok){GV=d.voices;if(document.getElementById('engine').value==='gemini')renderV();}}).catch(()=>{}); |
| initDrag(); |
| } |
| function doLogout(){ |
| stopCoinPoll(); |
| U='';COINS=0;ISADM=false; |
| sessionStorage.removeItem('recap_user'); |
| document.getElementById('app-screen').style.display='none'; |
| document.getElementById('login-screen').style.display='flex'; |
| cdw(); |
| } |
| |
| |
| let _pkgs=[], _pkgsMmk=[], _pkgsThb=[], _selPkg=null, _slipB64=null, _payInited=false, _selMeth=null, _payInfo={}, _curCurrency='mmk'; |
| |
| async function openPay(){ |
| document.getElementById('pov').classList.add('on'); |
| if(!_payInited){ await initPayModal(); _payInited=true; } |
| } |
| function closePay(){ |
| document.getElementById('pov').classList.remove('on'); |
| |
| goPayStep(1); |
| } |
| |
| function showPayTab(t){ |
| document.getElementById('ptab-buy').classList.toggle('on',t==='buy'); |
| document.getElementById('ptab-his').classList.toggle('on',t==='his'); |
| document.getElementById('ptab-buy-content').style.display=t==='buy'?'':'none'; |
| document.getElementById('ptab-his-content').style.display=t==='his'?'':'none'; |
| if(t==='his') loadPayHistory(); |
| } |
| |
| async function initPayModal(){ |
| const FB_MMK=[ |
| {coins:10,price:'12,000 MMK',price_thb:100,desc:'Process 10 ကြိမ်'}, |
| {coins:20,price:'24,000 MMK',price_thb:200,desc:'Process 20 ကြိမ်'}, |
| {coins:30,price:'36,000 MMK',price_thb:300,desc:'Process 30 ကြိမ်'}, |
| {coins:60,price:'72,000 MMK',price_thb:600,desc:'Process 60 ကြိမ်'}, |
| ]; |
| const FB_THB=[ |
| {coins:10,price:'100 THB',price_thb:100,desc:'Process 10 ကြိမ်'}, |
| {coins:20,price:'200 THB',price_thb:200,desc:'Process 20 ကြိမ်'}, |
| {coins:30,price:'300 THB',price_thb:300,desc:'Process 30 ကြိမ်'}, |
| {coins:60,price:'600 THB',price_thb:600,desc:'Process 60 ကြိမ်'}, |
| ]; |
| try{ |
| const r=await fetch('/api/payment/packages').then(r=>r.json()); |
| |
| if(r.packages&&r.packages.length){ |
| _pkgsMmk=r.packages.map(p=>({coins:p.coins,price:p.price,price_thb:p.price_thb||0,desc:p.desc})); |
| _pkgsThb=r.packages.map(p=>({coins:p.coins,price:(p.price_thb||'—')+' THB',price_thb:p.price_thb||0,desc:p.desc})); |
| } else { |
| _pkgsMmk=FB_MMK; _pkgsThb=FB_THB; |
| } |
| _payInfo={ |
| kbz_name:r.kbz_name||'—', kbz_number:r.kbz_number||'—', |
| kbz_qr_url:r.kbz_qr_url||'', |
| scb_name:r.scb_name||'—', scb_number:r.scb_number||'—', |
| promptpay:r.promptpay||'—', |
| truemoney_name:r.truemoney_name||'—', truemoney_number:r.truemoney_number||'—', |
| truemoney_qr_url:r.truemoney_qr_url||'' |
| }; |
| }catch(e){ |
| _pkgsMmk=FB_MMK; _pkgsThb=FB_THB; |
| _payInfo={kbz_name:'—',kbz_number:'—',kbz_qr_url:'',scb_name:'—',scb_number:'—',promptpay:'—',truemoney_name:'—',truemoney_number:'—',truemoney_qr_url:''}; |
| } |
| renderPkgs('mmk'); |
| } |
| |
| function renderPkgs(currency){ |
| _curCurrency=currency||_curCurrency; |
| _pkgs=_curCurrency==='thb'?_pkgsThb:_pkgsMmk; |
| _selPkg=null; |
| document.getElementById('buy-now-btn').disabled=true; |
| |
| const mmkBtn=document.getElementById('ctab-mmk'); |
| const thbBtn=document.getElementById('ctab-thb'); |
| if(mmkBtn&&thbBtn){ |
| const onS='flex:1;padding:10px 8px;border-radius:12px;border:2px solid rgba(139,92,246,.5);background:rgba(124,58,237,.2);color:#c4b5fd;font-family:var(--F);font-size:.82rem;font-weight:800;cursor:pointer;transition:.2s;display:flex;align-items:center;justify-content:center;gap:6px'; |
| const offS='flex:1;padding:10px 8px;border-radius:12px;border:2px solid rgba(255,255,255,.08);background:rgba(255,255,255,.04);color:rgba(255,255,255,.35);font-family:var(--F);font-size:.82rem;font-weight:800;cursor:pointer;transition:.2s;display:flex;align-items:center;justify-content:center;gap:6px'; |
| mmkBtn.style.cssText=_curCurrency==='mmk'?onS:offS; |
| thbBtn.style.cssText=_curCurrency==='thb'?onS:offS; |
| mmkBtn.innerHTML='<span style="font-size:1.1rem">🇲🇲</span> MMK'; |
| thbBtn.innerHTML='<span style="font-size:1.1rem">🇹🇭</span> THB'; |
| } |
| const icons=['🥉','🥈','🥇','💎']; |
| const tiers=['Starter','Basic','Pro','Unlimited']; |
| const pBg =['rgba(255,255,255,.07)','rgba(139,92,246,.2)','rgba(251,146,60,.15)','rgba(251,191,36,.15)']; |
| const pClr =['rgba(255,255,255,.75)','#c4b5fd','#fb923c','#fbbf24']; |
| document.getElementById('ppkgs').innerHTML=_pkgs.map((p,i)=>` |
| <div class="ppkg${i===1?' pop':''}" id="ppkg${i}" onclick="selPkg(${i})"> |
| <span class="ppkg-icon">${icons[i]||'🪙'}</span> |
| <div style="font-size:.58rem;font-weight:800;text-transform:uppercase;letter-spacing:.1em;color:rgba(255,255,255,.28);margin-bottom:5px">${tiers[i]}</div> |
| <div class="ppkg-c">${p.coins}</div> |
| <div class="ppkg-u">Coins</div> |
| <div class="ppkg-p" style="background:${pBg[i]};color:${pClr[i]}">${p.price}</div> |
| <div style="font-size:.6rem;color:rgba(255,255,255,.28);margin-top:5px">${p.desc||''}</div> |
| </div>`).join(''); |
| } |
| |
| function selPkg(i){ |
| document.querySelectorAll('.ppkg').forEach(e=>e.classList.remove('sel')); |
| document.getElementById('ppkg'+i).classList.add('sel'); |
| _selPkg=_pkgs[i]; |
| document.getElementById('buy-now-btn').disabled=false; |
| } |
| |
| |
| function goPayStep(n){ |
| document.querySelectorAll('.pay-step').forEach(e=>e.classList.remove('on')); |
| document.getElementById('pay-step-'+n).classList.add('on'); |
| } |
| function goPayStep2(){ |
| if(!_selPkg)return; |
| _selMeth=null; |
| document.querySelectorAll('.pmeth').forEach(e=>e.classList.remove('sel')); |
| document.getElementById('pay-info-box').style.display='none'; |
| goPayStep(2); |
| } |
| function goPayStep3(){ |
| if(!_selMeth)return; |
| _slipB64=null; |
| |
| document.getElementById('pslip-prev').style.display='none'; |
| document.getElementById('pslip-icon').style.display='block'; |
| document.getElementById('pslip-txt').style.display='block'; |
| document.getElementById('pslip-input').value=''; |
| document.getElementById('psubmit').disabled=true; |
| goPayStep(3); |
| } |
| |
| function selMeth(m){ |
| _selMeth=m; |
| document.querySelectorAll('.pmeth').forEach(e=>e.classList.remove('sel')); |
| document.getElementById('pmeth-'+m).classList.add('sel'); |
| |
| let rows=''; |
| if(m==='kbz'){ |
| const mmkAmt=_selPkg?(_selPkg.price||''):''; |
| const kbzCoins=_selPkg?_selPkg.coins:0; |
| const kbzQrUrl=`/api/payment/kbz_qr?amount=${kbzCoins*1000}`; |
| rows=`<div class="pinfo-row"><span class="pinfo-lbl">📱 KBZ Pay</span><span></span></div> |
| <div class="pinfo-row"><span class="pinfo-lbl">အမည် / Name</span><div style="display:flex;gap:8px;align-items:center"><span class="pinfo-val" id="pi-name">${_payInfo.kbz_name}</span><button class="pcopy" onclick="pcopy2('pi-name')">Copy</button></div></div> |
| <div class="pinfo-row"><span class="pinfo-lbl">ဖုန်းနံပါတ်</span><div style="display:flex;gap:8px;align-items:center"><span class="pinfo-val" id="pi-num">${_payInfo.kbz_number}</span><button class="pcopy" onclick="pcopy2('pi-num')">Copy</button></div></div> |
| ${_payInfo.kbz_qr_url||kbzQrUrl?`<div style="text-align:center;margin:10px 0"><img src="${_payInfo.kbz_qr_url||kbzQrUrl}" alt="KBZ Pay QR" style="width:180px;height:180px;border-radius:12px;border:2px solid rgba(255,255,255,.15)" onerror="this.style.display='none'"><div style="font-size:.68rem;color:rgba(255,255,255,.4);margin-top:4px">KBZ Pay QR Scan — ${mmkAmt}</div></div>`:''}`; |
| } else if(m==='wave'){ |
| rows=`<div class="pinfo-row"><span class="pinfo-lbl">🌊 Wave Money</span><span></span></div> |
| <div class="pinfo-row"><span class="pinfo-lbl">Name</span><div style="display:flex;gap:8px;align-items:center"><span class="pinfo-val" id="pi-name">${_payInfo.kbz_name}</span><button class="pcopy" onclick="pcopy2('pi-name')">Copy</button></div></div> |
| <div class="pinfo-row"><span class="pinfo-lbl">Phone</span><div style="display:flex;gap:8px;align-items:center"><span class="pinfo-val" id="pi-num">${_payInfo.kbz_number}</span><button class="pcopy" onclick="pcopy2('pi-num')">Copy</button></div></div>`; |
| } else if(m==='scb'){ |
| rows=`<div class="pinfo-row"><span class="pinfo-lbl">🏦 SCB Bank</span><span></span></div> |
| <div class="pinfo-row"><span class="pinfo-lbl">ชื่อ / Name</span><div style="display:flex;gap:8px;align-items:center"><span class="pinfo-val" id="pi-name">${_payInfo.scb_name}</span><button class="pcopy" onclick="pcopy2('pi-name')">Copy</button></div></div> |
| <div class="pinfo-row"><span class="pinfo-lbl">Account No.</span><div style="display:flex;gap:8px;align-items:center"><span class="pinfo-val" id="pi-num">${_payInfo.scb_number}</span><button class="pcopy" onclick="pcopy2('pi-num')">Copy</button></div></div>`; |
| } else if(m==='prompt'){ |
| const thbAmt=_selPkg?(_selPkg.price_thb||''):''; |
| const qrUrl=thbAmt?`/api/payment/promptpay_qr?amount=${thbAmt}`:''; |
| rows=`<div class="pinfo-row"><span class="pinfo-lbl">⚡ PromptPay</span><span></span></div> |
| <div class="pinfo-row"><span class="pinfo-lbl">เบอร์โทร</span><div style="display:flex;gap:8px;align-items:center"><span class="pinfo-val" id="pi-num">${_payInfo.promptpay}</span><button class="pcopy" onclick="pcopy2('pi-num')">Copy</button></div></div> |
| ${qrUrl?`<div style="text-align:center;margin:10px 0"><img src="${qrUrl}" alt="PromptPay QR" style="width:180px;height:180px;border-radius:12px;border:2px solid rgba(255,255,255,.15)" onerror="this.style.display='none'"><div style="font-size:.68rem;color:rgba(255,255,255,.4);margin-top:4px">QR Scan — ${thbAmt} ฿</div></div>`:''}`; |
| } else if(m==='truemoney'){ |
| const thbAmtTM=_selPkg?(_selPkg.price_thb||''):''; |
| const tmQrUrl=thbAmtTM?`/api/payment/truemoney_qr?amount=${thbAmtTM}`:''; |
| rows=`<div class="pinfo-row"><span class="pinfo-lbl">💛 TrueMoney Wallet</span><span></span></div> |
| <div class="pinfo-row"><span class="pinfo-lbl">ชื่อ / Name</span><div style="display:flex;gap:8px;align-items:center"><span class="pinfo-val" id="pi-name">${_payInfo.truemoney_name}</span><button class="pcopy" onclick="pcopy2('pi-name')">Copy</button></div></div> |
| <div class="pinfo-row"><span class="pinfo-lbl">เบอร์โทร</span><div style="display:flex;gap:8px;align-items:center"><span class="pinfo-val" id="pi-num">${_payInfo.truemoney_number}</span><button class="pcopy" onclick="pcopy2('pi-num')">Copy</button></div></div> |
| ${(_payInfo.truemoney_qr_url||tmQrUrl)?`<div style="text-align:center;margin:10px 0"><img src="${_payInfo.truemoney_qr_url||tmQrUrl}" alt="TrueMoney QR" style="width:180px;height:180px;border-radius:12px;border:2px solid rgba(255,255,255,.15)" onerror="this.style.display='none'"><div style="font-size:.68rem;color:rgba(255,255,255,.4);margin-top:4px">TrueMoney QR Scan — ${thbAmtTM} ฿</div></div>`:''}`; |
| } |
| |
| rows+=`<div class="pinfo-row" style="margin-top:4px"><span class="pinfo-lbl">💰 ชำระ / Amount</span><span class="pinfo-val" style="color:#fbbf24">${_selPkg?_selPkg.price:'—'}</span></div>`; |
| document.getElementById('pinfo-box-inner').innerHTML=rows; |
| document.getElementById('pay-info-box').style.display='block'; |
| } |
| |
| function pcopy2(id){ |
| const el=document.getElementById(id); |
| if(!el)return; |
| navigator.clipboard.writeText(el.textContent).then(()=>toast('✅ Copied!')); |
| } |
| |
| function handlePSlip(inp){ |
| const f=inp.files[0]; if(!f) return; |
| if(f.size>5*1024*1024){toast('❌ ပုံဆိုဒ် 5MB မကြီးပါနှင့်');return;} |
| const rd=new FileReader(); |
| rd.onload=e=>{ |
| _slipB64=e.target.result; |
| const img=document.getElementById('pslip-prev'); |
| img.src=_slipB64; img.style.display='block'; |
| document.getElementById('pslip-icon').style.display='none'; |
| document.getElementById('pslip-txt').style.display='none'; |
| document.getElementById('psubmit').disabled=false; |
| }; |
| rd.readAsDataURL(f); |
| } |
| |
| function checkPReady(){ |
| const btn=document.getElementById('psubmit'); |
| const ready=_selPkg&&_slipB64; |
| btn.disabled=!ready; |
| } |
| |
| async function submitPay(){ |
| if(!_selPkg||!_slipB64) return; |
| const btn=document.getElementById('psubmit'); |
| btn.disabled=true; btn.textContent='⏳ တင်နေသည်…'; |
| const methLabel={'kbz':'KBZ Pay','wave':'Wave Money','scb':'SCB Bank','prompt':'PromptPay','truemoney':'TrueMoney Wallet'}; |
| const priceLabel=_selPkg.price + ((_selMeth==='scb'||_selMeth==='prompt')?' (Thai Baht)':''); |
| try{ |
| const r=await fetch('/api/payment/submit',{ |
| method:'POST',headers:{'Content-Type':'application/json'}, |
| body:JSON.stringify({username:U,coins:_selPkg.coins,price:priceLabel, |
| method:methLabel[_selMeth]||_selMeth||'—',slip_image:_slipB64}) |
| }).then(r=>r.json()); |
| if(r.ok){ |
| toast('✅ '+r.msg); |
| setTimeout(()=>{closePay();setTimeout(()=>{openPay();showPayTab('his');},300);},1500); |
| } else { toast('❌ '+(r.msg||'Failed')); btn.disabled=false; btn.textContent='💰 Payment တင်ပေးပါ'; } |
| }catch(e){ toast('❌ Error: '+e.message); btn.disabled=false; btn.textContent='💰 Payment တင်ပေးပါ'; } |
| } |
| |
| async function loadPayHistory(){ |
| const el=document.getElementById('ph-list'); |
| if(!U){el.innerHTML='<div class="ph-empty">⚠️ Login ဦးဝင်ပါ</div>';return;} |
| el.innerHTML='<div class="ph-empty">⏳ Loading…</div>'; |
| try{ |
| const resp=await fetch(`/api/payment/history?username=${encodeURIComponent(U)}`); |
| if(!resp.ok){el.innerHTML=`<div class="ph-empty">❌ Server error ${resp.status}</div>`;return;} |
| const r=await resp.json(); |
| if(!r.ok||!r.payments?.length){ |
| el.innerHTML='<div class="ph-empty">📭 Payment မှတ်တမ်း မရှိသေးပါ</div>'; return; |
| } |
| const lbl={pending:'⏳ Pending',approved:'✅ Approved',rejected:'❌ Rejected'}; |
| el.innerHTML=[...r.payments].reverse().map(p=>` |
| <div class="ph-card"> |
| <div class="ph-top"> |
| <div> |
| <div class="ph-coins">🪙 ${p.coins} Coins</div> |
| <div class="ph-meta" style="margin-top:3px">${p.price}</div> |
| </div> |
| <span class="ph-badge ${p.status}">${lbl[p.status]||p.status}</span> |
| </div> |
| <div class="ph-meta">🆔 ${p.id} · ${p.created_at.slice(0,16).replace('T',' ')}</div> |
| ${p.admin_note?`<div style="margin-top:6px;font-size:.72rem;color:rgba(255,255,255,.35);padding:6px 10px;background:rgba(255,255,255,.04);border-radius:7px">📝 ${p.admin_note}</div>`:''} |
| </div>`).join(''); |
| }catch(e){ el.innerHTML=`<div class="ph-empty">❌ ${e.message}</div>`; } |
| } |
| |
| function pcopy(id){ |
| navigator.clipboard.writeText(document.getElementById(id).textContent) |
| .then(()=>toast('✅ Copied!')); |
| } |
| |
| |
| |
| function odw(){document.getElementById('dcoins').textContent=ISADM?'∞':COINS;document.getElementById('dov').classList.add('on');} |
| function cdw(){document.getElementById('dov').classList.remove('on');} |
| function scrollToAdmin(){document.getElementById('admp').scrollIntoView({behavior:'smooth'});} |
| function updC(n){if(n===-1||ISADM)return;COINS=n;document.getElementById('hcoins').textContent=n;document.getElementById('dcoins').textContent=n;} |
| |
| |
| function onUrl(v){ |
| const b=document.getElementById('ubadge'); |
| b.style.display='none';CACHE_KEY=''; |
| if(!v)return; |
| const vl=v.toLowerCase(); |
| if(vl.includes('youtube.com')||vl.includes('youtu.be')){b.textContent='YouTube';b.className='ubadge b-yt';b.style.display='';} |
| else if(vl.includes('tiktok.com')){b.textContent='TikTok';b.className='ubadge b-tt';b.style.display='';} |
| else if(vl.includes('instagram.com')){b.textContent='Instagram';b.className='ubadge b-ig';b.style.display='';} |
| else if(vl.includes('facebook.com')||vl.includes('fb.watch')){b.textContent='Facebook';b.className='ubadge b-fb';b.style.display='';} |
| if(URL_DEBOUNCE)clearTimeout(URL_DEBOUNCE); |
| if(v.startsWith('http')){ |
| URL_DEBOUNCE=setTimeout(()=>autoPreview(v),1200); |
| } |
| } |
| |
| async function autoPreview(url){ |
| const loading=document.getElementById('prev-loading'); |
| loading.classList.add('on'); |
| |
| const vid=document.getElementById('pvid'); |
| const thumb=document.getElementById('pthumb'); |
| vid.style.display='none'; vid.src=''; |
| thumb.style.display='none'; thumb.src=''; |
| try{ |
| const r=await fetch('/api/thumb',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({url})}); |
| if(!r.ok)throw new Error('Server error '+r.status); |
| const d=await r.json(); |
| if(d.ok){ |
| thumb.src=d.url; |
| thumb.style.display='block'; |
| document.getElementById('canvas-ph').style.display='none'; |
| thumb.onload=()=>{_syncSubPreview();}; |
| thumb.onerror=()=>{ |
| |
| thumb.style.display='none'; |
| document.getElementById('canvas-ph').style.display='flex'; |
| }; |
| }else{ |
| |
| document.getElementById('canvas-ph').innerHTML='<i class="fas fa-check-circle" style="font-size:1.8rem;opacity:.5;color:var(--green)"></i><span>URL ထည့်ပြီးပါပြီ</span>'; |
| } |
| }catch(e){toast('⚠️ Preview: '+e);} |
| loading.classList.remove('on'); |
| } |
| |
| function onFile(inp){const f=inp.files[0];if(!f)return;const n=document.getElementById('upzn');n.textContent=f.name;n.style.display='block';document.getElementById('vurl').value='';document.getElementById('ubadge').style.display='none';CACHE_KEY='';} |
| function setFN(id,inp){const f=inp.files[0];const el=document.getElementById(id);el.textContent=f?f.name:'';el.style.display=f?'block':'none';} |
| function hasVid(){return document.getElementById('vurl').value.trim()||(document.getElementById('vfile').files||[]).length>0;} |
| |
| |
| 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');} |
| } |
| |
| |
| function toggleBgm(){ |
| BGM_ON=!BGM_ON; |
| const b=document.getElementById('btn-bgm'); |
| b.className='icon-toggle'+(BGM_ON?' on':''); |
| b.innerHTML='<i class="fas fa-music"></i> '+(BGM_ON?'On':'Off'); |
| document.getElementById('bgm-wrap').style.display=BGM_ON?'block':'none'; |
| if(!BGM_ON){ |
| document.getElementById('bgm-file').value=''; |
| document.getElementById('bgm-name').textContent='Click to upload MP3 / WAV'; |
| document.getElementById('bgm-name').style.color='rgba(255,255,255,.5)'; |
| } |
| } |
| function onBgmFile(input){ |
| const f=input.files[0]; |
| if(!f)return; |
| document.getElementById('bgm-name').textContent=f.name; |
| document.getElementById('bgm-name').style.color='#c4b5fd'; |
| } |
| |
| |
| function onLang(){renderV();setSpd();} |
| function onEng(){renderV();const g=document.getElementById('engine').value==='gemini';document.getElementById('pvbtn').style.display=g?'none':'flex';const ew=document.getElementById('emotion-wrap');if(ew)ew.style.display=g?'':'none';if(g)setSpd();} |
| 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);});} |
| function setSpd(){const s={my:30,th:20,en:0}[document.getElementById('vlang').value]??30;document.getElementById('spd').value=s;document.getElementById('spdv').textContent=s+'%';} |
| async function doPrevVoice(){ |
| const voice=document.getElementById('vsel').value,speed=parseInt(document.getElementById('spd').value); |
| const vo_lang=document.getElementById('vlang').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',vo_lang})}); |
| if(!r.ok)throw new Error();const d=await r.json(); |
| if(d.ok){const a=document.getElementById('pva');a.src=d.url;a.play();}else toast('❌ '+d.msg);} |
| catch{toast('❌ Preview failed');} |
| btn.innerHTML='<i class="fas fa-play"></i>'; |
| } |
| |
| |
| let BLUR_ON=false, WM2_ON=false, LOGO2_ON=false; |
| |
| function _ovBtn(id, on, ac, bc){ |
| const b=document.getElementById(id); if(!b)return; |
| if(on){b.style.background=ac;b.style.borderColor=bc;b.style.color='#fff';b.textContent='On';} |
| else{b.style.background='';b.style.borderColor='';b.style.color='';b.textContent='Off';} |
| } |
| |
| function ovToggle(k){ |
| if(k==='blur'){ |
| BLUR_ON=!BLUR_ON; |
| _ovBtn('ov-blur',BLUR_ON,'rgba(239,68,68,.28)','#ef4444'); |
| document.getElementById('db-blur').style.display=BLUR_ON?'block':'none'; |
| const bh=document.getElementById('ov-blur-hint');if(bh)bh.style.display=BLUR_ON?'block':'none'; |
| return; |
| } |
| if(k==='wm'){ |
| WM2_ON=!WM2_ON; |
| _ovBtn('ov-wm',WM2_ON,'rgba(234,179,8,.28)','#ca8a04'); |
| document.getElementById('db-wm').style.display=WM2_ON?'block':'none'; |
| document.getElementById('ov-wm-wrap').style.display=WM2_ON?'block':'none'; |
| if(WM2_ON)setTimeout(()=>{const i=document.getElementById('wm-text2');if(i)i.focus();},60); |
| return; |
| } |
| if(k==='logo'){ |
| LOGO2_ON=!LOGO2_ON; |
| _ovBtn('ov-logo',LOGO2_ON,'rgba(16,185,129,.28)','#10b981'); |
| document.getElementById('db-logo').style.display=LOGO2_ON?'block':'none'; |
| document.getElementById('ov-logo-wrap').style.display=LOGO2_ON?'block':'none'; |
| return; |
| } |
| } |
| |
| function onLogoFile(inp){ |
| const f=inp.files[0]; if(!f)return; |
| document.getElementById('logo-name').textContent=f.name; |
| document.getElementById('logo-name').style.color='#6ee7b7'; |
| const r=new FileReader(); |
| r.onload=e=>{ |
| const box=document.getElementById('db-logo'); |
| box.style.backgroundImage='url('+e.target.result+')'; |
| box.style.backgroundSize='contain'; |
| box.style.backgroundRepeat='no-repeat'; |
| box.style.backgroundPosition='center'; |
| const lbl=document.getElementById('db-logo-lbl'); |
| if(lbl)lbl.style.display='none'; |
| }; |
| r.readAsDataURL(f); |
| } |
| |
| |
| function syncCanvasInner(v){ |
| applyCropPreview(v); |
| } |
| |
| function applyCropPreview(v){ |
| const crop=document.getElementById('crop').value; |
| const bg=document.getElementById('canvas-bg-blur'); |
| const vw=document.getElementById('canvas-video-wrap'); |
| const ci=document.getElementById('canvas-inner'); |
| |
| vw.style.position='';vw.style.left='';vw.style.right=''; |
| vw.style.width='100%';vw.style.margin=''; |
| bg.style.display='none'; |
| |
| if(!v.videoWidth) return; |
| |
| const RATIOS={'9:16':[9,16],'16:9':[16,9],'1:1':[1,1],'original':null}; |
| const tgt=RATIOS[crop]; |
| if(!tgt) return; |
| |
| const srcW=v.videoWidth, srcH=v.videoHeight; |
| const tgtRatio=tgt[0]/tgt[1]; |
| const srcRatio=srcW/srcH; |
| |
| if(Math.abs(tgtRatio-srcRatio)<0.05) return; |
| |
| try{ |
| const c=document.createElement('canvas'); |
| c.width=srcW;c.height=srcH; |
| const ctx=c.getContext('2d'); |
| ctx.drawImage(v,0,0,srcW,srcH); |
| bg.style.backgroundImage='url('+c.toDataURL('image/jpeg',0.4)+')'; |
| bg.style.display='block'; |
| } catch(e){ |
| bg.style.backgroundImage='none'; |
| bg.style.background='rgba(20,20,20,.9)'; |
| bg.style.display='block'; |
| } |
| |
| if(srcRatio > tgtRatio){ |
| const pct=(tgtRatio/srcRatio*100).toFixed(2)+'%'; |
| vw.style.position='relative'; |
| vw.style.width=pct; |
| vw.style.margin='0 auto'; |
| ci.style.left=((100-(tgtRatio/srcRatio*100))/2).toFixed(2)+'%'; |
| ci.style.width=(tgtRatio/srcRatio*100).toFixed(2)+'%'; |
| } else { |
| ci.style.left='0';ci.style.width='100%'; |
| } |
| } |
| function initDrag(){ |
| ['db-blur','db-wm','db-logo'].forEach(id=>{ |
| const el=document.getElementById(id); |
| if(el){makeDrag(el);makeResize(el,el.querySelector('.rh'));} |
| }); |
| } |
| function makeDrag(el){ |
| let ox,oy,ex,ey; |
| const down=e=>{if(e.target.classList.contains('rh'))return;e.preventDefault();const t=e.touches?e.touches[0]:e;ox=t.clientX;oy=t.clientY;const r=el.getBoundingClientRect(),pr=el.parentElement.getBoundingClientRect();ex=r.left-pr.left;ey=r.top-pr.top; |
| const mv=e=>{e.preventDefault();const t=e.touches?e.touches[0]:e;el.style.left=(ex+t.clientX-ox)+'px';el.style.top=(ey+t.clientY-oy)+'px';el.style.bottom='auto';}; |
| const up=()=>{document.removeEventListener('mousemove',mv);document.removeEventListener('mouseup',up);el.removeEventListener('touchmove',mv);el.removeEventListener('touchend',up);}; |
| document.addEventListener('mousemove',mv);document.addEventListener('mouseup',up);el.addEventListener('touchmove',mv,{passive:false});el.addEventListener('touchend',up); |
| }; |
| el.addEventListener('mousedown',down);el.addEventListener('touchstart',down,{passive:false}); |
| } |
| function makeResize(el,h){if(!h)return; |
| const down=e=>{e.stopPropagation();e.preventDefault();const t=e.touches?e.touches[0]:e;const sw=el.offsetWidth,sh=el.offsetHeight,sx=t.clientX,sy=t.clientY; |
| const mv=e=>{e.preventDefault();const t=e.touches?e.touches[0]:e;el.style.width=Math.max(40,sw+t.clientX-sx)+'px';el.style.height=Math.max(30,sh+t.clientY-sy)+'px';}; |
| const up=()=>{document.removeEventListener('mousemove',mv);document.removeEventListener('mouseup',up);h.removeEventListener('touchmove',mv);h.removeEventListener('touchend',up);}; |
| document.addEventListener('mousemove',mv);document.addEventListener('mouseup',up);h.addEventListener('touchmove',mv,{passive:false});h.addEventListener('touchend',up); |
| }; |
| h.addEventListener('mousedown',down);h.addEventListener('touchstart',down,{passive:false}); |
| } |
| function getBoxPct(id){ |
| const el=document.getElementById(id); |
| const ci=document.getElementById('canvas-inner'); |
| const video=document.getElementById('pvid'); |
| const crop=document.getElementById('crop').value; |
| const srcW=video.videoWidth||720, srcH=video.videoHeight||1280; |
| let tW,tH; |
| if(crop==='9:16'){tW=720;tH=1280;} |
| else if(crop==='16:9'){tW=1280;tH=720;} |
| else if(crop==='1:1'){tW=720;tH=720;} |
| else{tW=srcW;tH=srcH;} |
| const ciR=ci.getBoundingClientRect(); |
| const elR=el.getBoundingClientRect(); |
| const sx=tW/ciR.width, sy=tH/ciR.height; |
| return{ |
| x:Math.max(0,Math.min(1,((elR.left-ciR.left)*sx)/tW)), |
| y:Math.max(0,Math.min(1,((elR.top-ciR.top)*sy)/tH)), |
| w:Math.max(0,Math.min(1,(elR.width*sx)/tW)), |
| h:Math.max(0,Math.min(1,(elR.height*sy)/tH)) |
| }; |
| } |
| |
| |
| 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('tts_emotion',document.getElementById('tts-emotion')?document.getElementById('tts-emotion').value:''); |
| fd.append('speed',document.getElementById('spd').value); |
| |
| const _wmTxt=WM2_ON?(document.getElementById('wm-text2').value.trim()):''; |
| fd.append('watermark',_wmTxt); |
| if(WM2_ON && _wmTxt){ |
| const wp=getBoxPct('db-wm'); |
| fd.append('wmk_xp',wp.x.toFixed(4)); |
| fd.append('wmk_yp',wp.y.toFixed(4)); |
| fd.append('wmk_fontsize','28'); |
| } |
| |
| const _lf=document.getElementById('logo-file2'); |
| if(LOGO2_ON && _lf && _lf.files[0]){ |
| fd.append('logo_file',_lf.files[0]); |
| const lp=getBoxPct('db-logo'); |
| fd.append('logo_xp',lp.x.toFixed(4)); |
| fd.append('logo_yp',lp.y.toFixed(4)); |
| fd.append('logo_wp',lp.w.toFixed(4)); |
| } |
| 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); |
| if(CACHE_KEY)fd.append('cache_key',CACHE_KEY); |
| |
| fd.append('blur_enabled',BLUR_ON?'1':'0'); |
| if(BLUR_ON){ |
| const bp=getBoxPct('db-blur'); |
| const vw=document.getElementById('pvid'); |
| const vW=vw.videoWidth||720, vH=vw.videoHeight||1280; |
| const crop=document.getElementById('crop').value; |
| let tW=vW,tH=vH; |
| if(crop==='9:16'){tW=720;tH=1280;} |
| else if(crop==='16:9'){tW=1280;tH=720;} |
| else if(crop==='1:1'){tW=720;tH=720;} |
| fd.append('blur_xp',bp.x.toFixed(4)); |
| fd.append('blur_yp',bp.y.toFixed(4)); |
| fd.append('blur_wp',bp.w.toFixed(4)); |
| fd.append('blur_hp',bp.h.toFixed(4)); |
| } |
| fd.append('sub_enabled',SUB_ON?'1':'0'); |
| if(SUB_ON){ |
| fd.append('sub_size',document.getElementById('sub-size').value); |
| fd.append('sub_pos',document.getElementById('sub-pos').value); |
| fd.append('sub_color',document.getElementById('sub-color').value); |
| fd.append('sub_style',document.getElementById('sub-style').value); |
| } |
| const vf=document.getElementById('vfile');if(vf&&vf.files[0])fd.append('video_file',vf.files[0]); |
| const mf=document.getElementById('bgm-file');if(BGM_ON&&mf&&mf.files[0])fd.append('music_file',mf.files[0]); |
| return fd; |
| } |
| |
| |
| function fmt(s){const m=Math.floor(s/60);return m>0?m+'m '+(s%60)+'s':s+'s';} |
| const SMAP={8:'ps-dl',20:'ps-tr',45:'ps-ai',65:'ps-ts',78:'ps-vd'}; |
| let _waitMsgTimer=null; |
| function _startWaitMsgs(){ |
| const msgs=['⏳ တန်းစီစောင့်နေသည်…','⏳ Server နဲ့ ချိတ်ဆက်နေသည်…','⏳ Video URL စစ်ဆေးနေသည်…','⏳ ဒေါင်းလုပ်အတွက် ပြင်ဆင်နေသည်…']; |
| let i=0; |
| _waitMsgTimer=setInterval(()=>{i=(i+1)%msgs.length;document.getElementById('pmsg').textContent=msgs[i];},2500); |
| } |
| function _stopWaitMsgs(){if(_waitMsgTimer){clearInterval(_waitMsgTimer);_waitMsgTimer=null;}} |
| function showProg(on){document.getElementById('pc').style.display=on?'block':'none';if(on){document.getElementById('pb').style.width='0%';document.getElementById('pmsg').textContent='⏳ တန်းစီစောင့်နေသည်…';document.getElementById('ptmr').textContent='⏱ 0s';Object.values(SMAP).forEach(id=>document.getElementById(id).className='ps');_hideLiveOutputs();}else{_stopWaitMsgs();}} |
| function updSteps(pct){let hit=false;const e=Object.entries(SMAP);for(let i=0;i<e.length;i++){const[p,id]=e[i],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';}} |
| function startTick(){T0=Date.now();if(TICK)clearInterval(TICK);TICK=setInterval(()=>{document.getElementById('ptmr').textContent='⏱ '+fmt(Math.floor((Date.now()-T0)/1000));},1000);} |
| function stopTick(){if(TICK){clearInterval(TICK);TICK=null;}return Math.floor((Date.now()-T0)/1000);} |
| 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;background:rgba(255,255,255,.2);padding:2px 8px;border-radius:10px">1 Coin</span>';} |
| function toggleOut(type){const bodyMap={transcript:'out-tr-body',script:'out-sc-body',tts:'out-tts-body'};const chvMap={transcript:'out-tr-chv',script:'out-sc-chv',tts:'out-tts-chv'};const b=document.getElementById(bodyMap[type]);const c=document.getElementById(chvMap[type]);if(!b)return;const open=b.style.display==='block';b.style.display=open?'none':'block';if(c)c.style.transform=open?'':'rotate(180deg)';} |
| function _showLiveOutput(type,content){const wrapMap={transcript:'out-transcript',script:'out-script',tts:'out-tts'};const w=document.getElementById(wrapMap[type]);if(!w)return;w.style.display='block';document.getElementById('live-outputs').style.display='flex';if(type==='transcript'){document.getElementById('out-tr-text').textContent=content.text||'';if(content.lang)document.getElementById('out-tr-lang').textContent='('+content.lang+')';document.getElementById('out-tr-body').style.display='block';document.getElementById('out-tr-chv').style.transform='rotate(180deg)';}else if(type==='script'){document.getElementById('out-sc-text').textContent=content.text||'';document.getElementById('out-sc-body').style.display='block';document.getElementById('out-sc-chv').style.transform='rotate(180deg)';}else if(type==='tts'){const a=document.getElementById('out-tts-audio');a.src=content.url+'?t='+Date.now();document.getElementById('out-tts-body').style.display='block';document.getElementById('out-tts-chv').style.transform='rotate(180deg)';}} |
| function _hideLiveOutputs(){['out-transcript','out-script','out-tts'].forEach(id=>{const el=document.getElementById(id);if(el)el.style.display='none';});document.getElementById('live-outputs').style.display='none';} |
| function startSSE(tid){ |
| if(SSE)SSE.close(); |
| let _lastTr=false,_lastSc=false,_lastTts=false; |
| SSE=new EventSource('/api/progress/'+tid); |
| SSE.onmessage=e=>{try{ |
| const p=JSON.parse(e.data); |
| const msg=(p.msg||'').replace(/KEY-\d+\s*·\s*\S+/g,'').trim(); |
| document.getElementById('pb').style.width=(p.pct||0)+'%'; |
| document.getElementById('pmsg').textContent=msg; |
| updSteps(p.pct||0); |
| |
| if(p.transcript&&!_lastTr){_lastTr=true;_showLiveOutput('transcript',{text:p.transcript,lang:p.src_lang});} |
| if(p.script&&!_lastSc){_lastSc=true;_showLiveOutput('script',{text:p.script});} |
| if(p.tts_url&&!_lastTts){_lastTts=true;_showLiveOutput('tts',{url:p.tts_url});} |
| 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 မတွေ့ပါ');}} |
| else if(p.error){SSE.close();SSE=null;stopTick();showProg(false);_resetBtn();toast(msg||'❌ Process မအောင်မြင်ပါ');} |
| }catch(ex){}}; |
| } |
| |
| |
| async function doAuto(){ |
| if(!hasVid()){toast('❌ Select a video or enter a URL');return;} |
| const btn=document.getElementById('btnauto');btn.disabled=true;btn.innerHTML='<i class="fas fa-spinner sp"></i> Processing…'; |
| showProg(true);startTick();_startWaitMsgs(); |
| const fd=bfd(); |
| const tid=uid8();fd.append('tid',tid); |
| try{ |
| const ctrl=new AbortController(),tout=setTimeout(()=>ctrl.abort(),600000); |
| const r=await fetch('/api/process_all',{method:'POST',body:fd,signal:ctrl.signal}); |
| clearTimeout(tout); |
| if(!r.ok){_stopWaitMsgs();stopTick();showProg(false);_resetBtn();toast('❌ Server error '+r.status);return;} |
| const d=await r.json(); |
| if(!d.ok){_stopWaitMsgs();stopTick();showProg(false);_resetBtn();toast(d.msg||'❌ Process မအောင်မြင်ပါ');return;} |
| |
| _stopWaitMsgs(); |
| startSSE(tid); |
| }catch(e){ |
| _stopWaitMsgs();stopTick();showProg(false);_resetBtn(); |
| toast(e.name==='AbortError'?'❌ Timeout':'❌ '+e); |
| } |
| } |
| |
| |
| 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'); |
| rvid.src='';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 ပြီးပါပြီ!');return;}}catch(e){} |
| if(tries>=20){clearInterval(poll);rvid.src=url;toast('✅ Done');} |
| },1000); |
| } |
| |
| function dlVideo(){if(!OUT_URL)return;const a=document.createElement('a');a.href=OUT_URL;a.download='recap.mp4';a.click();} |
| 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'));} |
| |
| |
| 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||'';} |
| 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:'');} |
| 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();} |
| 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"><thead><tr><th>User</th><th>🪙</th><th>Vids</th><th>Status</th><th>Actions</th></tr></thead><tbody>'; |
| d.users.forEach(u=>{ |
| const banned=u.banned; |
| const rowStyle=banned?'opacity:.5;background:rgba(239,68,68,.05)':''; |
| const banBtn=banned |
| ?`<button class="delbtn" style="color:#10b981" onclick="qBan('${u.username}',false)" title="Unban"><i class="fas fa-unlock"></i></button>` |
| :`<button class="delbtn" style="color:#f59e0b" onclick="qBan('${u.username}',true)" title="Ban"><i class="fas fa-ban"></i></button>`; |
| h+=`<tr style="${rowStyle}"><td style="max-width:100px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${u.username}</td><td>${u.coins}</td><td>${u.videos}</td><td>${banned?'<span style="color:#ef4444;font-size:.7rem">🚫 Banned</span>':'<span style="color:#10b981;font-size:.7rem">✅ Active</span>'}</td><td style="display:flex;gap:3px">${banBtn}<button class="delbtn" onclick="qDel('${u.username}')"><i class="fas fa-trash"></i></button></td></tr>`; |
| }); |
| w.innerHTML=h+'</tbody></table>'; |
| } |
| 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();} |
| async function qBan(u,ban){const label=ban?'Ban':'Unban';if(!confirm(label+' '+u+'?'))return;const r=await fetch('/api/admin/ban_user',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({caller:U,username:u,ban})});const d=await r.json();toast(d.msg);if(d.ok)loadUsers();} |
| |
| |
| function openBroadcast(){ |
| const m=document.getElementById('bc-modal'); |
| m.style.display='flex'; |
| document.getElementById('bc-msg').focus(); |
| document.getElementById('bc-res').textContent=''; |
| document.getElementById('bc-msg').oninput=function(){ |
| document.getElementById('bc-char').textContent=this.value.length+' / 4096'; |
| }; |
| } |
| function closeBroadcast(){ |
| document.getElementById('bc-modal').style.display='none'; |
| document.getElementById('bc-msg').value=''; |
| document.getElementById('bc-char').textContent='0 / 4096'; |
| } |
| async function sendBroadcast(){ |
| const msg=document.getElementById('bc-msg').value.trim(); |
| const res=document.getElementById('bc-res'); |
| const btn=document.getElementById('bc-send-btn'); |
| if(!msg){res.style.color='#ef4444';res.textContent='❌ Message ထည့်ပါ';return;} |
| btn.disabled=true;btn.innerHTML='<i class="fas fa-spinner fa-spin"></i> ပို့နေသည်…'; |
| res.style.color='rgba(255,255,255,.4)';res.textContent=''; |
| try{ |
| const r=await fetch('/api/admin/broadcast',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({caller:U,message:msg})}); |
| const d=await r.json(); |
| if(d.ok){ |
| res.style.color='#4ade80';res.textContent=d.msg; |
| document.getElementById('bc-msg').value=''; |
| document.getElementById('bc-char').textContent='0 / 4096'; |
| setTimeout(closeBroadcast,2000); |
| }else{res.style.color='#ef4444';res.textContent=d.msg||'❌ Error';} |
| }catch(e){res.style.color='#ef4444';res.textContent='❌ Network error';} |
| btn.disabled=false;btn.innerHTML='<i class="fas fa-paper-plane"></i> ပို့မည်'; |
| } |
| async function loadPendingPayments(){ |
| const wrap=document.getElementById('adm-pending-wrap'); |
| const list=document.getElementById('adm-pending-list'); |
| const cnt=document.getElementById('adm-pending-count'); |
| list.innerHTML='<div style="font-size:.75rem;color:var(--muted);padding:8px">⏳ Loading…</div>'; |
| wrap.style.display='block'; |
| try{ |
| const r=await fetch('/api/admin/payments?caller='+encodeURIComponent(U)+'&status=pending'); |
| let d; |
| try{ d=await r.json(); } |
| catch(je){ list.innerHTML='<div style="color:var(--red);font-size:.75rem">❌ Server error (not JSON) — HTTP '+r.status+'</div>'; return; } |
| if(!d.ok){list.innerHTML='<div style="color:var(--red);font-size:.75rem">❌ '+(d.msg||'Error')+'</div>';return;} |
| const pays=d.payments||[]; |
| cnt.textContent=pays.length; |
| cnt.style.display=pays.length?'':'none'; |
| if(!pays.length){list.innerHTML='<div style="font-size:.78rem;color:var(--muted);padding:10px;text-align:center">✅ Pending payment မရှိပါ</div>';return;} |
| list.innerHTML=pays.map(p=>{ |
| const dt=p.created_at?p.created_at.replace('T',' ').substring(0,16):''; |
| return `<div class="adm-pay-card" id="paycard-${p.id}"> |
| <div class="adm-pay-card-top"> |
| <span class="adm-pay-card-user">👤 ${p.username}</span> |
| <span class="adm-pay-card-coins">🪙 ${p.coins} Coins</span> |
| </div> |
| <div class="adm-pay-card-info"> |
| 💵 ${p.price} MMK · 🆔 <code style="background:var(--bg3);padding:2px 5px;border-radius:4px;font-size:.7rem">${p.id}</code><br> |
| ⏰ ${dt} |
| </div> |
| ${p.slip_image?`<img class="adm-slip-img" id="slip-${p.id}" src="${p.slip_image}" onclick="this.style.display=this.style.display==='none'?'block':'none'">` :''} |
| <div class="adm-pay-card-btns"> |
| ${p.slip_image?`<button class="adm-pay-slip-btn" onclick="toggleSlip('${p.id}')"><i class="fas fa-image"></i> Slip</button>`:''} |
| <button class="adm-pay-approve" onclick="admPayAction('approve','${p.id}','${p.username}',${p.coins},this)">✅ Approve +${p.coins}</button> |
| <button class="adm-pay-reject" onclick="admPayAction('reject','${p.id}','${p.username}',0,this)">❌ Reject</button> |
| </div> |
| </div>`; |
| }).join(''); |
| }catch(e){list.innerHTML='<div style="color:var(--red);font-size:.75rem">❌ '+e+'</div>';} |
| } |
| |
| function toggleSlip(pid){ |
| const img=document.getElementById('slip-'+pid); |
| if(img) img.style.display=img.style.display==='block'?'none':'block'; |
| } |
| |
| async function admPayAction(action,pid,username,coins,btn){ |
| if(action==='reject'&&!confirm('Reject payment from '+username+'?'))return; |
| |
| if(btn){btn.disabled=true;btn.style.opacity='0.5';} |
| const ep=action==='approve'?'/api/admin/payment/approve':'/api/admin/payment/reject'; |
| const body={caller:U,payment_id:pid,username,coins}; |
| try{ |
| const r=await fetch(ep,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}); |
| const d=await r.json(); |
| if(d.ok){ |
| toast(action==='approve'?'✅ Approved! +'+coins+' coins → '+username:'❌ Rejected'); |
| const card=document.getElementById('paycard-'+pid); |
| if(card)card.remove(); |
| const remaining=document.querySelectorAll('[id^="paycard-"]').length; |
| const cnt=document.getElementById('adm-pending-count'); |
| if(cnt){cnt.textContent=remaining;cnt.style.display=remaining?'':'none';} |
| }else{ |
| toast('❌ '+(d.msg||'Error')); |
| if(btn){btn.disabled=false;btn.style.opacity='';} |
| } |
| }catch(e){ |
| toast('❌ Network error: '+e.message); |
| if(btn){btn.disabled=false;btn.style.opacity='';} |
| } |
| } |
| |
| |
| function toast(m){const e=document.getElementById('toast');e.textContent=m;e.classList.add('show');setTimeout(()=>e.classList.remove('show'),2800);} |
| |
| function uid8(){return([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,c=>(c^crypto.getRandomValues(new Uint8Array(1))[0]&15>>c/4).toString(16)).replace(/-/g,'').substring(0,8);} |
| async function doPaste(){try{const t=await navigator.clipboard.readText();if(t){const el=document.getElementById('vurl');el.value=t;onUrl(t);toast('✅ Pasted!');}else toast('❌ Clipboard empty');}catch(e){toast('❌ Clipboard access denied');}} |
| |
| document.addEventListener('keydown',e=>{if(e.key==='Enter'&&document.getElementById('login-screen').style.display!=='none'){if(document.getElementById('pane-li').style.display!=='none')doLogin();else doReg();}}); |
| |
| |
| function switchMode(m){ |
| const isRecap=m==='recap'; |
| document.getElementById('section-recap').style.display=isRecap?'':'none'; |
| document.getElementById('section-srt').style.display=isRecap?'none':''; |
| document.getElementById('mode-recap').classList.toggle('on',isRecap); |
| document.getElementById('mode-srt').classList.toggle('on',!isRecap); |
| } |
| |
| |
| let SRT_VOUT='', SRT_CACHE_KEY=''; |
| let SRT_TEXT='', SRT_VPATH_KEY=''; |
| let SRT_FLIP=false, SRT_COL=false; |
| let SRT_ZOOM=false, SRT_AUDIO=false; |
| let SRT_BLUR_ON=false, SRT_WM_ON=false, SRT_LOGO_ON=false; |
| let SRT_SSE=null, SRT_TICK=null, SRT_T0=0; |
| let SRT_URL_DEB=null; |
| let SRT_REGEN_COUNT=0; |
| |
| |
| function onSrtUrl(v){ |
| const b=document.getElementById('srt-ubadge'); |
| b.style.display='none'; SRT_CACHE_KEY=''; SRT_REGEN_COUNT=0; |
| if(!v) return; |
| const vl=v.toLowerCase(); |
| if(vl.includes('youtube.com')||vl.includes('youtu.be')){b.textContent='YouTube';b.className='ubadge b-yt';b.style.display='';} |
| else if(vl.includes('tiktok.com')){b.textContent='TikTok';b.className='ubadge b-tt';b.style.display='';} |
| else if(vl.includes('instagram.com')){b.textContent='Instagram';b.className='ubadge b-ig';b.style.display='';} |
| else if(vl.includes('facebook.com')||vl.includes('fb.watch')){b.textContent='Facebook';b.className='ubadge b-fb';b.style.display='';} |
| if(SRT_URL_DEB) clearTimeout(SRT_URL_DEB); |
| if(v.startsWith('http')){ |
| SRT_URL_DEB=setTimeout(()=>srtAutoThumb(v),1200); |
| } |
| } |
| |
| async function srtAutoThumb(url){ |
| const loading=document.getElementById('srt-prev-loading'); |
| loading.classList.add('on'); |
| 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){ |
| const th=document.getElementById('srt-pthumb'); |
| th.src=d.url; th.style.display='block'; |
| document.getElementById('srt-pvid').style.display='none'; |
| document.getElementById('srt-canvas-ph').style.display='none'; |
| th.onload=()=>{syncSrtSubPreview();}; |
| } |
| }catch(e){} |
| loading.classList.remove('on'); |
| } |
| |
| function onSrtVFile(inp){ |
| const f=inp.files[0]; if(!f) return; |
| document.getElementById('srt-upzn').textContent=f.name; |
| document.getElementById('srt-upzn').style.display='block'; |
| document.getElementById('srt-vurl').value=''; |
| document.getElementById('srt-ubadge').style.display='none'; |
| SRT_CACHE_KEY=''; SRT_REGEN_COUNT=0; |
| const url=URL.createObjectURL(f); |
| const v=document.getElementById('srt-pvid'); |
| v.src=url; v.style.display='block'; |
| document.getElementById('srt-pthumb').style.display='none'; |
| document.getElementById('srt-canvas-ph').style.display='none'; |
| v.onloadedmetadata=()=>{syncSrtSubPreview();}; |
| } |
| |
| function onSrtLogoFile(inp){ |
| const f=inp.files[0]; if(!f) return; |
| document.getElementById('srt-logo-name').textContent=f.name; |
| document.getElementById('srt-logo-name').style.color='#6ee7b7'; |
| const r=new FileReader(); |
| r.onload=e=>{ |
| const box=document.getElementById('srt-db-logo'); |
| box.style.backgroundImage='url('+e.target.result+')'; |
| box.style.backgroundSize='contain'; |
| box.style.backgroundRepeat='no-repeat'; |
| box.style.backgroundPosition='center'; |
| const lbl=document.getElementById('srt-db-logo-lbl'); |
| if(lbl) lbl.style.display='none'; |
| }; |
| r.readAsDataURL(f); |
| } |
| |
| async function srtPaste(){ |
| try{ |
| const t=await navigator.clipboard.readText(); |
| if(t){document.getElementById('srt-vurl').value=t;onSrtUrl(t);toast('✅ Pasted!');} |
| else toast('❌ Clipboard empty'); |
| }catch(e){toast('❌ Clipboard access denied');} |
| } |
| |
| |
| function _srtOvBtn(id, on, ac, bc){ |
| const b=document.getElementById(id); if(!b) return; |
| if(on){b.style.background=ac;b.style.borderColor=bc;b.style.color='#fff';b.textContent='On';} |
| else{b.style.background='';b.style.borderColor='';b.style.color='';b.textContent='Off';} |
| } |
| |
| function srtOvToggle(k){ |
| if(k==='blur'){ |
| SRT_BLUR_ON=!SRT_BLUR_ON; |
| _srtOvBtn('srt-ov-blur',SRT_BLUR_ON,'rgba(239,68,68,.28)','#ef4444'); |
| document.getElementById('srt-db-blur').style.display=SRT_BLUR_ON?'block':'none'; |
| document.getElementById('srt-ov-blur-hint').style.display=SRT_BLUR_ON?'block':'none'; |
| if(SRT_BLUR_ON) initSrtDrag(); |
| return; |
| } |
| if(k==='wm'){ |
| SRT_WM_ON=!SRT_WM_ON; |
| _srtOvBtn('srt-ov-wm',SRT_WM_ON,'rgba(234,179,8,.28)','#ca8a04'); |
| document.getElementById('srt-db-wm').style.display=SRT_WM_ON?'block':'none'; |
| document.getElementById('srt-ov-wm-wrap').style.display=SRT_WM_ON?'block':'none'; |
| if(SRT_WM_ON){initSrtDrag();setTimeout(()=>{const i=document.getElementById('srt-wm-text');if(i)i.focus();},60);} |
| return; |
| } |
| if(k==='logo'){ |
| SRT_LOGO_ON=!SRT_LOGO_ON; |
| _srtOvBtn('srt-ov-logo',SRT_LOGO_ON,'rgba(16,185,129,.28)','#10b981'); |
| document.getElementById('srt-db-logo').style.display=SRT_LOGO_ON?'block':'none'; |
| document.getElementById('srt-ov-logo-wrap').style.display=SRT_LOGO_ON?'block':'none'; |
| if(SRT_LOGO_ON) initSrtDrag(); |
| return; |
| } |
| if(k==='zoom'){ |
| SRT_ZOOM=!SRT_ZOOM; |
| _srtOvBtn('srt-ov-zoom',SRT_ZOOM,'rgba(139,92,246,.28)','#7c3aed'); |
| _srtApplyZoomPreview(); |
| return; |
| } |
| if(k==='audio'){ |
| SRT_AUDIO=!SRT_AUDIO; |
| _srtOvBtn('srt-ov-audio',SRT_AUDIO,'rgba(59,130,246,.28)','#2563eb'); |
| return; |
| } |
| } |
| |
| |
| function srtToggleOpt(k){ |
| if(k==='flip'){ |
| SRT_FLIP=!SRT_FLIP; |
| const b=document.getElementById('srt-btn-flip'); |
| b.style.background=SRT_FLIP?'rgba(56,189,248,.18)':'rgba(255,255,255,.05)'; |
| b.style.borderColor=SRT_FLIP?'#38bdf8':'rgba(255,255,255,.12)'; |
| b.style.color=SRT_FLIP?'#38bdf8':'rgba(255,255,255,.55)'; |
| b.innerHTML='<i class="fas fa-arrows-alt-h"></i> Flip: '+(SRT_FLIP?'On':'Off'); |
| } else if(k==='col'){ |
| SRT_COL=!SRT_COL; |
| const b=document.getElementById('srt-btn-col'); |
| b.style.background=SRT_COL?'rgba(251,191,36,.18)':'rgba(255,255,255,.05)'; |
| b.style.borderColor=SRT_COL?'#fbbf24':'rgba(255,255,255,.12)'; |
| b.style.color=SRT_COL?'#fbbf24':'rgba(255,255,255,.55)'; |
| b.innerHTML='<i class="fas fa-adjust"></i> Color: '+(SRT_COL?'On':'Off'); |
| } |
| } |
| |
| function _srtApplyZoomPreview(){ |
| const thumb=document.getElementById('srt-pthumb'); |
| const vid=document.getElementById('srt-pvid'); |
| const zv=document.getElementById('srt-zoom-factor'); |
| const scale=SRT_ZOOM&&zv?parseFloat('1.0'+(''+zv.value).slice(1)):1; |
| if(thumb) thumb.style.transform=scale>1?`scale(${scale})`:''; |
| if(vid) vid.style.transform=scale>1?`scale(${scale})`:''; |
| } |
| |
| function _subSizeOptions(crop){ |
| |
| if(crop==='9:16') |
| return '<option value="0.0350">XSmall</option><option value="0.0469" selected>Small</option>'; |
| if(crop==='1:1') |
| return '<option value="0.0469" selected>Small</option><option value="0.0625">Medium</option>'; |
| |
| return '<option value="0.0350">XSmall</option><option value="0.0469" selected>Small</option><option value="0.0625">Medium</option><option value="0.0781">Large</option>'; |
| } |
| |
| function onSrtCropChange(){ |
| const crop=document.getElementById('srt-crop').value; |
| const sel=document.getElementById('srt-sub-size'); |
| if(!sel) return; |
| sel.innerHTML=_subSizeOptions(crop); |
| syncSrtSubPreview(); |
| } |
| |
| function _filterSubSize(){ |
| |
| const crop=(document.getElementById('crop')||{value:'original'}).value; |
| const sel=document.getElementById('sub-size'); |
| if(!sel) return; |
| const cur=sel.value; |
| if(crop==='9:16') |
| sel.innerHTML='<option value="0.0350">XSmall</option><option value="0.0469" selected>Small</option>'; |
| else if(crop==='1:1') |
| sel.innerHTML='<option value="0.0469" selected>Small</option><option value="0.0625">Medium</option>'; |
| else |
| sel.innerHTML='<option value="0.0350">XSmall</option><option value="0.0469" selected>Small</option><option value="0.0625">Medium</option><option value="0.0781">Large</option>'; |
| |
| const opt=[...sel.options].find(o=>o.value===cur); |
| if(opt) sel.value=cur; else sel.selectedIndex=sel.options.length>1?1:0; |
| } |
| function syncSrtSubPreview(){ |
| const overlay=document.getElementById('srt-sub-overlay'); |
| const span=document.getElementById('srt-sub-preview-span'); |
| if(!overlay||!span) return; |
| const thumb=document.getElementById('srt-pthumb'); |
| const vid=document.getElementById('srt-pvid'); |
| const hasMedia=(thumb&&thumb.style.display!=='none'&&thumb.src)|| |
| (vid&&vid.style.display!=='none'&&vid.src); |
| if(!hasMedia){overlay.style.display='none';return;} |
| const posVal=parseInt((document.getElementById('srt-sub-pos')||{value:85}).value)||85; |
| const col=(document.getElementById('srt-sub-color')||{value:'white'}).value; |
| const sty=(document.getElementById('srt-sub-style')||{value:'outline'}).value; |
| const sizeVal=parseInt((document.getElementById('srt-sub-size')||{value:64}).value)||64; |
| |
| |
| |
| |
| const _vidEl=document.getElementById('srt-pvid'); |
| const _imgEl=document.getElementById('srt-pthumb'); |
| let _sNatW=0,_sNatH=0,_sElemW=0,_sElemH=0; |
| if(_vidEl&&_vidEl.style.display!=='none'&&_vidEl.videoHeight>0){ |
| const r=_vidEl.getBoundingClientRect(); |
| _sElemW=r.width;_sElemH=r.height;_sNatW=_vidEl.videoWidth;_sNatH=_vidEl.videoHeight; |
| } else if(_imgEl&&_imgEl.style.display!=='none'&&_imgEl.naturalHeight>0){ |
| const r=_imgEl.getBoundingClientRect(); |
| _sElemW=r.width;_sElemH=r.height;_sNatW=_imgEl.naturalWidth;_sNatH=_imgEl.naturalHeight; |
| } |
| const srtSizeFrac=parseFloat((document.getElementById('srt-sub-size')||{value:0.0547}).value)||0.0547; |
| const _srtCropSel=(document.getElementById('srt-crop')||{value:'original'}).value; |
| const _srtCi=document.getElementById('srt-canvas-inner'); |
| const _srtCiR=_srtCi?_srtCi.getBoundingClientRect():{width:300,height:300}; |
| let _srtEffH; |
| if(_srtCropSel==='9:16'){ |
| const _srtVwrap=document.getElementById('srt-canvas-video-wrap'); |
| const _srtVwrapW=_srtVwrap?_srtVwrap.getBoundingClientRect().width:_srtCiR.width; |
| _srtEffH=_srtVwrapW*(1280/720); |
| } else _srtEffH=_srtCiR.height; |
| let pxSize=Math.round(srtSizeFrac*_srtEffH); |
| |
| if(_srtCropSel==='1:1') pxSize=Math.min(pxSize,28); |
| else if(_srtCropSel==='16:9') pxSize=Math.min(pxSize,32); |
| const colMap={ |
| 'white':'#fff','yellow':'#ffe066','cyan':'#67e8f9','green':'#4ade80', |
| 'orange':'#fb923c','pink':'#f0abfc','red':'#f87171','lime':'#a3e635', |
| 'hotpink':'#ff69b4','gold':'#ffd700','violet':'#ee82ee','deepskyblue':'#00bfff','coral':'#ff7f50' |
| }; |
| |
| |
| |
| const _sWrap=document.getElementById('srt-canvas-video-wrap'); |
| const _sTopPx=_srtEffH*posVal/100; |
| overlay.style.display='block'; |
| overlay.style.top=_sTopPx+'px'; |
| overlay.style.transform='translateY(-50%)'; |
| span.style.color=colMap[col]||'#fff'; |
| span.style.fontSize=pxSize+'px'; |
| span.style.fontWeight='700'; |
| span.style.fontFamily="'Noto Sans Myanmar','Padauk',sans-serif"; |
| span.style.lineHeight='1.25'; |
| span.style.padding='0 8px'; |
| span.style.maxWidth='95%'; |
| if(sty==='box'){ |
| span.style.background='rgba(0,0,0,.65)'; |
| span.style.textShadow='none'; |
| span.style.webkitTextStroke=''; |
| } else if(sty==='shadow'){ |
| span.style.background='transparent'; |
| span.style.textShadow='3px 3px 8px rgba(0,0,0,.9),0 0 20px rgba(0,0,0,.8)'; |
| span.style.webkitTextStroke=''; |
| } else if(sty==='glow'){ |
| const gc=colMap[col]||'#fff'; |
| span.style.background='transparent'; |
| span.style.textShadow=`0 0 8px ${gc},0 0 20px ${gc},0 0 40px ${gc},2px 2px 4px #000`; |
| span.style.webkitTextStroke=''; |
| } else if(sty==='stroke'){ |
| span.style.background='transparent'; |
| span.style.textShadow='none'; |
| span.style.webkitTextStroke='2px #000'; |
| } else if(sty==='plain'){ |
| span.style.background='transparent'; |
| span.style.textShadow='none'; |
| span.style.webkitTextStroke=''; |
| } else { |
| |
| span.style.background='transparent'; |
| span.style.textShadow='0 0 4px #000,0 1px 6px #000,-1px -1px 0 #000,1px 1px 0 #000'; |
| span.style.webkitTextStroke=''; |
| } |
| |
| const dtPrev=document.getElementById('srt-sub-preview-text'); |
| if(dtPrev){ |
| dtPrev.style.color=colMap[col]||'#fff'; |
| if(sty==='box'){dtPrev.style.background='rgba(0,0,0,.65)';dtPrev.style.textShadow='none';dtPrev.style.webkitTextStroke='';} |
| else if(sty==='shadow'){dtPrev.style.background='transparent';dtPrev.style.textShadow='3px 3px 8px rgba(0,0,0,.9),0 0 20px rgba(0,0,0,.8)';dtPrev.style.webkitTextStroke='';} |
| else if(sty==='glow'){const gc=colMap[col]||'#fff';dtPrev.style.background='transparent';dtPrev.style.textShadow=`0 0 8px ${gc},0 0 20px ${gc},0 0 40px ${gc},2px 2px 4px #000`;dtPrev.style.webkitTextStroke='';} |
| else if(sty==='stroke'){dtPrev.style.background='transparent';dtPrev.style.textShadow='none';dtPrev.style.webkitTextStroke='2px #000';} |
| else if(sty==='plain'){dtPrev.style.background='transparent';dtPrev.style.textShadow='none';dtPrev.style.webkitTextStroke='';} |
| else{dtPrev.style.background='transparent';dtPrev.style.textShadow='0 0 4px #000,0 1px 6px #000,-1px -1px 0 #000,1px 1px 0 #000';dtPrev.style.webkitTextStroke='';} |
| } |
| } |
| |
| |
| function initSrtDrag(){ |
| ['srt-db-blur','srt-db-wm','srt-db-logo'].forEach(id=>{ |
| const el=document.getElementById(id); |
| if(el){srtMakeDrag(el);srtMakeResize(el,el.querySelector('.rh'));} |
| }); |
| } |
| function srtMakeDrag(el){ |
| if(el._srtDragDone) return; |
| el._srtDragDone=true; |
| let ox,oy,ex,ey; |
| const down=e=>{ |
| if(e.target.classList.contains('rh'))return; |
| e.preventDefault(); |
| const t=e.touches?e.touches[0]:e; |
| const pr=document.getElementById('srt-canvas-inner'); |
| const r=el.getBoundingClientRect(),pR=pr.getBoundingClientRect(); |
| ox=t.clientX;oy=t.clientY; |
| ex=r.left-pR.left;ey=r.top-pR.top; |
| const mv=e=>{ |
| e.preventDefault(); |
| const t=e.touches?e.touches[0]:e; |
| el.style.left=(ex+t.clientX-ox)+'px'; |
| el.style.top=(ey+t.clientY-oy)+'px'; |
| el.style.bottom='auto'; |
| }; |
| const up=()=>{ |
| document.removeEventListener('mousemove',mv); |
| document.removeEventListener('mouseup',up); |
| document.removeEventListener('touchmove',mv); |
| document.removeEventListener('touchend',up); |
| }; |
| document.addEventListener('mousemove',mv); |
| document.addEventListener('mouseup',up); |
| document.addEventListener('touchmove',mv,{passive:false}); |
| document.addEventListener('touchend',up); |
| }; |
| el.addEventListener('mousedown',down); |
| el.addEventListener('touchstart',down,{passive:false}); |
| } |
| function srtMakeResize(el,h){ |
| if(!h||h._srtResizeDone) return; |
| h._srtResizeDone=true; |
| const down=e=>{ |
| e.stopPropagation();e.preventDefault(); |
| const t=e.touches?e.touches[0]:e; |
| const sw=el.offsetWidth,sh=el.offsetHeight,sx=t.clientX,sy=t.clientY; |
| const mv=e=>{ |
| e.preventDefault(); |
| const t=e.touches?e.touches[0]:e; |
| el.style.width=Math.max(40,sw+t.clientX-sx)+'px'; |
| el.style.height=Math.max(30,sh+t.clientY-sy)+'px'; |
| }; |
| const up=()=>{ |
| document.removeEventListener('mousemove',mv); |
| document.removeEventListener('mouseup',up); |
| document.removeEventListener('touchmove',mv); |
| document.removeEventListener('touchend',up); |
| }; |
| document.addEventListener('mousemove',mv); |
| document.addEventListener('mouseup',up); |
| document.addEventListener('touchmove',mv,{passive:false}); |
| document.addEventListener('touchend',up); |
| }; |
| h.addEventListener('mousedown',down); |
| h.addEventListener('touchstart',down,{passive:false}); |
| } |
| function getSrtBoxPct(id){ |
| const el=document.getElementById(id); |
| const ci=document.getElementById('srt-canvas-inner'); |
| const video=document.getElementById('srt-pvid'); |
| const crop=document.getElementById('srt-crop').value; |
| const srcW=video.videoWidth||720, srcH=video.videoHeight||1280; |
| let tW,tH; |
| if(crop==='9:16'){tW=720;tH=1280;} |
| else if(crop==='16:9'){tW=1280;tH=720;} |
| else if(crop==='1:1'){tW=720;tH=720;} |
| else{tW=srcW;tH=srcH;} |
| const ciR=ci.getBoundingClientRect(); |
| const elR=el.getBoundingClientRect(); |
| const sx=tW/ciR.width, sy=tH/ciR.height; |
| return{ |
| x:Math.max(0,Math.min(1,((elR.left-ciR.left)*sx)/tW)), |
| y:Math.max(0,Math.min(1,((elR.top-ciR.top)*sy)/tH)), |
| w:Math.max(0,Math.min(1,(elR.width*sx)/tW)), |
| h:Math.max(0,Math.min(1,(elR.height*sy)/tH)) |
| }; |
| } |
| |
| function hasSrtVid(){ |
| return document.getElementById('srt-vurl').value.trim()|| |
| (document.getElementById('srt-vfile').files||[]).length>0; |
| } |
| |
| |
| function srtShowProg(on){ |
| document.getElementById('srt-pc').style.display=on?'block':'none'; |
| if(on){ |
| document.getElementById('srt-pb').style.width='0%'; |
| document.getElementById('srt-pmsg').textContent='⏳ တန်းစီစောင့်နေသည်…'; |
| document.getElementById('srt-ptmr').textContent='⏱ 0s'; |
| ['srt-ps-dl','srt-ps-ai','srt-ps-burn'].forEach(id=>{document.getElementById(id).className='ps';}); |
| } |
| } |
| function srtUpdSteps(pct){ |
| const MAP={8:'srt-ps-dl',50:'srt-ps-ai',75:'srt-ps-burn'}; |
| let hit=false; |
| Object.entries(MAP).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'; |
| }); |
| } |
| function srtStartTick(){SRT_T0=Date.now();if(SRT_TICK)clearInterval(SRT_TICK);SRT_TICK=setInterval(()=>{document.getElementById('srt-ptmr').textContent='⏱ '+fmt(Math.floor((Date.now()-SRT_T0)/1000));},1000);} |
| function srtStopTick(){if(SRT_TICK){clearInterval(SRT_TICK);SRT_TICK=null;}} |
| function srtResetBtn(){ |
| const b=document.getElementById('srt-gen-btn'); |
| if(b){b.disabled=false;b.innerHTML='<i class="fas fa-robot"></i> Generate Myanmar SRT <span style="opacity:.7;font-size:.78em;background:rgba(255,255,255,.2);padding:2px 8px;border-radius:10px">1 Coin</span>';} |
| const b2=document.getElementById('srt-burn-btn'); |
| if(b2){b2.disabled=false;b2.innerHTML='<i class="fas fa-film"></i> Render Video';} |
| _updateRegenBtn(); |
| } |
| |
| |
| async function doGenerateSrt(){ |
| if(!hasSrtVid()){toast('❌ Video URL သို့ file ထည့်ပါ');return;} |
| if(!U){toast('❌ Login ဦးဝင်ပါ');return;} |
| |
| const btn=document.getElementById('srt-gen-btn'); |
| btn.disabled=true; |
| btn.innerHTML='<i class="fas fa-spinner sp"></i> Generating…'; |
| |
| const pc=document.getElementById('srt-gen-pc'); |
| pc.style.display='block'; |
| document.getElementById('srt-step2-wrap').style.display='none'; |
| document.getElementById('srt-rc').style.display='none'; |
| |
| |
| SRT_T0=Date.now(); |
| if(SRT_TICK) clearInterval(SRT_TICK); |
| SRT_TICK=setInterval(()=>{ |
| const el=document.getElementById('srt-gen-ptmr'); |
| if(el) el.textContent='⏱ '+Math.floor((Date.now()-SRT_T0)/1000)+'s'; |
| },1000); |
| |
| const fd=new FormData(); |
| fd.append('username',U); |
| const vurl=document.getElementById('srt-vurl').value.trim(); |
| const vf=document.getElementById('srt-vfile').files[0]; |
| if(vf) fd.append('video_file',vf); |
| else fd.append('video_url',vurl); |
| if(SRT_CACHE_KEY) fd.append('cache_key',SRT_CACHE_KEY); |
| const tid=uid8(); fd.append('tid',tid); |
| |
| try{ |
| const ctrl=new AbortController(),tout=setTimeout(()=>ctrl.abort(),1200000); |
| const r=await fetch('/api/generate_srt',{method:'POST',body:fd,signal:ctrl.signal}); |
| clearTimeout(tout); |
| if(!r.ok){_srtGenFail('❌ Server error '+r.status);return;} |
| const d=await r.json(); |
| if(!d.ok){_srtGenFail(d.msg||'❌ Failed');return;} |
| _srtGenSSE(tid); |
| }catch(e){ |
| _srtGenFail(e.name==='AbortError'?'❌ Timeout':'❌ '+e); |
| } |
| } |
| |
| function _srtGenFail(msg){ |
| clearInterval(SRT_TICK); SRT_TICK=null; |
| document.getElementById('srt-gen-pc').style.display='none'; |
| srtResetBtn(); toast(msg); |
| } |
| |
| function _srtGenSSE(tid){ |
| const sse=new EventSource('/api/progress/'+tid); |
| sse.onmessage=e=>{ |
| try{ |
| const p=JSON.parse(e.data); |
| document.getElementById('srt-gen-pb').style.width=(p.pct||0)+'%'; |
| document.getElementById('srt-gen-pmsg').textContent=(p.msg||'').trim(); |
| |
| if((p.pct||0)>=8) document.getElementById('srt-ps-dl').classList.add('ps-done'); |
| if((p.pct||0)>=50) document.getElementById('srt-ps-ai').classList.add('ps-done'); |
| if(p.done){ |
| sse.close(); clearInterval(SRT_TICK); SRT_TICK=null; |
| document.getElementById('srt-gen-pc').style.display='none'; |
| srtResetBtn(); |
| if(p.coins!=null&&p.coins!==-1) updC(p.coins); |
| SRT_TEXT=p.srt||''; |
| SRT_VPATH_KEY=p.vpath_key||tid; |
| SRT_REGEN_COUNT++; |
| _updateRegenBtn(); |
| _srtShowStep2(p); |
| toast('✅ SRT ပြီးပါပြီ!'); |
| } else if(p.error){ |
| sse.close(); _srtGenFail((p.msg||'').trim()||'❌ Failed'); |
| } |
| }catch(ex){} |
| }; |
| } |
| |
| function _srtShowStep2(p){ |
| |
| const box=document.getElementById('srt-preview-box'); |
| box.textContent=SRT_TEXT; |
| |
| |
| const cnt=document.getElementById('srt-block-count'); |
| if(cnt) cnt.textContent=(p.total||0)+' lines'; |
| |
| |
| const blocks=SRT_TEXT.trim().split(/\n\s*\n/); |
| let firstTxt=''; |
| for(const blk of blocks){ |
| const lines=blk.trim().split('\n'); |
| for(let i=0;i<lines.length;i++){ |
| if(lines[i].includes('-->')){ |
| firstTxt=lines.slice(i+1).join(' ').trim(); |
| break; |
| } |
| } |
| if(firstTxt) break; |
| } |
| const fsw=document.getElementById('srt-first-sub-wrap'); |
| const fst=document.getElementById('srt-first-sub-txt'); |
| if(firstTxt && fsw && fst){ |
| fst.textContent=firstTxt; |
| fsw.style.display='block'; |
| |
| const span=document.getElementById('srt-sub-preview-span'); |
| if(span) span.textContent=firstTxt; |
| syncSrtSubPreview(); |
| } |
| |
| document.getElementById('srt-step2-wrap').style.display='block'; |
| document.getElementById('srt-step2-wrap').scrollIntoView({behavior:'smooth'}); |
| } |
| |
| |
| function _updateRegenBtn(){ |
| const btn=document.querySelector('[onclick="doRegenSrt()"]'); |
| if(!btn) return; |
| if(SRT_REGEN_COUNT>=2){ |
| btn.disabled=true; |
| btn.style.opacity='0.4'; |
| btn.style.cursor='not-allowed'; |
| btn.innerHTML='<i class="fas fa-lock"></i> Regen (၂/၂ ကုန်ပြီ)'; |
| } else { |
| btn.disabled=false; |
| btn.style.opacity=''; |
| btn.style.cursor=''; |
| btn.innerHTML='<i class="fas fa-redo"></i> Regenerate ('+(2-SRT_REGEN_COUNT)+' ကြိမ် ကျန်)'; |
| } |
| } |
| |
| |
| async function doRegenSrt(){ |
| if(!SRT_VPATH_KEY && !hasSrtVid()){toast('❌ Video မရှိပါ');return;} |
| if(!U){toast('❌ Login ဦးဝင်ပါ');return;} |
| if(SRT_REGEN_COUNT >= 2){toast('❌ Regenerate ၂ ခါ ကုန်ပြီ — Video အသစ်တင်ပါ');return;} |
| |
| document.getElementById('srt-step2-wrap').style.display='none'; |
| SRT_TEXT=''; SRT_VPATH_KEY=''; |
| |
| const fd=new FormData(); |
| fd.append('username',U); |
| const vurl=document.getElementById('srt-vurl').value.trim(); |
| const vf=document.getElementById('srt-vfile').files[0]; |
| if(vf) fd.append('video_file',vf); |
| else fd.append('video_url',vurl); |
| if(SRT_CACHE_KEY) fd.append('cache_key',SRT_CACHE_KEY); |
| fd.append('free_regen','1'); |
| const tid=uid8(); fd.append('tid',tid); |
| |
| const btn=document.querySelector('[onclick="doRegenSrt()"]'); |
| if(btn){btn.disabled=true;btn.innerHTML='<i class="fas fa-spinner sp"></i> Regenerating…';} |
| |
| const pc=document.getElementById('srt-gen-pc'); |
| pc.style.display='block'; |
| SRT_T0=Date.now(); |
| if(SRT_TICK) clearInterval(SRT_TICK); |
| SRT_TICK=setInterval(()=>{ |
| const el=document.getElementById('srt-gen-ptmr'); |
| if(el) el.textContent='⏱ '+Math.floor((Date.now()-SRT_T0)/1000)+'s'; |
| },1000); |
| |
| try{ |
| const ctrl=new AbortController(),tout=setTimeout(()=>ctrl.abort(),600000); |
| const r=await fetch('/api/generate_srt',{method:'POST',body:fd,signal:ctrl.signal}); |
| clearTimeout(tout); |
| const d=await r.json(); |
| if(!d.ok){pc.style.display='none';clearInterval(SRT_TICK);_updateRegenBtn();toast(d.msg||'❌ Failed');return;} |
| _srtGenSSE(d.tid||tid); |
| }catch(e){ |
| pc.style.display='none'; clearInterval(SRT_TICK); |
| _updateRegenBtn(); |
| toast('❌ '+e); |
| } |
| } |
| |
| |
| async function doProcessSrt(){ |
| if(!SRT_TEXT){toast('❌ SRT မရှိပါ — Generate ဦးလုပ်ပါ');return;} |
| if(!U){toast('❌ Login ဦးဝင်ပါ');return;} |
| |
| const btn=document.getElementById('srt-burn-btn'); |
| btn.disabled=true; |
| btn.innerHTML='<i class="fas fa-spinner sp"></i> Rendering…'; |
| document.getElementById('srt-pc').style.display='block'; |
| document.getElementById('srt-rc').style.display='none'; |
| |
| SRT_T0=Date.now(); |
| if(SRT_TICK) clearInterval(SRT_TICK); |
| SRT_TICK=setInterval(()=>{ |
| const el=document.getElementById('srt-ptmr'); |
| if(el) el.textContent='⏱ '+Math.floor((Date.now()-SRT_T0)/1000)+'s'; |
| },1000); |
| |
| const fd=new FormData(); |
| fd.append('username',U); |
| fd.append('srt_text',SRT_TEXT); |
| fd.append('vpath_key',SRT_VPATH_KEY); |
| |
| |
| const vurl=document.getElementById('srt-vurl').value.trim(); |
| const vf=document.getElementById('srt-vfile').files[0]; |
| if(vf) fd.append('video_file',vf); |
| else if(vurl) fd.append('video_url',vurl); |
| if(SRT_CACHE_KEY) fd.append('cache_key',SRT_CACHE_KEY); |
| |
| |
| fd.append('sub_pos',document.getElementById('srt-sub-pos').value); |
| fd.append('sub_size',document.getElementById('srt-sub-size').value); |
| fd.append('sub_color',document.getElementById('srt-sub-color').value); |
| fd.append('sub_style',document.getElementById('srt-sub-style').value); |
| |
| |
| fd.append('crop',document.getElementById('srt-crop').value); |
| fd.append('flip',SRT_FLIP?'1':'0'); |
| fd.append('color',SRT_COL?'1':'0'); |
| fd.append('zoom_enabled',SRT_ZOOM?'1':'0'); |
| if(SRT_ZOOM){ |
| const zvEl=document.getElementById('srt-zoom-factor'); |
| const zv=zvEl?zvEl.value:'105'; |
| fd.append('zoom_factor',(+zv/100).toFixed(2)); |
| } |
| fd.append('audio_boost',SRT_AUDIO?'1':'0'); |
| |
| |
| fd.append('blur_enabled',SRT_BLUR_ON?'1':'0'); |
| if(SRT_BLUR_ON){ |
| const bp=getSrtBoxPct('srt-db-blur'); |
| fd.append('blur_xp',bp.x.toFixed(4));fd.append('blur_yp',bp.y.toFixed(4)); |
| fd.append('blur_wp',bp.w.toFixed(4));fd.append('blur_hp',bp.h.toFixed(4)); |
| } |
| |
| |
| const wmkTxt=SRT_WM_ON?(document.getElementById('srt-wm-text').value.trim()):''; |
| fd.append('watermark',wmkTxt); |
| if(SRT_WM_ON&&wmkTxt){ |
| const wp=getSrtBoxPct('srt-db-wm'); |
| fd.append('wmk_xp',wp.x.toFixed(4));fd.append('wmk_yp',wp.y.toFixed(4)); |
| fd.append('wmk_fontsize','28'); |
| } |
| |
| |
| const lf=document.getElementById('srt-logo-file'); |
| if(SRT_LOGO_ON&&lf&&lf.files[0]){ |
| fd.append('logo_file',lf.files[0]); |
| const lp=getSrtBoxPct('srt-db-logo'); |
| fd.append('logo_xp',lp.x.toFixed(4));fd.append('logo_yp',lp.y.toFixed(4)); |
| fd.append('logo_wp',lp.w.toFixed(4)); |
| } |
| |
| const tid=uid8(); fd.append('tid',tid); |
| |
| try{ |
| const ctrl=new AbortController(),tout=setTimeout(()=>ctrl.abort(),600000); |
| const r=await fetch('/api/burn_srt',{method:'POST',body:fd,signal:ctrl.signal}); |
| clearTimeout(tout); |
| if(!r.ok){_srtBurnFail('❌ Server error '+r.status);return;} |
| const d=await r.json(); |
| if(!d.ok){_srtBurnFail(d.msg||'❌ Failed');return;} |
| _srtBurnSSE(tid); |
| }catch(e){ |
| _srtBurnFail(e.name==='AbortError'?'❌ Timeout':'❌ '+e); |
| } |
| } |
| |
| function _srtBurnFail(msg){ |
| clearInterval(SRT_TICK); SRT_TICK=null; |
| document.getElementById('srt-pc').style.display='none'; |
| srtResetBtn(); toast(msg); |
| } |
| |
| function _srtBurnSSE(tid){ |
| if(SRT_SSE) SRT_SSE.close(); |
| SRT_SSE=new EventSource('/api/progress/'+tid); |
| SRT_SSE.onmessage=e=>{ |
| try{ |
| const p=JSON.parse(e.data); |
| document.getElementById('srt-pb').style.width=(p.pct||0)+'%'; |
| document.getElementById('srt-pmsg').textContent=(p.msg||'').trim(); |
| if((p.pct||0)>=75) document.getElementById('srt-ps-burn').classList.add('ps-done'); |
| if(p.done){ |
| SRT_SSE.close(); SRT_SSE=null; clearInterval(SRT_TICK); SRT_TICK=null; |
| document.getElementById('srt-pc').style.display='none'; |
| srtResetBtn(); |
| if(p.coins!=null&&p.coins!==-1) updC(p.coins); |
| SRT_VOUT=p.output_url||''; |
| document.getElementById('srt-rvid').src=SRT_VOUT; |
| document.getElementById('srt-rc').style.display='block'; |
| document.getElementById('srt-rc').scrollIntoView({behavior:'smooth'}); |
| toast('✅ Video ပြီးပါပြီ!'); |
| } else if(p.error){ |
| SRT_SSE.close(); SRT_SSE=null; _srtBurnFail((p.msg||'').trim()||'❌ Failed'); |
| } |
| }catch(ex){} |
| }; |
| } |
| |
| function srtDlVideo(){ |
| if(!SRT_VOUT) return; |
| const a=document.createElement('a'); a.href=SRT_VOUT; a.download='myanmar_subtitle_video.mp4'; a.click(); |
| } |
| |
| function srtReset(){ |
| if(SRT_SSE){SRT_SSE.close();SRT_SSE=null;} |
| srtStopTick(); srtShowProg(false); |
| SRT_VOUT=''; SRT_CACHE_KEY=''; SRT_TEXT=''; SRT_VPATH_KEY=''; SRT_REGEN_COUNT=0; |
| SRT_FLIP=false; SRT_COL=false; SRT_ZOOM=false; SRT_AUDIO=false; |
| SRT_BLUR_ON=false; SRT_WM_ON=false; SRT_LOGO_ON=false; |
| document.getElementById('srt-vurl').value=''; |
| try{document.getElementById('srt-vfile').value='';}catch(e){} |
| document.getElementById('srt-upzn').style.display='none'; |
| document.getElementById('srt-ubadge').style.display='none'; |
| document.getElementById('srt-pvid').style.display='none'; |
| document.getElementById('srt-pvid').style.transform=''; |
| document.getElementById('srt-pthumb').style.display='none'; |
| document.getElementById('srt-pthumb').style.transform=''; |
| document.getElementById('srt-canvas-ph').style.display='flex'; |
| const sov=document.getElementById('srt-sub-overlay');if(sov)sov.style.display='none'; |
| document.getElementById('srt-rc').style.display='none'; |
| document.getElementById('srt-step2-wrap').style.display='none'; |
| document.getElementById('srt-gen-pc').style.display='none'; |
| const pb=document.getElementById('srt-preview-box');if(pb)pb.textContent=''; |
| const fsw=document.getElementById('srt-first-sub-wrap');if(fsw)fsw.style.display='none'; |
| const bflip=document.getElementById('srt-btn-flip'); |
| bflip.style.background='rgba(255,255,255,.05)';bflip.style.borderColor='rgba(255,255,255,.12)';bflip.style.color='rgba(255,255,255,.55)'; |
| bflip.innerHTML='<i class="fas fa-arrows-alt-h"></i> Flip: Off'; |
| const bcol=document.getElementById('srt-btn-col'); |
| bcol.style.background='rgba(255,255,255,.05)';bcol.style.borderColor='rgba(255,255,255,.12)';bcol.style.color='rgba(255,255,255,.55)'; |
| bcol.innerHTML='<i class="fas fa-adjust"></i> Color: Off'; |
| ['srt-ov-blur','srt-ov-wm','srt-ov-logo','srt-ov-zoom','srt-ov-audio'].forEach(id=>{ |
| const b=document.getElementById(id); if(b){b.style.background='';b.style.borderColor='';b.style.color='';b.textContent='Off';} |
| }); |
| ['srt-db-blur','srt-db-wm','srt-db-logo'].forEach(id=>{ |
| const el=document.getElementById(id); |
| if(el){el.style.display='none';el._srtDragDone=false;const rh=el.querySelector('.rh');if(rh)rh._srtResizeDone=false;} |
| }); |
| ['srt-ov-wm-wrap','srt-ov-logo-wrap','srt-ov-blur-hint'].forEach(id=>{const el=document.getElementById(id);if(el)el.style.display='none';}); |
| const lbl=document.getElementById('srt-db-logo-lbl'); if(lbl)lbl.style.display='flex'; |
| const ln=document.getElementById('srt-logo-name'); if(ln){ln.textContent='Click to upload PNG / JPG';ln.style.color='';} |
| const db=document.getElementById('srt-db-logo'); if(db){db.style.backgroundImage='';} |
| srtResetBtn(); |
| } |
| |
| |
| </script> |
|
|
| |
| <div class="pov" id="pov" onclick="if(event.target===this)closePay()"> |
| <div class="psheet"> |
|
|
| |
| <div class="psheet-hdr"> |
| <h2><span>🪙</span> Buy Credits</h2> |
| <button class="pclose" onclick="closePay()"><i class="fas fa-times"></i></button> |
| </div> |
|
|
| |
| <div class="tab2"> |
| <button id="ptab-buy" class="on" onclick="showPayTab('buy')">🛒 ဝယ်ရန်</button> |
| <button id="ptab-his" onclick="showPayTab('his')">📋 မှတ်တမ်း</button> |
| </div> |
|
|
| |
| <div id="ptab-buy-content"> |
|
|
| |
| <div class="pay-step on" id="pay-step-1"> |
| <div class="pstep-lbl"><span class="pstep-num">1</span> Package ရွေးပါ</div> |
| |
| <div style="display:flex;gap:8px;padding:10px 22px 0"> |
| <button id="ctab-mmk" onclick="renderPkgs('mmk')" style="flex:1;padding:10px 8px;border-radius:12px;border:2px solid rgba(139,92,246,.5);background:rgba(124,58,237,.2);color:#c4b5fd;font-family:var(--F);font-size:.82rem;font-weight:800;cursor:pointer;transition:.2s;display:flex;align-items:center;justify-content:center;gap:6px"><span style="font-size:1.1rem">🇲🇲</span> MMK</button> |
| <button id="ctab-thb" onclick="renderPkgs('thb')" style="flex:1;padding:10px 8px;border-radius:12px;border:2px solid rgba(255,255,255,.08);background:rgba(255,255,255,.04);color:rgba(255,255,255,.35);font-family:var(--F);font-size:.82rem;font-weight:800;cursor:pointer;transition:.2s;display:flex;align-items:center;justify-content:center;gap:6px"><span style="font-size:1.1rem">🇹🇭</span> THB</button> |
| </div> |
| <div class="ppkgs" id="ppkgs" style="display:grid;grid-template-columns:1fr 1fr;gap:10px;padding:14px 22px 4px"></div> |
| <button class="buy-now-btn" id="buy-now-btn" onclick="goPayStep2()" disabled style="margin:0 22px 4px"> |
| ဝယ်ရန် → |
| </button> |
| </div> |
|
|
| |
| <div class="pay-step" id="pay-step-2"> |
| <button class="pback-btn" onclick="goPayStep(1)"><i class="fas fa-arrow-left"></i> ပြန်သွား</button> |
| <div class="pstep-lbl" style="margin-top:6px"><span class="pstep-num">2</span> ငွေပေးချေနည်း ရွေးပါ</div> |
| <div class="pmeth-grid"> |
| <div class="pmeth" id="pmeth-kbz" onclick="selMeth('kbz')"> |
| <img class="pmeth-icon" src="https://raw.githubusercontent.com/Shangyi69/psonlineshop/main/kbz.png" alt="KBZ Pay"> |
| <div class="pmeth-name">KBZ Pay</div> |
| </div> |
| <div class="pmeth" id="pmeth-wave" style="opacity:.4;pointer-events:none;position:relative"> |
| <img class="pmeth-icon" src="https://raw.githubusercontent.com/Shangyi69/psonlineshop/main/wave.jpeg" alt="Wave Money"> |
| <div class="pmeth-name">Wave Money</div> |
| <div class="pmeth-sub" style="color:#ef4444;font-size:.6rem">ခဏပိတ်ထားသည်</div> |
| </div> |
| <div class="pmeth" id="pmeth-scb" onclick="selMeth('scb')"> |
| <img class="pmeth-icon" src="https://raw.githubusercontent.com/Shangyi69/psonlineshop/main/scb.jpeg" alt="SCB"> |
| <div class="pmeth-name">SCB Bank</div> |
| <div class="pmeth-sub">ไทยบาท</div> |
| </div> |
| <div class="pmeth" id="pmeth-prompt" onclick="selMeth('prompt')"> |
| <img class="pmeth-icon" src="https://raw.githubusercontent.com/Shangyi69/psonlineshop/main/prompt.png" alt="PromptPay"> |
| <div class="pmeth-name">PromptPay</div> |
| <div class="pmeth-sub">ไทยบาท</div> |
| </div> |
| <div class="pmeth" id="pmeth-truemoney" onclick="selMeth('truemoney')"> |
| <img class="pmeth-icon" src="https://raw.githubusercontent.com/Shangyi69/psonlineshop/main/true.png" alt="TrueMoney"> |
| <div class="pmeth-name">TrueMoney</div> |
| <div class="pmeth-sub">ไทยบาท</div> |
| </div> |
| </div> |
| |
| <div id="pay-info-box" style="display:none"> |
| <div class="pinfo-box" id="pinfo-box-inner"></div> |
| <button class="buy-now-btn" onclick="goPayStep3()"> |
| Slip တင်မည် → |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div class="pay-step" id="pay-step-3"> |
| <button class="pback-btn" onclick="goPayStep(2)"><i class="fas fa-arrow-left"></i> ပြန်သွား</button> |
| <div class="pstep-lbl" style="margin-top:6px"><span class="pstep-num">3</span> Slip တင်ပေးပါ</div> |
| <div class="pslip-drop" style="margin-top:10px" id="pslip-drop"> |
| <input type="file" accept="image/*" id="pslip-input" onchange="handlePSlip(this)"> |
| <div class="pi" id="pslip-icon">📎</div> |
| <div class="pt" id="pslip-txt">Slip ပုံကို ဒီနေရာ ဖိဆွဲချပါ<br><span style="color:rgba(255,255,255,.2);font-size:.7rem">သို့မဟုတ် နှိပ်ပြီး ရွေးပါ</span></div> |
| <img id="pslip-prev" class="pslip-preview"> |
| </div> |
| <button class="psubmit on" id="psubmit" onclick="submitPay()" disabled> |
| <i class="fas fa-paper-plane" style="margin-right:8px"></i>Payment တင်ပေးပါ |
| </button> |
| </div> |
|
|
| </div> |
|
|
| |
| <div id="ptab-his-content" style="display:none"> |
| <div class="ph-list-wrap"> |
| <div id="ph-list"><div class="ph-empty">⏳ Loading…</div></div> |
| </div> |
| </div> |
|
|
| </div> |
| </div> |
|
|
| |
| |
| <div id="bc-modal" style="display:none;position:fixed;inset:0;z-index:3000;align-items:center;justify-content:center;padding:16px;box-sizing:border-box" onclick="if(event.target===this)closeBroadcast()"> |
| <div style="position:absolute;inset:0;background:rgba(0,0,0,.6);backdrop-filter:blur(6px)"></div> |
| <div style="position:relative;width:100%;max-width:420px;background:linear-gradient(135deg,#1a1a2e 0%,#16213e 100%);border:1px solid rgba(99,102,241,.3);border-radius:20px;padding:24px;box-shadow:0 20px 60px rgba(0,0,0,.5)"> |
| <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px"> |
| <div style="display:flex;align-items:center;gap:10px"> |
| <div style="width:36px;height:36px;border-radius:10px;background:rgba(99,102,241,.2);border:1px solid rgba(99,102,241,.4);display:flex;align-items:center;justify-content:center;color:#6366f1;font-size:.95rem"><i class="fas fa-bullhorn"></i></div> |
| <div> |
| <div style="font-weight:800;font-size:1rem;color:#fff">Broadcast</div> |
| <div style="font-size:.7rem;color:rgba(255,255,255,.4)">Users အားလုံးထံ ပို့မည်</div> |
| </div> |
| </div> |
| <button onclick="closeBroadcast()" style="background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.12);border-radius:8px;width:30px;height:30px;color:rgba(255,255,255,.5);cursor:pointer;font-size:.8rem;display:flex;align-items:center;justify-content:center"><i class="fas fa-times"></i></button> |
| </div> |
| <textarea id="bc-msg" placeholder="ကြေငြာချက် ရေးပါ… Markdown ပံ့ပိုးသည် — *bold* _italic_" style="width:100%;box-sizing:border-box;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1);border-radius:12px;color:#fff;padding:14px;font-size:.85rem;font-family:var(--F);resize:none;min-height:120px;outline:none;line-height:1.6;transition:.2s" onfocus="this.style.borderColor='rgba(99,102,241,.5)'" onblur="this.style.borderColor='rgba(255,255,255,.1)'"></textarea> |
| <div id="bc-char" style="font-size:.68rem;color:rgba(255,255,255,.3);text-align:right;margin-top:4px;margin-bottom:12px">0 / 4096</div> |
| <div style="display:flex;gap:8px"> |
| <button onclick="closeBroadcast()" style="flex:1;padding:12px;border-radius:10px;border:1px solid rgba(255,255,255,.1);background:rgba(255,255,255,.06);color:rgba(255,255,255,.6);cursor:pointer;font-family:var(--F);font-size:.83rem;font-weight:600">မပို့တော့ပါ</button> |
| <button id="bc-send-btn" onclick="sendBroadcast()" style="flex:2;padding:12px;border-radius:10px;border:none;background:linear-gradient(135deg,#6366f1,#8b5cf6);color:#fff;cursor:pointer;font-family:var(--F);font-size:.83rem;font-weight:700;display:flex;align-items:center;justify-content:center;gap:7px"><i class="fas fa-paper-plane"></i> ပို့မည်</button> |
| </div> |
| <div id="bc-res" style="font-size:.78rem;margin-top:10px;text-align:center;font-weight:600;min-height:18px"></div> |
| </div> |
| </div> |
|
|
| <div id="vh-overlay" style="display:none;position:fixed;inset:0;z-index:2000;background:rgba(0,0,0,.7);backdrop-filter:blur(4px);overflow-y:auto;padding:20px 12px 40px"> |
| <div style="max-width:600px;margin:0 auto"> |
| <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px"> |
| <h2 style="color:#fff;font-size:1.2rem;margin:0"><i class="fas fa-film" style="color:#a855f7;margin-right:8px"></i>Video History</h2> |
| <button onclick="closeVideoHistory()" style="background:rgba(255,255,255,.1);border:none;color:#fff;width:36px;height:36px;border-radius:50%;font-size:1.1rem;cursor:pointer"><i class="fas fa-times"></i></button> |
| </div> |
| <p style="color:#888;font-size:.82rem;margin:0 0 16px">Videos are kept for 24 hours after creation.</p> |
| <div id="vh-list"></div> |
| </div> |
| </div> |
|
|
| <style> |
| .vh-card{background:#1a1a2e;border:1px solid rgba(168,85,247,.2);border-radius:12px;overflow:hidden;margin-bottom:14px} |
| .vh-thumb-wrap{position:relative;width:100%;aspect-ratio:16/9;background:#0d0d1a;cursor:pointer} |
| .vh-thumb-wrap video{width:100%;height:100%;object-fit:contain;display:block} |
| .vh-thumb-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.3);transition:opacity .2s} |
| .vh-thumb-overlay:hover{background:rgba(0,0,0,.1)} |
| .vh-play-ic{width:52px;height:52px;border-radius:50%;background:rgba(168,85,247,.85);display:flex;align-items:center;justify-content:center;color:#fff;font-size:1.3rem;pointer-events:none} |
| .vh-info{padding:12px 14px} |
| .vh-title{color:#e0d7ff;font-size:.95rem;font-weight:600;margin-bottom:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} |
| .vh-meta{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:10px} |
| .vh-time{color:#888;font-size:.78rem}<br>.vh-url{color:#888;font-size:.75rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:280px} |
| .vh-ttl{color:#a855f7;font-size:.75rem;background:rgba(168,85,247,.12);padding:2px 8px;border-radius:10px} |
| .vh-dl-btn{display:inline-flex;align-items:center;gap:6px;padding:7px 16px;background:linear-gradient(135deg,#7c3aed,#a855f7);border:none;border-radius:8px;color:#fff;font-size:.82rem;cursor:pointer;font-weight:600;transition:opacity .2s} |
| .vh-dl-btn:hover{opacity:.85} |
| .vh-empty{text-align:center;color:#666;padding:40px 0;font-size:.9rem} |
| </style> |
|
|
| <script> |
| function fmtExpire(secs){ |
| if(secs<=0)return'Expired'; |
| const h=Math.floor(secs/3600),m=Math.floor((secs%3600)/60); |
| return h>0?`${h}h ${m}m left`:`${m}m left`; |
| } |
| |
| async function openVideoHistory(){ |
| document.getElementById('vh-overlay').style.display='block'; |
| document.body.style.overflow='hidden'; |
| const list=document.getElementById('vh-list'); |
| list.innerHTML='<div class="vh-empty"><i class="fas fa-spinner" style="animation:spin 1s linear infinite"></i> Loading…</div>'; |
| try{ |
| const r=await fetch(`/api/video_history?username=${encodeURIComponent(U)}`); |
| const d=await r.json(); |
| if(!d.ok||!d.history||d.history.length===0){ |
| list.innerHTML='<div class="vh-empty"><i class="fas fa-film" style="font-size:2rem;opacity:.3;display:block;margin-bottom:10px"></i>No video history yet.<br>Process a video to see it here.</div>'; |
| return; |
| } |
| list.innerHTML=d.history.map(h=>{ |
| const src=h.output_url; |
| const srcUrl=h.source_url?`<span class="vh-url" title="${h.source_url}"><i class="fas fa-link" style="margin-right:3px"></i>${h.source_url}</span>`:''; |
| return `<div class="vh-card"> |
| <div class="vh-thumb-wrap" onclick="this.querySelector('video').paused?this.querySelector('video').play():this.querySelector('video').pause()"> |
| <video src="${src}" preload="metadata" playsinline></video> |
| <div class="vh-thumb-overlay" id="vhov_${h.tid}"> |
| <div class="vh-play-ic"><i class="fas fa-play" style="margin-left:3px"></i></div> |
| </div> |
| </div> |
| <div class="vh-info"> |
| <div class="vh-title">${h.title||'(no title)'}</div> |
| <div class="vh-meta"> |
| <span class="vh-time"><i class="fas fa-clock" style="margin-right:3px"></i>${h.created_at||''}</span> |
| <span class="vh-ttl">${fmtExpire(h.expires_in||0)}</span> |
| ${srcUrl} |
| </div> |
| <button class="vh-dl-btn" onclick="vhDl('${src}','${h.tid}')"><i class="fas fa-download"></i>Download</button> |
| </div> |
| </div>`; |
| }).join(''); |
| |
| list.querySelectorAll('video').forEach(v=>{ |
| v.addEventListener('play',()=>{const ov=v.closest('.vh-thumb-wrap').querySelector('.vh-thumb-overlay');if(ov)ov.style.opacity='0';}); |
| v.addEventListener('pause',()=>{const ov=v.closest('.vh-thumb-wrap').querySelector('.vh-thumb-overlay');if(ov)ov.style.opacity='1';}); |
| }); |
| }catch(e){list.innerHTML='<div class="vh-empty">❌ Failed to load history</div>';} |
| } |
| |
| function closeVideoHistory(){ |
| document.getElementById('vh-overlay').style.display='none'; |
| document.body.style.overflow=''; |
| |
| document.querySelectorAll('#vh-list video').forEach(v=>v.pause()); |
| } |
| |
| function vhDl(url,tid){ |
| const a=document.createElement('a');a.href=url;a.download=`recap_${tid}.mp4`;a.click(); |
| } |
| |
| document.getElementById('vh-overlay').addEventListener('click',function(e){if(e.target===this)closeVideoHistory();}); |
| </script> |
|
|
| <script> |
| if("serviceWorker" in navigator){ |
| window.addEventListener("load",()=>{ |
| navigator.serviceWorker.register("/sw.js") |
| .then(()=>console.log("[PWA] SW registered")) |
| .catch(e=>console.warn("[PWA] SW failed:",e)); |
| }); |
| } |
| </script> |
| <script> |
| |
| (function(){ |
| const panda = document.querySelector('.panda'); |
| if(!panda) return; |
| const pwFields = ['l-p','r-p']; |
| const allFields = ['l-u','r-u','l-p','r-p']; |
| function coverEyes(){ |
| panda.classList.add('peek-off'); |
| } |
| function uncoverEyes(){ |
| panda.classList.remove('peek-off'); |
| } |
| pwFields.forEach(id=>{ |
| const el = document.getElementById(id); |
| if(el){ |
| el.addEventListener('focus', coverEyes); |
| el.addEventListener('blur', uncoverEyes); |
| } |
| }); |
| |
| ['l-u','r-u'].forEach(id=>{ |
| const el = document.getElementById(id); |
| if(el){ |
| el.addEventListener('focus', uncoverEyes); |
| } |
| }); |
| })(); |
| </script> |
| </body> |
| </html> |