Phoe2004 commited on
Commit
fbf1f3e
Β·
verified Β·
1 Parent(s): 85d0625

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +5 -23
  2. index.html +9 -18
  3. manifest.json +24 -0
app.py CHANGED
@@ -392,13 +392,6 @@ def set_coins_fn(u, n):
392
  db['users'][u]['coins'] = int(n); save_db(db)
393
  return f'βœ… Coin = {n} πŸͺ™'
394
 
395
- def ban_fn(u, ban=True):
396
- db = load_db()
397
- if u not in db['users']: return '❌ User not found'
398
- db['users'][u]['banned'] = ban
399
- save_db(db)
400
- return f"βœ… {'Banned' if ban else 'Unbanned'}: {u}"
401
-
402
  def upd_stat(u, t):
403
  db = load_db()
404
  if u not in db['users']: return
@@ -895,6 +888,10 @@ def api_config():
895
  def google_verify():
896
  return 'google-site-verification: googlefd3d91bc095a2620.html', 200, {'Content-Type': 'text/html'}
897
 
 
 
 
 
898
  @app.route('/sitemap.xml')
899
  def sitemap():
900
  xml = '''<?xml version="1.0" encoding="UTF-8"?>
@@ -1550,8 +1547,6 @@ def api_process_all():
1550
  u = (request.form.get('username') or '').strip()
1551
  if not u: return jsonify(ok=False, msg='❌ Not logged in')
1552
  is_adm = (u == ADMIN_U)
1553
- if not is_adm and load_db()['users'].get(u, {}).get('banned'):
1554
- return jsonify(ok=False, msg='❌ Your account has been banned')
1555
  if not is_adm and get_coins(u) < 2:
1556
  return jsonify(ok=False, msg='❌ Not enough coins (need 2)')
1557
  # Check free_trial flag β€” system auto coins only
@@ -1805,8 +1800,7 @@ def api_users():
1805
  users = [{'username':k,'coins':v.get('coins',0),
1806
  'transcripts':v.get('total_transcripts',0),
1807
  'videos':v.get('total_videos',0),
1808
- 'created':v.get('created_at','')[:10],
1809
- 'banned':v.get('banned',False)}
1810
  for k,v in db['users'].items()]
1811
  return jsonify(ok=True, users=users)
1812
  except Exception as e:
@@ -1826,18 +1820,6 @@ def api_delete_user():
1826
  except Exception as e:
1827
  return jsonify(ok=False, msg=str(e))
1828
 
1829
- @app.route('/api/admin/ban_user', methods=['POST'])
1830
- def api_ban_user():
1831
- try:
1832
- d = request.get_json(force=True) or {}
1833
- if d.get('caller') != ADMIN_U: return jsonify(ok=False, msg='❌ Admin only')
1834
- u = d.get('username','').strip()
1835
- ban = d.get('ban', True)
1836
- msg = ban_fn(u, ban)
1837
- return jsonify(ok=True, msg=msg)
1838
- except Exception as e:
1839
- return jsonify(ok=False, msg=str(e))
1840
-
1841
  @app.route('/api/admin/gen_username')
1842
  def api_gen_username():
1843
  try:
 
392
  db['users'][u]['coins'] = int(n); save_db(db)
393
  return f'βœ… Coin = {n} πŸͺ™'
394
 
 
 
 
 
 
 
 
395
  def upd_stat(u, t):
396
  db = load_db()
397
  if u not in db['users']: return
 
888
  def google_verify():
889
  return 'google-site-verification: googlefd3d91bc095a2620.html', 200, {'Content-Type': 'text/html'}
890
 
891
+ @app.route('/manifest.json')
892
+ def pwa_manifest():
893
+ return send_from_directory(str(BASE_DIR), 'manifest.json', mimetype='application/manifest+json')
894
+
895
  @app.route('/sitemap.xml')
896
  def sitemap():
897
  xml = '''<?xml version="1.0" encoding="UTF-8"?>
 
1547
  u = (request.form.get('username') or '').strip()
1548
  if not u: return jsonify(ok=False, msg='❌ Not logged in')
1549
  is_adm = (u == ADMIN_U)
 
 
1550
  if not is_adm and get_coins(u) < 2:
1551
  return jsonify(ok=False, msg='❌ Not enough coins (need 2)')
1552
  # Check free_trial flag β€” system auto coins only
 
1800
  users = [{'username':k,'coins':v.get('coins',0),
1801
  'transcripts':v.get('total_transcripts',0),
1802
  'videos':v.get('total_videos',0),
1803
+ 'created':v.get('created_at','')[:10]}
 
1804
  for k,v in db['users'].items()]
