tomo2chin2 commited on
Commit
d9a1abd
·
verified ·
1 Parent(s): ff061fa

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +64 -322
app.py CHANGED
@@ -1,341 +1,83 @@
1
  import gradio as gr
2
- import os
3
- import tempfile
4
- import time
5
- import base64
6
  from selenium import webdriver
7
  from selenium.webdriver.chrome.options import Options
8
- from selenium.webdriver.chrome.service import Service
9
- from webdriver_manager.chrome import ChromeDriverManager
10
- from PIL import Image
11
- import io
12
 
13
- # Setup directories
14
- TEMP_DIR = os.path.join(tempfile.gettempdir(), "html-screenshots")
15
- os.makedirs(TEMP_DIR, exist_ok=True)
16
 
17
- def setup_driver():
18
- """Setup and return a headless Chrome driver"""
19
- chrome_options = Options()
20
- chrome_options.add_argument("--headless")
21
- chrome_options.add_argument("--no-sandbox")
22
- chrome_options.add_argument("--disable-dev-shm-usage")
23
- chrome_options.add_argument("--disable-gpu")
24
- chrome_options.add_argument("--window-size=1280,1024")
25
-
26
- # Use system-installed ChromeDriver on Hugging Face Spaces
27
- try:
28
- # Try direct ChromeDriver approach first (for Hugging Face Spaces)
29
- driver = webdriver.Chrome(options=chrome_options)
30
- except:
31
- try:
32
- # Explicit path as fallback (often works on HF Spaces)
33
- service = Service('/usr/bin/chromedriver')
34
- driver = webdriver.Chrome(service=service, options=chrome_options)
35
- except Exception as e:
36
- # Last resort - try webdriver manager
37
- service = Service(ChromeDriverManager().install())
38
- driver = webdriver.Chrome(service=service, options=chrome_options)
39
-
40
- return driver
41
 
42
- def get_screenshot(html_code, width=1280, height=800, full_page=False, wait_time=1):
43
- """Take a screenshot of the HTML using headless Chrome"""
44
- if not html_code.strip():
45
- return None, "Please enter some HTML code first."
46
-
47
- # Create a temporary HTML file
48
- timestamp = int(time.time())
49
- html_file = os.path.join(TEMP_DIR, f"temp_{timestamp}.html")
50
-
51
  try:
52
- # Add doctype and viewport meta if not present
53
- if "<!DOCTYPE" not in html_code:
54
- html_code = f"""<!DOCTYPE html>
55
- <html>
56
- <head>
57
- <meta charset="UTF-8">
58
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
59
- <style>
60
- body {{
61
- font-family: 'Arial', sans-serif;
62
- line-height: 1.6;
63
- color: #333;
64
- max-width: 100%;
65
- }}
66
- </style>
67
- </head>
68
- <body>
69
- {html_code}
70
- </body>
71
- </html>"""
72
-
73
- # Write HTML to temporary file
74
- with open(html_file, "w", encoding="utf-8") as f:
75
- f.write(html_code)
76
-
77
- # Initialize the driver
78
- driver = setup_driver()
79
-
80
- try:
81
- # Navigate to the HTML file
82
- driver.get(f"file://{html_file}")
83
-
84
- # Wait for any JavaScript to load
85
- time.sleep(wait_time)
86
-
87
- # Set window size
88
- driver.set_window_size(width, height)
89
-
90
- # Take screenshot
91
- if full_page:
92
- # Get the height of the page
93
- height = driver.execute_script("return Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);")
94
- driver.set_window_size(width, height)
95
-
96
- screenshot = driver.get_screenshot_as_png()
97
-
98
- # Convert to PIL Image for processing
99
- img = Image.open(io.BytesIO(screenshot))
100
-
101
- # Create an in-memory buffer for the image
102
- buffer = io.BytesIO()
103
- img.save(buffer, format="PNG")
104
- img_str = base64.b64encode(buffer.getvalue()).decode("utf-8")
105
-
106
- return f"data:image/png;base64,{img_str}", None
107
-
108
- finally:
109
- driver.quit()
110
-
111
- except Exception as e:
112
- return None, f"Error taking screenshot: {str(e)}"
113
- finally:
114
- # Clean up temporary file
115
- if os.path.exists(html_file):
116
- try:
117
- os.remove(html_file)
118
- except:
119
- pass
120
-
121
- return None, "Failed to generate screenshot."
122
 
