toanatp commited on
Commit
ffd3cfe
·
1 Parent(s): 302849c

Initial commit for Elevenlabs Worker 14

Browse files
Files changed (4) hide show
  1. Dockerfile +6 -0
  2. README.md +5 -3
  3. app.py +162 -0
  4. requirements.txt +4 -0
Dockerfile ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+ WORKDIR /code
3
+ COPY ./requirements.txt /code/requirements.txt
4
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
5
+ COPY ./app.py /code/app.py
6
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
  title: Elevenlabs Worker 14
3
- emoji: 🦀
4
- colorFrom: pink
5
- colorTo: yellow
6
  sdk: docker
7
  pinned: false
 
 
8
  ---
9
 
10
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
  title: Elevenlabs Worker 14
3
+ emoji: 👀
4
+ colorFrom: blue
5
+ colorTo: indigo
6
  sdk: docker
7
  pinned: false
8
+ license: mit
9
+ short_description: worker thợ 01
10
  ---
11
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #<--BẮT ĐẦU TOÀN BỘ CODE CHO FILE app.py (WORKER THỢ) - PHIÊN BẢN CUỐI CÙNG-->
2
+ import asyncio
3
+ import aiohttp
4
+ import os
5
+ from fastapi import FastAPI, Header, Depends, HTTPException
6
+ from pydantic import BaseModel, Field
7
+ from typing import List, Optional, Dict, Any
8
+ from datetime import datetime
9
+ import logging
10
+
11
+ # --- Cấu hình Logging ---
12
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
13
+ logger = logging.getLogger(__name__)
14
+
15
+ # --- Hằng số ---
16
+ CHECK_KEY_TIMEOUT = 10
17
+
18
+ # --- Xác thực ---
19
+ API_AUTH_KEY = os.getenv("AUTH_KEY", "default-secret-key-change-me")
20
+
21
+ async def api_key_auth(x_api_key: str = Header(...)):
22
+ if x_api_key != API_AUTH_KEY:
23
+ raise HTTPException(status_code=401, detail="Invalid or Missing API Key")
24
+
25
+ # --- Pydantic Models ---
26
+ class APIKeyPayload(BaseModel):
27
+ api_keys: List[str]
28
+
29
+ class KeyStatusResult(BaseModel):
30
+ key: str
31
+ status: str
32
+ credit_remaining: int
33
+ credit_limit: int
34
+ reset_date: Optional[str] = None
35
+ message: str
36
+
37
+ # --- Khởi tạo ứng dụng FastAPI ---
38
+ app = FastAPI(
39
+ title="ElevenLabs Credit Checker (Thợ)",
40
+ description="Nhận việc từ Load Balancer và thực hiện kiểm tra key tốc độ cao.",
41
+ version="2.0.0",
42
+ )
43
+
44
+ # --- Logic kiểm tra key ---
45
+ async def check_single_key_status(session: aiohttp.ClientSession, api_key: str) -> Dict[str, Any]:
46
+ url = "https://api.elevenlabs.io/v1/user/subscription"
47
+ headers = {"xi-api-key": api_key}
48
+ result = {"key": api_key, "status": "Error", "credit_remaining": 0, "credit_limit": 0, "reset_date": None, "message": ""}
49
+
50
+ if not api_key:
51
+ result["message"], result["status"] = "Key rỗng.", "Invalid"
52
+ return result
53
+
54
+ try:
55
+ timeout = aiohttp.ClientTimeout(total=CHECK_KEY_TIMEOUT)
56
+ async with session.get(url, headers=headers, timeout=timeout) as response:
57
+ if response.status == 200:
58
+ data = await response.json()
59
+ char_count = data.get("character_count", 0)
60
+ char_limit = data.get("character_limit", 0)
61
+ reset_unix = data.get("next_character_count_reset_unix")
62
+
63
+ result["status"] = "OK"
64
+ result["credit_remaining"] = max(0, char_limit - char_count)
65
+ result["credit_limit"] = char_limit
66
+ result["reset_date"] = datetime.fromtimestamp(reset_unix).strftime('%Y-%m-%d %H:%M:%S') if reset_unix else "N/A"
67
+ result["message"] = f"OK ({result['credit_remaining']:,}/{result['credit_limit']:,})"
68
+ elif response.status == 401:
69
+ result["message"], result["status"] = "Invalid/Unauthorized", "Invalid"
70
+ else:
71
+ body = await response.text()
72
+ result["message"], result["status"] = f"API Error {response.status}: {body[:100]}", "API Error"
73
+ return result
74
+ except (aiohttp.ClientError, asyncio.TimeoutError) as e:
75
+ result["message"], result["status"] = f"Network/Timeout Error", "Network Error"
76
+ return result
77
+ except Exception as e:
78
+ logger.exception(f"Lỗi không xác định khi kiểm tra key ...{api_key[-4:]}")
79
+ result["message"], result["status"] = f"Unknown Error: {str(e)}", "Unknown Error"
80
+ return result
81
+
82
+ # Trong file app.py của Worker Thợ
83
+
84
+ #<--BẮT ĐẦU THAY THẾ HÀM check_all_keys_logic -->
85
+ async def check_all_keys_logic(api_keys: List[str]) -> List[Dict[str, Any]]:
86
+ unique_keys = sorted(list(set(k.strip() for k in api_keys if k.strip())))
87
+ if not unique_keys:
88
+ return []
89
+
90
+ logger.info(f"Bắt đầu kiểm tra {len(unique_keys)} key...")
91
+
92
+ final_results: Dict[str, Any] = {}
93
+ keys_to_retry: List[str] = []
94
+
95
+ # --- VÒNG 1: KIỂM TRA TẤT CẢ CÁC KEY ---
96
+ logger.info(">>> Vòng 1: Kiểm tra toàn bộ lô key...")
97
+ async with aiohttp.ClientSession() as session:
98
+ tasks_round_1 = [check_single_key_status(session, key) for key in unique_keys]
99
+ results_round_1 = await asyncio.gather(*tasks_round_1, return_exceptions=True)
100
+
101
+ for i, res in enumerate(results_round_1):
102
+ key_in_check = unique_keys[i]
103
+
104
+ # Xử lý trường hợp task bị lỗi nghiêm trọng
105
+ if isinstance(res, Exception):
106
+ logger.error(f"Lỗi task nghiêm trọng cho key ...{key_in_check[-5:]}: {res}")
107
+ # Ghi nhận kết quả lỗi và thêm vào danh sách retry
108
+ final_results[key_in_check] = {"key": key_in_check, "status": "Task Error", "credit_remaining": 0, "credit_limit": 0, "reset_date": None, "message": str(res)}
109
+ keys_to_retry.append(key_in_check)
110
+ continue
111
+
112
+ # Ghi nhận kết quả từ vòng 1
113
+ final_results[key_in_check] = res
114
+
115
+ # Quyết định xem key này có cần retry hay không
116
+ # Chỉ retry các lỗi tạm thời như Network, Timeout, hoặc lỗi Server (5xx)
117
+ # Không retry các lỗi chắc chắn như Invalid key (401)
118
+ if res.get("status") in ["Network Error", "API Error", "Unknown Error", "Task Error"]:
119
+ # Kiểm tra chi tiết hơn message của API Error để loại trừ lỗi 401
120
+ if "401" in res.get("message", "") or "Invalid" in res.get("status", ""):
121
+ logger.info(f"Key ...{key_in_check[-5:]} bị lỗi 401/Invalid, sẽ KHÔNG thử lại.")
122
+ else:
123
+ logger.info(f"Key ...{key_in_check[-5:]} bị lỗi tạm thời ({res.get('status')}), sẽ được thử lại.")
124
+ keys_to_retry.append(key_in_check)
125
+
126
+ # --- VÒNG 2: CHỈ KIỂM TRA LẠI CÁC KEY BỊ LỖI ---
127
+ if keys_to_retry:
128
+ logger.info(f">>> Vòng 2: Thử lại {len(keys_to_retry)} key bị lỗi...")
129
+ await asyncio.sleep(1) # Thêm một khoảng nghỉ nhỏ trước khi retry
130
+
131
+ async with aiohttp.ClientSession() as session:
132
+ tasks_round_2 = [check_single_key_status(session, key) for key in keys_to_retry]
133
+ results_round_2 = await asyncio.gather(*tasks_round_2, return_exceptions=True)
134
+
135
+ for i, res_retry in enumerate(results_round_2):
136
+ key_in_retry = keys_to_retry[i]
137
+
138
+ if isinstance(res_retry, Exception):
139
+ logger.error(f"Lỗi task nghiêm trọng (Vòng 2) cho key ...{key_in_retry[-5:]}: {res_retry}")
140
+ # Giữ nguyên kết quả lỗi từ vòng 1 nếu vòng 2 cũng lỗi
141
+ continue
142
+
143
+ # GHI ĐÈ kết quả lỗi từ vòng 1 bằng kết quả mới từ vòng 2
144
+ # Bất kể kết quả vòng 2 là gì (OK hoặc vẫn là Error), nó đều mới hơn và chính xác hơn.
145
+ final_results[key_in_retry] = res_retry
146
+ logger.info(f"Đã cập nhật kết quả Vòng 2 cho key ...{key_in_retry[-5:]}. Status mới: {res_retry.get('status')}")
147
+
148
+ logger.info("Kiểm tra key hoàn tất.")
149
+ # Chuyển dictionary kết quả về lại dạng list theo đúng thứ tự ban đầu
150
+ return [final_results[key] for key in unique_keys]
151
+ #<--KẾT THÚC THAY THẾ HÀM check_all_keys_logic -->
152
+ # --- API Endpoint ---
153
+ @app.post("/check-credits", response_model=List[KeyStatusResult], dependencies=[Depends(api_key_auth)])
154
+ async def check_credits_endpoint(payload: APIKeyPayload):
155
+ """Nhận danh sách API key và trả về trạng thái của từng key."""
156
+ return await check_all_keys_logic(payload.api_keys)
157
+
158
+ @app.get("/", include_in_schema=False)
159
+ def root():
160
+ return {"message": "Chào mừng đến với Worker Thợ!"}
161
+
162
+ #<--KẾT THÚC TOÀN BỘ CODE CHO FILE app.py (WORKER THỢ) -->
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ fastapi
2
+ uvicorn[standard]
3
+ aiohttp
4
+ pydantic