hzruo commited on
Commit
ddf4eb6
·
verified ·
1 Parent(s): f229f74

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +426 -731
main.py CHANGED
@@ -1,735 +1,430 @@
1
- from fastapi import FastAPI, Request, Depends, HTTPException
2
- from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
3
- from fastapi.responses import StreamingResponse, HTMLResponse
4
- from fastapi.background import BackgroundTasks
5
- from contextlib import asynccontextmanager
6
- import requests
7
- from curl_cffi import requests as cffi_requests
8
- import uuid
9
- import json
10
- import time
11
- from typing import Optional
12
- import asyncio
13
- import base64
14
- import tempfile
15
- import os
16
- import re
17
- import threading
18
- from DrissionPage import ChromiumPage, ChromiumOptions
19
- from DrissionPage.common import Settings
20
- import logging
21
- from dotenv import load_dotenv
22
-
23
- # 加载环境变量
24
- load_dotenv(override=True)
25
-
26
- # 配置日志
27
- logging.basicConfig(
28
- level=logging.INFO, # 改为 INFO 级别
29
- format='%(asctime)s - %(levelname)s - %(message)s'
30
- )
31
- logger = logging.getLogger(__name__)
32
-
33
- # 全局配置 DrissionPage 设置
34
- Settings.set_browser_connect_timeout(60) # 增加连接超时时间
35
- Settings.set_language('en') # 设置错误信息为英文,方便调试
36
-
37
- # 修改全局数据存储
38
- global_data = {
39
- "cookie": None,
40
- "cookies": None,
41
- "last_update": 0,
42
- "cookie_expires": 0 # 添加 cookie 过期时间
43
- }
44
-
45
- @asynccontextmanager
46
- async def lifespan(app: FastAPI):
47
- # 启动时获取 cookie
48
- threading.Thread(target=get_cookie).start()
49
- yield
50
- # 关闭时清理资源
51
- global_data["cookie"] = None
52
- global_data["cookies"] = None
53
- global_data["last_update"] = 0
54
-
55
- app = FastAPI(lifespan=lifespan)
56
- security = HTTPBearer()
57
-
58
- # OpenAI API Key 配置,可以通过环境变量覆盖
59
- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", None)
60
- logger.info(f"OPENAI_API_KEY is set: {OPENAI_API_KEY is not None}")
61
- logger.info(f"OPENAI_API_KEY value: {OPENAI_API_KEY}")
62
-
63
- def get_cookie():
64
- try:
65
- logger.info("Starting cookie retrieval process...")
66
-
67
- # 创建配置对象
68
- options = ChromiumOptions()
69
-
70
- # 设置浏览器路径
71
- chrome_path = os.getenv('CHROME_PATH', '/usr/bin/google-chrome-stable')
72
- logger.info(f"Using Chrome path: {chrome_path}")
73
- options.set_browser_path(chrome_path)
74
-
75
- # 设置用户数据目录
76
- user_data_dir = os.getenv('CHROME_USER_DATA_DIR', '/tmp/chrome-data')
77
- logger.info(f"Using user data directory: {user_data_dir}")
78
- options.set_user_data_dir(user_data_dir) # 使用正确的方法设置用户数据目录
79
-
80
- # 设置无头模式和其他参数
81
- options.set_headless(True) # 使用 DrissionPage 提供的方法设置无头模式
82
- options.set_argument('--no-sandbox')
83
- options.set_argument('--disable-dev-shm-usage')
84
- options.set_argument('--disable-gpu')
85
- options.set_argument('--disable-software-rasterizer')
86
- options.set_argument('--disable-extensions')
87
- options.set_argument('--disable-setuid-sandbox')
88
- options.set_argument('--no-first-run')
89
- options.set_argument('--no-zygote')
90
- options.set_argument('--single-process')
91
- options.set_argument('--window-size=1920,1080')
92
- options.set_argument('--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36')
93
-
94
- # 启用详细日志
95
- options.set_argument('--enable-logging')
96
- options.set_argument('--v=1')
97
-
98
- # 使用配置对象创建页面
99
- logger.info("Creating ChromiumPage instance...")
100
- page = ChromiumPage(options)
101
-
102
- logger.info("Navigating to target website...")
103
- page.get("https://chat.akash.network/")
104
-
105
- logger.info("Waiting for page load...")
106
- time.sleep(10)
107
-
108
- logger.info("Getting cookies...")
109
- cookies = page.cookies()
110
- if not cookies:
111
- logger.error("No cookies found")
112
- page.quit()
113
- return None
114
-
115
- cookie_dict = {cookie['name']: cookie['value'] for cookie in cookies}
116
- if 'cf_clearance' not in cookie_dict:
117
- logger.error("cf_clearance cookie not found")
118
- page.quit()
119
- return None
120
-
121
- cookie_str = '; '.join([f"{cookie['name']}={cookie['value']}" for cookie in cookies])
122
- global_data["cookie"] = cookie_str
123
- global_data["last_update"] = time.time()
124
-
125
- expires = min([cookie.get('expires', float('inf')) for cookie in cookies])
126
- if expires != float('inf'):
127
- global_data["cookie_expires"] = expires
128
  else:
129
- global_data["cookie_expires"] = time.time() + 3600
130
-
131
- logger.info("Successfully retrieved cookies")
132
- page.quit()
133
- return cookie_str
134
-
135
- except Exception as e:
136
- logger.error(f"Error fetching cookie: {str(e)}")
137
- logger.error(f"Error type: {type(e)}")
138
- import traceback
139
- logger.error(f"Traceback: {traceback.format_exc()}")
140
- return None
141
-
142
- # 添加刷新 cookie 的函数
143
- async def refresh_cookie():
144
- logger.info("Refreshing cookie due to 401 error")
145
- # 标记 cookie 为过期
146
- global_data["cookie_expires"] = 0
147
- # 获取新的 cookie
148
- return get_cookie()
149
-
150
- async def check_and_update_cookie(background_tasks: BackgroundTasks):
151
- # 如果 cookie 不存在或已过期,则更新
152
- current_time = time.time()
153
- if not global_data["cookie"] or current_time >= global_data["cookie_expires"]:
154
- logger.info("Cookie expired or not available, refreshing...")
155
- background_tasks.add_task(get_cookie)
156
- else:
157
- logger.info("Using existing cookie")
158
-
159
- async def get_api_key(credentials: HTTPAuthorizationCredentials = Depends(security)):
160
- token = credentials.credentials
161
- logger.info(f"Received token: {token}")
162
-
163
- # 如果设置了 OPENAI_API_KEY,则需要验证
164
- if OPENAI_API_KEY is not None:
165
- # 去掉 Bearer 前缀后再比较
166
- clean_token = token.replace("Bearer ", "") if token.startswith("Bearer ") else token
167
- logger.info(f"Clean token: {clean_token}")
168
- if clean_token != OPENAI_API_KEY:
169
- logger.error(f"Token mismatch. Expected: {OPENAI_API_KEY}, Got: {clean_token}")
170
- raise HTTPException(
171
- status_code=401,
172
- detail="Invalid API key"
173
- )
174
- logger.info("API key validation passed")
175
-
176
- return True
177
-
178
- async def validate_cookie(background_tasks: BackgroundTasks):
179
- # 检查并更新 cookie(如果需要)
180
- await check_and_update_cookie(background_tasks)
181
-
182
- # 等待 cookie 初始化完成
183
- max_wait = 30 # 最大等待时间(秒)
184
- start_time = time.time()
185
- while not global_data["cookie"] and time.time() - start_time < max_wait:
186
- await asyncio.sleep(1)
187
- logger.info("Waiting for cookie initialization...")
188
-
189
- # 检查是否有有效的 cookie
190
- if not global_data["cookie"]:
191
- logger.error("Cookie not available after waiting")
192
- raise HTTPException(
193
- status_code=503,
194
- detail="Service temporarily unavailable - Cookie not available"
195
- )
196
-
197
- logger.info("Cookie validation passed")
198
- return global_data["cookie"]
199
-
200
- async def check_image_status(session: requests.Session, job_id: str, headers: dict) -> Optional[str]:
201
- """检查图片生成状态并获取生成的图片"""
202
- max_retries = 30
203
- for attempt in range(max_retries):
204
- try:
205
- print(f"\nAttempt {attempt + 1}/{max_retries} for job {job_id}")
206
- response = session.get(
207
- f'https://chat.akash.network/api/image-status?ids={job_id}',
208
- headers=headers
209
- )
210
- print(f"Status response code: {response.status_code}")
211
- status_data = response.json()
212
-
213
- if status_data and isinstance(status_data, list) and len(status_data) > 0:
214
- job_info = status_data[0]
215
- status = job_info.get('status')
216
- print(f"Job status: {status}")
217
-
218
- # 只有当状态为 completed 时才处理结果
219
- if status == "completed":
220
- result = job_info.get("result")
221
- if result and not result.startswith("Failed"):
222
- print("Got valid result, attempting upload...")
223
- image_url = await upload_to_xinyew(result, job_id)
224
- if image_url:
225
- print(f"Successfully uploaded image: {image_url}")
226
- return image_url
227
- print("Image upload failed")
228
- return None
229
- print("Invalid result received")
230
- return None
231
- elif status == "failed":
232
- print(f"Job {job_id} failed")
233
- return None
234
-
235
- # 如果状态是其他(如 pending),继续等待
236
- await asyncio.sleep(1)
237
- continue
238
-
239
- except Exception as e:
240
- print(f"Error checking status: {e}")
241
- return None
242
-
243
- print(f"Timeout waiting for job {job_id}")
244
- return None
245
-
246
- @app.get("/", response_class=HTMLResponse)
247
- async def health_check():
248
- """Health check endpoint"""
249
- status = {
250
- "status": "ok",
251
- "version": "1.0.0",
252
- "cookie_status": {
253
- "available": global_data["cookie"] is not None,
254
- "last_update": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(global_data["last_update"])) if global_data["last_update"] > 0 else None,
255
- "expires": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(global_data["cookie_expires"])) if global_data["cookie_expires"] > 0 else None
256
- }
257
- }
258
-
259
- html = f"""
260
- <!DOCTYPE html>
261
- <html>
262
- <head>
263
- <title>Akash API Status</title>
264
- <style>
265
- body {{
266
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
267
- line-height: 1.6;
268
- margin: 0;
269
- padding: 20px;
270
- background-color: #f5f5f5;
271
- }}
272
- .container {{
273
- max-width: 800px;
274
- margin: 0 auto;
275
- background-color: white;
276
- padding: 20px;
277
- border-radius: 8px;
278
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
279
- }}
280
- h1 {{
281
- color: #333;
282
- margin-top: 0;
283
- }}
284
- .status {{
285
- display: inline-block;
286
- padding: 4px 8px;
287
- border-radius: 4px;
288
- font-weight: bold;
289
- background-color: #4CAF50;
290
- color: white;
291
- }}
292
- .info {{
293
- margin-top: 20px;
294
- }}
295
- .info-item {{
296
- margin-bottom: 10px;
297
- }}
298
- .label {{
299
- font-weight: bold;
300
- color: #666;
301
- }}
302
- .value {{
303
- color: #333;
304
- }}
305
- .cookie-status {{
306
- margin-top: 20px;
307
- padding: 15px;
308
- background-color: #f8f9fa;
309
- border-radius: 4px;
310
- }}
311
- .cookie-status .available {{
312
- color: {"#4CAF50" if status["cookie_status"]["available"] else "#f44336"};
313
- }}
314
- </style>
315
- </head>
316
- <body>
317
- <div class="container">
318
- <h1>Akash API Status <span class="status">{status["status"]}</span></h1>
319
-
320
- <div class="info">
321
- <div class="info-item">
322
- <span class="label">Version:</span>
323
- <span class="value">{status["version"]}</span>
324
- </div>
325
- </div>
326
-
327
- <div class="cookie-status">
328
- <h2>Cookie Status</h2>
329
- <div class="info-item">
330
- <span class="label">Available:</span>
331
- <span class="value available">{str(status["cookie_status"]["available"])}</span>
332
- </div>
333
- <div class="info-item">
334
- <span class="label">Last Update:</span>
335
- <span class="value">{status["cookie_status"]["last_update"] or "Never"}</span>
336
- </div>
337
- <div class="info-item">
338
- <span class="label">Expires:</span>
339
- <span class="value">{status["cookie_status"]["expires"] or "Unknown"}</span>
340
- </div>
341
- </div>
342
- </div>
343
- </body>
344
- </html>
345
- """
346
-
347
- return html
348
-
349
- @app.post("/v1/chat/completions")
350
- async def chat_completions(
351
- request: Request,
352
- background_tasks: BackgroundTasks,
353
- api_key: bool = Depends(get_api_key),
354
- cookie: str = Depends(validate_cookie)
355
- ):
356
- try:
357
- data = await request.json()
358
-
359
- chat_id = str(uuid.uuid4()).replace('-', '')[:16]
360
-
361
- akash_data = {
362
- "id": chat_id,
363
- "messages": data.get('messages', []),
364
- "model": data.get('model', "DeepSeek-R1"),
365
- "system": data.get('system_message', "You are a helpful assistant."),
366
- "temperature": data.get('temperature', 0.6),
367
- "topP": data.get('top_p', 0.95)
368
- }
369
-
370
- # 构建请求头
371
- headers = {
372
- "Content-Type": "application/json",
373
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
374
- "Accept": "*/*",
375
- "Accept-Language": "en-US,en;q=0.9",
376
- "Accept-Encoding": "gzip, deflate, br",
377
- "Origin": "https://chat.akash.network",
378
- "Referer": "https://chat.akash.network/",
379
- "Sec-Fetch-Dest": "empty",
380
- "Sec-Fetch-Mode": "cors",
381
- "Sec-Fetch-Site": "same-origin",
382
- "Connection": "keep-alive"
383
- }
384
-
385
- # 设置 Cookie
386
- headers["Cookie"] = cookie
387
-
388
- with requests.Session() as session:
389
- response = session.post(
390
- 'https://chat.akash.network/api/chat',
391
- json=akash_data,
392
- headers=headers,
393
- stream=True
394
- )
395
-
396
- # 检查响应状态码,如果是 401,尝试刷新 cookie 并重试
397
- if response.status_code == 401:
398
- logger.info("Cookie expired, refreshing...")
399
- new_cookie = await refresh_cookie()
400
- if new_cookie:
401
- headers["Cookie"] = new_cookie
402
- response = session.post(
403
- 'https://chat.akash.network/api/chat',
404
- json=akash_data,
405
- headers=headers,
406
- stream=True
407
- )
408
-
409
- if response.status_code != 200:
410
- logger.error(f"Akash API error: {response.text}")
411
- raise HTTPException(
412
- status_code=response.status_code,
413
- detail=f"Akash API error: {response.text}"
414
- )
415
-
416
- def generate():
417
- content_buffer = ""
418
- for line in response.iter_lines():
419
- if not line:
420
- continue
421
-
422
- try:
423
- line_str = line.decode('utf-8')
424
- msg_type, msg_data = line_str.split(':', 1)
425
-
426
- if msg_type == '0':
427
- if msg_data.startswith('"') and msg_data.endswith('"'):
428
- msg_data = msg_data.replace('\\"', '"')
429
- msg_data = msg_data[1:-1]
430
- msg_data = msg_data.replace("\\n", "\n")
431
-
432
- # 在处理消息时先判断模型类型
433
- if data.get('model') == 'AkashGen' and "<image_generation>" in msg_data:
434
- # 图片生成模型的特殊处理
435
- async def process_and_send():
436
- messages = await process_image_generation(msg_data, session, headers, chat_id)
437
- if messages:
438
- return messages
439
- return None
440
-
441
- # 创建新的事件循环
442
- loop = asyncio.new_event_loop()
443
- asyncio.set_event_loop(loop)
444
- try:
445
- result_messages = loop.run_until_complete(process_and_send())
446
- finally:
447
- loop.close()
448
-
449
- if result_messages:
450
- for message in result_messages:
451
- yield f"data: {json.dumps(message)}\n\n"
452
- continue
453
-
454
- content_buffer += msg_data
455
-
456
- chunk = {
457
- "id": f"chatcmpl-{chat_id}",
458
- "object": "chat.completion.chunk",
459
- "created": int(time.time()),
460
- "model": data.get('model'),
461
- "choices": [{
462
- "delta": {"content": msg_data},
463
- "index": 0,
464
- "finish_reason": None
465
- }]
466
- }
467
- yield f"data: {json.dumps(chunk)}\n\n"
468
-
469
- elif msg_type in ['e', 'd']:
470
- chunk = {
471
- "id": f"chatcmpl-{chat_id}",
472
- "object": "chat.completion.chunk",
473
- "created": int(time.time()),
474
- "model": data.get('model'),
475
- "choices": [{
476
- "delta": {},
477
- "index": 0,
478
- "finish_reason": "stop"
479
- }]
480
- }
481
- yield f"data: {json.dumps(chunk)}\n\n"
482
- yield "data: [DONE]\n\n"
483
- break
484
-
485
- except Exception as e:
486
- print(f"Error processing line: {e}")
487
- continue
488
-
489
- return StreamingResponse(
490
- generate(),
491
- media_type='text/event-stream',
492
- headers={
493
- 'Cache-Control': 'no-cache',
494
- 'Connection': 'keep-alive',
495
- 'Content-Type': 'text/event-stream'
496
- }
497
- )
498
-
499
- except Exception as e:
500
- print(f"Error in chat_completions: {e}")
501
- import traceback
502
- print(traceback.format_exc())
503
- return {"error": str(e)}
504
-
505
- @app.get("/v1/models")
506
- async def list_models(
507
- background_tasks: BackgroundTasks,
508
- cookie: str = Depends(validate_cookie)
509
- ):
510
- try:
511
- headers = {
512
- "accept": "application/json",
513
- "accept-language": "en-US,en;q=0.9",
514
- "sec-ch-ua": '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
515
- "sec-ch-ua-mobile": "?0",
516
- "sec-ch-ua-platform": '"macOS"',
517
- "sec-fetch-dest": "document",
518
- "sec-fetch-mode": "cors",
519
- "sec-fetch-site": "same-origin",
520
- "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
521
- "referer": "https://chat.akash.network/"
522
- }
523
-
524
- # 设置 Cookie
525
- headers["Cookie"] = cookie
526
-
527
- print(f"Using cookie: {headers.get('Cookie', 'None')}")
528
- print("Sending request to get models...")
529
-
530
- response = requests.get(
531
- 'https://chat.akash.network/api/models',
532
- headers=headers
533
- )
534
-
535
- print(f"Models response status: {response.status_code}")
536
- print(f"Models response headers: {response.headers}")
537
-
538
- if response.status_code == 401:
539
- print("Authentication failed. Please check your API key.")
540
- return {"error": "Authentication failed. Please check your API key."}
541
-
542
- akash_response = response.json()
543
-
544
- # 添加错误处理和调试信息
545
- print(f"Akash API response: {akash_response}")
546
-
547
- # 检查响应格式并适配
548
- models_list = []
549
- if isinstance(akash_response, list):
550
- # 如果直接是列表
551
- models_list = akash_response
552
- elif isinstance(akash_response, dict):
553
- # 如果是字典格式
554
- models_list = akash_response.get("models", [])
555
  else:
556
- print(f"Unexpected response format: {type(akash_response)}")
557
- models_list = []
558
-
559
- # 转换为标准 OpenAI 格式
560
- openai_models = {
561
- "object": "list",
562
- "data": [
563
- {
564
- "id": model["id"] if isinstance(model, dict) else model,
565
- "object": "model",
566
- "created": int(time.time()),
567
- "owned_by": "akash",
568
- "permission": [{
569
- "id": f"modelperm-{model['id'] if isinstance(model, dict) else model}",
570
- "object": "model_permission",
571
- "created": int(time.time()),
572
- "allow_create_engine": False,
573
- "allow_sampling": True,
574
- "allow_logprobs": True,
575
- "allow_search_indices": False,
576
- "allow_view": True,
577
- "allow_fine_tuning": False,
578
- "organization": "*",
579
- "group": None,
580
- "is_blocking": False
581
- }]
582
- } for model in models_list
583
- ]
584
- }
585
-
586
- return openai_models
587
-
588
- except Exception as e:
589
- print(f"Error in list_models: {e}")
590
- import traceback
591
- print(traceback.format_exc())
592
- return {"error": str(e)}
593
-
594
- async def process_image_generation(msg_data: str, session: requests.Session, headers: dict, chat_id: str) -> Optional[list]:
595
- """处理图片生成的逻辑,返回多个消息块"""
596
- match = re.search(r"jobId='([^']+)' prompt='([^']+)' negative='([^']*)'", msg_data)
597
- if match:
598
- job_id, prompt, negative = match.groups()
599
- print(f"Starting image generation process for job_id: {job_id}")
600
-
601
- # 记录开始时间
602
- start_time = time.time()
603
-
604
- # 发送思考开始的消息
605
- think_msg = "<think>\n"
606
- think_msg += "🎨 Generating image...\n\n"
607
- think_msg += f"Prompt: {prompt}\n"
608
-
609
- # 检查图片状态和上传
610
- result = await check_image_status(session, job_id, headers)
611
-
612
- # 计算实际花费的时间
613
- elapsed_time = time.time() - start_time
614
-
615
- # 完成思考部分
616
- think_msg += f"\n🤔 Thinking for {elapsed_time:.1f}s...\n"
617
- think_msg += "</think>"
618
-
619
- # 返回两个独立的消息块
620
- messages = []
621
-
622
- # 第一个消息块:思考过程
623
- messages.append({
624
- "id": f"chatcmpl-{chat_id}-think",
625
- "object": "chat.completion.chunk",
626
- "created": int(time.time()),
627
- "model": "AkashGen",
628
- "choices": [{
629
- "delta": {"content": think_msg},
630
- "index": 0,
631
- "finish_reason": None
632
- }]
633
- })
634
-
635
- # 第二个消息块:图片结果
636
- if result:
637
- image_msg = f"\n\n![Generated Image]({result})"
638
- messages.append({
639
- "id": f"chatcmpl-{chat_id}-image",
640
- "object": "chat.completion.chunk",
641
- "created": int(time.time()),
642
- "model": "AkashGen",
643
- "choices": [{
644
- "delta": {"content": image_msg},
645
- "index": 0,
646
- "finish_reason": None
647
- }]
648
- })
649
  else:
650
- fail_msg = "\n\n*Image generation or upload failed.*"
651
- messages.append({
652
- "id": f"chatcmpl-{chat_id}-fail",
653
- "object": "chat.completion.chunk",
654
- "created": int(time.time()),
655
- "model": "AkashGen",
656
- "choices": [{
657
- "delta": {"content": fail_msg},
658
- "index": 0,
659
- "finish_reason": None
660
- }]
661
- })
662
-
663
- return messages
664
- return None
665
-
666
- async def upload_to_xinyew(image_base64: str, job_id: str) -> Optional[str]:
667
- """上传图片到新野图床并返回URL"""
668
- try:
669
- print(f"\n=== Starting image upload for job {job_id} ===")
670
- print(f"Base64 data length: {len(image_base64)}")
671
-
672
- # 解码base64图片数据
673
- try:
674
- image_data = base64.b64decode(image_base64.split(',')[1] if ',' in image_base64 else image_base64)
675
- print(f"Decoded image data length: {len(image_data)} bytes")
676
- except Exception as e:
677
- print(f"Error decoding base64: {e}")
678
- print(f"First 100 chars of base64: {image_base64[:100]}...")
679
- return None
680
-
681
- # 创建临时文件
682
- with tempfile.NamedTemporaryFile(suffix='.jpeg', delete=False) as temp_file:
683
- temp_file.write(image_data)
684
- temp_file_path = temp_file.name
685
-
686
- try:
687
- filename = f"{job_id}.jpeg"
688
- print(f"Using filename: {filename}")
689
-
690
- # 准备文件上传
691
- files = {
692
- 'file': (filename, open(temp_file_path, 'rb'), 'image/jpeg')
693
- }
694
-
695
- print("Sending request to xinyew.cn...")
696
- response = requests.post(
697
- 'https://api.xinyew.cn/api/jdtc',
698
- files=files,
699
- timeout=30
700
- )
701
-
702
- print(f"Upload response status: {response.status_code}")
703
- if response.status_code == 200:
704
- result = response.json()
705
- print(f"Upload response: {result}")
706
-
707
- if result.get('errno') == 0:
708
- url = result.get('data', {}).get('url')
709
- if url:
710
- print(f"Successfully got image URL: {url}")
711
- return url
712
- print("No URL in response data")
713
- else:
714
- print(f"Upload failed: {result.get('message')}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
715
  else:
716
- print(f"Upload failed with status {response.status_code}")
717
- print(f"Response content: {response.text}")
718
- return None
719
-
720
- finally:
721
- # 清理临时文件
722
- try:
723
- os.unlink(temp_file_path)
724
- except Exception as e:
725
- print(f"Error removing temp file: {e}")
726
-
727
- except Exception as e:
728
- print(f"Error in upload_to_xinyew: {e}")
729
- import traceback
730
- print(traceback.format_exc())
731
- return None
732
-
733
- if __name__ == '__main__':
734
- import uvicorn
735
- uvicorn.run(app, host='0.0.0.0', port=9000)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding:utf-8 -*-
2
+ """
3
+ @Author : g1879
4
+ @Contact : g1879@qq.com
5
+ @Website : https://DrissionPage.cn
6
+ @Copyright: (c) 2020 by g1879, Inc. All Rights Reserved.
7
+ """
8
+ from pathlib import Path
9
+ from re import search
10
+
11
+ from .options_manage import OptionsManager
12
+ from .._functions.settings import Settings as _S
13
+
14
+
15
+ class ChromiumOptions(object):
16
+ def __init__(self, read_file=True, ini_path=None):
17
+ self._user_data_path = None
18
+ self._user = 'Default'
19
+ self._prefs_to_del = []
20
+ self.clear_file_flags = False
21
+ self._is_headless = False
22
+ self._ua_set = False
23
+
24
+ if read_file is False:
25
+ ini_path = False
26
+ self.ini_path = None
27
+ elif ini_path:
28
+ ini_path = Path(ini_path).absolute()
29
+ if not ini_path.exists():
30
+ raise FileNotFoundError(_S._lang.join(_S._lang.INI_NOT_FOUND, PATH=ini_path))
31
+ self.ini_path = str(ini_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  else:
33
+ self.ini_path = str(Path(__file__).parent / 'configs.ini')
34
+
35
+ om = OptionsManager(ini_path)
36
+ options = om.chromium_options
37
+ self._download_path = om.paths.get('download_path', '.') or '.'
38
+ self._tmp_path = om.paths.get('tmp_path', None) or None
39
+ self._arguments = options.get('arguments', [])
40
+ self._browser_path = options.get('browser_path', '')
41
+ self._extensions = options.get('extensions', [])
42
+ self._prefs = options.get('prefs', {})
43
+ self._flags = options.get('flags', {})
44
+ self._address = options.get('address', None)
45
+ self._load_mode = options.get('load_mode', 'normal')
46
+ self._system_user_path = options.get('system_user_path', False)
47
+ self._existing_only = options.get('existing_only', False)
48
+ self._new_env = options.get('new_env', False)
49
+ for i in self._arguments:
50
+ if i.startswith('--headless'):
51
+ self._is_headless = True
52
+ break
53
+
54
+ self._proxy = om.proxies.get('http', None) or om.proxies.get('https', None)
55
+
56
+ user_path = user = False
57
+ for arg in self._arguments:
58
+ if arg.startswith('--user-data-dir='):
59
+ self.set_paths(user_data_path=arg[16:])
60
+ user_path = True
61
+ if arg.startswith('--profile-directory='):
62
+ self.set_user(arg[20:])
63
+ user = True
64
+ if user and user_path:
65
+ break
66
+
67
+ timeouts = om.timeouts
68
+ self._timeouts = {'base': timeouts['base'],
69
+ 'page_load': timeouts['page_load'],
70
+ 'script': timeouts['script']}
71
+
72
+ self._auto_port = options.get('auto_port', False)
73
+
74
+ others = om.others
75
+ self._retry_times = others.get('retry_times', 3)
76
+ self._retry_interval = others.get('retry_interval', 2)
77
+
78
+ return
79
+
80
+ def __repr__(self):
81
+ return f'<ChromiumOptions at {id(self)}>'
82
+
83
+ @property
84
+ def download_path(self):
85
+ return self._download_path
86
+
87
+ @property
88
+ def browser_path(self):
89
+ return self._browser_path
90
+
91
+ @property
92
+ def user_data_path(self):
93
+ return self._user_data_path
94
+
95
+ @property
96
+ def tmp_path(self):
97
+ return self._tmp_path
98
+
99
+ @property
100
+ def user(self):
101
+ return self._user
102
+
103
+ @property
104
+ def load_mode(self):
105
+ return self._load_mode
106
+
107
+ @property
108
+ def timeouts(self):
109
+ return self._timeouts
110
+
111
+ @property
112
+ def proxy(self):
113
+ return self._proxy
114
+
115
+ @property
116
+ def address(self):
117
+ return self._address
118
+
119
+ @property
120
+ def arguments(self):
121
+ return self._arguments
122
+
123
+ @property
124
+ def extensions(self):
125
+ return self._extensions
126
+
127
+ @property
128
+ def preferences(self):
129
+ return self._prefs
130
+
131
+ @property
132
+ def flags(self):
133
+ return self._flags
134
+
135
+ @property
136
+ def system_user_path(self):
137
+ return self._system_user_path
138
+
139
+ @property
140
+ def is_existing_only(self):
141
+ return self._existing_only
142
+
143
+ @property
144
+ def is_auto_port(self):
145
+ return self._auto_port
146
+
147
+ @property
148
+ def retry_times(self):
149
+ return self._retry_times
150
+
151
+ @property
152
+ def retry_interval(self):
153
+ return self._retry_interval
154
+
155
+ @property
156
+ def is_headless(self):
157
+ return self._is_headless
158
+
159
+ def set_retry(self, times=None, interval=None):
160
+ if times is not None:
161
+ self._retry_times = times
162
+ if interval is not None:
163
+ self._retry_interval = interval
164
+ return self
165
+
166
+ def set_argument(self, arg, value=None):
167
+ self.remove_argument(arg)
168
+ if value is not False:
169
+ if arg == '--headless':
170
+ if value == 'false':
171
+ self._is_headless = False
172
+ else:
173
+ if value is None:
174
+ value = 'new'
175
+ self._arguments.append(f'--headless={value}')
176
+ self._is_headless = True
177
+ else:
178
+ arg_str = arg if value is None else f'{arg}={value}'
179
+ self._arguments.append(arg_str)
180
+ elif arg == '--headless':
181
+ self._is_headless = False
182
+ return self
183
+
184
+ def remove_argument(self, value):
185
+ elements_to_delete = [arg for arg in self._arguments if arg == value or arg.startswith(f'{value}=')]
186
+ if not elements_to_delete:
187
+ return self
188
+
189
+ if len(elements_to_delete) == 1:
190
+ self._arguments.remove(elements_to_delete[0])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  else:
192
+ self._arguments = [arg for arg in self._arguments if arg not in elements_to_delete]
193
+
194
+ return self
195
+
196
+ def add_extension(self, path):
197
+ path = Path(path)
198
+ if not path.exists():
199
+ raise FileNotFoundError(_S._lang.join(_S._lang.EXT_NOT_FOUND, PATH=path))
200
+ self._extensions.append(str(path))
201
+ return self
202
+
203
+ def remove_extensions(self):
204
+ self._extensions = []
205
+ return self
206
+
207
+ def set_pref(self, arg, value):
208
+ self._prefs[arg] = value
209
+ return self
210
+
211
+ def remove_pref(self, arg):
212
+ self._prefs.pop(arg, None)
213
+ return self
214
+
215
+ def remove_pref_from_file(self, arg):
216
+ self._prefs_to_del.append(arg)
217
+ return self
218
+
219
+ def set_flag(self, flag, value=None):
220
+ if value is False:
221
+ self._flags.pop(flag, None)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  else:
223
+ self._flags[flag] = value
224
+ return self
225
+
226
+ def clear_flags_in_file(self):
227
+ self.clear_file_flags = True
228
+ return self
229
+
230
+ def clear_flags(self):
231
+ self._flags = {}
232
+ return self
233
+
234
+ def clear_arguments(self):
235
+ self._arguments = []
236
+ return self
237
+
238
+ def clear_prefs(self):
239
+ self._prefs = {}
240
+ return self
241
+
242
+ def set_timeouts(self, base=None, page_load=None, script=None):
243
+ if base is not None:
244
+ self._timeouts['base'] = base
245
+ if page_load is not None:
246
+ self._timeouts['page_load'] = page_load
247
+ if script is not None:
248
+ self._timeouts['script'] = script
249
+
250
+ return self
251
+
252
+ def set_user(self, user='Default'):
253
+ self.set_argument('--profile-directory', user)
254
+ self._user = user
255
+ return self
256
+
257
+ def headless(self, on_off=True):
258
+ on_off = 'new' if on_off else on_off
259
+ return self.set_argument('--headless', on_off)
260
+
261
+ def no_imgs(self, on_off=True):
262
+ on_off = None if on_off else False
263
+ return self.set_argument('--blink-settings=imagesEnabled=false', on_off)
264
+
265
+ def no_js(self, on_off=True):
266
+ on_off = None if on_off else False
267
+ return self.set_argument('--disable-javascript', on_off)
268
+
269
+ def mute(self, on_off=True):
270
+ on_off = None if on_off else False
271
+ return self.set_argument('--mute-audio', on_off)
272
+
273
+ def incognito(self, on_off=True):
274
+ on_off = None if on_off else False
275
+ self.set_argument('--incognito', on_off)
276
+ return self.set_argument('--inprivate', on_off) # edge
277
+
278
+ def new_env(self, on_off=True):
279
+ self._new_env = on_off
280
+ return self
281
+
282
+ def ignore_certificate_errors(self, on_off=True):
283
+ on_off = None if on_off else False
284
+ return self.set_argument('--ignore-certificate-errors', on_off)
285
+
286
+ def set_user_agent(self, user_agent):
287
+ return self.set_argument('--user-agent', user_agent)
288
+
289
+ def set_proxy(self, proxy):
290
+ if search(r'.*?:.*?@.*?\..*', proxy):
291
+ print(_S._lang.UNSUPPORTED_USER_PROXY)
292
+ if proxy.lower().startswith('socks'):
293
+ print(_S._lang.UNSUPPORTED_SOCKS_PROXY)
294
+ self._proxy = proxy
295
+ return self.set_argument('--proxy-server', proxy)
296
+
297
+ def set_load_mode(self, value):
298
+ if value not in ('normal', 'eager', 'none'):
299
+ raise ValueError(_S._lang.join(_S._lang.INCORRECT_VAL_, 'value',
300
+ ALLOW_VAL="'normal', 'eager', 'none'", CURR_VAL=value))
301
+ self._load_mode = value.lower()
302
+ return self
303
+
304
+ def set_paths(self, browser_path=None, local_port=None, address=None, download_path=None,
305
+ user_data_path=None, cache_path=None):
306
+ """快捷的路径设置函数
307
+ :param browser_path: 浏览器可执行文件路径
308
+ :param local_port: 本地端口号
309
+ :param address: 调试浏览器地址,例:127.0.0.1:9222
310
+ :param download_path: 下载文件路径
311
+ :param user_data_path: 用户数据路径
312
+ :param cache_path: 缓存路径
313
+ :return: 当前对象
314
+ """
315
+ if browser_path is not None:
316
+ self.set_browser_path(browser_path)
317
+
318
+ if local_port is not None:
319
+ self.set_local_port(local_port)
320
+
321
+ if address is not None:
322
+ self.set_address(address)
323
+
324
+ if download_path is not None:
325
+ self.set_download_path(download_path)
326
+
327
+ if user_data_path is not None:
328
+ self.set_user_data_path(user_data_path)
329
+
330
+ if cache_path is not None:
331
+ self.set_cache_path(cache_path)
332
+
333
+ return self
334
+
335
+ def set_local_port(self, port):
336
+ self._address = f'127.0.0.1:{port}'
337
+ self._auto_port = False
338
+ return self
339
+
340
+ def set_address(self, address):
341
+ address = address.replace('localhost', '127.0.0.1').lstrip('htps:/')
342
+ self._address = address
343
+ return self
344
+
345
+ def set_browser_path(self, path):
346
+ self._browser_path = str(path)
347
+ return self
348
+
349
+ def set_download_path(self, path):
350
+ self._download_path = '.' if path is None else str(path)
351
+ return self
352
+
353
+ def set_tmp_path(self, path):
354
+ self._tmp_path = str(path)
355
+ return self
356
+
357
+ def set_user_data_path(self, path):
358
+ u = str(path)
359
+ self.set_argument('--user-data-dir', u)
360
+ self._user_data_path = u
361
+ self._auto_port = False
362
+ return self
363
+
364
+ def set_cache_path(self, path):
365
+ self.set_argument('--disk-cache-dir', str(path))
366
+ return self
367
+
368
+ def use_system_user_path(self, on_off=True):
369
+ self._system_user_path = on_off
370
+ return self
371
+
372
+ def auto_port(self, on_off=True, scope=None):
373
+ if on_off:
374
+ self._auto_port = scope if scope else (9600, 59600)
375
+ else:
376
+ self._auto_port = False
377
+ return self
378
+
379
+ def existing_only(self, on_off=True):
380
+ self._existing_only = on_off
381
+ return self
382
+
383
+ def save(self, path=None):
384
+ if path == 'default':
385
+ path = (Path(__file__).parent / 'configs.ini').absolute()
386
+
387
+ elif path is None:
388
+ if self.ini_path:
389
+ path = Path(self.ini_path).absolute()
390
  else:
391
+ path = (Path(__file__).parent / 'configs.ini').absolute()
392
+
393
+ else:
394
+ path = Path(path).absolute()
395
+
396
+ path = path / 'config.ini' if path.is_dir() else path
397
+
398
+ if path.exists():
399
+ om = OptionsManager(path)
400
+ else:
401
+ om = OptionsManager(self.ini_path or (Path(__file__).parent / 'configs.ini'))
402
+
403
+ # 设置chromium_options
404
+ attrs = ('address', 'browser_path', 'arguments', 'extensions', 'user', 'load_mode',
405
+ 'auto_port', 'system_user_path', 'existing_only', 'flags', 'new_env')
406
+ for i in attrs:
407
+ om.set_item('chromium_options', i, self.__getattribute__(f'_{i}'))
408
+ # 设置代理
409
+ om.set_item('proxies', 'http', self._proxy or '')
410
+ om.set_item('proxies', 'https', self._proxy or '')
411
+ # 设置路径
412
+ om.set_item('paths', 'download_path', self._download_path or '')
413
+ om.set_item('paths', 'tmp_path', self._tmp_path or '')
414
+ # 设置timeout
415
+ om.set_item('timeouts', 'base', self._timeouts['base'])
416
+ om.set_item('timeouts', 'page_load', self._timeouts['page_load'])
417
+ om.set_item('timeouts', 'script', self._timeouts['script'])
418
+ # 设置重试
419
+ om.set_item('others', 'retry_times', self.retry_times)
420
+ om.set_item('others', 'retry_interval', self.retry_interval)
421
+ # 设置prefs
422
+ om.set_item('chromium_options', 'prefs', self._prefs)
423
+
424
+ path = str(path)
425
+ om.save(path)
426
+
427
+ return path
428
+
429
+ def save_to_default(self):
430
+ return self.save('default')