Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,18 +1,17 @@
|
|
1 |
import os
|
2 |
os.system("playwright install")
|
3 |
-
# app.py (Final, Working Async Version)
|
4 |
|
5 |
import gradio as gr
|
6 |
from playwright.async_api import async_playwright, Error as PlaywrightError
|
7 |
from bs4 import BeautifulSoup
|
8 |
import urllib.parse
|
9 |
-
import atexit
|
10 |
-
import re
|
11 |
import os
|
12 |
from itertools import cycle
|
13 |
import uuid
|
14 |
|
15 |
-
# --- 1.
|
|
|
16 |
P = None
|
17 |
BROWSER = None
|
18 |
REVOLVER = None
|
@@ -43,7 +42,7 @@ class CredentialRevolver:
|
|
43 |
def get_next(self): return next(self.proxy_cycler) if self.proxy_cycler else None
|
44 |
def count(self): return len(self.proxies)
|
45 |
|
46 |
-
# --- 3. ASYNC LOGIC ---
|
47 |
async def _fetch_and_update_tab_state(tab_state: TabState, url: str):
|
48 |
log = f"▶️ Navigating to {url}..."; live_page = LIVE_CONTEXTS[tab_state.id]["page"]
|
49 |
try:
|
@@ -91,22 +90,10 @@ async def handle_action(browser_state: BrowserState, action: str, value=None):
|
|
91 |
browser_state.active_tab_id = value; log = f"Switched to tab."
|
92 |
return browser_state, log
|
93 |
|
94 |
-
|
95 |
-
active_tab = browser_state.get_active_tab()
|
96 |
-
if not active_tab: return {page_content: gr.Markdown("No active tabs."), url_textbox: "", links_display: "", tab_selector: gr.Radio(choices=[])}
|
97 |
-
tab_choices = [(f"Tab {i}: {t.title[:25]}... (via {t.proxy_used})", t.id) for i, t in enumerate(browser_state.tabs)]
|
98 |
-
links_md = "### 🔗 Links on Page\n" + ('\n'.join(f"{i}. [{link['text'][:80]}]({link['url']})" for i, link in enumerate(active_tab.links[:25])) if active_tab.links else "_No links found._")
|
99 |
-
return {
|
100 |
-
page_content: gr.Markdown(f"# {active_tab.title}\n**URL:** {active_tab.url}\n\n---\n\n{active_tab.parsed_text[:2000]}..."),
|
101 |
-
url_textbox: gr.Textbox(value=active_tab.url), links_display: gr.Markdown(links_md),
|
102 |
-
tab_selector: gr.Radio(choices=tab_choices, value=active_tab.id, label="Active Tabs"),
|
103 |
-
}
|
104 |
-
|
105 |
-
# --- 4. GRADIO UI AND EVENT HANDLING ---
|
106 |
with gr.Blocks(theme=gr.themes.Soft(), title="Real Browser Demo") as demo:
|
107 |
browser_state = gr.State(BrowserState())
|
108 |
gr.Markdown("# 🛰️ Real Browser Demo (Final Working Version)")
|
109 |
-
gr.Markdown(f"This demo runs a real headless browser. All threading issues are resolved.")
|
110 |
# UI Layout is the same...
|
111 |
with gr.Row():
|
112 |
with gr.Column(scale=3):
|
@@ -120,8 +107,9 @@ with gr.Blocks(theme=gr.themes.Soft(), title="Real Browser Demo") as demo:
|
|
120 |
links_display = gr.Markdown("...");
|
121 |
with gr.Row(): click_num_box = gr.Number(label="Link #", scale=1, minimum=0, step=1); click_btn = gr.Button("Click Link", scale=2)
|
122 |
|
|
|
123 |
all_outputs = [page_content, url_textbox, links_display, tab_selector, log_display]
|
124 |
-
|
125 |
async def master_handler(current_state, action, value=None):
|
126 |
global APP_STARTED, P, BROWSER, REVOLVER
|
127 |
if not APP_STARTED:
|
@@ -131,18 +119,47 @@ with gr.Blocks(theme=gr.themes.Soft(), title="Real Browser Demo") as demo:
|
|
131 |
print(f"✅ Playwright started. {REVOLVER.count()} proxies loaded."); APP_STARTED = True
|
132 |
|
133 |
new_state, log = await handle_action(current_state, action, value)
|
134 |
-
ui_updates = update_ui_components(new_state)
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
147 |
|
148 |
demo.launch()
|
|
|
1 |
import os
|
2 |
os.system("playwright install")
|
3 |
+
# app.py (Final, Working Async Version with Bugfixes)
|
4 |
|
5 |
import gradio as gr
|
6 |
from playwright.async_api import async_playwright, Error as PlaywrightError
|
7 |
from bs4 import BeautifulSoup
|
8 |
import urllib.parse
|
|
|
|
|
9 |
import os
|
10 |
from itertools import cycle
|
11 |
import uuid
|
12 |
|
13 |
+
# --- 1. GLOBAL RESOURCES & STATE ---
|
14 |
+
# These are initialized on the first request to be compatible with Spaces.
|
15 |
P = None
|
16 |
BROWSER = None
|
17 |
REVOLVER = None
|
|
|
42 |
def get_next(self): return next(self.proxy_cycler) if self.proxy_cycler else None
|
43 |
def count(self): return len(self.proxies)
|
44 |
|
45 |
+
# --- 3. ASYNC LOGIC (Unchanged) ---
|
46 |
async def _fetch_and_update_tab_state(tab_state: TabState, url: str):
|
47 |
log = f"▶️ Navigating to {url}..."; live_page = LIVE_CONTEXTS[tab_state.id]["page"]
|
48 |
try:
|
|
|
90 |
browser_state.active_tab_id = value; log = f"Switched to tab."
|
91 |
return browser_state, log
|
92 |
|
93 |
+
# --- 4. GRADIO UI AND EVENT HANDLING (WITH FIXES) ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
with gr.Blocks(theme=gr.themes.Soft(), title="Real Browser Demo") as demo:
|
95 |
browser_state = gr.State(BrowserState())
|
96 |
gr.Markdown("# 🛰️ Real Browser Demo (Final Working Version)")
|
|
|
97 |
# UI Layout is the same...
|
98 |
with gr.Row():
|
99 |
with gr.Column(scale=3):
|
|
|
107 |
links_display = gr.Markdown("...");
|
108 |
with gr.Row(): click_num_box = gr.Number(label="Link #", scale=1, minimum=0, step=1); click_btn = gr.Button("Click Link", scale=2)
|
109 |
|
110 |
+
# This order must be consistent
|
111 |
all_outputs = [page_content, url_textbox, links_display, tab_selector, log_display]
|
112 |
+
|
113 |
async def master_handler(current_state, action, value=None):
|
114 |
global APP_STARTED, P, BROWSER, REVOLVER
|
115 |
if not APP_STARTED:
|
|
|
119 |
print(f"✅ Playwright started. {REVOLVER.count()} proxies loaded."); APP_STARTED = True
|
120 |
|
121 |
new_state, log = await handle_action(current_state, action, value)
|
122 |
+
ui_updates = update_ui_components(new_state)
|
123 |
+
|
124 |
+
# **THE CRITICAL FIX IS HERE:**
|
125 |
+
# We must return a tuple with a value for EACH output component, in the correct order.
|
126 |
+
return (
|
127 |
+
new_state, # 1. For the browser_state output
|
128 |
+
ui_updates[page_content], # 2.
|
129 |
+
ui_updates[url_textbox], # 3.
|
130 |
+
ui_updates[links_display], # 4.
|
131 |
+
ui_updates[tab_selector], # 5.
|
132 |
+
ui_updates[log_display] # 6.
|
133 |
+
)
|
134 |
+
|
135 |
+
# **THE SECOND CRITICAL FIX IS HERE:**
|
136 |
+
# Each event listener is a simple, separate async function that Gradio can correctly await.
|
137 |
+
|
138 |
+
async def on_load(state):
|
139 |
+
return await master_handler(state, "new_tab", None)
|
140 |
+
|
141 |
+
async def on_go_click(state, value):
|
142 |
+
return await master_handler(state, "go", value)
|
143 |
+
|
144 |
+
async def on_click_link(state, value):
|
145 |
+
return await master_handler(state, "click", value)
|
146 |
+
|
147 |
+
async def on_new_tab(state):
|
148 |
+
return await master_handler(state, "new_tab", None)
|
149 |
+
|
150 |
+
async def on_close_tab(state):
|
151 |
+
return await master_handler(state, "close_tab", None)
|
152 |
+
|
153 |
+
async def on_switch_tab(state, value):
|
154 |
+
return await master_handler(state, "switch_tab", value)
|
155 |
+
|
156 |
+
# Wire up the new, clean event handlers
|
157 |
+
demo.load(on_load, inputs=[browser_state], outputs=[browser_state, *all_outputs])
|
158 |
+
go_btn.click(on_go_click, [browser_state, url_textbox], [browser_state, *all_outputs], show_progress="full")
|
159 |
+
url_textbox.submit(on_go_click, [browser_state, url_textbox], [browser_state, *all_outputs], show_progress="full")
|
160 |
+
click_btn.click(on_click_link, [browser_state, click_num_box], [browser_state, *all_outputs], show_progress="full")
|
161 |
+
new_tab_btn.click(on_new_tab, [browser_state], [browser_state, *all_outputs], show_progress="full")
|
162 |
+
close_tab_btn.click(on_close_tab, [browser_state], [browser_state, *all_outputs])
|
163 |
+
tab_selector.input(on_switch_tab, [browser_state, tab_selector], [browser_state, *all_outputs])
|
164 |
|
165 |
demo.launch()
|