XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 1
返回提交历史

XFEstudio/gpt4free

Update providers.html

51b6d038
hlohaus <983577+hlohaus@users.noreply.github.com>
提交于

代码差异

4 个文件 +100 -33
Modified g4f/gui/server/backend_api.py +4 -3
@@ -157,14 +157,13 @@ class Backend_Api(Api):
157 157 logger.error(f"Secret validation failed: {e}")
158 158 return False
159 159
160 @app.route("/backend-api/v2/public-key", methods=["GET", "POST"])
160 @app.route("/backend-api/v2/public-key", methods=["GET"])
161 161 def get_public_key():
162 162 if not has_crypto:
163 163 return (
164 164 jsonify(
165 165 {"error": {"message": "Crypto support is not available"}}
166 166 ),
167 501,
168 167 )
169 168 # try:
170 169 # diff = time.time() - int(base64.b64decode(request.cookies.get("fingerprint")).decode())
@@ -173,13 +172,15 @@ class Backend_Api(Api):
173 172 # if diff > 60 * 60 * 2:
174 173 # return jsonify({"error": {"message": "Please refresh the page"}}), 403
175 174 # Send the public key to the client for encryption
176 return jsonify(
175 response = jsonify(
177 176 {
178 177 "public_key": public_key_pem.decode(),
179 178 "data": encrypt_data(sub_public_key, str(int(time.time()))),
180 179 "user": request.headers.get("x-user", "error"),
181 180 }
182 181 )
182 response.headers["cache-control"] = "no-cache"
183 return response
183 184
184 185 @app.route("/pa/providers", methods=["GET"])
185 186 async def pa_providers():
Modified g4f/gui/server/providers.html +22 -1
@@ -3,7 +3,28 @@
3 3 <head>
4 4 <meta charset="UTF-8">
5 5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Providers</title>
6 <title>AI Providers - GPT4Free | Browse 100+ Free AI Models</title>
7 <meta name="description" content="Browse all AI providers supported by GPT4Free. Find free AI chat, image generation, and text models from OpenAI, Anthropic, Google, Meta, and more.">
8 <meta name="keywords" content="AI providers, GPT4Free, free AI, OpenAI, Anthropic, Google AI, Meta AI, text generation, image generation, AI chat, free AI models">
9 <meta name="author" content="GPT4Free Team">
10
11 <!-- Open Graph -->
12 <meta property="og:type" content="website">
13 <meta property="og:title" content="AI Providers - GPT4Free">
14 <meta property="og:description" content="Browse all AI providers supported by GPT4Free. Free access to 100+ AI models.">
15 <meta property="og:url" content="https://g4f.space/providers">
16 <meta property="og:site_name" content="GPT4Free">
17 <meta property="og:image" content="https://g4f.space/screenshot/providers">
18
19 <!-- Twitter Card -->
20 <meta name="twitter:card" content="summary_large_image">
21 <meta name="twitter:title" content="AI Providers - GPT4Free">
22 <meta name="twitter:description" content="Browse all AI providers supported by GPT4Free. Free access to 100+ AI models.">
23 <meta name="twitter:image" content="https://g4f.space/screenshot/providers">
24
25 <!-- Canonical -->
26 <link rel="canonical" href="https://g4f.space/providers">
27
7 28 <style>
8 29 :root {
9 30 --primary: #6e48aa;
Modified g4f/gui/server/website.py +1 -1
@@ -320,7 +320,7 @@ class Website:
320 320 """
321 321
322 322 # Screenshot / logo section
323 logo_url = f"/screenshot?url={p.get('url', (p.get('base_url', p.get('baseUrl', ''))).replace('https://', '').replace('http://', '').replace('playground.ai.', '').replace('api.', '').replace('router.', '').split('/')[0])}"
323 logo_url = f"/screenshot?url={p.get('url', (p.get('base_url', p.get('baseUrl', ''))).replace('https://', '').replace('http://', '').replace('playground.ai.', '').replace('console.', '').replace('api.', '').replace('router.', '').split('/')[0])}"
324 324 screenshot_html = f"""
325 325 <div class="screenshot-section">
326 326 <img src="{logo_url}" alt="{escape(p['name'])} logo" class="provider-logo"
Modified g4f/requests/cdp.py +73 -28
@@ -287,6 +287,8 @@ def get_shared_browser(host: str, preferred_port: int, headless: bool = True) ->
287 287 "--disable-features=PrivacySandboxSettings4",
288 288 "--disable-blink-features=AutomationControlled",
289 289 "--remote-allow-origins=*",
290 "--disable-web-security",
291 "--disable-features=IsolateOrigins,site-per-process",
290 292 ]
291 293 if headless:
292 294 cmd.append("--headless=new")
@@ -670,34 +672,70 @@ class CDPSession:
670 672 return False
671 673
672 674 async def click_accept_button(self) -> bool:
673 """Find and click an 'Accept' or 'Einwilligen' button."""
675 """Find and click an 'Accept' or 'Einwilligen' button, including inside iframes."""
674 676 js_code = """
675 (() => {
676 const buttons = document.querySelectorAll('button, input[type="submit"]');
677 (() => {
678 const targetTexts = ['Accept', 'Accept all', 'Einwilligen', 'Alle akzeptieren', 'Zustimmen und weiter'];
679
680 function searchDocument(doc, offsetX = 0, offsetY = 0) {
681 try {
682 if (!doc) return null;
683
684 // 1. Search buttons in the current document
685 const buttons = doc.querySelectorAll('button, input[type="submit"], [role="button"]');
677 686 for (let button of buttons) {
678 const text = button.textContent.trim();
679 if (text === 'Accept' || text == 'Accept all' || text === 'Einwilligen' || text === 'Alle akzeptieren') {
687 const text = (button.innerText || button.value || button.textContent || '').trim();
688 if (targetTexts.includes(text)) {
689
690 // NEU: Scrollt das Element/den Container in den sichtbaren Bereich
691 button.scrollIntoView({ block: 'center', inline: 'center' });
692
693 // Wichtig: Nach dem Scrollen müssen die Koordinaten neu berechnet werden!
680 694 const rect = button.getBoundingClientRect();
681 return {
682 x: rect.left + window.scrollX,
683 y: rect.top + window.scrollY,
684 width: rect.width,
685 height: rect.height
686 };
695
696 if (rect.width > 0 && rect.height > 0) {
697 return [
698 offsetX + rect.left + rect.width / 2,
699 offsetY + rect.top + rect.height / 2
700 ];
701 }
687 702 }
688 703 }
689 return null;
690 })()
691 """
704
705 // 2. Search inside nested iframes
706 const iframes = doc.querySelectorAll('iframe');
707 for (let iframe of iframes) {
708 try {
709 const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document;
710 if (iframeDoc) {
711 const iframeRect = iframe.getBoundingClientRect();
712 const res = searchDocument(
713 iframeDoc,
714 offsetX + iframeRect.left,
715 offsetY + iframeRect.top
716 );
717 if (res) return res;
718 }
719 } catch (e) {
720 // Cross-origin iframe security restriction
721 }
722 }
723 } catch (e) {}
724 return null;
725 }
726
727 // window.scrollX/Y wird am Ende aufgeschlagen, falls du absolute Page-Koordinaten brauchst
728 return searchDocument(document, window.scrollX, window.scrollY);
729 })()
730 """
692 731 try:
693 732 rect = await self.evaluate_js(js_code)
694 if rect and isinstance(rect, dict) and rect.get("width", 0) > 0:
695 center_x = int(rect["x"] + rect["width"] / 2)
696 center_y = int(rect["y"] + rect["height"] / 2)
697 await self.click(center_x, center_y)
733 debug.log(f"Accept button rect: {rect}")
734 if rect and isinstance(rect, list) and len(rect) == 2:
735 await self.click(int(rect[0]), int(rect[1]))
698 736 return True
699 737 except Exception as e:
700 logger.debug(f"Failed to click accept button: {e}")
738 debug.log(f"Failed to click accept button: {e}")
701 739 return False
702 740
703 741 async def bypass_turnstile(self):
@@ -746,7 +784,7 @@ class CDPSession:
746 784 """Navigate to a URL and capture a screenshot, caching the result."""
747 785 datekey = datetime.date.today().isoformat()
748 786 screenshot_dir = get_screenshot_dir(datekey)
749 filename = f"{secure_filename(url.replace('https://', '').replace('http://', ''))}.png"
787 filename = f"{secure_filename(url.replace('https://', '').replace('http://', ''))}.jpg"
750 788 filepath = os.path.join(screenshot_dir, filename)
751 789 debug.log(f"Screenshot path: {filepath}")
752 790
@@ -759,22 +797,28 @@ class CDPSession:
759 797 await self.wait_for_network_idle(idle_time=5, timeout=15.0)
760 798 if await self.evaluate_js('!document.doctype'):
761 799 raise RuntimeError(f"Failed to load page {url} for screenshot, doctype={await self.evaluate_js('String(document.doctype)')}")
762 await asyncio.sleep(3)
763 800 # Try to click any "Accept" or "Einwilligen" cookie consent buttons
764 await self.click_accept_button()
765 await asyncio.sleep(0.5)
801 for _ in range(5):
802 debug.log("Attempting to click accept button...")
803 await asyncio.sleep(1)
804 if await self.click_accept_button():
805 debug.log("Clicked accept button.")
806 await asyncio.sleep(1)
807 break
808 await self.wait_for_network_idle(idle_time=5, timeout=15.0)
766 809 result = await self.call("Page.captureScreenshot")
767 810 image_bytes = base64.b64decode(result["data"])
768 811
769 # Resize to 1200x675
812 # Resize to 1200x630 and save as JPEG to reduce file size
770 813 if has_pillow:
771 814 from io import BytesIO
772 815 image = Image.open(BytesIO(image_bytes))
773 816 image = image.resize((1200, 630), Image.Resampling.LANCZOS)
774 817 width, height = image.size
775 818 image = image.crop((0, 0, max(0, width - 14), height))
819 image = image.convert("RGB") # JPEG does not support alpha channel
776 820 output = BytesIO()
777 image.save(output, format="PNG")
821 image.save(output, format="JPEG", quality=85, optimize=True)
778 822 image_bytes = output.getvalue()
779 823
780 824 Path(filepath).write_bytes(image_bytes)
@@ -1083,7 +1127,7 @@ class SyncCDPSession:
1083 1127 """Navigate to a URL and capture a screenshot, caching the result."""
1084 1128 datekey = datetime.date.today().isoformat()
1085 1129 screenshots_dir = get_screenshot_dir(datekey)
1086 filename = secure_filename(url)
1130 filename = f"{secure_filename(url)}.jpg"
1087 1131 filepath = os.path.join(screenshots_dir, filename)
1088 1132
1089 1133 if os.path.exists(filepath):
@@ -1098,15 +1142,16 @@ class SyncCDPSession:
1098 1142 result = self.call("Page.captureScreenshot")
1099 1143 image_bytes = base64.b64decode(result["data"])
1100 1144
1101 # Resize to 1200x675
1145 # Resize to 1200x675 and save as JPEG to reduce file size
1102 1146 if has_pillow:
1103 1147 from io import BytesIO
1104 1148 image = Image.open(BytesIO(image_bytes))
1105 1149 image = image.resize((1200, 675), Image.Resampling.LANCZOS)
1106 1150 width, height = image.size
1107 1151 image = image.crop((0, 0, max(0, width - 10), height))
1152 image = image.convert("RGB") # JPEG does not support alpha channel
1108 1153 output = BytesIO()
1109 image.save(output, format="PNG")
1154 image.save(output, format="JPEG", quality=85, optimize=True)
1110 1155 image_bytes = output.getvalue()
1111 1156
1112 1157 Path(filepath).write_bytes(image_bytes)