Spaces:
Running
Running
Upload app_v2_entry.py with huggingface_hub
Browse files- app_v2_entry.py +228 -15
app_v2_entry.py
CHANGED
|
@@ -1,11 +1,6 @@
|
|
| 1 |
"""VNEWS v2 Entry Point - with fast bongda proxy"""
|
| 2 |
import sys, os
|
| 3 |
-
from main import app, HEADERS, fetch_bongda_api, HL_LEAGUES
|
| 4 |
-
# Bongda headers — try both names for compatibility
|
| 5 |
-
try:
|
| 6 |
-
from main import BONGDA_HEADERS as _BDH
|
| 7 |
-
except ImportError:
|
| 8 |
-
from main import BONGDA_API_HEADERS as _BDH
|
| 9 |
|
| 10 |
try:
|
| 11 |
import ai_ext
|
|
@@ -423,10 +418,11 @@ def _get_hot_topics():
|
|
| 423 |
@app.get('/api/hot_topics')
|
| 424 |
def api_hot_topics():return JSONResponse({'topics':_get_hot_topics()})
|
| 425 |
@app.get('/')
|
| 426 |
-
# Serve v4
|
| 427 |
async def serve_index():
|
| 428 |
-
p=os.path.join(STATIC_DIR,'
|
| 429 |
if os.path.exists(p):return FileResponse(p,media_type='text/html')
|
|
|
|
|
|
|
| 430 |
return HTMLResponse('<h1>VNEWS</h1>')
|
| 431 |
@app.get('/api/hashtag/sources')
|
| 432 |
def _ht(topic:str=Query(...),page:int=Query(default=0)):
|
|
@@ -496,19 +492,237 @@ async def _pc(request:Request):
|
|
| 496 |
with _il:idb=_lj(IF);idb.setdefault(v,{'views':0,'likes':0,'comments':0});idb[v]['comments']=len(cms);_sj(IF,idb)
|
| 497 |
return JSONResponse({'comments':cms})
|
| 498 |
|
| 499 |
-
# ===
|
| 500 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 501 |
|
| 502 |
@app.get("/doi-bong/{path:path}")
|
| 503 |
-
async def
|
| 504 |
-
return
|
|
|
|
| 505 |
|
| 506 |
@app.get("/giai-dau/{path:path}")
|
| 507 |
-
async def
|
|
|
|
| 508 |
return RedirectResponse(f"https://bongda.com.vn/giai-dau/{path}", status_code=302)
|
| 509 |
|
|
|
|
| 510 |
@app.get("/cau-thu/{path:path}")
|
| 511 |
async def redirect_cau_thu(path: str):
|
|
|
|
| 512 |
return RedirectResponse(f"https://bongda.com.vn/cau-thu/{path}", status_code=302)
|
| 513 |
|
| 514 |
def _bg():
|
|
@@ -519,5 +733,4 @@ def _bg():
|
|
| 519 |
time.sleep(90)
|
| 520 |
threading.Thread(target=_bg,daemon=True).start()
|
| 521 |
|
| 522 |
-
app.mount('/static',StaticFiles(directory=STATIC_DIR),name='vnews_static')
|
| 523 |
-
# Updated: 1781062126
|
|
|
|
| 1 |
"""VNEWS v2 Entry Point - with fast bongda proxy"""
|
| 2 |
import sys, os
|
| 3 |
+
from main import app, HEADERS, BONGDA_HEADERS, fetch_bongda_api, HL_LEAGUES
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
try:
|
| 6 |
import ai_ext
|
|
|
|
| 418 |
@app.get('/api/hot_topics')
|
| 419 |
def api_hot_topics():return JSONResponse({'topics':_get_hot_topics()})
|
| 420 |
@app.get('/')
|
|
|
|
| 421 |
async def serve_index():
|
| 422 |
+
p=os.path.join(STATIC_DIR,'index_v3.html')
|
| 423 |
if os.path.exists(p):return FileResponse(p,media_type='text/html')
|
| 424 |
+
p2=os.path.join(STATIC_DIR,'index_v2.html')
|
| 425 |
+
if os.path.exists(p2):return FileResponse(p2,media_type='text/html')
|
| 426 |
return HTMLResponse('<h1>VNEWS</h1>')
|
| 427 |
@app.get('/api/hashtag/sources')
|
| 428 |
def _ht(topic:str=Query(...),page:int=Query(default=0)):
|
|
|
|
| 492 |
with _il:idb=_lj(IF);idb.setdefault(v,{'views':0,'likes':0,'comments':0});idb[v]['comments']=len(cms);_sj(IF,idb)
|
| 493 |
return JSONResponse({'comments':cms})
|
| 494 |
|
| 495 |
+
# ============================================================
|
| 496 |
+
# TEAM STATS — scrape bongda.com.vn /doi-bong/ page, render inline
|
| 497 |
+
# ============================================================
|
| 498 |
+
def _scrape_team_page(team_path):
|
| 499 |
+
"""Scrape team stats from bongda.com.vn team page."""
|
| 500 |
+
url = f"https://bongda.com.vn/doi-bong/{team_path}"
|
| 501 |
+
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Accept": "text/html", "Referer": "https://bongda.com.vn/"}
|
| 502 |
+
try:
|
| 503 |
+
r = req.get(url, headers=headers, timeout=15, allow_redirects=True)
|
| 504 |
+
if r.status_code != 200:
|
| 505 |
+
return None
|
| 506 |
+
soup = BeautifulSoup(r.text, 'html.parser')
|
| 507 |
+
except:
|
| 508 |
+
return None
|
| 509 |
+
|
| 510 |
+
team = {"name": "", "logo": "", "league": "", "country": "", "stats": {}, "recent": [], "squad": [], "found": False}
|
| 511 |
+
|
| 512 |
+
h1 = soup.select_one('h1')
|
| 513 |
+
if h1:
|
| 514 |
+
team['name'] = _clean(h1.get_text())
|
| 515 |
+
team['found'] = True
|
| 516 |
+
|
| 517 |
+
logo_el = soup.select_one('.team-logo img, .club-logo img, img.team-logo, .team-header img')
|
| 518 |
+
if logo_el:
|
| 519 |
+
team['logo'] = logo_el.get('src', '')
|
| 520 |
+
|
| 521 |
+
info_el = soup.select_one('.team-info, .club-info, .team-header')
|
| 522 |
+
if info_el:
|
| 523 |
+
txt = info_el.get_text()
|
| 524 |
+
if 'việt nam' in txt.lower() or 'vietnam' in txt.lower():
|
| 525 |
+
team['country'] = 'Việt Nam'
|
| 526 |
+
|
| 527 |
+
for li in soup.select('li.match-detail, .match-list .match-item, .recent-matches li'):
|
| 528 |
+
home_el = li.select_one('.home-team .name, .team-home')
|
| 529 |
+
away_el = li.select_one('.away-team .name, .team-away')
|
| 530 |
+
if not home_el or not away_el:
|
| 531 |
+
continue
|
| 532 |
+
home = _clean(home_el.get_text())
|
| 533 |
+
away = _clean(away_el.get_text())
|
| 534 |
+
status_el = li.select_one('.status a, .score')
|
| 535 |
+
score = ""
|
| 536 |
+
event_id = ""
|
| 537 |
+
match_url = ""
|
| 538 |
+
if status_el:
|
| 539 |
+
href = status_el.get('href', '')
|
| 540 |
+
if href:
|
| 541 |
+
match_url = 'https://bongda.com.vn' + href if href.startswith('/') else href
|
| 542 |
+
m = re.search(r'/tran-dau/(\d+)/', href)
|
| 543 |
+
if m:
|
| 544 |
+
event_id = m.group(1)
|
| 545 |
+
spans = status_el.find_all('span')
|
| 546 |
+
if len(spans) >= 3:
|
| 547 |
+
score = f"{spans[0].get_text(strip=True)} - {spans[2].get_text(strip=True)}"
|
| 548 |
+
elif status_el.select_one('.vs'):
|
| 549 |
+
score = 'VS'
|
| 550 |
+
time_el = li.select_one('.match-time, .time, .date')
|
| 551 |
+
mt = _clean(time_el.get_text()) if time_el else ''
|
| 552 |
+
team['recent'].append({
|
| 553 |
+
'home': home, 'away': away, 'score': score, 'time': mt,
|
| 554 |
+
'event_id': event_id, 'url': match_url
|
| 555 |
+
})
|
| 556 |
+
if len(team['recent']) >= 10:
|
| 557 |
+
break
|
| 558 |
+
|
| 559 |
+
standings_rows = []
|
| 560 |
+
for tr in soup.select('.standings-table tr, .league-table tr, table tbody tr'):
|
| 561 |
+
cells = tr.select('td')
|
| 562 |
+
if len(cells) >= 5:
|
| 563 |
+
pos = _clean(cells[0].get_text())
|
| 564 |
+
tname = _clean(cells[1].get_text()) if len(cells) > 1 else ''
|
| 565 |
+
played = _clean(cells[2].get_text()) if len(cells) > 2 else ''
|
| 566 |
+
gd = _clean(cells[3].get_text()) if len(cells) > 3 else ''
|
| 567 |
+
pts = _clean(cells[4].get_text()) if len(cells) > 4 else ''
|
| 568 |
+
if pos and tname and pos.isdigit():
|
| 569 |
+
standings_rows.append({'pos': pos, 'team': tname, 'played': played, 'gd': gd, 'pts': pts})
|
| 570 |
+
if standings_rows:
|
| 571 |
+
team['standings'] = standings_rows
|
| 572 |
+
|
| 573 |
+
for p in soup.select('.player-item, .squad-item, .player-card'):
|
| 574 |
+
pname_el = p.select_one('.player-name, .name, h3, h4')
|
| 575 |
+
pname = _clean(pname_el.get_text()) if pname_el else ''
|
| 576 |
+
if not pname:
|
| 577 |
+
continue
|
| 578 |
+
ppos_el = p.select_one('.position, .pos, .player-pos')
|
| 579 |
+
ppos = _clean(ppos_el.get_text()) if ppos_el else ''
|
| 580 |
+
team['squad'].append({'name': pname, 'position': ppos})
|
| 581 |
+
|
| 582 |
+
breadcrumb = soup.select_one('.breadcrumb, .breadcrumbs')
|
| 583 |
+
if breadcrumb:
|
| 584 |
+
for a in breadcrumb.select('a'):
|
| 585 |
+
lt = _clean(a.get_text())
|
| 586 |
+
if lt and lt.lower() != 'trang chủ':
|
| 587 |
+
team['league'] = lt
|
| 588 |
+
break
|
| 589 |
+
|
| 590 |
+
return team
|
| 591 |
+
|
| 592 |
+
|
| 593 |
+
@app.get("/api/team/{path:path}")
|
| 594 |
+
async def api_team_stats(path: str):
|
| 595 |
+
data = _scrape_team_page(path)
|
| 596 |
+
if data and data.get('found'):
|
| 597 |
+
return JSONResponse(data)
|
| 598 |
+
return JSONResponse({"found": False, "error": "Không tìm thấy đội bóng"})
|
| 599 |
+
|
| 600 |
+
|
| 601 |
+
def _render_team_page(path: str):
|
| 602 |
+
"""Render full HTML team stats page."""
|
| 603 |
+
data = _scrape_team_page(path)
|
| 604 |
+
if not data or not data.get('found'):
|
| 605 |
+
from fastapi.responses import RedirectResponse
|
| 606 |
+
return RedirectResponse(f"https://bongda.com.vn/doi-bong/{path}", status_code=302)
|
| 607 |
+
|
| 608 |
+
team = data
|
| 609 |
+
name = _clean(team.get('name') or 'Đội bóng')
|
| 610 |
+
logo = _clean(team.get('logo') or '')
|
| 611 |
+
league = _clean(team.get('league') or '')
|
| 612 |
+
country = _clean(team.get('country') or '')
|
| 613 |
+
|
| 614 |
+
recent_html = ''
|
| 615 |
+
if team.get('recent'):
|
| 616 |
+
for m in team['recent'][:10]:
|
| 617 |
+
eid = _clean(m.get('event_id', ''))
|
| 618 |
+
murl = _clean(m.get('url', ''))
|
| 619 |
+
sc = _clean(m.get('score', 'VS'))
|
| 620 |
+
mt = _clean(m.get('time', ''))
|
| 621 |
+
home = _clean(m.get('home', ''))
|
| 622 |
+
away = _clean(m.get('away', ''))
|
| 623 |
+
onclick = f'onclick="openMatch(\'{eid}\',\'{murl}\')"' if eid else ''
|
| 624 |
+
recent_html += f'<div class="tm-match" {onclick} style="cursor:pointer">'
|
| 625 |
+
recent_html += f'<span class="tm-match-time">{mt}</span>'
|
| 626 |
+
recent_html += f'<span class="tm-match-teams">{home} <span class="tm-score">{sc}</span> {away}</span>'
|
| 627 |
+
recent_html += '</div>'
|
| 628 |
+
|
| 629 |
+
standings_html = ''
|
| 630 |
+
if team.get('standings'):
|
| 631 |
+
standings_html = '<table class="tm-standings"><thead><tr><th>#</th><th>Đội</th><th>Trận</th><th>HS</th><th>Điểm</th></tr></thead><tbody>'
|
| 632 |
+
for row in team['standings']:
|
| 633 |
+
pos = _clean(row.get('pos', ''))
|
| 634 |
+
tname = _clean(row.get('team', ''))
|
| 635 |
+
played = _clean(row.get('played', ''))
|
| 636 |
+
gd = _clean(row.get('gd', ''))
|
| 637 |
+
pts = _clean(row.get('pts', ''))
|
| 638 |
+
highlight = ' class="tm-highlight"' if name and tname and name.lower()[:6] in tname.lower()[:6] else ''
|
| 639 |
+
standings_html += f'<tr{highlight}><td>{pos}</td><td>{tname}</td><td>{played}</td><td>{gd}</td><td>{pts}</td></tr>'
|
| 640 |
+
standings_html += '</tbody></table>'
|
| 641 |
+
|
| 642 |
+
squad_html = ''
|
| 643 |
+
if team.get('squad'):
|
| 644 |
+
for p in team['squad'][:30]:
|
| 645 |
+
pnm = _clean(p.get('name', ''))
|
| 646 |
+
pps = _clean(p.get('position', ''))
|
| 647 |
+
if not pnm:
|
| 648 |
+
continue
|
| 649 |
+
squad_html += f'<div class="tm-player"><span class="tm-player-name">{pnm}</span><span class="tm-player-pos">{pps}</span></div>'
|
| 650 |
+
|
| 651 |
+
logo_html = f'<img src="{logo}" class="tm-logo" alt="{name}" onerror="this.style.display=\'none\'">' if logo else ''
|
| 652 |
+
|
| 653 |
+
sections_html = ''
|
| 654 |
+
sections_html += '<div class="tm-section"><div class="tm-section-title">📋 Trận đấu gần nhất</div>'
|
| 655 |
+
sections_html += recent_html or '<div class="tm-nodata">Không có dữ liệu trận đấu</div>'
|
| 656 |
+
sections_html += '</div>'
|
| 657 |
+
|
| 658 |
+
if standings_html:
|
| 659 |
+
sections_html += '<div class="tm-section"><div class="tm-section-title">🏆 Bảng xếp hạng</div>' + standings_html + '</div>'
|
| 660 |
+
|
| 661 |
+
if squad_html:
|
| 662 |
+
sections_html += '<div class="tm-section"><div class="tm-section-title">👥 Đội hình</div><div>' + squad_html + '</div></div>'
|
| 663 |
+
|
| 664 |
+
html = f'''<!DOCTYPE html>
|
| 665 |
+
<html lang="vi">
|
| 666 |
+
<head>
|
| 667 |
+
<meta charset="utf-8">
|
| 668 |
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
| 669 |
+
<title>⚽ {name} — Thống kê | VNEWS</title>
|
| 670 |
+
<meta property="og:title" content="⚽ {name} — Thống kê | VNEWS">
|
| 671 |
+
<style>
|
| 672 |
+
*{{margin:0;padding:0;box-sizing:border-box}}body{{background:#0d1117;color:#e0e0e0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}}
|
| 673 |
+
.tm-header{{background:linear-gradient(135deg,#1a2a1f,#0d1117);border-bottom:2px solid #2d8659;padding:16px;text-align:center}}
|
| 674 |
+
.tm-top{{display:flex;align-items:center;justify-content:center;gap:12px;flex-wrap:wrap}}
|
| 675 |
+
.tm-logo{{width:64px;height:64px;object-fit:contain;border-radius:50%;border:3px solid #2d8659;background:#111}}
|
| 676 |
+
.tm-name{{font-size:24px;font-weight:900;color:#fff}}
|
| 677 |
+
.tm-badge{{font-size:12px;color:#888;margin-top:4px}}
|
| 678 |
+
.tm-badge span{{background:#1a2a1f;color:#5cb87a;padding:2px 8px;border-radius:4px;margin:0 2px}}
|
| 679 |
+
.tm-nav{{background:#161b22;padding:8px 16px;display:flex;gap:8px;align-items:center;border-bottom:1px solid #21262d}}
|
| 680 |
+
.tm-nav a{{color:#5cb87a;text-decoration:none;font-size:13px}}
|
| 681 |
+
.tm-nav a:hover{{text-decoration:underline}}
|
| 682 |
+
.tm-container{{max-width:900px;margin:0 auto;padding:16px}}
|
| 683 |
+
.tm-section{{margin-bottom:20px}}
|
| 684 |
+
.tm-section-title{{font-size:14px;font-weight:800;color:#5cb87a;margin-bottom:8px;padding-bottom:6px;border-bottom:1px solid #21262d}}
|
| 685 |
+
.tm-match{{display:flex;align-items:center;gap:10px;padding:10px 12px;background:#161b22;border-radius:6px;margin-bottom:4px;font-size:13px;border:1px solid #21262d;transition:background .2s}}
|
| 686 |
+
.tm-match:hover{{background:#1c2128;border-color:#2d8659}}
|
| 687 |
+
.tm-match-time{{font-size:11px;color:#888;min-width:60px}}
|
| 688 |
+
.tm-match-teams{{flex:1;color:#e0e0e0}}
|
| 689 |
+
.tm-score{{color:#f0c040;font-weight:800}}
|
| 690 |
+
.tm-standings{{width:100%;border-collapse:collapse;font-size:13px}}
|
| 691 |
+
.tm-standings th{{background:#1a2a1f;color:#5cb87a;padding:8px;text-align:left;font-size:11px;border-bottom:1px solid #2d8659}}
|
| 692 |
+
.tm-standings td{{padding:8px;border-bottom:1px solid #21262d}}
|
| 693 |
+
.tm-standings tr:hover td{{background:#161b22}}
|
| 694 |
+
.tm-highlight td{{background:#1a2a1f;border-left:3px solid #5cb87a}}
|
| 695 |
+
.tm-player{{display:inline-flex;align-items:center;gap:6px;background:#161b22;padding:5px 10px;border-radius:5px;margin:3px;font-size:12px;border:1px solid #21262d}}
|
| 696 |
+
.tm-player-name{{color:#e0e0e0}}
|
| 697 |
+
.tm-player-pos{{color:#888;font-size:10px}}
|
| 698 |
+
.tm-back{{display:inline-block;margin-bottom:12px;color:#5cb87a;text-decoration:none;font-size:14px}}
|
| 699 |
+
.tm-back:hover{{text-decoration:underline}}
|
| 700 |
+
.tm-nodata{{text-align:center;color:#666;padding:20px;font-size:13px}}
|
| 701 |
+
</style>
|
| 702 |
+
</head>
|
| 703 |
+
<body>
|
| 704 |
+
<div class="tm-header"><div class="tm-top">{logo_html}<div><div class="tm-name">⚽ {name}</div><div class="tm-badge">{league and f"<span>🏆 {league}</span>"}{country and f"<span>📍 {country}</span>"}<span>📡 VNEWS</span></div></div></div></div>
|
| 705 |
+
<div class="tm-nav"><a href="/" class="tm-back">← Quay về VNEWS</a> <a href="https://bongda.com.vn/doi-bong/{path}" target="_blank">Xem trên Bongda.com.vn ↗</a></div>
|
| 706 |
+
<div class="tm-container">{sections_html}</div>
|
| 707 |
+
<script>function openMatch(id,url){{if(!id)return;window.location.href='/?match='+id+(url?'&url='+encodeURIComponent(url):'');}}</script>
|
| 708 |
+
</body></html>'''
|
| 709 |
+
return HTMLResponse(html)
|
| 710 |
+
|
| 711 |
|
| 712 |
@app.get("/doi-bong/{path:path}")
|
| 713 |
+
async def page_team_stats(path: str):
|
| 714 |
+
return _render_team_page(path)
|
| 715 |
+
|
| 716 |
|
| 717 |
@app.get("/giai-dau/{path:path}")
|
| 718 |
+
async def page_league_internal(path: str):
|
| 719 |
+
from fastapi.responses import RedirectResponse
|
| 720 |
return RedirectResponse(f"https://bongda.com.vn/giai-dau/{path}", status_code=302)
|
| 721 |
|
| 722 |
+
|
| 723 |
@app.get("/cau-thu/{path:path}")
|
| 724 |
async def redirect_cau_thu(path: str):
|
| 725 |
+
from fastapi.responses import RedirectResponse
|
| 726 |
return RedirectResponse(f"https://bongda.com.vn/cau-thu/{path}", status_code=302)
|
| 727 |
|
| 728 |
def _bg():
|
|
|
|
| 733 |
time.sleep(90)
|
| 734 |
threading.Thread(target=_bg,daemon=True).start()
|
| 735 |
|
| 736 |
+
app.mount('/static',StaticFiles(directory=STATIC_DIR),name='vnews_static')
|
|
|