bep40 commited on
Commit
79bb876
·
verified ·
1 Parent(s): 0d3593b

Add persistent shorts stats/comments API (views, likes, shares, comments)

Browse files
Files changed (1) hide show
  1. main.py +47 -1
main.py CHANGED
@@ -1,5 +1,5 @@
1
  """VNEWS - FastAPI backend with livescore + xemlaibongda highlights + YouTube FPT shorts"""
2
- import hashlib, re, time, subprocess, json
3
  import html as html_lib
4
  from datetime import datetime
5
  from concurrent.futures import ThreadPoolExecutor, as_completed
@@ -19,6 +19,23 @@ _cache = {}
19
  _cache_ttl = 300
20
  _cache_ttl_live = 60
21
  _cache_ttl_yt = 1800
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  PRIORITY_LEAGUES = ["Ngoại Hạng Anh","FA Cup","Champions League","LaLiga","Copa del Rey","Serie A","Bundesliga","Ligue 1","V-League"]
23
  LEAGUE_IDS = {"nha":27110,"laliga":27233,"seriea":27044,"bundesliga":26891,"ligue1":27212}
24
  HL_LEAGUES = {"premier-league":{"path":"anh/premier-league","name":"Premier League","emoji":"🏴󠁧󠁢󠁥󠁮󠁧󠁿"},"fa-cup":{"path":"anh/fa-cup","name":"FA Cup","emoji":"🏆"},"bundesliga":{"path":"duc/bundesliga","name":"Bundesliga","emoji":"🇩🇪"},"serie-a":{"path":"italy/serie-a","name":"Serie A","emoji":"🇮🇹"},"la-liga":{"path":"tay-ban-nha/la-liga","name":"La Liga","emoji":"🇪🇸"},"champions-league":{"path":"cup-chau-au/uefa-champions-league","name":"Champions League","emoji":"⭐"},"europa-league":{"path":"cup-chau-au/uefa-europa-league","name":"Europa League","emoji":"🟠"},"world-cup":{"path":"the-gioi/world-cup-qualifiers","name":"World Cup 2026","emoji":"🌍"}}
@@ -246,6 +263,35 @@ def api_livescore_featured():
246
  # ===== VIDEO APIs =====
247
  @app.get("/api/shorts")
248
  def api_shorts():return JSONResponse(_cached("yt_shorts",scrape_shorts,ttl=_cache_ttl_yt))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  @app.get("/api/highlights")
250
  def api_highlights():return JSONResponse(_cached("xemlaibongda_hl",scrape_xemlaibongda,ttl=_cache_ttl))
251
  @app.get("/api/highlights/leagues")
 
1
  """VNEWS - FastAPI backend with livescore + xemlaibongda highlights + YouTube FPT shorts"""
2
+ import hashlib, re, time, subprocess, json, os, threading
3
  import html as html_lib
4
  from datetime import datetime
5
  from concurrent.futures import ThreadPoolExecutor, as_completed
 
19
  _cache_ttl = 300
20
  _cache_ttl_live = 60
21
  _cache_ttl_yt = 1800
22
+ SHORT_STATS_FILE = "/data/short_stats.json" if os.path.isdir("/data") else "/app/short_stats.json"
23
+ _short_lock = threading.Lock()
24
+ def _load_short_db():
25
+ try:
26
+ if os.path.exists(SHORT_STATS_FILE):
27
+ with open(SHORT_STATS_FILE,"r",encoding="utf-8") as f:return json.load(f)
28
+ except:pass
29
+ return {}
30
+ def _save_short_db(db):
31
+ try:
32
+ os.makedirs(os.path.dirname(SHORT_STATS_FILE),exist_ok=True)
33
+ tmp=SHORT_STATS_FILE+".tmp"
34
+ with open(tmp,"w",encoding="utf-8") as f:json.dump(db,f,ensure_ascii=False)
35
+ os.replace(tmp,SHORT_STATS_FILE)
36
+ except:pass
37
+
38
+ def _short_default():return {"views":0,"likes":0,"shares":0,"comments":[]}
39
  PRIORITY_LEAGUES = ["Ngoại Hạng Anh","FA Cup","Champions League","LaLiga","Copa del Rey","Serie A","Bundesliga","Ligue 1","V-League"]
40
  LEAGUE_IDS = {"nha":27110,"laliga":27233,"seriea":27044,"bundesliga":26891,"ligue1":27212}
41
  HL_LEAGUES = {"premier-league":{"path":"anh/premier-league","name":"Premier League","emoji":"🏴󠁧󠁢󠁥󠁮󠁧󠁿"},"fa-cup":{"path":"anh/fa-cup","name":"FA Cup","emoji":"🏆"},"bundesliga":{"path":"duc/bundesliga","name":"Bundesliga","emoji":"🇩🇪"},"serie-a":{"path":"italy/serie-a","name":"Serie A","emoji":"🇮🇹"},"la-liga":{"path":"tay-ban-nha/la-liga","name":"La Liga","emoji":"🇪🇸"},"champions-league":{"path":"cup-chau-au/uefa-champions-league","name":"Champions League","emoji":"⭐"},"europa-league":{"path":"cup-chau-au/uefa-europa-league","name":"Europa League","emoji":"🟠"},"world-cup":{"path":"the-gioi/world-cup-qualifiers","name":"World Cup 2026","emoji":"🌍"}}
 
263
  # ===== VIDEO APIs =====
264
  @app.get("/api/shorts")
265
  def api_shorts():return JSONResponse(_cached("yt_shorts",scrape_shorts,ttl=_cache_ttl_yt))
266
+ @app.get("/api/short-stats")
267
+ def api_short_stats(ids:str=Query(default="")):
268
+ arr=[x for x in ids.split(",") if x]
269
+ with _short_lock:
270
+ db=_load_short_db();out={}
271
+ for vid in arr:
272
+ st=db.get(vid) or _short_default()
273
+ out[vid]={"views":int(st.get("views",0)),"likes":int(st.get("likes",0)),"shares":int(st.get("shares",0)),"comments":st.get("comments",[])[:80]}
274
+ return JSONResponse({"stats":out})
275
+
276
+ @app.post("/api/short-action")
277
+ async def api_short_action(request:Request):
278
+ try:body=await request.json()
279
+ except:body={}
280
+ vid=str(body.get("id","")).strip();action=str(body.get("action","")).strip();txt=str(body.get("text","")).strip()
281
+ if not vid:return JSONResponse({"error":"missing id"},status_code=400)
282
+ with _short_lock:
283
+ db=_load_short_db();st=db.get(vid) or _short_default()
284
+ if action=="view":st["views"]=int(st.get("views",0))+1
285
+ elif action=="like":st["likes"]=int(st.get("likes",0))+1
286
+ elif action=="share":st["shares"]=int(st.get("shares",0))+1
287
+ elif action=="comment" and txt:
288
+ comments=st.get("comments",[])
289
+ comments.insert(0,{"text":txt[:180],"ts":int(time.time())})
290
+ st["comments"]=comments[:80]
291
+ st["updated"]=int(time.time());db[vid]=st;_save_short_db(db)
292
+ out={"views":int(st.get("views",0)),"likes":int(st.get("likes",0)),"shares":int(st.get("shares",0)),"comments":st.get("comments",[])[:80]}
293
+ return JSONResponse({"stats":out})
294
+
295
  @app.get("/api/highlights")
296
  def api_highlights():return JSONResponse(_cached("xemlaibongda_hl",scrape_xemlaibongda,ttl=_cache_ttl))
297
  @app.get("/api/highlights/leagues")