123
- def render_screenshot(html_code, width, height, full_page, wait_time):
124
- """Process HTML and return screenshot with potential error message"""
125
- screenshot, error = get_screenshot(
126
- html_code,
127
- width=width,
128
- height=height,
129
- full_page=full_page,
130
- wait_time=wait_time
131
- )
132
-
133
- if error:
134
- return None, error
135
-
136
- return screenshot, None
137
 
138
- # Create elegant theme inspired by Intercontinental Hotel Ishigaki
139
- theme = gr.themes.Soft(
140
- primary_hue="indigo",
141
- secondary_hue="blue",
142
- neutral_hue="slate",
143
- font=gr.themes.GoogleFont("Inter"),
144
- font_mono=gr.themes.GoogleFont("IBM Plex Mono"),
145
- ).set(
146
- button_primary_background_fill="*primary_500",
147
- button_primary_background_fill_hover="*primary_600",
148
- button_primary_text_color="white",
149
- button_secondary_background_fill="white",
150
- button_secondary_background_fill_hover="*neutral_100",
151
- button_secondary_text_color="*neutral_800",
152
- block_title_text_color="*primary_700",
153
- block_label_text_color="*neutral_600",
154
- background_fill_primary="white"
155
- )
156
-
157
- # Custom CSS for elegant styling
158
- css = """
159
- .gradio-container {
160
- max-width: 1200px !important;
161
- margin: 0 auto !important;
162
- background-color: #ffffff !important;
163
- box-shadow: 0 4px 30px rgba(0, 0, 0, 0.03) !important;
164
- border-radius: 16px !important;
165
- overflow: hidden !important;
166
- }
167
-
168
- .header-container {
169
- background: linear-gradient(135deg, #e0f2fe, #f0f9ff) !important;
170
- padding: 2rem 1.5rem 1.5rem !important;
171
- border-bottom: 1px solid rgba(0, 0, 0, 0.03) !important;
172
- margin-bottom: 1.5rem !important;
173
- }
174
 
175
- .header-title {
176
- font-weight: 300 !important;
177
- color: #1e40af !important;
178
- font-size: 2rem !important;
179
- margin: 0 !important;
180
- letter-spacing: -0.02em !important;
181
- }
182
 
183
- .header-subtitle {
184
- color: #475569 !important;
185
- font-weight: 400 !important;
186
- margin-top: 0.5rem !important;
187
- }
188
 
189
- .input-panel {
190
- background-color: white !important;
191
- border-radius: 10px !important;
192
- padding: 0.5rem !important;
193
- border: 1px solid #e2e8f0 !important;
194
- }
195
 
196
- .screenshot-container {
197
- background-color: white !important;
198
- border: 1px solid #e2e8f0 !important;
199
- border-radius: 10px !important;
200
- overflow: hidden !important;
201
- display: flex !important;
202
- flex-direction: column !important;
203
- align-items: center !important;
204
- justify-content: center !important;
205
- min-height: 400px !important;
206
- }
207
 
208
- .screenshot-container img {
209
- max-width: 100% !important;
210
- height: auto !important;
211
- object-fit: contain !important;
212
- margin: 0 auto !important;
213
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05) !important;
214
- }
215
 
216
- .error-message {
217
- color: #ef4444 !important;
218
- background-color: #fef2f2 !important;
219
- padding: 1rem !important;
220
- border-radius: 8px !important;
221
- margin-top: 1rem !important;
222
- font-size: 0.9rem !important;
223
- border-left: 3px solid #ef4444 !important;
224
- }
225
 
226
- .footer {
227
- text-align: center !important;
228
- padding: 1.5rem !important;
229
- color: #94a3b8 !important;
230
- font-size: 0.8rem !important;
231
- border-top: 1px solid #f1f5f9 !important;
232
- margin-top: 2rem !important;
233
- }
234
 
235
- /* Responsive adjustments */
236
- @media (max-width: 768px) {
237
- .header-title {
238
- font-size: 1.5rem !important;
239
- }
240
-
241
- .two-column {
242
- flex-direction: column !important;
243
- }
244
- }
245
- """
246
-
247
- # Create the Gradio interface
248
- with gr.Blocks(theme=theme, css=css) as demo:
249
- # Header
250
- with gr.Column(elem_classes=["header-container"]):
251
- gr.Markdown("# HTML Screenshot Generator", elem_classes=["header-title"])
252
- gr.Markdown("Capture beautiful screenshots of your HTML using headless Chrome",
253
- elem_classes=["header-subtitle"])
254
-
255
- # State for storing error messages
256
- error_message = gr.State("")
257
-
258
- # Main content with responsive layout
259
- with gr.Row(equal_height=True, elem_classes=["two-column"]) as layout_row:
260
- # Input panel
261
- with gr.Column(scale=1, elem_classes=["input-panel"]):
262
- html_input = gr.Code(
263
- label="HTML Code",
264
- language="html",
265
- value="<!-- Enter your HTML here -->\n<h1>Hello, World!</h1>\n<p>This is a sample HTML page.</p>",
266
- lines=15,
267
- elem_id="html-input"
268
- )
269
-
270
- with gr.Accordion("Screenshot Options", open=False):
271
- with gr.Row():
272
- width_input = gr.Slider(
273
- minimum=320, maximum=2560, value=1280, step=10,
274
- label="Width (px)"
275
- )
276
- height_input = gr.Slider(
277
- minimum=240, maximum=1600, value=800, step=10,
278
- label="Height (px)"
279
- )
280
-
281
- with gr.Row():
282
- full_page = gr.Checkbox(
283
- label="Capture Full Page Height",
284
- value=False
285
- )
286
- wait_time = gr.Slider(
287
- minimum=0.1, maximum=5.0, value=1.0, step=0.1,
288
- label="Wait Time (seconds)"
289
- )
290
-
291
- with gr.Row():
292
- clear_btn = gr.Button("Clear", variant="secondary")
293
- screenshot_btn = gr.Button("Take Screenshot", variant="primary")
294
-
295
- # Error message display
296
- error_display = gr.Markdown(
297
- visible=False,
298
- elem_classes=["error-message"]
299
- )
300
-
301
- # Screenshot panel
302
- with gr.Column(scale=1, elem_classes=["screenshot-container"]):
303
- screenshot_output = gr.Image(
304
- label="Screenshot Preview",
305
- type="filepath",
306
- height=600
307
- )
308
-
309
- download_btn = gr.Button("Download Screenshot", visible=False)
310
-
311
- # Footer
312
- with gr.Column(elem_classes=["footer"]):
313
- gr.Markdown("*Designed with the elegant simplicity of Intercontinental Hotel Ishigaki's Club Lounge in mind*")
314
-
315
- # Event handlers
316
- def process_screenshot(html_code, width, height, full_page, wait_time):
317
- screenshot, error = render_screenshot(html_code, width, height, full_page, wait_time)
318
- if error:
319
- return None, error, gr.update(visible=True), gr.update(visible=False)
320
- return screenshot, None, gr.update(visible=False), gr.update(visible=True)
321
-
322
- screenshot_btn.click(
323
- fn=process_screenshot,
324
- inputs=[html_input, width_input, height_input, full_page, wait_time],
325
- outputs=[screenshot_output, error_display, error_display, download_btn]
326
- )
327
-
328
- clear_btn.click(
329
- fn=lambda: (
330
- "<!-- Enter your HTML here -->\n<h1>Hello, World!</h1>\n<p>This is a sample HTML page.</p>",
331
- None,
332
- gr.update(visible=False),
333
- gr.update(visible=False)
334
- ),
335
- inputs=None,
336
- outputs=[html_input, screenshot_output, error_display, download_btn]
337
- )
338
 
