Update app.py
Browse files
app.py
CHANGED
@@ -1,169 +1,141 @@
|
|
1 |
-
import asyncio
|
2 |
-
import websockets
|
3 |
-
import json
|
4 |
import requests
|
5 |
-
import
|
6 |
-
import
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
#
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
#logging.info("Submitted successfully to Google Form.")
|
23 |
-
except requests.exceptions.RequestException as e:
|
24 |
-
logging.error("Failed to submit to Google Form: " + str(e))
|
25 |
-
|
26 |
-
def parse_card_values(dtCard):
|
27 |
-
# Parse card values based on modulus 20
|
28 |
-
card_values = []
|
29 |
-
for card in dtCard:
|
30 |
-
value = dtCard[card] % 20
|
31 |
-
if value in [1, 14]:
|
32 |
-
card_values.append(1)
|
33 |
-
elif value in [11, 12, 13]:
|
34 |
-
card_values.append(10)
|
35 |
-
elif value in range(2, 11):
|
36 |
-
card_values.append(value)
|
37 |
-
else:
|
38 |
-
card_values.append("")
|
39 |
-
return {"CardValues": card_values}
|
40 |
-
|
41 |
-
def handle_case_25(data):
|
42 |
-
# Extract data from case 25
|
43 |
-
group_id = data['groupID']
|
44 |
-
player_score = data['playerScore']
|
45 |
-
banker_score = data['bankerScore']
|
46 |
-
dt_card = data['dtCard']
|
47 |
-
card_values = parse_card_values(dt_card)["CardValues"]
|
48 |
-
|
49 |
-
# Determine result (Player, Banker, Tie)
|
50 |
-
if player_score == banker_score:
|
51 |
-
result = "T"
|
52 |
-
elif player_score > banker_score:
|
53 |
-
result = "P"
|
54 |
else:
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
# Current timestamp
|
68 |
-
today = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
69 |
-
|
70 |
-
# Construct form data
|
71 |
-
record_form = {
|
72 |
-
"entry.1910151925": group_id,
|
73 |
-
"entry.345081202": card_values[0],
|
74 |
-
"entry.790265993": card_values[2],
|
75 |
-
"entry.266290562": card_values[4],
|
76 |
-
"entry.1840046760": card_values[1],
|
77 |
-
"entry.2050858893": card_values[3],
|
78 |
-
"entry.1372409472": card_values[5],
|
79 |
-
"entry.211810544": player_score,
|
80 |
-
"entry.2078666320": banker_score,
|
81 |
-
"entry.1824106848": result,
|
82 |
-
"entry.1579231357": group_type,
|
83 |
-
"entry.911826972": game_no,
|
84 |
-
"entry.305589575": game_no_round,
|
85 |
-
"entry.992348569": dealer_name
|
86 |
-
}
|
87 |
-
return record_form
|
88 |
-
|
89 |
-
async def send_heartbeat(websocket):
|
90 |
-
# Send a heartbeat message to the WebSocket server
|
91 |
-
heartbeat_message = {"protocol": 999, "data": {}}
|
92 |
-
await websocket.send(json.dumps(heartbeat_message))
|
93 |
-
# Wait for a period of time before sending the next heartbeat
|
94 |
-
await asyncio.sleep(10) # Send heartbeat every 10 seconds
|
95 |
-
|
96 |
-
async def send_login(websocket):
|
97 |
-
# Create a JSON message containing login information
|
98 |
-
login_data = {
|
99 |
-
"protocol": 1,
|
100 |
-
"data": {
|
101 |
-
"sid": "ANONYMOUS-1000759",
|
102 |
-
"dtBetLimitSelectID": {
|
103 |
-
"101": 99101, "102": 99102, "103": 99103, "104": 99104,
|
104 |
-
"105": 99105, "106": 99106, "107": 99107, "108": 99108,
|
105 |
-
"110": 99110, "111": 99111, "112": 99112, "113": 99113,
|
106 |
-
"125": 99125, "126": 99126, "128": 99128, "129": 99129
|
107 |
-
},
|
108 |
-
"bGroupList": False,
|
109 |
-
"videoName": "HttpFlv",
|
110 |
-
"videoDelay": 3000,
|
111 |
-
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
|
112 |
-
}
|
113 |
-
}
|
114 |
-
login_message = json.dumps(login_data)
|
115 |
-
# Send the login message to the WebSocket server
|
116 |
-
await websocket.send(login_message)
|
117 |
-
|
118 |
-
async def init_sma_ws():
|
119 |
-
uri = "wss://a45gs-t.wmetg.com/15101"
|
120 |
try:
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
# Receive and process responses from the server
|
128 |
-
while True:
|
129 |
-
try:
|
130 |
-
response = await websocket.recv()
|
131 |
-
json_obj = json.loads(response)
|
132 |
-
protocol = json_obj["protocol"]
|
133 |
-
if protocol == 21:
|
134 |
-
info = json_obj["data"]
|
135 |
-
group_id = info['groupID']
|
136 |
-
game_no = info['gameNo']
|
137 |
-
game_no_round = info['gameNoRound']
|
138 |
-
dealer_name = info['dealerName']
|
139 |
-
group_type = info['groupType']
|
140 |
-
|
141 |
-
table[group_id] = {
|
142 |
-
'groupType': group_type,
|
143 |
-
'gameNo': game_no,
|
144 |
-
'gameNoRound': game_no_round,
|
145 |
-
'dealerName': dealer_name
|
146 |
-
}
|
147 |
-
|
148 |
-
if protocol == 25:
|
149 |
-
data = handle_case_25(json_obj["data"])
|
150 |
-
send_form(data)
|
151 |
-
except websockets.exceptions.ConnectionClosedError as e:
|
152 |
-
logging.error(f"Connection closed: {e}")
|
153 |
-
break
|
154 |
-
except asyncio.CancelledError:
|
155 |
-
logging.info("WebSocket connection cancelled.")
|
156 |
except Exception as e:
|
157 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
158 |
|
159 |
-
|
160 |
-
|
|
|
|
|
|
|
161 |
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
title="WebSocket Interface"
|
167 |
-
)
|
168 |
|
169 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import requests
|
2 |
+
from bs4 import BeautifulSoup
|
3 |
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
4 |
+
import re
|
5 |
+
import pypub
|
6 |
+
import os
|
7 |
+
import time # Thư viện để tính thời gian
|
8 |
+
import gradio as gr # Thêm thư viện Gradio
|
9 |
+
|
10 |
+
# Hàm để phân tích URL và tạo api_url và base_url
|
11 |
+
def parse_story_url(story_url):
|
12 |
+
match = re.search(r"https://truyenfull\.tv/([^/]+)-f3\.(\d+)/", story_url)
|
13 |
+
if match:
|
14 |
+
story_name = match.group(1)
|
15 |
+
story_id = match.group(2)
|
16 |
+
api_url = f"https://truyenfull.tv/api/chapters/{story_id}/"
|
17 |
+
base_url = f"https://truyenfull.tv/{story_name}/chuong-"
|
18 |
+
return story_name, story_id, api_url, base_url
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
else:
|
20 |
+
raise ValueError("URL không hợp lệ")
|
21 |
+
|
22 |
+
# Hàm để lấy thông tin các chương từ API
|
23 |
+
def get_chapter_info(api_url):
|
24 |
+
response = requests.get(api_url)
|
25 |
+
response.raise_for_status() # Ném l��i nếu không thành công
|
26 |
+
data = response.json()
|
27 |
+
return data.get('items', [])
|
28 |
+
|
29 |
+
# Hàm để lấy nội dung của một chương dựa trên thứ tự chương
|
30 |
+
def get_chapter_content(chapter_index, base_url):
|
31 |
+
chapter_url = base_url + str(chapter_index) + ".html"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
try:
|
33 |
+
response = requests.get(chapter_url)
|
34 |
+
response.raise_for_status()
|
35 |
+
soup = BeautifulSoup(response.content, 'html.parser')
|
36 |
+
content_div = soup.find('div', id='chapter-c', class_='chapter-c')
|
37 |
+
return content_div.get_text(separator='\n').strip() if content_div else "Không tìm thấy nội dung chương."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
except Exception as e:
|
39 |
+
print(f"Lỗi khi lấy nội dung chương {chapter_index}: {e}")
|
40 |
+
return "Không thể lấy nội dung."
|
41 |
+
|
42 |
+
# Hàm để lấy nội dung tất cả các chương và lưu vào file
|
43 |
+
def get_all_chapters_content(story_url, start_chapter, max_chapters):
|
44 |
+
story_name, story_id, api_url, base_url = parse_story_url(story_url)
|
45 |
+
|
46 |
+
chapters = get_chapter_info(api_url)
|
47 |
+
if not chapters:
|
48 |
+
return "Không tìm thấy chương nào."
|
49 |
+
|
50 |
+
# Giới hạn số chương tải xuống
|
51 |
+
chapters_to_load = chapters[start_chapter - 1:start_chapter - 1 + max_chapters]
|
52 |
+
chapter_contents = [] # Danh sách lưu nội dung các chương theo thứ tự
|
53 |
+
total_time = 0 # Biến để tính tổng thời gian thực hiện từng chương
|
54 |
+
|
55 |
+
# Sử dụng ThreadPoolExecutor để lấy nội dung các chương song song
|
56 |
+
with ThreadPoolExecutor(max_workers=10) as executor:
|
57 |
+
future_to_chapter = {executor.submit(get_chapter_content, idx + 1, base_url): idx + 1 for idx in range(len(chapters_to_load))}
|
58 |
+
for future in as_completed(future_to_chapter):
|
59 |
+
chapter_index = future_to_chapter[future]
|
60 |
+
start_time = time.time() # Bắt đầu đo thời gian cho mỗi chương
|
61 |
+
try:
|
62 |
+
content = future.result()
|
63 |
+
# Lưu nội dung chương vào danh sách theo thứ tự
|
64 |
+
chapter_contents.append((chapter_index, content, chapters[chapter_index - 1]['chapter_name'])) # Thêm tiêu đề chương
|
65 |
+
print(f"Đã lưu chương {chapter_index}")
|
66 |
+
except Exception as e:
|
67 |
+
print(f"Lỗi khi lấy nội dung chương {chapter_index}: {e}")
|
68 |
+
end_time = time.time() # Kết thúc đo thời gian
|
69 |
+
chapter_time = end_time - start_time
|
70 |
+
total_time += chapter_time # Cộng dồn thời gian cho mỗi chương
|
71 |
+
print(f"Thời gian tải chương {chapter_index}: {chapter_time:.2f} giây")
|
72 |
+
|
73 |
+
# Tính tổng thời gian và thời gian trung bình cho mỗi chương
|
74 |
+
avg_time_per_chapter = total_time / max_chapters if max_chapters > 0 else 0
|
75 |
+
print(f"Tổng thời gian tải {max_chapters} chương: {total_time:.2f} giây")
|
76 |
+
print(f"Thời gian trung bình cho mỗi chương: {avg_time_per_chapter:.2f} giây")
|
77 |
+
|
78 |
+
# Ghi nội dung các chương vào file theo thứ tự đã lưu
|
79 |
+
chapter_contents.sort(key=lambda x: x[0]) # Sắp xếp theo chỉ số chương
|
80 |
+
output_file = f"{story_name}.txt"
|
81 |
+
with open(output_file, 'w', encoding='utf-8') as f:
|
82 |
+
for chapter_index, content, chapter_title in chapter_contents:
|
83 |
+
chapter_name = f"{chapter_title}" # Tạo tên chương với tiêu đề
|
84 |
+
f.write(f"{chapter_name}\n\n")
|
85 |
+
f.write(f"{content}\n")
|
86 |
+
f.write("-" * 50 + "\n")
|
87 |
+
|
88 |
+
# Tạo file EPUB từ nội dung đã lưu
|
89 |
+
create_epub_from_chapters(chapter_contents, story_name)
|
90 |
+
|
91 |
+
# Trả về kết quả
|
92 |
+
return f"Đã tải thành công {max_chapters} chương. Tổng thời gian: {total_time:.2f} giây, Thời gian trung bình: {avg_time_per_chapter:.2f} giây. File TXT: {output_file}"
|
93 |
+
|
94 |
+
# Hàm để tạo file EPUB từ nội dung các chương
|
95 |
+
def create_epub_from_chapters(chapter_contents, story_name):
|
96 |
+
try:
|
97 |
+
# Tạo đối tượng Epub
|
98 |
+
my_epub = pypub.Epub(story_name)
|
99 |
|
100 |
+
# Thêm từng chương vào EPUB
|
101 |
+
for chapter_index, content, chapter_title in chapter_contents:
|
102 |
+
# Tạo chương từ nội dung đã có
|
103 |
+
my_chapter = pypub.create_chapter_from_text(content, chapter_title)
|
104 |
+
my_epub.add_chapter(my_chapter)
|
105 |
|
106 |
+
# Lưu file EPUB
|
107 |
+
output_directory = f"./{story_name}.epub"
|
108 |
+
my_epub.create(output_directory) # Lưu file EPUB
|
109 |
+
print(f"Đã tạo file EPUB: {output_directory}")
|
|
|
|
|
110 |
|
111 |
+
except Exception as e:
|
112 |
+
print(f"Lỗi khi tạo file EPUB: {e}")
|
113 |
+
|
114 |
+
# Giao diện Gradio
|
115 |
+
def gradio_interface(story_url, start_chapter, max_chapters):
|
116 |
+
# Bắt đầu đo thời gian cho toàn bộ quá trình
|
117 |
+
start_total_time = time.time()
|
118 |
+
|
119 |
+
# Gọi hàm tải và xử lý nội dung
|
120 |
+
result = get_all_chapters_content(story_url, int(start_chapter), int(max_chapters))
|
121 |
+
|
122 |
+
# Kết thúc đo thời gian
|
123 |
+
end_total_time = time.time()
|
124 |
+
|
125 |
+
# Tính tổng thời gian cho toàn bộ quá trình
|
126 |
+
total_process_time = end_total_time - start_total_time
|
127 |
+
result += f"\nTổng thời gian hoàn thành tất cả các chức năng: {total_process_time:.2f} giây"
|
128 |
+
return result
|
129 |
+
|
130 |
+
# Tạo giao diện với Gradio
|
131 |
+
gr.Interface(
|
132 |
+
fn=gradio_interface,
|
133 |
+
inputs=[
|
134 |
+
gr.Textbox(label="URL Truyện", placeholder="Nhập URL của truyện từ truyenfull.tv"),
|
135 |
+
gr.Textbox(label="Số chương bắt đầu", placeholder="Nhập số chương bắt đầu"),
|
136 |
+
gr.Textbox(label="Số chương muốn tải", placeholder="Nhập số chương muốn tải")
|
137 |
+
],
|
138 |
+
outputs="text",
|
139 |
+
title="Truyện Full Downloader",
|
140 |
+
description="Công cụ tải truyện từ truyenfull.tv và tạo file EPUB."
|
141 |
+
).launch()
|