Hana Celeste commited on
Commit
6638d42
·
verified ·
1 Parent(s): 59b9d72

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +99 -76
main.py CHANGED
@@ -7,91 +7,114 @@ app = FastAPI()
7
 
8
  IMGBB_API_KEY = "093dee93ec19418d26cc76f40e053725"
9
 
10
- # Từ điển Việt hóa
11
- TRANSLATIONS = {
12
- "Total Achievements": "Tổng Số Thành Tựu",
13
- "Spiral Abyss": "La Hoàn Thâm Cảnh",
14
- "Stygian Onslaught": "Ảo Cảnh Hiểm Ác"
15
- }
 
 
 
 
 
 
16
 
17
- async def get_enka_profile(uid: str):
18
- async with async_playwright() as p:
19
- browser = await p.chromium.launch(headless=True, args=['--no-sandbox'])
20
- context = await browser.new_context(
21
- viewport={'width': 1920, 'height': 1080},
22
- user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
23
- )
24
- page = await context.new_page()
25
-
26
- try:
27
- url = f"https://enka.network/u/{uid}/"
28
- await page.goto(url, wait_until="networkidle", timeout=60000)
29
-
30
- player_data = await page.evaluate("""
31
- () => {
32
- const name = document.querySelector('.details h1')?.innerText;
33
- const arMatch = document.querySelector('.ar')?.innerText.match(/AR(\\d+)/);
34
- const wlMatch = document.querySelector('.ar')?.innerText.match(/WL(\\d+)/);
35
- const signature = document.querySelector('.signature')?.innerText;
36
- const avatar_img = document.querySelector('.avatar-icon img')?.getAttribute('src');
37
-
38
- const stats = Array.from(document.querySelectorAll('tr.stat')).map(tr => ({
39
- value: tr.querySelector('td:first-child')?.innerText.trim(),
40
- label: tr.querySelector('td:last-child')?.innerText.trim()
41
- }));
42
-
43
- return {
44
- name,
45
- ar: arMatch ? arMatch[1] : null,
46
- wl: wlMatch ? wlMatch[1] : null,
47
- signature,
48
- avatar: avatar_img ? "https://enka.network" + avatar_img : null,
49
- stats
50
- };
51
- }
52
- """)
53
 
54
- # --- BẮT ĐẦU VIỆT HÓA ---
55
- if player_data.get("stats"):
56
- for item in player_data["stats"]:
57
- original_label = item["label"]
58
- # Nếu nhãn có trong từ điển thì thay thế, không thì giữ nguyên
59
- item["label"] = TRANSLATIONS.get(original_label, original_label)
60
- # ------------------------
61
-
62
- # Lấy danh sách nhân vật
63
- characters = await page.evaluate("""
64
- () => {
65
- const items = document.querySelectorAll('.CharacterList .avatar');
66
- return Array.from(items).map(el => {
67
- const figure = el.querySelector('figure.chara');
68
- if (!figure) return null;
69
- const style = figure.getAttribute('style');
70
- const icon_path = style.match(/url\\((.*)\\)/)[1].replace(/["']/g, "");
71
- const level = el.querySelector('.level')?.innerText || "0";
72
- const name = icon_path.split('_').pop().split('.')[0];
73
- return { name, level, icon: "https://enka.network" + icon_path };
74
- }).filter(i => i !== null);
75
  }
76
- """)
77
 
78
- await browser.close()
79
- return {"uid": uid, "player": player_data, "characters": characters}
 
 
80
 
81
- except Exception as e:
82
- if 'browser' in locals(): await browser.close()
83
- return {"error": str(e)}
 
 
 
 
 
 
 
 
 
 
84
 
85
- # --- GIỮ NGUYÊN CÁC ENDPOINT KHÁC ---
86
- @app.get("/info")
87
- async def info(uid: str):
88
- return await get_enka_profile(uid)
 
 
 
 
 
 
 
89
 
90
  @app.get("/gen")
91
  async def generate(uid: str, char: str = None):
92
- # (Giữ nguyên hàm get_enka_card như cũ)
93
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
  @app.get("/")
96
  def home():
97
- return {"status": "Enka Vietnamese API is Live"}
 
7
 
8
  IMGBB_API_KEY = "093dee93ec19418d26cc76f40e053725"
9
 
10
+ # --- HELPER: KHỞI TẠO TRÌNH DUYỆT ---
11
+ async def get_page(uid: str):
12
+ browser = await async_playwright().start()
13
+ browser_instance = await browser.chromium.launch(headless=True, args=['--no-sandbox'])
14
+ context = await browser_instance.new_context(
15
+ viewport={'width': 1920, 'height': 1080},
16
+ user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
17
+ )
18
+ page = await context.new_page()
19
+ url = f"https://enka.network/u/{uid}/"
20
+ await page.goto(url, wait_until="networkidle", timeout=60000)
21
+ return browser_instance, page
22
 