339
- # Launch the app
340
  if __name__ == "__main__":
341
- demo.launch()
 
1
  import gradio as gr
 
 
 
 
2
  from selenium import webdriver
3
  from selenium.webdriver.chrome.options import Options
4
+ from selenium.webdriver.common.by import By
5
+ import time
6
+ import os
 
7
 
8
+ def capture_screenshot(html_content, width=1280, height=720):
9
+ """
10
+ 指定されたHTMLコンテンツのスクリーンショットを撮る関数。
11
 
12
+ Args:
13
+ html_content: スクリーンショットを撮りたいHTMLコンテンツの文字列。
14
+ width: ブラウザウィンドウの幅 (ピクセル単位)
15
+ height: ブラウザウィンドウの高さ (ピクセル単位)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
+ Returns:
18
+ スクリーンショット画像のファイルパス。
19
+ エラーが発生した場合はNoneを返す。
20
+ """
 
 
 
 
 
21
  try:
22
+ # Headless Chrome のオプションを設定
23
+ chrome_options = Options()
24
+ chrome_options.add_argument("--headless") # ヘッドレスモードを有効にする
25
+ chrome_options.add_argument("--no-sandbox") # sandboxを無効にする(rootで実行する場合に必要)
26
+ chrome_options.add_argument("--disable-dev-shm-usage") # /dev/shm を使わないようにする
27
+ chrome_options.add_argument(f"--window-size={width},{height}") # ウィンドウサイズ
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
+ #chromedriverのパスを指定(環境変数から取得)
30
+ chromedriver_path = os.environ.get("CHROMEDRIVER_PATH")
31
+ if not chromedriver_path:
32
+ raise ValueError("CHROMEDRIVER_PATH 環境変数が設定されていません。")
 
 
 
 
 
 
 
 
 
 
33
 