1805
  return jsonify(ok=True, users=users)
1806
  except Exception as e:
 
1820
  except Exception as e:
1821
  return jsonify(ok=False, msg=str(e))
1822
 
 
 
 
 
 
 
 
 
 
 
 
 
1823
  @app.route('/api/admin/gen_username')
1824
  def api_gen_username():
1825
  try:
index.html CHANGED
@@ -2,7 +2,14 @@
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width,initial-scale=1.0">
 
 
 
 
 
 
 
6
  <title>Recap Studio</title>
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
  <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
@@ -1517,24 +1524,8 @@ function copyCap(){const t=[OUT_CAP,OUT_TAGS].filter(Boolean).join('\n\n');if(!t
1517
  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||'';}
1518
  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:'');}
1519
  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();}
1520
- async function loadUsers(){
1521
- const r=await fetch('/api/admin/users?caller='+encodeURIComponent(U));
1522
- if(!r.ok)return;const d=await r.json();if(!d.ok)return;
1523
- const w=document.getElementById('utw');
1524
- if(!d.users.length){w.innerHTML='<div style="font-size:.75rem;color:var(--muted);padding:6px">No users</div>';return;}
1525
- let h='<table class="ut"><thead><tr><th>User</th><th>πŸͺ™</th><th>Vids</th><th>Status</th><th>Actions</th></tr></thead><tbody>';
1526
- d.users.forEach(u=>{
1527
- const banned=u.banned;
1528
- const rowStyle=banned?'opacity:.5;background:rgba(239,68,68,.05)':'';
1529
- const banBtn=banned
1530
- ?`<button class="delbtn" style="color:#10b981" onclick="qBan('${u.username}',false)" title="Unban"><i class="fas fa-unlock"></i></button>`
1531
- :`<button class="delbtn" style="color:#f59e0b" onclick="qBan('${u.username}',true)" title="Ban"><i class="fas fa-ban"></i></button>`;
1532
- 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>`;
1533
- });
1534
- w.innerHTML=h+'</tbody></table>';
1535
- }
1536
  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();}
1537
- 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();}
1538
 
1539
  /* ══ PENDING PAYMENTS ══ */
1540
  async function loadPendingPayments(){
 
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1.0,viewport-fit=cover">
6
+ <meta name="mobile-web-app-capable" content="yes">
7
+ <meta name="apple-mobile-web-app-capable" content="yes">
8
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
9
+ <meta name="apple-mobile-web-app-title" content="Recap Studio">
10
+ <meta name="theme-color" content="#1a1a2e">
11
+ <link rel="manifest" href="/manifest.json">
12
+ <link rel="apple-touch-icon" href="https://raw.githubusercontent.com/Shangyi69/psonlineshop/main/logo.png">
13
  <title>Recap Studio</title>
14
  <link rel="preconnect" href="https://fonts.googleapis.com">
15
  <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
 
1524
  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||'';}
1525
  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:'');}
1526
  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();}
1527
+ 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>Username</th><th>Coins</th><th>Videos</th><th>Created</th><th></th></tr></thead><tbody>';d.users.forEach(u=>{h+=`<tr><td>${u.username}</td><td>πŸͺ™${u.coins}</td><td>${u.videos}</td><td>${u.created||''}</td><td><button class="delbtn" onclick="qDel('${u.username}')"><i class="fas fa-trash"></i></button></td></tr>`;});w.innerHTML=h+'</tbody></table>';}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1528
  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();}
 
1529
 
1530
  /* ══ PENDING PAYMENTS ══ */
1531
  async function loadPendingPayments(){
manifest.json ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Recap Studio",
3
+ "short_name": "Recap Studio",
4
+ "description": "AI-powered video recap generator",
5
+ "start_url": "/",
6
+ "display": "standalone",
7
+ "background_color": "#1a1a2e",
8
+ "theme_color": "#1a1a2e",
9
+ "orientation": "portrait",
10
+ "icons": [
11
+ {
12
+ "src": "https://raw.githubusercontent.com/Shangyi69/psonlineshop/main/logo.png",
13
+ "sizes": "192x192",
14
+ "type": "image/png",
15
+ "purpose": "any maskable"
16
+ },
17
+ {
18
+ "src": "https://raw.githubusercontent.com/Shangyi69/psonlineshop/main/logo.png",
19
+ "sizes": "512x512",
20
+ "type": "image/png",
21
+ "purpose": "any maskable"
22
+ }
23
+ ]
24
+ }