tfrere commited on
Commit
0eb5f56
·
0 Parent(s):
Files changed (5) hide show
  1. Dockerfile +47 -0
  2. README.md +69 -0
  3. app/server.py +62 -0
  4. pyproject.toml +19 -0
  5. templates/index.html +151 -0
Dockerfile ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Create non-root user
6
+ RUN useradd -m -u 1000 user
7
+
8
+ # Install system dependencies and Chrome for Playwright
9
+ RUN apt-get update && apt-get install -y \
10
+ wget \
11
+ gnupg \
12
+ curl \
13
+ && pip install --upgrade pip \
14
+ && pip install poetry
15
+
16
+ # Copy poetry configuration
17
+ COPY pyproject.toml poetry.lock* ./
18
+
19
+ # Install Python dependencies using Poetry
20
+ RUN poetry config virtualenvs.create false \
21
+ && poetry install --no-interaction --no-ansi --only main
22
+
23
+ # Install Playwright browsers
24
+ RUN playwright install chromium && \
25
+ playwright install-deps chromium
26
+
27
+ # Create directories
28
+ RUN mkdir -p static templates screenshots && \
29
+ chown -R user:user /app
30
+
31
+ # Copy application code
32
+ COPY app /app/app
33
+ COPY templates /app/templates
34
+ COPY static /app/static
35
+
36
+ # Environment variables
37
+ ENV PORT=7860 \
38
+ HOST=0.0.0.0
39
+
40
+ # Switch to non-root user
41
+ USER user
42
+
43
+ # Expose the port
44
+ EXPOSE 7860
45
+
46
+ # Start command
47
+ CMD ["uvicorn", "app.server:app", "--host", "0.0.0.0", "--port", "7860"]
README.md ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Minimal Browser Screenshot Experiment
2
+
3
+ Cette application web permet de prendre des captures d'écran de sites web en utilisant un navigateur headless et de les afficher directement dans l'interface utilisateur.
4
+
5
+ ## Fonctionnalités
6
+
7
+ - Interface web simple et réactive
8
+ - Saisie d'URL avec valeur par défaut pointant vers le Open LLM Leaderboard
9
+ - Capture d'écran via Playwright (navigateur Chromium)
10
+ - Affichage du résultat dans l'interface
11
+
12
+ ## Technologies utilisées
13
+
14
+ - **Backend**: FastAPI, Playwright
15
+ - **Frontend**: HTML/JS avec Tailwind CSS
16
+ - **Gestion des dépendances**: Poetry
17
+ - **Déploiement**: Docker, Hugging Face Spaces
18
+
19
+ ## Installation locale
20
+
21
+ ### Prérequis
22
+
23
+ - Python 3.10+
24
+ - Poetry
25
+ - Docker (optionnel)
26
+
27
+ ### Installation avec Poetry
28
+
29
+ ```bash
30
+ # Cloner le dépôt
31
+ git clone [url-du-repo]
32
+ cd minimal-browser-screenshot-experiment
33
+
34
+ # Installer les dépendances
35
+ poetry install
36
+
37
+ # Installer les navigateurs Playwright
38
+ poetry run playwright install chromium
39
+ poetry run playwright install-deps chromium
40
+
41
+ # Lancer l'application
42
+ poetry run uvicorn app.server:app --host 0.0.0.0 --port 7860
43
+ ```
44
+
45
+ ### Utilisation avec Docker
46
+
47
+ ```bash
48
+ # Construire l'image Docker
49
+ docker build -t minimal-browser-screenshot .
50
+
51
+ # Lancer le conteneur
52
+ docker run -p 7860:7860 minimal-browser-screenshot
53
+ ```
54
+
55
+ ## Déploiement sur Hugging Face Spaces
56
+
57
+ Cette application est conçue pour être facilement déployée sur un Hugging Face Space.
58
+
59
+ 1. Créez un nouveau Space de type Docker
60
+ 2. Associez ce dépôt GitHub à votre Space
61
+ 3. Le Space utilisera automatiquement le Dockerfile fourni pour construire et déployer l'application
62
+
63
+ ## Utilisation
64
+
65
+ 1. Accédez à l'application via votre navigateur
66
+ 2. Entrez l'URL du site dont vous souhaitez prendre une capture d'écran (par défaut: Open LLM Leaderboard)
67
+ 3. Cliquez sur "Prendre une capture d'écran"
68
+ 4. Attendez quelques secondes pour que le navigateur headless charge la page et prenne la capture
69
+ 5. La capture d'écran s'affiche dans l'interface
app/server.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import asyncio
3
+ from fastapi import FastAPI, Request, Form
4
+ from fastapi.responses import HTMLResponse, JSONResponse, FileResponse
5
+ from fastapi.staticfiles import StaticFiles
6
+ from fastapi.templating import Jinja2Templates
7
+ from playwright.async_api import async_playwright
8
+ import uuid
9
+ import logging
10
+
11
+ # Configure logging
12
+ logging.basicConfig(level=logging.INFO)
13
+ logger = logging.getLogger(__name__)
14
+
15
+ # Create FastAPI app
16
+ app = FastAPI()
17
+
18
+ # Set up templates and static files
19
+ templates = Jinja2Templates(directory="templates")
20
+ app.mount("/static", StaticFiles(directory="static"), name="static")
21
+ os.makedirs("screenshots", exist_ok=True)
22
+
23
+ # Mount the screenshots directory
24
+ app.mount("/screenshots", StaticFiles(directory="screenshots"), name="screenshots")
25
+
26
+ @app.get("/", response_class=HTMLResponse)
27
+ async def read_root(request: Request):
28
+ return templates.TemplateResponse("index.html", {"request": request})
29
+
30
+ @app.post("/take-screenshot")
31
+ async def take_screenshot(url: str = Form(...)):
32
+ logger.info(f"Taking screenshot of URL: {url}")
33
+
34
+ try:
35
+ # Generate a unique filename
36
+ filename = f"{uuid.uuid4()}.png"
37
+ filepath = f"screenshots/{filename}"
38
+
39
+ # Take the screenshot with Playwright
40
+ async with async_playwright() as p:
41
+ browser = await p.chromium.launch()
42
+ page = await browser.new_page()
43
+ await page.goto(url, wait_until="networkidle")
44
+ await page.screenshot(path=filepath)
45
+ await browser.close()
46
+
47
+ return JSONResponse({
48
+ "success": True,
49
+ "screenshot_url": f"/screenshots/{filename}"
50
+ })
51
+
52
+ except Exception as e:
53
+ logger.error(f"Error taking screenshot: {str(e)}")
54
+ return JSONResponse({
55
+ "success": False,
56
+ "error": str(e)
57
+ }, status_code=500)
58
+
59
+ # Add a health check endpoint
60
+ @app.get("/health")
61
+ async def health_check():
62
+ return {"status": "ok"}
pyproject.toml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [tool.poetry]
2
+ name = "minimal-browser-screenshot"
3
+ version = "0.1.0"
4
+ description = "Application qui prend des captures d'écran de sites web via un navigateur"
5
+ authors = ["HuggingFace Team"]
6
+ readme = "README.md"
7
+
8
+ [tool.poetry.dependencies]
9
+ python = "^3.10"
10
+ fastapi = "^0.103.1"
11
+ uvicorn = "^0.23.2"
12
+ jinja2 = "^3.1.2"
13
+ python-multipart = "^0.0.6"
14
+ playwright = "^1.39.0"
15
+ aiofiles = "^23.2.1"
16
+
17
+ [build-system]
18
+ requires = ["poetry-core"]
19
+ build-backend = "poetry.core.masonry.api"
templates/index.html ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Capture d'écran du navigateur</title>
7
+ <link
8
+ href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css"
9
+ rel="stylesheet"
10
+ />
11
+ <style>
12
+ .loading {
13
+ display: none;
14
+ }
15
+ .screenshot-container {
16
+ max-width: 100%;
17
+ overflow-x: auto;
18
+ }
19
+ .screenshot-image {
20
+ max-width: 100%;
21
+ }
22
+ </style>
23
+ </head>
24
+ <body class="bg-gray-100 min-h-screen">
25
+ <div class="container mx-auto px-4 py-8">
26
+ <h1 class="text-3xl font-bold text-center mb-8">
27
+ Capture d'écran du navigateur
28
+ </h1>
29
+
30
+ <div class="bg-white p-6 rounded-lg shadow-md">
31
+ <form id="screenshot-form" class="mb-4">
32
+ <div class="mb-4">
33
+ <label for="url" class="block text-gray-700 font-medium mb-2"
34
+ >URL à capturer</label
35
+ >
36
+ <input
37
+ type="url"
38
+ id="url"
39
+ name="url"
40
+ value="https://huggingface.co/spaces/HuggingFaceH4/open_llm_leaderboard"
41
+ class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
42
+ required
43
+ />
44
+ </div>
45
+ <button
46
+ type="submit"
47
+ class="w-full bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-4 rounded-lg"
48
+ >
49
+ Prendre une capture d'écran
50
+ </button>
51
+ </form>
52
+
53
+ <div id="loading" class="loading flex justify-center items-center py-8">
54
+ <svg
55
+ class="animate-spin -ml-1 mr-3 h-8 w-8 text-blue-500"
56
+ xmlns="http://www.w3.org/2000/svg"
57
+ fill="none"
58
+ viewBox="0 0 24 24"
59
+ >
60
+ <circle
61
+ class="opacity-25"
62
+ cx="12"
63
+ cy="12"
64
+ r="10"
65
+ stroke="currentColor"
66
+ stroke-width="4"
67
+ ></circle>
68
+ <path
69
+ class="opacity-75"
70
+ fill="currentColor"
71
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
72
+ ></path>
73
+ </svg>
74
+ <span class="text-lg text-gray-700">Capture d'écran en cours...</span>
75
+ </div>
76
+
77
+ <div
78
+ id="error"
79
+ class="hidden bg-red-100 text-red-700 p-4 rounded-lg mt-4"
80
+ ></div>
81
+
82
+ <div id="screenshot" class="hidden mt-6">
83
+ <h2 class="text-xl font-semibold mb-4">Résultat :</h2>
84
+ <div class="screenshot-container border rounded-lg p-2">
85
+ <img
86
+ id="screenshot-image"
87
+ class="screenshot-image mx-auto"
88
+ src=""
89
+ alt="Capture d'écran"
90
+ />
91
+ </div>
92
+ </div>
93
+ </div>
94
+ </div>
95
+
96
+ <script>
97
+ document
98
+ .getElementById("screenshot-form")
99
+ .addEventListener("submit", async function (e) {
100
+ e.preventDefault();
101
+
102
+ const url = document.getElementById("url").value;
103
+ const loadingEl = document.getElementById("loading");
104
+ const errorEl = document.getElementById("error");
105
+ const screenshotEl = document.getElementById("screenshot");
106
+ const screenshotImageEl = document.getElementById("screenshot-image");
107
+
108
+ // Reset UI
109
+ errorEl.classList.add("hidden");
110
+ errorEl.textContent = "";
111
+ screenshotEl.classList.add("hidden");
112
+ loadingEl.style.display = "flex";
113
+
114
+ try {
115
+ // Create form data
116
+ const formData = new FormData();
117
+ formData.append("url", url);
118
+
119
+ // Send request
120
+ const response = await fetch("/take-screenshot", {
121
+ method: "POST",
122
+ body: formData,
123
+ });
124
+
125
+ const data = await response.json();
126
+
127
+ if (response.ok && data.success) {
128
+ // Show screenshot
129
+ screenshotImageEl.src = data.screenshot_url;
130
+ screenshotEl.classList.remove("hidden");
131
+ } else {
132
+ // Show error
133
+ errorEl.textContent =
134
+ data.error ||
135
+ "Une erreur est survenue lors de la capture d'écran.";
136
+ errorEl.classList.remove("hidden");
137
+ }
138
+ } catch (error) {
139
+ // Show error
140
+ errorEl.textContent =
141
+ "Une erreur est survenue lors de la connexion au serveur.";
142
+ errorEl.classList.remove("hidden");
143
+ console.error(error);
144
+ } finally {
145
+ // Hide loading
146
+ loadingEl.style.display = "none";
147
+ }
148
+ });
149
+ </script>
150
+ </body>
151
+ </html>