34
+ # Chrome WebDriver を初期化
35
+ driver = webdriver.Chrome(executable_path=chromedriver_path, options=chrome_options)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
+ # HTMLコンテンツをファイルに書き込む(一時ファイル)
38
+ temp_html_path = "temp.html"
39
+ with open(temp_html_path, "w", encoding="utf-8") as f:
40
+ f.write(html_content)
 
 
 
41
 
 
 
 
 
 
42
 
43
+ # ブラウザで一時HTMLファイルを開く
44
+ driver.get("file://" + os.path.abspath(temp_html_path))
45
+ time.sleep(1) # 念のため少し待つ(JavaScriptの実行などを考慮)
 
 
 
46
 
47
+ # スクリーンショットを撮る
48
+ screenshot_path = "screenshot.png"
49
+ driver.save_screenshot(screenshot_path)
 
 
 
 
 
 
 
 
50
 
51
+ # ブラウザを閉じる
52
+ driver.quit()
 
 
 
 
 
53
 
54
+ # 一時HTMLファイルを削除
55
+ os.remove(temp_html_path)
 
 
 
 
 
 
 
56
 
57
+ return screenshot_path
 
 
 
 
 
 
 
58
 
59
+ except Exception as e:
60
+ print(f"Error: {e}")
61
+ return None
62
+
63
+
64
+ # Gradio インターフェースの定義
65
+ iface = gr.Interface(
66
+ fn=capture_screenshot,
67
+ inputs=[
68
+ gr.HTML(label="HTML Content"), # HTML入力用のコンポーネント
69
+ gr.Slider(minimum=320, maximum=1920, value=1280, label="Width"), # 幅指定
70
+ gr.Slider(minimum=240, maximum=1080, value=720, label="Height"), # 高さ指定
71
+
72
+ ],
73
+ outputs=gr.Image(type="filepath", label="Screenshot"), # 画像出力
74
+ title="HTML Screenshot Capture",
75
+ description="Enter HTML content and capture a screenshot.",
76
+ examples=[
77
+ ["<h1>Hello, World!</h1><p>This is a test.</p>", 800, 600], # サンプル
78
+ ["<div style='background-color: lightblue; padding: 20px;'><h2>Styled Div</h2></div>", 640, 480], # サンプル
79
+ ]
80
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
 
82
  if __name__ == "__main__":
83
+ iface.launch()