23
+ # --- ENDPOINTS ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
+ @app.get("/info")
26
+ async def info(uid: str):
27
+ """Lấy thông tin Profile + Danh sách nhân vật (Đã fix lỗi NULL)"""
28
+ browser, page = await get_page(uid)
29
+ try:
30
+ data = await page.evaluate("""
31
+ () => {
32
+ const name = document.querySelector('.details h1')?.innerText;
33
+ const signature = document.querySelector('.signature')?.innerText;
34
+ const avatar_img = document.querySelector('.avatar-icon img')?.getAttribute('src');
35
+
36
+ // Fix AR & WL: Lấy text tổng rồi dùng Regex linh hoạt
37
+ const arContainer = document.querySelector('.ar');
38
+ let ar = null, wl = null;
39
+ if (arContainer) {
40
+ const text = arContainer.innerText;
41
+ const arMatch = text.match(/AR\\s*(\\d+)/i);
42
+ const wlMatch = text.match(/WL\\s*(\\d+)/i);
43
+ ar = arMatch ? arMatch[1] : null;
44
+ wl = wlMatch ? wlMatch[1] : null;
 
45
  }
 
46
 
47
+ const stats = Array.from(document.querySelectorAll('tr.stat')).map(tr => ({
48
+ value: tr.querySelector('td:first-child')?.innerText.trim(),
49
+ label: tr.querySelector('td:last-child')?.innerText.trim()
50
+ }));
51
 
52
+ const characters = Array.from(document.querySelectorAll('.CharacterList .avatar')).map(el => {
53
+ const figure = el.querySelector('figure.chara');
54
+ if (!figure) return null;
55
+ const style = figure.getAttribute('style');
56
+ const icon_path = style.match(/url\\((.*)\\)/)[1].replace(/["']/g, "");
57
+ const level = el.querySelector('.level')?.innerText || "0";
58
+ const charName = icon_path.split('_').pop().split('.')[0];
59
+ return {
60
+ name: charName,
61
+ level: level,
62
+ icon: "https://enka.network" + icon_path
63
+ };
64
+ }).filter(i => i !== null);
65
 
66
+ return {
67
+ player: { name, ar, wl, signature, avatar: avatar_img ? "https://enka.network" + avatar_img : null, stats },
68
+ characters: characters
69
+ };
70
+ }
71
+ """)
72
+ await browser.close()
73
+ return {"uid": uid, **data}
74
+ except Exception as e:
75
+ await browser.close()
76
+ return {"error": str(e)}
77
 
78
  @app.get("/gen")
79
  async def generate(uid: str, char: str = None):
80
+ """Tự động chọn nhân vật chụp ảnh Card"""
81
+ browser, page = await get_page(uid)
82
+ try:
83
+ if char:
84
+ # Chuẩn hóa tên (viết hoa chữ đầu) để tìm trong style của Enka
85
+ formatted_char = char.strip().capitalize()
86
+ char_selector = f"//div[contains(@class, 'avatar')]//figure[contains(@style, '{formatted_char}')]"
87
+ try:
88
+ await page.wait_for_selector(char_selector, timeout=5000)
89
+ await page.click(char_selector)
90
+ await asyncio.sleep(1.5) # Đợi render
91
+ except: pass
92
+
93
+ # Nhấn nút download ảnh
94
+ await page.click('button[data-icon="image"]')
95
+ await page.wait_for_selector('.Loda img[src^="blob:"]', timeout=20000)
96
+
97
+ img_base64 = await page.evaluate("""
98
+ async () => {
99
+ const img = document.querySelector('.Loda img[src^="blob:"]');
100
+ const res = await fetch(img.src);
101
+ const blob = await res.blob();
102
+ return new Promise(r => {
103
+ const reader = new FileReader();
104
+ reader.onloadend = () => r(reader.result.split(',')[1]);
105
+ reader.readAsDataURL(blob);
106
+ });
107
+ }
108
+ """)
109
+ await browser.close()
110
+
111
+ # Upload ImgBB
112
+ res = requests.post("https://api.imgbb.com/1/upload", data={"key": IMGBB_API_KEY, "image": img_base64})
113
+ return {"uid": uid, "char": char or "default", "card_url": res.json().get('data', {}).get('url')}
114
+ except Exception as e:
115
+ await browser.close()
116
+ return {"error": str(e)}
117
 
118
  @app.get("/")
119
  def home():
120
+ return {"status": "Enka API Final is Running", "endpoints": ["/info?uid=", "/gen?uid=&char="]}